#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; }