1
/***************************************************************************
5
File to handle emulation of the SNES "DSP-1" add-on chip.
7
Original C++ "dsp1emul.cpp" by Andreas Naive
8
Based on research by Overload, The Dumper, Neviksti and Andreas Naive
9
MAME/MESS C conversion by R. Belmont
11
This is up to date with the source version dated June 2006.
13
***************************************************************************/
15
#define dsp1_VERSION 0x0102
17
// The DSP-1 status register has 16 bits, but only
18
// the upper 8 bits can be accessed from an external device, so all these
19
// positions are referred to the upper byte (bits D8 to D15)
21
enum SrFlags { DRC = 0x04, DRS = 0x10, RQM = 0x80 };
23
// According to Overload's docs, these are the meanings of the flags:
24
// DRC: The Data Register Control (DRC) bit specifies the data transfer length to and from the host CPU.
25
// 0: Data transfer to and from the DSP-1 is 16 bits.
26
// 1: Data transfer to and from the DSP-1 is 8 bits.
27
// DRS: The Data Register Status (DRS) bit indicates the data transfer status in the case of transfering 16-bit data.
28
// 0: Data transfer has terminated.
29
// 1: Data transfer in progress.
30
// RQM: The Request for Master (RQM) indicates that the DSP1 is requesting host CPU for data read/write.
31
// 0: Internal Data Register Transfer.
32
// 1: External Data Register Transfer.
34
enum Fsm_Major_State { WAIT_COMMAND, READ_DATA, WRITE_DATA };
35
enum Max_Data_Accesses { MAX_READS = 7, MAX_WRITES = 1024 };
39
void (*callback)(INT16 *, INT16 *);
46
// some RAM variables shared between commands
47
INT16 MatrixA[3][3]; // attitude matrix A
50
INT16 CentreX, CentreY, CentreZ; // center of projection
51
INT16 CentreZ_C, CentreZ_E;
52
INT16 VOffset; // vertical offset of the screen with regard to the centre of projection
53
INT16 Les, C_Les, E_Les;
57
INT16 SecAZS_C1, SecAZS_E1;
58
INT16 SecAZS_C2, SecAZS_E2;
59
INT16 Nx, Ny, Nz; // normal vector to the screen (norm 1, points toward the center of projection)
60
INT16 Gx, Gy, Gz; // center of the screen (global coordinates)
61
INT16 Hx, Hy; // horizontal vector of the screen (Hz=0, norm 1, points toward the right of the screen)
62
INT16 Vx, Vy, Vz; // vertical vector of the screen (norm 1, points toward the top of the screen)
65
struct _snes_dsp1_state
68
UINT8 Sr; // status register
70
UINT16 Dr; // "internal" representation of the data register
71
Fsm_Major_State FsmMajorState; // current major state of the FSM
72
UINT8 Command; // current command processed by the FSM
73
UINT8 DataCounter; // #UINT16 read/writes counter used by the FSM
74
INT16 ReadBuffer[MAX_READS];
75
INT16 WriteBuffer[MAX_WRITES];
76
UINT8 Freeze; // need explanation? ;)
80
//////////////////////////////////////////////////////////////////
82
// Data ROM, as logged from a DSP-1B with the 0x1f command;
83
// it contains the tables and constants used by the commands.
84
// The tables used are: two shift tables (0x022-0x031 and 0x031-0x040 -this last one
85
// with an error in 0x03c which has survived to all the DSP-1 revisions-); a inverse
86
// table (used as initial guess) at 0x065-0x0e4; a square root table (used also
87
// as initial guess) at 0x0e5-0x115; two sin and cos tables (used as nodes to construct
88
// a interpolation curve) at, respectively, 0x116-0x197 and 0x196-0x215.
89
// As a curiosity, in the positions 0x21c-0x31c it's contained a
90
// 257-points arccos table that, apparently, have been not used anywhere
91
// (maybe for the MaxAZS_Exp table?).
96
static struct _snes_dsp1_state dsp1_state;
101
static void dsp1_fsm_step(UINT8 read, UINT8 *data); // FSM logic
104
static void dsp1_memory_test(INT16 *input, INT16 *output);
105
static void dsp1_memory_dump(INT16 *input, INT16 *output);
106
static void dsp1_memory_size(INT16 *input, INT16 *output);
107
static void dsp1_multiply(INT16* input, INT16* output);
108
static void dsp1_multiply2(INT16* input, INT16* output);
109
static void dsp1_inverse(INT16 *input, INT16 *output);
110
static void dsp1_triangle(INT16 *input, INT16 *output);
111
static void dsp1_radius(INT16 *input, INT16 *output);
112
static void dsp1_range(INT16 *input, INT16 *output);
113
static void dsp1_range2(INT16 *input, INT16 *output);
114
static void dsp1_distance(INT16 *input, INT16 *output);
115
static void dsp1_rotate(INT16 *input, INT16 *output);
116
static void dsp1_polar(INT16 *input, INT16 *output);
117
static void dsp1_attitudeA(INT16 *input, INT16 *output);
118
static void dsp1_attitudeB(INT16 *input, INT16 *output);
119
static void dsp1_attitudeC(INT16 *input, INT16 *output);
120
static void dsp1_objectiveA(INT16 *input, INT16 *output);
121
static void dsp1_objectiveB(INT16 *input, INT16 *output);
122
static void dsp1_objectiveC(INT16 *input, INT16 *output);
123
static void dsp1_subjectiveA(INT16 *input, INT16 *output);
124
static void dsp1_subjectiveB(INT16 *input, INT16 *output);
125
static void dsp1_subjectiveC(INT16 *input, INT16 *output);
126
static void dsp1_scalarA(INT16 *input, INT16 *output);
127
static void dsp1_scalarB(INT16 *input, INT16 *output);
128
static void dsp1_scalarC(INT16 *input, INT16 *output);
129
static void dsp1_gyrate(INT16 *input, INT16 *output);
130
static void dsp1_parameter(INT16 *input, INT16 *output);
131
static void dsp1_raster(INT16 *input, INT16 *output);
132
static void dsp1_target(INT16 *input, INT16 *output);
133
static void dsp1_project(INT16 *input, INT16 *output);
135
// auxiliar functions
136
static INT16 dsp1_sin(INT16 Angle);
137
static INT16 dsp1_cos(INT16 Angle);
138
static void inverse(INT16 Coefficient, INT16 Exponent, INT16 *iCoefficient, INT16 *iExponent);
139
static INT16 denormalize_and_clip(INT16 C, INT16 E);
140
static void normalize(INT16 m, INT16 *Coefficient, INT16 *Exponent);
141
static void normalize_double(INT32 Product, INT16 *Coefficient, INT16 *Exponent);
142
static INT16 shiftR(INT16 C, INT16 E);
145
static void dsp1_register_save(running_machine *machine);
147
//////////////////////////////////////////////////////////////////
149
// The info on this table follows Overload's docs.
151
static const struct dsp1_Command mCommandTable[0x40] =
153
{&dsp1_multiply, 2, 1}, //0x00
154
{&dsp1_attitudeA, 4, 0}, //0x01
155
{&dsp1_parameter, 7, 4}, //0x02
156
{&dsp1_subjectiveA, 3, 3}, //0x03
157
{&dsp1_triangle, 2, 2}, //0x04
158
{&dsp1_attitudeA, 4, 0}, //0x01
159
{&dsp1_project, 3, 3}, //0x06
160
{&dsp1_memory_test, 1, 1}, //0x0f
161
{&dsp1_radius, 3, 2}, //0x08
162
{&dsp1_objectiveA, 3, 3}, //0x0d
163
{&dsp1_raster, 1, 4}, // 0x0a. This will normally work in continuous mode
164
{&dsp1_scalarA, 3, 1}, //0x0b
165
{&dsp1_rotate, 3, 2}, //0x0c
166
{&dsp1_objectiveA, 3, 3}, //0x0d
167
{&dsp1_target, 2, 2}, //0x0e
168
{&dsp1_memory_test, 1, 1}, //0x0f
170
{&dsp1_inverse, 2, 2}, //0x10
171
{&dsp1_attitudeB, 4, 0}, //0x11
172
{&dsp1_parameter, 7, 4}, //0x02
173
{&dsp1_subjectiveB, 3, 3}, //0x13
174
{&dsp1_gyrate, 6, 3}, //0x14
175
{&dsp1_attitudeB, 4, 0}, //0x11
176
{&dsp1_project, 3, 3}, //0x06
177
{&dsp1_memory_dump, 1, 1024}, //0x1f
178
{&dsp1_range, 4, 1}, //0x18
179
{&dsp1_objectiveB, 3, 3}, //0x1d
180
{0, 0, 0}, // 0x1a; the chip freezes
181
{&dsp1_scalarB, 3, 1}, //0x1b
182
{&dsp1_polar, 6, 3}, //0x1c
183
{&dsp1_objectiveB, 3, 3}, //0x1d
184
{&dsp1_target, 2, 2}, //0x0e
185
{&dsp1_memory_dump, 1, 1024}, //0x1f
187
{&dsp1_multiply2, 2, 1}, //0x20
188
{&dsp1_attitudeC, 4, 0}, //0x21
189
{&dsp1_parameter, 7, 4}, //0x02
190
{&dsp1_subjectiveC, 3, 3}, //0x23
191
{&dsp1_triangle, 2, 2}, //0x04
192
{&dsp1_attitudeC, 4, 0}, //0x21
193
{&dsp1_project, 3, 3}, //0x06
194
{&dsp1_memory_size, 1, 1}, //0x2f
195
{&dsp1_distance, 3, 1}, //0x28
196
{&dsp1_objectiveC, 3, 3}, //0x2d
197
{0, 0, 0}, // 0x1a; the chip freezes
198
{&dsp1_scalarC, 3, 1}, //0x2b
199
{&dsp1_rotate, 3, 2}, //0x0c
200
{&dsp1_objectiveC, 3, 3}, //0x2d
201
{&dsp1_target, 2, 2}, //0x0e
202
{&dsp1_memory_size, 1, 1}, //0x2f
204
{&dsp1_inverse, 2, 2}, //0x10
205
{&dsp1_attitudeA, 4, 0}, //0x01
206
{&dsp1_parameter, 7, 4}, //0x02
207
{&dsp1_subjectiveA, 3, 3}, //0x03
208
{&dsp1_gyrate, 6, 3}, //0x14
209
{&dsp1_attitudeA, 4, 0}, //0x01
210
{&dsp1_project, 3, 3}, //0x06
211
{&dsp1_memory_dump, 1, 1024}, //0x1f
212
{&dsp1_range2, 4, 1}, //0x38
213
{&dsp1_objectiveA, 3, 3}, //0x0d
214
{0, 0, 0}, // 0x1a; the chip freezes
215
{&dsp1_scalarA, 3, 1}, //0x0b
216
{&dsp1_polar, 6, 3}, //0x1c
217
{&dsp1_objectiveA, 3, 3}, //0x0d
218
{&dsp1_target, 2, 2}, //0x0e
219
{&dsp1_memory_dump, 1, 1024}, //0x1f
222
//////////////////////////////////////////////////////////////////
224
static const INT16 dsp1_MaxAZS_Exp[16] = {
225
0x38b4, 0x38b7, 0x38ba, 0x38be, 0x38c0, 0x38c4, 0x38c7, 0x38ca,
226
0x38ce, 0x38d0, 0x38d4, 0x38d7, 0x38da, 0x38dd, 0x38e0, 0x38e4
229
//////////////////////////////////////////////////////////////////
231
static const INT16 dsp1_sin_table[256] = {
232
0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2,
233
0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11,
234
0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a,
235
0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842,
236
0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6,
237
0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504,
238
0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3,
239
0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6,
240
0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d,
241
0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c,
242
0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24,
243
0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4,
244
0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4,
245
0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de,
246
0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b,
247
0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324,
248
-0x0000, -0x0324, -0x0647, -0x096a, -0x0c8b, -0x0fab, -0x12c8, -0x15e2,
249
-0x18f8, -0x1c0b, -0x1f19, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11,
250
-0x30fb, -0x33de, -0x36ba, -0x398c, -0x3c56, -0x3f17, -0x41ce, -0x447a,
251
-0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842,
252
-0x5a82, -0x5cb4, -0x5ed7, -0x60ec, -0x62f2, -0x64e8, -0x66cf, -0x68a6,
253
-0x6a6d, -0x6c24, -0x6dca, -0x6f5f, -0x70e2, -0x7255, -0x73b5, -0x7504,
254
-0x7641, -0x776c, -0x7884, -0x798a, -0x7a7d, -0x7b5d, -0x7c29, -0x7ce3,
255
-0x7d8a, -0x7e1d, -0x7e9d, -0x7f09, -0x7f62, -0x7fa7, -0x7fd8, -0x7ff6,
256
-0x7fff, -0x7ff6, -0x7fd8, -0x7fa7, -0x7f62, -0x7f09, -0x7e9d, -0x7e1d,
257
-0x7d8a, -0x7ce3, -0x7c29, -0x7b5d, -0x7a7d, -0x798a, -0x7884, -0x776c,
258
-0x7641, -0x7504, -0x73b5, -0x7255, -0x70e2, -0x6f5f, -0x6dca, -0x6c24,
259
-0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f2, -0x60ec, -0x5ed7, -0x5cb4,
260
-0x5a82, -0x5842, -0x55f5, -0x539b, -0x5133, -0x4ebf, -0x4c3f, -0x49b4,
261
-0x471c, -0x447a, -0x41ce, -0x3f17, -0x3c56, -0x398c, -0x36ba, -0x33de,
262
-0x30fb, -0x2e11, -0x2b1f, -0x2826, -0x2528, -0x2223, -0x1f19, -0x1c0b,
263
-0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324};
265
//////////////////////////////////////////////////////////////////
267
// Optimised for Performance
268
static const INT16 dsp1_mul_table[256] = {
269
0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015,
270
0x0019, 0x001c, 0x001f, 0x0022, 0x0025, 0x0028, 0x002b, 0x002f,
271
0x0032, 0x0035, 0x0038, 0x003b, 0x003e, 0x0041, 0x0045, 0x0048,
272
0x004b, 0x004e, 0x0051, 0x0054, 0x0057, 0x005b, 0x005e, 0x0061,
273
0x0064, 0x0067, 0x006a, 0x006d, 0x0071, 0x0074, 0x0077, 0x007a,
274
0x007d, 0x0080, 0x0083, 0x0087, 0x008a, 0x008d, 0x0090, 0x0093,
275
0x0096, 0x0099, 0x009d, 0x00a0, 0x00a3, 0x00a6, 0x00a9, 0x00ac,
276
0x00af, 0x00b3, 0x00b6, 0x00b9, 0x00bc, 0x00bf, 0x00c2, 0x00c5,
277
0x00c9, 0x00cc, 0x00cf, 0x00d2, 0x00d5, 0x00d8, 0x00db, 0x00df,
278
0x00e2, 0x00e5, 0x00e8, 0x00eb, 0x00ee, 0x00f1, 0x00f5, 0x00f8,
279
0x00fb, 0x00fe, 0x0101, 0x0104, 0x0107, 0x010b, 0x010e, 0x0111,
280
0x0114, 0x0117, 0x011a, 0x011d, 0x0121, 0x0124, 0x0127, 0x012a,
281
0x012d, 0x0130, 0x0133, 0x0137, 0x013a, 0x013d, 0x0140, 0x0143,
282
0x0146, 0x0149, 0x014d, 0x0150, 0x0153, 0x0156, 0x0159, 0x015c,
283
0x015f, 0x0163, 0x0166, 0x0169, 0x016c, 0x016f, 0x0172, 0x0175,
284
0x0178, 0x017c, 0x017f, 0x0182, 0x0185, 0x0188, 0x018b, 0x018e,
285
0x0192, 0x0195, 0x0198, 0x019b, 0x019e, 0x01a1, 0x01a4, 0x01a8,
286
0x01ab, 0x01ae, 0x01b1, 0x01b4, 0x01b7, 0x01ba, 0x01be, 0x01c1,
287
0x01c4, 0x01c7, 0x01ca, 0x01cd, 0x01d0, 0x01d4, 0x01d7, 0x01da,
288
0x01dd, 0x01e0, 0x01e3, 0x01e6, 0x01ea, 0x01ed, 0x01f0, 0x01f3,
289
0x01f6, 0x01f9, 0x01fc, 0x0200, 0x0203, 0x0206, 0x0209, 0x020c,
290
0x020f, 0x0212, 0x0216, 0x0219, 0x021c, 0x021f, 0x0222, 0x0225,
291
0x0228, 0x022c, 0x022f, 0x0232, 0x0235, 0x0238, 0x023b, 0x023e,
292
0x0242, 0x0245, 0x0248, 0x024b, 0x024e, 0x0251, 0x0254, 0x0258,
293
0x025b, 0x025e, 0x0261, 0x0264, 0x0267, 0x026a, 0x026e, 0x0271,
294
0x0274, 0x0277, 0x027a, 0x027d, 0x0280, 0x0284, 0x0287, 0x028a,
295
0x028d, 0x0290, 0x0293, 0x0296, 0x029a, 0x029d, 0x02a0, 0x02a3,
296
0x02a6, 0x02a9, 0x02ac, 0x02b0, 0x02b3, 0x02b6, 0x02b9, 0x02bc,
297
0x02bf, 0x02c2, 0x02c6, 0x02c9, 0x02cc, 0x02cf, 0x02d2, 0x02d5,
298
0x02d8, 0x02db, 0x02df, 0x02e2, 0x02e5, 0x02e8, 0x02eb, 0x02ee,
299
0x02f1, 0x02f5, 0x02f8, 0x02fb, 0x02fe, 0x0301, 0x0304, 0x0307,
300
0x030b, 0x030e, 0x0311, 0x0314, 0x0317, 0x031a, 0x031d, 0x0321};
302
//////////////////////////////////////////////////////////////////
304
static UINT8 dsp1_get_sr( void )
306
dsp1_state.SrLowByteAccess = ~dsp1_state.SrLowByteAccess;
308
if (dsp1_state.SrLowByteAccess)
311
return dsp1_state.Sr;
314
//////////////////////////////////////////////////////////////////
316
static UINT8 dsp1_get_dr( void )
320
dsp1_fsm_step(1, &oDr);
324
//////////////////////////////////////////////////////////////////
326
static void dsp1_set_dr( UINT8 iDr )
328
dsp1_fsm_step(0, &iDr);
331
//////////////////////////////////////////////////////////////////
333
static void dsp1_init( running_machine *machine )
336
UINT8 *dspin = machine->region("addons")->base();
338
dsp1_state.Sr = DRC|RQM;
339
dsp1_state.SrLowByteAccess = FALSE;
340
dsp1_state.Dr = 0x0080; // Only a supposition. Is this correct?
341
dsp1_state.Freeze = FALSE;
342
dsp1_state.FsmMajorState = WAIT_COMMAND;
343
memset(&dsp1_state.shared, 0, sizeof(struct SharedData)); // another supposition
345
// expand the DSP-1 data ROM
346
for (i = 0; i < 2048; i += 2)
348
dsp1_state.DataRom[i / 2] = (dspin[i] << 8) | dspin[i + 1];
351
dsp1_register_save(machine);
354
//////////////////////////////////////////////////////////////////
356
// Though the DSP-1 is unaware of the type of operation (read or write)
357
// we need to know what is being done by the program, as the class
358
// is responsible for maintaining the binding between the
359
// "external" and "internal" representations of the DR (data register).
361
static void dsp1_fsm_step( UINT8 read, UINT8 *data )
363
if (0 == (dsp1_state.Sr & RQM))
366
// Now RQM would be cleared; however, as this code is not to be used in
367
// a multithread environment, we will simply fake RQM operation.
368
// (The only exception would be Op1A's freeze.)
373
if (dsp1_state.Sr & DRS)
374
*data = (UINT8)(dsp1_state.Dr >> 8);
376
*data = (UINT8)(dsp1_state.Dr);
380
if (dsp1_state.Sr & DRS)
382
dsp1_state.Dr &= 0x00ff;
383
dsp1_state.Dr |= *data << 8;
387
dsp1_state.Dr &= 0xff00;
388
dsp1_state.Dr |= *data;
393
switch (dsp1_state.FsmMajorState)
396
dsp1_state.Command = (UINT8)(dsp1_state.Dr);
397
if (!(dsp1_state.Command & 0xc0)) // valid command?
399
switch(dsp1_state.Command)
405
dsp1_state.Freeze = TRUE;
410
dsp1_state.DataCounter = 0;
411
dsp1_state.FsmMajorState = READ_DATA;
412
dsp1_state.Sr &= ~DRC;
419
dsp1_state.Sr ^= DRS;
420
if (!(dsp1_state.Sr & DRS))
422
dsp1_state.ReadBuffer[dsp1_state.DataCounter++] = (INT16)(dsp1_state.Dr);
423
if (dsp1_state.DataCounter >= mCommandTable[dsp1_state.Command].reads)
425
(*mCommandTable[dsp1_state.Command].callback)(dsp1_state.ReadBuffer, dsp1_state.WriteBuffer);
426
if (0 != mCommandTable[dsp1_state.Command].writes) // any output?
428
dsp1_state.DataCounter = 0;
429
dsp1_state.Dr = (UINT16)(dsp1_state.WriteBuffer[dsp1_state.DataCounter]);
430
dsp1_state.FsmMajorState = WRITE_DATA;
434
dsp1_state.Dr = 0x0080; // valid command completion
435
dsp1_state.FsmMajorState = WAIT_COMMAND;
436
dsp1_state.Sr |= DRC;
443
dsp1_state.Sr ^= DRS;
444
if (!(dsp1_state.Sr & DRS))
446
++dsp1_state.DataCounter;
447
if (dsp1_state.DataCounter >= mCommandTable[dsp1_state.Command].writes)
449
if ((dsp1_state.Command == 0x0a) && (dsp1_state.Dr != 0x8000))
451
// works in continuous mode
452
dsp1_state.ReadBuffer[0]++; // next raster line
453
(*mCommandTable[dsp1_state.Command].callback)(dsp1_state.ReadBuffer, dsp1_state.WriteBuffer);
454
dsp1_state.DataCounter = 0;
455
dsp1_state.Dr = (UINT16)(dsp1_state.WriteBuffer[dsp1_state.DataCounter]);
459
dsp1_state.Dr = 0x0080; // valid command completion
460
dsp1_state.FsmMajorState = WAIT_COMMAND;
461
dsp1_state.Sr |= DRC;
466
dsp1_state.Dr = (UINT16)(dsp1_state.WriteBuffer[dsp1_state.DataCounter]);
474
// Now RQM would be set (except when executing Op1A -command equals 0x1a, 0x2a or 0x3a-).
475
if (dsp1_state.Freeze)
476
dsp1_state.Sr &= ~RQM;
479
//////////////////////////////////////////////////////////////////
481
static void dsp1_memory_test( INT16 *input, INT16 *output )
483
// INT16 *Size = &input[0];
484
INT16 *Result = &output[0];
489
//////////////////////////////////////////////////////////////////
491
static void dsp1_memory_dump( INT16 *input, INT16 *output )
493
memcpy(output, dsp1_state.DataRom, 1024);
496
//////////////////////////////////////////////////////////////////
498
static void dsp1_memory_size( INT16 *input, INT16 *output )
500
INT16* Size = &output[0];
505
//////////////////////////////////////////////////////////////////
507
// 16-bit multiplication
509
static void dsp1_multiply( INT16 *input, INT16 *output )
511
INT16 Multiplicand = input[0];
512
INT16 Multiplier = input[1];
513
INT16* Product = &output[0];
515
*Product = Multiplicand * Multiplier >> 15;
518
//////////////////////////////////////////////////////////////////
520
// 16-bit multiplication. 'Alternative' method. Can anyone check this carefully?
522
static void dsp1_multiply2( INT16 *input, INT16 *output )
524
INT16 Multiplicand = input[0];
525
INT16 Multiplier = input[1];
526
INT16* Product = &output[0];
528
*Product = (Multiplicand * Multiplier >> 15) + 1;
531
//////////////////////////////////////////////////////////////////
533
// This command determines the inverse of a floating point decimal number.
535
static void dsp1_inverse( INT16 *input, INT16 *output )
537
INT16 Coefficient = input[0];
538
INT16 Exponent = input[1];
539
INT16* iCoefficient = &output[0];
540
INT16* iExponent = &output[1];
542
inverse(Coefficient, Exponent, iCoefficient, iExponent);
545
//////////////////////////////////////////////////////////////////
547
// Vector component calculation. Determines the X and Y components for a
548
// two-dimensional vector whose size and direction is known.
549
// Y = Radius * sin(Angle)
550
// X = Radius * cos(Angle)
552
static void dsp1_triangle( INT16 *input, INT16 *output )
554
INT16 Angle = input[0];
555
INT16 Radius = input[1];
556
INT16* Y = &output[0];
557
INT16* X = &output[1];
559
*Y = dsp1_sin(Angle) * Radius >> 15;
560
*X = dsp1_cos(Angle) * Radius >> 15;
563
//////////////////////////////////////////////////////////////////
565
// Determines the squared norm of a vector (X,Y,Z)
566
// The output is Radius = X^2+Y^2+Z^2 (double integer)
568
static void dsp1_radius( INT16 *input, INT16 *output )
573
INT16* RadiusLow = &output[0];
574
INT16* RadiusHigh = &output[1];
578
Radius = (X * X + Y * Y + Z * Z) << 1;
579
*RadiusLow = (INT16)(Radius);
580
*RadiusHigh = (INT16)(Radius >> 16);
583
//////////////////////////////////////////////////////////////////
585
// Vector size comparison. This command compares the size of the vector (X,Y,Z) and the distance (R)
586
// from a particular point, and so may be used to determine if a point is within the sphere or radius R.
587
// The output is D = X^2+Y^2+Z^2-R^2
589
static void dsp1_range( INT16 *input, INT16 *output )
594
INT16 Radius = input[3];
595
INT16* Range = &output[0];
597
*Range = (X * X + Y * Y + Z * Z - Radius * Radius) >> 15;
600
//////////////////////////////////////////////////////////////////
602
// Vector size comparison. 'Alternative' method.
604
static void dsp1_range2( INT16 *input, INT16 *output )
609
INT16 Radius = input[3];
610
INT16* Range = &output[0];
612
*Range = ((X * X + Y * Y + Z * Z - Radius * Radius) >> 15) + 1;
615
//////////////////////////////////////////////////////////////////
617
// This command calculates the norm of a (X,Y,Z) vector, or the distance from
618
// the point (X,Y,Z) to (0,0,0), as you prefer to see it.
619
// Distance = sqrt(X^2+Y^2+Z^2)
620
// The square root of a number 'a' is calculated by doing this: you
621
// write 'a' as b*2^2n, with 'b' between 1/4 and 1; then, you calculate
622
// c=sqrt(b) by using lineal interpolation between points of a
623
// look-up table and, finally, you output the result as c*2^n.
625
static void dsp1_distance( INT16 *input, INT16 *output )
630
INT16* Distance = &output[0];
631
INT16 Pos, Node1, Node2;
633
INT32 Radius = X * X + Y * Y + Z * Z;
641
normalize_double(Radius, &C, &E);
643
C = C * 0x4000 >> 15;
645
Pos = C * 0x0040 >> 15;
647
Node1 = dsp1_state.DataRom[0x00d5 + Pos];
648
Node2 = dsp1_state.DataRom[0x00d6 + Pos];
650
*Distance = ((Node2 - Node1) * (C & 0x1ff) >> 9) + Node1;
652
#if dsp1_VERSION < 0x0102
654
*Distance -= (Node2 - Node1);
656
*Distance >>= (E >> 1);
660
//////////////////////////////////////////////////////////////////
662
// Determines the (X2, Y2) coordinates obtained by rotating (X1, Y1)
663
// clockwise for an angle 'Angle'. The official documentation says
664
// 'counterclockwise', but it's obviously wrong (surprise! :P)
666
// In matrix notation:
667
// |X2| |cos(Angle) sin(Angle)| |X1|
669
// |Y2| |-sin(Angle cos(Angle)| |Y1|
671
static void dsp1_rotate( INT16 *input, INT16 *output )
673
INT16 Angle = input[0];
676
INT16* X2 = &output[0];
677
INT16* Y2 = &output[1];
679
*X2 = (Y1 * dsp1_sin(Angle) >> 15) + (X1 * dsp1_cos(Angle) >> 15);
680
*Y2 = (Y1 * dsp1_cos(Angle) >> 15) - (X1 * dsp1_sin(Angle) >> 15);
683
//////////////////////////////////////////////////////////////////
685
// Calculate the coordinates (X2, Y2, Z2) obtained when rotating (X1, Y1, Z1)
686
// three-dimensionally. Rotation is done in the order of Az around the Z axis,
687
// Ay around the Y axis and Ax around the X axis. As occur with the "attitude" commands
688
// (see comments in the "gyrate" command), qthis doesn't match what explained in
689
// the official documentation, but it's coherent with what it is done in the "attitude"
690
// command (but not with the "gyrate" command).
692
// In matrix notation:
693
// |X2| |1 0 0 | |cosRy 0 -sinRy| | cosRz sinRz 0| |X1|
694
// |Y2| = |0 cosRx sinRx| | 0 1 0 | |-sinRz cosRz 0| |Y1|
695
// |Z2| |0 -sinRx cosRx| |sinRy 0 cosRy| | 0 0 1| |Z1|
697
static void dsp1_polar( INT16 *input, INT16 *output )
705
INT16* X2 = &output[0];
706
INT16* Y2 = &output[1];
707
INT16* Z2 = &output[2];
712
X = (Y1 * dsp1_sin(Az) >> 15) + (X1 * dsp1_cos(Az) >> 15);
713
Y = (Y1 * dsp1_cos(Az) >> 15) - (X1 * dsp1_sin(Az) >> 15);
717
Z = (X1 * dsp1_sin(Ay) >> 15) + (Z1 * dsp1_cos(Ay) >> 15);
718
X = (X1 * dsp1_cos(Ay) >> 15) - (Z1 * dsp1_sin(Ay) >> 15);
722
Y = (Z1 * dsp1_sin(Ax) >> 15) + (Y1 * dsp1_cos(Ax) >> 15);
723
Z = (Z1 * dsp1_cos(Ax) >> 15) - (Y1 * dsp1_sin(Ax) >> 15);
727
//////////////////////////////////////////////////////////////////
729
// Set up the elements of an "attitude matrix" (there are other ones):
730
// S | cosRz sinRz 0| |cosRy 0 -sinRy| |1 0 0 |
731
// MatrixA = - |-sinRz cosRz 0| | 0 1 0 | |0 cosRx sinRx|
732
// 2 | 0 0 1| |sinRy 0 cosRy| |0 -sinRx cosRx|
733
// This matrix is thought to be used within the following framework:
734
// let's suppose we define positive rotations around a system of orthogonal axes in this manner:
735
// a rotation of +90 degrees around axis3 converts axis2 into axis1
736
// a rotation of +90 degrees around axis2 converts axis1 into axis3
737
// a rotation of +90 degrees around axis1 converts axis3 into axis2
738
// and let's suppose that we have defined a new orthonormal axes system (FLU)
739
// by doing the following operations about the standard one (XYZ):
740
// first rotating the XYZ system around Z by an angle Rz (obtaining X'Y'Z'),
741
// then rotating the resulting system around Y by an angle Ry (obtaining X''Y''Z'')
742
// and, finally, rotating the resulting system around X by an angle Rx (obtaining FLU)
743
// This FLU (forward/left/up) system represents an "attitude" and, then, the matrix here defined
744
// is the change of coordinates matrix that transform coordinates in the FLU
745
// system (the "object coordinates") into the standard XYZ system (the "global coordinates"),
746
// multiplied by a scale factor S/2, that is:
748
// |y| * - = MatrixA * |l|
750
// In a similar way, if we use the transpose of the matrix, we can transform global coordinates
751
// into object coordinates:
753
// |l| * - = MatrixA_transposed * |y|
761
static void dsp1_attitudeA( INT16 *input, INT16 *output )
768
INT16 SinRz = dsp1_sin(Rz);
769
INT16 CosRz = dsp1_cos(Rz);
770
INT16 SinRy = dsp1_sin(Ry);
771
INT16 CosRy = dsp1_cos(Ry);
772
INT16 SinRx = dsp1_sin(Rx);
773
INT16 CosRx = dsp1_cos(Rx);
777
dsp1_state.shared.MatrixA[0][0] = (S * CosRz >> 15) * CosRy >> 15;
778
dsp1_state.shared.MatrixA[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15);
779
dsp1_state.shared.MatrixA[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15);
781
dsp1_state.shared.MatrixA[1][0] = -((S * SinRz >> 15) * CosRy >> 15);
782
dsp1_state.shared.MatrixA[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15);
783
dsp1_state.shared.MatrixA[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15);
785
dsp1_state.shared.MatrixA[2][0] = S * SinRy >> 15;
786
dsp1_state.shared.MatrixA[2][1] = -((S * SinRx >> 15) * CosRy >> 15);
787
dsp1_state.shared.MatrixA[2][2] = (S * CosRx >> 15) * CosRy >> 15;
790
//////////////////////////////////////////////////////////////////
792
// Same than 'attitudeA', but with a difference attitude matrix (matrixB)
794
static void dsp1_attitudeB( INT16 *input, INT16 *output )
801
INT16 SinRz = dsp1_sin(Rz);
802
INT16 CosRz = dsp1_cos(Rz);
803
INT16 SinRy = dsp1_sin(Ry);
804
INT16 CosRy = dsp1_cos(Ry);
805
INT16 SinRx = dsp1_sin(Rx);
806
INT16 CosRx = dsp1_cos(Rx);
810
dsp1_state.shared.MatrixB[0][0] = (S * CosRz >> 15) * CosRy >> 15;
811
dsp1_state.shared.MatrixB[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15);
812
dsp1_state.shared.MatrixB[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15);
814
dsp1_state.shared.MatrixB[1][0] = -((S * SinRz >> 15) * CosRy >> 15);
815
dsp1_state.shared.MatrixB[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15);
816
dsp1_state.shared.MatrixB[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15);
818
dsp1_state.shared.MatrixB[2][0] = S * SinRy >> 15;
819
dsp1_state.shared.MatrixB[2][1] = -((S * SinRx >> 15) * CosRy >> 15);
820
dsp1_state.shared.MatrixB[2][2] = (S * CosRx >> 15) * CosRy >> 15;
823
//////////////////////////////////////////////////////////////////
825
// Same than 'attitudeA', but with a difference attitude matrix (matrixC)
827
static void dsp1_attitudeC( INT16 *input, INT16 *output )
834
INT16 SinRz = dsp1_sin(Rz);
835
INT16 CosRz = dsp1_cos(Rz);
836
INT16 SinRy = dsp1_sin(Ry);
837
INT16 CosRy = dsp1_cos(Ry);
838
INT16 SinRx = dsp1_sin(Rx);
839
INT16 CosRx = dsp1_cos(Rx);
843
dsp1_state.shared.MatrixC[0][0] = (S * CosRz >> 15) * CosRy >> 15;
844
dsp1_state.shared.MatrixC[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15);
845
dsp1_state.shared.MatrixC[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15);
847
dsp1_state.shared.MatrixC[1][0] = -((S * SinRz >> 15) * CosRy >> 15);
848
dsp1_state.shared.MatrixC[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15);
849
dsp1_state.shared.MatrixC[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15);
851
dsp1_state.shared.MatrixC[2][0] = S * SinRy >> 15;
852
dsp1_state.shared.MatrixC[2][1] = -((S * SinRx >> 15) * CosRy >> 15);
853
dsp1_state.shared.MatrixC[2][2] = (S * CosRx >> 15) * CosRy >> 15;
856
//////////////////////////////////////////////////////////////////
858
// Convert global coordinates (X,Y,Z) to object coordinates (F,L,U)
859
// See the comment in "attitudeA" for a explanation about the calculation.
861
// input[0]: X ; input[1]: Y ; input[2]: Z
862
// &output[0]: F ; &output[1]: L ; &output[2]: U
864
static void dsp1_objectiveA( INT16 *input, INT16 *output )
869
INT16* F = &output[0];
870
INT16* L = &output[1];
871
INT16* U = &output[2];
873
*F = (dsp1_state.shared.MatrixA[0][0] * X >> 15) + (dsp1_state.shared.MatrixA[1][0] * Y >> 15) + (dsp1_state.shared.MatrixA[2][0] * Z >> 15);
874
*L = (dsp1_state.shared.MatrixA[0][1] * X >> 15) + (dsp1_state.shared.MatrixA[1][1] * Y >> 15) + (dsp1_state.shared.MatrixA[2][1] * Z >> 15);
875
*U = (dsp1_state.shared.MatrixA[0][2] * X >> 15) + (dsp1_state.shared.MatrixA[1][2] * Y >> 15) + (dsp1_state.shared.MatrixA[2][2] * Z >> 15);
878
//////////////////////////////////////////////////////////////////
880
// Same than 'objectiveA', but for the 'B' attitude
882
static void dsp1_objectiveB( INT16 *input, INT16 *output )
887
INT16* F = &output[0];
888
INT16* L = &output[1];
889
INT16* U = &output[2];
891
*F = (dsp1_state.shared.MatrixB[0][0] * X >> 15) + (dsp1_state.shared.MatrixB[1][0] * Y >> 15) + (dsp1_state.shared.MatrixB[2][0] * Z >> 15);
892
*L = (dsp1_state.shared.MatrixB[0][1] * X >> 15) + (dsp1_state.shared.MatrixB[1][1] * Y >> 15) + (dsp1_state.shared.MatrixB[2][1] * Z >> 15);
893
*U = (dsp1_state.shared.MatrixB[0][2] * X >> 15) + (dsp1_state.shared.MatrixB[1][2] * Y >> 15) + (dsp1_state.shared.MatrixB[2][2] * Z >> 15);
896
//////////////////////////////////////////////////////////////////
898
// Same than 'objectiveA', but for the 'C' attitude
900
static void dsp1_objectiveC( INT16 *input, INT16 *output )
905
INT16* F = &output[0];
906
INT16* L = &output[1];
907
INT16* U = &output[2];
909
*F = (dsp1_state.shared.MatrixC[0][0] * X >> 15) + (dsp1_state.shared.MatrixC[1][0] * Y >> 15) + (dsp1_state.shared.MatrixC[2][0] * Z >> 15);
910
*L = (dsp1_state.shared.MatrixC[0][1] * X >> 15) + (dsp1_state.shared.MatrixC[1][1] * Y >> 15) + (dsp1_state.shared.MatrixC[2][1] * Z >> 15);
911
*U = (dsp1_state.shared.MatrixC[0][2] * X >> 15) + (dsp1_state.shared.MatrixC[1][2] * Y >> 15) + (dsp1_state.shared.MatrixC[2][2] * Z >> 15);
914
//////////////////////////////////////////////////////////////////
916
// Convert object coordinates (F,L,U) to object coordinates (X,Y,Z)
917
// See the comment in "attitudeA" for a explanation about the calculation.
919
// input[0]: F ; input[1]: L ; input[2]: U
920
// &output[0]: X ; &output[1]: Y ; &output[2]: Z
922
static void dsp1_subjectiveA( INT16 *input, INT16 *output )
927
INT16* X = &output[0];
928
INT16* Y = &output[1];
929
INT16* Z = &output[2];
931
*X = (dsp1_state.shared.MatrixA[0][0] * F >> 15) + (dsp1_state.shared.MatrixA[0][1] * L >> 15) + (dsp1_state.shared.MatrixA[0][2] * U >> 15);
932
*Y = (dsp1_state.shared.MatrixA[1][0] * F >> 15) + (dsp1_state.shared.MatrixA[1][1] * L >> 15) + (dsp1_state.shared.MatrixA[1][2] * U >> 15);
933
*Z = (dsp1_state.shared.MatrixA[2][0] * F >> 15) + (dsp1_state.shared.MatrixA[2][1] * L >> 15) + (dsp1_state.shared.MatrixA[2][2] * U >> 15);
936
//////////////////////////////////////////////////////////////////
938
// Same than 'subjectiveA', but for the 'B' attitude
940
static void dsp1_subjectiveB( INT16 *input, INT16 *output )
945
INT16* X = &output[0];
946
INT16* Y = &output[1];
947
INT16* Z = &output[2];
949
*X = (dsp1_state.shared.MatrixB[0][0] * F >> 15) + (dsp1_state.shared.MatrixB[0][1] * L >> 15) + (dsp1_state.shared.MatrixB[0][2] * U >> 15);
950
*Y = (dsp1_state.shared.MatrixB[1][0] * F >> 15) + (dsp1_state.shared.MatrixB[1][1] * L >> 15) + (dsp1_state.shared.MatrixB[1][2] * U >> 15);
951
*Z = (dsp1_state.shared.MatrixB[2][0] * F >> 15) + (dsp1_state.shared.MatrixB[2][1] * L >> 15) + (dsp1_state.shared.MatrixB[2][2] * U >> 15);
954
//////////////////////////////////////////////////////////////////
956
// Same than 'subjectiveA', but for the 'C' attitude
958
static void dsp1_subjectiveC(INT16 *input, INT16 *output)
963
INT16* X = &output[0];
964
INT16* Y = &output[1];
965
INT16* Z = &output[2];
967
*X = (dsp1_state.shared.MatrixC[0][0] * F >> 15) + (dsp1_state.shared.MatrixC[0][1] * L >> 15) + (dsp1_state.shared.MatrixC[0][2] * U >> 15);
968
*Y = (dsp1_state.shared.MatrixC[1][0] * F >> 15) + (dsp1_state.shared.MatrixC[1][1] * L >> 15) + (dsp1_state.shared.MatrixC[1][2] * U >> 15);
969
*Z = (dsp1_state.shared.MatrixC[2][0] * F >> 15) + (dsp1_state.shared.MatrixC[2][1] * L >> 15) + (dsp1_state.shared.MatrixC[2][2] * U >> 15);
972
//////////////////////////////////////////////////////////////////
974
// This command calculates the inner product (S) of a vector (X,Y,Z) and
975
// the first column of MatrixA. It should be noted that that first column
976
// represent the global coordinates of an unity vector in the forward
977
// direction in the object coordinate system (coordinates (1,0,0) in the FLU
980
// input[0]: X ; input[1]: Y ; input[2]: Z
983
static void dsp1_scalarA( INT16 *input, INT16 *output )
988
INT16* S = &output[0];
990
*S = (X * dsp1_state.shared.MatrixA[0][0] + Y * dsp1_state.shared.MatrixA[1][0] + Z * dsp1_state.shared.MatrixA[2][0]) >> 15;
993
//////////////////////////////////////////////////////////////////
995
// Same than 'scalarA', but for the 'B' attitude
997
static void dsp1_scalarB( INT16 *input, INT16 *output )
1002
INT16* S = &output[0];
1004
*S = (X * dsp1_state.shared.MatrixB[0][0] + Y * dsp1_state.shared.MatrixB[1][0] + Z * dsp1_state.shared.MatrixB[2][0]) >> 15;
1007
//////////////////////////////////////////////////////////////////
1009
// Same than 'scalarA', but for the 'C' attitude
1011
static void dsp1_scalarC( INT16 *input, INT16 *output )
1016
INT16* S = &output[0];
1018
*S = (X * dsp1_state.shared.MatrixC[0][0] + Y * dsp1_state.shared.MatrixC[1][0] + Z * dsp1_state.shared.MatrixC[2][0]) >> 15;
1021
//////////////////////////////////////////////////////////////////
1023
// This command determines the final attitude angles after the body with attitude angles (Ax, Ay, Az) with
1024
// respect to the global coordinates is rotated by the minor angular displacements (DeltaF, DeltaL, DeltaU).
1025
// It means that the XYZ axes are rotated by (Ax, Ay, Az) to obtain the FLU axes and, then, these
1026
// are rotated by (DeltaF, DeltaL, DeltaU). The command calculates and return the new FLU angles respect to the
1027
// XYZ system (Rx, Ry, Rz)
1028
// The formulae are:
1029
// Rx = Ax + (DeltaU*sin(Ay)+DeltaF*cos(Ay))
1030
// Ry = Ay + DeltaL - tan(Ax)*(DeltaU*cos(Ay)+DeltaF*sin(Ay))
1031
// Rz = Az + sec(Ax)*(DeltaU*cos(Ay)-DeltaF*sin(Ay))
1033
// Now the discussion: according to the official documentation, as described in various commands, you pass from
1034
// XYZ to FLU by doing the rotations in the order Y, X, Z. In this command, the formulae are coherent with the
1035
// fact that Y is the first axis to do a rotation around it. However, in the "attitude" command, while the official
1036
// document describe it that way, we have discovered, when reverse engineering the command, that the calculated
1037
// matrix do the rotation around Y in the second place. This incoherent behaviour of various commands is, in my
1038
// opinion, a pretty severe implementation error. However, if you only use small "minor displacements", the error term
1039
// introduced by that incoherence should be almost negligible.
1041
static void dsp1_gyrate( INT16 *input, INT16 *output )
1043
INT16 Az = input[0];
1044
INT16 Ax = input[1];
1045
INT16 Ay = input[2];
1049
INT16* Rz = &output[0];
1050
INT16* Rx = &output[1];
1051
INT16* Ry = &output[2];
1053
INT16 CSec, ESec, CSin, C, E;
1054
INT16 SinAy = dsp1_sin(Ay);
1055
INT16 CosAy = dsp1_cos(Ay);
1057
inverse(dsp1_cos(Ax), 0, &CSec, &ESec);
1059
// Rotation Around Z
1060
normalize_double(U * CosAy - F * SinAy, &C, &E);
1064
normalize(C * CSec >> 15, &C, &E);
1066
*Rz = Az + denormalize_and_clip(C, E);
1068
// Rotation Around X
1069
*Rx = Ax + (U * SinAy >> 15) + (F * CosAy >> 15);
1071
// Rotation Around Y
1072
normalize_double(U * CosAy + F * SinAy, &C, &E);
1076
normalize(dsp1_sin(Ax), &CSin, &E);
1078
normalize(-(C * (CSec * CSin >> 15) >> 15), &C, &E);
1080
*Ry = Ay + denormalize_and_clip(C, E) + L;
1083
//////////////////////////////////////////////////////////////////
1086
// Set-up the projection framework. Besides returning some values, it store in RAM some values that
1087
// will be used by the other three projection commands (raster, target an project)
1089
// (Fx, Fy, Fz)-> coordinates of base point (global coordinates)
1090
// Lfe-> distance between the base point and the viewpoint (center of projection)
1091
// Les-> distance between the base point and the screen
1092
// Aas-> azimuth angle (0 degrees is east; 90 degrees is north)
1093
// Azs-> zenith angle (0 degrees is zenith)
1095
// Vof-> raster line of imaginary center (whatever it means ;) )
1096
// Vva-> raster line representing the horizon line
1097
// (Cx, Cy)-> coordinates of the projection of the center of the screen over the ground (ground coordinates)
1099
static void dsp1_parameter( INT16 *input, INT16 *output )
1101
INT16 Fx = input[0];
1102
INT16 Fy = input[1];
1103
INT16 Fz = input[2];
1104
INT16 Lfe = input[3];
1105
INT16 Les = input[4];
1106
INT16 Aas = input[5];
1107
INT16 Azs = input[6];
1108
INT16* Vof = &output[0];
1109
INT16* Vva = &output[1];
1110
INT16* Cx = &output[2];
1111
INT16* Cy = &output[3];
1114
INT16 LfeNx, LfeNy, LfeNz;
1115
INT16 LesNx, LesNy, LesNz;
1118
// Copy Zenith angle for clipping
1121
// Store Les and his coefficient and exponent when normalized
1122
dsp1_state.shared.Les = Les;
1123
dsp1_state.shared.E_Les = 0;
1124
normalize(Les, &dsp1_state.shared.C_Les, &dsp1_state.shared.E_Les);
1126
// Store Sine and Cosine of Azimuth and Zenith angle
1127
dsp1_state.shared.SinAas = dsp1_sin(Aas);
1128
dsp1_state.shared.CosAas = dsp1_cos(Aas);
1129
dsp1_state.shared.SinAzs = dsp1_sin(Azs);
1130
dsp1_state.shared.CosAzs = dsp1_cos(Azs);
1132
// normal vector to the screen (norm 1, points toward the center of projection)
1133
dsp1_state.shared.Nx = dsp1_state.shared.SinAzs * -dsp1_state.shared.SinAas >> 15;
1134
dsp1_state.shared.Ny = dsp1_state.shared.SinAzs * dsp1_state.shared.CosAas >> 15;
1135
dsp1_state.shared.Nz = dsp1_state.shared.CosAzs * 0x7fff >> 15;
1137
// horizontal vector of the screen (Hz=0, norm 1, points toward the right of the screen)
1138
dsp1_state.shared.Hx = dsp1_state.shared.CosAas*0x7fff>>15;
1139
dsp1_state.shared.Hy = dsp1_state.shared.SinAas*0x7fff>>15;
1141
// vertical vector of the screen (norm 1, points toward the top of the screen)
1142
dsp1_state.shared.Vx = dsp1_state.shared.CosAzs * -dsp1_state.shared.SinAas >> 15;
1143
dsp1_state.shared.Vy = dsp1_state.shared.CosAzs * dsp1_state.shared.CosAas >> 15;
1144
dsp1_state.shared.Vz = -dsp1_state.shared.SinAzs * 0x7fff>>15;
1146
LfeNx = Lfe * dsp1_state.shared.Nx >> 15;
1147
LfeNy = Lfe * dsp1_state.shared.Ny >> 15;
1148
LfeNz = Lfe * dsp1_state.shared.Nz >> 15;
1150
// Center of Projection
1151
dsp1_state.shared.CentreX = Fx + LfeNx;
1152
dsp1_state.shared.CentreY = Fy + LfeNy;
1153
dsp1_state.shared.CentreZ = Fz + LfeNz;
1155
LesNx = Les * dsp1_state.shared.Nx >> 15;
1156
LesNy = Les * dsp1_state.shared.Ny >> 15;
1157
LesNz = Les * dsp1_state.shared.Nz >> 15;
1159
// center of the screen (global coordinates)
1160
dsp1_state.shared.Gx = dsp1_state.shared.CentreX - LesNx;
1161
dsp1_state.shared.Gy = dsp1_state.shared.CentreY - LesNy;
1162
dsp1_state.shared.Gz = dsp1_state.shared.CentreZ - LesNz;
1165
normalize(dsp1_state.shared.CentreZ, &C, &E);
1167
dsp1_state.shared.CentreZ_C = C;
1168
dsp1_state.shared.CentreZ_E = E;
1170
// Determine clip boundary and clip Zenith angle if necessary
1171
// (Why to clip? Maybe to avoid the screen can only show sky with no ground? Only a guess...)
1172
MaxAZS = dsp1_MaxAZS_Exp[-E];
1177
if (AZS < MaxAZS + 1)
1186
// Store Sine and Cosine of clipped Zenith angle
1187
dsp1_state.shared.SinAZS = dsp1_sin(AZS);
1188
dsp1_state.shared.CosAZS = dsp1_cos(AZS);
1190
// calculate the separation of (cx, cy) from the projection of
1191
// the 'centre of projection' over the ground... (CentreZ*tg(AZS))
1192
inverse(dsp1_state.shared.CosAZS, 0, &dsp1_state.shared.SecAZS_C1, &dsp1_state.shared.SecAZS_E1);
1193
normalize(C * dsp1_state.shared.SecAZS_C1 >> 15, &C, &E);
1194
E += dsp1_state.shared.SecAZS_E1;
1195
C = denormalize_and_clip(C, E) * dsp1_state.shared.SinAZS >> 15;
1197
// ... and then take into account the position of the centre of
1198
// projection and the azimuth angle
1199
dsp1_state.shared.CentreX += C * dsp1_state.shared.SinAas >> 15;
1200
dsp1_state.shared.CentreY -= C * dsp1_state.shared.CosAas >> 15;
1202
*Cx = dsp1_state.shared.CentreX;
1203
*Cy = dsp1_state.shared.CentreY;
1205
// Raster number of imaginary center and horizontal line
1208
if ((Azs != AZS) || (Azs == MaxAZS))
1212
// correct vof and vva when Azs is outside the 'non-clipping interval'
1213
// we have only some few Taylor coefficients, so we cannot guess which ones
1214
// are the approximated functions and, what is worse, we don't know why
1215
// the own clipping stuff (and, particularly, this correction) is done
1224
// Vof += x+(1/3)*x^3, where x ranges from 0 to PI/4 when Azs-MaxAZS goes from 0 to 0x2000
1225
C = Aux * dsp1_state.DataRom[0x0328] >> 15;
1226
C = (C * Aux >> 15) + dsp1_state.DataRom[0x0327];
1227
*Vof -= (C * Aux >> 15) * Les >> 15;
1229
// CosAZS *= 1+(1/2)*x^2+(5/24)*x^24, where x ranges from 0 to PI/4 when Azs-MaxAZS goes from 0 to 0x2000
1230
C = Aux * Aux >> 15;
1231
Aux = (C * dsp1_state.DataRom[0x0324] >> 15) + dsp1_state.DataRom[0x0325];
1232
dsp1_state.shared.CosAZS += (C * Aux >> 15) * dsp1_state.shared.CosAZS >> 15;
1235
// vertical offset of the screen with regard to the horizontal plane
1236
// containing the centre of projection
1237
dsp1_state.shared.VOffset = Les * dsp1_state.shared.CosAZS >> 15;
1239
// The horizon line (the line in the screen that is crossed by the horizon plane
1240
// -the horizontal plane containing the 'centre of projection'-),
1241
// will be at distance Les*cotg(AZS) from the centre of the screen. This is difficult
1242
// to explain but easily seen in a graph. To better see it, consider it in this way:
1243
// Les*tg(AZS-90), draw some lines and apply basic trigonometry. ;)
1244
inverse(dsp1_state.shared.SinAZS, 0, &CSec, &E);
1245
normalize(dsp1_state.shared.VOffset, &C, &E);
1246
normalize(C * CSec >> 15, &C, &E);
1254
*Vva = denormalize_and_clip(-C, E);
1256
// Store Secant of clipped Zenith angle
1257
inverse(dsp1_state.shared.CosAZS, 0, &dsp1_state.shared.SecAZS_C2, &dsp1_state.shared.SecAZS_E2);
1260
//////////////////////////////////////////////////////////////////
1262
// Calculates the matrix which transform an object situated on a raster line (Vs) into
1263
// his projection over the ground. The modified SecAZS is used here, so
1264
// i don't understand the fine details, but, basically, it's done
1265
// this way: The vertical offset between the point of projection and the
1266
// raster line is calculated (Vs*SinAzs>>15)+VOffset, then the height of
1267
// the center of projection is measured in that units (*CentreZ_C). If, now
1268
// you consider the "reference case" (center of projection at an unit of height),
1269
// the projection of a thin strip containing the raster line will have the same
1270
// width (as the raster line would be on the ground in this case, but will suffer a
1271
// change of scale in height (as the ground and the vertical axis would form an angle of 180-Azs degrees).
1272
// This scale factor, when the angle 'center of screen-center of projection-raster line' is small,
1273
// can be aproximated by the one of the center of the screen, 1/cos(Azs).(**) (Here is when it's used
1274
// SecAZS). By last, you have to consider the effect of the azimuth angle Aas, and you are done.
1276
// Using matrix notation:
1277
// |A B| Centre_ZS | cos(Aas) -sin(Aas)| | 1 0 |
1278
// ProjectionMatrix = | | = ----------- * | | * | |
1279
// |C D| Vs*sin(Azs) | sin(Aas) cos(Aas)| | 0 sec(Azs)|
1282
// If Les=1, the vertical offset between the center
1283
// of projection and the center of the screen is Cos(Azs); then, if the vertical
1284
// offset is 1, the ratio of the projection over the ground respect to the
1285
// line on the screen is 1/cos(Azs).
1287
static void dsp1_raster( INT16 *input, INT16 *output )
1289
INT16 Vs = input[0];
1290
INT16* An = &output[0];
1291
INT16* Bn = &output[1];
1292
INT16* Cn = &output[2];
1293
INT16* Dn = &output[3];
1297
inverse((Vs * dsp1_state.shared.SinAzs >> 15) + dsp1_state.shared.VOffset, 7, &C, &E);
1299
E += dsp1_state.shared.CentreZ_E;
1300
C1 = C * dsp1_state.shared.CentreZ_C >> 15;
1302
E1 = E + dsp1_state.shared.SecAZS_E2;
1304
normalize(C1, &C, &E);
1305
C = denormalize_and_clip(C, E);
1307
*An = C * dsp1_state.shared.CosAas >> 15;
1308
*Cn = C * dsp1_state.shared.SinAas >> 15;
1310
normalize(C1 * dsp1_state.shared.SecAZS_C2 >> 15, &C, &E1);
1311
C = denormalize_and_clip(C, E1);
1313
*Bn = C * -dsp1_state.shared.SinAas >> 15;
1314
*Dn = C * dsp1_state.shared.CosAas >> 15;
1317
//////////////////////////////////////////////////////////////////
1319
// Calculate the projection over the ground of a selected point of screen
1320
// It simply apply the projection matrix described in the "Raster" command
1321
// to the vector (H,V) transposed, and add the result to the position of
1322
// the centre of projection.
1323
// The only special point to take into account is the directions on the screen:
1324
// H is positive rightward, but V is positive downward; this is why
1325
// the signs take that configuration
1327
static void dsp1_target( INT16 *input, INT16 *output )
1331
INT16* X = &output[0];
1332
INT16* Y = &output[1];
1336
inverse((V * dsp1_state.shared.SinAzs >> 15) + dsp1_state.shared.VOffset, 8, &C, &E);
1338
E += dsp1_state.shared.CentreZ_E;
1339
C1 = C * dsp1_state.shared.CentreZ_C >> 15;
1341
E1 = E + dsp1_state.shared.SecAZS_E1;
1344
normalize(C1, &C, &E);
1345
C = denormalize_and_clip(C, E) * H >> 15;
1347
*X = dsp1_state.shared.CentreX + (C * dsp1_state.shared.CosAas >> 15);
1348
*Y = dsp1_state.shared.CentreY - (C * dsp1_state.shared.SinAas >> 15);
1351
normalize(C1 * dsp1_state.shared.SecAZS_C1 >> 15, &C, &E1);
1352
C = denormalize_and_clip(C, E1) * V >> 15;
1354
*X += C * -dsp1_state.shared.SinAas >> 15;
1355
*Y += C * dsp1_state.shared.CosAas >> 15;
1358
//////////////////////////////////////////////////////////////////
1360
// Calculation of the projection over the screen (H,V) of an object (X,Y,Z) and his
1361
// 'enlargement ratio' (M). The positive directions on the screen are as described
1362
// in the targe command. M is scaled down by 2^-7, that is, M==0x0100 means ratio 1:1
1364
static void dsp1_project( INT16 *input, INT16 *output )
1369
INT16* H = &output[0];
1370
INT16* V = &output[1];
1371
INT16* M = &output[2];
1374
INT16 E, E2, E3, E4, E5, refE, E6, E7;
1375
INT16 C2, C4, C6, C8, C9, C10, C11, C12, C16, C17, C18, C19, C20, C21, C22, C23, C24, C25, C26;
1378
E4 = E3 = E2 = E = E5 = 0;
1380
normalize_double((INT32)(X) - dsp1_state.shared.Gx, &Px, &E4);
1381
normalize_double((INT32)(Y) - dsp1_state.shared.Gy, &Py, &E);
1382
normalize_double((INT32)(Z) - dsp1_state.shared.Gz, &Pz, &E3);
1383
Px >>= 1; E4--; // to avoid overflows when calculating the scalar products
1387
refE = (E < E3) ? E : E3;
1388
refE = (refE < E4) ? refE : E4;
1390
Px = shiftR(Px, E4 - refE); // normalize them to the same exponent
1391
Py = shiftR(Py, E - refE);
1392
Pz = shiftR(Pz, E3 - refE);
1394
C11 = -(Px * dsp1_state.shared.Nx >> 15);
1395
C8 = -(Py * dsp1_state.shared.Ny >> 15);
1396
C9 = -(Pz * dsp1_state.shared.Nz >> 15);
1397
C12 = C11 + C8 + C9; // this cannot overflow!
1399
aux4 = C12; // de-normalization with 32-bits arithmetic
1400
refE = 16 - refE; // refE can be up to 3
1409
aux = (UINT16)(dsp1_state.shared.Les) + aux4; // Les - the scalar product of P with the normal vector of the screen
1410
normalize_double(aux, &C10, &E2);
1413
inverse(C10, 0, &C4, &E4);
1414
C2 = C4 * dsp1_state.shared.C_Les >> 15; // scale factor
1419
C16 = (Px * dsp1_state.shared.Hx >> 15);
1420
C20 = (Py * dsp1_state.shared.Hy >> 15);
1421
C17 = C16 + C20; // scalar product of P with the normalized horizontal vector of the screen...
1423
C18 = C17 * C2 >> 15; // ... multiplied by the scale factor
1424
normalize(C18, &C19, &E7);
1425
*H = denormalize_and_clip(C19, dsp1_state.shared.E_Les - E2 + refE + E7);
1429
C21 = Px * dsp1_state.shared.Vx >> 15;
1430
C22 = Py * dsp1_state.shared.Vy >> 15;
1431
C23 = Pz * dsp1_state.shared.Vz >> 15;
1432
C24 = C21 + C22 + C23; // scalar product of P with the normalized vertical vector of the screen...
1434
C26 = C24 * C2 >> 15; // ... multiplied by the scale factor
1435
normalize(C26, &C25, &E6);
1436
*V = denormalize_and_clip(C25, dsp1_state.shared.E_Les - E2 + refE + E6);
1439
normalize(C2, &C6, &E4);
1440
*M = denormalize_and_clip(C6, E4 + dsp1_state.shared.E_Les - E2 - 7); // M is the scale factor divided by 2^7
1443
//////////////////////////////////////////////////////////////////
1445
// Calculate the sine of the input parameter
1446
// this is done by linear interpolation between
1447
// the points of a look-up table
1449
static INT16 dsp1_sin( INT16 Angle )
1455
if (Angle == -32768)
1458
return -dsp1_sin(-Angle);
1460
S = dsp1_sin_table[Angle >> 8] + (dsp1_mul_table[Angle & 0xff] * dsp1_sin_table[0x40 + (Angle >> 8)] >> 15);
1466
//////////////////////////////////////////////////////////////////
1468
// Calculate the cosine of the input parameter.
1469
// It's used the same method than in sin(INT16)
1471
static INT16 dsp1_cos( INT16 Angle )
1477
if (Angle == -32768)
1481
S = dsp1_sin_table[0x40 + (Angle >> 8)] - (dsp1_mul_table[Angle & 0xff] * dsp1_sin_table[Angle >> 8] >> 15);
1487
//////////////////////////////////////////////////////////////////
1489
// Determines the inverse of a floating point decimal number
1490
// iCoefficient*2^iExponent = 1/(Coefficient*2^Exponent), with the output
1491
// normalized (iCoefficient represents a number whose absolute value is between 1/2 and 1)
1492
// To invert 'Coefficient' a first initial guess is taken from a look-up table
1493
// and, then, two iterations of the Newton method (applied to the function
1494
// f(x)=1/(2*x)-Coefficient) are done. This results in a close approximation (iCoefficient) to a number 'y'
1495
// that verify Coefficient*y=1/2. This is why you have to correct the exponent by one
1498
static void inverse( INT16 Coefficient, INT16 Exponent, INT16 *iCoefficient, INT16 *iExponent )
1500
// Step One: Division by Zero
1501
if (Coefficient == 0x0000)
1503
*iCoefficient = 0x7fff;
1504
*iExponent = 0x002f;
1510
// Step Two: Remove Sign
1511
if (Coefficient < 0)
1513
if (Coefficient < -32767)
1514
Coefficient = -32767;
1515
Coefficient = -Coefficient;
1519
// Step Three: Normalize
1520
while (Coefficient < 0x4000)
1526
// Step Four: Special Case
1527
if (Coefficient == 0x4000)
1528
if (Sign == 1) *iCoefficient = 0x7fff;
1530
*iCoefficient = -0x4000;
1534
// Step Five: Initial Guess
1535
INT16 i = dsp1_state.DataRom[((Coefficient - 0x4000) >> 7) + 0x0065];
1537
// Step Six: Iterate Newton's Method
1538
i = (i + (-i * (Coefficient * i >> 15) >> 15)) << 1;
1539
i = (i + (-i * (Coefficient * i >> 15) >> 15)) << 1;
1541
*iCoefficient = i * Sign;
1544
*iExponent = 1 - Exponent;
1548
//////////////////////////////////////////////////////////////////
1550
static INT16 denormalize_and_clip( INT16 C, INT16 E )
1562
return C * dsp1_state.DataRom[0x0031 + E] >> 15;
1567
//////////////////////////////////////////////////////////////////
1569
// Normalize the input number (m), understood as ranging from -1 to 1,
1570
// to the form: Coefficient*2^Exponent,
1571
// where the absolute value of Coefficient is >= 1/2
1572
// (Coefficient>=0x4000 or Coefficient <= (INT16)0xc001)
1574
static void normalize( INT16 m, INT16 *Coefficient, INT16 *Exponent )
1580
while ((m & i) && i)
1586
while (!(m & i) && i)
1593
*Coefficient = m * dsp1_state.DataRom[0x21 + e] << 1;
1600
//////////////////////////////////////////////////////////////////
1602
// Same than 'normalize' but with an INT32 input
1604
static void normalize_double( INT32 Product, INT16 *Coefficient, INT16 *Exponent )
1606
INT16 n = Product & 0x7fff;
1607
INT16 m = Product >> 15;
1612
while ((m & i) && i)
1618
while (!(m & i) && i)
1626
*Coefficient = m * dsp1_state.DataRom[0x0021 + e] << 1;
1629
*Coefficient += n * dsp1_state.DataRom[0x0040 - e] >> 15;
1635
while ((n & i) && i)
1641
while (!(n & i) && i)
1648
*Coefficient = n * dsp1_state.DataRom[0x0012 + e] << 1;
1659
//////////////////////////////////////////////////////////////////
1661
// Shift to the right
1663
static INT16 shiftR( INT16 C, INT16 E )
1665
return (C * dsp1_state.DataRom[0x0031 + E] >> 15);
1668
//////////////////////////////////////////////////////////////////
1670
// Save DSP1 variables
1672
static void dsp1_register_save( running_machine *machine )
1674
state_save_register_global(machine, dsp1_state.Sr);
1675
state_save_register_global(machine, dsp1_state.Dr);
1676
state_save_register_global(machine, dsp1_state.SrLowByteAccess);
1677
state_save_register_global(machine, dsp1_state.FsmMajorState);
1678
state_save_register_global(machine, dsp1_state.Command);
1679
state_save_register_global(machine, dsp1_state.DataCounter);
1680
state_save_register_global(machine, dsp1_state.Freeze);
1681
state_save_register_global_array(machine, dsp1_state.ReadBuffer);
1682
state_save_register_global_array(machine, dsp1_state.WriteBuffer);
1683
state_save_register_global_array(machine, dsp1_state.shared.MatrixA[0]);
1684
state_save_register_global_array(machine, dsp1_state.shared.MatrixA[1]);
1685
state_save_register_global_array(machine, dsp1_state.shared.MatrixA[2]);
1686
state_save_register_global_array(machine, dsp1_state.shared.MatrixB[0]);
1687
state_save_register_global_array(machine, dsp1_state.shared.MatrixB[1]);
1688
state_save_register_global_array(machine, dsp1_state.shared.MatrixB[2]);
1689
state_save_register_global_array(machine, dsp1_state.shared.MatrixC[0]);
1690
state_save_register_global_array(machine, dsp1_state.shared.MatrixC[1]);
1691
state_save_register_global_array(machine, dsp1_state.shared.MatrixC[2]);
1692
state_save_register_global(machine, dsp1_state.shared.CentreX);
1693
state_save_register_global(machine, dsp1_state.shared.CentreY);
1694
state_save_register_global(machine, dsp1_state.shared.CentreZ);
1695
state_save_register_global(machine, dsp1_state.shared.CentreZ_C);
1696
state_save_register_global(machine, dsp1_state.shared.CentreZ_E);
1697
state_save_register_global(machine, dsp1_state.shared.VOffset);
1698
state_save_register_global(machine, dsp1_state.shared.Les);
1699
state_save_register_global(machine, dsp1_state.shared.C_Les);
1700
state_save_register_global(machine, dsp1_state.shared.E_Les);
1701
state_save_register_global(machine, dsp1_state.shared.SinAas);
1702
state_save_register_global(machine, dsp1_state.shared.CosAas);
1703
state_save_register_global(machine, dsp1_state.shared.SinAzs);
1704
state_save_register_global(machine, dsp1_state.shared.CosAzs);
1705
state_save_register_global(machine, dsp1_state.shared.SinAZS);
1706
state_save_register_global(machine, dsp1_state.shared.CosAZS);
1707
state_save_register_global(machine, dsp1_state.shared.SecAZS_C1);
1708
state_save_register_global(machine, dsp1_state.shared.SecAZS_E1);
1709
state_save_register_global(machine, dsp1_state.shared.SecAZS_C2);
1710
state_save_register_global(machine, dsp1_state.shared.SecAZS_E2);
1711
state_save_register_global(machine, dsp1_state.shared.Nx);
1712
state_save_register_global(machine, dsp1_state.shared.Ny);
1713
state_save_register_global(machine, dsp1_state.shared.Nz);
1714
state_save_register_global(machine, dsp1_state.shared.Gx);
1715
state_save_register_global(machine, dsp1_state.shared.Gy);
1716
state_save_register_global(machine, dsp1_state.shared.Gz);
1717
state_save_register_global(machine, dsp1_state.shared.Hx);
1718
state_save_register_global(machine, dsp1_state.shared.Hy);
1719
state_save_register_global(machine, dsp1_state.shared.Vx);
1720
state_save_register_global(machine, dsp1_state.shared.Vy);
1721
state_save_register_global(machine, dsp1_state.shared.Vz);