Path: blob/main/misc/emulator/xnes/snes9x/dsp4.cpp
28515 views
/***********************************************************************************1Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.23(c) Copyright 1996 - 2002 Gary Henderson ([email protected]),4Jerremy Koot ([email protected])56(c) Copyright 2002 - 2004 Matthew Kendora78(c) Copyright 2002 - 2005 Peter Bortas ([email protected])910(c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)1112(c) Copyright 2001 - 2006 John Weidman ([email protected])1314(c) Copyright 2002 - 2006 funkyass ([email protected]),15Kris Bleakley ([email protected])1617(c) Copyright 2002 - 2010 Brad Jorsch ([email protected]),18Nach ([email protected]),1920(c) Copyright 2002 - 2011 zones ([email protected])2122(c) Copyright 2006 - 2007 nitsuja2324(c) Copyright 2009 - 2011 BearOso,25OV2262728BS-X C emulator code29(c) Copyright 2005 - 2006 Dreamer Nom,30zones3132C4 x86 assembler and some C emulation code33(c) Copyright 2000 - 2003 _Demo_ ([email protected]),34Nach,35zsKnight ([email protected])3637C4 C++ code38(c) Copyright 2003 - 2006 Brad Jorsch,39Nach4041DSP-1 emulator code42(c) Copyright 1998 - 2006 _Demo_,43Andreas Naive ([email protected]),44Gary Henderson,45Ivar ([email protected]),46John Weidman,47Kris Bleakley,48Matthew Kendora,49Nach,50neviksti ([email protected])5152DSP-2 emulator code53(c) Copyright 2003 John Weidman,54Kris Bleakley,55Lord Nightmare ([email protected]),56Matthew Kendora,57neviksti5859DSP-3 emulator code60(c) Copyright 2003 - 2006 John Weidman,61Kris Bleakley,62Lancer,63z80 gaiden6465DSP-4 emulator code66(c) Copyright 2004 - 2006 Dreamer Nom,67John Weidman,68Kris Bleakley,69Nach,70z80 gaiden7172OBC1 emulator code73(c) Copyright 2001 - 2004 zsKnight,74pagefault ([email protected]),75Kris Bleakley76Ported from x86 assembler to C by sanmaiwashi7778SPC7110 and RTC C++ emulator code used in 1.39-1.5179(c) Copyright 2002 Matthew Kendora with research by80zsKnight,81John Weidman,82Dark Force8384SPC7110 and RTC C++ emulator code used in 1.52+85(c) Copyright 2009 byuu,86neviksti8788S-DD1 C emulator code89(c) Copyright 2003 Brad Jorsch with research by90Andreas Naive,91John Weidman9293S-RTC C emulator code94(c) Copyright 2001 - 2006 byuu,95John Weidman9697ST010 C++ emulator code98(c) Copyright 2003 Feather,99John Weidman,100Kris Bleakley,101Matthew Kendora102103Super FX x86 assembler emulator code104(c) Copyright 1998 - 2003 _Demo_,105pagefault,106zsKnight107108Super FX C emulator code109(c) Copyright 1997 - 1999 Ivar,110Gary Henderson,111John Weidman112113Sound emulator code used in 1.5-1.51114(c) Copyright 1998 - 2003 Brad Martin115(c) Copyright 1998 - 2006 Charles Bilyue'116117Sound emulator code used in 1.52+118(c) Copyright 2004 - 2007 Shay Green ([email protected])119120SH assembler code partly based on x86 assembler code121(c) Copyright 2002 - 2004 Marcus Comstedt ([email protected])1221232xSaI filter124(c) Copyright 1999 - 2001 Derek Liauw Kie Fa125126HQ2x, HQ3x, HQ4x filters127(c) Copyright 2003 Maxim Stepin ([email protected])128129NTSC filter130(c) Copyright 2006 - 2007 Shay Green131132GTK+ GUI code133(c) Copyright 2004 - 2011 BearOso134135Win32 GUI code136(c) Copyright 2003 - 2006 blip,137funkyass,138Matthew Kendora,139Nach,140nitsuja141(c) Copyright 2009 - 2011 OV2142143Mac OS GUI code144(c) Copyright 1998 - 2001 John Stiles145(c) Copyright 2001 - 2011 zones146147148Specific ports contains the works of other authors. See headers in149individual files.150151152Snes9x homepage: http://www.snes9x.com/153154Permission to use, copy, modify and/or distribute Snes9x in both binary155and source form, for non-commercial purposes, is hereby granted without156fee, providing that this license information and copyright notice appear157with all copies and any derived work.158159This software is provided 'as-is', without any express or implied160warranty. In no event shall the authors be held liable for any damages161arising from the use of this software or it's derivatives.162163Snes9x is freeware for PERSONAL USE only. Commercial users should164seek permission of the copyright holders first. Commercial use includes,165but is not limited to, charging money for Snes9x or software derived from166Snes9x, including Snes9x or derivatives in commercial game bundles, and/or167using Snes9x as a promotion for your commercial product.168169The copyright holders request that bug fixes and improvements to the code170should be forwarded to them so everyone can benefit from the modifications171in future versions.172173Super NES and Super Nintendo Entertainment System are trademarks of174Nintendo Co., Limited and its subsidiary companies.175***********************************************************************************/176177/*178Due recognition and credit are given on Overload's DSP website.179Thank those contributors for their hard work on this chip.180181Fixed-point math reminder:182[sign, integer, fraction]1831.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0')1841.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0')185*/186187188#include "snes9x.h"189#include "memmap.h"190191#define DSP4_CLEAR_OUT() \192{ DSP4.out_count = 0; DSP4.out_index = 0; }193194#define DSP4_WRITE_BYTE(d) \195{ WRITE_WORD(DSP4.output + DSP4.out_count, (d)); DSP4.out_count++; }196197#define DSP4_WRITE_WORD(d) \198{ WRITE_WORD(DSP4.output + DSP4.out_count, (d)); DSP4.out_count += 2; }199200#ifndef MSB_FIRST201#define DSP4_WRITE_16_WORD(d) \202{ memcpy(DSP4.output + DSP4.out_count, (d), 32); DSP4.out_count += 32; }203#else204#define DSP4_WRITE_16_WORD(d) \205{ for (int p = 0; p < 16; p++) DSP4_WRITE_WORD((d)[p]); }206#endif207208// used to wait for dsp i/o209#define DSP4_WAIT(x) \210DSP4.in_index = 0; DSP4.Logic = (x); return211212// 1.7.8 -> 1.15.16213#define SEX78(a) (((int32) ((int16) (a))) << 8)214215// 1.15.0 -> 1.15.16216#define SEX16(a) (((int32) ((int16) (a))) << 16)217218static int16 DSP4_READ_WORD (void);219static int32 DSP4_READ_DWORD (void);220static int16 DSP4_Inverse (int16);221static void DSP4_Multiply (int16, int16, int32 *);222static void DSP4_OP01 (void);223static void DSP4_OP03 (void);224static void DSP4_OP05 (void);225static void DSP4_OP06 (void);226static void DSP4_OP07 (void);227static void DSP4_OP08 (void);228static void DSP4_OP09 (void);229static void DSP4_OP0A (int16, int16 *, int16 *, int16 *, int16 *);230static void DSP4_OP0B (bool8 *, int16, int16, int16, bool8, bool8);231static void DSP4_OP0D (void);232static void DSP4_OP0E (void);233static void DSP4_OP0F (void);234static void DSP4_OP10 (void);235static void DSP4_OP11 (int16, int16, int16, int16, int16 *);236static void DSP4_SetByte (void);237static void DSP4_GetByte (void);238239240static int16 DSP4_READ_WORD (void)241{242int16 out;243244out = READ_WORD(DSP4.parameters + DSP4.in_index);245DSP4.in_index += 2;246247return (out);248}249250static int32 DSP4_READ_DWORD (void)251{252int32 out;253254out = READ_DWORD(DSP4.parameters + DSP4.in_index);255DSP4.in_index += 4;256257return (out);258}259260static int16 DSP4_Inverse (int16 value)261{262// Attention: This lookup table is not verified263const uint16 div_lut[64] =264{2650x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249,2660x1000, 0x0e38, 0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888,2670x0800, 0x0787, 0x071c, 0x06bc, 0x0666, 0x0618, 0x05d1, 0x0590,2680x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469, 0x0444, 0x0421,2690x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348,2700x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9,2710x02aa, 0x029c, 0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253,2720x0249, 0x023e, 0x0234, 0x022b, 0x0222, 0x0219, 0x0210, 0x0208273};274275// saturate bounds276if (value < 0)277value = 0;278if (value > 63)279value = 63;280281return (div_lut[value]);282}283284static void DSP4_Multiply (int16 Multiplicand, int16 Multiplier, int32 *Product)285{286*Product = (Multiplicand * Multiplier << 1) >> 1;287}288289static void DSP4_OP01 (void)290{291DSP4.waiting4command = FALSE;292293// op flow control294switch (DSP4.Logic)295{296case 1: goto resume1; break;297case 2: goto resume2; break;298case 3: goto resume3; break;299}300301////////////////////////////////////////////////////302// process initial inputs303304// sort inputs305DSP4.world_y = DSP4_READ_DWORD();306DSP4.poly_bottom[0][0] = DSP4_READ_WORD();307DSP4.poly_top[0][0] = DSP4_READ_WORD();308DSP4.poly_cx[1][0] = DSP4_READ_WORD();309DSP4.viewport_bottom = DSP4_READ_WORD();310DSP4.world_x = DSP4_READ_DWORD();311DSP4.poly_cx[0][0] = DSP4_READ_WORD();312DSP4.poly_ptr[0][0] = DSP4_READ_WORD();313DSP4.world_yofs = DSP4_READ_WORD();314DSP4.world_dy = DSP4_READ_DWORD();315DSP4.world_dx = DSP4_READ_DWORD();316DSP4.distance = DSP4_READ_WORD();317DSP4_READ_WORD(); // 0x0000318DSP4.world_xenv = DSP4_READ_DWORD();319DSP4.world_ddy = DSP4_READ_WORD();320DSP4.world_ddx = DSP4_READ_WORD();321DSP4.view_yofsenv = DSP4_READ_WORD();322323// initial (x, y, offset) at starting raster line324DSP4.view_x1 = (DSP4.world_x + DSP4.world_xenv) >> 16;325DSP4.view_y1 = DSP4.world_y >> 16;326DSP4.view_xofs1 = DSP4.world_x >> 16;327DSP4.view_yofs1 = DSP4.world_yofs;328DSP4.view_turnoff_x = 0;329DSP4.view_turnoff_dx = 0;330331// first raster line332DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];333334do335{336////////////////////////////////////////////////////337// process one iteration of projection338339// perspective projection of world (x, y, scroll) points340// based on the current projection lines341DSP4.view_x2 = (((DSP4.world_x + DSP4.world_xenv) >> 16) * DSP4.distance >> 15) + (DSP4.view_turnoff_x * DSP4.distance >> 15);342DSP4.view_y2 = (DSP4.world_y >> 16) * DSP4.distance >> 15;343DSP4.view_xofs2 = DSP4.view_x2;344DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;345346// 1. World x-location before transformation347// 2. Viewer x-position at the next348// 3. World y-location before perspective projection349// 4. Viewer y-position below the horizon350// 5. Number of raster lines drawn in this iteration351DSP4_CLEAR_OUT();352DSP4_WRITE_WORD((DSP4.world_x + DSP4.world_xenv) >> 16);353DSP4_WRITE_WORD(DSP4.view_x2);354DSP4_WRITE_WORD(DSP4.world_y >> 16);355DSP4_WRITE_WORD(DSP4.view_y2);356357//////////////////////////////////////////////////////358359// SR = 0x00360361// determine # of raster lines used362DSP4.segments = DSP4.poly_raster[0][0] - DSP4.view_y2;363364// prevent overdraw365if (DSP4.view_y2 >= DSP4.poly_raster[0][0])366DSP4.segments = 0;367else368DSP4.poly_raster[0][0] = DSP4.view_y2;369370// don't draw outside the window371if (DSP4.view_y2 < DSP4.poly_top[0][0])372{373DSP4.segments = 0;374375// flush remaining raster lines376if (DSP4.view_y1 >= DSP4.poly_top[0][0])377DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];378}379380// SR = 0x80381382DSP4_WRITE_WORD(DSP4.segments);383384//////////////////////////////////////////////////////385386// scan next command if no SR check needed387if (DSP4.segments)388{389int32 px_dx, py_dy;390int32 x_scroll, y_scroll;391392// SR = 0x00393394// linear interpolation (lerp) between projected points395px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;396py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;397398// starting step values399x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);400y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);401402// SR = 0x80403404// rasterize line405for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)406{407// 1. HDMA memory pointer (bg1)408// 2. vertical scroll offset ($210E)409// 3. horizontal scroll offset ($210D)410DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);411DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);412DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);413414// update memory address415DSP4.poly_ptr[0][0] -= 4;416417// update screen values418x_scroll += px_dx;419y_scroll += py_dy;420}421}422423////////////////////////////////////////////////////424// Post-update425426// update new viewer (x, y, scroll) to last raster line drawn427DSP4.view_x1 = DSP4.view_x2;428DSP4.view_y1 = DSP4.view_y2;429DSP4.view_xofs1 = DSP4.view_xofs2;430DSP4.view_yofs1 = DSP4.view_yofs2;431432// add deltas for projection lines433DSP4.world_dx += SEX78(DSP4.world_ddx);434DSP4.world_dy += SEX78(DSP4.world_ddy);435436// update projection lines437DSP4.world_x += (DSP4.world_dx + DSP4.world_xenv);438DSP4.world_y += DSP4.world_dy;439440// update road turnoff position441DSP4.view_turnoff_x += DSP4.view_turnoff_dx;442443////////////////////////////////////////////////////444// command check445446// scan next command447DSP4.in_count = 2;448DSP4_WAIT(1);449450resume1:451452// check for termination453DSP4.distance = DSP4_READ_WORD();454if (DSP4.distance == -0x8000)455break;456457// road turnoff458if ((uint16) DSP4.distance == 0x8001)459{460DSP4.in_count = 6;461DSP4_WAIT(2);462463resume2:464465DSP4.distance = DSP4_READ_WORD();466DSP4.view_turnoff_x = DSP4_READ_WORD();467DSP4.view_turnoff_dx = DSP4_READ_WORD();468469// factor in new changes470DSP4.view_x1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);471DSP4.view_xofs1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);472473// update stepping values474DSP4.view_turnoff_x += DSP4.view_turnoff_dx;475476DSP4.in_count = 2;477DSP4_WAIT(1);478}479480// already have 2 bytes read481DSP4.in_count = 6;482DSP4_WAIT(3);483484resume3:485486// inspect inputs487DSP4.world_ddy = DSP4_READ_WORD();488DSP4.world_ddx = DSP4_READ_WORD();489DSP4.view_yofsenv = DSP4_READ_WORD();490491// no envelope here492DSP4.world_xenv = 0;493}494while (1);495496// terminate op497DSP4.waiting4command = TRUE;498}499500static void DSP4_OP03 (void)501{502DSP4.OAM_RowMax = 33;503memset(DSP4.OAM_Row, 0, 64);504}505506static void DSP4_OP05 (void)507{508DSP4.OAM_index = 0;509DSP4.OAM_bits = 0;510memset(DSP4.OAM_attr, 0, 32);511DSP4.sprite_count = 0;512}513514static void DSP4_OP06 (void)515{516DSP4_CLEAR_OUT();517DSP4_WRITE_16_WORD(DSP4.OAM_attr);518}519520static void DSP4_OP07 (void)521{522DSP4.waiting4command = FALSE;523524// op flow control525switch (DSP4.Logic)526{527case 1: goto resume1; break;528case 2: goto resume2; break;529}530531////////////////////////////////////////////////////532// sort inputs533534DSP4.world_y = DSP4_READ_DWORD();535DSP4.poly_bottom[0][0] = DSP4_READ_WORD();536DSP4.poly_top[0][0] = DSP4_READ_WORD();537DSP4.poly_cx[1][0] = DSP4_READ_WORD();538DSP4.viewport_bottom = DSP4_READ_WORD();539DSP4.world_x = DSP4_READ_DWORD();540DSP4.poly_cx[0][0] = DSP4_READ_WORD();541DSP4.poly_ptr[0][0] = DSP4_READ_WORD();542DSP4.world_yofs = DSP4_READ_WORD();543DSP4.distance = DSP4_READ_WORD();544DSP4.view_y2 = DSP4_READ_WORD();545DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;546DSP4.view_x2 = DSP4_READ_WORD();547DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;548DSP4.view_yofsenv = DSP4_READ_WORD();549550// initial (x, y, offset) at starting raster line551DSP4.view_x1 = DSP4.world_x >> 16;552DSP4.view_y1 = DSP4.world_y >> 16;553DSP4.view_xofs1 = DSP4.view_x1;554DSP4.view_yofs1 = DSP4.world_yofs;555556// first raster line557DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];558559do560{561////////////////////////////////////////////////////562// process one iteration of projection563564// add shaping565DSP4.view_x2 += DSP4.view_dx;566DSP4.view_y2 += DSP4.view_dy;567568// vertical scroll calculation569DSP4.view_xofs2 = DSP4.view_x2;570DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;571572// 1. Viewer x-position at the next573// 2. Viewer y-position below the horizon574// 3. Number of raster lines drawn in this iteration575DSP4_CLEAR_OUT();576DSP4_WRITE_WORD(DSP4.view_x2);577DSP4_WRITE_WORD(DSP4.view_y2);578579//////////////////////////////////////////////////////580581// SR = 0x00582583// determine # of raster lines used584DSP4.segments = DSP4.view_y1 - DSP4.view_y2;585586// prevent overdraw587if (DSP4.view_y2 >= DSP4.poly_raster[0][0])588DSP4.segments = 0;589else590DSP4.poly_raster[0][0] = DSP4.view_y2;591592// don't draw outside the window593if (DSP4.view_y2 < DSP4.poly_top[0][0])594{595DSP4.segments = 0;596597// flush remaining raster lines598if (DSP4.view_y1 >= DSP4.poly_top[0][0])599DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];600}601602// SR = 0x80603604DSP4_WRITE_WORD(DSP4.segments);605606//////////////////////////////////////////////////////607608// scan next command if no SR check needed609if (DSP4.segments)610{611int32 px_dx, py_dy;612int32 x_scroll, y_scroll;613614// SR = 0x00615616// linear interpolation (lerp) between projected points617px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;618py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;619620// starting step values621x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);622y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);623624// SR = 0x80625626// rasterize line627for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)628{629// 1. HDMA memory pointer (bg2)630// 2. vertical scroll offset ($2110)631// 3. horizontal scroll offset ($210F)632DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);633DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);634DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);635636// update memory address637DSP4.poly_ptr[0][0] -= 4;638639// update screen values640x_scroll += px_dx;641y_scroll += py_dy;642}643}644645/////////////////////////////////////////////////////646// Post-update647648// update new viewer (x, y, scroll) to last raster line drawn649DSP4.view_x1 = DSP4.view_x2;650DSP4.view_y1 = DSP4.view_y2;651DSP4.view_xofs1 = DSP4.view_xofs2;652DSP4.view_yofs1 = DSP4.view_yofs2;653654////////////////////////////////////////////////////655// command check656657// scan next command658DSP4.in_count = 2;659DSP4_WAIT(1);660661resume1:662663// check for opcode termination664DSP4.distance = DSP4_READ_WORD();665if (DSP4.distance == -0x8000)666break;667668// already have 2 bytes in queue669DSP4.in_count = 10;670DSP4_WAIT(2);671672resume2:673674// inspect inputs675DSP4.view_y2 = DSP4_READ_WORD();676DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;677DSP4.view_x2 = DSP4_READ_WORD();678DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;679DSP4.view_yofsenv = DSP4_READ_WORD();680}681while (1);682683DSP4.waiting4command = TRUE;684}685686static void DSP4_OP08 (void)687{688int16 win_left, win_right;689int16 view_x[2], view_y[2];690int16 envelope[2][2];691692DSP4.waiting4command = FALSE;693694// op flow control695switch (DSP4.Logic)696{697case 1: goto resume1; break;698case 2: goto resume2; break;699}700701////////////////////////////////////////////////////702// process initial inputs for two polygons703704// clip values705DSP4.poly_clipRt[0][0] = DSP4_READ_WORD();706DSP4.poly_clipRt[0][1] = DSP4_READ_WORD();707DSP4.poly_clipRt[1][0] = DSP4_READ_WORD();708DSP4.poly_clipRt[1][1] = DSP4_READ_WORD();709710DSP4.poly_clipLf[0][0] = DSP4_READ_WORD();711DSP4.poly_clipLf[0][1] = DSP4_READ_WORD();712DSP4.poly_clipLf[1][0] = DSP4_READ_WORD();713DSP4.poly_clipLf[1][1] = DSP4_READ_WORD();714715// unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6)716DSP4_READ_WORD();717DSP4_READ_WORD();718DSP4_READ_WORD();719DSP4_READ_WORD();720721// unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7)722DSP4_READ_WORD();723DSP4_READ_WORD();724DSP4_READ_WORD();725DSP4_READ_WORD();726727// polygon centering (left, right)728DSP4.poly_cx[0][0] = DSP4_READ_WORD();729DSP4.poly_cx[0][1] = DSP4_READ_WORD();730DSP4.poly_cx[1][0] = DSP4_READ_WORD();731DSP4.poly_cx[1][1] = DSP4_READ_WORD();732733// HDMA pointer locations734DSP4.poly_ptr[0][0] = DSP4_READ_WORD();735DSP4.poly_ptr[0][1] = DSP4_READ_WORD();736DSP4.poly_ptr[1][0] = DSP4_READ_WORD();737DSP4.poly_ptr[1][1] = DSP4_READ_WORD();738739// starting raster line below the horizon740DSP4.poly_bottom[0][0] = DSP4_READ_WORD();741DSP4.poly_bottom[0][1] = DSP4_READ_WORD();742DSP4.poly_bottom[1][0] = DSP4_READ_WORD();743DSP4.poly_bottom[1][1] = DSP4_READ_WORD();744745// top boundary line to clip746DSP4.poly_top[0][0] = DSP4_READ_WORD();747DSP4.poly_top[0][1] = DSP4_READ_WORD();748DSP4.poly_top[1][0] = DSP4_READ_WORD();749DSP4.poly_top[1][1] = DSP4_READ_WORD();750751// unknown752// (ex. 1P = $2FC8, $0034, $FF5C, $0035)753//754// (ex. 2P = $3178, $0034, $FFCC, $0035)755// (ex. 2P = $2FC8, $0034, $FFCC, $0035)756DSP4_READ_WORD();757DSP4_READ_WORD();758DSP4_READ_WORD();759DSP4_READ_WORD();760761// look at guidelines for both polygon shapes762DSP4.distance = DSP4_READ_WORD();763view_x[0] = DSP4_READ_WORD();764view_y[0] = DSP4_READ_WORD();765view_x[1] = DSP4_READ_WORD();766view_y[1] = DSP4_READ_WORD();767768// envelope shaping guidelines (one frame only)769envelope[0][0] = DSP4_READ_WORD();770envelope[0][1] = DSP4_READ_WORD();771envelope[1][0] = DSP4_READ_WORD();772envelope[1][1] = DSP4_READ_WORD();773774// starting base values to project from775DSP4.poly_start[0] = view_x[0];776DSP4.poly_start[1] = view_x[1];777778// starting raster lines to begin drawing779DSP4.poly_raster[0][0] = view_y[0];780DSP4.poly_raster[0][1] = view_y[0];781DSP4.poly_raster[1][0] = view_y[1];782DSP4.poly_raster[1][1] = view_y[1];783784// starting distances785DSP4.poly_plane[0] = DSP4.distance;786DSP4.poly_plane[1] = DSP4.distance;787788// SR = 0x00789790// re-center coordinates791win_left = DSP4.poly_cx[0][0] - view_x[0] + envelope[0][0];792win_right = DSP4.poly_cx[0][1] - view_x[0] + envelope[0][1];793794// saturate offscreen data for polygon #1795if (win_left < DSP4.poly_clipLf[0][0])796win_left = DSP4.poly_clipLf[0][0];797if (win_left > DSP4.poly_clipRt[0][0])798win_left = DSP4.poly_clipRt[0][0];799if (win_right < DSP4.poly_clipLf[0][1])800win_right = DSP4.poly_clipLf[0][1];801if (win_right > DSP4.poly_clipRt[0][1])802win_right = DSP4.poly_clipRt[0][1];803804// SR = 0x80805806// initial output for polygon #1807DSP4_CLEAR_OUT();808DSP4_WRITE_BYTE(win_left & 0xff);809DSP4_WRITE_BYTE(win_right & 0xff);810811do812{813int16 polygon;814815////////////////////////////////////////////////////816// command check817818// scan next command819DSP4.in_count = 2;820DSP4_WAIT(1);821822resume1:823824// terminate op825DSP4.distance = DSP4_READ_WORD();826if (DSP4.distance == -0x8000)827break;828829// already have 2 bytes in queue830DSP4.in_count = 16;831DSP4_WAIT(2);832833resume2:834835// look at guidelines for both polygon shapes836view_x[0] = DSP4_READ_WORD();837view_y[0] = DSP4_READ_WORD();838view_x[1] = DSP4_READ_WORD();839view_y[1] = DSP4_READ_WORD();840841// envelope shaping guidelines (one frame only)842envelope[0][0] = DSP4_READ_WORD();843envelope[0][1] = DSP4_READ_WORD();844envelope[1][0] = DSP4_READ_WORD();845envelope[1][1] = DSP4_READ_WORD();846847////////////////////////////////////////////////////848// projection begins849850// init851DSP4_CLEAR_OUT();852853//////////////////////////////////////////////854// solid polygon renderer - 2 shapes855856for (polygon = 0; polygon < 2; polygon++)857{858int32 left_inc, right_inc;859int16 x1_final, x2_final;860int16 env[2][2];861int16 poly;862863// SR = 0x00864865// # raster lines to draw866DSP4.segments = DSP4.poly_raster[polygon][0] - view_y[polygon];867868// prevent overdraw869if (DSP4.segments > 0)870{871// bump drawing cursor872DSP4.poly_raster[polygon][0] = view_y[polygon];873DSP4.poly_raster[polygon][1] = view_y[polygon];874}875else876DSP4.segments = 0;877878// don't draw outside the window879if (view_y[polygon] < DSP4.poly_top[polygon][0])880{881DSP4.segments = 0;882883// flush remaining raster lines884if (view_y[polygon] >= DSP4.poly_top[polygon][0])885DSP4.segments = view_y[polygon] - DSP4.poly_top[polygon][0];886}887888// SR = 0x80889890// tell user how many raster structures to read in891DSP4_WRITE_WORD(DSP4.segments);892893// normal parameters894poly = polygon;895896/////////////////////////////////////////////////////897898// scan next command if no SR check needed899if (DSP4.segments)900{901int32 w_left, w_right;902903// road turnoff selection904if ((uint16) envelope[polygon][0] == (uint16) 0xc001)905poly = 1;906else907if (envelope[polygon][1] == 0x3fff)908poly = 1;909910///////////////////////////////////////////////911// left side of polygon912913// perspective correction on additional shaping parameters914env[0][0] = envelope[polygon][0] * DSP4.poly_plane[poly] >> 15;915env[0][1] = envelope[polygon][0] * DSP4.distance >> 15;916917// project new shapes (left side)918x1_final = view_x[poly] + env[0][0];919x2_final = DSP4.poly_start[poly] + env[0][1];920921// interpolate between projected points with shaping922left_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4.segments) << 1;923if (DSP4.segments == 1)924left_inc = -left_inc;925926///////////////////////////////////////////////927// right side of polygon928929// perspective correction on additional shaping parameters930env[1][0] = envelope[polygon][1] * DSP4.poly_plane[poly] >> 15;931env[1][1] = envelope[polygon][1] * DSP4.distance >> 15;932933// project new shapes (right side)934x1_final = view_x[poly] + env[1][0];935x2_final = DSP4.poly_start[poly] + env[1][1];936937// interpolate between projected points with shaping938right_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4.segments) << 1;939if (DSP4.segments == 1)940right_inc = -right_inc;941942///////////////////////////////////////////////943// update each point on the line944945w_left = SEX16(DSP4.poly_cx[polygon][0] - DSP4.poly_start[poly] + env[0][0]);946w_right = SEX16(DSP4.poly_cx[polygon][1] - DSP4.poly_start[poly] + env[1][0]);947948// update distance drawn into world949DSP4.poly_plane[polygon] = DSP4.distance;950951// rasterize line952for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)953{954int16 x_left, x_right;955956// project new coordinates957w_left += left_inc;958w_right += right_inc;959960// grab integer portion, drop fraction (no rounding)961x_left = w_left >> 16;962x_right = w_right >> 16;963964// saturate offscreen data965if (x_left < DSP4.poly_clipLf[polygon][0])966x_left = DSP4.poly_clipLf[polygon][0];967if (x_left > DSP4.poly_clipRt[polygon][0])968x_left = DSP4.poly_clipRt[polygon][0];969if (x_right < DSP4.poly_clipLf[polygon][1])970x_right = DSP4.poly_clipLf[polygon][1];971if (x_right > DSP4.poly_clipRt[polygon][1])972x_right = DSP4.poly_clipRt[polygon][1];973974// 1. HDMA memory pointer975// 2. Left window position ($2126/$2128)976// 3. Right window position ($2127/$2129)977DSP4_WRITE_WORD(DSP4.poly_ptr[polygon][0]);978DSP4_WRITE_BYTE(x_left & 0xff);979DSP4_WRITE_BYTE(x_right & 0xff);980981// update memory pointers982DSP4.poly_ptr[polygon][0] -= 4;983DSP4.poly_ptr[polygon][1] -= 4;984} // end rasterize line985}986987////////////////////////////////////////////////988// Post-update989990// new projection spot to continue rasterizing from991DSP4.poly_start[polygon] = view_x[poly];992} // end polygon rasterizer993}994while (1);995996// unknown output997DSP4_CLEAR_OUT();998DSP4_WRITE_WORD(0);9991000DSP4.waiting4command = TRUE;1001}10021003static void DSP4_OP09 (void)1004{1005DSP4.waiting4command = FALSE;10061007// op flow control1008switch (DSP4.Logic)1009{1010case 1: goto resume1; break;1011case 2: goto resume2; break;1012case 3: goto resume3; break;1013case 4: goto resume4; break;1014case 5: goto resume5; break;1015case 6: goto resume6; break;1016}10171018////////////////////////////////////////////////////1019// process initial inputs10201021// grab screen information1022DSP4.viewport_cx = DSP4_READ_WORD();1023DSP4.viewport_cy = DSP4_READ_WORD();1024DSP4_READ_WORD(); // 0x00001025DSP4.viewport_left = DSP4_READ_WORD();1026DSP4.viewport_right = DSP4_READ_WORD();1027DSP4.viewport_top = DSP4_READ_WORD();1028DSP4.viewport_bottom = DSP4_READ_WORD();10291030// starting raster line below the horizon1031DSP4.poly_bottom[0][0] = DSP4.viewport_bottom - DSP4.viewport_cy;1032DSP4.poly_raster[0][0] = 0x100;10331034do1035{1036////////////////////////////////////////////////////1037// check for new sprites10381039DSP4.in_count = 4;1040DSP4_WAIT(1);10411042resume1:10431044////////////////////////////////////////////////1045// raster overdraw check10461047DSP4.raster = DSP4_READ_WORD();10481049// continue updating the raster line where overdraw begins1050if (DSP4.raster < DSP4.poly_raster[0][0])1051{1052DSP4.sprite_clipy = DSP4.viewport_bottom - (DSP4.poly_bottom[0][0] - DSP4.raster);1053DSP4.poly_raster[0][0] = DSP4.raster;1054}10551056/////////////////////////////////////////////////1057// identify sprite10581059// op termination1060DSP4.distance = DSP4_READ_WORD();1061if (DSP4.distance == -0x8000)1062goto terminate;10631064// no sprite1065if (DSP4.distance == 0x0000)1066continue;10671068////////////////////////////////////////////////////1069// process projection information10701071// vehicle sprite1072if ((uint16) DSP4.distance == 0x9000)1073{1074int16 car_left, car_right, car_back;1075int16 impact_left, impact_back;1076int16 world_spx, world_spy;1077int16 view_spx, view_spy;1078uint16 energy;10791080// we already have 4 bytes we want1081DSP4.in_count = 14;1082DSP4_WAIT(2);10831084resume2:10851086// filter inputs1087energy = DSP4_READ_WORD();1088impact_back = DSP4_READ_WORD();1089car_back = DSP4_READ_WORD();1090impact_left = DSP4_READ_WORD();1091car_left = DSP4_READ_WORD();1092DSP4.distance = DSP4_READ_WORD();1093car_right = DSP4_READ_WORD();10941095// calculate car's world (x, y) values1096world_spx = car_right - car_left;1097world_spy = car_back;10981099// add in collision vector [needs bit-twiddling]1100world_spx -= energy * (impact_left - car_left) >> 16;1101world_spy -= energy * (car_back - impact_back) >> 16;11021103// perspective correction for world (x, y)1104view_spx = world_spx * DSP4.distance >> 15;1105view_spy = world_spy * DSP4.distance >> 15;11061107// convert to screen values1108DSP4.sprite_x = DSP4.viewport_cx + view_spx;1109DSP4.sprite_y = DSP4.viewport_bottom - (DSP4.poly_bottom[0][0] - view_spy);11101111// make the car's (x)-coordinate available1112DSP4_CLEAR_OUT();1113DSP4_WRITE_WORD(world_spx);11141115// grab a few remaining vehicle values1116DSP4.in_count = 4;1117DSP4_WAIT(3);11181119resume3:11201121// add vertical lift factor1122DSP4.sprite_y += DSP4_READ_WORD();1123}1124// terrain sprite1125else1126{1127int16 world_spx, world_spy;1128int16 view_spx, view_spy;11291130// we already have 4 bytes we want1131DSP4.in_count = 10;1132DSP4_WAIT(4);11331134resume4:11351136// sort loop inputs1137DSP4.poly_cx[0][0] = DSP4_READ_WORD();1138DSP4.poly_raster[0][1] = DSP4_READ_WORD();1139world_spx = DSP4_READ_WORD();1140world_spy = DSP4_READ_WORD();11411142// compute base raster line from the bottom1143DSP4.segments = DSP4.poly_bottom[0][0] - DSP4.raster;11441145// perspective correction for world (x, y)1146view_spx = world_spx * DSP4.distance >> 15;1147view_spy = world_spy * DSP4.distance >> 15;11481149// convert to screen values1150DSP4.sprite_x = DSP4.viewport_cx + view_spx - DSP4.poly_cx[0][0];1151DSP4.sprite_y = DSP4.viewport_bottom - DSP4.segments + view_spy;1152}11531154// default sprite size: 16x161155DSP4.sprite_size = 1;1156DSP4.sprite_attr = DSP4_READ_WORD();11571158////////////////////////////////////////////////////1159// convert tile data to SNES OAM format11601161do1162{1163int16 sp_x, sp_y, sp_attr, sp_dattr;1164int16 sp_dx, sp_dy;1165int16 pixels;1166uint16 header;1167bool8 draw;11681169DSP4.in_count = 2;1170DSP4_WAIT(5);11711172resume5:11731174draw = TRUE;11751176// opcode termination1177DSP4.raster = DSP4_READ_WORD();1178if (DSP4.raster == -0x8000)1179goto terminate;11801181// stop code1182if (DSP4.raster == 0x0000 && !DSP4.sprite_size)1183break;11841185// toggle sprite size1186if (DSP4.raster == 0x0000)1187{1188DSP4.sprite_size = !DSP4.sprite_size;1189continue;1190}11911192// check for valid sprite header1193header = DSP4.raster;1194header >>= 8;1195if (header != 0x20 &&1196header != 0x2e && // This is for attractor sprite1197header != 0x40 &&1198header != 0x60 &&1199header != 0xa0 &&1200header != 0xc0 &&1201header != 0xe0)1202break;12031204// read in rest of sprite data1205DSP4.in_count = 4;1206DSP4_WAIT(6);12071208resume6:12091210draw = TRUE;12111212/////////////////////////////////////1213// process tile data12141215// sprite deltas1216sp_dattr = DSP4.raster;1217sp_dy = DSP4_READ_WORD();1218sp_dx = DSP4_READ_WORD();12191220// update coordinates to screen space1221sp_x = DSP4.sprite_x + sp_dx;1222sp_y = DSP4.sprite_y + sp_dy;12231224// update sprite nametable/attribute information1225sp_attr = DSP4.sprite_attr + sp_dattr;12261227// allow partially visibile tiles1228pixels = DSP4.sprite_size ? 15 : 7;12291230DSP4_CLEAR_OUT();12311232// transparent tile to clip off parts of a sprite (overdraw)1233if (DSP4.sprite_clipy - pixels <= sp_y && sp_y <= DSP4.sprite_clipy && sp_x >= DSP4.viewport_left - pixels && sp_x <= DSP4.viewport_right && DSP4.sprite_clipy >= DSP4.viewport_top - pixels && DSP4.sprite_clipy <= DSP4.viewport_bottom)1234DSP4_OP0B(&draw, sp_x, DSP4.sprite_clipy, 0x00EE, DSP4.sprite_size, 0);12351236// normal sprite tile1237if (sp_x >= DSP4.viewport_left - pixels && sp_x <= DSP4.viewport_right && sp_y >= DSP4.viewport_top - pixels && sp_y <= DSP4.viewport_bottom && sp_y <= DSP4.sprite_clipy)1238DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4.sprite_size, 0);12391240// no following OAM data1241DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1);1242}1243while (1);1244}1245while (1);12461247terminate:1248DSP4.waiting4command = TRUE;1249}12501251static void DSP4_OP0A (int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4)1252{1253const uint16 OP0A_Values[16] =1254{12550x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150,12560xfe80, 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd01257};12581259*o4 = OP0A_Values[(n2 & 0x000f)];1260*o3 = OP0A_Values[(n2 & 0x00f0) >> 4];1261*o2 = OP0A_Values[(n2 & 0x0f00) >> 8];1262*o1 = OP0A_Values[(n2 & 0xf000) >> 12];1263}12641265static void DSP4_OP0B (bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop)1266{1267int16 Row1, Row2;12681269// SR = 0x0012701271// align to nearest 8-pixel row1272Row1 = (sp_y >> 3) & 0x1f;1273Row2 = (Row1 + 1) & 0x1f;12741275// check boundaries1276if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb)))1277*draw = 0;12781279if (size)1280{1281if (DSP4.OAM_Row[Row1] + 1 >= DSP4.OAM_RowMax)1282*draw = 0;1283if (DSP4.OAM_Row[Row2] + 1 >= DSP4.OAM_RowMax)1284*draw = 0;1285}1286else1287{1288if (DSP4.OAM_Row[Row1] >= DSP4.OAM_RowMax)1289*draw = 0;1290}12911292// emulator fail-safe (unknown if this really exists)1293if (DSP4.sprite_count >= 128)1294*draw = 0;12951296// SR = 0x8012971298if (*draw)1299{1300// Row tiles1301if (size)1302{1303DSP4.OAM_Row[Row1] += 2;1304DSP4.OAM_Row[Row2] += 2;1305}1306else1307DSP4.OAM_Row[Row1]++;13081309// yield OAM output1310DSP4_WRITE_WORD(1);13111312// pack OAM data: x, y, name, attr1313DSP4_WRITE_BYTE(sp_x & 0xff);1314DSP4_WRITE_BYTE(sp_y & 0xff);1315DSP4_WRITE_WORD(sp_attr);13161317DSP4.sprite_count++;13181319// OAM: size, msb data1320// save post-oam table data for future retrieval1321DSP4.OAM_attr[DSP4.OAM_index] |= ((sp_x < 0 || sp_x > 255) << DSP4.OAM_bits);1322DSP4.OAM_bits++;13231324DSP4.OAM_attr[DSP4.OAM_index] |= (size << DSP4.OAM_bits);1325DSP4.OAM_bits++;13261327// move to next byte in buffer1328if (DSP4.OAM_bits == 16)1329{1330DSP4.OAM_bits = 0;1331DSP4.OAM_index++;1332}1333}1334else1335if (stop)1336// yield no OAM output1337DSP4_WRITE_WORD(0);1338}13391340static void DSP4_OP0D (void)1341{1342DSP4.waiting4command = FALSE;13431344// op flow control1345switch (DSP4.Logic)1346{1347case 1: goto resume1; break;1348case 2: goto resume2; break;1349}13501351////////////////////////////////////////////////////1352// process initial inputs13531354// sort inputs1355DSP4.world_y = DSP4_READ_DWORD();1356DSP4.poly_bottom[0][0] = DSP4_READ_WORD();1357DSP4.poly_top[0][0] = DSP4_READ_WORD();1358DSP4.poly_cx[1][0] = DSP4_READ_WORD();1359DSP4.viewport_bottom = DSP4_READ_WORD();1360DSP4.world_x = DSP4_READ_DWORD();1361DSP4.poly_cx[0][0] = DSP4_READ_WORD();1362DSP4.poly_ptr[0][0] = DSP4_READ_WORD();1363DSP4.world_yofs = DSP4_READ_WORD();1364DSP4.world_dy = DSP4_READ_DWORD();1365DSP4.world_dx = DSP4_READ_DWORD();1366DSP4.distance = DSP4_READ_WORD();1367DSP4_READ_WORD(); // 0x00001368DSP4.world_xenv = SEX78(DSP4_READ_WORD());1369DSP4.world_ddy = DSP4_READ_WORD();1370DSP4.world_ddx = DSP4_READ_WORD();1371DSP4.view_yofsenv = DSP4_READ_WORD();13721373// initial (x, y, offset) at starting raster line1374DSP4.view_x1 = (DSP4.world_x + DSP4.world_xenv) >> 16;1375DSP4.view_y1 = DSP4.world_y >> 16;1376DSP4.view_xofs1 = DSP4.world_x >> 16;1377DSP4.view_yofs1 = DSP4.world_yofs;13781379// first raster line1380DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];13811382do1383{1384////////////////////////////////////////////////////1385// process one iteration of projection13861387// perspective projection of world (x, y, scroll) points1388// based on the current projection lines1389DSP4.view_x2 = (((DSP4.world_x + DSP4.world_xenv) >> 16) * DSP4.distance >> 15) + (DSP4.view_turnoff_x * DSP4.distance >> 15);1390DSP4.view_y2 = (DSP4.world_y >> 16) * DSP4.distance >> 15;1391DSP4.view_xofs2 = DSP4.view_x2;1392DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;13931394// 1. World x-location before transformation1395// 2. Viewer x-position at the current1396// 3. World y-location before perspective projection1397// 4. Viewer y-position below the horizon1398// 5. Number of raster lines drawn in this iteration1399DSP4_CLEAR_OUT();1400DSP4_WRITE_WORD((DSP4.world_x + DSP4.world_xenv) >> 16);1401DSP4_WRITE_WORD(DSP4.view_x2);1402DSP4_WRITE_WORD(DSP4.world_y >> 16);1403DSP4_WRITE_WORD(DSP4.view_y2);14041405//////////////////////////////////////////////////////////14061407// SR = 0x0014081409// determine # of raster lines used1410DSP4.segments = DSP4.view_y1 - DSP4.view_y2;14111412// prevent overdraw1413if (DSP4.view_y2 >= DSP4.poly_raster[0][0])1414DSP4.segments = 0;1415else1416DSP4.poly_raster[0][0] = DSP4.view_y2;14171418// don't draw outside the window1419if (DSP4.view_y2 < DSP4.poly_top[0][0])1420{1421DSP4.segments = 0;14221423// flush remaining raster lines1424if (DSP4.view_y1 >= DSP4.poly_top[0][0])1425DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];1426}14271428// SR = 0x8014291430DSP4_WRITE_WORD(DSP4.segments);14311432//////////////////////////////////////////////////////////14331434// scan next command if no SR check needed1435if (DSP4.segments)1436{1437int32 px_dx, py_dy;1438int32 x_scroll, y_scroll;14391440// SR = 0x0014411442// linear interpolation (lerp) between projected points1443px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;1444py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;14451446// starting step values1447x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);1448y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);14491450// SR = 0x8014511452// rasterize line1453for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)1454{1455// 1. HDMA memory pointer (bg1)1456// 2. vertical scroll offset ($210E)1457// 3. horizontal scroll offset ($210D)1458DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);1459DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);1460DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);14611462// update memory address1463DSP4.poly_ptr[0][0] -= 4;14641465// update screen values1466x_scroll += px_dx;1467y_scroll += py_dy;1468}1469}14701471/////////////////////////////////////////////////////1472// Post-update14731474// update new viewer (x, y, scroll) to last raster line drawn1475DSP4.view_x1 = DSP4.view_x2;1476DSP4.view_y1 = DSP4.view_y2;1477DSP4.view_xofs1 = DSP4.view_xofs2;1478DSP4.view_yofs1 = DSP4.view_yofs2;14791480// add deltas for projection lines1481DSP4.world_dx += SEX78(DSP4.world_ddx);1482DSP4.world_dy += SEX78(DSP4.world_ddy);14831484// update projection lines1485DSP4.world_x += (DSP4.world_dx + DSP4.world_xenv);1486DSP4.world_y += DSP4.world_dy;14871488////////////////////////////////////////////////////1489// command check14901491// scan next command1492DSP4.in_count = 2;1493DSP4_WAIT(1);14941495resume1:14961497// inspect input1498DSP4.distance = DSP4_READ_WORD();14991500// terminate op1501if (DSP4.distance == -0x8000)1502break;15031504// already have 2 bytes in queue1505DSP4.in_count = 6;1506DSP4_WAIT(2);15071508resume2:15091510// inspect inputs1511DSP4.world_ddy = DSP4_READ_WORD();1512DSP4.world_ddx = DSP4_READ_WORD();1513DSP4.view_yofsenv = DSP4_READ_WORD();15141515// no envelope here1516DSP4.world_xenv = 0;1517}1518while (1);15191520DSP4.waiting4command = TRUE;1521}15221523static void DSP4_OP0E (void)1524{1525DSP4.OAM_RowMax = 16;1526memset(DSP4.OAM_Row, 0, 64);1527}15281529static void DSP4_OP0F (void)1530{1531DSP4.waiting4command = FALSE;15321533// op flow control1534switch (DSP4.Logic)1535{1536case 1: goto resume1; break;1537case 2: goto resume2; break;1538case 3: goto resume3; break;1539case 4: goto resume4; break;1540}15411542////////////////////////////////////////////////////1543// process initial inputs15441545// sort inputs1546DSP4_READ_WORD(); // 0x00001547DSP4.world_y = DSP4_READ_DWORD();1548DSP4.poly_bottom[0][0] = DSP4_READ_WORD();1549DSP4.poly_top[0][0] = DSP4_READ_WORD();1550DSP4.poly_cx[1][0] = DSP4_READ_WORD();1551DSP4.viewport_bottom = DSP4_READ_WORD();1552DSP4.world_x = DSP4_READ_DWORD();1553DSP4.poly_cx[0][0] = DSP4_READ_WORD();1554DSP4.poly_ptr[0][0] = DSP4_READ_WORD();1555DSP4.world_yofs = DSP4_READ_WORD();1556DSP4.world_dy = DSP4_READ_DWORD();1557DSP4.world_dx = DSP4_READ_DWORD();1558DSP4.distance = DSP4_READ_WORD();1559DSP4_READ_WORD(); // 0x00001560DSP4.world_xenv = DSP4_READ_DWORD();1561DSP4.world_ddy = DSP4_READ_WORD();1562DSP4.world_ddx = DSP4_READ_WORD();1563DSP4.view_yofsenv = DSP4_READ_WORD();15641565// initial (x, y, offset) at starting raster line1566DSP4.view_x1 = (DSP4.world_x + DSP4.world_xenv) >> 16;1567DSP4.view_y1 = DSP4.world_y >> 16;1568DSP4.view_xofs1 = DSP4.world_x >> 16;1569DSP4.view_yofs1 = DSP4.world_yofs;1570DSP4.view_turnoff_x = 0;1571DSP4.view_turnoff_dx = 0;15721573// first raster line1574DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];15751576do1577{1578////////////////////////////////////////////////////1579// process one iteration of projection15801581// perspective projection of world (x, y, scroll) points1582// based on the current projection lines1583DSP4.view_x2 = ((DSP4.world_x + DSP4.world_xenv) >> 16) * DSP4.distance >> 15;1584DSP4.view_y2 = (DSP4.world_y >> 16) * DSP4.distance >> 15;1585DSP4.view_xofs2 = DSP4.view_x2;1586DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;15871588// 1. World x-location before transformation1589// 2. Viewer x-position at the next1590// 3. World y-location before perspective projection1591// 4. Viewer y-position below the horizon1592// 5. Number of raster lines drawn in this iteration1593DSP4_CLEAR_OUT();1594DSP4_WRITE_WORD((DSP4.world_x + DSP4.world_xenv) >> 16);1595DSP4_WRITE_WORD(DSP4.view_x2);1596DSP4_WRITE_WORD(DSP4.world_y >> 16);1597DSP4_WRITE_WORD(DSP4.view_y2);15981599//////////////////////////////////////////////////////16001601// SR = 0x0016021603// determine # of raster lines used1604DSP4.segments = DSP4.poly_raster[0][0] - DSP4.view_y2;16051606// prevent overdraw1607if (DSP4.view_y2 >= DSP4.poly_raster[0][0])1608DSP4.segments = 0;1609else1610DSP4.poly_raster[0][0] = DSP4.view_y2;16111612// don't draw outside the window1613if (DSP4.view_y2 < DSP4.poly_top[0][0])1614{1615DSP4.segments = 0;16161617// flush remaining raster lines1618if (DSP4.view_y1 >= DSP4.poly_top[0][0])1619DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];1620}16211622// SR = 0x8016231624DSP4_WRITE_WORD(DSP4.segments);16251626//////////////////////////////////////////////////////16271628// scan next command if no SR check needed1629if (DSP4.segments)1630{1631int32 px_dx, py_dy;1632int32 x_scroll, y_scroll;16331634for (DSP4.lcv = 0; DSP4.lcv < 4; DSP4.lcv++)1635{1636// grab inputs1637DSP4.in_count = 4;1638DSP4_WAIT(1);16391640resume1:16411642for (;;)1643{1644int16 dist;1645int16 color, red, green, blue;16461647dist = DSP4_READ_WORD();1648color = DSP4_READ_WORD();16491650// U1+B5+G5+R51651red = color & 0x1f;1652green = (color >> 5) & 0x1f;1653blue = (color >> 10) & 0x1f;16541655// dynamic lighting1656red = (red * dist >> 15) & 0x1f;1657green = (green * dist >> 15) & 0x1f;1658blue = (blue * dist >> 15) & 0x1f;1659color = red | (green << 5) | (blue << 10);16601661DSP4_CLEAR_OUT();1662DSP4_WRITE_WORD(color);16631664break;1665}1666}16671668//////////////////////////////////////////////////////16691670// SR = 0x0016711672// linear interpolation (lerp) between projected points1673px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;1674py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;16751676// starting step values1677x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);1678y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);16791680// SR = 0x8016811682// rasterize line1683for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)1684{1685// 1. HDMA memory pointer1686// 2. vertical scroll offset ($210E)1687// 3. horizontal scroll offset ($210D)1688DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);1689DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);1690DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);16911692// update memory address1693DSP4.poly_ptr[0][0] -= 4;16941695// update screen values1696x_scroll += px_dx;1697y_scroll += py_dy;1698}1699}17001701////////////////////////////////////////////////////1702// Post-update17031704// update new viewer (x, y, scroll) to last raster line drawn1705DSP4.view_x1 = DSP4.view_x2;1706DSP4.view_y1 = DSP4.view_y2;1707DSP4.view_xofs1 = DSP4.view_xofs2;1708DSP4.view_yofs1 = DSP4.view_yofs2;17091710// add deltas for projection lines1711DSP4.world_dx += SEX78(DSP4.world_ddx);1712DSP4.world_dy += SEX78(DSP4.world_ddy);17131714// update projection lines1715DSP4.world_x += (DSP4.world_dx + DSP4.world_xenv);1716DSP4.world_y += DSP4.world_dy;17171718// update road turnoff position1719DSP4.view_turnoff_x += DSP4.view_turnoff_dx;17201721////////////////////////////////////////////////////1722// command check17231724// scan next command1725DSP4.in_count = 2;1726DSP4_WAIT(2);17271728resume2:17291730// check for termination1731DSP4.distance = DSP4_READ_WORD();1732if (DSP4.distance == -0x8000)1733break;17341735// road splice1736if ((uint16) DSP4.distance == 0x8001)1737{1738DSP4.in_count = 6;1739DSP4_WAIT(3);17401741resume3:17421743DSP4.distance = DSP4_READ_WORD();1744DSP4.view_turnoff_x = DSP4_READ_WORD();1745DSP4.view_turnoff_dx = DSP4_READ_WORD();17461747// factor in new changes1748DSP4.view_x1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);1749DSP4.view_xofs1 += (DSP4.view_turnoff_x * DSP4.distance >> 15);17501751// update stepping values1752DSP4.view_turnoff_x += DSP4.view_turnoff_dx;17531754DSP4.in_count = 2;1755DSP4_WAIT(2);1756}17571758// already have 2 bytes in queue1759DSP4.in_count = 6;1760DSP4_WAIT(4);17611762resume4:17631764// inspect inputs1765DSP4.world_ddy = DSP4_READ_WORD();1766DSP4.world_ddx = DSP4_READ_WORD();1767DSP4.view_yofsenv = DSP4_READ_WORD();17681769// no envelope here1770DSP4.world_xenv = 0;1771}1772while (1);17731774// terminate op1775DSP4.waiting4command = TRUE;1776}17771778static void DSP4_OP10 (void)1779{1780DSP4.waiting4command = FALSE;17811782// op flow control1783switch (DSP4.Logic)1784{1785case 1: goto resume1; break;1786case 2: goto resume2; break;1787case 3: goto resume3; break;1788}17891790////////////////////////////////////////////////////1791// sort inputs17921793DSP4_READ_WORD(); // 0x00001794DSP4.world_y = DSP4_READ_DWORD();1795DSP4.poly_bottom[0][0] = DSP4_READ_WORD();1796DSP4.poly_top[0][0] = DSP4_READ_WORD();1797DSP4.poly_cx[1][0] = DSP4_READ_WORD();1798DSP4.viewport_bottom = DSP4_READ_WORD();1799DSP4.world_x = DSP4_READ_DWORD();1800DSP4.poly_cx[0][0] = DSP4_READ_WORD();1801DSP4.poly_ptr[0][0] = DSP4_READ_WORD();1802DSP4.world_yofs = DSP4_READ_WORD();1803DSP4.distance = DSP4_READ_WORD();1804DSP4.view_y2 = DSP4_READ_WORD();1805DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;1806DSP4.view_x2 = DSP4_READ_WORD();1807DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;1808DSP4.view_yofsenv = DSP4_READ_WORD();18091810// initial (x, y, offset) at starting raster line1811DSP4.view_x1 = DSP4.world_x >> 16;1812DSP4.view_y1 = DSP4.world_y >> 16;1813DSP4.view_xofs1 = DSP4.view_x1;1814DSP4.view_yofs1 = DSP4.world_yofs;18151816// first raster line1817DSP4.poly_raster[0][0] = DSP4.poly_bottom[0][0];18181819do1820{1821////////////////////////////////////////////////////1822// process one iteration of projection18231824// add shaping1825DSP4.view_x2 += DSP4.view_dx;1826DSP4.view_y2 += DSP4.view_dy;18271828// vertical scroll calculation1829DSP4.view_xofs2 = DSP4.view_x2;1830DSP4.view_yofs2 = (DSP4.world_yofs * DSP4.distance >> 15) + DSP4.poly_bottom[0][0] - DSP4.view_y2;18311832// 1. Viewer x-position at the next1833// 2. Viewer y-position below the horizon1834// 3. Number of raster lines drawn in this iteration1835DSP4_CLEAR_OUT();1836DSP4_WRITE_WORD(DSP4.view_x2);1837DSP4_WRITE_WORD(DSP4.view_y2);18381839//////////////////////////////////////////////////////18401841// SR = 0x0018421843// determine # of raster lines used1844DSP4.segments = DSP4.view_y1 - DSP4.view_y2;18451846// prevent overdraw1847if (DSP4.view_y2 >= DSP4.poly_raster[0][0])1848DSP4.segments = 0;1849else1850DSP4.poly_raster[0][0] = DSP4.view_y2;18511852// don't draw outside the window1853if (DSP4.view_y2 < DSP4.poly_top[0][0])1854{1855DSP4.segments = 0;18561857// flush remaining raster lines1858if (DSP4.view_y1 >= DSP4.poly_top[0][0])1859DSP4.segments = DSP4.view_y1 - DSP4.poly_top[0][0];1860}18611862// SR = 0x8018631864DSP4_WRITE_WORD(DSP4.segments);18651866//////////////////////////////////////////////////////18671868// scan next command if no SR check needed1869if (DSP4.segments)1870{1871for (DSP4.lcv = 0; DSP4.lcv < 4; DSP4.lcv++)1872{1873// grab inputs1874DSP4.in_count = 4;1875DSP4_WAIT(1);18761877resume1:18781879for (;;)1880{1881int16 dist;1882int16 color, red, green, blue;18831884dist = DSP4_READ_WORD();1885color = DSP4_READ_WORD();18861887// U1+B5+G5+R51888red = color & 0x1f;1889green = (color >> 5) & 0x1f;1890blue = (color >> 10) & 0x1f;18911892// dynamic lighting1893red = (red * dist >> 15) & 0x1f;1894green = (green * dist >> 15) & 0x1f;1895blue = (blue * dist >> 15) & 0x1f;1896color = red | (green << 5) | (blue << 10);18971898DSP4_CLEAR_OUT();1899DSP4_WRITE_WORD(color);19001901break;1902}1903}1904}19051906//////////////////////////////////////////////////////19071908// scan next command if no SR check needed1909if (DSP4.segments)1910{1911int32 px_dx, py_dy;1912int32 x_scroll, y_scroll;19131914// SR = 0x0019151916// linear interpolation (lerp) between projected points1917px_dx = (DSP4.view_xofs2 - DSP4.view_xofs1) * DSP4_Inverse(DSP4.segments) << 1;1918py_dy = (DSP4.view_yofs2 - DSP4.view_yofs1) * DSP4_Inverse(DSP4.segments) << 1;19191920// starting step values1921x_scroll = SEX16(DSP4.poly_cx[0][0] + DSP4.view_xofs1);1922y_scroll = SEX16(-DSP4.viewport_bottom + DSP4.view_yofs1 + DSP4.view_yofsenv + DSP4.poly_cx[1][0] - DSP4.world_yofs);19231924// SR = 0x8019251926// rasterize line1927for (DSP4.lcv = 0; DSP4.lcv < DSP4.segments; DSP4.lcv++)1928{1929// 1. HDMA memory pointer (bg2)1930// 2. vertical scroll offset ($2110)1931// 3. horizontal scroll offset ($210F)1932DSP4_WRITE_WORD(DSP4.poly_ptr[0][0]);1933DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16);1934DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16);19351936// update memory address1937DSP4.poly_ptr[0][0] -= 4;19381939// update screen values1940x_scroll += px_dx;1941y_scroll += py_dy;1942}1943}19441945/////////////////////////////////////////////////////1946// Post-update19471948// update new viewer (x, y, scroll) to last raster line drawn1949DSP4.view_x1 = DSP4.view_x2;1950DSP4.view_y1 = DSP4.view_y2;1951DSP4.view_xofs1 = DSP4.view_xofs2;1952DSP4.view_yofs1 = DSP4.view_yofs2;19531954////////////////////////////////////////////////////1955// command check19561957// scan next command1958DSP4.in_count = 2;1959DSP4_WAIT(2);19601961resume2:19621963// check for opcode termination1964DSP4.distance = DSP4_READ_WORD();1965if (DSP4.distance == -0x8000)1966break;19671968// already have 2 bytes in queue1969DSP4.in_count = 10;1970DSP4_WAIT(3);19711972resume3:19731974// inspect inputs1975DSP4.view_y2 = DSP4_READ_WORD();1976DSP4.view_dy = DSP4_READ_WORD() * DSP4.distance >> 15;1977DSP4.view_x2 = DSP4_READ_WORD();1978DSP4.view_dx = DSP4_READ_WORD() * DSP4.distance >> 15;1979}1980while (1);19811982DSP4.waiting4command = TRUE;1983}19841985static void DSP4_OP11 (int16 A, int16 B, int16 C, int16 D, int16 *M)1986{1987// 0x155 = 341 = Horizontal Width of the Screen1988*M = ((A * 0x0155 >> 2) & 0xf000) | ((B * 0x0155 >> 6) & 0x0f00) | ((C * 0x0155 >> 10) & 0x00f0) | ((D * 0x0155 >> 14) & 0x000f);1989}19901991static void DSP4_SetByte (void)1992{1993// clear pending read1994if (DSP4.out_index < DSP4.out_count)1995{1996DSP4.out_index++;1997return;1998}19992000if (DSP4.waiting4command)2001{2002if (DSP4.half_command)2003{2004DSP4.command |= (DSP4.byte << 8);2005DSP4.in_index = 0;2006DSP4.waiting4command = FALSE;2007DSP4.half_command = FALSE;2008DSP4.out_count = 0;2009DSP4.out_index = 0;20102011DSP4.Logic = 0;20122013switch (DSP4.command)2014{2015case 0x0000: DSP4.in_count = 4; break;2016case 0x0001: DSP4.in_count = 44; break;2017case 0x0003: DSP4.in_count = 0; break;2018case 0x0005: DSP4.in_count = 0; break;2019case 0x0006: DSP4.in_count = 0; break;2020case 0x0007: DSP4.in_count = 34; break;2021case 0x0008: DSP4.in_count = 90; break;2022case 0x0009: DSP4.in_count = 14; break;2023case 0x000a: DSP4.in_count = 6; break;2024case 0x000b: DSP4.in_count = 6; break;2025case 0x000d: DSP4.in_count = 42; break;2026case 0x000e: DSP4.in_count = 0; break;2027case 0x000f: DSP4.in_count = 46; break;2028case 0x0010: DSP4.in_count = 36; break;2029case 0x0011: DSP4.in_count = 8; break;2030default:2031DSP4.waiting4command = TRUE;2032break;2033}2034}2035else2036{2037DSP4.command = DSP4.byte;2038DSP4.half_command = TRUE;2039}2040}2041else2042{2043DSP4.parameters[DSP4.in_index] = DSP4.byte;2044DSP4.in_index++;2045}20462047if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index)2048{2049// Actually execute the command2050DSP4.waiting4command = TRUE;2051DSP4.out_index = 0;2052DSP4.in_index = 0;20532054switch (DSP4.command)2055{2056// 16-bit multiplication2057case 0x0000:2058{2059int16 multiplier, multiplicand;2060int32 product;20612062multiplier = DSP4_READ_WORD();2063multiplicand = DSP4_READ_WORD();20642065DSP4_Multiply(multiplicand, multiplier, &product);20662067DSP4_CLEAR_OUT();2068DSP4_WRITE_WORD(product);2069DSP4_WRITE_WORD(product >> 16);20702071break;2072}20732074// single-player track projection2075case 0x0001:2076DSP4_OP01();2077break;20782079// single-player selection2080case 0x0003:2081DSP4_OP03();2082break;20832084// clear OAM2085case 0x0005:2086DSP4_OP05();2087break;20882089// transfer OAM2090case 0x0006:2091DSP4_OP06();2092break;20932094// single-player track turnoff projection2095case 0x0007:2096DSP4_OP07();2097break;20982099// solid polygon projection2100case 0x0008:2101DSP4_OP08();2102break;21032104// sprite projection2105case 0x0009:2106DSP4_OP09();2107break;21082109// unknown2110case 0x000A:2111{2112DSP4_READ_WORD();2113int16 in2a = DSP4_READ_WORD();2114DSP4_READ_WORD();2115int16 out1a, out2a, out3a, out4a;21162117DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a);21182119DSP4_CLEAR_OUT();2120DSP4_WRITE_WORD(out1a);2121DSP4_WRITE_WORD(out2a);2122DSP4_WRITE_WORD(out3a);2123DSP4_WRITE_WORD(out4a);21242125break;2126}21272128// set OAM2129case 0x000B:2130{2131int16 sp_x = DSP4_READ_WORD();2132int16 sp_y = DSP4_READ_WORD();2133int16 sp_attr = DSP4_READ_WORD();2134bool8 draw = TRUE;21352136DSP4_CLEAR_OUT();2137DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1);21382139break;2140}21412142// multi-player track projection2143case 0x000D:2144DSP4_OP0D();2145break;21462147// multi-player selection2148case 0x000E:2149DSP4_OP0E();2150break;21512152// single-player track projection with lighting2153case 0x000F:2154DSP4_OP0F();2155break;21562157// single-player track turnoff projection with lighting2158case 0x0010:2159DSP4_OP10();2160break;21612162// unknown: horizontal mapping command2163case 0x0011:2164{2165int16 a, b, c, d, m;21662167d = DSP4_READ_WORD();2168c = DSP4_READ_WORD();2169b = DSP4_READ_WORD();2170a = DSP4_READ_WORD();21712172DSP4_OP11(a, b, c, d, &m);21732174DSP4_CLEAR_OUT();2175DSP4_WRITE_WORD(m);21762177break;2178}21792180default:2181break;2182}2183}2184}21852186static void DSP4_GetByte (void)2187{2188if (DSP4.out_count)2189{2190DSP4.byte = (uint8) DSP4.output[DSP4.out_index & 0x1FF];21912192DSP4.out_index++;2193if (DSP4.out_count == DSP4.out_index)2194DSP4.out_count = 0;2195}2196else2197DSP4.byte = 0xff;2198}21992200void DSP4SetByte (uint8 byte, uint16 address)2201{2202if (address < DSP0.boundary)2203{2204DSP4.byte = byte;2205DSP4.address = address;2206DSP4_SetByte();2207}2208}22092210uint8 DSP4GetByte (uint16 address)2211{2212if (address < DSP0.boundary)2213{2214DSP4.address = address;2215DSP4_GetByte();2216return (DSP4.byte);2217}22182219return (0x80);2220}222122222223