Guest User

Super Mario Land luascript

a guest
May 4th, 2025
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.79 KB | Gaming | 0 0
  1. -- this outputs some information related to the map loading procedure when the screen scrolls,
  2. -- hopefully helpful for researching and recreating the scroll bug
  3. -- script by mugg ; with help from ais523, Sand
  4.  
  5. ----- CONFIGURATION -----
  6. local PRINT_INFO = true
  7.  
  8. local TRY_RUN_TESTS = true
  9. local run_test =  -- only set one of these true
  10. {
  11.         [1] = false, -- unpause test
  12.         [2] = true -- idle into B press/jumprun test
  13. }
  14. ----- CONFIGURATION END -----
  15.  
  16. console.clear()
  17. memory.usememorydomain("System Bus")
  18.  
  19. local cycles_at_frame_start = emu.totalexecutedcycles()
  20. local cycles_at_frame_end = emu.totalexecutedcycles()
  21. local print_after_this_frame = emu.framecount() + 1
  22. local game_version = "-"
  23. local t = ""
  24. local t_frame_info = ""
  25. local frames_processed = 0
  26.  
  27. -- these have to happen in the same frame
  28. local requirement1 = false -- timer interrupt happens while ffea is 0x01
  29. local requirement2 = false -- ffea gets set to 0x03
  30.  
  31. local bugframe_tbl = {}
  32. local double_timer_interrupt_tbl = {}
  33. local timer_interrupts_same_frame = 0
  34.  
  35. local waitframes = emu.framecount() +1 -- used by testing code, to bruteforce for the bug
  36.  
  37. if gameinfo.getromhash() == "3A4DDB39B234A67FFB361EE7ABC3D23E0A8B1C89" then
  38.     game_version = "1.0"
  39. elseif gameinfo.getromhash() == "418203621B887CAA090215D97E3F509B79AFFD3E" then
  40.     game_version = "1.1"
  41. else
  42.     print("This script will work only with GB Super Mario Land v1.0 or v1.1.")
  43.     return
  44. end
  45.  
  46. function text(x, y, text, color, backcolor)
  47.     gui.pixelText(x, y, text, color, backcolor)
  48. end
  49.  
  50. event.onframestart(function()
  51.     cycles_at_frame_start = emu.totalexecutedcycles()
  52.    
  53.     if emu.framecount() > print_after_this_frame then
  54.         local prefix = ""
  55.         if frames_processed > 1 then
  56.             prefix = "\n"
  57.         end
  58.         t_frame_info = prefix .. "   FRAME " .. emu.framecount() + 1
  59.     end
  60. end)
  61.  
  62. event.onframeend(function()
  63.  
  64.     if emu.framecount()-1 > print_after_this_frame then
  65.         t_frame_info = t_frame_info .. "  Cy:" .. emu.totalexecutedcycles() - cycles_at_frame_end
  66.     end
  67.    
  68.     if emu.islagged()
  69.     and emu.framecount()-1 > print_after_this_frame then
  70.         t_frame_info = t_frame_info .. "  **LAG**"
  71.     end
  72.        
  73.     cycles_at_frame_end = emu.totalexecutedcycles()
  74.  
  75.     -- only print if the string isn't empty
  76.     if PRINT_INFO then
  77.         if #t_frame_info > 0 and #t > 0 then
  78.             print(t_frame_info .. "\n" .. t)
  79.         elseif #t_frame_info > 0 then
  80.             print(t_frame_info)
  81.         end
  82.     end
  83.    
  84.     -- bugframe happened, save framecount and pixel pos
  85.     if requirement1 and requirement2 then
  86.         print("Bug frame!!!")
  87.         local bugframe_info = {emu.framecount(), memory.read_u8(0xFFE9)*8}
  88.         table.insert(bugframe_tbl, bugframe_info)
  89.     end
  90.    
  91.     -- reset values
  92.     requirement1 = false
  93.     requirement2 = false
  94.     t = ""
  95.     t_frame_info = ""
  96.     timer_interrupts_same_frame = 0
  97.  
  98.     frames_processed = frames_processed + 1
  99. end)
  100.  
  101. tastudio.onbranchload(function()
  102.     print_after_this_frame = emu.framecount() + 1
  103.     frames_processed = -1
  104.     console.clear()
  105.     bugframe_tbl = {}
  106.     double_timer_interrupt_tbl = {}
  107. end)
  108.  
  109. event.onloadstate(function()
  110.     print_after_this_frame = emu.framecount() + 1
  111.     frames_processed = -1
  112.     console.clear()
  113.     bugframe_tbl = {}
  114.     double_timer_interrupt_tbl = {}
  115. end)
  116.  
  117. -- Write to $FFEA
  118. -- passed val is always 0, it seems, hinting that this hook is not fully implemented
  119. event.on_bus_write(function(addr, val, flags)
  120.     if emu.framecount() > print_after_this_frame then
  121.         local raw_val = memory.read_u8(0xFFEA)
  122.         local val = string.upper(string.format("%02x",raw_val))
  123.         local myname = "Write $FFEA (" .. val .. ")"
  124.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  125.        
  126.         if raw_val == 0x03 then
  127.             requirement2 = true
  128.         end
  129.     end
  130. end, 0xFFEA)
  131.  
  132. -- Write to $FFE9
  133. event.on_bus_write(function(addr, val, flags)
  134.     if emu.framecount() > print_after_this_frame then
  135.         local val = string.upper(string.format("%02x",memory.read_u8(0xFFE9)))
  136.         local myname = "Write $FFE9 (" .. val .. ")"
  137.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  138.     end
  139. end, 0xFFE9)
  140.  
  141. -- Exec $0040
  142. event.on_bus_exec(function(addr, val, flags)
  143.     if emu.framecount() > print_after_this_frame then
  144.         local myname = "Execu $0040 (VBLANK INTERRUPT)"
  145.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  146.     end
  147. end, 0x0040)
  148.  
  149. -- Exec $0050
  150. event.on_bus_exec(function(addr, val, flags)
  151.     if emu.framecount() > print_after_this_frame then
  152.         local myname = "Execu $0050 (TIMER INTERRUPT)"
  153.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  154.        
  155.         local ffea = memory.read_u8(0xFFEA)
  156.         if ffea == 0x01 then
  157.             requirement1 = true
  158.         end
  159.        
  160.         timer_interrupts_same_frame = timer_interrupts_same_frame + 1
  161.         if timer_interrupts_same_frame >= 2 then
  162.             --t = t .. "\nTwo Timer Interrupts!"
  163.             table.insert(double_timer_interrupt_tbl, emu.framecount()+1)
  164.         end
  165.     end
  166. end, 0x0050)
  167.  
  168. -- Exec $0060
  169. --[[
  170. event.on_bus_exec(function(addr, val, flags)
  171.     if emu.framecount() > print_after_this_frame then
  172.         local myname = "Execu $0060 (JOYPAD INTERRUPT)"
  173.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  174.     end
  175. end, 0x0060)
  176. --]]
  177.  
  178. -- game version dependent execs
  179. if game_version == "1.0" then
  180.  
  181.     event.on_bus_exec(function(addr, val, flags)
  182.         if emu.framecount() > print_after_this_frame then
  183.             local myname = "Execu $2258 (READ $FFEA)"
  184.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  185.         end
  186.     end, 0x224F)
  187.    
  188.     event.on_bus_exec(function(addr, val, flags)
  189.         if emu.framecount() > print_after_this_frame then
  190.             local myname = "Execu $2254 (READ $FFE9)"
  191.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  192.         end
  193.     end, 0x2254)
  194.  
  195. elseif game_version == "1.1" then
  196.  
  197.     event.on_bus_exec(function(addr, val, flags)
  198.         if emu.framecount() > print_after_this_frame then
  199.             local myname = "Execu $2258 (READ $FFEA)"
  200.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  201.         end
  202.     end, 0x2258)
  203.    
  204.     event.on_bus_exec(function(addr, val, flags)
  205.         if emu.framecount() > print_after_this_frame then
  206.        
  207.             local myname = "Execu $225D (READ $FFE9)"
  208.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  209.         end
  210.     end, 0x225D)
  211.  
  212. end
  213.  
  214. while true do
  215.    
  216.     gui.clearGraphics()
  217.    
  218.     if #bugframe_tbl > 0 then
  219.         for i=1, #bugframe_tbl, 1 do
  220.             local bugframe_entry = bugframe_tbl[i]
  221.            
  222.             if bugframe_entry ~= nil then
  223.                        
  224.                 local bugframe_framecount = bugframe_entry[1]
  225.                 local bugframe_pixel = bugframe_entry[2]
  226.                 local current_pixel = memory.read_u8(0xFFE9) * 8
  227.                 local display_pixel = (bugframe_pixel - current_pixel) - memory.read_u8(0xFF43)%8 + 216
  228.  
  229.                 text(10, 10 + i*8, "bug Frame " .. bugframe_framecount, 0xFF00FF00, 0x80000000)
  230.            
  231.                 if display_pixel > 160 then
  232.                     text(136,10 + i*8, "--->", 0xFFFF0000, 0x80000000)
  233.                 else
  234.                     gui.drawLine(display_pixel, 17, display_pixel, 144, 0xFFFF0000)
  235.                 end
  236.                
  237.                 if display_pixel < 0 then
  238.                     bugframe_tbl[i] = nil
  239.                 end
  240.                
  241.             end
  242.         end
  243.     end
  244.    
  245.     --[[
  246.     if #double_timer_interrupt_tbl > 0 then
  247.         for j=1, #double_timer_interrupt_tbl, 1 do
  248.             local frame = double_timer_interrupt_tbl[j]
  249.             text(10, 80 + j*8, "two timer interrupts frame " .. frame, 0xFF00FF00, 0x80000000)
  250.         end
  251.     end
  252.     --]]
  253.    
  254.     if TRY_RUN_TESTS then
  255.    
  256.         if run_test[1] then
  257.             -- Pause, then let this run while keeping previous input in taseditor
  258.             -- script will attempt unpause at increasing framecounts
  259.             tastudio.submitinputchange(emu.framecount(), "Right", true)
  260.             tastudio.submitinputchange(emu.framecount(), "A", true)
  261.             if emu.framecount() == waitframes then
  262.                 tastudio.submitinputchange(emu.framecount()+1, "Start", true)
  263.             end
  264.             if emu.framecount() > waitframes + 120 then
  265.                 savestate.loadslot(5)
  266.                 waitframes = waitframes + 1
  267.             end
  268.             tastudio.applyinputchanges()
  269.         end
  270.  
  271.         if run_test[2] then
  272.             -- Have Mario on the ground, clear upcoming 200 frames in taseditor
  273.             -- script will attempt increasing amounts of idleframes then jumprun.
  274.             if emu.framecount() >= waitframes then
  275.                 tastudio.submitinputchange(emu.framecount(), "Right", true)
  276.                 tastudio.submitinputchange(emu.framecount(), "B", true)
  277.             end
  278.            
  279.             if emu.framecount() > waitframes+6 then
  280.                 tastudio.submitinputchange(emu.framecount(), "A", true)
  281.             end
  282.            
  283.             if emu.framecount() > waitframes + 120 then
  284.                 savestate.loadslot(5)
  285.                 waitframes = waitframes + 1
  286.             end
  287.             tastudio.applyinputchanges()
  288.         end
  289.  
  290.     end
  291.  
  292.     emu.frameadvance()
  293. end
  294.  
Advertisement
Add Comment
Please, Sign In to add comment