Mercurial > repos > blastem
comparison vdp.c @ 1931:374a5ae694e8 mame_interp
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 18 Apr 2020 11:42:53 -0700 |
parents | c7e3e3ebb64a |
children | c3c62dbf1ceb |
comparison
equal
deleted
inserted
replaced
1843:13abdc98379e | 1931:374a5ae694e8 |
---|---|
68 {127, 0, 0}, //Window | 68 {127, 0, 0}, //Window |
69 {0, 127, 0}, //B | 69 {0, 127, 0}, //B |
70 {127, 0, 127} //Sprites | 70 {127, 0, 127} //Sprites |
71 }; | 71 }; |
72 | 72 |
73 static uint32_t calc_crop(uint32_t crop, uint32_t border) | |
74 { | |
75 return crop >= border ? 0 : border - crop; | |
76 } | |
77 | |
73 static void update_video_params(vdp_context *context) | 78 static void update_video_params(vdp_context *context) |
74 { | 79 { |
80 uint32_t top_crop = render_overscan_top(); | |
81 uint32_t bot_crop = render_overscan_bot(); | |
82 uint32_t border_top; | |
75 if (context->regs[REG_MODE_2] & BIT_MODE_5) { | 83 if (context->regs[REG_MODE_2] & BIT_MODE_5) { |
76 if (context->regs[REG_MODE_2] & BIT_PAL) { | 84 if (context->regs[REG_MODE_2] & BIT_PAL) { |
77 if (context->flags2 & FLAG2_REGION_PAL) { | 85 if (context->flags2 & FLAG2_REGION_PAL) { |
78 context->inactive_start = PAL_INACTIVE_START; | 86 context->inactive_start = PAL_INACTIVE_START; |
79 context->border_top = BORDER_TOP_V30_PAL; | 87 border_top = BORDER_TOP_V30_PAL; |
80 context->border_bot = BORDER_BOT_V30_PAL; | 88 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V30_PAL); |
81 } else { | 89 } else { |
82 //the behavior here is rather weird and needs more investigation | 90 //the behavior here is rather weird and needs more investigation |
83 context->inactive_start = 0xF0; | 91 context->inactive_start = 0xF0; |
84 context->border_top = 1; | 92 border_top = 1; |
85 context->border_bot = 3; | 93 context->border_bot = calc_crop(bot_crop, 3); |
86 } | 94 } |
87 } else { | 95 } else { |
88 context->inactive_start = NTSC_INACTIVE_START; | 96 context->inactive_start = NTSC_INACTIVE_START; |
89 if (context->flags2 & FLAG2_REGION_PAL) { | 97 if (context->flags2 & FLAG2_REGION_PAL) { |
90 context->border_top = BORDER_TOP_V28_PAL; | 98 border_top = BORDER_TOP_V28_PAL; |
91 context->border_bot = BORDER_BOT_V28_PAL; | 99 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V28_PAL); |
92 } else { | 100 } else { |
93 context->border_top = BORDER_TOP_V28; | 101 border_top = BORDER_TOP_V28; |
94 context->border_bot = BORDER_TOP_V28; | 102 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V28); |
95 } | 103 } |
96 } | 104 } |
97 if (context->regs[REG_MODE_4] & BIT_H40) { | 105 if (context->regs[REG_MODE_4] & BIT_H40) { |
98 context->max_sprites_frame = MAX_SPRITES_FRAME; | 106 context->max_sprites_frame = MAX_SPRITES_FRAME; |
99 context->max_sprites_line = MAX_SPRITES_LINE; | 107 context->max_sprites_line = MAX_SPRITES_LINE; |
110 } | 118 } |
111 } | 119 } |
112 } else { | 120 } else { |
113 context->inactive_start = MODE4_INACTIVE_START; | 121 context->inactive_start = MODE4_INACTIVE_START; |
114 if (context->flags2 & FLAG2_REGION_PAL) { | 122 if (context->flags2 & FLAG2_REGION_PAL) { |
115 context->border_top = BORDER_TOP_V24_PAL; | 123 border_top = BORDER_TOP_V24_PAL; |
116 context->border_bot = BORDER_BOT_V24_PAL; | 124 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V24_PAL); |
117 } else { | 125 } else { |
118 context->border_top = BORDER_TOP_V24; | 126 border_top = BORDER_TOP_V24; |
119 context->border_bot = BORDER_BOT_V24; | 127 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V24); |
120 } | 128 } |
121 if (!(context->regs[REG_MODE_1] & BIT_MODE_4)){ | 129 if (!(context->regs[REG_MODE_1] & BIT_MODE_4)){ |
122 context->state = INACTIVE; | 130 context->state = INACTIVE; |
123 } else if (context->state == INACTIVE) { | 131 } else if (context->state == INACTIVE) { |
124 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active | 132 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active |
128 else if (context->vcounter == 0x1FF) { | 136 else if (context->vcounter == 0x1FF) { |
129 context->state = PREPARING; | 137 context->state = PREPARING; |
130 } | 138 } |
131 } | 139 } |
132 } | 140 } |
141 context->border_top = calc_crop(top_crop, border_top); | |
142 context->top_offset = border_top - context->border_top; | |
133 } | 143 } |
134 | 144 |
135 static uint8_t color_map_init_done; | 145 static uint8_t color_map_init_done; |
136 | 146 |
137 vdp_context *init_vdp_context(uint8_t region_pal) | 147 vdp_context *init_vdp_context(uint8_t region_pal, uint8_t has_max_vsram) |
138 { | 148 { |
139 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE); | 149 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE); |
140 if (headless) { | 150 if (headless) { |
141 context->output = malloc(LINEBUF_SIZE * sizeof(uint32_t)); | 151 context->fb = malloc(512 * LINEBUF_SIZE * sizeof(uint32_t)); |
142 context->output_pitch = 0; | 152 context->output_pitch = LINEBUF_SIZE * sizeof(uint32_t); |
143 } else { | 153 } else { |
144 context->cur_buffer = FRAMEBUFFER_ODD; | 154 context->cur_buffer = FRAMEBUFFER_ODD; |
145 context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch); | 155 context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch); |
146 } | 156 } |
147 context->sprite_draws = MAX_DRAWS; | 157 context->sprite_draws = MAX_SPRITES_LINE; |
148 context->fifo_write = 0; | 158 context->fifo_write = 0; |
149 context->fifo_read = -1; | 159 context->fifo_read = -1; |
150 context->regs[REG_HINT] = context->hint_counter = 0xFF; | 160 context->regs[REG_HINT] = context->hint_counter = 0xFF; |
161 context->vsram_size = has_max_vsram ? MAX_VSRAM_SIZE : MIN_VSRAM_SIZE; | |
151 | 162 |
152 if (!color_map_init_done) { | 163 if (!color_map_init_done) { |
153 uint8_t b,g,r; | 164 uint8_t b,g,r; |
154 for (uint16_t color = 0; color < (1 << 12); color++) { | 165 for (uint16_t color = 0; color < (1 << 12); color++) { |
155 if (color & FBUF_SHADOW) { | 166 if (color & FBUF_SHADOW) { |
233 } | 244 } |
234 if (region_pal) { | 245 if (region_pal) { |
235 context->flags2 |= FLAG2_REGION_PAL; | 246 context->flags2 |= FLAG2_REGION_PAL; |
236 } | 247 } |
237 update_video_params(context); | 248 update_video_params(context); |
238 if (!headless) { | 249 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top); |
239 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top); | |
240 } | |
241 return context; | 250 return context; |
242 } | 251 } |
243 | 252 |
244 void vdp_free(vdp_context *context) | 253 void vdp_free(vdp_context *context) |
245 { | 254 { |
267 } | 276 } |
268 } | 277 } |
269 | 278 |
270 static void render_sprite_cells(vdp_context * context) | 279 static void render_sprite_cells(vdp_context * context) |
271 { | 280 { |
281 if (context->cur_slot > MAX_SPRITES_LINE) { | |
282 context->cur_slot--; | |
283 return; | |
284 } | |
285 if (context->cur_slot < 0) { | |
286 return; | |
287 } | |
272 sprite_draw * d = context->sprite_draw_list + context->cur_slot; | 288 sprite_draw * d = context->sprite_draw_list + context->cur_slot; |
273 context->serial_address = d->address; | 289 uint16_t address = d->address; |
274 if (context->cur_slot >= context->sprite_draws) { | 290 address += context->sprite_x_offset * d->height * 4; |
275 | 291 context->serial_address = address; |
276 uint16_t dir; | 292 uint16_t dir; |
277 int16_t x; | 293 int16_t x; |
278 if (d->h_flip) { | 294 if (d->h_flip) { |
279 x = d->x_pos + 7; | 295 x = d->x_pos + 7 + 8 * (d->width - context->sprite_x_offset - 1); |
280 dir = -1; | 296 dir = -1; |
281 } else { | 297 } else { |
282 x = d->x_pos; | 298 x = d->x_pos + context->sprite_x_offset * 8; |
283 dir = 1; | 299 dir = 1; |
284 } | 300 } |
285 //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x); | 301 if (d->x_pos) { |
286 context->cur_slot--; | 302 context->flags |= FLAG_CAN_MASK; |
287 for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) { | 303 if (!(context->flags & FLAG_MASKED)) { |
288 if (x >= 0 && x < 320) { | 304 x -= 128; |
289 if (!(context->linebuf[x] & 0xF)) { | 305 //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x); |
290 context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority; | 306 uint8_t collide = 0; |
291 } else if (context->vdpmem[address] >> 4) { | 307 if (x >= 8 && x < 312) { |
292 context->flags2 |= FLAG2_SPRITE_COLLIDE; | 308 //sprite is fully visible |
293 } | 309 for (; address != ((context->serial_address+4) & 0xFFFF); address++) { |
294 } | 310 uint8_t pixel = context->vdpmem[address] >> 4; |
295 x += dir; | 311 if (!(context->linebuf[x] & 0xF)) { |
296 if (x >= 0 && x < 320) { | 312 context->linebuf[x] = pixel | d->pal_priority; |
297 if (!(context->linebuf[x] & 0xF)) { | 313 } else { |
298 context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority; | 314 collide |= pixel; |
299 } else if (context->vdpmem[address] & 0xF) { | 315 } |
300 context->flags2 |= FLAG2_SPRITE_COLLIDE; | 316 x += dir; |
301 } | 317 pixel = context->vdpmem[address] & 0xF; |
302 } | 318 if (!(context->linebuf[x] & 0xF)) { |
303 x += dir; | 319 context->linebuf[x] = pixel | d->pal_priority; |
304 } | 320 } else { |
305 } else { | 321 collide |= pixel; |
322 } | |
323 x += dir; | |
324 } | |
325 } else if (x > -8 && x < 327) { | |
326 //sprite is partially visible | |
327 for (; address != ((context->serial_address+4) & 0xFFFF); address++) { | |
328 if (x >= 0 && x < 320) { | |
329 uint8_t pixel = context->vdpmem[address] >> 4; | |
330 if (!(context->linebuf[x] & 0xF)) { | |
331 context->linebuf[x] = pixel | d->pal_priority; | |
332 } else { | |
333 collide |= pixel; | |
334 } | |
335 } | |
336 x += dir; | |
337 if (x >= 0 && x < 320) { | |
338 uint8_t pixel = context->vdpmem[address] & 0xF; | |
339 if (!(context->linebuf[x] & 0xF)) { | |
340 context->linebuf[x] = pixel | d->pal_priority; | |
341 } else { | |
342 collide |= pixel; | |
343 } | |
344 } | |
345 x += dir; | |
346 } | |
347 } | |
348 if (collide) { | |
349 context->flags2 |= FLAG2_SPRITE_COLLIDE; | |
350 } | |
351 } | |
352 } else if (context->flags & FLAG_CAN_MASK) { | |
353 context->flags |= FLAG_MASKED; | |
354 context->flags &= ~FLAG_CAN_MASK; | |
355 } | |
356 | |
357 context->sprite_x_offset++; | |
358 if (context->sprite_x_offset == d->width) { | |
359 d->x_pos = 0; | |
360 context->sprite_x_offset = 0; | |
306 context->cur_slot--; | 361 context->cur_slot--; |
307 } | 362 } |
308 } | 363 } |
309 | 364 |
310 static void fetch_sprite_cells_mode4(vdp_context * context) | 365 static void fetch_sprite_cells_mode4(vdp_context * context) |
528 "Status: %X\n", | 583 "Status: %X\n", |
529 context->address, context->cd, cd_name(context->cd), | 584 context->address, context->cd, cd_name(context->cd), |
530 (context->flags & FLAG_PENDING) ? "word" : (context->flags2 & FLAG2_BYTE_PENDING) ? "byte" : "none", | 585 (context->flags & FLAG_PENDING) ? "word" : (context->flags2 & FLAG2_BYTE_PENDING) ? "byte" : "none", |
531 context->vcounter, context->hslot*2, (context->flags2 & FLAG2_VINT_PENDING) ? "true" : "false", | 586 context->vcounter, context->hslot*2, (context->flags2 & FLAG2_VINT_PENDING) ? "true" : "false", |
532 (context->flags2 & FLAG2_HINT_PENDING) ? "true" : "false", vdp_control_port_read(context)); | 587 (context->flags2 & FLAG2_HINT_PENDING) ? "true" : "false", vdp_control_port_read(context)); |
588 printf("\nDebug Register: %X | Output disabled: %s, Force Layer: %d\n", context->test_port, | |
589 (context->test_port & TEST_BIT_DISABLE) ? "true" : "false", context->test_port >> 7 & 3 | |
590 ); | |
533 //restore flags as calling vdp_control_port_read can change them | 591 //restore flags as calling vdp_control_port_read can change them |
534 context->flags = old_flags; | 592 context->flags = old_flags; |
535 context->flags2 = old_flags2; | 593 context->flags2 = old_flags2; |
536 } | 594 } |
537 | 595 |
693 if (context->double_res) { | 751 if (context->double_res) { |
694 address = ((tileinfo & 0x3FF) << 6) + row * 4; | 752 address = ((tileinfo & 0x3FF) << 6) + row * 4; |
695 } else { | 753 } else { |
696 address = ((tileinfo & 0x7FF) << 5) + row * 4; | 754 address = ((tileinfo & 0x7FF) << 5) + row * 4; |
697 } | 755 } |
698 int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF; | 756 context->sprite_draws--; |
699 if (x) { | 757 context->sprite_draw_list[context->sprite_draws].x_pos = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF; |
700 context->flags |= FLAG_CAN_MASK; | 758 context->sprite_draw_list[context->sprite_draws].address = address; |
701 } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { | 759 context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority; |
702 context->flags |= FLAG_MASKED; | 760 context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0; |
703 } | 761 context->sprite_draw_list[context->sprite_draws].width = width; |
704 | 762 context->sprite_draw_list[context->sprite_draws].height = height; |
705 context->flags &= ~FLAG_DOT_OFLOW; | |
706 int16_t i; | |
707 if (context->flags & FLAG_MASKED) { | |
708 for (i=0; i < width && context->sprite_draws; i++) { | |
709 --context->sprite_draws; | |
710 context->sprite_draw_list[context->sprite_draws].x_pos = -128; | |
711 context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4; | |
712 } | |
713 } else { | |
714 x -= 128; | |
715 int16_t base_x = x; | |
716 int16_t dir; | |
717 if (tileinfo & MAP_BIT_H_FLIP) { | |
718 x += (width-1) * 8; | |
719 dir = -8; | |
720 } else { | |
721 dir = 8; | |
722 } | |
723 //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); | |
724 for (i=0; i < width && context->sprite_draws; i++, x += dir) { | |
725 --context->sprite_draws; | |
726 context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4; | |
727 context->sprite_draw_list[context->sprite_draws].x_pos = x; | |
728 context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority; | |
729 context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0; | |
730 } | |
731 } | |
732 //Used to be i < width | |
733 //TODO: Confirm this is the right condition on hardware | |
734 if (!context->sprite_draws) { | |
735 context->flags |= FLAG_DOT_OFLOW; | |
736 } | |
737 } else { | |
738 context->flags |= FLAG_DOT_OFLOW; | |
739 } | 763 } |
740 } | 764 } |
741 context->cur_slot++; | 765 context->cur_slot++; |
742 } | 766 } |
743 | 767 |
788 addr = address & 0x1F; | 812 addr = address & 0x1F; |
789 value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00); | 813 value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00); |
790 } | 814 } |
791 write_cram_internal(context, addr, value); | 815 write_cram_internal(context, addr, value); |
792 | 816 |
793 if (context->hslot >= BG_START_SLOT && ( | 817 if (context->output && context->hslot >= BG_START_SLOT && ( |
794 context->vcounter < context->inactive_start + context->border_bot | 818 context->vcounter < context->inactive_start + context->border_bot |
795 || context->vcounter > 0x200 - context->border_top | 819 || context->vcounter > 0x200 - context->border_top |
796 )) { | 820 )) { |
797 uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2; | 821 uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2; |
798 if (context->hslot < bg_end_slot) { | 822 if (context->hslot < bg_end_slot) { |
913 write_cram(context, start->address, start->partial ? context->fifo[context->fifo_write].value : start->value); | 937 write_cram(context, start->address, start->partial ? context->fifo[context->fifo_write].value : start->value); |
914 } | 938 } |
915 break; | 939 break; |
916 } | 940 } |
917 case VSRAM_WRITE: | 941 case VSRAM_WRITE: |
918 if (((start->address/2) & 63) < VSRAM_SIZE) { | 942 if (((start->address/2) & 63) < context->vsram_size) { |
919 //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); | 943 //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); |
920 if (start->partial == 3) { | 944 if (start->partial == 3) { |
921 if (start->address & 1) { | 945 if (start->address & 1) { |
922 context->vsram[(start->address/2) & 63] &= 0xFF; | 946 context->vsram[(start->address/2) & 63] &= 0xFF; |
923 context->vsram[(start->address/2) & 63] |= start->value << 8; | 947 context->vsram[(start->address/2) & 63] |= start->value << 8; |
950 } else { | 974 } else { |
951 context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1]; | 975 context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1]; |
952 | 976 |
953 context->flags |= FLAG_READ_FETCHED; | 977 context->flags |= FLAG_READ_FETCHED; |
954 } | 978 } |
955 } else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING))) { | 979 } else if (!(context->cd & 1) && !(context->flags & FLAG_READ_FETCHED)) { |
956 switch(context->cd & 0xF) | 980 switch(context->cd & 0xF) |
957 { | 981 { |
958 case VRAM_READ: | 982 case VRAM_READ: |
959 if (context->flags2 & FLAG2_READ_PENDING) { | 983 if (context->flags2 & FLAG2_READ_PENDING) { |
960 context->prefetch |= context->vdpmem[context->address | 1]; | 984 context->prefetch |= context->vdpmem[context->address | 1]; |
987 //Should this happen after the prefetch or after the read? | 1011 //Should this happen after the prefetch or after the read? |
988 increment_address(context); | 1012 increment_address(context); |
989 break; | 1013 break; |
990 case VSRAM_READ: { | 1014 case VSRAM_READ: { |
991 uint16_t address = (context->address /2) & 63; | 1015 uint16_t address = (context->address /2) & 63; |
992 if (address >= VSRAM_SIZE) { | 1016 if (address >= context->vsram_size) { |
993 address = 0; | 1017 address = 0; |
994 } | 1018 } |
995 context->prefetch = context->vsram[address] & VSRAM_BITS; | 1019 context->prefetch = context->vsram[address] & VSRAM_BITS; |
996 context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; | 1020 context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; |
997 context->flags |= FLAG_READ_FETCHED; | 1021 context->flags |= FLAG_READ_FETCHED; |
1123 } | 1147 } |
1124 vscroll &= context->vscroll_latch[vsram_off] + line; | 1148 vscroll &= context->vscroll_latch[vsram_off] + line; |
1125 context->v_offset = vscroll & v_offset_mask; | 1149 context->v_offset = vscroll & v_offset_mask; |
1126 //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); | 1150 //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); |
1127 vscroll >>= vscroll_shift; | 1151 vscroll >>= vscroll_shift; |
1128 uint16_t hscroll_mask; | 1152 //TODO: Verify the behavior for a setting of 2 |
1129 uint16_t v_mul; | 1153 static const uint16_t hscroll_masks[] = {0x1F, 0x3F, 0x1F, 0x7F}; |
1130 switch(context->regs[REG_SCROLL] & 0x3) | 1154 static const uint16_t v_shifts[] = {6, 7, 0, 8}; |
1131 { | 1155 uint16_t hscroll_mask = hscroll_masks[context->regs[REG_SCROLL] & 0x3]; |
1132 case 0: | 1156 uint16_t v_shift = v_shifts[context->regs[REG_SCROLL] & 0x3]; |
1133 hscroll_mask = 0x1F; | |
1134 v_mul = 64; | |
1135 break; | |
1136 case 0x1: | |
1137 hscroll_mask = 0x3F; | |
1138 v_mul = 128; | |
1139 break; | |
1140 case 0x2: | |
1141 //TODO: Verify this behavior | |
1142 hscroll_mask = 0x1F; | |
1143 v_mul = 0; | |
1144 break; | |
1145 case 0x3: | |
1146 hscroll_mask = 0x7F; | |
1147 v_mul = 256; | |
1148 break; | |
1149 } | |
1150 uint16_t hscroll, offset; | 1157 uint16_t hscroll, offset; |
1151 for (int i = 0; i < 2; i++) { | 1158 for (int i = 0; i < 2; i++) { |
1152 hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask; | 1159 hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask; |
1153 offset = address + ((vscroll * v_mul + hscroll*2) & 0x1FFF); | 1160 offset = address + (((vscroll << v_shift) + hscroll*2) & 0x1FFF); |
1154 //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); | 1161 //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); |
1155 uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; | 1162 uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; |
1156 if (i) { | 1163 if (i) { |
1157 context->col_2 = col_val; | 1164 context->col_2 = col_val; |
1158 } else { | 1165 } else { |
1206 } else { | 1213 } else { |
1207 address += 4 * context->v_offset; | 1214 address += 4 * context->v_offset; |
1208 } | 1215 } |
1209 uint8_t pal_priority = (col >> 9) & 0x70; | 1216 uint8_t pal_priority = (col >> 9) & 0x70; |
1210 uint32_t bits = *((uint32_t *)(&context->vdpmem[address])); | 1217 uint32_t bits = *((uint32_t *)(&context->vdpmem[address])); |
1218 tmp_buf += offset; | |
1211 if (col & MAP_BIT_H_FLIP) { | 1219 if (col & MAP_BIT_H_FLIP) { |
1212 uint32_t shift = 28; | 1220 uint32_t shift = 28; |
1213 for (int i = 0; i < 4; i++) | 1221 for (int i = 0; i < 4; i++) |
1214 { | 1222 { |
1215 uint8_t right = pal_priority | ((bits >> shift) & 0xF); | 1223 uint8_t right = pal_priority | ((bits >> shift) & 0xF); |
1216 shift -= 4; | 1224 shift -= 4; |
1217 tmp_buf[offset++] = pal_priority | ((bits >> shift) & 0xF); | 1225 *(tmp_buf++) = pal_priority | ((bits >> shift) & 0xF); |
1218 shift -= 4; | 1226 shift -= 4; |
1219 offset &= SCROLL_BUFFER_MASK; | 1227 *(tmp_buf++) = right; |
1220 tmp_buf[offset++] = right; | |
1221 offset &= SCROLL_BUFFER_MASK; | |
1222 } | 1228 } |
1223 } else { | 1229 } else { |
1224 for (int i = 0; i < 4; i++) | 1230 for (int i = 0; i < 4; i++) |
1225 { | 1231 { |
1226 uint8_t right = pal_priority | (bits & 0xF); | 1232 uint8_t right = pal_priority | (bits & 0xF); |
1227 bits >>= 4; | 1233 bits >>= 4; |
1228 tmp_buf[offset++] = pal_priority | (bits & 0xF); | 1234 *(tmp_buf++) = pal_priority | (bits & 0xF); |
1229 offset &= SCROLL_BUFFER_MASK; | |
1230 bits >>= 4; | 1235 bits >>= 4; |
1231 tmp_buf[offset++] = right; | 1236 *(tmp_buf++) = right; |
1232 offset &= SCROLL_BUFFER_MASK; | |
1233 } | 1237 } |
1234 } | 1238 } |
1235 } | 1239 } |
1236 | 1240 |
1237 static void render_map_1(vdp_context * context) | 1241 static void render_map_1(vdp_context * context) |
1326 return (sh_pixel){.index = pixel, .intensity = intensity}; | 1330 return (sh_pixel){.index = pixel, .intensity = intensity}; |
1327 } | 1331 } |
1328 | 1332 |
1329 static void render_normal(vdp_context *context, int32_t col, uint8_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off) | 1333 static void render_normal(vdp_context *context, int32_t col, uint8_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off) |
1330 { | 1334 { |
1331 int start = 0; | 1335 uint8_t *sprite_buf = context->linebuf + col * 8; |
1332 if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) { | 1336 if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) { |
1333 memset(dst, 0, 8); | 1337 memset(dst, 0, 8); |
1334 memset(debug_dst, DBG_SRC_BG, 8); | 1338 memset(debug_dst, DBG_SRC_BG, 8); |
1335 dst += 8; | 1339 dst += 8; |
1336 debug_dst += 8; | 1340 debug_dst += 8; |
1337 start = 8; | 1341 sprite_buf += 8; |
1338 } | 1342 plane_a_off += 8; |
1339 uint8_t *sprite_buf = context->linebuf + col * 8 + start; | 1343 plane_b_off += 8; |
1340 for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) | 1344 for (int i = 0; i < 8; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) |
1341 { | 1345 { |
1342 uint8_t sprite, plane_a, plane_b; | 1346 uint8_t sprite, plane_a, plane_b; |
1343 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK]; | 1347 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK]; |
1344 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK]; | 1348 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK]; |
1345 sprite = *sprite_buf; | 1349 *(dst++) = composite_normal(context, debug_dst, *sprite_buf, plane_a, plane_b, context->regs[REG_BG_COLOR]) & 0x3F; |
1346 *(dst++) = composite_normal(context, debug_dst, sprite, plane_a, plane_b, context->regs[REG_BG_COLOR]) & 0x3F; | 1350 debug_dst++; |
1347 debug_dst++; | 1351 } |
1352 } else { | |
1353 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) | |
1354 { | |
1355 uint8_t sprite, plane_a, plane_b; | |
1356 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK]; | |
1357 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK]; | |
1358 *(dst++) = composite_normal(context, debug_dst, *sprite_buf, plane_a, plane_b, context->regs[REG_BG_COLOR]) & 0x3F; | |
1359 debug_dst++; | |
1360 } | |
1348 } | 1361 } |
1349 } | 1362 } |
1350 | 1363 |
1351 static void render_highlight(vdp_context *context, int32_t col, uint8_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off) | 1364 static void render_highlight(vdp_context *context, int32_t col, uint8_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off) |
1352 { | 1365 { |
1607 uint8_t a_src, src; | 1620 uint8_t a_src, src; |
1608 if (context->flags & FLAG_WINDOW) { | 1621 if (context->flags & FLAG_WINDOW) { |
1609 plane_a_off = context->buf_a_off; | 1622 plane_a_off = context->buf_a_off; |
1610 a_src = DBG_SRC_W; | 1623 a_src = DBG_SRC_W; |
1611 } else { | 1624 } else { |
1612 plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); | 1625 plane_a_off = context->buf_a_off - context->hscroll_a_fine; |
1613 a_src = DBG_SRC_A; | 1626 a_src = DBG_SRC_A; |
1614 } | 1627 } |
1615 plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); | 1628 plane_b_off = context->buf_b_off - context->hscroll_b_fine; |
1616 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); | 1629 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); |
1617 | 1630 |
1618 if (context->regs[REG_MODE_4] & BIT_HILIGHT) { | 1631 if (context->regs[REG_MODE_4] & BIT_HILIGHT) { |
1619 if (output_disabled || test_layer) { | 1632 if (output_disabled || test_layer) { |
1620 render_testreg_highlight(context, col, dst, debug_dst, plane_a_off, plane_b_off, output_disabled, test_layer); | 1633 render_testreg_highlight(context, col, dst, debug_dst, plane_a_off, plane_b_off, output_disabled, test_layer); |
1640 switch(test_layer) | 1653 switch(test_layer) |
1641 { | 1654 { |
1642 case 1: | 1655 case 1: |
1643 memset(dst, 0, BORDER_LEFT); | 1656 memset(dst, 0, BORDER_LEFT); |
1644 memset(debug_dst, DBG_SRC_BG, BORDER_LEFT); | 1657 memset(debug_dst, DBG_SRC_BG, BORDER_LEFT); |
1658 dst += BORDER_LEFT; | |
1645 break; | 1659 break; |
1646 case 2: { | 1660 case 2: { |
1647 //plane A | 1661 //plane A |
1648 //TODO: Deal with Window layer | 1662 //TODO: Deal with Window layer |
1649 int i; | 1663 int i; |
1650 i = 0; | 1664 i = 0; |
1651 uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF) + (16 - BORDER_LEFT); | 1665 uint8_t buf_off = context->buf_a_off - context->hscroll_a_fine + (16 - BORDER_LEFT); |
1652 //uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); | 1666 //uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); |
1653 for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++) | 1667 for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++) |
1654 { | 1668 { |
1655 *dst = context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK]; | 1669 *dst = context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK]; |
1656 *debug_dst = DBG_SRC_A; | 1670 *debug_dst = DBG_SRC_A; |
1659 } | 1673 } |
1660 case 3: { | 1674 case 3: { |
1661 //plane B | 1675 //plane B |
1662 int i; | 1676 int i; |
1663 i = 0; | 1677 i = 0; |
1664 uint8_t buf_off = context->buf_b_off - (context->hscroll_b & 0xF) + (16 - BORDER_LEFT); | 1678 uint8_t buf_off = context->buf_b_off - context->hscroll_b_fine + (16 - BORDER_LEFT); |
1665 //uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK); | 1679 //uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK); |
1666 for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++) | 1680 for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++) |
1667 { | 1681 { |
1668 *dst = context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK]; | 1682 *dst = context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK]; |
1669 *debug_dst = DBG_SRC_B; | 1683 *debug_dst = DBG_SRC_B; |
1672 } | 1686 } |
1673 } | 1687 } |
1674 } else { | 1688 } else { |
1675 memset(dst, pixel, BORDER_LEFT); | 1689 memset(dst, pixel, BORDER_LEFT); |
1676 memset(debug_dst, DBG_SRC_BG, BORDER_LEFT); | 1690 memset(debug_dst, DBG_SRC_BG, BORDER_LEFT); |
1677 } | 1691 dst += BORDER_LEFT; |
1678 dst += BORDER_LEFT; | 1692 } |
1679 } | 1693 } |
1680 context->done_composite = dst; | 1694 context->done_composite = dst; |
1681 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; | 1695 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; |
1682 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; | 1696 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; |
1683 } | 1697 } |
1723 context->buf_a_off = (context->buf_a_off + 8) & 15; | 1737 context->buf_a_off = (context->buf_a_off + 8) & 15; |
1724 | 1738 |
1725 uint8_t *dst = context->compositebuf + col * 8 + BORDER_LEFT; | 1739 uint8_t *dst = context->compositebuf + col * 8 + BORDER_LEFT; |
1726 uint8_t *debug_dst = context->layer_debug_buf + col * 8 + BORDER_LEFT; | 1740 uint8_t *debug_dst = context->layer_debug_buf + col * 8 + BORDER_LEFT; |
1727 if (context->state == PREPARING) { | 1741 if (context->state == PREPARING) { |
1728 memset(dst, 0, 8); | 1742 memset(dst, 0x10 + (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET, 8); |
1729 memset(debug_dst, DBG_SRC_BG, 8); | 1743 memset(debug_dst, DBG_SRC_BG, 8); |
1730 context->done_composite = dst + 8; | 1744 context->done_composite = dst + 8; |
1731 return; | 1745 return; |
1732 } | 1746 } |
1733 | 1747 |
1750 *(debug_dst++) = DBG_SRC_S; | 1764 *(debug_dst++) = DBG_SRC_S; |
1751 } | 1765 } |
1752 } | 1766 } |
1753 context->done_composite = dst; | 1767 context->done_composite = dst; |
1754 } else { | 1768 } else { |
1755 memset(dst, 0, 8); | 1769 memset(dst, 0x10 + (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET, 8); |
1756 memset(dst, DBG_SRC_BG, 8); | 1770 memset(debug_dst, DBG_SRC_BG, 8); |
1757 context->done_composite = dst + 8; | 1771 context->done_composite = dst + 8; |
1758 } | 1772 } |
1759 } | 1773 } |
1760 | 1774 |
1761 static uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19}; | 1775 static uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19}; |
1807 } else { | 1821 } else { |
1808 line += context->border_top; | 1822 line += context->border_top; |
1809 } | 1823 } |
1810 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) { | 1824 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) { |
1811 uint32_t *fb = context->debug_fbs[VDP_DEBUG_CRAM] + context->debug_fb_pitch[VDP_DEBUG_CRAM] * line / sizeof(uint32_t); | 1825 uint32_t *fb = context->debug_fbs[VDP_DEBUG_CRAM] + context->debug_fb_pitch[VDP_DEBUG_CRAM] * line / sizeof(uint32_t); |
1812 for (int i = 0; i < 64; i++) | 1826 if (context->regs[REG_MODE_2] & BIT_MODE_5) { |
1813 { | 1827 for (int i = 0; i < 64; i++) |
1814 for (int x = 0; x < 8; x++) | |
1815 { | 1828 { |
1816 *(fb++) = context->colors[i]; | 1829 for (int x = 0; x < 8; x++) |
1830 { | |
1831 *(fb++) = context->colors[i]; | |
1832 } | |
1833 } | |
1834 } else { | |
1835 for (int i = MODE4_OFFSET; i < MODE4_OFFSET+32; i++) | |
1836 { | |
1837 for (int x = 0; x < 16; x++) | |
1838 { | |
1839 *(fb++) = context->colors[i]; | |
1840 } | |
1817 } | 1841 } |
1818 } | 1842 } |
1819 } | 1843 } |
1820 if ( | 1844 if ( |
1821 context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE) | 1845 context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE) |
1986 | 2010 |
1987 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) { | 2011 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) { |
1988 uint32_t starting_line = 512 - 32*4; | 2012 uint32_t starting_line = 512 - 32*4; |
1989 uint32_t *line = context->debug_fbs[VDP_DEBUG_CRAM] | 2013 uint32_t *line = context->debug_fbs[VDP_DEBUG_CRAM] |
1990 + context->debug_fb_pitch[VDP_DEBUG_CRAM] * starting_line / sizeof(uint32_t); | 2014 + context->debug_fb_pitch[VDP_DEBUG_CRAM] * starting_line / sizeof(uint32_t); |
1991 for (int pal = 0; pal < 4; pal ++) | 2015 if (context->regs[REG_MODE_2] & BIT_MODE_5) { |
1992 { | 2016 for (int pal = 0; pal < 4; pal ++) |
1993 uint32_t *cur; | |
1994 for (int y = 0; y < 31; y++) | |
1995 { | 2017 { |
2018 uint32_t *cur; | |
2019 for (int y = 0; y < 31; y++) | |
2020 { | |
2021 cur = line; | |
2022 for (int offset = 0; offset < 16; offset++) | |
2023 { | |
2024 for (int x = 0; x < 31; x++) | |
2025 { | |
2026 *(cur++) = context->colors[pal * 16 + offset]; | |
2027 } | |
2028 *(cur++) = 0xFF000000; | |
2029 } | |
2030 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); | |
2031 } | |
1996 cur = line; | 2032 cur = line; |
1997 for (int offset = 0; offset < 16; offset++) | 2033 for (int x = 0; x < 512; x++) |
1998 { | 2034 { |
1999 for (int x = 0; x < 31; x++) | 2035 *(cur++) = 0xFF000000; |
2036 } | |
2037 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); | |
2038 } | |
2039 } else { | |
2040 for (int pal = 0; pal < 2; pal ++) | |
2041 { | |
2042 uint32_t *cur; | |
2043 for (int y = 0; y < 31; y++) | |
2044 { | |
2045 cur = line; | |
2046 for (int offset = MODE4_OFFSET; offset < MODE4_OFFSET + 16; offset++) | |
2000 { | 2047 { |
2001 *(cur++) = context->colors[pal * 16 + offset]; | 2048 for (int x = 0; x < 31; x++) |
2049 { | |
2050 *(cur++) = context->colors[pal * 16 + offset]; | |
2051 } | |
2052 *(cur++) = 0xFF000000; | |
2002 } | 2053 } |
2054 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); | |
2055 } | |
2056 cur = line; | |
2057 for (int x = 0; x < 512; x++) | |
2058 { | |
2003 *(cur++) = 0xFF000000; | 2059 *(cur++) = 0xFF000000; |
2004 } | 2060 } |
2005 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); | 2061 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); |
2006 } | 2062 } |
2007 cur = line; | |
2008 for (int x = 0; x < 512; x++) | |
2009 { | |
2010 *(cur++) = 0xFF000000; | |
2011 } | |
2012 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t); | |
2013 } | 2063 } |
2014 render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_CRAM], 512); | 2064 render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_CRAM], 512); |
2015 context->debug_fbs[VDP_DEBUG_CRAM] = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_CRAM], &context->debug_fb_pitch[VDP_DEBUG_CRAM]); | 2065 context->debug_fbs[VDP_DEBUG_CRAM] = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_CRAM], &context->debug_fb_pitch[VDP_DEBUG_CRAM]); |
2016 } | 2066 } |
2017 if (context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)) { | 2067 if (context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)) { |
2020 } | 2070 } |
2021 } | 2071 } |
2022 | 2072 |
2023 void vdp_force_update_framebuffer(vdp_context *context) | 2073 void vdp_force_update_framebuffer(vdp_context *context) |
2024 { | 2074 { |
2025 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) | 2075 if (!context->fb) { |
2026 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL | 2076 return; |
2027 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28; | 2077 } |
2078 uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top; | |
2028 | 2079 |
2029 uint16_t to_fill = lines_max - context->output_lines; | 2080 uint16_t to_fill = lines_max - context->output_lines; |
2030 memset( | 2081 memset( |
2031 ((char *)context->fb) + context->output_pitch * context->output_lines, | 2082 ((char *)context->fb) + context->output_pitch * context->output_lines, |
2032 0, | 2083 0, |
2037 vdp_update_per_frame_debug(context); | 2088 vdp_update_per_frame_debug(context); |
2038 } | 2089 } |
2039 | 2090 |
2040 static void advance_output_line(vdp_context *context) | 2091 static void advance_output_line(vdp_context *context) |
2041 { | 2092 { |
2042 if (headless) { | 2093 //This function is kind of gross because of the need to deal with vertical border busting via mode changes |
2043 if (context->vcounter == context->inactive_start) { | 2094 uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top; |
2044 context->frame++; | 2095 uint32_t output_line = context->vcounter; |
2045 } | 2096 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) { |
2046 context->vcounter &= 0x1FF; | 2097 //vcounter increment occurs much later in Mode 4 |
2047 } else { | 2098 output_line++; |
2048 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) | 2099 } |
2049 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL | 2100 |
2050 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28; | 2101 if (context->output_lines >= lines_max || (!context->pushed_frame && output_line == context->inactive_start + context->border_top)) { |
2051 | 2102 //we've either filled up a full frame or we're at the bottom of screen in the current defined mode + border crop |
2052 if (context->output_lines == lines_max) { | 2103 if (!headless) { |
2053 render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER)); | 2104 render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER)); |
2054 context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? FRAMEBUFFER_EVEN : FRAMEBUFFER_ODD; | 2105 uint8_t is_even = context->flags2 & FLAG2_EVEN_FIELD; |
2055 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch); | 2106 if (context->vcounter <= context->inactive_start && (context->regs[REG_MODE_4] & BIT_INTERLACE)) { |
2056 vdp_update_per_frame_debug(context); | 2107 is_even = !is_even; |
2057 context->h40_lines = 0; | 2108 } |
2058 context->frame++; | 2109 context->cur_buffer = is_even ? FRAMEBUFFER_EVEN : FRAMEBUFFER_ODD; |
2110 context->pushed_frame = 1; | |
2111 context->fb = NULL; | |
2112 } | |
2113 vdp_update_per_frame_debug(context); | |
2114 context->h40_lines = 0; | |
2115 context->frame++; | |
2116 context->output_lines = 0; | |
2117 } | |
2118 | |
2119 if (output_line < context->inactive_start + context->border_bot) { | |
2120 if (context->output_lines) { | |
2121 output_line = context->output_lines++;//context->border_top + context->vcounter; | |
2122 } else if (!output_line && !context->border_top) { | |
2123 //top border is completely cropped so we won't hit the case below | |
2124 output_line = 0; | |
2125 context->output_lines = 1; | |
2126 context->pushed_frame = 0; | |
2127 } else { | |
2128 context->output_lines = output_line + 1; | |
2129 } | |
2130 } else if (output_line >= 0x200 - context->border_top) { | |
2131 if (output_line == 0x200 - context->border_top) { | |
2132 //We're at the top of the display, force context->output_lines to be zero to avoid | |
2133 //potential screen rolling if the mode is changed at an inopportune time | |
2059 context->output_lines = 0; | 2134 context->output_lines = 0; |
2060 } | 2135 context->pushed_frame = 0; |
2061 uint32_t output_line = context->vcounter; | 2136 } |
2062 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) { | 2137 output_line = context->output_lines++;//context->vcounter - (0x200 - context->border_top); |
2063 //vcounter increment occurs much later in Mode 4 | 2138 } else { |
2064 output_line++; | 2139 context->output = NULL; |
2065 } | 2140 return; |
2066 if (output_line < context->inactive_start + context->border_bot && context->output_lines > 0) { | 2141 } |
2067 output_line = context->output_lines++;//context->border_top + context->vcounter; | 2142 if (!context->fb) { |
2068 } else if (output_line >= 0x200 - context->border_top) { | 2143 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch); |
2069 if (output_line == 0x200 - context->border_top) { | 2144 } |
2070 //We're at the top of the display, force context->output_lines to be zero to avoid | 2145 output_line += context->top_offset; |
2071 //potential screen rolling if the mode is changed at an inopportune time | 2146 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line); |
2072 context->output_lines = 0; | |
2073 } | |
2074 output_line = context->output_lines++;//context->vcounter - (0x200 - context->border_top); | |
2075 } else { | |
2076 context->output = NULL; | |
2077 } | |
2078 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line); | |
2079 #ifdef DEBUG_FB_FILL | 2147 #ifdef DEBUG_FB_FILL |
2080 for (int i = 0; i < LINEBUF_SIZE; i++) | 2148 for (int i = 0; i < LINEBUF_SIZE; i++) |
2081 { | 2149 { |
2082 context->output[i] = 0xFFFF00FF; | 2150 context->output[i] = 0xFFFF00FF; |
2083 } | 2151 } |
2084 #endif | 2152 #endif |
2085 if (context->output && (context->regs[REG_MODE_4] & BIT_H40)) { | 2153 if (context->output && (context->regs[REG_MODE_4] & BIT_H40)) { |
2086 context->h40_lines++; | 2154 context->h40_lines++; |
2087 } | |
2088 } | 2155 } |
2089 } | 2156 } |
2090 | 2157 |
2091 void vdp_release_framebuffer(vdp_context *context) | 2158 void vdp_release_framebuffer(vdp_context *context) |
2092 { | 2159 { |
2093 render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER)); | 2160 if (context->fb) { |
2094 context->output = context->fb = NULL; | 2161 render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER)); |
2162 context->output = context->fb = NULL; | |
2163 } | |
2095 } | 2164 } |
2096 | 2165 |
2097 void vdp_reacquire_framebuffer(vdp_context *context) | 2166 void vdp_reacquire_framebuffer(vdp_context *context) |
2098 { | 2167 { |
2099 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch); | 2168 uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top; |
2100 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) | |
2101 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL | |
2102 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28; | |
2103 if (context->output_lines <= lines_max && context->output_lines > 0) { | 2169 if (context->output_lines <= lines_max && context->output_lines > 0) { |
2104 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1)); | 2170 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch); |
2171 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1 + context->top_offset)); | |
2105 } else { | 2172 } else { |
2106 context->output = NULL; | 2173 context->output = NULL; |
2107 } | 2174 } |
2108 } | 2175 } |
2109 | 2176 |
2136 case 2: { | 2203 case 2: { |
2137 //plane A | 2204 //plane A |
2138 //TODO: Deal with Window layer | 2205 //TODO: Deal with Window layer |
2139 int i; | 2206 int i; |
2140 i = 0; | 2207 i = 0; |
2141 uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF); | 2208 uint8_t buf_off = context->buf_a_off - context->hscroll_a_fine; |
2142 //uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); | 2209 //uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK); |
2143 for (; i < BORDER_RIGHT; buf_off++, i++, dst++) | 2210 for (; i < BORDER_RIGHT; buf_off++, i++, dst++) |
2144 { | 2211 { |
2145 *dst = context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK] & 0x3F; | 2212 *dst = context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK] & 0x3F; |
2146 } | 2213 } |
2213 if (slot != (BG_START_SLOT + (256+HORIZ_BORDER)/2)) {\ | 2280 if (slot != (BG_START_SLOT + (256+HORIZ_BORDER)/2)) {\ |
2214 if ((*src & 0x3F) | test_layer) {\ | 2281 if ((*src & 0x3F) | test_layer) {\ |
2215 *(dst++) = context->colors[*(src++)];\ | 2282 *(dst++) = context->colors[*(src++)];\ |
2216 } else {\ | 2283 } else {\ |
2217 *(dst++) = context->colors[(*(src++) & 0xC0) | bgindex];\ | 2284 *(dst++) = context->colors[(*(src++) & 0xC0) | bgindex];\ |
2285 }\ | |
2286 }\ | |
2287 } | |
2288 | |
2289 //BG_START_SLOT => dst = 0, src = border | |
2290 //BG_START_SLOT + 13/2=6, dst = 6, src = border + comp + 13 | |
2291 #define OUTPUT_PIXEL_MODE4(slot) if ((slot) >= BG_START_SLOT) {\ | |
2292 uint8_t *src = context->compositebuf + ((slot) - BG_START_SLOT) *2;\ | |
2293 uint32_t *dst = context->output + ((slot) - BG_START_SLOT) *2;\ | |
2294 if ((slot) - BG_START_SLOT < BORDER_LEFT/2) {\ | |
2295 *(dst++) = context->colors[bgindex];\ | |
2296 *(dst++) = context->colors[bgindex];\ | |
2297 } else if ((slot) - BG_START_SLOT < (BORDER_LEFT+256)/2){\ | |
2298 if ((slot) - BG_START_SLOT == BORDER_LEFT/2) {\ | |
2299 *(dst++) = context->colors[bgindex];\ | |
2300 src++;\ | |
2301 } else {\ | |
2302 *(dst++) = context->colors[*(src++)];\ | |
2303 }\ | |
2304 *(dst++) = context->colors[*(src++)];\ | |
2305 } else if ((slot) - BG_START_SLOT <= (HORIZ_BORDER+256)/2) {\ | |
2306 *(dst++) = context->colors[bgindex];\ | |
2307 if ((slot) - BG_START_SLOT < (HORIZ_BORDER+256)/2) {\ | |
2308 *(dst++) = context->colors[bgindex];\ | |
2218 }\ | 2309 }\ |
2219 }\ | 2310 }\ |
2220 } | 2311 } |
2221 | 2312 |
2222 #define COLUMN_RENDER_BLOCK(column, startcyc) \ | 2313 #define COLUMN_RENDER_BLOCK(column, startcyc) \ |
2289 render_map_output(context->vcounter, column, context);\ | 2380 render_map_output(context->vcounter, column, context);\ |
2290 CHECK_LIMIT | 2381 CHECK_LIMIT |
2291 | 2382 |
2292 #define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \ | 2383 #define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \ |
2293 case startcyc:\ | 2384 case startcyc:\ |
2294 OUTPUT_PIXEL(startcyc)\ | 2385 OUTPUT_PIXEL_MODE4(startcyc)\ |
2295 read_map_mode4(column, context->vcounter, context);\ | 2386 read_map_mode4(column, context->vcounter, context);\ |
2296 CHECK_LIMIT\ | 2387 CHECK_LIMIT\ |
2297 case ((startcyc+1)&0xFF):\ | 2388 case ((startcyc+1)&0xFF):\ |
2298 OUTPUT_PIXEL((startcyc+1)&0xFF)\ | 2389 OUTPUT_PIXEL_MODE4((startcyc+1)&0xFF)\ |
2299 if (column & 3) {\ | 2390 if (column & 3) {\ |
2300 scan_sprite_table_mode4(context);\ | 2391 scan_sprite_table_mode4(context);\ |
2301 } else {\ | 2392 } else {\ |
2302 external_slot(context);\ | 2393 external_slot(context);\ |
2303 }\ | 2394 }\ |
2304 CHECK_LIMIT\ | 2395 CHECK_LIMIT\ |
2305 case ((startcyc+2)&0xFF):\ | 2396 case ((startcyc+2)&0xFF):\ |
2306 OUTPUT_PIXEL((startcyc+2)&0xFF)\ | 2397 OUTPUT_PIXEL_MODE4((startcyc+2)&0xFF)\ |
2307 fetch_map_mode4(column, context->vcounter, context);\ | 2398 fetch_map_mode4(column, context->vcounter, context);\ |
2308 CHECK_LIMIT\ | 2399 CHECK_LIMIT\ |
2309 case ((startcyc+3)&0xFF):\ | 2400 case ((startcyc+3)&0xFF):\ |
2310 OUTPUT_PIXEL((startcyc+3)&0xFF)\ | 2401 OUTPUT_PIXEL_MODE4((startcyc+3)&0xFF)\ |
2311 render_map_mode4(context->vcounter, column, context);\ | 2402 render_map_mode4(context->vcounter, column, context);\ |
2312 CHECK_LIMIT | 2403 CHECK_LIMIT |
2313 | 2404 |
2314 #define CHECK_LIMIT_HSYNC(slot) \ | 2405 #define CHECK_LIMIT_HSYNC(slot) \ |
2315 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ | 2406 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ |
2422 | 2513 |
2423 #define CALC_SLOT(slot, increment) ((slot+increment) > 147 && (slot+increment) < 233 ? (slot+increment-148+233): (slot+increment)) | 2514 #define CALC_SLOT(slot, increment) ((slot+increment) > 147 && (slot+increment) < 233 ? (slot+increment-148+233): (slot+increment)) |
2424 | 2515 |
2425 #define SPRITE_RENDER_H32_MODE4(slot) \ | 2516 #define SPRITE_RENDER_H32_MODE4(slot) \ |
2426 case slot:\ | 2517 case slot:\ |
2427 OUTPUT_PIXEL_H32(slot)\ | 2518 OUTPUT_PIXEL_MODE4(slot)\ |
2428 read_sprite_x_mode4(context);\ | 2519 read_sprite_x_mode4(context);\ |
2429 MODE4_CHECK_SLOT_LINE(slot)\ | 2520 MODE4_CHECK_SLOT_LINE(slot)\ |
2430 case CALC_SLOT(slot, 1):\ | 2521 case CALC_SLOT(slot, 1):\ |
2431 OUTPUT_PIXEL(CALC_SLOT(slot, 1))\ | 2522 OUTPUT_PIXEL_MODE4(CALC_SLOT(slot, 1))\ |
2432 read_sprite_x_mode4(context);\ | 2523 read_sprite_x_mode4(context);\ |
2433 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot,1))\ | 2524 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot,1))\ |
2434 case CALC_SLOT(slot, 2):\ | 2525 case CALC_SLOT(slot, 2):\ |
2435 OUTPUT_PIXEL(CALC_SLOT(slot, 2))\ | 2526 OUTPUT_PIXEL_MODE4(CALC_SLOT(slot, 2))\ |
2436 fetch_sprite_cells_mode4(context);\ | 2527 fetch_sprite_cells_mode4(context);\ |
2437 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 2))\ | 2528 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 2))\ |
2438 case CALC_SLOT(slot, 3):\ | 2529 case CALC_SLOT(slot, 3):\ |
2439 OUTPUT_PIXEL(CALC_SLOT(slot, 3))\ | 2530 OUTPUT_PIXEL_MODE4(CALC_SLOT(slot, 3))\ |
2440 render_sprite_cells_mode4(context);\ | 2531 render_sprite_cells_mode4(context);\ |
2441 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 3))\ | 2532 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 3))\ |
2442 case CALC_SLOT(slot, 4):\ | 2533 case CALC_SLOT(slot, 4):\ |
2443 OUTPUT_PIXEL(CALC_SLOT(slot, 4))\ | 2534 OUTPUT_PIXEL_MODE4(CALC_SLOT(slot, 4))\ |
2444 fetch_sprite_cells_mode4(context);\ | 2535 fetch_sprite_cells_mode4(context);\ |
2445 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 4))\ | 2536 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 4))\ |
2446 case CALC_SLOT(slot, 5):\ | 2537 case CALC_SLOT(slot, 5):\ |
2447 OUTPUT_PIXEL(CALC_SLOT(slot, 5))\ | 2538 OUTPUT_PIXEL_MODE4(CALC_SLOT(slot, 5))\ |
2448 render_sprite_cells_mode4(context);\ | 2539 render_sprite_cells_mode4(context);\ |
2449 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 5)) | 2540 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 5)) |
2450 | 2541 |
2451 static uint32_t dummy_buffer[LINEBUF_SIZE]; | 2542 static uint32_t dummy_buffer[LINEBUF_SIZE]; |
2543 static void vdp_h40_line(vdp_context * context) | |
2544 { | |
2545 uint16_t address; | |
2546 uint32_t mask; | |
2547 uint32_t const slot_cycles = MCLKS_SLOT_H40; | |
2548 uint8_t bgindex = context->regs[REG_BG_COLOR] & 0x3F; | |
2549 uint8_t test_layer = context->test_port >> 7 & 3; | |
2550 | |
2551 //165 | |
2552 if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) { | |
2553 //TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode | |
2554 //See note in vdp_h32 for why this was originally moved out of read_map_scroll | |
2555 //Skitchin' has a similar problem, but uses H40 mode. It seems to be able to hit the extern slot at 232 | |
2556 //pretty consistently | |
2557 context->vscroll_latch[0] = context->vsram[0]; | |
2558 context->vscroll_latch[1] = context->vsram[1]; | |
2559 } | |
2560 render_sprite_cells(context); | |
2561 //166 | |
2562 render_sprite_cells(context); | |
2563 //167 | |
2564 context->sprite_index = 0x80; | |
2565 context->slot_counter = 0; | |
2566 render_border_garbage( | |
2567 context, | |
2568 context->sprite_draw_list[context->cur_slot].address, | |
2569 context->tmp_buf_b, context->buf_b_off, | |
2570 context->col_1 | |
2571 ); | |
2572 render_sprite_cells(context); | |
2573 scan_sprite_table(context->vcounter, context); | |
2574 //168 | |
2575 render_border_garbage( | |
2576 context, | |
2577 context->sprite_draw_list[context->cur_slot].address, | |
2578 context->tmp_buf_b, | |
2579 context->buf_b_off + 8, | |
2580 context->col_2 | |
2581 ); | |
2582 //Do palette lookup for end of previous line | |
2583 uint8_t *src = context->compositebuf + (LINE_CHANGE_H40 - BG_START_SLOT) *2; | |
2584 uint32_t *dst = context->output + (LINE_CHANGE_H40 - BG_START_SLOT) *2; | |
2585 if (test_layer) { | |
2586 for (int i = 0; i < LINEBUF_SIZE - (LINE_CHANGE_H40 - BG_START_SLOT) * 2; i++) | |
2587 { | |
2588 *(dst++) = context->colors[*(src++)]; | |
2589 } | |
2590 } else { | |
2591 for (int i = 0; i < LINEBUF_SIZE - (LINE_CHANGE_H40 - BG_START_SLOT) * 2; i++) | |
2592 { | |
2593 if (*src & 0x3F) { | |
2594 *(dst++) = context->colors[*(src++)]; | |
2595 } else { | |
2596 *(dst++) = context->colors[(*(src++) & 0xC0) | bgindex]; | |
2597 } | |
2598 } | |
2599 } | |
2600 advance_output_line(context); | |
2601 //168-242 (inclusive) | |
2602 for (int i = 0; i < 28; i++) | |
2603 { | |
2604 render_sprite_cells(context); | |
2605 scan_sprite_table(context->vcounter, context); | |
2606 } | |
2607 //243 | |
2608 render_border_garbage( | |
2609 context, | |
2610 context->sprite_draw_list[context->cur_slot].address, | |
2611 context->tmp_buf_a, | |
2612 context->buf_a_off, | |
2613 context->col_1 | |
2614 ); | |
2615 //244 | |
2616 address = (context->regs[REG_HSCROLL] & 0x3F) << 10; | |
2617 mask = 0; | |
2618 if (context->regs[REG_MODE_3] & 0x2) { | |
2619 mask |= 0xF8; | |
2620 } | |
2621 if (context->regs[REG_MODE_3] & 0x1) { | |
2622 mask |= 0x7; | |
2623 } | |
2624 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2); | |
2625 address += (context->vcounter & mask) * 4; | |
2626 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; | |
2627 context->hscroll_a_fine = context->hscroll_a & 0xF; | |
2628 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; | |
2629 context->hscroll_b_fine = context->hscroll_b & 0xF; | |
2630 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); | |
2631 //243-246 inclusive | |
2632 for (int i = 0; i < 3; i++) | |
2633 { | |
2634 render_sprite_cells(context); | |
2635 scan_sprite_table(context->vcounter, context); | |
2636 } | |
2637 //247 | |
2638 render_border_garbage( | |
2639 context, | |
2640 context->sprite_draw_list[context->cur_slot].address, | |
2641 context->tmp_buf_b, | |
2642 context->buf_b_off, | |
2643 context->col_1 | |
2644 ); | |
2645 render_sprite_cells(context); | |
2646 scan_sprite_table(context->vcounter, context); | |
2647 //248 | |
2648 render_border_garbage( | |
2649 context, | |
2650 context->sprite_draw_list[context->cur_slot].address, | |
2651 context->tmp_buf_b, | |
2652 context->buf_b_off + 8, | |
2653 context->col_2 | |
2654 ); | |
2655 render_sprite_cells(context); | |
2656 scan_sprite_table(context->vcounter, context); | |
2657 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; | |
2658 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; | |
2659 //250 | |
2660 render_sprite_cells(context); | |
2661 scan_sprite_table(context->vcounter, context); | |
2662 //251 | |
2663 scan_sprite_table(context->vcounter, context);//Just a guess | |
2664 //252 | |
2665 scan_sprite_table(context->vcounter, context);//Just a guess | |
2666 //254 | |
2667 render_sprite_cells(context); | |
2668 scan_sprite_table(context->vcounter, context); | |
2669 //255 | |
2670 if (context->cur_slot >= 0 && context->sprite_draw_list[context->cur_slot].x_pos) { | |
2671 context->flags |= FLAG_DOT_OFLOW; | |
2672 } | |
2673 scan_sprite_table(context->vcounter, context); | |
2674 //0 | |
2675 scan_sprite_table(context->vcounter, context);//Just a guess | |
2676 //seems like the sprite table scan fills a shift register | |
2677 //values are FIFO, but unused slots precede used slots | |
2678 //so we set cur_slot to slot_counter and let it wrap around to | |
2679 //the beginning of the list | |
2680 context->cur_slot = context->slot_counter; | |
2681 context->sprite_x_offset = 0; | |
2682 context->sprite_draws = MAX_SPRITES_LINE; | |
2683 //background planes and layer compositing | |
2684 for (int col = 0; col < 42; col+=2) | |
2685 { | |
2686 read_map_scroll_a(col, context->vcounter, context); | |
2687 render_map_1(context); | |
2688 render_map_2(context); | |
2689 read_map_scroll_b(col, context->vcounter, context); | |
2690 render_map_3(context); | |
2691 render_map_output(context->vcounter, col, context); | |
2692 } | |
2693 //sprite rendering phase 2 | |
2694 for (int i = 0; i < MAX_SPRITES_LINE; i++) | |
2695 { | |
2696 read_sprite_x(context->vcounter, context); | |
2697 } | |
2698 //163 | |
2699 context->cur_slot = MAX_SPRITES_LINE-1; | |
2700 memset(context->linebuf, 0, LINEBUF_SIZE); | |
2701 render_border_garbage( | |
2702 context, | |
2703 context->sprite_draw_list[context->cur_slot].address, | |
2704 context->tmp_buf_a, context->buf_a_off, | |
2705 context->col_1 | |
2706 ); | |
2707 context->flags &= ~FLAG_MASKED; | |
2708 render_sprite_cells(context); | |
2709 //164 | |
2710 render_border_garbage( | |
2711 context, | |
2712 context->sprite_draw_list[context->cur_slot].address, | |
2713 context->tmp_buf_a, context->buf_a_off + 8, | |
2714 context->col_2 | |
2715 ); | |
2716 render_sprite_cells(context); | |
2717 context->cycles += MCLKS_LINE; | |
2718 vdp_advance_line(context); | |
2719 src = context->compositebuf; | |
2720 dst = context->output; | |
2721 if (test_layer) { | |
2722 for (int i = 0; i < (LINE_CHANGE_H40 - BG_START_SLOT) * 2; i++) | |
2723 { | |
2724 *(dst++) = context->colors[*(src++)]; | |
2725 } | |
2726 } else { | |
2727 for (int i = 0; i < (LINE_CHANGE_H40 - BG_START_SLOT) * 2; i++) | |
2728 { | |
2729 if (*src & 0x3F) { | |
2730 *(dst++) = context->colors[*(src++)]; | |
2731 } else { | |
2732 *(dst++) = context->colors[(*(src++) & 0xC0) | bgindex]; | |
2733 } | |
2734 } | |
2735 } | |
2736 } | |
2452 static void vdp_h40(vdp_context * context, uint32_t target_cycles) | 2737 static void vdp_h40(vdp_context * context, uint32_t target_cycles) |
2453 { | 2738 { |
2454 uint16_t address; | 2739 uint16_t address; |
2455 uint32_t mask; | 2740 uint32_t mask; |
2456 uint32_t const slot_cycles = MCLKS_SLOT_H40; | 2741 uint32_t const slot_cycles = MCLKS_SLOT_H40; |
2464 switch(context->hslot) | 2749 switch(context->hslot) |
2465 { | 2750 { |
2466 for (;;) | 2751 for (;;) |
2467 { | 2752 { |
2468 case 165: | 2753 case 165: |
2754 //only consider doing a line at a time if the FIFO is empty, there are no pending reads and there is no DMA running | |
2755 if (context->fifo_read == -1 && !(context->flags & FLAG_DMA_RUN) && ((context->cd & 1) || (context->flags & FLAG_READ_FETCHED))) { | |
2756 while (target_cycles - context->cycles >= MCLKS_LINE && context->state != PREPARING && context->vcounter != context->inactive_start) { | |
2757 vdp_h40_line(context); | |
2758 } | |
2759 CHECK_ONLY | |
2760 } | |
2469 OUTPUT_PIXEL(165) | 2761 OUTPUT_PIXEL(165) |
2470 if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) { | 2762 if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) { |
2471 //TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode | 2763 //TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode |
2472 //See note in vdp_h32 for why this was originally moved out of read_map_scroll | 2764 //See note in vdp_h32 for why this was originally moved out of read_map_scroll |
2473 //Skitchin' has a similar problem, but uses H40 mode. It seems to be able to hit the extern slot at 232 | 2765 //Skitchin' has a similar problem, but uses H40 mode. It seems to be able to hit the extern slot at 232 |
2551 mask |= 0x7; | 2843 mask |= 0x7; |
2552 } | 2844 } |
2553 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2); | 2845 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2); |
2554 address += (context->vcounter & mask) * 4; | 2846 address += (context->vcounter & mask) * 4; |
2555 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; | 2847 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; |
2848 context->hscroll_a_fine = context->hscroll_a & 0xF; | |
2556 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; | 2849 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; |
2850 context->hscroll_b_fine = context->hscroll_b & 0xF; | |
2557 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); | 2851 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); |
2558 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } | 2852 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } |
2559 context->hslot++; | 2853 context->hslot++; |
2560 context->cycles += h40_hsync_cycles[14]; | 2854 context->cycles += h40_hsync_cycles[14]; |
2561 CHECK_ONLY //provides "garbage" for border when plane A selected | 2855 CHECK_ONLY //provides "garbage" for border when plane A selected |
2579 case 253: | 2873 case 253: |
2580 read_map_scroll_b(0, context->vcounter, context); | 2874 read_map_scroll_b(0, context->vcounter, context); |
2581 CHECK_LIMIT | 2875 CHECK_LIMIT |
2582 SPRITE_RENDER_H40(254) | 2876 SPRITE_RENDER_H40(254) |
2583 case 255: | 2877 case 255: |
2878 if (context->cur_slot >= 0 && context->sprite_draw_list[context->cur_slot].x_pos) { | |
2879 context->flags |= FLAG_DOT_OFLOW; | |
2880 } | |
2584 render_map_3(context); | 2881 render_map_3(context); |
2585 scan_sprite_table(context->vcounter, context);//Just a guess | 2882 scan_sprite_table(context->vcounter, context);//Just a guess |
2586 CHECK_LIMIT | 2883 CHECK_LIMIT |
2587 case 0: | 2884 case 0: |
2588 render_map_output(context->vcounter, 0, context); | 2885 render_map_output(context->vcounter, 0, context); |
2590 //seems like the sprite table scan fills a shift register | 2887 //seems like the sprite table scan fills a shift register |
2591 //values are FIFO, but unused slots precede used slots | 2888 //values are FIFO, but unused slots precede used slots |
2592 //so we set cur_slot to slot_counter and let it wrap around to | 2889 //so we set cur_slot to slot_counter and let it wrap around to |
2593 //the beginning of the list | 2890 //the beginning of the list |
2594 context->cur_slot = context->slot_counter; | 2891 context->cur_slot = context->slot_counter; |
2595 context->sprite_draws = MAX_DRAWS; | 2892 context->sprite_x_offset = 0; |
2596 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); | 2893 context->sprite_draws = MAX_SPRITES_LINE; |
2597 CHECK_LIMIT | 2894 CHECK_LIMIT |
2598 COLUMN_RENDER_BLOCK(2, 1) | 2895 COLUMN_RENDER_BLOCK(2, 1) |
2599 COLUMN_RENDER_BLOCK(4, 9) | 2896 COLUMN_RENDER_BLOCK(4, 9) |
2600 COLUMN_RENDER_BLOCK(6, 17) | 2897 COLUMN_RENDER_BLOCK(6, 17) |
2601 COLUMN_RENDER_BLOCK_REFRESH(8, 25) | 2898 COLUMN_RENDER_BLOCK_REFRESH(8, 25) |
2624 external_slot(context); | 2921 external_slot(context); |
2625 CHECK_LIMIT | 2922 CHECK_LIMIT |
2626 //sprite render to line buffer starts | 2923 //sprite render to line buffer starts |
2627 case 163: | 2924 case 163: |
2628 OUTPUT_PIXEL(163) | 2925 OUTPUT_PIXEL(163) |
2629 context->cur_slot = MAX_DRAWS-1; | 2926 context->cur_slot = MAX_SPRITES_LINE-1; |
2630 memset(context->linebuf, 0, LINEBUF_SIZE); | 2927 memset(context->linebuf, 0, LINEBUF_SIZE); |
2631 render_border_garbage( | 2928 render_border_garbage( |
2632 context, | 2929 context, |
2633 context->sprite_draw_list[context->cur_slot].address, | 2930 context->sprite_draw_list[context->cur_slot].address, |
2634 context->tmp_buf_a, context->buf_a_off, | 2931 context->tmp_buf_a, context->buf_a_off, |
2635 context->col_1 | 2932 context->col_1 |
2636 ); | 2933 ); |
2934 context->flags &= ~FLAG_MASKED; | |
2637 render_sprite_cells(context); | 2935 render_sprite_cells(context); |
2638 CHECK_LIMIT | 2936 CHECK_LIMIT |
2639 case 164: | 2937 case 164: |
2640 OUTPUT_PIXEL(164) | 2938 OUTPUT_PIXEL(164) |
2641 render_border_garbage( | 2939 render_border_garbage( |
2767 mask |= 0x7; | 3065 mask |= 0x7; |
2768 } | 3066 } |
2769 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2); | 3067 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2); |
2770 address += (context->vcounter & mask) * 4; | 3068 address += (context->vcounter & mask) * 4; |
2771 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; | 3069 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; |
3070 context->hscroll_a_fine = context->hscroll_a & 0xF; | |
2772 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; | 3071 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; |
3072 context->hscroll_b_fine = context->hscroll_b & 0xF; | |
2773 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); | 3073 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); |
2774 CHECK_LIMIT //provides "garbage" for border when plane A selected | 3074 CHECK_LIMIT //provides "garbage" for border when plane A selected |
2775 SPRITE_RENDER_H32(245) | 3075 SPRITE_RENDER_H32(245) |
2776 SPRITE_RENDER_H32(246) | 3076 SPRITE_RENDER_H32(246) |
2777 SPRITE_RENDER_H32(247) //provides "garbage" for border when plane B selected | 3077 SPRITE_RENDER_H32(247) //provides "garbage" for border when plane B selected |
2780 case 249: | 3080 case 249: |
2781 read_map_scroll_a(0, context->vcounter, context); | 3081 read_map_scroll_a(0, context->vcounter, context); |
2782 CHECK_LIMIT | 3082 CHECK_LIMIT |
2783 SPRITE_RENDER_H32(250) | 3083 SPRITE_RENDER_H32(250) |
2784 case 251: | 3084 case 251: |
3085 if (context->cur_slot >= 0 && context->sprite_draw_list[context->cur_slot].x_pos) { | |
3086 context->flags |= FLAG_DOT_OFLOW; | |
3087 } | |
2785 render_map_1(context); | 3088 render_map_1(context); |
2786 scan_sprite_table(context->vcounter, context);//Just a guess | 3089 scan_sprite_table(context->vcounter, context);//Just a guess |
2787 CHECK_LIMIT | 3090 CHECK_LIMIT |
2788 case 252: | 3091 case 252: |
2789 render_map_2(context); | 3092 render_map_2(context); |
2805 scan_sprite_table(context->vcounter, context);//Just a guess | 3108 scan_sprite_table(context->vcounter, context);//Just a guess |
2806 //reverse context slot counter so it counts the number of sprite slots | 3109 //reverse context slot counter so it counts the number of sprite slots |
2807 //filled rather than the number of available slots | 3110 //filled rather than the number of available slots |
2808 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; | 3111 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; |
2809 context->cur_slot = context->slot_counter; | 3112 context->cur_slot = context->slot_counter; |
2810 context->sprite_draws = MAX_DRAWS_H32; | 3113 context->sprite_x_offset = 0; |
2811 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); | 3114 context->sprite_draws = MAX_SPRITES_LINE_H32; |
2812 CHECK_LIMIT | 3115 CHECK_LIMIT |
2813 COLUMN_RENDER_BLOCK(2, 1) | 3116 COLUMN_RENDER_BLOCK(2, 1) |
2814 COLUMN_RENDER_BLOCK(4, 9) | 3117 COLUMN_RENDER_BLOCK(4, 9) |
2815 COLUMN_RENDER_BLOCK(6, 17) | 3118 COLUMN_RENDER_BLOCK(6, 17) |
2816 COLUMN_RENDER_BLOCK_REFRESH(8, 25) | 3119 COLUMN_RENDER_BLOCK_REFRESH(8, 25) |
2836 CHECK_LIMIT | 3139 CHECK_LIMIT |
2837 } | 3140 } |
2838 //sprite render to line buffer starts | 3141 //sprite render to line buffer starts |
2839 case 131: | 3142 case 131: |
2840 OUTPUT_PIXEL(131) | 3143 OUTPUT_PIXEL(131) |
2841 context->cur_slot = MAX_DRAWS_H32-1; | 3144 context->cur_slot = MAX_SPRITES_LINE_H32-1; |
2842 memset(context->linebuf, 0, LINEBUF_SIZE); | 3145 memset(context->linebuf, 0, LINEBUF_SIZE); |
2843 render_border_garbage( | 3146 render_border_garbage( |
2844 context, | 3147 context, |
2845 context->sprite_draw_list[context->cur_slot].address, | 3148 context->sprite_draw_list[context->cur_slot].address, |
2846 context->tmp_buf_a, context->buf_a_off, | 3149 context->tmp_buf_a, context->buf_a_off, |
2847 context->col_1 | 3150 context->col_1 |
2848 ); | 3151 ); |
3152 context->flags &= ~FLAG_MASKED; | |
2849 render_sprite_cells(context); | 3153 render_sprite_cells(context); |
2850 CHECK_LIMIT | 3154 CHECK_LIMIT |
2851 case 132: | 3155 case 132: |
2852 OUTPUT_PIXEL(132) | 3156 OUTPUT_PIXEL(132) |
2853 render_border_garbage( | 3157 render_border_garbage( |
2980 COLUMN_RENDER_BLOCK_MODE4(28, 117) | 3284 COLUMN_RENDER_BLOCK_MODE4(28, 117) |
2981 COLUMN_RENDER_BLOCK_MODE4(29, 121) | 3285 COLUMN_RENDER_BLOCK_MODE4(29, 121) |
2982 COLUMN_RENDER_BLOCK_MODE4(30, 125) | 3286 COLUMN_RENDER_BLOCK_MODE4(30, 125) |
2983 COLUMN_RENDER_BLOCK_MODE4(31, 129) | 3287 COLUMN_RENDER_BLOCK_MODE4(31, 129) |
2984 case 133: | 3288 case 133: |
2985 OUTPUT_PIXEL(133) | 3289 OUTPUT_PIXEL_MODE4(133) |
2986 external_slot(context); | 3290 external_slot(context); |
2987 CHECK_LIMIT | 3291 CHECK_LIMIT |
2988 case 134: | 3292 case 134: |
2989 OUTPUT_PIXEL(134) | 3293 OUTPUT_PIXEL_MODE4(134) |
2990 external_slot(context); | 3294 external_slot(context); |
2991 CHECK_LIMIT | 3295 CHECK_LIMIT |
2992 case 135: | 3296 case 135: |
2993 OUTPUT_PIXEL(135) | 3297 OUTPUT_PIXEL_MODE4(135) |
2994 external_slot(context); | 3298 external_slot(context); |
2995 CHECK_LIMIT | 3299 CHECK_LIMIT |
2996 case 136: { | 3300 case 136: { |
2997 OUTPUT_PIXEL(136) | 3301 OUTPUT_PIXEL_MODE4(136) |
2998 external_slot(context); | 3302 external_slot(context); |
2999 //set things up for sprite rendering in the next slot | 3303 //set things up for sprite rendering in the next slot |
3000 memset(context->linebuf, 0, LINEBUF_SIZE); | 3304 memset(context->linebuf, 0, LINEBUF_SIZE); |
3001 context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1; | 3305 context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1; |
3002 context->sprite_draws = MAX_DRAWS_H32_MODE4; | 3306 context->sprite_draws = MAX_DRAWS_H32_MODE4; |
3026 len = BORDER_LEFT; | 3330 len = BORDER_LEFT; |
3027 } | 3331 } |
3028 uint8_t *src = NULL; | 3332 uint8_t *src = NULL; |
3029 if (test_layer == 2) { | 3333 if (test_layer == 2) { |
3030 //plane A | 3334 //plane A |
3031 src_off += context->buf_a_off + context->hscroll_a; | 3335 src_off += context->buf_a_off - (context->hscroll_a & 0xF); |
3032 src = context->tmp_buf_a; | 3336 src = context->tmp_buf_a; |
3033 } else if (test_layer == 3){ | 3337 } else if (test_layer == 3){ |
3034 //plane B | 3338 //plane B |
3035 src_off += context->buf_b_off + context->hscroll_b; | 3339 src_off += context->buf_b_off - (context->hscroll_b & 0xF); |
3036 src = context->tmp_buf_b; | 3340 src = context->tmp_buf_b; |
3037 } else { | 3341 } else { |
3038 //sprite layer | 3342 //sprite layer |
3039 memset(dst, 0, len); | 3343 memset(dst, 0, len); |
3040 dst += len; | 3344 dst += len; |
3062 static void vdp_inactive(vdp_context *context, uint32_t target_cycles, uint8_t is_h40, uint8_t mode_5) | 3366 static void vdp_inactive(vdp_context *context, uint32_t target_cycles, uint8_t is_h40, uint8_t mode_5) |
3063 { | 3367 { |
3064 uint8_t buf_clear_slot, index_reset_slot, bg_end_slot, vint_slot, line_change, jump_start, jump_dest, latch_slot; | 3368 uint8_t buf_clear_slot, index_reset_slot, bg_end_slot, vint_slot, line_change, jump_start, jump_dest, latch_slot; |
3065 uint8_t index_reset_value, max_draws, max_sprites; | 3369 uint8_t index_reset_value, max_draws, max_sprites; |
3066 uint16_t vint_line, active_line; | 3370 uint16_t vint_line, active_line; |
3067 uint32_t bg_color; | |
3068 | 3371 |
3069 if (mode_5) { | 3372 if (mode_5) { |
3070 if (is_h40) { | 3373 if (is_h40) { |
3071 latch_slot = 165; | 3374 latch_slot = 165; |
3072 buf_clear_slot = 163; | 3375 buf_clear_slot = 163; |
3073 index_reset_slot = 167; | 3376 index_reset_slot = 167; |
3074 bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2; | 3377 bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2; |
3075 max_draws = MAX_DRAWS-1; | 3378 max_draws = MAX_SPRITES_LINE-1; |
3076 max_sprites = MAX_SPRITES_LINE; | 3379 max_sprites = MAX_SPRITES_LINE; |
3077 index_reset_value = 0x80; | 3380 index_reset_value = 0x80; |
3078 vint_slot = VINT_SLOT_H40; | 3381 vint_slot = VINT_SLOT_H40; |
3079 line_change = LINE_CHANGE_H40; | 3382 line_change = LINE_CHANGE_H40; |
3080 jump_start = 182; | 3383 jump_start = 182; |
3081 jump_dest = 229; | 3384 jump_dest = 229; |
3082 } else { | 3385 } else { |
3083 bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2; | 3386 bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2; |
3084 max_draws = MAX_DRAWS_H32-1; | 3387 max_draws = MAX_SPRITES_LINE_H32-1; |
3085 max_sprites = MAX_SPRITES_LINE_H32; | 3388 max_sprites = MAX_SPRITES_LINE_H32; |
3086 buf_clear_slot = 128; | 3389 buf_clear_slot = 128; |
3087 index_reset_slot = 132; | 3390 index_reset_slot = 132; |
3088 index_reset_value = 0x80; | 3391 index_reset_value = 0x80; |
3089 vint_slot = VINT_SLOT_H32; | 3392 vint_slot = VINT_SLOT_H32; |
3106 index_reset_slot = 253; | 3409 index_reset_slot = 253; |
3107 index_reset_value = 0; | 3410 index_reset_value = 0; |
3108 vint_line = context->inactive_start + 1; | 3411 vint_line = context->inactive_start + 1; |
3109 vint_slot = VINT_SLOT_MODE4; | 3412 vint_slot = VINT_SLOT_MODE4; |
3110 line_change = LINE_CHANGE_MODE4; | 3413 line_change = LINE_CHANGE_MODE4; |
3111 bg_color = render_map_color(0, 0, 0); | |
3112 jump_start = 147; | 3414 jump_start = 147; |
3113 jump_dest = 233; | 3415 jump_dest = 233; |
3114 if (context->regs[REG_MODE_1] & BIT_MODE_4) { | 3416 if (context->regs[REG_MODE_1] & BIT_MODE_4) { |
3115 active_line = 0x1FF; | 3417 active_line = 0x1FF; |
3116 } else { | 3418 } else { |
3154 case 7: | 3456 case 7: |
3155 render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off, context->col_1); | 3457 render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off, context->col_1); |
3156 break; | 3458 break; |
3157 case 0: | 3459 case 0: |
3158 render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off+8, context->col_2); | 3460 render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off+8, context->col_2); |
3461 break; | |
3462 case 1: | |
3159 inactive_test_output(context, is_h40, test_layer); | 3463 inactive_test_output(context, is_h40, test_layer); |
3160 break; | 3464 break; |
3161 } | 3465 } |
3162 } | 3466 } |
3163 | 3467 |
3187 context->flags2 ^= FLAG2_EVEN_FIELD; | 3491 context->flags2 ^= FLAG2_EVEN_FIELD; |
3188 } | 3492 } |
3189 | 3493 |
3190 if (dst) { | 3494 if (dst) { |
3191 uint8_t bg_index; | 3495 uint8_t bg_index; |
3496 uint32_t bg_color; | |
3192 if (mode_5) { | 3497 if (mode_5) { |
3193 bg_index = context->regs[REG_BG_COLOR] & 0x3F; | 3498 bg_index = context->regs[REG_BG_COLOR] & 0x3F; |
3194 bg_color = context->colors[bg_index]; | 3499 bg_color = context->colors[bg_index]; |
3195 } else if (context->regs[REG_MODE_1] & BIT_MODE_4) { | 3500 } else if (context->regs[REG_MODE_1] & BIT_MODE_4) { |
3196 bg_index = 0x10 + (context->regs[REG_BG_COLOR] & 0xF); | 3501 bg_index = 0x10 + (context->regs[REG_BG_COLOR] & 0xF); |
3197 bg_color = context->colors[MODE4_OFFSET + bg_index]; | 3502 bg_color = context->colors[MODE4_OFFSET + bg_index]; |
3503 } else { | |
3504 bg_color = render_map_color(0, 0, 0); | |
3198 } | 3505 } |
3199 if (context->done_composite) { | 3506 if (context->done_composite) { |
3200 uint8_t pixel = context->compositebuf[dst-context->output]; | 3507 uint8_t pixel = context->compositebuf[dst-context->output]; |
3201 if (!(pixel & 0x3F | test_layer)) { | 3508 if (!(pixel & 0x3F | test_layer)) { |
3202 pixel = pixel & 0xC0 | bg_index; | 3509 pixel = pixel & 0xC0 | bg_index; |
3382 hv |= get_ext_vcounter(context); | 3689 hv |= get_ext_vcounter(context); |
3383 | 3690 |
3384 return hv; | 3691 return hv; |
3385 } | 3692 } |
3386 | 3693 |
3694 static void clear_pending(vdp_context *context) | |
3695 { | |
3696 context->flags &= ~FLAG_PENDING; | |
3697 context->address = context->address_latch; | |
3698 //It seems like the DMA enable bit doesn't so much enable DMA so much | |
3699 //as it enables changing CD5 from control port writes | |
3700 if (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) { | |
3701 context->cd = context->cd_latch; | |
3702 } else { | |
3703 context->cd = (context->cd & 0x20) | (context->cd_latch & 0x1F); | |
3704 } | |
3705 } | |
3706 | |
3387 int vdp_control_port_write(vdp_context * context, uint16_t value) | 3707 int vdp_control_port_write(vdp_context * context, uint16_t value) |
3388 { | 3708 { |
3389 //printf("control port write: %X at %d\n", value, context->cycles); | 3709 //printf("control port write: %X at %d\n", value, context->cycles); |
3390 if (context->flags & FLAG_DMA_RUN) { | 3710 if (context->flags & FLAG_DMA_RUN) { |
3391 return -1; | 3711 return -1; |
3392 } | 3712 } |
3393 if (context->flags & FLAG_PENDING) { | 3713 if (context->flags & FLAG_PENDING) { |
3394 context->address = (context->address & 0x3FFF) | (value << 14 & 0x1C000); | 3714 context->address_latch = (context->address_latch & 0x3FFF) | (value << 14 & 0x1C000); |
3395 //It seems like the DMA enable bit doesn't so much enable DMA so much | 3715 context->cd_latch = (context->cd_latch & 0x3) | ((value >> 2) & ~0x3 & 0xFF); |
3396 //as it enables changing CD5 from control port writes | 3716 clear_pending(context); |
3397 uint8_t preserve = (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) ? 0x3 : 0x23; | |
3398 context->cd = (context->cd & preserve) | ((value >> 2) & ~preserve & 0xFF); | |
3399 context->flags &= ~FLAG_PENDING; | |
3400 //Should these be taken care of here or after the first write? | 3717 //Should these be taken care of here or after the first write? |
3401 context->flags &= ~FLAG_READ_FETCHED; | 3718 context->flags &= ~FLAG_READ_FETCHED; |
3402 context->flags2 &= ~FLAG2_READ_PENDING; | 3719 context->flags2 &= ~FLAG2_READ_PENDING; |
3403 //printf("New Address: %X, New CD: %X\n", context->address, context->cd); | 3720 //printf("New Address: %X, New CD: %X\n", context->address, context->cd); |
3404 if (context->cd & 0x20) { | 3721 if (context->cd & 0x20) { |
3423 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); | 3740 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); |
3424 } | 3741 } |
3425 } | 3742 } |
3426 } else { | 3743 } else { |
3427 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5; | 3744 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5; |
3428 context->address = (context->address &0xC000) | (value & 0x3FFF); | 3745 //contrary to what's in Charles MacDonald's doc, it seems top 2 address bits are cleared |
3429 context->cd = (context->cd & 0x3C) | (value >> 14); | 3746 //needed for the Mona in 344 Bytes demo |
3747 context->address_latch = (context->address_latch & 0x1C000) | (value & 0x3FFF); | |
3748 context->cd_latch = (context->cd_latch & 0x3C) | (value >> 14); | |
3430 if ((value & 0xC000) == 0x8000) { | 3749 if ((value & 0xC000) == 0x8000) { |
3431 //Register write | 3750 //Register write |
3432 uint8_t reg = (value >> 8) & 0x1F; | 3751 uint8_t reg = (value >> 8) & 0x1F; |
3433 if (reg < (mode_5 ? VDP_REGS : 0xB)) { | 3752 if (reg < (mode_5 ? VDP_REGS : 0xB)) { |
3434 //printf("register %d set to %X\n", reg, value & 0xFF); | 3753 //printf("register %d set to %X\n", reg, value & 0xFF); |
3486 //printf("data port write: %X at %d\n", value, context->cycles); | 3805 //printf("data port write: %X at %d\n", value, context->cycles); |
3487 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) { | 3806 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) { |
3488 return -1; | 3807 return -1; |
3489 } | 3808 } |
3490 if (context->flags & FLAG_PENDING) { | 3809 if (context->flags & FLAG_PENDING) { |
3491 context->flags &= ~FLAG_PENDING; | 3810 clear_pending(context); |
3492 //Should these be cleared here? | 3811 //Should these be cleared here? |
3493 context->flags &= ~FLAG_READ_FETCHED; | 3812 context->flags &= ~FLAG_READ_FETCHED; |
3494 context->flags2 &= ~FLAG2_READ_PENDING; | 3813 context->flags2 &= ~FLAG2_READ_PENDING; |
3495 } | 3814 } |
3496 /*if (context->fifo_cur == context->fifo_end) { | 3815 /*if (context->fifo_cur == context->fifo_end) { |
3521 } | 3840 } |
3522 | 3841 |
3523 void vdp_data_port_write_pbc(vdp_context * context, uint8_t value) | 3842 void vdp_data_port_write_pbc(vdp_context * context, uint8_t value) |
3524 { | 3843 { |
3525 if (context->flags & FLAG_PENDING) { | 3844 if (context->flags & FLAG_PENDING) { |
3526 context->flags &= ~FLAG_PENDING; | 3845 clear_pending(context); |
3527 //Should these be cleared here? | 3846 //Should these be cleared here? |
3528 context->flags &= ~FLAG_READ_FETCHED; | 3847 context->flags &= ~FLAG_READ_FETCHED; |
3529 context->flags2 &= ~FLAG2_READ_PENDING; | 3848 context->flags2 &= ~FLAG2_READ_PENDING; |
3530 } | 3849 } |
3531 context->flags2 &= ~FLAG2_BYTE_PENDING; | 3850 context->flags2 &= ~FLAG2_BYTE_PENDING; |
3560 context->test_port = value; | 3879 context->test_port = value; |
3561 } | 3880 } |
3562 | 3881 |
3563 uint16_t vdp_control_port_read(vdp_context * context) | 3882 uint16_t vdp_control_port_read(vdp_context * context) |
3564 { | 3883 { |
3565 context->flags &= ~FLAG_PENDING; | 3884 if (context->flags & FLAG_PENDING) { |
3885 clear_pending(context); | |
3886 } | |
3566 context->flags2 &= ~FLAG2_BYTE_PENDING; | 3887 context->flags2 &= ~FLAG2_BYTE_PENDING; |
3567 //Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch | 3888 //Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch |
3568 uint16_t value = context->system->get_open_bus_value(context->system) & 0xFC00; | 3889 uint16_t value = context->system->get_open_bus_value(context->system) & 0xFC00; |
3569 if (context->fifo_read < 0) { | 3890 if (context->fifo_read < 0) { |
3570 value |= 0x200; | 3891 value |= 0x200; |
3610 } | 3931 } |
3611 | 3932 |
3612 uint16_t vdp_data_port_read(vdp_context * context) | 3933 uint16_t vdp_data_port_read(vdp_context * context) |
3613 { | 3934 { |
3614 if (context->flags & FLAG_PENDING) { | 3935 if (context->flags & FLAG_PENDING) { |
3615 context->flags &= ~FLAG_PENDING; | 3936 clear_pending(context); |
3616 //Should these be cleared here? | 3937 //Should these be cleared here? |
3617 context->flags &= ~FLAG_READ_FETCHED; | 3938 context->flags &= ~FLAG_READ_FETCHED; |
3618 context->flags2 &= ~FLAG2_READ_PENDING; | 3939 context->flags2 &= ~FLAG2_READ_PENDING; |
3619 } | 3940 } |
3620 if (context->cd & 1) { | 3941 if (context->cd & 1) { |
3627 return context->prefetch; | 3948 return context->prefetch; |
3628 } | 3949 } |
3629 | 3950 |
3630 uint8_t vdp_data_port_read_pbc(vdp_context * context) | 3951 uint8_t vdp_data_port_read_pbc(vdp_context * context) |
3631 { | 3952 { |
3632 context->flags &= ~(FLAG_PENDING | FLAG_READ_FETCHED); | 3953 if (context->flags & FLAG_PENDING) { |
3954 clear_pending(context); | |
3955 } | |
3956 context->flags &= ~FLAG_READ_FETCHED; | |
3633 context->flags2 &= ~FLAG2_BYTE_PENDING; | 3957 context->flags2 &= ~FLAG2_BYTE_PENDING; |
3634 | 3958 |
3635 context->cd = VRAM_READ8; | 3959 context->cd = VRAM_READ8; |
3636 return context->prefetch; | 3960 return context->prefetch; |
3637 } | |
3638 | |
3639 uint16_t vdp_test_port_read(vdp_context * context) | |
3640 { | |
3641 //TODO: Find out what actually gets returned here | |
3642 return context->test_port; | |
3643 } | 3961 } |
3644 | 3962 |
3645 void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) | 3963 void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) |
3646 { | 3964 { |
3647 context->cycles -= deduction; | 3965 context->cycles -= deduction; |
3932 context->flags2 &= ~FLAG2_HINT_PENDING; | 4250 context->flags2 &= ~FLAG2_HINT_PENDING; |
3933 } | 4251 } |
3934 } | 4252 } |
3935 } | 4253 } |
3936 | 4254 |
4255 #define VDP_STATE_VERSION 3 | |
3937 void vdp_serialize(vdp_context *context, serialize_buffer *buf) | 4256 void vdp_serialize(vdp_context *context, serialize_buffer *buf) |
3938 { | 4257 { |
4258 save_int8(buf, VDP_STATE_VERSION); | |
3939 save_int8(buf, VRAM_SIZE / 1024);//VRAM size in KB, needed for future proofing | 4259 save_int8(buf, VRAM_SIZE / 1024);//VRAM size in KB, needed for future proofing |
3940 save_buffer8(buf, context->vdpmem, VRAM_SIZE); | 4260 save_buffer8(buf, context->vdpmem, VRAM_SIZE); |
3941 save_buffer16(buf, context->cram, CRAM_SIZE); | 4261 save_buffer16(buf, context->cram, CRAM_SIZE); |
3942 save_buffer16(buf, context->vsram, VSRAM_SIZE); | 4262 save_buffer16(buf, context->vsram, MAX_VSRAM_SIZE); |
3943 save_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE); | 4263 save_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE); |
3944 for (int i = 0; i <= REG_DMASRC_H; i++) | 4264 for (int i = 0; i <= REG_DMASRC_H; i++) |
3945 { | 4265 { |
3946 save_int8(buf, context->regs[i]); | 4266 save_int8(buf, context->regs[i]); |
3947 } | 4267 } |
3988 //FIXME: Sprite rendering state is currently a mess | 4308 //FIXME: Sprite rendering state is currently a mess |
3989 save_int8(buf, context->sprite_index); | 4309 save_int8(buf, context->sprite_index); |
3990 save_int8(buf, context->sprite_draws); | 4310 save_int8(buf, context->sprite_draws); |
3991 save_int8(buf, context->slot_counter); | 4311 save_int8(buf, context->slot_counter); |
3992 save_int8(buf, context->cur_slot); | 4312 save_int8(buf, context->cur_slot); |
3993 for (int i = 0; i < MAX_DRAWS; i++) | 4313 for (int i = 0; i < MAX_SPRITES_LINE; i++) |
3994 { | 4314 { |
3995 sprite_draw *draw = context->sprite_draw_list + i; | 4315 sprite_draw *draw = context->sprite_draw_list + i; |
3996 save_int16(buf, draw->address); | 4316 save_int16(buf, draw->address); |
3997 save_int16(buf, draw->x_pos); | 4317 save_int16(buf, draw->x_pos); |
3998 save_int8(buf, draw->pal_priority); | 4318 save_int8(buf, draw->pal_priority); |
3999 save_int8(buf, draw->h_flip); | 4319 save_int8(buf, draw->h_flip); |
4320 save_int8(buf, draw->width); | |
4321 save_int8(buf, draw->height); | |
4000 } | 4322 } |
4001 for (int i = 0; i < MAX_SPRITES_LINE; i++) | 4323 for (int i = 0; i < MAX_SPRITES_LINE; i++) |
4002 { | 4324 { |
4003 sprite_info *info = context->sprite_info_list + i; | 4325 sprite_info *info = context->sprite_info_list + i; |
4004 save_int8(buf, info->size); | 4326 save_int8(buf, info->size); |
4008 save_buffer8(buf, context->linebuf, LINEBUF_SIZE); | 4330 save_buffer8(buf, context->linebuf, LINEBUF_SIZE); |
4009 | 4331 |
4010 save_int32(buf, context->cycles); | 4332 save_int32(buf, context->cycles); |
4011 save_int32(buf, context->pending_vint_start); | 4333 save_int32(buf, context->pending_vint_start); |
4012 save_int32(buf, context->pending_hint_start); | 4334 save_int32(buf, context->pending_hint_start); |
4335 save_int32(buf, context->address_latch); | |
4336 save_int8(buf, context->cd_latch); | |
4013 } | 4337 } |
4014 | 4338 |
4015 void vdp_deserialize(deserialize_buffer *buf, void *vcontext) | 4339 void vdp_deserialize(deserialize_buffer *buf, void *vcontext) |
4016 { | 4340 { |
4017 vdp_context *context = vcontext; | 4341 vdp_context *context = vcontext; |
4018 uint8_t vramk = load_int8(buf); | 4342 uint8_t version = load_int8(buf); |
4343 uint8_t vramk; | |
4344 if (version == 64) { | |
4345 vramk = version; | |
4346 version = 0; | |
4347 } else { | |
4348 vramk = load_int8(buf); | |
4349 } | |
4350 if (version > VDP_STATE_VERSION) { | |
4351 warning("Save state has VDP version %d, but this build only understands versions %d and lower", version, VDP_STATE_VERSION); | |
4352 } | |
4019 load_buffer8(buf, context->vdpmem, (vramk * 1024) <= VRAM_SIZE ? vramk * 1024 : VRAM_SIZE); | 4353 load_buffer8(buf, context->vdpmem, (vramk * 1024) <= VRAM_SIZE ? vramk * 1024 : VRAM_SIZE); |
4020 if ((vramk * 1024) > VRAM_SIZE) { | 4354 if ((vramk * 1024) > VRAM_SIZE) { |
4021 buf->cur_pos += (vramk * 1024) - VRAM_SIZE; | 4355 buf->cur_pos += (vramk * 1024) - VRAM_SIZE; |
4022 } | 4356 } |
4023 load_buffer16(buf, context->cram, CRAM_SIZE); | 4357 load_buffer16(buf, context->cram, CRAM_SIZE); |
4024 for (int i = 0; i < CRAM_SIZE; i++) | 4358 for (int i = 0; i < CRAM_SIZE; i++) |
4025 { | 4359 { |
4026 update_color_map(context, i, context->cram[i]); | 4360 update_color_map(context, i, context->cram[i]); |
4027 } | 4361 } |
4028 load_buffer16(buf, context->vsram, VSRAM_SIZE); | 4362 load_buffer16(buf, context->vsram, version > 1 ? MAX_VSRAM_SIZE : MIN_VSRAM_SIZE); |
4029 load_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE); | 4363 load_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE); |
4030 for (int i = 0; i <= REG_DMASRC_H; i++) | 4364 for (int i = 0; i <= REG_DMASRC_H; i++) |
4031 { | 4365 { |
4032 context->regs[i] = load_int8(buf); | 4366 context->regs[i] = load_int8(buf); |
4033 } | 4367 } |
4075 context->buf_b_off = load_int8(buf) & SCROLL_BUFFER_MASK; | 4409 context->buf_b_off = load_int8(buf) & SCROLL_BUFFER_MASK; |
4076 context->sprite_index = load_int8(buf); | 4410 context->sprite_index = load_int8(buf); |
4077 context->sprite_draws = load_int8(buf); | 4411 context->sprite_draws = load_int8(buf); |
4078 context->slot_counter = load_int8(buf); | 4412 context->slot_counter = load_int8(buf); |
4079 context->cur_slot = load_int8(buf); | 4413 context->cur_slot = load_int8(buf); |
4080 for (int i = 0; i < MAX_DRAWS; i++) | 4414 if (version == 0) { |
4081 { | 4415 int cur_draw = 0; |
4082 sprite_draw *draw = context->sprite_draw_list + i; | 4416 for (int i = 0; i < MAX_SPRITES_LINE * 2; i++) |
4083 draw->address = load_int16(buf); | 4417 { |
4084 draw->x_pos = load_int16(buf); | 4418 if (cur_draw < MAX_SPRITES_LINE) { |
4085 draw->pal_priority = load_int8(buf); | 4419 sprite_draw *last = cur_draw ? context->sprite_draw_list + cur_draw - 1 : NULL; |
4086 draw->h_flip = load_int8(buf); | 4420 sprite_draw *draw = context->sprite_draw_list + cur_draw++; |
4421 draw->address = load_int16(buf); | |
4422 draw->x_pos = load_int16(buf); | |
4423 draw->pal_priority = load_int8(buf); | |
4424 draw->h_flip = load_int8(buf); | |
4425 draw->width = 1; | |
4426 draw->height = 8; | |
4427 | |
4428 if (last && last->width < 4 && last->h_flip == draw->h_flip && last->pal_priority == draw->pal_priority) { | |
4429 int adjust_x = draw->x_pos + draw->h_flip ? -8 : 8; | |
4430 int height = draw->address - last->address /4; | |
4431 if (last->x_pos == adjust_x && ( | |
4432 (last->width > 1 && height == last->height) || | |
4433 (last->width == 1 && (height == 8 || height == 16 || height == 24 || height == 32)) | |
4434 )) { | |
4435 //current draw appears to be part of the same sprite as the last one, combine it | |
4436 cur_draw--; | |
4437 last->width++; | |
4438 } | |
4439 } | |
4440 } else { | |
4441 load_int16(buf); | |
4442 load_int16(buf); | |
4443 load_int8(buf); | |
4444 load_int8(buf); | |
4445 } | |
4446 } | |
4447 } else { | |
4448 for (int i = 0; i < MAX_SPRITES_LINE; i++) | |
4449 { | |
4450 sprite_draw *draw = context->sprite_draw_list + i; | |
4451 draw->address = load_int16(buf); | |
4452 draw->x_pos = load_int16(buf); | |
4453 draw->pal_priority = load_int8(buf); | |
4454 draw->h_flip = load_int8(buf); | |
4455 draw->width = load_int8(buf); | |
4456 draw->height = load_int8(buf); | |
4457 } | |
4087 } | 4458 } |
4088 for (int i = 0; i < MAX_SPRITES_LINE; i++) | 4459 for (int i = 0; i < MAX_SPRITES_LINE; i++) |
4089 { | 4460 { |
4090 sprite_info *info = context->sprite_info_list + i; | 4461 sprite_info *info = context->sprite_info_list + i; |
4091 info->size = load_int8(buf); | 4462 info->size = load_int8(buf); |
4095 load_buffer8(buf, context->linebuf, LINEBUF_SIZE); | 4466 load_buffer8(buf, context->linebuf, LINEBUF_SIZE); |
4096 | 4467 |
4097 context->cycles = load_int32(buf); | 4468 context->cycles = load_int32(buf); |
4098 context->pending_vint_start = load_int32(buf); | 4469 context->pending_vint_start = load_int32(buf); |
4099 context->pending_hint_start = load_int32(buf); | 4470 context->pending_hint_start = load_int32(buf); |
4471 if (version > 2) { | |
4472 context->address_latch = load_int32(buf); | |
4473 context->cd_latch = load_int8(buf); | |
4474 } else { | |
4475 context->address_latch = context->address; | |
4476 context->cd_latch = context->cd; | |
4477 } | |
4100 update_video_params(context); | 4478 update_video_params(context); |
4101 } | 4479 } |
4102 | 4480 |
4103 static vdp_context *current_vdp; | 4481 static vdp_context *current_vdp; |
4104 static void vdp_debug_window_close(uint8_t which) | 4482 static void vdp_debug_window_close(uint8_t which) |