Seyed Masoud Hosseini · Overview · Study log · Ideas · Transcript · RSS feed
Embedded Systems, 6502 breadboard computer · Lecture 11 of 29 · 27:35
Hardware Interrupts on the 6502
Study guide
What this lecture covers
Most CPUs don't just execute instructions in a straight line; they can be interrupted mid-program to run other code and then resume where they left off. This lecture asks how that actually works at the hardware level on the 6502, building directly on earlier videos in Ben Eater's breadboard computer series that set up the reset vector and the binary-to-decimal display program.
By the end, you'll understand the difference between the 6502's two interrupt pins (IRQ and NMI), how to set up interrupt vectors and handlers in assembly, why a naive button-triggered interrupt behaves in surprising ways, and why real hardware interrupt sources need more than a simple switch to work correctly.
Key ideas
- Interrupt vectors: the 6502 reads a handler address from a fixed ROM location when it resets (
$FFFC/$FFFD), and similarly from$FFFA/$FFFBfor NMI and$FFFE/$FFFFfor IRQ. - RTI: the
RTI(return from interrupt) instruction resumes the interrupted program from where the CPU left off. - IRQ is maskable: interrupt requests can be disabled via the interrupt-disable status bit, cleared with the
CLIinstruction to enable interrupts. - IRQ is level-triggered: as long as the IRQ pin is held low, the CPU keeps re-entering the interrupt handler, which caused the counter in the demo to jump by hundreds or thousands per button press.
- NMI is edge-triggered and unmaskable: it fires once on a falling transition and can't be disabled, even during timing-sensitive code.
- Shared interrupt lines need acknowledgment: real devices (keyboard, disk, network) are expected to hold the interrupt line low until the handler explicitly services and clears that specific source, something a plain switch can't do.
- Race conditions across interrupts: an interrupt firing in the middle of a multi-byte read can corrupt data (e.g. reading half the counter before and half after an increment); disabling interrupts around critical sections avoids this.
- NMI's real use case: because it can't be masked, NMI suits urgent conditions like imminent power loss, not routine, shareable interrupt sources.
Walkthrough
Introduction to interrupts (0:00)
The lecture explains that interrupts let the CPU pause normal execution and jump to handler code, and introduces the 6502's two interrupt pins, IRQ (pin 4) and NMI (pin 6), both active low.
IRQ and NMI signals, and setting up interrupt vectors (1:03)
The video adds NMI and IRQ vector addresses ($FFFA and $FFFE) alongside the existing reset vector, pointing them at new nmi and irq labels, and introduces RTI to return from a handler.
Writing the interrupt handler and hardware implementation (3:26 and 4:20)
A button is wired to the IRQ pin through a pull-up resistor, so pressing it pulls the pin low. The IRQ handler increments a 16-bit counter in RAM, and the existing binary-to-decimal program (from the previous video) is modified to loop continuously and print that counter to the LCD.
Testing the counter (5:40)
The first test does nothing on a button press, because the interrupt-disable status bit is set by default.
Fixing the disabled interrupt and interrupt behavior analysis (10:44 and 12:40)
Adding CLI enables interrupts, but now a single button press increments the counter by thousands, revealing that IRQ is level-triggered: the CPU keeps re-entering the handler as long as the pin is held low, effectively freezing the rest of the program while the button is down.
Understanding IRQ mechanics (14:11)
The datasheet explains that the IRQ line is meant to stay low until the interrupt handler clears whatever triggered it, a model designed for shared interrupt sources like keyboards or disk controllers that need to be individually acknowledged, something a plain switch can't do.
Non-maskable interrupt (NMI) and its testing (18:02 and 20:08)
Moving the button to the NMI pin and adding an NMI handler shows the opposite behavior: one interrupt fires per falling edge (occasionally two, due to switch bounce), regardless of how long the button is held.
Data consistency issues and IRQ vs NMI use cases (21:55 and 23:46)
The lecture explains why NMI's inability to be disabled is risky for timing-sensitive code, walks through a race condition where an interrupt mid-read can corrupt a multi-byte counter value, and shows disabling interrupts around the critical read as the fix. It closes by contrasting IRQ's suitability for shareable, acknowledgeable sources against NMI's niche use, such as reacting to imminent power loss.
Before you watch
- Watch the earlier videos on setting up the reset vector and the binary-to-decimal display program, since both are reused and extended here.
- Be familiar with the 6502 status register, especially the interrupt-disable bit, and instructions like
CLI,SEIandRTI.
Check your understanding
- What is the difference between how the IRQ pin and the NMI pin trigger an interrupt?
- Why did the counter jump by a large, unpredictable amount on each IRQ button press instead of incrementing by one?
- Why is a plain switch a poor way to trigger a real IRQ-style interrupt source?
- How can an interrupt firing in the middle of a multi-byte memory read corrupt the value being read, and how does the lecture prevent it?
- Why is the non-maskable interrupt reserved for situations like imminent power loss rather than everyday use?
Chapters
- 0:00 Introduction to interrupts
- 1:03 IRQ and NMI signals
- 2:07 Setting up interrupt vectors
- 3:26 Writing the interrupt handler
- 4:20 Hardware implementation
- 5:40 Testing the counter
- 10:44 Fixing the disabled interrupt
- 12:40 Interrupt behavior analysis
- 14:11 Understanding IRQ mechanics
- 18:02 Non-maskable interrupt (NMI)
- 18:58 Implementing NMI handler
- 20:08 NMI testing and limitations
- 21:55 Data consistency issues
- 23:46 IRQ vs NMI use cases
- 26:03 Conclusion and next steps
From the YouTube description
More on the 6502 computer: https://eater.net/6502
Support these videos on Patreon: https://www.patreon.com/beneater or https://eater.net/support for other ways to support.
------------------
Social media:
Website: https://www.eater.net
Twitter: https://x.com/beneater
Patreon: https://patreon.com/beneater
Reddit: https://www.reddit.com/r/beneater
Special thanks to these supporters for making this video possible:
Adrien Friggeri, Alexander Wendland, Andrew Vauter, Anson VanDoren, Anthanasius, Armin Brauns, bapu, Ben Dyson, Ben Kamens, Ben Williams, Bill Cooksey, Binh Tran, Bouke Groenescheij, Bradley Pirtle, Bryan Brickman, Carlos Ambrozak, Christopher Blackmon, Daniel Jeppsson, Daniel Sackett, Daniel Tang, Dave Burley, Dave Walter, David Brown, David Clark, David House, David Sastre Medina, David Turnbull, David Turner, Dean Winger, Dmitry Guyvoronsky, Dušan Dželebdžić, Dzevad Trumic, Emilio Mendoza, Eric Brummer, Eric Busalacchi, Eric Dynowski, Eric Twilegar, Erik Broeders, Eugene Bulkin, fxshlein, George Miroshnykov, Harry McDow, HaykH, Hidde de Jong, Ian Tait, Ingo Eble, Ivan Sorokin, Jason DeStefano, Jason Specland, JavaXP, Jay Binks, Jayne Gabriele, Jeremy A., Jim Kelly, Jim Knowler, Jim Van Meggelen, Joe OConnor, Joe Pregracke, Joel Jakobsson, Joel Messerli, Joel Miller, Johannes Lundberg, John Fenwick, John Meade, Jon Dugan, Joseph Locke, Joshua King, Kefen, Kenneth Christensen, Kent Collins, Koreo, Lambda GPU Workstations, Larry, Lucas Nestor, Lukasz Pacholik, Maksym Zavershynskyi, Marcus Classon, Martin Roth, Mats Fredriksson, Matt Alexander, Matthäus Pawelczyk, melvin2001, Michael Burke, Michael Garland, Michael Tedder, Michael Timbrook, Miguel Ríos, Mikel Lindsaar, Nicholas Counts, Nicholas Moresco, Örn Arnarson, Paul Pluzhnikov, Paul Randal, Pete Dietl, Philip Hofstetter, Randy True, Ric King, Richard Wells, Rob Bruno, Robert Diaz, sam raza, Sam Rose, SonOfSofaman, Stefan Nesinger, Stefanus Du Toit, Stephen Kelley, Stephen Riley, Stephen Smithstone, Steve Jones, Steve Gorman, Steven Pequeno, TheWebMachine, Tom Burns, Vlad Goran, Vladimir Kanazir, Warren Miller, xisente, Yusuke Saito
← Binary to Decimal: Why It's Not That Simple · Interrupt Handling with the 6522 VIA →
