Daniel Martin
7/31/2006 11:56:00 AM
> # Here's my entry for the Chip8 quiz question - I expect to be far
> # away from net access when the time limit expires. I implemented
Well who knew they'd have easily accessible wireless within spitting
distance of Rocky Mountain National Park?
Here's my solution again, but this time without > marks that prevent
people from trying it, and hopefully without mangled lines:
______________
#! /usr/bin/env ruby
# Here's my entry for the Chip8 quiz question - I expect to be far
# away from net access when the time limit expires. I implemented
# a few of the operations not in the quiz because they were very
# easy to add, and I thought that they would be useful in writing
# some of my own Chip-8 programs, but I ran out of time before vacation
# and so never wrote my Chip-8 division program. Doing a full simulator
# of Chip-8 with ASCII-art graphics sounds a bit interesting, and I
# might do it at some point, depending on my desire for serious retro
# computing.
#
# My program runs the file given as the first argument, or, if given
# no arguments runs the test program from the quiz. The second and
# subsequent arguments should be of the form <reg>=<value>, with
# both <reg> and <value> written in hex.
#
# Only the registers whose values are changed (including those initialized
# from the command line) are dumped when the program exits.
class Chip8
def initialize(memblock)
@ip = 0
@memblock = memblock
@registers = Array.new(16){nil}
end
# These are the pieces of the current operation
def op; (@memblock[@ip]||0)/16; end
def x; (@memblock[@ip]||0)%16; end
def y; (@memblock[@ip+1]||0)/16; end
def k; (@memblock[@ip+1]||0); end
def n; 256*x + k; end
def op2;(@memblock[@ip+1]||0)%16; end
# Some convenient accessor functions
def [](r); @registers[r]; end
def []=(r,v); @registers[r]=v; end
def vx; self[x]; end
def vy; self[y]; end
# Used to implement both addition and subtraction
def add(a,b)
ret1 = a + b
ret = ret1 & 0xff
self[0xF] = (ret1!=ret)?1:0
ret
end
def do_instruction
case op
when 0
return false if [x,k] == [0,0]
raise "bad/unknown opcode #{@memblock[@ip,2].unpack('H4')}"
when 1; @ip=n; return true
when 3; @ip += 2 if vx == k
when 4; @ip += 2 if vx != k
when 5; @ip += 2 if vx == vy
when 6; self[x] = k
when 7; self[x] = add(vx,k)
when 8
case op2
when 0; self[x] = vy
when 1; self[x] |= vy
when 2; self[x] &= vy
when 3; self[x] ^= vy
when 4; self[x] = add(vx,vy)
when 5; self[x] = add(vx,256-vy)
when 7; self[x] = add(256-vx,vy)
when 6; self[0xF] = vx & 1; self[x] >>= 1
when 0xE; self[0xF] = vx >> 7; self[x] <<= 1
else
raise "bad opcode #{@memblock[@ip,2].unpack('H4')}"
end
when 12; self[x] = k & rand(256)
else
raise "bad opcode #{@memblock[@ip,2].unpack('H4')}"
end
@ip += 2
return true
end
def run
1 while do_instruction
end
def dump
contents = @registers.map {|a| a ? [a].pack('C').unpack("B8") : nil}
@registers.each_index { |i|
printf("V%X:%s (%d)\n",i,contents[i],@registers[i]) if contents[i]
}
end
def Chip8.run(memblock, init={})
a = Chip8.new(memblock)
init.each{|h,k| a[h] = k}
a.run
a.dump
end
end
if __FILE__ == $0
if ARGV.empty?
# test program from the quiz spec
Chip8.run(%w{ 61 77 62 45 71 01 83 20 81 21 81 22
82 33 81 34 82 35 81 06 83 27 83 0e 64 ff c4 11
32 bb 10 00 00 00 }.map{|b| [b].pack("H2")}.join)
else
init = {}
buff = ""
if ARGV[1] and ARGV[1] =~ /^\d+$/
buff = "\0" * ARGV[1].to_i
end
File.open(ARGV[0], "rb") {|f| buff += f.read}
ARGV[1..-1].each {|s| r,v = s.split(/=/); init[r.hex]=v.hex}
Chip8.run(buff, init)
end
end
__END__