0 D$404mNJNL04e$FHuqaY}},m,Ҫm^ @2TvͫgE#2Tv2Tvjk l?m ڶn"fh``0wiqڶ`{`{`ڶp }@}``֢q`?aFxF?GiGiq*hcphc p?a??Ȁ?` "Ԏ4"f>3fh3hyIiyIi`vF@vl3e)deUt)U >0p1P2@3030404 4 4 5 55555555555555555555 ?          c-- title: CHIP-80 -- author: The Beesh-Spweesh! -- desc: A CHIP-8 Interpreter/VM/Emulator -- input: keyboard -- Emulator options X,Y=24,12 -- Screen offset SCALE=3 -- Screen scaling factor (1...3) THEME=0 -- Display theme (0...15) STACKLIMIT=32 -- Stack top limit CLOCKRATE=10 -- Cycles per frame --[[ Emulation quirks (WIP) BW_CLEAR_VF=false -- 8xy1, 8xy2 and 8xy3 trash VF (V[15]) IGNORE_VY =false -- 8xy6 and 8xyE use only Vx CLIP_SPR =false -- Clip sprites at screen edges SPR_BLANK =false -- Wait for next frame before drawing ]] -- CPU registers init V={[0]=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} PC,I,SP,DT=0x200,0,0,0 STACK={} -- ERR=false function write(a,v) if a>=0x200 then poke(0x4000+a,v) end end function read(a) return peek(0x4000+a) end function clsc8() rect(X,Y,64*SCALE,32*SCALE,THEME) end keys={[0]=24,28,29,30,17,23,5,1,19,4,26,3,31,18,6,22} function keypad(k) if k<=#keys then return key(keys[k]) end end function FX0Ascan() for k=0,#keys do if keyp(keys[k]) then return k end end end instrSet={ [0]=function(op) if op==0x0E0 then clsc8() elseif op==0x0EE then SP=SP-1 if SP<0 then reset() end PC=STACK[SP] or 0 else ERR=true end PC=PC+2&0xFFF end, function(addr) PC=addr end, function(addr) STACK[SP]=PC SP,PC=SP+1,addr if SP==STACKLIMIT then reset() end end, function(i) PC=PC+(V[i>>8]==i&255 and 4 or 2)&0xFFF end, function(i) PC=PC+(V[i>>8]==i&255 and 2 or 4)&0xFFF end, function(i) if i&15==0 then PC=PC+(V[i>>8]==V[i>>4&15] and 4 or 2)&0xFFF else ERR,PC=true,PC+2&0xFFF end end, function(i) V[i>>8]=i&0xFF PC=PC+2&0xFFF end, function(i) V[i>>8]=V[i>>8]+i&0xFF PC=PC+2&0xFFF end, function(i) local x,y,op=i>>8,i>>4&15,i&15 if op==0x0 then V[x]=V[y] elseif op==0x1 then V[x]=V[x]|V[y] elseif op==0x2 then V[x]=V[x]&V[y] elseif op==0x3 then V[x]=V[x]~V[y] elseif op==0x4 then local a,b=V[x],V[y] V[x]=a+b&0xFF V[15]=a+b>0xFF and 1 or 0 elseif op==0x5 then local a,b=V[x],V[y] V[x]=a-b&0xFF V[15]=a>=b and 1 or 0 elseif op==0x6 then local lsb=V[x]&1 V[x]=V[x]>>1 V[15]=lsb elseif op==0x7 then local a,b=V[y],V[x] V[x]=a-b&0xFF V[15]=a>=b and 1 or 0 elseif op==0xE then local msb=V[x]>>7 V[x]=V[x]<<1&0xFF V[15]=msb else ERR=true end PC=PC+2&0xFFF end, function(i) if i&15==0 then PC=PC+(V[i>>8]==V[i>>4&15] and 2 or 4)&0xFFF else ERR,PC=true,PC+2&0xFFF end end, function(addr) I,PC=addr,PC+2&0xFFF end, function(disp) PC=V[0]+disp&0xFFF end, function(i) V[i>>8]=math.random(0,255)&i PC=PC+2&0xFFF end, function(i) local x,y,h=V[i>>8]+7,V[i>>4&15],i&15 V[15]=0 for r=0,h-1 do local ar,b=Y+(y+r&31)*SCALE,read(I+r&0xFFF) for c=0,7 do local ac=X+(x-c&63)*SCALE local old,new=pix(ac,ar),b>>c&1 rect(ac,ar,SCALE,SCALE,old~new*15) if old~=THEME and new==1 then V[15]=1 end end end PC=PC+2&0xFFF end, function(i) local op=i&255 if op==0x9E then PC=PC+(keypad(V[i>>8]) and 4 or 2)&0xFFF elseif op==0xA1 then PC=PC+(keypad(V[i>>8]) and 2 or 4)&0xFFF else ERR,PC=true,PC+2&0xFFF end end, function(i) local x,op=i>>8,i&255 if op==0x07 then V[x]=DT elseif op==0x0A then local keycode=FX0Ascan() if keycode then V[x]=keycode else return end elseif op==0x15 then DT=V[x] elseif op==0x18 then sfx(0,nil,V[x]) elseif op==0x1E then I=I+V[x]&0xFFF --[[V[15]=I>0xFFF and 1 or 0 I=I&0xFFF]] elseif op==0x29 then I=(V[x]&15)*5 elseif op==0x33 then write(I,V[x]//100) write(I+1&0xFFF,V[x]//10%10) write(I+2&0xFFF,V[x]%10) elseif op==0x55 then for v=0,x do write(I+v&0xFFF,V[v]) end elseif op==0x65 then for v=0,x do V[v]=read(I+v&0xFFF) end else ERR=true end PC=PC+2&0xFFF end } function disassemble(i) if i==0x00E0 then return "Clear screen" elseif i==0x00EE then return "Return from subroutine" else local nnn,nn,n,x,y=i&0xFFF,i&0xFF,i&0xF,i>>8&15,i>>4&15 local op=i>>12 if op==0x1 then return "Jump to "..x12(nnn) elseif op==0x2 then return "Call subroutine at "..x12(nnn) elseif op==0x3 then return "V"..x.." != "..x8(nn).."?" elseif op==0x4 then return "V"..x.." == "..x8(nn).."?" elseif op==0x5 and n==0 then return "V"..x.." != V"..y.."?" elseif op==0x6 then return "V"..x.." = "..x8(nn) elseif op==0x7 then return "V"..x.." += "..x8(nn) elseif op==0x8 then if n==0x0 then return "V"..x.." = V"..y elseif n==0x1 then return "V"..x.." |= V"..y elseif n==0x2 then return "V"..x.." &= V"..y elseif n==0x3 then return "V"..x.." ^^= V"..y elseif n==0x4 then return "V"..x.." += V"..y elseif n==0x5 then return "V"..x.." -= V"..y elseif n==0x6 then return "V"..x.." >>= 1" elseif n==0x7 then return "V"..x.." = V"..y.." - V"..x elseif n==0xE then return "V"..x.." <<= 1" end elseif op==0x9 and n==0 then return "V"..x.." == V"..y.."?" elseif op==0xA then return "I = "..x12(nnn) elseif op==0xB then return "Jump to V0 + "..x12(nnn) elseif op==0xC then return "V"..x.." = random() & "..x8(nn) elseif op==0xD then return "Draw(V"..x..", V"..y..", "..n..")" elseif op==0xE then if nn==0x9E then return "Key V"..x.." != pressed?" elseif nn==0xA1 then return "Key V"..x.." == pressed?" end elseif op==0xF then if nn==0x07 then return "V"..x.." = DT" elseif nn==0x0A then return "V"..x.." is waiting for a keypress..." elseif nn==0x15 then return "DT = V"..x elseif nn==0x18 then return "SoundTimer = V"..x elseif nn==0x1E then return "I += V"..x elseif nn==0x29 then return "I = character(V"..x..")" elseif nn==0x33 then return "Dump 3-digit BCD of V"..x elseif nn==0x55 then return "Dump V0 to V"..x elseif nn==0x65 then return "Load V0 to V"..x end end end end memcpy(0x4200,0x8000,3584) -- Copy ROM in map to CHIP-8 RAM -- 0 1 2 3 4 5 6 7 8 9 A B C D E F hexFont="F999F26227F1F8FF1F1F99F11F8F1FF8F9FF1244F9F9FF9F1FF9F99E9E9EF888FE999EF8F8FF8F88" for i=1,#hexFont do poke(0x3FFF+i,tonumber(hexFont:sub(i,i),16)<<4) end --function x4(v) return ("%1X"):format(v) end function x8(v) return ("%02X"):format(v) end function x12(v) return ("%03X"):format(v) end cls(1) poke(0x3FF8,3) clsc8() function TIC() for ticks=1,CLOCKRATE do instr=read(PC)<<8|read(PC+1&0xFFF) instrSet[instr>>12](instr&0x0FFF) end DT=math.max(DT-1,0) if ERR then print("INVALID OPCODE FETCHED!",1,1,6) end rect(1,115,238,20,0) for v=0,15 do print(x8(V[v]),2+15*v,116,10,true) end print("I: "..x12(I),1,122,15,true) print("SP: "..SP,102,122) print("DT: "..x8(DT),203,122,15,true) local dis=disassemble(instr) if dis then print(x12(PC)..": "..dis,2,128,10,true) else print(x12(PC)..": Invalid opcode",2,128,6,true) end end