-- -i,
Here is my solution to the current quiz. It has a few flaws, and it
uses a method call for each statement (which is probably quite slow).
The nice thing is that you can dump a program and view it in an
assembler-like way.
The implementation is quite simple, I don't think i need to explain it
in detail. I hope i haven't screwed up somewhere :)
Tom
-- -Content-Disposition: inline; filename=emu.rb
#!/usr/bin/env ruby
class Emulator
def initialize
@pc #program counter
@prog rray.new #where the read program is stored
@register ¨Âáù®îå÷¨±¶© £ôèå íáãèéîèá±¶ òåçéóôåò end
#read program into memory
def scan(filename)
opcode rray.new
f ile.new(filename)
loop do
opcode.clear
begin
#read 2 byte and split it into 4 bit chunks
2.times {
ch .readchar
opcode << ((ch&0xF0) >> 4)
opcode << (ch&0xF)
}
rescue
f.close
break
end
#determine which opcode we are dealing with, and add it to
#the program
case opcode[0]
when 1
#Jump!
@prog << [:jmp, (opcode[1]<<8)+(opcode[2]<<4)+opcode[3]]
when 3
#skip when equal
@prog << [:skip_eq, opcode[1], (opcode[2]<<4)+opcode[3]]
when 6
#load constant into register
@prog << [:load_c, opcode[1], (opcode[2]<<4)+opcode[3]]
when 7
#add constant to register
@prog << [:add_c, opcode[1], (opcode[2]<<4)+opcode[3]]
when 8
case opcode[3]
when 0
#load value of register in register
@prog << [:load_r, opcode[1], opcode[2]]
#some bitwise operations
when 1
@prog << [:or, opcode[1], opcode[2]]
when 2
@prog << [:and, opcode[1], opcode[2]]
when 3
@prog << [:xor, opcode[1], opcode[2]]
#adding and substracting 2 registers
when 4
@prog << [:add_r, opcode[1], opcode[2]]
when 5
@prog << [:sub_r, opcode[1], opcode[2]]
when 6
@prog << [:shift_r, opcode[1]]
when 7
#same as sub, but vx -vx
@prog << [:sub2_r, opcode[1], opcode[2]]
when 0xE
@prog << [:shift_l, opcode[1]]
end
when 0xC
#set register to random value AND constant
@prog << [:load_rnd_and_c, opcode[1], (opcode[2]<<4)+opcode[3]]
else
#exit when we don't understand an opcode
@prog << [:end]
end
end
end
def run pc l
@pc if pc il #program counter
while @prog[@pc]! end]
#execute instruction
self.send(*@prog[@pc])
#next instruction
@pc+ end
end
#simple debug-method, waits after each instruction for a newline
#and dumps the registers
def step
@pc while @prog[@pc]! end]
self.send(*@prog[@pc])
@pc+ dump
STDIN.readline
end
end
#dump registers
def dump
print "PC: ", @pc, "\n"
1.upto(16) { |i|
print "V%02d: %08b (%03d)"%[i,@register[i], @register[i]], "\n" if @register[i]! l
}
end
#show which program was read
def dump_prog
@prog.each do |instr|
print instr[0].to_s, " "
print instr[1].to_s if instr[1]
print ", ", instr[2].to_s if instr[2]
print "\n"
end
end
#one method for each operation
private
def jmp addr
@pcr/4-1 #each instruction has 4 byte. -1, because @pc gets incremented in main loop
end
def skip_eq rx, c
@pc+if @register[rx]
end
def load_c rx, c
@register[rx]¥¿ end
def load_r rx, ry
@register[rx] egister[ry]
end
def add_c rx, c
tmp egister[rx]+c
@register[rx] p&0xFF
@register[0xF] tmp&0x100)>>8)
end
def and rx, ry
@register[rx] register[rx]&@register[ry])
end
def or rx, ry
@register[rx] register[rx]|@register[ry])
end
def xor rx, ry
@register[rx] register[rx]^@register[ry])
end
def add_r rx, ry
tmp egister[rx]+@register[ry]
@register[rx] p&0xFF
@register[0xF] tmp&0x100)>>8)
end
def sub_r rx, ry
tmp 100+@register[rx]-@register[ry]
@register[rx] p&0xFF
@register[0xF] tmp&0x100)>>8)
end
def sub2_r rx, ry
tmp 100+@register[ry]-@register[rx]
@register[rx] p&0xFF
@register[0xF] tmp&0x100)>>8)
end
def shift_l rx
@register[0xF] egister[rx]&0x80 #is this correct?
@register[rx] @register[rx]<<1)&0xFF)
end
def shift_r rx
@register[0xF] egister[rx]&0x1
@register[rx] register[rx]>>1)
end
def load_rnd_and_c rx, c
tmp
nd(0x100)
@register[rx] p&c
end
end
if ARGV[0] il
STDERR.puts "Usage: #{$0} <program file>\n"
exit 1
end
if !File.exist? ARGV[0]
STDERR.puts "Error: File not found.\n"
exit 1
end
emu mulator.new
emu.scan(ARGV[0])
print "Program read: \n"
print "