#ifdef ST0010_CPP

int16 ST0010::sin(int16 theta) {
  return sin_table[(theta >> 8) & 0xff];
}

int16 ST0010::cos(int16 theta) {
  return sin_table[((theta + 0x4000) >> 8) & 0xff];
}

uint8 ST0010::readb(uint16 addr) {
  return ram[addr & 0xfff];
}

uint16 ST0010::readw(uint16 addr) {
  return (readb(addr + 0) <<  0) |
         (readb(addr + 1) <<  8);
}

uint32 ST0010::readd(uint16 addr) {
  return (readb(addr + 0) <<  0) |
         (readb(addr + 1) <<  8) |
         (readb(addr + 2) << 16) |
         (readb(addr + 3) << 24);
}

void ST0010::writeb(uint16 addr, uint8 data) {
  ram[addr & 0xfff] = data;
}

void ST0010::writew(uint16 addr, uint16 data) {
  writeb(addr + 0, data >> 0);
  writeb(addr + 1, data >> 8);
}

void ST0010::writed(uint16 addr, uint32 data) {
  writeb(addr + 0, data >>  0);
  writeb(addr + 1, data >>  8);
  writeb(addr + 2, data >> 16);
  writeb(addr + 3, data >> 24);
}

//ST-0010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather
//bsnes port - Copyright (C) 2007 byuu

void ST0010::op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta) {
  if((x0 < 0) && (y0 < 0)) {
    x1 = -x0;
    y1 = -y0;
    quadrant = -0x8000;
  } else if(x0 < 0) {
    x1 = y0;
    y1 = -x0;
    quadrant = -0x4000;
  } else if(y0 < 0) {
    x1 = -y0;
    y1 = x0;
    quadrant = 0x4000;
  } else {
    x1 = x0;
    y1 = y0;
    quadrant = 0x0000;
  }

  while((x1 > 0x1f) || (y1 > 0x1f)) {
    if(x1 > 1) { x1 >>= 1; }
    if(y1 > 1) { y1 >>= 1; }
  }

  if(y1 == 0) { quadrant += 0x4000; }

  theta = (arctan[y1][x1] << 8) ^ quadrant;
}

//

void ST0010::op_01() {
  int16 x0 = readw(0x0000);
  int16 y0 = readw(0x0002);
  int16 x1, y1, quadrant, theta;

  op_01(x0, y0, x1, y1, quadrant, theta);

  writew(0x0000, x1);
  writew(0x0002, y1);
  writew(0x0004, quadrant);
//writew(0x0006, y0);  //Overload's docs note this write occurs, SNES9x disagrees
  writew(0x0010, theta);
}

void ST0010::op_02() {
  int16 positions = readw(0x0024);
  uint16 *places  = (uint16*)(ram + 0x0040);
  uint16 *drivers = (uint16*)(ram + 0x0080);

  bool sorted;
  uint16 temp;
  if(positions > 1) {
    do {
      sorted = true;
      for(int i = 0; i < positions - 1; i++) {
        if(places[i] < places[i + 1]) {
          temp = places[i + 1];
          places[i + 1] = places[i];
          places[i] = temp;

          temp = drivers[i + 1];
          drivers[i + 1] = drivers[i];
          drivers[i] = temp;

          sorted = false;
        }
      }
      positions--;
    } while(!sorted);
  }
}

void ST0010::op_03() {
  int16 x0 = readw(0x0000);
  int16 y0 = readw(0x0002);
  int16 multiplier = readw(0x0004);
  int32 x1, y1;

  x1 = x0 * multiplier << 1;
  y1 = y0 * multiplier << 1;

  writed(0x0010, x1);
  writed(0x0014, y1);
}

void ST0010::op_04() {
  int16 x = readw(0x0000);
  int16 y = readw(0x0002);
  int16 square;
  //calculate the vector length of (x,y)
  square = (int16)sqrt((double)(y * y + x * x));

  writew(0x0010, square);
}

void ST0010::op_05() {
  int32 dx, dy;
  int16 a1, b1, c1;
  uint16 o1;
  bool wrap = false;

  //target (x,y) coordinates
  int16 ypos_max = readw(0x00c0);
  int16 xpos_max = readw(0x00c2);

  //current coordinates and direction
  int32 ypos = readd(0x00c4);
  int32 xpos = readd(0x00c8);
  uint16 rot = readw(0x00cc);

  //physics
  uint16 speed = readw(0x00d4);
  uint16 accel = readw(0x00d6);
  uint16 speed_max = readw(0x00d8);

  //special condition acknowledgement
  int16 system = readw(0x00da);
  int16 flags = readw(0x00dc);

  //new target coordinates
  int16 ypos_new = readw(0x00de);
  int16 xpos_new = readw(0x00e0);

  //mask upper bit
  xpos_new &= 0x7fff;

  //get the current distance
  dx = xpos_max - (xpos >> 16);
  dy = ypos_max - (ypos >> 16);

  //quirk: clear and move in9
  writew(0x00d2, 0xffff);
  writew(0x00da, 0x0000);

  //grab the target angle
  op_01(dy, dx, a1, b1, c1, (int16&)o1);

  //check for wrapping
  if(abs(o1 - rot) > 0x8000) {
    o1 += 0x8000;
    rot += 0x8000;
    wrap = true;
  }

  uint16 old_speed = speed;

  //special case
  if(abs(o1 - rot) == 0x8000) {
    speed = 0x100;
  }

  //slow down for sharp curves
  else if(abs(o1 - rot) >= 0x1000) {
  uint32 slow = abs(o1 - rot);
    slow >>= 4;  //scaling
    speed -= slow;
  }

  //otherwise accelerate
  else {
    speed += accel;
    if(speed > speed_max) {
      speed = speed_max;  //clip speed
    }
  }

  //prevent negative/positive overflow
  if(abs(old_speed - speed) > 0x8000) {
    if(old_speed < speed) { speed = 0; }
    else speed = 0xff00;
  }

  //adjust direction by so many degrees
  //be careful of negative adjustments
  if((o1 > rot && (o1 - rot) > 0x80) || (o1 < rot && (rot - o1) >= 0x80)) {
    if(o1 < rot) { rot -= 0x280; }
    else if(o1 > rot) { rot += 0x280; }
  }

  //turn off wrapping
  if(wrap) { rot -= 0x8000; }

  //now check the distances (store for later)
  dx = (xpos_max << 16) - xpos;
  dy = (ypos_max << 16) - ypos;
  dx >>= 16;
  dy >>= 16;

  //if we're in so many units of the target, signal it
  if((system && (dy <= 6 && dy >= -8) && (dx <= 126 && dx >= -128)) || (!system && (dx <= 6 && dx >= -8) && (dy <= 126 && dy >= -128))) {
    //announce our new destination and flag it
    xpos_max = xpos_new & 0x7fff;
    ypos_max = ypos_new;
    flags |= 0x08;
  }

  //update position
  xpos -= (cos(rot) * 0x400 >> 15) * (speed >> 8) << 1;
  ypos -= (sin(rot) * 0x400 >> 15) * (speed >> 8) << 1;

  //quirk: mask upper byte
  xpos &= 0x1fffffff;
  ypos &= 0x1fffffff;

  writew(0x00c0, ypos_max);
  writew(0x00c2, xpos_max);
  writed(0x00c4, ypos);
  writed(0x00c8, xpos);
  writew(0x00cc, rot);
  writew(0x00d4, speed);
  writew(0x00dc, flags);
}

void ST0010::op_06() {
  int16 multiplicand = readw(0x0000);
  int16 multiplier = readw(0x0002);
  int32 product;

  product = multiplicand * multiplier << 1;

  writed(0x0010, product);
}

void ST0010::op_07() {
  int16 theta = readw(0x0000);

  int16 data;
  for(int i = 0, offset = 0; i < 176; i++) {
    data = mode7_scale[i] * cos(theta) >> 15;
    writew(0x00f0 + offset, data);
    writew(0x0510 + offset, data);

    data = mode7_scale[i] * sin(theta) >> 15;
    writew(0x0250 + offset, data);
    if(data) { data = ~data; }
    writew(0x03b0 + offset, data);

    offset += 2;
  }
}

void ST0010::op_08() {
  int16 x0 = readw(0x0000);
  int16 y0 = readw(0x0002);
  int16 theta = readw(0x0004);
  int16 x1, y1;

  x1 = (y0 * sin(theta) >> 15) + (x0 * cos(theta) >> 15);
  y1 = (y0 * cos(theta) >> 15) - (x0 * sin(theta) >> 15);

  writew(0x0010, x1);
  writew(0x0012, y1);
}

#endif
