--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--