Advertisement
Guest User

Super Mario Land luascript

a guest
May 6th, 2025
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.46 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 = false
  7. local PAUSE_WHEN_BUG_HAPPENS = true
  8. local TRY_RUN_TESTS = true
  9. local run_test =  -- only set one of these true
  10. {
  11.         [1] = false,    -- unpause test
  12.         [2] = false,    -- idle into B press/jumprun test
  13.         [3] = false,    -- keep running and repress B test
  14.         [4] = false,    -- keep running and repress right test
  15.         [5] = false,    -- custom probability input test
  16.         [6] = true      -- custom probability input test 2
  17. }
  18. ----- CONFIGURATION END -----
  19.  
  20. console.clear()
  21. memory.usememorydomain("System Bus")
  22.  
  23. local cycles_at_frame_start = emu.totalexecutedcycles()
  24. local cycles_at_frame_end = emu.totalexecutedcycles()
  25. local print_after_this_frame = emu.framecount() + 1
  26. local game_version = "-"
  27. local t = ""
  28. local t_frame_info = ""
  29. local frames_processed = 0
  30.  
  31. -- these have to happen in the same frame
  32. local requirement1 = false -- timer interrupt happens while ffea is 0x01
  33. local requirement2 = false -- ffea gets set to 0x03
  34.  
  35. local bugframe_tbl = {}
  36. local double_timer_interrupt_tbl = {}
  37. local timer_interrupts_same_frame = 0
  38.  
  39. local waitframes = emu.framecount() +1 -- used by testing code, to bruteforce for the bug
  40. local vary3 = math.random(21,46)
  41. local vary4 = math.random(0,4)
  42. local vary5 = math.random(0,13)
  43.  
  44. if gameinfo.getromhash() == "3A4DDB39B234A67FFB361EE7ABC3D23E0A8B1C89" then
  45.     game_version = "1.0"
  46. elseif gameinfo.getromhash() == "418203621B887CAA090215D97E3F509B79AFFD3E" then
  47.     game_version = "1.1"
  48. else
  49.     print("This script will work only with GB Super Mario Land v1.0 or v1.1.")
  50.     return
  51. end
  52.  
  53. function text(x, y, text, color, backcolor)
  54.     gui.pixelText(x, y, text, color, backcolor)
  55. end
  56.  
  57. event.onframestart(function()
  58.     cycles_at_frame_start = emu.totalexecutedcycles()
  59.    
  60.     if emu.framecount() > print_after_this_frame then
  61.         local prefix = ""
  62.         if frames_processed > 1 then
  63.             prefix = "\n"
  64.         end
  65.         t_frame_info = prefix .. "   FRAME " .. emu.framecount() + 1
  66.     end
  67. end)
  68.  
  69. event.onframeend(function()
  70.  
  71.     if emu.framecount()-1 > print_after_this_frame then
  72.         t_frame_info = t_frame_info .. "  Cy:" .. emu.totalexecutedcycles() - cycles_at_frame_end
  73.     end
  74.    
  75.     if emu.islagged()
  76.     and emu.framecount()-1 > print_after_this_frame then
  77.         t_frame_info = t_frame_info .. "  **LAG**"
  78.     end
  79.        
  80.     cycles_at_frame_end = emu.totalexecutedcycles()
  81.  
  82.     -- only print if the string isn't empty
  83.     if PRINT_INFO then
  84.         if #t_frame_info > 0 and #t > 0 then
  85.             print(t_frame_info .. "\n" .. t)
  86.         elseif #t_frame_info > 0 then
  87.             print(t_frame_info)
  88.         end
  89.     end
  90.    
  91.     -- bugframe happened, save framecount and pixel pos
  92.     if requirement1 and requirement2 then
  93.         print("Bug frame!!!")
  94.         local bugframe_info = {emu.framecount(), memory.read_u8(0xFFE9)*8}
  95.         table.insert(bugframe_tbl, bugframe_info)
  96.     end
  97.    
  98.     -- reset values
  99.     requirement1 = false
  100.     requirement2 = false
  101.     t = ""
  102.     t_frame_info = ""
  103.     timer_interrupts_same_frame = 0
  104.  
  105.     frames_processed = frames_processed + 1
  106. end)
  107.  
  108. tastudio.onbranchload(function()
  109.     print_after_this_frame = emu.framecount() + 1
  110.     frames_processed = -1
  111.     console.clear()
  112.     bugframe_tbl = {}
  113.     double_timer_interrupt_tbl = {}
  114.    
  115.     vary3 = math.random(22,45)
  116.     vary4 = math.random(0,3)
  117.     vary5 = math.random(0,7)
  118. end)
  119.  
  120. event.onloadstate(function()
  121.     print_after_this_frame = emu.framecount() + 1
  122.     frames_processed = -1
  123.     console.clear()
  124.     bugframe_tbl = {}
  125.     double_timer_interrupt_tbl = {}
  126.  
  127.     vary3 = math.random(22,45)
  128.     vary4 = math.random(0,3)
  129.     vary5 = math.random(0,7)
  130. end)
  131.  
  132. -- Write to $FFEA
  133. -- passed val is always 0, it seems, hinting that this hook is not fully implemented
  134. event.on_bus_write(function(addr, val, flags)
  135.     if emu.framecount() > print_after_this_frame then
  136.         local raw_val = memory.read_u8(0xFFEA)
  137.         local val = string.upper(string.format("%02x",raw_val))
  138.         local myname = "Write $FFEA (" .. val .. ")"
  139.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  140.        
  141.         if raw_val == 0x03 then
  142.             requirement2 = true
  143.         end
  144.     end
  145. end, 0xFFEA)
  146.  
  147. -- Write to $FFE9
  148. event.on_bus_write(function(addr, val, flags)
  149.     if emu.framecount() > print_after_this_frame then
  150.         local val = string.upper(string.format("%02x",memory.read_u8(0xFFE9)))
  151.         local myname = "Write $FFE9 (" .. val .. ")"
  152.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  153.     end
  154. end, 0xFFE9)
  155.  
  156. -- Exec $0040
  157. event.on_bus_exec(function(addr, val, flags)
  158.     if emu.framecount() > print_after_this_frame then
  159.         local myname = "Execu $0040 (VBLANK INTERRUPT)"
  160.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  161.     end
  162. end, 0x0040)
  163.  
  164. -- Exec $0050
  165. event.on_bus_exec(function(addr, val, flags)
  166.     if emu.framecount() > print_after_this_frame then
  167.         local myname = "Execu $0050 (TIMER INTERRUPT)"
  168.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  169.        
  170.         local ffea = memory.read_u8(0xFFEA)
  171.         if ffea == 0x01 then
  172.             requirement1 = true
  173.         end
  174.        
  175.         timer_interrupts_same_frame = timer_interrupts_same_frame + 1
  176.         if timer_interrupts_same_frame >= 2 then
  177.             --t = t .. "\nTwo Timer Interrupts!"
  178.             table.insert(double_timer_interrupt_tbl, emu.framecount()+1)
  179.         end
  180.     end
  181. end, 0x0050)
  182.  
  183. -- Exec $0060
  184. --[[
  185. event.on_bus_exec(function(addr, val, flags)
  186.     if emu.framecount() > print_after_this_frame then
  187.         local myname = "Execu $0060 (JOYPAD INTERRUPT)"
  188.         t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  189.     end
  190. end, 0x0060)
  191. --]]
  192.  
  193. -- game version dependent execs
  194. if game_version == "1.0" then
  195.  
  196.     event.on_bus_exec(function(addr, val, flags)
  197.         if emu.framecount() > print_after_this_frame then
  198.             local myname = "Execu $2258 (READ $FFEA)"
  199.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  200.         end
  201.     end, 0x224F)
  202.    
  203.     event.on_bus_exec(function(addr, val, flags)
  204.         if emu.framecount() > print_after_this_frame then
  205.             local myname = "Execu $2254 (READ $FFE9)"
  206.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  207.         end
  208.     end, 0x2254)
  209.  
  210. elseif game_version == "1.1" then
  211.  
  212.     event.on_bus_exec(function(addr, val, flags)
  213.         if emu.framecount() > print_after_this_frame then
  214.             local myname = "Execu $2258 (READ $FFEA)"
  215.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  216.         end
  217.     end, 0x2258)
  218.    
  219.     event.on_bus_exec(function(addr, val, flags)
  220.         if emu.framecount() > print_after_this_frame then
  221.        
  222.             local myname = "Execu $225D (READ $FFE9)"
  223.             t = t .. "\n" .. string.format("%-32s", myname) .. " cycle: " .. emu.totalexecutedcycles() - cycles_at_frame_start
  224.         end
  225.     end, 0x225D)
  226.  
  227. end
  228.  
  229. while true do
  230.    
  231.     gui.clearGraphics()
  232.    
  233.     if #bugframe_tbl > 0 then
  234.         for i=1, #bugframe_tbl, 1 do
  235.             local bugframe_entry = bugframe_tbl[i]
  236.            
  237.             if bugframe_entry ~= nil then
  238.                        
  239.                 local bugframe_framecount = bugframe_entry[1]
  240.                 local bugframe_pixel = bugframe_entry[2]
  241.                 local current_pixel = memory.read_u8(0xFFE9) * 8
  242.                 local display_pixel = (bugframe_pixel - current_pixel) - memory.read_u8(0xFF43)%8 + 216
  243.  
  244.                 text(10, 10 + i*8, "bug Frame " .. bugframe_framecount, 0xFF00FF00, 0x80000000)
  245.            
  246.                 if display_pixel > 160 then
  247.                     text(136,10 + i*8, "--->", 0xFFFF0000, 0x80000000)
  248.                 else
  249.                     gui.drawLine(display_pixel, 17, display_pixel, 144, 0xFFFF0000)
  250.                 end
  251.                
  252.                 if PAUSE_WHEN_BUG_HAPPENS then client.pause() end
  253.                
  254.                 if display_pixel < 0 then
  255.                     bugframe_tbl[i] = nil
  256.                 end
  257.             end
  258.         end
  259.     end
  260.    
  261.     --[[
  262.     if #double_timer_interrupt_tbl > 0 then
  263.         for j=1, #double_timer_interrupt_tbl, 1 do
  264.             local frame = double_timer_interrupt_tbl[j]
  265.             text(10, 80 + j*8, "two timer interrupts frame " .. frame, 0xFF00FF00, 0x80000000)
  266.         end
  267.     end
  268.     --]]
  269.    
  270.     if TRY_RUN_TESTS then
  271.    
  272.         if run_test[1] then
  273.             -- Pause, then let this run while keeping previous input in tastudio
  274.             -- script will attempt unpause at increasing framecounts
  275.             tastudio.submitinputchange(emu.framecount(), "Right", true)
  276.             tastudio.submitinputchange(emu.framecount(), "A", true)
  277.             if emu.framecount() == waitframes then
  278.                 tastudio.submitinputchange(emu.framecount()+1, "Start", true)
  279.             end
  280.             if emu.framecount() > waitframes + 120 then
  281.                 savestate.loadslot(5)
  282.                 waitframes = waitframes + 1
  283.             end
  284.             tastudio.applyinputchanges()
  285.         end
  286.  
  287.         if run_test[2] then
  288.             -- Have Mario on the ground, clear upcoming 200 frames in tastudio
  289.             -- script will attempt increasing amounts of idleframes then run+jump.
  290.             if emu.framecount() >= waitframes then
  291.                 tastudio.submitinputchange(emu.framecount(), "Right", true)
  292.                 tastudio.submitinputchange(emu.framecount(), "B", true)
  293.             end
  294.            
  295.             if emu.framecount() > waitframes+6 then
  296.                 tastudio.submitinputchange(emu.framecount(), "A", true)
  297.             end
  298.            
  299.             if emu.framecount() > waitframes + 120 then
  300.                 savestate.loadslot(5)
  301.                 waitframes = waitframes + 1
  302.             end
  303.             tastudio.applyinputchanges()
  304.         end
  305.        
  306.         if run_test[3] then
  307.             -- Have Mario on the ground, clear upcoming 200 frames in tastudio
  308.             -- script will keep running right and repress B at increasingly later times
  309.             if emu.framecount() == waitframes then
  310.                 tastudio.submitinputchange(emu.framecount(), "Right", true)
  311.                 tastudio.submitinputchange(emu.framecount(), "B", false)
  312.             else
  313.                 tastudio.submitinputchange(emu.framecount(), "Right", true)
  314.                 tastudio.submitinputchange(emu.framecount(), "B", true)
  315.             end
  316.            
  317.             if emu.framecount() > 86570 then
  318.             --  tastudio.submitinputchange(emu.framecount(), "A", true)
  319.             end
  320.  
  321.             if emu.framecount() > waitframes + 120 then
  322.                 savestate.loadslot(5)
  323.                 waitframes = waitframes + 1
  324.             end
  325.             tastudio.applyinputchanges()
  326.         end
  327.        
  328.         if run_test[4] then
  329.             -- Have Mario on the ground, clear upcoming 200 frames in tastudio
  330.             -- script will keep running right and not hold 1 for one frame at increasing framecounts
  331.             if emu.framecount() == waitframes then
  332.                 tastudio.submitinputchange(emu.framecount(), "Right", false)
  333.                 tastudio.submitinputchange(emu.framecount(), "B", true)
  334.             else
  335.                 tastudio.submitinputchange(emu.framecount(), "Right", true)
  336.                 tastudio.submitinputchange(emu.framecount(), "B", true)
  337.             end
  338.            
  339.             if emu.framecount() > waitframes + 120 then
  340.                 savestate.loadslot(5)
  341.                 waitframes = waitframes + 1
  342.             end
  343.             tastudio.applyinputchanges()
  344.         end
  345.        
  346.         if run_test[5] then
  347.             -- Have Mario on the ground, clear upcoming 200 frames in tastudio
  348.             -- script will do inputs at set chance
  349.             local not_here = false
  350.                             --[[(memory.read_u8(0xFFF3) > 0x83 and memory.read_u8(0xFFF3) < 0x8B)
  351.                             or (memory.read_u8(0xFFF3) > 0x93 and memory.read_u8(0xFFF3) < 0x9B)
  352.                             or (memory.read_u8(0xFFF3) > 0xa3 and memory.read_u8(0xFFF3) < 0xaB)
  353.                             or (memory.read_u8(0xFFF3) > 0xb3 and memory.read_u8(0xFFF3) < 0xbB)
  354.                             or (memory.read_u8(0xFFF3) > 0xc3 and memory.read_u8(0xFFF3) < 0xcB)
  355.                             or (memory.read_u8(0xFFF3) > 0xd3 and memory.read_u8(0xFFF3) < 0xdB)
  356.                             --]]
  357.             local vary1 = math.random(1,80)
  358.             local vary2 = math.random(1,40)
  359.             local vary3 = math.random(28,42)
  360.             if math.random(1,1000) < (980-vary1) or not_here then
  361.                 tastudio.submitinputchange(emu.framecount(), "Right", true)
  362.             else
  363.                 tastudio.submitinputchange(emu.framecount(), "Right", false)
  364.             end
  365.             if (math.random(1,1000) < (979-vary2) and emu.framecount() > waitframes + vary3) or not_here then
  366.                 tastudio.submitinputchange(emu.framecount(), "B", true)
  367.             else
  368.                 tastudio.submitinputchange(emu.framecount(), "B", false)
  369.             end
  370.                
  371.             if emu.framecount() > waitframes + 95 then
  372.                 savestate.loadslot(5)
  373.             end
  374.             tastudio.applyinputchanges()
  375.         end
  376.        
  377.         if run_test[6] then
  378.             -- Have Mario on the ground, clear upcoming 200 frames in tastudio
  379.             -- script will do inputs at set chance
  380.             -- start this at 86282
  381.  
  382.             local vary1 = math.random(1,80)
  383.             local vary2 = math.random(1,50)
  384.             local press_b = true
  385.             local press_a = false
  386.             local press_right = true
  387.            
  388.             -- star block jump
  389.             if emu.framecount() >= waitframes + vary4 and emu.framecount() < waitframes + vary4 + 10 then
  390.                 press_a = true
  391.             else
  392.                 press_a = false
  393.             end
  394.            
  395.             -- ending jump
  396.             if memory.read_u8(0xFFF3) > 0x83 then
  397.                 press_a = true
  398.             end
  399.            
  400.             -- b kill
  401.             if emu.framecount() == waitframes + 17 + vary5 then
  402.                 press_b = false
  403.             end
  404.            
  405.             -- random
  406.             if not press_a then
  407.                 if not (math.random(1,1000) < (980-vary1)) then
  408.                     if emu.framecount() > waitframes + 10 then
  409.                         press_right = false
  410.                     end
  411.                 end
  412.                 if emu.framecount() > waitframes + 17 + vary5 then
  413.                     if math.random(1,1000) > (980-vary2) and (emu.framecount() > waitframes + 16 + vary3) then
  414.                         press_b = false
  415.                     end
  416.                 end
  417.             end
  418.  
  419.             tastudio.submitinputchange(emu.framecount(), "A", press_a)
  420.             tastudio.submitinputchange(emu.framecount(), "B", press_b)
  421.             tastudio.submitinputchange(emu.framecount(), "Right", press_right)
  422.            
  423.             if emu.framecount() > waitframes + 155 or emu.lagcount() > 407 then
  424.                 savestate.loadslot(5)
  425.             end
  426.             tastudio.applyinputchanges()
  427.         end
  428.     end
  429.  
  430.     emu.frameadvance()
  431. end
  432.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement