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 }