comparison vdp.c @ 460:788ba843a731

Implement FIFO latency and improve DMA accuracy
author Mike Pavone <pavone@retrodev.com>
date Tue, 10 Sep 2013 00:29:46 -0700
parents e9b6fe443bf2
children 6221f8f534fa
comparison
equal deleted inserted replaced
456:249d24973682 460:788ba843a731
25 #define MCLK_WEIRD_END (HSYNC_SLOT_H40*MCLKS_SLOT_H40 + 332) 25 #define MCLK_WEIRD_END (HSYNC_SLOT_H40*MCLKS_SLOT_H40 + 332)
26 #define SLOT_WEIRD_END (HSYNC_SLOT_H40+17) 26 #define SLOT_WEIRD_END (HSYNC_SLOT_H40+17)
27 #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32) 27 #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32)
28 #define HBLANK_CLEAR_H40 (MCLK_WEIRD_END+61*4) 28 #define HBLANK_CLEAR_H40 (MCLK_WEIRD_END+61*4)
29 #define HBLANK_CLEAR_H32 (HSYNC_END_H32 + 46*5) 29 #define HBLANK_CLEAR_H32 (HSYNC_END_H32 + 46*5)
30 #define FIFO_LATENCY 3
30 31
31 int32_t color_map[1 << 12]; 32 int32_t color_map[1 << 12];
32 uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; 33 uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255};
33 34
34 uint8_t debug_base[][3] = { 35 uint8_t debug_base[][3] = {
116 r += 72; 117 r += 72;
117 } 118 }
118 } 119 }
119 context->debugcolors[color] = render_map_color(r, g, b); 120 context->debugcolors[color] = render_map_color(r, g, b);
120 } 121 }
122 }
123 }
124
125 int is_refresh(vdp_context * context, uint32_t slot)
126 {
127 if (context->latched_mode & BIT_H40) {
128 return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210);
129 } else {
130 //TODO: Figure out which slots are refresh when display is off in 32-cell mode
131 //These numbers are guesses based on H40 numbers
132 return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152);
133 //The numbers below are the refresh slots during active display
134 //return (slot == 66 || slot == 98 || slot == 130 || slot == 162);
121 } 135 }
122 } 136 }
123 137
124 void render_sprite_cells(vdp_context * context) 138 void render_sprite_cells(vdp_context * context)
125 { 139 {
369 #define VSRAM_WRITE 5 383 #define VSRAM_WRITE 5
370 #define DMA_START 0x20 384 #define DMA_START 0x20
371 385
372 void external_slot(vdp_context * context) 386 void external_slot(vdp_context * context)
373 { 387 {
388 fifo_entry * start = (context->fifo_end - FIFO_SIZE);
389 if (context->fifo_cur != start && start->cycle <= context->cycles) {
390 switch (start->cd & 0xF)
391 {
392 case VRAM_WRITE:
393 if (start->partial) {
394 //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1);
395 context->vdpmem[start->address ^ 1] = start->value;
396 } else {
397 //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address);
398 context->vdpmem[start->address] = start->value >> 8;
399 start->partial = 1;
400 //skip auto-increment and removal of entry from fifo
401 return;
402 }
403 break;
404 case CRAM_WRITE: {
405 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1));
406 write_cram(context, start->address, start->value);
407 break;
408 }
409 case VSRAM_WRITE:
410 if (((start->address/2) & 63) < VSRAM_SIZE) {
411 //printf("VSRAM Write: %X to %X\n", start->value, context->address);
412 context->vsram[(start->address/2) & 63] = start->value;
413 }
414
415 break;
416 }
417 fifo_entry * cur = start+1;
418 if (cur < context->fifo_cur) {
419 memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur));
420 }
421 context->fifo_cur -= 1;
422 } else {
423 context->flags |= FLAG_UNUSED_SLOT;
424 }
425 }
426
427 void run_dma_src(vdp_context * context, uint32_t slot)
428 {
374 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode 429 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode
375 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations 430 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations
376 //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy 431 //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy
377 if(context->flags & FLAG_DMA_RUN) { 432 if (context->fifo_cur == context->fifo_end) {
378 uint16_t dma_len; 433 return;
379 switch(context->regs[REG_DMASRC_H] & 0xC0) 434 }
380 { 435 uint16_t read_val;
381 //68K -> VDP 436 uint8_t ran_source = 0, partial = 0;
382 case 0: 437 uint16_t dma_len;
383 case 0x40: 438 switch(context->regs[REG_DMASRC_H] & 0xC0)
439 {
440 //68K -> VDP
441 case 0:
442 case 0x40:
443 if (!slot || !is_refresh(context, slot-1)) {
444 read_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
445 ran_source = 1;
446 }
447 break;
448 //Copy
449 case 0xC0:
450 if (context->flags & FLAG_UNUSED_SLOT) {
384 switch(context->dma_cd & 0xF) 451 switch(context->dma_cd & 0xF)
385 { 452 {
386 case VRAM_WRITE: 453 case VRAM_WRITE:
387 if (context->flags & FLAG_DMA_PROG) { 454 read_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]];
388 context->vdpmem[context->address ^ 1] = context->dma_val; 455 break;
389 context->flags &= ~FLAG_DMA_PROG; 456 case CRAM_WRITE:
457 read_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)];
458 break;
459 case VSRAM_WRITE:
460 if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) {
461 read_val = context->vsram[context->regs[REG_DMASRC_L] & 63];
390 } else { 462 } else {
391 context->dma_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); 463 read_val = 0;
392 context->vdpmem[context->address] = context->dma_val >> 8; 464 }
393 context->flags |= FLAG_DMA_PROG; 465 break;
394 } 466 }
395 break; 467 ran_source = 1;
396 case CRAM_WRITE: { 468 context->flags &= ~FLAG_UNUSED_SLOT;
397 write_cram(context, context->address, read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L])); 469 }
398 //printf("CRAM DMA | %X set to %X from %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->cycles); 470 break;
399 break; 471 case 0x80:
400 } 472 read_val = (context->cd & 0xF) == VRAM_WRITE ? context->last_write_val >> 8 : context->last_write_val;
401 case VSRAM_WRITE: 473 partial = 1;
402 if (((context->address/2) & 63) < VSRAM_SIZE) { 474 ran_source = 1;
403 context->vsram[(context->address/2) & 63] = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); 475 break;
404 } 476 }
405 break; 477
406 } 478 if (ran_source) {
407 break; 479 context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
408 //Fill 480 context->fifo_cur->address = context->address;
409 case 0x80: 481 context->fifo_cur->value = read_val;
410 switch(context->dma_cd & 0xF) 482 context->fifo_cur->cd = context->cd;
411 { 483 context->fifo_cur->partial = partial;
412 case VRAM_WRITE: 484 context->fifo_cur++;
413 //Charles MacDonald's VDP doc says that the low byte gets written first 485 context->regs[REG_DMASRC_L] += 1;
414 context->vdpmem[context->address] = context->dma_val; 486 if (!context->regs[REG_DMASRC_L]) {
415 context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); 487 context->regs[REG_DMASRC_M] += 1;
416 break; 488 }
417 case CRAM_WRITE: 489 context->address += context->regs[REG_AUTOINC];
418 write_cram(context, context->address, context->dma_val); 490 dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1;
419 //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); 491 context->regs[REG_DMALEN_H] = dma_len >> 8;
420 break; 492 context->regs[REG_DMALEN_L] = dma_len;
421 case VSRAM_WRITE: 493 if (!dma_len) {
422 if (((context->address/2) & 63) < VSRAM_SIZE) { 494 //printf("DMA end at cycle %d\n", context->cycles);
423 context->vsram[(context->address/2) & 63] = context->dma_val; 495 context->flags &= ~FLAG_DMA_RUN;
424 } 496 context->cd &= 0xF;
425 break;
426 }
427 break;
428 //Copy
429 case 0xC0:
430 if (context->flags & FLAG_DMA_PROG) {
431 switch(context->dma_cd & 0xF)
432 {
433 case VRAM_WRITE:
434 context->vdpmem[context->address] = context->dma_val;
435 break;
436 case CRAM_WRITE: {
437 write_cram(context, context->address, context->dma_val);
438 //printf("CRAM DMA Copy | %X set to %X from %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->regs[REG_DMASRC_L] & (CRAM_SIZE-1), context->cycles);
439 break;
440 }
441 case VSRAM_WRITE:
442 if (((context->address/2) & 63) < VSRAM_SIZE) {
443 context->vsram[(context->address/2) & 63] = context->dma_val;
444 }
445 break;
446 }
447 context->flags &= ~FLAG_DMA_PROG;
448 } else {
449 //I assume, that DMA copy copies from the same RAM as the destination
450 //but it's possible I'm mistaken
451 switch(context->dma_cd & 0xF)
452 {
453 case VRAM_WRITE:
454 context->dma_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]];
455 break;
456 case CRAM_WRITE:
457 context->dma_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)];
458 break;
459 case VSRAM_WRITE:
460 if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) {
461 context->dma_val = context->vsram[context->regs[REG_DMASRC_L] & 63];
462 } else {
463 context->dma_val = 0;
464 }
465 break;
466 }
467 context->flags |= FLAG_DMA_PROG;
468 }
469 break;
470 }
471 if (!(context->flags & FLAG_DMA_PROG)) {
472 context->address += context->regs[REG_AUTOINC];
473 context->regs[REG_DMASRC_L] += 1;
474 if (!context->regs[REG_DMASRC_L]) {
475 context->regs[REG_DMASRC_M] += 1;
476 }
477 dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1;
478 context->regs[REG_DMALEN_H] = dma_len >> 8;
479 context->regs[REG_DMALEN_L] = dma_len;
480 if (!dma_len) {
481 //printf("DMA end at cycle %d\n", context->cycles);
482 context->flags &= ~FLAG_DMA_RUN;
483 }
484 }
485 } else {
486 fifo_entry * start = (context->fifo_end - FIFO_SIZE);
487 if (context->fifo_cur != start && start->cycle <= context->cycles) {
488 if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
489 //printf("DMA fill started at %d\n", context->cycles);
490 context->flags |= FLAG_DMA_RUN;
491 context->dma_val = start->value;
492 context->address = start->address; //undo auto-increment
493 context->dma_cd = context->cd;
494 } else {
495 switch (start->cd & 0xF)
496 {
497 case VRAM_WRITE:
498 if (start->partial) {
499 //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1);
500 context->vdpmem[start->address ^ 1] = start->value;
501 } else {
502 //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address);
503 context->vdpmem[start->address] = start->value >> 8;
504 start->partial = 1;
505 //skip auto-increment and removal of entry from fifo
506 return;
507 }
508 break;
509 case CRAM_WRITE: {
510 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1));
511 write_cram(context, start->address, start->value);
512 break;
513 }
514 case VSRAM_WRITE:
515 if (((start->address/2) & 63) < VSRAM_SIZE) {
516 //printf("VSRAM Write: %X to %X\n", start->value, context->address);
517 context->vsram[(start->address/2) & 63] = start->value;
518 }
519 break;
520 }
521 //context->address += context->regs[REG_AUTOINC];
522 }
523 fifo_entry * cur = start+1;
524 if (cur < context->fifo_cur) {
525 memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur));
526 }
527 context->fifo_cur -= 1;
528 } else {
529 context->flags |= FLAG_UNUSED_SLOT;
530 } 497 }
531 } 498 }
532 } 499 }
533 500
534 #define WINDOW_RIGHT 0x80 501 #define WINDOW_RIGHT 0x80
1236 void latch_mode(vdp_context * context) 1203 void latch_mode(vdp_context * context)
1237 { 1204 {
1238 context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL); 1205 context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL);
1239 } 1206 }
1240 1207
1241 int is_refresh(vdp_context * context, uint32_t slot)
1242 {
1243 if (context->latched_mode & BIT_H40) {
1244 //TODO: Determine behavior for DMA fills and copies
1245 return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210
1246 || ((context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) != VRAM_WRITE)) && (
1247 //both of the missed reads occurred right next to each other, but there seems
1248 //to be some buffering involved, these values produce similar artifacts
1249 //to what I see on my Model 2
1250 slot == 34 || slot == 66 || slot == 99 || slot == 130 || slot == 162 || slot == 194));
1251 } else {
1252 //TODO: Figure out which slots are refresh when display is off in 32-cell mode
1253 //These numbers are guesses based on H40 numbers
1254 return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152
1255 || ((context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) != VRAM_WRITE)) && (
1256 slot == 21 || slot == 53 || slot == 85 || slot == 117 || slot == 149));
1257 //The numbers below are the refresh slots during active display
1258 //return (slot == 66 || slot == 98 || slot == 130 || slot == 162);
1259 }
1260 }
1261
1262 void check_render_bg(vdp_context * context, int32_t line, uint32_t slot) 1208 void check_render_bg(vdp_context * context, int32_t line, uint32_t slot)
1263 { 1209 {
1264 if (line > 0) { 1210 if (line > 0) {
1265 line -= 1; 1211 line -= 1;
1266 int starti = -1; 1212 int starti = -1;
1297 1243
1298 void vdp_run_context(vdp_context * context, uint32_t target_cycles) 1244 void vdp_run_context(vdp_context * context, uint32_t target_cycles)
1299 { 1245 {
1300 while(context->cycles < target_cycles) 1246 while(context->cycles < target_cycles)
1301 { 1247 {
1248 context->flags &= ~FLAG_UNUSED_SLOT;
1302 uint32_t line = context->cycles / MCLKS_LINE; 1249 uint32_t line = context->cycles / MCLKS_LINE;
1303 uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE; 1250 uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
1304 if (!context->cycles) { 1251 if (!context->cycles) {
1305 latch_mode(context); 1252 latch_mode(context);
1306 } 1253 }
1426 } 1373 }
1427 if (line < active_lines) { 1374 if (line < active_lines) {
1428 check_render_bg(context, line, slot); 1375 check_render_bg(context, line, slot);
1429 } 1376 }
1430 } 1377 }
1378 if (context->flags & FLAG_DMA_RUN && !is_refresh(context, slot)) {
1379 run_dma_src(context, slot);
1380 }
1431 context->cycles += inccycles; 1381 context->cycles += inccycles;
1432 } 1382 }
1433 } 1383 }
1434 1384
1435 uint32_t vdp_run_to_vblank(vdp_context * context) 1385 uint32_t vdp_run_to_vblank(vdp_context * context)
1520 } 1470 }
1521 1471
1522 int vdp_data_port_write(vdp_context * context, uint16_t value) 1472 int vdp_data_port_write(vdp_context * context, uint16_t value)
1523 { 1473 {
1524 //printf("data port write: %X at %d\n", value, context->cycles); 1474 //printf("data port write: %X at %d\n", value, context->cycles);
1525 if (context->flags & FLAG_DMA_RUN) { 1475 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
1526 return -1; 1476 return -1;
1527 } 1477 }
1528 if (!(context->cd & 1)) { 1478 if (!(context->cd & 1)) {
1529 //ignore writes when cd is configured for read 1479 //ignore writes when cd is configured for read
1530 return 0; 1480 return 0;
1531 } 1481 }
1532 context->flags &= ~FLAG_PENDING; 1482 context->flags &= ~FLAG_PENDING;
1533 /*if (context->fifo_cur == context->fifo_end) { 1483 /*if (context->fifo_cur == context->fifo_end) {
1534 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); 1484 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
1535 }*/ 1485 }*/
1486 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
1487 context->flags &= ~FLAG_DMA_RUN;
1488 }
1536 while (context->fifo_cur == context->fifo_end) { 1489 while (context->fifo_cur == context->fifo_end) {
1537 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); 1490 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
1538 } 1491 }
1539 context->fifo_cur->cycle = context->cycles; 1492 context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
1540 context->fifo_cur->address = context->address; 1493 context->fifo_cur->address = context->address;
1541 context->fifo_cur->value = value; 1494 context->fifo_cur->value = value;
1495 context->last_write_val = value;
1496 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
1497 context->flags |= FLAG_DMA_RUN;
1498 }
1542 context->fifo_cur->cd = context->cd; 1499 context->fifo_cur->cd = context->cd;
1543 context->fifo_cur->partial = 0; 1500 context->fifo_cur->partial = 0;
1544 context->fifo_cur++; 1501 context->fifo_cur++;
1545 context->address += context->regs[REG_AUTOINC]; 1502 context->address += context->regs[REG_AUTOINC];
1546 return 0; 1503 return 0;