From 1a06ff47d1c1604bb5e37c73a7e2b46f66ff83ce Mon Sep 17 00:00:00 2001 From: Hunter <60201971+HooferDevelops@users.noreply.github.com> Date: Tue, 13 Apr 2021 18:46:23 -0400 Subject: [PATCH] Add files via upload --- rLuaGB.rbxlx | 12280 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 12280 insertions(+) create mode 100644 rLuaGB.rbxlx diff --git a/rLuaGB.rbxlx b/rLuaGB.rbxlx new file mode 100644 index 0000000..f3654cc --- /dev/null +++ b/rLuaGB.rbxlx @@ -0,0 +1,12280 @@ + + null + nil + + + false + + Default^0^1\Plugin_Unselectable_Group^1^2 + RBXC4CA890BA1DE43D6AFD8F90DD256E662 + 0 + true + 0 + true + 196.199997 + 0 + 0 + 0 + + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + yuZpQdnvvUBOTYh1jqZ2cA== + + 0 + 0 + 0 + + Workspace + true + 0 + null + 0 + -1 + false + 64 + 0 + 1024 + + true + false + + + + + 0 + true + + -0.5 + 0.5 + 0 + 0 + -0.5 + 0.5 + 4 + 0 + + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + true + true + true + 0 + 4288914085 + + false + + false + -0.5 + 0.5 + 0 + 0 + -0.5 + 0.5 + 0 + 0 + true + false + 256 + + Terrain + AgMAAAAAAAAAAAAAAAA= + + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + 0 + -0.5 + 0.5 + 0 + 0 + 0 + + 0 + 0 + 0 + + AQU= + -1 + + -0.5 + 0.5 + 3 + 0 + 0 + + 0 + 0 + 0 + + + 0.0470588282 + 0.329411775 + 0.360784322 + + 1 + 0.300000012 + 0.150000006 + 10 + + 2044 + 252 + 2044 + + + + + + + + 0 + 20 + 20 + 1 + 0 + 0 + 0 + 0.707211494 + 0.707002044 + 0 + -0.707002044 + 0.707211494 + + null + 6 + 70 + 0 + + 0 + 5.85995865 + 5.85577011 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 1 + + true + 1 + Camera + -1 + + + + + + + 0 + + 3.32999992 + 1 + SoundService + true + 1 + -1 + + + + + + + NonReplicatedCSGDictionaryService + -1 + + + + + + + CSGDictionaryService + -1 + + + + + + + false + true + Chat + -1 + + + + + + + Instance + -1 + + + + + + + false + 30 + Players + 30 + 5 + -1 + + + + + + + ReplicatedFirst + -1 + + + + + + + TweenService + -1 + + + + + + + PermissionsService + -1 + + + + + + + + + PlayerEmulatorService + false + + -1 + + + + + + + 0 + + 0 + true + StudioData + -1 + 5493847492 + 1920707755 + + + + + + true + + true + 128 + 0.5 + 0 + 7.19999981 + 50 + 89 + true + 16 + 0 + 0 + 0 + 0 + 0 + false + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 1 + 0.95 1 + 0.9 1.05 + 0 1 + 0.7 1 + 100 + true + StarterPlayer + 100 + -1 + + true + + + + + StarterPlayerScripts + -1 + + + + + + + StarterCharacterScripts + -1 + + + + + + + + StarterPack + -1 + + + + + + + StarterGui + false + 2 + true + -1 + + + + + + true + 0 + true + false + Canvas + false + null + -1 + + 0 + + + + false + + 0.5 + 0.5 + + + true + 0 + + 0.752941191 + 0.737254918 + 0.725490212 + + 0 + + 0.56078434 + 0.552941203 + 0.572549045 + + 0 + 0 + false + false + 0 + Canvas + null + null + null + null + + 0.5 + 0 + 0.5 + 0 + + null + 0 + false + null + + 0 + 160 + 0 + 144 + + 0 + -1 + 0 + + true + 1 + + + + true + + 0 + 0 + + + true + true + 0 + + 1 + 0.231372565 + 0.160784319 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 1 + false + false + 44 + 0 + 1 + -1 + false + ResetButton + null + null + null + null + + 0 + 0 + 0 + -10 + + false + null + 0 + true + false + null + + 0 + 8 + 0 + 8 + + 0 + -1 + 0 + + RESET + + 1 + 1 + 1 + + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + true + 1 + + + + + + 1 + 0 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + + 0 1 1 1 0 1 0.72549 0.72549 0.72549 0 + true + UIGradient + + 0 + 0 + + 90 + -1 + + 0 0 0 1 0 0 + + + + + true + + 0 + 0 + + + true + true + 0 + + 1 + 0.231372565 + 0.160784319 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 1 + false + false + 44 + 0 + 1 + -1 + false + SetSaveStateButton + null + null + null + null + + 0 + 10 + 0 + -10 + + false + null + 0 + true + false + null + + 0 + 8 + 0 + 8 + + 0 + -1 + 0 + + SAVE + + 1 + 1 + 1 + + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + false + 1 + + + + + + 1 + 0 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + UIPadding + + 0 + 2 + + + 0 + 0 + + + 0 + 0 + + + 0 + 2 + + -1 + + + + + + + true + + 0 + 0 + + + true + true + 0 + + 1 + 0.231372565 + 0.160784319 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 1 + false + false + 44 + 0 + 1 + -1 + false + StartSaveButton + null + null + null + null + + 0 + 20 + 0 + -10 + + false + null + 0 + true + false + null + + 0 + 8 + 0 + 8 + + 0 + -1 + 0 + + LOAD + + 1 + 1 + 1 + + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + false + 1 + + + + + + 1 + 0 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + UIPadding + + 0 + 2 + + + 0 + 0 + + + 0 + 0 + + + 0 + 2 + + -1 + + + + + + + false + + 0 + 0 + + + true + 0 + + 1 + 1 + 1 + + 1 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 1 + false + false + 0 + Pixels + null + null + null + null + + 0 + 0 + 0 + 0 + + null + 0 + false + null + + 1 + 0 + 1 + 0 + + 0 + -1 + 0 + + true + 1 + + + + + + + false + + CanvasManager + {CC20B0D2-4DE3-4327-A364-2234FBF8548B} + 0 then + filebrowser.cursor_pos = filebrowser.cursor_pos - 1 + if filebrowser.cursor_pos < filebrowser.scroll_pos + 1 and filebrowser.scroll_pos > 0 then + filebrowser.scroll_pos = filebrowser.cursor_pos - 1 + end + end + if key == "Down" and filebrowser.cursor_pos < #filebrowser.items - 1 then + filebrowser.cursor_pos = filebrowser.cursor_pos + 1 + if filebrowser.cursor_pos > filebrowser.scroll_pos + 9 then + filebrowser.scroll_pos = filebrowser.cursor_pos - 9 + end + end + end +end + +main.gameboy.input.key_pressed = input_state + + +local StopButton = script.Parent.Canvas.ResetButton + +StopButton.MouseButton1Click:Connect(function() + main:reset() + main.emulator_running = false + main.gameboy.input.key_pressed = input_state +end) + +local RomImportMenu = game.Players.LocalPlayer.PlayerGui:WaitForChild("ImportScreen"):WaitForChild("Menu") + +RomImportMenu.Cancel.MouseButton1Click:Connect(function() + filebrowser:refresh_items() + RomImportMenu.Visible = false +end) + +RomImportMenu.RomConfirm.MouseButton1Click:Connect(function() + game.ReplicatedStorage.SaveROM:InvokeServer(RomImportMenu.RomURL.Text, RomImportMenu.RomName.Text) + RomImportMenu.RomURL.Text = "" + RomImportMenu.RomName.Text = "" + filebrowser:refresh_items() + RomImportMenu.Visible = false +end) +]]> + -1 + + + + + + + filebrowser + {E29638FA-4D7D-4DE6-A977-9A5A793DE1C9} + 3) then + self.palette_index = 1 + end +end + +function filebrowser:draw_string(str, dx, dy, color, max_x) + max_x = max_x or 159 + for i = 1, #str do + local char = string.byte(str, i) + if 31 < char and char < 128 then + char = char - 32 + local font_x = char * 4 + local font_y = 0 + for x = 0, 3 do + if i * 4 - 4 + x + dx < max_x then + for y = 0, 5 do + if y + dy <= 143 then + local rgb, alpha = self.ui.image:get_pixel(font_x + x + 1, font_y + y + 1, "font") + if alpha > 0 then + self.game_screen[y + dy][i * 4 - 4 + x + dx] = color + end + end + end + end + end + end + end +end + +function filebrowser:refresh_items() + coroutine.wrap(function() + local roms = game.ReplicatedStorage.GetROMs:InvokeServer() + for _,rom in pairs(game.ReplicatedStorage.ROMs:GetChildren()) do + table.insert(roms, rom) + end + self.items = { + { + ["Name"] = "Import ROM", + ["Value"] = "IMPORT_ROM_ENTRY" + } + } + for _,rom in pairs(roms) do + table.insert(self.items, rom) + end + end)() +end + +function filebrowser:draw() + -- queue up everything and then draw. + local frames = self.frame_counter + local scroll_amount = math.floor(frames / 8) + self:draw_background(scroll_amount, scroll_amount * -1) + self:shadow_box(7, 15, 146, 81) + self.ui.image.palette = self.palettes[self.palette_index] + + -- highlight box + self:draw_rectangle(8, 17 + ((self.cursor_pos - self.scroll_pos) * 7), 144, 7, self.palettes[self.palette_index][2], true) + + -- Filebrowser / game selection menu + local y = 18 + local i = 0 - self.scroll_pos + for _, item in pairs(self.items) do + if i >= 0 and i < 11 then + local color = self.palettes[self.palette_index][2] + if i + self.scroll_pos == self.cursor_pos then + color = self.palettes[self.palette_index][3] + end + self:draw_string(item.Name or item["Name"] or "ERR", 10, y, color, 152) + y = y + 7 + end + i = i + 1 + end + + -- Logo + self.ui.image:draw_image(22, 0, "logo", self.game_screen) + self.ui.image:draw_image(7, 0, "dango", self.game_screen) + + self.ui.image:draw_image(133, 4, "palette_chooser", self.game_screen) + self.ui.image:draw_image(136, 108, "round_button", self.game_screen) -- A + self.ui.image:draw_image(124, 120, "round_button", self.game_screen) -- B + self.ui.image:draw_image(86, 114, "pill_button", self.game_screen) -- Start + self.ui.image:draw_image(51, 114, "pill_button", self.game_screen) -- Select + self.ui.image:draw_image(12, 104, "d_pad", self.game_screen) + --self.ui.image:draw_image(111, 0, "folder", self.game_screen) + + self:draw_string("X", 140, 111, self.palettes[self.palette_index][3]) -- A + self:draw_string("Z", 128, 123, self.palettes[self.palette_index][3]) -- B + self:draw_string("ENTER", 92, 117, self.palettes[self.palette_index][3]) -- Start + self:draw_string("RSHIFT", 55, 117, self.palettes[self.palette_index][3]) -- Select + + for y=0,self.ui.canvas_height do + for x=0,self.ui.canvas_width do + self.ui:set_canvas_pixel(x, y, self.game_screen[y][x]) + end + end + + self.frame_counter = self.frame_counter + 2 +end + +return filebrowser]]> + -1 + + + + + + + + UIScale + 3 + -1 + + + + + + + + true + 0 + true + false + Controller + true + null + -1 + + 1 + + + + false + + 0.5 + 0 + + + true + 0 + + 0.752941191 + 0.737254918 + 0.725490212 + + 0 + + 0.56078434 + 0.552941203 + 0.572549045 + + 0 + 5 + false + false + 0 + Controller + null + null + null + null + + 0.5 + 0 + 1 + -99 + + null + 0 + false + null + + 0 + 208 + 0 + 87 + + 0 + -1 + 0 + + true + 1 + + + + true + + 0 + 0 + + + true + true + 0 + + 0.427451015 + 0.411764741 + 0.415686309 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.Return + null + null + null + null + + 0 + 85 + 0 + 70 + + false + null + 0 + true + false + null + + 0 + 38 + 0 + 14 + + 0 + -1 + 0 + + START + + 1 + 1 + 1 + + false + 11 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 1 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.427451015 + 0.411764741 + 0.415686309 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.RightShift + null + null + null + null + + 0 + 44 + 0 + 70 + + false + null + 0 + true + false + null + + 0 + 38 + 0 + 14 + + 0 + -1 + 0 + + SELECT + + 1 + 1 + 1 + + false + 11 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 1 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.549019635 + 0.184313729 + 0.368627459 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.X + null + null + null + null + + 0 + 179 + 0 + 13 + + false + null + 0 + true + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + A + + 1 + 1 + 1 + + false + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 3 + + + + + + 1 + 0 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.549019635 + 0.184313729 + 0.368627459 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.Z + null + null + null + null + + 0 + 151 + 0 + 29 + + false + null + 0 + true + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + B + + 1 + 1 + 1 + + false + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 3 + + + + + + 1 + 0 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + false + + 0 + 0 + + + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 0 + DPad + null + null + null + null + + 0 + 22 + 0 + 29 + + null + 0 + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + true + 2 + + + + true + + 0 + 0 + + + true + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.A + null + null + null + null + + 0 + -16 + 0 + 0 + + false + null + 0 + true + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + ← + + 1 + 1 + 1 + + false + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 0 + true + 2 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.D + null + null + null + null + + 0 + 16 + 0 + 0 + + false + null + 0 + true + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + → + + 1 + 1 + 1 + + false + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 0 + true + 2 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.S + null + null + null + null + + 0 + 0 + 0 + 16 + + false + null + 0 + true + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + ↓ + + 1 + 1 + 1 + + false + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 2 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.W + null + null + null + null + + 0 + 0 + 0 + -16 + + false + null + 0 + true + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + ↑ + + 1 + 1 + 1 + + false + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 2 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + false + + 0 + 0 + + + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 0 + Cover + null + null + null + null + + 0 + 2 + 0 + 0 + + null + 0 + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + true + 2 + + + + + false + + 0 + 0 + + + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 0 + Cover + null + null + null + null + + 0 + -2 + 0 + 0 + + null + 0 + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + true + 2 + + + + + false + + 0 + 0 + + + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 0 + Cover + null + null + null + null + + 0 + 0 + 0 + 2 + + null + 0 + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + true + 2 + + + + + false + + 0 + 0 + + + true + 0 + + 0.176470593 + 0.176470593 + 0.184313729 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 0 + Cover + null + null + null + null + + 0 + 0 + 0 + -2 + + null + 0 + false + null + + 0 + 16 + 0 + 16 + + 0 + -1 + 0 + + true + 2 + + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.427450985 + 0.411764711 + 0.41568628 + + 0 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 0 + false + false + 44 + 0 + 1 + -1 + false + Enum.KeyCode.P + null + null + null + null + + 0 + 126 + 0 + 70 + + false + null + 0 + true + false + null + + 0 + 38 + 0 + 14 + + 0 + -1 + 0 + + PAUSE + + 1 + 1 + 1 + + false + 11 + + 0 + 0 + 0 + + 1 + 0 + 0 + false + 2 + 1 + true + 1 + + + + + + 0 + 4 + + UICorner + -1 + + + + + + + 0 1 1 1 0 1 0.866667 0.866667 0.866667 0 + true + UIGradient + + 0 + 0 + + 45 + -1 + + 0 0 0 1 0 0 + + + + + + + 0 1 1 1 0 1 0.905882 0.905882 0.905882 0 + true + UIGradient + + 0 + 0 + + 90 + -1 + + 0 0 0 1 0 0 + + + + + + + + true + 0 + true + false + FPSCounterGui + true + null + 2194390315 + + 0 + + + + false + + 1 + 1 + + + true + 0 + + 1 + 1 + 1 + + 1 + + 0.105882362 + 0.164705887 + 0.207843155 + + 0 + 1 + false + false + 44 + 0 + 1 + -1 + FPSCounterText + null + null + null + null + + 1 + -3 + 1 + -3 + + false + null + 0 + false + null + + 0.0511753969 + 0 + 0.0234332774 + 0 + + 0 + -1 + + 60 FPS + + 1 + 1 + 1 + + true + 14 + + 0 + 0 + 0 + + 0 + 0 + 0 + true + 1 + 1 + true + 1 + + + + + false + + FPSCounterScript + {E3926F35-FC1C-47B9-A77E-2C56EA3373D1} + + -1 + + + + + + + + + false + + Controller + {FE567043-2CE0-4C8C-AD73-CA11E90A12FD} + + -1 + + + + + + + true + 0 + true + false + ImportScreen + true + null + -1 + + 1 + + + + false + + 0.5 + 0.5 + + + true + 0 + + 0.878431439 + 0.972549081 + 0.815686345 + + 0 + + 0.203921571 + 0.407843143 + 0.337254912 + + 0 + 2 + false + false + 0 + Menu + null + null + null + null + + 0.5 + 0 + 0.5 + 0 + + null + 0 + false + null + + 0.227272734 + 0 + 0.273058265 + 0 + + 0 + -1 + 0 + + false + 1 + + + + true + + 0 + 0 + + + true + 0 + + 0.533333361 + 0.752941251 + 0.43921572 + + 0 + + 0.203921571 + 0.407843143 + 0.337254912 + + 0 + 2 + true + false + false + 41 + 0 + 1 + -1 + false + RomURL + null + null + null + null + + 1 + 1 + 1 + + Enter Rom URL... + + 0.100000001 + 0 + 0.222222224 + 0 + + false + null + 0 + true + null + true + + 0.800000012 + 0 + 0.088888891 + 0 + + 0 + -1 + + + + 0 + 0 + 0 + + true + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + true + 1 + + + + + true + + 0 + 0 + + + true + 0 + + 0.533333361 + 0.752941251 + 0.43921572 + + 0 + + 0.203921571 + 0.407843143 + 0.337254912 + + 0 + 2 + true + false + false + 41 + 0 + 1 + -1 + false + RomName + null + null + null + null + + 1 + 1 + 1 + + Enter Rom Label... + + 0.100000001 + 0 + 0.377777785 + 0 + + false + null + 0 + true + null + true + + 0.800000012 + 0 + 0.088888891 + 0 + + 0 + -1 + + + + 0 + 0 + 0 + + true + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + true + 1 + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.533333361 + 0.752941251 + 0.43921572 + + 0 + + 0.203921571 + 0.407843143 + 0.337254912 + + 0 + 2 + false + false + 41 + 0 + 1 + -1 + false + RomConfirm + null + null + null + null + + 0.184 + 0 + 0.533333361 + 0 + + false + null + 0 + true + false + null + + 0.632000029 + 0 + 0.11999999 + 0 + + 0 + -1 + 0 + + Confirm + + 1 + 1 + 1 + + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + true + 1 + + + + + true + + 0 + 0 + + + true + true + 0 + + 0.533333361 + 0.752941251 + 0.43921572 + + 0 + + 0.203921571 + 0.407843143 + 0.337254912 + + 0 + 2 + false + false + 41 + 0 + 1 + -1 + false + Cancel + null + null + null + null + + 0.184 + 0 + 0.720000029 + 0 + + false + null + 0 + true + false + null + + 0.632000029 + 0 + 0.119999997 + 0 + + 0 + -1 + 0 + + Cancel + + 1 + 1 + 1 + + true + 14 + + 0 + 0 + 0 + + 1 + 0 + 0 + true + 2 + 1 + true + 1 + + + + + + + + + LocalizationService + -1 + + + + + + + Teleport Service + -1 + + + + + + + CollectionService + -1 + + + + + + + PhysicsService + -1 + + + + + + + Geometry + -1 + + + + + + false + false + + InsertService + -1 + + + + + + InsertionHash + -1 + + {0CA8B70B-CDF7-40A8-B7F8-F4328A7E4D48} + + + + + + + GamePassService + -1 + + + + + + + 1000 + Debris + -1 + + + + + + + CookiesService + -1 + + + + + + + VRService + -1 + + + + + + + ContextActionService + -1 + + + + + + + Instance + -1 + + + + + + + AssetService + -1 + + + + + + + TouchInputService + -1 + + + + + + + + AnalyticsService + -1 + + + + + + + Selection + -1 + + + + + + + false + ServerScriptService + -1 + + + + + + false + + RawDataRequest + {6A431750-9D6C-459F-95C3-8778BA60D1C6} + + -1 + + + + + + + false + + DataHandler + {42B06BB1-8458-4EDB-B38C-0F63CC612254} + + -1 + + + + + + + false + + PlayerPrevention + {EFFA7F72-250F-4745-BD62-2319D23A406B} + + -1 + + + + + + + + ServerStorage + -1 + + + + + + PlayerROMs + -1 + + + + + + + + ReplicatedStorage + -1 + + + + + + LuaGB + -1 + + + + + + gameboy + -1 + + + + + + graphics + -1 + + + + + + + cache + {BCF5E3BE-9E7D-4121-A53E-0DF617E34CC2} + 127 then + tile_index = tile_index - 256 + end + -- add offset to re-root at tile 256 (so effectively, we read from tile 192 - 384) + tile_index = tile_index + 256 + end + if attr[x][y].bank == 1 then + tile_index = tile_index + 384 + end + if attr[x][y].horizontal_flip then + map[x][y] = cache.tiles_h_flipped[tile_index] + else + map[x][y] = cache.tiles[tile_index] + end + end + + cache.refreshTileMap = function(address, map, attr) + for x = 0, 31 do + for y = 0, 31 do + cache.refreshTileIndex(x, y, address, map, attr) + end + end + end + + cache.refreshTileMaps = function() + cache.refreshTileMap(0x9800, cache.map_0, cache.map_0_attr) + cache.refreshTileMap(0x9C00, cache.map_1, cache.map_1_attr) + end + + cache.refreshTileAttributes = function() + for x = 0, 31 do + for y = 0, 31 do + cache.refreshAttributes(cache.map_0_attr, x, y, 0x9800 + (y * 32) + x) + cache.refreshAttributes(cache.map_1_attr, x, y, 0x9C00 + (y * 32) + x) + end + end + end + + cache.refreshAll = function() + cache.refreshTiles() + cache.refreshTileAttributes() + cache.refreshTileMaps() + end + + return cache +end + +return Cache +]]> + -1 + + + + + + + + init + {C151C649-4F7C-4D6D-AC88-54D8BB9C9B5D} + = 0x8000 and address <= 0x97FF then + graphics.cache.refreshTile(address, graphics.vram.bank) + end + if address >= 0x9800 and address <= 0x9BFF then + local x = address % 32 + local y = math.floor((address - 0x9800) / 32) + if graphics.vram.bank == 1 then + graphics.cache.refreshAttributes(graphics.cache.map_0_attr, x, y, address) + end + graphics.cache.refreshTileIndex(x, y, 0x9800, graphics.cache.map_0, graphics.cache.map_0_attr) + end + if address >= 0x9C00 and address <= 0x9FFF then + local x = address % 32 + local y = math.floor((address - 0x9C00) / 32) + if graphics.vram.bank == 1 then + graphics.cache.refreshAttributes(graphics.cache.map_1_attr, x, y, address) + end + graphics.cache.refreshTileIndex(x, y, 0x9C00, graphics.cache.map_1, graphics.cache.map_1_attr) + end + end + setmetatable(graphics.vram_map, graphics.vram_map.mt) + memory.map_block(0x80, 0x9F, graphics.vram_map, 0) + + graphics.oam_raw = memory.generate_block(0xA0, 0xFE00) + graphics.oam = {} + graphics.oam.mt = {} + graphics.oam.mt.__index = function(table, address) + if address <= 0xFE9F then + return graphics.oam_raw[address] + end + -- out of range? So sorry, return nothing + return 0x00 + end + graphics.oam.mt.__newindex = function(table, address, byte) + if address <= 0xFE9F then + graphics.oam_raw[address] = byte + graphics.cache.refreshOamEntry(math.floor((address - 0xFE00) / 4)) + end + -- out of range? So sorry, discard the write + return + end + setmetatable(graphics.oam, graphics.oam.mt) + memory.map_block(0xFE, 0xFE, graphics.oam, 0) + + io.write_logic[0x4F] = function(byte) + if graphics.gameboy.type == graphics.gameboy.types.color then + io.ram[0x4F] = bit32.band(0x1, byte) + graphics.vram.bank = bit32.band(0x1, byte) + else + -- Not sure if the write mask should apply in DMG / SGB mode + io.ram[0x4F] = byte + end + end + + graphics.initialize = function(gameboy) + graphics.gameboy = gameboy + graphics.registers.status.SetMode(2) + graphics.clear_screen() + graphics.reset() + end + + graphics.reset = function() + graphics.cache.reset() + graphics.palette.reset() + + -- zero out all of VRAM: + for i = 0x8000, (0x8000 + (16 * 2 * 1024) - 1) do + graphics.vram[i] = 0 + end + + -- zero out all of OAM + for i = 0xFE00, 0xFE9F do + graphics.oam[i] = 0 + end + + graphics.vblank_count = 0 + graphics.last_edge = 0 + graphics.vram.bank = 0 + graphics.lcdstat = false + + graphics.clear_screen() + graphics.registers.status.SetMode(2) + end + + graphics.save_state = function() + local state = {} + + state.vram = {} + for i = 0x8000, (0x8000 + (16 * 2 * 1024) - 1) do + state.vram[i] = graphics.vram[i] + end + + state.vram_bank = graphics.vram.bank + + state.oam = {} + for i = 0xFE00, 0xFE9F do + state.oam[i] = graphics.oam[i] + end + + state.vblank_count = graphics.vblank_count + state.last_edge = graphics.last_edge + state.lcdstat = graphics.lcdstat + state.mode = graphics.registers.status.mode + + state.palette = {} + state.palette.bg = graphics.palette.bg + state.palette.obj0 = graphics.palette.obj0 + state.palette.obj1 = graphics.palette.obj1 + + state.color_bg = {} + state.color_obj = {} + state.color_bg_raw = {} + state.color_obj_raw = {} + + for p = 0, 7 do + state.color_bg[p] = graphics.palette.color_bg[p] + state.color_obj[p] = graphics.palette.color_obj[p] + end + + for i = 0, 63 do + state.color_bg_raw[i] = graphics.palette.color_bg_raw[i] + state.color_obj_raw[i] = graphics.palette.color_obj_raw[i] + end + + return state + end + + graphics.load_state = function(state) + for i = 0x8000, (0x8000 + (16 * 2 * 1024) - 1) do + graphics.vram[i] = state.vram[i] + end + + graphics.vram.bank = state.vram_bank + + for i = 0xFE00, 0xFE9F do + graphics.oam[i] = state.oam[i] + end + graphics.vblank_count = state.vblank_count + graphics.last_edge = state.last_edge + graphics.lcdstat = state.lcdstat + graphics.registers.status.mode = state.mode + + graphics.palette.bg = state.palette.bg + graphics.palette.obj0 = state.palette.obj0 + graphics.palette.obj1 = state.palette.obj1 + + for p = 0, 7 do + graphics.palette.color_bg[p] = state.color_bg[p] + graphics.palette.color_obj[p] = state.color_obj[p] + end + + for i = 0, 63 do + graphics.palette.color_bg_raw[i] = state.color_bg_raw[i] + graphics.palette.color_obj_raw[i] = state.color_obj_raw[i] + end + + graphics.cache.refreshAll() + io.write_logic[ports.STAT](io.ram[ports.STAT]) + io.write_logic[ports.LCDC](io.ram[ports.LCDC]) + end + + local time_at_this_mode = function() + return timers.system_clock - graphics.last_edge + end + + local scanline_data = {} + scanline_data.x = 0 + scanline_data.bg_tile_x = 0 + scanline_data.bg_tile_y = 0 + scanline_data.sub_x = 0 + scanline_data.sub_y = 0 + scanline_data.active_tile = nil + scanline_data.active_attr = nil + scanline_data.current_map = nil + scanline_data.current_map_attr = nil + scanline_data.window_active = false + scanline_data.bg_index = {} + scanline_data.bg_priority = {} + scanline_data.active_palette = nil + + graphics.refresh_lcdstat = function() + local lcdstat = false + local status = graphics.registers.status + + lcdstat = + (status.lyc_interrupt_enabled and io.ram[ports.LY] == io.ram[ports.LYC]) or + (status.oam_interrupt_enabled and status.mode == 2) or + (status.vblank_interrupt_enabled and status.mode == 1) or + (status.hblank_interrupt_enabled and status.mode == 0) + + -- If this is a *rising* edge, raise the LCDStat interrupt + if graphics.lcdstat == false and lcdstat == true then + interrupts.raise(interrupts.LCDStat) + end + + graphics.lcdstat = lcdstat + end + + io.write_logic[ports.LY] = function(byte) + -- LY, writes reset the counter + io.ram[ports.LY] = 0 + graphics.refresh_lcdstat() + end + + io.write_logic[ports.LYC] = function(byte) + -- LY, writes reset the counter + io.ram[ports.LYC] = byte + graphics.refresh_lcdstat() + end + + -- HBlank: Period between scanlines + local handle_mode = {} + handle_mode[0] = function() + if timers.system_clock - graphics.last_edge > 204 then + graphics.last_edge = graphics.last_edge + 204 + io.ram[ports.LY] = io.ram[ports.LY] + 1 + if io.ram[ports.LY] == io.ram[ports.LYC] then + -- set the LY compare bit + io.ram[ports.STAT] = bit32.bor(io.ram[ports.STAT], 0x4) + else + -- clear the LY compare bit + io.ram[ports.STAT] = bit32.band(io.ram[ports.STAT], 0xFB) + end + + if io.ram[ports.LY] >= 144 then + graphics.registers.status.SetMode(1) + graphics.vblank_count = graphics.vblank_count + 1 + interrupts.raise(interrupts.VBlank) + else + graphics.registers.status.SetMode(2) + end + + graphics.refresh_lcdstat() + else + graphics.next_edge = graphics.last_edge + 204 + end + end + + --VBlank: nothing to do except wait for the next frame + handle_mode[1] = function() + if timers.system_clock - graphics.last_edge > 456 then + graphics.last_edge = graphics.last_edge + 456 + io.ram[ports.LY] = io.ram[ports.LY] + 1 + graphics.refresh_lcdstat() + else + graphics.next_edge = graphics.last_edge + 456 + end + + if io.ram[ports.LY] >= 154 then + io.ram[ports.LY] = 0 + graphics.initialize_frame() + graphics.registers.status.SetMode(2) + graphics.refresh_lcdstat() + end + + if io.ram[ports.LY] == io.ram[ports.LYC] then + -- set the LY compare bit + io.ram[ports.STAT] = bit32.bor(io.ram[ports.STAT], 0x4) + else + -- clear the LY compare bit + io.ram[ports.STAT] = bit32.band(io.ram[ports.STAT], 0xFB) + end + end + + -- OAM Read: OAM cannot be accessed + handle_mode[2] = function() + if timers.system_clock - graphics.last_edge > 80 then + graphics.last_edge = graphics.last_edge + 80 + graphics.initialize_scanline() + graphics.registers.status.SetMode(3) + graphics.refresh_lcdstat() + else + graphics.next_edge = graphics.last_edge + 80 + end + end + -- VRAM Read: Neither VRAM, OAM, nor CGB palettes can be read + handle_mode[3] = function() + local duration = timers.system_clock - graphics.last_edge + graphics.draw_next_pixels(duration) + if timers.system_clock - graphics.last_edge > 172 then + graphics.last_edge = graphics.last_edge + 172 + graphics.draw_sprites_into_scanline(io.ram[ports.LY], scanline_data.bg_index, scanline_data.bg_priority) + graphics.registers.status.SetMode(0) + -- If enabled, fire an HBlank interrupt + graphics.refresh_lcdstat() + -- If the hblank dma is active, copy the next block + dma.do_hblank() + else + graphics.next_edge = graphics.last_edge + 172 + end + end + + graphics.update = function() + if graphics.registers.display_enabled then + handle_mode[graphics.registers.status.mode]() + else + -- erase our clock debt, so we don't do stupid timing things when the + -- display is enabled again later + graphics.last_edge = timers.system_clock + graphics.next_edge = timers.system_clock + graphics.registers.status.SetMode(0) + io.ram[ports.LY] = 0 + graphics.refresh_lcdstat() + end + end + + local function plot_pixel(buffer, x, y, r, g, b) + buffer[y][x][1] = r + buffer[y][x][2] = g + buffer[y][x][3] = b + end + + local frame_data = {} + frame_data.window_pos_y = 0 + frame_data.window_draw_y = 0 + + graphics.initialize_frame = function() + -- latch WY at the beginning of the *frame* + frame_data.window_pos_y = io.ram[ports.WY] + frame_data.window_draw_y = 0 + end + + graphics.initialize_scanline = function() + scanline_data.x = 0 + + scanline_data.bg_tile_x = math.floor(io.ram[ports.SCX] / 8) + scanline_data.bg_tile_y = math.floor((io.ram[ports.LY] + io.ram[ports.SCY]) / 8) + if scanline_data.bg_tile_y >= 32 then + scanline_data.bg_tile_y = scanline_data.bg_tile_y - 32 + end + + scanline_data.sub_x = io.ram[ports.SCX] % 8 + scanline_data.sub_y = (io.ram[ports.LY] + io.ram[ports.SCY]) % 8 + + scanline_data.current_map = graphics.registers.background_tilemap + scanline_data.current_map_attr = graphics.registers.background_attr + + scanline_data.active_attr = scanline_data.current_map_attr[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + scanline_data.active_tile = scanline_data.current_map[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + scanline_data.window_active = false + end + + graphics.switch_to_window = function() + local ly = io.ram[ports.LY] + local w_x = io.ram[ports.WX] - 7 + if graphics.registers.window_enabled and scanline_data.x >= w_x and ly >= frame_data.window_pos_y then + -- switch to window map + scanline_data.current_map = graphics.registers.window_tilemap + scanline_data.current_map_attr = graphics.registers.window_attr + scanline_data.bg_tile_x = math.floor((scanline_data.x - w_x) / 8) + scanline_data.bg_tile_y = math.floor(frame_data.window_draw_y / 8) + scanline_data.sub_x = (scanline_data.x - w_x) % 8 + scanline_data.sub_y = (frame_data.window_draw_y) % 8 + frame_data.window_draw_y = frame_data.window_draw_y + 1 + if frame_data.window_draw_y > 143 then + frame_data.window_draw_y = 143 + end + + scanline_data.active_attr = scanline_data.current_map_attr[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + scanline_data.active_tile = scanline_data.current_map[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + scanline_data.window_active = true + end + end + + graphics.draw_next_pixels = function(duration) + local ly = io.ram[ports.LY] + local game_screen = graphics.game_screen + + while scanline_data.x < duration and scanline_data.x < 160 do + local dx = scanline_data.x + if not scanline_data.window_active then + graphics.switch_to_window() + end + + local bg_index = 0 --default, in case no background is enabled + if graphics.registers.background_enabled then + -- DRAW BG PIXEL HERE + local sub_x = scanline_data.sub_x + local sub_y = scanline_data.sub_y + bg_index = scanline_data.active_tile[sub_x][sub_y] + local active_palette = scanline_data.active_attr.palette[bg_index] + game_screen[ly][dx][1] = active_palette[1] + game_screen[ly][dx][2] = active_palette[2] + game_screen[ly][dx][3] = active_palette[3] + end + + scanline_data.bg_index[scanline_data.x] = bg_index + scanline_data.bg_priority[scanline_data.x] = scanline_data.active_attr.priority + + scanline_data.x = scanline_data.x + 1 + scanline_data.sub_x = scanline_data.sub_x + 1 + if scanline_data.sub_x > 7 then + -- fetch next tile + scanline_data.sub_x = 0 + scanline_data.bg_tile_x = scanline_data.bg_tile_x + 1 + if scanline_data.bg_tile_x >= 32 then + scanline_data.bg_tile_x = scanline_data.bg_tile_x - 32 + end + if not scanline_data.window_active then + scanline_data.sub_y = (ly + io.ram[ports.SCY]) % 8 + scanline_data.bg_tile_y = math.floor((ly + io.ram[ports.SCY]) / 8) + if scanline_data.bg_tile_y >= 32 then + scanline_data.bg_tile_y = scanline_data.bg_tile_y - 32 + end + end + + local tile_attr = scanline_data.current_map_attr[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + if tile_attr.vertical_flip then + scanline_data.sub_y = 7 - scanline_data.sub_y + end + + scanline_data.active_attr = scanline_data.current_map_attr[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + scanline_data.active_tile = scanline_data.current_map[scanline_data.bg_tile_x][scanline_data.bg_tile_y] + end + end + end + + graphics.getIndexFromTilemap = function(map, tile_data, x, y) + local tile_x = bit32.rshift(x, 3) + local tile_y = bit32.rshift(y, 3) + local tile_index = map[tile_x][tile_y] + + local subpixel_x = x - (tile_x * 8) + local subpixel_y = y - (tile_y * 8) + + if tile_data == 0x9000 then + if tile_index > 127 then + tile_index = tile_index - 256 + end + -- add offset to re-root at tile 256 (so effectively, we read from tile 192 - 384) + tile_index = tile_index + 256 + end + + if graphics.gameboy.type == graphics.gameboy.types.color then + local map_attr = graphics.cache.map_0_attr + if map == graphics.cache.map_1 then + map_attr = graphics.cache.map_1_attr + end + local tile_attributes = map_attr[tile_x][tile_y] + tile_index = tile_index + tile_attributes.bank * 384 + + if tile_attributes.horizontal_flip == true then + subpixel_x = (7 - subpixel_x) + end + + if tile_attributes.vertical_flip == true then + subpixel_y = (7 - subpixel_y) + end + end + + return graphics.cache.tiles[tile_index][subpixel_x][subpixel_y] + end + + graphics.draw_sprites_into_scanline = function(scanline, bg_index, bg_priority) + if not graphics.registers.sprites_enabled then + return + end + local active_sprites = {} + local sprite_size = 8 + if graphics.registers.large_sprites then + sprite_size = 16 + end + + -- Collect up to the 10 highest priority sprites in a list. + -- Sprites have priority first by their X coordinate, then by their index + -- in the list. + local i = 0 + while i < 40 do + -- is this sprite being displayed on this scanline? (respect to Y coordinate) + local sprite_y = graphics.cache.oam[i].y + local sprite_lower = sprite_y + local sprite_upper = sprite_y + sprite_size + if scanline >= sprite_lower and scanline < sprite_upper then + if #active_sprites < 10 then + table.insert(active_sprites, i) + else + -- There are more than 10 sprites in the table, so we need to pick + -- a candidate to vote off the island (possibly this one) + local lowest_priority = i + local lowest_priotity_index = nil + for j = 1, #active_sprites do + local lowest_x = graphics.cache.oam[lowest_priority].x + local candidate_x = graphics.cache.oam[active_sprites[j]].x + if candidate_x > lowest_x then + lowest_priority = active_sprites[j] + lowest_priority_index = j + end + end + if lowest_priority_index then + active_sprites[lowest_priority_index] = i + end + end + end + i = i + 1 + end + + -- now, for every sprite in the list, display it on the current scanline + for i = #active_sprites, 1, -1 do + local sprite = graphics.cache.oam[active_sprites[i]] + + local sub_y = 16 - ((sprite.y + 16) - scanline) + if sprite.vertical_flip then + sub_y = sprite_size - 1 - sub_y + end + + local tile = sprite.tile + if sprite_size == 16 then + tile = sprite.upper_tile + if sub_y >= 8 then + tile = sprite.lower_tile + sub_y = sub_y - 8 + end + end + + local game_screen = graphics.game_screen + + for x = 0, 7 do + local display_x = sprite.x + x + if display_x >= 0 and display_x < 160 then + local sub_x = x + if x_flipped then + sub_x = 7 - x + end + local subpixel_index = tile[sub_x][sub_y] + if subpixel_index > 0 then + if (bg_priority[display_x] == false and not sprite.bg_priority) or bg_index[display_x] == 0 or graphics.registers.oam_priority then + local subpixel_color = sprite.palette[subpixel_index] + game_screen[scanline][display_x][1] = subpixel_color[1] + game_screen[scanline][display_x][2] = subpixel_color[2] + game_screen[scanline][display_x][3] = subpixel_color[3] + end + end + end + end + end + if #active_sprites > 0 then + end + end + + return graphics +end + +return Graphics +]]> + -1 + + + + + + + + palette + {15770DCE-BD51-4287-98DF-EC76E57AA33E} + 0 do + palette = bit32.rshift(palette, 2) + index = index - 1 + end + return dmg_colors[bit32.band(palette, 0x3)] + end + + -- DMG palettes + io.write_logic[ports.BGP] = function(byte) + io.ram[ports.BGP] = byte + for i = 0, 3 do + palette.bg[i] = getColorFromIndex(i, byte) + end + graphics.update() + end + + io.write_logic[ports.OBP0] = function(byte) + io.ram[ports.OBP0] = byte + for i = 0, 3 do + palette.obj0[i] = getColorFromIndex(i, byte) + end + graphics.update() + end + + io.write_logic[ports.OBP1] = function(byte) + io.ram[ports.OBP1] = byte + for i = 0, 3 do + palette.obj1[i] = getColorFromIndex(i, byte) + end + graphics.update() + end + + palette.color_bg_index = 0 + palette.color_bg_auto_increment = false + palette.color_obj_index = 0 + palette.color_obj_auto_increment = false + + -- Color Palettes + io.write_logic[0x68] = function(byte) + io.ram[0x68] = byte + palette.color_bg_index = bit32.band(byte, 0x3F) + palette.color_bg_auto_increment = bit32.band(byte, 0x80) ~= 0 + end + + io.write_logic[0x69] = function(byte) + palette.color_bg_raw[palette.color_bg_index] = byte + + -- Update the palette cache for this byte pair + local low_byte = palette.color_bg_raw[bit32.band(palette.color_bg_index, 0xFE)] + local high_byte = palette.color_bg_raw[bit32.band(palette.color_bg_index, 0xFE) + 1] + local rgb5_color = bit32.lshift(high_byte, 8) + low_byte + local r = bit32.band(rgb5_color, 0x001F) * 8 + local g = bit32.rshift(bit32.band(rgb5_color, 0x03E0), 5) * 8 + local b = bit32.rshift(bit32.band(rgb5_color, 0x7C00), 10) * 8 + local palette_index = math.floor(palette.color_bg_index / 8) + local color_index = math.floor((palette.color_bg_index % 8) / 2) + palette.color_bg[palette_index][color_index] = {r, g, b} + + if palette.color_bg_auto_increment then + palette.color_bg_index = palette.color_bg_index + 1 + if palette.color_bg_index > 63 then + palette.color_bg_index = 0 + end + end + end + + io.read_logic[0x69] = function() + return palette.color_bg_raw[palette.color_bg_index] + end + + io.write_logic[0x6A] = function(byte) + io.ram[0x6A] = byte + palette.color_obj_index = bit32.band(byte, 0x3F) + palette.color_obj_auto_increment = bit32.band(byte, 0x80) ~= 0 + end + + io.write_logic[0x6B] = function(byte) + palette.color_obj_raw[palette.color_obj_index] = byte + + -- Update the palette cache for this byte pair + local low_byte = palette.color_obj_raw[bit32.band(palette.color_obj_index, 0xFE)] + local high_byte = palette.color_obj_raw[bit32.band(palette.color_obj_index, 0xFE) + 1] + local rgb5_color = bit32.lshift(high_byte, 8) + low_byte + local r = bit32.band(rgb5_color, 0x001F) * 8 + local g = bit32.rshift(bit32.band(rgb5_color, 0x03E0), 5) * 8 + local b = bit32.rshift(bit32.band(rgb5_color, 0x7C00), 10) * 8 + local palette_index = math.floor(palette.color_obj_index / 8) + local color_index = math.floor((palette.color_obj_index % 8) / 2) + palette.color_obj[palette_index][color_index] = {r, g, b} + + if palette.color_obj_auto_increment then + palette.color_obj_index = palette.color_obj_index + 1 + if palette.color_obj_index > 63 then + palette.color_obj_index = 0 + end + end + end + + io.read_logic[0x6B] = function() + return palette.color_obj_raw[palette.color_obj_index] + end + + return palette +end + +return Palette +]]> + -1 + + + + + + + + registers + {FA9DE1DC-03FD-4FC3-8A5C-547692097148} + + -1 + + + + + + + + mbc + -1 + + + + + + + mbc1 + {00E06D01-2022-4A88-9EA3-AA66ADE5F325} + = 0x4000 and address <= 0x7FFF then + local rom_bank = mbc1.rom_bank + if mbc1.mode == 0 then + rom_bank = rom_bank + bit32.lshift(mbc1.ram_bank, 5) + end + return mbc1.raw_data[(rom_bank * 16 * 1024) + (address - 0x4000)] + end + + if address >= 0xA000 and address <= 0xBFFF and mbc1.ram_enable then + local ram_bank = 0 + if mbc1.mode == 1 then + ram_bank = mbc1.ram_bank + end + return mbc1.external_ram[(address - 0xA000) + (ram_bank * 8 * 1024)] + end + return 0x00 + end + mbc1.mt.__newindex = function(table, address, value) + if address <= 0x1FFF then + if bit32.band(0x0A, value) == 0x0A then + mbc1.ram_enable = true + else + mbc1.ram_enable = false + end + return + end + if address >= 0x2000 and address <= 0x3FFF then + -- Select the lower 5 bits of the ROM bank + -- HARDWARE BUG: bank 0 is translated into bank 1 for weird reasons + value = bit32.band(value, 0x1F) + if value == 0 then + value = 1 + end + mbc1.rom_bank = value + return + end + if address >= 0x4000 and address <= 0x5FFF then + mbc1.ram_bank = bit32.band(value, 0x03) + return + end + if address >= 0x6000 and address <= 0x7FFF then + mbc1.mode = bit32.band(value, 0x01) + return + end + + -- Handle actually writing to External RAM + if address >= 0xA000 and address <= 0xBFFF and mbc1.ram_enable then + local ram_bank = 0 + if mbc1.mode == 1 then + ram_bank = mbc1.ram_bank + end + mbc1.external_ram[(address - 0xA000) + (ram_bank * 8 * 1024)] = value + mbc1.external_ram.dirty = true + return + end + end + + mbc1.reset = function(self) + self.rom_bank = 1 + self.ram_bank = 0 + self.mode = 0 + self.ram_enable = false + end + + mbc1.save_state = function(self) + return { + rom_bank = self.rom_bank, + ram_bank = self.ram_bank, + mode = self.mode, + ram_enable = self.ram_enable} + end + + mbc1.load_state = function(self, state_data) + self:reset() + + self.rom_bank = state_data.rom_bank + self.ram_bank = state_data.ram_bank + self.mode = state_data.mode + self.ram_enable = state_data.ram_enable + end + + setmetatable(mbc1, mbc1.mt) + + return mbc1 +end + +return Mbc1 +]]> + -1 + + + + + + + + mbc2 + {784B3294-1806-465B-9D50-F3E972EE43A0} + = 0x4000 and address <= 0x7FFF then + local rom_bank = mbc2.rom_bank + return mbc2.raw_data[(rom_bank * 16 * 1024) + (address - 0x4000)] + end + + if address >= 0xA000 and address <= 0xA1FF and mbc2.ram_enable then + -- For MBC2, only the lower 4 bits of each RAM byte are available for use + return bit32.band(0x0F, mbc2.external_ram[(address - 0xA000)]) + end + + return 0x00 + end + mbc2.mt.__newindex = function(table, address, value) + if address <= 0x1FFF and bit32.band(address, 0x0100) == 0 then + if bit32.band(0x0A, value) == 0x0A then + mbc2.ram_enable = true + else + mbc2.ram_enable = false + end + return + end + if address >= 0x2000 and address <= 0x3FFF and bit32.band(address, 0x0100) ~= 0 then + -- Select the ROM bank (4 bits) + value = bit32.band(value, 0x0F) + if value == 0 then + value = 1 + end + mbc2.rom_bank = value + return + end + + -- Handle actually writing to External RAM + if address >= 0xA000 and address <= 0xBFFF and mbc2.ram_enable then + mbc2.external_ram[(address - 0xA000)] = bit32.band(0x0F, value) + mbc2.external_ram.dirty = true + return + end + end + + mbc2.reset = function(self) + self.rom_bank = 1 + self.ram_enable = false + end + + mbc2.save_state = function(self) + return {rom_bank = self.rom_bank, ram_enable = self.ram_enable} + end + + mbc2.load_state = function(self, state_data) + self:reset() + + self.rom_bank = state_data.rom_bank + self.ram_enable = state_data.ram_enable + end + + setmetatable(mbc2, mbc2.mt) + + return mbc2 +end + +return Mbc2 +]]> + -1 + + + + + + + + mbc3 + {25CD4E50-1F46-4136-AB68-28FBDACDDEB0} + = 0x4000 and address <= 0x7FFF then + local rom_bank = mbc3.rom_bank + return mbc3.raw_data[(rom_bank * 16 * 1024) + (address - 0x4000)] + end + + if address >= 0xA000 and address <= 0xBFFF and mbc3.ram_enable then + local ram_bank = mbc3.ram_bank + if (ram_bank >= 0x08 and ram_bank <= 0x0C) then + return mbc3.rtc:get_register(mbc3.rtc.rambank_selected) + else + return mbc3.external_ram[(address - 0xA000) + (ram_bank * 8 * 1024)] + end + end + return 0x00 + end + mbc3.mt.__newindex = function(table, address, value) + if address <= 0x1FFF then + if bit32.band(0x0A, value) == 0x0A then + mbc3.ram_enable = true + else + mbc3.ram_enable = false + end + return + end + if address >= 0x2000 and address <= 0x3FFF then + -- Select the lower 7 bits of the ROM bank + value = bit32.band(value, 0x7F) + if value == 0 then + value = 1 + end + mbc3.rom_bank = value + return + end + if address >= 0x4000 and address <= 0x5FFF then + mbc3.rtc_enable = false + if value <= 0x03 then + mbc3.ram_bank = bit32.band(value, 0x03) + return + end + if value >= 0x08 and value <= 0x0C then + mbc3.rtc_enable = true + mbc3.rtc_select = value + return + end + end + if address >= 0x6000 and address <= 0x8000 then + mbc3.rtc:write_command(value) + return + end + + + -- Handle actually writing to External RAM + if address >= 0xA000 and address <= 0xC000 and mbc3.ram_enable then + if (mbc3.rtc_select <= 0x03) then + local ram_bank = mbc3.ram_bank + mbc3.external_ram[(address - 0xA000) + (ram_bank * 8 * 1024)] = value + mbc3.external_ram.dirty = true + elseif mbc3.rtc_select >= 0x08 and mbc3.rtc_select <= 0x0c then + mbc3.rtc:set_register(mbc3.rtc_select, value) + else + print("Invalid RAM bank selected.") + end + + return + end + end + + mbc3.reset = function(self) + self.rom_bank = 1 + self.ram_bank = 0 + self.ram_enable = false + self.rtc_enable = false + self.rtc_select = 0x08 + end + + mbc3.save_state = function(self) + return { + rom_bank = self.rom_bank, + ram_bank = self.ram_bank, + ram_enable = self.ram_enable, + rtc_enable = self.rtc_enable, + rtc_select = self.rtc_enable} + end + + mbc3.load_state = function(self, state_data) + self:reset() + + self.rom_bank = state_data.rom_bank + self.ram_bank = state_data.ram_bank + self.ram_enable = state_data.ram_enable + self.rtc_enable = state_data.rtc_enable + self.rtc_select = state_data.rtc_select + end + + setmetatable(mbc3, mbc3.mt) + + return mbc3 +end + +return Mbc3 +]]> + -1 + + + + + + + + mbc5 + {05D6D2C9-CD26-41E5-BEE5-8A2BCB5AA22D} + = 0x4000 and address <= 0x7FFF then + local rom_bank = mbc5.rom_bank + return mbc5.raw_data[(rom_bank * 16 * 1024) + (address - 0x4000)] + end + + if address >= 0xA000 and address <= 0xBFFF and mbc5.ram_enable then + local ram_bank = mbc5.ram_bank + return mbc5.external_ram[(address - 0xA000) + (ram_bank * 8 * 1024)] + end + return 0x00 + end + mbc5.mt.__newindex = function(table, address, value) + if address <= 0x1FFF then + if bit32.band(0x0A, value) == 0x0A then + mbc5.ram_enable = true + else + mbc5.ram_enable = false + end + return + end + if address >= 0x2000 and address <= 0x2FFF then + -- Write the lower 8 bits of the ROM bank + mbc5.rom_bank = bit32.band(mbc5.rom_bank, 0xFF00) + value + return + end + if address >= 0x3000 and address <= 0x3FFF then + if mbc5.header.rom_size > (4096 * 1024) then + -- This is a >4MB game, so set the high bit of the bank select + mbc5.rom_bank = bit32.band(mbc5.rom_bank, 0xFF) + bit32.lshift(bit32.band(value, 0x01), 8) + else + -- This is a <= 4MB game. Do nothing! + end + return + end + if address >= 0x4000 and address <= 0x5FFF then + local ram_mask = 0x0F + if mbc5.rumble_pak then + ram_mask = 0x7 + end + mbc5.ram_bank = bit32.band(value, ram_mask) + if bit32.band(value, 0x08) ~= 0 and mbc5.rumbling == false then + --print("Rumble on!") + mbc5.rumbling = true + end + if bit32.band(value, 0x08) ~= 0 and mbc5.rumbling == true then + --print("Rumble off!") + mbc5.rumbling = false + end + return + end + + -- Handle actually writing to External RAM + if address >= 0xA000 and address <= 0xBFFF and mbc5.ram_enable then + local ram_bank = mbc5.ram_bank + mbc5.external_ram[(address - 0xA000) + (ram_bank * 8 * 1024)] = value + mbc5.external_ram.dirty = true + return + end + end + + mbc5.reset = function(self) + self.rom_bank = 1 + self.ram_bank = 0 + self.ram_enable = false + end + + mbc5.save_state = function(self) + return { + rom_bank = self.rom_bank, + ram_bank = self.ram_bank, + ram_enable = self.ram_enable, + rumble_pak = self.rumble_pak} + end + + mbc5.load_state = function(self, state_data) + self:reset() + + self.rom_bank = state_data.rom_bank + self.ram_bank = state_data.ram_bank + self.ram_enable = state_data.ram_enable + self.rumble_pak = state_data.rumble_pak + self.rumbling = false + end + + setmetatable(mbc5, mbc5.mt) + + return mbc5 +end + +return Mbc5 +]]> + -1 + + + + + + + + none + {240F0A84-C535-459E-8BE4-8C1A1206753D} + + -1 + + + + + + + + z80 + -1 + + + + + + + arithmetic + {5BF906B8-F3C8-49FD-AF62-E9A7E14E2FCF} + 0xF + + local sum = reg.a + value + + -- carry (and overflow correction) + flags.c = sum > 0xFF + + reg.a = band(sum, 0xFF) + + flags.z = reg.a == 0 + flags.n = false + end + + local adc_to_a = function(value) + -- half-carry + local carry = 0 + if flags.c then + carry = 1 + end + flags.h = band(reg.a, 0xF) + band(value, 0xF) + carry > 0xF + local sum = reg.a + value + carry + + -- carry (and overflow correction) + flags.c = sum > 0xFF + reg.a = band(sum, 0xFF) + + flags.z = reg.a == 0 + flags.n = false + end + + -- add A, r + opcodes[0x80] = function() add_to_a(reg.b) end + opcodes[0x81] = function() add_to_a(reg.c) end + opcodes[0x82] = function() add_to_a(reg.d) end + opcodes[0x83] = function() add_to_a(reg.e) end + opcodes[0x84] = function() add_to_a(reg.h) end + opcodes[0x85] = function() add_to_a(reg.l) end + opcode_cycles[0x86] = 8 + opcodes[0x86] = function() add_to_a(read_at_hl()) end + opcodes[0x87] = function() add_to_a(reg.a) end + + -- add A, nn + opcode_cycles[0xC6] = 8 + opcodes[0xC6] = function() add_to_a(read_nn()) end + + -- adc A, r + opcodes[0x88] = function() adc_to_a(reg.b) end + opcodes[0x89] = function() adc_to_a(reg.c) end + opcodes[0x8A] = function() adc_to_a(reg.d) end + opcodes[0x8B] = function() adc_to_a(reg.e) end + opcodes[0x8C] = function() adc_to_a(reg.h) end + opcodes[0x8D] = function() adc_to_a(reg.l) end + opcode_cycles[0x8E] = 8 + opcodes[0x8E] = function() adc_to_a(read_at_hl()) end + opcodes[0x8F] = function() adc_to_a(reg.a) end + + -- adc A, nn + opcode_cycles[0xCE] = 8 + opcodes[0xCE] = function() adc_to_a(read_nn()) end + + sub_from_a = function(value) + -- half-carry + flags.h = band(reg.a, 0xF) - band(value, 0xF) < 0 + reg.a = reg.a - value + + -- carry (and overflow correction) + flags.c = reg.a < 0 or reg.a > 0xFF + reg.a = band(reg.a, 0xFF) + + flags.z = reg.a == 0 + flags.n = true + end + + sbc_from_a = function(value) + local carry = 0 + if flags.c then + carry = 1 + end + -- half-carry + flags.h = band(reg.a, 0xF) - band(value, 0xF) - carry < 0 + + local difference = reg.a - value - carry + + -- carry (and overflow correction) + flags.c = difference < 0 or difference > 0xFF + reg.a = band(difference, 0xFF) + + flags.z = reg.a == 0 + flags.n = true + end + + -- sub A, r + opcodes[0x90] = function() sub_from_a(reg.b) end + opcodes[0x91] = function() sub_from_a(reg.c) end + opcodes[0x92] = function() sub_from_a(reg.d) end + opcodes[0x93] = function() sub_from_a(reg.e) end + opcodes[0x94] = function() sub_from_a(reg.h) end + opcodes[0x95] = function() sub_from_a(reg.l) end + opcode_cycles[0x96] = 8 + opcodes[0x96] = function() sub_from_a(read_at_hl()) end + opcodes[0x97] = function() sub_from_a(reg.a) end + + -- sub A, nn + opcode_cycles[0xD6] = 8 + opcodes[0xD6] = function() sub_from_a(read_nn()) end + + -- sbc A, r + opcodes[0x98] = function() sbc_from_a(reg.b) end + opcodes[0x99] = function() sbc_from_a(reg.c) end + opcodes[0x9A] = function() sbc_from_a(reg.d) end + opcodes[0x9B] = function() sbc_from_a(reg.e) end + opcodes[0x9C] = function() sbc_from_a(reg.h) end + opcodes[0x9D] = function() sbc_from_a(reg.l) end + opcode_cycles[0x9E] = 8 + opcodes[0x9E] = function() sbc_from_a(read_at_hl()) end + opcodes[0x9F] = function() sbc_from_a(reg.a) end + + -- sbc A, nn + opcode_cycles[0xDE] = 8 + opcodes[0xDE] = function() sbc_from_a(read_nn()) end + + -- daa + -- BCD adjustment, correct implementation details located here: + -- http://www.z80.info/z80syntx.htm#DAA + opcodes[0x27] = function() + local a = reg.a + if not flags.n then + -- Addition Mode, adjust BCD for previous addition-like instruction + if band(0xF, a) > 0x9 or flags.h then + a = a + 0x6 + end + if a > 0x9F or flags.c then + a = a + 0x60 + end + else + -- Subtraction mode! Adjust BCD for previous subtraction-like instruction + if flags.h then + a = band(a - 0x6, 0xFF) + end + if flags.c then + a = a - 0x60 + end + end + -- Always reset H and Z + flags.h = false + flags.z = false + + -- If a is greater than 0xFF, set the carry flag + if band(0x100, a) == 0x100 then + flags.c = true + end + -- Note: Do NOT clear the carry flag otherwise. This is how hardware + -- behaves, yes it's weird. + + reg.a = band(a, 0xFF) + -- Update zero flag based on A's contents + flags.z = reg.a == 0 + end + + add_to_hl = function(value) + -- half carry + flags.h = band(reg.hl(), 0xFFF) + band(value, 0xFFF) > 0xFFF + local sum = reg.hl() + value + + -- carry + flags.c = sum > 0xFFFF or sum < 0x0000 + reg.set_hl(band(sum, 0xFFFF)) + flags.n = false + end + + -- add HL, rr + opcode_cycles[0x09] = 8 + opcode_cycles[0x19] = 8 + opcode_cycles[0x29] = 8 + opcode_cycles[0x39] = 8 + opcodes[0x09] = function() add_to_hl(reg.bc()) end + opcodes[0x19] = function() add_to_hl(reg.de()) end + opcodes[0x29] = function() add_to_hl(reg.hl()) end + opcodes[0x39] = function() add_to_hl(reg.sp) end + + -- inc rr + opcode_cycles[0x03] = 8 + opcodes[0x03] = function() + reg.set_bc(band(reg.bc() + 1, 0xFFFF)) + end + + opcode_cycles[0x13] = 8 + opcodes[0x13] = function() + reg.set_de(band(reg.de() + 1, 0xFFFF)) + end + + opcode_cycles[0x23] = 8 + opcodes[0x23] = function() + reg.set_hl(band(reg.hl() + 1, 0xFFFF)) + end + + opcode_cycles[0x33] = 8 + opcodes[0x33] = function() + reg.sp = band(reg.sp + 1, 0xFFFF) + end + + -- dec rr + opcode_cycles[0x0B] = 8 + opcodes[0x0B] = function() + reg.set_bc(band(reg.bc() - 1, 0xFFFF)) + end + + opcode_cycles[0x1B] = 8 + opcodes[0x1B] = function() + reg.set_de(band(reg.de() - 1, 0xFFFF)) + end + + opcode_cycles[0x2B] = 8 + opcodes[0x2B] = function() + reg.set_hl(band(reg.hl() - 1, 0xFFFF)) + end + + opcode_cycles[0x3B] = 8 + opcodes[0x3B] = function() + reg.sp = band(reg.sp - 1, 0xFFFF) + end + + -- add SP, dd + opcode_cycles[0xE8] = 16 + opcodes[0xE8] = function() + local offset = read_nn() + -- offset comes in as unsigned 0-255, so convert it to signed -128 - 127 + if band(offset, 0x80) ~= 0 then + offset = offset + 0xFF00 + end + + -- half carry + --if band(reg.sp, 0xFFF) + offset > 0xFFF or band(reg.sp, 0xFFF) + offset < 0 then + flags.h = band(reg.sp, 0xF) + band(offset, 0xF) > 0xF + -- carry + flags.c = band(reg.sp, 0xFF) + band(offset, 0xFF) > 0xFF + + reg.sp = reg.sp + offset + reg.sp = band(reg.sp, 0xFFFF) + + flags.z = false + flags.n = false + end +end + +return apply +]]> + -1 + + + + + + + + bitwise + {71FA55A5-F268-4794-8D1A-F9E72E12A343} + + -1 + + + + + + + + call + {3F3FBEDE-E0EE-4606-B40E-E455BE5B3E06} + + -1 + + + + + + + + cp + {29F98401-912F-4B53-8DF9-3631BBB429DF} + 0xFF + temp = (temp + 0x100) % 0x100 + + flags.z = temp == 0 + flags.n = true + end + + -- cp A, r + opcodes[0xB8] = function() cp_with_a(reg.b) end + opcodes[0xB9] = function() cp_with_a(reg.c) end + opcodes[0xBA] = function() cp_with_a(reg.d) end + opcodes[0xBB] = function() cp_with_a(reg.e) end + opcodes[0xBC] = function() cp_with_a(reg.h) end + opcodes[0xBD] = function() cp_with_a(reg.l) end + opcode_cycles[0xBE] = 8 + opcodes[0xBE] = function() cp_with_a(read_at_hl()) end + opcodes[0xBF] = function() cp_with_a(reg.a) end + + -- cp A, nn + opcode_cycles[0xFE] = 8 + opcodes[0xFE] = function() cp_with_a(read_nn()) end +end + +return apply +]]> + -1 + + + + + + + + inc_dec + {4E20D4ED-8DC1-4D04-AE9F-0E4ED974F51E} + + -1 + + + + + + + + init + {99668368-32F7-4CE8-9CB2-3C8DD715E316} + + -1 + + + + + + + + jp + {D57B7504-9C30-413A-B092-9752415253DA} + 127 then + offset = offset - 256 + end + reg.pc = (reg.pc + offset) % 0x10000 + end + + -- jr nn + opcode_cycles[0x18] = 12 + opcodes[0x18] = function() + jump_relative_to_nn() + end + + -- jr nz, nn + opcode_cycles[0x20] = 12 + opcodes[0x20] = function() + if not flags.z then + jump_relative_to_nn() + else + reg.pc = reg.pc + 1 + z80.add_cycles(-4) + end + end + + -- jr nc, nn + opcode_cycles[0x30] = 12 + opcodes[0x30] = function() + if not flags.c then + jump_relative_to_nn() + else + reg.pc = reg.pc + 1 + z80.add_cycles(-4) + end + end + + -- jr z, nn + opcode_cycles[0x28] = 12 + opcodes[0x28] = function() + if flags.z then + jump_relative_to_nn() + else + reg.pc = reg.pc + 1 + z80.add_cycles(-4) + end + end + + -- jr c, nn + opcode_cycles[0x38] = 12 + opcodes[0x38] = function() + if flags.c then + jump_relative_to_nn() + else + reg.pc = reg.pc + 1 + z80.add_cycles(-4) + end + end +end + +return apply +]]> + -1 + + + + + + + + ld + {1E66ED1D-7959-4D14-AE01-D81F8C733A9E} + + -1 + + + + + + + + registers + {4C42D996-EF35-4529-BA0E-202CAB943B9D} + + -1 + + + + + + + + rl_rr_cb + {C24379CF-3954-4669-AF7C-3DB516DFF850} + + -1 + + + + + + + + stack + {066D0796-0BA2-49C1-81EF-1EDAD8860CAC} + + -1 + + + + + + + + + audio + {85C46E73-D061-4719-8E9B-0F7227284076} + 0 then + audio.tone1.volume_direction = 1 + else + audio.tone1.volume_direction = -1 + end + local envelope_step_data = bit32.band(byte, 0x07) + local envelope_step_cycles = envelope_step_data * 65536 + audio.tone1.volume_step_length = envelope_step_cycles + end + + -- Channel 1 Frequency - Low Bits + io.write_logic[ports.NR13] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR13] = byte + local freq_high = bit32.lshift(bit32.band(io.ram[ports.NR14], 0x07), 8) + local freq_low = byte + local freq_value = freq_high + freq_low + audio.tone1.period = 32 * (2048 - freq_value) + audio.tone1.frequency_shadow = freq_value + end + + -- Channel 1 Frequency and Trigger - High Bits + io.write_logic[ports.NR14] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR14] = byte + local restart = (bit32.band(byte, 0x80) ~= 0) + local continuous = (bit32.band(byte, 0x40) == 0) + local freq_high = bit32.lshift(bit32.band(byte, 0x07), 8) + local freq_low = io.ram[ports.NR13] + local freq_value = freq_high + freq_low + + audio.tone1.period = 32 * (2048 - freq_value) + audio.tone1.continuous = continuous + if restart then + audio.tone1.base_cycle = timers.system_clock + audio.tone1.active = true + end + audio.tone1.frequency_shadow = freq_value + audio.tone1.period_conter = (2048 - freq_value) + audio.tone1.frequency_shift_counter = 0 + end + + -- Channel 2 Sound Length / Wave Pattern Duty + io.write_logic[ports.NR21] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR21] = byte + local wave_pattern = bit32.rshift(bit32.band(byte, 0xC0), 6) + audio.tone2.duty_length = wave_patterns[wave_pattern] + audio.tone2.wave_pattern = wave_pattern + local length_data = bit32.band(byte, 0x3F) + local length_cycles = (64 - length_data) * 16384 + audio.tone2.max_length = length_cycles + end + + -- Channel 2 Volume Envelope + io.write_logic[ports.NR22] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR22] = byte + audio.tone2.volume_initial = bit32.rshift(bit32.band(byte, 0xF0), 4) + local direction = bit32.band(byte, 0x08) + if direction > 0 then + audio.tone2.volume_direction = 1 + else + audio.tone2.volume_direction = -1 + end + local envelope_step_data = bit32.band(byte, 0x07) + local envelope_step_cycles = envelope_step_data * 65536 + audio.tone2.volume_step_length = envelope_step_cycles + end + + -- Channel 2 Frequency - Low Bits + io.write_logic[ports.NR23] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR23] = byte + local freq_high = bit32.lshift(bit32.band(io.ram[ports.NR24], 0x07), 8) + local freq_low = byte + local freq_value = freq_high + freq_low + audio.tone2.period = 32 * (2048 - freq_value) + audio.tone2.frequency_shadow = freq_value + end + + -- Channel 2 Frequency and Trigger - High Bits + io.write_logic[ports.NR24] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR24] = byte + local restart = (bit32.band(byte, 0x80) ~= 0) + local continuous = (bit32.band(byte, 0x40) == 0) + local freq_high = bit32.lshift(bit32.band(byte, 0x07), 8) + local freq_low = io.ram[ports.NR23] + local freq_value = freq_high + freq_low + + audio.tone2.period = 32 * (2048 - freq_value) + audio.tone2.period_conter = (2048 - freq_value) + audio.tone2.frequency_shadow = freq_value + audio.tone2.continuous = continuous + if restart then + audio.tone2.base_cycle = timers.system_clock + audio.tone2.active = true + end + end + + -- Channel 3 Enabled + io.write_logic[ports.NR30] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR30] = byte + audio.wave3.enabled = bit32.band(byte, 0x80) ~= 0 + end + + -- Channel 3 Length + io.write_logic[ports.NR31] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR31] = byte + local length_cycles = (256 - byte) * 4096 + audio.wave3.max_length = length_cycles + end + + -- Channel 3 Volume + local volume_shift_mappings = {} + volume_shift_mappings[0] = 4 + volume_shift_mappings[1] = 0 + volume_shift_mappings[2] = 1 + volume_shift_mappings[3] = 2 + io.write_logic[ports.NR32] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR32] = byte + local volume_select = bit32.rshift(bit32.band(byte, 0x60), 5) + audio.wave3.volume_shift = volume_shift_mappings[volume_select] + end + + -- Channel 3 Frequency - Low Bits + io.write_logic[ports.NR33] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR33] = byte + local freq_high = bit32.lshift(bit32.band(io.ram[ports.NR34], 0x07), 8) + local freq_low = byte + local freq_value = freq_high + freq_low + audio.wave3.period = 64 * (2048 - freq_value) + audio.wave3.frequency_shadow = freq_value + end + + -- Channel 3 Frequency and Trigger - High Bits + io.write_logic[ports.NR34] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR34] = byte + local restart = (bit32.band(byte, 0x80) ~= 0) + local continuous = (bit32.band(byte, 0x40) == 0) + local freq_high = bit32.lshift(bit32.band(byte, 0x07), 8) + local freq_low = io.ram[ports.NR33] + local freq_value = freq_high + freq_low + + audio.wave3.period = 64 * (2048 - freq_value) + audio.wave3.period_conter = (2048 - freq_value) + audio.wave3.frequency_shadow = freq_value + audio.wave3.continuous = continuous + if restart then + audio.wave3.base_cycle = timers.system_clock + audio.wave3.sample_index = 0 + audio.wave3.active = true + end + end + + -- Channel 4 Length + io.write_logic[ports.NR41] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR41] = byte + local wave_pattern = bit32.rshift(bit32.band(byte, 0xC0), 6) + audio.noise4.duty_length = wave_patterns[wave_pattern] + local length_data = bit32.band(byte, 0x3F) + local length_cycles = (64 - length_data) * 16384 + audio.noise4.max_length = length_cycles + end + + -- Channel 4 Volume Envelope + io.write_logic[ports.NR42] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR42] = byte + audio.noise4.volume_initial = bit32.rshift(bit32.band(byte, 0xF0), 4) + local direction = bit32.band(byte, 0x08) + if direction > 0 then + audio.noise4.volume_direction = 1 + else + audio.noise4.volume_direction = -1 + end + local envelope_step_data = bit32.band(byte, 0x07) + local envelope_step_cycles = envelope_step_data * 65536 + audio.noise4.volume_step_length = envelope_step_cycles + end + + local polynomial_divisors = {} + polynomial_divisors[0] = 8 + polynomial_divisors[1] = 16 + polynomial_divisors[2] = 32 + polynomial_divisors[3] = 48 + polynomial_divisors[4] = 64 + polynomial_divisors[5] = 80 + polynomial_divisors[6] = 96 + polynomial_divisors[7] = 112 + + -- Channel 4 Polynomial Counter + io.write_logic[ports.NR43] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR43] = byte + local shift_clock_frequency = bit32.rshift(bit32.band(byte, 0xF0), 4) + local wide_step = bit32.band(byte, 0x08) == 0 + local dividing_ratio = polynomial_divisors[bit32.band(byte, 0x07)] + + -- Maybe? + audio.noise4.polynomial_period = bit32.lshift(dividing_ratio, shift_clock_frequency) + audio.noise4.polynomial_wide = wide_step + end + + -- Channel 4 Trigger + io.write_logic[ports.NR44] = function(byte) + audio.generate_pending_samples() + io.ram[ports.NR44] = byte + local restart = (bit32.band(byte, 0x80) ~= 0) + local continuous = (bit32.band(byte, 0x40) == 0) + + audio.noise4.continuous = continuous + if restart then + audio.noise4.base_cycle = timers.system_clock + -- Reset the LSFR to all 1's + audio.noise4.polynomial_lfsr = 0x7FFF + audio.noise4.active = true + end + end + + audio.tone1.update_frequency_shift = function(clock_cycle) + local tone1 = audio.tone1 + -- A shift_time of 0 disables frequency shifting entirely + if tone1.frequency_shift_time > 0 then + local next_edge = tone1.base_cycle + tone1.frequency_shift_time * tone1.frequency_shift_counter + if clock_cycle >= next_edge then + local adjustment = bit32.rshift(tone1.frequency_shadow, tone1.frequency_shift_amount) * tone1.frequency_shift_direction + tone1.frequency_shadow = tone1.frequency_shadow + adjustment + if tone1.frequency_shadow >= 2048 then + tone1.frequency_shadow = 2047 + tone1.active = false + end + tone1.period = 32 * (2048 - tone1.frequency_shadow) + tone1.frequency_shift_counter = tone1.frequency_shift_counter + 1 + end + end + end + + audio.noise4.update_lfsr = function(clock_cycle) + --print(clock_cycle - audio.noise4.polynomial_last_shift) + --print(audio.noise4.polynomial_period) + while clock_cycle - audio.noise4.polynomial_last_shift > audio.noise4.polynomial_period do + local lfsr = audio.noise4.polynomial_lfsr + -- Grab the lowest two bits in LSFR and XOR them together + local bit0 = bit32.band(lfsr, 0x1) + local bit1 = bit32.rshift(bit32.band(lfsr, 0x2), 1) + local xor = bit32.bxor(bit0, bit1) + -- Shift LSFR down by one + lfsr = bit32.rshift(lfsr, 1) + -- Place the XOR'd bit into the high bit (14) always + xor = bit32.lshift(xor, 14) + lfsr = bit32.bor(xor, lfsr) + if not audio.noise4.polynomial_wide then + -- place the XOR'd bit into bit 6 as well + xor = bit32.rshift(xor, 8) + lfsr = bit32.bor(xor, bit32.band(lfsr, 0x7FBF)) + end + audio.noise4.polynomial_last_shift = audio.noise4.polynomial_last_shift + audio.noise4.polynomial_period + audio.noise4.polynomial_lfsr = lfsr + end + end + + audio.tone1.generate_sample = function(clock_cycle) + audio.tone1.update_frequency_shift(clock_cycle) + local tone1 = audio.tone1 + local duration = clock_cycle - tone1.base_cycle + if tone1.continuous or (duration <= tone1.max_length) then + local volume = tone1.volume_initial + if tone1.volume_step_length > 0 then + volume = volume + tone1.volume_direction * math.floor(duration / tone1.volume_step_length) + end + if volume > 0 then + if volume > 0xF then + volume = 0xF + end + + while clock_cycle > tone1.frequency_last_update + 4 do + tone1.period_counter = tone1.period_counter - 1 + if tone1.period_counter <= 0 then + tone1.period_counter = (2048 - tone1.frequency_shadow) + tone1.wave_duty_counter = tone1.wave_duty_counter + 1 + if tone1.wave_duty_counter >= 8 then + tone1.wave_duty_counter = 0 + end + end + tone1.frequency_last_update = tone1.frequency_last_update + 4 + end + + if wave_pattern_tables[tone1.wave_pattern][tone1.wave_duty_counter + 1] == 0 then + return volume / 0xF * -1 + else + return volume / 0xF + end + end + else + audio.tone1.active = false + end + return 0 + end + + audio.tone2.generate_sample = function(clock_cycle) + local tone2 = audio.tone2 + local duration = clock_cycle - tone2.base_cycle + if tone2.continuous or (duration <= tone2.max_length) then + local volume = tone2.volume_initial + if tone2.volume_step_length > 0 then + volume = volume + tone2.volume_direction * math.floor(duration / tone2.volume_step_length) + end + if volume > 0 then + if volume > 0xF then + volume = 0xF + end + + while clock_cycle > tone2.frequency_last_update + 4 do + tone2.period_counter = tone2.period_counter - 1 + if tone2.period_counter <= 0 then + tone2.period_counter = (2048 - tone2.frequency_shadow) + tone2.wave_duty_counter = tone2.wave_duty_counter + 1 + if tone2.wave_duty_counter >= 8 then + tone2.wave_duty_counter = 0 + end + end + tone2.frequency_last_update = tone2.frequency_last_update + 4 + end + + if wave_pattern_tables[tone2.wave_pattern][tone2.wave_duty_counter + 1] == 0 then + return volume / 0xF * -1 + else + return volume / 0xF + end + end + else + tone2.active = false + end + return 0 + end + + audio.wave3.generate_sample = function(clock_cycle) + local wave3 = audio.wave3 + local duration = clock_cycle - wave3.base_cycle + if wave3.enabled then + if wave3.continuous or (duration <= wave3.max_length) then + --local period = wave3.period + --local period_progress = (duration % period) / (period) + --local sample_index = math.floor(period_progress * 32) + while clock_cycle > wave3.frequency_last_update + 2 do + wave3.period_counter = wave3.period_counter - 1 + if wave3.period_counter <= 0 then + wave3.period_counter = (2048 - wave3.frequency_shadow) + wave3.sample_index = wave3.sample_index + 1 + if wave3.sample_index >= 32 then + wave3.sample_index = 0 + end + end + wave3.frequency_last_update = wave3.frequency_last_update + 2 + end + + local byte_index = bit32.rshift(wave3.sample_index, 1) + local sample = io.ram[0x30 + byte_index] + -- If this is an even numbered sample, shift the high nybble + -- to the lower nybble + if wave3.sample_index % 2 == 0 then + sample = bit32.rshift(sample, 4) + end + -- Regardless, mask out the lower nybble; this becomes our sample to play + sample = bit32.band(sample, 0x0F) + -- Shift the sample based on the volume parameter + sample = bit32.rshift(sample, wave3.volume_shift) + -- This sample will be from 0-15, we need to adjust it so that it's from -1 to 1 + sample = (sample - 8) / 8 + return sample + else + wave3.active = false + end + else + wave3.active = false + end + return 0 + end + + audio.noise4.generate_sample = function(clock_cycle) + audio.noise4.update_lfsr(clock_cycle) + local noise4 = audio.noise4 + local duration = clock_cycle - noise4.base_cycle + if noise4.continuous or (duration <= noise4.max_length) then + local volume = noise4.volume_initial + if noise4.volume_step_length > 0 then + volume = volume + noise4.volume_direction * math.floor(duration / noise4.volume_step_length) + end + if volume > 0 then + if volume > 0xF then + volume = 0xF + end + -- Output high / low is based on the INVERTED low bit of LFSR + if bit32.band(noise4.polynomial_lfsr, 0x1) == 0 then + return volume / 0xF + else + return volume / 0xF * -1 + end + end + else + noise4.active = false + end + return 0 + end + + audio.__on_buffer_full = function(buffer) end + + audio.debug = {} + audio.debug.current_sample = 0 + audio.debug.max_samples = 128 + audio.debug.tone1 = {} + audio.debug.tone2 = {} + audio.debug.wave3 = {} + audio.debug.noise4 = {} + audio.debug.final = {} + for i = 0, audio.debug.max_samples do + audio.debug.tone1[i] = 0 + audio.debug.tone2[i] = 0 + audio.debug.wave3[i] = 0 + audio.debug.noise4[i] = 0 + audio.debug.final[i] = 0 + end + + audio.save_debug_samples = function(tone1, tone2, wave3, noise4, final) + local debug = audio.debug + debug.tone1[debug.current_sample] = tone1 + debug.tone2[debug.current_sample] = tone2 + debug.wave3[debug.current_sample] = wave3 + debug.noise4[debug.current_sample] = noise4 + debug.final[debug.current_sample] = final + debug.current_sample = debug.current_sample + 1 + if debug.current_sample >= debug.max_samples then + debug.current_sample = 0 + end + end + + audio.debug.enabled = false + + audio.generate_pending_samples = function() + while next_sample_cycle < timers.system_clock do + local tone1 = audio.tone1.generate_sample(next_sample_cycle) + local tone2 = audio.tone2.generate_sample(next_sample_cycle) + local wave3 = audio.wave3.generate_sample(next_sample_cycle) + local noise4 = audio.noise4.generate_sample(next_sample_cycle) + + local sample_left = 0 + local sample_right = 0 + + local channels_enabled = io.ram[ports.NR51] + if bit32.band(channels_enabled, 0x80) ~= 0 and not audio.noise4.debug_disabled then + sample_right = sample_right + noise4 + end + if bit32.band(channels_enabled, 0x40) ~= 0 and not audio.wave3.debug_disabled then + sample_right = sample_right + wave3 + end + if bit32.band(channels_enabled, 0x20) ~= 0 and not audio.tone2.debug_disabled then + sample_right = sample_right + tone2 + end + if bit32.band(channels_enabled, 0x10) ~= 0 and not audio.tone1.debug_disabled then + sample_right = sample_right + tone1 + end + + if bit32.band(channels_enabled, 0x08) ~= 0 and not audio.noise4.debug_disabled then + sample_left = sample_left + noise4 + end + if bit32.band(channels_enabled, 0x04) ~= 0 and not audio.wave3.debug_disabled then + sample_left = sample_left + wave3 + end + if bit32.band(channels_enabled, 0x02) ~= 0 and not audio.tone2.debug_disabled then + sample_left = sample_left + tone2 + end + if bit32.band(channels_enabled, 0x01) ~= 0 and not audio.tone1.debug_disabled then + sample_left = sample_left + tone1 + end + + sample_right = sample_right / 4 + sample_left = sample_left / 4 + + if audio.debug.enabled then + -- Debug in mono + audio.save_debug_samples(tone1, tone2, wave3, noise4, (tone1 + tone2 + wave3 + noise4) / 4) + end + + -- Left/Right Channel Volume + local right_volume = bit32.rshift(bit32.band(io.ram[ports.NR50], 0x70), 4) + local left_volume = bit32.band(io.ram[ports.NR50], 0x07) + + sample_right = sample_right * right_volume / 7 + sample_left = sample_left * left_volume / 7 + + audio.buffer[next_sample] = sample_left + next_sample = next_sample + 1 + audio.buffer[next_sample] = sample_right + next_sample = next_sample + 1 + if next_sample >= 1024 then + audio.__on_buffer_full(audio.buffer) + next_sample = 0 + end + next_sample_cycle = next_sample_cycle + 128 --number of clocks per sample at 32 KHz + end + end + + audio.on_buffer_full = function(callback) + audio.__on_buffer_full = callback + end + + audio.update = function() + audio.generate_pending_samples() + end + + return audio +end + +return Audio +]]> + -1 + + + + + + + + cartridge + {4D0BCB7B-8249-489F-AC9C-6218472DF1A0} + + -1 + + + + + + + + dma + {86E35824-2126-4B4B-9BB4-BA4E7AF070D7} + %04X Length: %04X", dma.source, dma.destination, dma.length)) + end + end + + return dma +end + +return Dma +]]> + -1 + + + + + + + + init + {8E33F481-D7B7-4A5E-8679-D2510662A4A5} + self.graphics.next_edge then + self.graphics.update() + end + self.processor.process_instruction() + return +end + +function Gameboy:run_until_vblank() + local instructions = 0 + while self.io.ram[self.io.ports.LY] == 144 and instructions < 100000 do + self:step() + instructions = instructions + 1 + end + while self.io.ram[self.io.ports.LY] ~= 144 and instructions < 100000 do + self:step() + instructions = instructions + 1 + end + self.audio.update() +end + +function Gameboy:run_until_hblank() + local old_scanline = self.io.ram[self.io.ports.LY] + local instructions = 0 + while old_scanline == self.io.ram[self.io.ports.LY] and instructions < 100000 do + self:step() + instructions = instructions + 1 + end + self.audio.update() +end + +local call_opcodes = {[0xCD]=true, [0xC4]=true, [0xD4]=true, [0xCC]=true, [0xDC]=true} +local rst_opcodes = {[0xC7]=true, [0xCF]=true, [0xD7]=true, [0xDF]=true, [0xE7]=true, [0xEF]=true, [0xF7]=true, [0xFF]=true} + +function Gameboy:step_over() + -- Make sure the *current* opcode is a CALL / RST + local instructions = 0 + local pc = self.processor.registers.pc + local opcode = self.memory[pc] + if call_opcodes[opcode] then + local return_address = bit32.band(pc + 3, 0xFFFF) + while self.processor.registers.pc ~= return_address and instructions < 10000000 do + self:step() + instructions = instructions + 1 + end + return + end + if rst_opcodes[opcode] then + local return_address = bit32.band(pc + 1, 0xFFFF) + while self.processor.registers.pc ~= return_address and instructions < 10000000 do + self:step() + instructions = instructions + 1 + end + return + end + print("Not a CALL / RST opcode! Bailing.") +end + +local ret_opcodes = {[0xC9]=true, [0xC0]=true, [0xD0]=true, [0xC8]=true, [0xD8]=true, [0xD9]=true} + +function Gameboy:run_until_ret() + local instructions = 0 + while ret_opcodes[self.memory[self.processor.registers.pc]] ~= true and instructions < 10000000 do + self:step() + instructions = instructions + 1 + end +end + +local gameboy_defaults = {} +for k, v in pairs(Gameboy) do + gameboy_defaults[k] = v +end + +Gameboy.new = function(overrides) + local new_gameboy = {} + for k, v in pairs(gameboy_defaults) do + if overrides[k] then + new_gameboy[k] = overrides[k] + else + new_gameboy[k] = gameboy_defaults[k] + end + end + + new_gameboy.memory = new_gameboy.memory.new(new_gameboy) + + new_gameboy.io = new_gameboy.io.new(new_gameboy) + + new_gameboy.interrupts = new_gameboy.interrupts.new(new_gameboy) + + new_gameboy.timers = new_gameboy.timers.new(new_gameboy) + + new_gameboy.audio = new_gameboy.audio.new(new_gameboy) + new_gameboy.cartridge = new_gameboy.cartridge.new(new_gameboy) + new_gameboy.dma = new_gameboy.dma.new(new_gameboy) + new_gameboy.graphics = new_gameboy.graphics.new(new_gameboy) + new_gameboy.input = new_gameboy.input.new(new_gameboy) + new_gameboy.processor = new_gameboy.processor.new(new_gameboy) + + new_gameboy:initialize() + + return new_gameboy +end + +return Gameboy +]]> + -1 + + + + + + + + input + {DF43BADD-0B35-4D88-B3BD-FC8612F8C4D1} + 128 then + if command_bits[128] ~= 0 then + print("Invalid command! 129th bit was not 0") + end + local command, length, parameters = decode_snes_command(command_bits) + local command_name = snes_packet_names[command] or "UNKNOWN!!" + print("SNES Command: " .. command_name .. " [" .. hex(command) .. "] Length: " .. length) + local hex_params = hex(parameters[1]) + for i = 2, 15 do + hex_params = hex_params .. " " .. hex(parameters[i]) + end + print("SNES Parameters: ", hex_params) + command_started = false + end + end + else + -- Check to see if we are starting a new command + if pulse == 0x3 and last_write == 0x0 then + command_started = true + command_index = 0 + end + end + + last_write = pulse + end + + return input +end + +return Input +]]> + -1 + + + + + + + + interrupts + {FE8C0D64-9833-41BA-A6FC-79AD0F66F82C} + + -1 + + + + + + + + io + {C9D2E070-213F-40CB-AD11-8A9E0ED60E48} + + -1 + + + + + + + + memory + {EB64DB04-4433-4978-912F-C60F87B613B7} + 0xFF or ending_high_byte > 0xFF then + print("Bad block, bailing", starting_high_byte, ending_high_byte) + return + end + + --starting_address = starting_address or bit32.lshift(starting_high_byte, 8) + for i = starting_high_byte, ending_high_byte do + --block_map[bit32.lshift(i, 8)] = {start=starting_address, block=mapped_block} + block_map[bit32.lshift(i, 8)] = mapped_block + end + end + + memory.generate_block = function(size, starting_address) + starting_address = starting_address or 0 + local block = {} + for i = 0, size - 1 do + block[starting_address + i] = 0 + end + return block + end + + -- Default, unmapped memory + memory.unmapped = {} + memory.unmapped.mt = {} + memory.unmapped.mt.__index = function(table, key) + return 0x00 + end + memory.unmapped.mt.__newindex = function(table, key, value) + -- Do nothing! + end + setmetatable(memory.unmapped, memory.unmapped.mt) + memory.map_block(0, 0xFF, memory.unmapped) + + -- Main Memory + memory.work_ram_0 = memory.generate_block(4 * 1024, 0xC000) + memory.work_ram_1_raw = memory.generate_block(4 * 7 * 1024, 0xD000) + memory.work_ram_1 = {} + memory.work_ram_1.bank = 1 + memory.work_ram_1.mt = {} + memory.work_ram_1.mt.__index = function(table, address) + return memory.work_ram_1_raw[address + ((memory.work_ram_1.bank - 1) * 4 * 1024)] + end + memory.work_ram_1.mt.__newindex = function(table, address, value) + memory.work_ram_1_raw[address + ((memory.work_ram_1.bank - 1) * 4 * 1024)] = value + end + setmetatable(memory.work_ram_1, memory.work_ram_1.mt) + memory.map_block(0xC0, 0xCF, memory.work_ram_0, 0) + memory.map_block(0xD0, 0xDF, memory.work_ram_1, 0) + + memory.work_ram_echo = {} + memory.work_ram_echo.mt = {} + memory.work_ram_echo.mt.__index = function(table, key) + return memory.read_byte(key - 0xE000 + 0xC000) + end + memory.work_ram_echo.mt.__newindex = function(table, key, value) + memory.write_byte(key - 0xE000 + 0xC000, value) + end + setmetatable(memory.work_ram_echo, memory.work_ram_echo.mt) + memory.map_block(0xE0, 0xFD, memory.work_ram_echo, 0) + + memory.read_byte = function(address) + local high_byte = bit32.band(address, 0xFF00) + return block_map[high_byte][address] + end + + memory.write_byte = function(address, byte) + local high_byte = bit32.band(address, 0xFF00) + block_map[high_byte][address] = byte + end + + memory.reset = function() + -- It's tempting to want to zero out all 0x0000-0xFFFF, but + -- instead here we'll reset only that memory which this module + -- DIRECTLY controls, so initialization logic can be performed + -- elsewhere as appropriate. + + for i = 0xC000, 0xCFFF do + memory.work_ram_0[i] = 0 + end + + for i = 0xD000, 0xDFFF do + memory.work_ram_1[i] = 0 + end + + memory.work_ram_1.bank = 1 + end + + memory.save_state = function() + local state = {} + + state.work_ram_0 = {} + for i = 0xC000, 0xCFFF do + state.work_ram_0[i] = memory.work_ram_0[i] + end + + state.work_ram_1_raw = {} + for i = 0xD000, (0xD000 + (4 * 7 * 1024) - 1) do + state.work_ram_1_raw[i] = memory.work_ram_1_raw[i] + end + + state.work_ram_1_bank = 1 + + return state + end + + memory.load_state = function(state) + for i = 0xC000, 0xCFFF do + memory.work_ram_0[i] = state.work_ram_0[i] + end + for i = 0xD000, (0xD000 + (4 * 7 * 1024) - 1) do + memory.work_ram_1_raw[i] = state.work_ram_1_raw[i] + end + + memory.work_ram_1.bank = state.work_ram_1_bank + end + + -- Fancy: make access to ourselves act as an array, reading / writing memory using the above + -- logic. This should cause memory[address] to behave just as it would on hardware. + memory.mt = {} + memory.mt.__index = function(table, key) + return memory.read_byte(key) + end + memory.mt.__newindex = function(table, key, value) + memory.write_byte(key, value) + end + setmetatable(memory, memory.mt) + + return memory +end + +return Memory +]]> + -1 + + + + + + + + opcode_names + {A2395DEF-9548-4DE3-B502-CCB5E469FCFD} + + -1 + + + + + + + + rom_header + {AE95DF07-5198-449C-9C21-28CE1F3202FB} + + -1 + + + + + + + + timers + {0E79B5B3-37F3-414D-8D9F-107FAC886BEE} + self.timer_offset + self.clock_rates[rate_select] do + io.ram[io.ports.TIMA] = bit32.band(io.ram[io.ports.TIMA] + 1, 0xFF) + self.timer_offset = self.timer_offset + self.clock_rates[rate_select] + if io.ram[io.ports.TIMA] == 0x00 then + --overflow happened, first reset TIMA to TMA + io.ram[io.ports.TIMA] = io.ram[io.ports.TMA] + --then, fire off the timer interrupt + interrupts.raise(interrupts.Timer) + end + end + end + end + + function timers:reset() + self.system_clock = 0 + self.div_base = 0 + self.timer_offset = 0 + self.timer_enabled = false + end + + function timers:save_state() + return { + system_clock = self.system_clock, + div_base = self.div_base, + timer_offset = self.timer_offset, + timer_enabled = self.timer_enabled} + end + + function timers:load_state(state) + self.system_clock = state.system_clock + self.div_base = state.div_base + self.timer_offset = state.timer_offset + self.timer_enabled = state.timer_enabled + end + + return timers +end + +return Timers +]]> + -1 + + + + + + + + rtc + {3653F087-9E5C-4C4F-A53C-4F9683057C7A} + 1) then + self.day_carry = 1 + self.day_latch_high = bit32.band(self.day_latch_high, 0b1) + self.timezero += 0x200 * 3600 * 24 + end + end + + rtc.write_command = function(self, value) + if (value == 0x00) then + self.latch_enabled = false + elseif (value == 0x01) then + if (not self.latch_enabled) then + print("Latching RTC") + self:latch_rtc() + end + self.latch_enabled = true + else + print("Invalid RTC Input: " .. value) + end + end + + rtc.get_register = function(self, register) + if (not self.latch_enabled) then + print("RTC attempted to get register, but latch is not enabled.") + end + + if (register == 0x08) then + return self.sec_latch + elseif (register == 0x09) then + return self.min_latch + elseif (register == 0x0A) then + return self.hour_latch + elseif (register == 0x0B) then + return self.day_latch_low + elseif (register == 0x0C) then + local day_high = bit32.band(self.day_latch_high, 0b1) + local halt = bit32.lshift(self.halt, 6) + local day_carry = bit32.lshift(self.day_carry, 7) + return day_high + halt + day_carry + else + print("Invalid RTC Register: " .. register) + end + end + + rtc.set_register = function(self, register, value) + if (not self.latch_enabled) then + print("RTC attempted to set register, but latch is not enabled.") + end + + local delta_time = os.time() - self.timezero + + if (register == 0x08) then + self.timezero = self.timezero - math.floor((delta_time % 60) + 0.5) - value + elseif (register == 0x09) then + self.timezero = math.floor((delta_time / 60 % 60) + 0.5) - value + elseif (register == 0x0A) then + self.timezero = math.floor((delta_time / 3600 % 24) + 0.5) - value + elseif (register == 0x0B) then + self.timezero = math.floor((delta_time / 3600 / 24) + 0.5) - value + elseif (register == 0x0C) then + local day_high = bit32.band(value, 0b1) + local halt = bit32.rshift(bit32.band(value, 0b1000000), 6) + local day_carry = bit32.rshift(bit32.band(value, 0b10000000), 7) + + self.halt = halt + if (self.halt == 0) then + + else + print("Stopping RTC not implemented.") + end + + self.timezero = self.timezero - math.floor((delta_time / 3600 / 24) + 0.5) - (bit32.lshift(day_high, 8)) + self.day_carry = day_carry + else + print("Invalid RTC Register: " .. register .. ", " .. value) + end + end + + return rtc +end + +return RTC +]]> + -1 + + + + + + + + vendor + -1 + + + + + + + binser + {6D671570-3740-4269-B7EA-124DE3C954AA} + len or x ~= floor(x) +end + +local function type_check(x, tp, name) + assert(type(x) == tp, + format("Expected parameter %q to be of type %q.", name, tp)) +end + +local bigIntSupport = false +local isInteger +if math.type then -- Detect Lua 5.3 + local mtype = math.type + bigIntSupport = loadstring[[ + local char = string.char + return function(n) + local nn = n < 0 and -(n + 1) or n + local b1 = nn // 0x100000000000000 + local b2 = nn // 0x1000000000000 % 0x100 + local b3 = nn // 0x10000000000 % 0x100 + local b4 = nn // 0x100000000 % 0x100 + local b5 = nn // 0x1000000 % 0x100 + local b6 = nn // 0x10000 % 0x100 + local b7 = nn // 0x100 % 0x100 + local b8 = nn % 0x100 + if n < 0 then + b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4 + b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8 + end + return char(212, b1, b2, b3, b4, b5, b6, b7, b8) + end]]() + isInteger = function(x) + return mtype(x) == 'integer' + end +else + isInteger = function(x) + return floor(x) == x + end +end + +-- Copyright (C) 2012-2015 Francois Perrad. +-- number serialization code modified from https://github.com/fperrad/lua-MessagePack +-- Encode a number as a big-endian ieee-754 double, big-endian signed 64 bit integer, or a small integer +local function number_to_str(n) + if isInteger(n) then -- int + if n <= 100 and n >= -27 then -- 1 byte, 7 bits of data + return char(n + 27) + elseif n <= 8191 and n >= -8192 then -- 2 bytes, 14 bits of data + n = n + 8192 + return char(128 + (floor(n / 0x100) % 0x100), n % 0x100) + elseif bigIntSupport then + return bigIntSupport(n) + end + end + local sign = 0 + if n < 0.0 then + sign = 0x80 + n = -n + end + local m, e = frexp(n) -- mantissa, exponent + if m ~= m then + return char(203, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + elseif m == 1/0 then + if sign == 0 then + return char(203, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + else + return char(203, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + end + end + e = e + 0x3FE + if e < 1 then -- denormalized numbers + m = m * 2 ^ (52 + e) + e = 0 + else + m = (m * 2 - 1) * 2 ^ 52 + end + return char(203, + sign + floor(e / 0x10), + (e % 0x10) * 0x10 + floor(m / 0x1000000000000), + floor(m / 0x10000000000) % 0x100, + floor(m / 0x100000000) % 0x100, + floor(m / 0x1000000) % 0x100, + floor(m / 0x10000) % 0x100, + floor(m / 0x100) % 0x100, + m % 0x100) +end + +-- Copyright (C) 2012-2015 Francois Perrad. +-- number deserialization code also modified from https://github.com/fperrad/lua-MessagePack +local function number_from_str(str, index) + local b = byte(str, index) + if b < 128 then + return b - 27, index + 1 + elseif b < 192 then + return byte(str, index + 1) + 0x100 * (b - 128) - 8192, index + 2 + end + local b1, b2, b3, b4, b5, b6, b7, b8 = byte(str, index + 1, index + 8) + if b == 212 then + local flip = b1 >= 128 + if flip then -- negative + b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4 + b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8 + end + local n = ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 + if flip then + return (-n) - 1, index + 9 + else + return n, index + 9 + end + end + local sign = b1 > 0x7F and -1 or 1 + local e = (b1 % 0x80) * 0x10 + floor(b2 / 0x10) + local m = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 + local n + if e == 0 then + if m == 0 then + n = sign * 0.0 + else + n = sign * (m / 2 ^ 52) * 2 ^ -1022 + end + elseif e == 0x7FF then + if m == 0 then + n = sign * (1/0) + else + n = 0.0/0.0 + end + else + n = sign * (1.0 + m / 2 ^ 52) * 2 ^ (e - 0x3FF) + end + return n, index + 9 +end + +local types = {} + +types["nil"] = function(x, visited, accum) + accum[#accum + 1] = "\202" +end + +function types.number(x, visited, accum) + accum[#accum + 1] = number_to_str(x) +end + +function types.boolean(x, visited, accum) + accum[#accum + 1] = x and "\204" or "\205" +end + +function types.string(x, visited, accum) + local alen = #accum + if visited[x] then + accum[alen + 1] = "\208" + accum[alen + 2] = number_to_str(visited[x]) + else + visited[x] = visited.next + visited.next = visited.next + 1 + accum[alen + 1] = "\206" + accum[alen + 2] = number_to_str(#x) + accum[alen + 3] = x + end +end + +local function check_custom_type(x, visited, accum) + local res = resources[x] + if res then + accum[#accum + 1] = "\211" + types[type(res)](res, visited, accum) + return true + end + local mt = getmetatable(x) + local id = mt and ids[mt] + if id then + if x == visited.temp then + error("Infinite loop in constructor.") + end + visited.temp = x + accum[#accum + 1] = "\209" + types[type(id)](id, visited, accum) + local args, len = pack(serializers[id](x)) + accum[#accum + 1] = number_to_str(len) + for i = 1, len do + local arg = args[i] + types[type(arg)](arg, visited, accum) + end + visited[x] = visited.next + visited.next = visited.next + 1 + return true + end +end + +function types.userdata(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, accum) then return end + error("Cannot serialize this userdata.") + end +end + +function types.table(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, accum) then return end + visited[x] = visited.next + visited.next = visited.next + 1 + local xlen = #x + accum[#accum + 1] = "\207" + accum[#accum + 1] = number_to_str(xlen) + for i = 1, xlen do + local v = x[i] + types[type(v)](v, visited, accum) + end + local key_count = 0 + for k in pairs(x) do + if not_array_index(k, xlen) then + key_count = key_count + 1 + end + end + accum[#accum + 1] = number_to_str(key_count) + for k, v in pairs(x) do + if not_array_index(k, xlen) then + types[type(k)](k, visited, accum) + types[type(v)](v, visited, accum) + end + end + end +end + +types["function"] = function(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, accum) then return end + visited[x] = visited.next + visited.next = visited.next + 1 + local str = string.dump(x) + accum[#accum + 1] = "\210" + accum[#accum + 1] = number_to_str(#str) + accum[#accum + 1] = str + end +end + +types.cdata = function(x, visited, accum) + if visited[x] then + accum[#accum + 1] = "\208" + accum[#accum + 1] = number_to_str(visited[x]) + else + if check_custom_type(x, visited, #accum) then return end + error("Cannot serialize this cdata.") + end +end + +types.thread = function() error("Cannot serialize threads.") end + +local function deserialize_value(str, index, visited) + local t = byte(str, index) + if not t then return end + if t < 128 then + return t - 27, index + 1 + elseif t < 192 then + return byte(str, index + 1) + 0x100 * (t - 128) - 8192, index + 2 + elseif t == 202 then + return nil, index + 1 + elseif t == 203 then + return number_from_str(str, index) + elseif t == 204 then + return true, index + 1 + elseif t == 205 then + return false, index + 1 + elseif t == 206 then + local length, dataindex = deserialize_value(str, index + 1, visited) + local nextindex = dataindex + length + local substr = sub(str, dataindex, nextindex - 1) + visited[#visited + 1] = substr + return substr, nextindex + elseif t == 207 then + local count, nextindex = number_from_str(str, index + 1) + local ret = {} + visited[#visited + 1] = ret + for i = 1, count do + ret[i], nextindex = deserialize_value(str, nextindex, visited) + end + count, nextindex = number_from_str(str, nextindex) + for i = 1, count do + local k, v + k, nextindex = deserialize_value(str, nextindex, visited) + v, nextindex = deserialize_value(str, nextindex, visited) + ret[k] = v + end + return ret, nextindex + elseif t == 208 then + local ref, nextindex = number_from_str(str, index + 1) + return visited[ref], nextindex + elseif t == 209 then + local count + local name, nextindex = deserialize_value(str, index + 1, visited) + count, nextindex = number_from_str(str, nextindex) + local args = {} + for i = 1, count do + args[i], nextindex = deserialize_value(str, nextindex, visited) + end + local ret = deserializers[name](unpack(args)) + visited[#visited + 1] = ret + return ret, nextindex + elseif t == 210 then + local length, dataindex = deserialize_value(str, index + 1, visited) + local nextindex = dataindex + length + local ret = loadstring(sub(str, dataindex, nextindex - 1)) + visited[#visited + 1] = ret + return ret, nextindex + elseif t == 211 then + local res, nextindex = deserialize_value(str, index + 1, visited) + return resources_by_name[res], nextindex + elseif t == 212 then + return number_from_str(str, index) + else + error("Could not deserialize type byte " .. t .. ".") + end +end + +local function serialize(...) + local visited = {next = 1} + local accum = {} + for i = 1, select("#", ...) do + local x = select(i, ...) + types[type(x)](x, visited, accum) + end + return concat(accum) +end + +local function make_file_writer(file) + return setmetatable({}, { + __newindex = function(_, _, v) + file:write(v) + end + }) +end + +local function serialize_to_file(path, mode, ...) + local file, err = io.open(path, mode) + assert(file, err) + local visited = {next = 1} + local accum = make_file_writer(file) + for i = 1, select("#", ...) do + local x = select(i, ...) + types[type(x)](x, visited, accum) + end + -- flush the writer + file:flush() + file:close() +end + +local function writeFile(path, ...) + return serialize_to_file(path, "wb", ...) +end + +local function appendFile(path, ...) + return serialize_to_file(path, "ab", ...) +end + +local function deserialize(str, index) + assert(type(str) == "string", "Expected string to deserialize.") + local vals = {} + index = index or 1 + local visited = {} + local len = 0 + local val + while index do + val, index = deserialize_value(str, index, visited) + if index then + len = len + 1 + vals[len] = val + end + end + return vals, len +end + +local function deserializeN(str, n, index) + assert(type(str) == "string", "Expected string to deserialize.") + n = n or 1 + assert(type(n) == "number", "Expected a number for parameter n.") + assert(n > 0 and floor(n) == n, "N must be a poitive integer.") + local vals = {} + index = index or 1 + local visited = {} + local len = 0 + local val + while index and len < n do + val, index = deserialize_value(str, index, visited) + if index then + len = len + 1 + vals[len] = val + end + end + vals[len + 1] = index + return unpack(vals, 1, n + 1) +end + +local function readFile(path) + local file, err = io.open(path, "rb") + assert(file, err) + local str = file:read("*all") + file:close() + return deserialize(str) +end + +local function default_deserialize(metatable) + return function(...) + local ret = {} + for i = 1, select("#", ...), 2 do + ret[select(i, ...)] = select(i + 1, ...) + end + return setmetatable(ret, metatable) + end +end + +local function default_serialize(x) + assert(type(x) == "table", + "Default serialization for custom types only works for tables.") + local args = {} + local len = 0 + for k, v in pairs(x) do + args[len + 1], args[len + 2] = k, v + len = len + 2 + end + return unpack(args, 1, len) +end + +-- Templating + +local function normalize_template(template) + local ret = {} + for i = 1, #template do + ret[i] = template[i] + end + local non_array_part = {} + -- The non-array part of the template (nested templates) have to be deterministic, so they are sorted. + -- This means that inherently non deterministicly sortable keys (tables, functions) should NOT be used + -- in templates. Looking for way around this. + for k in pairs(template) do + if not_array_index(k, #template) then + non_array_part[#non_array_part + 1] = k + end + end + table.sort(non_array_part) + for i = 1, #non_array_part do + local name = non_array_part[i] + ret[#ret + 1] = {name, normalize_template(template[name])} + end + return ret +end + +local function templatepart_serialize(part, argaccum, x, len) + local extras = {} + local extracount = 0 + for k, v in pairs(x) do + extras[k] = v + extracount = extracount + 1 + end + for i = 1, #part do + extracount = extracount - 1 + if type(part[i]) == "table" then + extras[part[i][1]] = nil + len = templatepart_serialize(part[i][2], argaccum, x[part[i][1]], len) + else + extras[part[i]] = nil + len = len + 1 + argaccum[len] = x[part[i]] + end + end + if extracount > 0 then + argaccum[len + 1] = extras + else + argaccum[len + 1] = nil + end + return len + 1 +end + +local function templatepart_deserialize(ret, part, values, vindex) + for i = 1, #part do + local name = part[i] + if type(name) == "table" then + local newret = {} + ret[name[1]] = newret + vindex = templatepart_deserialize(newret, name[2], values, vindex) + else + ret[name] = values[vindex] + vindex = vindex + 1 + end + end + local extras = values[vindex] + if extras then + for k, v in pairs(extras) do + ret[k] = v + end + end + return vindex + 1 +end + +local function template_serializer_and_deserializer(metatable, template) + return function(x) + argaccum = {} + local len = templatepart_serialize(template, argaccum, x, 0) + return unpack(argaccum, 1, len) + end, function(...) + local ret = {} + local len = select("#", ...) + local args = {...} + templatepart_deserialize(ret, template, args, 1) + return setmetatable(ret, metatable) + end +end + +local function register(metatable, name, serialize, deserialize) + name = name or metatable.name + serialize = serialize or metatable._serialize + deserialize = deserialize or metatable._deserialize + if not serialize then + if metatable._template then + local t = normalize_template(metatable._template) + serialize, deserialize = template_serializer_and_deserializer(metatable, t) + elseif not deserialize then + serialize = default_serialize + deserialize = default_deserialize(metatable) + else + serialize = metatable + end + end + type_check(metatable, "table", "metatable") + type_check(name, "string", "name") + type_check(serialize, "function", "serialize") + type_check(deserialize, "function", "deserialize") + assert(not ids[metatable], "Metatable already registered.") + assert(not mts[name], ("Name %q already registered."):format(name)) + mts[name] = metatable + ids[metatable] = name + serializers[name] = serialize + deserializers[name] = deserialize + return metatable +end + +local function unregister(item) + local name, metatable + if type(item) == "string" then -- assume name + name, metatable = item, mts[item] + else -- assume metatable + name, metatable = ids[item], item + end + type_check(name, "string", "name") + type_check(metatable, "table", "metatable") + mts[name] = nil + ids[metatable] = nil + serializers[name] = nil + deserializers[name] = nil + return metatable +end + +local function registerClass(class, name) + name = name or class.name + if class.__instanceDict then -- middleclass + register(class.__instanceDict, name) + else -- assume 30log or similar library + register(class, name) + end + return class +end + +local function registerResource(resource, name) + type_check(name, "string", "name") + assert(not resources[resource], + "Resource already registered.") + assert(not resources_by_name[name], + format("Resource %q already exists.", name)) + resources_by_name[name] = resource + resources[resource] = name + return resource +end + +local function unregisterResource(name) + type_check(name, "string", "name") + assert(resources_by_name[name], format("Resource %q does not exist.", name)) + local resource = resources_by_name[name] + resources_by_name[name] = nil + resources[resource] = nil + return resource +end + +return { + -- aliases + s = serialize, + d = deserialize, + dn = deserializeN, + r = readFile, + w = writeFile, + a = appendFile, + + serialize = serialize, + deserialize = deserialize, + deserializeN = deserializeN, + readFile = readFile, + writeFile = writeFile, + appendFile = appendFile, + register = register, + unregister = unregister, + registerResource = registerResource, + unregisterResource = unregisterResource, + registerClass = registerClass +} +]]> + -1 + + + + + + + + + init + {905581D3-B266-442B-B3A7-390EA62C7C93} + + -1 + + + + + + + + LICENSE + {A4EF7199-B481-4640-8569-74634185032C} + + -1 + + + + + + + + RawData + -1 + + + + + + + SetSaveState + -1 + + + + + + + GetSaveState + -1 + + + + + + + + canvasController + {408AB71A-D327-4C66-BC33-76C083ED1DDB} + + -1 + + + + + + + LinkCableSerial + -1 + + + + + + + UI + -1 + + + + + + + init + {0F42DB5C-8A59-4124-B0C7-2BA4228C9D03} + + -1 + + + + + + + + image + {ECA4B349-A58E-485E-AB50-7C921B775670} + + -1 + + + + + + + PNG + {69268804-9696-4DB7-9A26-96D161AEF5A2} + 0 then + data = reader:ForkReader(length) + crc = reader:ReadUInt32() + end + + local chunk = + { + Length = length; + Type = chunkType; + + Data = data; + CRC = crc; + } + + local handler = chunks:FindFirstChild(chunkType) + + if handler then + handler = require(handler) + handler(file, chunk) + end + + table.insert(file.Chunks, chunk) + end + + -- Decompress the zlib stream. + local success, response = pcall(function () + local result = {} + local index = 0 + + Deflate:InflateZlib + { + Input = BinaryReader.new(file.ZlibStream); + + Output = function (byte) + index = index + 1 + result[index] = string.char(byte) + end + } + + return table.concat(result) + end) + + if not success then + error("PNG - Unable to unpack PNG data. " .. tostring(response), 2) + end + + -- Grab expected info from the file. + + local width = file.Width + local height = file.Height + + local bitDepth = file.BitDepth + local colorType = file.ColorType + + local buffer = BinaryReader.new(response) + file.ZlibStream = nil + + local bitmap = {} + file.Bitmap = bitmap + + local channels = getBytesPerPixel(colorType) + file.NumChannels = channels + + local bpp = math.max(1, channels * (bitDepth / 8)) + file.BytesPerPixel = bpp + + -- Unfilter the buffer and + -- load it into the bitmap. + + for row = 1, height do + local filterType = buffer:ReadByte() + local scanline = buffer:ReadBytes(width * bpp, true) + + bitmap[row] = {} + + if filterType == 0 then + -- None + Unfilter:None(scanline, bitmap, bpp, row) + elseif filterType == 1 then + -- Sub + Unfilter:Sub(scanline, bitmap, bpp, row) + elseif filterType == 2 then + -- Up + Unfilter:Up(scanline, bitmap, bpp, row) + elseif filterType == 3 then + -- Average + Unfilter:Average(scanline, bitmap, bpp, row) + elseif filterType == 4 then + -- Paeth + Unfilter:Paeth(scanline, bitmap, bpp, row) + end + end + + return setmetatable(file, PNG) +end + +return PNG]]> + -1 + + + + + + Chunks + -1 + + + + + + + IEND + {42FE4A9C-36E3-4D97-AC5B-B90CBFAE48F0} + + -1 + + + + + + + + bKGD + {EA2C4A88-3323-41EB-85C0-9C63A8CFF36E} + + -1 + + + + + + + + sRGB + {8391331B-01A8-4871-A759-E4D691797281} + + -1 + + + + + + + + tRNS + {E67B6674-2AA9-4191-A2AC-EAC267731CFC} + + -1 + + + + + + + + IHDR + {EAA6C9B2-4C09-48AF-BEC4-14663497B112} + + -1 + + + + + + + + PLTE + {4F495F88-F6CB-4AAF-8DE7-82E2A14691AE} + + -1 + + + + + + + + tIME + {2EC4C071-DB5F-4B9F-B013-809C588E76AB} + + -1 + + + + + + + + IDAT + {02E98519-DA19-4498-80D3-240F08F168F9} + + -1 + + + + + + + + gAMA + {0228A408-87CD-4A99-8BBC-FD6E16EF3843} + + -1 + + + + + + + + cHRM + {8120E04C-A8A9-444E-A176-D385F58FB5A5} + + -1 + + + + + + + + tEXt + {941A630C-90B3-4428-8B0F-576DBB48AF5B} + + -1 + + + + + + + + Modules + -1 + + + + + + + BinaryReader + {9081C0A9-2147-4690-8CF3-F8086D127F47} + = (2 ^ (numBits - 1)) then + value = value - (2 ^ numBits) + end + + return value +end + +function BinaryReader:ReadUInt16() + local upper, lower = self:ReadBytes(2) + return (upper * 256) + lower +end + +function BinaryReader:ReadInt16() + local unsigned = self:ReadUInt16() + return self:TwosComplementOf(unsigned, 16) +end + +function BinaryReader:ReadUInt32() + local upper = self:ReadUInt16() + local lower = self:ReadUInt16() + + return (upper * 65536) + lower +end + +function BinaryReader:ReadInt32() + local unsigned = self:ReadUInt32() + return self:TwosComplementOf(unsigned, 32) +end + +function BinaryReader:ReadString(length) + if length == nil then + length = self:ReadByte() + end + + local pos = self.Position + local nextPos = math.min(self.Length, pos + length) + + local result = self.Buffer:sub(pos, nextPos - 1) + self.Position = nextPos + + return result +end + +function BinaryReader:ForkReader(length) + local chunk = self:ReadString(length) + return BinaryReader.new(chunk) +end + +return BinaryReader]]> + -1 + + + + + + + + Deflate + {3B782B4D-9266-471A-B3B6-0D7911B4A159} + 7 then + error("invalid zlib window size: cinfo=" .. cinfo) + end + + local windowSize = 2 ^ (cinfo + 8) + + if (cmf * 256 + flg) % 31 ~= 0 then + error("invalid zlib header (bad fcheck sum)") + end + + if fdict == 1 then + error("FIX:TODO - FDICT not currently implemented") + end + + return windowSize +end + +local function parseHuffmanTables(bitStream) + local numLits = bitStream:Read(5) -- # of literal/length codes - 257 + local numDists = bitStream:Read(5) -- # of distance codes - 1 + local numCodes = bitStream:Read(4) -- # of code length codes - 4 + + local codeLens = {} + + for i = 1, numCodes + 4 do + local index = order[i] + codeLens[index] = bitStream:Read(3) + end + + codeLens = createHuffmanTable(codeLens, true) + + local function decode(numCodes) + local init = {} + local numBits + local val = 0 + + while val < numCodes do + local codeLen = codeLens:Read(bitStream) + local numRepeats + + if codeLen <= 15 then + numRepeats = 1 + numBits = codeLen + elseif codeLen == 16 then + numRepeats = 3 + bitStream:Read(2) + elseif codeLen == 17 then + numRepeats = 3 + bitStream:Read(3) + numBits = 0 + elseif codeLen == 18 then + numRepeats = 11 + bitStream:Read(7) + numBits = 0 + end + + for i = 1, numRepeats do + init[val] = numBits + val = val + 1 + end + end + + return createHuffmanTable(init, true) + end + + local numLitCodes = numLits + 257 + local numDistCodes = numDists + 1 + + local litTable = decode(numLitCodes) + local distTable = decode(numDistCodes) + + return litTable, distTable +end + +local function parseCompressedItem(bitStream, state, litTable, distTable) + local val = litTable:Read(bitStream) + + if val < 256 then -- literal + write(state, val) + elseif val == 256 then -- end of block + return true + else + local lenBase = lens[val - 257] + local numExtraBits = lext[val - 257] + + local extraBits = bitStream:Read(numExtraBits) + local len = lenBase + extraBits + + local distVal = distTable:Read(bitStream) + local distBase = dists[distVal] + + local distNumExtraBits = dext[distVal] + local distExtraBits = bitStream:Read(distNumExtraBits) + + local dist = distBase + distExtraBits + + for i = 1, len do + local pos = (state.Pos - 1 - dist) % 32768 + 1 + local byte = assert(state.Window[pos], "invalid distance") + write(state, byte) + end + end + + return false +end + +local function parseBlock(bitStream, state) + local bFinal = bitStream:Read(1) + local bType = bitStream:Read(2) + + if bType == BTYPE_NO_COMPRESSION then + local left = bitStream:GetBitsLeft() + bitStream:Read(left) + + local len = bitStream:Read(16) + local nlen = bitStream:Read(16) + + for i = 1, len do + local byte = bitStream:Read(8) + write(state, byte) + end + elseif bType == BTYPE_FIXED_HUFFMAN or bType == BTYPE_DYNAMIC_HUFFMAN then + local litTable, distTable + + if bType == BTYPE_DYNAMIC_HUFFMAN then + litTable, distTable = parseHuffmanTables(bitStream) + else + litTable = createHuffmanTable(fixedLit) + distTable = createHuffmanTable(fixedDist) + end + + repeat until parseCompressedItem(bitStream, state, litTable, distTable) + else + error("unrecognized compression type") + end + + return bFinal ~= 0 +end + +function Deflate:Inflate(io) + local state = createState(io.Output) + local bitStream = getBitStream(io.Input) + + repeat until parseBlock(bitStream, state) +end + +function Deflate:InflateZlib(io) + local bitStream = getBitStream(io.Input) + local windowSize = parseZlibHeader(bitStream) + + self:Inflate + { + Input = bitStream; + Output = io.Output; + } + + local bitsLeft = bitStream:GetBitsLeft() + bitStream:Read(bitsLeft) +end + +return Deflate]]> + -1 + + + + + + + + Unfilter + {6CF373F1-F9ED-4733-83A8-BF63892332F5} + 1 then + local upperRow = pixels[row - 1] + + for i = 1, #scanline do + local x = scanline[i] + local b = upperRow[i] + pixels[row][i] = bit32.band(x + b, 0xFF) + end + else + self:None(scanline, pixels, bpp, row) + end +end + +function Unfilter:Average(scanline, pixels, bpp, row) + if row > 1 then + for i = 1, bpp do + local x = scanline[i] + local b = pixels[row - 1][i] + + b = bit32.rshift(b, 1) + pixels[row][i] = bit32.band(x + b, 0xFF) + end + + for i = bpp + 1, #scanline do + local x = scanline[i] + local b = pixels[row - 1][i] + + local a = pixels[row][i - bpp] + local ab = bit32.rshift(a + b, 1) + + pixels[row][i] = bit32.band(x + ab, 0xFF) + end + else + for i = 1, bpp do + pixels[row][i] = scanline[i] + end + + for i = bpp + 1, #scanline do + local x = scanline[i] + local b = pixels[row - 1][i] + + b = bit32.rshift(b, 1) + pixels[row][i] = bit32.band(x + b, 0xFF) + end + end +end + +function Unfilter:Paeth(scanline, pixels, bpp, row) + if row > 1 then + local pr + + for i = 1, bpp do + local x = scanline[i] + local b = pixels[row - 1][i] + pixels[row][i] = bit32.band(x + b, 0xFF) + end + + for i = bpp + 1, #scanline do + local a = pixels[row][i - bpp] + local b = pixels[row - 1][i] + local c = pixels[row - 1][i - bpp] + + local x = scanline[i] + local p = a + b - c + + local pa = math.abs(p - a) + local pb = math.abs(p - b) + local pc = math.abs(p - c) + + if pa <= pb and pa <= pc then + pr = a + elseif pb <= pc then + pr = b + else + pr = c + end + + pixels[row][i] = bit32.band(x + pr, 0xFF) + end + else + self:Sub(scanline, pixels, bpp, row) + end +end + +return Unfilter]]> + -1 + + + + + + + + + + + GetROMs + -1 + + + + + + + SaveROM + -1 + + + + + + + ROMs + -1 + + + + + + gurbsadventure.gb + -1 + + https://cdn.discordapp.com/attachments/800465522227544094/831275037465378826/GurbsAdventure.gb + + + + + + pk91.gb + -1 + + https://cdn.discordapp.com/attachments/800465522227544094/831275534427619338/pk91.gb + + + + + + tir2.gb + -1 + + https://cdn.discordapp.com/attachments/800465522227544094/831278522236010537/tir2.gb + + + + + + dcqid1.gb + -1 + + https://cdn.discordapp.com/attachments/800465522227544094/831308447122587678/dcqid1.gb + + + + + + + DisabledROMs + -1 + + + + + + deadeus.gb + -1 + + https://cdn.discordapp.com/attachments/800465522227544094/830606346410786826/Deadeus.gb + + + + + + + + Instance + -1 + + + + + + + Instance + -1 + + + + + + + 0.203921586 + 0.203921586 + 0.203921586 + + + 1 + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 0 + 0 + 0 + + 0.752941251 + 0.752941251 + 0.752941251 + + 100000 + 0 + 41.7332993 + true + Lighting + + 0.0470588282 + 0.0470588282 + 0.0470588282 + + false + 0.200000003 + -1 + + 2 + 06:00:00 + + + + + false + 0 + rbxassetid://0 + Sky + rbxassetid://3595370552 + rbxassetid://3595370552 + rbxassetid://3595370552 + rbxassetid://3595370552 + rbxassetid://3595370552 + rbxassetid://3595370552 + 3595446009 + 0 + 0 + rbxassetid://0 + + + + + + + + Teams + -1 + + + + + + + true + + false + true + true + true + TestService + 0 + 0 + -1 + + 10 + + + + + + true + 16 + ProximityPromptService + -1 + + + + + + + true + HttpService + -1 + + + + + + + LanguageService + -1 + + + + + + + true + false + DataStoreService + -1 + + + + + + + \ No newline at end of file