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