Mercurial > repos > simple16
view src/vdp.c @ 49:5f30c4d18d79
Updated the HTML documentation a bit
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 30 Aug 2016 22:04:29 -0700 |
parents | 6e7bfe83d2b0 |
children |
line wrap: on
line source
#include <stdint.h> #include <string.h> #include <stdio.h> #include "vdp.h" #include "system.h" #define MAX_ACTIVE_LINES 240 #define TOTAL_LINES 262 #define ACTIVE_WIDTH 320 #define TOTAL_WIDTH 416 #define VDP_STATUS_FB_SELECT 1 #define VDP_STATUS_PENDING_VINT 2 #define VDP_STATUS_VBLANK 4 #define VDP_STATUS_CRAM_PENDING 8 #define VDP_STATUS_VINT_ENABLED 0x2000 #define VDP_STATUS_DEPTH 0x4000 #define VDP_STATUS_ENABLED 0x8000 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; } void vdp_run(vdp *context, uint32_t target) { uint8_t *current_fb = context->status & VDP_STATUS_FB_SELECT ? context->vram + 64*1024 : context->vram; while (context->cycles < target) { context->hcounter+=2; if (context->hcounter == TOTAL_WIDTH) { context->hcounter = 0; context->vcounter++; if (context->vcounter == TOTAL_LINES) { context->vcounter = 0; } } //Draw to framebuffer if (context->vcounter < MAX_ACTIVE_LINES && context->hcounter < ACTIVE_WIDTH) { if (!context->framebuffer) { 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); } uint16_t *dest = context->framebuffer + context->vcounter * context->pitch + context->hcounter; if ( context->status & VDP_STATUS_ENABLED && context->vcounter >= context->top_skip && context->vcounter < MAX_ACTIVE_LINES - context->bottom_skip ) { if (context->status & VDP_STATUS_DEPTH) { uint16_t offset = context->start_offset + (context->vcounter - context->top_skip) * ACTIVE_WIDTH + context->hcounter; //8bpp *(dest++) = context->cram[current_fb[offset++]]; *dest = context->cram[current_fb[offset]]; } else { //4bpp uint8_t pixels = current_fb[context->start_offset + (context->vcounter - context->top_skip) * ACTIVE_WIDTH + context->hcounter >> 1]; *(dest++) = context->cram[context->pal_select | pixels >> 4]; *dest = context->cram[context->pal_select | (pixels & 0xF)]; } } else { *(dest++) = context->cram[0]; *dest = context->cram[0]; } } else if (context->framebuffer && context->hcounter < ACTIVE_WIDTH) { system_framebuffer_updated(); context->framebuffer = NULL; } if (!context->hcounter) { if (context->vcounter == (context->vcounter - context->bottom_skip)) { context->status |= VDP_STATUS_PENDING_VINT | VDP_STATUS_VBLANK; } else if (context->vcounter == context->top_skip) { //clear pending interrupt flag since VBlank is over context->status &= ~(VDP_STATUS_PENDING_VINT | VDP_STATUS_VBLANK); } } context->cycles += context->clock_inc; } } void vdp_write_mode(vdp *context, uint16_t value) { uint16_t status_bits = VDP_STATUS_ENABLED | VDP_STATUS_DEPTH | VDP_STATUS_VINT_ENABLED | VDP_STATUS_FB_SELECT; context->status &= ~status_bits; context->status |= value & status_bits; context->pal_select = value >> 7 & 0x30; context->top_skip = value >> 6 & 0x1F; context->bottom_skip = value >> 1 & 0x1F; } void vdp_write_cram(vdp *context, uint16_t value) { if (context->status & VDP_STATUS_CRAM_PENDING) { context->cram[context->pal_write_index++] = value; if (!(--context->pal_write_count)) { context->status &= ~VDP_STATUS_CRAM_PENDING; } } else { context->pal_write_count = value; context->pal_write_index = value >> 8; context->status |= VDP_STATUS_CRAM_PENDING; } } 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 + ((TOTAL_WIDTH - context->hcounter) >> 1) * context->clock_inc; uint32_t vint_line = (MAX_ACTIVE_LINES - context->bottom_skip); if (context->vcounter < vint_line) { return next_line_cyc + (vint_line - next_line) * (TOTAL_WIDTH >> 1) * context->clock_inc; } else { return next_line_cyc + (vint_line + TOTAL_LINES - next_line) * (TOTAL_WIDTH >> 1) * context->clock_inc; } } 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; } uint8_t *vdp_get_back_buffer(vdp *context) { return context->status & VDP_STATUS_FB_SELECT ? context->vram : context->vram + 64*1024; }