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/*
8 Due recognition and credit are given on Overload's DSP website.
9 Thank those contributors for their hard work on this chip.
10
11 Fixed-point math reminder:
12 [sign, integer, fraction]
13 1.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0')
14 1.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0')
15*/
16
17
18#include "snes9x.h"
19#include "memmap.h"
20
21#define DSP4_CLEAR_OUT() \
22 { DSP4.out_count = 0; DSP4.out_index = 0; }
23
24#define DSP4_WRITE_BYTE(d) \
25 { WRITE_WORD(DSP4.output + DSP4.out_count, (d)); DSP4.out_count++; }
26
27#define DSP4_WRITE_WORD(d) \
28 { WRITE_WORD(DSP4.output + DSP4.out_count, (d)); DSP4.out_count += 2; }
29
30#ifndef MSB_FIRST
31#define DSP4_WRITE_16_WORD(d) \
32 { memcpy(DSP4.output + DSP4.out_count, (d), 32); DSP4.out_count += 32; }
33#else
34#define DSP4_WRITE_16_WORD(d) \
35 { for (int p = 0; p < 16; p++) DSP4_WRITE_WORD((d)[p]); }
36#endif
37
38// used to wait for dsp i/o
39#define DSP4_WAIT(x) \
40 DSP4.in_index = 0; DSP4.Logic = (x); return
41
42// 1.7.8 -> 1.15.16
43#define SEX78(a) (((int32) ((int16) (a))) << 8)
44
45// 1.15.0 -> 1.15.16
46#define SEX16(a) (((int32) ((int16) (a))) << 16)
47
48static int16 DSP4_READ_WORD (void);
49static int32 DSP4_READ_DWORD (void);
50static int16 DSP4_Inverse (int16);
51static void DSP4_Multiply (int16, int16, int32 *);
52static void DSP4_OP01 (void);
53static void DSP4_OP03 (void);
54static void DSP4_OP05 (void);
55static void DSP4_OP06 (void);
56static void DSP4_OP07 (void);
57static void DSP4_OP08 (void);
58static void DSP4_OP09 (void);
59static void DSP4_OP0A (int16, int16 *, int16 *, int16 *, int16 *);
60static void DSP4_OP0B (bool8 *, int16, int16, int16, bool8, bool8);
61static void DSP4_OP0D (void);
62static void DSP4_OP0E (void);
63static void DSP4_OP0F (void);
64static void DSP4_OP10 (void);
65static void DSP4_OP11 (int16, int16, int16, int16, int16 *);
66static void DSP4_SetByte (void);
67static void DSP4_GetByte (void);
68
69
70static int16 DSP4_READ_WORD (void)
71{
72 int16 out;
73
74 out = READ_WORD(DSP4.parameters + DSP4.in_index);
75 DSP4.in_index += 2;
76
77 return (out);
78}
79
80static int32 DSP4_READ_DWORD (void)
81{
82 int32 out;
83
84 out = READ_DWORD(DSP4.parameters + DSP4.in_index);
85 DSP4.in_index += 4;
86
87 return (out);
88}
89
90static int16 DSP4_Inverse (int16 value)
91{
92 // Attention: This lookup table is not verified
93 const uint16 div_lut[64] =
94 {
95 0x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249,
96 0x1000, 0x0e38, 0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888,
97 0x0800, 0x0787, 0x071c, 0x06bc, 0x0666, 0x0618, 0x05d1, 0x0590,
98 0x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469, 0x0444, 0x0421,
99 0x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348,
100 0x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9,
101 0x02aa, 0x029c, 0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253,
102 0x0249, 0x023e, 0x0234, 0x022b, 0x0222, 0x0219, 0x0210, 0x0208
103 };
104
105 // saturate bounds
106 if (value < 0)
107 value = 0;
108 if (value > 63)
109 value = 63;
110
111 return (div_lut[value]);
112}
113
114static void DSP4_Multiply (int16 Multiplicand, int16 Multiplier, int32 *Product)
115{
116 *Product = (Multiplicand * Multiplier << 1) >> 1;
117}
118
119static void DSP4_OP01 (void)
120{
121 DSP4.waiting4command = FALSE;
122
123 // op flow control
124 switch (DSP4.Logic)
125 {
126 case 1: goto resume1; break;
127 case 2: goto resume2; break;
128 case 3: goto resume3; break;
129 }
130
131 ////////////////////////////////////////////////////
132 // process initial inputs
133
134 // sort inputs
135 DSP4.world_y = DSP4_READ_DWORD();
136 DSP4.poly_bottom[0][0] = DSP4_READ_WORD();
137 DSP4.poly_top[0][0] = DSP4_READ_WORD();
138 DSP4.poly_cx[1][0] = DSP4_READ_WORD();
139 DSP4.viewport_bottom = DSP4_READ_WORD();
140 DSP4.world_x = DSP4_READ_DWORD();
141 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
142 DSP4.poly_ptr[0][0] = DSP4_READ_WORD();
143 DSP4.world_yofs = DSP4_READ_WORD();
144 DSP4.world_dy = DSP4_READ_DWORD();
145 DSP4.world_dx = DSP4_READ_DWORD();
146 DSP4.distance = DSP4_READ_WORD();
147 DSP4_READ_WORD(); // 0x0000
148 DSP4.world_xenv = DSP4_READ_DWORD();
149 DSP4.world_ddy = DSP4_READ_WORD();
150 DSP4.world_ddx = DSP4_READ_WORD();
151 DSP4.view_yofsenv = DSP4_READ_WORD();
152
153 // initial (x, y, offset) at starting raster line
154 DSP4.view_x1 = (DSP4.world_x + DSP4.world_xenv) >> 16;
155 DSP4.view_y1 = DSP4.world_y >> 16;
156 DSP4.view_xofs1 = DSP4.world_x >> 16;
157 DSP4.view_yofs1 = DSP4.world_yofs;
158 DSP4.view_turnoff_x = 0;
159 DSP4.view_turnoff_dx = 0;
160
161 // first raster line
162 DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];
163
164 do
165 {
166 ////////////////////////////////////////////////////
167 // process one iteration of projection
168
169 // perspective projection of world (x, y, scroll) points
170 // based on the current projection lines
171 DSP4.view_x2 = (((DSP4.world_x + DSP4.world_xenv) >> 16) * DSP4.distance >> 15) + (DSP4.view_turnoff_x * DSP4.distance >> 15);
172 DSP4.view_y2 = (DSP4.world_y >> 16) * DSP4.distance >> 15;
173 DSP4.view_xofs2 = DSP4.view_x2;
174 DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;
175
176 // 1. World x-location before transformation
177 // 2. Viewer x-position at the next
178 // 3. World y-location before perspective projection
179 // 4. Viewer y-position below the horizon
180 // 5. Number of raster lines drawn in this iteration
181 DSP4_CLEAR_OUT();
182 DSP4_WRITE_WORD((DSP4.world_x + DSP4.world_xenv) >> 16);
183 DSP4_WRITE_WORD(DSP4.view_x2);
184 DSP4_WRITE_WORD(DSP4.world_y >> 16);
185 DSP4_WRITE_WORD(DSP4.view_y2);
186
187 //////////////////////////////////////////////////////
188
189 // SR = 0x00
190
191 // determine # of raster lines used
192 DSP4.segments = DSP4.poly_raster[0][0] - DSP4.view_y2;
193
194 // prevent overdraw
195 if (DSP4.view_y2 >= DSP4.poly_raster[0][0])
196 DSP4.segments = 0;
197 else
198 DSP4.poly_raster[0][0] = DSP4.view_y2;
199
200 // don't draw outside the window
201 if (DSP4.view_y2 < DSP4.poly_top[0][0])
202 {
203 DSP4.segments = 0;
204
205 // flush remaining raster lines
206 if (DSP4.view_y1 >= DSP4.poly_top[0][0])
207 DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];
208 }
209
210 // SR = 0x80
211
212 DSP4_WRITE_WORD(DSP4.segments);
213
214 //////////////////////////////////////////////////////
215
216 // scan next command if no SR check needed
217 if (DSP4.segments)
218 {
219 int32 px_dx, py_dy;
220 int32 x_scroll, y_scroll;
221
222 // SR = 0x00
223
224 // linear interpolation (lerp) between projected points
225 px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;
226 py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;
227
228 // starting step values
229 x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);
230 y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);
231
232 // SR = 0x80
233
234 // rasterize line
235 for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)
236 {
237 // 1. HDMA memory pointer (bg1)
238 // 2. vertical scroll offset ($210E)
239 // 3. horizontal scroll offset ($210D)
240 DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);
241 DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);
242 DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);
243
244 // update memory address
245 DSP4.poly_ptr[0][0] -= 4;
246
247 // update screen values
248 x_scroll += px_dx;
249 y_scroll += py_dy;
250 }
251 }
252
253 ////////////////////////////////////////////////////
254 // Post-update
255
256 // update new viewer (x, y, scroll) to last raster line drawn
257 DSP4.view_x1 = DSP4.view_x2;
258 DSP4.view_y1 = DSP4.view_y2;
259 DSP4.view_xofs1 = DSP4.view_xofs2;
260 DSP4.view_yofs1 = DSP4.view_yofs2;
261
262 // add deltas for projection lines
263 DSP4.world_dx += SEX78(DSP4.world_ddx);
264 DSP4.world_dy += SEX78(DSP4.world_ddy);
265
266 // update projection lines
267 DSP4.world_x += (DSP4.world_dx + DSP4.world_xenv);
268 DSP4.world_y += DSP4.world_dy;
269
270 // update road turnoff position
271 DSP4.view_turnoff_x += DSP4.view_turnoff_dx;
272
273 ////////////////////////////////////////////////////
274 // command check
275
276 // scan next command
277 DSP4.in_count = 2;
278 DSP4_WAIT(1);
279
280 resume1:
281
282 // check for termination
283 DSP4.distance = DSP4_READ_WORD();
284 if (DSP4.distance == -0x8000)
285 break;
286
287 // road turnoff
288 if ((uint16) DSP4.distance == 0x8001)
289 {
290 DSP4.in_count = 6;
291 DSP4_WAIT(2);
292
293 resume2:
294
295 DSP4.distance = DSP4_READ_WORD();
296 DSP4.view_turnoff_x = DSP4_READ_WORD();
297 DSP4.view_turnoff_dx = DSP4_READ_WORD();
298
299 // factor in new changes
300 DSP4.view_x1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);
301 DSP4.view_xofs1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);
302
303 // update stepping values
304 DSP4.view_turnoff_x += DSP4.view_turnoff_dx;
305
306 DSP4.in_count = 2;
307 DSP4_WAIT(1);
308 }
309
310 // already have 2 bytes read
311 DSP4.in_count = 6;
312 DSP4_WAIT(3);
313
314 resume3:
315
316 // inspect inputs
317 DSP4.world_ddy = DSP4_READ_WORD();
318 DSP4.world_ddx = DSP4_READ_WORD();
319 DSP4.view_yofsenv = DSP4_READ_WORD();
320
321 // no envelope here
322 DSP4.world_xenv = 0;
323 }
324 while (1);
325
326 // terminate op
327 DSP4.waiting4command = TRUE;
328}
329
330static void DSP4_OP03 (void)
331{
332 DSP4.OAM_RowMax = 33;
333 memset(DSP4.OAM_Row, 0, 64);
334}
335
336static void DSP4_OP05 (void)
337{
338 DSP4.OAM_index = 0;
339 DSP4.OAM_bits = 0;
340 memset(DSP4.OAM_attr, 0, 32);
341 DSP4.sprite_count = 0;
342}
343
344static void DSP4_OP06 (void)
345{
346 DSP4_CLEAR_OUT();
347 DSP4_WRITE_16_WORD(DSP4.OAM_attr);
348}
349
350static void DSP4_OP07 (void)
351{
352 DSP4.waiting4command = FALSE;
353
354 // op flow control
355 switch (DSP4.Logic)
356 {
357 case 1: goto resume1; break;
358 case 2: goto resume2; break;
359 }
360
361 ////////////////////////////////////////////////////
362 // sort inputs
363
364 DSP4.world_y = DSP4_READ_DWORD();
365 DSP4.poly_bottom[0][0] = DSP4_READ_WORD();
366 DSP4.poly_top[0][0] = DSP4_READ_WORD();
367 DSP4.poly_cx[1][0] = DSP4_READ_WORD();
368 DSP4.viewport_bottom = DSP4_READ_WORD();
369 DSP4.world_x = DSP4_READ_DWORD();
370 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
371 DSP4.poly_ptr[0][0] = DSP4_READ_WORD();
372 DSP4.world_yofs = DSP4_READ_WORD();
373 DSP4.distance = DSP4_READ_WORD();
374 DSP4.view_y2 = DSP4_READ_WORD();
375 DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;
376 DSP4.view_x2 = DSP4_READ_WORD();
377 DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;
378 DSP4.view_yofsenv = DSP4_READ_WORD();
379
380 // initial (x, y, offset) at starting raster line
381 DSP4.view_x1 = DSP4.world_x >> 16;
382 DSP4.view_y1 = DSP4.world_y >> 16;
383 DSP4.view_xofs1 = DSP4.view_x1;
384 DSP4.view_yofs1 = DSP4.world_yofs;
385
386 // first raster line
387 DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];
388
389 do
390 {
391 ////////////////////////////////////////////////////
392 // process one iteration of projection
393
394 // add shaping
395 DSP4.view_x2 += DSP4.view_dx;
396 DSP4.view_y2 += DSP4.view_dy;
397
398 // vertical scroll calculation
399 DSP4.view_xofs2 = DSP4.view_x2;
400 DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;
401
402 // 1. Viewer x-position at the next
403 // 2. Viewer y-position below the horizon
404 // 3. Number of raster lines drawn in this iteration
405 DSP4_CLEAR_OUT();
406 DSP4_WRITE_WORD(DSP4.view_x2);
407 DSP4_WRITE_WORD(DSP4.view_y2);
408
409 //////////////////////////////////////////////////////
410
411 // SR = 0x00
412
413 // determine # of raster lines used
414 DSP4.segments = DSP4.view_y1 - DSP4.view_y2;
415
416 // prevent overdraw
417 if (DSP4.view_y2 >= DSP4.poly_raster[0][0])
418 DSP4.segments = 0;
419 else
420 DSP4.poly_raster[0][0] = DSP4.view_y2;
421
422 // don't draw outside the window
423 if (DSP4.view_y2 < DSP4.poly_top[0][0])
424 {
425 DSP4.segments = 0;
426
427 // flush remaining raster lines
428 if (DSP4.view_y1 >= DSP4.poly_top[0][0])
429 DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];
430 }
431
432 // SR = 0x80
433
434 DSP4_WRITE_WORD(DSP4.segments);
435
436 //////////////////////////////////////////////////////
437
438 // scan next command if no SR check needed
439 if (DSP4.segments)
440 {
441 int32 px_dx, py_dy;
442 int32 x_scroll, y_scroll;
443
444 // SR = 0x00
445
446 // linear interpolation (lerp) between projected points
447 px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;
448 py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;
449
450 // starting step values
451 x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);
452 y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);
453
454 // SR = 0x80
455
456 // rasterize line
457 for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)
458 {
459 // 1. HDMA memory pointer (bg2)
460 // 2. vertical scroll offset ($2110)
461 // 3. horizontal scroll offset ($210F)
462 DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);
463 DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);
464 DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);
465
466 // update memory address
467 DSP4.poly_ptr[0][0] -= 4;
468
469 // update screen values
470 x_scroll += px_dx;
471 y_scroll += py_dy;
472 }
473 }
474
475 /////////////////////////////////////////////////////
476 // Post-update
477
478 // update new viewer (x, y, scroll) to last raster line drawn
479 DSP4.view_x1 = DSP4.view_x2;
480 DSP4.view_y1 = DSP4.view_y2;
481 DSP4.view_xofs1 = DSP4.view_xofs2;
482 DSP4.view_yofs1 = DSP4.view_yofs2;
483
484 ////////////////////////////////////////////////////
485 // command check
486
487 // scan next command
488 DSP4.in_count = 2;
489 DSP4_WAIT(1);
490
491 resume1:
492
493 // check for opcode termination
494 DSP4.distance = DSP4_READ_WORD();
495 if (DSP4.distance == -0x8000)
496 break;
497
498 // already have 2 bytes in queue
499 DSP4.in_count = 10;
500 DSP4_WAIT(2);
501
502 resume2:
503
504 // inspect inputs
505 DSP4.view_y2 = DSP4_READ_WORD();
506 DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;
507 DSP4.view_x2 = DSP4_READ_WORD();
508 DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;
509 DSP4.view_yofsenv = DSP4_READ_WORD();
510 }
511 while (1);
512
513 DSP4.waiting4command = TRUE;
514}
515
516static void DSP4_OP08 (void)
517{
518 int16 win_left, win_right;
519 int16 view_x[2], view_y[2];
520 int16 envelope[2][2];
521
522 DSP4.waiting4command = FALSE;
523
524 // op flow control
525 switch (DSP4.Logic)
526 {
527 case 1: goto resume1; break;
528 case 2: goto resume2; break;
529 }
530
531 ////////////////////////////////////////////////////
532 // process initial inputs for two polygons
533
534 // clip values
535 DSP4.poly_clipRt[0][0] = DSP4_READ_WORD();
536 DSP4.poly_clipRt[0][1] = DSP4_READ_WORD();
537 DSP4.poly_clipRt[1][0] = DSP4_READ_WORD();
538 DSP4.poly_clipRt[1][1] = DSP4_READ_WORD();
539
540 DSP4.poly_clipLf[0][0] = DSP4_READ_WORD();
541 DSP4.poly_clipLf[0][1] = DSP4_READ_WORD();
542 DSP4.poly_clipLf[1][0] = DSP4_READ_WORD();
543 DSP4.poly_clipLf[1][1] = DSP4_READ_WORD();
544
545 // unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6)
546 DSP4_READ_WORD();
547 DSP4_READ_WORD();
548 DSP4_READ_WORD();
549 DSP4_READ_WORD();
550
551 // unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7)
552 DSP4_READ_WORD();
553 DSP4_READ_WORD();
554 DSP4_READ_WORD();
555 DSP4_READ_WORD();
556
557 // polygon centering (left, right)
558 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
559 DSP4.poly_cx[0][1] = DSP4_READ_WORD();
560 DSP4.poly_cx[1][0] = DSP4_READ_WORD();
561 DSP4.poly_cx[1][1] = DSP4_READ_WORD();
562
563 // HDMA pointer locations
564 DSP4.poly_ptr[0][0] = DSP4_READ_WORD();
565 DSP4.poly_ptr[0][1] = DSP4_READ_WORD();
566 DSP4.poly_ptr[1][0] = DSP4_READ_WORD();
567 DSP4.poly_ptr[1][1] = DSP4_READ_WORD();
568
569 // starting raster line below the horizon
570 DSP4.poly_bottom[0][0] = DSP4_READ_WORD();
571 DSP4.poly_bottom[0][1] = DSP4_READ_WORD();
572 DSP4.poly_bottom[1][0] = DSP4_READ_WORD();
573 DSP4.poly_bottom[1][1] = DSP4_READ_WORD();
574
575 // top boundary line to clip
576 DSP4.poly_top[0][0] = DSP4_READ_WORD();
577 DSP4.poly_top[0][1] = DSP4_READ_WORD();
578 DSP4.poly_top[1][0] = DSP4_READ_WORD();
579 DSP4.poly_top[1][1] = DSP4_READ_WORD();
580
581 // unknown
582 // (ex. 1P = $2FC8, $0034, $FF5C, $0035)
583 //
584 // (ex. 2P = $3178, $0034, $FFCC, $0035)
585 // (ex. 2P = $2FC8, $0034, $FFCC, $0035)
586 DSP4_READ_WORD();
587 DSP4_READ_WORD();
588 DSP4_READ_WORD();
589 DSP4_READ_WORD();
590
591 // look at guidelines for both polygon shapes
592 DSP4.distance = DSP4_READ_WORD();
593 view_x[0] = DSP4_READ_WORD();
594 view_y[0] = DSP4_READ_WORD();
595 view_x[1] = DSP4_READ_WORD();
596 view_y[1] = DSP4_READ_WORD();
597
598 // envelope shaping guidelines (one frame only)
599 envelope[0][0] = DSP4_READ_WORD();
600 envelope[0][1] = DSP4_READ_WORD();
601 envelope[1][0] = DSP4_READ_WORD();
602 envelope[1][1] = DSP4_READ_WORD();
603
604 // starting base values to project from
605 DSP4.poly_start[0] = view_x[0];
606 DSP4.poly_start[1] = view_x[1];
607
608 // starting raster lines to begin drawing
609 DSP4.poly_raster[0][0] = view_y[0];
610 DSP4.poly_raster[0][1] = view_y[0];
611 DSP4.poly_raster[1][0] = view_y[1];
612 DSP4.poly_raster[1][1] = view_y[1];
613
614 // starting distances
615 DSP4.poly_plane[0] = DSP4.distance;
616 DSP4.poly_plane[1] = DSP4.distance;
617
618 // SR = 0x00
619
620 // re-center coordinates
621 win_left = DSP4.poly_cx[0][0] - view_x[0] + envelope[0][0];
622 win_right = DSP4.poly_cx[0][1] - view_x[0] + envelope[0][1];
623
624 // saturate offscreen data for polygon #1
625 if (win_left < DSP4.poly_clipLf[0][0])
626 win_left = DSP4.poly_clipLf[0][0];
627 if (win_left > DSP4.poly_clipRt[0][0])
628 win_left = DSP4.poly_clipRt[0][0];
629 if (win_right < DSP4.poly_clipLf[0][1])
630 win_right = DSP4.poly_clipLf[0][1];
631 if (win_right > DSP4.poly_clipRt[0][1])
632 win_right = DSP4.poly_clipRt[0][1];
633
634 // SR = 0x80
635
636 // initial output for polygon #1
637 DSP4_CLEAR_OUT();
638 DSP4_WRITE_BYTE(win_left & 0xff);
639 DSP4_WRITE_BYTE(win_right & 0xff);
640
641 do
642 {
643 int16 polygon;
644
645 ////////////////////////////////////////////////////
646 // command check
647
648 // scan next command
649 DSP4.in_count = 2;
650 DSP4_WAIT(1);
651
652 resume1:
653
654 // terminate op
655 DSP4.distance = DSP4_READ_WORD();
656 if (DSP4.distance == -0x8000)
657 break;
658
659 // already have 2 bytes in queue
660 DSP4.in_count = 16;
661 DSP4_WAIT(2);
662
663 resume2:
664
665 // look at guidelines for both polygon shapes
666 view_x[0] = DSP4_READ_WORD();
667 view_y[0] = DSP4_READ_WORD();
668 view_x[1] = DSP4_READ_WORD();
669 view_y[1] = DSP4_READ_WORD();
670
671 // envelope shaping guidelines (one frame only)
672 envelope[0][0] = DSP4_READ_WORD();
673 envelope[0][1] = DSP4_READ_WORD();
674 envelope[1][0] = DSP4_READ_WORD();
675 envelope[1][1] = DSP4_READ_WORD();
676
677 ////////////////////////////////////////////////////
678 // projection begins
679
680 // init
681 DSP4_CLEAR_OUT();
682
683 //////////////////////////////////////////////
684 // solid polygon renderer - 2 shapes
685
686 for (polygon = 0; polygon < 2; polygon++)
687 {
688 int32 left_inc, right_inc;
689 int16 x1_final, x2_final;
690 int16 env[2][2];
691 int16 poly;
692
693 // SR = 0x00
694
695 // # raster lines to draw
696 DSP4.segments = DSP4.poly_raster[polygon][0] - view_y[polygon];
697
698 // prevent overdraw
699 if (DSP4.segments > 0)
700 {
701 // bump drawing cursor
702 DSP4.poly_raster[polygon][0] = view_y[polygon];
703 DSP4.poly_raster[polygon][1] = view_y[polygon];
704 }
705 else
706 DSP4.segments = 0;
707
708 // don't draw outside the window
709 if (view_y[polygon] < DSP4.poly_top[polygon][0])
710 {
711 DSP4.segments = 0;
712
713 // flush remaining raster lines
714 if (view_y[polygon] >= DSP4.poly_top[polygon][0])
715 DSP4.segments = view_y[polygon] - DSP4.poly_top[polygon][0];
716 }
717
718 // SR = 0x80
719
720 // tell user how many raster structures to read in
721 DSP4_WRITE_WORD(DSP4.segments);
722
723 // normal parameters
724 poly = polygon;
725
726 /////////////////////////////////////////////////////
727
728 // scan next command if no SR check needed
729 if (DSP4.segments)
730 {
731 int32 w_left, w_right;
732
733 // road turnoff selection
734 if ((uint16) envelope[polygon][0] == (uint16) 0xc001)
735 poly = 1;
736 else
737 if (envelope[polygon][1] == 0x3fff)
738 poly = 1;
739
740 ///////////////////////////////////////////////
741 // left side of polygon
742
743 // perspective correction on additional shaping parameters
744 env[0][0] = envelope[polygon][0] * DSP4.poly_plane[poly] >> 15;
745 env[0][1] = envelope[polygon][0] * DSP4.distance >> 15;
746
747 // project new shapes (left side)
748 x1_final = view_x[poly] + env[0][0];
749 x2_final = DSP4.poly_start[poly] + env[0][1];
750
751 // interpolate between projected points with shaping
752 left_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4.segments) << 1;
753 if (DSP4.segments == 1)
754 left_inc = -left_inc;
755
756 ///////////////////////////////////////////////
757 // right side of polygon
758
759 // perspective correction on additional shaping parameters
760 env[1][0] = envelope[polygon][1] * DSP4.poly_plane[poly] >> 15;
761 env[1][1] = envelope[polygon][1] * DSP4.distance >> 15;
762
763 // project new shapes (right side)
764 x1_final = view_x[poly] + env[1][0];
765 x2_final = DSP4.poly_start[poly] + env[1][1];
766
767 // interpolate between projected points with shaping
768 right_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4.segments) << 1;
769 if (DSP4.segments == 1)
770 right_inc = -right_inc;
771
772 ///////////////////////////////////////////////
773 // update each point on the line
774
775 w_left = SEX16(DSP4.poly_cx[polygon][0] - DSP4.poly_start[poly] + env[0][0]);
776 w_right = SEX16(DSP4.poly_cx[polygon][1] - DSP4.poly_start[poly] + env[1][0]);
777
778 // update distance drawn into world
779 DSP4.poly_plane[polygon] = DSP4.distance;
780
781 // rasterize line
782 for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)
783 {
784 int16 x_left, x_right;
785
786 // project new coordinates
787 w_left += left_inc;
788 w_right += right_inc;
789
790 // grab integer portion, drop fraction (no rounding)
791 x_left = w_left >> 16;
792 x_right = w_right >> 16;
793
794 // saturate offscreen data
795 if (x_left < DSP4.poly_clipLf[polygon][0])
796 x_left = DSP4.poly_clipLf[polygon][0];
797 if (x_left > DSP4.poly_clipRt[polygon][0])
798 x_left = DSP4.poly_clipRt[polygon][0];
799 if (x_right < DSP4.poly_clipLf[polygon][1])
800 x_right = DSP4.poly_clipLf[polygon][1];
801 if (x_right > DSP4.poly_clipRt[polygon][1])
802 x_right = DSP4.poly_clipRt[polygon][1];
803
804 // 1. HDMA memory pointer
805 // 2. Left window position ($2126/$2128)
806 // 3. Right window position ($2127/$2129)
807 DSP4_WRITE_WORD(DSP4.poly_ptr[polygon][0]);
808 DSP4_WRITE_BYTE(x_left & 0xff);
809 DSP4_WRITE_BYTE(x_right & 0xff);
810
811 // update memory pointers
812 DSP4.poly_ptr[polygon][0] -= 4;
813 DSP4.poly_ptr[polygon][1] -= 4;
814 } // end rasterize line
815 }
816
817 ////////////////////////////////////////////////
818 // Post-update
819
820 // new projection spot to continue rasterizing from
821 DSP4.poly_start[polygon] = view_x[poly];
822 } // end polygon rasterizer
823 }
824 while (1);
825
826 // unknown output
827 DSP4_CLEAR_OUT();
828 DSP4_WRITE_WORD(0);
829
830 DSP4.waiting4command = TRUE;
831}
832
833static void DSP4_OP09 (void)
834{
835 DSP4.waiting4command = FALSE;
836
837 // op flow control
838 switch (DSP4.Logic)
839 {
840 case 1: goto resume1; break;
841 case 2: goto resume2; break;
842 case 3: goto resume3; break;
843 case 4: goto resume4; break;
844 case 5: goto resume5; break;
845 case 6: goto resume6; break;
846 }
847
848 ////////////////////////////////////////////////////
849 // process initial inputs
850
851 // grab screen information
852 DSP4.viewport_cx = DSP4_READ_WORD();
853 DSP4.viewport_cy = DSP4_READ_WORD();
854 DSP4_READ_WORD(); // 0x0000
855 DSP4.viewport_left = DSP4_READ_WORD();
856 DSP4.viewport_right = DSP4_READ_WORD();
857 DSP4.viewport_top = DSP4_READ_WORD();
858 DSP4.viewport_bottom = DSP4_READ_WORD();
859
860 // starting raster line below the horizon
861 DSP4.poly_bottom[0][0] = DSP4.viewport_bottom - DSP4.viewport_cy;
862 DSP4.poly_raster[0][0] = 0x100;
863
864 do
865 {
866 ////////////////////////////////////////////////////
867 // check for new sprites
868
869 DSP4.in_count = 4;
870 DSP4_WAIT(1);
871
872 resume1:
873
874 ////////////////////////////////////////////////
875 // raster overdraw check
876
877 DSP4.raster = DSP4_READ_WORD();
878
879 // continue updating the raster line where overdraw begins
880 if (DSP4.raster < DSP4.poly_raster[0][0])
881 {
882 DSP4.sprite_clipy = DSP4.viewport_bottom - (DSP4.poly_bottom[0][0] - DSP4.raster);
883 DSP4.poly_raster[0][0] = DSP4.raster;
884 }
885
886 /////////////////////////////////////////////////
887 // identify sprite
888
889 // op termination
890 DSP4.distance = DSP4_READ_WORD();
891 if (DSP4.distance == -0x8000)
892 goto terminate;
893
894 // no sprite
895 if (DSP4.distance == 0x0000)
896 continue;
897
898 ////////////////////////////////////////////////////
899 // process projection information
900
901 // vehicle sprite
902 if ((uint16) DSP4.distance == 0x9000)
903 {
904 int16 car_left, car_right, car_back;
905 int16 impact_left, impact_back;
906 int16 world_spx, world_spy;
907 int16 view_spx, view_spy;
908 uint16 energy;
909
910 // we already have 4 bytes we want
911 DSP4.in_count = 14;
912 DSP4_WAIT(2);
913
914 resume2:
915
916 // filter inputs
917 energy = DSP4_READ_WORD();
918 impact_back = DSP4_READ_WORD();
919 car_back = DSP4_READ_WORD();
920 impact_left = DSP4_READ_WORD();
921 car_left = DSP4_READ_WORD();
922 DSP4.distance = DSP4_READ_WORD();
923 car_right = DSP4_READ_WORD();
924
925 // calculate car's world (x, y) values
926 world_spx = car_right - car_left;
927 world_spy = car_back;
928
929 // add in collision vector [needs bit-twiddling]
930 world_spx -= energy * (impact_left - car_left) >> 16;
931 world_spy -= energy * (car_back - impact_back) >> 16;
932
933 // perspective correction for world (x, y)
934 view_spx = world_spx * DSP4.distance >> 15;
935 view_spy = world_spy * DSP4.distance >> 15;
936
937 // convert to screen values
938 DSP4.sprite_x = DSP4.viewport_cx + view_spx;
939 DSP4.sprite_y = DSP4.viewport_bottom - (DSP4.poly_bottom[0][0] - view_spy);
940
941 // make the car's (x)-coordinate available
942 DSP4_CLEAR_OUT();
943 DSP4_WRITE_WORD(world_spx);
944
945 // grab a few remaining vehicle values
946 DSP4.in_count = 4;
947 DSP4_WAIT(3);
948
949 resume3:
950
951 // add vertical lift factor
952 DSP4.sprite_y += DSP4_READ_WORD();
953 }
954 // terrain sprite
955 else
956 {
957 int16 world_spx, world_spy;
958 int16 view_spx, view_spy;
959
960 // we already have 4 bytes we want
961 DSP4.in_count = 10;
962 DSP4_WAIT(4);
963
964 resume4:
965
966 // sort loop inputs
967 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
968 DSP4.poly_raster[0][1] = DSP4_READ_WORD();
969 world_spx = DSP4_READ_WORD();
970 world_spy = DSP4_READ_WORD();
971
972 // compute base raster line from the bottom
973 DSP4.segments = DSP4.poly_bottom[0][0] - DSP4.raster;
974
975 // perspective correction for world (x, y)
976 view_spx = world_spx * DSP4.distance >> 15;
977 view_spy = world_spy * DSP4.distance >> 15;
978
979 // convert to screen values
980 DSP4.sprite_x = DSP4.viewport_cx + view_spx - DSP4.poly_cx[0][0];
981 DSP4.sprite_y = DSP4.viewport_bottom - DSP4.segments + view_spy;
982 }
983
984 // default sprite size: 16x16
985 DSP4.sprite_size = 1;
986 DSP4.sprite_attr = DSP4_READ_WORD();
987
988 ////////////////////////////////////////////////////
989 // convert tile data to SNES OAM format
990
991 do
992 {
993 int16 sp_x, sp_y, sp_attr, sp_dattr;
994 int16 sp_dx, sp_dy;
995 int16 pixels;
996 uint16 header;
997 bool8 draw;
998
999 DSP4.in_count = 2;
1000 DSP4_WAIT(5);
1001
1002 resume5:
1003
1004 draw = TRUE;
1005
1006 // opcode termination
1007 DSP4.raster = DSP4_READ_WORD();
1008 if (DSP4.raster == -0x8000)
1009 goto terminate;
1010
1011 // stop code
1012 if (DSP4.raster == 0x0000 && !DSP4.sprite_size)
1013 break;
1014
1015 // toggle sprite size
1016 if (DSP4.raster == 0x0000)
1017 {
1018 DSP4.sprite_size = !DSP4.sprite_size;
1019 continue;
1020 }
1021
1022 // check for valid sprite header
1023 header = DSP4.raster;
1024 header >>= 8;
1025 if (header != 0x20 &&
1026 header != 0x2e && // This is for attractor sprite
1027 header != 0x40 &&
1028 header != 0x60 &&
1029 header != 0xa0 &&
1030 header != 0xc0 &&
1031 header != 0xe0)
1032 break;
1033
1034 // read in rest of sprite data
1035 DSP4.in_count = 4;
1036 DSP4_WAIT(6);
1037
1038 resume6:
1039
1040 draw = TRUE;
1041
1042 /////////////////////////////////////
1043 // process tile data
1044
1045 // sprite deltas
1046 sp_dattr = DSP4.raster;
1047 sp_dy = DSP4_READ_WORD();
1048 sp_dx = DSP4_READ_WORD();
1049
1050 // update coordinates to screen space
1051 sp_x = DSP4.sprite_x + sp_dx;
1052 sp_y = DSP4.sprite_y + sp_dy;
1053
1054 // update sprite nametable/attribute information
1055 sp_attr = DSP4.sprite_attr + sp_dattr;
1056
1057 // allow partially visibile tiles
1058 pixels = DSP4.sprite_size ? 15 : 7;
1059
1060 DSP4_CLEAR_OUT();
1061
1062 // transparent tile to clip off parts of a sprite (overdraw)
1063 if (DSP4.sprite_clipy - pixels <= sp_y && sp_y <= DSP4.sprite_clipy && sp_x >= DSP4.viewport_left - pixels && sp_x <= DSP4.viewport_right && DSP4.sprite_clipy >= DSP4.viewport_top - pixels && DSP4.sprite_clipy <= DSP4.viewport_bottom)
1064 DSP4_OP0B(&draw, sp_x, DSP4.sprite_clipy, 0x00EE, DSP4.sprite_size, 0);
1065
1066 // normal sprite tile
1067 if (sp_x >= DSP4.viewport_left - pixels && sp_x <= DSP4.viewport_right && sp_y >= DSP4.viewport_top - pixels && sp_y <= DSP4.viewport_bottom && sp_y <= DSP4.sprite_clipy)
1068 DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4.sprite_size, 0);
1069
1070 // no following OAM data
1071 DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1);
1072 }
1073 while (1);
1074 }
1075 while (1);
1076
1077 terminate:
1078 DSP4.waiting4command = TRUE;
1079}
1080
1081static void DSP4_OP0A (int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4)
1082{
1083 const uint16 OP0A_Values[16] =
1084 {
1085 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150,
1086 0xfe80, 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0
1087 };
1088
1089 *o4 = OP0A_Values[(n2 & 0x000f)];
1090 *o3 = OP0A_Values[(n2 & 0x00f0) >> 4];
1091 *o2 = OP0A_Values[(n2 & 0x0f00) >> 8];
1092 *o1 = OP0A_Values[(n2 & 0xf000) >> 12];
1093}
1094
1095static void DSP4_OP0B (bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop)
1096{
1097 int16 Row1, Row2;
1098
1099 // SR = 0x00
1100
1101 // align to nearest 8-pixel row
1102 Row1 = (sp_y >> 3) & 0x1f;
1103 Row2 = (Row1 + 1) & 0x1f;
1104
1105 // check boundaries
1106 if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb)))
1107 *draw = 0;
1108
1109 if (size)
1110 {
1111 if (DSP4.OAM_Row[Row1] + 1 >= DSP4.OAM_RowMax)
1112 *draw = 0;
1113 if (DSP4.OAM_Row[Row2] + 1 >= DSP4.OAM_RowMax)
1114 *draw = 0;
1115 }
1116 else
1117 {
1118 if (DSP4.OAM_Row[Row1] >= DSP4.OAM_RowMax)
1119 *draw = 0;
1120 }
1121
1122 // emulator fail-safe (unknown if this really exists)
1123 if (DSP4.sprite_count >= 128)
1124 *draw = 0;
1125
1126 // SR = 0x80
1127
1128 if (*draw)
1129 {
1130 // Row tiles
1131 if (size)
1132 {
1133 DSP4.OAM_Row[Row1] += 2;
1134 DSP4.OAM_Row[Row2] += 2;
1135 }
1136 else
1137 DSP4.OAM_Row[Row1]++;
1138
1139 // yield OAM output
1140 DSP4_WRITE_WORD(1);
1141
1142 // pack OAM data: x, y, name, attr
1143 DSP4_WRITE_BYTE(sp_x & 0xff);
1144 DSP4_WRITE_BYTE(sp_y & 0xff);
1145 DSP4_WRITE_WORD(sp_attr);
1146
1147 DSP4.sprite_count++;
1148
1149 // OAM: size, msb data
1150 // save post-oam table data for future retrieval
1151 DSP4.OAM_attr[DSP4.OAM_index] |= ((sp_x < 0 || sp_x > 255) << DSP4.OAM_bits);
1152 DSP4.OAM_bits++;
1153
1154 DSP4.OAM_attr[DSP4.OAM_index] |= (size << DSP4.OAM_bits);
1155 DSP4.OAM_bits++;
1156
1157 // move to next byte in buffer
1158 if (DSP4.OAM_bits == 16)
1159 {
1160 DSP4.OAM_bits = 0;
1161 DSP4.OAM_index++;
1162 }
1163 }
1164 else
1165 if (stop)
1166 // yield no OAM output
1167 DSP4_WRITE_WORD(0);
1168}
1169
1170static void DSP4_OP0D (void)
1171{
1172 DSP4.waiting4command = FALSE;
1173
1174 // op flow control
1175 switch (DSP4.Logic)
1176 {
1177 case 1: goto resume1; break;
1178 case 2: goto resume2; break;
1179 }
1180
1181 ////////////////////////////////////////////////////
1182 // process initial inputs
1183
1184 // sort inputs
1185 DSP4.world_y = DSP4_READ_DWORD();
1186 DSP4.poly_bottom[0][0] = DSP4_READ_WORD();
1187 DSP4.poly_top[0][0] = DSP4_READ_WORD();
1188 DSP4.poly_cx[1][0] = DSP4_READ_WORD();
1189 DSP4.viewport_bottom = DSP4_READ_WORD();
1190 DSP4.world_x = DSP4_READ_DWORD();
1191 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
1192 DSP4.poly_ptr[0][0] = DSP4_READ_WORD();
1193 DSP4.world_yofs = DSP4_READ_WORD();
1194 DSP4.world_dy = DSP4_READ_DWORD();
1195 DSP4.world_dx = DSP4_READ_DWORD();
1196 DSP4.distance = DSP4_READ_WORD();
1197 DSP4_READ_WORD(); // 0x0000
1198 DSP4.world_xenv = SEX78(DSP4_READ_WORD());
1199 DSP4.world_ddy = DSP4_READ_WORD();
1200 DSP4.world_ddx = DSP4_READ_WORD();
1201 DSP4.view_yofsenv = DSP4_READ_WORD();
1202
1203 // initial (x, y, offset) at starting raster line
1204 DSP4.view_x1 = (DSP4.world_x + DSP4.world_xenv) >> 16;
1205 DSP4.view_y1 = DSP4.world_y >> 16;
1206 DSP4.view_xofs1 = DSP4.world_x >> 16;
1207 DSP4.view_yofs1 = DSP4.world_yofs;
1208
1209 // first raster line
1210 DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];
1211
1212 do
1213 {
1214 ////////////////////////////////////////////////////
1215 // process one iteration of projection
1216
1217 // perspective projection of world (x, y, scroll) points
1218 // based on the current projection lines
1219 DSP4.view_x2 = (((DSP4.world_x + DSP4.world_xenv) >> 16) * DSP4.distance >> 15) + (DSP4.view_turnoff_x * DSP4.distance >> 15);
1220 DSP4.view_y2 = (DSP4.world_y >> 16) * DSP4.distance >> 15;
1221 DSP4.view_xofs2 = DSP4.view_x2;
1222 DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;
1223
1224 // 1. World x-location before transformation
1225 // 2. Viewer x-position at the current
1226 // 3. World y-location before perspective projection
1227 // 4. Viewer y-position below the horizon
1228 // 5. Number of raster lines drawn in this iteration
1229 DSP4_CLEAR_OUT();
1230 DSP4_WRITE_WORD((DSP4.world_x + DSP4.world_xenv) >> 16);
1231 DSP4_WRITE_WORD(DSP4.view_x2);
1232 DSP4_WRITE_WORD(DSP4.world_y >> 16);
1233 DSP4_WRITE_WORD(DSP4.view_y2);
1234
1235 //////////////////////////////////////////////////////////
1236
1237 // SR = 0x00
1238
1239 // determine # of raster lines used
1240 DSP4.segments = DSP4.view_y1 - DSP4.view_y2;
1241
1242 // prevent overdraw
1243 if (DSP4.view_y2 >= DSP4.poly_raster[0][0])
1244 DSP4.segments = 0;
1245 else
1246 DSP4.poly_raster[0][0] = DSP4.view_y2;
1247
1248 // don't draw outside the window
1249 if (DSP4.view_y2 < DSP4.poly_top[0][0])
1250 {
1251 DSP4.segments = 0;
1252
1253 // flush remaining raster lines
1254 if (DSP4.view_y1 >= DSP4.poly_top[0][0])
1255 DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];
1256 }
1257
1258 // SR = 0x80
1259
1260 DSP4_WRITE_WORD(DSP4.segments);
1261
1262 //////////////////////////////////////////////////////////
1263
1264 // scan next command if no SR check needed
1265 if (DSP4.segments)
1266 {
1267 int32 px_dx, py_dy;
1268 int32 x_scroll, y_scroll;
1269
1270 // SR = 0x00
1271
1272 // linear interpolation (lerp) between projected points
1273 px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;
1274 py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;
1275
1276 // starting step values
1277 x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);
1278 y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);
1279
1280 // SR = 0x80
1281
1282 // rasterize line
1283 for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)
1284 {
1285 // 1. HDMA memory pointer (bg1)
1286 // 2. vertical scroll offset ($210E)
1287 // 3. horizontal scroll offset ($210D)
1288 DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);
1289 DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);
1290 DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);
1291
1292 // update memory address
1293 DSP4.poly_ptr[0][0] -= 4;
1294
1295 // update screen values
1296 x_scroll += px_dx;
1297 y_scroll += py_dy;
1298 }
1299 }
1300
1301 /////////////////////////////////////////////////////
1302 // Post-update
1303
1304 // update new viewer (x, y, scroll) to last raster line drawn
1305 DSP4.view_x1 = DSP4.view_x2;
1306 DSP4.view_y1 = DSP4.view_y2;
1307 DSP4.view_xofs1 = DSP4.view_xofs2;
1308 DSP4.view_yofs1 = DSP4.view_yofs2;
1309
1310 // add deltas for projection lines
1311 DSP4.world_dx += SEX78(DSP4.world_ddx);
1312 DSP4.world_dy += SEX78(DSP4.world_ddy);
1313
1314 // update projection lines
1315 DSP4.world_x += (DSP4.world_dx + DSP4.world_xenv);
1316 DSP4.world_y += DSP4.world_dy;
1317
1318 ////////////////////////////////////////////////////
1319 // command check
1320
1321 // scan next command
1322 DSP4.in_count = 2;
1323 DSP4_WAIT(1);
1324
1325 resume1:
1326
1327 // inspect input
1328 DSP4.distance = DSP4_READ_WORD();
1329
1330 // terminate op
1331 if (DSP4.distance == -0x8000)
1332 break;
1333
1334 // already have 2 bytes in queue
1335 DSP4.in_count = 6;
1336 DSP4_WAIT(2);
1337
1338 resume2:
1339
1340 // inspect inputs
1341 DSP4.world_ddy = DSP4_READ_WORD();
1342 DSP4.world_ddx = DSP4_READ_WORD();
1343 DSP4.view_yofsenv = DSP4_READ_WORD();
1344
1345 // no envelope here
1346 DSP4.world_xenv = 0;
1347 }
1348 while (1);
1349
1350 DSP4.waiting4command = TRUE;
1351}
1352
1353static void DSP4_OP0E (void)
1354{
1355 DSP4.OAM_RowMax = 16;
1356 memset(DSP4.OAM_Row, 0, 64);
1357}
1358
1359static void DSP4_OP0F (void)
1360{
1361 DSP4.waiting4command = FALSE;
1362
1363 // op flow control
1364 switch (DSP4.Logic)
1365 {
1366 case 1: goto resume1; break;
1367 case 2: goto resume2; break;
1368 case 3: goto resume3; break;
1369 case 4: goto resume4; break;
1370 }
1371
1372 ////////////////////////////////////////////////////
1373 // process initial inputs
1374
1375 // sort inputs
1376 DSP4_READ_WORD(); // 0x0000
1377 DSP4.world_y = DSP4_READ_DWORD();
1378 DSP4.poly_bottom[0][0] = DSP4_READ_WORD();
1379 DSP4.poly_top[0][0] = DSP4_READ_WORD();
1380 DSP4.poly_cx[1][0] = DSP4_READ_WORD();
1381 DSP4.viewport_bottom = DSP4_READ_WORD();
1382 DSP4.world_x = DSP4_READ_DWORD();
1383 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
1384 DSP4.poly_ptr[0][0] = DSP4_READ_WORD();
1385 DSP4.world_yofs = DSP4_READ_WORD();
1386 DSP4.world_dy = DSP4_READ_DWORD();
1387 DSP4.world_dx = DSP4_READ_DWORD();
1388 DSP4.distance = DSP4_READ_WORD();
1389 DSP4_READ_WORD(); // 0x0000
1390 DSP4.world_xenv = DSP4_READ_DWORD();
1391 DSP4.world_ddy = DSP4_READ_WORD();
1392 DSP4.world_ddx = DSP4_READ_WORD();
1393 DSP4.view_yofsenv = DSP4_READ_WORD();
1394
1395 // initial (x, y, offset) at starting raster line
1396 DSP4.view_x1 = (DSP4.world_x + DSP4.world_xenv) >> 16;
1397 DSP4.view_y1 = DSP4.world_y >> 16;
1398 DSP4.view_xofs1 = DSP4.world_x >> 16;
1399 DSP4.view_yofs1 = DSP4.world_yofs;
1400 DSP4.view_turnoff_x = 0;
1401 DSP4.view_turnoff_dx = 0;
1402
1403 // first raster line
1404 DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];
1405
1406 do
1407 {
1408 ////////////////////////////////////////////////////
1409 // process one iteration of projection
1410
1411 // perspective projection of world (x, y, scroll) points
1412 // based on the current projection lines
1413 DSP4.view_x2 = ((DSP4.world_x + DSP4.world_xenv) >> 16) * DSP4.distance >> 15;
1414 DSP4.view_y2 = (DSP4.world_y >> 16) * DSP4.distance >> 15;
1415 DSP4.view_xofs2 = DSP4.view_x2;
1416 DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;
1417
1418 // 1. World x-location before transformation
1419 // 2. Viewer x-position at the next
1420 // 3. World y-location before perspective projection
1421 // 4. Viewer y-position below the horizon
1422 // 5. Number of raster lines drawn in this iteration
1423 DSP4_CLEAR_OUT();
1424 DSP4_WRITE_WORD((DSP4.world_x + DSP4.world_xenv) >> 16);
1425 DSP4_WRITE_WORD(DSP4.view_x2);
1426 DSP4_WRITE_WORD(DSP4.world_y >> 16);
1427 DSP4_WRITE_WORD(DSP4.view_y2);
1428
1429 //////////////////////////////////////////////////////
1430
1431 // SR = 0x00
1432
1433 // determine # of raster lines used
1434 DSP4.segments = DSP4.poly_raster[0][0] - DSP4.view_y2;
1435
1436 // prevent overdraw
1437 if (DSP4.view_y2 >= DSP4.poly_raster[0][0])
1438 DSP4.segments = 0;
1439 else
1440 DSP4.poly_raster[0][0] = DSP4.view_y2;
1441
1442 // don't draw outside the window
1443 if (DSP4.view_y2 < DSP4.poly_top[0][0])
1444 {
1445 DSP4.segments = 0;
1446
1447 // flush remaining raster lines
1448 if (DSP4.view_y1 >= DSP4.poly_top[0][0])
1449 DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];
1450 }
1451
1452 // SR = 0x80
1453
1454 DSP4_WRITE_WORD(DSP4.segments);
1455
1456 //////////////////////////////////////////////////////
1457
1458 // scan next command if no SR check needed
1459 if (DSP4.segments)
1460 {
1461 int32 px_dx, py_dy;
1462 int32 x_scroll, y_scroll;
1463
1464 for (DSP4.lcv = 0; DSP4.lcv < 4; DSP4.lcv++)
1465 {
1466 // grab inputs
1467 DSP4.in_count = 4;
1468 DSP4_WAIT(1);
1469
1470 resume1:
1471
1472 for (;;)
1473 {
1474 int16 dist;
1475 int16 color, red, green, blue;
1476
1477 dist = DSP4_READ_WORD();
1478 color = DSP4_READ_WORD();
1479
1480 // U1+B5+G5+R5
1481 red = color & 0x1f;
1482 green = (color >> 5) & 0x1f;
1483 blue = (color >> 10) & 0x1f;
1484
1485 // dynamic lighting
1486 red = (red * dist >> 15) & 0x1f;
1487 green = (green * dist >> 15) & 0x1f;
1488 blue = (blue * dist >> 15) & 0x1f;
1489 color = red | (green << 5) | (blue << 10);
1490
1491 DSP4_CLEAR_OUT();
1492 DSP4_WRITE_WORD(color);
1493
1494 break;
1495 }
1496 }
1497
1498 //////////////////////////////////////////////////////
1499
1500 // SR = 0x00
1501
1502 // linear interpolation (lerp) between projected points
1503 px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;
1504 py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;
1505
1506 // starting step values
1507 x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);
1508 y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);
1509
1510 // SR = 0x80
1511
1512 // rasterize line
1513 for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)
1514 {
1515 // 1. HDMA memory pointer
1516 // 2. vertical scroll offset ($210E)
1517 // 3. horizontal scroll offset ($210D)
1518 DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);
1519 DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);
1520 DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);
1521
1522 // update memory address
1523 DSP4.poly_ptr[0][0] -= 4;
1524
1525 // update screen values
1526 x_scroll += px_dx;
1527 y_scroll += py_dy;
1528 }
1529 }
1530
1531 ////////////////////////////////////////////////////
1532 // Post-update
1533
1534 // update new viewer (x, y, scroll) to last raster line drawn
1535 DSP4.view_x1 = DSP4.view_x2;
1536 DSP4.view_y1 = DSP4.view_y2;
1537 DSP4.view_xofs1 = DSP4.view_xofs2;
1538 DSP4.view_yofs1 = DSP4.view_yofs2;
1539
1540 // add deltas for projection lines
1541 DSP4.world_dx += SEX78(DSP4.world_ddx);
1542 DSP4.world_dy += SEX78(DSP4.world_ddy);
1543
1544 // update projection lines
1545 DSP4.world_x += (DSP4.world_dx + DSP4.world_xenv);
1546 DSP4.world_y += DSP4.world_dy;
1547
1548 // update road turnoff position
1549 DSP4.view_turnoff_x += DSP4.view_turnoff_dx;
1550
1551 ////////////////////////////////////////////////////
1552 // command check
1553
1554 // scan next command
1555 DSP4.in_count = 2;
1556 DSP4_WAIT(2);
1557
1558 resume2:
1559
1560 // check for termination
1561 DSP4.distance = DSP4_READ_WORD();
1562 if (DSP4.distance == -0x8000)
1563 break;
1564
1565 // road splice
1566 if ((uint16) DSP4.distance == 0x8001)
1567 {
1568 DSP4.in_count = 6;
1569 DSP4_WAIT(3);
1570
1571 resume3:
1572
1573 DSP4.distance = DSP4_READ_WORD();
1574 DSP4.view_turnoff_x = DSP4_READ_WORD();
1575 DSP4.view_turnoff_dx = DSP4_READ_WORD();
1576
1577 // factor in new changes
1578 DSP4.view_x1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);
1579 DSP4.view_xofs1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);
1580
1581 // update stepping values
1582 DSP4.view_turnoff_x += DSP4.view_turnoff_dx;
1583
1584 DSP4.in_count = 2;
1585 DSP4_WAIT(2);
1586 }
1587
1588 // already have 2 bytes in queue
1589 DSP4.in_count = 6;
1590 DSP4_WAIT(4);
1591
1592 resume4:
1593
1594 // inspect inputs
1595 DSP4.world_ddy = DSP4_READ_WORD();
1596 DSP4.world_ddx = DSP4_READ_WORD();
1597 DSP4.view_yofsenv = DSP4_READ_WORD();
1598
1599 // no envelope here
1600 DSP4.world_xenv = 0;
1601 }
1602 while (1);
1603
1604 // terminate op
1605 DSP4.waiting4command = TRUE;
1606}
1607
1608static void DSP4_OP10 (void)
1609{
1610 DSP4.waiting4command = FALSE;
1611
1612 // op flow control
1613 switch (DSP4.Logic)
1614 {
1615 case 1: goto resume1; break;
1616 case 2: goto resume2; break;
1617 case 3: goto resume3; break;
1618 }
1619
1620 ////////////////////////////////////////////////////
1621 // sort inputs
1622
1623 DSP4_READ_WORD(); // 0x0000
1624 DSP4.world_y = DSP4_READ_DWORD();
1625 DSP4.poly_bottom[0][0] = DSP4_READ_WORD();
1626 DSP4.poly_top[0][0] = DSP4_READ_WORD();
1627 DSP4.poly_cx[1][0] = DSP4_READ_WORD();
1628 DSP4.viewport_bottom = DSP4_READ_WORD();
1629 DSP4.world_x = DSP4_READ_DWORD();
1630 DSP4.poly_cx[0][0] = DSP4_READ_WORD();
1631 DSP4.poly_ptr[0][0] = DSP4_READ_WORD();
1632 DSP4.world_yofs = DSP4_READ_WORD();
1633 DSP4.distance = DSP4_READ_WORD();
1634 DSP4.view_y2 = DSP4_READ_WORD();
1635 DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;
1636 DSP4.view_x2 = DSP4_READ_WORD();
1637 DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;
1638 DSP4.view_yofsenv = DSP4_READ_WORD();
1639
1640 // initial (x, y, offset) at starting raster line
1641 DSP4.view_x1 = DSP4.world_x >> 16;
1642 DSP4.view_y1 = DSP4.world_y >> 16;
1643 DSP4.view_xofs1 = DSP4.view_x1;
1644 DSP4.view_yofs1 = DSP4.world_yofs;
1645
1646 // first raster line
1647 DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];
1648
1649 do
1650 {
1651 ////////////////////////////////////////////////////
1652 // process one iteration of projection
1653
1654 // add shaping
1655 DSP4.view_x2 += DSP4.view_dx;
1656 DSP4.view_y2 += DSP4.view_dy;
1657
1658 // vertical scroll calculation
1659 DSP4.view_xofs2 = DSP4.view_x2;
1660 DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;
1661
1662 // 1. Viewer x-position at the next
1663 // 2. Viewer y-position below the horizon
1664 // 3. Number of raster lines drawn in this iteration
1665 DSP4_CLEAR_OUT();
1666 DSP4_WRITE_WORD(DSP4.view_x2);
1667 DSP4_WRITE_WORD(DSP4.view_y2);
1668
1669 //////////////////////////////////////////////////////
1670
1671 // SR = 0x00
1672
1673 // determine # of raster lines used
1674 DSP4.segments = DSP4.view_y1 - DSP4.view_y2;
1675
1676 // prevent overdraw
1677 if (DSP4.view_y2 >= DSP4.poly_raster[0][0])
1678 DSP4.segments = 0;
1679 else
1680 DSP4.poly_raster[0][0] = DSP4.view_y2;
1681
1682 // don't draw outside the window
1683 if (DSP4.view_y2 < DSP4.poly_top[0][0])
1684 {
1685 DSP4.segments = 0;
1686
1687 // flush remaining raster lines
1688 if (DSP4.view_y1 >= DSP4.poly_top[0][0])
1689 DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];
1690 }
1691
1692 // SR = 0x80
1693
1694 DSP4_WRITE_WORD(DSP4.segments);
1695
1696 //////////////////////////////////////////////////////
1697
1698 // scan next command if no SR check needed
1699 if (DSP4.segments)
1700 {
1701 for (DSP4.lcv = 0; DSP4.lcv < 4; DSP4.lcv++)
1702 {
1703 // grab inputs
1704 DSP4.in_count = 4;
1705 DSP4_WAIT(1);
1706
1707 resume1:
1708
1709 for (;;)
1710 {
1711 int16 dist;
1712 int16 color, red, green, blue;
1713
1714 dist = DSP4_READ_WORD();
1715 color = DSP4_READ_WORD();
1716
1717 // U1+B5+G5+R5
1718 red = color & 0x1f;
1719 green = (color >> 5) & 0x1f;
1720 blue = (color >> 10) & 0x1f;
1721
1722 // dynamic lighting
1723 red = (red * dist >> 15) & 0x1f;
1724 green = (green * dist >> 15) & 0x1f;
1725 blue = (blue * dist >> 15) & 0x1f;
1726 color = red | (green << 5) | (blue << 10);
1727
1728 DSP4_CLEAR_OUT();
1729 DSP4_WRITE_WORD(color);
1730
1731 break;
1732 }
1733 }
1734 }
1735
1736 //////////////////////////////////////////////////////
1737
1738 // scan next command if no SR check needed
1739 if (DSP4.segments)
1740 {
1741 int32 px_dx, py_dy;
1742 int32 x_scroll, y_scroll;
1743
1744 // SR = 0x00
1745
1746 // linear interpolation (lerp) between projected points
1747 px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;
1748 py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;
1749
1750 // starting step values
1751 x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);
1752 y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);
1753
1754 // SR = 0x80
1755
1756 // rasterize line
1757 for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)
1758 {
1759 // 1. HDMA memory pointer (bg2)
1760 // 2. vertical scroll offset ($2110)
1761 // 3. horizontal scroll offset ($210F)
1762 DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);
1763 DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);
1764 DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);
1765
1766 // update memory address
1767 DSP4.poly_ptr[0][0] -= 4;
1768
1769 // update screen values
1770 x_scroll += px_dx;
1771 y_scroll += py_dy;
1772 }
1773 }
1774
1775 /////////////////////////////////////////////////////
1776 // Post-update
1777
1778 // update new viewer (x, y, scroll) to last raster line drawn
1779 DSP4.view_x1 = DSP4.view_x2;
1780 DSP4.view_y1 = DSP4.view_y2;
1781 DSP4.view_xofs1 = DSP4.view_xofs2;
1782 DSP4.view_yofs1 = DSP4.view_yofs2;
1783
1784 ////////////////////////////////////////////////////
1785 // command check
1786
1787 // scan next command
1788 DSP4.in_count = 2;
1789 DSP4_WAIT(2);
1790
1791 resume2:
1792
1793 // check for opcode termination
1794 DSP4.distance = DSP4_READ_WORD();
1795 if (DSP4.distance == -0x8000)
1796 break;
1797
1798 // already have 2 bytes in queue
1799 DSP4.in_count = 10;
1800 DSP4_WAIT(3);
1801
1802 resume3:
1803
1804 // inspect inputs
1805 DSP4.view_y2 = DSP4_READ_WORD();
1806 DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;
1807 DSP4.view_x2 = DSP4_READ_WORD();
1808 DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;
1809 }
1810 while (1);
1811
1812 DSP4.waiting4command = TRUE;
1813}
1814
1815static void DSP4_OP11 (int16 A, int16 B, int16 C, int16 D, int16 *M)
1816{
1817 // 0x155 = 341 = Horizontal Width of the Screen
1818 *M = ((A * 0x0155 >> 2) & 0xf000) | ((B * 0x0155 >> 6) & 0x0f00) | ((C * 0x0155 >> 10) & 0x00f0) | ((D * 0x0155 >> 14) & 0x000f);
1819}
1820
1821static void DSP4_SetByte (void)
1822{
1823 // clear pending read
1824 if (DSP4.out_index < DSP4.out_count)
1825 {
1826 DSP4.out_index++;
1827 return;
1828 }
1829
1830 if (DSP4.waiting4command)
1831 {
1832 if (DSP4.half_command)
1833 {
1834 DSP4.command |= (DSP4.byte << 8);
1835 DSP4.in_index = 0;
1836 DSP4.waiting4command = FALSE;
1837 DSP4.half_command = FALSE;
1838 DSP4.out_count = 0;
1839 DSP4.out_index = 0;
1840
1841 DSP4.Logic = 0;
1842
1843 switch (DSP4.command)
1844 {
1845 case 0x0000: DSP4.in_count = 4; break;
1846 case 0x0001: DSP4.in_count = 44; break;
1847 case 0x0003: DSP4.in_count = 0; break;
1848 case 0x0005: DSP4.in_count = 0; break;
1849 case 0x0006: DSP4.in_count = 0; break;
1850 case 0x0007: DSP4.in_count = 34; break;
1851 case 0x0008: DSP4.in_count = 90; break;
1852 case 0x0009: DSP4.in_count = 14; break;
1853 case 0x000a: DSP4.in_count = 6; break;
1854 case 0x000b: DSP4.in_count = 6; break;
1855 case 0x000d: DSP4.in_count = 42; break;
1856 case 0x000e: DSP4.in_count = 0; break;
1857 case 0x000f: DSP4.in_count = 46; break;
1858 case 0x0010: DSP4.in_count = 36; break;
1859 case 0x0011: DSP4.in_count = 8; break;
1860 default:
1861 DSP4.waiting4command = TRUE;
1862 break;
1863 }
1864 }
1865 else
1866 {
1867 DSP4.command = DSP4.byte;
1868 DSP4.half_command = TRUE;
1869 }
1870 }
1871 else
1872 {
1873 DSP4.parameters[DSP4.in_index] = DSP4.byte;
1874 DSP4.in_index++;
1875 }
1876
1877 if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index)
1878 {
1879 // Actually execute the command
1880 DSP4.waiting4command = TRUE;
1881 DSP4.out_index = 0;
1882 DSP4.in_index = 0;
1883
1884 switch (DSP4.command)
1885 {
1886 // 16-bit multiplication
1887 case 0x0000:
1888 {
1889 int16 multiplier, multiplicand;
1890 int32 product;
1891
1892 multiplier = DSP4_READ_WORD();
1893 multiplicand = DSP4_READ_WORD();
1894
1895 DSP4_Multiply(multiplicand, multiplier, &product);
1896
1897 DSP4_CLEAR_OUT();
1898 DSP4_WRITE_WORD(product);
1899 DSP4_WRITE_WORD(product >> 16);
1900
1901 break;
1902 }
1903
1904 // single-player track projection
1905 case 0x0001:
1906 DSP4_OP01();
1907 break;
1908
1909 // single-player selection
1910 case 0x0003:
1911 DSP4_OP03();
1912 break;
1913
1914 // clear OAM
1915 case 0x0005:
1916 DSP4_OP05();
1917 break;
1918
1919 // transfer OAM
1920 case 0x0006:
1921 DSP4_OP06();
1922 break;
1923
1924 // single-player track turnoff projection
1925 case 0x0007:
1926 DSP4_OP07();
1927 break;
1928
1929 // solid polygon projection
1930 case 0x0008:
1931 DSP4_OP08();
1932 break;
1933
1934 // sprite projection
1935 case 0x0009:
1936 DSP4_OP09();
1937 break;
1938
1939 // unknown
1940 case 0x000A:
1941 {
1942 DSP4_READ_WORD();
1943 int16 in2a = DSP4_READ_WORD();
1944 DSP4_READ_WORD();
1945 int16 out1a, out2a, out3a, out4a;
1946
1947 DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a);
1948
1949 DSP4_CLEAR_OUT();
1950 DSP4_WRITE_WORD(out1a);
1951 DSP4_WRITE_WORD(out2a);
1952 DSP4_WRITE_WORD(out3a);
1953 DSP4_WRITE_WORD(out4a);
1954
1955 break;
1956 }
1957
1958 // set OAM
1959 case 0x000B:
1960 {
1961 int16 sp_x = DSP4_READ_WORD();
1962 int16 sp_y = DSP4_READ_WORD();
1963 int16 sp_attr = DSP4_READ_WORD();
1964 bool8 draw = TRUE;
1965
1966 DSP4_CLEAR_OUT();
1967 DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1);
1968
1969 break;
1970 }
1971
1972 // multi-player track projection
1973 case 0x000D:
1974 DSP4_OP0D();
1975 break;
1976
1977 // multi-player selection
1978 case 0x000E:
1979 DSP4_OP0E();
1980 break;
1981
1982 // single-player track projection with lighting
1983 case 0x000F:
1984 DSP4_OP0F();
1985 break;
1986
1987 // single-player track turnoff projection with lighting
1988 case 0x0010:
1989 DSP4_OP10();
1990 break;
1991
1992 // unknown: horizontal mapping command
1993 case 0x0011:
1994 {
1995 int16 a, b, c, d, m;
1996
1997 d = DSP4_READ_WORD();
1998 c = DSP4_READ_WORD();
1999 b = DSP4_READ_WORD();
2000 a = DSP4_READ_WORD();
2001
2002 DSP4_OP11(a, b, c, d, &m);
2003
2004 DSP4_CLEAR_OUT();
2005 DSP4_WRITE_WORD(m);
2006
2007 break;
2008 }
2009
2010 default:
2011 break;
2012 }
2013 }
2014}
2015
2016static void DSP4_GetByte (void)
2017{
2018 if (DSP4.out_count)
2019 {
2020 DSP4.byte = (uint8) DSP4.output[DSP4.out_index & 0x1FF];
2021
2022 DSP4.out_index++;
2023 if (DSP4.out_count == DSP4.out_index)
2024 DSP4.out_count = 0;
2025 }
2026 else
2027 DSP4.byte = 0xff;
2028}
2029
2030void DSP4SetByte (uint8 byte, uint16 address)
2031{
2032 if (address < DSP0.boundary)
2033 {
2034 DSP4.byte = byte;
2035 DSP4.address = address;
2036 DSP4_SetByte();
2037 }
2038}
2039
2040uint8 DSP4GetByte (uint16 address)
2041{
2042 if (address < DSP0.boundary)
2043 {
2044 DSP4.address = address;
2045 DSP4_GetByte();
2046 return (DSP4.byte);
2047 }
2048
2049 return (0x80);
2050}
2051