diff --git a/zsnes/src/Makefile.in b/zsnes/src/Makefile.in index 813b231a..026cefde 100644 --- a/zsnes/src/Makefile.in +++ b/zsnes/src/Makefile.in @@ -34,7 +34,8 @@ CHIPSOBJ=${CHIPDIR}/sfxproc.o ${CHIPDIR}/fxemu2.o ${CHIPDIR}/dsp1proc.o\ ${CHIPDIR}/fxemu2b.o ${CHIPDIR}/fxemu2c.o ${CHIPDIR}/fxtable.o\ ${CHIPDIR}/sa1proc.o ${CHIPDIR}/sa1regs.o ${CHIPDIR}/dsp1emu.o\ ${CHIPDIR}/st10proc.o ${CHIPDIR}/seta10.o ${CHIPDIR}/dsp2proc.o\ - ${CHIPDIR}/sdd1emu.o ${CHIPDIR}/c4emu.o + ${CHIPDIR}/sdd1emu.o ${CHIPDIR}/c4emu.o ${CHIPDIR}/dsp4proc.o\ + ${CHIPDIR}/dsp4emu.o CPUOBJ=${CPUDIR}/addrni.o ${CPUDIR}/dma.o ${CPUDIR}/dsp.o ${CPUDIR}/dspproc.o\ ${CPUDIR}/execute.o ${CPUDIR}/irq.o ${CPUDIR}/memory.o\ @@ -115,6 +116,9 @@ ${CHIPDIR}/sfxproc.o: ${CHIPDIR}/sfxproc.asm macros.mac\ ${CHIPDIR}/st10proc.o: ${CHIPDIR}/st10proc.asm macros.mac ${CHIPDIR}/seta10.o: ${CHIPDIR}/seta10.c ${CHIPDIR}/dsp1emu.o: ${CHIPDIR}/dsp1emu.c +${CHIPDIR}/dsp4proc.o: ${CHIPDIR}/dsp4proc.asm macros.mac +${CHIPDIR}/dsp4emu.o: ${CHIPDIR}/dsp4emu.c + ui.o: ui.asm macros.mac cfgload.o:cfgload.c macros.mac init.o:init.asm macros.mac diff --git a/zsnes/src/chips/dsp4emu.c b/zsnes/src/chips/dsp4emu.c new file mode 100644 index 00000000..3b40f8e0 --- /dev/null +++ b/zsnes/src/chips/dsp4emu.c @@ -0,0 +1,2284 @@ +/* +Copyright (C) 1997-2005 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach ) + +http://www.zsnes.com +http://sourceforge.net/projects/zsnes + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +typedef unsigned char bool8; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef char int8; +typedef short int16; +typedef long int32; +#define FALSE 0 +#define TRUE 1 + +struct +{ + bool8 waiting4command; + bool8 half_command; + uint16 command; + uint32 in_count; + uint32 in_index; + uint32 out_count; + uint32 out_index; + uint8 parameters[512]; + uint8 output[512]; +} DSP4; + +/* +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) + +// op control +int8 DSP4_Logic; // controls op flow + + +// projection format +int16 lcv; // loop-control variable +int16 distance; // z-position into virtual world +int16 raster; // current raster line +int16 segments; // number of raster lines drawn + +// 1.15.16 or 1.15.0 [sign, integer, fraction] +int32 world_x; // line of x-projection in world +int32 world_y; // line of y-projection in world +int32 world_dx; // projection line x-delta +int32 world_dy; // projection line y-delta +int16 world_ddx; // x-delta increment +int16 world_ddy; // y-delta increment +int32 world_xenv; // world x-shaping factor +int16 world_yofs; // world y-vertical scroll + +int16 view_x1; // current viewer-x +int16 view_y1; // current viewer-y +int16 view_x2; // future viewer-x +int16 view_y2; // future viewer-y +int16 view_dx; // view x-delta factor +int16 view_dy; // view y-delta factor +int16 view_xofs1; // current viewer x-vertical scroll +int16 view_yofs1; // current viewer y-vertical scroll +int16 view_xofs2; // future viewer x-vertical scroll +int16 view_yofs2; // future viewer y-vertical scroll +int16 view_yofsenv; // y-scroll shaping factor + + +// drawing area + +int16 viewport_cx; // x-center of viewport window +int16 viewport_cy; // y-center of render window +int16 viewport_left; // x-left of viewport +int16 viewport_right; // x-right of viewport +int16 viewport_top; // y-top of viewport +int16 viewport_bottom; // y-bottom of viewport + + +// sprite structure + +int16 sprite_x; // projected x-pos of sprite +int16 sprite_y; // projected y-pos of sprite +int16 sprite_attr; // obj attributes +bool8 sprite_size; // sprite size: 8x8 or 16x16 +int16 sprite_clipy; // visible line to clip pixels off +int16 sprite_count; + + +// generic projection variables designed for +// two solid polygons + two polygon sides + +int16 poly_clipLf[2][2]; // left clip boundary +int16 poly_clipRt[2][2]; // right clip boundary +int16 poly_ptr[2][2]; // HDMA structure pointers +int16 poly_raster[2][2]; // current raster line below horizon +int16 poly_top[2][2]; // top clip boundary +int16 poly_bottom[2][2]; // bottom clip boundary +int16 poly_cx[2][2]; // center for left/right points +int16 poly_start[2]; // current projection points +int16 poly_plane[2]; // previous z-plane distance + + +// OAM +int16 OAM_attr[16]; // OAM (size,MSB) data +int16 OAM_index; // index into OAM table +int16 OAM_bits; // offset into OAM table + +int16 OAM_RowMax; // maximum number of tiles per 8 aligned pixels (row) +int16 OAM_Row[32]; // current number of tiles per row + +////////////////////////////////////////////////////////////// + +// input protocol + +inline int16 DSP4_READ_WORD() +{ + int16 out; + + out = READ_WORD(DSP4.parameters + DSP4.in_index); + DSP4.in_index += 2; + + return out; +} + +inline 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; } + +#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_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 verified +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]; +} + +////////////////////////////////////////////////////////////// + +// Prototype +void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop); + +////////////////////////////////////////////////////////////// + +// OP00 +void DSP4_Multiply(int16 Multiplicand, int16 Multiplier, int32 *Product) +{ + *Product = (Multiplicand * Multiplier << 1) >> 1; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP01() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + world_y = DSP4_READ_DWORD(); + poly_bottom[0][0] = DSP4_READ_WORD(); + poly_top[0][0] = DSP4_READ_WORD(); + poly_cx[1][0] = DSP4_READ_WORD(); + viewport_bottom = DSP4_READ_WORD(); + world_x = DSP4_READ_DWORD(); + poly_cx[0][0] = DSP4_READ_WORD(); + poly_ptr[0][0] = DSP4_READ_WORD(); + world_yofs = DSP4_READ_WORD(); + world_dy = DSP4_READ_DWORD(); + world_dx = DSP4_READ_DWORD(); + distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + world_xenv = DSP4_READ_DWORD(); + world_ddy = DSP4_READ_WORD(); + world_ddx = DSP4_READ_WORD(); + view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting raster line + view_x1 = (world_x + world_xenv) >> 16; + view_y1 = world_y >> 16; + view_xofs1 = world_x >> 16; + view_yofs1 = world_yofs; + + // first raster line + poly_raster[0][0] = poly_bottom[0][0]; + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + view_x2 = ((world_x + world_xenv) >> 16) * distance >> 15; + view_y2 = (world_y >> 16) * distance >> 15; + view_xofs2 = view_x2; + view_yofs2 = (world_yofs * distance >> 15) + poly_bottom[0][0] - 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 raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((world_x + world_xenv) >> 16); + DSP4_WRITE_WORD(view_x2); + DSP4_WRITE_WORD(world_y >> 16); + DSP4_WRITE_WORD(view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of raster lines used + segments = poly_raster[0][0] - view_y2; + + // prevent overdraw + if (view_y2 >= poly_raster[0][0]) + segments = 0; + else + poly_raster[0][0] = view_y2; + + // don't draw outside the window + if (view_y2 < poly_top[0][0]) + { + segments = 0; + + // flush remaining raster lines + if (view_y1 >= poly_top[0][0]) + segments = view_y1 - poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (view_xofs2 - view_xofs1) * DSP4_Inverse(segments) << 1; + py_dy = (view_yofs2 - view_yofs1) * DSP4_Inverse(segments) << 1; + + // starting step values + x_scroll = SEX16(poly_cx[0][0] + view_xofs1); + y_scroll = SEX16(-viewport_bottom + view_yofs1 + view_yofsenv + poly_cx[1][0] - world_yofs); + + // SR = 0x80 + + // rasterize line + for (lcv = 0; lcv < segments; lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(poly_ptr[0][0]); + DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16); + DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16); + + + // update memory address + 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 raster line drawn + view_x1 = view_x2; + view_y1 = view_y2; + view_xofs1 = view_xofs2; + view_yofs1 = view_yofs2; + + // add deltas for projection lines + world_dx += SEX78(world_ddx); + world_dy += SEX78(world_ddy); + + // update projection lines + world_x += (world_dx + world_xenv); + world_y += world_dy; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // check for termination + distance = DSP4_READ_WORD(); + if (distance == -0x8000) + break; + + if ((uint16) distance == 0x8001) + { + /* + (code) + 308000 W: 01 + 308001 W: 80 + 308000 W: E4 + 308001 W: 0B + 308000 W: 5C + 308001 W: A0 + 308000 W: 38 + 308001 W: FD + (normal) + 308000 W: 07 + 308001 W: 0B + 308000 W: 00 + 308001 W: 00 + 308000 W: 00 + 308001 W: 01 + 308000 W: 00 + 308001 W: 00 + */ + + DSP4.in_count = 6; + DSP4_WAIT(2) resume2 : distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + DSP4.in_count = 2; + DSP4_WAIT(1) + } + + // already have 2 bytes read + DSP4.in_count = 6; + DSP4_WAIT(3) resume3 : + + // inspect inputs + world_ddy = DSP4_READ_WORD(); + world_ddx = DSP4_READ_WORD(); + view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + world_xenv = 0; + } + while (1); + + // terminate op + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP03() +{ + int16 i; + OAM_RowMax = 33; + + for (i = 0; i < 32; i++) + { + OAM_Row[i] = 0; + } +} + + +////////////////////////////////////////////////////////////// + + +void DSP4_OP05() +{ + int16 lcv; + OAM_index = 0; + OAM_bits = 0; + for (lcv = 0; lcv < 16; lcv++) + { + OAM_attr[lcv] = 0; + } + sprite_count = 0; +} + + +////////////////////////////////////////////////////////////// + +void DSP4_OP06() +{ + int16 lcv; + DSP4_CLEAR_OUT(); + + for (lcv = 0; lcv < 16; lcv++) + { + DSP4_WRITE_WORD(OAM_attr[lcv]); + } +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP07() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // sort inputs + + world_y = DSP4_READ_DWORD(); + poly_bottom[0][0] = DSP4_READ_WORD(); + poly_top[0][0] = DSP4_READ_WORD(); + poly_cx[1][0] = DSP4_READ_WORD(); + viewport_bottom = DSP4_READ_WORD(); + world_x = DSP4_READ_DWORD(); + poly_cx[0][0] = DSP4_READ_WORD(); + poly_ptr[0][0] = DSP4_READ_WORD(); + world_yofs = DSP4_READ_WORD(); + distance = DSP4_READ_WORD(); + view_y2 = DSP4_READ_WORD(); + view_dy = DSP4_READ_WORD() * distance >> 15; + view_x2 = DSP4_READ_WORD(); + view_dx = DSP4_READ_WORD() * distance >> 15; + view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting raster line + view_x1 = world_x >> 16; + view_y1 = world_y >> 16; + view_xofs1 = view_x1; + view_yofs1 = world_yofs; + + // first raster line + poly_raster[0][0] = poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // add shaping + view_x2 += view_dx; + view_y2 += view_dy; + + // vertical scroll calculation + view_xofs2 = view_x2; + view_yofs2 = (world_yofs * distance >> 15) + poly_bottom[0][0] - view_y2; + + // 1. Viewer x-position at the next + // 2. Viewer y-position below the horizon + // 3. Number of raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(view_x2); + DSP4_WRITE_WORD(view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of raster lines used + segments = view_y1 - view_y2; + + // prevent overdraw + if (view_y2 >= poly_raster[0][0]) + segments = 0; + else + poly_raster[0][0] = view_y2; + + // don't draw outside the window + if (view_y2 < poly_top[0][0]) + { + segments = 0; + + // flush remaining raster lines + if (view_y1 >= poly_top[0][0]) + segments = view_y1 - poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (view_xofs2 - view_xofs1) * DSP4_Inverse(segments) << 1; + py_dy = (view_yofs2 - view_yofs1) * DSP4_Inverse(segments) << 1; + + // starting step values + x_scroll = SEX16(poly_cx[0][0] + view_xofs1); + y_scroll = SEX16(-viewport_bottom + view_yofs1 + view_yofsenv + poly_cx[1][0] - world_yofs); + + // SR = 0x80 + + // rasterize line + for (lcv = 0; lcv < segments; lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(poly_ptr[0][0]); + DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16); + DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16); + + // update memory address + 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 raster line drawn + view_x1 = view_x2; + view_y1 = view_y2; + view_xofs1 = view_xofs2; + view_yofs1 = view_yofs2; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // check for opcode termination + distance = DSP4_READ_WORD(); + if (distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 10; + DSP4_WAIT(2) resume2 : + + // inspect inputs + view_y2 = DSP4_READ_WORD(); + view_dy = DSP4_READ_WORD() * distance >> 15; + view_x2 = DSP4_READ_WORD(); + view_dx = DSP4_READ_WORD() * distance >> 15; + view_yofsenv = DSP4_READ_WORD(); + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP08() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // process initial inputs for two polygons + + int16 win_left, win_right; + int16 view_x[2], view_y[2]; + int16 envelope[2][2]; + + // clip values + poly_clipRt[0][0] = DSP4_READ_WORD(); + poly_clipRt[0][1] = DSP4_READ_WORD(); + poly_clipRt[1][0] = DSP4_READ_WORD(); + poly_clipRt[1][1] = DSP4_READ_WORD(); + + poly_clipLf[0][0] = DSP4_READ_WORD(); + poly_clipLf[0][1] = DSP4_READ_WORD(); + poly_clipLf[1][0] = DSP4_READ_WORD(); + poly_clipLf[1][1] = DSP4_READ_WORD(); + + // unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6) + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7) + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // polygon centering (left,right) + poly_cx[0][0] = DSP4_READ_WORD(); + poly_cx[0][1] = DSP4_READ_WORD(); + poly_cx[1][0] = DSP4_READ_WORD(); + poly_cx[1][1] = DSP4_READ_WORD(); + + // HDMA pointer locations + poly_ptr[0][0] = DSP4_READ_WORD(); + poly_ptr[0][1] = DSP4_READ_WORD(); + poly_ptr[1][0] = DSP4_READ_WORD(); + poly_ptr[1][1] = DSP4_READ_WORD(); + + // starting raster line below the horizon + poly_bottom[0][0] = DSP4_READ_WORD(); + poly_bottom[0][1] = DSP4_READ_WORD(); + poly_bottom[1][0] = DSP4_READ_WORD(); + poly_bottom[1][1] = DSP4_READ_WORD(); + + // top boundary line to clip + poly_top[0][0] = DSP4_READ_WORD(); + poly_top[0][1] = DSP4_READ_WORD(); + poly_top[1][0] = DSP4_READ_WORD(); + poly_top[1][1] = DSP4_READ_WORD(); + + // unknown + // (ex. 1P = $2FC8, $0034, $FF5C, $0035) + // + // (ex. 2P = $3178, $0034, $FFCC, $0035) + // (ex. 2P = $2FC8, $0034, $FFCC, $0035) + + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // look at guidelines for both polygon shapes + distance = DSP4_READ_WORD(); + view_x[0] = DSP4_READ_WORD(); + view_y[0] = DSP4_READ_WORD(); + view_x[1] = DSP4_READ_WORD(); + view_y[1] = DSP4_READ_WORD(); + + // envelope shaping guidelines (one frame only) + envelope[0][0] = DSP4_READ_WORD(); + envelope[0][1] = DSP4_READ_WORD(); + envelope[1][0] = DSP4_READ_WORD(); + envelope[1][1] = DSP4_READ_WORD(); + + // starting base values to project from + poly_start[0] = view_x[0]; + poly_start[1] = view_x[1]; + + // starting raster lines to begin drawing + poly_raster[0][0] = view_y[0]; + poly_raster[0][1] = view_y[0]; + poly_raster[1][0] = view_y[1]; + poly_raster[1][1] = view_y[1]; + + // starting distances + poly_plane[0] = distance; + poly_plane[1] = distance; + + // SR = 0x00 + + // re-center coordinates + win_left = poly_cx[0][0] - view_x[0] + envelope[0][0]; + win_right = poly_cx[0][1] - view_x[0] + envelope[0][1]; + + // saturate offscreen data for polygon #1 + if (win_left < poly_clipLf[0][0]) + { + win_left = poly_clipLf[0][0]; + } + if (win_left > poly_clipRt[0][0]) + { + win_left = poly_clipRt[0][0]; + } + if (win_right < poly_clipLf[0][1]) + { + win_right = poly_clipLf[0][1]; + } + if (win_right > poly_clipRt[0][1]) + { + win_right = poly_clipRt[0][1]; + } + + // SR = 0x80 + + // initial output for polygon #1 + DSP4_CLEAR_OUT(); + DSP4_WRITE_BYTE(win_left & 0xff); + DSP4_WRITE_BYTE(win_right & 0xff); + + + do + { + int16 polygon; + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // terminate op + distance = DSP4_READ_WORD(); + if (distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 16; + + DSP4_WAIT(2) resume2 : + + // look at guidelines for both polygon shapes + view_x[0] = DSP4_READ_WORD(); + view_y[0] = DSP4_READ_WORD(); + view_x[1] = DSP4_READ_WORD(); + view_y[1] = DSP4_READ_WORD(); + + // envelope shaping guidelines (one frame only) + envelope[0][0] = DSP4_READ_WORD(); + envelope[0][1] = DSP4_READ_WORD(); + envelope[1][0] = DSP4_READ_WORD(); + envelope[1][1] = DSP4_READ_WORD(); + + //////////////////////////////////////////////////// + // projection begins + + // init + DSP4_CLEAR_OUT(); + + + ////////////////////////////////////////////// + // solid polygon renderer - 2 shapes + + for (polygon = 0; polygon < 2; polygon++) + { + int32 left_inc, right_inc; + int16 x1_final, x2_final; + int16 env[2][2]; + + // SR = 0x00 + + // # raster lines to draw + segments = poly_raster[polygon][0] - view_y[polygon]; + + // prevent overdraw + if (segments > 0) + { + // bump drawing cursor + poly_raster[polygon][0] = view_y[polygon]; + poly_raster[polygon][1] = view_y[polygon]; + } + else + segments = 0; + + // don't draw outside the window + if (view_y[polygon] < poly_top[polygon][0]) + { + segments = 0; + + // flush remaining raster lines + if (view_y[polygon] >= poly_top[polygon][0]) + segments = view_y[polygon] - poly_top[polygon][0]; + } + + // SR = 0x80 + + // tell user how many raster structures to read in + DSP4_WRITE_WORD(segments); + + ///////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + /////////////////////////////////////////////// + // left side of polygon + + // perspective correction on additional shaping parameters + env[0][0] = envelope[polygon][0] * poly_plane[polygon] >> 15; + env[0][1] = envelope[polygon][0] * distance >> 15; + + // project new shapes (left side) + x1_final = view_x[polygon] + env[0][0]; + x2_final = poly_start[polygon] + env[0][1]; + + // interpolate between projected points with shaping + left_inc = (x2_final - x1_final) * DSP4_Inverse(segments) << 1; + if (segments == 1) + left_inc = -left_inc; + + /////////////////////////////////////////////// + // right side of polygon + + // perspective correction on additional shaping parameters + env[1][0] = envelope[polygon][1] * poly_plane[polygon] >> 15; + env[1][1] = envelope[polygon][1] * distance >> 15; + + // project new shapes (right side) + x1_final = view_x[polygon] + env[1][0]; + x2_final = poly_start[polygon] + env[1][1]; + + // interpolate between projected points with shaping + right_inc = (x2_final - x1_final) * DSP4_Inverse(segments) << 1; + if (segments == 1) + right_inc = -right_inc; + + /////////////////////////////////////////////// + // update each point on the line + + int32 win_left, win_right; + + win_left = SEX16(poly_cx[polygon][0] - poly_start[polygon] + env[0][0]); + win_right = SEX16(poly_cx[polygon][1] - poly_start[polygon] + env[1][0]); + + // update distance drawn into world + poly_plane[polygon] = distance; + + // rasterize line + for (lcv = 0; lcv < segments; lcv++) + { + int16 x_left, x_right; + + // project new coordinates + win_left += left_inc; + win_right += right_inc; + + // grab integer portion, drop fraction (no rounding) + x_left = win_left >> 16; + x_right = win_right >> 16; + + // saturate offscreen data + if (x_left < poly_clipLf[polygon][0]) + x_left = poly_clipLf[polygon][0]; + if (x_left > poly_clipRt[polygon][0]) + x_left = poly_clipRt[polygon][0]; + if (x_right < poly_clipLf[polygon][1]) + x_right = poly_clipLf[polygon][1]; + if (x_right > poly_clipRt[polygon][1]) + x_right = poly_clipRt[polygon][1]; + + // 1. HDMA memory pointer + // 2. Left window position ($2126/$2128) + // 3. Right window position ($2127/$2129) + + DSP4_WRITE_WORD(poly_ptr[polygon][0]); + DSP4_WRITE_BYTE(x_left & 0xff); + DSP4_WRITE_BYTE(x_right & 0xff); + + + // update memory pointers + poly_ptr[polygon][0] -= 4; + poly_ptr[polygon][1] -= 4; + } // end rasterize line + } + + //////////////////////////////////////////////// + // Post-update + + // new projection spot to continue rasterizing from + poly_start[polygon] = view_x[polygon]; + } // end polygon rasterizer + } + while (1); + + // unknown output + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(0); + + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP09() +{ + 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; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // grab screen information + viewport_cx = DSP4_READ_WORD(); + viewport_cy = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + viewport_left = DSP4_READ_WORD(); + viewport_right = DSP4_READ_WORD(); + viewport_top = DSP4_READ_WORD(); + viewport_bottom = DSP4_READ_WORD(); + + // starting raster line below the horizon + poly_bottom[0][0] = viewport_bottom - viewport_cy; + poly_raster[0][0] = 0x100; + + do + { + //////////////////////////////////////////////////// + // check for new sprites + + DSP4.in_count = 4; + DSP4_WAIT(1) resume1 : + + //////////////////////////////////////////////// + // raster overdraw check + + raster = DSP4_READ_WORD(); + + // continue updating the raster line where overdraw begins + if (raster < poly_raster[0][0]) + { + sprite_clipy = viewport_bottom - (poly_bottom[0][0] - raster); + poly_raster[0][0] = raster; + } + + ///////////////////////////////////////////////// + // identify sprite + + // op termination + distance = DSP4_READ_WORD(); + if (distance == -0x8000) + goto terminate; + + + // no sprite + if (distance == 0x0000) + { + continue; + } + + //////////////////////////////////////////////////// + // process projection information + + // vehicle sprite + if ((uint16) distance == 0x9000) + { + int16 car_left, car_right, car_back; + int16 impact_left, impact_back; + int16 world_spx, world_spy; + int16 view_spx, view_spy; + uint16 energy; + + // we already have 4 bytes we want + DSP4.in_count = 14; + DSP4_WAIT(2) resume2 : + + // filter inputs + energy = DSP4_READ_WORD(); + impact_back = DSP4_READ_WORD(); + car_back = DSP4_READ_WORD(); + impact_left = DSP4_READ_WORD(); + car_left = DSP4_READ_WORD(); + distance = DSP4_READ_WORD(); + car_right = DSP4_READ_WORD(); + + // calculate car's world (x,y) values + world_spx = car_right - car_left; + world_spy = car_back; + + // add in collision vector [needs bit-twiddling] + world_spx -= energy * (impact_left - car_left) >> 16; + world_spy -= energy * (car_back - impact_back) >> 16; + + // perspective correction for world (x,y) + view_spx = world_spx * distance >> 15; + view_spy = world_spy * distance >> 15; + + // convert to screen values + sprite_x = viewport_cx + view_spx; + sprite_y = viewport_bottom - (poly_bottom[0][0] - view_spy); + + // make the car's (x)-coordinate available + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(world_spx); + + // grab a few remaining vehicle values + DSP4.in_count = 4; + DSP4_WAIT(3) resume3 : + + // add vertical lift factor + sprite_y += DSP4_READ_WORD(); + } + // terrain sprite + else + { + int16 world_spx, world_spy; + int16 view_spx, view_spy; + + // we already have 4 bytes we want + DSP4.in_count = 10; + DSP4_WAIT(4) resume4 : + + // sort loop inputs + poly_cx[0][0] = DSP4_READ_WORD(); + poly_raster[0][1] = DSP4_READ_WORD(); + world_spx = DSP4_READ_WORD(); + world_spy = DSP4_READ_WORD(); + + // compute base raster line from the bottom + segments = poly_bottom[0][0] - raster; + + // perspective correction for world (x,y) + view_spx = world_spx * distance >> 15; + view_spy = world_spy * distance >> 15; + + // convert to screen values + sprite_x = viewport_cx + view_spx - poly_cx[0][0]; + sprite_y = viewport_bottom - segments + view_spy; + } + + // default sprite size: 16x16 + sprite_size = 1; + sprite_attr = DSP4_READ_WORD(); + + //////////////////////////////////////////////////// + // convert tile data to SNES OAM format + + do + { + uint16 header; + + int16 sp_x, sp_y, sp_attr, sp_dattr; + int16 sp_dx, sp_dy; + int16 pixels; + + DSP4.in_count = 2; + DSP4_WAIT(5) resume5 : + + // opcode termination + raster = DSP4_READ_WORD(); + if (raster == -0x8000) + goto terminate; + + // stop code + if (raster == 0x0000 && !sprite_size) + break; + + // toggle sprite size + if (raster == 0x0000) + { + sprite_size = !sprite_size; + continue; + } + + // check for valid sprite header + header = raster; + header >>= 8; + if (header != 0x20 && + header != 0x40 && + header != 0x60 && + header != 0xa0 && + header != 0xc0 && + header != 0xe0) + break; + + // read in rest of sprite data + DSP4.in_count = 4; + DSP4_WAIT(6) resume6 : + + ///////////////////////////////////// + // process tile data + + // sprite deltas + sp_dattr = raster; + sp_dy = DSP4_READ_WORD(); + sp_dx = DSP4_READ_WORD(); + + // update coordinates to screen space + sp_x = sprite_x + sp_dx; + sp_y = sprite_y + sp_dy; + + // update sprite nametable/attribute information + sp_attr = sprite_attr + sp_dattr; + + // allow partially visibile tiles + pixels = sprite_size ? 15 : 7; + + bool8 draw = TRUE; + + + DSP4_CLEAR_OUT(); + + // transparent tile to clip off parts of a sprite (overdraw) + if (sprite_clipy - pixels <= sp_y && + sp_y <= sprite_clipy && + sp_x >= viewport_left - pixels && + sp_x <= viewport_right && + sprite_clipy >= viewport_top - pixels && + sprite_clipy <= viewport_bottom) + { + DSP4_OP0B(&draw, sp_x, sprite_clipy, 0x00EE, sprite_size, 0); + } + + + // normal sprite tile + if (sp_x >= viewport_left - pixels && + sp_x <= viewport_right && + sp_y >= viewport_top - pixels && + sp_y <= viewport_bottom && + sp_y <= sprite_clipy) + { + DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, sprite_size, 0); + } + + + // no following OAM data + DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1); + } + while (1); + } + while (1); + + terminate : DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +const uint16 OP0A_Values[16] = { 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150, 0xfe80, + 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0 }; + +void DSP4_OP0A(int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4) +{ + *o4 = OP0A_Values[(n2 & 0x000f)]; + *o3 = OP0A_Values[(n2 & 0x00f0) >> 4]; + *o2 = OP0A_Values[(n2 & 0x0f00) >> 8]; + *o1 = OP0A_Values[(n2 & 0xf000) >> 12]; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop) +{ + int16 Row1, Row2; + + // SR = 0x00 + + // align to nearest 8-pixel row + Row1 = (sp_y >> 3) & 0x1f; + Row2 = (Row1 + 1) & 0x1f; + + // check boundaries + if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb))) + { + *draw = 0; + } + if (size) + { + if (OAM_Row[Row1] + 1 >= OAM_RowMax) + *draw = 0; + if (OAM_Row[Row2] + 1 >= OAM_RowMax) + *draw = 0; + } + else + { + if (OAM_Row[Row1] >= OAM_RowMax) + { + *draw = 0; + } + } + + // emulator fail-safe (unknown if this really exists) + if (sprite_count >= 128) + { + *draw = 0; + } + + // SR = 0x80 + + if (*draw) + { + // Row tiles + if (size) + { + OAM_Row[Row1] += 2; + OAM_Row[Row2] += 2; + } + else + { + OAM_Row[Row1]++; + } + + // yield OAM output + DSP4_WRITE_WORD(1); + + // pack OAM data: x,y,name,attr + DSP4_WRITE_BYTE(sp_x & 0xff); + DSP4_WRITE_BYTE(sp_y & 0xff); + DSP4_WRITE_WORD(sp_attr); + + sprite_count++; + + // OAM: size,msb data + // save post-oam table data for future retrieval + OAM_attr[OAM_index] |= ((sp_x <0 || sp_x> 255) << OAM_bits); + OAM_bits++; + + OAM_attr[OAM_index] |= (size << OAM_bits); + OAM_bits++; + + // move to next byte in buffer + if (OAM_bits == 16) + { + OAM_bits = 0; + OAM_index++; + } + } + else if (stop) + { + // yield no OAM output + DSP4_WRITE_WORD(0); + } +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP0D() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + world_y = DSP4_READ_DWORD(); + poly_bottom[0][0] = DSP4_READ_WORD(); + poly_top[0][0] = DSP4_READ_WORD(); + poly_cx[1][0] = DSP4_READ_WORD(); + viewport_bottom = DSP4_READ_WORD(); + world_x = DSP4_READ_DWORD(); + poly_cx[0][0] = DSP4_READ_WORD(); + poly_ptr[0][0] = DSP4_READ_WORD(); + world_yofs = DSP4_READ_WORD(); + world_dy = DSP4_READ_DWORD(); + world_dx = DSP4_READ_DWORD(); + distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + world_xenv = SEX78(DSP4_READ_WORD()); + world_ddy = DSP4_READ_WORD(); + world_ddx = DSP4_READ_WORD(); + view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting raster line + view_x1 = (world_x + world_xenv) >> 16; + view_y1 = world_y >> 16; + view_xofs1 = world_x >> 16; + view_yofs1 = world_yofs; + + // first raster line + poly_raster[0][0] = poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + view_x2 = ((world_x + world_xenv) >> 16) * distance >> 15; + view_y2 = (world_y >> 16) * distance >> 15; + view_xofs2 = view_x2; + view_yofs2 = (world_yofs * distance >> 15) + poly_bottom[0][0] - view_y2; + + // 1. World x-location before transformation + // 2. Viewer x-position at the current + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((world_x + world_xenv) >> 16); + DSP4_WRITE_WORD(view_x2); + DSP4_WRITE_WORD(world_y >> 16); + DSP4_WRITE_WORD(view_y2); + + ////////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of raster lines used + segments = view_y1 - view_y2; + + // prevent overdraw + if (view_y2 >= poly_raster[0][0]) + segments = 0; + else + poly_raster[0][0] = view_y2; + + // don't draw outside the window + if (view_y2 < poly_top[0][0]) + { + segments = 0; + + // flush remaining raster lines + if (view_y1 >= poly_top[0][0]) + segments = view_y1 - poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(segments); + + ////////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (view_xofs2 - view_xofs1) * DSP4_Inverse(segments) << 1; + py_dy = (view_yofs2 - view_yofs1) * DSP4_Inverse(segments) << 1; + + // starting step values + x_scroll = SEX16(poly_cx[0][0] + view_xofs1); + y_scroll = SEX16(-viewport_bottom + view_yofs1 + view_yofsenv + poly_cx[1][0] - world_yofs); + + // SR = 0x80 + + // rasterize line + for (lcv = 0; lcv < segments; lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(poly_ptr[0][0]); + DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16); + DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16); + + + // update memory address + 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 raster line drawn + view_x1 = view_x2; + view_y1 = view_y2; + view_xofs1 = view_xofs2; + view_yofs1 = view_yofs2; + + // add deltas for projection lines + world_dx += SEX78(world_ddx); + world_dy += SEX78(world_ddy); + + // update projection lines + world_x += (world_dx + world_xenv); + world_y += world_dy; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // inspect input + distance = DSP4_READ_WORD(); + + // terminate op + if (distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 6; + DSP4_WAIT(2) resume2 : + + // inspect inputs + world_ddy = DSP4_READ_WORD(); + world_ddx = DSP4_READ_WORD(); + view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + world_xenv = 0; + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP0E() +{ + int16 i; + OAM_RowMax = 16; + + for (i = 0; i < 32; i++) + { + OAM_Row[i] = 0; + } +} + + +////////////////////////////////////////////////////////////// + +void DSP4_OP0F() +{ + 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; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_READ_WORD(); // 0x0000 + world_y = DSP4_READ_DWORD(); + poly_bottom[0][0] = DSP4_READ_WORD(); + poly_top[0][0] = DSP4_READ_WORD(); + poly_cx[1][0] = DSP4_READ_WORD(); + viewport_bottom = DSP4_READ_WORD(); + world_x = DSP4_READ_DWORD(); + poly_cx[0][0] = DSP4_READ_WORD(); + poly_ptr[0][0] = DSP4_READ_WORD(); + world_yofs = DSP4_READ_WORD(); + world_dy = DSP4_READ_DWORD(); + world_dx = DSP4_READ_DWORD(); + distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + world_xenv = DSP4_READ_DWORD(); + world_ddy = DSP4_READ_WORD(); + world_ddx = DSP4_READ_WORD(); + view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting raster line + view_x1 = (world_x + world_xenv) >> 16; + view_y1 = world_y >> 16; + view_xofs1 = world_x >> 16; + view_yofs1 = world_yofs; + + // first raster line + poly_raster[0][0] = poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + view_x2 = ((world_x + world_xenv) >> 16) * distance >> 15; + view_y2 = (world_y >> 16) * distance >> 15; + view_xofs2 = view_x2; + view_yofs2 = (world_yofs * distance >> 15) + poly_bottom[0][0] - 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 raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((world_x + world_xenv) >> 16); + DSP4_WRITE_WORD(view_x2); + DSP4_WRITE_WORD(world_y >> 16); + DSP4_WRITE_WORD(view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of raster lines used + segments = poly_raster[0][0] - view_y2; + + // prevent overdraw + if (view_y2 >= poly_raster[0][0]) + segments = 0; + else + poly_raster[0][0] = view_y2; + + // don't draw outside the window + if (view_y2 < poly_top[0][0]) + { + segments = 0; + + // flush remaining raster lines + if (view_y1 >= poly_top[0][0]) + segments = view_y1 - poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + for (lcv = 0; lcv < 4; lcv++) + { + // grab inputs + DSP4.in_count = 4; + DSP4_WAIT(1); + resume1 : + for (;;) + { + int16 distance; + int16 color; + + distance = DSP4_READ_WORD(); + color = DSP4_READ_WORD(); + + // U1+B5+G5+R5 + int16 red = color & 0x1f; + int16 green = (color >> 5) & 0x1f; + int16 blue = (color >> 10) & 0x1f; + + // dynamic lighting + red = (red * distance >> 15) & 0x1f; + green = (green * distance >> 15) & 0x1f; + blue = (blue * distance >> 15) & 0x1f; + color = red | (green << 5) | (blue << 10); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(color); + break; + } + } + + ////////////////////////////////////////////////////// + + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (view_xofs2 - view_xofs1) * DSP4_Inverse(segments) << 1; + py_dy = (view_yofs2 - view_yofs1) * DSP4_Inverse(segments) << 1; + + + // starting step values + x_scroll = SEX16(poly_cx[0][0] + view_xofs1); + y_scroll = SEX16(-viewport_bottom + view_yofs1 + view_yofsenv + poly_cx[1][0] - world_yofs); + + // SR = 0x80 + + // rasterize line + for (lcv = 0; lcv < segments; lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(poly_ptr[0][0]); + DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16); + DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16); + + // update memory address + 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 raster line drawn + view_x1 = view_x2; + view_y1 = view_y2; + view_xofs1 = view_xofs2; + view_yofs1 = view_yofs2; + + // add deltas for projection lines + world_dx += SEX78(world_ddx); + world_dy += SEX78(world_ddy); + + // update projection lines + world_x += (world_dx + world_xenv); + world_y += world_dy; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(2) resume2 : + + // check for termination + distance = DSP4_READ_WORD(); + if (distance == -0x8000) + break; + + if ((uint16) distance == 0x8001) + { + /* + (code) + 308000 W: 01 + 308001 W: 80 + 308000 W: E4 + 308001 W: 0B + 308000 W: 5C + 308001 W: A0 + 308000 W: 38 + 308001 W: FD + (normal) + 308000 W: 07 + 308001 W: 0B + 308000 W: 00 + 308001 W: 00 + 308000 W: 00 + 308001 W: 01 + 308000 W: 00 + 308001 W: 00 + */ + + DSP4.in_count = 6; + DSP4_WAIT(3) resume3 : distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + DSP4.in_count = 2; + DSP4_WAIT(2) + } + + // already have 2 bytes in queue + DSP4.in_count = 6; + DSP4_WAIT(4) resume4 : + + // inspect inputs + world_ddy = DSP4_READ_WORD(); + world_ddx = DSP4_READ_WORD(); + view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + world_xenv = 0; + } + while (1); + + // terminate op + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP10() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + } + + //////////////////////////////////////////////////// + // sort inputs + + DSP4_READ_WORD(); // 0x0000 + world_y = DSP4_READ_DWORD(); + poly_bottom[0][0] = DSP4_READ_WORD(); + poly_top[0][0] = DSP4_READ_WORD(); + poly_cx[1][0] = DSP4_READ_WORD(); + viewport_bottom = DSP4_READ_WORD(); + world_x = DSP4_READ_DWORD(); + poly_cx[0][0] = DSP4_READ_WORD(); + poly_ptr[0][0] = DSP4_READ_WORD(); + world_yofs = DSP4_READ_WORD(); + distance = DSP4_READ_WORD(); + view_y2 = DSP4_READ_WORD(); + view_dy = DSP4_READ_WORD() * distance >> 15; + view_x2 = DSP4_READ_WORD(); + view_dx = DSP4_READ_WORD() * distance >> 15; + view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting raster line + view_x1 = world_x >> 16; + view_y1 = world_y >> 16; + view_xofs1 = view_x1; + view_yofs1 = world_yofs; + + // first raster line + poly_raster[0][0] = poly_bottom[0][0]; + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // add shaping + view_x2 += view_dx; + view_y2 += view_dy; + + // vertical scroll calculation + view_xofs2 = view_x2; + view_yofs2 = (world_yofs * distance >> 15) + poly_bottom[0][0] - view_y2; + + // 1. Viewer x-position at the next + // 2. Viewer y-position below the horizon + // 3. Number of raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(view_x2); + DSP4_WRITE_WORD(view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of raster lines used + segments = view_y1 - view_y2; + + // prevent overdraw + if (view_y2 >= poly_raster[0][0]) + segments = 0; + else + poly_raster[0][0] = view_y2; + + // don't draw outside the window + if (view_y2 < poly_top[0][0]) + { + segments = 0; + + // flush remaining raster lines + if (view_y1 >= poly_top[0][0]) + segments = view_y1 - poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + for (lcv = 0; lcv < 4; lcv++) + { + // grab inputs + DSP4.in_count = 4; + DSP4_WAIT(1); + resume1 : + for (;;) + { + int16 distance; + int16 color; + + distance = DSP4_READ_WORD(); + color = DSP4_READ_WORD(); + + // U1+B5+G5+R5 + int16 red = color & 0x1f; + int16 green = (color >> 5) & 0x1f; + int16 blue = (color >> 10) & 0x1f; + + // dynamic lighting + red = (red * distance >> 15) & 0x1f; + green = (green * distance >> 15) & 0x1f; + blue = (blue * distance >> 15) & 0x1f; + color = red | (green << 5) | (blue << 10); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(color); + } + } + } + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (view_xofs2 - view_xofs1) * DSP4_Inverse(segments) << 1; + py_dy = (view_yofs2 - view_yofs1) * DSP4_Inverse(segments) << 1; + + // starting step values + x_scroll = SEX16(poly_cx[0][0] + view_xofs1); + y_scroll = SEX16(-viewport_bottom + view_yofs1 + view_yofsenv + poly_cx[1][0] - world_yofs); + + // SR = 0x80 + + // rasterize line + for (lcv = 0; lcv < segments; lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(poly_ptr[0][0]); + DSP4_WRITE_WORD((y_scroll + 0x8000) >> 16); + DSP4_WRITE_WORD((x_scroll + 0x8000) >> 16); + + // update memory address + 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 raster line drawn + view_x1 = view_x2; + view_y1 = view_y2; + view_xofs1 = view_xofs2; + view_yofs1 = view_yofs2; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(2) resume2 : + + // check for opcode termination + distance = DSP4_READ_WORD(); + if (distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 10; + DSP4_WAIT(3) resume3 : + + + // inspect inputs + view_y2 = DSP4_READ_WORD(); + view_dy = DSP4_READ_WORD() * distance >> 15; + view_x2 = DSP4_READ_WORD(); + view_dx = DSP4_READ_WORD() * distance >> 15; + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP11(int16 A, int16 B, int16 C, int16 D, int16 *M) +{ + // 0x155 = 341 = Horizontal Width of the Screen + *M = ((A * 0x0155 >> 2) & 0xf000) | + ((B * 0x0155 >> 6) & 0x0f00) | + ((C * 0x0155 >> 10) & 0x00f0) | + ((D * 0x0155 >> 14) & 0x000f); +} + + + + + +///////////////////////////////////////////////////////////// +//Processing Code +///////////////////////////////////////////////////////////// + + + + +uint8 dsp4_byte; +uint16 dsp4_address; + +void InitDSP4() +{ + DSP4.waiting4command = TRUE; +} + +void DSP4SetByte() +{ + uint8 byte = dsp4_byte; + uint16 address = dsp4_address; + + if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000)) + { + // clear pending read + if (DSP4.out_index < DSP4.out_count) + { + DSP4.out_index++; + return; + } + + if (DSP4.waiting4command) + { + if (DSP4.half_command) + { + DSP4.command |= (byte << 8); + DSP4.in_index = 0; + DSP4.waiting4command = FALSE; + DSP4.half_command = FALSE; + DSP4.out_count = 0; + DSP4.out_index = 0; + + DSP4_Logic = 0; + + + switch (DSP4.command) + { + case 0x0000: + DSP4.in_count = 4; break; + case 0x0001: + DSP4.in_count = 44; break; + case 0x0003: + DSP4.in_count = 0; break; + case 0x0005: + DSP4.in_count = 0; break; + case 0x0006: + DSP4.in_count = 0; break; + case 0x0007: + DSP4.in_count = 34; break; + case 0x0008: + DSP4.in_count = 90; break; + case 0x0009: + DSP4.in_count = 14; break; + case 0x000a: + DSP4.in_count = 6; break; + case 0x000b: + DSP4.in_count = 6; break; + case 0x000d: + DSP4.in_count = 42; break; + case 0x000e: + DSP4.in_count = 0; break; + case 0x000f: + DSP4.in_count = 46; break; + case 0x0010: + DSP4.in_count = 36; break; + case 0x0011: + DSP4.in_count = 8; break; + default: + DSP4.waiting4command = TRUE; + break; + } + } + else + { + DSP4.command = byte; + DSP4.half_command = TRUE; + } + } + else + { + DSP4.parameters[DSP4.in_index] = byte; + DSP4.in_index++; + } + + if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index) + { + // Actually execute the command + DSP4.waiting4command = TRUE; + DSP4.out_index = 0; + DSP4.in_index = 0; + + switch (DSP4.command) + { + // 16-bit multiplication + case 0x0000: + { + int16 multiplier, multiplicand; + int32 product; + + + multiplier = DSP4_READ_WORD(); + multiplicand = DSP4_READ_WORD(); + + DSP4_Multiply(multiplicand, multiplier, &product); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(product); + DSP4_WRITE_WORD(product >> 16); + } + break; + + // single-player track projection + case 0x0001: + DSP4_OP01(); break; + + // single-player selection + case 0x0003: + DSP4_OP03(); break; + + // clear OAM + case 0x0005: + DSP4_OP05(); break; + + // transfer OAM + case 0x0006: + DSP4_OP06(); break; + + // single-player track fork projection + case 0x0007: + DSP4_OP07(); break; + + // solid polygon projection + case 0x0008: + DSP4_OP08(); break; + + // sprite projection + case 0x0009: + DSP4_OP09(); break; + + // unknown + case 0x000A: + { + int16 in1a = DSP4_READ_WORD(); + int16 in2a = DSP4_READ_WORD(); + int16 in3a = DSP4_READ_WORD(); + int16 out1a, out2a, out3a, out4a; + + + DSP4_OP0A(in2a, &out1a, &out2a, &out3a, &out4a); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(out1a); + DSP4_WRITE_WORD(out2a); + DSP4_WRITE_WORD(out3a); + DSP4_WRITE_WORD(out4a); + } + break; + + // set OAM + case 0x000B: + { + int16 sp_x = DSP4_READ_WORD(); + int16 sp_y = DSP4_READ_WORD(); + int16 sp_attr = DSP4_READ_WORD(); + bool8 draw = 1; + + DSP4_CLEAR_OUT(); + + DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1); + } + break; + + // multi-player track projection + case 0x000D: + DSP4_OP0D(); break; + + // multi-player selection + case 0x000E: + DSP4_OP0E(); break; + + // single-player track projection with lighting + case 0x000F: + DSP4_OP0F(); break; + + // single-player track fork projection with lighting + case 0x0010: + DSP4_OP10(); break; + + // unknown: horizontal mapping command + case 0x0011: + { + int16 a, b, c, d, m; + + + d = DSP4_READ_WORD(); + c = DSP4_READ_WORD(); + b = DSP4_READ_WORD(); + a = DSP4_READ_WORD(); + + DSP4_OP11(a, b, c, d, &m); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(m); + + break; + } + + default: + break; + } + } + } +} + +void DSP4GetByte() +{ + if ((dsp4_address & 0xf000) == 0x6000 || (dsp4_address >= 0x8000 && dsp4_address < 0xc000)) + { + if (DSP4.out_count) + { + dsp4_byte = (uint8) DSP4.output[DSP4.out_index]; + DSP4.out_index++; + if (DSP4.out_count == DSP4.out_index) + DSP4.out_count = 0; + } + else + { + dsp4_byte = 0xff; + } + } + else + { + dsp4_byte = 0x80; + } +} diff --git a/zsnes/src/chips/dsp4proc.asm b/zsnes/src/chips/dsp4proc.asm new file mode 100644 index 00000000..e1bdfdf7 --- /dev/null +++ b/zsnes/src/chips/dsp4proc.asm @@ -0,0 +1,62 @@ +;Copyright (C) 1997-2005 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach ) +; +;http://www.zsnes.com +;http://sourceforge.net/projects/zsnes +; +;This program is free software; you can redistribute it and/or +;modify it under the terms of the GNU General Public License +;as published by the Free Software Foundation; either +;version 2 of the License, or (at your option) any later +;version. +; +;This program is distributed in the hope that it will be useful, +;but WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;GNU General Public License for more details. +; +;You should have received a copy of the GNU General Public License +;along with this program; if not, write to the Free Software +;Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +%include "macros.mac" + +EXTSYM dsp4_address,dsp4_byte,DSP4GetByte,DSP4SetByte + +NEWSYM DSP4Read8b + mov word[dsp4_address],cx + pushad + call DSP4GetByte + popad + mov al,byte[dsp4_byte] + ret + +NEWSYM DSP4Write8b + mov word[dsp4_address],cx + mov byte[dsp4_byte],al + pushad + call DSP4SetByte + popad + ret + +NEWSYM DSP4Read16b + mov word[dsp4_address],cx + pushad + call DSP4GetByte + popad + mov al,byte[dsp4_byte] + pushad + call DSP4GetByte + popad + mov ah,byte[dsp4_byte] + ret +; +NEWSYM DSP4Write16b + mov word[dsp4_address],cx + mov byte[dsp4_byte],al + pushad + call DSP4SetByte + mov byte[dsp4_byte],ah + call DSP4SetByte + popad + ret + diff --git a/zsnes/src/init.asm b/zsnes/src/init.asm index 218555d1..28ff79d2 100644 --- a/zsnes/src/init.asm +++ b/zsnes/src/init.asm @@ -140,6 +140,8 @@ EXTSYM setaaccessbankr16,setaaccessbankw16,setaaccessbankr16a,setaaccessbankw16a EXTSYM DSP2Read8b,DSP2Read16b,DSP2Write8b,DSP2Write16b,InitDSP2 +EXTSYM DSP4Read8b,DSP4Read16b,DSP4Write8b,DSP4Write16b,InitDSP4 + %ifdef __LINUX__ EXTSYM LoadDir, popdir, pushdir %endif @@ -3083,13 +3085,25 @@ NEWSYM CheckROMType cmp byte[DSP3Enable],1 je .initdsp cmp byte[DSP4Enable],1 - je .initdsp -; call InitDSP4 + pushad + call InitDSP4 + popad + xor ecx,ecx +.dsp4loop + mov dword[memtabler8+30h*4+ecx],DSP4Read8b + mov dword[memtablew8+30h*4+ecx],DSP4Write8b + mov dword[memtabler16+30h*4+ecx],DSP4Read16b + mov dword[memtablew16+30h*4+ecx],DSP4Write16b + add ecx,4 + cmp ecx,16*4 + jne .dsp4loop jmp .notDSP1Hi .initdsp2 call InitDSP2 .initdsp + pushad call InitDSP + popad mov byte[DSP1Type],1 cmp byte[romtype],2 jne .notDSP1Hi diff --git a/zsnes/src/makefile.ms b/zsnes/src/makefile.ms index 5c60ada8..8f213cb1 100644 --- a/zsnes/src/makefile.ms +++ b/zsnes/src/makefile.ms @@ -147,7 +147,8 @@ CHIPSOBJ=${CHIPDIR}/dsp1emu${OE} ${CHIPDIR}/fxemu2${OE} ${CHIPDIR}/sfxproc${OE}\ ${CHIPDIR}/fxemu2b${OE} ${CHIPDIR}/fxemu2c${OE} ${CHIPDIR}/fxtable${OE}\ ${CHIPDIR}/sa1proc${OE} ${CHIPDIR}/sa1regs${OE} ${CHIPDIR}/dsp1proc${OE}\ ${CHIPDIR}/st10proc${OE} ${CHIPDIR}/seta10${OE} ${CHIPDIR}/dsp2proc${OE}\ - ${CHIPDIR}/sdd1emu${OE} ${CHIPDIR}/c4emu${OE} + ${CHIPDIR}/sdd1emu${OE} ${CHIPDIR}/c4emu${OE} ${CHIPDIR}/dsp4proc${OE}\ + ${CHIPDIR}/dsp4emu${OE} CPUOBJ=${CPUDIR}/dma${OE} ${CPUDIR}/dsp${OE} ${CPUDIR}/dspproc${OE}\ ${CPUDIR}/execute${OE} ${CPUDIR}/irq${OE} ${CPUDIR}/memory${OE}\ @@ -309,6 +310,9 @@ ${CHIPDIR}/fxemu2b${OE}: $< ${CHIPDIR}/fxemu2.mac ${CHIPDIR}/fxemu2b.mac ${CHIPDIR}/fxemu2c${OE}: $< macros.mac ${CHIPDIR}/fxemu2.mac ${CHIPDIR}/fxemu2b.mac ${CHIPDIR}/fxemu2c.mac ${CHIPDIR}/fxtable${OE}: $< macros.mac ${CHIPDIR}/sa1proc${OE}: $< macros.mac +${CHIPDIR}/dsp4proc${OE}: $< macros.mac +${CHIPDIR}/dsp4emu${OE}: $< + ${GUIDIR}/gui${OE}: $< ${GUIDIR}/guitools.inc\ ${GUIDIR}/guimisc.inc ${GUIDIR}/guimouse.inc ${GUIDIR}/guiwindp.inc\