--Set system bus. The full addresses are my preference.
--I desire information packed in the given address itself.
--Also, if pointers are used, they sometimes use the full bus address.
if not memory.usememorydomain("System Bus") then error("Someone renamed the domains again.") end
--Renaming. I like the descriptive names, but I want something shorter.
--I'm also selecting functions I think I'll need.
--Also has the advantage in case of renamed functions or different emulators.
local R1u= memory.read_u8
--#############################################################################
--Handle RNG
--Data structure expected:
-- RNG_Table
-- [1] 00E4 R0
-- [2] 00E5 R1
-- [3] 00E6 R2
-- [4] 00E7 R3
-- TA 0092 Timer_Always
-- TL 009D Timer_Lagged
--These six values are what's required to know the output.
--If a rename is suggested, I'll probably listen.
--Because lag shifts the RNG, it's not precisely possible to know what the
--future RNG will hold, although knowing the underlying numbers may help to
--know what to expect as lag frames "shift" the RNG a little.
--In addition, whenever the RNG is called, R2 is modified using a different
--formula than the standard RNG shift, so if an enemy needs to "think", or an
--item is about to drop, the RNG can be "unpredictably" modified for the next
--16 frames. Since R2 is shifted out entirely after 16 frames, without any
--effect on the standard shifts, the change is only local to this call.
--It still happens more frequently than would be desired.
--*****************************************************************************
local function Roll(T)
--*****************************************************************************
--Gets the next RNG value. This is the "shift" calculation.
--In-place modification to the table. Make a copy first if you want the old.
--Will not modify timers, even though Timer_Always also updates with it.
local v= bit.band(0x02,bit.bxor(T[1],T[2])) / 2
for i= 1, 4 do
T[i]= T[i]+256*v
v= T[i]%2
T[i]= math.floor(T[i]/2)
end
end
--*****************************************************************************
local function LoadRNG(T)
--*****************************************************************************
--Reads the RNG values from memory.
--You can feed a table (it'll modify) or let it construct one itself.
--Returns the initialized table.
T= T or {}
T[1]= R1u(0x00E4) --R0
T[2]= R1u(0x00E5) --R1
T[3]= R1u(0x00E6) --R2
T[4]= R1u(0x00E7) --R3
T.TA= R1u(0x0092) --Timer_Always
T.TL= R1u(0x009D) --Timer_Lagged
return T
end
--*****************************************************************************
local function CalculateRNG(RNG)
--*****************************************************************************
--Gets the calculated RNG value. This is the "call" calculation.
--Returns three values: The RNG output, and the new R2.
-- Number: RNG output (value used for whatever RNG stuff is needed)
-- Number: What R2 is replaced with after the call is done
--This function will not do in-place modification. That's up to the caller.
--Take note that R2 and R3 both are modified. R3 takes the RNG output.
--[[
Carry is always set upon entry (add +1 to what you see)
0E:AE64 LDA $00E6 R2
0E:AE66 ADC $00E7 R3
0E:AE68 ADC $0000 #$59 (used for function call)
0E:AE6A SBC $009D Frame timer (affected by lag)
0E:AE6C STA $00E6 Modify R2 right away
0E:AE6E ADC $0001 #$AE (used for function call)
0E:AE70 SBC $00E5 R1
0E:AE72 ADC $0092 Frame timer (always runs)
0E:AE74 STA $00E7 R3
]]--
local RNG_Output= (( --Also New_R3
8
+ RNG[3] --R2
+ RNG[4] --R3
- RNG.TL --Timer_Lagged
- RNG[2] --R1
)%255 + 1
+ RNG.TA --Timer_Always
)%256
local New_R2= ((
0x59
+ RNG[3] --R2
+ RNG[4] --R3
)%255 + 1
+ (255 - RNG.TL)
)%256
return RNG_Output, New_R2
end
local Item1= 0xFFFFFFFF
local Item2= 0xFF00FFFF
local Item3= 0xFF0080FF
local Item4= 0xFFFFFF00
local Item5= 0xFFFF8000
local NoItem=0xFF808080
local ItemThresholds= {
[0]=Item1,Item1,
Item2,
Item3,Item3,Item3,Item3,Item3,
Item4,Item4,
Item5,Item5,Item5,Item5
}
--*****************************************************************************
local function ItemColor(v)
--*****************************************************************************
return ItemThresholds[v%50] or NoItem
end
--#############################################################################
--Canvas
--Sure feels slow, somehow. The extra space is still appreciated, however.
--Mostly just throwing something together and seeing what happens.
--The RNG appears to be predicted appropriately, if it's any consolation.
local RNG_Count= 40
local canvas = gui.createcanvas(200, RNG_Count*11+1);
local RNG= {{},{}} --For sake of calling the table constructor just once
--*****************************************************************************
local function HandleCanvas()
--*****************************************************************************
--Yay, canvas!
--We also do RNG calculations here.
canvas.Clear(0xFF000000);
LoadRNG(RNG[1]) --If you never call the RNG, this is what you get
LoadRNG(RNG[2]) --If called just once at this moment, what you get
RNG[2][4],RNG[2][3]= CalculateRNG(RNG[2]) --To show effects of RNG call
for i= 0, RNG_Count-1 do
for r= 1, 2 do
local v= CalculateRNG(RNG[r])
local clr= ItemColor(v)
canvas.DrawText(100 - 50*r,i*11,string.format("%3d",
v
),clr)
Roll(RNG[r])
RNG[r].TL= (RNG[r].TL+1)%256
RNG[r].TA= (RNG[r].TA+1)%256
end
end
canvas.Refresh();
end
--#############################################################################
--Management
-- while true do emu.frameadvance() end ; The generic loop we're used to.
--Not much to do except call one other function, really.
--*****************************************************************************
while true do
--*****************************************************************************
HandleCanvas()
emu.frameadvance()
end