📄 dsp4emu.c
字号:
/*Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )http://www.zsnes.comhttp://sourceforge.net/projects/zsneshttps://zsnes.bountysource.comThis program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseversion 2 as published by the Free Software Foundation.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include <string.h>#include "dsp4emu.h"/*Due recognition and credit are given on Overload's DSP website.Thank those contributors for their hard work on this chip.Fixed-point math reminder:[sign, integer, fraction]1.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0')1.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0')*/#define READ_WORD(s) (*(uint16 *) (s))#define READ_DWORD(s) (*(uint32 *) (s))#define WRITE_WORD(s, d) (*(uint16 *) (s)) = (d)#define WRITE_DWORD(s, d) (*(uint32 *) (s)) = (d)struct DSP4_t DSP4;struct DSP4_vars_t DSP4_vars;//////////////////////////////////////////////////////////////// input protocolstatic int16 DSP4_READ_WORD(){ int16 out; out = READ_WORD(DSP4.parameters + DSP4.in_index); DSP4.in_index += 2; return out;}static int32 DSP4_READ_DWORD(){ int32 out; out = READ_DWORD(DSP4.parameters + DSP4.in_index); DSP4.in_index += 4; return out;}//////////////////////////////////////////////////////////////// output protocol#define DSP4_CLEAR_OUT() \{ DSP4.out_count = 0; DSP4.out_index = 0; }#define DSP4_WRITE_BYTE( d ) \{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count++; }#define DSP4_WRITE_WORD( d ) \{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count += 2; }#ifndef MSB_FIRST#define DSP4_WRITE_16_WORD( d ) \{ memcpy(DSP4.output + DSP4.out_count, ( d ), 32); DSP4.out_count += 32; }#else#define DSP4_WRITE_16_WORD( d ) \{ int16 *p = ( d ), *end = ( d )+16; \ for (; p != end; p++) \ { \ WRITE_WORD( DSP4.output + DSP4.out_count, *p ); \ } \ DSP4.out_count += 32; \}#endif#ifdef PRINT_OP#define DSP4_WRITE_DEBUG( x, d ) \ WRITE_WORD( nop + x, d );#endif#ifdef DEBUG_DSP#define DSP4_WRITE_DEBUG( x, d ) \ WRITE_WORD( nop + x, d );#endif//////////////////////////////////////////////////////////////// used to wait for dsp i/o#define DSP4_WAIT( x ) \ DSP4.in_index = 0; DSP4_vars.DSP4_Logic = x; return;//////////////////////////////////////////////////////////////// 1.7.8 -> 1.15.16#define SEX78( a ) ( ( (int32) ( (int16) (a) ) ) << 8 )// 1.15.0 -> 1.15.16#define SEX16( a ) ( ( (int32) ( (int16) (a) ) ) << 16 )#ifdef PRINT_OP#define U16( a ) ( (uint16) ( a ) )#endif#ifdef DEBUG_DSP#define U16( a ) ( (uint16) ( a ) )#endif//////////////////////////////////////////////////////////////// Attention: This lookup table is not verifiedstatic const uint16 div_lut[64] = { 0x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249, 0x1000, 0x0e38, 0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888, 0x0800, 0x0787, 0x071c, 0x06bc, 0x0666, 0x0618, 0x05d1, 0x0590, 0x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348, 0x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9, 0x02aa, 0x029c, 0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253, 0x0249, 0x023e, 0x0234, 0x022b, 0x0222, 0x0219, 0x0210, 0x0208, };int16 DSP4_Inverse(int16 value){ // saturate bounds if (value < 0) { value = 0; } if (value > 63) { value = 63; } return div_lut[value];}//////////////////////////////////////////////////////////////// Prototypevoid DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop);//////////////////////////////////////////////////////////////// OP00void DSP4_Multiply(int16 Multiplicand, int16 Multiplier, int32 *Product){ *Product = (Multiplicand * Multiplier << 1) >> 1;}//////////////////////////////////////////////////////////////void DSP4_OP01(){ DSP4.waiting4command = FALSE; // op flow control switch (DSP4_vars.DSP4_Logic) { case 1: goto resume1; break; case 2: goto resume2; break; case 3: goto resume3; break; } //////////////////////////////////////////////////// // process initial inputs // sort inputs DSP4_vars.world_y = DSP4_READ_DWORD(); DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); DSP4_vars.viewport_bottom = DSP4_READ_WORD(); DSP4_vars.world_x = DSP4_READ_DWORD(); DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); DSP4_vars.world_yofs = DSP4_READ_WORD(); DSP4_vars.world_dy = DSP4_READ_DWORD(); DSP4_vars.world_dx = DSP4_READ_DWORD(); DSP4_vars.distance = DSP4_READ_WORD(); DSP4_READ_WORD(); // 0x0000 DSP4_vars.world_xenv = DSP4_READ_DWORD(); DSP4_vars.world_ddy = DSP4_READ_WORD(); DSP4_vars.world_ddx = DSP4_READ_WORD(); DSP4_vars.view_yofsenv = DSP4_READ_WORD(); // initial (x,y,offset) at starting DSP4_vars.raster line DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; DSP4_vars.view_turnoff_x = 0; DSP4_vars.view_turnoff_dx = 0; // first DSP4_vars.raster line DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; do { //////////////////////////////////////////////////// // process one iteration of projection // perspective projection of world (x,y,scroll) points // based on the current projection lines DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 )); DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); DSP4_vars.view_xofs2 = DSP4_vars.view_x2; DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; // 1. World x-location before transformation // 2. Viewer x-position at the next // 3. World y-location before perspective projection // 4. Viewer y-position below the horizon // 5. Number of DSP4_vars.raster lines drawn in this iteration DSP4_CLEAR_OUT(); DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); DSP4_WRITE_WORD(DSP4_vars.view_x2); DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); DSP4_WRITE_WORD(DSP4_vars.view_y2); ////////////////////////////////////////////////////// // SR = 0x00 // determine # of DSP4_vars.raster lines used DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2; // prevent overdraw if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) DSP4_vars.segments = 0; else DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; // don't draw outside the window if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) { DSP4_vars.segments = 0; // flush remaining DSP4_vars.raster lines if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; } // SR = 0x80 DSP4_WRITE_WORD(DSP4_vars.segments); ////////////////////////////////////////////////////// // scan next command if no SR check needed if (DSP4_vars.segments) { int32 px_dx, py_dy; int32 x_scroll, y_scroll; // SR = 0x00 // linear interpolation (lerp) between projected points px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; // starting step values x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); // SR = 0x80 // rasterize line for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) { // 1. HDMA memory pointer (bg1) // 2. vertical scroll offset ($210E) // 3. horizontal scroll offset ($210D) DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); // update memory address DSP4_vars.poly_ptr[0][0] -= 4; // update screen values x_scroll += px_dx; y_scroll += py_dy; } } //////////////////////////////////////////////////// // Post-update // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn DSP4_vars.view_x1 = DSP4_vars.view_x2; DSP4_vars.view_y1 = DSP4_vars.view_y2; DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; // add deltas for projection lines DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); // update projection lines DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); DSP4_vars.world_y += DSP4_vars.world_dy; // update road turnoff position DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; //////////////////////////////////////////////////// // command check // scan next command DSP4.in_count = 2; DSP4_WAIT(1) resume1 : // check for termination DSP4_vars.distance = DSP4_READ_WORD(); if (DSP4_vars.distance == -0x8000) break; // road turnoff if( (uint16) DSP4_vars.distance == 0x8001 ) { DSP4.in_count = 6; DSP4_WAIT(2) resume2: DSP4_vars.distance = DSP4_READ_WORD(); DSP4_vars.view_turnoff_x = DSP4_READ_WORD(); DSP4_vars.view_turnoff_dx = DSP4_READ_WORD(); // factor in new changes DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); // update stepping values DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; DSP4.in_count = 2; DSP4_WAIT(1) } // already have 2 bytes read DSP4.in_count = 6; DSP4_WAIT(3) resume3 : // inspect inputs DSP4_vars.world_ddy = DSP4_READ_WORD(); DSP4_vars.world_ddx = DSP4_READ_WORD(); DSP4_vars.view_yofsenv = DSP4_READ_WORD(); // no envelope here DSP4_vars.world_xenv = 0; } while (1); // terminate op DSP4.waiting4command = TRUE;}//////////////////////////////////////////////////////////////void DSP4_OP03(){ DSP4_vars.OAM_RowMax = 33; memset(DSP4_vars.OAM_Row, 0, 64);}//////////////////////////////////////////////////////////////void DSP4_OP05(){ DSP4_vars.OAM_index = 0; DSP4_vars.OAM_bits = 0; memset(DSP4_vars.OAM_attr, 0, 32); DSP4_vars.sprite_count = 0;}//////////////////////////////////////////////////////////////void DSP4_OP06(){ DSP4_CLEAR_OUT(); DSP4_WRITE_16_WORD(DSP4_vars.OAM_attr);}//////////////////////////////////////////////////////////////void DSP4_OP07(){ DSP4.waiting4command = FALSE; // op flow control switch (DSP4_vars.DSP4_Logic) { case 1: goto resume1; break; case 2: goto resume2; break; } //////////////////////////////////////////////////// // sort inputs DSP4_vars.world_y = DSP4_READ_DWORD(); DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -