Nested If-Else in NASM Assembler Macros Explained
Dr. Jonas Birch shows how NASM's context stack enables nested if-else constructs in assembly macros—and what that reveals about systems programming culture.
Written by AI. Dev Kapoor

Photo: AI. Tomoko Hayashi
There's a specific kind of stubbornness that lives inside systems programming culture, and it isn't always a virtue. The community has long treated abstraction with a suspicion that borders on ideology — if you need a readable if-then-else, the thinking goes, maybe you should be writing C. Assembly is for people who know what they're doing. Suffering through label collision hell when you nest conditionals isn't a bug in the culture; it's the filter.
The problem is that this attitude has calcified into a genuine infrastructure gap. People are writing serious assembly — bootloaders, OS kernels, embedded firmware, hypervisor components — and they are largely doing it without the kind of progressive, community-developed macro tooling that other language ecosystems take for granted. The documentation is scattered, the tutorials are either trivial or ancient, and the intermediate-to-advanced practitioner is mostly on their own.
That's the context in which Dr. Jonas Birch's recent deep-dive into advanced NASM macros lands. The video is a feature-length walkthrough that tackles a genuinely thorny problem: can you build a proper, nestable if-then-else construct inside assembler macros? And it's worth covering not just because the technical answer is interesting, but because the existence of this content says something about who is and isn't being served by mainstream developer education.
The Problem Is Messier Than It Looks
Birch opens by laying out why you'd even want this. "As you know, there are no real if statements inside of assembly. There's just conditional jumps." That one sentence is doing a lot of work. For anyone who hasn't written assembly, the implication is that every branch in your logic requires you to manually name a label, jump to it conditionally, jump past the else-block, define another label — and when you nest that logic two or three levels deep, you end up managing a label namespace that would make a COBOL programmer wince.
The naive solution — write a macro that emits a jump and a label — works until you try to use it twice. Then you have two macros both emitting a label called if_ne, and NASM refuses. Label collision. You can't define the same label twice in the same binary.
Birch's first attempt at solving this uses NASM's %%label syntax, which generates unique local labels scoped to a single macro invocation. Clean, elegant — and completely insufficient here. The problem is that the if-then-else construct spans multiple macros: if_statement, then, else, and if_end are all separate macros that need to share a label. Local macro labels don't cross that boundary. "This type of construction works perfect when we are only dealing with one macro," Birch explains. "But here we have the if statement, we have the then, we have the else, we have a lot of different macros. And each time we define a new macro, this will become a new label. So, we can't refer to a label from another macro."
This is the kind of failure mode that sends people back to raw conditional jumps and hand-labeled spaghetti. It's also a failure mode that mainstream developer education never prepares you for, because mainstream developer education stopped at "here's how to write a simple macro" about fifteen years ago.
The Context Stack Is the Actual Insight
The solution Birch arrives at is NASM's context stack — a less-discussed feature that lets you push a named context frame, define labels scoped to that context using %$label syntax, and then pop the frame when you're done. Because the context frame persists across multiple macro invocations until you explicitly pop it, the separate macros in your if-then-else chain can all reference the same scoped labels.
The mechanics: inside the if_statement macro, Birch pushes a context frame onto the stack with %push if. The then, else, and if_end macros then reference labels using %$ — pointing into the current context rather than generating fresh macro-local labels. When if_end fires, it pops the context, cleaning up. Nest another if-statement inside the first one, and a new context frame gets pushed on top; the inner block's labels live in that frame, the outer block's in the one below. No collision.
It's genuinely elegant once you see it. NASM's context stack was designed for exactly this — macro-level state management across multiple invocations — but you'd be forgiven for not knowing that, because it's the kind of feature that lives in the reference documentation and not in any tutorial aimed at people actually building things.
Birch also folds in compile-time error checking. If you write a then without a preceding if_statement, the macro can detect that the context stack is in the wrong state and emit a meaningful error message rather than producing broken output. "Which means that if we try to compile this, we get this if then else syntax error," he notes. "Which is a little bit more helpful than just stuff like this alone." That's not a flashy feature, but it's the kind of ergonomic care that distinguishes macro tooling you can actually maintain from macro tooling you built once and are afraid to touch.
What This Costs, and Who That Affects
Here's where I'd push back a little on the pure enthusiasm. The context stack approach works, but it asks a lot of anyone inheriting this code. The %push, %$, and %pop conventions are not obvious to readers who haven't internalized NASM's macro model — which is almost everyone, because almost no one teaches it. A team of three working on an OS kernel, one of whom built this macro library, is one person-departure away from macros that feel like archaeology.
That documentation burden is non-trivial. The pattern Birch demonstrates — an incremental build from naive implementation through three iterations to a working, nestable solution — is pedagogically excellent in a tutorial context. It's less excellent as a model for how you'd hand this off to a colleague. You'd need to write the equivalent of a small language specification to explain what if_statement, then, else, and if_end are actually doing at the assembler level.
There's also a narrower set of right-use-cases than the video's framing might suggest. Birch's primary application is his 16-bit OS kernel, which is explicitly a project where assembly is the only reasonable choice. For the majority of cases where someone is considering assembly for performance reasons, the macro complexity trades against the assembly's legibility advantage in ways that deserve a harder look. This isn't a reason not to build the tooling — Birch's use case is completely legitimate — but it's worth naming rather than waving past.
The Underserved Community Problem
None of this changes what I find genuinely compelling about this video as a cultural artifact. The systems programming community is not well-served by the educational content ecosystem. The gap between "hello world in NASM" and "here's how to build maintainable macro infrastructure for a large assembly project" is enormous, and it's mostly filled by individuals who figured things out themselves and occasionally write it up on a personal blog that may or may not still resolve.
Birch opens the video with a note about being out for a while due to health issues — a pulmonary embolism, specifically — before getting into NASM context stacks. That juxtaposition is just the reality of how deep-dive technical content gets made. Not by content teams with editorial pipelines, but by individual practitioners who decided to teach what they know, on their own schedule, around their actual lives.
The audience for this kind of content is real, it's underserved by mainstream platforms, and it's working on software that matters — kernels, firmware, embedded systems, the kind of code that runs before the operating system loads. Asking why mainstream developer education doesn't serve them is partly a question about what developer education is actually for, and who it's designed to support.
Whether the context stack pattern Birch demonstrates becomes part of an emerging shared library of assembly macro tools, or stays siloed in individual projects, probably depends on whether there's a community structure to aggregate and maintain it. That community structure, for assembly programmers, is notably thin.
By Dev Kapoor, Open Source & Developer Communities Correspondent, Buzzrag
We Watch Tech YouTube So You Don't Have To
Get the week's best tech insights, summarized and delivered to your inbox. No fluff, no spam.
More Like This
How Cloudflare Uses Lava Lamps to Encrypt the Internet
Cloudflare's San Francisco office has a wall of 100 lava lamps generating entropy for SSL/TLS encryption. Here's why computers can't be truly random.
Bridging the Gap: C++ Workshop Tackles Industry Reality
Amir Kirsh's workshop addresses the persistent divide between academic C++ and production code—and questions whether one-day training can solve it.
How the Nest Thermostat Launched the Smart Home Era
Tony Fadell's Nest Learning Thermostat didn't just fix an ugly device—it sparked the smart home era. A look at what it got right, wrong, and what Google killed.
Building a Bitmap in C and Who Really Needs This
Dr. Jonas Birch's bitmap tutorial in C reveals a quiet gap in how systems programming knowledge gets transmitted—and who's filling it, and why.
AI Website Builder Creates Full Site From Business Card
Gary Explains tests Readdy AI's ability to generate professional websites from business cards alone. Five minutes, zero code—but what does this mean for web dev?
GPT 5.5 Isn't Actually Running Unless You Check These Settings
Most people don't realize their new AI model isn't even activated. Here's what TheAIGRID found about GPT 5.5's hidden configuration issues.
RAG·vector embedding
2026-07-25This article is indexed as a 1536-dimensional vector for semantic retrieval. Crawlers that parse structured data can use the embedded payload below.