[HN Gopher] A toy RTOS inside Super Mario Bros. using emulator s...
       ___________________________________________________________________
        
       A toy RTOS inside Super Mario Bros. using emulator save states
        
       This started as a throwaway metaphor in a blog post, but is now
       fully runnable: a toy RTOS with preemptive multitasking inside of
       Super Mario Bros. on the NES.  Essentially, this is:  - A
       rudimentary preemptive RTOS  - Using an unmodified NES emulator
       (FCEUX) as the CPU                   - "Unmodified" depending on
       how you define terms       - With emulator save states as the
       thread contexts  - With support for (very basic) mutexes, interrupt
       masking, and condition variables  - Demonstrated using Super Mario
       Bros. 1-1 with sections of the map dedicated to various
       synchronization primitives  There are many simplifications and
       shortcuts taken (doesn't even have task priorities), and it doesn't
       map 1:1 to true multithreading (e.g., emulator save states
       represent the state of the entire machine including RAM, whereas
       thread contexts represent a much more minimal slice), but I think
       it's A) pretty interesting and B) a unique visceral explanation of
       threads.
        
       Author : notorious_pgb
       Score  : 84 points
       Date   : 2025-05-28 20:15 UTC (2 hours ago)
        
 (HTM) web link (prettygoodblog.com)
 (TXT) w3m dump (prettygoodblog.com)
        
       | ignormies wrote:
       | This is a super cool visual demonstration of RTOS/scheduling! I
       | love the region-based critical sections!
       | 
       | I took a real-time operating systems course in university as an
       | elective. One of the hardest courses I took the whole four years,
       | but also one of the most interesting. Had a great professor, who
       | gave really demanding, but very instructive, project-based
       | assignments.
       | 
       | I need to find a toy project to play around with this domain
       | again.
        
         | notorious_pgb wrote:
         | Thank you!
         | 
         | I'm curious how effective you feel this specific example
         | might've been if it were delivered during your course. I
         | _suspect_ I 've stumbled across a really helpful teaching tool,
         | but having not gone to university, I don't actually know how
         | this stuff is being taught :v
        
           | throwanem wrote:
           | Nor I, but _I 've_ learned from the metaphor, for what that's
           | worth. And the demystification of primitives has considerable
           | value these days.
        
             | notorious_pgb wrote:
             | > Nor I, but I've learned from the metaphor, for what
             | that's worth.
             | 
             | It's the sickest possible outcome for me, so: worth a lot!
        
       | Refreeze5224 wrote:
       | This is a fantastic way of demonstrating threading and
       | scheduling, well done!
        
       | bitwize wrote:
       | "...But first we need to talk about parallel universes."
        
       | dsfdsfsfew wrote:
       | Kind of pushing the definition of RTOS here, I was expecting
       | something like using ACE to run a concurrent program, still an
       | interesting and informative post.
        
         | notorious_pgb wrote:
         | You're goddamn right it's pushing it.
         | 
         | I struggled with how to convey this; the ultimate goal is to
         | _viscerally_ demonstrate _what a thread IS_ , so I'm
         | comfortable with it being... something strange... but I do want
         | it to resemble actual concurrent systems to a useful degree.
         | 
         | Like, the thread scheduler code is userland code -- similar to,
         | say, FreeRTOS -- but the "threads" themselves aren't _exactly_
         | threads in an exact sense, since their context packages are
         | entire save states for a console, not just its processor.
         | 
         | Also, the game code (the true userland?) has no ability to
         | access the threading API whatsoever, which really harms the
         | analogy.
         | 
         | So I went with RTOS because it's a preemptive scheduler with
         | synchronous reschedule-on-block; where the scheduler is
         | implemented in the same codebase as the code using it. But to
         | be honest, nothing I know of fits.
        
       | loas wrote:
       | I really enjoyed this, thank you.
        
       | shakna wrote:
       | If you're going to make this your DooM thing, clearly the next
       | thing to do is grab a DooM engine and get to work. (PsyDoom has
       | Lua support even).
       | 
       | But congrats. This is worth being included in a uni course.
        
         | notorious_pgb wrote:
         | It'd be really interesting to push this to that level -- the
         | big challenge likely being that DooM's state (including video
         | memory) is certainly much larger and expensive to swap than an
         | NES save state.
         | 
         | One would have to get really clever...
        
       | anthk wrote:
       | Forth does that too.
       | https://www.bradrodriguez.com/papers/mtasking.html
        
       | gwbas1c wrote:
       | Last night I read an article about eliminating lag in emulators.
       | It's done with a similar concept. Basically, for each frame, the
       | emulator calculates the state for different button combinations.
       | Then, based on the button you actually push, the state shown
       | moves to the precalculated one.
        
         | jchw wrote:
         | Not 100% familiar with exactly what this is but I am familiar
         | with run-ahead, which is basically also the same idea as GGPO,
         | but for eliminating lag:
         | 
         | - Run the emulator one frame normally, using real polled input.
         | Somehow snapshot the state.
         | 
         | - Then, run the emulator n frames (usually just one) with the
         | same input. Present the video + audio from the _last_ frame.
         | 
         | - Synchronize. (Some emulators can get very fancy; instead of
         | just waiting for vsync, they'll also delay until the end of the
         | window minus estimated processing time, to poll input at the
         | last possible moment.)
         | 
         | - Roll back to the saved snapshot. (I believe you can also
         | optimize if you know the inputs really didn't change, avoiding
         | a lot of rollbacks at the cost of less predictable frame
         | times.)
         | 
         | The main reason this is even a good idea is because most games
         | will have some of their own processing latency by design, so
         | jumping a frame or two ahead usually doesn't have any
         | noticeable side-effects. This is a pretty cool idea since
         | obviously modern computers with LCD screens have a lot more
         | latency basically everywhere versus older simpler machines
         | connected to CRTs.
         | 
         | Unfortunately, this sort of approach only works when your
         | emulator's state is small enough and fast enough to restore.
         | 
         | I actually have been dying to experiment with designing an
         | emulator to have fast incremental snapshots from the ground up
         | to see if you could manage to make this feasible for more
         | modern consoles. You could, for example, track dirty memory
         | pages with userfaultfd/MEM_WRITE_WATCH, and design structures
         | like JIT caches to be able to handle rewinding without having
         | to drop the entire cache. I'm actually not sure that all
         | emulators clear their caches upon loading state, but you know,
         | more generally, I would like to know how fast and small you
         | could get save states to be if you were designing for that from
         | the ground up.
        
           | achierius wrote:
           | What do you mean by JIT cache in this case -- like an inline
           | data cache, or like a cache for the jitted binary code?
        
       | liamwire wrote:
       | Brilliant visual demonstration, really helps build an intuition
       | for the concepts at play.
        
       | Sohcahtoa82 wrote:
       | That's pretty neat.
       | 
       | The metaphor I usually go for when it comes to for threads is
       | having several widgets that need to be assembled. To begin
       | working on Widget B, you have to pause the work on Widget A.
       | Adding SMP is like adding a second person to help assemble
       | widgets, but you're still using the same toolbox, so if you both
       | need the same screwdriver, there's no performance benefit to
       | having a second person. Multicore is having multiple workbenches
       | each with their own toolbox so they can operate completely in
       | parallel.
       | 
       | A mutex is like having a specialized tool that can only be used
       | by one widget assembly at a time. Like, each workbench might have
       | a full set of screwdrivers, hammers, wrenches, sockets, etc., but
       | you only have 1 welder.
       | 
       | A semaphore is a tool that can be used by a limited number of
       | widgets, like an oven that can fit up to 4 widgets.
        
       ___________________________________________________________________
       (page generated 2025-05-28 23:00 UTC)