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