All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.pawelkrol.CPU6502.Operation.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.github.pawelkrol.CPU6502

import com.typesafe.scalalogging.StrictLogging

import Status._

abstract class Operation(memory: Memory, register: Register) {

  /** Cycle count for the current instruction */
  var cycleCount = 0

  protected def get_val_from_addr(addr: Short) = memory.get_val_from_addr(addr)

  protected def get_val_from_zp(addr: Short) = memory.get_val_from_zp(addr)

  private def get_addr_ABS = get_val_from_addr((register.PC + 1).toShort)

  private def get_addr_ABSX = get_addr_ABS + register.XR()

  private def get_addr_ABSY = get_addr_ABS + register.YR()

  private def get_addr_ZP = memory.read(register.PC + 1)

  private def get_addr_ZPX = (get_addr_ZP + register.XR()) & 0xff

  private def get_addr_ZPY = (get_addr_ZP + register.YR()) & 0xff

  private def get_addr_INDX = get_val_from_zp(get_addr_ZPX)

  private def get_addr_INDY = (get_val_from_zp(get_addr_ZP) + register.YR()).toShort

  private def get_arg_IMM = memory.read(register.PC + 1)

  private def get_arg_ZP = memory.read(get_addr_ZP)

  private def get_arg_ZPX = memory.read(get_addr_ZPX)

  private def get_arg_ZPY = memory.read(get_addr_ZPY)

  private def get_arg_ABS = memory.read(get_addr_ABS)

  private def get_arg_ABSX = memory.read(get_addr_ABSX)

  private def get_arg_ABSY = memory.read(get_addr_ABS + register.YR())

  private def get_arg_INDX = memory.read(get_addr_INDX)

  private def get_arg_INDY = memory.read(get_addr_INDY)

  private def get_arg_REL = memory.read(register.PC + 1)

  /** [$09] ORA #$FF */
  /** [$29] AND #$FF */
  /** [$49] EOR #$FF */
  private def opImmediate(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_IMM)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$05] ORA $FF */
  /** [$25] AND $FF */
  /** [$45] EOR $FF */
  private def opZeroPage(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_ZP)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$15] ORA $FF */
  /** [$35] AND $FF */
  /** [$55] EOR $FF */
  private def opZeroPageX(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_ZPX)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$0d] ORA $FFFF */
  /** [$2d] AND $FFFF */
  /** [$4d] EOR $FFFF */
  private def opAbsolute(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_ABS)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$1d] ORA $FFFF,X */
  /** [$3d] AND $FFFF,X */
  /** [$5d] EOR $FFFF,X */
  private def opAbsoluteX(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_ABSX)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
    addPageCrossPenalty(register.XR())
  }

  /** [$19] ORA $FFFF,Y */
  /** [$39] AND $FFFF,Y */
  /** [$59] EOR $FFFF,Y */
  private def opAbsoluteY(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_ABSY)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
    addPageCrossPenalty(register.YR())
  }

  /** [$01] ORA ($FF,X) */
  /** [$21] AND ($FF,X) */
  /** [$41] EOR ($FF,X) */
  private def opIndirectX(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_INDX)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$11] ORA ($FF),Y */
  /** [$31] AND ($FF),Y */
  /** [$51] EOR ($FF),Y */
  private def opIndirectY(op: (ByteVal, ByteVal) => ByteVal) {
    register.AC = op(register.AC, get_arg_INDY)
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$61] ADC ($FF,X) */
  /** [$65] ADC $FF */
  /** [$69] ADC #$FF */
  /** [$6d] ADC $FFFF */
  /** [$75] ADC $FF,X */
  private def opADC(term: ByteVal) {
    val carry = if (register.getStatusFlag(CF)) 0x01 else 0x00
    val old = register.AC()
    val rhs = term & 0xff

    if (register.getStatusFlag(DF)) {
      val lo = (old & 0x0f) + (rhs & 0x0f) + carry
      val loDec = if (lo >= 0x0a) ((lo + 0x06) & 0x0f) + 0x10 else lo
      val sum = (old & 0xf0) + (rhs & 0xf0) + loDec
      val res = (old & 0xf0).toByte + (rhs & 0xf0).toByte + loDec.toByte
      register.testStatusFlag(ZF, (old + rhs + carry).toShort)
      register.testStatusFlag(SF, sum.toShort)
      val decSum = if (sum >= 0xa0) sum + 0x60 else sum
      register.testStatusFlag(CF, decSum.toShort)
      register.setStatusFlag(OF, (res < -128) || (res > 127))
      register.AC = decSum
    }
    else {
      val sum = old + rhs + carry
      register.testStatusFlag(ZF, sum.toShort)
      register.testStatusFlag(SF, sum.toShort)
      register.testStatusFlag(CF, sum.toShort)
      register.setStatusFlag(OF, (((old ^ rhs) & 0x80) != 0x80) && (((old ^ sum) & 0x80) == 0x80))
      register.AC = sum
    }
  }

  /** [$7d] ADC $FFFF,X */
  private def opAbsoluteXADC {
    opADC(get_arg_ABSX)
    addPageCrossPenalty(register.XR())
  }

  /** [$79] ADC $FFFF,Y */
  private def opAbsoluteYADC {
    opADC(get_arg_ABSY)
    addPageCrossPenalty(register.YR())
  }

  /** [$71] ADC ($FF),Y */
  private def opIndirectYADC {
    opADC(get_arg_INDY)
    addPageCrossPenalty(get_val_from_zp(get_addr_ZP), register.YR())
  }

  /** [$10] BPL *-1 */
  /** [$30] BMI *-1 */
  /** [$50] BVC *-1 */
  /** [$70] BVS *-1 */
  /** [$90] BCC *-1 */
  /** [$b0] BCS *-1 */
  /** [$d0] BNE *-1 */
  /** [$f0] BEQ *-1 */
  private def opRelative(expr: Boolean) {
    if (expr) {
      cycleCount += 1
      val offset = get_arg_REL.value
      val oldPCH = Util.word2Nibbles(register.PC)._2
      register.advancePC(offset)
      val newPCH = Util.word2Nibbles((register.PC + 2).toShort)._2
      if (oldPCH != newPCH)
        cycleCount += 1
    }
  }

  private def opCompare(value: ByteVal, term: ByteVal) {
    val result = value() - term()
    register.testStatusFlag(ZF, (result & 0xff).toShort)
    register.testStatusFlag(SF, result.toShort)
    register.setStatusFlag(CF, result >= 0x00)
  }

  /** [$c1] CMP ($FF,X) */
  /** [$c5] CMP $FF */
  /** [$c9] CMP #$FF */
  /** [$cd] CMP $FFFF */
  /** [$d5] CMP $FF,X */
  private def opCMP(term: ByteVal) {
    opCompare(register.AC, term)
  }

  /** [$dd] CMP $FFFF,X */
  private def opAbsoluteXCMP {
    opCMP(get_arg_ABSX)
    addPageCrossPenalty(register.XR())
  }

  /** [$d9] CMP $FFFF,Y */
  private def opAbsoluteYCMP {
    opCMP(get_arg_ABSY)
    addPageCrossPenalty(register.YR())
  }

  /** [$d1] CMP ($FF),Y */
  private def opIndirectYCMP {
    opCMP(get_arg_INDY)
    addPageCrossPenalty(get_val_from_zp(get_addr_ZP), register.YR())
  }

  /** [$e0] CPX #$FF */
  /** [$e4] CPX $FF */
  /** [$ec] CPX $FFFF */
  private def opCPX(term: ByteVal) {
    opCompare(register.XR, term)
  }

  /** [$c0] CPY #$FF */
  /** [$c4] CPY $FF */
  /** [$cc] CPY $FFFF */
  private def opCPY(term: ByteVal) {
    opCompare(register.YR, term)
  }

  /** [$e1] SBC ($FF,X) */
  /** [$e5] SBC $FF */
  /** [$e9] SBC #$FF */
  /** [$ed] SBC $FFFF */
  /** [$f5] SBC $FF,X */
  private def opSBC(term: ByteVal) {
    if (register.getStatusFlag(DF)) {
      val carry = if (register.getStatusFlag(CF)) 0x00 else 0x01
      val old = register.AC()
      val src = term()
      val tmp = old - src - carry
      val tmp_a = (old & 0x0f) - (src & 0x0f) - carry
      val tmp_b = if ((tmp_a & 0x10) == 0x10)
        ((tmp_a - 0x06) & 0x0f) | ((old & 0xf0) - (src & 0xf0) - 0x10)
      else
        (tmp_a & 0x0f) | ((old & 0xf0) - (src & 0xf0))
      val tmp_c = if ((tmp_b & 0x100) == 0x100) tmp_b - 0x60 else tmp_b
      register.testStatusFlag(SF, tmp_c.toShort)
      register.setStatusFlag(CF, (tmp & 0x1ff) < 0x100)
      register.setStatusFlag(ZF, (tmp & 0xff) == 0x00)
      register.setStatusFlag(OF, (((old ^ tmp) & 0x80) == 0x80) && (((old ^ src) & 0x80) == 0x80))
      register.AC = tmp_c
    }
    else {
      val carry = if (register.getStatusFlag(CF)) 0x01 else 0x00
      val rhs = term & 0xff
      val old = register.AC()
      val diff = old - rhs - (carry ^ 0x01)
      register.testStatusFlag(ZF, diff.toShort)
      register.testStatusFlag(SF, diff.toShort)
      register.setStatusFlag(CF, diff <= 0xff && diff >= 0x00)
      register.setStatusFlag(OF, ((old ^ rhs).toByte & (old ^ diff).toByte & 0x80) == 0x80)
      register.AC = diff
    }
  }

  /** [$fd] SBC $FFFF,X */
  private def opAbsoluteXSBC {
    opSBC(get_arg_ABSX)
    addPageCrossPenalty(register.XR())
  }

  /** [$f9] SBC $FFFF,Y */
  private def opAbsoluteYSBC {
    opSBC(get_arg_ABSY)
    addPageCrossPenalty(register.YR())
  }

  /** [$f1] SBC ($FF),Y */
  private def opIndirectYSBC {
    opSBC(get_arg_INDY)
    addPageCrossPenalty(get_val_from_zp(get_addr_ZP), register.YR())
  }

  private def pushWordToStack(word: Short) {
    val (pcl, pch) = Util.word2Nibbles(word)
    register.push(memory, pch)
    register.push(memory, pcl)
  }

  protected def interrupt(pc: Short) {
    pushWordToStack(pc)
    register.push(memory, register.status)
    register.setStatusFlag(IF, true)
  }

  /** [$00] BRK */
  private def opBRK {
    register.setStatusFlag(BF, true)
    interrupt((register.PC + 2).toShort)
    register.setPC(get_val_from_addr(0xfffe.toShort))
    register.advancePC(-OpCode_BRK_IMP.memSize) // additionally compensate for an advancement in "eval"
  }

  /** [$20] JSR $FFFF */
  private def opJSR {
    val address = get_addr_ABS
    pushWordToStack((register.PC + 2).toShort)
    register.setPC(address)
    register.advancePC(-OpCode_JSR_ABS.memSize) // additionally compensate for an advancement in "eval"
  }

  /** [$4c] JMP $FFFF */
  private def opAbsoluteJMP {
    register.setPC(get_addr_ABS)
    register.advancePC(-OpCode_JMP_ABS.memSize) // additionally compensate for an advancement in "eval"
  }

  /** [$6c] JMP ($FFFF) */
  private def opIndirectJMP {
    val pc = register.PC
    val lo = get_addr_ABS
    val hi = (lo & 0xff00) | ((lo + 1) & 0x00ff)
    register.setPC(get_arg_ABS() | (memory.read(hi)() << 8))
    if (hi.toShort != (lo + 1).toShort)
      Application.logInfo("6502 indirect jump bug triggered at $%04x, indirect address = $%04x".format(pc, lo))
    register.advancePC(-OpCode_JMP_IND.memSize) // additionally compensate for an advancement in "eval"
  }

  private def opASL(value: ByteVal): ByteVal = {
    val valueRolled = value() << 1
    register.testStatusFlag(ZF, valueRolled.toShort)
    register.testStatusFlag(SF, valueRolled.toShort)
    register.setStatusFlag(CF, (valueRolled & 0x0100) == 0x0100)
    valueRolled
  }

  private def opLSR(value: ByteVal): ByteVal = {
    register.setStatusFlag(CF, (value() & 0x01) == 0x01)
    val valueRolled = value() >> 1
    register.testStatusFlag(ZF, valueRolled.toShort)
    register.testStatusFlag(SF, valueRolled.toShort)
    valueRolled
  }

  private def opROL(value: ByteVal): ByteVal = {
    val carry = if (register.getStatusFlag(CF)) 0x01 else 0x00
    val valueRolled = (value() << 1) | carry
    register.testStatusFlag(ZF, valueRolled.toShort)
    register.testStatusFlag(SF, valueRolled.toShort)
    register.testStatusFlag(CF, valueRolled.toShort)
    valueRolled
  }

  private def opROR(value: ByteVal): ByteVal = {
    val carry = if (register.getStatusFlag(CF)) 0x0100 else 0x0000
    val valueToRoll = value() | carry
    register.setStatusFlag(CF, (valueToRoll & 0x0001) > 0)
    val valueRolled = valueToRoll >> 1
    register.testStatusFlag(ZF, valueRolled.toShort)
    register.testStatusFlag(SF, valueRolled.toShort)
    valueRolled
  }

  /** [$06] ASL $FF */
  /** [$26] ROL $FF */
  /** [$46] LSR $FF */
  /** [$66] ROR $FF */
  private def opZeroPage(op: (ByteVal) => ByteVal) {
    memory.write(get_addr_ZP, op(get_arg_ZP))
  }

  /** [$0a] ASL A */
  /** [$2a] ROL A */
  /** [$4a] LSR A */
  /** [$6a] ROR A */
  private def opAccumulator(op: (ByteVal) => ByteVal) {
    register.AC = op(register.AC)
  }

  /** [$0e] ASL $FFFF */
  /** [$2e] ROL $FFFF */
  /** [$4e] LSR $FFFF */
  /** [$6e] ROR $FFFF */
  private def opAbsolute(op: (ByteVal) => ByteVal) {
    memory.write(get_addr_ABS, op(get_arg_ABS))
  }

  /** [$16] ASL $FF,X */
  /** [$36] ROL $FF,X */
  /** [$56] LSR $FF,X */
  /** [$76] ROR $FF,X */
  private def opZeroPageX(op: (ByteVal) => ByteVal) {
    memory.write(get_addr_ZPX, op(get_arg_ZPX))
  }

  /** [$1e] ASL $FFFF,X */
  /** [$3e] ROL $FFFF,X */
  /** [$5e] LSR $FFFF,X */
  /** [$7e] ROR $FFFF,X */
  private def opAbsoluteX(op: (ByteVal) => ByteVal) {
    memory.write(get_addr_ABSX, op(get_arg_ABSX))
  }

  /** [$08] PHP */
  private def opPHP {
    register.push(memory, register.status & ~BF.srBits)
  }

  /** [$28] PLP */
  private def opPLP {
    register.status = register.pop(memory) & ~BF.srBits
  }

  /** [$48] PHA */
  private def opPHA {
    register.push(memory, register.AC)
  }

  /** [$68] PLA */
  private def opPLA {
    val valuePopped = register.pop(memory)
    register.AC = valuePopped
    register.testStatusFlag(ZF, valuePopped.toShort)
    register.testStatusFlag(SF, valuePopped.toShort)
  }

  /** [$18] CLC */
  /** [$58] CLI */
  /** [$b8] CLV */
  /** [$d8] CLD */
  private def opClearFlag(flag: Flag) {
    register.setStatusFlag(flag, false)
  }

  /** [$38] SEC */
  /** [$78] SEI */
  /** [$f8] SED */
  private def opSetFlag(flag: Flag) {
    register.setStatusFlag(flag, true)
  }

  /** [$24] BIT $FF */
  /** [$2c] BIT $FFFF */
  private def opBIT(bits: ByteVal) {
    register.setStatusFlag(SF, (bits & 0x80) == 0x80)
    register.setStatusFlag(OF, (bits & 0x40) == 0x40)
    register.setStatusFlag(ZF, (bits & register.AC) == 0x00)
  }

  private def pullProgramCounterFromStack {
    val pcl = register.pop(memory)
    val pch = register.pop(memory)
    register.PC = Util.nibbles2Word(pcl, pch)
  }

  /** [$40] RTI */
  private def opRTI {
    register.status = register.pop(memory)
    pullProgramCounterFromStack
    register.advancePC(-OpCode_RTI.memSize) // additionally compensate for an advancement in "eval"
  }

  /** [$60] RTS */
  private def opRTS {
    pullProgramCounterFromStack
    register.advancePC(-OpCode_RTS.memSize + 0x01) // additionally compensate for an advancement in "eval"
  }

  /** [$81] STA ($FF,X) */
  /** [$85] STA $FF */
  /** [$8d] STA $FFFF */
  /** [$91] STA ($FF),Y */
  /** [$95] STA $FF,X */
  /** [$99] STA $FFFF,Y */
  /** [$9d] STA $FFFF,X */
  private def opSTA(address: => Short) {
    memory.write(address, register.AC)
  }

  /** [$84] STY $FF */
  /** [$8c] STY $FFFF */
  /** [$94] STY $FF,X */
  private def opSTY(address: => Short) {
    memory.write(address, register.YR)
  }

  /** [$86] STX $FF */
  /** [$8e] STX $FFFF */
  /** [$96] STX $FF,Y */
  private def opSTX(address: => Short) {
    memory.write(address, register.XR)
  }

  /** [$88] DEY */
  private def opDEY {
    register.YR -= 1
    register.testStatusFlag(ZF, register.YR)
    register.testStatusFlag(SF, register.YR)
  }

  /** [$ca] DEX */
  private def opDEX {
    register.XR -= 1
    register.testStatusFlag(ZF, register.XR)
    register.testStatusFlag(SF, register.XR)
  }

  /** [$c8] INY */
  private def opINY {
    register.YR += 1
    register.testStatusFlag(ZF, register.YR)
    register.testStatusFlag(SF, register.YR)
  }

  /** [$e8] INX */
  private def opINX {
    register.XR += 1
    register.testStatusFlag(ZF, register.XR)
    register.testStatusFlag(SF, register.XR)
  }

  /** [$8a] TXA */
  private def opTXA {
    register.AC = register.XR
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$98] TYA */
  private def opTYA {
    register.AC = register.YR
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$9a] TXS */
  private def opTXS {
    register.SP = register.XR
  }

  /** [$a8] TAY */
  private def opTAY {
    register.YR = register.AC
    register.testStatusFlag(ZF, register.YR)
    register.testStatusFlag(SF, register.YR)
  }

  /** [$aa] TAX */
  private def opTAX {
    register.XR = register.AC
    register.testStatusFlag(ZF, register.XR)
    register.testStatusFlag(SF, register.XR)
  }

  /** [$ba] TSX */
  private def opTSX {
    register.XR = register.SP
    register.testStatusFlag(ZF, register.XR)
    register.testStatusFlag(SF, register.XR)
  }

  /** [$a0] LDY #$FF */
  /** [$a4] LDY $FF */
  /** [$ac] LDY $FFFF */
  /** [$b4] LDY $FF,X */
  private def opLDY(value: ByteVal) {
    register.YR = value
    register.testStatusFlag(ZF, register.YR)
    register.testStatusFlag(SF, register.YR)
  }

  /** [$bc] LDY $FFFF,X */
  private def opAbsoluteXLDY {
    opLDY(get_arg_ABSX)
    addPageCrossPenalty(register.XR())
  }

  /** [$a2] LDX #$FF */
  /** [$a6] LDX $FF */
  /** [$ae] LDX $FFFF */
  /** [$b6] LDX $FF,Y */
  private def opLDX(value: ByteVal) {
    register.XR = value
    register.testStatusFlag(ZF, register.XR)
    register.testStatusFlag(SF, register.XR)
  }

  /** [$be] LDX $FFFF,Y */
  private def opAbsoluteYLDX {
    opLDX(get_arg_ABSY)
    addPageCrossPenalty(register.YR())
  }

  /** [$a1] LDA ($FF,X) */
  /** [$a5] LDA $FF */
  /** [$a9] LDA #$FF */
  /** [$ad] LDA $FFFF */
  /** [$b5] LDA $FF,Y */
  private def opLDA(value: ByteVal) {
    register.AC = value
    register.testStatusFlag(ZF, register.AC)
    register.testStatusFlag(SF, register.AC)
  }

  /** [$bd] LDA $FFFF,X */
  private def opAbsoluteXLDA {
    opLDA(get_arg_ABSX)
    addPageCrossPenalty(register.XR())
  }

  /** [$b9] LDA $FFFF,Y */
  private def opAbsoluteYLDA {
    opLDA(get_arg_ABSY)
    addPageCrossPenalty(register.YR())
  }

  /** [$b1] LDA ($FF),Y */
  private def opIndirectYLDA {
    opLDA(get_arg_INDY)
    addPageCrossPenalty(get_val_from_zp(get_addr_ZP), register.YR())
  }

  /** [$c6] DEC $FF */
  /** [$ce] DEC $FFFF */
  /** [$d6] DEC $FF,X */
  /** [$de] DEC $FFFF,X */
  private def opDEC(address: Short) {
    val decrementedValue: ByteVal = memory.read(address) - 1
    memory.write(address, decrementedValue)
    register.testStatusFlag(ZF, decrementedValue)
    register.testStatusFlag(SF, decrementedValue)
  }

  /** [$e6] INC $FF */
  /** [$ee] INC $FFFF */
  /** [$f6] INC $FF,X */
  /** [$fe] INC $FFFF,X */
  private def opINC(address: Short) {
    val incrementedValue: ByteVal = memory.read(address) + 1
    memory.write(address, incrementedValue)
    register.testStatusFlag(ZF, incrementedValue)
    register.testStatusFlag(SF, incrementedValue)
  }

  private def addPageCrossPenalty(address: Short, offset: Int) {
    if (page_cross(address, offset))
      cycleCount += 1
  }

  private def addPageCrossPenalty(offset: Int) {
    addPageCrossPenalty(get_addr_ABS, offset)
  }

  def eval(opCode: OpCode) {
    cycleCount = 0

    opCode match {
      case OpCode_BRK_IMP =>  // $00
        opBRK
      case OpCode_ORA_INDX => // $01
        opIndirectX(_ | _)
      case OpCode_ORA_ZP =>   // $05
        opZeroPage(_ | _)
      case OpCode_ASL_ZP =>   // $06
        opZeroPage(opASL(_))
      case OpCode_PHP =>      // $08
        opPHP
      case OpCode_ORA_IMM =>  // $09
        opImmediate(_ | _)
      case OpCode_ASL_AC =>   // $0a
        opAccumulator(opASL(_))
      case OpCode_ORA_ABS =>  // $0d
        opAbsolute(_ | _)
      case OpCode_ASL_ABS =>  // $0e
        opAbsolute(opASL(_))
      case OpCode_BPL_REL =>  // $10
        opRelative(!register.getStatusFlag(SF))
      case OpCode_ORA_INDY => // $11
        opIndirectY(_ | _)
      case OpCode_ORA_ZPX =>  // $15
        opZeroPageX(_ | _)
      case OpCode_ASL_ZPX =>  // $16
        opZeroPageX(opASL(_))
      case OpCode_CLC =>      // $18
        opClearFlag(CF)
      case OpCode_ORA_ABSY => // $19
        opAbsoluteY(_ | _)
      case OpCode_ORA_ABSX => // $1d
        opAbsoluteX(_ | _)
      case OpCode_ASL_ABSX => // $1e
        opAbsoluteX(opASL(_))
      case OpCode_JSR_ABS =>  // $20
        opJSR
      case OpCode_AND_INDX => // $21
        opIndirectX(_ & _)
      case OpCode_BIT_ZP =>   // $24
        opBIT(get_arg_ZP)
      case OpCode_AND_ZP =>   // $25
        opZeroPage(_ & _)
      case OpCode_ROL_ZP =>   // $26
        opZeroPage(opROL(_))
      case OpCode_PLP =>      // $28
        opPLP
      case OpCode_AND_IMM =>  // $29
        opImmediate(_ & _)
      case OpCode_ROL_AC =>   // $2a
        opAccumulator(opROL(_))
      case OpCode_BIT_ABS =>  // $2c
        opBIT(get_arg_ABS)
      case OpCode_AND_ABS =>  // $2d
        opAbsolute(_ & _)
      case OpCode_ROL_ABS =>  // $2e
        opAbsolute(opROL(_))
      case OpCode_BMI_REL =>  // $30
        opRelative(register.getStatusFlag(SF))
      case OpCode_AND_INDY => // $31
        opIndirectY(_ & _)
      case OpCode_AND_ZPX =>  // $35
        opZeroPageX(_ & _)
      case OpCode_ROL_ZPX =>  // $36
        opZeroPageX(opROL(_))
      case OpCode_SEC =>      // $38
        opSetFlag(CF)
      case OpCode_AND_ABSY => // $39
        opAbsoluteY(_ & _)
      case OpCode_AND_ABSX => // $3d
        opAbsoluteX(_ & _)
      case OpCode_ROL_ABSX => // $3e
        opAbsoluteX(opROL(_))
      case OpCode_RTI =>      // $40
        opRTI
      case OpCode_EOR_INDX => // $41
        opIndirectX(_ ^ _)
      case OpCode_EOR_ZP =>   // $45
        opZeroPage(_ ^ _)
      case OpCode_LSR_ZP =>   // $46
        opZeroPage(opLSR(_))
      case OpCode_PHA =>      // $48
        opPHA
      case OpCode_EOR_IMM =>  // $49
        opImmediate(_ ^ _)
      case OpCode_LSR_AC =>   // $4a
        opAccumulator(opLSR(_))
      case OpCode_JMP_ABS =>  // $4c
        opAbsoluteJMP
      case OpCode_EOR_ABS =>  // $4d
        opAbsolute(_ ^ _)
      case OpCode_LSR_ABS =>  // $4e
        opAbsolute(opLSR(_))
      case OpCode_BVC_REL =>  // $50
        opRelative(!register.getStatusFlag(OF))
      case OpCode_EOR_INDY => // $51
        opIndirectY(_ ^ _)
      case OpCode_EOR_ZPX =>  // $55
        opZeroPageX(_ ^ _)
      case OpCode_LSR_ZPX =>  // $56
        opZeroPageX(opLSR(_))
      case OpCode_CLI =>      // $58
        opClearFlag(IF)
      case OpCode_EOR_ABSY => // $59
        opAbsoluteY(_ ^ _)
      case OpCode_EOR_ABSX => // $5d
        opAbsoluteX(_ ^ _)
      case OpCode_LSR_ABSX => // $5e
        opAbsoluteX(opLSR(_))
      case OpCode_RTS =>      // $60
        opRTS
      case OpCode_ADC_INDX => // $61
        opADC(get_arg_INDX)
      case OpCode_ADC_ZP =>   // $65
        opADC(get_arg_ZP)
      case OpCode_ROR_ZP =>   // $66
        opZeroPage(opROR(_))
      case OpCode_PLA =>      // $68
        opPLA
      case OpCode_ADC_IMM =>  // $69
        opADC(get_arg_IMM)
      case OpCode_ROR_AC =>   // $6a
        opAccumulator(opROR(_))
      case OpCode_JMP_IND =>  // $6c
        opIndirectJMP
      case OpCode_ADC_ABS =>  // $6d
        opADC(get_arg_ABS)
      case OpCode_ROR_ABS =>  // $6e
        opAbsolute(opROR(_))
      case OpCode_BVS_REL =>  // $70
        opRelative(register.getStatusFlag(OF))
      case OpCode_ADC_INDY => // $71
        opIndirectYADC
      case OpCode_ADC_ZPX =>  // $75
        opADC(get_arg_ZPX)
      case OpCode_ROR_ZPX =>  // $76
        opZeroPageX(opROR(_))
      case OpCode_SEI =>      // $78
        opSetFlag(IF)
      case OpCode_ADC_ABSY => // $79
        opAbsoluteYADC
      case OpCode_ADC_ABSX => // $7d
        opAbsoluteXADC
      case OpCode_ROR_ABSX => // $7e
        opAbsoluteX(opROR(_))
      case OpCode_STA_INDX => // $81
        opSTA(get_addr_INDX)
      case OpCode_STY_ZP =>   // $84
        opSTY(get_addr_ZP)
      case OpCode_STA_ZP =>   // $85
        opSTA(get_addr_ZP)
      case OpCode_STX_ZP =>   // $86
        opSTX(get_addr_ZP)
      case OpCode_DEY =>      // $88
        opDEY
      case OpCode_TXA =>      // $8a
        opTXA
      case OpCode_STY_ABS =>  // $8c
        opSTY(get_addr_ABS)
      case OpCode_STA_ABS =>  // $8d
        opSTA(get_addr_ABS)
      case OpCode_STX_ABS =>  // $8e
        opSTX(get_addr_ABS)
      case OpCode_BCC_REL =>  // $90
        opRelative(!register.getStatusFlag(CF))
      case OpCode_STA_INDY => // $91
        opSTA(get_addr_INDY)
      case OpCode_STY_ZPX =>  // $94
        opSTY(get_addr_ZPX)
      case OpCode_STA_ZPX =>  // $95
        opSTA(get_addr_ZPX)
      case OpCode_STX_ZPY =>  // $96
        opSTX(get_addr_ZPY)
      case OpCode_TYA =>      // $98
        opTYA
      case OpCode_STA_ABSY => // $99
        opSTA(get_addr_ABSY.toShort)
      case OpCode_TXS =>      // $9a
        opTXS
      case OpCode_STA_ABSX => // $9d
        opSTA(get_addr_ABSX.toShort)
      case OpCode_LDY_IMM =>  // $a0
        opLDY(get_arg_IMM)
      case OpCode_LDA_INDX => // $a1
        opLDA(get_arg_INDX)
      case OpCode_LDX_IMM =>  // $a2
        opLDX(get_arg_IMM)
      case OpCode_LDY_ZP =>   // $a4
        opLDY(get_arg_ZP)
      case OpCode_LDA_ZP =>   // $a5
        opLDA(get_arg_ZP)
      case OpCode_LDX_ZP =>   // $a6
        opLDX(get_arg_ZP)
      case OpCode_TAY =>      // $a8
        opTAY
      case OpCode_LDA_IMM =>  // $a9
        opLDA(get_arg_IMM)
      case OpCode_TAX =>      // $aa
        opTAX
      case OpCode_LDY_ABS =>  // $ac
        opLDY(get_arg_ABS)
      case OpCode_LDA_ABS =>  // $ab
        opLDA(get_arg_ABS)
      case OpCode_LDX_ABS =>  // $ae
        opLDX(get_arg_ABS)
      case OpCode_BCS_REL =>  // $b0
        opRelative(register.getStatusFlag(CF))
      case OpCode_LDA_INDY => // $b1
        opIndirectYLDA
      case OpCode_LDY_ZPX =>  // $b4
        opLDY(get_arg_ZPX)
      case OpCode_LDA_ZPX =>  // $b5
        opLDA(get_arg_ZPX)
      case OpCode_LDX_ZPY =>  // $b6
        opLDX(get_arg_ZPY)
      case OpCode_CLV =>      // $b8
        opClearFlag(OF)
      case OpCode_LDA_ABSY => // $b9
        opAbsoluteYLDA
      case OpCode_TSX =>      // $ba
        opTSX
      case OpCode_LDY_ABSX => // $bc
        opAbsoluteXLDY
      case OpCode_LDA_ABSX => // $bd
        opAbsoluteXLDA
      case OpCode_LDX_ABSY => // $be
        opAbsoluteYLDX
      case OpCode_CPY_IMM =>  // $c0
        opCPY(get_arg_IMM)
      case OpCode_CMP_INDX => // $c1
        opCMP(get_arg_INDX)
      case OpCode_CPY_ZP =>   // $c4
        opCPY(get_arg_ZP)
      case OpCode_CMP_ZP =>   // $c5
        opCMP(get_arg_ZP)
      case OpCode_DEC_ZP =>   // $c6
        opDEC(get_addr_ZP)
      case OpCode_INY =>      // $c8
        opINY
      case OpCode_CMP_IMM =>  // $c9
        opCMP(get_arg_IMM)
      case OpCode_DEX =>      // $ca
        opDEX
      case OpCode_CPY_ABS =>  // $cc
        opCPY(get_arg_ABS)
      case OpCode_CMP_ABS =>  // $cd
        opCMP(get_arg_ABS)
      case OpCode_DEC_ABS =>  // $ce
        opDEC(get_addr_ABS)
      case OpCode_BNE_REL =>  // $d0
        opRelative(!register.getStatusFlag(ZF))
      case OpCode_CMP_INDY => // $d1
        opIndirectYCMP
      case OpCode_CMP_ZPX =>  // $d5
        opCMP(get_arg_ZPX)
      case OpCode_DEC_ZPX =>  // $d6
        opDEC(get_addr_ZPX)
      case OpCode_CLD =>      // $d8
        opClearFlag(DF)
      case OpCode_CMP_ABSY => // $d9
        opAbsoluteYCMP
      case OpCode_CMP_ABSX => // $dd
        opAbsoluteXCMP
      case OpCode_DEC_ABSX => // $de
        opDEC(get_addr_ABSX.toShort)
      case OpCode_CPX_IMM =>  // $e0
        opCPX(get_arg_IMM)
      case OpCode_SBC_INDX => // $e1
        opSBC(get_arg_INDX)
      case OpCode_CPX_ZP =>   // $e4
        opCPX(get_arg_ZP)
      case OpCode_SBC_ZP =>   // $e5
        opSBC(get_arg_ZP)
      case OpCode_INC_ZP =>   // $e6
        opINC(get_addr_ZP)
      case OpCode_INX =>      // $e8
        opINX
      case OpCode_SBC_IMM =>  // $e9
        opSBC(get_arg_IMM)
      case OpCode_NOP =>      // $ea
      case OpCode_CPX_ABS =>  // $ec
        opCPX(get_arg_ABS)
      case OpCode_SBC_ABS =>  // $ed
        opSBC(get_arg_ABS)
      case OpCode_INC_ABS =>  // $ee
        opINC(get_addr_ABS)
      case OpCode_BEQ_REL =>  // $f0
        opRelative(register.getStatusFlag(ZF))
      case OpCode_SBC_INDY => // $f1
        opIndirectYSBC
      case OpCode_SBC_ZPX =>  // $f5
        opSBC(get_arg_ZPX)
      case OpCode_INC_ZPX =>  // $f6
        opINC(get_addr_ZPX)
      case OpCode_SED =>      // $f8
        opSetFlag(DF)
      case OpCode_SBC_ABSY => // $f9
        opAbsoluteYSBC
      case OpCode_SBC_ABSX => // $fd
        opAbsoluteXSBC
      case OpCode_INC_ABSX => // $fe
        opINC(get_addr_ABSX.toShort)
    }
    cycleCount += opCode.cycles
    register.advancePC(opCode.memSize)
  }

  /** Test for page cross */
  private def page_cross(address: Short, offset: ByteVal) = (address & 0xff) + offset >= 0x100
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy