mirror of
https://github.com/libretro/snes9x2005.git
synced 2024-11-30 12:00:27 +00:00
1243 lines
31 KiB
C
1243 lines
31 KiB
C
#include "../copyright"
|
|
|
|
#include "dsp4.h"
|
|
#include "memmap.h"
|
|
|
|
#define DSP4_READ_WORD(x) \
|
|
READ_WORD(DSP4.parameters+x)
|
|
|
|
#define DSP4_WRITE_WORD(x,d) \
|
|
WRITE_WORD(DSP4.output+x,d);
|
|
|
|
// used to wait for dsp i/o
|
|
#define DSP4_WAIT(x) \
|
|
DSP4_Logic = x; \
|
|
return
|
|
|
|
int32_t DSP4_Multiply(int16_t Multiplicand, int16_t Multiplier)
|
|
{
|
|
return Multiplicand * Multiplier;
|
|
}
|
|
|
|
int16_t DSP4_UnknownOP11(int16_t A, int16_t B, int16_t C, int16_t D)
|
|
{
|
|
return ((A * 0x0155 >> 2) & 0xf000) | ((B * 0x0155 >> 6) & 0x0f00) | ((C * 0x0155 >> 10) & 0x00f0) | ((D * 0x0155 >> 14) & 0x000f);
|
|
}
|
|
|
|
void DSP4_Op06(bool size, bool msb)
|
|
{
|
|
// save post-oam table data for future retrieval
|
|
op06_OAM[op06_index] |= (msb << (op06_offset + 0));
|
|
op06_OAM[op06_index] |= (size << (op06_offset + 1));
|
|
op06_offset += 2;
|
|
|
|
if (op06_offset == 8)
|
|
{
|
|
// move to next byte in buffer
|
|
op06_offset = 0;
|
|
op06_index++;
|
|
}
|
|
}
|
|
|
|
void DSP4_Op01(void)
|
|
{
|
|
int16_t plane;
|
|
int16_t index, lcv;
|
|
int16_t py_dy, px_dx;
|
|
int16_t y_out, x_out;
|
|
uint16_t command;
|
|
DSP4.waiting4command = false;
|
|
|
|
switch (DSP4_Logic) // op flow control
|
|
{
|
|
case 1:
|
|
goto resume1;
|
|
break;
|
|
case 2:
|
|
goto resume2;
|
|
break;
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// process initial inputs
|
|
|
|
// sort inputs
|
|
project_focaly = DSP4_READ_WORD(0x02);
|
|
raster = DSP4_READ_WORD(0x04);
|
|
viewport_top = DSP4_READ_WORD(0x06);
|
|
project_y = DSP4_READ_WORD(0x08);
|
|
viewport_bottom = DSP4_READ_WORD(0x0a);
|
|
project_x1low = DSP4_READ_WORD(0x0c);
|
|
project_focalx = DSP4_READ_WORD(0x0e);
|
|
project_centerx = DSP4_READ_WORD(0x10);
|
|
project_ptr = DSP4_READ_WORD(0x12);
|
|
project_pitchylow = DSP4_READ_WORD(0x16);
|
|
project_pitchy = DSP4_READ_WORD(0x18);
|
|
project_pitchxlow = DSP4_READ_WORD(0x1a);
|
|
project_pitchx = DSP4_READ_WORD(0x1c);
|
|
far_plane = DSP4_READ_WORD(0x1e);
|
|
project_y1low = DSP4_READ_WORD(0x22);
|
|
|
|
// pre-compute
|
|
view_plane = PLANE_START;
|
|
|
|
// find starting projection points
|
|
project_x1 = project_focalx;
|
|
project_y -= viewport_bottom;
|
|
project_x = project_centerx + project_x1;
|
|
|
|
// multi-op storage
|
|
multi_index1 = 0;
|
|
multi_index2 = 0;
|
|
|
|
////////////////////////////////////////////////////
|
|
// command check
|
|
|
|
do
|
|
{
|
|
// scan next command
|
|
DSP4.in_count = 2;
|
|
DSP4_WAIT(1);
|
|
|
|
resume1:
|
|
// inspect input
|
|
command = DSP4_READ_WORD(0);
|
|
|
|
// check for termination
|
|
if(command == 0x8000)
|
|
break;
|
|
|
|
// already have 2 bytes in queue
|
|
DSP4.in_index = 2;
|
|
DSP4.in_count = 8;
|
|
DSP4_WAIT(2);
|
|
|
|
////////////////////////////////////////////////////
|
|
// process one iteration of projection
|
|
|
|
// inspect inputs
|
|
|
|
resume2:
|
|
plane = DSP4_READ_WORD(0);
|
|
px_dx = 0;
|
|
|
|
// ignore invalid data
|
|
if((uint16_t) plane == 0x8001)
|
|
continue;
|
|
|
|
// one-time init
|
|
if (far_plane)
|
|
{
|
|
// setup final parameters
|
|
project_focalx += plane;
|
|
project_x1 = project_focalx;
|
|
project_y1 = project_focaly;
|
|
plane = far_plane;
|
|
far_plane = 0;
|
|
}
|
|
|
|
// use proportional triangles to project new coords
|
|
project_x2 = project_focalx * plane / view_plane;
|
|
project_y2 = project_focaly * plane / view_plane;
|
|
|
|
// quadratic regression (rough)
|
|
if (project_focaly >= -0x0f)
|
|
py_dy = (int16_t)(project_focaly * project_focaly * -0.20533553 - 1.08330005 * project_focaly - 69.61094639);
|
|
else
|
|
py_dy = (int16_t)(project_focaly * project_focaly * -0.000657035759 - 1.07629051 * project_focaly - 65.69315963);
|
|
|
|
// approximate # of raster lines
|
|
segments = ABS(project_y2 - project_y1);
|
|
|
|
// prevent overdraw
|
|
if(project_y2 >= raster)
|
|
segments = 0;
|
|
else
|
|
raster = project_y2;
|
|
|
|
// don't draw outside the window
|
|
if(project_y2 < viewport_top)
|
|
segments = 0;
|
|
|
|
// project new positions
|
|
if (segments > 0)
|
|
px_dx = ((project_x2 - project_x1) << 8) / segments; // interpolate between projected points
|
|
|
|
// prepare output
|
|
DSP4.out_count = 8 + 2 + 6 * segments;
|
|
|
|
// pre-block data
|
|
DSP4_WRITE_WORD(0, project_focalx);
|
|
DSP4_WRITE_WORD(2, project_x2);
|
|
DSP4_WRITE_WORD(4, project_focaly);
|
|
DSP4_WRITE_WORD(6, project_y2);
|
|
DSP4_WRITE_WORD(8, segments);
|
|
|
|
index = 10;
|
|
|
|
for (lcv = 0; lcv < segments; lcv++) // iterate through each point
|
|
{
|
|
// step through the projected line
|
|
y_out = project_y + ((py_dy * lcv) >> 8);
|
|
x_out = project_x + ((px_dx * lcv) >> 8);
|
|
|
|
// data
|
|
DSP4_WRITE_WORD(index + 0, project_ptr);
|
|
DSP4_WRITE_WORD(index + 2, y_out);
|
|
DSP4_WRITE_WORD(index + 4, x_out);
|
|
index += 6;
|
|
|
|
// post-update
|
|
project_ptr -= 4;
|
|
}
|
|
|
|
// post-update
|
|
project_y += ((py_dy * lcv) >> 8);
|
|
project_x += ((px_dx * lcv) >> 8);
|
|
|
|
if (segments > 0) // new positions
|
|
{
|
|
project_x1 = project_x2;
|
|
project_y1 = project_y2;
|
|
|
|
// multi-op storage
|
|
multi_focaly[multi_index2++] = project_focaly;
|
|
multi_farplane[1] = plane;
|
|
multi_raster[1] = project_y1 - 1;
|
|
}
|
|
|
|
// update projection points
|
|
project_pitchy += (int8_t)DSP4.parameters[3];
|
|
project_pitchx += (int8_t)DSP4.parameters[5];
|
|
|
|
project_focaly += project_pitchy;
|
|
project_focalx += project_pitchx;
|
|
} while (1);
|
|
|
|
// terminate op
|
|
DSP4.waiting4command = true;
|
|
DSP4.out_count = 0;
|
|
}
|
|
|
|
void DSP4_Op07(void)
|
|
{
|
|
uint16_t command;
|
|
int16_t plane;
|
|
int16_t index, lcv;
|
|
int16_t y_out, x_out;
|
|
int16_t py_dy, px_dx;
|
|
|
|
DSP4.waiting4command = false;
|
|
|
|
// op flow control
|
|
switch (DSP4_Logic)
|
|
{
|
|
case 1:
|
|
goto resume1;
|
|
break;
|
|
case 2:
|
|
goto resume2;
|
|
break;
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// sort inputs
|
|
|
|
project_focaly = DSP4_READ_WORD(0x02);
|
|
raster = DSP4_READ_WORD(0x04);
|
|
viewport_top = DSP4_READ_WORD(0x06);
|
|
project_y = DSP4_READ_WORD(0x08);
|
|
viewport_bottom = DSP4_READ_WORD(0x0a);
|
|
project_x1low = DSP4_READ_WORD(0x0c);
|
|
project_x1 = DSP4_READ_WORD(0x0e);
|
|
project_centerx = DSP4_READ_WORD(0x10);
|
|
project_ptr = DSP4_READ_WORD(0x12);
|
|
|
|
// pre-compute
|
|
view_plane = PLANE_START;
|
|
|
|
// find projection targets
|
|
project_y1 = project_focaly;
|
|
project_y -= viewport_bottom;
|
|
project_x = project_centerx + project_x1;
|
|
|
|
// multi-op storage
|
|
multi_index2 = 0;
|
|
|
|
////////////////////////////////////////////////////
|
|
// command check
|
|
|
|
do
|
|
{
|
|
// scan next command
|
|
DSP4.in_count = 2;
|
|
DSP4_WAIT(1);
|
|
|
|
resume1:
|
|
// inspect input
|
|
command = DSP4_READ_WORD(0);
|
|
|
|
// check for opcode termination
|
|
if(command == 0x8000)
|
|
break;
|
|
|
|
// already have 2 bytes in queue
|
|
DSP4.in_index = 2;
|
|
DSP4.in_count = 12;
|
|
DSP4_WAIT(2);
|
|
|
|
////////////////////////////////////////////////////
|
|
// process one loop of projection
|
|
|
|
resume2:
|
|
px_dx = 0;
|
|
|
|
// inspect inputs
|
|
plane = DSP4_READ_WORD(0);
|
|
project_y2 = DSP4_READ_WORD(2);
|
|
project_x2 = DSP4_READ_WORD(6);
|
|
|
|
// ignore invalid data
|
|
if((uint16_t) plane == 0x8001)
|
|
continue;
|
|
|
|
// multi-op storage
|
|
project_focaly = multi_focaly[multi_index2];
|
|
|
|
// quadratic regression (rough)
|
|
if (project_focaly >= -0x0f)
|
|
py_dy = (int16_t)(project_focaly * project_focaly * -0.20533553 - 1.08330005 * project_focaly - 69.61094639);
|
|
else
|
|
py_dy = (int16_t)(project_focaly * project_focaly * -0.000657035759 - 1.07629051 * project_focaly - 65.69315963);
|
|
|
|
// approximate # of raster lines
|
|
segments = ABS(project_y2 - project_y1);
|
|
|
|
// prevent overdraw
|
|
if(project_y2 >= raster)
|
|
segments = 0;
|
|
else
|
|
raster = project_y2;
|
|
|
|
// don't draw outside the window
|
|
if(project_y2 < viewport_top)
|
|
segments = 0;
|
|
|
|
// project new positions
|
|
if (segments > 0)
|
|
{
|
|
// interpolate between projected points
|
|
px_dx = ((project_x2 - project_x1) << 8) / segments;
|
|
}
|
|
|
|
// prepare pre-output
|
|
DSP4.out_count = 4 + 2 + 6 * segments;
|
|
|
|
DSP4_WRITE_WORD(0, project_x2);
|
|
DSP4_WRITE_WORD(2, project_y2);
|
|
DSP4_WRITE_WORD(4, segments);
|
|
|
|
index = 6;
|
|
for (lcv = 0; lcv < segments; lcv++)
|
|
{
|
|
// pre-compute
|
|
y_out = project_y + ((py_dy * lcv) >> 8);
|
|
x_out = project_x + ((px_dx * lcv) >> 8);
|
|
|
|
// data
|
|
DSP4_WRITE_WORD(index + 0, project_ptr);
|
|
DSP4_WRITE_WORD(index + 2, y_out);
|
|
DSP4_WRITE_WORD(index + 4, x_out);
|
|
index += 6;
|
|
|
|
// post-update
|
|
project_ptr -= 4;
|
|
}
|
|
|
|
// update internal variables
|
|
project_y += ((py_dy * lcv) >> 8);
|
|
project_x += ((px_dx * lcv) >> 8);
|
|
|
|
// new positions
|
|
if (segments > 0)
|
|
{
|
|
project_x1 = project_x2;
|
|
project_y1 = project_y2;
|
|
|
|
// multi-op storage
|
|
multi_index2++;
|
|
}
|
|
} while (1);
|
|
|
|
DSP4.waiting4command = true;
|
|
DSP4.out_count = 0;
|
|
}
|
|
|
|
void DSP4_Op08(void)
|
|
{
|
|
uint16_t command;
|
|
// used in envelope shaping
|
|
int16_t x1_final;
|
|
int16_t x2_final;
|
|
int16_t plane, x_left, y_left, x_right, y_right;
|
|
int16_t envelope1, envelope2;
|
|
DSP4.waiting4command = false;
|
|
|
|
// op flow control
|
|
switch (DSP4_Logic)
|
|
{
|
|
case 1:
|
|
goto resume1;
|
|
break;
|
|
case 2:
|
|
goto resume2;
|
|
break;
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// process initial inputs
|
|
|
|
// clip values
|
|
path_clipRight[0] = DSP4_READ_WORD(0x00);
|
|
path_clipRight[1] = DSP4_READ_WORD(0x02);
|
|
path_clipRight[2] = DSP4_READ_WORD(0x04);
|
|
path_clipRight[3] = DSP4_READ_WORD(0x06);
|
|
|
|
path_clipLeft[0] = DSP4_READ_WORD(0x08);
|
|
path_clipLeft[1] = DSP4_READ_WORD(0x0a);
|
|
path_clipLeft[2] = DSP4_READ_WORD(0x0c);
|
|
path_clipLeft[3] = DSP4_READ_WORD(0x0e);
|
|
|
|
// path positions
|
|
path_pos[0] = DSP4_READ_WORD(0x20);
|
|
path_pos[1] = DSP4_READ_WORD(0x22);
|
|
path_pos[2] = DSP4_READ_WORD(0x24);
|
|
path_pos[3] = DSP4_READ_WORD(0x26);
|
|
|
|
// data locations
|
|
path_ptr[0] = DSP4_READ_WORD(0x28);
|
|
path_ptr[1] = DSP4_READ_WORD(0x2a);
|
|
path_ptr[2] = DSP4_READ_WORD(0x2c);
|
|
path_ptr[3] = DSP4_READ_WORD(0x2e);
|
|
|
|
// project_y1 lines
|
|
path_raster[0] = DSP4_READ_WORD(0x30);
|
|
path_raster[1] = DSP4_READ_WORD(0x32);
|
|
path_raster[2] = DSP4_READ_WORD(0x34);
|
|
path_raster[3] = DSP4_READ_WORD(0x36);
|
|
|
|
// viewport_top
|
|
path_top[0] = DSP4_READ_WORD(0x38);
|
|
path_top[1] = DSP4_READ_WORD(0x3a);
|
|
path_top[2] = DSP4_READ_WORD(0x3c);
|
|
path_top[3] = DSP4_READ_WORD(0x3e);
|
|
|
|
// unknown (constants)
|
|
|
|
view_plane = PLANE_START;
|
|
|
|
////////////////////////////////////////////////////
|
|
// command check
|
|
|
|
do
|
|
{
|
|
// scan next command
|
|
DSP4.in_count = 2;
|
|
DSP4_WAIT(1);
|
|
|
|
resume1:
|
|
// inspect input
|
|
command = DSP4_READ_WORD(0);
|
|
|
|
// terminate op
|
|
if(command == 0x8000)
|
|
break;
|
|
|
|
// already have 2 bytes in queue
|
|
DSP4.in_index = 2;
|
|
DSP4.in_count = 18;
|
|
DSP4_WAIT(2);
|
|
|
|
resume2:
|
|
////////////////////////////////////////////////////
|
|
// projection begins
|
|
|
|
// look at guidelines
|
|
plane = DSP4_READ_WORD(0x00);
|
|
x_left = DSP4_READ_WORD(0x02);
|
|
y_left = DSP4_READ_WORD(0x04);
|
|
x_right = DSP4_READ_WORD(0x06);
|
|
y_right = DSP4_READ_WORD(0x08);
|
|
|
|
// envelope guidelines (one frame only)
|
|
envelope1 = DSP4_READ_WORD(0x0a);
|
|
envelope2 = DSP4_READ_WORD(0x0c);
|
|
|
|
// ignore invalid data
|
|
if((uint16_t) plane == 0x8001)
|
|
continue;
|
|
|
|
// first init
|
|
if (plane == 0x7fff)
|
|
{
|
|
int32_t pos1, pos2;
|
|
|
|
// initialize projection
|
|
path_x[0] = x_left;
|
|
path_x[1] = x_right;
|
|
|
|
path_y[0] = y_left;
|
|
path_y[1] = y_right;
|
|
|
|
// update coordinates
|
|
path_pos[0] -= x_left;
|
|
path_pos[1] -= x_left;
|
|
path_pos[2] -= x_right;
|
|
path_pos[3] -= x_right;
|
|
|
|
pos1 = path_pos[0] + envelope1;
|
|
pos2 = path_pos[1] + envelope2;
|
|
|
|
// clip offscreen data
|
|
if(pos1 < path_clipLeft[0])
|
|
pos1 = path_clipLeft[0];
|
|
if(pos1 > path_clipRight[0])
|
|
pos1 = path_clipRight[0];
|
|
if(pos2 < path_clipLeft[1])
|
|
pos2 = path_clipLeft[1];
|
|
if(pos2 > path_clipRight[1])
|
|
pos2 = path_clipRight[1];
|
|
|
|
path_plane[0] = plane;
|
|
path_plane[1] = plane;
|
|
|
|
// initial output
|
|
DSP4.out_count = 2;
|
|
DSP4.output[0] = pos1 & 0xFF;
|
|
DSP4.output[1] = pos2 & 0xFF;
|
|
}
|
|
// proceed with projection
|
|
else
|
|
{
|
|
int16_t index = 0, lcv;
|
|
int16_t left_inc = 0, right_inc = 0;
|
|
int16_t dx1 = 0, dx2 = 0, dx3, dx4;
|
|
|
|
// # segments to traverse
|
|
segments = ABS(y_left - path_y[0]);
|
|
|
|
// prevent overdraw
|
|
if(y_left >= path_raster[0])
|
|
segments = 0;
|
|
else
|
|
path_raster[0] = y_left;
|
|
|
|
// don't draw outside the window
|
|
if(path_raster[0] < path_top[0])
|
|
segments = 0;
|
|
|
|
// proceed if visibility rules apply
|
|
if (segments > 0)
|
|
{
|
|
// use previous data
|
|
dx1 = (envelope1 * path_plane[0] / view_plane);
|
|
dx2 = (envelope2 * path_plane[0] / view_plane);
|
|
|
|
// use temporary envelope pitch (this frame only)
|
|
dx3 = (envelope1 * plane / view_plane);
|
|
dx4 = (envelope2 * plane / view_plane);
|
|
|
|
// project new shapes (left side)
|
|
x1_final = x_left + dx1;
|
|
x2_final = path_x[0] + dx3;
|
|
|
|
// interpolate between projected points with shaping
|
|
left_inc = ((x2_final - x1_final) << 8) / segments;
|
|
|
|
// project new shapes (right side)
|
|
x1_final = x_left + dx2;
|
|
x2_final = path_x[0] + dx4;
|
|
|
|
// interpolate between projected points with shaping
|
|
right_inc = ((x2_final - x1_final) << 8) / segments;
|
|
path_plane[0] = plane;
|
|
}
|
|
|
|
// zone 1
|
|
DSP4.out_count = (2 + 4 * segments);
|
|
DSP4_WRITE_WORD(index, segments);
|
|
index += 2;
|
|
|
|
for (lcv = 1; lcv <= segments; lcv++)
|
|
{
|
|
int16_t pos1, pos2;
|
|
|
|
// pre-compute
|
|
pos1 = path_pos[0] + ((left_inc * lcv) >> 8) + dx1;
|
|
pos2 = path_pos[1] + ((right_inc * lcv) >> 8) + dx2;
|
|
|
|
// clip offscreen data
|
|
if(pos1 < path_clipLeft[0])
|
|
pos1 = path_clipLeft[0];
|
|
if(pos1 > path_clipRight[0])
|
|
pos1 = path_clipRight[0];
|
|
if(pos2 < path_clipLeft[1])
|
|
pos2 = path_clipLeft[1];
|
|
if(pos2 > path_clipRight[1])
|
|
pos2 = path_clipRight[1];
|
|
|
|
// data
|
|
DSP4_WRITE_WORD(index, path_ptr[0]);
|
|
index += 2;
|
|
DSP4.output[index++] = pos1 & 0xFF;
|
|
DSP4.output[index++] = pos2 & 0xFF;
|
|
|
|
// post-update
|
|
path_ptr[0] -= 4;
|
|
path_ptr[1] -= 4;
|
|
}
|
|
lcv--;
|
|
|
|
if (segments > 0)
|
|
{
|
|
// project points w/out the envelopes
|
|
int16_t inc = ((path_x[0] - x_left) << 8) / segments;
|
|
|
|
// post-store
|
|
path_pos[0] += ((inc * lcv) >> 8);
|
|
path_pos[1] += ((inc * lcv) >> 8);
|
|
|
|
path_x[0] = x_left;
|
|
path_y[0] = y_left;
|
|
}
|
|
|
|
//////////////////////////////////////////////
|
|
// zone 2
|
|
segments = ABS(y_right - path_y[1]);
|
|
|
|
// prevent overdraw
|
|
if(y_right >= path_raster[2])
|
|
segments = 0;
|
|
else path_raster[2] = y_right;
|
|
|
|
// don't draw outside the window
|
|
if(path_raster[2] < path_top[2])
|
|
segments = 0;
|
|
|
|
// proceed if visibility rules apply
|
|
if (segments > 0)
|
|
{
|
|
// use previous data
|
|
dx1 = (envelope1 * path_plane[1] / view_plane);
|
|
dx2 = (envelope2 * path_plane[1] / view_plane);
|
|
|
|
// use temporary envelope pitch (this frame only)
|
|
dx3 = (envelope1 * plane / view_plane);
|
|
dx4 = (envelope2 * plane / view_plane);
|
|
|
|
// project new shapes (left side)
|
|
x1_final = x_left + dx1;
|
|
x2_final = path_x[1] + dx3;
|
|
|
|
// interpolate between projected points with shaping
|
|
left_inc = ((x2_final - x1_final) << 8) / segments;
|
|
|
|
// project new shapes (right side)
|
|
x1_final = x_left + dx2;
|
|
x2_final = path_x[1] + dx4;
|
|
|
|
// interpolate between projected points with shaping
|
|
right_inc = ((x2_final - x1_final) << 8) / segments;
|
|
|
|
path_plane[1] = plane;
|
|
}
|
|
|
|
// write out results
|
|
DSP4.out_count += (2 + 4 * segments);
|
|
DSP4_WRITE_WORD(index, segments);
|
|
index += 2;
|
|
|
|
for (lcv = 1; lcv <= segments; lcv++)
|
|
{
|
|
int16_t pos1, pos2;
|
|
|
|
// pre-compute
|
|
pos1 = path_pos[2] + ((left_inc * lcv) >> 8) + dx1;
|
|
pos2 = path_pos[3] + ((right_inc * lcv) >> 8) + dx2;
|
|
|
|
// clip offscreen data
|
|
if(pos1 < path_clipLeft[2])
|
|
pos1 = path_clipLeft[2];
|
|
if(pos1 > path_clipRight[2])
|
|
pos1 = path_clipRight[2];
|
|
if(pos2 < path_clipLeft[3])
|
|
pos2 = path_clipLeft[3];
|
|
if(pos2 > path_clipRight[3])
|
|
pos2 = path_clipRight[3];
|
|
|
|
// data
|
|
DSP4_WRITE_WORD(index, path_ptr[2]);
|
|
index += 2;
|
|
DSP4.output[index++] = pos1 & 0xFF;
|
|
DSP4.output[index++] = pos2 & 0xFF;
|
|
|
|
// post-update
|
|
path_ptr[2] -= 4;
|
|
path_ptr[3] -= 4;
|
|
}
|
|
lcv--;
|
|
|
|
if (segments > 0)
|
|
{
|
|
// project points w/out the envelopes
|
|
int16_t inc = ((path_x[1] - x_right) << 8) / segments;
|
|
|
|
// post-store
|
|
path_pos[2] += ((inc * lcv) >> 8);
|
|
path_pos[3] += ((inc * lcv) >> 8);
|
|
|
|
path_x[1] = x_right;
|
|
path_y[1] = y_right;
|
|
}
|
|
}
|
|
} while (1);
|
|
|
|
DSP4.waiting4command = true;
|
|
DSP4.out_count = 2;
|
|
DSP4_WRITE_WORD(0, 0);
|
|
}
|
|
|
|
void DSP4_Op0D(void)
|
|
{
|
|
uint16_t command;
|
|
// inspect inputs
|
|
int16_t plane;
|
|
int16_t index, lcv;
|
|
int16_t py_dy, px_dx;
|
|
int16_t y_out, x_out;
|
|
|
|
DSP4.waiting4command = false;
|
|
|
|
// op flow control
|
|
switch (DSP4_Logic)
|
|
{
|
|
case 1:
|
|
goto resume1;
|
|
break;
|
|
case 2:
|
|
goto resume2;
|
|
break;
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// process initial inputs
|
|
|
|
// sort inputs
|
|
project_focaly = DSP4_READ_WORD(0x02);
|
|
raster = DSP4_READ_WORD(0x04);
|
|
viewport_top = DSP4_READ_WORD(0x06);
|
|
project_y = DSP4_READ_WORD(0x08);
|
|
viewport_bottom = DSP4_READ_WORD(0x0a);
|
|
project_x1low = DSP4_READ_WORD(0x0c);
|
|
project_x1 = DSP4_READ_WORD(0x0e);
|
|
project_focalx = DSP4_READ_WORD(0x0e);
|
|
project_centerx = DSP4_READ_WORD(0x10);
|
|
project_ptr = DSP4_READ_WORD(0x12);
|
|
project_pitchylow = DSP4_READ_WORD(0x16);
|
|
project_pitchy = DSP4_READ_WORD(0x18);
|
|
project_pitchxlow = DSP4_READ_WORD(0x1a);
|
|
project_pitchx = DSP4_READ_WORD(0x1c);
|
|
far_plane = DSP4_READ_WORD(0x1e);
|
|
|
|
// multi-op storage
|
|
multi_index1++;
|
|
multi_index1 %= 4;
|
|
|
|
// remap 0D->09 window data ahead of time
|
|
// index starts at 1-3,0
|
|
//
|
|
// Op0D: BL,TL,BR,TR
|
|
// Op09: TL,TR,BL,BR (1,2,3,0)
|
|
switch (multi_index1)
|
|
{
|
|
case 1:
|
|
multi_index2 = 3;
|
|
break;
|
|
case 2:
|
|
multi_index2 = 1;
|
|
break;
|
|
case 3:
|
|
multi_index2 = 0;
|
|
break;
|
|
case 0:
|
|
multi_index2 = 2;
|
|
break;
|
|
}
|
|
|
|
// pre-compute
|
|
view_plane = PLANE_START;
|
|
|
|
// figure out projection data
|
|
project_y -= viewport_bottom;
|
|
project_x = project_centerx + project_x1;
|
|
|
|
////////////////////////////////////////////////////
|
|
// command check
|
|
|
|
do
|
|
{
|
|
// scan next command
|
|
DSP4.in_count = 2;
|
|
DSP4_WAIT(1);
|
|
|
|
resume1:
|
|
// inspect input
|
|
command = DSP4_READ_WORD(0);
|
|
|
|
// terminate op
|
|
if(command == 0x8000)
|
|
break;
|
|
|
|
// already have 2 bytes in queue
|
|
DSP4.in_index = 2;
|
|
DSP4.in_count = 8;
|
|
DSP4_WAIT(2);
|
|
|
|
////////////////////////////////////////////////////
|
|
// project section of the track
|
|
|
|
resume2:
|
|
plane = DSP4_READ_WORD(0);
|
|
px_dx = 0;
|
|
|
|
|
|
// ignore invalid data
|
|
if((uint16_t) plane == 0x8001)
|
|
continue;
|
|
|
|
// one-time init
|
|
if (far_plane)
|
|
{
|
|
// setup final data
|
|
project_x1 = project_focalx;
|
|
project_y1 = project_focaly;
|
|
plane = far_plane;
|
|
far_plane = 0;
|
|
}
|
|
|
|
// use proportional triangles to project new coords
|
|
project_x2 = project_focalx * plane / view_plane;
|
|
project_y2 = project_focaly * plane / view_plane;
|
|
|
|
// quadratic regression (rough)
|
|
if (project_focaly >= -0x0f)
|
|
py_dy = (int16_t)(project_focaly * project_focaly * -0.20533553 - 1.08330005 * project_focaly - 69.61094639);
|
|
else
|
|
py_dy = (int16_t)(project_focaly * project_focaly * -0.000657035759 - 1.07629051 * project_focaly - 65.69315963);
|
|
|
|
// approximate # of raster lines
|
|
segments = ABS(project_y2 - project_y1);
|
|
|
|
// prevent overdraw
|
|
if(project_y2 >= raster)
|
|
segments = 0;
|
|
else
|
|
raster = project_y2;
|
|
|
|
// don't draw outside the window
|
|
if(project_y2 < viewport_top)
|
|
segments = 0;
|
|
|
|
// project new positions
|
|
if (segments > 0)
|
|
{
|
|
// interpolate between projected points
|
|
px_dx = ((project_x2 - project_x1) << 8) / segments;
|
|
}
|
|
|
|
// prepare output
|
|
DSP4.out_count = 8 + 2 + 6 * segments;
|
|
DSP4_WRITE_WORD(0, project_focalx);
|
|
DSP4_WRITE_WORD(2, project_x2);
|
|
DSP4_WRITE_WORD(4, project_focaly);
|
|
DSP4_WRITE_WORD(6, project_y2);
|
|
DSP4_WRITE_WORD(8, segments);
|
|
index = 10;
|
|
|
|
for (lcv = 0; lcv < segments; lcv++) // iterate through each point
|
|
{
|
|
// step through the projected line
|
|
y_out = project_y + ((py_dy * lcv) >> 8);
|
|
x_out = project_x + ((px_dx * lcv) >> 8);
|
|
|
|
// data
|
|
DSP4_WRITE_WORD(index + 0, project_ptr);
|
|
DSP4_WRITE_WORD(index + 2, y_out);
|
|
DSP4_WRITE_WORD(index + 4, x_out);
|
|
index += 6;
|
|
|
|
// post-update
|
|
project_ptr -= 4;
|
|
}
|
|
|
|
// post-update
|
|
project_y += ((py_dy * lcv) >> 8);
|
|
project_x += ((px_dx * lcv) >> 8);
|
|
|
|
if (segments > 0)
|
|
{
|
|
project_x1 = project_x2;
|
|
project_y1 = project_y2;
|
|
|
|
// multi-op storage
|
|
multi_farplane[multi_index2] = plane;
|
|
multi_raster[multi_index2] = project_y1;
|
|
}
|
|
|
|
// update focal projection points
|
|
project_pitchy += (int8_t)DSP4.parameters[3];
|
|
project_pitchx += (int8_t)DSP4.parameters[5];
|
|
|
|
project_focaly += project_pitchy;
|
|
project_focalx += project_pitchx;
|
|
} while (1);
|
|
|
|
DSP4.waiting4command = true;
|
|
DSP4.out_count = 0;
|
|
}
|
|
|
|
void DSP4_Op09(void)
|
|
{
|
|
uint16_t command;
|
|
bool clip;
|
|
int16_t sp_x, sp_y, sp_oam, sp_msb;
|
|
int16_t sp_dx, sp_dy;
|
|
|
|
DSP4.waiting4command = false;
|
|
|
|
// op flow control
|
|
switch (DSP4_Logic)
|
|
{
|
|
case 1:
|
|
goto resume1;
|
|
break;
|
|
case 2:
|
|
goto resume2;
|
|
break;
|
|
case 3:
|
|
goto resume3;
|
|
break;
|
|
case 4:
|
|
goto resume4;
|
|
break;
|
|
case 5:
|
|
goto resume5;
|
|
break;
|
|
case 6:
|
|
goto resume6;
|
|
break;
|
|
case 7:
|
|
goto resume7;
|
|
break;
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// process initial inputs
|
|
|
|
// grab screen information
|
|
view_plane = PLANE_START;
|
|
center_x = DSP4_READ_WORD(0x00);
|
|
center_y = DSP4_READ_WORD(0x02);
|
|
viewport_left = DSP4_READ_WORD(0x06);
|
|
viewport_right = DSP4_READ_WORD(0x08);
|
|
viewport_top = DSP4_READ_WORD(0x0a);
|
|
viewport_bottom = DSP4_READ_WORD(0x0c);
|
|
|
|
// expand viewport dimensions
|
|
viewport_left -= 8;
|
|
|
|
// cycle through viewport window data
|
|
multi_index1++;
|
|
multi_index1 %= 4;
|
|
|
|
// convert track line to the window region
|
|
project_y2 = center_y + multi_raster[multi_index1] * (viewport_bottom - center_y) / (0x33 - 0);
|
|
if (!op09_mode)
|
|
project_y2 -= 2;
|
|
|
|
goto no_sprite;
|
|
|
|
do
|
|
{
|
|
////////////////////////////////////////////////////
|
|
// check for new sprites
|
|
do
|
|
{
|
|
uint16_t second;
|
|
|
|
DSP4.in_count = 4;
|
|
DSP4.in_index = 2;
|
|
DSP4_WAIT(1);
|
|
|
|
resume1:
|
|
// try to classify sprite
|
|
second = DSP4_READ_WORD(2);
|
|
|
|
// op termination
|
|
if(second == 0x8000)
|
|
goto terminate;
|
|
|
|
second >>= 8;
|
|
sprite_type = 0;
|
|
|
|
// vehicle sprite
|
|
if (second == 0x90)
|
|
{
|
|
sprite_type = 1;
|
|
break;
|
|
}
|
|
// terrain sprite
|
|
else if (second != 0)
|
|
{
|
|
sprite_type = 2;
|
|
break;
|
|
}
|
|
|
|
no_sprite:
|
|
// no sprite. try again
|
|
DSP4.in_count = 2;
|
|
DSP4_WAIT(2);
|
|
|
|
resume2:;
|
|
} while (1);
|
|
|
|
////////////////////////////////////////////////////
|
|
// process projection information
|
|
|
|
sprite_found:
|
|
// vehicle sprite
|
|
if (sprite_type == 1)
|
|
{
|
|
int16_t plane;
|
|
int16_t car_left, car_right;
|
|
int16_t focal_back;
|
|
int32_t height;
|
|
|
|
// we already have 4 bytes we want
|
|
DSP4.in_count = 6 + 12;
|
|
DSP4.in_index = 4;
|
|
DSP4_WAIT(3);
|
|
|
|
resume3:
|
|
// filter inputs
|
|
project_y1 = DSP4_READ_WORD(0x00);
|
|
focal_back = DSP4_READ_WORD(0x06);
|
|
car_left = DSP4_READ_WORD(0x0c);
|
|
plane = DSP4_READ_WORD(0x0e);
|
|
car_right = DSP4_READ_WORD(0x10);
|
|
|
|
// calculate car's x-center
|
|
project_focalx = car_right - car_left;
|
|
|
|
// determine how far into the screen to project
|
|
project_focaly = focal_back;
|
|
project_x = project_focalx * plane / view_plane;
|
|
segments = 0x33 - project_focaly * plane / view_plane;
|
|
far_plane = plane;
|
|
|
|
// prepare memory
|
|
sprite_x = center_x + project_x;
|
|
sprite_y = viewport_bottom - segments;
|
|
far_plane = plane;
|
|
|
|
// make the car's x-center available
|
|
DSP4.out_count = 2;
|
|
DSP4_WRITE_WORD(0, project_focalx);
|
|
|
|
// grab a few remaining vehicle values
|
|
DSP4.in_count = 4;
|
|
|
|
DSP4_WAIT(4);
|
|
|
|
resume4: // store final values
|
|
height = DSP4_READ_WORD(0);
|
|
sprite_offset = DSP4_READ_WORD(2);
|
|
|
|
// vertical lift factor
|
|
sprite_y += height;
|
|
}
|
|
else if (sprite_type == 2) // terrain sprite
|
|
{
|
|
int16_t plane;
|
|
|
|
// we already have 4 bytes we want
|
|
DSP4.in_count = 6 + 6 + 2;
|
|
DSP4.in_index = 4;
|
|
DSP4_WAIT(5);
|
|
|
|
resume5:
|
|
// sort loop inputs
|
|
project_y1 = DSP4_READ_WORD(0x00);
|
|
plane = DSP4_READ_WORD(0x02);
|
|
project_centerx = DSP4_READ_WORD(0x04);
|
|
project_focalx = DSP4_READ_WORD(0x08);
|
|
project_focaly = DSP4_READ_WORD(0x0a);
|
|
sprite_offset = DSP4_READ_WORD(0x0c);
|
|
|
|
// determine distances into virtual world
|
|
segments = 0x33 - project_y1;
|
|
project_x = project_focalx * plane / view_plane;
|
|
project_y = project_focaly * plane / view_plane;
|
|
|
|
// prepare memory
|
|
sprite_x = center_x + project_x - project_centerx;
|
|
sprite_y = viewport_bottom - segments + project_y;
|
|
far_plane = plane;
|
|
}
|
|
|
|
// default sprite size: 16x16
|
|
sprite_size = true;
|
|
|
|
// convert tile data to OAM
|
|
|
|
do
|
|
{
|
|
DSP4.in_count = 2;
|
|
DSP4_WAIT(6);
|
|
|
|
resume6:
|
|
command = DSP4_READ_WORD(0);
|
|
|
|
// opcode termination
|
|
if(command == 0x8000)
|
|
goto terminate;
|
|
|
|
// toggle sprite size
|
|
if (command == 0x0000)
|
|
{
|
|
sprite_size = !sprite_size;
|
|
continue;
|
|
}
|
|
|
|
// new sprite information
|
|
command >>= 8;
|
|
if (command != 0x20 && command != 0x40 && command != 0x60 && command != 0xa0 && command != 0xc0 && command != 0xe0)
|
|
break;
|
|
|
|
DSP4.in_count = 6;
|
|
DSP4.in_index = 2;
|
|
DSP4_WAIT(7);
|
|
|
|
/////////////////////////////////////
|
|
// process tile data
|
|
|
|
resume7:
|
|
// sprite deltas
|
|
sp_dy = DSP4_READ_WORD(2);
|
|
sp_dx = DSP4_READ_WORD(4);
|
|
|
|
// update coordinates
|
|
sp_y = sprite_y + sp_dy;
|
|
sp_x = sprite_x + sp_dx;
|
|
|
|
// reject points outside the clipping window
|
|
clip = false;
|
|
if(sp_x < viewport_left || sp_x > viewport_right)
|
|
clip = true;
|
|
if(sp_y < viewport_top || sp_y > viewport_bottom)
|
|
clip = true;
|
|
|
|
// track depth sorting
|
|
if(far_plane <= multi_farplane[multi_index1] && sp_y >= project_y2)
|
|
clip = true;
|
|
|
|
// don't draw offscreen coordinates
|
|
DSP4.out_count = 0;
|
|
if (!clip)
|
|
{
|
|
int16_t out_index = 0;
|
|
int16_t offset = DSP4_READ_WORD(0);
|
|
|
|
// update sprite nametable/attribute information
|
|
sp_oam = sprite_offset + offset;
|
|
sp_msb = (sp_x < 0 || sp_x > 255);
|
|
|
|
// emit transparency information
|
|
if((sprite_offset & 0x08) && ((sprite_type == 1 && sp_y >= 0xcc) || (sprite_type == 2 && sp_y >= 0xbb)))
|
|
{
|
|
DSP4.out_count = 6;
|
|
|
|
// one block of OAM data
|
|
DSP4_WRITE_WORD(0, 1);
|
|
|
|
// OAM: x,y,tile,no attr
|
|
DSP4.output[2] = sp_x & 0xFF;
|
|
DSP4.output[3] = (sp_y + 6) & 0xFF;
|
|
DSP4_WRITE_WORD(4, 0xEE);
|
|
out_index = 6;
|
|
|
|
// OAM: size,msb data
|
|
DSP4_Op06(sprite_size, (int8_t) sp_msb);
|
|
}
|
|
|
|
// normal data
|
|
DSP4.out_count += 8;
|
|
|
|
// one block of OAM data
|
|
DSP4_WRITE_WORD(out_index + 0, 1);
|
|
|
|
// OAM: x,y,tile,attr
|
|
DSP4.output[out_index + 2] = sp_x & 0xFF;
|
|
DSP4.output[out_index + 3] = sp_y & 0xFF;
|
|
DSP4_WRITE_WORD(out_index + 4, sp_oam);
|
|
|
|
// no following OAM data
|
|
DSP4_WRITE_WORD(out_index + 6, 0);
|
|
|
|
// OAM: size,msb data
|
|
DSP4_Op06(sprite_size, (int8_t) sp_msb);
|
|
}
|
|
|
|
// no sprite information
|
|
if (DSP4.out_count == 0)
|
|
{
|
|
DSP4.out_count = 2;
|
|
DSP4_WRITE_WORD(0, 0);
|
|
}
|
|
} while (1);
|
|
|
|
/////////////////////////////////////
|
|
// special cases: plane == 0x0000
|
|
|
|
// special vehicle case
|
|
if (command == 0x90)
|
|
{
|
|
sprite_type = 1;
|
|
|
|
// shift bytes
|
|
DSP4.parameters[2] = DSP4.parameters[0];
|
|
DSP4.parameters[3] = DSP4.parameters[1];
|
|
DSP4.parameters[0] = 0;
|
|
DSP4.parameters[1] = 0;
|
|
|
|
goto sprite_found;
|
|
}
|
|
else if (command != 0x00 && command != 0xff) // special terrain case
|
|
{
|
|
sprite_type = 2;
|
|
|
|
// shift bytes
|
|
DSP4.parameters[2] = DSP4.parameters[0];
|
|
DSP4.parameters[3] = DSP4.parameters[1];
|
|
DSP4.parameters[0] = 0;
|
|
DSP4.parameters[1] = 0;
|
|
|
|
goto sprite_found;
|
|
}
|
|
} while (1);
|
|
|
|
terminate:
|
|
DSP4.waiting4command = true;
|
|
DSP4.out_count = 0;
|
|
}
|