annotate vdp.c @ 989:d70000fdff0b

Implemented IR and undefined bits of info word for address error exception frames
author Michael Pavone <pavone@retrodev.com>
date Wed, 27 Apr 2016 21:39:17 -0700
parents bd4d698d995b
children f9ee6f746cb4
rev   line source
pavone@467 1 /*
pavone@467 2 Copyright 2013 Michael Pavone
pavone@470 3 This file is part of BlastEm.
pavone@467 4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
pavone@467 5 */
pavone@20 6 #include "vdp.h"
pavone@75 7 #include "blastem.h"
pavone@20 8 #include <stdlib.h>
pavone@20 9 #include <string.h>
pavone@426 10 #include "render.h"
pavone@20 11
pavone@622 12 #define NTSC_INACTIVE_START 224
pavone@622 13 #define PAL_INACTIVE_START 240
pavone@20 14 #define BUF_BIT_PRIORITY 0x40
pavone@20 15 #define MAP_BIT_PRIORITY 0x8000
pavone@20 16 #define MAP_BIT_H_FLIP 0x800
pavone@20 17 #define MAP_BIT_V_FLIP 0x1000
pavone@20 18
pavone@39 19 #define SCROLL_BUFFER_SIZE 32
pavone@436 20 #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1)
pavone@436 21 #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2)
pavone@39 22
pavone@328 23 #define MCLKS_SLOT_H40 16
pavone@328 24 #define MCLKS_SLOT_H32 20
pavone@622 25 #define VINT_SLOT_H40 4 //21 slots before HSYNC, 16 during, 10 after
pavone@711 26 #define VINT_SLOT_H32 4 //old value was 23, but recent tests suggest the actual value is close to the H40 one
pavone@922 27 #define HSYNC_SLOT_H40 228
pavone@697 28 #define HSYNC_END_H40 (HSYNC_SLOT_H40+17)
pavone@331 29 #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32)
pavone@647 30 #define HBLANK_START_H40 178 //should be 179 according to Nemesis, but 178 seems to fit slightly better with my test ROM results
pavone@647 31 #define HBLANK_END_H40 0 //should be 5.5 according to Nemesis, but 0 seems to fit better with my test ROM results
pavone@647 32 #define HBLANK_START_H32 233 //should be 147 according to Nemesis which is very different from my test ROM result
pavone@647 33 #define HBLANK_END_H32 0 //should be 5 according to Nemesis, but 0 seems to fit better with my test ROM results
pavone@647 34 #define LINE_CHANGE_H40 165
pavone@647 35 #define LINE_CHANGE_H32 132
pavone@717 36 #define VBLANK_START_H40 (LINE_CHANGE_H40+2)
pavone@717 37 #define VBLANK_START_H32 (LINE_CHANGE_H32+2)
pavone@460 38 #define FIFO_LATENCY 3
pavone@328 39
pavone@426 40 int32_t color_map[1 << 12];
pavone@426 41 uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255};
pavone@426 42
pavone@437 43 uint8_t debug_base[][3] = {
pavone@437 44 {127, 127, 127}, //BG
pavone@437 45 {0, 0, 127}, //A
pavone@437 46 {127, 0, 0}, //Window
pavone@437 47 {0, 127, 0}, //B
pavone@437 48 {127, 0, 127} //Sprites
pavone@437 49 };
pavone@437 50
pavone@426 51 uint8_t color_map_init_done;
pavone@426 52
pavone@623 53 void init_vdp_context(vdp_context * context, uint8_t region_pal)
pavone@20 54 {
pavone@58 55 memset(context, 0, sizeof(*context));
pavone@20 56 context->vdpmem = malloc(VRAM_SIZE);
pavone@58 57 memset(context->vdpmem, 0, VRAM_SIZE);
pavone@505 58 /*
pavone@488 59 */
pavone@505 60 if (headless) {
pavone@505 61 context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (32 / 8));
pavone@505 62 memset(context->framebuf, 0, FRAMEBUF_ENTRIES * (32 / 8));
pavone@505 63 context->evenbuf = malloc(FRAMEBUF_ENTRIES * (32 / 8));
pavone@505 64 memset(context->evenbuf, 0, FRAMEBUF_ENTRIES * (32 / 8));
pavone@505 65 } else {
pavone@505 66 render_alloc_surfaces(context);
pavone@505 67 }
pavone@488 68 context->framebuf = context->oddbuf;
pavone@39 69 context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
pavone@54 70 memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
pavone@20 71 context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
pavone@39 72 context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE;
pavone@26 73 context->sprite_draws = MAX_DRAWS;
pavone@471 74 context->fifo_write = 0;
pavone@471 75 context->fifo_read = -1;
pavone@505 76
pavone@426 77 if (!color_map_init_done) {
pavone@426 78 uint8_t b,g,r;
pavone@426 79 for (uint16_t color = 0; color < (1 << 12); color++) {
pavone@426 80 if (color & FBUF_SHADOW) {
pavone@426 81 b = levels[(color >> 9) & 0x7];
pavone@426 82 g = levels[(color >> 5) & 0x7];
pavone@426 83 r = levels[(color >> 1) & 0x7];
pavone@426 84 } else if(color & FBUF_HILIGHT) {
pavone@426 85 b = levels[((color >> 9) & 0x7) + 7];
pavone@426 86 g = levels[((color >> 5) & 0x7) + 7];
pavone@426 87 r = levels[((color >> 1) & 0x7) + 7];
pavone@426 88 } else {
pavone@426 89 b = levels[(color >> 8) & 0xE];
pavone@426 90 g = levels[(color >> 4) & 0xE];
pavone@426 91 r = levels[color & 0xE];
pavone@426 92 }
pavone@426 93 color_map[color] = render_map_color(r, g, b);
pavone@426 94 }
pavone@426 95 color_map_init_done = 1;
pavone@426 96 }
pavone@437 97 for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++)
pavone@437 98 {
pavone@437 99 uint8_t src = color & DBG_SRC_MASK;
pavone@437 100 if (src > DBG_SRC_S) {
pavone@437 101 context->debugcolors[color] = 0;
pavone@437 102 } else {
pavone@437 103 uint8_t r,g,b;
pavone@437 104 b = debug_base[src][0];
pavone@437 105 g = debug_base[src][1];
pavone@437 106 r = debug_base[src][2];
pavone@437 107 if (color & DBG_PRIORITY)
pavone@437 108 {
pavone@437 109 if (b) {
pavone@437 110 b += 48;
pavone@437 111 }
pavone@437 112 if (g) {
pavone@437 113 g += 48;
pavone@437 114 }
pavone@437 115 if (r) {
pavone@437 116 r += 48;
pavone@437 117 }
pavone@437 118 }
pavone@437 119 if (color & DBG_SHADOW) {
pavone@437 120 b /= 2;
pavone@437 121 g /= 2;
pavone@437 122 r /=2 ;
pavone@437 123 }
pavone@437 124 if (color & DBG_HILIGHT) {
pavone@437 125 if (b) {
pavone@437 126 b += 72;
pavone@437 127 }
pavone@437 128 if (g) {
pavone@437 129 g += 72;
pavone@437 130 }
pavone@437 131 if (r) {
pavone@437 132 r += 72;
pavone@437 133 }
pavone@437 134 }
pavone@437 135 context->debugcolors[color] = render_map_color(r, g, b);
pavone@437 136 }
pavone@437 137 }
pavone@623 138 if (region_pal) {
pavone@623 139 context->flags2 |= FLAG2_REGION_PAL;
pavone@623 140 }
pavone@20 141 }
pavone@20 142
pavone@884 143 void vdp_free(vdp_context *context)
pavone@884 144 {
pavone@884 145 free(context->vdpmem);
pavone@884 146 free(context->linebuf);
pavone@884 147 if (headless) {
pavone@884 148 free(context->oddbuf);
pavone@884 149 free(context->evenbuf);
pavone@884 150 } else {
pavone@884 151 render_free_surfaces(context);
pavone@884 152 }
pavone@884 153 free(context);
pavone@884 154 }
pavone@884 155
pavone@460 156 int is_refresh(vdp_context * context, uint32_t slot)
pavone@460 157 {
pavone@622 158 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@622 159 return slot == 250 || slot == 26 || slot == 59 || slot == 90 || slot == 122 || slot == 154;
pavone@460 160 } else {
pavone@460 161 //TODO: Figure out which slots are refresh when display is off in 32-cell mode
pavone@460 162 //These numbers are guesses based on H40 numbers
pavone@622 163 return slot == 243 || slot == 19 || slot == 51 || slot == 83 || slot == 115;
pavone@460 164 //The numbers below are the refresh slots during active display
pavone@622 165 //return (slot == 29 || slot == 61 || slot == 93 || slot == 125);
pavone@460 166 }
pavone@460 167 }
pavone@460 168
pavone@21 169 void render_sprite_cells(vdp_context * context)
pavone@20 170 {
pavone@21 171 if (context->cur_slot >= context->sprite_draws) {
pavone@21 172 sprite_draw * d = context->sprite_draw_list + context->cur_slot;
pavone@450 173
pavone@20 174 uint16_t dir;
pavone@20 175 int16_t x;
pavone@20 176 if (d->h_flip) {
pavone@20 177 x = d->x_pos + 7;
pavone@20 178 dir = -1;
pavone@20 179 } else {
pavone@20 180 x = d->x_pos;
pavone@20 181 dir = 1;
pavone@20 182 }
pavone@27 183 //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x);
pavone@26 184 context->cur_slot--;
pavone@143 185 for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) {
pavone@27 186 if (x >= 0 && x < 320 && !(context->linebuf[x] & 0xF)) {
pavone@494 187 if (context->linebuf[x] && (context->vdpmem[address] >> 4)) {
pavone@494 188 context->flags2 |= FLAG2_SPRITE_COLLIDE;
pavone@494 189 }
pavone@20 190 context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority;
pavone@20 191 }
pavone@20 192 x += dir;
pavone@27 193 if (x >= 0 && x < 320 && !(context->linebuf[x] & 0xF)) {
pavone@494 194 if (context->linebuf[x] && (context->vdpmem[address] & 0xF)) {
pavone@494 195 context->flags2 |= FLAG2_SPRITE_COLLIDE;
pavone@494 196 }
pavone@20 197 context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority;
pavone@20 198 }
pavone@20 199 x += dir;
pavone@20 200 }
pavone@20 201 }
pavone@20 202 }
pavone@20 203
pavone@322 204 void vdp_print_sprite_table(vdp_context * context)
pavone@322 205 {
pavone@322 206 uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9;
pavone@322 207 uint16_t current_index = 0;
pavone@322 208 uint8_t count = 0;
pavone@322 209 do {
pavone@322 210 uint16_t address = current_index * 8 + sat_address;
pavone@322 211 uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * 8;
pavone@322 212 uint8_t width = (((context->vdpmem[address+2] >> 2) & 0x3) + 1) * 8;
pavone@322 213 int16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & 0x1FF;
pavone@323 214 int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF;
pavone@322 215 uint16_t link = context->vdpmem[address+3] & 0x7F;
pavone@323 216 uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3;
pavone@323 217 uint8_t pri = context->vdpmem[address + 4] >> 7;
pavone@450 218 uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;
pavone@515 219 printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
pavone@322 220 current_index = link;
pavone@322 221 count++;
pavone@322 222 } while (current_index != 0 && count < 80);
pavone@322 223 }
pavone@322 224
pavone@705 225 #define VRAM_READ 0 //0000
pavone@705 226 #define VRAM_WRITE 1 //0001
pavone@705 227 //2 would trigger register write 0010
pavone@705 228 #define CRAM_WRITE 3 //0011
pavone@705 229 #define VSRAM_READ 4 //0100
pavone@705 230 #define VSRAM_WRITE 5//0101
pavone@705 231 //6 would trigger regsiter write 0110
pavone@980 232 //7 is a mystery //0111
pavone@705 233 #define CRAM_READ 8 //1000
pavone@705 234 //9 is also a mystery //1001
pavone@705 235 //A would trigger register write 1010
pavone@705 236 //B is a mystery 1011
pavone@705 237 #define VRAM_READ8 0xC //1100
pavone@705 238 //D is a mystery 1101
pavone@705 239 //E would trigger register write 1110
pavone@705 240 //F is a mystery 1111
pavone@980 241
pavone@980 242 //Possible theory on how bits work
pavone@980 243 //CD0 = Read/Write flag
pavone@980 244 //CD2,(CD1|CD3) = RAM type
pavone@980 245 // 00 = VRAM
pavone@980 246 // 01 = CRAM
pavone@980 247 // 10 = VSRAM
pavone@980 248 // 11 = VRAM8
pavone@980 249 //Would result in
pavone@980 250 // 7 = VRAM8 write
pavone@980 251 // 9 = CRAM write alias
pavone@980 252 // B = CRAM write alias
pavone@980 253 // D = VRAM8 write alias
pavone@980 254 // F = VRAM8 write alais
pavone@980 255
pavone@705 256 #define DMA_START 0x20
pavone@705 257
pavone@705 258 const char * cd_name(uint8_t cd)
pavone@705 259 {
pavone@705 260 switch (cd & 0xF)
pavone@705 261 {
pavone@705 262 case VRAM_READ:
pavone@705 263 return "VRAM read";
pavone@705 264 case VRAM_WRITE:
pavone@705 265 return "VRAM write";
pavone@705 266 case CRAM_WRITE:
pavone@705 267 return "CRAM write";
pavone@705 268 case VSRAM_READ:
pavone@705 269 return "VSRAM read";
pavone@705 270 case VSRAM_WRITE:
pavone@705 271 return "VSRAM write";
pavone@705 272 case VRAM_READ8:
pavone@705 273 return "VRAM read (undocumented 8-bit mode)";
pavone@705 274 default:
pavone@705 275 return "invalid";
pavone@705 276 }
pavone@705 277 }
pavone@705 278
pavone@327 279 void vdp_print_reg_explain(vdp_context * context)
pavone@327 280 {
pavone@327 281 char * hscroll[] = {"full", "7-line", "cell", "line"};
pavone@327 282 printf("**Mode Group**\n"
pavone@327 283 "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n"
pavone@327 284 "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n"
pavone@327 285 "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n"
pavone@327 286 "0C: %.2X | Width: %d, Shadow/Highlight: %s\n",
pavone@757 287 context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", (context->regs[REG_MODE_1] & BIT_PAL_SEL) != 0,
pavone@327 288 context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled",
pavone@450 289 context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled",
pavone@327 290 context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4,
pavone@327 291 context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full",
pavone@327 292 hscroll[context->regs[REG_MODE_3] & 0x3],
pavone@327 293 context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled");
pavone@327 294 printf("\n**Table Group**\n"
pavone@327 295 "02: %.2X | Scroll A Name Table: $%.4X\n"
pavone@327 296 "03: %.2X | Window Name Table: $%.4X\n"
pavone@327 297 "04: %.2X | Scroll B Name Table: $%.4X\n"
pavone@327 298 "05: %.2X | Sprite Attribute Table: $%.4X\n"
pavone@327 299 "0D: %.2X | HScroll Data Table: $%.4X\n",
pavone@327 300 context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0x38) << 10,
pavone@327 301 context->regs[REG_WINDOW], (context->regs[REG_WINDOW] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3C : 0x3E)) << 10,
pavone@327 302 context->regs[REG_SCROLL_B], (context->regs[REG_SCROLL_B] & 0x7) << 13,
pavone@621 303 context->regs[REG_SAT], (context->regs[REG_SAT] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x7E : 0x7F)) << 9,
pavone@621 304 context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x3F) << 10);
pavone@327 305 char * sizes[] = {"32", "64", "invalid", "128"};
pavone@327 306 printf("\n**Misc Group**\n"
pavone@327 307 "07: %.2X | Backdrop Color: $%X\n"
pavone@327 308 "0A: %.2X | H-Int Counter: %u\n"
pavone@327 309 "0F: %.2X | Auto-increment: $%X\n"
pavone@327 310 "10: %.2X | Scroll A/B Size: %sx%s\n",
pavone@505 311 context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR],
pavone@450 312 context->regs[REG_HINT], context->regs[REG_HINT],
pavone@327 313 context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
pavone@327 314 context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
pavone@621 315 char * src_types[] = {"68K", "68K", "Copy", "Fill"};
pavone@621 316 printf("\n**DMA Group**\n"
pavone@621 317 "13: %.2X |\n"
pavone@621 318 "14: %.2X | DMA Length: $%.4X words\n"
pavone@621 319 "15: %.2X |\n"
pavone@621 320 "16: %.2X |\n"
pavone@621 321 "17: %.2X | DMA Source Address: $%.6X, Type: %s\n",
pavone@621 322 context->regs[REG_DMALEN_L],
pavone@621 323 context->regs[REG_DMALEN_H], context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L],
pavone@621 324 context->regs[REG_DMASRC_L],
pavone@621 325 context->regs[REG_DMASRC_M],
pavone@621 326 context->regs[REG_DMASRC_H],
pavone@629 327 context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1,
pavone@621 328 src_types[context->regs[REG_DMASRC_H] >> 6 & 3]);
pavone@438 329 printf("\n**Internal Group**\n"
pavone@438 330 "Address: %X\n"
pavone@705 331 "CD: %X - %s\n"
pavone@647 332 "Pending: %s\n"
pavone@647 333 "VCounter: %d\n"
pavone@647 334 "HCounter: %d\n",
pavone@705 335 context->address, context->cd, cd_name(context->cd), (context->flags & FLAG_PENDING) ? "true" : "false",
pavone@647 336 context->vcounter, context->hslot*2);
pavone@450 337
pavone@450 338 //TODO: Window Group, DMA Group
pavone@327 339 }
pavone@327 340
pavone@20 341 void scan_sprite_table(uint32_t line, vdp_context * context)
pavone@20 342 {
pavone@20 343 if (context->sprite_index && context->slot_counter) {
pavone@20 344 line += 1;
pavone@20 345 line &= 0xFF;
pavone@413 346 uint16_t ymask, ymin;
pavone@413 347 uint8_t height_mult;
pavone@413 348 if (context->double_res) {
pavone@413 349 line *= 2;
pavone@413 350 if (context->framebuf != context->oddbuf) {
pavone@413 351 line++;
pavone@413 352 }
pavone@413 353 ymask = 0x3FF;
pavone@413 354 ymin = 256;
pavone@413 355 height_mult = 16;
pavone@413 356 } else {
pavone@413 357 ymask = 0x1FF;
pavone@413 358 ymin = 128;
pavone@413 359 height_mult = 8;
pavone@413 360 }
pavone@20 361 context->sprite_index &= 0x7F;
pavone@622 362 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@38 363 if (context->sprite_index >= MAX_SPRITES_FRAME) {
pavone@38 364 context->sprite_index = 0;
pavone@38 365 return;
pavone@38 366 }
pavone@38 367 } else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) {
pavone@38 368 context->sprite_index = 0;
pavone@38 369 return;
pavone@38 370 }
pavone@20 371 //TODO: Read from SAT cache rather than from VRAM
pavone@20 372 uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9;
pavone@20 373 uint16_t address = context->sprite_index * 8 + sat_address;
pavone@413 374 line += ymin;
pavone@415 375 uint16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & ymask;
pavone@413 376 uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * height_mult;
pavone@21 377 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
pavone@21 378 if (y <= line && line < (y + height)) {
pavone@27 379 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
pavone@20 380 context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2];
pavone@20 381 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
pavone@413 382 context->sprite_info_list[context->slot_counter].y = y-ymin;
pavone@20 383 }
pavone@20 384 context->sprite_index = context->vdpmem[address+3] & 0x7F;
pavone@20 385 if (context->sprite_index && context->slot_counter)
pavone@20 386 {
pavone@20 387 address = context->sprite_index * 8 + sat_address;
pavone@415 388 y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & ymask;
pavone@413 389 height = ((context->vdpmem[address+2] & 0x3) + 1) * height_mult;
pavone@323 390 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
pavone@21 391 if (y <= line && line < (y + height)) {
pavone@27 392 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
pavone@20 393 context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2];
pavone@20 394 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
pavone@413 395 context->sprite_info_list[context->slot_counter].y = y-ymin;
pavone@20 396 }
pavone@21 397 context->sprite_index = context->vdpmem[address+3] & 0x7F;
pavone@20 398 }
pavone@20 399 }
pavone@20 400 }
pavone@20 401
pavone@20 402 void read_sprite_x(uint32_t line, vdp_context * context)
pavone@20 403 {
pavone@34 404 if (context->cur_slot >= context->slot_counter) {
pavone@34 405 if (context->sprite_draws) {
pavone@34 406 line += 1;
pavone@34 407 line &= 0xFF;
pavone@34 408 //in tiles
pavone@34 409 uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1;
pavone@34 410 //in pixels
pavone@34 411 uint8_t height = ((context->sprite_info_list[context->cur_slot].size & 0x3) + 1) * 8;
pavone@413 412 if (context->double_res) {
pavone@413 413 line *= 2;
pavone@413 414 if (context->framebuf != context->oddbuf) {
pavone@413 415 line++;
pavone@413 416 }
pavone@413 417 height *= 2;
pavone@413 418 }
pavone@34 419 uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
pavone@450 420 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
pavone@34 421 uint8_t pal_priority = (tileinfo >> 9) & 0x70;
pavone@34 422 uint8_t row;
pavone@34 423 if (tileinfo & MAP_BIT_V_FLIP) {
pavone@34 424 row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line;
pavone@34 425 } else {
pavone@34 426 row = line-context->sprite_info_list[context->cur_slot].y;
pavone@34 427 }
pavone@413 428 uint16_t address;
pavone@413 429 if (context->double_res) {
pavone@413 430 address = ((tileinfo & 0x3FF) << 6) + row * 4;
pavone@413 431 } else {
pavone@413 432 address = ((tileinfo & 0x7FF) << 5) + row * 4;
pavone@413 433 }
pavone@323 434 int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF;
pavone@36 435 if (x) {
pavone@36 436 context->flags |= FLAG_CAN_MASK;
pavone@36 437 } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) {
pavone@36 438 context->flags |= FLAG_MASKED;
pavone@36 439 }
pavone@450 440
pavone@36 441 context->flags &= ~FLAG_DOT_OFLOW;
pavone@36 442 int16_t i;
pavone@36 443 if (context->flags & FLAG_MASKED) {
pavone@36 444 for (i=0; i < width && context->sprite_draws; i++) {
pavone@36 445 --context->sprite_draws;
pavone@36 446 context->sprite_draw_list[context->sprite_draws].x_pos = -128;
pavone@34 447 }
pavone@36 448 } else {
pavone@34 449 x -= 128;
pavone@34 450 int16_t base_x = x;
pavone@34 451 int16_t dir;
pavone@34 452 if (tileinfo & MAP_BIT_H_FLIP) {
pavone@34 453 x += (width-1) * 8;
pavone@34 454 dir = -8;
pavone@34 455 } else {
pavone@34 456 dir = 8;
pavone@34 457 }
pavone@34 458 //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);
pavone@35 459 for (i=0; i < width && context->sprite_draws; i++, x += dir) {
pavone@34 460 --context->sprite_draws;
pavone@34 461 context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4;
pavone@34 462 context->sprite_draw_list[context->sprite_draws].x_pos = x;
pavone@34 463 context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority;
pavone@34 464 context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0;
pavone@34 465 }
pavone@34 466 }
pavone@36 467 if (i < width) {
pavone@36 468 context->flags |= FLAG_DOT_OFLOW;
pavone@36 469 }
pavone@36 470 context->cur_slot--;
pavone@20 471 } else {
pavone@34 472 context->flags |= FLAG_DOT_OFLOW;
pavone@20 473 }
pavone@20 474 }
pavone@20 475 }
pavone@20 476
pavone@427 477 void write_cram(vdp_context * context, uint16_t address, uint16_t value)
pavone@427 478 {
pavone@427 479 uint16_t addr = (address/2) & (CRAM_SIZE-1);
pavone@427 480 context->cram[addr] = value;
pavone@427 481 context->colors[addr] = color_map[value & 0xEEE];
pavone@427 482 context->colors[addr + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW];
pavone@427 483 context->colors[addr + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT];
pavone@427 484 }
pavone@427 485
pavone@980 486 #define CRAM_BITS 0xEEE
pavone@980 487 #define VSRAM_BITS 0x7FF
pavone@980 488 #define VSRAM_DIRTY_BITS 0xF800
pavone@980 489
pavone@984 490 void vdp_advance_dma(vdp_context * context)
pavone@984 491 {
pavone@984 492 context->regs[REG_DMASRC_L] += 1;
pavone@984 493 if (!context->regs[REG_DMASRC_L]) {
pavone@984 494 context->regs[REG_DMASRC_M] += 1;
pavone@984 495 }
pavone@984 496 context->address += context->regs[REG_AUTOINC];
pavone@984 497 uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1;
pavone@984 498 context->regs[REG_DMALEN_H] = dma_len >> 8;
pavone@984 499 context->regs[REG_DMALEN_L] = dma_len;
pavone@984 500 if (!dma_len) {
pavone@984 501 context->flags &= ~FLAG_DMA_RUN;
pavone@984 502 context->cd &= 0xF;
pavone@984 503 }
pavone@984 504 }
pavone@984 505
pavone@20 506 void external_slot(vdp_context * context)
pavone@20 507 {
pavone@984 508 if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80 && context->fifo_read < 0) {
pavone@984 509 context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1);
pavone@984 510 fifo_entry * cur = context->fifo + context->fifo_read;
pavone@984 511 cur->cycle = context->cycles;
pavone@984 512 cur->address = context->address;
pavone@984 513 cur->partial = 2;
pavone@984 514 vdp_advance_dma(context);
pavone@984 515 }
pavone@471 516 fifo_entry * start = context->fifo + context->fifo_read;
pavone@471 517 if (context->fifo_read >= 0 && start->cycle <= context->cycles) {
pavone@460 518 switch (start->cd & 0xF)
pavone@460 519 {
pavone@460 520 case VRAM_WRITE:
pavone@460 521 if (start->partial) {
pavone@471 522 //printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16);
pavone@478 523 context->vdpmem[start->address ^ 1] = start->partial == 2 ? start->value >> 8 : start->value;
pavone@460 524 } else {
pavone@471 525 //printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16);
pavone@460 526 context->vdpmem[start->address] = start->value >> 8;
pavone@460 527 start->partial = 1;
pavone@460 528 //skip auto-increment and removal of entry from fifo
pavone@460 529 return;
pavone@460 530 }
pavone@460 531 break;
pavone@460 532 case CRAM_WRITE: {
pavone@460 533 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1));
pavone@479 534 write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value);
pavone@460 535 break;
pavone@460 536 }
pavone@460 537 case VSRAM_WRITE:
pavone@460 538 if (((start->address/2) & 63) < VSRAM_SIZE) {
pavone@718 539 //printf("VSRAM Write: %X to %X @ vcounter: %d, hslot: %d, cycle: %d\n", start->value, context->address, context->vcounter, context->hslot, context->cycles);
pavone@479 540 context->vsram[(start->address/2) & 63] = start->partial == 2 ? context->fifo[context->fifo_write].value : start->value;
pavone@460 541 }
pavone@460 542
pavone@460 543 break;
pavone@460 544 }
pavone@471 545 context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1);
pavone@471 546 if (context->fifo_read == context->fifo_write) {
pavone@983 547 if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
pavone@983 548 context->flags |= FLAG_DMA_RUN;
pavone@983 549 }
pavone@471 550 context->fifo_read = -1;
pavone@460 551 }
pavone@980 552 } else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0xC0) {
pavone@980 553 if (context->flags & FLAG_READ_FETCHED) {
pavone@980 554 context->vdpmem[context->address ^ 1] = context->prefetch;
pavone@980 555
pavone@980 556 //Update DMA state
pavone@984 557 vdp_advance_dma(context);
pavone@980 558
pavone@980 559 context->flags &= ~FLAG_READ_FETCHED;
pavone@980 560 } else {
pavone@980 561 context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1];
pavone@980 562
pavone@980 563 context->flags |= FLAG_READ_FETCHED;
pavone@980 564 }
pavone@980 565 } else if (!(context->cd & 1) && !(context->flags & FLAG_READ_FETCHED)){
pavone@980 566 switch(context->cd & 0xF)
pavone@980 567 {
pavone@980 568 case VRAM_READ:
pavone@980 569 if (context->flags2 & FLAG2_READ_PENDING) {
pavone@980 570 context->prefetch |= context->vdpmem[context->address | 1];
pavone@980 571 context->flags |= FLAG_READ_FETCHED;
pavone@980 572 context->flags2 &= ~FLAG2_READ_PENDING;
pavone@980 573 //Should this happen after the prefetch or after the read?
pavone@980 574 //context->address += context->regs[REG_AUTOINC];
pavone@980 575 } else {
pavone@980 576 context->prefetch = context->vdpmem[context->address & 0xFFFE] << 8;
pavone@980 577 context->flags2 |= FLAG2_READ_PENDING;
pavone@980 578 }
pavone@980 579 break;
pavone@980 580 case VRAM_READ8:
pavone@980 581 context->prefetch = context->vdpmem[context->address ^ 1];
pavone@980 582 context->prefetch |= context->fifo[context->fifo_write].value & 0xFF00;
pavone@980 583 context->flags |= FLAG_READ_FETCHED;
pavone@980 584 //Should this happen after the prefetch or after the read?
pavone@980 585 //context->address += context->regs[REG_AUTOINC];
pavone@980 586 break;
pavone@980 587 case CRAM_READ:
pavone@980 588 context->prefetch = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS;
pavone@980 589 context->prefetch |= context->fifo[context->fifo_write].value & ~CRAM_BITS;
pavone@980 590 context->flags |= FLAG_READ_FETCHED;
pavone@980 591 //Should this happen after the prefetch or after the read?
pavone@980 592 //context->address += context->regs[REG_AUTOINC];
pavone@980 593 break;
pavone@980 594 case VSRAM_READ: {
pavone@980 595 uint16_t address = (context->address /2) & 63;
pavone@980 596 if (address >= VSRAM_SIZE) {
pavone@980 597 address = 0;
pavone@980 598 }
pavone@980 599 context->prefetch = context->vsram[address] & VSRAM_BITS;
pavone@980 600 context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS;
pavone@980 601 context->flags |= FLAG_READ_FETCHED;
pavone@980 602 //Should this happen after the prefetch or after the read?
pavone@980 603 //context->address += context->regs[REG_AUTOINC];
pavone@980 604 break;
pavone@980 605 }
pavone@980 606 }
pavone@460 607 }
pavone@460 608 }
pavone@460 609
pavone@924 610 void run_dma_src(vdp_context * context, int32_t slot)
pavone@460 611 {
pavone@75 612 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode
pavone@75 613 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations
pavone@471 614 if (context->fifo_write == context->fifo_read) {
pavone@460 615 return;
pavone@460 616 }
pavone@478 617 fifo_entry * cur = NULL;
pavone@984 618 if (!(context->regs[REG_DMASRC_H] & 0x80))
pavone@460 619 {
pavone@984 620 //68K -> VDP
pavone@924 621 if (slot == -1 || !is_refresh(context, slot-1)) {
pavone@478 622 cur = context->fifo + context->fifo_write;
pavone@622 623 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
pavone@478 624 cur->address = context->address;
pavone@478 625 cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
pavone@478 626 cur->cd = context->cd;
pavone@478 627 cur->partial = 0;
pavone@478 628 if (context->fifo_read < 0) {
pavone@478 629 context->fifo_read = context->fifo_write;
pavone@478 630 }
pavone@478 631 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
pavone@984 632 vdp_advance_dma(context);
pavone@75 633 }
pavone@54 634 }
pavone@20 635 }
pavone@20 636
pavone@40 637 #define WINDOW_RIGHT 0x80
pavone@40 638 #define WINDOW_DOWN 0x80
pavone@40 639
pavone@25 640 void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_t address, uint16_t hscroll_val, vdp_context * context)
pavone@20 641 {
pavone@417 642 uint16_t window_line_shift, v_offset_mask, vscroll_shift;
pavone@414 643 if (context->double_res) {
pavone@413 644 line *= 2;
pavone@413 645 if (context->framebuf != context->oddbuf) {
pavone@413 646 line++;
pavone@413 647 }
pavone@417 648 window_line_shift = 4;
pavone@417 649 v_offset_mask = 0xF;
pavone@417 650 vscroll_shift = 4;
pavone@417 651 } else {
pavone@417 652 window_line_shift = 3;
pavone@417 653 v_offset_mask = 0x7;
pavone@417 654 vscroll_shift = 3;
pavone@414 655 }
pavone@40 656 if (!vsram_off) {
pavone@40 657 uint16_t left_col, right_col;
pavone@40 658 if (context->regs[REG_WINDOW_H] & WINDOW_RIGHT) {
pavone@920 659 left_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2 + 2;
pavone@41 660 right_col = 42;
pavone@41 661 } else {
pavone@40 662 left_col = 0;
pavone@40 663 right_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2;
pavone@40 664 if (right_col) {
pavone@40 665 right_col += 2;
pavone@40 666 }
pavone@41 667 }
pavone@41 668 uint16_t top_line, bottom_line;
pavone@41 669 if (context->regs[REG_WINDOW_V] & WINDOW_DOWN) {
pavone@417 670 top_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
pavone@417 671 bottom_line = context->double_res ? 481 : 241;
pavone@40 672 } else {
pavone@41 673 top_line = 0;
pavone@417 674 bottom_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
pavone@40 675 }
pavone@41 676 if ((column >= left_col && column < right_col) || (line >= top_line && line < bottom_line)) {
pavone@41 677 uint16_t address = context->regs[REG_WINDOW] << 10;
pavone@41 678 uint16_t line_offset, offset, mask;
pavone@622 679 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@41 680 address &= 0xF000;
pavone@417 681 line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF;
pavone@41 682 mask = 0x7F;
pavone@450 683
pavone@40 684 } else {
pavone@41 685 address &= 0xF800;
pavone@417 686 line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF;
pavone@41 687 mask = 0x3F;
pavone@40 688 }
pavone@417 689 if (context->double_res) {
pavone@417 690 mask <<= 1;
pavone@417 691 mask |= 1;
pavone@417 692 }
pavone@42 693 offset = address + line_offset + (((column - 2) * 2) & mask);
pavone@41 694 context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
pavone@54 695 //printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
pavone@42 696 offset = address + line_offset + (((column - 1) * 2) & mask);
pavone@41 697 context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
pavone@417 698 context->v_offset = (line) & v_offset_mask;
pavone@41 699 context->flags |= FLAG_WINDOW;
pavone@41 700 return;
pavone@40 701 }
pavone@40 702 context->flags &= ~FLAG_WINDOW;
pavone@40 703 }
pavone@20 704 uint16_t vscroll;
pavone@20 705 switch(context->regs[REG_SCROLL] & 0x30)
pavone@20 706 {
pavone@20 707 case 0:
pavone@20 708 vscroll = 0xFF;
pavone@20 709 break;
pavone@20 710 case 0x10:
pavone@20 711 vscroll = 0x1FF;
pavone@20 712 break;
pavone@20 713 case 0x20:
pavone@20 714 //TODO: Verify this behavior
pavone@20 715 vscroll = 0;
pavone@20 716 break;
pavone@20 717 case 0x30:
pavone@20 718 vscroll = 0x3FF;
pavone@20 719 break;
pavone@20 720 }
pavone@414 721 if (context->double_res) {
pavone@413 722 vscroll <<= 1;
pavone@413 723 vscroll |= 1;
pavone@414 724 }
pavone@718 725 //TODO: Further research on vscroll latch behavior and the "first column bug"
pavone@718 726 if (!column) {
pavone@718 727 if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
pavone@718 728 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@718 729 //Based on observed behavior documented by Eke-Eke, I'm guessing the VDP
pavone@718 730 //ends up fetching the last value on the VSRAM bus in the H40 case
pavone@718 731 //getting the last latched value should be close enough for now
pavone@718 732 if (!vsram_off) {
pavone@718 733 context->vscroll_latch[0] = context->vscroll_latch[1];
pavone@718 734 }
pavone@718 735 } else {
pavone@718 736 //supposedly it's always forced to 0 in the H32 case
pavone@718 737 context->vscroll_latch[0] = context->vscroll_latch[1] = 0;
pavone@718 738 }
pavone@718 739 } else {
pavone@718 740 context->vscroll_latch[vsram_off] = context->vsram[vsram_off];
pavone@718 741 }
pavone@718 742 } else if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
pavone@710 743 context->vscroll_latch[vsram_off] = context->vsram[column - 2 + vsram_off];
pavone@710 744 }
pavone@710 745 vscroll &= context->vscroll_latch[vsram_off] + line;
pavone@414 746 context->v_offset = vscroll & v_offset_mask;
pavone@26 747 //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);
pavone@414 748 vscroll >>= vscroll_shift;
pavone@20 749 uint16_t hscroll_mask;
pavone@20 750 uint16_t v_mul;
pavone@20 751 switch(context->regs[REG_SCROLL] & 0x3)
pavone@20 752 {
pavone@20 753 case 0:
pavone@108 754 hscroll_mask = 0x1F;
pavone@20 755 v_mul = 64;
pavone@20 756 break;
pavone@20 757 case 0x1:
pavone@39 758 hscroll_mask = 0x3F;
pavone@20 759 v_mul = 128;
pavone@20 760 break;
pavone@20 761 case 0x2:
pavone@20 762 //TODO: Verify this behavior
pavone@20 763 hscroll_mask = 0;
pavone@20 764 v_mul = 0;
pavone@20 765 break;
pavone@20 766 case 0x3:
pavone@108 767 hscroll_mask = 0x7F;
pavone@20 768 v_mul = 256;
pavone@20 769 break;
pavone@20 770 }
pavone@28 771 uint16_t hscroll, offset;
pavone@28 772 for (int i = 0; i < 2; i++) {
pavone@39 773 hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask;
pavone@39 774 offset = address + ((vscroll * v_mul + hscroll*2) & 0x1FFF);
pavone@39 775 //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);
pavone@28 776 uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
pavone@28 777 if (i) {
pavone@28 778 context->col_2 = col_val;
pavone@28 779 } else {
pavone@28 780 context->col_1 = col_val;
pavone@28 781 }
pavone@28 782 }
pavone@20 783 }
pavone@20 784
pavone@20 785 void read_map_scroll_a(uint16_t column, uint32_t line, vdp_context * context)
pavone@20 786 {
pavone@25 787 read_map_scroll(column, 0, line, (context->regs[REG_SCROLL_A] & 0x38) << 10, context->hscroll_a, context);
pavone@20 788 }
pavone@20 789
pavone@20 790 void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context)
pavone@20 791 {
pavone@25 792 read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context);
pavone@20 793 }
pavone@20 794
pavone@436 795 void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context)
pavone@20 796 {
pavone@413 797 uint16_t address;
pavone@413 798 uint8_t shift, add;
pavone@413 799 if (context->double_res) {
pavone@413 800 address = ((col & 0x3FF) << 6);
pavone@413 801 shift = 1;
pavone@413 802 add = context->framebuf != context->oddbuf ? 1 : 0;
pavone@413 803 } else {
pavone@413 804 address = ((col & 0x7FF) << 5);
pavone@413 805 shift = 0;
pavone@413 806 add = 0;
pavone@413 807 }
pavone@20 808 if (col & MAP_BIT_V_FLIP) {
pavone@414 809 address += 28 - 4 * context->v_offset/*((context->v_offset << shift) + add)*/;
pavone@20 810 } else {
pavone@414 811 address += 4 * context->v_offset/*((context->v_offset << shift) + add)*/;
pavone@20 812 }
pavone@20 813 uint16_t pal_priority = (col >> 9) & 0x70;
pavone@20 814 int32_t dir;
pavone@20 815 if (col & MAP_BIT_H_FLIP) {
pavone@436 816 offset += 7;
pavone@436 817 offset &= SCROLL_BUFFER_MASK;
pavone@20 818 dir = -1;
pavone@20 819 } else {
pavone@20 820 dir = 1;
pavone@20 821 }
pavone@20 822 for (uint32_t i=0; i < 4; i++, address++)
pavone@20 823 {
pavone@436 824 tmp_buf[offset] = pal_priority | (context->vdpmem[address] >> 4);
pavone@436 825 offset += dir;
pavone@436 826 offset &= SCROLL_BUFFER_MASK;
pavone@436 827 tmp_buf[offset] = pal_priority | (context->vdpmem[address] & 0xF);
pavone@436 828 offset += dir;
pavone@436 829 offset &= SCROLL_BUFFER_MASK;
pavone@20 830 }
pavone@20 831 }
pavone@20 832
pavone@20 833 void render_map_1(vdp_context * context)
pavone@20 834 {
pavone@436 835 render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context);
pavone@20 836 }
pavone@20 837
pavone@20 838 void render_map_2(vdp_context * context)
pavone@20 839 {
pavone@436 840 render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context);
pavone@20 841 }
pavone@20 842
pavone@20 843 void render_map_3(vdp_context * context)
pavone@20 844 {
pavone@436 845 render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context);
pavone@20 846 }
pavone@20 847
pavone@20 848 void render_map_output(uint32_t line, int32_t col, vdp_context * context)
pavone@20 849 {
pavone@20 850 if (line >= 240) {
pavone@20 851 return;
pavone@20 852 }
pavone@436 853 render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context);
pavone@719 854 uint32_t *dst;
pavone@436 855 uint8_t *sprite_buf, *plane_a, *plane_b;
pavone@436 856 int plane_a_off, plane_b_off;
pavone@20 857 if (col)
pavone@20 858 {
pavone@20 859 col-=2;
pavone@719 860 dst = context->framebuf;
pavone@719 861 dst += line * 320 + col * 8;
pavone@722 862 if (context->debug < 2) {
pavone@722 863 sprite_buf = context->linebuf + col * 8;
pavone@722 864 uint8_t a_src, src;
pavone@722 865 if (context->flags & FLAG_WINDOW) {
pavone@722 866 plane_a_off = context->buf_a_off;
pavone@722 867 a_src = DBG_SRC_W;
pavone@722 868 } else {
pavone@722 869 plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF);
pavone@722 870 a_src = DBG_SRC_A;
pavone@722 871 }
pavone@722 872 plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
pavone@722 873 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
pavone@450 874
pavone@722 875 if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
pavone@722 876 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
pavone@722 877 plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
pavone@722 878 plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
pavone@748 879 uint8_t pixel = context->regs[REG_BG_COLOR];
pavone@748 880 uint32_t *colors = context->colors;
pavone@722 881 src = DBG_SRC_BG;
pavone@722 882 if (*plane_b & 0xF) {
pavone@722 883 pixel = *plane_b;
pavone@722 884 src = DBG_SRC_B;
pavone@722 885 }
pavone@748 886 uint8_t intensity = *plane_b & BUF_BIT_PRIORITY;
pavone@722 887 if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
pavone@722 888 pixel = *plane_a;
pavone@722 889 src = DBG_SRC_A;
pavone@722 890 }
pavone@748 891 intensity |= *plane_a & BUF_BIT_PRIORITY;
pavone@748 892 if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
pavone@748 893 if ((*sprite_buf & 0x3F) == 0x3E) {
pavone@748 894 intensity += BUF_BIT_PRIORITY;
pavone@748 895 } else if ((*sprite_buf & 0x3F) == 0x3F) {
pavone@748 896 intensity = 0;
pavone@748 897 } else {
pavone@722 898 pixel = *sprite_buf;
pavone@722 899 src = DBG_SRC_S;
pavone@722 900 if ((pixel & 0xF) == 0xE) {
pavone@748 901 intensity = BUF_BIT_PRIORITY;
pavone@748 902 } else {
pavone@748 903 intensity |= pixel & BUF_BIT_PRIORITY;
pavone@722 904 }
pavone@722 905 }
pavone@748 906 }
pavone@748 907 if (!intensity) {
pavone@748 908 src |= DBG_SHADOW;
pavone@437 909 colors += CRAM_SIZE;
pavone@748 910 } else if (intensity == BUF_BIT_PRIORITY*2) {
pavone@748 911 src |= DBG_HILIGHT;
pavone@748 912 colors += CRAM_SIZE*2;
pavone@722 913 }
pavone@884 914
pavone@722 915 uint32_t outpixel;
pavone@722 916 if (context->debug) {
pavone@722 917 outpixel = context->debugcolors[src];
pavone@722 918 } else {
pavone@748 919 outpixel = colors[pixel & 0x3F];
pavone@722 920 }
pavone@722 921 *(dst++) = outpixel;
pavone@722 922 }
pavone@722 923 } else {
pavone@722 924 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
pavone@722 925 plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
pavone@722 926 plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
pavone@722 927 uint8_t pixel = context->regs[REG_BG_COLOR];
pavone@722 928 src = DBG_SRC_BG;
pavone@722 929 if (*plane_b & 0xF) {
pavone@722 930 pixel = *plane_b;
pavone@722 931 src = DBG_SRC_B;
pavone@722 932 }
pavone@722 933 if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
pavone@722 934 pixel = *plane_a;
pavone@722 935 src = DBG_SRC_A;
pavone@722 936 }
pavone@722 937 if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
pavone@230 938 pixel = *sprite_buf;
pavone@437 939 src = DBG_SRC_S;
pavone@230 940 }
pavone@722 941 uint32_t outpixel;
pavone@722 942 if (context->debug) {
pavone@722 943 outpixel = context->debugcolors[src];
pavone@722 944 } else {
pavone@722 945 outpixel = context->colors[pixel & 0x3F];
pavone@722 946 }
pavone@722 947 *(dst++) = outpixel;
pavone@230 948 }
pavone@722 949 }
pavone@722 950 } else if (context->debug == 2) {
pavone@722 951 if (col < 32) {
pavone@722 952 *(dst++) = context->colors[col * 2];
pavone@722 953 *(dst++) = context->colors[col * 2];
pavone@722 954 *(dst++) = context->colors[col * 2];
pavone@722 955 *(dst++) = context->colors[col * 2];
pavone@722 956 *(dst++) = context->colors[col * 2 + 1];
pavone@722 957 *(dst++) = context->colors[col * 2 + 1];
pavone@722 958 *(dst++) = context->colors[col * 2 + 1];
pavone@722 959 *(dst++) = context->colors[col * 2 + 1];
pavone@722 960 *(dst++) = context->colors[col * 2 + 2];
pavone@722 961 *(dst++) = context->colors[col * 2 + 2];
pavone@722 962 *(dst++) = context->colors[col * 2 + 2];
pavone@722 963 *(dst++) = context->colors[col * 2 + 2];
pavone@722 964 *(dst++) = context->colors[col * 2 + 3];
pavone@722 965 *(dst++) = context->colors[col * 2 + 3];
pavone@722 966 *(dst++) = context->colors[col * 2 + 3];
pavone@722 967 *(dst++) = context->colors[col * 2 + 3];
pavone@722 968 } else if (col == 32 || line >= 192) {
pavone@722 969 for (int32_t i = 0; i < 16; i ++) {
pavone@722 970 *(dst++) = 0;
pavone@437 971 }
pavone@722 972 } else {
pavone@722 973 for (int32_t i = 0; i < 16; i ++) {
pavone@722 974 *(dst++) = context->colors[line / 3 + (col - 34) * 0x20];
pavone@722 975 }
pavone@20 976 }
pavone@230 977 } else {
pavone@771 978 uint32_t base = (context->debug - 3) * 0x200;
pavone@771 979 uint32_t cell = base + (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col;
pavone@771 980 uint32_t address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
pavone@722 981 for (int32_t i = 0; i < 4; i ++) {
pavone@722 982 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
pavone@722 983 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
pavone@722 984 address++;
pavone@722 985 }
pavone@722 986 cell++;
pavone@771 987 address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
pavone@722 988 for (int32_t i = 0; i < 4; i ++) {
pavone@722 989 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
pavone@722 990 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
pavone@722 991 address++;
pavone@230 992 }
pavone@20 993 }
pavone@20 994 }
pavone@436 995 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
pavone@436 996 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
pavone@20 997 }
pavone@20 998
pavone@822 999 uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19};
pavone@822 1000
pavone@822 1001 void vdp_advance_line(vdp_context *context)
pavone@822 1002 {
pavone@822 1003 context->vcounter++;
pavone@822 1004 context->vcounter &= 0x1FF;
pavone@822 1005 if (context->flags2 & FLAG2_REGION_PAL) {
pavone@822 1006 if (context->latched_mode & BIT_PAL) {
pavone@822 1007 if (context->vcounter == 0x10B) {
pavone@822 1008 context->vcounter = 0x1D2;
pavone@822 1009 }
pavone@822 1010 } else if (context->vcounter == 0x103){
pavone@822 1011 context->vcounter = 0x1CA;
pavone@822 1012 }
pavone@822 1013 } else if (!(context->latched_mode & BIT_PAL) && context->vcounter == 0xEB) {
pavone@822 1014 context->vcounter = 0x1E5;
pavone@822 1015 }
pavone@884 1016
pavone@822 1017 if (context->vcounter > (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
pavone@822 1018 context->hint_counter = context->regs[REG_HINT];
pavone@822 1019 } else if (context->hint_counter) {
pavone@822 1020 context->hint_counter--;
pavone@822 1021 } else {
pavone@822 1022 context->flags2 |= FLAG2_HINT_PENDING;
pavone@822 1023 context->pending_hint_start = context->cycles;
pavone@822 1024 context->hint_counter = context->regs[REG_HINT];
pavone@822 1025 }
pavone@822 1026 }
pavone@822 1027
pavone@822 1028 #define CHECK_ONLY if (context->cycles >= target_cycles) { return; }
pavone@924 1029 #define CHECK_LIMIT if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } context->hslot++; context->cycles += slot_cycles; CHECK_ONLY
pavone@822 1030
pavone@20 1031 #define COLUMN_RENDER_BLOCK(column, startcyc) \
pavone@20 1032 case startcyc:\
pavone@822 1033 read_map_scroll_a(column, context->vcounter, context);\
pavone@822 1034 CHECK_LIMIT\
pavone@922 1035 case ((startcyc+1)&0xFF):\
pavone@20 1036 external_slot(context);\
pavone@822 1037 CHECK_LIMIT\
pavone@922 1038 case ((startcyc+2)&0xFF):\
pavone@20 1039 render_map_1(context);\
pavone@822 1040 CHECK_LIMIT\
pavone@922 1041 case ((startcyc+3)&0xFF):\
pavone@20 1042 render_map_2(context);\
pavone@822 1043 CHECK_LIMIT\
pavone@922 1044 case ((startcyc+4)&0xFF):\
pavone@822 1045 read_map_scroll_b(column, context->vcounter, context);\
pavone@822 1046 CHECK_LIMIT\
pavone@922 1047 case ((startcyc+5)&0xFF):\
pavone@822 1048 read_sprite_x(context->vcounter, context);\
pavone@822 1049 CHECK_LIMIT\
pavone@922 1050 case ((startcyc+6)&0xFF):\
pavone@20 1051 render_map_3(context);\
pavone@822 1052 CHECK_LIMIT\
pavone@922 1053 case ((startcyc+7)&0xFF):\
pavone@822 1054 render_map_output(context->vcounter, column, context);\
pavone@822 1055 CHECK_LIMIT
pavone@20 1056
pavone@20 1057 #define COLUMN_RENDER_BLOCK_REFRESH(column, startcyc) \
pavone@20 1058 case startcyc:\
pavone@822 1059 read_map_scroll_a(column, context->vcounter, context);\
pavone@822 1060 CHECK_LIMIT\
pavone@20 1061 case (startcyc+1):\
pavone@822 1062 /* refresh, no don't run dma src */\
pavone@822 1063 context->hslot++;\
pavone@822 1064 context->cycles += slot_cycles;\
pavone@822 1065 CHECK_ONLY\
pavone@20 1066 case (startcyc+2):\
pavone@20 1067 render_map_1(context);\
pavone@822 1068 CHECK_LIMIT\
pavone@20 1069 case (startcyc+3):\
pavone@20 1070 render_map_2(context);\
pavone@822 1071 CHECK_LIMIT\
pavone@20 1072 case (startcyc+4):\
pavone@822 1073 read_map_scroll_b(column, context->vcounter, context);\
pavone@822 1074 CHECK_LIMIT\
pavone@20 1075 case (startcyc+5):\
pavone@822 1076 read_sprite_x(context->vcounter, context);\
pavone@822 1077 CHECK_LIMIT\
pavone@20 1078 case (startcyc+6):\
pavone@20 1079 render_map_3(context);\
pavone@822 1080 CHECK_LIMIT\
pavone@20 1081 case (startcyc+7):\
pavone@822 1082 render_map_output(context->vcounter, column, context);\
pavone@822 1083 CHECK_LIMIT
pavone@884 1084
pavone@822 1085 #define SPRITE_RENDER_H40(slot) \
pavone@822 1086 case slot:\
pavone@822 1087 render_sprite_cells( context);\
pavone@822 1088 scan_sprite_table(context->vcounter, context);\
pavone@924 1089 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
pavone@822 1090 if (slot == 182) {\
pavone@822 1091 context->hslot = 229;\
pavone@922 1092 context->cycles += h40_hsync_cycles[0];\
pavone@822 1093 } else {\
pavone@822 1094 context->hslot++;\
pavone@822 1095 if (slot >= HSYNC_SLOT_H40 && slot < HSYNC_END_H40) {\
pavone@822 1096 context->cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];\
pavone@822 1097 } else {\
pavone@822 1098 context->cycles += slot_cycles;\
pavone@822 1099 }\
pavone@822 1100 }\
pavone@822 1101 CHECK_ONLY
pavone@884 1102
pavone@822 1103 #define SPRITE_RENDER_H32(slot) \
pavone@822 1104 case slot:\
pavone@822 1105 render_sprite_cells( context);\
pavone@822 1106 scan_sprite_table(context->vcounter, context);\
pavone@924 1107 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
pavone@822 1108 if (slot == 147) {\
pavone@822 1109 context->hslot = 233;\
pavone@822 1110 } else {\
pavone@822 1111 context->hslot++;\
pavone@822 1112 }\
pavone@822 1113 context->cycles += slot_cycles;\
pavone@822 1114 CHECK_ONLY
pavone@884 1115
pavone@20 1116
pavone@822 1117 void vdp_h40(vdp_context * context, uint32_t target_cycles)
pavone@20 1118 {
pavone@20 1119 uint16_t address;
pavone@20 1120 uint32_t mask;
pavone@822 1121 uint32_t const slot_cycles = MCLKS_SLOT_H40;
pavone@822 1122 switch(context->hslot)
pavone@822 1123 {
pavone@822 1124 for (;;)
pavone@20 1125 {
pavone@922 1126 //sprite attribute table scan starts
pavone@647 1127 case 165:
pavone@822 1128 context->sprite_index = 0x80;
pavone@822 1129 context->slot_counter = MAX_SPRITES_LINE;
pavone@21 1130 render_sprite_cells( context);
pavone@822 1131 scan_sprite_table(context->vcounter, context);
pavone@822 1132 CHECK_LIMIT
pavone@922 1133 SPRITE_RENDER_H40(166)
pavone@922 1134 SPRITE_RENDER_H40(167)
pavone@922 1135 case 168:
pavone@922 1136 external_slot(context);
pavone@922 1137 CHECK_LIMIT
pavone@922 1138 SPRITE_RENDER_H40(169)
pavone@922 1139 SPRITE_RENDER_H40(170)
pavone@922 1140 SPRITE_RENDER_H40(171)
pavone@822 1141 SPRITE_RENDER_H40(172)
pavone@822 1142 SPRITE_RENDER_H40(173)
pavone@822 1143 SPRITE_RENDER_H40(174)
pavone@822 1144 SPRITE_RENDER_H40(175)
pavone@822 1145 SPRITE_RENDER_H40(176)
pavone@822 1146 SPRITE_RENDER_H40(177)
pavone@822 1147 SPRITE_RENDER_H40(178)
pavone@822 1148 SPRITE_RENDER_H40(179)
pavone@822 1149 SPRITE_RENDER_H40(180)
pavone@822 1150 SPRITE_RENDER_H40(181)
pavone@922 1151 //!HSYNC asserted
pavone@822 1152 SPRITE_RENDER_H40(182)
pavone@822 1153 SPRITE_RENDER_H40(229)
pavone@822 1154 SPRITE_RENDER_H40(230)
pavone@822 1155 SPRITE_RENDER_H40(231)
pavone@822 1156 SPRITE_RENDER_H40(232)
pavone@822 1157 SPRITE_RENDER_H40(233)
pavone@822 1158 SPRITE_RENDER_H40(234)
pavone@822 1159 SPRITE_RENDER_H40(235)
pavone@822 1160 SPRITE_RENDER_H40(236)
pavone@822 1161 SPRITE_RENDER_H40(237)
pavone@822 1162 SPRITE_RENDER_H40(238)
pavone@822 1163 SPRITE_RENDER_H40(239)
pavone@822 1164 SPRITE_RENDER_H40(240)
pavone@822 1165 SPRITE_RENDER_H40(241)
pavone@922 1166 case 242:
pavone@20 1167 address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
pavone@20 1168 mask = 0;
pavone@20 1169 if (context->regs[REG_MODE_3] & 0x2) {
pavone@20 1170 mask |= 0xF8;
pavone@20 1171 }
pavone@20 1172 if (context->regs[REG_MODE_3] & 0x1) {
pavone@20 1173 mask |= 0x7;
pavone@20 1174 }
pavone@822 1175 address += (context->vcounter & mask) * 4;
pavone@20 1176 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
pavone@20 1177 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
pavone@822 1178 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
pavone@924 1179 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); }
pavone@822 1180 context->hslot++;
pavone@822 1181 context->cycles += h40_hsync_cycles[14];
pavone@822 1182 CHECK_ONLY
pavone@20 1183 //!HSYNC high
pavone@922 1184 SPRITE_RENDER_H40(243)
pavone@922 1185 SPRITE_RENDER_H40(244)
pavone@922 1186 SPRITE_RENDER_H40(245)
pavone@922 1187 SPRITE_RENDER_H40(246)
pavone@922 1188 case 247:
pavone@822 1189 read_map_scroll_a(0, context->vcounter, context);
pavone@822 1190 CHECK_LIMIT
pavone@922 1191 SPRITE_RENDER_H40(248)
pavone@922 1192 case 249:
pavone@20 1193 render_map_1(context);
pavone@822 1194 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@922 1195 CHECK_LIMIT
pavone@922 1196 case 250:
pavone@20 1197 render_map_2(context);
pavone@822 1198 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@822 1199 CHECK_LIMIT
pavone@922 1200 case 251:
pavone@822 1201 read_map_scroll_b(0, context->vcounter, context);
pavone@822 1202 CHECK_LIMIT
pavone@922 1203 SPRITE_RENDER_H40(252)
pavone@922 1204 case 253:
pavone@20 1205 render_map_3(context);
pavone@822 1206 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@822 1207 CHECK_LIMIT
pavone@922 1208 case 254:
pavone@822 1209 if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
pavone@822 1210 context->flags2 |= FLAG2_VINT_PENDING;
pavone@822 1211 context->pending_vint_start = context->cycles;
pavone@822 1212 }
pavone@822 1213 render_map_output(context->vcounter, 0, context);
pavone@822 1214 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@20 1215 //reverse context slot counter so it counts the number of sprite slots
pavone@20 1216 //filled rather than the number of available slots
pavone@21 1217 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
pavone@21 1218 context->cur_slot = MAX_SPRITES_LINE-1;
pavone@20 1219 context->sprite_draws = MAX_DRAWS;
pavone@36 1220 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
pavone@822 1221 CHECK_LIMIT
pavone@922 1222 COLUMN_RENDER_BLOCK(2, 255)
pavone@922 1223 COLUMN_RENDER_BLOCK(4, 7)
pavone@922 1224 COLUMN_RENDER_BLOCK(6, 15)
pavone@922 1225 COLUMN_RENDER_BLOCK_REFRESH(8, 23)
pavone@922 1226 COLUMN_RENDER_BLOCK(10, 31)
pavone@922 1227 COLUMN_RENDER_BLOCK(12, 39)
pavone@922 1228 COLUMN_RENDER_BLOCK(14, 47)
pavone@922 1229 COLUMN_RENDER_BLOCK_REFRESH(16, 55)
pavone@922 1230 COLUMN_RENDER_BLOCK(18, 63)
pavone@922 1231 COLUMN_RENDER_BLOCK(20, 71)
pavone@922 1232 COLUMN_RENDER_BLOCK(22, 79)
pavone@922 1233 COLUMN_RENDER_BLOCK_REFRESH(24, 87)
pavone@922 1234 COLUMN_RENDER_BLOCK(26, 95)
pavone@922 1235 COLUMN_RENDER_BLOCK(28, 103)
pavone@922 1236 COLUMN_RENDER_BLOCK(30, 111)
pavone@922 1237 COLUMN_RENDER_BLOCK_REFRESH(32, 119)
pavone@922 1238 COLUMN_RENDER_BLOCK(34, 127)
pavone@922 1239 COLUMN_RENDER_BLOCK(36, 135)
pavone@922 1240 COLUMN_RENDER_BLOCK(38, 143)
pavone@922 1241 COLUMN_RENDER_BLOCK_REFRESH(40, 151)
pavone@922 1242 case 159:
pavone@922 1243 external_slot(context);
pavone@922 1244 CHECK_LIMIT
pavone@922 1245 case 160:
pavone@922 1246 external_slot(context);
pavone@922 1247 CHECK_LIMIT
pavone@922 1248 //sprite render to line buffer starts
pavone@922 1249 case 161:
pavone@922 1250 context->cur_slot = MAX_DRAWS-1;
pavone@922 1251 memset(context->linebuf, 0, LINEBUF_SIZE);
pavone@922 1252 render_sprite_cells(context);
pavone@922 1253 CHECK_LIMIT
pavone@922 1254 case 162:
pavone@922 1255 render_sprite_cells(context);
pavone@922 1256 CHECK_LIMIT
pavone@922 1257 case 163:
pavone@922 1258 render_sprite_cells(context);
pavone@922 1259 CHECK_LIMIT
pavone@922 1260 case 164:
pavone@922 1261 render_sprite_cells(context);
pavone@922 1262 vdp_advance_line(context);
pavone@922 1263 if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
pavone@922 1264 context->hslot++;
pavone@922 1265 context->cycles += slot_cycles;
pavone@922 1266 return;
pavone@922 1267 }
pavone@922 1268 CHECK_LIMIT
pavone@20 1269 }
pavone@822 1270 default:
pavone@822 1271 context->hslot++;
pavone@822 1272 context->cycles += slot_cycles;
pavone@822 1273 return;
pavone@822 1274 }
pavone@20 1275 }
pavone@20 1276
pavone@822 1277 void vdp_h32(vdp_context * context, uint32_t target_cycles)
pavone@20 1278 {
pavone@37 1279 uint16_t address;
pavone@37 1280 uint32_t mask;
pavone@822 1281 uint32_t const slot_cycles = MCLKS_SLOT_H32;
pavone@822 1282 switch(context->hslot)
pavone@822 1283 {
pavone@822 1284 for (;;)
pavone@20 1285 {
pavone@923 1286 //sprite attribute table scan starts
pavone@647 1287 case 132:
pavone@822 1288 context->sprite_index = 0x80;
pavone@822 1289 context->slot_counter = MAX_SPRITES_LINE_H32;
pavone@37 1290 render_sprite_cells( context);
pavone@822 1291 scan_sprite_table(context->vcounter, context);
pavone@822 1292 CHECK_LIMIT
pavone@923 1293 SPRITE_RENDER_H32(133)
pavone@923 1294 SPRITE_RENDER_H32(134)
pavone@923 1295 SPRITE_RENDER_H32(135)
pavone@923 1296 SPRITE_RENDER_H32(136)
pavone@923 1297 SPRITE_RENDER_H32(137)
pavone@923 1298 SPRITE_RENDER_H32(138)
pavone@822 1299 SPRITE_RENDER_H32(139)
pavone@822 1300 SPRITE_RENDER_H32(140)
pavone@822 1301 SPRITE_RENDER_H32(141)
pavone@923 1302 case 142:
pavone@923 1303 external_slot(context);
pavone@923 1304 CHECK_LIMIT
pavone@822 1305 SPRITE_RENDER_H32(143)
pavone@822 1306 SPRITE_RENDER_H32(144)
pavone@822 1307 SPRITE_RENDER_H32(145)
pavone@822 1308 SPRITE_RENDER_H32(146)
pavone@822 1309 SPRITE_RENDER_H32(147)
pavone@923 1310 //HSYNC start
pavone@923 1311 SPRITE_RENDER_H32(233)
pavone@822 1312 SPRITE_RENDER_H32(234)
pavone@822 1313 SPRITE_RENDER_H32(235)
pavone@822 1314 SPRITE_RENDER_H32(236)
pavone@822 1315 SPRITE_RENDER_H32(237)
pavone@822 1316 SPRITE_RENDER_H32(238)
pavone@822 1317 SPRITE_RENDER_H32(239)
pavone@923 1318 case 240:
pavone@37 1319 external_slot(context);
pavone@822 1320 CHECK_LIMIT
pavone@923 1321 case 241:
pavone@37 1322 address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
pavone@37 1323 mask = 0;
pavone@37 1324 if (context->regs[REG_MODE_3] & 0x2) {
pavone@37 1325 mask |= 0xF8;
pavone@37 1326 }
pavone@37 1327 if (context->regs[REG_MODE_3] & 0x1) {
pavone@37 1328 mask |= 0x7;
pavone@37 1329 }
pavone@822 1330 address += (context->vcounter & mask) * 4;
pavone@37 1331 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
pavone@37 1332 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
pavone@822 1333 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
pavone@822 1334 CHECK_LIMIT
pavone@923 1335 SPRITE_RENDER_H32(242)
pavone@923 1336 SPRITE_RENDER_H32(243)
pavone@923 1337 SPRITE_RENDER_H32(244)
pavone@923 1338 SPRITE_RENDER_H32(245)
pavone@37 1339 //!HSYNC high
pavone@923 1340 case 246:
pavone@822 1341 read_map_scroll_a(0, context->vcounter, context);
pavone@822 1342 CHECK_LIMIT
pavone@923 1343 SPRITE_RENDER_H32(247)
pavone@923 1344 case 248:
pavone@37 1345 render_map_1(context);
pavone@822 1346 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@822 1347 CHECK_LIMIT
pavone@923 1348 case 249:
pavone@37 1349 render_map_2(context);
pavone@822 1350 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@923 1351 CHECK_LIMIT
pavone@923 1352 case 250:
pavone@822 1353 read_map_scroll_b(0, context->vcounter, context);
pavone@822 1354 CHECK_LIMIT
pavone@923 1355 case 251:
pavone@37 1356 render_sprite_cells(context);
pavone@822 1357 scan_sprite_table(context->vcounter, context);
pavone@822 1358 CHECK_LIMIT
pavone@923 1359 case 252:
pavone@37 1360 render_map_3(context);
pavone@822 1361 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@822 1362 CHECK_LIMIT
pavone@923 1363 case 253:
pavone@822 1364 render_map_output(context->vcounter, 0, context);
pavone@822 1365 scan_sprite_table(context->vcounter, context);//Just a guess
pavone@37 1366 //reverse context slot counter so it counts the number of sprite slots
pavone@37 1367 //filled rather than the number of available slots
pavone@37 1368 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
pavone@37 1369 context->cur_slot = MAX_SPRITES_LINE_H32-1;
pavone@37 1370 context->sprite_draws = MAX_DRAWS_H32;
pavone@37 1371 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
pavone@822 1372 CHECK_LIMIT
pavone@923 1373 COLUMN_RENDER_BLOCK(2, 254)
pavone@923 1374 COLUMN_RENDER_BLOCK(4, 6)
pavone@923 1375 COLUMN_RENDER_BLOCK(6, 14)
pavone@923 1376 COLUMN_RENDER_BLOCK_REFRESH(8, 22)
pavone@923 1377 COLUMN_RENDER_BLOCK(10, 30)
pavone@923 1378 COLUMN_RENDER_BLOCK(12, 38)
pavone@923 1379 COLUMN_RENDER_BLOCK(14, 46)
pavone@923 1380 COLUMN_RENDER_BLOCK_REFRESH(16, 54)
pavone@923 1381 COLUMN_RENDER_BLOCK(18, 62)
pavone@923 1382 COLUMN_RENDER_BLOCK(20, 70)
pavone@923 1383 COLUMN_RENDER_BLOCK(22, 78)
pavone@923 1384 COLUMN_RENDER_BLOCK_REFRESH(24, 86)
pavone@923 1385 COLUMN_RENDER_BLOCK(26, 94)
pavone@923 1386 COLUMN_RENDER_BLOCK(28, 102)
pavone@923 1387 COLUMN_RENDER_BLOCK(30, 110)
pavone@923 1388 COLUMN_RENDER_BLOCK_REFRESH(32, 118)
pavone@923 1389 case 126:
pavone@923 1390 external_slot(context);
pavone@923 1391 CHECK_LIMIT
pavone@923 1392 case 127:
pavone@923 1393 external_slot(context);
pavone@923 1394 CHECK_LIMIT
pavone@923 1395 //sprite render to line buffer starts
pavone@923 1396 case 128:
pavone@923 1397 context->cur_slot = MAX_DRAWS_H32-1;
pavone@923 1398 memset(context->linebuf, 0, LINEBUF_SIZE);
pavone@923 1399 render_sprite_cells(context);
pavone@923 1400 CHECK_LIMIT
pavone@923 1401 case 129:
pavone@923 1402 render_sprite_cells(context);
pavone@923 1403 CHECK_LIMIT
pavone@923 1404 case 130:
pavone@923 1405 render_sprite_cells(context);
pavone@923 1406 CHECK_LIMIT
pavone@923 1407 case 131:
pavone@923 1408 render_sprite_cells(context);
pavone@923 1409 vdp_advance_line(context);
pavone@923 1410 if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
pavone@923 1411 context->hslot++;
pavone@923 1412 context->cycles += slot_cycles;
pavone@923 1413 return;
pavone@923 1414 }
pavone@923 1415 CHECK_LIMIT
pavone@20 1416 }
pavone@822 1417 default:
pavone@822 1418 context->hslot++;
pavone@822 1419 context->cycles += MCLKS_SLOT_H32;
pavone@503 1420 }
pavone@503 1421 }
pavone@503 1422
pavone@20 1423 void latch_mode(vdp_context * context)
pavone@20 1424 {
pavone@622 1425 context->latched_mode = context->regs[REG_MODE_2] & BIT_PAL;
pavone@20 1426 }
pavone@20 1427
pavone@330 1428 void check_render_bg(vdp_context * context, int32_t line, uint32_t slot)
pavone@54 1429 {
pavone@622 1430 int starti = -1;
pavone@622 1431 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@622 1432 if (slot >= 12 && slot < 172) {
pavone@622 1433 uint32_t x = (slot-12)*2;
pavone@622 1434 starti = line * 320 + x;
pavone@622 1435 }
pavone@622 1436 } else {
pavone@622 1437 if (slot >= 11 && slot < 139) {
pavone@622 1438 uint32_t x = (slot-11)*2;
pavone@622 1439 starti = line * 320 + x;
pavone@622 1440 }
pavone@622 1441 }
pavone@622 1442 if (starti >= 0) {
pavone@719 1443 uint32_t color = context->colors[context->regs[REG_BG_COLOR]];
pavone@719 1444 uint32_t * start = context->framebuf;
pavone@719 1445 start += starti;
pavone@719 1446 for (int i = 0; i < 2; i++) {
pavone@719 1447 *(start++) = color;
pavone@54 1448 }
pavone@54 1449 }
pavone@54 1450 }
pavone@54 1451
pavone@717 1452
pavone@20 1453 void vdp_run_context(vdp_context * context, uint32_t target_cycles)
pavone@20 1454 {
pavone@20 1455 while(context->cycles < target_cycles)
pavone@20 1456 {
pavone@822 1457 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
pavone@822 1458 //line 0x1FF is basically active even though it's not displayed
pavone@822 1459 uint8_t active_slot = context->vcounter < inactive_start || context->vcounter == 0x1FF;
pavone@822 1460 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40;
pavone@822 1461 if (context->regs[REG_MODE_2] & DISPLAY_ENABLE && active_slot) {
pavone@822 1462 if (is_h40) {
pavone@822 1463 vdp_h40(context, target_cycles);
pavone@822 1464 } else {
pavone@822 1465 vdp_h32(context, target_cycles);
pavone@647 1466 }
pavone@647 1467 } else {
pavone@622 1468 if (is_h40) {
pavone@922 1469 if (context->hslot == 161) {
pavone@822 1470 context->cur_slot = MAX_DRAWS-1;
pavone@822 1471 memset(context->linebuf, 0, LINEBUF_SIZE);
pavone@922 1472 } else if (context->hslot == 165) {
pavone@822 1473 context->sprite_index = 0x80;
pavone@822 1474 context->slot_counter = MAX_SPRITES_LINE;
pavone@503 1475 }
pavone@20 1476 } else {
pavone@923 1477 if (context->hslot == 128) {
pavone@822 1478 context->cur_slot = MAX_DRAWS_H32-1;
pavone@822 1479 memset(context->linebuf, 0, LINEBUF_SIZE);
pavone@923 1480 } else if (context->hslot == 132) {
pavone@822 1481 context->sprite_index = 0x80;
pavone@822 1482 context->slot_counter = MAX_SPRITES_LINE_H32;
pavone@822 1483 }
pavone@20 1484 }
pavone@822 1485 if(context->vcounter == inactive_start) {
pavone@822 1486 uint32_t intslot = context->regs[REG_MODE_4] & BIT_H40 ? VINT_SLOT_H40 : VINT_SLOT_H32;
pavone@822 1487 if (context->hslot == intslot) {
pavone@822 1488 context->flags2 |= FLAG2_VINT_PENDING;
pavone@822 1489 context->pending_vint_start = context->cycles;
pavone@822 1490 }
pavone@822 1491 }
pavone@822 1492 uint32_t inccycles;
pavone@822 1493 if (is_h40) {
pavone@922 1494 if (context->hslot == 182) {
pavone@922 1495 inccycles = h40_hsync_cycles[0];
pavone@922 1496 } else if (context->hslot < HSYNC_SLOT_H40 || context->hslot >= HSYNC_END_H40) {
pavone@822 1497 inccycles = MCLKS_SLOT_H40;
pavone@822 1498 } else {
pavone@822 1499 inccycles = h40_hsync_cycles[context->hslot-HSYNC_SLOT_H40];
pavone@822 1500 }
pavone@822 1501 } else {
pavone@822 1502 inccycles = MCLKS_SLOT_H32;
pavone@822 1503 }
pavone@822 1504 if (!is_refresh(context, context->hslot)) {
pavone@54 1505 external_slot(context);
pavone@54 1506 }
pavone@822 1507 if (context->vcounter < inactive_start) {
pavone@822 1508 check_render_bg(context, context->vcounter, context->hslot);
pavone@54 1509 }
pavone@822 1510 if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) {
pavone@822 1511 run_dma_src(context, context->hslot);
pavone@822 1512 }
pavone@822 1513 context->cycles += inccycles;
pavone@622 1514 context->hslot++;
pavone@622 1515 if (is_h40) {
pavone@647 1516 if (context->hslot == LINE_CHANGE_H40) {
pavone@717 1517 vdp_advance_line(context);
pavone@699 1518 if (context->vcounter == (inactive_start + 8)) {
pavone@699 1519 context->frame++;
pavone@699 1520 }
pavone@622 1521 } else if (context->hslot == 183) {
pavone@622 1522 context->hslot = 229;
pavone@622 1523 }
pavone@622 1524 } else {
pavone@647 1525 if (context->hslot == LINE_CHANGE_H32) {
pavone@717 1526 vdp_advance_line(context);
pavone@699 1527 if (context->vcounter == (inactive_start + 8)) {
pavone@699 1528 context->frame++;
pavone@699 1529 }
pavone@622 1530 } else if (context->hslot == 148) {
pavone@622 1531 context->hslot = 233;
pavone@622 1532 }
pavone@622 1533 }
pavone@622 1534 }
pavone@20 1535 }
pavone@20 1536 }
pavone@20 1537
pavone@20 1538 uint32_t vdp_run_to_vblank(vdp_context * context)
pavone@20 1539 {
pavone@622 1540 uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_INACTIVE_START : NTSC_INACTIVE_START) * MCLKS_LINE;
pavone@20 1541 vdp_run_context(context, target_cycles);
pavone@20 1542 return context->cycles;
pavone@20 1543 }
pavone@20 1544
pavone@75 1545 void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles)
pavone@75 1546 {
pavone@75 1547 for(;;) {
pavone@75 1548 uint32_t dmalen = (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L];
pavone@75 1549 if (!dmalen) {
pavone@75 1550 dmalen = 0x10000;
pavone@75 1551 }
pavone@622 1552 uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20);
pavone@75 1553 if ((context->regs[REG_DMASRC_H] & 0xC0) == 0xC0 || (context->cd & 0xF) == VRAM_WRITE) {
pavone@75 1554 //DMA copies take twice as long to complete since they require a read and a write
pavone@75 1555 //DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word
pavone@75 1556 min_dma_complete *= 2;
pavone@75 1557 }
pavone@75 1558 min_dma_complete += context->cycles;
pavone@75 1559 if (target_cycles < min_dma_complete) {
pavone@75 1560 vdp_run_context(context, target_cycles);
pavone@75 1561 return;
pavone@75 1562 } else {
pavone@75 1563 vdp_run_context(context, min_dma_complete);
pavone@75 1564 if (!(context->flags & FLAG_DMA_RUN)) {
pavone@75 1565 return;
pavone@75 1566 }
pavone@75 1567 }
pavone@75 1568 }
pavone@75 1569 }
pavone@75 1570
pavone@75 1571 int vdp_control_port_write(vdp_context * context, uint16_t value)
pavone@54 1572 {
pavone@471 1573 //printf("control port write: %X at %d\n", value, context->cycles);
pavone@149 1574 if (context->flags & FLAG_DMA_RUN) {
pavone@149 1575 return -1;
pavone@149 1576 }
pavone@54 1577 if (context->flags & FLAG_PENDING) {
pavone@54 1578 context->address = (context->address & 0x3FFF) | (value << 14);
pavone@983 1579 //It seems like the DMA enable bit doesn't so much enable DMA so much
pavone@983 1580 //as it enables changing CD5 from control port writes
pavone@983 1581 uint8_t preserve = (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) ? 0x3 : 0x23;
pavone@983 1582 context->cd = (context->cd & preserve) | ((value >> 2) & ~preserve & 0xFF);
pavone@75 1583 context->flags &= ~FLAG_PENDING;
pavone@980 1584 //Should these be taken care of here or after the first write?
pavone@980 1585 context->flags &= ~FLAG_READ_FETCHED;
pavone@980 1586 context->flags2 &= ~FLAG2_READ_PENDING;
pavone@453 1587 //printf("New Address: %X, New CD: %X\n", context->address, context->cd);
pavone@983 1588 if (context->cd & 0x20) {
pavone@327 1589 //
pavone@75 1590 if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
pavone@75 1591 //DMA copy or 68K -> VDP, transfer starts immediately
pavone@75 1592 context->flags |= FLAG_DMA_RUN;
pavone@131 1593 context->dma_cd = context->cd;
pavone@717 1594 //printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot);
pavone@75 1595 if (!(context->regs[REG_DMASRC_H] & 0x80)) {
pavone@327 1596 //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]);
pavone@75 1597 return 1;
pavone@327 1598 } else {
pavone@327 1599 //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
pavone@75 1600 }
pavone@327 1601 } else {
pavone@453 1602 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd);
pavone@75 1603 }
pavone@63 1604 }
pavone@54 1605 } else {
pavone@54 1606 if ((value & 0xC000) == 0x8000) {
pavone@54 1607 //Register write
pavone@54 1608 uint8_t reg = (value >> 8) & 0x1F;
pavone@475 1609 if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) {
pavone@453 1610 //printf("register %d set to %X\n", reg, value & 0xFF);
pavone@480 1611 if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
pavone@480 1612 context->hv_latch = vdp_hv_counter_read(context);
pavone@480 1613 }
pavone@505 1614 if (reg == REG_BG_COLOR) {
pavone@505 1615 value &= 0x3F;
pavone@505 1616 }
pavone@718 1617 /*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) {
pavone@717 1618 printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame);
pavone@718 1619 }*/
pavone@54 1620 context->regs[reg] = value;
pavone@413 1621 if (reg == REG_MODE_4) {
pavone@413 1622 context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
pavone@415 1623 if (!context->double_res) {
pavone@415 1624 context->framebuf = context->oddbuf;
pavone@415 1625 }
pavone@983 1626 }
pavone@476 1627 context->cd &= 0x3C;
pavone@54 1628 }
pavone@54 1629 } else {
pavone@54 1630 context->flags |= FLAG_PENDING;
pavone@54 1631 context->address = (context->address &0xC000) | (value & 0x3FFF);
pavone@54 1632 context->cd = (context->cd &0x3C) | (value >> 14);
pavone@980 1633 //Should these be taken care of here or after the second write?
pavone@980 1634 //context->flags &= ~FLAG_READ_FETCHED;
pavone@980 1635 //context->flags2 &= ~FLAG2_READ_PENDING;
pavone@54 1636 }
pavone@54 1637 }
pavone@75 1638 return 0;
pavone@54 1639 }
pavone@54 1640
pavone@149 1641 int vdp_data_port_write(vdp_context * context, uint16_t value)
pavone@54 1642 {
pavone@471 1643 //printf("data port write: %X at %d\n", value, context->cycles);
pavone@460 1644 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
pavone@149 1645 return -1;
pavone@149 1646 }
pavone@980 1647 if (context->flags & FLAG_PENDING) {
pavone@980 1648 context->flags &= ~FLAG_PENDING;
pavone@980 1649 //Should these be cleared here?
pavone@980 1650 context->flags &= ~FLAG_READ_FETCHED;
pavone@980 1651 context->flags2 &= ~FLAG2_READ_PENDING;
pavone@980 1652 }
pavone@109 1653 /*if (context->fifo_cur == context->fifo_end) {
pavone@54 1654 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
pavone@109 1655 }*/
pavone@460 1656 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
pavone@460 1657 context->flags &= ~FLAG_DMA_RUN;
pavone@460 1658 }
pavone@471 1659 while (context->fifo_write == context->fifo_read) {
pavone@622 1660 vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
pavone@54 1661 }
pavone@471 1662 fifo_entry * cur = context->fifo + context->fifo_write;
pavone@622 1663 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
pavone@471 1664 cur->address = context->address;
pavone@471 1665 cur->value = value;
pavone@471 1666 cur->cd = context->cd;
pavone@471 1667 cur->partial = 0;
pavone@471 1668 if (context->fifo_read < 0) {
pavone@471 1669 context->fifo_read = context->fifo_write;
pavone@471 1670 }
pavone@471 1671 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
pavone@138 1672 context->address += context->regs[REG_AUTOINC];
pavone@149 1673 return 0;
pavone@54 1674 }
pavone@54 1675
pavone@470 1676 void vdp_test_port_write(vdp_context * context, uint16_t value)
pavone@470 1677 {
pavone@470 1678 //TODO: implement test register
pavone@470 1679 }
pavone@470 1680
pavone@54 1681 uint16_t vdp_control_port_read(vdp_context * context)
pavone@54 1682 {
pavone@54 1683 context->flags &= ~FLAG_PENDING;
pavone@980 1684 //Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch
pavone@981 1685 uint16_t value = get_open_bus_value() & 0xFC00;
pavone@471 1686 if (context->fifo_read < 0) {
pavone@54 1687 value |= 0x200;
pavone@54 1688 }
pavone@471 1689 if (context->fifo_read == context->fifo_write) {
pavone@54 1690 value |= 0x100;
pavone@54 1691 }
pavone@317 1692 if (context->flags2 & FLAG2_VINT_PENDING) {
pavone@413 1693 value |= 0x80;
pavone@413 1694 }
pavone@494 1695 if (context->flags & FLAG_DOT_OFLOW) {
pavone@494 1696 value |= 0x40;
pavone@494 1697 }
pavone@494 1698 if (context->flags2 & FLAG2_SPRITE_COLLIDE) {
pavone@494 1699 value |= 0x20;
pavone@494 1700 //TODO: Test when this is actually cleared
pavone@494 1701 context->flags2 &= ~FLAG2_SPRITE_COLLIDE;
pavone@494 1702 }
pavone@413 1703 if ((context->regs[REG_MODE_4] & BIT_INTERLACE) && context->framebuf == context->oddbuf) {
pavone@413 1704 value |= 0x10;
pavone@317 1705 }
pavone@622 1706 uint32_t line= context->vcounter;
pavone@622 1707 uint32_t slot = context->hslot;
pavone@717 1708 uint32_t inactive_start = (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START);
pavone@921 1709 if ((line >= inactive_start && line < 0x1FF) || !(context->regs[REG_MODE_2] & BIT_DISP_EN)) {
pavone@318 1710 value |= 0x8;
pavone@318 1711 }
pavone@622 1712 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@622 1713 if (slot < HBLANK_END_H40 || slot > HBLANK_START_H40) {
pavone@622 1714 value |= 0x4;
pavone@622 1715 }
pavone@622 1716 } else {
pavone@622 1717 if (slot < HBLANK_END_H32 || slot > HBLANK_START_H32) {
pavone@622 1718 value |= 0x4;
pavone@622 1719 }
pavone@318 1720 }
pavone@983 1721 if (context->cd & 0x20) {
pavone@141 1722 value |= 0x2;
pavone@75 1723 }
pavone@714 1724 if (context->flags2 & FLAG2_REGION_PAL) {
pavone@317 1725 value |= 0x1;
pavone@317 1726 }
pavone@459 1727 //printf("status read at cycle %d returned %X\n", context->cycles, value);
pavone@54 1728 return value;
pavone@54 1729 }
pavone@54 1730
pavone@54 1731 uint16_t vdp_data_port_read(vdp_context * context)
pavone@54 1732 {
pavone@980 1733 if (context->flags & FLAG_PENDING) {
pavone@980 1734 context->flags &= ~FLAG_PENDING;
pavone@980 1735 //Should these be cleared here?
pavone@980 1736 context->flags &= ~FLAG_READ_FETCHED;
pavone@980 1737 context->flags2 &= ~FLAG2_READ_PENDING;
pavone@980 1738 }
pavone@138 1739 if (context->cd & 1) {
pavone@54 1740 return 0;
pavone@54 1741 }
pavone@980 1742 while (!(context->flags & FLAG_READ_FETCHED)) {
pavone@622 1743 vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
pavone@54 1744 }
pavone@980 1745 context->flags &= ~FLAG_READ_FETCHED;
pavone@980 1746 //Should this happen after the prefetch or after the read?
pavone@54 1747 context->address += context->regs[REG_AUTOINC];
pavone@980 1748 return context->prefetch;
pavone@54 1749 }
pavone@54 1750
pavone@137 1751 uint16_t vdp_hv_counter_read(vdp_context * context)
pavone@137 1752 {
pavone@480 1753 if (context->regs[REG_MODE_1] & BIT_HVC_LATCH) {
pavone@480 1754 return context->hv_latch;
pavone@480 1755 }
pavone@622 1756 uint32_t line= context->vcounter & 0xFF;
pavone@647 1757 uint32_t linecyc = context->hslot;
pavone@137 1758 linecyc &= 0xFF;
pavone@413 1759 if (context->double_res) {
pavone@413 1760 line <<= 1;
pavone@413 1761 if (line & 0x100) {
pavone@413 1762 line |= 1;
pavone@413 1763 }
pavone@413 1764 }
pavone@137 1765 return (line << 8) | linecyc;
pavone@137 1766 }
pavone@137 1767
pavone@470 1768 uint16_t vdp_test_port_read(vdp_context * context)
pavone@470 1769 {
pavone@470 1770 //TODO: Find out what actually gets returned here
pavone@470 1771 return 0xFFFF;
pavone@470 1772 }
pavone@470 1773
pavone@65 1774 void vdp_adjust_cycles(vdp_context * context, uint32_t deduction)
pavone@65 1775 {
pavone@65 1776 context->cycles -= deduction;
pavone@717 1777 if (context->pending_vint_start >= deduction) {
pavone@717 1778 context->pending_vint_start -= deduction;
pavone@717 1779 } else {
pavone@717 1780 context->pending_vint_start = 0;
pavone@717 1781 }
pavone@717 1782 if (context->pending_hint_start >= deduction) {
pavone@717 1783 context->pending_hint_start -= deduction;
pavone@717 1784 } else {
pavone@717 1785 context->pending_hint_start = 0;
pavone@717 1786 }
pavone@471 1787 if (context->fifo_read >= 0) {
pavone@471 1788 int32_t idx = context->fifo_read;
pavone@471 1789 do {
pavone@471 1790 if (context->fifo[idx].cycle >= deduction) {
pavone@471 1791 context->fifo[idx].cycle -= deduction;
pavone@471 1792 } else {
pavone@471 1793 context->fifo[idx].cycle = 0;
pavone@471 1794 }
pavone@471 1795 idx = (idx+1) & (FIFO_SIZE-1);
pavone@471 1796 } while(idx != context->fifo_write);
pavone@65 1797 }
pavone@65 1798 }
pavone@65 1799
pavone@717 1800 uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context)
pavone@717 1801 {
pavone@717 1802 if (context->hslot < 183) {
pavone@717 1803 return MCLKS_LINE - context->hslot * MCLKS_SLOT_H40;
pavone@717 1804 } else if (context->hslot < HSYNC_END_H40) {
pavone@717 1805 uint32_t before_hsync = context->hslot < HSYNC_SLOT_H40 ? (HSYNC_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40 : 0;
pavone@717 1806 uint32_t hsync = 0;
pavone@717 1807 for (int i = context->hslot <= HSYNC_SLOT_H40 ? 0 : context->hslot - HSYNC_SLOT_H40; i < sizeof(h40_hsync_cycles)/sizeof(uint32_t); i++)
pavone@717 1808 {
pavone@717 1809 hsync += h40_hsync_cycles[i];
pavone@717 1810 }
pavone@717 1811 uint32_t after_hsync = (256- HSYNC_END_H40) * MCLKS_SLOT_H40;
pavone@717 1812 return before_hsync + hsync + after_hsync;
pavone@717 1813 } else {
pavone@717 1814 return (256-context->hslot) * MCLKS_SLOT_H40;
pavone@717 1815 }
pavone@717 1816 }
pavone@717 1817
pavone@623 1818 uint32_t vdp_cycles_next_line(vdp_context * context)
pavone@623 1819 {
pavone@623 1820 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@647 1821 if (context->hslot < LINE_CHANGE_H40) {
pavone@697 1822 return (LINE_CHANGE_H40 - context->hslot) * MCLKS_SLOT_H40;
pavone@623 1823 } else {
pavone@717 1824 return vdp_cycles_hslot_wrap_h40(context) + LINE_CHANGE_H40 * MCLKS_SLOT_H40;
pavone@623 1825 }
pavone@623 1826 } else {
pavone@647 1827 if (context->hslot < LINE_CHANGE_H32) {
pavone@647 1828 return (LINE_CHANGE_H32 - context->hslot) * MCLKS_SLOT_H32;
pavone@623 1829 } else if (context->hslot < 148) {
pavone@647 1830 return MCLKS_LINE - (context->hslot - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
pavone@623 1831 } else {
pavone@647 1832 return (256-context->hslot + LINE_CHANGE_H32) * MCLKS_SLOT_H32;
pavone@623 1833 }
pavone@623 1834 }
pavone@623 1835 }
pavone@623 1836
pavone@623 1837 uint32_t vdp_cycles_to_line(vdp_context * context, uint32_t target)
pavone@623 1838 {
pavone@623 1839 uint32_t jump_start, jump_dst;
pavone@623 1840 if (context->flags2 & FLAG2_REGION_PAL) {
pavone@623 1841 if (context->latched_mode & BIT_PAL) {
pavone@623 1842 jump_start = 0x10B;
pavone@623 1843 jump_dst = 0x1D2;
pavone@623 1844 } else {
pavone@623 1845 jump_start = 0x103;
pavone@623 1846 jump_dst = 0x1CA;
pavone@623 1847 }
pavone@623 1848 } else {
pavone@623 1849 if (context->latched_mode & BIT_PAL) {
pavone@623 1850 jump_start = 0;
pavone@623 1851 jump_dst = 0;
pavone@623 1852 } else {
pavone@623 1853 jump_start = 0xEB;
pavone@623 1854 jump_dst = 0x1E5;
pavone@623 1855 }
pavone@623 1856 }
pavone@623 1857 uint32_t lines;
pavone@623 1858 if (context->vcounter < target) {
pavone@623 1859 if (target < jump_start) {
pavone@623 1860 lines = target - context->vcounter;
pavone@623 1861 } else {
pavone@623 1862 lines = jump_start - context->vcounter + target - jump_dst;
pavone@623 1863 }
pavone@623 1864 } else {
pavone@623 1865 if (context->vcounter < jump_start) {
pavone@718 1866 lines = jump_start - context->vcounter + 512 - jump_dst;
pavone@623 1867 } else {
pavone@718 1868 lines = 512 - context->vcounter;
pavone@623 1869 }
pavone@623 1870 if (target < jump_start) {
pavone@623 1871 lines += target;
pavone@623 1872 } else {
pavone@623 1873 lines += jump_start + target - jump_dst;
pavone@623 1874 }
pavone@623 1875 }
pavone@623 1876 return MCLKS_LINE * (lines - 1) + vdp_cycles_next_line(context);
pavone@623 1877 }
pavone@623 1878
pavone@680 1879 uint32_t vdp_frame_end_line(vdp_context * context)
pavone@623 1880 {
pavone@623 1881 uint32_t frame_end;
pavone@623 1882 if (context->flags2 & FLAG2_REGION_PAL) {
pavone@623 1883 if (context->latched_mode & BIT_PAL) {
pavone@623 1884 frame_end = PAL_INACTIVE_START + 8;
pavone@623 1885 } else {
pavone@623 1886 frame_end = NTSC_INACTIVE_START + 8;
pavone@623 1887 }
pavone@623 1888 } else {
pavone@623 1889 if (context->latched_mode & BIT_PAL) {
pavone@623 1890 frame_end = 512;
pavone@623 1891 } else {
pavone@623 1892 frame_end = NTSC_INACTIVE_START + 8;
pavone@623 1893 }
pavone@623 1894 }
pavone@680 1895 return frame_end;
pavone@680 1896 }
pavone@680 1897
pavone@680 1898 uint32_t vdp_cycles_to_frame_end(vdp_context * context)
pavone@680 1899 {
pavone@680 1900 return context->cycles + vdp_cycles_to_line(context, vdp_frame_end_line(context));
pavone@680 1901 }
pavone@680 1902
pavone@317 1903 uint32_t vdp_next_hint(vdp_context * context)
pavone@317 1904 {
pavone@327 1905 if (!(context->regs[REG_MODE_1] & BIT_HINT_EN)) {
pavone@317 1906 return 0xFFFFFFFF;
pavone@317 1907 }
pavone@317 1908 if (context->flags2 & FLAG2_HINT_PENDING) {
pavone@717 1909 return context->pending_hint_start;
pavone@317 1910 }
pavone@622 1911 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
pavone@623 1912 uint32_t hint_line;
pavone@708 1913 if (context->vcounter + context->hint_counter >= inactive_start) {
pavone@724 1914 if (context->regs[REG_HINT] > inactive_start) {
pavone@724 1915 return 0xFFFFFFFF;
pavone@724 1916 }
pavone@623 1917 hint_line = context->regs[REG_HINT];
pavone@623 1918 } else {
pavone@623 1919 hint_line = context->vcounter + context->hint_counter + 1;
pavone@317 1920 }
pavone@623 1921
pavone@623 1922 return context->cycles + vdp_cycles_to_line(context, hint_line);
pavone@317 1923 }
pavone@317 1924
pavone@317 1925 uint32_t vdp_next_vint(vdp_context * context)
pavone@317 1926 {
pavone@327 1927 if (!(context->regs[REG_MODE_2] & BIT_VINT_EN)) {
pavone@317 1928 return 0xFFFFFFFF;
pavone@317 1929 }
pavone@317 1930 if (context->flags2 & FLAG2_VINT_PENDING) {
pavone@717 1931 return context->pending_vint_start;
pavone@317 1932 }
pavone@623 1933
pavone@623 1934
pavone@623 1935 return vdp_next_vint_z80(context);
pavone@317 1936 }
pavone@317 1937
pavone@333 1938 uint32_t vdp_next_vint_z80(vdp_context * context)
pavone@333 1939 {
pavone@622 1940 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
pavone@623 1941 if (context->vcounter == inactive_start) {
pavone@623 1942 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@699 1943 if (context->hslot >= LINE_CHANGE_H40) {
pavone@717 1944 return context->cycles + vdp_cycles_hslot_wrap_h40(context) + VINT_SLOT_H40 * MCLKS_SLOT_H40;
pavone@717 1945 } else if (context->hslot <= VINT_SLOT_H40) {
pavone@623 1946 return context->cycles + (VINT_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40;
pavone@623 1947 }
pavone@623 1948 } else {
pavone@699 1949 if (context->hslot >= LINE_CHANGE_H32) {
pavone@623 1950 if (context->hslot < 148) {
pavone@623 1951 return context->cycles + (VINT_SLOT_H32 + 148 - context->hslot + 256 - 233) * MCLKS_SLOT_H32;
pavone@623 1952 } else {
pavone@623 1953 return context->cycles + (VINT_SLOT_H32 + 256 - context->hslot) * MCLKS_SLOT_H32;
pavone@623 1954 }
pavone@717 1955 } else if (context->hslot <= VINT_SLOT_H32) {
pavone@623 1956 return context->cycles + (VINT_SLOT_H32 - context->hslot) * MCLKS_SLOT_H32;
pavone@623 1957 }
pavone@623 1958 }
pavone@623 1959 }
pavone@623 1960 int32_t cycles_to_vint = vdp_cycles_to_line(context, inactive_start);
pavone@622 1961 if (context->regs[REG_MODE_4] & BIT_H40) {
pavone@717 1962 cycles_to_vint += MCLKS_LINE - (LINE_CHANGE_H40 - VINT_SLOT_H40) * MCLKS_SLOT_H40;
pavone@333 1963 } else {
pavone@699 1964 cycles_to_vint += (VINT_SLOT_H32 + 148 - LINE_CHANGE_H32 + 256 - 233) * MCLKS_SLOT_H32;
pavone@333 1965 }
pavone@623 1966 return context->cycles + cycles_to_vint;
pavone@333 1967 }
pavone@333 1968
pavone@953 1969 void vdp_int_ack(vdp_context * context)
pavone@317 1970 {
pavone@952 1971 //Apparently the VDP interrupt controller is not very smart
pavone@952 1972 //Instead of paying attention to what interrupt is being acknowledged it just
pavone@952 1973 //clears the pending flag for whatever interrupt it is currently asserted
pavone@952 1974 //which may be different from the interrupt it was asserting when the 68k
pavone@952 1975 //started the interrupt process. The window for this is narrow and depends
pavone@952 1976 //on the latency between the int enable register write and the interrupt being
pavone@952 1977 //asserted, but Fatal Rewind depends on this due to some buggy code
pavone@952 1978 if ((context->flags2 & FLAG2_VINT_PENDING) && (context->regs[REG_MODE_2] & BIT_VINT_EN)) {
pavone@317 1979 context->flags2 &= ~FLAG2_VINT_PENDING;
pavone@952 1980 } else if((context->flags2 & FLAG2_HINT_PENDING) && (context->regs[REG_MODE_1] & BIT_HINT_EN)) {
pavone@317 1981 context->flags2 &= ~FLAG2_HINT_PENDING;
pavone@317 1982 }
pavone@317 1983 }
pavone@317 1984