comparison jag_video.c @ 1089:87597a048d38

Initial implementation of video output hardware
author Michael Pavone <pavone@retrodev.com>
date Wed, 12 Oct 2016 09:39:52 -0700
parents c0a026e974f4
children a68274a25e2f
comparison
equal deleted inserted replaced
1088:c0a026e974f4 1089:87597a048d38
1 #include <stdint.h> 1 #include <stdint.h>
2 #include <stdlib.h> 2 #include <stdlib.h>
3 #include <stdio.h> 3 #include <stdio.h>
4 #include "jag_video.h" 4 #include "jag_video.h"
5 #include "render.h"
5 6
6 enum { 7 enum {
7 VMODE_CRY, 8 VMODE_CRY,
8 VMODE_RGB24, 9 VMODE_RGB24,
9 VMODE_DIRECT16, 10 VMODE_DIRECT16,
10 VMODE_RGB16, 11 VMODE_RGB16,
11 VMODE_VARIABLE 12 VMODE_VARIABLE
12 }; 13 };
13 14
15 #define BIT_TBGEN 1
16
14 char *vmode_names[] = { 17 char *vmode_names[] = {
15 "CRY", 18 "CRY",
19 "RGB24",
16 "RGB16", 20 "RGB16",
17 "DIRECT16", 21 "DIRECT16",
18 "VARIABLE" 22 "VARIABLE"
19 }; 23 };
20 24
25 static uint8_t cry_red[9][16] = {
26 {0, 34, 68, 102, 135, 169, 203, 237, 255, 255, 255, 255, 255, 255, 255, 255},
27 {0, 34, 68, 102, 135, 169, 203, 230, 247, 255, 255, 255, 255, 255, 255, 255},
28 {0, 34, 68, 102, 135, 170, 183, 197, 214, 235, 255, 255, 255, 255, 255, 255},
29 {0, 34, 68, 102, 130, 141, 153, 164, 181, 204, 227, 249, 255, 255, 255, 255},
30 {0, 34, 68, 95, 104, 113, 122, 131, 148, 173, 198, 223, 248, 255, 255, 255},
31 {0, 34, 64, 71, 78, 85, 91, 98, 115, 143, 170, 197, 224, 252, 255, 255},
32 {0, 34, 43, 47, 52, 56, 61, 65, 82, 112, 141, 171, 200, 230, 255, 255},
33 {0, 19, 21, 23, 26, 28, 30, 32, 49, 81, 113, 145, 177, 208, 240, 255},
34 {0, 0, 0, 0, 0, 0, 0, 0, 17, 51, 85, 119, 153, 187, 221, 255}
35 };
36
37 static uint8_t cry_green[16][8] = {
38 {0, 0, 0, 0, 0, 0, 0, 0},
39 {17, 19, 21, 23, 26, 28, 30, 32},
40 {34, 38, 43, 47, 52, 56, 61, 65},
41 {51, 57, 64, 71, 78, 85, 91, 98},
42 {68, 77, 86, 95, 104, 113, 122, 131},
43 {85, 96, 107, 119, 130, 141, 153, 164},
44 {102, 115, 129, 142, 156, 170, 183, 197},
45 {119, 134, 150, 166, 182, 198, 214, 230},
46 {136, 154, 172, 190, 208, 226, 244, 255},
47 {153, 173, 193, 214, 234, 255, 255, 255},
48 {170, 192, 215, 238, 255, 255, 255, 255},
49 {187, 211, 236, 255, 255, 255, 255, 255},
50 {204, 231, 255, 255, 255, 255, 255, 255},
51 {221, 250, 255, 255, 255, 255, 255, 255},
52 {238, 255, 255, 255, 255, 255, 255, 255},
53 {255, 255, 255, 255, 255, 255, 255, 255},
54 };
55
56 static uint32_t table_cry[0x10000];
57 static uint32_t table_rgb[0x10000];
58 static uint32_t table_variable[0x10000];
59
60 static uint32_t cry_to_rgb(uint16_t cry)
61 {
62 uint32_t y = cry & 0xFF;
63 if (y) {
64 uint8_t c = cry >> 12;
65 uint8_t r = cry >> 8 & 0xF;
66
67 uint32_t red = cry_red[c < 7 ? 0 : c - 7][r];
68 uint32_t green = cry_green[c][r < 8 ? r : 15 - r];
69 uint32_t blue = cry_red[c < 7 ? 0 : c - 7][15-r];
70 red = red * 255 / y;
71 blue = blue * 255 / y;
72 green = green * 255 / y;
73 return render_map_color(red, green, blue);
74 } else {
75 return render_map_color(0, 0, 0);
76 }
77 }
78
79 static uint32_t rgb16_to_rgb(uint16_t rgb)
80 {
81 return render_map_color(
82 rgb >> 8 & 0xF8,
83 rgb << 2 & 0xFC,
84 rgb >> 4 & 0xF8
85 );
86 }
87
21 jag_video *jag_video_init(void) 88 jag_video *jag_video_init(void)
22 { 89 {
90 static uint8_t table_init_done = 0;
91 if (!table_init_done) {
92 for (int i = 0; i < 0x10000; i++)
93 {
94 table_cry[i] = cry_to_rgb(i);
95 table_rgb[i] = rgb16_to_rgb(i);
96 table_variable[i] = i & 1 ? rgb16_to_rgb(i & 0xFFFE) : cry_to_rgb(i);
97 }
98 table_init_done = 1;
99 }
23 return calloc(1, sizeof(jag_video)); 100 return calloc(1, sizeof(jag_video));
24 } 101 }
25 102
103 static void copy_16(uint32_t *dst, uint32_t len, uint16_t *linebuffer, uint32_t *table)
104 {
105 for (; len; len--, dst++, linebuffer++)
106 {
107 *dst = table[*linebuffer];
108 }
109 }
110
111 static void copy_linebuffer(jag_video *context, uint16_t *linebuffer)
112 {
113 if (!context->output) {
114 return;
115 }
116 uint32_t *dst = context->output;
117 uint32_t len;
118 if (context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN1]) {
119 if (
120 context->regs[VID_HDISP_BEGIN2] == context->regs[VID_HDISP_BEGIN1]
121 || context->regs[VID_HDISP_BEGIN2] > (context->regs[VID_HPERIOD] | 0x400)
122 ) {
123 //only one line buffer per line, so copy the previous line in its entirety
124 len = context->regs[VID_HDISP_END] - 0x400 + context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1] + 2;
125 } else {
126 //copy the second half of the previous line
127 if (context->regs[VID_HDISP_BEGIN2] & 0x400) {
128 //BEGIN2 is after the HCOUNT jump
129 dst += context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1]
130 + context->regs[VID_HDISP_BEGIN2] - 0x400 + 1;
131 len = context->regs[VID_HDISP_END] - context->regs[VID_HDISP_BEGIN2] + 1;
132 } else {
133 //BEGIN2 is before the HCOUNT jump
134 dst += context->regs[VID_HDISP_BEGIN2] - context->regs[VID_HDISP_BEGIN1];
135 len = context->regs[VID_HDISP_END] + context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN2] + 2;
136 }
137 }
138 context->output += context->output_pitch / sizeof(uint32_t);
139 } else {
140 //copy the first half of the current line
141 if (context->regs[VID_HDISP_BEGIN2] & 0x400) {
142 //BEGIN2 is after the HCOUNT jump
143 len = context->regs[VID_HDISP_BEGIN2] - 0x400 + context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1] + 1;
144 } else {
145 //BEGIN2 is before the HCOUNT jump
146 len = context->regs[VID_HDISP_BEGIN2] - context->regs[VID_HDISP_BEGIN1];
147 }
148 }
149 len /= context->pclock_div;
150 switch (context->mode)
151 {
152 case VMODE_CRY:
153 copy_16(dst, len, linebuffer, table_cry);
154 break;
155 case VMODE_RGB24:
156 //TODO: Implement me
157 break;
158 case VMODE_DIRECT16:
159 //TODO: Implement this once I better understand what would happen on hardware with composite output
160 break;
161 case VMODE_RGB16:
162 copy_16(dst, len, linebuffer, table_rgb);
163 break;
164 case VMODE_VARIABLE:
165 copy_16(dst, len, linebuffer, table_variable);
166 break;
167 }
168 }
169
26 void jag_video_run(jag_video *context, uint32_t target_cycle) 170 void jag_video_run(jag_video *context, uint32_t target_cycle)
27 { 171 {
28 context->cycles = target_cycle; 172 if (context->regs[VID_VMODE] & BIT_TBGEN) {
29 173 while (context->cycles < target_cycle)
174 {
175 //TODO: Optimize this to not actually increment one step at a time
176 if (
177 (
178 context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN1]
179 || context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN2]
180 )
181 && context->regs[VID_VCOUNT] >= context->regs[VID_VDISP_BEGIN]
182 && context->regs[VID_VCOUNT] < context->regs[VID_VDISP_END]
183 ) {
184 //swap linebuffers, render linebuffer to framebuffer and kick off object processor
185 if (context->write_line_buffer == context->line_buffer_a) {
186 context->write_line_buffer = context->line_buffer_b;
187 copy_linebuffer(context, context->line_buffer_a);
188 } else {
189 context->write_line_buffer = context->line_buffer_a;
190 copy_linebuffer(context, context->line_buffer_b);
191 }
192 //clear new write line buffer with background color
193 for (int i = 0; i < LINEBUFFER_WORDS; i++)
194 {
195 context->write_line_buffer[i] = context->regs[VID_BGCOLOR];
196 }
197
198 //TODO: kick off object processor
199 }
200
201 if (
202 !context->output
203 && context->regs[VID_VCOUNT] == context->regs[VID_VDISP_BEGIN]
204 && context->regs[VID_HCOUNT] == context->regs[VID_HDISP_BEGIN1]
205 ) {
206 context->output = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
207 } else if (context->output && context->regs[VID_VCOUNT] >= context->regs[VID_VDISP_END]) {
208 int width = (context->regs[VID_HPERIOD] - context->regs[VID_HDISP_BEGIN1]
209 + context->regs[VID_HDISP_END] - 1024 + 2) / context->pclock_div;
210 render_framebuffer_updated(FRAMEBUFFER_ODD, width);
211 context->output = NULL;
212 }
213
214 if ((context->regs[VID_HCOUNT] & 0x3FF) == context->regs[VID_HPERIOD]) {
215 //reset bottom 10 bits to zero, flip the 11th bit which represents which half of the line we're on
216 context->regs[VID_HCOUNT] = (context->regs[VID_HCOUNT] & 0x400) ^ 0x400;
217 //increment half-line counter
218 if (context->regs[VID_VCOUNT] == context->regs[VID_VPERIOD]) {
219 context->regs[VID_VCOUNT] = 0;
220 } else {
221 context->regs[VID_VCOUNT]++;
222 }
223 } else {
224 context->regs[VID_HCOUNT]++;
225 }
226 context->cycles++;
227 }
228 } else {
229 context->cycles = target_cycle;
230 }
30 } 231 }
31 232
32 static uint8_t is_reg_writeable(uint32_t address) 233 static uint8_t is_reg_writeable(uint32_t address)
33 { 234 {
34 return address < VID_HLPEN || address >= VID_OBJLIST1; 235 return address < VID_HLPEN || address >= VID_OBJLIST1;
45 if (value & 0x10) { 246 if (value & 0x10) {
46 context->mode = VMODE_VARIABLE; 247 context->mode = VMODE_VARIABLE;
47 } else { 248 } else {
48 context->mode = value >> 1 & 3; 249 context->mode = value >> 1 & 3;
49 } 250 }
50 printf("Mode %s, pixel clock divider: %d, time base generation: %s\n", vmode_names[context->mode], context->pclock_div, value & 1 ? "enabled" : "disabled"); 251 printf("Mode %s, pixel clock divider: %d, time base generation: %s\n", vmode_names[context->mode], context->pclock_div, value & BIT_TBGEN ? "enabled" : "disabled");
51 } 252 }
52 switch (reg) 253 switch (reg)
53 { 254 {
54 case VID_OBJLIST1: 255 case VID_OBJLIST1:
55 printf("Object List Pointer 1: %X\n", value); 256 printf("Object List Pointer 1: %X\n", value);