Instantly share code, notes, and snippets.

Embed
What would you like to do?
CHIP-8 Ruby Emülatörü
class CHIP8
attr_accessor :memory, :gpio, :display_buffer, :stack,
:key_inputs, :fonts, :opcode, :index, :pc,
:delay_timer, :sound_timer, :should_draw,
:instructions, :vx, :vy
def initialize
@memory = [0] * 4096 # 4096 Baytlık belleğimiz
@gpio = [0] * 16 # 16 tane genel amaçlı register'ı tutan bir dizi
@display_buffer = [0] * 2048 # 64 * 32 = 2048 piksellik ekranımız
@stack = [] # Alt yordamları tutmamızı sağlayacak olan yığın
@key_inputs = [0] * 16 # 16 girdili klavyemiz
@fonts = [ # Her biri 5 bayt olarak şekilde, 16 tane fontumuz
0xF0, 0x90, 0x90, 0x90, 0xF0, # 0
0x20, 0x60, 0x20, 0x20, 0x70, # 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, # 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, # 3
0x90, 0x90, 0xF0, 0x10, 0x10, # 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, # 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, # 6
0xF0, 0x10, 0x20, 0x40, 0x40, # 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, # 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, # 9
0xF0, 0x90, 0xF0, 0x90, 0x90, # A
0xE0, 0x90, 0xE0, 0x90, 0xE0, # B
0xF0, 0x80, 0x80, 0x80, 0xF0, # C
0xE0, 0x90, 0x90, 0x90, 0xE0, # D
0xF0, 0x80, 0xF0, 0x80, 0xF0, # E
0xF0, 0x80, 0xF0, 0x80, 0x80 # F
]
@opcode = 0 # Çalıştırılacak olan işlemin kodu
@index = 0 # Index register'ımız
@pc = 0x200 # Program sayacımız. ilk 512 baytı es geçiyoruz.
@should_draw = false # Görüntü çizilmesi için bir boolean değişkeni
@vx, @vy = 0, 0 # Register'ları tutan değişkenler
@delay_timer = 0
@sound_timer = 0
# Fontları belleğe yüklüyoruz
(0...80).each { |i| @memory[i] = @fonts[i] }
# Ve en baba kısım: Talimatları bir Hash'e koyarak
# haritalama yapıyoruz.
@instructions = {
0x0000 => :_0ZZZ,
0x00e0 => :_0ZZ0,
0x00ee => :_0ZZE,
0x1000 => :_1ZZZ,
0x2000 => :_2ZZZ,
0x3000 => :_3ZZZ,
0x4000 => :_4ZZZ,
0x5000 => :_5ZZZ,
0x6000 => :_6ZZZ,
0x7000 => :_7ZZZ,
0x8000 => :_8ZZZ,
0x8FF0 => :_8ZZ0,
0x8FF1 => :_8ZZ1,
0x8FF2 => :_8ZZ2,
0x8FF3 => :_8ZZ3,
0x8FF4 => :_8ZZ4,
0x8FF5 => :_8ZZ5,
0x8FF6 => :_8ZZ6,
0x8FF7 => :_8ZZ7,
0x8FFE => :_8ZZE,
0x9000 => :_9ZZZ,
0xA000 => :_AZZZ,
0xB000 => :_BZZZ,
0xC000 => :_CZZZ,
0xD000 => :_DZZZ,
0xE000 => :_EZZZ,
0xE00E => :_EZZE,
0xE001 => :_EZZ1,
0xF000 => :_FZZZ,
0xF007 => :_FZ07,
0xF00A => :_FZ0A,
0xF015 => :_FZ15,
0xF018 => :_FZ18,
0xF01E => :_FZ1E,
0xF029 => :_FZ29,
0xF033 => :_FZ33,
0xF055 => :_FZ55,
0xF065 => :_FZ65
}
end
def load_rom(path)
rom_file = File.open(path, 'rb') { |i| i.read }
rom_file.split('').each_with_index do |byte, index|
@memory[index + 0x200] = byte.ord
end
end
def cycle
@opcode = (@memory[@pc] << 8) | @memory[@pc + 1] # İşlem kodunu alıyoruz
@pc += 2 # Program sayacını arttırıyoruz
@vx = (@opcode & 0x0F00) >> 8 # İşlem kodunu maskeleyip
@vy = (@opcode & 0x00F0) >> 4 # register'ları değişkenlere atıyoruz
ext_op = @opcode & 0xF000 # İşlem kodunu maskeleyip, talimatı alıyoruz
puts "Running instruction: #{@instructions[ext_op].to_s[1..-1]}" rescue nil
run_instruction ext_op # Ve talimatı çalıştırıyoruz
@delay_timer -= 1 if @delay_timer > 0 # Gecikme ve ses sayaçlarını
@sound_timer -= 1 if @sound_timer > 0 # azaltıyoruz
end
def test
while true
sleep 0.1
cycle
end
end
private
def run_instruction(ext_op)
method(@instructions[ext_op]).call rescue puts "Unknown instruction: #{ext_op.to_s(16)}"
end
def get_key
(0...16).each { |i| return i if @key_inputs[i] == 1 }
return -1
end
def _0ZZZ
ext_op = @opcode & 0xF0FF
run_instruction ext_op
end
def _0ZZ0
@display_buffer = [0] * 64 * 32
@should_draw = true
end
def _0ZZE
@pc = @stack.pop
end
def _1ZZZ
@pc = @opcode & 0x0FFF
end
def _2ZZZ
@stack.push @pc
@pc = @opcode & 0x0FFF
end
def _3ZZZ
@pc += 2 if @gpio[@vx] == (@opcode & 0xFF)
end
def _4ZZZ
@pc += 2 if @gpio[@vx] != (@opcode & 0xFF)
end
def _5ZZZ
@pc += 2 if @gpio[@vx] == @gpio[@vy]
end
def _6ZZZ
@gpio[@vx] = @opcode & 0xFF
end
def _7ZZZ
@gpio[@vx] += (@opcode % 0xFF)
end
def _8ZZZ
ext_op = @opcode & 0xF00F
ext_op += 0xFF0
run_instruction ext_op
end
def _8ZZ0
@gpio[@vx] = @gpio[@vy] & 0xFF
end
def _8ZZ1
@gpio[@vx] |= @gpio[@vy]
@gpio[@vx] &= 0xFF
end
def _8ZZ2
@gpio[@vx] &= @gpio[@vy]
@gpio[@vx] &= 0xFF
end
def _8ZZ3
@gpio[@vx] ^= @gpio[@vy]
@gpio[@vx] &= 0xFF
end
def _8ZZ4
@gpio[0xF] = @gpio[@vx] + @gpio[@vy] > 0xFF ? 1 : 0
@gpio[@vx] += @gpio[@vy]
@gpio[@vx] &= 0xFF
end
def _8ZZ5
@gpio[0xF] = @gpio[@vx] < @gpio[@vy] ? 0 : 1
@gpio[@vx] -= @gpio[@vy]
@gpio[@vx] &= 0xFF
end
def _8ZZ6
@gpio[0xF] = @gpio[@vx] & 0x0001
@gpio[@vx] >>= 1
end
def _8ZZ7
@gpio[0xF] = @gpio[@vx] > @gpio[@vy] ? 0 : 1
@gpio[@vx] = @gpio[@vy] - @gpio[@vx]
end
def _8ZZE
@gpio[0xF] = (@gpio[@vx] & 0xF0) >> 7
@gpio[@vx] <<= 1
@gpio[@vx] &= 0xFF
end
def _9ZZZ
@pc += 2 if @gpio[@vx] != @gpio[@vy]
end
def _AZZZ
@index = @opcode & 0x0FFF
end
def _BZZZ
@pc = (@opcode & 0x0FFF) + @gpio[0]
end
def _CZZZ
r = (rand * 0xFF).to_i
@gpio[@vx] = r & (@opcode & 0xFF)
@gpio[@vx] &= 0xFF
end
def _DZZZ
@gpio[0xF] = 0
x = @gpio[@vx] & 0xFF
y = @gpio[@vy] & 0xFF
height = @opcode & 0x000F
row = 0
while row < height
current_row = @memory[row + @index]
pixel_offset = 0
while pixel_offset < 8
loc = x + pixel_offset + ((y + row) * 64)
pixel_offset += 1
next if (y + row) >= 32 || (x + pixel_offset - 1) >= 64
mask = 1 << 8 - pixel_offset
current_pixel = (current_row & mask) >> (8 - pixel_offset)
@display_buffer[loc] ^= current_pixel
@gpio[0xF] = @display_buffer[loc] == 0 ? 1 : 0
end
row += 1
end
@should_draw = true
end
def _EZZZ
ext_op = @opcode & 0xF00F
run_instruction ext_op
end
def _EZZE
key = @gpio[@vx] & 0xF
@pc += 2 if @key_inputs[key] == 1
end
def _EZZ1
key = @gpio[@vx] & 0xF
@pc += 2 if @key_inputs[key] == 1
end
def _FZZZ
ext_op = @opcode & 0xF0FF
run_instruction ext_op
end
def _FZ07
@gpio[@vx] = @delay_timer
end
def _FZ0A
ret = get_key
if ret >= 0
@gpio[@vx] = ret
else
@pc -= 2
end
end
def _FZ15
@delay_timer = @gpio[@vx]
end
def _FZ18
@sound_timer = @gpio[@vx]
end
def _FZ1E
@index += @gpio[@vx]
if @index > 0x0FFF
@gpio[0xF] = 1
@index &= 0xFFF
else
@gpio[0xF] = 0
end
end
def _FZ29
@index = (5 * (@gpio[@vx])) & 0x0FFF
end
def _FZ33
@memory[@index] = @gpio[@vx] / 100
@memory[@index + 1] = (@gpio[@vx] % 100) / 10
@memory[@index + 2] = @gpio[@vx] % 10
end
def _FZ55
(0..@vx).each { |i| @memory[@index + i] = @gpio[i] }
@index += @vx + 1
end
def _FZ65
(0..@vx).each { |i| @gpio[i] = @memory[@index + 1] }
@index += @vx + 1
end
end
emulator = CHIP8.new
emulator.load_rom('PONG')
emulator.test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment