Mercurial > repos > blastem
comparison vdp.c @ 471:f065769836e8
Implement FIFO as a ring buffer so the behavior of reads from invalid CRAM and VSRAM bits can be implemented properly
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 15 Sep 2013 22:20:43 -0700 |
parents | 541c1ae8abf3 |
children | 93dc0382fd70 |
comparison
equal
deleted
inserted
replaced
470:541c1ae8abf3 | 471:f065769836e8 |
---|---|
17 #define MAP_BIT_V_FLIP 0x1000 | 17 #define MAP_BIT_V_FLIP 0x1000 |
18 | 18 |
19 #define SCROLL_BUFFER_SIZE 32 | 19 #define SCROLL_BUFFER_SIZE 32 |
20 #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1) | 20 #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1) |
21 #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2) | 21 #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2) |
22 | |
23 #define FIFO_SIZE 4 | |
24 | 22 |
25 #define MCLKS_SLOT_H40 16 | 23 #define MCLKS_SLOT_H40 16 |
26 #define MCLKS_SLOT_H32 20 | 24 #define MCLKS_SLOT_H32 20 |
27 #define VINT_CYCLE_H40 (21*MCLKS_SLOT_H40+332+9*MCLKS_SLOT_H40) //21 slots before HSYNC, 16 during, 10 after | 25 #define VINT_CYCLE_H40 (21*MCLKS_SLOT_H40+332+9*MCLKS_SLOT_H40) //21 slots before HSYNC, 16 during, 10 after |
28 #define VINT_CYCLE_H32 ((33+20+7)*MCLKS_SLOT_H32) //33 slots before HSYNC, 20 during, 7 after TODO: confirm final number | 26 #define VINT_CYCLE_H32 ((33+20+7)*MCLKS_SLOT_H32) //33 slots before HSYNC, 20 during, 7 after TODO: confirm final number |
59 context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); | 57 context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); |
60 memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); | 58 memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); |
61 context->tmp_buf_a = context->linebuf + LINEBUF_SIZE; | 59 context->tmp_buf_a = context->linebuf + LINEBUF_SIZE; |
62 context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE; | 60 context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE; |
63 context->sprite_draws = MAX_DRAWS; | 61 context->sprite_draws = MAX_DRAWS; |
64 context->fifo_cur = malloc(sizeof(fifo_entry) * FIFO_SIZE); | 62 context->fifo_write = 0; |
65 context->fifo_end = context->fifo_cur + FIFO_SIZE; | 63 context->fifo_read = -1; |
66 context->b32 = render_depth() == 32; | 64 context->b32 = render_depth() == 32; |
67 if (!color_map_init_done) { | 65 if (!color_map_init_done) { |
68 uint8_t b,g,r; | 66 uint8_t b,g,r; |
69 for (uint16_t color = 0; color < (1 << 12); color++) { | 67 for (uint16_t color = 0; color < (1 << 12); color++) { |
70 if (color & FBUF_SHADOW) { | 68 if (color & FBUF_SHADOW) { |
388 #define VSRAM_WRITE 5 | 386 #define VSRAM_WRITE 5 |
389 #define DMA_START 0x20 | 387 #define DMA_START 0x20 |
390 | 388 |
391 void external_slot(vdp_context * context) | 389 void external_slot(vdp_context * context) |
392 { | 390 { |
393 fifo_entry * start = (context->fifo_end - FIFO_SIZE); | 391 fifo_entry * start = context->fifo + context->fifo_read; |
394 if (context->flags2 & FLAG2_READ_PENDING) { | 392 if (context->flags2 & FLAG2_READ_PENDING) { |
395 context->flags2 &= ~FLAG2_READ_PENDING; | 393 context->flags2 &= ~FLAG2_READ_PENDING; |
396 context->flags |= FLAG_UNUSED_SLOT; | 394 context->flags |= FLAG_UNUSED_SLOT; |
397 return; | 395 return; |
398 } | 396 } |
399 if (context->fifo_cur != start && start->cycle <= context->cycles) { | 397 if (context->fifo_read >= 0 && start->cycle <= context->cycles) { |
400 switch (start->cd & 0xF) | 398 switch (start->cd & 0xF) |
401 { | 399 { |
402 case VRAM_WRITE: | 400 case VRAM_WRITE: |
403 if (start->partial) { | 401 if (start->partial) { |
404 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); | 402 //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); |
405 context->last_fifo_val = start->value; | 403 context->last_fifo_val = start->value; |
406 context->vdpmem[start->address ^ 1] = start->value; | 404 context->vdpmem[start->address ^ 1] = start->value; |
407 } else { | 405 } else { |
408 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); | 406 //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); |
409 context->vdpmem[start->address] = start->value >> 8; | 407 context->vdpmem[start->address] = start->value >> 8; |
410 start->partial = 1; | 408 start->partial = 1; |
411 //skip auto-increment and removal of entry from fifo | 409 //skip auto-increment and removal of entry from fifo |
412 return; | 410 return; |
413 } | 411 } |
425 context->last_fifo_val = start->value; | 423 context->last_fifo_val = start->value; |
426 } | 424 } |
427 | 425 |
428 break; | 426 break; |
429 } | 427 } |
430 fifo_entry * cur = start+1; | 428 context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1); |
431 if (cur < context->fifo_cur) { | 429 if (context->fifo_read == context->fifo_write) { |
432 memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); | 430 context->fifo_read = -1; |
433 } | 431 } |
434 context->fifo_cur -= 1; | |
435 } else { | 432 } else { |
436 context->flags |= FLAG_UNUSED_SLOT; | 433 context->flags |= FLAG_UNUSED_SLOT; |
437 } | 434 } |
438 } | 435 } |
439 | 436 |
440 void run_dma_src(vdp_context * context, uint32_t slot) | 437 void run_dma_src(vdp_context * context, uint32_t slot) |
441 { | 438 { |
442 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode | 439 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode |
443 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations | 440 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations |
444 //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy | 441 //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy |
445 if (context->fifo_cur == context->fifo_end) { | 442 if (context->fifo_write == context->fifo_read) { |
446 return; | 443 return; |
447 } | 444 } |
448 uint16_t read_val; | 445 uint16_t read_val; |
449 uint8_t ran_source = 0, partial = 0; | 446 uint8_t ran_source = 0, partial = 0; |
450 uint16_t dma_len; | 447 uint16_t dma_len; |
487 ran_source = 1; | 484 ran_source = 1; |
488 break; | 485 break; |
489 } | 486 } |
490 | 487 |
491 if (ran_source) { | 488 if (ran_source) { |
492 context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; | 489 fifo_entry * cur = context->fifo + context->fifo_write; |
493 context->fifo_cur->address = context->address; | 490 cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; |
494 context->fifo_cur->value = read_val; | 491 cur->address = context->address; |
495 context->fifo_cur->cd = context->cd; | 492 cur->value = read_val; |
496 context->fifo_cur->partial = partial; | 493 cur->cd = context->cd; |
497 context->fifo_cur++; | 494 cur->partial = partial; |
495 if (context->fifo_read < 0) { | |
496 context->fifo_read = context->fifo_write; | |
497 } | |
498 context->fifo_write = (context->fifo_write+1) & (FIFO_SIZE-1); | |
498 context->regs[REG_DMASRC_L] += 1; | 499 context->regs[REG_DMASRC_L] += 1; |
499 if (!context->regs[REG_DMASRC_L]) { | 500 if (!context->regs[REG_DMASRC_L]) { |
500 context->regs[REG_DMASRC_M] += 1; | 501 context->regs[REG_DMASRC_M] += 1; |
501 } | 502 } |
502 context->address += context->regs[REG_AUTOINC]; | 503 context->address += context->regs[REG_AUTOINC]; |
1434 } | 1435 } |
1435 } | 1436 } |
1436 | 1437 |
1437 int vdp_control_port_write(vdp_context * context, uint16_t value) | 1438 int vdp_control_port_write(vdp_context * context, uint16_t value) |
1438 { | 1439 { |
1439 printf("control port write: %X at %d\n", value, context->cycles); | 1440 //printf("control port write: %X at %d\n", value, context->cycles); |
1440 if (context->flags & FLAG_DMA_RUN) { | 1441 if (context->flags & FLAG_DMA_RUN) { |
1441 return -1; | 1442 return -1; |
1442 } | 1443 } |
1443 if (context->flags & FLAG_PENDING) { | 1444 if (context->flags & FLAG_PENDING) { |
1444 context->address = (context->address & 0x3FFF) | (value << 14); | 1445 context->address = (context->address & 0x3FFF) | (value << 14); |
1488 return 0; | 1489 return 0; |
1489 } | 1490 } |
1490 | 1491 |
1491 int vdp_data_port_write(vdp_context * context, uint16_t value) | 1492 int vdp_data_port_write(vdp_context * context, uint16_t value) |
1492 { | 1493 { |
1493 printf("data port write: %X at %d\n", value, context->cycles); | 1494 //printf("data port write: %X at %d\n", value, context->cycles); |
1494 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { | 1495 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { |
1495 return -1; | 1496 return -1; |
1496 } | |
1497 if (!(context->cd & 1)) { | |
1498 //ignore writes when cd is configured for read | |
1499 return 0; | |
1500 } | 1497 } |
1501 context->flags &= ~FLAG_PENDING; | 1498 context->flags &= ~FLAG_PENDING; |
1502 /*if (context->fifo_cur == context->fifo_end) { | 1499 /*if (context->fifo_cur == context->fifo_end) { |
1503 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); | 1500 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); |
1504 }*/ | 1501 }*/ |
1505 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { | 1502 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { |
1506 context->flags &= ~FLAG_DMA_RUN; | 1503 context->flags &= ~FLAG_DMA_RUN; |
1507 } | 1504 } |
1508 while (context->fifo_cur == context->fifo_end) { | 1505 while (context->fifo_write == context->fifo_read) { |
1509 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); | 1506 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); |
1510 } | 1507 } |
1511 context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; | 1508 fifo_entry * cur = context->fifo + context->fifo_write; |
1512 context->fifo_cur->address = context->address; | 1509 cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; |
1513 context->fifo_cur->value = value; | 1510 cur->address = context->address; |
1511 cur->value = value; | |
1514 context->last_write_val = value; | 1512 context->last_write_val = value; |
1515 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { | 1513 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { |
1516 context->flags |= FLAG_DMA_RUN; | 1514 context->flags |= FLAG_DMA_RUN; |
1517 } | 1515 } |
1518 context->fifo_cur->cd = context->cd; | 1516 cur->cd = context->cd; |
1519 context->fifo_cur->partial = 0; | 1517 cur->partial = 0; |
1520 context->fifo_cur++; | 1518 if (context->fifo_read < 0) { |
1519 context->fifo_read = context->fifo_write; | |
1520 } | |
1521 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); | |
1521 context->address += context->regs[REG_AUTOINC]; | 1522 context->address += context->regs[REG_AUTOINC]; |
1522 return 0; | 1523 return 0; |
1523 } | 1524 } |
1524 | 1525 |
1525 void vdp_test_port_write(vdp_context * context, uint16_t value) | 1526 void vdp_test_port_write(vdp_context * context, uint16_t value) |
1529 | 1530 |
1530 uint16_t vdp_control_port_read(vdp_context * context) | 1531 uint16_t vdp_control_port_read(vdp_context * context) |
1531 { | 1532 { |
1532 context->flags &= ~FLAG_PENDING; | 1533 context->flags &= ~FLAG_PENDING; |
1533 uint16_t value = 0x3400; | 1534 uint16_t value = 0x3400; |
1534 if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) { | 1535 if (context->fifo_read < 0) { |
1535 value |= 0x200; | 1536 value |= 0x200; |
1536 } | 1537 } |
1537 if (context->fifo_cur == context->fifo_end) { | 1538 if (context->fifo_read == context->fifo_write) { |
1538 value |= 0x100; | 1539 value |= 0x100; |
1539 } | 1540 } |
1540 if (context->flags2 & FLAG2_VINT_PENDING) { | 1541 if (context->flags2 & FLAG2_VINT_PENDING) { |
1541 value |= 0x80; | 1542 value |= 0x80; |
1542 } | 1543 } |
1562 return value; | 1563 return value; |
1563 } | 1564 } |
1564 | 1565 |
1565 #define CRAM_BITS 0xEEE | 1566 #define CRAM_BITS 0xEEE |
1566 #define VSRAM_BITS 0x3FF | 1567 #define VSRAM_BITS 0x3FF |
1568 #define VSRAM_DIRTY_BITS 0xF800 | |
1567 | 1569 |
1568 uint16_t vdp_data_port_read(vdp_context * context) | 1570 uint16_t vdp_data_port_read(vdp_context * context) |
1569 { | 1571 { |
1570 context->flags &= ~FLAG_PENDING; | 1572 context->flags &= ~FLAG_PENDING; |
1571 if (context->cd & 1) { | 1573 if (context->cd & 1) { |
1589 } | 1591 } |
1590 value |= context->vdpmem[context->address ^ 1]; | 1592 value |= context->vdpmem[context->address ^ 1]; |
1591 break; | 1593 break; |
1592 case CRAM_READ: | 1594 case CRAM_READ: |
1593 value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; | 1595 value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; |
1594 value |= context->last_fifo_val & ~CRAM_BITS; | 1596 value |= context->fifo[context->fifo_write].value & ~CRAM_BITS; |
1595 break; | 1597 break; |
1596 case VSRAM_READ: | 1598 case VSRAM_READ: |
1597 if (((context->address / 2) & 63) < VSRAM_SIZE) { | 1599 if (((context->address / 2) & 63) < VSRAM_SIZE) { |
1598 value = context->vsram[context->address & 63] & VSRAM_BITS; | 1600 value = context->vsram[context->address & 63] & VSRAM_BITS; |
1599 value |= context->last_fifo_val & ~VSRAM_BITS; | 1601 value |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; |
1600 } | 1602 } |
1601 break; | 1603 break; |
1602 } | 1604 } |
1603 context->address += context->regs[REG_AUTOINC]; | 1605 context->address += context->regs[REG_AUTOINC]; |
1604 return value; | 1606 return value; |
1720 } | 1722 } |
1721 | 1723 |
1722 void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) | 1724 void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) |
1723 { | 1725 { |
1724 context->cycles -= deduction; | 1726 context->cycles -= deduction; |
1725 for(fifo_entry * start = (context->fifo_end - FIFO_SIZE); start < context->fifo_cur; start++) { | 1727 if (context->fifo_read >= 0) { |
1726 if (start->cycle >= deduction) { | 1728 int32_t idx = context->fifo_read; |
1727 start->cycle -= deduction; | 1729 do { |
1728 } else { | 1730 if (context->fifo[idx].cycle >= deduction) { |
1729 start->cycle = 0; | 1731 context->fifo[idx].cycle -= deduction; |
1730 } | 1732 } else { |
1733 context->fifo[idx].cycle = 0; | |
1734 } | |
1735 idx = (idx+1) & (FIFO_SIZE-1); | |
1736 } while(idx != context->fifo_write); | |
1731 } | 1737 } |
1732 } | 1738 } |
1733 | 1739 |
1734 uint32_t vdp_next_hint(vdp_context * context) | 1740 uint32_t vdp_next_hint(vdp_context * context) |
1735 { | 1741 { |