Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local comp = require("component")
- local ev = require("event")
- local bit = require("bit32")
- local fs = require("filesystem")
- local gpu = comp.gpu
- local shell = require("shell")
- local unicode = require("unicode")
- local computer = require("computer")
- local b_and, b_shl, b_shr, b_xor, b_or = bit.band, bit.lshift, bit.rshift, bit.bxor, bit.bor
- local floor, random = math.floor, math.random
- local s_char, s_sub, s_lower = string.char, string.sub, string.lower
- local os_clock = os.clock
- -- Sound card detection
- local hasSoundCard = comp.isAvailable("sound")
- local sound = nil
- local soundChannel = 1
- local soundEnabled = true
- if hasSoundCard then
- sound = comp.sound
- -- Initialize sound card
- sound.open(soundChannel)
- sound.setWave(soundChannel, sound.modes.square)
- sound.setVolume(soundChannel, 0.8)
- sound.setADSR(soundChannel, 1, 100, 0, 1)
- print("Sound card detected and initialized!")
- else
- print("No sound card detected, using computer.beep() for audio")
- end
- local isTier1 = (gpu.maxDepth() == 1)
- local hires = false
- local shiftQuirk = true
- local loadStoreQuirk = true
- local screenW, screenH = gpu.getResolution()
- local instructionsPerLoop = 150
- local dotMap = {
- {0x1, 0x8},
- {0x2, 0x10},
- {0x4, 0x20},
- {0x40, 0x80}
- }
- local mem = {} for i=0,4095 do mem[i]=0 end
- local V = {} for i=0,15 do V[i]=0 end
- local RPL = {} for i=0,7 do RPL[i]=0 end
- local stack = {}
- local I, PC, SP = 0, 0x200, 0
- local delayT, soundT = 0, 0
- local soundActive = false
- local keys = {}
- local keyMap = "1234qwerasdfzxcv"
- local keyPending, keyWaitReg, keyWaitAddr = nil, nil, nil
- local disp, lastDispChar = {}, {}
- local function clearScreen()
- for x=0,127 do
- disp[x] = disp[x] or {}
- for y=0,63 do disp[x][y] = false end
- end
- for i=1,screenW do lastDispChar[i] = {} end
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, screenW, screenH, " ")
- end
- local function drawStatus()
- local status = string.format(" SPEED: %d Cycles/Loop | Arrows to Adjust | Sound: %s",
- instructionsPerLoop,
- (hasSoundCard and soundEnabled and "CARD" or (hasSoundCard and "MUTED" or "BEEP")))
- gpu.setForeground(0x000000)
- gpu.setBackground(0xFFFFFF)
- gpu.set(1, screenH, status .. string.rep(" ", screenW - #status))
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- end
- -- Sound control functions
- local function startSound()
- if not soundEnabled then return end
- if hasSoundCard then
- -- Use the sound card for better audio
- sound.setFrequency(soundChannel, 440) -- 440Hz is a good beep frequency
- sound.setVolume(soundChannel, 0.8)
- -- Re-open channel if needed
- sound.close(soundChannel)
- sound.open(soundChannel)
- sound.setWave(soundChannel, sound.modes.square)
- sound.setADSR(soundChannel, 1, 100, 0, 1)
- sound.delay(16) -- 16ms beep
- sound.process()
- else
- -- Fallback to computer.beep
- computer.beep(440, 0.016)
- end
- end
- local function stopSound()
- if hasSoundCard and soundEnabled then
- sound.setVolume(soundChannel, 0)
- sound.process()
- end
- end
- local function toggleSound()
- if hasSoundCard then
- soundEnabled = not soundEnabled
- if not soundEnabled then
- sound.setVolume(soundChannel, 0)
- sound.process()
- else
- sound.setVolume(soundChannel, 0.8)
- end
- drawStatus()
- return true
- end
- return false
- end
- local function draw()
- local curW = hires and 128 or 64
- local curH = hires and 64 or 32
- local charW = curW / 2
- local charH = curH / 4
- local offX = floor((screenW - charW) / 2)
- local offY = floor((screenH - charH) / 2)
- for py = 0, charH - 1 do
- for px = 0, charW - 1 do
- local charCode = 0x2800
- for dy = 0, 3 do
- for dx = 0, 1 do
- if disp[px * 2 + dx][py * 4 + dy] then
- charCode = charCode + dotMap[dy + 1][dx + 1]
- end
- end
- end
- local char = unicode.char(charCode)
- if (py + offY + 1) < screenH then
- if char ~= lastDispChar[px + offX][py + offY] then
- gpu.set(px + offX + 1, py + offY + 1, char)
- lastDispChar[px + offX][py + offY] = char
- end
- end
- end
- end
- end
- local function loadFonts()
- local fonts = {
- 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,
- 0xFF,0xFF,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xFF,0xFF, 0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,
- 0xFF,0xFF,0x03,0x03,0xFF,0xFF,0xC0,0xC0,0xFF,0xFF, 0xFF,0xFF,0x03,0x03,0xFF,0xFF,0x03,0x03,0xFF,0xFF,
- 0xC3,0xC3,0xC3,0xC3,0xFF,0xFF,0x03,0x03,0x03,0x03, 0xFF,0xFF,0xC0,0xC0,0xFF,0xFF,0x03,0x03,0xFF,0xFF,
- 0xFF,0xFF,0xC0,0xC0,0xFF,0xFF,0xC3,0xC3,0xFF,0xFF, 0xFF,0xFF,0x03,0x03,0x06,0x0C,0x18,0x18,0x18,0x18,
- 0xFF,0xFF,0xC3,0xC3,0xFF,0xFF,0xC3,0xC3,0xFF,0xFF, 0xFF,0xFF,0xC3,0xC3,0xFF,0xFF,0x03,0x03,0xFF,0xFF,
- 0x7E,0x7E,0xC3,0xC3,0xC3,0xFF,0xFF,0xC3,0xC3,0xC3, 0xFC,0xFC,0xC3,0xC3,0xFC,0xFC,0xC3,0xC3,0xFC,0xFC,
- 0x3C,0x3C,0x60,0x60,0x60,0x60,0x60,0x60,0x3C,0x3C, 0xFC,0xFC,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xFC,0xFC,
- 0xFF,0xFF,0xC0,0xC0,0xF8,0xF8,0xC0,0xC0,0xFF,0xFF, 0xFF,0xFF,0xC0,0xC0,0xF8,0xF8,0xC0,0xC0,0xC0,0xC0
- }
- for i=0,#fonts-1 do mem[i] = fonts[i+1] end
- end
- local function selectRom()
- local path = shell.getWorkingDirectory()
- local files = {}
- for f in fs.list(path) do if f:match("%.ch8$") then files[#files+1] = f end end
- if #files == 0 then print("No .ch8 files found!") return nil end
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- gpu.fill(1,1,screenW,screenH," ")
- gpu.set(2, 2, "8\"\"\"88 8\"\"\"\"8 eeeee 8 ")
- gpu.set(2, 3, "8 8 8 \" 8 8 88")
- gpu.set(2, 4, "8 8 8e 8eee8 88")
- gpu.set(2, 5, "8 8 88 eeee 88 88 88")
- gpu.set(2, 6, "8 8 88 e 88 88 ")
- gpu.set(2, 7, "8eeee8 88eee8 88eee88 88")
- gpu.set(2, 8, "OC-8: CH8/SCH8 Emulator for OC by nonogamer9")
- if hasSoundCard then
- gpu.set(2, 9, "Sound Card: ENABLED (Press S to toggle)")
- else
- gpu.set(2, 9, "Sound: Using computer.beep() (no sound card detected)")
- end
- gpu.set(2, 10, "Select a ROM (1-"..#files.."):")
- for i, f in ipairs(files) do gpu.set(4, i+11, i..". "..f) end
- local choice = nil
- while true do
- local e = {ev.pull("key_down")}
- local n = tonumber(s_char(e[3]))
- if n and files[n] then choice = path.."/"..files[n] break end
- if e[4] == 28 then break end
- end
- return choice
- end
- local function step()
- if keyPending then return false end
- local op = b_or(b_shl(mem[PC], 8), mem[PC+1])
- PC = PC + 2
- local n1, x, y, n4 = b_shr(op, 12), b_and(b_shr(op, 8), 0xF), b_and(b_shr(op, 4), 0xF), b_and(op, 0xF)
- local nn, nnn = b_and(op, 0xFF), b_and(op, 0xFFF)
- if n1 == 0 then
- if op == 0x00E0 then clearScreen()
- elseif op == 0x00EE then PC = stack[SP] SP = SP - 1
- elseif op == 0x00FF then if not isTier1 then hires = true; clearScreen() end
- elseif op == 0x00FE then hires = false; clearScreen()
- elseif op == 0x00FD then
- -- Cleanup sound card on exit
- if hasSoundCard then
- sound.close(soundChannel)
- end
- os.exit()
- elseif op == 0x00FB then
- for sx=127,4,-1 do disp[sx] = disp[sx-4] end
- for sx=0,3 do disp[sx] = {}; for sy=0,63 do disp[sx][sy]=false end end
- elseif op == 0x00FC then
- for sx=0,123 do disp[sx] = disp[sx+4] end
- for sx=124,127 do disp[sx] = {}; for sy=0,63 do disp[sx][sy]=false end end
- elseif b_and(op, 0xFFF0) == 0x00C0 then
- local n = b_and(op, 0xF)
- for sx=0,127 do
- for sy=63,n,-1 do disp[sx][sy] = disp[sx][sy-n] end
- for sy=0,n-1 do disp[sx][sy] = false end
- end
- end
- elseif n1 == 1 then PC = nnn
- elseif n1 == 2 then SP = SP + 1; stack[SP] = PC; PC = nnn
- elseif n1 == 3 then if V[x] == nn then PC = PC + 2 end
- elseif n1 == 4 then if V[x] ~= nn then PC = PC + 2 end
- elseif n1 == 5 then if V[x] == V[y] then PC = PC + 2 end
- elseif n1 == 6 then V[x] = nn
- elseif n1 == 7 then V[x] = b_and(V[x] + nn, 0xFF)
- elseif n1 == 8 then
- if n4 == 0 then V[x] = V[y]
- elseif n4 == 1 then V[x] = b_or(V[x], V[y])
- elseif n4 == 2 then V[x] = b_and(V[x], V[y])
- elseif n4 == 3 then V[x] = b_xor(V[x], V[y])
- elseif n4 == 4 then local s = V[x]+V[y] V[x]=b_and(s,0xFF) V[15]=(s>0xFF) and 1 or 0
- elseif n4 == 5 then V[15]=(V[x]>=V[y]) and 1 or 0 V[x]=b_and(V[x]-V[y],0xFF)
- elseif n4 == 6 then
- if shiftQuirk then V[15]=b_and(V[x],1) V[x]=b_shr(V[x],1)
- else V[15]=b_and(V[y],1) V[x]=b_shr(V[y],1) end
- elseif n4 == 7 then V[15]=(V[y]>=V[x]) and 1 or 0 V[x]=b_and(V[y]-V[x],0xFF)
- elseif n4 == 14 then
- if shiftQuirk then V[15]=b_shr(V[x],7) V[x]=b_and(b_shl(V[x],1),0xFF)
- else V[15]=b_shr(V[y],7) V[x]=b_and(b_shl(V[y],1),0xFF) end
- end
- elseif n1 == 9 then if V[x] ~= V[y] then PC = PC + 2 end
- elseif n1 == 0xA then I = nnn
- elseif n1 == 0xB then PC = nnn + V[0]
- elseif n1 == 0xC then V[x] = b_and(random(0,255), nn)
- elseif n1 == 0xD then
- V[15] = 0
- local w, h = (n4 == 0 and 16 or 8), (n4 == 0 and 16 or n4)
- local limX, limY = (hires and 128 or 64), (hires and 64 or 32)
- for r=0, h-1 do
- local line = (n4 == 0) and b_or(b_shl(mem[I+r*2], 8), mem[I+r*2+1]) or mem[I+r]
- for c=0, w-1 do
- if b_and(line, b_shl(1, (w-1)-c)) ~= 0 then
- local tx, ty = b_and(V[x]+c, limX-1), b_and(V[y]+r, limY-1)
- if disp[tx][ty] then V[15] = 1 end
- disp[tx][ty] = not disp[tx][ty]
- end
- end
- end
- return true
- elseif n1 == 0xE then
- if nn == 0x9E then if keys[V[x]] then PC = PC + 2 end
- elseif nn == 0xA1 then if not keys[V[x]] then PC = PC + 2 end end
- elseif n1 == 0xF then
- if nn == 0x07 then V[x] = delayT
- elseif nn == 0x0A then keyPending, keyWaitReg, keyWaitAddr = true, x, PC
- elseif nn == 0x15 then delayT = V[x]
- elseif nn == 0x18 then
- soundT = V[x]
- -- Start sound immediately if sound timer is set
- if soundT > 0 and not soundActive then
- startSound()
- soundActive = true
- end
- elseif nn == 0x1E then I = b_and(I + V[x], 0xFFFF)
- elseif nn == 0x29 then I = V[x] * 5
- elseif nn == 0x30 then I = 80 + (V[x] * 10)
- elseif nn == 0x33 then mem[I]=floor(V[x]/100) mem[I+1]=floor((V[x]%100)/10) mem[I+2]=V[x]%10
- elseif nn == 0x55 then
- for i=0,x do mem[I+i]=V[i] end
- if not loadStoreQuirk then I = I + x + 1 end
- elseif nn == 0x65 then
- for i=0,x do V[i]=mem[I+i] end
- if not loadStoreQuirk then I = I + x + 1 end
- elseif nn == 0x75 then for i=0,math.min(x,7) do RPL[i]=V[i] end
- elseif nn == 0x85 then for i=0,math.min(x,7) do V[i]=RPL[i] end end
- end
- return false
- end
- loadFonts()
- local romPath = selectRom()
- if not romPath then return end
- local f = io.open(romPath, "rb")
- local d = f:read("*a")
- f:close()
- for i=1, #d do mem[0x200+i-1] = d:byte(i) end
- clearScreen()
- drawStatus()
- local nextTimer = os_clock()
- while true do
- local now = os_clock()
- while true do
- local ed = {ev.pull(0)}
- if not ed[1] then break end
- if ed[1] == "key_down" then
- local code = ed[4]
- local char = ed[3]
- local k = s_lower(s_char(char))
- -- Sound toggle with 'S' key
- if k == "s" and hasSoundCard then
- toggleSound()
- end
- if code == 200 then
- instructionsPerLoop = instructionsPerLoop + 25
- drawStatus()
- elseif code == 208 then
- instructionsPerLoop = math.max(1, instructionsPerLoop - 25)
- drawStatus()
- end
- for i=0,15 do if k == s_sub(keyMap, i+1, i+1) then
- keys[i] = true
- if keyPending then V[keyWaitReg], keyPending = i, nil end
- end end
- elseif ed[1] == "key_up" then
- local k = s_lower(s_char(ed[3]))
- for i=0,15 do if k == s_sub(keyMap, i+1, i+1) then keys[i] = false end end
- elseif ed[1] == "interrupted" then
- -- Cleanup on interrupt
- if hasSoundCard then
- sound.close(soundChannel)
- end
- return
- end
- end
- local redraw = false
- for _=1, instructionsPerLoop do if step() then redraw = true end end
- if redraw then draw() end
- if now >= nextTimer then
- if delayT > 0 then delayT = delayT - 1 end
- if soundT > 0 then
- soundT = soundT - 1
- if not soundActive then
- startSound()
- soundActive = true
- end
- else
- if soundActive then
- stopSound()
- soundActive = false
- end
- end
- nextTimer = now + 0.016
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment