2
Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
5
http://sourceforge.net/projects/zsnes
6
https://zsnes.bountysource.com
8
This program is free software; you can redistribute it and/or
9
modify it under the terms of the GNU General Public License
10
version 2 as published by the Free Software Foundation.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27
Due recognition and credit are given on Overload's DSP website.
28
Thank those contributors for their hard work on this chip.
31
Fixed-point math reminder:
33
[sign, integer, fraction]
34
1.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0')
35
1.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0')
39
#define READ_WORD(s) (*(uint16 *) (s))
40
#define READ_DWORD(s) (*(uint32 *) (s))
41
#define WRITE_WORD(s, d) (*(uint16 *) (s)) = (d)
42
#define WRITE_DWORD(s, d) (*(uint32 *) (s)) = (d)
45
struct DSP4_vars_t DSP4_vars;
47
//////////////////////////////////////////////////////////////
51
static int16 DSP4_READ_WORD()
55
out = READ_WORD(DSP4.parameters + DSP4.in_index);
61
static int32 DSP4_READ_DWORD()
65
out = READ_DWORD(DSP4.parameters + DSP4.in_index);
72
//////////////////////////////////////////////////////////////
76
#define DSP4_CLEAR_OUT() \
77
{ DSP4.out_count = 0; DSP4.out_index = 0; }
79
#define DSP4_WRITE_BYTE( d ) \
80
{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count++; }
82
#define DSP4_WRITE_WORD( d ) \
83
{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count += 2; }
86
#define DSP4_WRITE_16_WORD( d ) \
87
{ memcpy(DSP4.output + DSP4.out_count, ( d ), 32); DSP4.out_count += 32; }
89
#define DSP4_WRITE_16_WORD( d ) \
90
{ int16 *p = ( d ), *end = ( d )+16; \
91
for (; p != end; p++) \
93
WRITE_WORD( DSP4.output + DSP4.out_count, *p ); \
95
DSP4.out_count += 32; \
100
#define DSP4_WRITE_DEBUG( x, d ) \
101
WRITE_WORD( nop + x, d );
105
#define DSP4_WRITE_DEBUG( x, d ) \
106
WRITE_WORD( nop + x, d );
109
//////////////////////////////////////////////////////////////
111
// used to wait for dsp i/o
113
#define DSP4_WAIT( x ) \
114
DSP4.in_index = 0; DSP4_vars.DSP4_Logic = x; return;
116
//////////////////////////////////////////////////////////////
119
#define SEX78( a ) ( ( (int32) ( (int16) (a) ) ) << 8 )
122
#define SEX16( a ) ( ( (int32) ( (int16) (a) ) ) << 16 )
125
#define U16( a ) ( (uint16) ( a ) )
129
#define U16( a ) ( (uint16) ( a ) )
132
//////////////////////////////////////////////////////////////
134
// Attention: This lookup table is not verified
135
static const uint16 div_lut[64] = { 0x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249, 0x1000, 0x0e38,
136
0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888, 0x0800, 0x0787, 0x071c, 0x06bc,
137
0x0666, 0x0618, 0x05d1, 0x0590, 0x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469,
138
0x0444, 0x0421, 0x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348,
139
0x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9, 0x02aa, 0x029c,
140
0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253, 0x0249, 0x023e, 0x0234, 0x022b,
141
0x0222, 0x0219, 0x0210, 0x0208, };
142
int16 DSP4_Inverse(int16 value)
154
return div_lut[value];
157
//////////////////////////////////////////////////////////////
160
void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop);
162
//////////////////////////////////////////////////////////////
165
void DSP4_Multiply(int16 Multiplicand, int16 Multiplier, int32 *Product)
167
*Product = (Multiplicand * Multiplier << 1) >> 1;
170
//////////////////////////////////////////////////////////////
175
DSP4.waiting4command = FALSE;
178
switch (DSP4_vars.DSP4_Logic)
188
////////////////////////////////////////////////////
189
// process initial inputs
192
DSP4_vars.world_y = DSP4_READ_DWORD();
193
DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
194
DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
195
DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
196
DSP4_vars.viewport_bottom = DSP4_READ_WORD();
197
DSP4_vars.world_x = DSP4_READ_DWORD();
198
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
199
DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
200
DSP4_vars.world_yofs = DSP4_READ_WORD();
201
DSP4_vars.world_dy = DSP4_READ_DWORD();
202
DSP4_vars.world_dx = DSP4_READ_DWORD();
203
DSP4_vars.distance = DSP4_READ_WORD();
204
DSP4_READ_WORD(); // 0x0000
205
DSP4_vars.world_xenv = DSP4_READ_DWORD();
206
DSP4_vars.world_ddy = DSP4_READ_WORD();
207
DSP4_vars.world_ddx = DSP4_READ_WORD();
208
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
210
// initial (x,y,offset) at starting DSP4_vars.raster line
211
DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16);
212
DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16);
213
DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16);
214
DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
215
DSP4_vars.view_turnoff_x = 0;
216
DSP4_vars.view_turnoff_dx = 0;
218
// first DSP4_vars.raster line
219
DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
223
////////////////////////////////////////////////////
224
// process one iteration of projection
226
// perspective projection of world (x,y,scroll) points
227
// based on the current projection lines
228
DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ));
229
DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15);
230
DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
231
DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
234
// 1. World x-location before transformation
235
// 2. Viewer x-position at the next
236
// 3. World y-location before perspective projection
237
// 4. Viewer y-position below the horizon
238
// 5. Number of DSP4_vars.raster lines drawn in this iteration
241
DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16));
242
DSP4_WRITE_WORD(DSP4_vars.view_x2);
243
DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16));
244
DSP4_WRITE_WORD(DSP4_vars.view_y2);
246
//////////////////////////////////////////////////////
250
// determine # of DSP4_vars.raster lines used
251
DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2;
254
if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
255
DSP4_vars.segments = 0;
257
DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
259
// don't draw outside the window
260
if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
262
DSP4_vars.segments = 0;
264
// flush remaining DSP4_vars.raster lines
265
if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
266
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
271
DSP4_WRITE_WORD(DSP4_vars.segments);
273
//////////////////////////////////////////////////////
275
// scan next command if no SR check needed
276
if (DSP4_vars.segments)
279
int32 x_scroll, y_scroll;
283
// linear interpolation (lerp) between projected points
284
px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
285
py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
287
// starting step values
288
x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
289
y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
294
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
296
// 1. HDMA memory pointer (bg1)
297
// 2. vertical scroll offset ($210E)
298
// 3. horizontal scroll offset ($210D)
300
DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
301
DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16));
302
DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16));
305
// update memory address
306
DSP4_vars.poly_ptr[0][0] -= 4;
308
// update screen values
314
////////////////////////////////////////////////////
317
// update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
318
DSP4_vars.view_x1 = DSP4_vars.view_x2;
319
DSP4_vars.view_y1 = DSP4_vars.view_y2;
320
DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
321
DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
323
// add deltas for projection lines
324
DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx);
325
DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy);
327
// update projection lines
328
DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv);
329
DSP4_vars.world_y += DSP4_vars.world_dy;
331
// update road turnoff position
332
DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
334
////////////////////////////////////////////////////
339
DSP4_WAIT(1) resume1 :
341
// check for termination
342
DSP4_vars.distance = DSP4_READ_WORD();
343
if (DSP4_vars.distance == -0x8000)
347
if( (uint16) DSP4_vars.distance == 0x8001 )
350
DSP4_WAIT(2) resume2:
352
DSP4_vars.distance = DSP4_READ_WORD();
353
DSP4_vars.view_turnoff_x = DSP4_READ_WORD();
354
DSP4_vars.view_turnoff_dx = DSP4_READ_WORD();
356
// factor in new changes
357
DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
358
DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
360
// update stepping values
361
DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
367
// already have 2 bytes read
369
DSP4_WAIT(3) resume3 :
372
DSP4_vars.world_ddy = DSP4_READ_WORD();
373
DSP4_vars.world_ddx = DSP4_READ_WORD();
374
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
377
DSP4_vars.world_xenv = 0;
382
DSP4.waiting4command = TRUE;
385
//////////////////////////////////////////////////////////////
390
DSP4_vars.OAM_RowMax = 33;
391
memset(DSP4_vars.OAM_Row, 0, 64);
395
//////////////////////////////////////////////////////////////
400
DSP4_vars.OAM_index = 0;
401
DSP4_vars.OAM_bits = 0;
402
memset(DSP4_vars.OAM_attr, 0, 32);
403
DSP4_vars.sprite_count = 0;
407
//////////////////////////////////////////////////////////////
412
DSP4_WRITE_16_WORD(DSP4_vars.OAM_attr);
415
//////////////////////////////////////////////////////////////
420
DSP4.waiting4command = FALSE;
423
switch (DSP4_vars.DSP4_Logic)
431
////////////////////////////////////////////////////
434
DSP4_vars.world_y = DSP4_READ_DWORD();
435
DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
436
DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
437
DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
438
DSP4_vars.viewport_bottom = DSP4_READ_WORD();
439
DSP4_vars.world_x = DSP4_READ_DWORD();
440
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
441
DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
442
DSP4_vars.world_yofs = DSP4_READ_WORD();
443
DSP4_vars.distance = DSP4_READ_WORD();
444
DSP4_vars.view_y2 = DSP4_READ_WORD();
445
DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
446
DSP4_vars.view_x2 = DSP4_READ_WORD();
447
DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
448
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
450
// initial (x,y,offset) at starting DSP4_vars.raster line
451
DSP4_vars.view_x1 = (int16)(DSP4_vars.world_x >> 16);
452
DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16);
453
DSP4_vars.view_xofs1 = DSP4_vars.view_x1;
454
DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
456
// first DSP4_vars.raster line
457
DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
462
////////////////////////////////////////////////////
463
// process one iteration of projection
466
DSP4_vars.view_x2 += DSP4_vars.view_dx;
467
DSP4_vars.view_y2 += DSP4_vars.view_dy;
469
// vertical scroll calculation
470
DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
471
DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
473
// 1. Viewer x-position at the next
474
// 2. Viewer y-position below the horizon
475
// 3. Number of DSP4_vars.raster lines drawn in this iteration
478
DSP4_WRITE_WORD(DSP4_vars.view_x2);
479
DSP4_WRITE_WORD(DSP4_vars.view_y2);
481
//////////////////////////////////////////////////////
485
// determine # of DSP4_vars.raster lines used
486
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
489
if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
490
DSP4_vars.segments = 0;
492
DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
494
// don't draw outside the window
495
if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
497
DSP4_vars.segments = 0;
499
// flush remaining DSP4_vars.raster lines
500
if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
501
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
506
DSP4_WRITE_WORD(DSP4_vars.segments);
508
//////////////////////////////////////////////////////
510
// scan next command if no SR check needed
511
if (DSP4_vars.segments)
514
int32 x_scroll, y_scroll;
518
// linear interpolation (lerp) between projected points
519
px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
520
py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
522
// starting step values
523
x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
524
y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
529
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
531
// 1. HDMA memory pointer (bg2)
532
// 2. vertical scroll offset ($2110)
533
// 3. horizontal scroll offset ($210F)
535
DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
536
DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16));
537
DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16));
539
// update memory address
540
DSP4_vars.poly_ptr[0][0] -= 4;
542
// update screen values
548
/////////////////////////////////////////////////////
551
// update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
552
DSP4_vars.view_x1 = DSP4_vars.view_x2;
553
DSP4_vars.view_y1 = DSP4_vars.view_y2;
554
DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
555
DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
557
////////////////////////////////////////////////////
562
DSP4_WAIT(1) resume1 :
564
// check for opcode termination
565
DSP4_vars.distance = DSP4_READ_WORD();
566
if (DSP4_vars.distance == -0x8000)
569
// already have 2 bytes in queue
571
DSP4_WAIT(2) resume2 :
574
DSP4_vars.view_y2 = DSP4_READ_WORD();
575
DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
576
DSP4_vars.view_x2 = DSP4_READ_WORD();
577
DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
578
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
582
DSP4.waiting4command = TRUE;
585
//////////////////////////////////////////////////////////////
589
int16 win_left, win_right;
590
int16 view_x[2], view_y[2];
591
int16 envelope[2][2];
593
DSP4.waiting4command = FALSE;
596
switch (DSP4_vars.DSP4_Logic)
604
////////////////////////////////////////////////////
605
// process initial inputs for two polygons
608
DSP4_vars.poly_clipRt[0][0] = DSP4_READ_WORD();
609
DSP4_vars.poly_clipRt[0][1] = DSP4_READ_WORD();
610
DSP4_vars.poly_clipRt[1][0] = DSP4_READ_WORD();
611
DSP4_vars.poly_clipRt[1][1] = DSP4_READ_WORD();
613
DSP4_vars.poly_clipLf[0][0] = DSP4_READ_WORD();
614
DSP4_vars.poly_clipLf[0][1] = DSP4_READ_WORD();
615
DSP4_vars.poly_clipLf[1][0] = DSP4_READ_WORD();
616
DSP4_vars.poly_clipLf[1][1] = DSP4_READ_WORD();
618
// unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6)
624
// unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7)
630
// polygon centering (left,right)
631
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
632
DSP4_vars.poly_cx[0][1] = DSP4_READ_WORD();
633
DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
634
DSP4_vars.poly_cx[1][1] = DSP4_READ_WORD();
636
// HDMA pointer locations
637
DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
638
DSP4_vars.poly_ptr[0][1] = DSP4_READ_WORD();
639
DSP4_vars.poly_ptr[1][0] = DSP4_READ_WORD();
640
DSP4_vars.poly_ptr[1][1] = DSP4_READ_WORD();
642
// starting DSP4_vars.raster line below the horizon
643
DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
644
DSP4_vars.poly_bottom[0][1] = DSP4_READ_WORD();
645
DSP4_vars.poly_bottom[1][0] = DSP4_READ_WORD();
646
DSP4_vars.poly_bottom[1][1] = DSP4_READ_WORD();
648
// top boundary line to clip
649
DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
650
DSP4_vars.poly_top[0][1] = DSP4_READ_WORD();
651
DSP4_vars.poly_top[1][0] = DSP4_READ_WORD();
652
DSP4_vars.poly_top[1][1] = DSP4_READ_WORD();
655
// (ex. 1P = $2FC8, $0034, $FF5C, $0035)
657
// (ex. 2P = $3178, $0034, $FFCC, $0035)
658
// (ex. 2P = $2FC8, $0034, $FFCC, $0035)
665
// look at guidelines for both polygon shapes
666
DSP4_vars.distance = DSP4_READ_WORD();
667
view_x[0] = DSP4_READ_WORD();
668
view_y[0] = DSP4_READ_WORD();
669
view_x[1] = DSP4_READ_WORD();
670
view_y[1] = DSP4_READ_WORD();
672
// envelope shaping guidelines (one frame only)
673
envelope[0][0] = DSP4_READ_WORD();
674
envelope[0][1] = DSP4_READ_WORD();
675
envelope[1][0] = DSP4_READ_WORD();
676
envelope[1][1] = DSP4_READ_WORD();
678
// starting base values to project from
679
DSP4_vars.poly_start[0] = view_x[0];
680
DSP4_vars.poly_start[1] = view_x[1];
682
// starting DSP4_vars.raster lines to begin drawing
683
DSP4_vars.poly_raster[0][0] = view_y[0];
684
DSP4_vars.poly_raster[0][1] = view_y[0];
685
DSP4_vars.poly_raster[1][0] = view_y[1];
686
DSP4_vars.poly_raster[1][1] = view_y[1];
688
// starting distances
689
DSP4_vars.poly_plane[0] = DSP4_vars.distance;
690
DSP4_vars.poly_plane[1] = DSP4_vars.distance;
694
// re-center coordinates
695
win_left = DSP4_vars.poly_cx[0][0] - view_x[0] + envelope[0][0];
696
win_right = DSP4_vars.poly_cx[0][1] - view_x[0] + envelope[0][1];
698
// saturate offscreen data for polygon #1
699
if (win_left < DSP4_vars.poly_clipLf[0][0])
701
win_left = DSP4_vars.poly_clipLf[0][0];
703
if (win_left > DSP4_vars.poly_clipRt[0][0])
705
win_left = DSP4_vars.poly_clipRt[0][0];
707
if (win_right < DSP4_vars.poly_clipLf[0][1])
709
win_right = DSP4_vars.poly_clipLf[0][1];
711
if (win_right > DSP4_vars.poly_clipRt[0][1])
713
win_right = DSP4_vars.poly_clipRt[0][1];
718
// initial output for polygon #1
720
DSP4_WRITE_BYTE(win_left & 0xff);
721
DSP4_WRITE_BYTE(win_right & 0xff);
727
////////////////////////////////////////////////////
732
DSP4_WAIT(1) resume1 :
735
DSP4_vars.distance = DSP4_READ_WORD();
736
if (DSP4_vars.distance == -0x8000)
739
// already have 2 bytes in queue
742
DSP4_WAIT(2) resume2 :
744
// look at guidelines for both polygon shapes
745
view_x[0] = DSP4_READ_WORD();
746
view_y[0] = DSP4_READ_WORD();
747
view_x[1] = DSP4_READ_WORD();
748
view_y[1] = DSP4_READ_WORD();
750
// envelope shaping guidelines (one frame only)
751
envelope[0][0] = DSP4_READ_WORD();
752
envelope[0][1] = DSP4_READ_WORD();
753
envelope[1][0] = DSP4_READ_WORD();
754
envelope[1][1] = DSP4_READ_WORD();
756
////////////////////////////////////////////////////
763
//////////////////////////////////////////////
764
// solid polygon renderer - 2 shapes
766
for (polygon = 0; polygon < 2; polygon++)
768
int32 left_inc, right_inc;
769
int16 x1_final, x2_final;
775
// # DSP4_vars.raster lines to draw
776
DSP4_vars.segments = DSP4_vars.poly_raster[polygon][0] - view_y[polygon];
779
if (DSP4_vars.segments > 0)
781
// bump drawing cursor
782
DSP4_vars.poly_raster[polygon][0] = view_y[polygon];
783
DSP4_vars.poly_raster[polygon][1] = view_y[polygon];
786
DSP4_vars.segments = 0;
788
// don't draw outside the window
789
if (view_y[polygon] < DSP4_vars.poly_top[polygon][0])
791
DSP4_vars.segments = 0;
793
// flush remaining DSP4_vars.raster lines
794
if (view_y[polygon] >= DSP4_vars.poly_top[polygon][0])
795
DSP4_vars.segments = view_y[polygon] - DSP4_vars.poly_top[polygon][0];
800
// tell user how many DSP4_vars.raster structures to read in
801
DSP4_WRITE_WORD(DSP4_vars.segments);
806
/////////////////////////////////////////////////////
808
// scan next command if no SR check needed
809
if (DSP4_vars.segments)
811
int32 win_left, win_right;
813
// road turnoff selection
814
if( (uint16) envelope[ polygon ][ 0 ] == (uint16) 0xc001 )
816
else if( envelope[ polygon ][ 1 ] == 0x3fff )
819
///////////////////////////////////////////////
820
// left side of polygon
822
// perspective correction on additional shaping parameters
823
env[0][0] = envelope[polygon][0] * DSP4_vars.poly_plane[poly] >> 15;
824
env[0][1] = envelope[polygon][0] * DSP4_vars.distance >> 15;
826
// project new shapes (left side)
827
x1_final = view_x[poly] + env[0][0];
828
x2_final = DSP4_vars.poly_start[poly] + env[0][1];
830
// interpolate between projected points with shaping
831
left_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1;
832
if (DSP4_vars.segments == 1)
833
left_inc = -left_inc;
835
///////////////////////////////////////////////
836
// right side of polygon
838
// perspective correction on additional shaping parameters
839
env[1][0] = envelope[polygon][1] * DSP4_vars.poly_plane[poly] >> 15;;
840
env[1][1] = envelope[polygon][1] * DSP4_vars.distance >> 15;
842
// project new shapes (right side)
843
x1_final = view_x[poly] + env[1][0];
844
x2_final = DSP4_vars.poly_start[poly] + env[1][1];
847
// interpolate between projected points with shaping
848
right_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1;
849
if (DSP4_vars.segments == 1)
850
right_inc = -right_inc;
852
///////////////////////////////////////////////
853
// update each point on the line
855
win_left = SEX16(DSP4_vars.poly_cx[polygon][0] - DSP4_vars.poly_start[poly] + env[0][0]);
856
win_right = SEX16(DSP4_vars.poly_cx[polygon][1] - DSP4_vars.poly_start[poly] + env[1][0]);
858
// update DSP4_vars.distance drawn into world
859
DSP4_vars.poly_plane[polygon] = DSP4_vars.distance;
862
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
864
int16 x_left, x_right;
866
// project new coordinates
867
win_left += left_inc;
868
win_right += right_inc;
870
// grab integer portion, drop fraction (no rounding)
871
x_left = (int16)(win_left >> 16);
872
x_right = (int16)(win_right >> 16);
874
// saturate offscreen data
875
if (x_left < DSP4_vars.poly_clipLf[polygon][0])
876
x_left = DSP4_vars.poly_clipLf[polygon][0];
877
if (x_left > DSP4_vars.poly_clipRt[polygon][0])
878
x_left = DSP4_vars.poly_clipRt[polygon][0];
879
if (x_right < DSP4_vars.poly_clipLf[polygon][1])
880
x_right = DSP4_vars.poly_clipLf[polygon][1];
881
if (x_right > DSP4_vars.poly_clipRt[polygon][1])
882
x_right = DSP4_vars.poly_clipRt[polygon][1];
884
// 1. HDMA memory pointer
885
// 2. Left window position ($2126/$2128)
886
// 3. Right window position ($2127/$2129)
888
DSP4_WRITE_WORD(DSP4_vars.poly_ptr[polygon][0]);
889
DSP4_WRITE_BYTE(x_left & 0xff);
890
DSP4_WRITE_BYTE(x_right & 0xff);
893
// update memory pointers
894
DSP4_vars.poly_ptr[polygon][0] -= 4;
895
DSP4_vars.poly_ptr[polygon][1] -= 4;
896
} // end rasterize line
899
////////////////////////////////////////////////
902
// new projection spot to continue rasterizing from
903
DSP4_vars.poly_start[polygon] = view_x[poly];
904
} // end polygon rasterizer
913
DSP4.waiting4command = TRUE;
916
//////////////////////////////////////////////////////////////
920
DSP4.waiting4command = FALSE;
923
switch (DSP4_vars.DSP4_Logic)
939
////////////////////////////////////////////////////
940
// process initial inputs
942
// grab screen information
943
DSP4_vars.viewport_cx = DSP4_READ_WORD();
944
DSP4_vars.viewport_cy = DSP4_READ_WORD();
945
DSP4_READ_WORD(); // 0x0000
946
DSP4_vars.viewport_left = DSP4_READ_WORD();
947
DSP4_vars.viewport_right = DSP4_READ_WORD();
948
DSP4_vars.viewport_top = DSP4_READ_WORD();
949
DSP4_vars.viewport_bottom = DSP4_READ_WORD();
951
// starting DSP4_vars.raster line below the horizon
952
DSP4_vars.poly_bottom[0][0] = DSP4_vars.viewport_bottom - DSP4_vars.viewport_cy;
953
DSP4_vars.poly_raster[0][0] = 0x100;
957
////////////////////////////////////////////////////
958
// check for new sprites
961
DSP4_WAIT(1) resume1 :
963
////////////////////////////////////////////////
964
// DSP4_vars.raster overdraw check
966
DSP4_vars.raster = DSP4_READ_WORD();
968
// continue updating the DSP4_vars.raster line where overdraw begins
969
if (DSP4_vars.raster < DSP4_vars.poly_raster[0][0])
971
DSP4_vars.sprite_clipy = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster);
972
DSP4_vars.poly_raster[0][0] = DSP4_vars.raster;
975
/////////////////////////////////////////////////
979
DSP4_vars.distance = DSP4_READ_WORD();
980
if (DSP4_vars.distance == -0x8000)
985
if (DSP4_vars.distance == 0x0000)
990
////////////////////////////////////////////////////
991
// process projection information
994
if ((uint16) DSP4_vars.distance == 0x9000)
996
int16 car_left, car_right, car_back;
997
int16 impact_left, impact_back;
998
int16 world_spx, world_spy;
999
int16 view_spx, view_spy;
1002
// we already have 4 bytes we want
1004
DSP4_WAIT(2) resume2 :
1007
energy = DSP4_READ_WORD();
1008
impact_back = DSP4_READ_WORD();
1009
car_back = DSP4_READ_WORD();
1010
impact_left = DSP4_READ_WORD();
1011
car_left = DSP4_READ_WORD();
1012
DSP4_vars.distance = DSP4_READ_WORD();
1013
car_right = DSP4_READ_WORD();
1015
// calculate car's world (x,y) values
1016
world_spx = car_right - car_left;
1017
world_spy = car_back;
1019
// add in collision vector [needs bit-twiddling]
1020
world_spx -= energy * (impact_left - car_left) >> 16;
1021
world_spy -= energy * (car_back - impact_back) >> 16;
1023
// perspective correction for world (x,y)
1024
view_spx = world_spx * DSP4_vars.distance >> 15;
1025
view_spy = world_spy * DSP4_vars.distance >> 15;
1027
// convert to screen values
1028
DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx;
1029
DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - view_spy);
1031
// make the car's (x)-coordinate available
1033
DSP4_WRITE_WORD(world_spx);
1035
// grab a few remaining vehicle values
1037
DSP4_WAIT(3) resume3 :
1039
// add vertical lift factor
1040
DSP4_vars.sprite_y += DSP4_READ_WORD();
1045
int16 world_spx, world_spy;
1046
int16 view_spx, view_spy;
1048
// we already have 4 bytes we want
1050
DSP4_WAIT(4) resume4 :
1053
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
1054
DSP4_vars.poly_raster[0][1] = DSP4_READ_WORD();
1055
world_spx = DSP4_READ_WORD();
1056
world_spy = DSP4_READ_WORD();
1058
// compute base DSP4_vars.raster line from the bottom
1059
DSP4_vars.segments = DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster;
1061
// perspective correction for world (x,y)
1062
view_spx = world_spx * DSP4_vars.distance >> 15;
1063
view_spy = world_spy * DSP4_vars.distance >> 15;
1065
// convert to screen values
1066
DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx - DSP4_vars.poly_cx[0][0];
1067
DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - DSP4_vars.segments + view_spy;
1070
// default sprite size: 16x16
1071
DSP4_vars.sprite_size = 1;
1072
DSP4_vars.sprite_attr = DSP4_READ_WORD();
1074
////////////////////////////////////////////////////
1075
// convert tile data to SNES OAM format
1081
int16 sp_x, sp_y, sp_attr, sp_dattr;
1088
DSP4_WAIT(5) resume5 :
1092
// opcode termination
1093
DSP4_vars.raster = DSP4_READ_WORD();
1094
if (DSP4_vars.raster == -0x8000)
1098
if (DSP4_vars.raster == 0x0000 && !DSP4_vars.sprite_size)
1101
// toggle sprite size
1102
if (DSP4_vars.raster == 0x0000)
1104
DSP4_vars.sprite_size = !DSP4_vars.sprite_size;
1108
// check for valid sprite header
1109
header = DSP4_vars.raster;
1111
if (header != 0x20 &&
1112
header != 0x2e && //This is for attractor sprite
1120
// read in rest of sprite data
1122
DSP4_WAIT(6) resume6 :
1126
/////////////////////////////////////
1127
// process tile data
1130
sp_dattr = DSP4_vars.raster;
1131
sp_dy = DSP4_READ_WORD();
1132
sp_dx = DSP4_READ_WORD();
1134
// update coordinates to screen space
1135
sp_x = DSP4_vars.sprite_x + sp_dx;
1136
sp_y = DSP4_vars.sprite_y + sp_dy;
1138
// update sprite nametable/attribute information
1139
sp_attr = DSP4_vars.sprite_attr + sp_dattr;
1141
// allow partially visibile tiles
1142
pixels = DSP4_vars.sprite_size ? 15 : 7;
1146
// transparent tile to clip off parts of a sprite (overdraw)
1147
if (DSP4_vars.sprite_clipy - pixels <= sp_y &&
1148
sp_y <= DSP4_vars.sprite_clipy &&
1149
sp_x >= DSP4_vars.viewport_left - pixels &&
1150
sp_x <= DSP4_vars.viewport_right &&
1151
DSP4_vars.sprite_clipy >= DSP4_vars.viewport_top - pixels &&
1152
DSP4_vars.sprite_clipy <= DSP4_vars.viewport_bottom)
1154
DSP4_OP0B(&draw, sp_x, DSP4_vars.sprite_clipy, 0x00EE, DSP4_vars.sprite_size, 0);
1158
// normal sprite tile
1159
if (sp_x >= DSP4_vars.viewport_left - pixels &&
1160
sp_x <= DSP4_vars.viewport_right &&
1161
sp_y >= DSP4_vars.viewport_top - pixels &&
1162
sp_y <= DSP4_vars.viewport_bottom &&
1163
sp_y <= DSP4_vars.sprite_clipy)
1165
DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4_vars.sprite_size, 0);
1169
// no following OAM data
1170
DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1);
1176
terminate : DSP4.waiting4command = TRUE;
1179
//////////////////////////////////////////////////////////////
1181
const uint16 OP0A_Values[16] = { 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150, 0xfe80,
1182
0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0 };
1184
void DSP4_OP0A(int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4)
1186
*o4 = OP0A_Values[(n2 & 0x000f)];
1187
*o3 = OP0A_Values[(n2 & 0x00f0) >> 4];
1188
*o2 = OP0A_Values[(n2 & 0x0f00) >> 8];
1189
*o1 = OP0A_Values[(n2 & 0xf000) >> 12];
1192
//////////////////////////////////////////////////////////////
1194
void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop)
1200
// align to nearest 8-pixel row
1201
Row1 = (sp_y >> 3) & 0x1f;
1202
Row2 = (Row1 + 1) & 0x1f;
1205
if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb)))
1211
if (DSP4_vars.OAM_Row[Row1] + 1 >= DSP4_vars.OAM_RowMax)
1213
if (DSP4_vars.OAM_Row[Row2] + 1 >= DSP4_vars.OAM_RowMax)
1218
if (DSP4_vars.OAM_Row[Row1] >= DSP4_vars.OAM_RowMax)
1224
// emulator fail-safe (unknown if this really exists)
1225
if (DSP4_vars.sprite_count >= 128)
1237
DSP4_vars.OAM_Row[Row1] += 2;
1238
DSP4_vars.OAM_Row[Row2] += 2;
1242
DSP4_vars.OAM_Row[Row1]++;
1248
// pack OAM data: x,y,name,attr
1249
DSP4_WRITE_BYTE(sp_x & 0xff);
1250
DSP4_WRITE_BYTE(sp_y & 0xff);
1251
DSP4_WRITE_WORD(sp_attr);
1253
DSP4_vars.sprite_count++;
1255
// OAM: size,msb data
1256
// save post-oam table data for future retrieval
1257
DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= ((sp_x <0 || sp_x> 255) << DSP4_vars.OAM_bits);
1258
DSP4_vars.OAM_bits++;
1260
DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= (size << DSP4_vars.OAM_bits);
1261
DSP4_vars.OAM_bits++;
1263
// move to next byte in buffer
1264
if (DSP4_vars.OAM_bits == 16)
1266
DSP4_vars.OAM_bits = 0;
1267
DSP4_vars.OAM_index++;
1272
// yield no OAM output
1277
//////////////////////////////////////////////////////////////
1281
DSP4.waiting4command = FALSE;
1284
switch (DSP4_vars.DSP4_Logic)
1287
goto resume1; break;
1289
goto resume2; break;
1292
////////////////////////////////////////////////////
1293
// process initial inputs
1296
DSP4_vars.world_y = DSP4_READ_DWORD();
1297
DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
1298
DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
1299
DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
1300
DSP4_vars.viewport_bottom = DSP4_READ_WORD();
1301
DSP4_vars.world_x = DSP4_READ_DWORD();
1302
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
1303
DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
1304
DSP4_vars.world_yofs = DSP4_READ_WORD();
1305
DSP4_vars.world_dy = DSP4_READ_DWORD();
1306
DSP4_vars.world_dx = DSP4_READ_DWORD();
1307
DSP4_vars.distance = DSP4_READ_WORD();
1308
DSP4_READ_WORD(); // 0x0000
1309
DSP4_vars.world_xenv = SEX78(DSP4_READ_WORD());
1310
DSP4_vars.world_ddy = DSP4_READ_WORD();
1311
DSP4_vars.world_ddx = DSP4_READ_WORD();
1312
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
1314
// initial (x,y,offset) at starting DSP4_vars.raster line
1315
DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16);
1316
DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16);
1317
DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16);
1318
DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
1320
// first DSP4_vars.raster line
1321
DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
1326
////////////////////////////////////////////////////
1327
// process one iteration of projection
1329
// perspective projection of world (x,y,scroll) points
1330
// based on the current projection lines
1331
DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ));
1332
DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15);
1333
DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
1334
DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
1336
// 1. World x-location before transformation
1337
// 2. Viewer x-position at the current
1338
// 3. World y-location before perspective projection
1339
// 4. Viewer y-position below the horizon
1340
// 5. Number of DSP4_vars.raster lines drawn in this iteration
1343
DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16));
1344
DSP4_WRITE_WORD(DSP4_vars.view_x2);
1345
DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16));
1346
DSP4_WRITE_WORD(DSP4_vars.view_y2);
1348
//////////////////////////////////////////////////////////
1352
// determine # of DSP4_vars.raster lines used
1353
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
1356
if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
1357
DSP4_vars.segments = 0;
1359
DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
1361
// don't draw outside the window
1362
if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
1364
DSP4_vars.segments = 0;
1366
// flush remaining DSP4_vars.raster lines
1367
if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
1368
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
1373
DSP4_WRITE_WORD(DSP4_vars.segments);
1375
//////////////////////////////////////////////////////////
1377
// scan next command if no SR check needed
1378
if (DSP4_vars.segments)
1381
int32 x_scroll, y_scroll;
1385
// linear interpolation (lerp) between projected points
1386
px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
1387
py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
1389
// starting step values
1390
x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
1391
y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
1396
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
1398
// 1. HDMA memory pointer (bg1)
1399
// 2. vertical scroll offset ($210E)
1400
// 3. horizontal scroll offset ($210D)
1402
DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
1403
DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16));
1404
DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16));
1407
// update memory address
1408
DSP4_vars.poly_ptr[0][0] -= 4;
1410
// update screen values
1416
/////////////////////////////////////////////////////
1419
// update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
1420
DSP4_vars.view_x1 = DSP4_vars.view_x2;
1421
DSP4_vars.view_y1 = DSP4_vars.view_y2;
1422
DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
1423
DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
1425
// add deltas for projection lines
1426
DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx);
1427
DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy);
1429
// update projection lines
1430
DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv);
1431
DSP4_vars.world_y += DSP4_vars.world_dy;
1433
////////////////////////////////////////////////////
1436
// scan next command
1438
DSP4_WAIT(1) resume1 :
1441
DSP4_vars.distance = DSP4_READ_WORD();
1444
if (DSP4_vars.distance == -0x8000)
1447
// already have 2 bytes in queue
1449
DSP4_WAIT(2) resume2:
1452
DSP4_vars.world_ddy = DSP4_READ_WORD();
1453
DSP4_vars.world_ddx = DSP4_READ_WORD();
1454
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
1457
DSP4_vars.world_xenv = 0;
1461
DSP4.waiting4command = TRUE;
1464
//////////////////////////////////////////////////////////////
1469
DSP4_vars.OAM_RowMax = 16;
1470
memset(DSP4_vars.OAM_Row, 0, 64);
1474
//////////////////////////////////////////////////////////////
1478
DSP4.waiting4command = FALSE;
1481
switch (DSP4_vars.DSP4_Logic)
1484
goto resume1; break;
1486
goto resume2; break;
1488
goto resume3; break;
1490
goto resume4; break;
1493
////////////////////////////////////////////////////
1494
// process initial inputs
1497
DSP4_READ_WORD(); // 0x0000
1498
DSP4_vars.world_y = DSP4_READ_DWORD();
1499
DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
1500
DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
1501
DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
1502
DSP4_vars.viewport_bottom = DSP4_READ_WORD();
1503
DSP4_vars.world_x = DSP4_READ_DWORD();
1504
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
1505
DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
1506
DSP4_vars.world_yofs = DSP4_READ_WORD();
1507
DSP4_vars.world_dy = DSP4_READ_DWORD();
1508
DSP4_vars.world_dx = DSP4_READ_DWORD();
1509
DSP4_vars.distance = DSP4_READ_WORD();
1510
DSP4_READ_WORD(); // 0x0000
1511
DSP4_vars.world_xenv = DSP4_READ_DWORD();
1512
DSP4_vars.world_ddy = DSP4_READ_WORD();
1513
DSP4_vars.world_ddx = DSP4_READ_WORD();
1514
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
1516
// initial (x,y,offset) at starting DSP4_vars.raster line
1517
DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16);
1518
DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16);
1519
DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16);
1520
DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
1521
DSP4_vars.view_turnoff_x = 0;
1522
DSP4_vars.view_turnoff_dx = 0;
1524
// first DSP4_vars.raster line
1525
DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
1530
////////////////////////////////////////////////////
1531
// process one iteration of projection
1533
// perspective projection of world (x,y,scroll) points
1534
// based on the current projection lines
1535
DSP4_vars.view_x2 = (int16)(((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16) * DSP4_vars.distance >> 15);
1536
DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15);
1537
DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
1538
DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
1540
// 1. World x-location before transformation
1541
// 2. Viewer x-position at the next
1542
// 3. World y-location before perspective projection
1543
// 4. Viewer y-position below the horizon
1544
// 5. Number of DSP4_vars.raster lines drawn in this iteration
1547
DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16));
1548
DSP4_WRITE_WORD(DSP4_vars.view_x2);
1549
DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16));
1550
DSP4_WRITE_WORD(DSP4_vars.view_y2);
1552
//////////////////////////////////////////////////////
1556
// determine # of DSP4_vars.raster lines used
1557
DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2;
1560
if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
1561
DSP4_vars.segments = 0;
1563
DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
1565
// don't draw outside the window
1566
if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
1568
DSP4_vars.segments = 0;
1570
// flush remaining DSP4_vars.raster lines
1571
if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
1572
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
1577
DSP4_WRITE_WORD(DSP4_vars.segments);
1579
//////////////////////////////////////////////////////
1581
// scan next command if no SR check needed
1582
if (DSP4_vars.segments)
1585
int32 x_scroll, y_scroll;
1587
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++)
1596
int16 color, red, green, blue;
1598
distance = DSP4_READ_WORD();
1599
color = DSP4_READ_WORD();
1603
green = (color >> 5) & 0x1f;
1604
blue = (color >> 10) & 0x1f;
1607
red = (red * distance >> 15) & 0x1f;
1608
green = (green * distance >> 15) & 0x1f;
1609
blue = (blue * distance >> 15) & 0x1f;
1610
color = red | (green << 5) | (blue << 10);
1613
DSP4_WRITE_WORD(color);
1618
//////////////////////////////////////////////////////
1622
// linear interpolation (lerp) between projected points
1623
px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
1624
py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
1627
// starting step values
1628
x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
1629
y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
1634
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
1636
// 1. HDMA memory pointer
1637
// 2. vertical scroll offset ($210E)
1638
// 3. horizontal scroll offset ($210D)
1640
DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
1641
DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16));
1642
DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16));
1644
// update memory address
1645
DSP4_vars.poly_ptr[0][0] -= 4;
1647
// update screen values
1653
////////////////////////////////////////////////////
1656
// update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
1657
DSP4_vars.view_x1 = DSP4_vars.view_x2;
1658
DSP4_vars.view_y1 = DSP4_vars.view_y2;
1659
DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
1660
DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
1662
// add deltas for projection lines
1663
DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx);
1664
DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy);
1666
// update projection lines
1667
DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv);
1668
DSP4_vars.world_y += DSP4_vars.world_dy;
1670
// update road turnoff position
1671
DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
1673
////////////////////////////////////////////////////
1676
// scan next command
1678
DSP4_WAIT(2) resume2:
1680
// check for termination
1681
DSP4_vars.distance = DSP4_READ_WORD();
1682
if (DSP4_vars.distance == -0x8000)
1686
if( (uint16) DSP4_vars.distance == 0x8001 )
1689
DSP4_WAIT(3) resume3:
1691
DSP4_vars.distance = DSP4_READ_WORD();
1692
DSP4_vars.view_turnoff_x = DSP4_READ_WORD();
1693
DSP4_vars.view_turnoff_dx = DSP4_READ_WORD();
1695
// factor in new changes
1696
DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
1697
DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
1699
// update stepping values
1700
DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
1706
// already have 2 bytes in queue
1708
DSP4_WAIT(4) resume4 :
1711
DSP4_vars.world_ddy = DSP4_READ_WORD();
1712
DSP4_vars.world_ddx = DSP4_READ_WORD();
1713
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
1716
DSP4_vars.world_xenv = 0;
1721
DSP4.waiting4command = TRUE;
1724
//////////////////////////////////////////////////////////////
1729
DSP4.waiting4command = FALSE;
1732
switch (DSP4_vars.DSP4_Logic)
1735
goto resume1; break;
1737
goto resume2; break;
1739
goto resume3; break;
1742
////////////////////////////////////////////////////
1745
DSP4_READ_WORD(); // 0x0000
1746
DSP4_vars.world_y = DSP4_READ_DWORD();
1747
DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
1748
DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
1749
DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
1750
DSP4_vars.viewport_bottom = DSP4_READ_WORD();
1751
DSP4_vars.world_x = DSP4_READ_DWORD();
1752
DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
1753
DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
1754
DSP4_vars.world_yofs = DSP4_READ_WORD();
1755
DSP4_vars.distance = DSP4_READ_WORD();
1756
DSP4_vars.view_y2 = DSP4_READ_WORD();
1757
DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
1758
DSP4_vars.view_x2 = DSP4_READ_WORD();
1759
DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
1760
DSP4_vars.view_yofsenv = DSP4_READ_WORD();
1762
// initial (x,y,offset) at starting DSP4_vars.raster line
1763
DSP4_vars.view_x1 = (int16)(DSP4_vars.world_x >> 16);
1764
DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16);
1765
DSP4_vars.view_xofs1 = DSP4_vars.view_x1;
1766
DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
1768
// first DSP4_vars.raster line
1769
DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
1773
////////////////////////////////////////////////////
1774
// process one iteration of projection
1777
DSP4_vars.view_x2 += DSP4_vars.view_dx;
1778
DSP4_vars.view_y2 += DSP4_vars.view_dy;
1780
// vertical scroll calculation
1781
DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
1782
DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
1784
// 1. Viewer x-position at the next
1785
// 2. Viewer y-position below the horizon
1786
// 3. Number of DSP4_vars.raster lines drawn in this iteration
1789
DSP4_WRITE_WORD(DSP4_vars.view_x2);
1790
DSP4_WRITE_WORD(DSP4_vars.view_y2);
1792
//////////////////////////////////////////////////////
1796
// determine # of DSP4_vars.raster lines used
1797
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
1800
if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
1801
DSP4_vars.segments = 0;
1803
DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
1805
// don't draw outside the window
1806
if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
1808
DSP4_vars.segments = 0;
1810
// flush remaining DSP4_vars.raster lines
1811
if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
1812
DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
1817
DSP4_WRITE_WORD(DSP4_vars.segments);
1819
//////////////////////////////////////////////////////
1821
// scan next command if no SR check needed
1822
if (DSP4_vars.segments)
1824
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++)
1833
int16 color, red, green, blue;
1835
distance = DSP4_READ_WORD();
1836
color = DSP4_READ_WORD();
1840
green = (color >> 5) & 0x1f;
1841
blue = (color >> 10) & 0x1f;
1844
red = (red * distance >> 15) & 0x1f;
1845
green = (green * distance >> 15) & 0x1f;
1846
blue = (blue * distance >> 15) & 0x1f;
1847
color = red | (green << 5) | (blue << 10);
1850
DSP4_WRITE_WORD(color);
1856
//////////////////////////////////////////////////////
1858
// scan next command if no SR check needed
1859
if (DSP4_vars.segments)
1862
int32 x_scroll, y_scroll;
1866
// linear interpolation (lerp) between projected points
1867
px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
1868
py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
1870
// starting step values
1871
x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
1872
y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
1877
for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
1879
// 1. HDMA memory pointer (bg2)
1880
// 2. vertical scroll offset ($2110)
1881
// 3. horizontal scroll offset ($210F)
1883
DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
1884
DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16));
1885
DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16));
1887
// update memory address
1888
DSP4_vars.poly_ptr[0][0] -= 4;
1890
// update screen values
1896
/////////////////////////////////////////////////////
1899
// update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
1900
DSP4_vars.view_x1 = DSP4_vars.view_x2;
1901
DSP4_vars.view_y1 = DSP4_vars.view_y2;
1902
DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
1903
DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
1905
////////////////////////////////////////////////////
1908
// scan next command
1910
DSP4_WAIT(2) resume2 :
1912
// check for opcode termination
1913
DSP4_vars.distance = DSP4_READ_WORD();
1914
if (DSP4_vars.distance == -0x8000)
1917
// already have 2 bytes in queue
1919
DSP4_WAIT(3) resume3 :
1923
DSP4_vars.view_y2 = DSP4_READ_WORD();
1924
DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
1925
DSP4_vars.view_x2 = DSP4_READ_WORD();
1926
DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
1930
DSP4.waiting4command = TRUE;
1933
//////////////////////////////////////////////////////////////
1935
void DSP4_OP11(int16 A, int16 B, int16 C, int16 D, int16 *M)
1937
// 0x155 = 341 = Horizontal Width of the Screen
1938
*M = ((A * 0x0155 >> 2) & 0xf000) |
1939
((B * 0x0155 >> 6) & 0x0f00) |
1940
((C * 0x0155 >> 10) & 0x00f0) |
1941
((D * 0x0155 >> 14) & 0x000f);
1948
/////////////////////////////////////////////////////////////
1950
/////////////////////////////////////////////////////////////
1952
uint16 dsp4_address;
1956
memset(&DSP4, 0, sizeof(DSP4));
1957
DSP4.waiting4command = TRUE;
1962
// clear pending read
1963
if (DSP4.out_index < DSP4.out_count)
1969
if (DSP4.waiting4command)
1971
if (DSP4.half_command)
1973
DSP4.command |= (dsp4_byte << 8);
1975
DSP4.waiting4command = FALSE;
1976
DSP4.half_command = FALSE;
1980
DSP4_vars.DSP4_Logic = 0;
1983
switch (DSP4.command)
1986
DSP4.in_count = 4; break;
1988
DSP4.in_count = 44; break;
1990
DSP4.in_count = 0; break;
1992
DSP4.in_count = 0; break;
1994
DSP4.in_count = 0; break;
1996
DSP4.in_count = 34; break;
1998
DSP4.in_count = 90; break;
2000
DSP4.in_count = 14; break;
2002
DSP4.in_count = 6; break;
2004
DSP4.in_count = 6; break;
2006
DSP4.in_count = 42; break;
2008
DSP4.in_count = 0; break;
2010
DSP4.in_count = 46; break;
2012
DSP4.in_count = 36; break;
2014
DSP4.in_count = 8; break;
2016
DSP4.waiting4command = TRUE;
2022
DSP4.command = dsp4_byte;
2023
DSP4.half_command = TRUE;
2028
DSP4.parameters[DSP4.in_index] = dsp4_byte;
2032
if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index)
2034
// Actually execute the command
2035
DSP4.waiting4command = TRUE;
2039
switch (DSP4.command)
2041
// 16-bit multiplication
2044
int16 multiplier, multiplicand;
2047
multiplier = DSP4_READ_WORD();
2048
multiplicand = DSP4_READ_WORD();
2050
DSP4_Multiply(multiplicand, multiplier, &product);
2053
DSP4_WRITE_WORD((uint16)(product));
2054
DSP4_WRITE_WORD((uint16)(product >> 16));
2058
// single-player track projection
2062
// single-player selection
2074
// single-player track turnoff projection
2078
// solid polygon projection
2082
// sprite projection
2089
//int16 in1a = DSP4_READ_WORD();
2090
int16 in2a = DSP4_READ_WORD();
2091
//int16 in3a = DSP4_READ_WORD();
2092
int16 out1a, out2a, out3a, out4a;
2094
DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a);
2097
DSP4_WRITE_WORD(out1a);
2098
DSP4_WRITE_WORD(out2a);
2099
DSP4_WRITE_WORD(out3a);
2100
DSP4_WRITE_WORD(out4a);
2107
int16 sp_x = DSP4_READ_WORD();
2108
int16 sp_y = DSP4_READ_WORD();
2109
int16 sp_attr = DSP4_READ_WORD();
2114
DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1);
2118
// multi-player track projection
2122
// multi-player selection
2126
// single-player track projection with lighting
2130
// single-player track turnoff projection with lighting
2134
// unknown: horizontal mapping command
2137
int16 a, b, c, d, m;
2140
d = DSP4_READ_WORD();
2141
c = DSP4_READ_WORD();
2142
b = DSP4_READ_WORD();
2143
a = DSP4_READ_WORD();
2145
DSP4_OP11(a, b, c, d, &m);
2163
dsp4_byte = (uint8) DSP4.output[DSP4.out_index&0x1FF];
2165
if (DSP4.out_count == DSP4.out_index)