Constants
ADD = define_opcode(32767.to_u16, "add", 45) do |chip8, instruction|
x = (instruction & 3840) >> 8
k = instruction & 255
chip8.v[x] = ((chip8.v[x].to_u16 + k) & 255).to_u8
end
ADDI = define_opcode(65310.to_u16, "addi", 86) do |chip8, instruction|
x = (instruction & 3840) >> 8
chip8.i = chip8.i &+ chip8.v[x]
end
ADDV = define_opcode(36852.to_u16, "addv", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
result = chip8.v[x].to_u16 + chip8.v[y]
chip8.v[x] = (result & 255).to_u8
chip8.v[15] = (result > 255 ? 1 : 0).to_u8
end
Set vX to result of vX + vY, setting vF to 0 or 1 if overflow or not even if X == F
AND = define_opcode(36850.to_u16, "and", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
__temp_c0aebda1_3 = x
__temp_c0aebda1_4 = chip8.v
__temp_c0aebda1_4[__temp_c0aebda1_3] = __temp_c0aebda1_4[__temp_c0aebda1_3] & chip8.v[y]
chip8.v[15] = 0
end
Set vX to result of vX & vY
CALL = define_opcode(12287.to_u16, "call", 105) do |chip8, instruction|
chip8.stack.push(chip8.pc)
chip8.pc = instruction & 4095
Log.debug do "
Jumped to #{chip8.pc.to_s(16)}" end
end
Push current address onto stack and call subroutine
CLS = define_opcode(224.to_u16, "cls", 109) do |chip8, instruction|
(0...chip8.pixels.size).each do |row|
(0...chip8.pixels[row].size).each do |col|
chip8.pixels[row][col] = 0
end
end
chip8.io.clear_pixels
end
COPY = define_opcode(36848.to_u16, "copy", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
chip8.v[x] = chip8.v[y]
end
Copy value from register vY into vX
DRAW = define_opcode(57343.to_u16, "draw", 8333) do |chip8, instruction|
x = (3840 & instruction) >> 8
y = (240 & instruction) >> 4
n = 15 & instruction
dirty_pixels = [] of
Tuple(
Int32,
Int32)
has_unset_pixel = false
start_x = (chip8.v[x] %
IO::CHIP8_DISPLAY_WIDTH).to_i32
start_y = (chip8.v[y] %
IO::CHIP8_DISPLAY_HEIGHT).to_i32
(0...n).each do |row|
pixmap = chip8.memory[chip8.i + row]
(0...8).each do |col|
pixel = (pixmap >> ((8 - col) - 1)) & 1
adjusted_col = start_x + col
adjusted_row = start_y + row
if adjusted_row >= chip8.pixels.size || adjusted_col >= chip8.pixels[0].size
Log.warn do "
Attempted to write out of bounds: (#{adjusted_col}, #{adjusted_row})" end
break
end
if (pixel == 1) && (chip8.pixels[adjusted_row][adjusted_col] == 1)
has_unset_pixel = true
end
__temp_c0aebda1_7 = adjusted_col
__temp_c0aebda1_8 = chip8.pixels[adjusted_row]
__temp_c0aebda1_8[__temp_c0aebda1_7] = __temp_c0aebda1_8[__temp_c0aebda1_7] ^ pixel
dirty_pixels << {adjusted_col, adjusted_row}
end
end
if has_unset_pixel
chip8.v[15] = 1
end
dirty_pixels.each do |col, row|
chip8.io.set_pixel(col, row, chip8.pixels[row][col].zero? ?
IO::PixelState::Off :
IO::PixelState::On)
end
end
Draw sprite at position [vX, vY] using sprite data from location I[0..N]
JMP = define_opcode(8191.to_u16, "jmp", 105) do |chip8, instruction|
chip8.pc = instruction & 4095
Log.debug do "
Jumped to #{chip8.pc.to_s(16)}" end
end
JMPNAS = define_opcode(4095.to_u16, "jmpnas", 105) do |chip8, instruction|
end
Jump to native assembler subroutine
JMPREL = define_opcode(49151.to_u16, "jmprel", 105) do |chip8, instruction|
n = instruction & 4095
chip8.pc = chip8.v[0].to_u16 + n
end
LSHIFT = define_opcode(36862.to_u16, "lshift", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
shifted_out = (chip8.v[y] & 128) >> 7
chip8.v[x] = chip8.v[y] << 1
chip8.v[15] = shifted_out
end
Set vX to result of vY << 1, setting vF to shifted out bit even if X == F
MOV = define_opcode(65381.to_u16, "mov", 605) do |chip8, instruction|
x = (instruction & 3840) >> 8
(0..x).each do |i|
if (chip8.i + i) >= chip8.memory.size
break
end
chip8.v[i] = chip8.memory[chip8.i + i]
end
end
MOVM = define_opcode(65365.to_u16, "movm", 605) do |chip8, instruction|
x = (instruction & 3840) >> 8
(0..x).each do |i|
if (chip8.i + i) >= chip8.memory.size
break
end
chip8.memory[chip8.i + i] = chip8.v[i]
end
end
MOVMBCD = define_opcode(65331.to_u16, "movmbcd", 927) do |chip8, instruction|
x = (instruction & 3840) >> 8
rem = chip8.v[x]
if chip8.i < chip8.memory.size
chip8.memory[chip8.i], rem = rem.divmod(100)
end
if (chip8.i + 1) < chip8.memory.size
chip8.memory[chip8.i + 1], rem = rem.divmod(10)
end
if (chip8.i + 2) < chip8.memory.size
chip8.memory[chip8.i + 2] = rem % 10
end
end
OR = define_opcode(36849.to_u16, "or", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
__temp_c0aebda1_1 = x
__temp_c0aebda1_2 = chip8.v
__temp_c0aebda1_2[__temp_c0aebda1_1] = __temp_c0aebda1_2[__temp_c0aebda1_1] | chip8.v[y]
chip8.v[15] = 0
end
Set vX to result of vX | vY
RAND = define_opcode(53247.to_u16, "rand", 164) do |chip8, instruction|
x = (instruction & 3840) >> 8
n = instruction & 255
chip8.v[x] = ((
Random.new.rand(255)) & n).to_u8
end
Set vX to random value AND-ed with k (byte)
READKEY = define_opcode(65290.to_u16, "readkey", 0) do |chip8, instruction|
if chip8.hault
else
chip8.pc = chip8.pc - 2
chip8.hault = true
chip8.custom_flags[:key_read] = false
end
key = chip8.io.read_key
if key && key[:state].released?
x = (3840 & instruction) >> 8
chip8.v[x] = key[:key]
chip8.custom_flags[:key_read] = true
chip8.sound_timer = 1
elsif chip8.custom_flags[:key_read] && (chip8.sound_timer == 0)
chip8.hault = false
chip8.pc = chip8.pc + 2
chip8.custom_flags.delete(:key_read)
end
end
Read next key pressed, write it to vX (clear screen in Megachip mode)
RET = define_opcode(238.to_u16, "ret", 105) do |chip8, instruction|
chip8.pc = chip8.stack.pop
end
RSHIFT = define_opcode(36854.to_u16, "rshift", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
shifted_out = chip8.v[y] & 1
chip8.v[x] = chip8.v[y] >> 1
chip8.v[15] = shifted_out
end
Set vX to result of vY >> 1, setting vF to shifted out bit even if X == F
RSUBV = define_opcode(36855.to_u16, "rsub", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
is_underflowing = chip8.v[y] < chip8.v[x]
chip8.v[x] = chip8.v[y] &- chip8.v[x]
chip8.v[15] = (is_underflowing ? 0 : 1).to_u8
end
Set vX to result of vY - vX, setting vF to 0 or 1 if underflow or not even if X == F
SET = define_opcode(28671.to_u16, "set", 27) do |chip8, instruction|
x = (instruction & 3840) >> 8
chip8.v[x] = (instruction & 255).to_u8
end
SETDV = define_opcode(65301.to_u16, "setdv", 45) do |chip8, instruction|
x = (instruction & 3840) >> 8
chip8.delay_timer = chip8.v[x]
end
SETI = define_opcode(45055.to_u16, "seti", 55) do |chip8, instruction|
chip8.i = 4095.to_u16 & instruction
end
SETIV = define_opcode(65321.to_u16, "setiv", 91) do |chip8, instruction|
x = (instruction & 3840) >> 8
chip8.i = 15.to_u16 & chip8.v[x]
end
Copy vX's low nibble to I
SETSV = define_opcode(65304.to_u16, "setsv", 45) do |chip8, instruction|
x = (instruction & 3840) >> 8
chip8.sound_timer = chip8.v[x]
end
Set sound timer from vX (beep as long as sound timer > 0)
SETVD = define_opcode(65287.to_u16, "setvd", 45) do |chip8, instruction|
x = (instruction & 3840) >> 8
chip8.v[x] = chip8.delay_timer
end
Copy delay timer value to vX
SKIPIFEQ = define_opcode(16383.to_u16, "skipifeq", 55) do |chip8, instruction|
x = (instruction & 3840) >> 8
k = instruction & 255
if chip8.v[x] == k
chip8.pc = chip8.pc + 2
end
end
Skip next opcode if vX register is equal to k
SKIPIFEQV = define_opcode(24560.to_u16, "skipifeqv", 73) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
if chip8.v[x] == chip8.v[y]
chip8.pc = chip8.pc + 2
end
end
Skip next opcode if register vX == vY
SKIPIFKEY = define_opcode(61342.to_u16, "skipifkey", 73) do |chip8, instruction|
x = (3840 & instruction) >> 8
chip8.io.read_key.try do |key|
if (key[:key] == (chip8.v[x] & 15)) && key[:state].pressed?
chip8.pc = chip8.pc + 2
end
end
end
Skip next opcode if key pressed matches lower 4 bits of vX
SKIPIFNEQ = define_opcode(20479.to_u16, "skipifneq", 55) do |chip8, instruction|
x = (instruction & 3840) >> 8
k = instruction & 255
if chip8.v[x] != k
chip8.pc = chip8.pc + 2
end
end
Skip next opcode if vX is not equal to k
SKIPIFNEQV = define_opcode(40944.to_u16, "skipifneqv", 73) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
if chip8.v[x] != chip8.v[y]
chip8.pc = chip8.pc + 2
end
end
Skip next opcode if vX != vY
SKIPIFNKEY = define_opcode(61345.to_u16, "skipifnkey", 73) do |chip8, instruction|
x = (3840 & instruction) >> 8
key = chip8.io.read_key
if key.nil? || ((key[:key] != (chip8.v[x] & 15)) && key[:state].pressed?)
chip8.pc = chip8.pc + 2
end
end
Skip next opcode if lower 4 bits of vX don't match key pressed
SUBV = define_opcode(36853.to_u16, "sub", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
is_underflowing = chip8.v[x] < chip8.v[y]
chip8.v[x] = chip8.v[x] &- chip8.v[y]
chip8.v[15] = (is_underflowing ? 0 : 1).to_u8
end
Set vX to result of vX - vY, setting vF to 0 or 1 if underflow or not even if X == F
XOR = define_opcode(36851.to_u16, "xor", 200) do |chip8, instruction|
x = (instruction & 3840) >> 8
y = (instruction & 240) >> 4
__temp_c0aebda1_5 = x
__temp_c0aebda1_6 = chip8.v
__temp_c0aebda1_6[__temp_c0aebda1_5] = __temp_c0aebda1_6[__temp_c0aebda1_5] ^ chip8.v[y]
chip8.v[15] = 0
end
Set vX to result of vX ^ vY