Mercurial > repos > blastem
comparison cd_graphics.c @ 2072:cc13c100b027
Merge Sega CD branch now that it sort of works
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 30 Jan 2022 22:29:29 -0800 |
parents | 598017ef4b0d |
children | c3241eff3c3a |
comparison
equal
deleted
inserted
replaced
2052:3748a2a8a4b7 | 2072:cc13c100b027 |
---|---|
1 #include "cd_graphics.h" | |
2 #include "backend.h" | |
3 | |
4 void cd_graphics_init(segacd_context *cd) | |
5 { | |
6 cd->graphics_int_cycle = CYCLE_NEVER; | |
7 } | |
8 | |
9 #define BIT_HFLIP 0x8000 | |
10 | |
11 static uint8_t get_src_pixel(segacd_context *cd) | |
12 { | |
13 uint16_t x = cd->graphics_x >> 11; | |
14 uint16_t y = cd->graphics_y >> 11; | |
15 cd->graphics_x += cd->graphics_dx; | |
16 cd->graphics_x &= 0xFFFFFF; | |
17 cd->graphics_y += cd->graphics_dy; | |
18 cd->graphics_y &= 0xFFFFFF; | |
19 uint16_t stamp_shift, pixel_mask; | |
20 uint16_t stamp_num_mask; | |
21 if (cd->gate_array[GA_STAMP_SIZE] & BIT_STS) { | |
22 //32x32 stamps | |
23 stamp_shift = 5; | |
24 pixel_mask = 0x1F; | |
25 stamp_num_mask = 0x3FC; | |
26 } else { | |
27 //16x16 stamps | |
28 stamp_shift = 4; | |
29 pixel_mask = 0xF; | |
30 stamp_num_mask = 0x3FF; | |
31 } | |
32 uint16_t stamp_x = x >> stamp_shift; | |
33 uint16_t stamp_y = y >> stamp_shift; | |
34 uint16_t max, base_mask; | |
35 uint32_t row_shift; | |
36 if (cd->gate_array[GA_STAMP_SIZE] & BIT_SMS) { | |
37 max = 4096 >> stamp_shift; | |
38 base_mask = 0xE000 << ((5 - stamp_shift) << 1); | |
39 //128 stamps in 32x32 mode, 256 stamps in 16x16 mode | |
40 row_shift = 12 - stamp_shift; | |
41 } else { | |
42 max = 256 >> stamp_shift; | |
43 base_mask = 0xFFE0 << ((5 - stamp_shift) << 1); | |
44 //8 stamps in 32x32 mode, 16 stamps in 16x16 mode | |
45 row_shift = 8 - stamp_shift; | |
46 } | |
47 if (stamp_x > max || stamp_y > max) { | |
48 if (cd->gate_array[GA_STAMP_SIZE] & BIT_RPT) { | |
49 stamp_x &= max - 1; | |
50 stamp_y &= max - 1; | |
51 } else { | |
52 return 0; | |
53 } | |
54 } | |
55 uint32_t address = (cd->gate_array[GA_STAMP_MAP_BASE] & base_mask) << 1; | |
56 address += (stamp_y << row_shift) + stamp_x; | |
57 uint16_t stamp_def = cd->word_ram[address]; | |
58 uint16_t stamp_num = stamp_def & stamp_num_mask; | |
59 if (!stamp_num) { | |
60 //manual says stamp 0 can't be used, I assume that means it's treated as transparent | |
61 return 0; | |
62 } | |
63 uint16_t pixel_x = x & pixel_mask; | |
64 uint16_t pixel_y = y & pixel_mask; | |
65 if (stamp_def & BIT_HFLIP) { | |
66 pixel_x = pixel_mask - pixel_x; | |
67 } | |
68 uint16_t tmp; | |
69 switch (stamp_def >> 13 & 3) | |
70 { | |
71 case 0: | |
72 break; | |
73 case 1: | |
74 tmp = pixel_y; | |
75 pixel_y = pixel_x; | |
76 pixel_x = pixel_mask - tmp; | |
77 break; | |
78 case 2: | |
79 tmp = pixel_y; | |
80 pixel_y = pixel_mask - pixel_x; | |
81 pixel_x = pixel_mask - tmp; | |
82 break; | |
83 case 3: | |
84 tmp = pixel_y; | |
85 pixel_y = pixel_mask - pixel_x; | |
86 pixel_x = tmp; | |
87 break; | |
88 } | |
89 uint16_t cell_x = pixel_x >> 3; | |
90 uint32_t pixel_address = stamp_num << 6; | |
91 pixel_address += (pixel_y << 1) + (cell_x << (stamp_shift + 1)) + (pixel_x >> 2 & 1); | |
92 uint16_t word = cd->word_ram[pixel_address]; | |
93 switch (pixel_x & 3) | |
94 { | |
95 default: | |
96 case 0: | |
97 return word >> 12; | |
98 case 1: | |
99 return word >> 8 & 0xF; | |
100 case 2: | |
101 return word >> 4 & 0xF; | |
102 case 3: | |
103 return word & 0xF; | |
104 } | |
105 | |
106 } | |
107 | |
108 enum { | |
109 FETCH_X, | |
110 FETCH_Y, | |
111 FETCH_DX, | |
112 FETCH_DY, | |
113 PIXEL0, | |
114 PIXEL1, | |
115 PIXEL2, | |
116 PIXEL3, | |
117 DRAW | |
118 }; | |
119 | |
120 void draw_pixels(segacd_context *cd) | |
121 { | |
122 uint16_t to_draw = 4 - (cd->graphics_dst_x & 3); | |
123 uint16_t x_end = cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3); | |
124 if (cd->graphics_dst_x + to_draw > x_end) { | |
125 to_draw = cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3) - cd->graphics_dst_x; | |
126 } | |
127 for(uint16_t i = 0; i < to_draw; i++) | |
128 { | |
129 uint32_t dst_address = cd->gate_array[GA_IMAGE_BUFFER_START] << 1; | |
130 dst_address += cd->graphics_dst_y << 1; | |
131 dst_address += cd->graphics_dst_x >> 2 & 1; | |
132 dst_address += ((cd->graphics_dst_x >> 3) * cd->gate_array[GA_IMAGE_BUFFER_VCELLS]) << 4; | |
133 uint16_t pixel_shift = 12 - 4 * (cd->graphics_dst_x & 3); | |
134 uint16_t pixel = cd->graphics_pixels[i] << pixel_shift; | |
135 uint16_t src_mask_check = 0xF << pixel_shift; | |
136 uint16_t src_mask_keep = ~src_mask_check; | |
137 pixel &= src_mask_check; | |
138 switch (cd->gate_array[1] >> 3 & 3) | |
139 { | |
140 case 0: | |
141 //priority mode off | |
142 cd->word_ram[dst_address] &= src_mask_keep; | |
143 cd->word_ram[dst_address] |= pixel; | |
144 break; | |
145 case 1: | |
146 //underwrite | |
147 if (pixel && ! (cd->word_ram[dst_address] & src_mask_check)) { | |
148 cd->word_ram[dst_address] &= src_mask_keep; | |
149 cd->word_ram[dst_address] |= pixel; | |
150 } | |
151 break; | |
152 case 3: | |
153 //overwrite | |
154 if (pixel) { | |
155 cd->word_ram[dst_address] &= src_mask_keep; | |
156 cd->word_ram[dst_address] |= pixel; | |
157 } | |
158 break; | |
159 } | |
160 cd->graphics_dst_x++; | |
161 } | |
162 if (cd->graphics_dst_x == x_end) { | |
163 cd->graphics_dst_y++; | |
164 --cd->gate_array[GA_IMAGE_BUFFER_LINES]; | |
165 cd->gate_array[GA_TRACE_VECTOR_BASE] += 2; | |
166 cd->graphics_step = FETCH_X; | |
167 } else { | |
168 cd->graphics_step = PIXEL0; | |
169 } | |
170 } | |
171 | |
172 #define CHECK_CYCLES cd->graphics_step++; if(cd->graphics_cycle >= cycle) break | |
173 #define CHECK_ONLY if(cd->graphics_cycle >= cycle) break | |
174 | |
175 static void do_graphics(segacd_context *cd, uint32_t cycle) | |
176 { | |
177 if (!cd->gate_array[GA_IMAGE_BUFFER_LINES]) { | |
178 return; | |
179 } | |
180 while (cd->graphics_cycle < cycle) | |
181 { | |
182 switch (cd->graphics_step) | |
183 { | |
184 case FETCH_X: | |
185 cd->graphics_x = cd->word_ram[cd->gate_array[GA_TRACE_VECTOR_BASE] << 1] << 8; | |
186 cd->graphics_cycle += 3*4; | |
187 cd->graphics_dst_x = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3; | |
188 CHECK_CYCLES; | |
189 case FETCH_Y: | |
190 cd->graphics_y = cd->word_ram[(cd->gate_array[GA_TRACE_VECTOR_BASE] << 1) + 1] << 8; | |
191 cd->graphics_cycle += 2*4; | |
192 CHECK_CYCLES; | |
193 case FETCH_DX: | |
194 cd->graphics_dx = cd->word_ram[(cd->gate_array[GA_TRACE_VECTOR_BASE] << 1) + 2]; | |
195 if (cd->graphics_dx & 0x8000) { | |
196 cd->graphics_dx |= 0xFF0000; | |
197 } | |
198 cd->graphics_cycle += 2*4; | |
199 CHECK_CYCLES; | |
200 case FETCH_DY: | |
201 cd->graphics_dy = cd->word_ram[(cd->gate_array[GA_TRACE_VECTOR_BASE] << 1) + 3]; | |
202 if (cd->graphics_dy & 0x8000) { | |
203 cd->graphics_dy |= 0xFF0000; | |
204 } | |
205 cd->graphics_cycle += 2*4; | |
206 CHECK_CYCLES; | |
207 case PIXEL0: | |
208 cd->graphics_pixels[0] = get_src_pixel(cd); | |
209 cd->graphics_cycle += 2*4; | |
210 if ((cd->graphics_dst_x & 3) == 3 || (cd->graphics_dst_x + 1 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { | |
211 cd->graphics_step = DRAW; | |
212 CHECK_ONLY; | |
213 } else { | |
214 CHECK_CYCLES; | |
215 } | |
216 case PIXEL1: | |
217 cd->graphics_pixels[1] = get_src_pixel(cd); | |
218 cd->graphics_cycle += 2*4; | |
219 if ((cd->graphics_dst_x & 3) == 2 || (cd->graphics_dst_x + 2 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { | |
220 cd->graphics_step = DRAW; | |
221 CHECK_ONLY; | |
222 } else { | |
223 CHECK_CYCLES; | |
224 } | |
225 case PIXEL2: | |
226 cd->graphics_pixels[2] = get_src_pixel(cd); | |
227 cd->graphics_cycle += 2*4; | |
228 if ((cd->graphics_dst_x & 3) == 1 || (cd->graphics_dst_x + 3 == cd->gate_array[GA_IMAGE_BUFFER_HDOTS] + (cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3))) { | |
229 cd->graphics_step = DRAW; | |
230 CHECK_ONLY; | |
231 } else { | |
232 CHECK_CYCLES; | |
233 } | |
234 case PIXEL3: | |
235 cd->graphics_pixels[3] = get_src_pixel(cd); | |
236 cd->graphics_cycle += 2*4; | |
237 CHECK_CYCLES; | |
238 case DRAW: | |
239 draw_pixels(cd); | |
240 cd->graphics_cycle += 1*4; | |
241 if (!cd->gate_array[GA_IMAGE_BUFFER_LINES]) { | |
242 break; | |
243 } | |
244 CHECK_ONLY; | |
245 } | |
246 } | |
247 } | |
248 | |
249 void cd_graphics_run(segacd_context *cd, uint32_t cycle) | |
250 { | |
251 while (cd->graphics_cycle < cycle) | |
252 { | |
253 if (cd->gate_array[GA_STAMP_SIZE] & BIT_GRON) { | |
254 do_graphics(cd, cycle); | |
255 //end calculation and actual emulated execution time probably don't 100% line up yet | |
256 //deal with that here for now | |
257 for(; cd->graphics_cycle < cycle; cd->graphics_cycle += 4) | |
258 { | |
259 } | |
260 if (cd->graphics_cycle >= cd->graphics_int_cycle) { | |
261 cd->gate_array[GA_STAMP_SIZE] &= ~BIT_GRON; | |
262 break; | |
263 } | |
264 } else { | |
265 cd->graphics_cycle = cycle; | |
266 } | |
267 } | |
268 } | |
269 void cd_graphics_start(segacd_context *cd) | |
270 { | |
271 if (!(cd->gate_array[GA_STAMP_SIZE] & BIT_GRON)) { | |
272 printf("grahpics start @ %u\n", cd->graphics_cycle); | |
273 cd->gate_array[GA_STAMP_SIZE] |= BIT_GRON; | |
274 //Manual scan is bad, but formula appears to be | |
275 // vsize * (13 + 2 * hoffset + 9 * (hdots + hoffset - 1)) | |
276 //with an additional 13? cycle setup cost per line | |
277 uint32_t lines = cd->gate_array[GA_IMAGE_BUFFER_LINES]; | |
278 uint32_t hdots = cd->gate_array[GA_IMAGE_BUFFER_HDOTS]; | |
279 uint32_t hoffset = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] & 3; | |
280 cd->graphics_int_cycle = cd->graphics_cycle + 4 * lines * (13 + 2 * hoffset + 9 * (hdots + hoffset - 1)); | |
281 cd->graphics_dst_y = cd->gate_array[GA_IMAGE_BUFFER_OFFSET] >> 3; | |
282 } else { | |
283 printf("graphics start ignored @ %u\n", cd->graphics_cycle); | |
284 } | |
285 | |
286 } |