Mercurial > repos > blastem
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); |