1 | /*****************************************************************************\ |
---|---|
2 | Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. |
3 | This file is licensed under the Snes9x License. |
4 | For further information, consult the LICENSE file in the root directory. |
5 | \*****************************************************************************/ |
6 | |
7 | #include "snes9x.h" |
8 | #include "memmap.h" |
9 | |
10 | static void DSP2_Op01 (void); |
11 | static void DSP2_Op03 (void); |
12 | static void DSP2_Op05 (void); |
13 | static void DSP2_Op06 (void); |
14 | static void DSP2_Op09 (void); |
15 | static void DSP2_Op0D (void); |
16 | |
17 | |
18 | // convert bitmap to bitplane tile |
19 | static void DSP2_Op01 (void) |
20 | { |
21 | // Op01 size is always 32 bytes input and output |
22 | // The hardware does strange things if you vary the size |
23 | |
24 | uint8 c0, c1, c2, c3; |
25 | uint8 *p1 = DSP2.parameters; |
26 | uint8 *p2a = DSP2.output; |
27 | uint8 *p2b = DSP2.output + 16; // halfway |
28 | |
29 | // Process 8 blocks of 4 bytes each |
30 | |
31 | for (int j = 0; j < 8; j++) |
32 | { |
33 | c0 = *p1++; |
34 | c1 = *p1++; |
35 | c2 = *p1++; |
36 | c3 = *p1++; |
37 | |
38 | *p2a++ = (c0 & 0x10) << 3 | |
39 | (c0 & 0x01) << 6 | |
40 | (c1 & 0x10) << 1 | |
41 | (c1 & 0x01) << 4 | |
42 | (c2 & 0x10) >> 1 | |
43 | (c2 & 0x01) << 2 | |
44 | (c3 & 0x10) >> 3 | |
45 | (c3 & 0x01); |
46 | |
47 | *p2a++ = (c0 & 0x20) << 2 | |
48 | (c0 & 0x02) << 5 | |
49 | (c1 & 0x20) | |
50 | (c1 & 0x02) << 3 | |
51 | (c2 & 0x20) >> 2 | |
52 | (c2 & 0x02) << 1 | |
53 | (c3 & 0x20) >> 4 | |
54 | (c3 & 0x02) >> 1; |
55 | |
56 | *p2b++ = (c0 & 0x40) << 1 | |
57 | (c0 & 0x04) << 4 | |
58 | (c1 & 0x40) >> 1 | |
59 | (c1 & 0x04) << 2 | |
60 | (c2 & 0x40) >> 3 | |
61 | (c2 & 0x04) | |
62 | (c3 & 0x40) >> 5 | |
63 | (c3 & 0x04) >> 2; |
64 | |
65 | *p2b++ = (c0 & 0x80) | |
66 | (c0 & 0x08) << 3 | |
67 | (c1 & 0x80) >> 2 | |
68 | (c1 & 0x08) << 1 | |
69 | (c2 & 0x80) >> 4 | |
70 | (c2 & 0x08) >> 1 | |
71 | (c3 & 0x80) >> 6 | |
72 | (c3 & 0x08) >> 3; |
73 | } |
74 | } |
75 | |
76 | // set transparent color |
77 | static void DSP2_Op03 (void) |
78 | { |
79 | DSP2.Op05Transparent = DSP2.parameters[0]; |
80 | } |
81 | |
82 | // replace bitmap using transparent color |
83 | static void DSP2_Op05 (void) |
84 | { |
85 | // Overlay bitmap with transparency. |
86 | // Input: |
87 | // |
88 | // Bitmap 1: i[0] <=> i[size-1] |
89 | // Bitmap 2: i[size] <=> i[2*size-1] |
90 | // |
91 | // Output: |
92 | // |
93 | // Bitmap 3: o[0] <=> o[size-1] |
94 | // |
95 | // Processing: |
96 | // |
97 | // Process all 4-bit pixels (nibbles) in the bitmap |
98 | // |
99 | // if ( BM2_pixel == transparent_color ) |
100 | // pixelout = BM1_pixel |
101 | // else |
102 | // pixelout = BM2_pixel |
103 | |
104 | // The max size bitmap is limited to 255 because the size parameter is a byte |
105 | // I think size=0 is an error. The behavior of the chip on size=0 is to |
106 | // return the last value written to DR if you read DR on Op05 with |
107 | // size = 0. I don't think it's worth implementing this quirk unless it's |
108 | // proven necessary. |
109 | |
110 | uint8 color; |
111 | uint8 c1, c2; |
112 | uint8 *p1 = DSP2.parameters; |
113 | uint8 *p2 = DSP2.parameters + DSP2.Op05Len; |
114 | uint8 *p3 = DSP2.output; |
115 | |
116 | color = DSP2.Op05Transparent & 0x0f; |
117 | |
118 | for (int32 n = 0; n < DSP2.Op05Len; n++) |
119 | { |
120 | c1 = *p1++; |
121 | c2 = *p2++; |
122 | *p3++ = (((c2 >> 4) == color) ? c1 & 0xf0: c2 & 0xf0) | (((c2 & 0x0f) == color) ? c1 & 0x0f: c2 & 0x0f); |
123 | } |
124 | } |
125 | |
126 | // reverse bitmap |
127 | static void DSP2_Op06 (void) |
128 | { |
129 | // Input: |
130 | // size |
131 | // bitmap |
132 | |
133 | for (int32 i = 0, j = DSP2.Op06Len - 1; i < DSP2.Op06Len; i++, j--) |
134 | DSP2.output[j] = (DSP2.parameters[i] << 4) | (DSP2.parameters[i] >> 4); |
135 | } |
136 | |
137 | // multiply |
138 | static void DSP2_Op09 (void) |
139 | { |
140 | DSP2.Op09Word1 = DSP2.parameters[0] | (DSP2.parameters[1] << 8); |
141 | DSP2.Op09Word2 = DSP2.parameters[2] | (DSP2.parameters[3] << 8); |
142 | |
143 | uint32 temp = DSP2.Op09Word1 * DSP2.Op09Word2; |
144 | DSP2.output[0] = temp & 0xFF; |
145 | DSP2.output[1] = (temp >> 8) & 0xFF; |
146 | DSP2.output[2] = (temp >> 16) & 0xFF; |
147 | DSP2.output[3] = (temp >> 24) & 0xFF; |
148 | } |
149 | |
150 | // scale bitmap |
151 | static void DSP2_Op0D (void) |
152 | { |
153 | // Bit accurate hardware algorithm - uses fixed point math |
154 | // This should match the DSP2 Op0D output exactly |
155 | // I wouldn't recommend using this unless you're doing hardware debug. |
156 | // In some situations it has small visual artifacts that |
157 | // are not readily apparent on a TV screen but show up clearly |
158 | // on a monitor. Use Overload's scaling instead. |
159 | // This is for hardware verification testing. |
160 | // |
161 | // One note: the HW can do odd byte scaling but since we divide |
162 | // by two to get the count of bytes this won't work well for |
163 | // odd byte scaling (in any of the current algorithm implementations). |
164 | // So far I haven't seen Dungeon Master use it. |
165 | // If it does we can adjust the parameters and code to work with it |
166 | |
167 | uint32 multiplier; // Any size int >= 32-bits |
168 | uint32 pixloc; // match size of multiplier |
169 | uint8 pixelarray[512]; |
170 | |
171 | if (DSP2.Op0DInLen <= DSP2.Op0DOutLen) |
172 | multiplier = 0x10000; // In our self defined fixed point 0x10000 == 1 |
173 | else |
174 | multiplier = (DSP2.Op0DInLen << 17) / ((DSP2.Op0DOutLen << 1) + 1); |
175 | |
176 | pixloc = 0; |
177 | |
178 | for (int32 i = 0; i < DSP2.Op0DOutLen * 2; i++) |
179 | { |
180 | int32 j = pixloc >> 16; |
181 | |
182 | if (j & 1) |
183 | pixelarray[i] = DSP2.parameters[j >> 1] & 0x0f; |
184 | else |
185 | pixelarray[i] = (DSP2.parameters[j >> 1] & 0xf0) >> 4; |
186 | |
187 | pixloc += multiplier; |
188 | } |
189 | |
190 | for (int32 i = 0; i < DSP2.Op0DOutLen; i++) |
191 | DSP2.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1]; |
192 | } |
193 | |
194 | /* |
195 | static void DSP2_Op0D (void) |
196 | { |
197 | // Overload's algorithm - use this unless doing hardware testing |
198 | |
199 | // One note: the HW can do odd byte scaling but since we divide |
200 | // by two to get the count of bytes this won't work well for |
201 | // odd byte scaling (in any of the current algorithm implementations). |
202 | // So far I haven't seen Dungeon Master use it. |
203 | // If it does we can adjust the parameters and code to work with it |
204 | |
205 | int32 pixel_offset; |
206 | uint8 pixelarray[512]; |
207 | |
208 | for (int32 i = 0; i < DSP2.Op0DOutLen * 2; i++) |
209 | { |
210 | pixel_offset = (i * DSP2.Op0DInLen) / DSP2.Op0DOutLen; |
211 | |
212 | if ((pixel_offset & 1) == 0) |
213 | pixelarray[i] = DSP2.parameters[pixel_offset >> 1] >> 4; |
214 | else |
215 | pixelarray[i] = DSP2.parameters[pixel_offset >> 1] & 0x0f; |
216 | } |
217 | |
218 | for (int32 i = 0; i < DSP2.Op0DOutLen; i++) |
219 | DSP2.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1]; |
220 | } |
221 | */ |
222 | |
223 | void DSP2SetByte (uint8 byte, uint16 address) |
224 | { |
225 | if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000)) |
226 | { |
227 | if (DSP2.waiting4command) |
228 | { |
229 | DSP2.command = byte; |
230 | DSP2.in_index = 0; |
231 | DSP2.waiting4command = FALSE; |
232 | |
233 | switch (byte) |
234 | { |
235 | case 0x01: DSP2.in_count = 32; break; |
236 | case 0x03: DSP2.in_count = 1; break; |
237 | case 0x05: DSP2.in_count = 1; break; |
238 | case 0x06: DSP2.in_count = 1; break; |
239 | case 0x09: DSP2.in_count = 4; break; |
240 | case 0x0D: DSP2.in_count = 2; break; |
241 | default: |
242 | #ifdef DEBUGGER |
243 | //printf("Op%02X\n", byte); |
244 | #endif |
245 | case 0x0f: DSP2.in_count = 0; break; |
246 | } |
247 | } |
248 | else |
249 | { |
250 | DSP2.parameters[DSP2.in_index] = byte; |
251 | DSP2.in_index++; |
252 | } |
253 | |
254 | if (DSP2.in_count == DSP2.in_index) |
255 | { |
256 | DSP2.waiting4command = TRUE; |
257 | DSP2.out_index = 0; |
258 | |
259 | switch (DSP2.command) |
260 | { |
261 | case 0x01: |
262 | DSP2.out_count = 32; |
263 | DSP2_Op01(); |
264 | break; |
265 | |
266 | case 0x03: |
267 | DSP2_Op03(); |
268 | break; |
269 | |
270 | case 0x05: |
271 | if (DSP2.Op05HasLen) |
272 | { |
273 | DSP2.Op05HasLen = FALSE; |
274 | DSP2.out_count = DSP2.Op05Len; |
275 | DSP2_Op05(); |
276 | } |
277 | else |
278 | { |
279 | DSP2.Op05Len = DSP2.parameters[0]; |
280 | DSP2.in_index = 0; |
281 | DSP2.in_count = 2 * DSP2.Op05Len; |
282 | DSP2.Op05HasLen = TRUE; |
283 | if (byte) |
284 | DSP2.waiting4command = FALSE; |
285 | } |
286 | |
287 | break; |
288 | |
289 | case 0x06: |
290 | if (DSP2.Op06HasLen) |
291 | { |
292 | DSP2.Op06HasLen = FALSE; |
293 | DSP2.out_count = DSP2.Op06Len; |
294 | DSP2_Op06(); |
295 | } |
296 | else |
297 | { |
298 | DSP2.Op06Len = DSP2.parameters[0]; |
299 | DSP2.in_index = 0; |
300 | DSP2.in_count = DSP2.Op06Len; |
301 | DSP2.Op06HasLen = TRUE; |
302 | if (byte) |
303 | DSP2.waiting4command = FALSE; |
304 | } |
305 | |
306 | break; |
307 | |
308 | case 0x09: |
309 | DSP2.out_count = 4; |
310 | DSP2_Op09(); |
311 | break; |
312 | |
313 | case 0x0D: |
314 | if (DSP2.Op0DHasLen) |
315 | { |
316 | DSP2.Op0DHasLen = FALSE; |
317 | DSP2.out_count = DSP2.Op0DOutLen; |
318 | DSP2_Op0D(); |
319 | } |
320 | else |
321 | { |
322 | DSP2.Op0DInLen = DSP2.parameters[0]; |
323 | DSP2.Op0DOutLen = DSP2.parameters[1]; |
324 | DSP2.in_index = 0; |
325 | DSP2.in_count = (DSP2.Op0DInLen + 1) >> 1; |
326 | DSP2.Op0DHasLen = TRUE; |
327 | if (byte) |
328 | DSP2.waiting4command = FALSE; |
329 | } |
330 | |
331 | break; |
332 | |
333 | case 0x0f: |
334 | default: |
335 | break; |
336 | } |
337 | } |
338 | } |
339 | } |
340 | |
341 | uint8 DSP2GetByte (uint16 address) |
342 | { |
343 | uint8 t; |
344 | |
345 | if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000)) |
346 | { |
347 | if (DSP2.out_count) |
348 | { |
349 | t = (uint8) DSP2.output[DSP2.out_index]; |
350 | DSP2.out_index++; |
351 | if (DSP2.out_count == DSP2.out_index) |
352 | DSP2.out_count = 0; |
353 | } |
354 | else |
355 | t = 0xff; |
356 | } |
357 | else |
358 | t = 0x80; |
359 | |
360 | return (t); |
361 | } |
362 |