~ubuntu-branches/ubuntu/karmic/zsnes/karmic

« back to all changes in this revision

Viewing changes to src/chips/dsp4emu.c

  • Committer: Bazaar Package Importer
  • Author(s): Joshua Kwan
  • Date: 2007-06-04 21:46:47 UTC
  • mfrom: (1.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070604214647-j5zowa8vplkug0yj
Tags: 1.510-1
* New upstream release - all patches merged. closes: #380734, #419270
* Add a .desktop file, thanks Nicholas Wheeler. closes: #367942
* Include a ton of documentation that ships with the tarball.
  closes: #392143 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
 
3
 
 
4
http://www.zsnes.com
 
5
http://sourceforge.net/projects/zsnes
 
6
https://zsnes.bountysource.com
 
7
 
 
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.
 
11
 
 
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.
 
16
 
 
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.
 
20
*/
 
21
 
 
22
#include <string.h>
 
23
#include "dsp4emu.h"
 
24
 
 
25
 
 
26
/*
 
27
Due recognition and credit are given on Overload's DSP website.
 
28
Thank those contributors for their hard work on this chip.
 
29
 
 
30
 
 
31
Fixed-point math reminder:
 
32
 
 
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')
 
36
*/
 
37
 
 
38
 
 
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)
 
43
 
 
44
struct DSP4_t DSP4;
 
45
struct DSP4_vars_t DSP4_vars;
 
46
 
 
47
//////////////////////////////////////////////////////////////
 
48
 
 
49
// input protocol
 
50
 
 
51
static int16 DSP4_READ_WORD()
 
52
{
 
53
  int16 out;
 
54
 
 
55
  out = READ_WORD(DSP4.parameters + DSP4.in_index);
 
56
  DSP4.in_index += 2;
 
57
 
 
58
  return out;
 
59
}
 
60
 
 
61
static int32 DSP4_READ_DWORD()
 
62
{
 
63
  int32 out;
 
64
 
 
65
  out = READ_DWORD(DSP4.parameters + DSP4.in_index);
 
66
  DSP4.in_index += 4;
 
67
 
 
68
  return out;
 
69
}
 
70
 
 
71
 
 
72
//////////////////////////////////////////////////////////////
 
73
 
 
74
// output protocol
 
75
 
 
76
#define DSP4_CLEAR_OUT() \
 
77
{ DSP4.out_count = 0; DSP4.out_index = 0; }
 
78
 
 
79
#define DSP4_WRITE_BYTE( d ) \
 
80
{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count++; }
 
81
 
 
82
#define DSP4_WRITE_WORD( d ) \
 
83
{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count += 2; }
 
84
 
 
85
#ifndef MSB_FIRST
 
86
#define DSP4_WRITE_16_WORD( d ) \
 
87
{ memcpy(DSP4.output + DSP4.out_count, ( d ), 32); DSP4.out_count += 32; }
 
88
#else
 
89
#define DSP4_WRITE_16_WORD( d )                         \
 
90
{ int16 *p = ( d ), *end = ( d )+16;                    \
 
91
  for (; p != end; p++)                                 \
 
92
  {                                                     \
 
93
    WRITE_WORD( DSP4.output + DSP4.out_count, *p );     \
 
94
  }                                                     \
 
95
  DSP4.out_count += 32;                                 \
 
96
}
 
97
#endif
 
98
 
 
99
#ifdef PRINT_OP
 
100
#define DSP4_WRITE_DEBUG( x, d ) \
 
101
  WRITE_WORD( nop + x, d );
 
102
#endif
 
103
 
 
104
#ifdef DEBUG_DSP
 
105
#define DSP4_WRITE_DEBUG( x, d ) \
 
106
  WRITE_WORD( nop + x, d );
 
107
#endif
 
108
 
 
109
//////////////////////////////////////////////////////////////
 
110
 
 
111
// used to wait for dsp i/o
 
112
 
 
113
#define DSP4_WAIT( x ) \
 
114
  DSP4.in_index = 0; DSP4_vars.DSP4_Logic = x; return;
 
115
 
 
116
//////////////////////////////////////////////////////////////
 
117
 
 
118
// 1.7.8 -> 1.15.16
 
119
#define SEX78( a ) ( ( (int32) ( (int16) (a) ) ) << 8 )
 
120
 
 
121
// 1.15.0 -> 1.15.16
 
122
#define SEX16( a ) ( ( (int32) ( (int16) (a) ) ) << 16 )
 
123
 
 
124
#ifdef PRINT_OP
 
125
#define U16( a ) ( (uint16) ( a ) )
 
126
#endif
 
127
 
 
128
#ifdef DEBUG_DSP
 
129
#define U16( a ) ( (uint16) ( a ) )
 
130
#endif
 
131
 
 
132
//////////////////////////////////////////////////////////////
 
133
 
 
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)
 
143
{
 
144
  // saturate bounds
 
145
  if (value < 0)
 
146
  {
 
147
    value = 0;
 
148
  }
 
149
  if (value > 63)
 
150
  {
 
151
    value = 63;
 
152
  }
 
153
 
 
154
  return div_lut[value];
 
155
}
 
156
 
 
157
//////////////////////////////////////////////////////////////
 
158
 
 
159
// Prototype
 
160
void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop);
 
161
 
 
162
//////////////////////////////////////////////////////////////
 
163
 
 
164
// OP00
 
165
void DSP4_Multiply(int16 Multiplicand, int16 Multiplier, int32 *Product)
 
166
{
 
167
  *Product = (Multiplicand * Multiplier << 1) >> 1;
 
168
}
 
169
 
 
170
//////////////////////////////////////////////////////////////
 
171
 
 
172
 
 
173
void DSP4_OP01()
 
174
{
 
175
  DSP4.waiting4command = FALSE;
 
176
 
 
177
  // op flow control
 
178
  switch (DSP4_vars.DSP4_Logic)
 
179
  {
 
180
    case 1:
 
181
      goto resume1; break;
 
182
    case 2:
 
183
      goto resume2; break;
 
184
    case 3:
 
185
      goto resume3; break;
 
186
  }
 
187
 
 
188
  ////////////////////////////////////////////////////
 
189
  // process initial inputs
 
190
 
 
191
  // sort 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();
 
209
 
 
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;
 
217
 
 
218
  // first DSP4_vars.raster line
 
219
  DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
 
220
 
 
221
  do
 
222
  {
 
223
    ////////////////////////////////////////////////////
 
224
    // process one iteration of projection
 
225
 
 
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;
 
232
 
 
233
 
 
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
 
239
 
 
240
    DSP4_CLEAR_OUT();
 
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);
 
245
 
 
246
    //////////////////////////////////////////////////////
 
247
 
 
248
    // SR = 0x00
 
249
 
 
250
    // determine # of DSP4_vars.raster lines used
 
251
    DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2;
 
252
 
 
253
    // prevent overdraw
 
254
    if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
 
255
      DSP4_vars.segments = 0;
 
256
    else
 
257
      DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
 
258
 
 
259
    // don't draw outside the window
 
260
    if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
 
261
    {
 
262
      DSP4_vars.segments = 0;
 
263
 
 
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];
 
267
    }
 
268
 
 
269
    // SR = 0x80
 
270
 
 
271
    DSP4_WRITE_WORD(DSP4_vars.segments);
 
272
 
 
273
    //////////////////////////////////////////////////////
 
274
 
 
275
    // scan next command if no SR check needed
 
276
    if (DSP4_vars.segments)
 
277
    {
 
278
      int32 px_dx, py_dy;
 
279
      int32 x_scroll, y_scroll;
 
280
 
 
281
      // SR = 0x00
 
282
 
 
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;
 
286
 
 
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);
 
290
 
 
291
      // SR = 0x80
 
292
 
 
293
      // rasterize line
 
294
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
 
295
      {
 
296
        // 1. HDMA memory pointer (bg1)
 
297
        // 2. vertical scroll offset ($210E)
 
298
        // 3. horizontal scroll offset ($210D)
 
299
 
 
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));
 
303
 
 
304
 
 
305
        // update memory address
 
306
        DSP4_vars.poly_ptr[0][0] -= 4;
 
307
 
 
308
        // update screen values
 
309
        x_scroll += px_dx;
 
310
        y_scroll += py_dy;
 
311
      }
 
312
    }
 
313
 
 
314
    ////////////////////////////////////////////////////
 
315
    // Post-update
 
316
 
 
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;
 
322
 
 
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);
 
326
 
 
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;
 
330
 
 
331
    // update road turnoff position
 
332
    DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
 
333
 
 
334
    ////////////////////////////////////////////////////
 
335
    // command check
 
336
 
 
337
    // scan next command
 
338
    DSP4.in_count = 2;
 
339
    DSP4_WAIT(1) resume1 :
 
340
 
 
341
    // check for termination
 
342
    DSP4_vars.distance = DSP4_READ_WORD();
 
343
    if (DSP4_vars.distance == -0x8000)
 
344
      break;
 
345
 
 
346
    // road turnoff
 
347
    if( (uint16) DSP4_vars.distance == 0x8001 )
 
348
    {
 
349
      DSP4.in_count = 6;
 
350
      DSP4_WAIT(2) resume2:
 
351
 
 
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();
 
355
 
 
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 );
 
359
 
 
360
      // update stepping values
 
361
      DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
 
362
 
 
363
      DSP4.in_count = 2;
 
364
      DSP4_WAIT(1)
 
365
    }
 
366
 
 
367
    // already have 2 bytes read
 
368
    DSP4.in_count = 6;
 
369
    DSP4_WAIT(3) resume3 :
 
370
 
 
371
    // inspect inputs
 
372
    DSP4_vars.world_ddy = DSP4_READ_WORD();
 
373
    DSP4_vars.world_ddx = DSP4_READ_WORD();
 
374
    DSP4_vars.view_yofsenv = DSP4_READ_WORD();
 
375
 
 
376
    // no envelope here
 
377
    DSP4_vars.world_xenv = 0;
 
378
  }
 
379
  while (1);
 
380
 
 
381
  // terminate op
 
382
  DSP4.waiting4command = TRUE;
 
383
}
 
384
 
 
385
//////////////////////////////////////////////////////////////
 
386
 
 
387
 
 
388
void DSP4_OP03()
 
389
{
 
390
  DSP4_vars.OAM_RowMax = 33;
 
391
  memset(DSP4_vars.OAM_Row, 0, 64);
 
392
}
 
393
 
 
394
 
 
395
//////////////////////////////////////////////////////////////
 
396
 
 
397
 
 
398
void DSP4_OP05()
 
399
{
 
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;
 
404
}
 
405
 
 
406
 
 
407
//////////////////////////////////////////////////////////////
 
408
 
 
409
void DSP4_OP06()
 
410
{
 
411
  DSP4_CLEAR_OUT();
 
412
  DSP4_WRITE_16_WORD(DSP4_vars.OAM_attr);
 
413
}
 
414
 
 
415
//////////////////////////////////////////////////////////////
 
416
 
 
417
 
 
418
void DSP4_OP07()
 
419
{
 
420
  DSP4.waiting4command = FALSE;
 
421
 
 
422
  // op flow control
 
423
  switch (DSP4_vars.DSP4_Logic)
 
424
  {
 
425
    case 1:
 
426
      goto resume1; break;
 
427
    case 2:
 
428
      goto resume2; break;
 
429
  }
 
430
 
 
431
  ////////////////////////////////////////////////////
 
432
  // sort inputs
 
433
 
 
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();
 
449
 
 
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;
 
455
 
 
456
  // first DSP4_vars.raster line
 
457
  DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
 
458
 
 
459
 
 
460
  do
 
461
  {
 
462
    ////////////////////////////////////////////////////
 
463
    // process one iteration of projection
 
464
 
 
465
    // add shaping
 
466
    DSP4_vars.view_x2 += DSP4_vars.view_dx;
 
467
    DSP4_vars.view_y2 += DSP4_vars.view_dy;
 
468
 
 
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;
 
472
 
 
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
 
476
 
 
477
    DSP4_CLEAR_OUT();
 
478
    DSP4_WRITE_WORD(DSP4_vars.view_x2);
 
479
    DSP4_WRITE_WORD(DSP4_vars.view_y2);
 
480
 
 
481
    //////////////////////////////////////////////////////
 
482
 
 
483
    // SR = 0x00
 
484
 
 
485
    // determine # of DSP4_vars.raster lines used
 
486
    DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
 
487
 
 
488
    // prevent overdraw
 
489
    if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
 
490
      DSP4_vars.segments = 0;
 
491
    else
 
492
      DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
 
493
 
 
494
    // don't draw outside the window
 
495
    if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
 
496
    {
 
497
      DSP4_vars.segments = 0;
 
498
 
 
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];
 
502
    }
 
503
 
 
504
    // SR = 0x80
 
505
 
 
506
    DSP4_WRITE_WORD(DSP4_vars.segments);
 
507
 
 
508
    //////////////////////////////////////////////////////
 
509
 
 
510
    // scan next command if no SR check needed
 
511
    if (DSP4_vars.segments)
 
512
    {
 
513
      int32 px_dx, py_dy;
 
514
      int32 x_scroll, y_scroll;
 
515
 
 
516
      // SR = 0x00
 
517
 
 
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;
 
521
 
 
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);
 
525
 
 
526
      // SR = 0x80
 
527
 
 
528
      // rasterize line
 
529
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
 
530
      {
 
531
        // 1. HDMA memory pointer (bg2)
 
532
        // 2. vertical scroll offset ($2110)
 
533
        // 3. horizontal scroll offset ($210F)
 
534
 
 
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));
 
538
 
 
539
        // update memory address
 
540
        DSP4_vars.poly_ptr[0][0] -= 4;
 
541
 
 
542
        // update screen values
 
543
        x_scroll += px_dx;
 
544
        y_scroll += py_dy;
 
545
      }
 
546
    }
 
547
 
 
548
    /////////////////////////////////////////////////////
 
549
    // Post-update
 
550
 
 
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;
 
556
 
 
557
    ////////////////////////////////////////////////////
 
558
    // command check
 
559
 
 
560
    // scan next command
 
561
    DSP4.in_count = 2;
 
562
    DSP4_WAIT(1) resume1 :
 
563
 
 
564
    // check for opcode termination
 
565
    DSP4_vars.distance = DSP4_READ_WORD();
 
566
    if (DSP4_vars.distance == -0x8000)
 
567
      break;
 
568
 
 
569
    // already have 2 bytes in queue
 
570
    DSP4.in_count = 10;
 
571
    DSP4_WAIT(2) resume2 :
 
572
 
 
573
    // inspect inputs
 
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();
 
579
  }
 
580
  while (1);
 
581
 
 
582
  DSP4.waiting4command = TRUE;
 
583
}
 
584
 
 
585
//////////////////////////////////////////////////////////////
 
586
 
 
587
void DSP4_OP08()
 
588
{
 
589
  int16 win_left, win_right;
 
590
  int16 view_x[2], view_y[2];
 
591
  int16 envelope[2][2];
 
592
 
 
593
  DSP4.waiting4command = FALSE;
 
594
 
 
595
  // op flow control
 
596
  switch (DSP4_vars.DSP4_Logic)
 
597
  {
 
598
    case 1:
 
599
      goto resume1; break;
 
600
    case 2:
 
601
      goto resume2; break;
 
602
  }
 
603
 
 
604
  ////////////////////////////////////////////////////
 
605
  // process initial inputs for two polygons
 
606
 
 
607
  // clip values
 
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();
 
612
 
 
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();
 
617
 
 
618
  // unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6)
 
619
  DSP4_READ_WORD();
 
620
  DSP4_READ_WORD();
 
621
  DSP4_READ_WORD();
 
622
  DSP4_READ_WORD();
 
623
 
 
624
  // unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7)
 
625
  DSP4_READ_WORD();
 
626
  DSP4_READ_WORD();
 
627
  DSP4_READ_WORD();
 
628
  DSP4_READ_WORD();
 
629
 
 
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();
 
635
 
 
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();
 
641
 
 
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();
 
647
 
 
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();
 
653
 
 
654
  // unknown
 
655
  // (ex. 1P = $2FC8, $0034, $FF5C, $0035)
 
656
  //
 
657
  // (ex. 2P = $3178, $0034, $FFCC, $0035)
 
658
  // (ex. 2P = $2FC8, $0034, $FFCC, $0035)
 
659
 
 
660
  DSP4_READ_WORD();
 
661
  DSP4_READ_WORD();
 
662
  DSP4_READ_WORD();
 
663
  DSP4_READ_WORD();
 
664
 
 
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();
 
671
 
 
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();
 
677
 
 
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];
 
681
 
 
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];
 
687
 
 
688
  // starting distances
 
689
  DSP4_vars.poly_plane[0] = DSP4_vars.distance;
 
690
  DSP4_vars.poly_plane[1] = DSP4_vars.distance;
 
691
 
 
692
  // SR = 0x00
 
693
 
 
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];
 
697
 
 
698
  // saturate offscreen data for polygon #1
 
699
  if (win_left < DSP4_vars.poly_clipLf[0][0])
 
700
  {
 
701
    win_left = DSP4_vars.poly_clipLf[0][0];
 
702
  }
 
703
  if (win_left > DSP4_vars.poly_clipRt[0][0])
 
704
  {
 
705
    win_left = DSP4_vars.poly_clipRt[0][0];
 
706
  }
 
707
  if (win_right < DSP4_vars.poly_clipLf[0][1])
 
708
  {
 
709
    win_right = DSP4_vars.poly_clipLf[0][1];
 
710
  }
 
711
  if (win_right > DSP4_vars.poly_clipRt[0][1])
 
712
  {
 
713
    win_right = DSP4_vars.poly_clipRt[0][1];
 
714
  }
 
715
 
 
716
  // SR = 0x80
 
717
 
 
718
  // initial output for polygon #1
 
719
  DSP4_CLEAR_OUT();
 
720
  DSP4_WRITE_BYTE(win_left & 0xff);
 
721
  DSP4_WRITE_BYTE(win_right & 0xff);
 
722
 
 
723
 
 
724
  do
 
725
  {
 
726
    int16 polygon;
 
727
    ////////////////////////////////////////////////////
 
728
    // command check
 
729
 
 
730
    // scan next command
 
731
    DSP4.in_count = 2;
 
732
    DSP4_WAIT(1) resume1 :
 
733
 
 
734
    // terminate op
 
735
    DSP4_vars.distance = DSP4_READ_WORD();
 
736
    if (DSP4_vars.distance == -0x8000)
 
737
      break;
 
738
 
 
739
    // already have 2 bytes in queue
 
740
    DSP4.in_count = 16;
 
741
 
 
742
    DSP4_WAIT(2) resume2 :
 
743
 
 
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();
 
749
 
 
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();
 
755
 
 
756
    ////////////////////////////////////////////////////
 
757
    // projection begins
 
758
 
 
759
    // init
 
760
    DSP4_CLEAR_OUT();
 
761
 
 
762
 
 
763
    //////////////////////////////////////////////
 
764
    // solid polygon renderer - 2 shapes
 
765
 
 
766
    for (polygon = 0; polygon < 2; polygon++)
 
767
    {
 
768
      int32 left_inc, right_inc;
 
769
      int16 x1_final, x2_final;
 
770
      int16 env[2][2];
 
771
      int16 poly;
 
772
 
 
773
      // SR = 0x00
 
774
 
 
775
      // # DSP4_vars.raster lines to draw
 
776
      DSP4_vars.segments = DSP4_vars.poly_raster[polygon][0] - view_y[polygon];
 
777
 
 
778
      // prevent overdraw
 
779
      if (DSP4_vars.segments > 0)
 
780
      {
 
781
        // bump drawing cursor
 
782
        DSP4_vars.poly_raster[polygon][0] = view_y[polygon];
 
783
        DSP4_vars.poly_raster[polygon][1] = view_y[polygon];
 
784
      }
 
785
      else
 
786
        DSP4_vars.segments = 0;
 
787
 
 
788
      // don't draw outside the window
 
789
      if (view_y[polygon] < DSP4_vars.poly_top[polygon][0])
 
790
      {
 
791
        DSP4_vars.segments = 0;
 
792
 
 
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];
 
796
      }
 
797
 
 
798
      // SR = 0x80
 
799
 
 
800
      // tell user how many DSP4_vars.raster structures to read in
 
801
      DSP4_WRITE_WORD(DSP4_vars.segments);
 
802
 
 
803
      // normal parameters
 
804
      poly = polygon;
 
805
 
 
806
      /////////////////////////////////////////////////////
 
807
 
 
808
      // scan next command if no SR check needed
 
809
      if (DSP4_vars.segments)
 
810
      {
 
811
        int32 win_left, win_right;
 
812
 
 
813
        // road turnoff selection
 
814
        if( (uint16) envelope[ polygon ][ 0 ] == (uint16) 0xc001 )
 
815
          poly = 1;
 
816
        else if( envelope[ polygon ][ 1 ] == 0x3fff )
 
817
          poly = 1;
 
818
 
 
819
        ///////////////////////////////////////////////
 
820
        // left side of polygon
 
821
 
 
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;
 
825
 
 
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];
 
829
 
 
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;
 
834
 
 
835
        ///////////////////////////////////////////////
 
836
        // right side of polygon
 
837
 
 
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;
 
841
 
 
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];
 
845
 
 
846
 
 
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;
 
851
 
 
852
        ///////////////////////////////////////////////
 
853
        // update each point on the line
 
854
 
 
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]);
 
857
 
 
858
        // update DSP4_vars.distance drawn into world
 
859
        DSP4_vars.poly_plane[polygon] = DSP4_vars.distance;
 
860
 
 
861
        // rasterize line
 
862
        for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
 
863
        {
 
864
          int16 x_left, x_right;
 
865
 
 
866
          // project new coordinates
 
867
          win_left += left_inc;
 
868
          win_right += right_inc;
 
869
 
 
870
          // grab integer portion, drop fraction (no rounding)
 
871
          x_left = (int16)(win_left >> 16);
 
872
          x_right = (int16)(win_right >> 16);
 
873
 
 
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];
 
883
 
 
884
          // 1. HDMA memory pointer
 
885
          // 2. Left window position ($2126/$2128)
 
886
          // 3. Right window position ($2127/$2129)
 
887
 
 
888
          DSP4_WRITE_WORD(DSP4_vars.poly_ptr[polygon][0]);
 
889
          DSP4_WRITE_BYTE(x_left & 0xff);
 
890
          DSP4_WRITE_BYTE(x_right & 0xff);
 
891
 
 
892
 
 
893
          // update memory pointers
 
894
          DSP4_vars.poly_ptr[polygon][0] -= 4;
 
895
          DSP4_vars.poly_ptr[polygon][1] -= 4;
 
896
        } // end rasterize line
 
897
      }
 
898
 
 
899
      ////////////////////////////////////////////////
 
900
      // Post-update
 
901
 
 
902
      // new projection spot to continue rasterizing from
 
903
      DSP4_vars.poly_start[polygon] = view_x[poly];
 
904
    } // end polygon rasterizer
 
905
  }
 
906
  while (1);
 
907
 
 
908
  // unknown output
 
909
  DSP4_CLEAR_OUT();
 
910
  DSP4_WRITE_WORD(0);
 
911
 
 
912
 
 
913
  DSP4.waiting4command = TRUE;
 
914
}
 
915
 
 
916
//////////////////////////////////////////////////////////////
 
917
 
 
918
void DSP4_OP09()
 
919
{
 
920
  DSP4.waiting4command = FALSE;
 
921
 
 
922
  // op flow control
 
923
  switch (DSP4_vars.DSP4_Logic)
 
924
  {
 
925
    case 1:
 
926
      goto resume1; break;
 
927
    case 2:
 
928
      goto resume2; break;
 
929
    case 3:
 
930
      goto resume3; break;
 
931
    case 4:
 
932
      goto resume4; break;
 
933
    case 5:
 
934
      goto resume5; break;
 
935
    case 6:
 
936
      goto resume6; break;
 
937
  }
 
938
 
 
939
  ////////////////////////////////////////////////////
 
940
  // process initial inputs
 
941
 
 
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();
 
950
 
 
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;
 
954
 
 
955
  do
 
956
  {
 
957
    ////////////////////////////////////////////////////
 
958
    // check for new sprites
 
959
 
 
960
    DSP4.in_count = 4;
 
961
    DSP4_WAIT(1) resume1 :
 
962
 
 
963
    ////////////////////////////////////////////////
 
964
    // DSP4_vars.raster overdraw check
 
965
 
 
966
    DSP4_vars.raster = DSP4_READ_WORD();
 
967
 
 
968
    // continue updating the DSP4_vars.raster line where overdraw begins
 
969
    if (DSP4_vars.raster < DSP4_vars.poly_raster[0][0])
 
970
    {
 
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;
 
973
    }
 
974
 
 
975
    /////////////////////////////////////////////////
 
976
    // identify sprite
 
977
 
 
978
    // op termination
 
979
    DSP4_vars.distance = DSP4_READ_WORD();
 
980
    if (DSP4_vars.distance == -0x8000)
 
981
      goto terminate;
 
982
 
 
983
 
 
984
    // no sprite
 
985
    if (DSP4_vars.distance == 0x0000)
 
986
    {
 
987
      continue;
 
988
    }
 
989
 
 
990
    ////////////////////////////////////////////////////
 
991
    // process projection information
 
992
 
 
993
    // vehicle sprite
 
994
    if ((uint16) DSP4_vars.distance == 0x9000)
 
995
    {
 
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;
 
1000
      uint16 energy;
 
1001
 
 
1002
      // we already have 4 bytes we want
 
1003
      DSP4.in_count = 14;
 
1004
      DSP4_WAIT(2) resume2 :
 
1005
 
 
1006
      // filter inputs
 
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();
 
1014
 
 
1015
      // calculate car's world (x,y) values
 
1016
      world_spx = car_right - car_left;
 
1017
      world_spy = car_back;
 
1018
 
 
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;
 
1022
 
 
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;
 
1026
 
 
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);
 
1030
 
 
1031
      // make the car's (x)-coordinate available
 
1032
      DSP4_CLEAR_OUT();
 
1033
      DSP4_WRITE_WORD(world_spx);
 
1034
 
 
1035
      // grab a few remaining vehicle values
 
1036
      DSP4.in_count = 4;
 
1037
      DSP4_WAIT(3) resume3 :
 
1038
 
 
1039
      // add vertical lift factor
 
1040
      DSP4_vars.sprite_y += DSP4_READ_WORD();
 
1041
    }
 
1042
    // terrain sprite
 
1043
    else
 
1044
    {
 
1045
      int16 world_spx, world_spy;
 
1046
      int16 view_spx, view_spy;
 
1047
 
 
1048
      // we already have 4 bytes we want
 
1049
      DSP4.in_count = 10;
 
1050
      DSP4_WAIT(4) resume4 :
 
1051
 
 
1052
      // sort loop inputs
 
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();
 
1057
 
 
1058
      // compute base DSP4_vars.raster line from the bottom
 
1059
      DSP4_vars.segments = DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster;
 
1060
 
 
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;
 
1064
 
 
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;
 
1068
    }
 
1069
 
 
1070
    // default sprite size: 16x16
 
1071
    DSP4_vars.sprite_size = 1;
 
1072
    DSP4_vars.sprite_attr = DSP4_READ_WORD();
 
1073
 
 
1074
    ////////////////////////////////////////////////////
 
1075
    // convert tile data to SNES OAM format
 
1076
 
 
1077
    do
 
1078
    {
 
1079
      uint16 header;
 
1080
 
 
1081
      int16 sp_x, sp_y, sp_attr, sp_dattr;
 
1082
      int16 sp_dx, sp_dy;
 
1083
      int16 pixels;
 
1084
 
 
1085
      bool8 draw;
 
1086
 
 
1087
      DSP4.in_count = 2;
 
1088
      DSP4_WAIT(5) resume5 :
 
1089
 
 
1090
      draw = TRUE;
 
1091
 
 
1092
      // opcode termination
 
1093
      DSP4_vars.raster = DSP4_READ_WORD();
 
1094
      if (DSP4_vars.raster == -0x8000)
 
1095
        goto terminate;
 
1096
 
 
1097
      // stop code
 
1098
      if (DSP4_vars.raster == 0x0000 && !DSP4_vars.sprite_size)
 
1099
        break;
 
1100
 
 
1101
      // toggle sprite size
 
1102
      if (DSP4_vars.raster == 0x0000)
 
1103
      {
 
1104
        DSP4_vars.sprite_size = !DSP4_vars.sprite_size;
 
1105
        continue;
 
1106
      }
 
1107
 
 
1108
      // check for valid sprite header
 
1109
      header = DSP4_vars.raster;
 
1110
      header >>= 8;
 
1111
      if (header != 0x20 &&
 
1112
          header != 0x2e && //This is for attractor sprite
 
1113
          header != 0x40 &&
 
1114
          header != 0x60 &&
 
1115
          header != 0xa0 &&
 
1116
          header != 0xc0 &&
 
1117
          header != 0xe0)
 
1118
        break;
 
1119
 
 
1120
      // read in rest of sprite data
 
1121
      DSP4.in_count = 4;
 
1122
      DSP4_WAIT(6) resume6 :
 
1123
 
 
1124
      draw = TRUE;
 
1125
 
 
1126
      /////////////////////////////////////
 
1127
      // process tile data
 
1128
 
 
1129
      // sprite deltas
 
1130
      sp_dattr = DSP4_vars.raster;
 
1131
      sp_dy = DSP4_READ_WORD();
 
1132
      sp_dx = DSP4_READ_WORD();
 
1133
 
 
1134
      // update coordinates to screen space
 
1135
      sp_x = DSP4_vars.sprite_x + sp_dx;
 
1136
      sp_y = DSP4_vars.sprite_y + sp_dy;
 
1137
 
 
1138
      // update sprite nametable/attribute information
 
1139
      sp_attr = DSP4_vars.sprite_attr + sp_dattr;
 
1140
 
 
1141
      // allow partially visibile tiles
 
1142
      pixels = DSP4_vars.sprite_size ? 15 : 7;
 
1143
 
 
1144
      DSP4_CLEAR_OUT();
 
1145
 
 
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)
 
1153
      {
 
1154
        DSP4_OP0B(&draw, sp_x, DSP4_vars.sprite_clipy, 0x00EE, DSP4_vars.sprite_size, 0);
 
1155
      }
 
1156
 
 
1157
 
 
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)
 
1164
      {
 
1165
        DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4_vars.sprite_size, 0);
 
1166
      }
 
1167
 
 
1168
 
 
1169
      // no following OAM data
 
1170
      DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1);
 
1171
    }
 
1172
    while (1);
 
1173
  }
 
1174
  while (1);
 
1175
 
 
1176
  terminate : DSP4.waiting4command = TRUE;
 
1177
}
 
1178
 
 
1179
//////////////////////////////////////////////////////////////
 
1180
 
 
1181
const uint16 OP0A_Values[16] = { 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150, 0xfe80,
 
1182
                                 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0 };
 
1183
 
 
1184
void DSP4_OP0A(int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4)
 
1185
{
 
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];
 
1190
}
 
1191
 
 
1192
//////////////////////////////////////////////////////////////
 
1193
 
 
1194
void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop)
 
1195
{
 
1196
  int16 Row1, Row2;
 
1197
 
 
1198
  // SR = 0x00
 
1199
 
 
1200
  // align to nearest 8-pixel row
 
1201
  Row1 = (sp_y >> 3) & 0x1f;
 
1202
  Row2 = (Row1 + 1) & 0x1f;
 
1203
 
 
1204
  // check boundaries
 
1205
  if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb)))
 
1206
  {
 
1207
    *draw = 0;
 
1208
  }
 
1209
  if (size)
 
1210
  {
 
1211
    if (DSP4_vars.OAM_Row[Row1] + 1 >= DSP4_vars.OAM_RowMax)
 
1212
      *draw = 0;
 
1213
    if (DSP4_vars.OAM_Row[Row2] + 1 >= DSP4_vars.OAM_RowMax)
 
1214
      *draw = 0;
 
1215
  }
 
1216
  else
 
1217
  {
 
1218
    if (DSP4_vars.OAM_Row[Row1] >= DSP4_vars.OAM_RowMax)
 
1219
    {
 
1220
      *draw = 0;
 
1221
    }
 
1222
  }
 
1223
 
 
1224
  // emulator fail-safe (unknown if this really exists)
 
1225
  if (DSP4_vars.sprite_count >= 128)
 
1226
  {
 
1227
    *draw = 0;
 
1228
  }
 
1229
 
 
1230
  // SR = 0x80
 
1231
 
 
1232
  if (*draw)
 
1233
  {
 
1234
    // Row tiles
 
1235
    if (size)
 
1236
    {
 
1237
      DSP4_vars.OAM_Row[Row1] += 2;
 
1238
      DSP4_vars.OAM_Row[Row2] += 2;
 
1239
    }
 
1240
    else
 
1241
    {
 
1242
      DSP4_vars.OAM_Row[Row1]++;
 
1243
    }
 
1244
 
 
1245
    // yield OAM output
 
1246
    DSP4_WRITE_WORD(1);
 
1247
 
 
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);
 
1252
 
 
1253
    DSP4_vars.sprite_count++;
 
1254
 
 
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++;
 
1259
 
 
1260
    DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= (size << DSP4_vars.OAM_bits);
 
1261
    DSP4_vars.OAM_bits++;
 
1262
 
 
1263
    // move to next byte in buffer
 
1264
    if (DSP4_vars.OAM_bits == 16)
 
1265
    {
 
1266
      DSP4_vars.OAM_bits = 0;
 
1267
      DSP4_vars.OAM_index++;
 
1268
    }
 
1269
  }
 
1270
  else if (stop)
 
1271
  {
 
1272
    // yield no OAM output
 
1273
    DSP4_WRITE_WORD(0);
 
1274
  }
 
1275
}
 
1276
 
 
1277
//////////////////////////////////////////////////////////////
 
1278
 
 
1279
void DSP4_OP0D()
 
1280
{
 
1281
  DSP4.waiting4command = FALSE;
 
1282
 
 
1283
  // op flow control
 
1284
  switch (DSP4_vars.DSP4_Logic)
 
1285
  {
 
1286
    case 1:
 
1287
      goto resume1; break;
 
1288
    case 2:
 
1289
      goto resume2; break;
 
1290
  }
 
1291
 
 
1292
  ////////////////////////////////////////////////////
 
1293
  // process initial inputs
 
1294
 
 
1295
  // sort 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();
 
1313
 
 
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;
 
1319
 
 
1320
  // first DSP4_vars.raster line
 
1321
  DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
 
1322
 
 
1323
 
 
1324
  do
 
1325
  {
 
1326
    ////////////////////////////////////////////////////
 
1327
    // process one iteration of projection
 
1328
 
 
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;
 
1335
 
 
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
 
1341
 
 
1342
    DSP4_CLEAR_OUT();
 
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);
 
1347
 
 
1348
    //////////////////////////////////////////////////////////
 
1349
 
 
1350
    // SR = 0x00
 
1351
 
 
1352
    // determine # of DSP4_vars.raster lines used
 
1353
    DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
 
1354
 
 
1355
    // prevent overdraw
 
1356
    if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
 
1357
      DSP4_vars.segments = 0;
 
1358
    else
 
1359
      DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
 
1360
 
 
1361
    // don't draw outside the window
 
1362
    if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
 
1363
    {
 
1364
      DSP4_vars.segments = 0;
 
1365
 
 
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];
 
1369
    }
 
1370
 
 
1371
    // SR = 0x80
 
1372
 
 
1373
    DSP4_WRITE_WORD(DSP4_vars.segments);
 
1374
 
 
1375
    //////////////////////////////////////////////////////////
 
1376
 
 
1377
    // scan next command if no SR check needed
 
1378
    if (DSP4_vars.segments)
 
1379
    {
 
1380
      int32 px_dx, py_dy;
 
1381
      int32 x_scroll, y_scroll;
 
1382
 
 
1383
      // SR = 0x00
 
1384
 
 
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;
 
1388
 
 
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);
 
1392
 
 
1393
      // SR = 0x80
 
1394
 
 
1395
      // rasterize line
 
1396
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
 
1397
      {
 
1398
        // 1. HDMA memory pointer (bg1)
 
1399
        // 2. vertical scroll offset ($210E)
 
1400
        // 3. horizontal scroll offset ($210D)
 
1401
 
 
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));
 
1405
 
 
1406
 
 
1407
        // update memory address
 
1408
        DSP4_vars.poly_ptr[0][0] -= 4;
 
1409
 
 
1410
        // update screen values
 
1411
        x_scroll += px_dx;
 
1412
        y_scroll += py_dy;
 
1413
      }
 
1414
    }
 
1415
 
 
1416
    /////////////////////////////////////////////////////
 
1417
    // Post-update
 
1418
 
 
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;
 
1424
 
 
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);
 
1428
 
 
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;
 
1432
 
 
1433
    ////////////////////////////////////////////////////
 
1434
    // command check
 
1435
 
 
1436
    // scan next command
 
1437
    DSP4.in_count = 2;
 
1438
    DSP4_WAIT(1) resume1 :
 
1439
 
 
1440
    // inspect input
 
1441
    DSP4_vars.distance = DSP4_READ_WORD();
 
1442
 
 
1443
    // terminate op
 
1444
    if (DSP4_vars.distance == -0x8000)
 
1445
      break;
 
1446
 
 
1447
    // already have 2 bytes in queue
 
1448
    DSP4.in_count = 6;
 
1449
    DSP4_WAIT(2) resume2:
 
1450
 
 
1451
    // inspect inputs
 
1452
    DSP4_vars.world_ddy = DSP4_READ_WORD();
 
1453
    DSP4_vars.world_ddx = DSP4_READ_WORD();
 
1454
    DSP4_vars.view_yofsenv = DSP4_READ_WORD();
 
1455
 
 
1456
    // no envelope here
 
1457
    DSP4_vars.world_xenv = 0;
 
1458
  }
 
1459
  while (1);
 
1460
 
 
1461
  DSP4.waiting4command = TRUE;
 
1462
}
 
1463
 
 
1464
//////////////////////////////////////////////////////////////
 
1465
 
 
1466
 
 
1467
void DSP4_OP0E()
 
1468
{
 
1469
  DSP4_vars.OAM_RowMax = 16;
 
1470
  memset(DSP4_vars.OAM_Row, 0, 64);
 
1471
}
 
1472
 
 
1473
 
 
1474
//////////////////////////////////////////////////////////////
 
1475
 
 
1476
void DSP4_OP0F()
 
1477
{
 
1478
  DSP4.waiting4command = FALSE;
 
1479
 
 
1480
  // op flow control
 
1481
  switch (DSP4_vars.DSP4_Logic)
 
1482
  {
 
1483
    case 1:
 
1484
      goto resume1; break;
 
1485
    case 2:
 
1486
      goto resume2; break;
 
1487
    case 3:
 
1488
      goto resume3; break;
 
1489
    case 4:
 
1490
      goto resume4; break;
 
1491
  }
 
1492
 
 
1493
  ////////////////////////////////////////////////////
 
1494
  // process initial inputs
 
1495
 
 
1496
  // sort 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();
 
1515
 
 
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;
 
1523
 
 
1524
  // first DSP4_vars.raster line
 
1525
  DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
 
1526
 
 
1527
 
 
1528
  do
 
1529
  {
 
1530
    ////////////////////////////////////////////////////
 
1531
    // process one iteration of projection
 
1532
 
 
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;
 
1539
 
 
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
 
1545
 
 
1546
    DSP4_CLEAR_OUT();
 
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);
 
1551
 
 
1552
    //////////////////////////////////////////////////////
 
1553
 
 
1554
    // SR = 0x00
 
1555
 
 
1556
    // determine # of DSP4_vars.raster lines used
 
1557
    DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2;
 
1558
 
 
1559
    // prevent overdraw
 
1560
    if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
 
1561
      DSP4_vars.segments = 0;
 
1562
    else
 
1563
      DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
 
1564
 
 
1565
    // don't draw outside the window
 
1566
    if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
 
1567
    {
 
1568
      DSP4_vars.segments = 0;
 
1569
 
 
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];
 
1573
    }
 
1574
 
 
1575
    // SR = 0x80
 
1576
 
 
1577
    DSP4_WRITE_WORD(DSP4_vars.segments);
 
1578
 
 
1579
    //////////////////////////////////////////////////////
 
1580
 
 
1581
    // scan next command if no SR check needed
 
1582
    if (DSP4_vars.segments)
 
1583
    {
 
1584
      int32 px_dx, py_dy;
 
1585
      int32 x_scroll, y_scroll;
 
1586
 
 
1587
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++)
 
1588
      {
 
1589
        // grab inputs
 
1590
        DSP4.in_count = 4;
 
1591
        DSP4_WAIT(1);
 
1592
        resume1 :
 
1593
        for (;;)
 
1594
        {
 
1595
          int16 distance;
 
1596
          int16 color, red, green, blue;
 
1597
 
 
1598
          distance = DSP4_READ_WORD();
 
1599
          color = DSP4_READ_WORD();
 
1600
 
 
1601
          // U1+B5+G5+R5
 
1602
          red = color & 0x1f;
 
1603
          green = (color >> 5) & 0x1f;
 
1604
          blue = (color >> 10) & 0x1f;
 
1605
 
 
1606
          // dynamic lighting
 
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);
 
1611
 
 
1612
          DSP4_CLEAR_OUT();
 
1613
          DSP4_WRITE_WORD(color);
 
1614
          break;
 
1615
        }
 
1616
      }
 
1617
 
 
1618
      //////////////////////////////////////////////////////
 
1619
 
 
1620
      // SR = 0x00
 
1621
 
 
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;
 
1625
 
 
1626
 
 
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);
 
1630
 
 
1631
      // SR = 0x80
 
1632
 
 
1633
      // rasterize line
 
1634
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
 
1635
      {
 
1636
        // 1. HDMA memory pointer
 
1637
        // 2. vertical scroll offset ($210E)
 
1638
        // 3. horizontal scroll offset ($210D)
 
1639
 
 
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));
 
1643
 
 
1644
        // update memory address
 
1645
        DSP4_vars.poly_ptr[0][0] -= 4;
 
1646
 
 
1647
        // update screen values
 
1648
        x_scroll += px_dx;
 
1649
        y_scroll += py_dy;
 
1650
      }
 
1651
    }
 
1652
 
 
1653
    ////////////////////////////////////////////////////
 
1654
    // Post-update
 
1655
 
 
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;
 
1661
 
 
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);
 
1665
 
 
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;
 
1669
 
 
1670
    // update road turnoff position
 
1671
    DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
 
1672
 
 
1673
    ////////////////////////////////////////////////////
 
1674
    // command check
 
1675
 
 
1676
    // scan next command
 
1677
    DSP4.in_count = 2;
 
1678
    DSP4_WAIT(2) resume2:
 
1679
 
 
1680
    // check for termination
 
1681
    DSP4_vars.distance = DSP4_READ_WORD();
 
1682
    if (DSP4_vars.distance == -0x8000)
 
1683
      break;
 
1684
 
 
1685
    // road splice
 
1686
    if( (uint16) DSP4_vars.distance == 0x8001 )
 
1687
    {
 
1688
      DSP4.in_count = 6;
 
1689
      DSP4_WAIT(3) resume3:
 
1690
 
 
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();
 
1694
 
 
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 );
 
1698
 
 
1699
      // update stepping values
 
1700
      DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
 
1701
 
 
1702
      DSP4.in_count = 2;
 
1703
      DSP4_WAIT(2)
 
1704
    }
 
1705
 
 
1706
    // already have 2 bytes in queue
 
1707
    DSP4.in_count = 6;
 
1708
    DSP4_WAIT(4) resume4 :
 
1709
 
 
1710
    // inspect inputs
 
1711
    DSP4_vars.world_ddy = DSP4_READ_WORD();
 
1712
    DSP4_vars.world_ddx = DSP4_READ_WORD();
 
1713
    DSP4_vars.view_yofsenv = DSP4_READ_WORD();
 
1714
 
 
1715
    // no envelope here
 
1716
    DSP4_vars.world_xenv = 0;
 
1717
  }
 
1718
  while (1);
 
1719
 
 
1720
  // terminate op
 
1721
  DSP4.waiting4command = TRUE;
 
1722
}
 
1723
 
 
1724
//////////////////////////////////////////////////////////////
 
1725
 
 
1726
 
 
1727
void DSP4_OP10()
 
1728
{
 
1729
  DSP4.waiting4command = FALSE;
 
1730
 
 
1731
  // op flow control
 
1732
  switch (DSP4_vars.DSP4_Logic)
 
1733
  {
 
1734
    case 1:
 
1735
      goto resume1; break;
 
1736
    case 2:
 
1737
      goto resume2; break;
 
1738
    case 3:
 
1739
      goto resume3; break;
 
1740
  }
 
1741
 
 
1742
  ////////////////////////////////////////////////////
 
1743
  // sort inputs
 
1744
 
 
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();
 
1761
 
 
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;
 
1767
 
 
1768
  // first DSP4_vars.raster line
 
1769
  DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
 
1770
 
 
1771
  do
 
1772
  {
 
1773
    ////////////////////////////////////////////////////
 
1774
    // process one iteration of projection
 
1775
 
 
1776
    // add shaping
 
1777
    DSP4_vars.view_x2 += DSP4_vars.view_dx;
 
1778
    DSP4_vars.view_y2 += DSP4_vars.view_dy;
 
1779
 
 
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;
 
1783
 
 
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
 
1787
 
 
1788
    DSP4_CLEAR_OUT();
 
1789
    DSP4_WRITE_WORD(DSP4_vars.view_x2);
 
1790
    DSP4_WRITE_WORD(DSP4_vars.view_y2);
 
1791
 
 
1792
    //////////////////////////////////////////////////////
 
1793
 
 
1794
    // SR = 0x00
 
1795
 
 
1796
    // determine # of DSP4_vars.raster lines used
 
1797
    DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
 
1798
 
 
1799
    // prevent overdraw
 
1800
    if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
 
1801
      DSP4_vars.segments = 0;
 
1802
    else
 
1803
      DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
 
1804
 
 
1805
    // don't draw outside the window
 
1806
    if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
 
1807
    {
 
1808
      DSP4_vars.segments = 0;
 
1809
 
 
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];
 
1813
    }
 
1814
 
 
1815
    // SR = 0x80
 
1816
 
 
1817
    DSP4_WRITE_WORD(DSP4_vars.segments);
 
1818
 
 
1819
    //////////////////////////////////////////////////////
 
1820
 
 
1821
    // scan next command if no SR check needed
 
1822
    if (DSP4_vars.segments)
 
1823
    {
 
1824
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++)
 
1825
      {
 
1826
        // grab inputs
 
1827
        DSP4.in_count = 4;
 
1828
        DSP4_WAIT(1);
 
1829
        resume1 :
 
1830
        for (;;)
 
1831
        {
 
1832
          int16 distance;
 
1833
          int16 color, red, green, blue;
 
1834
 
 
1835
          distance = DSP4_READ_WORD();
 
1836
          color = DSP4_READ_WORD();
 
1837
 
 
1838
          // U1+B5+G5+R5
 
1839
          red = color & 0x1f;
 
1840
          green = (color >> 5) & 0x1f;
 
1841
          blue = (color >> 10) & 0x1f;
 
1842
 
 
1843
          // dynamic lighting
 
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);
 
1848
 
 
1849
          DSP4_CLEAR_OUT();
 
1850
          DSP4_WRITE_WORD(color);
 
1851
          break;
 
1852
        }
 
1853
      }
 
1854
    }
 
1855
 
 
1856
    //////////////////////////////////////////////////////
 
1857
 
 
1858
    // scan next command if no SR check needed
 
1859
    if (DSP4_vars.segments)
 
1860
    {
 
1861
      int32 px_dx, py_dy;
 
1862
      int32 x_scroll, y_scroll;
 
1863
 
 
1864
      // SR = 0x00
 
1865
 
 
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;
 
1869
 
 
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);
 
1873
 
 
1874
      // SR = 0x80
 
1875
 
 
1876
      // rasterize line
 
1877
      for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
 
1878
      {
 
1879
        // 1. HDMA memory pointer (bg2)
 
1880
        // 2. vertical scroll offset ($2110)
 
1881
        // 3. horizontal scroll offset ($210F)
 
1882
 
 
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));
 
1886
 
 
1887
        // update memory address
 
1888
        DSP4_vars.poly_ptr[0][0] -= 4;
 
1889
 
 
1890
        // update screen values
 
1891
        x_scroll += px_dx;
 
1892
        y_scroll += py_dy;
 
1893
      }
 
1894
    }
 
1895
 
 
1896
    /////////////////////////////////////////////////////
 
1897
    // Post-update
 
1898
 
 
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;
 
1904
 
 
1905
    ////////////////////////////////////////////////////
 
1906
    // command check
 
1907
 
 
1908
    // scan next command
 
1909
    DSP4.in_count = 2;
 
1910
    DSP4_WAIT(2) resume2 :
 
1911
 
 
1912
    // check for opcode termination
 
1913
    DSP4_vars.distance = DSP4_READ_WORD();
 
1914
    if (DSP4_vars.distance == -0x8000)
 
1915
      break;
 
1916
 
 
1917
    // already have 2 bytes in queue
 
1918
    DSP4.in_count = 10;
 
1919
    DSP4_WAIT(3) resume3 :
 
1920
 
 
1921
 
 
1922
    // inspect inputs
 
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;
 
1927
  }
 
1928
  while (1);
 
1929
 
 
1930
  DSP4.waiting4command = TRUE;
 
1931
}
 
1932
 
 
1933
//////////////////////////////////////////////////////////////
 
1934
 
 
1935
void DSP4_OP11(int16 A, int16 B, int16 C, int16 D, int16 *M)
 
1936
{
 
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);
 
1942
}
 
1943
 
 
1944
 
 
1945
 
 
1946
 
 
1947
 
 
1948
/////////////////////////////////////////////////////////////
 
1949
//Processing Code
 
1950
/////////////////////////////////////////////////////////////
 
1951
uint8 dsp4_byte;
 
1952
uint16 dsp4_address;
 
1953
 
 
1954
void InitDSP4()
 
1955
{
 
1956
  memset(&DSP4, 0, sizeof(DSP4));
 
1957
  DSP4.waiting4command = TRUE;
 
1958
}
 
1959
 
 
1960
void DSP4SetByte()
 
1961
{
 
1962
  // clear pending read
 
1963
  if (DSP4.out_index < DSP4.out_count)
 
1964
  {
 
1965
    DSP4.out_index++;
 
1966
    return;
 
1967
  }
 
1968
 
 
1969
  if (DSP4.waiting4command)
 
1970
  {
 
1971
    if (DSP4.half_command)
 
1972
    {
 
1973
      DSP4.command |= (dsp4_byte << 8);
 
1974
      DSP4.in_index = 0;
 
1975
      DSP4.waiting4command = FALSE;
 
1976
      DSP4.half_command = FALSE;
 
1977
      DSP4.out_count = 0;
 
1978
      DSP4.out_index = 0;
 
1979
 
 
1980
      DSP4_vars.DSP4_Logic = 0;
 
1981
 
 
1982
 
 
1983
      switch (DSP4.command)
 
1984
      {
 
1985
        case 0x0000:
 
1986
          DSP4.in_count = 4; break;
 
1987
        case 0x0001:
 
1988
          DSP4.in_count = 44; break;
 
1989
        case 0x0003:
 
1990
          DSP4.in_count = 0; break;
 
1991
        case 0x0005:
 
1992
          DSP4.in_count = 0; break;
 
1993
        case 0x0006:
 
1994
          DSP4.in_count = 0; break;
 
1995
        case 0x0007:
 
1996
          DSP4.in_count = 34; break;
 
1997
        case 0x0008:
 
1998
          DSP4.in_count = 90; break;
 
1999
        case 0x0009:
 
2000
          DSP4.in_count = 14; break;
 
2001
        case 0x000a:
 
2002
          DSP4.in_count = 6; break;
 
2003
        case 0x000b:
 
2004
          DSP4.in_count = 6; break;
 
2005
        case 0x000d:
 
2006
          DSP4.in_count = 42; break;
 
2007
        case 0x000e:
 
2008
          DSP4.in_count = 0; break;
 
2009
        case 0x000f:
 
2010
          DSP4.in_count = 46; break;
 
2011
        case 0x0010:
 
2012
          DSP4.in_count = 36; break;
 
2013
        case 0x0011:
 
2014
          DSP4.in_count = 8; break;
 
2015
        default:
 
2016
          DSP4.waiting4command = TRUE;
 
2017
          break;
 
2018
      }
 
2019
    }
 
2020
    else
 
2021
    {
 
2022
      DSP4.command = dsp4_byte;
 
2023
      DSP4.half_command = TRUE;
 
2024
    }
 
2025
  }
 
2026
  else
 
2027
  {
 
2028
    DSP4.parameters[DSP4.in_index] = dsp4_byte;
 
2029
    DSP4.in_index++;
 
2030
  }
 
2031
 
 
2032
  if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index)
 
2033
  {
 
2034
    // Actually execute the command
 
2035
    DSP4.waiting4command = TRUE;
 
2036
    DSP4.out_index = 0;
 
2037
    DSP4.in_index = 0;
 
2038
 
 
2039
    switch (DSP4.command)
 
2040
    {
 
2041
        // 16-bit multiplication
 
2042
      case 0x0000:
 
2043
      {
 
2044
        int16 multiplier, multiplicand;
 
2045
        int32 product;
 
2046
 
 
2047
        multiplier = DSP4_READ_WORD();
 
2048
        multiplicand = DSP4_READ_WORD();
 
2049
 
 
2050
        DSP4_Multiply(multiplicand, multiplier, &product);
 
2051
 
 
2052
        DSP4_CLEAR_OUT();
 
2053
        DSP4_WRITE_WORD((uint16)(product));
 
2054
        DSP4_WRITE_WORD((uint16)(product >> 16));
 
2055
      }
 
2056
      break;
 
2057
 
 
2058
      // single-player track projection
 
2059
      case 0x0001:
 
2060
        DSP4_OP01(); break;
 
2061
 
 
2062
      // single-player selection
 
2063
      case 0x0003:
 
2064
        DSP4_OP03(); break;
 
2065
 
 
2066
      // clear OAM
 
2067
      case 0x0005:
 
2068
        DSP4_OP05(); break;
 
2069
 
 
2070
      // transfer OAM
 
2071
      case 0x0006:
 
2072
        DSP4_OP06(); break;
 
2073
 
 
2074
      // single-player track turnoff projection
 
2075
      case 0x0007:
 
2076
        DSP4_OP07(); break;
 
2077
 
 
2078
      // solid polygon projection
 
2079
      case 0x0008:
 
2080
        DSP4_OP08(); break;
 
2081
 
 
2082
      // sprite projection
 
2083
      case 0x0009:
 
2084
        DSP4_OP09(); break;
 
2085
 
 
2086
      // unknown
 
2087
      case 0x000A:
 
2088
      {
 
2089
        //int16 in1a = DSP4_READ_WORD();
 
2090
        int16 in2a = DSP4_READ_WORD();
 
2091
        //int16 in3a = DSP4_READ_WORD();
 
2092
        int16 out1a, out2a, out3a, out4a;
 
2093
 
 
2094
        DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a);
 
2095
 
 
2096
        DSP4_CLEAR_OUT();
 
2097
        DSP4_WRITE_WORD(out1a);
 
2098
        DSP4_WRITE_WORD(out2a);
 
2099
        DSP4_WRITE_WORD(out3a);
 
2100
        DSP4_WRITE_WORD(out4a);
 
2101
      }
 
2102
      break;
 
2103
 
 
2104
      // set OAM
 
2105
      case 0x000B:
 
2106
      {
 
2107
        int16 sp_x = DSP4_READ_WORD();
 
2108
        int16 sp_y = DSP4_READ_WORD();
 
2109
        int16 sp_attr = DSP4_READ_WORD();
 
2110
        bool8 draw = 1;
 
2111
 
 
2112
        DSP4_CLEAR_OUT();
 
2113
 
 
2114
        DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1);
 
2115
      }
 
2116
      break;
 
2117
 
 
2118
      // multi-player track projection
 
2119
      case 0x000D:
 
2120
        DSP4_OP0D(); break;
 
2121
 
 
2122
      // multi-player selection
 
2123
      case 0x000E:
 
2124
        DSP4_OP0E(); break;
 
2125
 
 
2126
      // single-player track projection with lighting
 
2127
      case 0x000F:
 
2128
        DSP4_OP0F(); break;
 
2129
 
 
2130
      // single-player track turnoff projection with lighting
 
2131
      case 0x0010:
 
2132
        DSP4_OP10(); break;
 
2133
 
 
2134
      // unknown: horizontal mapping command
 
2135
      case 0x0011:
 
2136
      {
 
2137
        int16 a, b, c, d, m;
 
2138
 
 
2139
 
 
2140
        d = DSP4_READ_WORD();
 
2141
        c = DSP4_READ_WORD();
 
2142
        b = DSP4_READ_WORD();
 
2143
        a = DSP4_READ_WORD();
 
2144
 
 
2145
        DSP4_OP11(a, b, c, d, &m);
 
2146
 
 
2147
        DSP4_CLEAR_OUT();
 
2148
        DSP4_WRITE_WORD(m);
 
2149
 
 
2150
        break;
 
2151
      }
 
2152
 
 
2153
      default:
 
2154
        break;
 
2155
    }
 
2156
  }
 
2157
}
 
2158
 
 
2159
void DSP4GetByte()
 
2160
{
 
2161
  if (DSP4.out_count)
 
2162
  {
 
2163
    dsp4_byte = (uint8) DSP4.output[DSP4.out_index&0x1FF];
 
2164
    DSP4.out_index++;
 
2165
    if (DSP4.out_count == DSP4.out_index)
 
2166
      DSP4.out_count = 0;
 
2167
  }
 
2168
  else
 
2169
  {
 
2170
    dsp4_byte = 0xff;
 
2171
  }
 
2172
}