├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── beep.ogg ├── bg.png ├── icon0.png ├── index.lua └── startup.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: Rinnegatamante 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /beep.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/MicroCHIP/3ab1bb93c58605351bc94b87052502a9cdd6c986/beep.ogg -------------------------------------------------------------------------------- /bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/MicroCHIP/3ab1bb93c58605351bc94b87052502a9cdd6c986/bg.png -------------------------------------------------------------------------------- /icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/MicroCHIP/3ab1bb93c58605351bc94b87052502a9cdd6c986/icon0.png -------------------------------------------------------------------------------- /index.lua: -------------------------------------------------------------------------------- 1 | -- CHIP-8 State 2 | local opcode = 0 3 | local ram = {} 4 | local V = {} 5 | local I = 0x000 6 | local PC = 0x000 7 | local SP = 0 8 | local screen = {} -- 64x32 -> 960x480 (x15) 9 | local super_screen = {} -- 128x64 -> 896x448 (x7) 10 | local delay_timer = 0 11 | local sound_timer = 0 12 | local stack = {} 13 | local hp48_flags = {} 14 | local key = {} 15 | local updateScreen = false 16 | local beep = nil 17 | local cur_rom = "" 18 | local schip_mode = false 19 | local update_timers = false 20 | 21 | -- Custom colors 22 | local bg_r = 0 23 | local bg_g = 0 24 | local bg_b = 0 25 | local nbg_r = 255 26 | local nbg_g = 255 27 | local nbg_b = 255 28 | 29 | -- Colors 30 | local white = Color.new(255, 255, 255) 31 | local black = Color.new(0, 0, 0) 32 | local cyan = Color.new(0, 162, 232) 33 | local yellow = Color.new(255, 255, 0) 34 | local red = Color.new(255, 0, 0) 35 | local green = Color.new(0, 255, 0) 36 | local blue = Color.new(0, 0, 255) 37 | local bg_color = Color.new(bg_r, bg_g, bg_b) 38 | local nbg_color = Color.new(nbg_r, nbg_g, nbg_b) 39 | local old_bg_color = bg_color 40 | local old_nbg_color = nbg_color 41 | 42 | -- Key Mapping 43 | local keys = { 44 | {["name"] = "Up", ["val"] = SCE_CTRL_UP, ["int"] = 0x1}, 45 | {["name"] = "Down", ["val"] = SCE_CTRL_DOWN, ["int"] = 0x4}, 46 | {["name"] = "Left", ["val"] = SCE_CTRL_LEFT, ["int"] = 0x2}, 47 | {["name"] = "Right", ["val"] = SCE_CTRL_RIGHT, ["int"] = 0x3}, 48 | {["name"] = "Triangle", ["val"] = SCE_CTRL_TRIANGLE, ["int"] = 0xC}, 49 | {["name"] = "Cross", ["val"] = SCE_CTRL_CROSS, ["int"] = 0xD}, 50 | {["name"] = "Square", ["val"] = SCE_CTRL_SQUARE, ["int"] = 0xE}, 51 | {["name"] = "Circle", ["val"] = SCE_CTRL_CIRCLE, ["int"] = 0xF}, 52 | {["name"] = "L Trigger", ["val"] = SCE_CTRL_LTRIGGER, ["int"] = 0x5}, 53 | {["name"] = "R Trigger", ["val"] = SCE_CTRL_RTRIGGER, ["int"] = 0x6}, 54 | {["name"] = "Start", ["val"] = SCE_CTRL_START, ["int"] = 0x7}, 55 | {["name"] = "Select", ["val"] = SCE_CTRL_SELECT, ["int"] = 0x8} 56 | } 57 | local keys_backup = {} 58 | 59 | -- Localizing most used functions for performance 60 | local drawRect = Graphics.fillRect 61 | 62 | -- Emulator State 63 | local state = 0 64 | local old_state = 0 65 | local emuFolder = "ux0:data/MicroCHIP" 66 | local romFolder = emuFolder .. "/roms" 67 | local savFolder = emuFolder .. "/saves" 68 | local roms = {} 69 | local ver = "1.0" 70 | local cursor = 1 71 | local currentRomCursor = -1 72 | local oldpad = SCE_CTRL_CROSS 73 | local notification = "" 74 | local t = nil 75 | local old_t = nil 76 | local pause_menu_entries = {"Resume game", "Save savestate", "Load savestate", "Reset game", "Show/Hide Debugger", "Options", "Close rom"} 77 | local debugger = false 78 | 79 | -- CHIP-8 Fontset 80 | local fontset = { 81 | 0xF0, 0x90, 0x90, 0x90, 0xF0, -- 0 82 | 0x20, 0x60, 0x20, 0x20, 0x70, -- 1 83 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, -- 2 84 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, -- 3 85 | 0x90, 0x90, 0xF0, 0x10, 0x10, -- 4 86 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, -- 5 87 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, -- 6 88 | 0xF0, 0x10, 0x20, 0x40, 0x40, -- 7 89 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, -- 8 90 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, -- 9 91 | 0xF0, 0x90, 0xF0, 0x90, 0x90, -- A 92 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, -- B 93 | 0xF0, 0x80, 0x80, 0x80, 0xF0, -- C 94 | 0xE0, 0x90, 0x90, 0x90, 0xE0, -- D 95 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, -- E 96 | 0xF0, 0x80, 0xF0, 0x80, 0x80 -- F 97 | } 98 | 99 | -- SCHIP-8 Fontset 100 | local super_fontset = { 101 | 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, -- 0 102 | 0x18, 0x78, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, -- 1 103 | 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, -- 2 104 | 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, -- 3 105 | 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, -- 4 106 | 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, -- 5 107 | 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, -- 6 108 | 0xFF, 0xFF, 0x03, 0x03, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x18, -- 7 109 | 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, -- 8 110 | 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, -- 9 111 | 0x7E, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, -- A 112 | 0xFC, 0xFC, 0xC3, 0xC3, 0xFC, 0xFC, 0xC3, 0xC3, 0xFC, 0xFC, -- B 113 | 0x3C, 0xFF, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0xFF, 0x3C, -- C 114 | 0xFC, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, 0xFC, -- D 115 | 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, -- E 116 | 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0 -- F 117 | } 118 | 119 | -- Clear CHIP-8 screen 120 | function clearScreen() 121 | for i=0, 0x800 do 122 | screen[i] = 0 123 | end 124 | updateScreen = true 125 | end 126 | 127 | -- Clear SCHIP-8 screen 128 | function clearSuperScreen() 129 | for i=0, 0x2000 do 130 | super_screen[i] = 0 131 | end 132 | updateScreen = true 133 | end 134 | 135 | -- Push a notification state on screen 136 | function pushNotification(text) 137 | notification = text 138 | state_timer = 500 139 | end 140 | 141 | -- Initialize CHIP-8 machine 142 | function initEmu() 143 | 144 | -- Resetting emulator state 145 | PC = 0x200 146 | opcode = 0 147 | I = 0x000 148 | SP = 0 149 | schip_mode = false 150 | 151 | -- Clearing screen 152 | clearScreen() 153 | clearSuperScreen() 154 | 155 | -- Resetting stack, keystates, registers, HP48 flags and ram 156 | for i=0, 15 do 157 | stack[i] = 0 158 | V[i] = 0 159 | key[i] = 0 160 | end 161 | for i=0, 0xFFF do 162 | ram[i] = 0 163 | end 164 | for i=0, 7 do 165 | hp48_flags[i] = 0 166 | end 167 | 168 | -- Reset timers 169 | delay_timer = 0 170 | sound_timer = 0 171 | 172 | -- Loading CHIP-8 fontset 173 | for i=0, 0x4F do 174 | ram[i] = fontset[i+1] 175 | end 176 | 177 | -- Loading SCHIP-8 fontset 178 | for i=0, 0x9F do 179 | ram[i + 0x50] = super_fontset[i+1] 180 | end 181 | 182 | -- Selecting a seed for random purposes 183 | h,m,s = System.getTime() 184 | dv,d,m,y = System.getDate() 185 | seed = s + 60*s + h*3600 + d*24*3600 186 | math.randomseed(seed) 187 | 188 | end 189 | 190 | -- Terminate system 191 | function termEmu() 192 | Sound.close(beep) 193 | System.exit() 194 | end 195 | 196 | -- Clear device screen 197 | function purgeScreen() 198 | for i=0,2 do 199 | Graphics.initBlend() 200 | Screen.clear() 201 | Graphics.termBlend() 202 | Screen.flip() 203 | end 204 | end 205 | 206 | -- Load a CHIP-8 rom 207 | function loadRom(filename) 208 | initEmu() 209 | fd = System.openFile(filename, FREAD) 210 | rom_size = System.sizeFile(fd) 211 | for i=0, rom_size-1 do 212 | ram[i + 0x200] = string.byte(System.readFile(fd, 1)) 213 | end 214 | System.closeFile(fd) 215 | purgeScreen() 216 | cur_rom = roms[currentRomCursor] 217 | pushNotification("Rom " .. cur_rom .. " loaded successfully!") 218 | end 219 | 220 | -- Draw CHIP-8 display on screen 221 | function drawScreen() 222 | if schip_mode then 223 | for y=0, 63 do 224 | for x=0, 127 do 225 | x_val = (x * 7) + 32 226 | y_val = (y * 7) + 48 227 | if super_screen[bit32.lshift(y,7) + x] == 0 then 228 | drawRect(x_val, x_val + 7, y_val, y_val + 7, bg_color) 229 | else 230 | drawRect(x_val, x_val + 7, y_val, y_val + 7, nbg_color) 231 | end 232 | end 233 | end 234 | else 235 | for y=0, 31 do 236 | for x=0, 63 do 237 | x_val = x * 15 238 | y_val = (y * 15) + 32 239 | if screen[bit32.lshift(y,6) + x] == 0 then 240 | drawRect(x_val, x_val + 15, y_val, y_val + 15, bg_color) 241 | else 242 | drawRect(x_val, x_val + 15, y_val, y_val + 15, nbg_color) 243 | end 244 | end 245 | end 246 | end 247 | updateScreen = false 248 | end 249 | 250 | -- Handle CHIP-8 keyboard 251 | function handleKeys() 252 | local i = 1 253 | local pad = Controls.read() 254 | while i <= #keys do 255 | key[keys[i].int] = Controls.check(pad, keys[i].val) 256 | i = i + 1 257 | end 258 | t = Controls.readTouch() 259 | if t ~= nil and not (old_t ~= nil) then 260 | if state == 1 then 261 | state = 2 262 | pushNotification("Game paused.") 263 | cursor = 1 264 | end 265 | end 266 | old_t = t 267 | end 268 | 269 | -- Show an error on screen 270 | function showError(text) 271 | System.setMessage(text, false, BUTTON_OK) 272 | while true do 273 | Graphics.initBlend() 274 | Graphics.termBlend() 275 | Screen.flip() 276 | status = System.getMessageState() 277 | if status ~= RUNNING then 278 | break 279 | end 280 | end 281 | if state == 0 then 282 | System.exit() 283 | else 284 | state = 0 285 | end 286 | end 287 | 288 | -- Show an answer on screen 289 | function showAnswer(text) 290 | System.setMessage(text, false, BUTTON_YES_NO) 291 | while true do 292 | Graphics.initBlend() 293 | Graphics.termBlend() 294 | Screen.flip() 295 | status = System.getMessageState() 296 | if status ~= RUNNING then 297 | if status == CANCELED then 298 | return false 299 | else 300 | return true 301 | end 302 | end 303 | end 304 | end 305 | 306 | -- Execute a CHIP-8 cycle 307 | function executeOpcode() 308 | 309 | -- Fetching opcode 310 | opcode = bit32.bor(bit32.lshift(ram[PC],8), ram[PC + 1]) 311 | local bit1 = bit32.rshift(bit32.band(opcode,0xF000),12) 312 | local bit2 = bit32.rshift(bit32.band(opcode,0x0F00),8) 313 | local bit3 = bit32.rshift(bit32.band(opcode,0x00F0),4) 314 | local bit4 = bit32.band(opcode,0x000F) 315 | 316 | -- Executing opcode 317 | PC = PC + 2 318 | if bit1 == 0x0 then 319 | if bit3 == 0xC then -- SCD nibble *SCHIP* 320 | for y=0, 63 do 321 | for x=0, 127 do 322 | local yline = bit32.lshift(y , 7) 323 | super_screen[x + bit32.lshift(y + bit4,7)] = super_screen[x + yline] 324 | if y < bit4 then 325 | super_screen[x + yline] = 0 326 | end 327 | end 328 | end 329 | elseif bit3 == 0xE and bit4 == 0x0 then -- CLS 330 | if schip_mode then 331 | clearSuperScreen() 332 | else 333 | clearScreen() 334 | end 335 | elseif bit3 == 0xE and bit4 == 0xE then -- RET 336 | SP = SP - 1 337 | PC = stack[SP] 338 | elseif bit3 == 0xF and bit4 == 0xB then -- SCR *SCHIP* 339 | for y=0, 63 do 340 | local yline = bit32.lshift(y , 7) 341 | for x=4, 127 do 342 | super_screen[x + yline] = super_screen[x + yline - 4] 343 | end 344 | super_screen[yline] = 0 345 | super_screen[yline+1] = 0 346 | super_screen[yline+2] = 0 347 | super_screen[yline+3] = 0 348 | end 349 | elseif bit3 == 0xF and bit4 == 0xC then -- SCL *SCHIP* 350 | for y=0, 63 do 351 | local yline = bit32.lshift(y , 7) 352 | for x=0, 123 do 353 | super_screen[x + yline] = super_screen[x + yline + 4] 354 | end 355 | super_screen[yline+124] = 0 356 | super_screen[yline+125] = 0 357 | super_screen[yline+126] = 0 358 | super_screen[yline+127] = 0 359 | end 360 | elseif bit3 == 0xF and bit4 == 0xD then -- EXIT 361 | state = 0 362 | elseif bit3 == 0xF and bit4 == 0xE then -- LOW *SCHIP* 363 | schip_mode = false 364 | elseif bit3 == 0xF and bit4 == 0xF then -- HIGH *SCHIP* 365 | schip_mode = true 366 | else 367 | showError("ERROR: Unknown opcode: 0x" .. string.format("%X",opcode)) 368 | end 369 | elseif bit1 == 0x1 then -- JP addr 370 | PC = bit32.band(opcode,0x0FFF) 371 | elseif bit1 == 0x2 then -- CALL addr 372 | stack[SP] = PC 373 | SP = SP + 1 374 | PC = bit32.band(opcode,0x0FFF) 375 | elseif bit1 == 0x3 then -- SE Vx, byte 376 | if V[bit2] == bit32.band(opcode, 0x00FF) then 377 | PC = PC + 2 378 | end 379 | elseif bit1 == 0x4 then -- SNE Vx, byte 380 | if V[bit2] ~= bit32.band(opcode, 0x00FF) then 381 | PC = PC + 2 382 | end 383 | elseif bit1 == 0x5 then -- SE Vx, Vy 384 | if V[bit2] == V[bit3] then 385 | PC = PC + 2 386 | end 387 | elseif bit1 == 0x6 then -- LD Vx, byte 388 | V[bit2] = bit32.band(opcode, 0x00FF) 389 | elseif bit1 == 0x7 then -- ADD Vx, byte 390 | V[bit2] = bit32.band(V[bit2] + bit32.band(opcode, 0x00FF), 0x00FF) 391 | elseif bit1 == 0x8 then 392 | if bit4 == 0x0 then -- LD Vx, Vy 393 | V[bit2] = V[bit3] 394 | elseif bit4 == 0x1 then -- OR Vx, Vy 395 | V[bit2] = bit32.bor(V[bit2],V[bit3]) 396 | elseif bit4 == 0x2 then -- AND Vx, Vy 397 | V[bit2] = bit32.band(V[bit2],V[bit3]) 398 | elseif bit4 == 0x3 then -- XOR Vx, Vy 399 | V[bit2] = bit32.bxor(V[bit2],V[bit3]) 400 | elseif bit4 == 0x4 then -- ADD Vx, Vy 401 | V[bit2] = V[bit2] + V[bit3] 402 | if V[bit2] > 0xFF then 403 | V[0xF] = 1 404 | V[bit2] = bit32.band(V[bit2], 0x00FF) 405 | else 406 | V[0xF] = 0 407 | end 408 | elseif bit4 == 0x5 then -- SUB Vx, Vy 409 | V[bit2] = V[bit2] - V[bit3] 410 | if V[bit2] > 0 then 411 | V[0xF] = 1 412 | else 413 | V[0xF] = 0 414 | V[bit2] = bit32.band(V[bit2], 0x00FF) 415 | end 416 | elseif bit4 == 0x6 then -- SHR Vx 417 | V[0xF] = bit32.band(V[bit2],1) 418 | V[bit2] = bit32.rshift(V[bit2],1) 419 | elseif bit4 == 0x7 then -- SUBN Vx, Vy 420 | V[bit2] = V[bit3] - V[bit2] 421 | if V[bit2] > 0 then 422 | V[0xF] = 1 423 | else 424 | V[0xF] = 0 425 | V[bit2] = bit32.band(V[bit2], 0x00FF) 426 | end 427 | elseif bit4 == 0xE then -- SHL Vx 428 | if bit32.band(V[bit2], 0x80) == 0x80 then 429 | V[0xF] = 1 430 | else 431 | V[0xF] = 0 432 | end 433 | V[bit2] = bit32.band(bit32.lshift(V[bit2],1), 0x00FF) 434 | else 435 | showError("ERROR: Unknown opcode: 0x" .. string.format("%X",opcode)) 436 | end 437 | elseif bit1 == 0x9 then -- SNE Vx, Vy 438 | if V[bit2] ~= V[bit3] then 439 | PC = PC + 2 440 | end 441 | elseif bit1 == 0xA then -- LD I, addr 442 | I = bit32.band(opcode,0x0FFF) 443 | elseif bit1 == 0xB then -- JP V0, addr 444 | PC = V[0] + bit32.band(opcode, 0x0FFF) 445 | elseif bit1 == 0xC then -- RND Vx, byte 446 | V[bit2] = bit32.band(math.random(0, 0xFF), bit32.band(opcode, 0x00FF)) 447 | elseif bit1 == 0xD then -- DRW Vx, Vy, nibble 448 | local x = V[bit2] 449 | local y = V[bit3] 450 | local h = bit4 451 | local pixel 452 | V[0xF] = 0 453 | if schip_mode then 454 | if h == 0 then 455 | for yline=0, 15 do 456 | local y_idx = (y + yline) % 64 457 | local y_pixel = bit32.lshift(yline,1) 458 | pixel = ram[I + y_pixel] 459 | for xline=0, 7 do 460 | local x_idx = (x + xline) % 128 461 | if ((bit32.band(pixel,bit32.rshift(0x80, xline))) > 0) then 462 | pixel_idx = x_idx + (bit32.lshift(y_idx, 7)) 463 | if super_screen[pixel_idx] == 1 then 464 | V[0xF] = 1 465 | end 466 | super_screen[pixel_idx] = bit32.bxor(super_screen[pixel_idx],1) 467 | end 468 | end 469 | pixel = ram[I + y_pixel] 470 | for xline=8, 15 do 471 | local x_idx = (x + xline) % 128 472 | if ((bit32.band(pixel,bit32.rshift(0x80, xline))) > 0) then 473 | pixel_idx = x_idx + (bit32.lshift(y_idx, 7)) 474 | if super_screen[pixel_idx] == 1 then 475 | V[0xF] = 1 476 | end 477 | super_screen[pixel_idx] = bit32.bxor(super_screen[pixel_idx],1) 478 | end 479 | end 480 | end 481 | else 482 | for yline=0, h-1 do 483 | local y_idx = (y + yline) % 64 484 | pixel = ram[I + yline] 485 | for xline=0, 7 do 486 | local x_idx = (x + xline) % 128 487 | if ((bit32.band(pixel,bit32.rshift(0x80, xline))) > 0) then 488 | pixel_idx = x_idx + (bit32.lshift(y_idx, 7)) 489 | if super_screen[pixel_idx] == 1 then 490 | V[0xF] = 1 491 | end 492 | super_screen[pixel_idx] = bit32.bxor(super_screen[pixel_idx],1) 493 | end 494 | end 495 | end 496 | end 497 | else 498 | if h == 0 then 499 | h = 16 500 | end 501 | for yline=0, h-1 do 502 | local y_idx = (y + yline) % 32 503 | pixel = ram[I + yline] 504 | for xline=0, 7 do 505 | local x_idx = (x + xline) % 64 506 | if ((bit32.band(pixel,bit32.rshift(0x80, xline))) > 0) then 507 | pixel_idx = x_idx + (bit32.lshift(y_idx, 6)) 508 | if screen[pixel_idx] == 1 then 509 | V[0xF] = 1 510 | end 511 | screen[pixel_idx] = bit32.bxor(screen[pixel_idx],1) 512 | end 513 | end 514 | end 515 | end 516 | updateScreen = true 517 | elseif bit1 == 0xE then 518 | if bit3 == 0x9 and bit4 == 0xE then -- SKP Vx 519 | if key[V[bit2]] then 520 | PC = PC + 2 521 | end 522 | elseif bit3 == 0xA and bit4 == 0x1 then -- SKPN Vx 523 | if not key[V[bit2]] then 524 | PC = PC + 2 525 | end 526 | else 527 | showError("ERROR: Unknown opcode: 0x" .. string.format("%X",opcode)) 528 | end 529 | elseif bit1 == 0xF then 530 | if bit3 == 0x0 and bit4 == 0x7 then -- LD Vx, DT 531 | V[bit2] = delay_timer 532 | elseif bit3 == 0x0 and bit4 == 0xA then -- LD Vx, K 533 | keyPress = false 534 | for key_idx=0, 15 do 535 | if key[key_idx] then 536 | V[bit2] = key_idx 537 | keyPress = true 538 | break 539 | end 540 | end 541 | if not keyPress then 542 | PC = PC - 2 543 | end 544 | elseif bit3 == 0x1 and bit4 == 0x5 then -- LD DT, Vx 545 | delay_timer = V[bit2] 546 | elseif bit3 == 0x1 and bit4 == 0x8 then -- LD ST, Vx 547 | sound_timer = V[bit2] 548 | elseif bit3 == 0x1 and bit4 == 0xE then -- ADD I, Vx 549 | I = I + V[bit2] 550 | elseif bit3 == 0x2 and bit4 == 0x9 then -- LD F, Vx 551 | I = V[bit2] * 5 552 | elseif bit3 == 0x3 and bit4 == 0x0 then -- LD LF, Vx *SCHIP* 553 | I = V[bit2] * 10 + 0x50 554 | elseif bit3 == 0x3 and bit4 == 0x3 then -- LD B, Vx 555 | local n = V[bit2] 556 | ram[I] = math.floor(n / 100) % 10 557 | ram[I + 1] = math.floor(n / 10) % 10 558 | ram[I + 2] = n % 10 559 | elseif bit3 == 0x5 and bit4 == 0x5 then -- LD [I], Vx 560 | for i=0, bit2 do 561 | ram[I + i] = V[i] 562 | end 563 | elseif bit3 == 0x6 and bit4 == 0x5 then -- LD Vx, [I] 564 | for i=0, bit2 do 565 | V[i] = ram[I + i] 566 | end 567 | elseif bit3 == 0x7 and bit4 == 0x5 then -- LD R, Vx *SCHIP* 568 | for i=0, bit2 do 569 | hp48_flags[i] = V[i] 570 | end 571 | elseif bit3 == 0x8 and bit4 == 0x5 then -- LD Vx, R *SCHIP* 572 | for i=0, bit2 do 573 | V[i] = hp48_flags[i] 574 | end 575 | else 576 | showError("ERROR: Unknown opcode: 0x" .. string.format("%X",opcode)) 577 | end 578 | else 579 | showError("ERROR: Unknown opcode: 0x" .. string.format("%X",opcode)) 580 | end 581 | 582 | end 583 | 584 | function handleTimers() 585 | 586 | update_timers = not update_timers 587 | 588 | -- Updating timers 589 | if update_timers then 590 | if delay_timer > 0 then 591 | delay_timer = delay_timer - 1 592 | end 593 | if sound_timer > 0 then 594 | if sound_timer == 1 then 595 | Sound.play(beep, NO_LOOP) 596 | end 597 | sound_timer = sound_timer - 1 598 | end 599 | end 600 | 601 | end 602 | 603 | -- Get Roms List 604 | function getRomList() 605 | System.createDirectory(romFolder) 606 | System.createDirectory(savFolder) 607 | files = System.listDirectory(romFolder) 608 | for i, file in pairs(files) do 609 | if not file.directory then 610 | table.insert(roms, file.name) 611 | end 612 | end 613 | if #roms == 0 then 614 | showError("FATAL ERROR: No roms detected.") 615 | end 616 | end 617 | 618 | -- Draw roms selector on screen 619 | function drawRomSelector() 620 | Screen.clear() 621 | Graphics.debugPrint(0, 0, "MicroCHIP v." .. ver .. " - Select rom to play", yellow) 622 | if cursor > 24 then 623 | first = cursor - 24 624 | else 625 | first = 0 626 | end 627 | for i, rom in pairs(roms) do 628 | if i > first then 629 | if cursor == i then 630 | Graphics.debugPrint(0, 15 + (i - first) * 20, rom, cyan) 631 | else 632 | Graphics.debugPrint(0, 15 + (i - first) * 20, rom, white) 633 | end 634 | end 635 | end 636 | end 637 | 638 | -- Handle rom selector user input 639 | function handleRomSelection() 640 | local loadState = false 641 | pad = Controls.read() 642 | if Controls.check(pad, SCE_CTRL_UP) and not Controls.check(oldpad, SCE_CTRL_UP) then 643 | cursor = cursor - 1 644 | if cursor < 1 then 645 | cursor = #roms 646 | end 647 | elseif Controls.check(pad, SCE_CTRL_DOWN) and not Controls.check(oldpad, SCE_CTRL_DOWN) then 648 | cursor = cursor + 1 649 | if cursor > #roms then 650 | cursor = 1 651 | end 652 | elseif Controls.check(pad, SCE_CTRL_CROSS) and not Controls.check(oldpad, SCE_CTRL_CROSS) then 653 | if System.doesFileExist(savFolder.."/"..roms[cursor]..".sav") then 654 | loadState = showAnswer("A savestate has been detected. Do you want to load it?") 655 | end 656 | currentRomCursor = cursor 657 | loadRom(romFolder.."/"..roms[cursor]) 658 | if loadState then 659 | loadSavestate() 660 | end 661 | state = 1 662 | end 663 | oldpad = pad 664 | end 665 | 666 | -- Load a savestate 667 | function loadSavestate() 668 | if not System.doesFileExist(savFolder.."/"..cur_rom..".sav") then 669 | return false 670 | end 671 | local fd = System.openFile(savFolder.."/"..cur_rom..".sav", FREAD) 672 | PC_b1 = string.byte(System.readFile(fd,1)) 673 | PC_b2 = string.byte(System.readFile(fd,1)) 674 | PC = PC_b2 + bit32.lshift(PC_b1,8) 675 | I_b1 = string.byte(System.readFile(fd,1)) 676 | I_b2 = string.byte(System.readFile(fd,1)) 677 | I = I_b2 + bit32.lshift(I_b1,8) 678 | SP = string.byte(System.readFile(fd,1)) 679 | delay_timer = string.byte(System.readFile(fd,1)) 680 | sound_timer = string.byte(System.readFile(fd,1)) 681 | for i=0, 15 do 682 | V[i] = string.byte(System.readFile(fd,1)) 683 | end 684 | for i=0, 15 do 685 | S_b1 = string.byte(System.readFile(fd,1)) 686 | S_b2 = string.byte(System.readFile(fd,1)) 687 | stack[i] = S_b2 + bit32.lshift(S_b1,8) 688 | end 689 | for i=0, 0x800 do 690 | screen[i] = string.byte(System.readFile(fd,1)) 691 | end 692 | for i=0, 0xFFF do 693 | ram[i] = string.byte(System.readFile(fd,1)) 694 | end 695 | System.closeFile(fd) 696 | return true 697 | end 698 | 699 | -- Save a savestate 700 | function saveSavestate() 701 | if System.doesFileExist(savFolder.."/"..cur_rom..".sav") then 702 | System.deleteFile(savFolder.."/"..cur_rom..".sav") 703 | end 704 | local fd = System.openFile(savFolder.."/"..cur_rom..".sav", FCREATE) 705 | local buffer = "" 706 | PC_b1 = bit32.rshift(bit32.band(PC,0xFF00),8) 707 | PC_b2 = bit32.band(PC,0x00FF) 708 | buffer = buffer .. string.char(PC_b1) .. string.char(PC_b2) 709 | I_b1 = bit32.rshift(bit32.band(I,0xFF00),8) 710 | I_b2 = bit32.band(I,0x00FF) 711 | buffer = buffer .. string.char(I_b1) .. string.char(I_b2) 712 | buffer = buffer .. string.char(SP) .. string.char(delay_timer) .. string.char(sound_timer) 713 | for i=0, 15 do 714 | buffer = buffer .. string.char(V[i]) 715 | end 716 | for i=0, 15 do 717 | S_b1 = bit32.rshift(bit32.band(stack[i],0xFF00),8) 718 | S_b2 = bit32.band(stack[i],0x00FF) 719 | buffer = buffer .. string.char(S_b1) .. string.char(S_b2) 720 | end 721 | for i=0, 0x800 do 722 | buffer = buffer .. string.char(screen[i]) 723 | end 724 | for i=0, 0xFFF do 725 | buffer = buffer .. string.char(ram[i]) 726 | end 727 | System.writeFile(fd,buffer,0x1828) 728 | System.closeFile(fd) 729 | end 730 | 731 | -- Handle pause menu keys 732 | function handlePauseKeys() 733 | t = Controls.readTouch() 734 | if t ~= nil and not (old_t ~= nil) then 735 | state = 1 736 | pushNotification("Game resumed.") 737 | end 738 | old_t = t 739 | pad = Controls.read() 740 | if Controls.check(pad, SCE_CTRL_UP) and not Controls.check(oldpad, SCE_CTRL_UP) then 741 | cursor = cursor - 1 742 | if cursor < 1 then 743 | cursor = #pause_menu_entries 744 | end 745 | elseif Controls.check(pad, SCE_CTRL_DOWN) and not Controls.check(oldpad, SCE_CTRL_DOWN) then 746 | cursor = cursor + 1 747 | if cursor > #pause_menu_entries then 748 | cursor = 1 749 | end 750 | elseif Controls.check(pad, SCE_CTRL_CROSS) and not Controls.check(oldpad, SCE_CTRL_CROSS) then 751 | if cursor == 1 then -- Resume game 752 | state = 1 753 | pushNotification("Game resumed.") 754 | elseif cursor == 2 then -- Save savestate 755 | saveSavestate() 756 | pushNotification("Savestate created successfully!") 757 | elseif cursor == 3 then -- Load savestate 758 | if loadSavestate() then 759 | pushNotification("Savestate loaded successfully!") 760 | else 761 | pushNotification("Savestate does not exist") 762 | end 763 | elseif cursor == 4 then -- Reset game 764 | loadRom(romFolder.."/"..roms[currentRomCursor]) 765 | state = 1 766 | elseif cursor == 5 then -- Show / Hide debugger 767 | debugger = not debugger 768 | if debugger then 769 | pushNotification("Debugger enabled") 770 | else 771 | -- Clear screen to remove debugger stuff 772 | purgeScreen() 773 | pushNotification("Debugger disabled") 774 | end 775 | elseif cursor == 6 then -- Options 776 | state = 3 777 | cursor = 1 778 | old_state = 2 779 | old_bg_color = bg_color 780 | old_nbg_color = nbg_color 781 | elseif cursor == 7 then -- Close rom 782 | state = 0 783 | cursor = currentRomCursor 784 | end 785 | end 786 | oldpad = pad 787 | end 788 | 789 | -- Draws pause menu 790 | function drawPauseMenu() 791 | -- Calculate the space occupied by the entries based on their number 792 | local menu_entries_height = #pause_menu_entries*22 793 | drawRect(300, 660, 150, 150 + menu_entries_height, white) 794 | drawRect(301, 659, 151, 149 + menu_entries_height, black) 795 | for i=1, #pause_menu_entries do 796 | if i == cursor then 797 | Graphics.debugPrint(305, 155 + (i-1)*20, pause_menu_entries[i], cyan) 798 | else 799 | Graphics.debugPrint(305, 155 + (i-1)*20, pause_menu_entries[i], white) 800 | end 801 | end 802 | end 803 | 804 | -- Draws debugger 805 | function drawDebugger() 806 | drawRect(5, 955, 470, 540, white) 807 | drawRect(6, 954, 471, 539, black) 808 | Graphics.debugPrint(10, 475, "PC: 0x" .. string.format("%X",PC), white) 809 | Graphics.debugPrint(10, 495, "SP: 0x" .. string.format("%X",SP), white) 810 | if SP > 0 then 811 | Graphics.debugPrint(10, 515, "RA: 0x" .. string.format("%X",stack[SP-1]), white) 812 | else 813 | Graphics.debugPrint(10, 515, "RA: 0x00", white) 814 | end 815 | for i=0, 15 do 816 | local z = i % 3 817 | Graphics.debugPrint(130 + math.floor(i/3) * 120, 475 + 20 * z, "V" .. i .. ": 0x" .. string.format("%X",V[i]), white) 818 | end 819 | Graphics.debugPrint(730, 495, "I: 0x" .. string.format("%X",I), white) 820 | Graphics.debugPrint(730, 515, "Ins: 0x" .. string.format("%X",opcode), white) 821 | end 822 | 823 | -- Save options config 824 | function saveConfig() 825 | if System.doesFileExist(emuFolder .. "/options.cfg") then 826 | System.deleteFile(emuFolder .. "/options.cfg") 827 | end 828 | local fd = System.openFile(emuFolder .. "/options.cfg", FCREATE) 829 | System.writeFile(fd, string.char(bg_r), 1) 830 | System.writeFile(fd, string.char(bg_g), 1) 831 | System.writeFile(fd, string.char(bg_b), 1) 832 | System.writeFile(fd, string.char(nbg_r), 1) 833 | System.writeFile(fd, string.char(nbg_g), 1) 834 | System.writeFile(fd, string.char(nbg_b), 1) 835 | for i=1, 12 do 836 | System.writeFile(fd, string.char(keys[i].int), 1) 837 | end 838 | System.closeFile(fd) 839 | pushNotification("Config saved successfully!") 840 | end 841 | 842 | -- Load options config 843 | function loadConfig() 844 | if System.doesFileExist(emuFolder .. "/options.cfg") then 845 | local fd = System.openFile(emuFolder .. "/options.cfg", FREAD) 846 | bg_r = string.byte(System.readFile(fd, 1)) 847 | bg_g = string.byte(System.readFile(fd, 1)) 848 | bg_b = string.byte(System.readFile(fd, 1)) 849 | nbg_r = string.byte(System.readFile(fd, 1)) 850 | nbg_g = string.byte(System.readFile(fd, 1)) 851 | nbg_b = string.byte(System.readFile(fd, 1)) 852 | for i=1, 12 do 853 | keys[i].int = string.byte(System.readFile(fd, 1)) 854 | end 855 | System.closeFile(fd) 856 | bg_color = Color.new(bg_r, bg_g, bg_b) 857 | nbg_color = Color.new(nbg_r, nbg_g, nbg_b) 858 | end 859 | end 860 | 861 | -- Draw keys rebind menu 862 | function drawRebindMenu() 863 | Screen.clear() 864 | for i=1, 12 do 865 | if i == cursor then 866 | Graphics.debugPrint(20, 30 + i * 20, keys[i].name .. " : " .. string.format("%X", keys[i].int), cyan) 867 | else 868 | Graphics.debugPrint(20, 30 + i * 20, keys[i].name .. " : " .. string.format("%X", keys[i].int), white) 869 | end 870 | end 871 | if cursor == 13 then 872 | Graphics.debugPrint(20, 320, "Save changes", cyan) 873 | else 874 | Graphics.debugPrint(20, 320, "Save changes", white) 875 | end 876 | if cursor == 14 then 877 | Graphics.debugPrint(20, 340, "Discard changes", cyan) 878 | else 879 | Graphics.debugPrint(20, 340, "Discard changes", white) 880 | end 881 | end 882 | 883 | function handleRebindKeys() 884 | pad = Controls.read() 885 | if Controls.check(pad, SCE_CTRL_UP) and not Controls.check(oldpad, SCE_CTRL_UP) then 886 | cursor = cursor - 1 887 | if cursor < 1 then 888 | cursor = 14 889 | end 890 | elseif Controls.check(pad, SCE_CTRL_DOWN) and not Controls.check(oldpad, SCE_CTRL_DOWN) then 891 | cursor = cursor + 1 892 | if cursor > 14 then 893 | cursor = 1 894 | end 895 | elseif Controls.check(pad, SCE_CTRL_CROSS) and not Controls.check(oldpad, SCE_CTRL_CROSS) then 896 | if cursor >= 13 then 897 | state = 3 898 | cursor = 1 899 | if cursor == 14 then 900 | for i=1, 12 do 901 | keys[i].int = keys_backup[i] 902 | end 903 | end 904 | end 905 | elseif Controls.check(pad, SCE_CTRL_LEFT) and not Controls.check(oldpad, SCE_CTRL_LEFT) then 906 | if cursor <= 12 then 907 | if keys[cursor].int > 0x1 then 908 | keys[cursor].int = keys[cursor].int - 1 909 | end 910 | end 911 | elseif Controls.check(pad, SCE_CTRL_RIGHT) and not Controls.check(oldpad, SCE_CTRL_RIGHT) then 912 | if cursor <= 12 then 913 | if keys[cursor].int < 0xF then 914 | keys[cursor].int = keys[cursor].int + 1 915 | end 916 | end 917 | end 918 | oldpad = pad 919 | end 920 | 921 | -- Draw options menu 922 | function drawOptionsMenu() 923 | Screen.clear() 924 | 925 | -- Background color tab 926 | drawRect(20, 940, 50, 220, white) 927 | drawRect(21, 939, 51, 219, black) 928 | drawRect(40, 80, 140, 180, white) 929 | drawRect(41, 79, 141, 179, bg_color) 930 | if cursor > 0 and cursor < 4 then 931 | drawRect(99, 867, 104 + (cursor - 1) * 40, 126 + (cursor - 1) * 40, cyan) 932 | end 933 | drawRect(100, 101 + 3 * bg_r, 105, 125, red) 934 | drawRect(100, 101 + 3 * bg_g, 145, 165, green) 935 | drawRect(100, 101 + 3 * bg_b, 185, 205, blue) 936 | Graphics.debugPrint(40, 65, "Background color", white) 937 | 938 | -- Non background color tab 939 | drawRect(20, 940, 240, 410, white) 940 | drawRect(21, 939, 241, 409, black) 941 | drawRect(40, 80, 330, 370, white) 942 | drawRect(41, 79, 331, 369, nbg_color) 943 | if cursor > 3 and cursor < 7 then 944 | drawRect(99, 867, 294 + (cursor - 4) * 40, 316 + (cursor - 4) * 40, cyan) 945 | end 946 | drawRect(100, 101 + 3 * nbg_r, 295, 315, red) 947 | drawRect(100, 101 + 3 * nbg_g, 335, 355, green) 948 | drawRect(100, 101 + 3 * nbg_b, 375, 395, blue) 949 | Graphics.debugPrint(40, 255, "Sprites color", white) 950 | 951 | -- Menu entries 952 | if cursor == 7 then 953 | Graphics.debugPrint(20, 450, "Rebind keys", cyan) 954 | else 955 | Graphics.debugPrint(20, 450, "Rebind keys", white) 956 | end 957 | if cursor == 8 then 958 | Graphics.debugPrint(20, 470, "Save changes", cyan) 959 | else 960 | Graphics.debugPrint(20, 470, "Save changes", white) 961 | end 962 | if cursor == 9 then 963 | Graphics.debugPrint(20, 490, "Discard changes", cyan) 964 | else 965 | Graphics.debugPrint(20, 490, "Discard changes", white) 966 | end 967 | 968 | end 969 | 970 | function handleOptionsKeys() 971 | pad = Controls.read() 972 | if Controls.check(pad, SCE_CTRL_UP) and not Controls.check(oldpad, SCE_CTRL_UP) then 973 | cursor = cursor - 1 974 | if cursor < 1 then 975 | cursor = 9 976 | end 977 | elseif Controls.check(pad, SCE_CTRL_DOWN) and not Controls.check(oldpad, SCE_CTRL_DOWN) then 978 | cursor = cursor + 1 979 | if cursor > 9 then 980 | cursor = 1 981 | end 982 | elseif Controls.check(pad, SCE_CTRL_CROSS) and not Controls.check(oldpad, SCE_CTRL_CROSS) then 983 | if cursor == 7 then 984 | state = 4 985 | cursor = 1 986 | for i=1, 12 do 987 | keys_backup[i] = keys[i].int 988 | end 989 | elseif cursor == 8 then 990 | state = old_state 991 | cursor = 1 992 | saveConfig() 993 | elseif cursor == 9 then 994 | state = old_state 995 | cursor = 1 996 | bg_color = old_bg_color 997 | nbg_color = old_nbg_color 998 | bg_r = Color.getR(bg_color) 999 | bg_g = Color.getG(bg_color) 1000 | bg_b = Color.getB(bg_color) 1001 | nbg_r = Color.getR(nbg_color) 1002 | nbg_g = Color.getG(nbg_color) 1003 | nbg_b = Color.getB(nbg_color) 1004 | end 1005 | if state == 1 then 1006 | cursor = 4 1007 | end 1008 | elseif Controls.check(pad, SCE_CTRL_LEFT) then 1009 | if cursor == 1 then 1010 | bg_r = bg_r - 1 1011 | if bg_r < 0 then 1012 | bg_r = 0 1013 | end 1014 | elseif cursor == 2 then 1015 | bg_g = bg_g - 1 1016 | if bg_g < 0 then 1017 | bg_g = 0 1018 | end 1019 | elseif cursor == 3 then 1020 | bg_b = bg_b - 1 1021 | if bg_b < 0 then 1022 | bg_b = 0 1023 | end 1024 | elseif cursor == 4 then 1025 | nbg_r = nbg_r - 1 1026 | if nbg_r < 0 then 1027 | nbg_r = 0 1028 | end 1029 | elseif cursor == 5 then 1030 | nbg_g = nbg_g - 1 1031 | if nbg_g < 0 then 1032 | nbg_g = 0 1033 | end 1034 | elseif cursor == 6 then 1035 | nbg_b = nbg_b - 1 1036 | if nbg_b < 0 then 1037 | nbg_b = 0 1038 | end 1039 | end 1040 | bg_color = Color.new(bg_r, bg_g, bg_b) 1041 | nbg_color = Color.new(nbg_r, nbg_g, nbg_b) 1042 | elseif Controls.check(pad, SCE_CTRL_RIGHT) then 1043 | if cursor == 1 then 1044 | bg_r = bg_r + 1 1045 | if bg_r > 255 then 1046 | bg_r = 255 1047 | end 1048 | elseif cursor == 2 then 1049 | bg_g = bg_g + 1 1050 | if bg_g > 255 then 1051 | bg_g = 255 1052 | end 1053 | elseif cursor == 3 then 1054 | bg_b = bg_b + 1 1055 | if bg_b > 255 then 1056 | bg_b = 255 1057 | end 1058 | elseif cursor == 4 then 1059 | nbg_r = nbg_r + 1 1060 | if nbg_r > 255 then 1061 | nbg_r = 255 1062 | end 1063 | elseif cursor == 5 then 1064 | nbg_g = nbg_g + 1 1065 | if nbg_g > 255 then 1066 | nbg_g = 255 1067 | end 1068 | elseif cursor == 6 then 1069 | nbg_b = nbg_b + 1 1070 | if nbg_b > 255 then 1071 | nbg_b = 255 1072 | end 1073 | end 1074 | bg_color = Color.new(bg_r, bg_g, bg_b) 1075 | nbg_color = Color.new(nbg_r, nbg_g, nbg_b) 1076 | end 1077 | oldpad = pad 1078 | end 1079 | 1080 | -- Initializing emulator 1081 | Sound.init() 1082 | beep = Sound.open("app0:beep.ogg") 1083 | getRomList() 1084 | loadConfig() 1085 | 1086 | -- Main loop 1087 | while true do 1088 | 1089 | if state == 0 then -- Rom selection 1090 | Graphics.initBlend() 1091 | drawRomSelector() 1092 | Graphics.termBlend() 1093 | Screen.flip() 1094 | handleRomSelection() 1095 | elseif state == 1 then -- Game loop 1096 | handleKeys() 1097 | for i=0, 8 do 1098 | executeOpcode() 1099 | end 1100 | if updateScreen then 1101 | Graphics.initBlend() 1102 | drawScreen() 1103 | drawRect(0, 960, 0, 32, black) 1104 | if state_timer > 0 then 1105 | Graphics.debugPrint(0, 0, notification, yellow) 1106 | state_timer = state_timer - 1 1107 | end 1108 | if debugger then 1109 | drawDebugger() 1110 | end 1111 | Graphics.termBlend() 1112 | Screen.flip() 1113 | end 1114 | handleTimers() 1115 | elseif state == 2 then -- Pause menu 1116 | Graphics.initBlend() 1117 | drawScreen() 1118 | drawRect(0, 960, 0, 32, black) 1119 | if state_timer > 0 then 1120 | Graphics.debugPrint(0, 0, notification, yellow) 1121 | state_timer = state_timer - 1 1122 | end 1123 | drawPauseMenu() 1124 | if debugger then 1125 | drawDebugger() 1126 | end 1127 | Graphics.termBlend() 1128 | Screen.flip() 1129 | handlePauseKeys() 1130 | elseif state == 3 then -- Options Menu 1131 | Graphics.initBlend() 1132 | drawOptionsMenu() 1133 | Graphics.termBlend() 1134 | Screen.flip() 1135 | handleOptionsKeys() 1136 | elseif state == 4 then -- Keys Rebinding 1137 | Graphics.initBlend() 1138 | drawRebindMenu() 1139 | Graphics.termBlend() 1140 | Screen.flip() 1141 | handleRebindKeys() 1142 | end 1143 | 1144 | end 1145 | -------------------------------------------------------------------------------- /startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/MicroCHIP/3ab1bb93c58605351bc94b87052502a9cdd6c986/startup.png --------------------------------------------------------------------------------