class Rnes::Cpu

Constants

INTERRUPTION_VECTOR_FOR_IRQ_OR_BRK
INTERRUPTION_VECTOR_FOR_NMI
INTERRUPTION_VECTOR_FOR_RESET

Attributes

bus[R]

@return [Rnes::CpuBus] bus

registers[R]

@return [Rnes::CpuRegisters]

Public Class Methods

new(bus:, interrupt_line:) click to toggle source

@param [Rnes::CpuBus] bus @param [Rnes::InterruptLine] interrupt_line

# File lib/rnes/cpu.rb, line 22
def initialize(bus:, interrupt_line:)
  @branched = false
  @bus = bus
  @crossed = false
  @interrupt_line = interrupt_line
  @registers = ::Rnes::CpuRegisters.new
end

Public Instance Methods

read_operation() click to toggle source

@note For logging. @return [Rnes::Operation]

# File lib/rnes/cpu.rb, line 32
def read_operation
  address = @registers.program_counter
  operation_code = read(address)
  ::Rnes::Operation.build(operation_code)
end
reset() click to toggle source
# File lib/rnes/cpu.rb, line 38
def reset
  @registers.reset
  @registers.program_counter = read_word(INTERRUPTION_VECTOR_FOR_RESET)
end
step() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 44
def step
  handle_interrupts
  operation = fetch_operation
  operand = fetch_operand_by(operation.addressing_mode)
  execute_operation(
    addressing_mode: operation.addressing_mode,
    operand: operand,
    operation_name: operation.name,
  )
  cycles_count = operation.cycle + (@branched ? 1 : 0) + (@crossed ? 1 : 0)
  @branched = false
  @crossed = false
  cycles_count
end

Private Instance Methods

branch(address) click to toggle source

@param [Integer] address

# File lib/rnes/cpu.rb, line 62
def branch(address)
  @branched = true
  @registers.program_counter = address
end
execute_operation(addressing_mode:, operand:, operation_name:) click to toggle source

@param [Symbol] addressing_mode @param [Integer, nil] operand @param [Symbol] operation_name @return [Integer]

# File lib/rnes/cpu.rb, line 71
def execute_operation(addressing_mode:, operand:, operation_name:)
  case operation_name
  when :ADC
    if addressing_mode == :immediate
      execute_operation_adc_for_immediate_addressing(operand)
    else
      execute_operation_adc_for_non_immediate_addressing(operand)
    end
  when :AND
    if addressing_mode == :immediate
      execute_operation_and_for_immediate_addressing(operand)
    else
      execute_operation_and_for_non_immediate_addressing(operand)
    end
  when :ASL
    if addressing_mode == :accumulator
      execute_operation_asl_for_accoumulator(operand)
    else
      execute_operation_asl_for_non_accumulator(operand)
    end
  when :BCC
    execute_operation_bcc(operand)
  when :BCS
    execute_operation_bcs(operand)
  when :BEQ
    execute_operation_beq(operand)
  when :BIT
    execute_operation_bit(operand)
  when :BMI
    execute_operation_bmi(operand)
  when :BNE
    execute_operation_bne(operand)
  when :BPL
    execute_operation_bpl(operand)
  when :BRK
    execute_operation_brk(operand)
  when :BVC
    execute_operation_bvc(operand)
  when :BVS
    execute_operation_bvs(operand)
  when :CLC
    execute_operation_clc(operand)
  when :CLD
    execute_operation_cld(operand)
  when :CLI
    execute_operation_cli(operand)
  when :CLV
    execute_operation_clv(operand)
  when :CMP
    if addressing_mode == :immediate
      execute_operation_cmp_for_immediate_addressing(operand)
    else
      execute_operation_cmp_for_non_immediate_addressing(operand)
    end
  when :CPX
    if addressing_mode == :immediate
      execute_operation_cpx_for_immediate_addressing(operand)
    else
      execute_operation_cpx_for_non_immediate_addressing(operand)
    end
  when :CPY
    if addressing_mode == :immediate
      execute_operation_cpy_for_immediate_addressing(operand)
    else
      execute_operation_cpy_for_non_immediate_addressing(operand)
    end
  when :DCP
    execute_operation_dcp(operand)
  when :DEC
    execute_operation_dec(operand)
  when :DEX
    execute_operation_dex(operand)
  when :DEY
    execute_operation_dey(operand)
  when :EOR
    if addressing_mode == :immediate
      execute_operation_eor_for_immediate_addressing(operand)
    else
      execute_operation_eor_for_non_immediate_addressing(operand)
    end
  when :INC
    execute_operation_inc(operand)
  when :INX
    execute_operation_inx(operand)
  when :INY
    execute_operation_iny(operand)
  when :ISB
    execute_operation_isb(operand)
  when :JMP
    execute_operation_jmp(operand)
  when :JSR
    execute_operation_jsr(operand)
  when :LAX
    execute_operation_lax(operand)
  when :LDA
    if addressing_mode == :immediate
      execute_operation_lda_for_immediate_addressing(operand)
    else
      execute_operation_lda_for_non_immediate_addressing(operand)
    end
  when :LDX
    if addressing_mode == :immediate
      execute_operation_ldx_for_immediate_addressing(operand)
    else
      execute_operation_ldx_for_non_immediate_addressing(operand)
    end
  when :LDY
    if addressing_mode == :immediate
      execute_operation_ldy_for_immediate_addressing(operand)
    else
      execute_operation_ldy_for_non_immediate_addressing(operand)
    end
  when :LSR
    if addressing_mode == :accumulator
      execute_operation_lsr_for_accumulator(operand)
    else
      execute_operation_lsr_for_non_accumulator(operand)
    end
  when :NOP
    execute_operation_nop(operand)
  when :NOPD
    execute_operation_nopd(operand)
  when :NOPI
    execute_operation_nopi(operand)
  when :ORA
    if addressing_mode == :immediate
      execute_operation_ora_for_immediate_addressing(operand)
    else
      execute_operation_ora_for_non_immediate_addressing(operand)
    end
  when :PHA
    execute_operation_pha(operand)
  when :PHP
    execute_operation_php(operand)
  when :PLA
    execute_operation_pla(operand)
  when :PLP
    execute_operation_plp(operand)
  when :RLA
    execute_operation_rla(operand)
  when :ROL
    if addressing_mode == :accumulator
      execute_operation_rol_for_accumulator(operand)
    else
      execute_operation_rol_for_non_accumulator(operand)
    end
  when :ROR
    if addressing_mode == :accumulator
      execute_operation_ror_for_accumulator(operand)
    else
      execute_operation_ror_for_non_accumulator(operand)
    end
  when :RRA
    execute_operation_rra(operand)
  when :RTI
    execute_operation_rti(operand)
  when :RTS
    execute_operation_rts(operand)
  when :SAX
    execute_operation_sax(operand)
  when :SBC
    if addressing_mode == :immediate
      execute_operation_sbc_for_immediate_addressing(operand)
    else
      execute_operation_sbc_for_non_immediate_addressing(operand)
    end
  when :SEC
    execute_operation_sec(operand)
  when :SED
    execute_operation_sed(operand)
  when :SEI
    execute_operation_sei(operand)
  when :SLO
    execute_operation_slo(operand)
  when :SRE
    execute_operation_sre(operand)
  when :STA
    execute_operation_sta(operand)
  when :STX
    execute_operation_stx(operand)
  when :STY
    execute_operation_sty(operand)
  when :TAX
    execute_operation_tax(operand)
  when :TAY
    execute_operation_tay(operand)
  when :TSX
    execute_operation_tsx(operand)
  when :TXA
    execute_operation_txa(operand)
  when :TXS
    execute_operation_txs(operand)
  when :TYA
    execute_operation_tya(operand)
  else
    raise ::Rnes::Errors::InvalidOperationError, "Invalid operation: #{operation_name}"
  end
end
execute_operation_adc_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 271
def execute_operation_adc_for_immediate_addressing(operand)
  result = operand + @registers.accumulator + @registers.carry_bit
  @registers.carry = result > 0xFF
  @registers.negative = result[7] == 1
  @registers.overflow = (@registers.accumulator ^ operand)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
  @registers.zero = (result & 0xFF).zero?
  @registers.accumulator = result & 0xFF
end
execute_operation_adc_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 281
def execute_operation_adc_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_adc_for_immediate_addressing(operand)
end
execute_operation_and_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 287
def execute_operation_and_for_immediate_addressing(operand)
  result = operand & @registers.accumulator
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_and_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 295
def execute_operation_and_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_and_for_immediate_addressing(operand)
end
execute_operation_asl_for_accoumulator(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 301
def execute_operation_asl_for_accoumulator(_operand)
  value = @registers.accumulator
  result = (value << 1) & 0xFF
  @registers.carry = value[7] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_asl_for_non_accumulator(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 311
def execute_operation_asl_for_non_accumulator(operand)
  value = read(operand)
  result = (value << 1) & 0xFF
  @registers.carry = value[7] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  write(operand, result)
end
execute_operation_bcc(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 321
def execute_operation_bcc(operand)
  unless @registers.carry?
    branch(operand)
  end
end
execute_operation_bcs(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 328
def execute_operation_bcs(operand)
  if @registers.carry?
    branch(operand)
  end
end
execute_operation_beq(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 335
def execute_operation_beq(operand)
  if @registers.zero?
    branch(operand)
  end
end
execute_operation_bit(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 342
def execute_operation_bit(operand)
  result = read(operand)
  @registers.overflow = result[6] == 1
  @registers.negative = result[7] == 1
  @registers.zero = (@registers.accumulator & result).zero?
end
execute_operation_bmi(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 350
def execute_operation_bmi(operand)
  if @registers.negative?
    branch(operand)
  end
end
execute_operation_bne(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 357
def execute_operation_bne(operand)
  unless @registers.zero?
    branch(operand)
  end
end
execute_operation_bpl(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 364
def execute_operation_bpl(operand)
  unless @registers.negative?
    branch(operand)
  end
end
execute_operation_brk(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 371
def execute_operation_brk(_operand)
  @registers.break = true
  @registers.program_counter += 1
  push_word(@registers.program_counter)
  push(@registers.status)
  unless @registers.interrupt?
    @registers.interrupt = true
    @registers.program_counter = read_word(INTERRUPTION_VECTOR_FOR_IRQ_OR_BRK)
  end
  @registers.program_counter -= 1
end
execute_operation_bvc(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 384
def execute_operation_bvc(operand)
  unless @registers.overflow?
    branch(operand)
  end
end
execute_operation_bvs(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 391
def execute_operation_bvs(operand)
  if @registers.overflow?
    branch(operand)
  end
end
execute_operation_clc(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 398
def execute_operation_clc(_operand)
  @registers.carry = false
end
execute_operation_cld(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 403
def execute_operation_cld(_operand)
  @registers.decimal = false
end
execute_operation_cli(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 408
def execute_operation_cli(_operand)
  @registers.interrupt = false
end
execute_operation_clv(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 413
def execute_operation_clv(_operand)
  @registers.overflow = false
end
execute_operation_cmp_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 418
def execute_operation_cmp_for_immediate_addressing(operand)
  result = @registers.accumulator - operand
  @registers.carry = result >= 0
  @registers.negative = result[7] == 1
  @registers.zero = (result & 0xFF).zero?
end
execute_operation_cmp_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 426
def execute_operation_cmp_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_cmp_for_immediate_addressing(operand)
end
execute_operation_cpx_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 432
def execute_operation_cpx_for_immediate_addressing(operand)
  result = @registers.index_x - operand
  @registers.carry = result >= 0
  @registers.negative = result[7] == 1
  @registers.zero = (result & 0xFF).zero?
end
execute_operation_cpx_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 440
def execute_operation_cpx_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_cpx_for_immediate_addressing(operand)
end
execute_operation_cpy_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 446
def execute_operation_cpy_for_immediate_addressing(operand)
  result = @registers.index_y - operand
  @registers.carry = result >= 0
  @registers.negative = result[7] == 1
  @registers.zero = (result & 0xFF).zero?
end
execute_operation_cpy_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 454
def execute_operation_cpy_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_cpy_for_immediate_addressing(operand)
end
execute_operation_dcp(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 460
def execute_operation_dcp(operand)
  result = (read(operand) - 1) & 0xFF
  sub_result = (@registers.accumulator - result) & 0x1FF
  @registers.negative = sub_result[7] == 1
  @registers.zero = sub_result.zero?
  write(operand, result)
end
execute_operation_dec(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 469
def execute_operation_dec(operand)
  result = (read(operand) - 1) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  write(operand, result)
end
execute_operation_dex(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 477
def execute_operation_dex(_operand)
  result = (@registers.index_x - 1) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_x = result
end
execute_operation_dey(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 485
def execute_operation_dey(_operand)
  result = (@registers.index_y - 1) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_y = result
end
execute_operation_eor_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 493
def execute_operation_eor_for_immediate_addressing(operand)
  result = (operand ^ @registers.accumulator) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_eor_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 501
def execute_operation_eor_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_eor_for_immediate_addressing(operand)
end
execute_operation_inc(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 507
def execute_operation_inc(operand)
  result = (read(operand) + 1) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  write(operand, result)
end
execute_operation_inx(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 515
def execute_operation_inx(_operand)
  result = (@registers.index_x + 1) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_x = result
end
execute_operation_iny(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 523
def execute_operation_iny(_operand)
  result = (@registers.index_y + 1) & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_y = result
end
execute_operation_isb(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 531
def execute_operation_isb(operand)
  value = (read(operand) + 1) & 0xFF
  result = (~value & 0xFF) + @registers.accumulator + @registers.carry_bit
  @registers.overflow = (@registers.accumulator ^ value)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
  @registers.carry = result > 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result & 0xFF
  write(operand, value)
end
execute_operation_jmp(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 543
def execute_operation_jmp(operand)
  @registers.program_counter = operand
end
execute_operation_jsr(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 548
def execute_operation_jsr(operand)
  push_word(@registers.program_counter - 1)
  @registers.program_counter = operand
end
execute_operation_lax(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 554
def execute_operation_lax(operand)
  result = read(operand)
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
  @registers.index_x = result
end
execute_operation_lda_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 563
def execute_operation_lda_for_immediate_addressing(operand)
  @registers.negative = operand[7] == 1
  @registers.zero = operand.zero?
  @registers.accumulator = operand
end
execute_operation_lda_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 570
def execute_operation_lda_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_lda_for_immediate_addressing(operand)
end
execute_operation_ldx_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 576
def execute_operation_ldx_for_immediate_addressing(operand)
  @registers.negative = operand[7] == 1
  @registers.zero = operand.zero?
  @registers.index_x = operand
end
execute_operation_ldx_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 583
def execute_operation_ldx_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_ldx_for_immediate_addressing(operand)
end
execute_operation_ldy_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 589
def execute_operation_ldy_for_immediate_addressing(operand)
  @registers.negative = operand[7] == 1
  @registers.zero = operand.zero?
  @registers.index_y = operand
end
execute_operation_ldy_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 596
def execute_operation_ldy_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_ldy_for_immediate_addressing(operand)
end
execute_operation_lsr_for_accumulator(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 602
def execute_operation_lsr_for_accumulator(_operand)
  value = @registers.accumulator
  result = value >> 1
  @registers.carry = value[0] == 1
  @registers.negative = false
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_lsr_for_non_accumulator(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 612
def execute_operation_lsr_for_non_accumulator(operand)
  value = read(operand)
  result = value >> 1
  @registers.carry = value[0] == 1
  @registers.negative = false
  @registers.zero = result.zero?
  write(operand, result)
end
execute_operation_nop(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 622
def execute_operation_nop(operand)
end
execute_operation_nopd(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 626
def execute_operation_nopd(_operand)
  @registers.program_counter += 1
end
execute_operation_nopi(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 631
def execute_operation_nopi(_operand)
  @registers.program_counter += 2
end
execute_operation_ora_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 636
def execute_operation_ora_for_immediate_addressing(operand)
  result = @registers.accumulator | operand
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result & 0xFF
end
execute_operation_ora_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 644
def execute_operation_ora_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_ora_for_immediate_addressing(operand)
end
execute_operation_pha(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 650
def execute_operation_pha(_operand)
  push(@registers.accumulator)
end
execute_operation_php(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 655
def execute_operation_php(_operand)
  push(@registers.status | 0x10)
end
execute_operation_pla(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 660
def execute_operation_pla(_operand)
  result = pop
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_plp(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 668
def execute_operation_plp(_operand)
  @registers.status = pop & 0b11101111
  @registers.reserved = true
end
execute_operation_rla(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 674
def execute_operation_rla(operand)
  value = (read(operand) << 1) + @registers.carry_bit
  result = (value & @registers.accumulator) & 0xFF
  @registers.carry = value[8] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
  write(operand, value & 0xFF)
end
execute_operation_rol_for_accumulator(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 685
def execute_operation_rol_for_accumulator(_operand)
  value = @registers.accumulator
  result = ((value << 1) | @registers.carry_bit) & 0xFF
  @registers.carry = value[7] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_rol_for_non_accumulator(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 695
def execute_operation_rol_for_non_accumulator(operand)
  value = read(operand)
  result = ((value << 1) | @registers.carry_bit) & 0xFF
  @registers.carry = value[7] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  write(operand, result)
end
execute_operation_ror_for_accumulator(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 705
def execute_operation_ror_for_accumulator(_operand)
  value = @registers.accumulator
  result = ((value >> 1) | (@registers.carry_bit << 7))
  @registers.carry = value[0] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_ror_for_non_accumulator(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 715
def execute_operation_ror_for_non_accumulator(operand)
  value = read(operand)
  result = ((value >> 1) | (@registers.carry_bit << 7))
  @registers.carry = value[0] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  write(operand, result)
end
execute_operation_rra(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 725
def execute_operation_rra(operand)
  read_value = read(operand)
  value = (read_value >> 1) | (@registers.carry_bit << 7)
  result = value + @registers.accumulator + read_value[0]
  @registers.carry = result > 0xFF
  @registers.overflow = (@registers.accumulator ^ value)[7].zero? && !(@registers.accumulator ^ result)[7].zero?
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result & 0xFF
  write(operand, value)
end
execute_operation_rti(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 738
def execute_operation_rti(_operand)
  @registers.status = pop
  @registers.program_counter = pop_word
  @registers.reserved = true
end
execute_operation_rts(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 745
def execute_operation_rts(_operand)
  @registers.program_counter = pop_word
  @registers.program_counter += 1
end
execute_operation_sax(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 751
def execute_operation_sax(operand)
  result = @registers.accumulator & @registers.index_x
  write(operand, result)
end
execute_operation_sbc_for_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 757
def execute_operation_sbc_for_immediate_addressing(operand)
  result = @registers.accumulator - operand - 1 + @registers.carry_bit
  @registers.overflow = ((@registers.accumulator ^ result) & 0x80 != 0 && ((@registers.accumulator ^ operand) & 0x80) != 0)
  @registers.carry = result >= 0
  @registers.negative = result[7] == 1
  @registers.zero = (result & 0xFF).zero?
  @registers.accumulator = result & 0xFF
end
execute_operation_sbc_for_non_immediate_addressing(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 767
def execute_operation_sbc_for_non_immediate_addressing(operand)
  operand = read(operand)
  execute_operation_sbc_for_immediate_addressing(operand)
end
execute_operation_sec(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 773
def execute_operation_sec(_operand)
  @registers.carry = true
end
execute_operation_sed(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 778
def execute_operation_sed(_operand)
  @registers.decimal = true
end
execute_operation_sei(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 783
def execute_operation_sei(_operand)
  @registers.interrupt = true
end
execute_operation_slo(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 788
def execute_operation_slo(operand)
  read_value = read(operand)
  value = (read_value << 1) & 0xFF
  result = value | @registers.accumulator
  @registers.carry = read_value[7] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
  write(operand, value)
end
execute_operation_sre(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 800
def execute_operation_sre(operand)
  read_value = read(operand)
  value = read_value >> 1
  result = value ^ @registers.accumulator
  @registers.carry = read_value[0] == 1
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
  write(operand, value)
end
execute_operation_sta(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 812
def execute_operation_sta(operand)
  write(operand, @registers.accumulator)
end
execute_operation_stx(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 817
def execute_operation_stx(operand)
  write(operand, @registers.index_x)
end
execute_operation_sty(operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 822
def execute_operation_sty(operand)
  write(operand, @registers.index_y)
end
execute_operation_tax(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 827
def execute_operation_tax(_operand)
  result = @registers.accumulator
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_x = result
end
execute_operation_tay(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 835
def execute_operation_tay(_operand)
  result = @registers.accumulator
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_y = result
end
execute_operation_tsx(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 843
def execute_operation_tsx(_operand)
  result = @registers.stack_pointer & 0xFF
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.index_x = result
end
execute_operation_txa(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 851
def execute_operation_txa(_operand)
  result = @registers.index_x
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
execute_operation_txs(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 859
def execute_operation_txs(_operand)
  @registers.stack_pointer = @registers.index_x + 0x100
end
execute_operation_tya(_operand) click to toggle source

@param [Integer] operand

# File lib/rnes/cpu.rb, line 864
def execute_operation_tya(_operand)
  result = @registers.index_y
  @registers.negative = result[7] == 1
  @registers.zero = result.zero?
  @registers.accumulator = result
end
fetch() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 872
def fetch
  address = @registers.program_counter
  value = read(address)
  @registers.program_counter += 1
  value
end
fetch_operand_by(addressing_mode) click to toggle source

@param [Symbol] addressing_mode

# File lib/rnes/cpu.rb, line 880
def fetch_operand_by(addressing_mode)
  case addressing_mode
  when :absolute
    fetch_operand_by_absolute_addressing
  when :absolute_x
    fetch_operand_by_absolute_x_addressing
  when :absolute_y
    fetch_operand_by_absolute_y_addressing
  when :accumulator
    fetch_operand_by_accumulator_addressing
  when :immediate
    fetch_operand_by_immediate_addressing
  when :implied
    fetch_operand_by_implied_addressing
  when :indirect_absolute
    fetch_operand_by_indirect_absolute_addressing
  when :post_indexed_indirect
    fetch_operand_by_post_indexed_indirect_addressing
  when :pre_indexed_indirect
    fetch_operand_by_pre_indexed_indirect_addressing
  when :relative
    fetch_operand_by_relative_addressing
  when :zero_page
    fetch_operand_by_zero_page_addressing
  when :zero_page_x
    fetch_operand_by_zero_page_x_addressing
  when :zero_page_y
    fetch_operand_by_zero_page_y_addressing
  else
    raise ::Rnes::Errors::InvalidAddressingModeError, "Invalid addressing mode: #{addressing_mode}"
  end
end
fetch_operand_by_absolute_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 914
def fetch_operand_by_absolute_addressing
  fetch_word
end
fetch_operand_by_absolute_x_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 919
def fetch_operand_by_absolute_x_addressing
  base_address = fetch_word
  @crossed = (base_address & 0xFF00) != ((base_address + @registers.index_x) & 0xFF00)
  (base_address + @registers.index_x) & 0xFFFF
end
fetch_operand_by_absolute_y_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 926
def fetch_operand_by_absolute_y_addressing
  base_address = fetch_word
  @crossed = (base_address & 0xFF00) != ((base_address + @registers.index_y) & 0xFF00)
  (base_address + @registers.index_y) & 0xFFFF
end
fetch_operand_by_accumulator_addressing() click to toggle source

@return [nil]

# File lib/rnes/cpu.rb, line 933
def fetch_operand_by_accumulator_addressing
end
fetch_operand_by_immediate_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 937
def fetch_operand_by_immediate_addressing
  fetch
end
fetch_operand_by_implied_addressing() click to toggle source

@return [nil]

# File lib/rnes/cpu.rb, line 942
def fetch_operand_by_implied_addressing
end
fetch_operand_by_indirect_absolute_addressing() click to toggle source

@note The address must not overlap a page boundary as a bug in the original 6502 prevents it from being fetched properly. @return [Integer]

# File lib/rnes/cpu.rb, line 947
def fetch_operand_by_indirect_absolute_addressing
  address = fetch_word
  low = read(address)
  high = read((address & 0xFF00) | ((address + 1) & 0xFF))
  low + (high << 8)
end
fetch_operand_by_post_indexed_indirect_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 963
def fetch_operand_by_post_indexed_indirect_addressing
  base_address = fetch
  address = (read_word_with_wrap_around(base_address) + @registers.index_y) & 0xFFFF
  @crossed = (address & 0xFF00) != (base_address & 0xFF00)
  address
end
fetch_operand_by_pre_indexed_indirect_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 955
def fetch_operand_by_pre_indexed_indirect_addressing
  base_address = (fetch + @registers.index_x) & 0xFF
  address = read_word_with_wrap_around(base_address)
  @crossed = (address & 0xFF00) != (base_address & 0xFF00)
  address
end
fetch_operand_by_relative_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 971
def fetch_operand_by_relative_addressing
  int8 = fetch
  offset = int8 >= 0x80 ? int8 - 256 : int8
  address = @registers.program_counter + offset
  @crossed = (address & 0xFF00) != (@registers.program_counter & 0xFF00)
  address
end
fetch_operand_by_zero_page_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 980
def fetch_operand_by_zero_page_addressing
  fetch
end
fetch_operand_by_zero_page_x_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 985
def fetch_operand_by_zero_page_x_addressing
  (fetch + @registers.index_x) & 0xFF
end
fetch_operand_by_zero_page_y_addressing() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 990
def fetch_operand_by_zero_page_y_addressing
  (fetch + @registers.index_y) & 0xFF
end
fetch_operation() click to toggle source

@return [Rnes::Operation]

# File lib/rnes/cpu.rb, line 995
def fetch_operation
  operation_code = fetch
  ::Rnes::Operation.build(operation_code)
end
fetch_word() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 1001
def fetch_word
  fetch | (fetch << 8)
end
handle_interrupts() click to toggle source
# File lib/rnes/cpu.rb, line 1005
def handle_interrupts
  if @interrupt_line.nmi
    handle_nmi
  end
  if !@registers.interrupt? && @interrupt_line.irq
    handle_irq
  end
end
handle_irq() click to toggle source
# File lib/rnes/cpu.rb, line 1014
def handle_irq
  @interrupt_line.deassert_irq
  @registers.break = false
  push_word(@registers.program_counter)
  push(@registers.status)
  @registers.interrupt = true
  @registers.program_counter = read_word(INTERRUPTION_VECTOR_FOR_IRQ_OR_BRK)
end
handle_nmi() click to toggle source
# File lib/rnes/cpu.rb, line 1023
def handle_nmi
  @interrupt_line.deassert_nmi
  @registers.break = false
  push_word(@registers.program_counter)
  push(@registers.status)
  @registers.interrupt = true
  @registers.program_counter = read_word(INTERRUPTION_VECTOR_FOR_NMI)
end
pop() click to toggle source

@return [Integer] @raise [Rnes::Errors::StackPointerOverflowError]

# File lib/rnes/cpu.rb, line 1034
def pop
  if @registers.stack_pointer < 0x1FF
    @registers.stack_pointer += 1
  else
    @registers.stack_pointer = 0x100
  end
  read(@registers.stack_pointer)
end
pop_word() click to toggle source

@return [Integer]

# File lib/rnes/cpu.rb, line 1044
def pop_word
  pop | pop << 8
end
push(value) click to toggle source

@param [Integer] value @raise [Rnes::Errors::StackPointerOverflowError]

# File lib/rnes/cpu.rb, line 1050
def push(value)
  write(@registers.stack_pointer, value)
  if @registers.stack_pointer > 0x100
    @registers.stack_pointer -= 1
  else
    @registers.stack_pointer = 0x1FF
  end
end
push_word(value) click to toggle source

@param [Integer] value

# File lib/rnes/cpu.rb, line 1060
def push_word(value)
  push(value >> 8)
  push(value & 0xFF)
end
read(address) click to toggle source

@param [Integer] address @return [Integer]

# File lib/rnes/cpu.rb, line 1067
def read(address)
  @bus.read(address)
end
read_word(address) click to toggle source

@param [Integer] address @return [Integer]

# File lib/rnes/cpu.rb, line 1073
def read_word(address)
  read(address) | read((address + 1) & 0xFFFF) << 8
end
read_word_with_wrap_around(byte) click to toggle source

@param [Integer] byte Unsigned integer from 0x00 to 0xFF.

# File lib/rnes/cpu.rb, line 1078
def read_word_with_wrap_around(byte)
  low = read(byte)
  high = read((byte + 1) & 0xFF)
  low + (high << 8)
end
write(address, value) click to toggle source

@param [Integer] address @param [Integer] value

# File lib/rnes/cpu.rb, line 1086
def write(address, value)
  @bus.write(address, value)
end