Thanks!
I do the bounds check there to see if I should remove the block that went offscreen or add the new block I scrolled in. There's a check area around the screen borders.
Array of functions doesn't seem to speed me up at all. But switching to 2 nested loops saved me some 10 fps.
What I had in mind was some way to stop using such a huge array of cache entries, but I still don't know how.

Download Gargoyles.luaLanguage: lua

```
-- Gargoyles, Genesis (BizHawk)
-- feos, 2015-2016
--== Shortcuts ==--
rb = memory.read_u8
rw = memory.read_u16_be
rws = memory.read_s16_be
r24 = memory.read_u24_be
rl = memory.read_u32_be
box = gui.drawBox
text = gui.pixelText
line = gui.drawLine
AND = bit.band
SHIFT = bit.rshift
--== RAM addresses ==--
levnum = 0xff00ba
LevelFlr = 0xff00c0
LevelCon = 0xff00c4
mapline_tab = 0xff0244
GlobalBase = 0xff1c76
GolBase = 0xff2c76
MapA_Buff = 0xff4af0
--== Camera Hack ==--
camhack = false
div = 1 -- scale
size = 16/div -- block size
--== Other stuff ==--
XposLast = 0
YposLast = 0
room = 0
workinglast = 0
lagcount = emu.lagcount()
gui.defaultPixelFont("fceux")
--== Block cache ==--
cache = {}
function main()
rnd1 = rl (0xff001c)
rnd2 = rw (0xff0020)
working = rb (0xff0073)
xblocks = rw (0xff00d4)
mapw = rw (0xff00d4)*8
maph = rw (0xff00d6)*8
Xpos = rws(0xff0106)
Ypos = rws(0xff0108)
camx = rws(0xff010c)+16
camy = rws(0xff010e)+16
run = rb (0xff1699)
inv = rw (0xff16d2)
health = rws(0xff2cc6)
backx = camx
backy = camy
Xspd = Xpos-XposLast
Yspd = Ypos-YposLast
XposLast = Xpos
YposLast = Ypos
facing = AND(rb(GolBase+0x48),2) -- object flag 1
Background()
rndlast = rnd1
workinglast = working
end
function Background()
if working>0 then
cache = {}
return
end
local border = 0
local offset = 32
local basex = camx+border
local basey = camy+border
local basei = PosToIndex(basex-offset,basey-offset)
local boundx = 320-border
local boundy = 224-border
local xblockstockeck = ((camx+boundx+offset)-(basex-offset))/16
local yblockstockeck = ((camy+boundy+offset)-(basey-offset))/16
for yblock = 0,yblockstockeck do
for xblock = 0,xblockstockeck do
local i = yblock*xblocks+xblock+basei
local x=basex+xblock*16
local y=basey+yblock*16
if InBounds(x,basex-offset,camx+boundx+offset) then
local unit = cache[i]
if unit == nil or workinglast>0 then
if InBounds(x,basex,camx+boundx)
and InBounds(y,basey,camy+boundy)
then cache[i] = GetBlock(x,y)
end
else
if not InBounds(x,basex,camx+boundx)
and not InBounds(y,basey,camy+boundy)
then cache[i] = nil
end
end
if unit ~= nil then
DrawBG(unit,x,y)
end
elseif cache[i] ~= nil
then cache[i] = nil
end
end
end
end
col = 0 -- block color
opout = 0x33000000 -- outer opacity
opin = 0x66000000 -- inner opacity
op = 0xff000000
DrawBlock = {
[0x80] = function(x1,y1,x2,y2) -- WALL
col = 0x00ffffff -- white
line(x1,y1,x1,y2,col+op) -- left
line(x2,y1,x2,y2,col+op) -- right
end,
[0x81] = function(x1,y1,x2,y2) -- CEILING
col = 0x00ffffff -- white
line(x1,y2,x2,y2,col+op) -- bottom
end,
[0x82] = function(x1,y1,x2,y2) -- CLIMB_U
col = 0x0000ffff -- cyan
line(x1,y2,x2,y2,col+op) -- bottom
end,
[0x83] = function(x1,y1,x2,y2) -- CLIMB_R
col = 0x0000ffff -- cyan
line(x1,y1,x1,y2,col+op) -- left
end,
[0x84] = function(x1,y1,x2,y2) -- CLIMB_L
col = 0x0000ffff -- cyan
line(x2,y1,x2,y2,col+op) -- right
end,
[0x85] = function(x1,y1,x2,y2) -- CLIMB_LR
col = 0x0000ffff -- cyan
line(x1,y1,x1,y2,col+op) -- left
line(x2,y1,x2,y2,col+op) -- right
end,
[0x86] = function(x1,y1,x2,y2) -- CLIMB_R_STAND_R
col = 0x00ffffff -- white
line(x1,y1,x2,y1,col+op) -- top
col = 0x0000ffff -- cyan
line(x1,y1,x1,y2,col+op) -- left
end,
[0x87] = function(x1,y1,x2,y2) -- CLIMB_L_STAND_L
col = 0x00ffffff -- white
line(x1,y1,x2,y1,col+op) -- top
col = 0x0000ffff -- cyan
line(x2,y1,x2,y2,col+op) -- right
end,
[0x88] = function(x1,y1,x2,y2) -- CLIMB_LR_STAND_LR
col = 0x00ffffff -- white
line(x1,y1,x2,y1,col+op) -- top
col = 0x00ff00ff -- cyan
line(x1,y1,x1,y2,col+op) -- left
col = 0x0000ffff -- cyan
line(x2,y1,x2,y2,col+op) -- right
end,
[0x70] = function(x1,y1,x2,y2) -- GRAB_SWING
col = 0x0000ff00 -- green
box(x1,y1,x2,y2,col,col+opout)
end,
[0x7f] = function(x1,y1,x2,y2) -- EXIT
col = 0x00ffff00 -- yellow
end,
[0xd0] = function(x1,y1,x2,y2) -- SPIKES
col = 0x00ff0000 -- red
box(x1,y1,x2,y2,col,col+opout)
end,
[0xd1] = function(x1,y1,x2,y2) -- SPIKES
col = 0x00ff0000 -- red
box(x1,y1,x2,y2,col,col+opout)
end
}
function DrawBlockDefault(x1,y1,x2,y2)-- LEVEL_SPECIFIC
col = 0x00ff8800 -- orange
box(x1,y1,x2,y2,col+opin,col+opout)
end
function DrawBG(unit, x, y)
local val = unit.block
local x1 = x/div-camx/div-camx%size
local x2 = x1+size-1
local y1 = y/div-camy/div-camy%size
local y2 = y1+size-1
if unit.contour ~= nil then
box(x1,y1,x2,y2,0x5500ff00,0x5500ff00)
for pixel=0,15 do
if unit.contour[pixel]>0 then
gui.drawPixel(
x1+pixel/div,
y1+unit.contour[pixel]/div-1/div,
0xffffff00)
end
end
end
if val>0 then
local Fn = DrawBlock[val] or DrawBlockDefault
Fn(x1,y1,x2,y2)
box(x1,y1,x2,y2,col+opin,col+opout)
end
end
function GetBlock(x,y)
if working>0 then return nil end
local final = { contour={}, block=0 }
if x>0 and x<mapw>0 and y<maph>0 or rb(a1+8)>0 then
for pixel=0,15 do
final.contour[pixel] = rb(a1+pixel)
end
else
final.contour = nil
end
else
return nil
end
return final
end
function PosToIndex(x,y)
return math.floor(x/16)+math.floor(y/16)*xblocks
end
function IndexToPos(i)
return { x=(i%xblocks)*16, y=math.floor(i/xblocks)*16 }
end
function InBounds(x,minimum,maximum)
if x>=minimum and x<=maximum
then return true
else return false
end
end
while true do
main()
emu.frameadvance()
end
```