Guest User

OnScreen RAM

a guest
Jan 25th, 2022
183
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.46 KB | None | 0 0
  1. local systemres = {["GB"] = {160,144},
  2.                    ["NES"] = {256, 224}
  3.                   }
  4. local RAMrange = {["GB"] = {0,0xFFFF,0xC000},       -- The allowable range of RAM organized as {min, max, default}.
  5.                   ["NES"] = {0,0xFFFF,0}
  6.                  }
  7. local screendims = systemres[emu.getsystemid()] or {256, 240}       -- Default to NES dimensions, I guess.
  8. local range = RAMrange[emu.getsystemid()] or {0,0xFFFF,0xC000}
  9. range["min"], range["max"], range["default"] = range[1], range[2], range[3]
  10. local RAMpage = range["default"]
  11.  
  12. local state={
  13.              ["RAMwh"]={64, 64},                -- Default width and height of the box displaying our RAM.
  14.              ["RAMposfun"]="ul",                -- The function for placing the RAM onscreen.
  15.              ["RAMpos"]={0,0},                  -- Where on screen to display the RAM.  Default is top-left corner.
  16.              ["messagepos"]={74, 10},           -- Where to display messages/RAM.  I'm "cheating" this into the bottom right corner, but its position might not be correct depending on the system.
  17.              ["persist"]=120,                   -- How long in frames to display messages for.
  18.              ["minwidth"]=2,                    -- Minimum allowed width of RAM display.
  19.              ["maxwidth"]=128,                  -- Maximum allowed width of RAM display.
  20.              ["minheight"]=2,                   -- Minimum allowed height of RAM display.
  21.              ["maxheight"]=128,                 -- Maximum allowed height of RAM display.
  22.              ["screendims"]=screendims,         -- Dimensions of the screen.  Set automatically by the game system but defaults to NES resolution.  What are the screen dimensions of some other systems?
  23.              ["range"]=range,                   -- Minimum and maximum allowable range of the RAM in format {low, high, default}.  Depends on the system but defaults to Game Boy values.
  24.              ["hashtype"]="prime",              -- Current hash function for drawing RAM.
  25.              ["RAMpage"]=RAMpage,               -- Where in the RAM the upper-left corner of our RAM display corresponds to.  Default comes from range table.
  26.              ["keys"]={},                       -- Keys pressed this frame.
  27.              ["last"]={},                       -- Keys pressed previous frame.
  28.              ["message"]="",                    -- The message to be displayed.
  29.              ["countdown"]=0,                   -- When countdown reaches 0, stop displaying messages.
  30.              ["nprime"]=5000,                   -- Which prime number to use for prime hash.  Default is the 5000th prime.
  31.              ["showcursor"]=false,              -- If true, draw the cursor.
  32.              ["numberformat"]="unsigned"        -- The number format of displayed RAM values.
  33.             }
  34.  
  35. unpack = unpack or table.unpack
  36. console.clear()
  37.  
  38. -- Make prime numbers and create a color lookup table.
  39. ---[[
  40. local n = 5000      -- Which prime to use.
  41. local nmax = 10000      -- How many primes in our table.
  42.  
  43. -- Create a hash by computing p^b, where p is prime.
  44. local function ptob(b,p)
  45.     local hexc=1
  46.     p = p or 503
  47.     for i=1,b do
  48.         hexc = math.fmod(hexc*p, 0x1000000)
  49.     end
  50.     return 0xFF000000 + hexc
  51. end
  52.  
  53. -- Our table of prime numbers.
  54. local primetable={2}
  55. do      -- Enclosed in a "do" block to clear up k and p for later use, if wanted.
  56.     local k=3
  57.     local p=2
  58.     while p<=nmax do
  59.         local isprime=true
  60.         local ceiling=math.sqrt(k)
  61.         local pp=1
  62.         while primetable[pp]<=ceiling do
  63.             if k/primetable[pp] == math.floor(k/primetable[pp]) then
  64.                 isprime=false
  65.                 break
  66.             end
  67.             pp=pp+1
  68.         end
  69.         if isprime then
  70.             primetable[p]=k
  71.             p=p+1
  72.         end
  73.         k=k+1
  74.     end
  75. end
  76.  
  77. -- Makes the color table so we can look up the colors on the fly rather than recalculate them every frame.
  78. local function primehash(p)
  79.     local colort={}
  80.     for i=0,255 do
  81.         colort[i]=ptob(i,p)
  82.     end
  83.    
  84.     return colort
  85. end
  86.  
  87. -- In case you wish to just make a random hash.
  88. local function randomhash()
  89.     local colort={}
  90.     for i=0,255 do
  91.         colort[i]=0xFF000000 + math.random(0xFFFFFF)
  92.     end
  93.    
  94.     return colort
  95. end
  96.  
  97. -- ... Or maybe you want a monochrome view of the RAM to find counters and such.
  98. local function monochrome()
  99.     local colort={}
  100.     for i=0,255 do
  101.         colort[i]=0xFF000000 + 0x010101*i
  102.     end
  103.    
  104.     return colort
  105. end
  106.  
  107. local colort = primehash(primetable[n])
  108. --]]
  109.  
  110.  
  111. state["palette"] = colort
  112. state["primetable"]=primetable
  113.  
  114.  
  115. -- Turns a relative memory location into {x,y} coordinate vector.
  116. local function getcoords(b, RAMwh)
  117.     local w = RAMwh[1]
  118.     local x = math.fmod(b,w)
  119.     local y = math.floor(b/w+0.001)
  120.    
  121.     return {x, y}
  122. end
  123.  
  124.  
  125. -- Need to edit these four functions to take into account possibility that pos is right or bottom...
  126. function state:morewidth()
  127.     local w, page, maxwidth, rmax = self.RAMwh[1], self.RAMpage, self.maxwidth, self.range.max
  128.     local delta = self.RAMwh[1]*self.RAMwh[2]
  129.     w = (w<maxwidth and page+2*delta-1<=rmax) and 2*w or w
  130.     self.RAMwh[1]=w
  131.     self[self.RAMposfun](self)      -- Have to re-run the RAM positioning function to make sure we're not off the screen.
  132.     self.message=w
  133.     self.countdown=self.persist
  134. end
  135.  
  136. function state:lesswidth()
  137.     local w, minwidth = self.RAMwh[1], self.minwidth
  138.     w = (w/2>minwidth) and math.floor(w/2 + 0.001) or minwidth
  139.     self.RAMwh[1]=w
  140.     self[self.RAMposfun](self)
  141.     self.message=w
  142.     self.countdown=self.persist
  143. end
  144.  
  145. function state:moreheight()
  146.     local h, page, maxheight, rmax = self.RAMwh[2], self.RAMpage, self.maxheight, self.range.max
  147.     local delta = self.RAMwh[1]*self.RAMwh[2]
  148.     h = (h<maxheight and page+2*delta-1<=rmax) and 2*h or h
  149.     self.RAMwh[2]=h
  150.     self[self.RAMposfun](self)
  151.     self.message=h
  152.     self.countdown=self.persist
  153. end
  154.  
  155. function state:lessheight()
  156.     local h, minheight = self.RAMwh[2], self.minheight
  157.     h = (h/2>minheight) and math.floor(h/2 + 0.001) or minheight
  158.     self.RAMwh[2]=h
  159.     self[self.RAMposfun](self)
  160.     self.message=h
  161.     self.countdown=self.persist
  162. end
  163.  
  164.  
  165. function state:pageforward()
  166.     local page, rmax=self.RAMpage, self.range[2]
  167.     local delta=self.RAMwh[1]*self.RAMwh[2]
  168.     page = (page+2*delta-1 <= rmax) and page+delta or page
  169.     self.RAMpage = page
  170.     self.message = bizstring.hex(page)
  171.     self.countdown=self.persist
  172. end
  173.  
  174. function state:pageback()
  175.     local page, rmin=self.RAMpage, self.range[1]
  176.     local delta=self.RAMwh[1]*self.RAMwh[2]
  177.     page = (page-delta >= rmin) and page-delta or page
  178.     self.RAMpage = page
  179.     self.message = bizstring.hex(page)
  180.     self.countdown=self.persist
  181. end
  182.  
  183.  
  184. function state:nextprime()
  185.     if self.hashtype == "prime" then
  186.         self.nprime = (self.nprime<#self.primetable) and self.nprime+1 or #self.primetable
  187.         self.message=self.primetable[self.nprime]
  188.         self.countdown=self.persist
  189.         self.palette = primehash(self.message)
  190.     end
  191. end
  192.  
  193. function state:prevprime()
  194.     if self.hashtype == "prime" then
  195.         self.nprime = (self.nprime>1) and self.nprime-1 or 1
  196.         self.message=self.primetable[self.nprime]
  197.         self.countdown=self.persist
  198.         self.palette = primehash(self.message)
  199.     end
  200. end
  201.  
  202.  
  203. function state:RAMleft()
  204.     self.RAMpos[1]=0
  205. end
  206.  
  207. function state:RAMcenterh()
  208.     local w, c = self.RAMwh[1], math.floor(self.screendims[1]/2 + 0.01)
  209.     self.RAMpos[1] = c - math.floor(w/2 + 0.01)
  210. end
  211.  
  212. function state:RAMright()
  213.     local w, r = self.RAMwh[1], self.screendims[1]
  214.     self.RAMpos[1] = r - w
  215. end
  216.  
  217. function state:RAMtop()
  218.     self.RAMpos[2]=0
  219. end
  220.  
  221. function state:RAMcenterv()
  222.     local h, c = self.RAMwh[2], math.floor(self.screendims[2]/2 + 0.01)
  223.     self.RAMpos[2] = c - math.floor(h/2 + 0.01)
  224. end
  225.  
  226. function state:RAMbottom()
  227.     local h, b = self.RAMwh[2], self.screendims[2]
  228.     self.RAMpos[2] = b - h
  229. end
  230.  
  231. function state:ul()
  232.     self:RAMleft()
  233.     self:RAMtop()
  234.     self.RAMposfun="ul"
  235.     local x=self.RAMpos[1]+self.RAMwh[1]+10
  236.     local y=self.RAMpos[2]+10
  237.     self.messagepos={x,y}
  238. end
  239.  
  240. function state:uc()
  241.     self:RAMcenterh()
  242.     self:RAMtop()
  243.     self.RAMposfun="uc"
  244.     local x=self.RAMpos[1]+self.RAMwh[1]+10
  245.     local y=self.RAMpos[2]+10
  246.     self.messagepos={x,y}
  247. end
  248.  
  249. function state:ur()
  250.     self:RAMright()
  251.     self:RAMtop()
  252.     self.RAMposfun="ur"
  253.     local x=self.RAMpos[1]-50
  254.     local y=self.RAMpos[2]+10
  255.     self.messagepos={x,y}
  256. end
  257.  
  258. function state:cl()
  259.     self:RAMleft()
  260.     self:RAMcenterv()
  261.     self.RAMposfun="cl"
  262.     local x=self.RAMpos[1]+self.RAMwh[1]+10
  263.     local y=self.RAMpos[2]+10
  264.     self.messagepos={x,y}
  265. end
  266.  
  267. function state:cc()
  268.     self:RAMcenterh()
  269.     self:RAMcenterv()
  270.     self.RAMposfun="cc"
  271.     local x=self.RAMpos[1]+self.RAMwh[1]+10
  272.     local y=self.RAMpos[2]+10
  273.     self.messagepos={x,y}
  274. end
  275.  
  276. function state:cr()
  277.     self:RAMright()
  278.     self:RAMcenterv()
  279.     self.RAMposfun="cr"
  280.     local x=self.RAMpos[1]-50
  281.     local y=self.RAMpos[2]+10
  282.     self.messagepos={x,y}
  283. end
  284.  
  285. function state:bl()
  286.     self:RAMleft()
  287.     self:RAMbottom()
  288.     self.RAMposfun="bl"
  289.     local x=self.RAMpos[1]+self.RAMwh[1]+10
  290.     local y=self.RAMpos[2]+10
  291.     self.messagepos={x,y}
  292. end
  293.  
  294. function state:bc()
  295.     self:RAMcenterh()
  296.     self:RAMbottom()
  297.     self.RAMposfun="bc"
  298.     local x=self.RAMpos[1]+self.RAMwh[1]+10
  299.     local y=self.RAMpos[2]+10
  300.     self.messagepos={x,y}
  301. end
  302.  
  303. function state:br()
  304.     self:RAMright()
  305.     self:RAMbottom()
  306.     self.RAMposfun="br"
  307.     local x=self.RAMpos[1]-50
  308.     local y=self.RAMpos[2]+10
  309.     self.messagepos={x,y}
  310. end
  311.  
  312.  
  313. function state:togglecursor()
  314.     state.showcursor = not(state.showcursor)
  315. end
  316.  
  317.  
  318. function state:cyclehash()
  319.     local currhashstyle=self.hashtype
  320.     local cycleorder={"prime", "random", "monochrome"}
  321.     local functionorder={makecolortable, randomhash, monochrome}
  322.     local index = 1     -- Default to monochrome, so calling this function switches to prime.
  323.     for k, v in pairs(cycleorder) do
  324.         if currhashstyle==v then
  325.             index=k
  326.         end
  327.     end
  328.     index = math.fmod(index,3)+1
  329.     currhashstyle = cycleorder[index]
  330.     self.hashtype=currhashstyle
  331.     self.message=currhashstyle
  332.     self.countdown=self.persist
  333.     if self.hashtype == "prime" then
  334.         local p = self.primetable[self.nprime]
  335.         self.palette = primehash(p)
  336.     elseif self.hashtype == "random" then
  337.         self.palette = randomhash()
  338.     elseif self.hashtype == "monochrome" then
  339.         self.palette = monochrome()
  340.     end
  341. end
  342.  
  343. function state:cycleformat()
  344.     -- This time done a little more elegantly than cyclehash, using a table directly.
  345.     local formattable = {["unsigned"] = "signed", ["signed"]="hex", ["hex"]="unsigned"}
  346.     self.numberformat = formattable[self.numberformat]
  347. end
  348.  
  349. function state:disp(x, y, val)
  350.     if self.numberformat == "unsigned" then
  351.         gui.pixelText(x, y, val)
  352.     elseif self.numberformat == "signed" then
  353.         if val>127 then
  354.             val=val-256
  355.         end
  356.         gui.pixelText(x, y, val)
  357.     elseif self.numberformat == "hex" then
  358.         gui.pixelText(x, y, bizstring.hex(val))
  359.     end
  360. end
  361.  
  362. state.KFtable = {["RightBracket"]="morewidth",
  363.                  ["LeftBracket"]="lesswidth",
  364.                  ["KeypadSubtract"]="lessheight",
  365.                  ["KeypadAdd"]="moreheight",
  366.                  ["PageDown"]="pageforward",
  367.                  ["PageUp"]="pageback",
  368.                  ["H"]="cyclehash",
  369.                  ["M"]="nextprime",
  370.                  ["N"]="prevprime",
  371.                  ["K"]="togglecursor",
  372.                  ["J"]="cycleformat",
  373.                  ["Keypad7"]="ul",
  374.                  ["Keypad8"]="uc",
  375.                  ["Keypad9"]="ur",
  376.                  ["Keypad4"]="cl",
  377.                  ["Keypad5"]="cc",
  378.                  ["Keypad6"]="cr",
  379.                  ["Keypad1"]="bl",
  380.                  ["Keypad2"]="bc",
  381.                  ["Keypad3"]="br"
  382.                 }
  383.  
  384. function state:KFs()
  385.     for k, v in pairs(self.pressed) do
  386.         local currfunction=self.KFtable[k]
  387.         if currfunction then
  388.             self[currfunction](self)
  389.         end
  390.     end
  391. end
  392.  
  393. function state:update()
  394.     self.keys, self.last = input.get(), self.keys
  395.     self.pressed = {}
  396.     for k,v in pairs(self.keys) do
  397.         self.pressed[k]=not(self.last[k])
  398.         if not(self.pressed[k]) then self.pressed[k]=nil end
  399.     end
  400.     self:KFs()
  401. end
  402.  
  403. function state:draw()
  404.     local x, y=self.RAMpos[1], self.RAMpos[2]
  405.     local w, h=self.RAMwh[1], self.RAMwh[2]
  406.     gui.drawRectangle(x,y,w-1,h-1,"black","black")
  407.    
  408.     local page, palette = self.RAMpage, self.palette
  409.     for j = 0, h-1 do
  410.         for i = 0, w-1 do
  411.             local b = memory.readbyte(page + j*w + i)
  412.             if not(b==0) then gui.drawPixel(x+i, y+j, palette[b]) end
  413.         end
  414.     end
  415. end
  416.  
  417. -- Draws a cursor over the current mouse position.  Only meant for debugging and demonstration purposes.
  418. function drawcursor(x,y)
  419.     local c="magenta"
  420.     gui.drawLine(x-3,y,x-1,y,c)
  421.     gui.drawLine(x,y-3,x,y-1,c)
  422.     gui.drawLine(x+1,y,x+3,y,c)
  423.     gui.drawLine(x,y+1,x,y+3,c)
  424. end
  425.  
  426. function state:displaymsgs()
  427.     if self.countdown>0 then
  428.         gui.pixelText(self.messagepos[1], self.messagepos[2], self.message)
  429.         self.countdown = self.countdown-1
  430.     end
  431.    
  432.     local mouse=input.getmouse()
  433.     local x, y = mouse.X, mouse.Y
  434.     if self.showcursor then drawcursor(x,y) end
  435.     local rx, ry = self.RAMpos[1], self.RAMpos[2]
  436.     local w, h = self.RAMwh[1], self.RAMwh[2]
  437.     local page = self.RAMpage
  438.    
  439.     if x>=rx and x<rx+w and y>=ry and y<ry+h then
  440.         local ax, ay=x-self.RAMpos[1], y-self.RAMpos[2]
  441.         local b = page+ay*w+x
  442.         local val = memory.readbyte(b)
  443.         gui.pixelText(self.messagepos[1], self.messagepos[2]+10, bizstring.hex(b))
  444.         self:disp(self.messagepos[1], self.messagepos[2]+20, val)
  445.     end
  446. end
  447.  
  448.  
  449.  
  450. while true do
  451.     state:update()
  452.     state:draw()
  453.     state:displaymsgs()
  454.    
  455.    
  456.     emu.frameadvance()
  457.    
  458.     keys, last = input.get(), keys
  459. end
Advertisement
Add Comment
Please, Sign In to add comment