auto SPC700::instructionImpliedModify(fps op, uint8& target) -> void {
  idle();
  target = alu(target);
}

auto SPC700::instructionAbsoluteModify(fps op) -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  uint8 data = read(absolute);
  write(absolute, alu(data));
}

auto SPC700::instructionDirectPageModify(fps op) -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  store(direct, alu(data));
}

auto SPC700::instructionDirectPageModifyWord(int adjust) -> void {
  uint8 direct = fetch();
  uint16 data = load(direct) + adjust;
  store(direct++, data >> 0);
  data += load(direct) << 8;
  store(direct++, data >> 8);
  ZF = data == 0;
  NF = data & 0x8000;
}

auto SPC700::instructionDirectPageXModify(fps op) -> void {
  uint8 direct = fetch();
  idle();
  uint8 data = load(direct + X);
  store(direct + X, alu(data));
}

auto SPC700::instructionBranch(bool take) -> void {
  uint8 data = fetch();
  if(!take) return;
  idle();
  idle();
  PC += (int8)data;
}

auto SPC700::instructionPull(uint8& data) -> void {
  idle();
  idle();
  data = pull();
}

auto SPC700::instructionPush(uint8 data) -> void {
  idle();
  idle();
  push(data);
}

auto SPC700::instructionAbsoluteRead(fpb op, uint8& target) -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  uint8 data = read(absolute);
  target = alu(target, data);
}

auto SPC700::instructionAbsoluteIndexedRead(fpb op, uint8& index) -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  idle();
  uint8 data = read(absolute + index);
  A = alu(A, data);
}

auto SPC700::instructionImmediateRead(fpb op, uint8& target) -> void {
  uint8 data = fetch();
  target = alu(target, data);
}

auto SPC700::instructionDirectPageRead(fpb op, uint8& target) -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  target = alu(target, data);
}

auto SPC700::instructionDirectPageIndexedRead(fpb op, uint8& target, uint8& index) -> void {
  uint8 direct = fetch();
  idle();
  uint8 data = load(direct + index);
  target = alu(target, data);
}

auto SPC700::instructionDirectPageReadWord(fpw op) -> void {
  uint8 direct = fetch();
  uint16 data = load(direct++);
  if(op != &SPC700::algorithmCPW) idle();
  data |= load(direct++) << 8;
  YA = alu(YA, data);
}

auto SPC700::instructionIndirectPageXRead(fpb op) -> void {
  uint8 direct = fetch() + X;
  idle();
  uint16 absolute = load(direct++);
  absolute |= load(direct++) << 8;
  uint8 data = read(absolute);
  A = alu(A, data);
}

auto SPC700::instructionIndirectPageYRead(fpb op) -> void {
  uint8 direct = fetch();
  idle();
  uint16 absolute = load(direct++);
  absolute |= load(direct++) << 8;
  uint8 data = read(absolute + Y);
  A = alu(A, data);
}

auto SPC700::instructionIndirectXRead(fpb op) -> void {
  idle();
  uint8 data = load(X);
  A = alu(A, data);
}

auto SPC700::instructionAbsoluteModifyBit(uint3 mode) -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  uint3 bit = absolute >> 13;
  absolute &= 0x1fff;
  uint8 data = read(absolute);
  switch(mode) {
  case 0:  //orc  addr:bit
    idle();
    CF |= data.bit(bit);
    break;
  case 1:  //orc !addr:bit
    idle();
    CF |= !data.bit(bit);
    break;
  case 2:  //and  addr:bit
    CF &= data.bit(bit);
    break;
  case 3:  //and !addr:bit
    CF &= !data.bit(bit);
    break;
  case 4:  //eor  addr:bit
    idle();
    CF ^= data.bit(bit);
    break;
  case 5:  //ldc  addr:bit
    CF = data.bit(bit);
    break;
  case 6:  //stc  addr:bit
    idle();
    data.bit(bit) = CF;
    write(absolute, data);
    break;
  case 7:  //not  addr:bit
    data.bit(bit) ^= 1;
    write(absolute, data);
    break;
  }
}

auto SPC700::instructionFlagClear(bool& flag) -> void {
  idle();
  if(&flag == &IF) idle();
  flag = 0;
}

auto SPC700::instructionFlagSet(bool& flag) -> void {
  idle();
  if(&flag == &IF) idle();
  flag = 1;
}

auto SPC700::instructionTransfer(uint8& from, uint8& to) -> void {
  idle();
  to = from;
  if(&to == &S) return;
  ZF = to == 0;
  NF = to & 0x80;
}

auto SPC700::instructionAbsoluteWrite(uint8& data) -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  read(absolute);
  write(absolute, data);
}

auto SPC700::instructionAbsoluteIndexedWrite(uint8& index) -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  idle();
  absolute += index;
  read(absolute);
  write(absolute, A);
}

auto SPC700::instructionDirectPageWrite(uint8& data) -> void {
  uint8 direct = fetch();
  load(direct);
  store(direct, data);
}

auto SPC700::instructionDirectPageIndexedWrite(uint8& data, uint8& index) -> void {
  uint8 direct = fetch() + index;
  idle();
  load(direct);
  store(direct, data);
}

auto SPC700::instructionDirectPageWriteImmediate(fpb op) -> void {
  uint8 immediate = fetch();
  uint8 direct = fetch();
  uint8 data = load(direct);
  data = alu(data, immediate);
  op != &SPC700::algorithmCMP ? store(direct, data) : idle();
}

auto SPC700::instructionDirectPageWriteDirectPage(fpb op) -> void {
  uint8 source = fetch();
  uint8 rhs = load(source);
  uint8 target = fetch();
  uint8 lhs;
  if(op != &SPC700::algorithmST) lhs = load(target);
  lhs = alu(lhs, rhs);
  op != &SPC700::algorithmCMP ? store(target, lhs) : idle();
}

auto SPC700::instructionIndirectXWriteIndirectY(fpb op) -> void {
  idle();
  uint8 rhs = load(Y);
  uint8 lhs = load(X);
  lhs = alu(lhs, rhs);
  op != &SPC700::algorithmCMP ? store(X, lhs) : idle();
}

//

auto SPC700::instructionBBC(uint3 bit) -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  uint8 displacement = fetch();
  idle();
  if(data.bit(bit) == 1) return;
  idle();
  idle();
  PC += (int8)displacement;
}

auto SPC700::instructionBBS(uint3 bit) -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  uint8 displacement = fetch();
  idle();
  if(data.bit(bit) == 0) return;
  idle();
  idle();
  PC += (int8)displacement;
}

auto SPC700::instructionBNEDirectPage() -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  uint8 displacement = fetch();
  idle();
  if(A == data) return;
  idle();
  idle();
  PC += (int8)displacement;
}

auto SPC700::instructionBNEDirectPageDecrement() -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  store(direct, --data);
  uint8 displacement = fetch();
  if(data == 0) return;
  idle();
  idle();
  PC += (int8)displacement;
}

auto SPC700::instructionBNEDirectPageX() -> void {
  uint8 direct = fetch();
  idle();
  uint8 data = load(direct + X);
  uint8 displacement = fetch();
  idle();
  if(A == data) return;
  idle();
  idle();
  PC += (int8)displacement;
}

auto SPC700::instructionBNEYDecrement() -> void {
  uint8 displacement = fetch();
  idle();
  idle();
  if(--Y == 0) return;
  idle();
  idle();
  PC += (int8)displacement;
}

auto SPC700::instructionBRK() -> void {
  uint16 absolute = read(0xffde);
  absolute |= read(0xffdf) << 8;
  idle();
  idle();
  push(PC >> 8);
  push(PC >> 0);
  push(P);
  PC = absolute;
  IF = 0;
  BF = 1;
}

auto SPC700::instructionCLR(uint3 bit) -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  data.bit(bit) = 0;
  store(direct, data);
}

auto SPC700::instructionCLV() -> void {
  idle();
  HF = 0;
  VF = 0;
}

auto SPC700::instructionCMC() -> void {
  idle();
  idle();
  CF = !CF;
}

auto SPC700::instructionDAA() -> void {
  idle();
  idle();
  if(CF || A > 0x99) {
    A += 0x60;
    CF = 1;
  }
  if(HF || (A & 15) > 0x09) {
    A += 0x06;
  }
  ZF = A == 0;
  NF = A & 0x80;
}

auto SPC700::instructionDAS() -> void {
  idle();
  idle();
  if(!CF || A > 0x99) {
    A -= 0x60;
    CF = 0;
  }
  if(!HF || (A & 15) > 0x09) {
    A -= 0x06;
  }
  ZF = A == 0;
  NF = A & 0x80;
}

auto SPC700::instructionDIV() -> void {
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  uint16 ya = YA;
  //overflow set if quotient >= 256
  HF = (Y & 15) >= (X & 15);
  VF = Y >= X;
  if(Y < (X << 1)) {
    //if quotient is <= 511 (will fit into 9-bit result)
    A = ya / X;
    Y = ya % X;
  } else {
    //otherwise, the quotient won't fit into VF + A
    //this emulates the odd behavior of the S-SMP in this case
    A = 255 - (ya - (X << 9)) / (256 - X);
    Y = X   + (ya - (X << 9)) % (256 - X);
  }
  //result is set based on a (quotient) only
  ZF = A == 0;
  NF = A & 0x80;
}

auto SPC700::instructionJMPAbsolute() -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  PC = absolute;
}

auto SPC700::instructionJMPIndirectAbsoluteX() -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  idle();
  absolute += X;
  uint16 pc = read(absolute++);
  pc |= read(absolute++) << 8;
  PC = pc;
}

auto SPC700::instructionJSPDirectPage() -> void {
  uint8 direct = fetch();
  idle();
  idle();
  push(PC >> 8);
  push(PC >> 0);
  PC = 0xff00 | direct;
}

auto SPC700::instructionJSRAbsolute() -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  idle();
  idle();
  idle();
  push(PC >> 8);
  push(PC >> 0);
  PC = absolute;
}

auto SPC700::instructionJST(uint4 vector) -> void {
  uint16 absolute = 0xffde - (vector << 1);
  uint16 pc = read(absolute++);
  pc |= read(absolute++) << 8;
  idle();
  idle();
  idle();
  push(PC >> 8);
  push(PC >> 0);
  PC = pc;
}

auto SPC700::instructionLDAIndirectXIncrement() -> void {
  idle();
  A = load(X++);
  idle();
  ZF = A == 0;
  NF = A & 0x80;
}

auto SPC700::instructionMUL() -> void {
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  idle();
  uint16 ya = Y * A;
  A = ya >> 0;
  Y = ya >> 8;
  //result is set based on y (high-byte) only
  ZF = Y == 0;
  NF = Y & 0x80;
}

auto SPC700::instructionNOP() -> void {
  idle();
}

auto SPC700::instructionPLP() -> void {
  idle();
  idle();
  P = pull();
}

auto SPC700::instructionRTI() -> void {
  P = pull();
  uint16 absolute = pull();
  absolute |= pull() << 8;
  idle();
  idle();
  PC = absolute;
}

auto SPC700::instructionRTS() -> void {
  uint16 absolute = pull();
  absolute |= pull() << 8;
  idle();
  idle();
  PC = absolute;
}

auto SPC700::instructionSET(uint3 bit) -> void {
  uint8 direct = fetch();
  uint8 data = load(direct);
  data.bit(bit) = 1;
  store(direct, data);
}

auto SPC700::instructionSTAIndirectPageX() -> void {
  uint8 direct = fetch() + X;
  idle();
  uint16 absolute = load(direct++);
  absolute |= load(direct++) << 8;
  read(absolute);
  write(absolute, A);
}

auto SPC700::instructionSTAIndirectPageY() -> void {
  uint8 direct = fetch();
  uint16 absolute = load(direct++);
  absolute |= load(direct++) << 8;
  idle();
  absolute += Y;
  read(absolute);
  write(absolute, A);
}

auto SPC700::instructionSTAIndirectX() -> void {
  idle();
  load(X);
  store(X, A);
}

auto SPC700::instructionSTAIndirectXIncrement() -> void {
  idle();
  idle();
  store(X++, A);
}

auto SPC700::instructionSTP() -> void {
  r.stp = true;
  while(r.stp && !synchronizing()) {
    idle();
    idle();
  }
}

auto SPC700::instructionSTWDirectPage() -> void {
  uint8 direct = fetch();
  load(direct);
  store(direct++, A);
  store(direct++, Y);
}

auto SPC700::instructionTRBAbsolute() -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  uint8 data = read(absolute);
  ZF = (A - data) == 0;
  NF = (A - data) & 0x80;
  read(absolute);
  write(absolute, data & ~A);
}

auto SPC700::instructionTSBAbsolute() -> void {
  uint16 absolute = fetch();
  absolute |= fetch() << 8;
  uint8 data = read(absolute);
  ZF = (A - data) == 0;
  NF = (A - data) & 0x80;
  read(absolute);
  write(absolute, data | A);
}

auto SPC700::instructionWAI() -> void {
  r.wai = true;
  while(r.wai && !synchronizing()) {
    idle();
    idle();
  }
}

auto SPC700::instructionXCN() -> void {
  idle();
  idle();
  idle();
  idle();
  A = A >> 4 | A << 4;
  ZF = A == 0;
  NF = A & 0x80;
}
