nonogamer9

OC-8 : A Chip-8 Emulator For OpenComputers (version 1.1)

Jul 21st, 2024 (edited)
241
1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.34 KB | Gaming | 1 0
  1. local component = require("component")
  2. local computer = require("computer")
  3. local event = require("event")
  4. local gpu = component.gpu
  5. local keyboard = require("keyboard")
  6. local bit32 = require("bit32")
  7. local filesystem = require("filesystem")
  8.  
  9. gpu.setResolution(160, 50)
  10.  
  11. local chip8 = {
  12.   memory = {},
  13.   V = {},
  14.   I = 0,
  15.   PC = 0x200,
  16.   stack = {},
  17.   SP = 0,
  18.   delay_timer = 0,
  19.   sound_timer = 0,
  20.   keys = {},
  21.   draw_flag = false,
  22.   opcode = 0,
  23.   clock_speed = 700,
  24.   waiting_for_key = false,
  25.   key_register = 0,
  26.   shift_quirk = true,
  27.   jump_quirk = false,
  28.   load_store_quirk = true,
  29. }
  30.  
  31. local width, height = 64, 32
  32. local scale_x, scale_y = 2, 1
  33. local buffer = {}
  34. local frame_buffer = {}
  35.  
  36. local font = {
  37.   0xF0, 0x90, 0x90, 0x90, 0xF0,
  38.   0x20, 0x60, 0x20, 0x20, 0x70,
  39.   0xF0, 0x10, 0xF0, 0x80, 0xF0,
  40.   0xF0, 0x10, 0xF0, 0x10, 0xF0,
  41.   0x90, 0x90, 0xF0, 0x10, 0x10,
  42.   0xF0, 0x80, 0xF0, 0x10, 0xF0,
  43.   0xF0, 0x80, 0xF0, 0x90, 0xF0,
  44.   0xF0, 0x10, 0x20, 0x40, 0x40,
  45.   0xF0, 0x90, 0xF0, 0x90, 0xF0,
  46.   0xF0, 0x90, 0xF0, 0x10, 0xF0,
  47.   0xF0, 0x90, 0xF0, 0x90, 0x90,
  48.   0xE0, 0x90, 0xE0, 0x90, 0xE0,
  49.   0xF0, 0x80, 0x80, 0x80, 0xF0,
  50.   0xE0, 0x90, 0x90, 0x90, 0xE0,
  51.   0xF0, 0x80, 0xF0, 0x80, 0xF0,
  52.   0xF0, 0x80, 0xF0, 0x80, 0x80
  53. }
  54.  
  55. function listCH8Files()
  56.   local files = {}
  57.   for file in filesystem.list("/home/") do
  58.     if file:match("%.ch8$") then
  59.       table.insert(files, file)
  60.     end
  61.   end
  62.   return files
  63. end
  64.  
  65. function chip8.init()
  66.   for i = 1, 4096 do chip8.memory[i] = 0 end
  67.   for i = 0, 15 do
  68.     chip8.V[i] = 0
  69.     chip8.keys[i] = false
  70.   end
  71.  
  72.   for y = 1, height do
  73.     buffer[y] = {}
  74.     frame_buffer[y] = {}
  75.     for x = 1, width do
  76.       buffer[y][x] = 0
  77.       frame_buffer[y][x] = 0
  78.     end
  79.   end
  80.  
  81.   for i = 1, #font do
  82.     chip8.memory[i] = font[i]
  83.   end
  84.  
  85.   chip8.I = 0
  86.   chip8.PC = 0x200
  87.   chip8.SP = 0
  88.   chip8.delay_timer = 0
  89.   chip8.sound_timer = 0
  90.   chip8.draw_flag = true
  91.   chip8.opcode = 0
  92.   chip8.waiting_for_key = false
  93.   chip8.key_register = 0
  94. end
  95.  
  96. function chip8.loadROM(filename)
  97.   local file = io.open(filename, "rb")
  98.   if not file then error("Could not open ROM file: " .. filename) end
  99.   local address = 0x200
  100.   while true do
  101.     local byte = file:read(1)
  102.     if not byte then break end
  103.     chip8.memory[address] = string.byte(byte)
  104.     address = address + 1
  105.   end
  106.   file:close()
  107. end
  108.  
  109. function chip8.emulateCycle()
  110.   chip8.opcode = bit32.lshift(chip8.memory[chip8.PC], 8) + chip8.memory[chip8.PC + 1]
  111.   chip8.PC = chip8.PC + 2
  112.  
  113.   local x = bit32.rshift(bit32.band(chip8.opcode, 0x0F00), 8)
  114.   local y = bit32.rshift(bit32.band(chip8.opcode, 0x00F0), 4)
  115.   local nnn = bit32.band(chip8.opcode, 0x0FFF)
  116.   local nn = bit32.band(chip8.opcode, 0x00FF)
  117.   local n = bit32.band(chip8.opcode, 0x000F)
  118.  
  119.   local first_nibble = bit32.rshift(chip8.opcode, 12)
  120.  
  121.   if first_nibble == 0x0 then
  122.     if nn == 0xE0 then
  123.       for y = 1, height do for x = 1, width do buffer[y][x] = 0 end end
  124.       chip8.draw_flag = true
  125.     elseif nn == 0xEE then
  126.       chip8.SP = chip8.SP - 1
  127.       chip8.PC = chip8.stack[chip8.SP]
  128.     end
  129.   elseif first_nibble == 0x1 then
  130.     chip8.PC = nnn
  131.   elseif first_nibble == 0x2 then
  132.     chip8.stack[chip8.SP] = chip8.PC
  133.     chip8.SP = chip8.SP + 1
  134.     chip8.PC = nnn
  135.   elseif first_nibble == 0x3 then
  136.     if chip8.V[x] == nn then chip8.PC = chip8.PC + 2 end
  137.   elseif first_nibble == 0x4 then
  138.     if chip8.V[x] ~= nn then chip8.PC = chip8.PC + 2 end
  139.   elseif first_nibble == 0x5 then
  140.     if chip8.V[x] == chip8.V[y] then chip8.PC = chip8.PC + 2 end
  141.   elseif first_nibble == 0x6 then
  142.     chip8.V[x] = nn
  143.   elseif first_nibble == 0x7 then
  144.     chip8.V[x] = bit32.band(chip8.V[x] + nn, 0xFF)
  145.   elseif first_nibble == 0x8 then
  146.     if n == 0x0 then
  147.       chip8.V[x] = chip8.V[y]
  148.     elseif n == 0x1 then
  149.       chip8.V[x] = bit32.bor(chip8.V[x], chip8.V[y])
  150.     elseif n == 0x2 then
  151.       chip8.V[x] = bit32.band(chip8.V[x], chip8.V[y])
  152.     elseif n == 0x3 then
  153.       chip8.V[x] = bit32.bxor(chip8.V[x], chip8.V[y])
  154.     elseif n == 0x4 then
  155.       local sum = chip8.V[x] + chip8.V[y]
  156.       chip8.V[0xF] = sum > 0xFF and 1 or 0
  157.       chip8.V[x] = bit32.band(sum, 0xFF)
  158.     elseif n == 0x5 then
  159.       chip8.V[0xF] = chip8.V[x] > chip8.V[y] and 1 or 0
  160.       chip8.V[x] = bit32.band(chip8.V[x] - chip8.V[y], 0xFF)
  161.     elseif n == 0x6 then
  162.       if not chip8.shift_quirk then
  163.         chip8.V[x] = chip8.V[y]
  164.       end
  165.       chip8.V[0xF] = bit32.band(chip8.V[x], 0x1)
  166.       chip8.V[x] = bit32.rshift(chip8.V[x], 1)
  167.     elseif n == 0x7 then
  168.       chip8.V[0xF] = chip8.V[y] > chip8.V[x] and 1 or 0
  169.       chip8.V[x] = bit32.band(chip8.V[y] - chip8.V[x], 0xFF)
  170.     elseif n == 0xE then
  171.       if not chip8.shift_quirk then
  172.         chip8.V[x] = chip8.V[y]
  173.       end
  174.       chip8.V[0xF] = bit32.rshift(chip8.V[x], 7)
  175.       chip8.V[x] = bit32.band(bit32.lshift(chip8.V[x], 1), 0xFF)
  176.     end
  177.   elseif first_nibble == 0x9 then
  178.     if chip8.V[x] ~= chip8.V[y] then chip8.PC = chip8.PC + 2 end
  179.   elseif first_nibble == 0xA then
  180.     chip8.I = nnn
  181.   elseif first_nibble == 0xB then
  182.     if chip8.jump_quirk then
  183.       chip8.PC = nnn + chip8.V[x]
  184.     else
  185.       chip8.PC = nnn + chip8.V[0]
  186.     end
  187.   elseif first_nibble == 0xC then
  188.     chip8.V[x] = bit32.band(math.random(0, 255), nn)
  189.   elseif first_nibble == 0xD then
  190.     local x_coord = chip8.V[x] % width
  191.     local y_coord = chip8.V[y] % height
  192.     chip8.V[0xF] = 0
  193.     for row = 0, n-1 do
  194.       local sprite = chip8.memory[chip8.I + row]
  195.       for col = 0, 7 do
  196.         if bit32.band(sprite, bit32.rshift(0x80, col)) ~= 0 then
  197.           local px, py = (x_coord + col) % width, (y_coord + row) % height
  198.           if buffer[py+1][px+1] == 1 then chip8.V[0xF] = 1 end
  199.           buffer[py+1][px+1] = bit32.bxor(buffer[py+1][px+1], 1)
  200.         end
  201.       end
  202.     end
  203.     chip8.draw_flag = true
  204.   elseif first_nibble == 0xE then
  205.     if nn == 0x9E then
  206.       if chip8.keys[chip8.V[x]] then chip8.PC = chip8.PC + 2 end
  207.     elseif nn == 0xA1 then
  208.       if not chip8.keys[chip8.V[x]] then chip8.PC = chip8.PC + 2 end
  209.     end
  210.   elseif first_nibble == 0xF then
  211.     if nn == 0x07 then
  212.       chip8.V[x] = chip8.delay_timer
  213.     elseif nn == 0x0A then
  214.       chip8.waiting_for_key = true
  215.       chip8.key_register = x
  216.     elseif nn == 0x15 then
  217.       chip8.delay_timer = chip8.V[x]
  218.     elseif nn == 0x18 then
  219.       chip8.sound_timer = chip8.V[x]
  220.     elseif nn == 0x1E then
  221.       chip8.I = chip8.I + chip8.V[x]
  222.     elseif nn == 0x29 then
  223.       chip8.I = chip8.V[x] * 5
  224.     elseif nn == 0x33 then
  225.       chip8.memory[chip8.I] = math.floor(chip8.V[x] / 100)
  226.       chip8.memory[chip8.I + 1] = math.floor((chip8.V[x] % 100) / 10)
  227.       chip8.memory[chip8.I + 2] = chip8.V[x] % 10
  228.     elseif nn == 0x55 then
  229.       for i = 0, x do
  230.         chip8.memory[chip8.I + i] = chip8.V[i]
  231.       end
  232.       if not chip8.load_store_quirk then
  233.         chip8.I = chip8.I + x + 1
  234.       end
  235.     elseif nn == 0x65 then
  236.       for i = 0, x do
  237.         chip8.V[i] = chip8.memory[chip8.I + i]
  238.       end
  239.       if not chip8.load_store_quirk then
  240.         chip8.I = chip8.I + x + 1
  241.       end
  242.     end
  243.   else
  244.     error("Unknown opcode: " .. string.format("%04X", chip8.opcode))
  245.   end
  246. end
  247.  
  248. function chip8.updateTimers()
  249.   if chip8.delay_timer > 0 then chip8.delay_timer = chip8.delay_timer - 1 end
  250.   if chip8.sound_timer > 0 then
  251.     chip8.sound_timer = chip8.sound_timer - 1
  252.     if chip8.sound_timer == 0 then computer.beep(1000, 0.1) end
  253.   end
  254. end
  255.  
  256. function chip8.handleInput()
  257.   local keymap = {
  258.     ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0xC,
  259.     ['q'] = 0x4, ['w'] = 0x5, ['e'] = 0x6, ['r'] = 0xD,
  260.     ['a'] = 0x7, ['s'] = 0x8, ['d'] = 0x9, ['f'] = 0xE,
  261.     ['z'] = 0xA, ['x'] = 0x0, ['c'] = 0xB, ['v'] = 0xF
  262.   }
  263.  
  264.   for key, chip8_key in pairs(keymap) do
  265.     if keyboard.isKeyDown(keyboard.keys[key]) then
  266.       chip8.keys[chip8_key] = true
  267.     else
  268.       chip8.keys[chip8_key] = false
  269.     end
  270.   end
  271.  
  272.   if chip8.waiting_for_key then
  273.     for chip8_key, pressed in pairs(chip8.keys) do
  274.       if pressed then
  275.         chip8.V[chip8.key_register] = chip8_key
  276.         chip8.waiting_for_key = false
  277.         chip8.PC = chip8.PC + 2
  278.         break
  279.       end
  280.     end
  281.   end
  282. end
  283.  
  284. function chip8.updateScreen()
  285.   if chip8.draw_flag then
  286.     for y = 1, height do
  287.       for x = 1, width do
  288.         if frame_buffer[y][x] ~= buffer[y][x] then
  289.           local color = buffer[y][x] == 1 and 0xFFFFFF or 0x000000
  290.           gpu.setBackground(color)
  291.           gpu.fill((x-1)*scale_x+1, (y-1)*scale_y+1, scale_x, scale_y, " ")
  292.           frame_buffer[y][x] = buffer[y][x]
  293.         end
  294.       end
  295.     end
  296.     chip8.draw_flag = false
  297.   end
  298. end
  299.  
  300. function chip8.setQuirks(shift, jump, load_store)
  301.   chip8.shift_quirk = shift
  302.   chip8.jump_quirk = jump
  303.   chip8.load_store_quirk = load_store
  304. end
  305.  
  306. function chip8.run(rom_filename)
  307.   chip8.init()
  308.   chip8.loadROM(rom_filename)
  309.   chip8.setQuirks(true, false, true)
  310.  
  311.   gpu.setBackground(0x000000)
  312.   gpu.setForeground(0x000000)
  313.   gpu.fill(1, 1, 160, 50, " ")
  314.   gpu.setForeground(0xFFFFFF)
  315.  
  316.   chip8.updateScreen()
  317.  
  318.   local cycles_per_frame = 10
  319.   local frame_time = 1/60
  320.   local last_frame_time = computer.uptime()
  321.  
  322.   while true do
  323.     local start_time = computer.uptime()
  324.    
  325.     for _ = 1, cycles_per_frame do
  326.       chip8.emulateCycle()
  327.       chip8.updateTimers()
  328.       chip8.handleInput()
  329.     end
  330.    
  331.     chip8.updateScreen()
  332.  
  333.     local elapsed = computer.uptime() - start_time
  334.     if elapsed < frame_time then
  335.       os.sleep(frame_time - elapsed)
  336.     end
  337.  
  338.     if keyboard.isKeyDown(1) then break end
  339.   end
  340. end
  341.  
  342. print([[
  343.   @@@@@@   @@@@@@@           @@@@@@
  344.  @@!  @@@ !@@               @@!  @@@
  345.  @!@  !@! !@!      @!@!@!@!  !@!@!@
  346.  !!:  !!! :!!               !!:  !!!
  347.   : :. :   :: :: :           :.:: :
  348. ]])
  349. print("OC-8: CHIP-8 Emulator for OpenComputers coded by nonogamer9")
  350. print("Available ROMs in /home/:")
  351.  
  352. local roms = listCH8Files()
  353. for i, rom in ipairs(roms) do
  354.   print(i .. ". " .. rom)
  355. end
  356.  
  357. print("Enter the number of the ROM you want to run:")
  358. local choice = tonumber(io.read())
  359.  
  360. if choice and choice > 0 and choice <= #roms then
  361.   local rom_filename = "/home/" .. roms[choice]
  362.   chip8.run(rom_filename)
  363. else
  364.   print("Invalid selection. Emulator closed.")
  365. end
  366.  
  367. gpu.setBackground(0x000000)
  368. gpu.setForeground(0xFFFFFF)
  369. gpu.fill(1, 1, 160, 50, " ")
  370. print("OC-8 Emulator closed.")
  371.  
Add Comment
Please, Sign In to add comment