Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local computer = require("computer")
- local event = require("event")
- local gpu = component.gpu
- local keyboard = require("keyboard")
- local bit32 = require("bit32")
- local filesystem = require("filesystem")
- gpu.setResolution(160, 50)
- local chip8 = {
- memory = {},
- V = {},
- I = 0,
- PC = 0x200,
- stack = {},
- SP = 0,
- delay_timer = 0,
- sound_timer = 0,
- keys = {},
- draw_flag = false,
- opcode = 0,
- clock_speed = 700,
- waiting_for_key = false,
- key_register = 0,
- shift_quirk = true,
- jump_quirk = false,
- load_store_quirk = true,
- }
- local width, height = 64, 32
- local scale_x, scale_y = 2, 1
- local buffer = {}
- local frame_buffer = {}
- local font = {
- 0xF0, 0x90, 0x90, 0x90, 0xF0,
- 0x20, 0x60, 0x20, 0x20, 0x70,
- 0xF0, 0x10, 0xF0, 0x80, 0xF0,
- 0xF0, 0x10, 0xF0, 0x10, 0xF0,
- 0x90, 0x90, 0xF0, 0x10, 0x10,
- 0xF0, 0x80, 0xF0, 0x10, 0xF0,
- 0xF0, 0x80, 0xF0, 0x90, 0xF0,
- 0xF0, 0x10, 0x20, 0x40, 0x40,
- 0xF0, 0x90, 0xF0, 0x90, 0xF0,
- 0xF0, 0x90, 0xF0, 0x10, 0xF0,
- 0xF0, 0x90, 0xF0, 0x90, 0x90,
- 0xE0, 0x90, 0xE0, 0x90, 0xE0,
- 0xF0, 0x80, 0x80, 0x80, 0xF0,
- 0xE0, 0x90, 0x90, 0x90, 0xE0,
- 0xF0, 0x80, 0xF0, 0x80, 0xF0,
- 0xF0, 0x80, 0xF0, 0x80, 0x80
- }
- function listCH8Files()
- local files = {}
- for file in filesystem.list("/home/") do
- if file:match("%.ch8$") then
- table.insert(files, file)
- end
- end
- return files
- end
- function chip8.init()
- for i = 1, 4096 do chip8.memory[i] = 0 end
- for i = 0, 15 do
- chip8.V[i] = 0
- chip8.keys[i] = false
- end
- for y = 1, height do
- buffer[y] = {}
- frame_buffer[y] = {}
- for x = 1, width do
- buffer[y][x] = 0
- frame_buffer[y][x] = 0
- end
- end
- for i = 1, #font do
- chip8.memory[i] = font[i]
- end
- chip8.I = 0
- chip8.PC = 0x200
- chip8.SP = 0
- chip8.delay_timer = 0
- chip8.sound_timer = 0
- chip8.draw_flag = true
- chip8.opcode = 0
- chip8.waiting_for_key = false
- chip8.key_register = 0
- end
- function chip8.loadROM(filename)
- local file = io.open(filename, "rb")
- if not file then error("Could not open ROM file: " .. filename) end
- local address = 0x200
- while true do
- local byte = file:read(1)
- if not byte then break end
- chip8.memory[address] = string.byte(byte)
- address = address + 1
- end
- file:close()
- end
- function chip8.emulateCycle()
- chip8.opcode = bit32.lshift(chip8.memory[chip8.PC], 8) + chip8.memory[chip8.PC + 1]
- chip8.PC = chip8.PC + 2
- local x = bit32.rshift(bit32.band(chip8.opcode, 0x0F00), 8)
- local y = bit32.rshift(bit32.band(chip8.opcode, 0x00F0), 4)
- local nnn = bit32.band(chip8.opcode, 0x0FFF)
- local nn = bit32.band(chip8.opcode, 0x00FF)
- local n = bit32.band(chip8.opcode, 0x000F)
- local first_nibble = bit32.rshift(chip8.opcode, 12)
- if first_nibble == 0x0 then
- if nn == 0xE0 then
- for y = 1, height do for x = 1, width do buffer[y][x] = 0 end end
- chip8.draw_flag = true
- elseif nn == 0xEE then
- chip8.SP = chip8.SP - 1
- chip8.PC = chip8.stack[chip8.SP]
- end
- elseif first_nibble == 0x1 then
- chip8.PC = nnn
- elseif first_nibble == 0x2 then
- chip8.stack[chip8.SP] = chip8.PC
- chip8.SP = chip8.SP + 1
- chip8.PC = nnn
- elseif first_nibble == 0x3 then
- if chip8.V[x] == nn then chip8.PC = chip8.PC + 2 end
- elseif first_nibble == 0x4 then
- if chip8.V[x] ~= nn then chip8.PC = chip8.PC + 2 end
- elseif first_nibble == 0x5 then
- if chip8.V[x] == chip8.V[y] then chip8.PC = chip8.PC + 2 end
- elseif first_nibble == 0x6 then
- chip8.V[x] = nn
- elseif first_nibble == 0x7 then
- chip8.V[x] = bit32.band(chip8.V[x] + nn, 0xFF)
- elseif first_nibble == 0x8 then
- if n == 0x0 then
- chip8.V[x] = chip8.V[y]
- elseif n == 0x1 then
- chip8.V[x] = bit32.bor(chip8.V[x], chip8.V[y])
- elseif n == 0x2 then
- chip8.V[x] = bit32.band(chip8.V[x], chip8.V[y])
- elseif n == 0x3 then
- chip8.V[x] = bit32.bxor(chip8.V[x], chip8.V[y])
- elseif n == 0x4 then
- local sum = chip8.V[x] + chip8.V[y]
- chip8.V[0xF] = sum > 0xFF and 1 or 0
- chip8.V[x] = bit32.band(sum, 0xFF)
- elseif n == 0x5 then
- chip8.V[0xF] = chip8.V[x] > chip8.V[y] and 1 or 0
- chip8.V[x] = bit32.band(chip8.V[x] - chip8.V[y], 0xFF)
- elseif n == 0x6 then
- if not chip8.shift_quirk then
- chip8.V[x] = chip8.V[y]
- end
- chip8.V[0xF] = bit32.band(chip8.V[x], 0x1)
- chip8.V[x] = bit32.rshift(chip8.V[x], 1)
- elseif n == 0x7 then
- chip8.V[0xF] = chip8.V[y] > chip8.V[x] and 1 or 0
- chip8.V[x] = bit32.band(chip8.V[y] - chip8.V[x], 0xFF)
- elseif n == 0xE then
- if not chip8.shift_quirk then
- chip8.V[x] = chip8.V[y]
- end
- chip8.V[0xF] = bit32.rshift(chip8.V[x], 7)
- chip8.V[x] = bit32.band(bit32.lshift(chip8.V[x], 1), 0xFF)
- end
- elseif first_nibble == 0x9 then
- if chip8.V[x] ~= chip8.V[y] then chip8.PC = chip8.PC + 2 end
- elseif first_nibble == 0xA then
- chip8.I = nnn
- elseif first_nibble == 0xB then
- if chip8.jump_quirk then
- chip8.PC = nnn + chip8.V[x]
- else
- chip8.PC = nnn + chip8.V[0]
- end
- elseif first_nibble == 0xC then
- chip8.V[x] = bit32.band(math.random(0, 255), nn)
- elseif first_nibble == 0xD then
- local x_coord = chip8.V[x] % width
- local y_coord = chip8.V[y] % height
- chip8.V[0xF] = 0
- for row = 0, n-1 do
- local sprite = chip8.memory[chip8.I + row]
- for col = 0, 7 do
- if bit32.band(sprite, bit32.rshift(0x80, col)) ~= 0 then
- local px, py = (x_coord + col) % width, (y_coord + row) % height
- if buffer[py+1][px+1] == 1 then chip8.V[0xF] = 1 end
- buffer[py+1][px+1] = bit32.bxor(buffer[py+1][px+1], 1)
- end
- end
- end
- chip8.draw_flag = true
- elseif first_nibble == 0xE then
- if nn == 0x9E then
- if chip8.keys[chip8.V[x]] then chip8.PC = chip8.PC + 2 end
- elseif nn == 0xA1 then
- if not chip8.keys[chip8.V[x]] then chip8.PC = chip8.PC + 2 end
- end
- elseif first_nibble == 0xF then
- if nn == 0x07 then
- chip8.V[x] = chip8.delay_timer
- elseif nn == 0x0A then
- chip8.waiting_for_key = true
- chip8.key_register = x
- elseif nn == 0x15 then
- chip8.delay_timer = chip8.V[x]
- elseif nn == 0x18 then
- chip8.sound_timer = chip8.V[x]
- elseif nn == 0x1E then
- chip8.I = chip8.I + chip8.V[x]
- elseif nn == 0x29 then
- chip8.I = chip8.V[x] * 5
- elseif nn == 0x33 then
- chip8.memory[chip8.I] = math.floor(chip8.V[x] / 100)
- chip8.memory[chip8.I + 1] = math.floor((chip8.V[x] % 100) / 10)
- chip8.memory[chip8.I + 2] = chip8.V[x] % 10
- elseif nn == 0x55 then
- for i = 0, x do
- chip8.memory[chip8.I + i] = chip8.V[i]
- end
- if not chip8.load_store_quirk then
- chip8.I = chip8.I + x + 1
- end
- elseif nn == 0x65 then
- for i = 0, x do
- chip8.V[i] = chip8.memory[chip8.I + i]
- end
- if not chip8.load_store_quirk then
- chip8.I = chip8.I + x + 1
- end
- end
- else
- error("Unknown opcode: " .. string.format("%04X", chip8.opcode))
- end
- end
- function chip8.updateTimers()
- if chip8.delay_timer > 0 then chip8.delay_timer = chip8.delay_timer - 1 end
- if chip8.sound_timer > 0 then
- chip8.sound_timer = chip8.sound_timer - 1
- if chip8.sound_timer == 0 then computer.beep(1000, 0.1) end
- end
- end
- function chip8.handleInput()
- local keymap = {
- ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0xC,
- ['q'] = 0x4, ['w'] = 0x5, ['e'] = 0x6, ['r'] = 0xD,
- ['a'] = 0x7, ['s'] = 0x8, ['d'] = 0x9, ['f'] = 0xE,
- ['z'] = 0xA, ['x'] = 0x0, ['c'] = 0xB, ['v'] = 0xF
- }
- for key, chip8_key in pairs(keymap) do
- if keyboard.isKeyDown(keyboard.keys[key]) then
- chip8.keys[chip8_key] = true
- else
- chip8.keys[chip8_key] = false
- end
- end
- if chip8.waiting_for_key then
- for chip8_key, pressed in pairs(chip8.keys) do
- if pressed then
- chip8.V[chip8.key_register] = chip8_key
- chip8.waiting_for_key = false
- chip8.PC = chip8.PC + 2
- break
- end
- end
- end
- end
- function chip8.updateScreen()
- if chip8.draw_flag then
- for y = 1, height do
- for x = 1, width do
- if frame_buffer[y][x] ~= buffer[y][x] then
- local color = buffer[y][x] == 1 and 0xFFFFFF or 0x000000
- gpu.setBackground(color)
- gpu.fill((x-1)*scale_x+1, (y-1)*scale_y+1, scale_x, scale_y, " ")
- frame_buffer[y][x] = buffer[y][x]
- end
- end
- end
- chip8.draw_flag = false
- end
- end
- function chip8.setQuirks(shift, jump, load_store)
- chip8.shift_quirk = shift
- chip8.jump_quirk = jump
- chip8.load_store_quirk = load_store
- end
- function chip8.run(rom_filename)
- chip8.init()
- chip8.loadROM(rom_filename)
- chip8.setQuirks(true, false, true)
- gpu.setBackground(0x000000)
- gpu.setForeground(0x000000)
- gpu.fill(1, 1, 160, 50, " ")
- gpu.setForeground(0xFFFFFF)
- chip8.updateScreen()
- local cycles_per_frame = 10
- local frame_time = 1/60
- local last_frame_time = computer.uptime()
- while true do
- local start_time = computer.uptime()
- for _ = 1, cycles_per_frame do
- chip8.emulateCycle()
- chip8.updateTimers()
- chip8.handleInput()
- end
- chip8.updateScreen()
- local elapsed = computer.uptime() - start_time
- if elapsed < frame_time then
- os.sleep(frame_time - elapsed)
- end
- if keyboard.isKeyDown(1) then break end
- end
- end
- print([[
- @@@@@@ @@@@@@@ @@@@@@
- @@! @@@ !@@ @@! @@@
- @!@ !@! !@! @!@!@!@! !@!@!@
- !!: !!! :!! !!: !!!
- : :. : :: :: : :.:: :
- ]])
- print("OC-8: CHIP-8 Emulator for OpenComputers coded by nonogamer9")
- print("Available ROMs in /home/:")
- local roms = listCH8Files()
- for i, rom in ipairs(roms) do
- print(i .. ". " .. rom)
- end
- print("Enter the number of the ROM you want to run:")
- local choice = tonumber(io.read())
- if choice and choice > 0 and choice <= #roms then
- local rom_filename = "/home/" .. roms[choice]
- chip8.run(rom_filename)
- else
- print("Invalid selection. Emulator closed.")
- end
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- gpu.fill(1, 1, 160, 50, " ")
- print("OC-8 Emulator closed.")
Add Comment
Please, Sign In to add comment