summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2016-10-12 09:39:52 -0700
committerMichael Pavone <pavone@retrodev.com>2016-10-12 09:39:52 -0700
commit4f1275708c60e7b582a3e5a02fac4ee0300a9999 (patch)
tree8050f1112d7ec1d04d50386cd4c373ab742afb11
parentd8ac99946d19321cbe191747ac98bd481a6cdea6 (diff)
Initial implementation of video output hardware
-rw-r--r--jag_video.c207
-rw-r--r--jag_video.h5
-rw-r--r--jaguar.c28
3 files changed, 224 insertions, 16 deletions
diff --git a/jag_video.c b/jag_video.c
index a50a34e..7ed3b94 100644
--- a/jag_video.c
+++ b/jag_video.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <stdio.h>
#include "jag_video.h"
+#include "render.h"
enum {
VMODE_CRY,
@@ -11,22 +12,222 @@ enum {
VMODE_VARIABLE
};
+#define BIT_TBGEN 1
+
char *vmode_names[] = {
"CRY",
+ "RGB24",
"RGB16",
"DIRECT16",
"VARIABLE"
};
+static uint8_t cry_red[9][16] = {
+ {0, 34, 68, 102, 135, 169, 203, 237, 255, 255, 255, 255, 255, 255, 255, 255},
+ {0, 34, 68, 102, 135, 169, 203, 230, 247, 255, 255, 255, 255, 255, 255, 255},
+ {0, 34, 68, 102, 135, 170, 183, 197, 214, 235, 255, 255, 255, 255, 255, 255},
+ {0, 34, 68, 102, 130, 141, 153, 164, 181, 204, 227, 249, 255, 255, 255, 255},
+ {0, 34, 68, 95, 104, 113, 122, 131, 148, 173, 198, 223, 248, 255, 255, 255},
+ {0, 34, 64, 71, 78, 85, 91, 98, 115, 143, 170, 197, 224, 252, 255, 255},
+ {0, 34, 43, 47, 52, 56, 61, 65, 82, 112, 141, 171, 200, 230, 255, 255},
+ {0, 19, 21, 23, 26, 28, 30, 32, 49, 81, 113, 145, 177, 208, 240, 255},
+ {0, 0, 0, 0, 0, 0, 0, 0, 17, 51, 85, 119, 153, 187, 221, 255}
+};
+
+static uint8_t cry_green[16][8] = {
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ {17, 19, 21, 23, 26, 28, 30, 32},
+ {34, 38, 43, 47, 52, 56, 61, 65},
+ {51, 57, 64, 71, 78, 85, 91, 98},
+ {68, 77, 86, 95, 104, 113, 122, 131},
+ {85, 96, 107, 119, 130, 141, 153, 164},
+ {102, 115, 129, 142, 156, 170, 183, 197},
+ {119, 134, 150, 166, 182, 198, 214, 230},
+ {136, 154, 172, 190, 208, 226, 244, 255},
+ {153, 173, 193, 214, 234, 255, 255, 255},
+ {170, 192, 215, 238, 255, 255, 255, 255},
+ {187, 211, 236, 255, 255, 255, 255, 255},
+ {204, 231, 255, 255, 255, 255, 255, 255},
+ {221, 250, 255, 255, 255, 255, 255, 255},
+ {238, 255, 255, 255, 255, 255, 255, 255},
+ {255, 255, 255, 255, 255, 255, 255, 255},
+};
+
+static uint32_t table_cry[0x10000];
+static uint32_t table_rgb[0x10000];
+static uint32_t table_variable[0x10000];
+
+static uint32_t cry_to_rgb(uint16_t cry)
+{
+ uint32_t y = cry & 0xFF;
+ if (y) {
+ uint8_t c = cry >> 12;
+ uint8_t r = cry >> 8 & 0xF;
+
+ uint32_t red = cry_red[c < 7 ? 0 : c - 7][r];
+ uint32_t green = cry_green[c][r < 8 ? r : 15 - r];
+ uint32_t blue = cry_red[c < 7 ? 0 : c - 7][15-r];
+ red = red * 255 / y;
+ blue = blue * 255 / y;
+ green = green * 255 / y;
+ return render_map_color(red, green, blue);
+ } else {
+ return render_map_color(0, 0, 0);
+ }
+}
+
+static uint32_t rgb16_to_rgb(uint16_t rgb)
+{
+ return render_map_color(
+ rgb >> 8 & 0xF8,
+ rgb << 2 & 0xFC,
+ rgb >> 4 & 0xF8
+ );
+}
+
jag_video *jag_video_init(void)
{
+ static uint8_t table_init_done = 0;
+ if (!table_init_done) {
+ for (int i = 0; i < 0x10000; i++)
+ {
+ table_cry[i] = cry_to_rgb(i);
+ table_rgb[i] = rgb16_to_rgb(i);
+ table_variable[i] = i & 1 ? rgb16_to_rgb(i & 0xFFFE) : cry_to_rgb(i);
+ }
+ table_init_done = 1;
+ }
return calloc(1, sizeof(jag_video));
}
+static void copy_16(uint32_t *dst, uint32_t len, uint16_t *linebuffer, uint32_t *table)
+{
+ for (; len; len--, dst++, linebuffer++)
+ {
+ *dst = table[*linebuffer];
+ }
+}
+
+static void copy_linebuffer(jag_video *context, uint16_t *linebuffer)
+{
+ if (!context->output) {
+ return;
+ }
+ uint32_t *dst = context->output;
+ uint32_t len;
+ if (context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN1]) {
+ if (
+ context->regs[VID_HDISP_BEGIN2] == context->regs[VID_HDISP_BEGIN1]
+ || context->regs[VID_HDISP_BEGIN2] > (context->regs[VID_HPERIOD] | 0x400)
+ ) {
+ //only one line buffer per line, so copy the previous line in its entirety
+ len = context->regs[VID_HDISP_END] - 0x400 + context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1] + 2;
+ } else {
+ //copy the second half of the previous line
+ if (context->regs[VID_HDISP_BEGIN2] & 0x400) {
+ //BEGIN2 is after the HCOUNT jump
+ dst += context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1]
+ + context->regs[VID_HDISP_BEGIN2] - 0x400 + 1;
+ len = context->regs[VID_HDISP_END] - context->regs[VID_HDISP_BEGIN2] + 1;
+ } else {
+ //BEGIN2 is before the HCOUNT jump
+ dst += context->regs[VID_HDISP_BEGIN2] - context->regs[VID_HDISP_BEGIN1];
+ len = context->regs[VID_HDISP_END] + context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN2] + 2;
+ }
+ }
+ context->output += context->output_pitch / sizeof(uint32_t);
+ } else {
+ //copy the first half of the current line
+ if (context->regs[VID_HDISP_BEGIN2] & 0x400) {
+ //BEGIN2 is after the HCOUNT jump
+ len = context->regs[VID_HDISP_BEGIN2] - 0x400 + context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1] + 1;
+ } else {
+ //BEGIN2 is before the HCOUNT jump
+ len = context->regs[VID_HDISP_BEGIN2] - context->regs[VID_HDISP_BEGIN1];
+ }
+ }
+ len /= context->pclock_div;
+ switch (context->mode)
+ {
+ case VMODE_CRY:
+ copy_16(dst, len, linebuffer, table_cry);
+ break;
+ case VMODE_RGB24:
+ //TODO: Implement me
+ break;
+ case VMODE_DIRECT16:
+ //TODO: Implement this once I better understand what would happen on hardware with composite output
+ break;
+ case VMODE_RGB16:
+ copy_16(dst, len, linebuffer, table_rgb);
+ break;
+ case VMODE_VARIABLE:
+ copy_16(dst, len, linebuffer, table_variable);
+ break;
+ }
+}
+
void jag_video_run(jag_video *context, uint32_t target_cycle)
{
- context->cycles = target_cycle;
-
+ if (context->regs[VID_VMODE] & BIT_TBGEN) {
+ while (context->cycles < target_cycle)
+ {
+ //TODO: Optimize this to not actually increment one step at a time
+ if (
+ (
+ context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN1]
+ || context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN2]
+ )
+ && context->regs[VID_VCOUNT] >= context->regs[VID_VDISP_BEGIN]
+ && context->regs[VID_VCOUNT] < context->regs[VID_VDISP_END]
+ ) {
+ //swap linebuffers, render linebuffer to framebuffer and kick off object processor
+ if (context->write_line_buffer == context->line_buffer_a) {
+ context->write_line_buffer = context->line_buffer_b;
+ copy_linebuffer(context, context->line_buffer_a);
+ } else {
+ context->write_line_buffer = context->line_buffer_a;
+ copy_linebuffer(context, context->line_buffer_b);
+ }
+ //clear new write line buffer with background color
+ for (int i = 0; i < LINEBUFFER_WORDS; i++)
+ {
+ context->write_line_buffer[i] = context->regs[VID_BGCOLOR];
+ }
+
+ //TODO: kick off object processor
+ }
+
+ if (
+ !context->output
+ && context->regs[VID_VCOUNT] == context->regs[VID_VDISP_BEGIN]
+ && context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN1]
+ ) {
+ context->output = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
+ } else if (context->output && context->regs[VID_VCOUNT] >= context->regs[VID_VDISP_END]) {
+ int width = (context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1]
+ + context->regs[VID_HDISP_END] - 1024 + 2) / context->pclock_div;
+ render_framebuffer_updated(FRAMEBUFFER_ODD, width);
+ context->output = NULL;
+ }
+
+ if ((context->regs[VID_HCOUNT] & 0x3FF) == context->regs[VID_HPERIOD]) {
+ //reset bottom 10 bits to zero, flip the 11th bit which represents which half of the line we're on
+ context->regs[VID_HCOUNT] = (context->regs[VID_HCOUNT] & 0x400) ^ 0x400;
+ //increment half-line counter
+ if (context->regs[VID_VCOUNT] == context->regs[VID_VPERIOD]) {
+ context->regs[VID_VCOUNT] = 0;
+ } else {
+ context->regs[VID_VCOUNT]++;
+ }
+ } else {
+ context->regs[VID_HCOUNT]++;
+ }
+ context->cycles++;
+ }
+ } else {
+ context->cycles = target_cycle;
+ }
}
static uint8_t is_reg_writeable(uint32_t address)
@@ -47,7 +248,7 @@ void jag_video_reg_write(jag_video *context, uint32_t address, uint16_t value)
} else {
context->mode = value >> 1 & 3;
}
- printf("Mode %s, pixel clock divider: %d, time base generation: %s\n", vmode_names[context->mode], context->pclock_div, value & 1 ? "enabled" : "disabled");
+ printf("Mode %s, pixel clock divider: %d, time base generation: %s\n", vmode_names[context->mode], context->pclock_div, value & BIT_TBGEN ? "enabled" : "disabled");
}
switch (reg)
{
diff --git a/jag_video.h b/jag_video.h
index 4cef19e..95790ec 100644
--- a/jag_video.h
+++ b/jag_video.h
@@ -50,13 +50,14 @@ enum {
#define LINEBUFFER_WORDS 720
typedef struct {
- uint16_t regs[JAG_VIDEO_REGS];
+ uint32_t *output;
+ uint32_t output_pitch;
+ uint16_t regs[JAG_VIDEO_REGS];
uint16_t clut[256];
uint16_t line_buffer_a[LINEBUFFER_WORDS];
uint16_t line_buffer_b[LINEBUFFER_WORDS];
uint16_t *write_line_buffer;
- uint16_t *read_line_buffer;
uint32_t cycles;
uint8_t pclock_div;
diff --git a/jaguar.c b/jaguar.c
index 76ba875..ba5c779 100644
--- a/jaguar.c
+++ b/jaguar.c
@@ -7,6 +7,7 @@
#include "util.h"
#include "debug.h"
#include "config.h"
+#include "render.h"
//BIOS Area Memory map
// 10 00 00 - 10 04 00 : Video mode/ Memory control registers
@@ -238,20 +239,34 @@ uint16_t rom0_read_16(uint32_t address, jaguar_context *system)
return 0xFFFF;
}
+m68k_context * sync_components(m68k_context * context, uint32_t address)
+{
+ jaguar_context *system = context->system;
+ jag_video_run(system->video, context->current_cycle);
+ if (context->current_cycle > 0x10000000) {
+ context->current_cycle -= 0x10000000;
+ system->video->cycles -= 0x10000000;
+ }
+ return context;
+}
+
void *rom0_write_m68k(uint32_t address, void *context, uint16_t value)
{
+ sync_components(context, 0);
rom0_write_16(address, ((m68k_context *)context)->system, value);
return context;
}
uint16_t rom0_read_m68k(uint32_t address, void *context)
{
+ sync_components(context, 0);
return rom0_read_16(address, ((m68k_context *)context)->system);
}
void *rom0_write_m68k_b(uint32_t address, void *context, uint8_t value)
{
+ sync_components(context, 0);
//seems unlikely these areas support byte access
uint16_t value16 = value;
value16 |= value16 << 8;
@@ -261,6 +276,7 @@ void *rom0_write_m68k_b(uint32_t address, void *context, uint8_t value)
uint8_t rom0_read_m68k_b(uint32_t address, void *context)
{
+ sync_components(context, 0);
uint16_t value = rom0_read_16(address, ((m68k_context *)context)->system);
if (address & 1) {
return value;
@@ -268,17 +284,6 @@ uint8_t rom0_read_m68k_b(uint32_t address, void *context)
return value >> 8;
}
-m68k_context * sync_components(m68k_context * context, uint32_t address)
-{
- jaguar_context *system = context->system;
- jag_video_run(system->video, context->current_cycle);
- if (context->current_cycle > 0x10000000) {
- context->current_cycle -= 0x10000000;
- system->video->cycles -= 0x10000000;
- }
- return context;
-}
-
m68k_context *handle_m68k_reset(m68k_context *context)
{
puts("M68K executed RESET");
@@ -367,6 +372,7 @@ int main(int argc, char **argv)
fatal_error("Failed to read cart from %s\n", argv[2]);
}
jaguar_context *system = init_jaguar(bios, bios_size, cart, cart_size);
+ render_init(640, 480, "BlastJag", 60, 0);
m68k_reset(system->m68k);
return 0;
}