--oyUTqETQ0mS9luUI
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

My solution for managing binary numbers was to make a new class
BitArray.

It seems like I'm repeating myself and being awfully wordy in the main
loop.

Mitchell Koch

--oyUTqETQ0mS9luUI
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="chip8.rb"

#!/usr/bin/env ruby
# Chip-8 emulator

class Chip8
  # Load program
  def initialize(prog)
    # split program into 16 bit opcodes (4 hex digits)
    @prog  rog.unpack('S'*(prog.size/2)).inject([]) do |prog,i|
      prog << BitArray.new(i,16)
    end
    # initialize registers V0..VF to random 8 bit values
    @regs  0x0..0xF).inject([]){|m,i| m<<BitArray.rand(8)}
  end

  # Execute program
  def run
    addr  
    while opcode  prog[addr]
      case opcode.hex
      when /0000/
        break
      when /1.../
        addr  pcode.digits(1..3).val
        next
      when /3.../
        if @regs[opcode.digits(1).val] opcode.digits(2..3)
          addr + 
          next
        end
      when /6.../
        @regs[opcode.digits(1).val]  pcode.digits(2..3)
      when /7.../
        @regs[opcode.digits(1).val] + pcode.digits(2..3)
      when /8..0/
        @regs[opcode.digits(1).val]  regs[opcode.digits(2).val]
      when /8..1/
        @regs[opcode.digits(1).val] | regs[opcode.digits(2).val]
      when /8..2/
        @regs[opcode.digits(1).val] & regs[opcode.digits(2).val]
      when /8..3/
        @regs[opcode.digits(1).val] ^ regs[opcode.digits(2).val]
      when /8..4/
        @regs[opcode.digits(1).val], @regs[0xF]              @regs[opcode.digits(1).val] + @regs[opcode.digits(2).val]
      when /8..5/
        @regs[opcode.digits(1).val], @regs[0xF]              @regs[opcode.digits(1).val] - @regs[opcode.digits(2).val]
      when /8..6/
        @regs[opcode.digits(1).val], @regs[0xF]  regs[opcode.digits(1).val].rshift
      when /8..7/
        @regs[opcode.digits(1).val], @regs[0xF]              @regs[opcode.digits(2).val] - @regs[opcode.digits(1).val]
      when /8..e/
        @regs[opcode.digits(1).val], @regs[0xF]  regs[opcode.digits(1).val].lshift
      when /c.../
        @regs[opcode.digits(1).val]  itArray.rand(8) & opcode.digits(2..3)
      end
      addr + 
    end
    self
  end

  # Dump registers
  def to_s
    s  '
    @regs.each_with_index do |reg, idx|
      s << "V%X: %08b\n" % [idx, reg.val]
    end
    s
  end
end

# Houses numbers represented by a fixed number of bits
class BitArray
  attr_reader :val, :size

  def initialize(val, size)
    @val  al
    val_size  printf('%b', val).size
    if val_size > size
      raise "Cannot fit #{val} into #{size} bits."
    else
      @size  ize
    end
  end

  # Initialize a bit array with a random value < ize
  def BitArray.rand(size)
    val  ernel.rand(eval('0b'+'1'*size))
    BitArray.new(val, size)
  end

  def hex
    "%x" % @val
  end

  # Make a new BitArray based on the digits of another
  def digits(digits)
    digits  digits] unless digits.is_a? Enumerable
    hexnum   igits.inject(''){|m,i| m << hex[i].chr}
    numval   val('0x'+hexnum)
    numsize  exnum.size * 4
    BitArray.new(numval, numsize)
  end

  def &(other)
    BitArray.new((val & other.val), size)
  end

  def |(other)
    BitArray.new((val | other.val), size)
  end

  def ^(other)
    BitArray.new((val ^ other.val), size)
  end

  def +(other)
    newval  al + other.val
    binval  %b' % newval
    if binval.size > size
      [ BitArray.new(eval('0b'+binval[binval.size-size..-1]), size), 
        BitArray.new(1, 8) ]
    else
      BitArray.new(newval, size)
    end
  end

  def -(other)
    if val > ther.val
      [ BitArray.new(val-other.val, size), BitArray.new(1, 8) ]
    else
      binval  %b' % val
      borrowed  val('0b1' + '0'*(size-binval.size) + binval)
      [ BitArray.new(borrowed-other.val, size), BitArray.new(0, 8) ]
    end
  end

  def rshift
    if val % 2 0
      [BitArray.new(val/2,size), BitArray.new(0,8)]
    else
      [BitArray.new(val/2,size), BitArray.new(1,8)]
    end
  end

  def lshift
    binval  %b' % val
    binval  0'*(size-binval.size) + binval
    [ BitArray.new(eval('0b' + binval[1..-1] + '0'), size),
      BitArray.new(binval[0].chr.to_i, 8) ]
  end

  def other)
    (val other.val) && (size other.size)
  end
end

if __FILE__ $0
  ARGV.each {|f| puts Chip8.new(File.read(f)).run}
end

--oyUTqETQ0mS9luUI--