#include "../copyright"

#include "snes9x.h"
#include "dsp1.h"
#include "memmap.h"
#include "dsp1emu.c"
#include "dsp2emu.c"

void (*SetDSP)(uint8_t, uint16_t) = &DSP1SetByte;
uint8_t(*GetDSP)(uint16_t) = &DSP1GetByte;

void S9xResetDSP1(void)
{
   DSP1.waiting4command = true;
   DSP1.in_count = 0;
   DSP1.out_count = 0;
   DSP1.in_index = 0;
   DSP1.out_index = 0;
   DSP1.first_parameter = true;
}

uint8_t S9xGetDSP(uint16_t address)
{
   return (*GetDSP)(address);
}

void S9xSetDSP(uint8_t byte, uint16_t address)
{
   (*SetDSP)(byte, address);
}

void DSP1SetByte(uint8_t byte, uint16_t address)
{
   if ((address & 0xf000) == 0x6000 || (address & 0x7fff) < 0x4000)
   {
      if ((DSP1.command == 0x0A || DSP1.command == 0x1A) && DSP1.out_count != 0)
      {
         DSP1.out_count--;
         DSP1.out_index++;
         return;
      }
      else if (DSP1.waiting4command)
      {
         DSP1.command = byte;
         DSP1.in_index = 0;
         DSP1.waiting4command = false;
         DSP1.first_parameter = true;
         switch (byte) /* Mario Kart uses 0x00, 0x02, 0x06, 0x0c, 0x28, 0x0a */
         {
         case 0x07:
         case 0x0a:
         case 0x0f:
         case 0x1f:
         case 0x27:
         case 0x2f:
            DSP1.in_count = 1;
            break;
         case 0x00:
         case 0x04:
         case 0x0e:
         case 0x10:
         case 0x1e:
         case 0x20:
         case 0x24:
         case 0x2e:
         case 0x30:
         case 0x3e:
            DSP1.in_count = 2;
            break;
         case 0x03:
         case 0x06:
         case 0x08:
         case 0x09:
         case 0x0b:
         case 0x0c:
         case 0x0d:
         case 0x13:
         case 0x16:
         case 0x19:
         case 0x1b:
         case 0x1d:
         case 0x23:
         case 0x26:
         case 0x28:
         case 0x29:
         case 0x2b:
         case 0x2c:
         case 0x2d:
         case 0x33:
         case 0x36:
         case 0x39:
         case 0x3b:
         case 0x3d:
            DSP1.in_count = 3;
            break;
         case 0x01:
         case 0x05:
         case 0x11:
         case 0x15:
         case 0x18:
         case 0x21:
         case 0x25:
         case 0x31:
         case 0x35:
         case 0x38:
            DSP1.in_count = 4;
            break;
         case 0x14:
         case 0x1c:
         case 0x34:
         case 0x3c:
            DSP1.in_count = 6;
            break;
         case 0x02:
         case 0x12:
         case 0x22:
         case 0x32:
            DSP1.in_count = 7;
            break;
         case 0x1a:
         case 0x2a:
         case 0x3a:
            DSP1.command = 0x1a;
            DSP1.in_count = 1;
            break;
         case 0x17:
         case 0x37:
         case 0x3F:
            DSP1.command = 0x1f;
            DSP1.in_count = 1;
            break;
         default:
            DSP1.in_count = 0;
            DSP1.waiting4command = true;
            DSP1.first_parameter = true;
            break;
         }
         DSP1.in_count <<= 1;
      }
      else
      {
         DSP1.parameters [DSP1.in_index] = byte;
         DSP1.first_parameter = false;
         DSP1.in_index++;
      }

      if (DSP1.waiting4command || (DSP1.first_parameter && byte == 0x80))
      {
         DSP1.waiting4command = true;
         DSP1.first_parameter = false;
      }
      else if (!(DSP1.first_parameter && (DSP1.in_count != 0 || (DSP1.in_count == 0 && DSP1.in_index == 0))))
      {
         if (DSP1.in_count)
         {
            if (--DSP1.in_count == 0)
            {
               DSP1.waiting4command = true;
               DSP1.out_index = 0;
               switch (DSP1.command)
               {
               case 0x1f:
                  DSP1.out_count = 2048;
                  break;
               case 0x00: /* Multiple */
                  Op00Multiplicand = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op00Multiplier = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  DSPOp00();
                  DSP1.out_count = 2;
                  DSP1.output [0] = Op00Result & 0xFF;
                  DSP1.output [1] = (Op00Result >> 8) & 0xFF;
                  break;
               case 0x20: /* Multiple */
                  Op20Multiplicand = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op20Multiplier = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  DSPOp20();
                  DSP1.out_count = 2;
                  DSP1.output [0] = Op20Result & 0xFF;
                  DSP1.output [1] = (Op20Result >> 8) & 0xFF;
                  break;
               case 0x30:
               case 0x10: /* Inverse */
                  Op10Coefficient = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op10Exponent = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  DSPOp10();
                  DSP1.out_count = 4;
                  DSP1.output [0] = (uint8_t)(((int16_t) Op10CoefficientR) & 0xFF);
                  DSP1.output [1] = (uint8_t)((((int16_t) Op10CoefficientR) >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(((int16_t) Op10ExponentR) & 0xff);
                  DSP1.output [3] = (uint8_t)((((int16_t) Op10ExponentR) >> 8) & 0xff);
                  break;
               case 0x24:
               case 0x04: /* Sin and Cos of angle */
                  Op04Angle = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op04Radius = (uint16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  DSPOp04();
                  DSP1.out_count = 4;
                  DSP1.output [0] = (uint8_t)(Op04Sin & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op04Sin >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op04Cos & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op04Cos >> 8) & 0xFF);
                  break;
               case 0x08: /* Radius */
                  Op08X = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op08Y = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op08Z = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp08();
                  DSP1.out_count = 4;
                  DSP1.output [0] = (uint8_t)(((int16_t) Op08Ll) & 0xFF);
                  DSP1.output [1] = (uint8_t)((((int16_t) Op08Ll) >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(((int16_t) Op08Lh) & 0xFF);
                  DSP1.output [3] = (uint8_t)((((int16_t) Op08Lh) >> 8) & 0xFF);
                  break;
               case 0x18: /* Range */
                  Op18X = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op18Y = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op18Z = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op18R = (int16_t)(DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  DSPOp18();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op18D & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op18D >> 8) & 0xFF);
                  break;
               case 0x38: /* Range */
                  Op38X = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op38Y = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op38Z = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op38R = (int16_t)(DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  DSPOp38();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op38D & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op38D >> 8) & 0xFF);
                  break;
               case 0x28: /* Distance (vector length) */
                  Op28X = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op28Y = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op28Z = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp28();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op28R & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op28R >> 8) & 0xFF);
                  break;
               case 0x2c:
               case 0x0c: /* Rotate (2D rotate) */
                  Op0CA = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op0CX1 = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op0CY1 = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp0C();
                  DSP1.out_count = 4;
                  DSP1.output [0] = (uint8_t)(Op0CX2 & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op0CX2 >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op0CY2 & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op0CY2 >> 8) & 0xFF);
                  break;
               case 0x3c:
               case 0x1c: /* Polar (3D rotate) */
                  Op1CZ = (DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op1CY = (DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op1CX = (DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op1CXBR = (DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  Op1CYBR = (DSP1.parameters [8] | (DSP1.parameters[9] << 8));
                  Op1CZBR = (DSP1.parameters [10] | (DSP1.parameters[11] << 8));
                  DSPOp1C();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op1CXAR & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op1CXAR >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op1CYAR & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op1CYAR >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op1CZAR & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op1CZAR >> 8) & 0xFF);
                  break;
               case 0x32:
               case 0x22:
               case 0x12:
               case 0x02: /* Parameter (Projection) */
                  Op02FX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op02FY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op02FZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op02LFE = (int16_t)(DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  Op02LES = (int16_t)(DSP1.parameters [8] | (DSP1.parameters[9] << 8));
                  Op02AAS = (uint16_t)(DSP1.parameters [10] | (DSP1.parameters[11] << 8));
                  Op02AZS = (uint16_t)(DSP1.parameters [12] | (DSP1.parameters[13] << 8));
                  DSPOp02();
                  DSP1.out_count = 8;
                  DSP1.output [0] = (uint8_t)(Op02VOF & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op02VOF >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op02VVA & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op02VVA >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op02CX & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op02CX >> 8) & 0xFF);
                  DSP1.output [6] = (uint8_t)(Op02CY & 0xFF);
                  DSP1.output [7] = (uint8_t)((Op02CY >> 8) & 0xFF);
                  break;
               case 0x3a: /* 1a Mirror */
               case 0x2a: /* 1a Mirror */
               case 0x1a: /* Raster mode 7 matrix data */
               case 0x0a:
                  Op0AVS = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  DSPOp0A();
                  DSP1.out_count = 8;
                  DSP1.output [0] = (uint8_t)(Op0AA & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op0AB & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op0AC & 0xFF);
                  DSP1.output [6] = (uint8_t)(Op0AD & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op0AA >> 8) & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op0AB >> 8) & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op0AC >> 8) & 0xFF);
                  DSP1.output [7] = (uint8_t)((Op0AD >> 8) & 0xFF);
                  DSP1.in_index = 0;
                  break;
               case 0x16:
               case 0x26:
               case 0x36:
               case 0x06: /* Project object */
                  Op06X = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op06Y = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op06Z = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp06();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op06H & 0xff);
                  DSP1.output [1] = (uint8_t)((Op06H >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op06V & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op06V >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op06M & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op06M >> 8) & 0xFF);
                  break;
               case 0x1e:
               case 0x2e:
               case 0x3e:
               case 0x0e: /* Target */
                  Op0EH = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op0EV = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  DSPOp0E();
                  DSP1.out_count = 4;
                  DSP1.output [0] = (uint8_t)(Op0EX & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op0EX >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op0EY & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op0EY >> 8) & 0xFF);
                  break;
               case 0x05: /* Extra commands used by Pilot Wings */
               case 0x35:
               case 0x31:
               case 0x01: /* Set attitude matrix A */
                  Op01m = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op01Zr = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op01Yr = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op01Xr = (int16_t)(DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  DSPOp01();
                  break;
               case 0x15:
               case 0x11: /* Set attitude matrix B */
                  Op11m = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op11Zr = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op11Yr = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op11Xr = (int16_t)(DSP1.parameters [7] | (DSP1.parameters[7] << 8));
                  DSPOp11();
                  break;
               case 0x25:
               case 0x21: /* Set attitude matrix C */
                  Op21m = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op21Zr = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op21Yr = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op21Xr = (int16_t)(DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  DSPOp21();
                  break;
               case 0x09:
               case 0x39:
               case 0x3d:
               case 0x0d: /* Objective matrix A */
                  Op0DX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op0DY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op0DZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp0D();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op0DF & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op0DF >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op0DL & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op0DL >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op0DU & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op0DU >> 8) & 0xFF);
                  break;
               case 0x19:
               case 0x1d: /* Objective matrix B */
                  Op1DX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op1DY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op1DZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp1D();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op1DF & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op1DF >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op1DL & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op1DL >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op1DU & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op1DU >> 8) & 0xFF);
                  break;
               case 0x29:
               case 0x2d: /* Objective matrix C */
                  Op2DX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op2DY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op2DZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp2D();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op2DF & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op2DF >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op2DL & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op2DL >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op2DU & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op2DU >> 8) & 0xFF);
                  break;
               case 0x33:
               case 0x03: /* Subjective matrix A */
                  Op03F = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op03L = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op03U = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp03();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op03X & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op03X >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op03Y & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op03Y >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op03Z & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op03Z >> 8) & 0xFF);
                  break;
               case 0x13: /* Subjective matrix B */
                  Op13F = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op13L = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op13U = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp13();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op13X & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op13X >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op13Y & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op13Y >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op13Z & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op13Z >> 8) & 0xFF);
                  break;
               case 0x23: /* Subjective matrix C */
                  Op23F = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op23L = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op23U = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp23();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op23X & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op23X >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op23Y & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op23Y >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op23Z & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op23Z >> 8) & 0xFF);
                  break;
               case 0x3b:
               case 0x0b:
                  Op0BX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op0BY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op0BZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp0B();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op0BS & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op0BS >> 8) & 0xFF);
                  break;
               case 0x1b:
                  Op1BX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op1BY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op1BZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp1B();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op1BS & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op1BS >> 8) & 0xFF);
                  break;
               case 0x2b:
                  Op2BX = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op2BY = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op2BZ = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  DSPOp2B();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op2BS & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op2BS >> 8) & 0xFF);
                  break;
               case 0x34:
               case 0x14:
                  Op14Zr = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  Op14Xr = (int16_t)(DSP1.parameters [2] | (DSP1.parameters[3] << 8));
                  Op14Yr = (int16_t)(DSP1.parameters [4] | (DSP1.parameters[5] << 8));
                  Op14U = (int16_t)(DSP1.parameters [6] | (DSP1.parameters[7] << 8));
                  Op14F = (int16_t)(DSP1.parameters [8] | (DSP1.parameters[9] << 8));
                  Op14L = (int16_t)(DSP1.parameters [10] | (DSP1.parameters[11] << 8));
                  DSPOp14();
                  DSP1.out_count = 6;
                  DSP1.output [0] = (uint8_t)(Op14Zrr & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op14Zrr >> 8) & 0xFF);
                  DSP1.output [2] = (uint8_t)(Op14Xrr & 0xFF);
                  DSP1.output [3] = (uint8_t)((Op14Xrr >> 8) & 0xFF);
                  DSP1.output [4] = (uint8_t)(Op14Yrr & 0xFF);
                  DSP1.output [5] = (uint8_t)((Op14Yrr >> 8) & 0xFF);
                  break;
               case 0x27:
               case 0x2F:
                  Op2FUnknown = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  DSPOp2F();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op2FSize & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op2FSize >> 8) & 0xFF);
                  break;
               case 0x07:
               case 0x0F:
                  Op0FRamsize = (int16_t)(DSP1.parameters [0] | (DSP1.parameters[1] << 8));
                  DSPOp0F();
                  DSP1.out_count = 2;
                  DSP1.output [0] = (uint8_t)(Op0FPass & 0xFF);
                  DSP1.output [1] = (uint8_t)((Op0FPass >> 8) & 0xFF);
                  break;
               default:
                  break;
               }
            }
         }
      }
   }
}

uint8_t DSP1GetByte(uint16_t address)
{
   uint8_t t;
   if ((address & 0xf000) == 0x6000 || (address & 0x7fff) < 0x4000)
   {
      if (DSP1.out_count)
      {
         t = (uint8_t) DSP1.output [DSP1.out_index];
         DSP1.out_index++;
         if (--DSP1.out_count == 0)
         {
            if (DSP1.command == 0x1a || DSP1.command == 0x0a)
            {
               DSPOp0A();
               DSP1.out_count = 8;
               DSP1.out_index = 0;
               DSP1.output [0] = (Op0AA & 0xFF);
               DSP1.output [1] = (Op0AA >> 8) & 0xFF;
               DSP1.output [2] = (Op0AB & 0xFF);
               DSP1.output [3] = (Op0AB >> 8) & 0xFF;
               DSP1.output [4] = (Op0AC & 0xFF);
               DSP1.output [5] = (Op0AC >> 8) & 0xFF;
               DSP1.output [6] = (Op0AD & 0xFF);
               DSP1.output [7] = (Op0AD >> 8) & 0xFF;
            }
            if (DSP1.command == 0x1f)
            {
               if ((DSP1.out_index % 2) != 0)
                  t = (uint8_t)DSP1ROM[DSP1.out_index >> 1];
               else
                  t = DSP1ROM[DSP1.out_index >> 1] >> 8;
            }
         }
         DSP1.waiting4command = true;
      }
      else
         t = 0xff;
   }
   else
      t = 0x80;
   return t;
}

void DSP2SetByte(uint8_t byte, uint16_t address)
{
#ifndef FAST_LSB_WORD_ACCESS
   uint32_t temp;
#endif

   if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000))
   {
      if (DSP1.waiting4command)
      {
         DSP1.command = byte;
         DSP1.in_index = 0;
         DSP1.waiting4command = false;
         switch (byte)
         {
         default:
            DSP1.in_count = 0;
            break;
         case 0x03:
         case 0x05:
         case 0x06:
            DSP1.in_count = 1;
            break;
         case 0x0d:
            DSP1.in_count = 2;
            break;
         case 0x09:
            DSP1.in_count = 4;
            break;
         case 0x01:
            DSP1.in_count = 32;
            break;
         }
      }
      else
      {
         DSP1.parameters [DSP1.in_index] = byte;
         DSP1.in_index++;
      }

      if (DSP1.in_count == DSP1.in_index)
      {
         DSP1.waiting4command = true;
         DSP1.out_index = 0;
         switch (DSP1.command)
         {
         case 0x0D:
            if (DSP2Op0DHasLen)
            {
               DSP2Op0DHasLen = false;
               DSP1.out_count = DSP2Op0DOutLen;
               DSP2_Op0D();
            }
            else
            {
               DSP2Op0DInLen = DSP1.parameters[0];
               DSP2Op0DOutLen = DSP1.parameters[1];
               DSP1.in_index = 0;
               DSP1.in_count = (DSP2Op0DInLen + 1) >> 1;
               DSP2Op0DHasLen = true;
               if (byte)
                  DSP1.waiting4command = false;
            }
            break;
         case 0x06:
            if (DSP2Op06HasLen)
            {
               DSP2Op06HasLen = false;
               DSP1.out_count = DSP2Op06Len;
               DSP2_Op06();
            }
            else
            {
               DSP2Op06Len = DSP1.parameters[0];
               DSP1.in_index = 0;
               DSP1.in_count = DSP2Op06Len;
               DSP2Op06HasLen = true;
               if (byte)
                  DSP1.waiting4command = false;
            }
            break;
         case 0x01:
            DSP1.out_count = 32;
            DSP2_Op01();
            break;
         case 0x09: /* Multiply - don't yet know if this is signed or unsigned */
            DSP2Op09Word1 = DSP1.parameters[0] | (DSP1.parameters[1] << 8);
            DSP2Op09Word2 = DSP1.parameters[2] | (DSP1.parameters[3] << 8);
            DSP1.out_count = 4;
#ifdef FAST_LSB_WORD_ACCESS
            *(uint32_t*)DSP1.output = DSP2Op09Word1 * DSP2Op09Word2;
#else
            temp = DSP2Op09Word1 * DSP2Op09Word2;
            DSP1.output[0] = temp & 0xFF;
            DSP1.output[1] = (temp >> 8) & 0xFF;
            DSP1.output[2] = (temp >> 16) & 0xFF;
            DSP1.output[3] = (temp >> 24) & 0xFF;
#endif
            break;
         case 0x05:
            if (DSP2Op05HasLen)
            {
               DSP2Op05HasLen = false;
               DSP1.out_count = DSP2Op05Len;
               DSP2_Op05();
            }
            else
            {
               DSP2Op05Len = DSP1.parameters[0];
               DSP1.in_index = 0;
               DSP1.in_count = 2 * DSP2Op05Len;
               DSP2Op05HasLen = true;
               if (byte)
                  DSP1.waiting4command = false;
            }
            break;

         case 0x03:
            DSP2Op05Transparent = DSP1.parameters[0];
            break;
         case 0x0f:
         default:
            break;
         }
      }
   }
}

uint8_t DSP2GetByte(uint16_t address)
{
   uint8_t t;
   if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000))
   {
      if (DSP1.out_count)
      {
         t = (uint8_t) DSP1.output [DSP1.out_index];
         DSP1.out_index++;
         if (DSP1.out_count == DSP1.out_index)
            DSP1.out_count = 0;
      }
      else
         t = 0xff;
   }
   else
      t = 0x80;
   return t;
}

typedef struct
{
   bool     waiting4command;
   bool     half_command;
   uint16_t command;
   uint32_t in_count;
   uint32_t in_index;
   uint32_t out_count;
   uint32_t out_index;
   uint8_t  parameters [512];
   uint8_t  output     [512];
} SDSP4;

SDSP4 DSP4;

#include "dsp4emu.c"

bool DSP4_init = false;

void DSP4SetByte(uint8_t byte, uint16_t address)
{
   if (!DSP4_init) /* bootup */
   {
      DSP4.waiting4command = 1;
      DSP4_init = true;
   }

   if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000))
   {
      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 0x0003:
            case 0x0005:
            case 0x0006:
            case 0x000E:
               DSP4.in_count = 0;
               break;
            case 0x0000:
               DSP4.in_count = 4;
               break;
            case 0x000A:
            case 0x000B:
               DSP4.in_count = 6;
               break;
            case 0x0011:
               DSP4.in_count = 8;
               break;
            case 0x0009:
               DSP4.in_count = 14;
               break;
            case 0x0007:
               DSP4.in_count = 22;
               break;
            case 0x000D:
               DSP4.in_count = 34;
               break;
            case 0x0001:
               DSP4.in_count = 36;
               break;
            case 0x0008:
               DSP4.in_count = 72;
               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)
      {
         DSP4.waiting4command = true;
         DSP4.out_index = 0;
         DSP4.in_index = 0;
         switch (DSP4.command)
         {
         case 0x0000: /* 16-bit multiplication */
         {
            int16_t multiplier, multiplicand;
            int32_t product;
            multiplier = DSP4_READ_WORD(0);
            multiplicand = DSP4_READ_WORD(2);
            product = DSP4_Multiply(multiplicand, multiplier);
            DSP4.out_count = 4;
            DSP4_WRITE_WORD(0, product);
            DSP4_WRITE_WORD(2, product >> 16);
            break;
         }
         case 0x0011: /* unknown: horizontal mapping command */
         {
            int16_t a, b, c, d, m;
            a = DSP4_READ_WORD(6);
            b = DSP4_READ_WORD(4);
            c = DSP4_READ_WORD(2);
            d = DSP4_READ_WORD(0);
            m = DSP4_UnknownOP11(a, b, c, d);
            DSP4.out_count = 2;
            DSP4_WRITE_WORD(0, m);
            break;
         }
         case 0x0001: /* track projection */
            DSP4_Op01();
            break;
         case 0x0007: /* track projection (pass 2) */
            DSP4_Op07();
            break;
         case 0x0008: /* zone projections (fuel/repair/lap/teleport/...) */
            DSP4_Op08();
            break;
         case 0x0009: /* sprite transformation */
            DSP4_Op09();
            break;
         case 0x000D: /* fast track projection */
            DSP4_Op0D();
            break;
         case 0x0003: /* internal memory management (01) */
         {
            /* reset op09 data */
            op09_mode = false;
            break;
         }
         case 0x0005: /* internal memory management (06) */
         {
            int32_t lcv;
            /* clear OAM tables */
            op06_index = 0;
            op06_offset = 0;
            for (lcv = 0; lcv < 32; lcv++)
               op06_OAM[lcv] = 0;
            break;
         }
         case 0x000E: /* internal memory management (0D) */
         {
            /* reset op09 data */
            op09_mode = true;
            break;
         }
         case 0x0006: /* sprite OAM post-table data */
         {
            int32_t lcv;
            DSP4.out_count = 32;
            for (lcv = 0; lcv < 32; lcv++)
               DSP4.output[lcv] = op06_OAM[lcv];
            break;
         }
         case 0x000A: /* unknown */
         {
            int16_t out1a, out2a;
            out1a = (int16_t)0xff40;
            out2a = (int16_t)0x00c0;
            DSP4.out_count = 8;
            DSP4_WRITE_WORD(0, out1a);
            DSP4_WRITE_WORD(2, out2a);
            DSP4_WRITE_WORD(4, out1a);
            DSP4_WRITE_WORD(6, out2a);
            break;
         }
         case 0x000B: /* render player positions around track */
         {
            int16_t sp_x = DSP4_READ_WORD(0);
            int16_t sp_y = DSP4_READ_WORD(2);
            int16_t oam = DSP4_READ_WORD(4);

            if (!op09_mode) /* Only allow 1p/1p-split to yield output (???) */
            {
               /* yield OAM output */
               DSP4.out_count = 6;
               DSP4_WRITE_WORD(0, 1);

               /* pack OAM data: x,y,name,attr */
               DSP4.output[2] = sp_x & 0xff;
               DSP4.output[3] = sp_y & 0xff;
               DSP4_WRITE_WORD(4, oam);

               /* OAM: size,msb data */
               DSP4_Op06(false, false);
            }
            else /* 4p mode */
            {
               /* no OAM available */
               DSP4.out_count = 0;
               DSP4_WRITE_WORD(0, 0);
            }
            break;
         }
         default:
            break;
         }
      }
   }
}

uint8_t DSP4GetByte(uint16_t address)
{
   uint8_t t;
   if ((address & 0xf000) == 0x6000 || (address >= 0x8000 && address < 0xc000))
   {
      if (DSP4.out_count)
      {
         t = (uint8_t) DSP4.output [DSP4.out_index];
         DSP4.out_index++;
         if (DSP4.out_count == DSP4.out_index)
            DSP4.out_count = 0;
      }
      else
         t = 0xff;
   }
   else
      t = 0x80;

   return t;
}
