6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 4:52 am

All times are UTC




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Feb 18, 2014 1:22 am 
Offline
User avatar

Joined: Wed Sep 03, 2003 6:53 pm
Posts: 153
Location: Long Island, NY
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:
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!

_________________
Rich Cini
Build Master and maintainer of the Altair32 Emulation project
http://cini.classiccmp.org
http://altair32.classiccmp.org


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 2:09 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
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?


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 2:29 am 
Offline
User avatar

Joined: Wed Sep 03, 2003 6:53 pm
Posts: 153
Location: Long Island, NY
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: [url]]http://robotgestation.com/SensorForce.html[/url]. I'll run the math tomorrow. Not sure I have room on the board for another DIP, but we'll see.

Thanks again!

_________________
Rich Cini
Build Master and maintainer of the Altair32 Emulation project
http://cini.classiccmp.org
http://altair32.classiccmp.org


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 3:16 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
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?


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 5:19 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8505
Location: Midwestern USA
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.

Attachment:
File comment: NMI Debouncer
nmi_debounce.gif
nmi_debounce.gif [ 22.84 KiB | Viewed 2090 times ]

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 1:06 pm 
Offline
User avatar

Joined: Wed Sep 03, 2003 6:53 pm
Posts: 153
Location: Long Island, NY
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.

Attachment:
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!

_________________
Rich Cini
Build Master and maintainer of the Altair32 Emulation project
http://cini.classiccmp.org
http://altair32.classiccmp.org


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 4:17 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 4:28 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
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).


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 5:33 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
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?


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 5:59 pm 
Offline
User avatar

Joined: Wed Sep 03, 2003 6:53 pm
Posts: 153
Location: Long Island, NY
Thanks again. Lots to work on tonight!

_________________
Rich Cini
Build Master and maintainer of the Altair32 Emulation project
http://cini.classiccmp.org
http://altair32.classiccmp.org


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 6:19 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
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


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 6:38 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
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?


Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 6:42 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8505
Location: Midwestern USA
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.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Last edited by BigDumbDinosaur on Tue Feb 18, 2014 6:48 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Tue Feb 18, 2014 6:48 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8505
Location: Midwestern USA
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!


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 2:24 am 
Offline
User avatar

Joined: Wed Sep 03, 2003 6:53 pm
Posts: 153
Location: Long Island, NY
Ok, following up on this, I made changes to the code based on today's discussion but still kept the simple switch input
Code:
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!

_________________
Rich Cini
Build Master and maintainer of the Altair32 Emulation project
http://cini.classiccmp.org
http://altair32.classiccmp.org


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: