Mercurial > repos > simple16
view src/vdp.c @ 34:4a093f15fb1b
Finish controller test for controller 1
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 03 Apr 2016 19:03:35 -0700 |
parents | 083347ccd508 |
children | 6e7bfe83d2b0 |
line wrap: on
line source
#include <stdint.h> #include <string.h> #include <stdio.h> #include "vdp.h" #include "system.h" void vdp_init(vdp *context, uint32_t clock_div) { memset(context, 0, sizeof(vdp)); //clock div specifies the pixel clock divider //but our emulation step is half that fast context->clock_inc = clock_div*2; context->drawbuffer = context->linebuffers; context->readbuffer = context->linebuffers+320; } void vdp_run(vdp *context, uint32_t target) { while (context->cycles < target) { context->hcounter+=2; if (context->hcounter == 416) { context->hcounter = 0; context->vcounter++; if (context->vcounter == 262) { context->vcounter = 0; } } context->status &= ~(VDP_STATUS_VRAM|VDP_STATUS_SRAM); //Render to linebuffer if ((context->status & VDP_STATUS_ENABLED) && context->vcounter > 15 && context->vcounter < 240 && context->hcounter < 406) { if (context->hcounter < 246) { context->status |= VDP_STATUS_VRAM; if (!context->hcounter) { //flip linebuffers if (context->drawbuffer == context->linebuffers) { context->drawbuffer = context->linebuffers + 328; context->readbuffer = context->linebuffers; } else { context->drawbuffer = context->linebuffers; context->readbuffer = context->linebuffers + 328; } context->draw_dest = 0; //enable sprite scanning context->status |= VDP_STATUS_SPRITE_SCAN; context->current_draw = 0; } if (context->draw_counter) { context->draw_counter--; uint16_t pixels = context->vram[context->draw_source++]; for (int i = context->hflip ? 0 : 12; i >= 0 && i < 16; i+= context->hflip ? 4 : -4) { uint8_t pixel = ((pixels >> i) & 0xF) | context->palpriority; context->drawbuffer[context->draw_dest ^ (context->hflip << 2)] = pixel; context->draw_dest++; } } else { //00VV VVVV VVHH HHHH uint16_t vpos = (context->vscroll & 0x7FF) + context->vcounter - 16; uint16_t vmask = (context->vscroll >> 2) & 0x3E00; uint16_t vcoarse = (vpos << 3) & 0x3FC0; uint16_t vfine = vpos & 7; uint16_t hcoarse = ((context->hscroll >> 3) + context->hcounter/6) & 0x3F; uint16_t tableaddress = hcoarse | (vcoarse & ~vmask) | ((context->vscroll << 3) & vmask); //printf("VCounter: %X, VScroll: %X, HCounter: %X, Table: %X\n", context->vcounter, context->vscroll, context->hcounter, tableaddress); uint16_t entry = context->vram[tableaddress]; context->draw_source = (entry & 0x3FF) * 16; if (entry & 0x1000) { context->draw_source += 14 - vfine * 2; } else { context->draw_source += vfine * 2; } context->palpriority = entry >> 9 & 0x70; context->draw_counter = 2; context->hflip = (entry & 0x800) != 0; } if (context->status & VDP_STATUS_SPRITE_SCAN) { context->status |= VDP_STATUS_SRAM; uint16_t pos = context->sram[context->hcounter]; uint16_t y = pos & 0xFF; uint16_t x = pos >> 8; uint16_t atts = context->sram[context->hcounter+1]; x |= atts << 2 & 0x100; if (x | y) { uint16_t size = atts & 0x400 ? 16 : 8; if (context->vcounter >= y && context->vcounter < y + size) { uint16_t address = (atts & 0x3F) * 16; if (atts & 0x1000) { address += (size-1) * 2 - (context->vcounter - y) * 2; } else { address += (context->vcounter - y) * 2; } context->sprite_draws[context->current_draw].source = address; context->sprite_draws[context->current_draw].x = x; context->sprite_draws[context->current_draw].hflip = (atts & 0x800) != 0; context->sprite_draws[context->current_draw].palpriority = 0x80 | (atts >> 9 & 0x50); context->current_draw++; if (size == 16) { context->sprite_draws[context->current_draw].source = address + 32; context->sprite_draws[context->current_draw].x = x + 8; context->sprite_draws[context->current_draw].hflip = (atts & 0x800) != 0; context->sprite_draws[context->current_draw].palpriority = 0x80 | (atts >> 9 & 0x50); if (context->sprite_draws[context->current_draw].hflip) { context->sprite_draws[context->current_draw].x -= 8; context->sprite_draws[context->current_draw-1].x += 8; } } context->current_draw++; if (context->current_draw == 40) { //no more rendering capacity context->status &= ~VDP_STATUS_SPRITE_SCAN; context->current_draw = 0; } } } else { //hit sprite list terminator context->status &= ~VDP_STATUS_SPRITE_SCAN; context->current_draw = 0; } } } else { sprite_draw *draw = context->sprite_draws + (context->current_draw >> 1); if (draw->palpriority) { context->status |= VDP_STATUS_VRAM; uint16_t pixels = context->vram[draw->source + (context->current_draw & 1)]; uint16_t x = draw->x - 16 + (context->hscroll & 7); for (int i = draw->hflip ? 0 : 12; i >= 0 && i < 16; i+= draw->hflip ? 4 : -4, x++) { uint8_t pixel = (pixels >> i) & 0xF; if (pixel && x < 328 && ((draw->palpriority & 0x40) || !(context->drawbuffer[x] & 0x40))) { context->drawbuffer[x ^ (draw->hflip << 2)] = pixel | draw->palpriority; } } if (context->current_draw & 1) { draw->palpriority = 0; } else { draw->x += 4; } } context->current_draw++; } } //Draw to framebuffer if (context->vcounter > 8 && context->vcounter < 249 && context->hcounter < 320) { if (!context->hcounter && context->vcounter == 9) { context->framebuffer = system_get_framebuffer(&context->pitch); //pitch is in terms of bytes, but we want it in terms of pixels context->pitch /= sizeof(uint16_t); //clear pending interrupt flag since VBlank is over context->status &= ~VDP_STATUS_PENDING_VINT; } uint16_t *dest = context->framebuffer + (context->vcounter - 9) * context->pitch + context->hcounter; if (context->status & VDP_STATUS_ENABLED && context->vcounter > 16 && context->vcounter < 241) { *dest = context->cram[0x3F & context->readbuffer[context->hcounter]]; dest++; *dest = context->cram[0x3F & context->readbuffer[context->hcounter+1]]; } else { //Display is disabled or we're in the border area, draw the background color *dest = *context->cram; dest++; *dest = *context->cram; } } else if(!context->hcounter && context->vcounter == 249) { if (context->status & VDP_STATUS_ENABLED) { context->status |= VDP_STATUS_PENDING_VINT; } system_framebuffer_updated(); context->framebuffer = NULL; } //Handle the FIFO if (context->status & VDP_STATUS_FIFO) { switch (context->fifo_dest) { case FIFO_DEST_VRAM: if (!(context->status & VDP_STATUS_VRAM)) { context->vram[context->dest_offset++] = context->fifo; context->dest_offset &= sizeof(context->vram)/2-1; context->status &= ~VDP_STATUS_FIFO; } break; case FIFO_DEST_SRAM: if (!(context->status & VDP_STATUS_SRAM)) { context->sram[context->dest_offset++] = context->fifo; context->dest_offset &= sizeof(context->sram)/2-1; context->status &= ~VDP_STATUS_FIFO; } break; case FIFO_DEST_CRAM: context->cram[context->dest_offset++] = context->fifo; context->dest_offset &= sizeof(context->cram)/2-1; context->status &= ~VDP_STATUS_FIFO; break; } } context->cycles += context->clock_inc; } } void vdp_write_address(vdp *context, uint16_t value) { context->status &= ~VDP_STATUS_FIFO; if (!(value & 0x8000)) { context->fifo_dest = FIFO_DEST_VRAM; context->dest_offset = (value & (sizeof(context->vram) -1))/2; } else if ((value & 0xFF00) == 0xFE00) { context->fifo_dest = FIFO_DEST_SRAM; context->dest_offset = (value & (sizeof(context->sram) -1))/2; } else if ((value & 0xFF00) == 0xFF00) { context->fifo_dest = FIFO_DEST_CRAM; context->dest_offset = (value & (sizeof(context->cram) -1))/2; } } void vdp_write_data(vdp *context, uint16_t value) { context->fifo = value; context->status |= VDP_STATUS_FIFO; } void vdp_write_hscroll(vdp *context, uint16_t value) { context->hscroll = value & 0x1FF; if (value & 0x8000) { context->status |= VDP_STATUS_ENABLED; } else { context->status &= ~VDP_STATUS_ENABLED; } } uint32_t vdp_next_interrupt(vdp *context) { if (context->status & VDP_STATUS_PENDING_VINT) { return 0; } else if (context->status & VDP_STATUS_ENABLED) { uint32_t next_line = context->vcounter + 1; uint32_t next_line_cyc = context->cycles + ((416 - context->hcounter) >> 1) * context->clock_inc; if (context->vcounter < 249) { return next_line_cyc + (249 - next_line) * 832; } else { return next_line_cyc + (249 + 262 - next_line) * 832; } } else { return 0xFFFFFFFF; } } void vdp_ack_interrupt(vdp *context) { context->status &= ~VDP_STATUS_PENDING_VINT; } uint8_t vdp_interrupt_pending(vdp *context) { return (context->status & VDP_STATUS_PENDING_VINT) != 0; }