[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)