Using NMI as a programmer's "panic" switch

Programming the 6502 microprocessor and its relatives in assembly and other languages.
User avatar
RichCini
Posts: 156
Joined: 03 Sep 2003
Location: Long Island, NY
Contact:

Using NMI as a programmer's "panic" switch

Post by RichCini »

All --

I've run into a bit of a snag with some NMI code for the SBC I've built (based on Daryl Rictor's board). I'm creating a programmer's "panic" button that returns code execution to the monitor program...the code will never return to where it was originally executing.

I connected one of the VIAs to /NMI, configured CA1 with a pull-up resistor and a push button switch to ground, and configured and armed the correct interrupt -- this is how Commodore did it for the RUN/STOP-RESTORE combination, although I'm using only one switch.

Here is the NMI handler code:

Code: Select all

NMIVEC				
	SEI			
	PHA			;save regs
	TXA			
	PHA			
	TYA			
	PHA			
	LDA 	D1IFR		;check VIA interrupt flag
	BPL 	WARMEOI		;no flag, then issue EOI
	
WARM1
;	LDA 	D1IER		;get IER bitmap
;	ORA 	#%10000010	;$80 set mask for enabling interrupts
;	PHA			; according to existing bitmap
;	LDA 	#%01111111	;$7F mask - disable all VIA1 interrupts
;	STA 	D1IER		
;	PLA
;	STA	D1IER		; re-arm interrupts
	PLA			;restore registers
	TAY			
	PLA			
	TAX			
	PLA			
	CLI
	JMP	Monitor   ; monitor warm-start entry

WARMEOI				
	PLA			;restore registers
	TAY			
	PLA			
	TAX			
	PLA			
	RTI			;return from interrupt
The above code works, returning to the monitor program, but only once -- the code to re-arm the interrupt is commented out. When this code is enabled, it never returns from the interrupt, requiring a hard reset. I'm sure it's something obvious to the experienced programmer, but I'm plain missing it. Maybe I should re-arm the interrupt in the monitor?

I did read the Interrupts tutorial and other than my interrupt disable and the method of re-arming the interrupt, the code is pretty simple.

Any nudge in the right direction would be appreciated. Thanks!
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by GARTHWILSON »

RichCini wrote:
I connected one of the VIAs to /NMI, configured CA1 with a pull-up resistor and a push button switch to ground [...] The above code works, returning to the monitor program, but only once -- the code to re-arm the interrupt is commented out. When this code is enabled, it never returns from the interrupt, requiring a hard reset. I'm sure it's something obvious to the experienced programmer, but I'm plain missing it. Maybe I should re-arm the interrupt in the monitor? [...] Any nudge in the right direction would be appreciated. Thanks!
It sounds like you didn't de-bounce the switch. An RC followed by a Schmitt-trigger gate will do the job. Make it take 25-50ms to respond and send an active edge to CA1. Without the de-bouncing, you're probably entering the NMI ISR many, many times, possibly enough to overrun the stack.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
RichCini
Posts: 156
Joined: 03 Sep 2003
Location: Long Island, NY
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by RichCini »

GARTHWILSON wrote:
RichCini wrote:
I connected one of the VIAs to /NMI, configured CA1 with a pull-up resistor and a push button switch to ground [...] The above code works, returning to the monitor program, but only once -- the code to re-arm the interrupt is commented out. When this code is enabled, it never returns from the interrupt, requiring a hard reset. I'm sure it's something obvious to the experienced programmer, but I'm plain missing it. Maybe I should re-arm the interrupt in the monitor? [...] Any nudge in the right direction would be appreciated. Thanks!
It sounds like you didn't de-bounce the switch. An RC followed by a Schmitt-trigger gate will do the job. Make it take 25-50ms to respond and send an active edge to CA1. Without the de-bouncing, you're probably entering the NMI ISR many, many times, possibly enough to overrun the stack.
Garth --

Thanks. You know, the circuit, as simple as it is, was right out of the C64 manual. I thought that the initial check of the IER was Commodore's way to catch spurious interrupts. Also, interrupts are disabled on entry to the NMI so wouldn't that prevent the runaway condition you're describing?

This looks like a pretty solid circuit: http://robotgestation.com/SensorForce.html. I'll run the math tomorrow. Not sure I have room on the board for another DIP, but we'll see.

Thanks again!
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by GARTHWILSON »

RichCini wrote:
Also, interrupts are disabled on entry to the NMI so wouldn't that prevent the runaway condition you're describing?
The SEI at the beginning of the ISR is redundant, as it is already done automatically as part of the interrupt sequence; but that's for IRQ's not NMI's which are, by definition, non-maskable. However, looking at the data book again, it looks like the only way to clear the CA1 interrupt is to address the port-A register; so if you haven't read (or written) it yet, bounce on the switch should not be toggling the VIA's interrupt output. I wonder if there's anything there that the data book doesn't tell us. Can you catch a single, clean edge on an oscilloscope? I have a similar connection, but have not put it to use yet. One of the choices my reset routine gives is to re-start the routine whose address is in a variable called INIT-AP, so after I get control back, I can do that to immediately re-initialize everything and set the routine to running again in a second, without re-loading things. Still, the ABORT button is nice because you can get the attention of the processor if it's stuck in a loop whose exit condition will never be met, without resetting the I/O ICs.

Edit: It looks like you might be toggling the interrupt output by disabling and then re-enabling the interrupt by writing to the IER, both in the commented-out portion.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
BigDumbDinosaur
Posts: 9426
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by BigDumbDinosaur »

RichCini wrote:
All --

I've run into a bit of a snag with some NMI code for the SBC I've built (based on Daryl Rictor's board). I'm creating a programmer's "panic" button that returns code execution to the monitor program...the code will never return to where it was originally executing.

I connected one of the VIAs to /NMI, configured CA1 with a pull-up resistor and a push button switch to ground, and configured and armed the correct interrupt -- this is how Commodore did it for the RUN/STOP-RESTORE combination, although I'm using only one switch...
You're making it way too complicated. Hook up a Maxim DS1813 to /NMI, along with a push button between /NMI and ground (see attachment). When you press the push button and ground /NMI, the DS1813 will hold /NMI low for 150-200 milliseconds after you release the push button, thus debouncing the circuit. In your NMI handler, remove the beginning SEI, which as Garth pointed out, is redundant. Push the registers and enter your M/L monitor, making sure to re-enable IRQs at some point. Nothing else is needed.

What I have described is the exact setup I used in POC V1, which works like a charm. The Commodore method was necessary because /NMI is also used by the 6522 (6526 in the C-64 and C-128) to interrupt when the fake RS-232 handler needed attention. If your only use for /NMI is to regain control when a program goes berserk you don't need the extra stuff involved with the 6522.
NMI Debouncer
NMI Debouncer
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
RichCini
Posts: 156
Joined: 03 Sep 2003
Location: Long Island, NY
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by RichCini »

BigDumbDinosaur wrote:
RichCini wrote:
All --

I've run into a bit of a snag with some NMI code for the SBC I've built (based on Daryl Rictor's board). I'm creating a programmer's "panic" button that returns code execution to the monitor program...the code will never return to where it was originally executing.

I connected one of the VIAs to /NMI, configured CA1 with a pull-up resistor and a push button switch to ground, and configured and armed the correct interrupt -- this is how Commodore did it for the RUN/STOP-RESTORE combination, although I'm using only one switch...
You're making it way too complicated. Hook up a Maxim DS1813 to /NMI, along with a push button between /NMI and ground (see attachment). When you press the push button and ground /NMI, the DS1813 will hold /NMI low for 150-200 milliseconds after you release the push button, thus debouncing the circuit. In your NMI handler, remove the beginning SEI, which as Garth pointed out, is redundant. Push the registers and enter your M/L monitor, making sure to re-enable IRQs at some point. Nothing else is needed.

What I have described is the exact setup I used in POC V1, which works like a charm. The Commodore method was necessary because /NMI is also used by the 6522 (6526 in the C-64 and C-128) to interrupt when the fake RS-232 handler needed attention. If your only use for /NMI is to regain control when a program goes berserk you don't need the extra stuff involved with the 6522.
nmi_debounce.gif
This is excellent! I'm actually using the 1813 for handling reset on the board (DS1813-10). I'll give this a try since I have a bunch of them, and there's space on the board for it.

Regarding Garth's comment...the commented-out code is right from the Commodore kernal ROM in the NMI handler (with the RS232 stuff removed), as is the SEI.

Regarding the SEI, would disabling interrupts prevent the NMI code from being interrupted by an IRQ interrupt from another source? Why would the commented-out code, which disables and re-enables the interrupts on that VIA, cause the system to go into lala land? I can easily change it to a read on port A for testing purposes.

EDIT: Doesn't NMI also push the processor status word? So, jumping from the handler to the monitor leaves an extra byte on the stack, doesn't it?
Thanks again for the help!
teamtempest
Posts: 443
Joined: 08 Nov 2009
Location: Minnesota
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by teamtempest »

Quote:
The Commodore method was necessary because /NMI is also used by the 6522 (6526 in the C-64 and C-128) to interrupt when the fake RS-232 handler needed attention.
My understanding is that in the days of stone knives and bearskins all serial communication was handled by software. Then came the hardware UARTs, which drove out the software from the land. Nowadays if you see such a fossil you call it a "software UART".

I suppose it is "fake" in the sense that the handler does try to emulate a particular hardware UART.
teamtempest
Posts: 443
Joined: 08 Nov 2009
Location: Minnesota
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by teamtempest »

Quote:
Doesn't NMI also push the processor status word? So, jumping from the handler to the monitor leaves an extra byte on the stack, doesn't it?
True but so what? So is the program counter, for that matter.

When entering your monitor program you can do whatever you want, including re-setting the stack pointer. It might be useful first to recover and display register values (the program counter in particular might be a clue to where things went wrong).

You might even have two entry points to the monitor, one for "normal" entries and one for "NMI" entries. The "normal" entry might be a complete "cold start" of the machine, whereas an "NMI" entry would be a "recover and warm start" (so as to preserve as much state as possible so you can look at it).
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by GARTHWILSON »

RichCini wrote:
This is excellent! I'm actually using the 1813 for handling reset on the board (DS1813-10). I'll give this a try since I have a bunch of them, and there's space on the board for it.
Make sure you haven't forgotten what BDD said about NMI timing: "If your only use for /NMI is to regain control when a program goes berserk...," because the delay introduced by the DS1813 may make that line nearly useless for anything else.
Quote:
Regarding Garth's comment...the commented-out code is right from the Commodore kernal ROM in the NMI handler (with the RS232 stuff removed), as is the SEI.

Everyone goofs once in a while--even them. Since it didn't cause any problems, it didn't get caught. It's redundant. Another possibility that comes to mind is that something else could branch to there so the program counter gets there without an actual interrupt; but that might cause problems at RTI time.
Quote:
Regarding the SEI, would disabling interrupts prevent the NMI code from being interrupted by an IRQ interrupt from another source?
The interrupt sequences already set the interrupt-disable flag, I, which affects only the IRQ input, not the NMI input. Interrupts on the IRQ\ pin are the only ones that are ignored while this bit is set. It does not affect NMI's or BRK's, but IRQ's are automatically ignored.
Quote:
Why would the commented-out code, which disables and re-enables the interrupts on that VIA, cause the system to go into lala land?
Because, as the comment on one of the lines says, you disable all VIA1 interrupts, then, two lines later, enable them again; so the NMI line goes up, and then back down; and since it's non-maskable, and the CA1 interrupt was not taken care of, that extra falling edge on NMI makes it enter the routine again. And again. And again. And again. I think you'll have to read port A to stop the problem.
Quote:
EDIT: Doesn't NMI also push the processor status word? So, jumping from the handler to the monitor leaves an extra byte on the stack, doesn't it?
Yes. That's why the ISR doesn't need extra instructions to save P.

Garth, the interrupts junkie.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
RichCini
Posts: 156
Joined: 03 Sep 2003
Location: Long Island, NY
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by RichCini »

Thanks again. Lots to work on tonight!
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by BigEd »

BDD, a query about your use of a DS1813 as an NMI debouncer: I think it will cause an NMI at power-up time. Is that right, and does it need any particular attention?
Cheers
Ed
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by GARTHWILSON »

Ed, it should be ok, as the rising edge is not an issue.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
BigDumbDinosaur
Posts: 9426
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by BigDumbDinosaur »

teamtempest wrote:
Quote:
The Commodore method was necessary because /NMI is also used by the 6522 (6526 in the C-64 and C-128) to interrupt when the fake RS-232 handler needed attention.
My understanding is that in the days of stone knives and bearskins all serial communication was handled by software. Then came the hardware UARTs, which drove out the software from the land. Nowadays if you see such a fossil you call it a "software UART".

I suppose it is "fake" in the sense that the handler does try to emulate a particular hardware UART.
A little history: the NMOS 6551 was available in quantity prior to the introduction of the Commodore VIC-20 (1980), but was deemed to be too expensive for use in that machine. The VIC-20 had a pair of the recently introduced 6522s, so CBM's software engineers decided to emulate the 6551 in the kernel, using 6522 timer interrupts to shift bits in and out. It worked okay at 300 bps, which was the standard modem speed of the day. The code was quite convoluted and messy.

When the C-64 was conceived, the decision to continue with 6551 kernel emulation was made, even though by then the 6551 was quite inexpensive. It was Jack Tramiel trying to squeeze every last bit of cost out of the new machine. Unfortunately, when the VIC-20 kernel code was carried over to the C-64, it was not edited to remove the hoop-jumping that was required with the 6522's timer behavior—timer and interrupt control in the 6526 complex interface adapter (CIA) was simplified compared to the 6522. The result was that the C-64's RS-232 performance was needlessly hobbled by extra instructions that were no longer needed, plus was further crippled by some bugs that were solved and documented a number of years later by George Hugg (Transactor magazine, February 1989 on page 62).

Incidentally, George also confirmed what a number of us had suspected: there were two hardware defects in the 6526 that contributed to anomalous interrupt behavior. Critical to the RS-232 code was that if the CIA's interrupt status register was queried one or two clock cycles before a timer B interrupt was scheduled to occur, that interrupt would never come, causing a receive overrun. The other bug was in the time-of-day clock, which if set to interrupt when the tenths-of-seconds were exactly zero, would not always interrupt. The workaround if using time-of-day interrupts was to set the alarm time to HH:MM:SS.1, not HH:MM:SS.0.
Last edited by BigDumbDinosaur on Tue Feb 18, 2014 6:48 pm, edited 1 time in total.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
BigDumbDinosaur
Posts: 9426
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by BigDumbDinosaur »

BigEd wrote:
BDD, a query about your use of a DS1813 as an NMI debouncer: I think it will cause an NMI at power-up time. Is that right, and does it need any particular attention?
Cheers
Ed
Strangely enough, no. Immediately after power-on, the other DS1813 that is attached to reset has the MPU halted, so the latter doesn't pay attention to NMI. Even if the reset DS1813 clears first, startup will proceed normally, since as Garth said, the MPU doesn't react to the rising edge on NMI when its DS1813 clears.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
RichCini
Posts: 156
Joined: 03 Sep 2003
Location: Long Island, NY
Contact:

Re: Using NMI as a programmer's "panic" switch

Post by RichCini »

Ok, following up on this, I made changes to the code based on today's discussion but still kept the simple switch input

Code: Select all

NMIVEC				
	PHA			;save regs
	TXA			
	PHA			
	TYA			
	PHA			

	LDA	Via1PRA		; re-arm by reading port A
				; not sure if this clears bit7

	PLA			;restore registers
	TAY			
	PLA			
	TAX			
	PLA			
	CLI			; re-enable interrupts
	JMP	Brk2		; part of BRK handler

Although costing a few bytes, I left all the register saving in case I need it later. The Brk2 routine in my monitor is part of the BRK handler. Among other housekeeping, it resets the stack, prints the registers and returns to the monitor. This code works fine. I did confirm that multiple NMI triggers are occurring, so I need to work in debouncing.

I will give the DS1813 a try, but the trigger length is long and not knowing what people will be using this board for (I'm planning on making a run of boards for sale), I'm thinking that I should use an RC circuit with two HC14 gates as in the example I mentioned at the top.

Thanks!
Post Reply