Begin forwarded message:
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 =, with
both and 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 ; @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