6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Sep 21, 2024 11:37 pm

All times are UTC




Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Sat Aug 22, 2015 10:20 pm 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
In an other post in the h/w section, I've documented putting together a keypad which utilises an AVR to talk to a 65C22 VIA.

I'm now working on the 6502 code to fetch the ascii keycode from the keypad AVR and send it to a keyboard buffer. The buffer is located (for the moment) at $200 and the counter which keeps an eye on how many characters are within it is at $20. When the value in the buffer counter location ($20) is 0 then there are no characters in the buffer. If a routine retrieves a character from the buffer then the contents of $20 is decremented. An incoming character will cause the counter value in $20 to increment and the character is then inserted at $200 + contents of $20.
If the counter reaches $FF then any further characters are ignored.

Does someone mind reviewing my code to see if 1) I've implemented interrupts correctly, 2) implemented the VIA correctly, 3) the method that I've used for the keyboard buffer is efficient?

My code is listed below:
Code:
.setup
# Setup
VIA_A = $400F
VIA_A_IFR = VIA_A + $0D
VIA_A_IER = VIA_A + $0E
VIA_A_DDRA = VIA_A + $03
VIA_A_IRA = VIA_A + $01
KYBD_Buffer = $200
KYBD_Counter = $20

# Set port A as input
LDA #0
STA VIA_A_DDRA
# Enable interrupt for CB1
LDA #%10010000
STA VIA_A_IER

#### other setup code ####





.ISRhandler
# ISRhandler is store in the interrupt vector ($FFFE)
# ISR
# Usual stuff
PHA
PHX
# (SEI $ PHP taken care of by CPU already)

# Check to see if VIA generated IRQ
LDA #%10000000
BIT VIA_A_IFR
BEQ exitISR

# Check to see if CB1 generated IRQ
LDA #%00010000
BIT VIA_A_IFR
BEQ exitISR1

# So, we have a winner!
# Now clear the VIA IRQ. We do this now so the IRQB level can
# rise before the end of the ISR
JSR clear_VIA_IRQ

# Check that the keyboard buffer can accept the new key value.
# If it’s full then we ignore the new key press
LDA KYBD_Counter
CMP #$FF
BEQ exitISR

# Increase the keyboard counter and store the new key value
# in the buffer
INC KYBD_Counter
LDX KYBD_Counter
LDA VIA_A_IRA
STA KYBD_Buffer,X

.exitISR
PLX
PLA
# (CLI $ PLP taken care of by CPU when RTI is executed)
RTI

# Clear the IRQ status of the VIA
.exitISR1
JSR clear_VIA_IRQ
JMP exitISR

# clear the IRQ status of CB1
.clear_VIA_IRQ
LDA VIA_A_IFR
AND #%01101111
STA VIA_A_IFR
RTS


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 22, 2015 11:37 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8510
Location: Southern California
Several things:
  • Did you mean to put VIA_A starting at $400F? If it goes from $400F to $401E, that makes more more-complex address decoding than $4000-400F or even $4010-401F.

  • In your setup, you also need to choose the CB1 active edge in the PCR.

  • When you check to see if the VIA generated the interrupt, you can leave out the LDA #%10000000 and just do BIT VIA_A_IFR, BPL/BMI. BIT transfers the high bit to the N flag to branch on, regardless of what's in A.

  • You can replace the JSR clear_VIA_IRQ with LDA VIA_A_PB and save a lot of cycles and some instructions. The table in the data sheet shows what actions clear the various bits. This one clears the CB1 interrupt bit. In fact, you could put the 8 bits of key input on port B (PB) instead of A (which you have as IRA, "input register A"), and do the job even more efficiently.

  • If KYBD_Counter's only allowable negative value is $FF, you can skip the CMP #$FF and just do a BMI.


Make sure you've gone through the interrupts primer at http://wilsonminesco.com/6502interrupts/ . You could do the keyboard ring buffer much like what is shown for the RS-232 ring buffer at http://wilsonminesco.com/6502interrupts/#3.1 , although you could make the buffer smaller since you won't type ahead more than one or two dozen bytes.

_________________
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: Sun Aug 23, 2015 4:57 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8390
Location: Midwestern USA
GARTHWILSON wrote:
  • You can replace the JSR clear_VIA_IRQ with LDA VIA_A_PB and save a lot of cycles and some instructions.

Expanding on Garth's notes, as a rule it is not good practice to use subroutines in interrupt service handlers. Each JSR -- RTS combination will cost you 12 clock cycles. If the ISR is executed many times per second those 12 cycles quickly start to eat into performance. ISRs should be linear code for best performance.

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


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 23, 2015 6:13 am 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 674
In addition to what BDD said, subroutines inside interrupts also add additional stack pressure to your code. Since interrupts might happen at inopportune times in your program when the stack is deep, or if you're using the low end of page 1 for temporary data, that can cause unrepeatable errors in corrupting the stack area if it's too big.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 23, 2015 6:30 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8510
Location: Southern California
I wouldn't worry about stack depth. In tests where I was making heavy use of stacks, the maximum I managed to fill up was less than 20% of page 1. However, if you have recursive routines, or a multitasking OS, or lots of local variables and environments, you may have to be more careful; but one extra return address isn't going to matter much.

_________________
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: Sun Aug 23, 2015 8:52 am 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
Thanks for the advice, guys. Here's my latest code. Garth: I'm not sure if I'm clearing the CB1 IRQ properly as I may have misunderstood what you said about that (I did read your guide and the 65C22 manual though). I have removed the JSR/RTS, etc. though. With regard to the VIA address - I just threw in an arbitrary address which I intended to correct later.

Code:
.setup
# Setup
VIA = $4010
VIA_IRB = VIA + $01
VIA_DDRB = VIA + $03
VIA_PCR = VIA + $0C
VIA_IFR = VIA + $0D
VIA_IER = VIA + $0E
KYBD_Buffer = $200
KYBD_Counter = $20

# Set port B as input
LDA #0
STA VIA_DDRB
# Set handshaking control by writing to PCR. Set CB1
# to trigger on negative (high-to-low) edge transition
LDA VIA_PCR
ORA #%11101111
STA VIA_PCR

# Enable interrupt for CB1
LDA #%10010000
STA VIA_IER

#### other setup code ####





.ISRhandler
# ISRhandler is store in the interrupt vector ($FFFE)
# ISR
# Usual stuff
PHA
PHX
# (SEI $ PHP taken care of by CPU already)

# Check to see if VIA generated IRQ
BIT VIA_IFR
BPL exitISR

# Check to see if CB1 generated IRQ
LDA #%00010000
BIT VIA_IFR
BEQ exitISR1

# So, we have a winner!
# Now clear the VIA IRQ. We do this now so the IRQB level can
# rise before the end of the ISR
# clear the IRQ status of CB1
LDA VIA_IFR
AND #%01101111
STA VIA_IFR

# Check that the keyboard buffer can accept the new key value.
# If it’s full then we ignore the new key press
# Max is 127 as bit 7 is reserved for checking if the buffer is full
LDA KYBD_Counter
BMI exitISR

# Increase the keyboard counter and store the new key value
# in the buffer
INC KYBD_Counter
LDX KYBD_Counter
LDA VIA_IRB
STA KYBD_Buffer,X

.exitISR
PLX
PLA
# (CLI & PLP taken care of by CPU when RTI is executed)
RTI

# Clear the IRQ status of the VIA
.exitISR1
LDA VIA_IFR
AND #%01101111
STA VIA_IFR
JMP exitISR


RTS


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 23, 2015 12:39 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1382
A couple other bits.... for entry into your service routine, do a LDA VIA_IFR first, then do the BPL exitISR. This way you have the ISR contents in the A reg and can just do an AND #%00010000 to test for CB1 and branch if equal to zero. This saves an instruction and 3 bytes of space. Also, as you're using a 65C02, use a BRA exitISR instruction instead of the JMP exitISR at the bottom to exit the routine... saves another byte.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 23, 2015 8:57 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8390
Location: Midwestern USA
GARTHWILSON wrote:
I wouldn't worry about stack depth. In tests where I was making heavy use of stacks, the maximum I managed to fill up was less than 20% of page 1. However, if you have recursive routines, or a multitasking OS, or lots of local variables and environments, you may have to be more careful; but one extra return address isn't going to matter much.

A worst-case scenario might be one in which all registers are preserved prior to calling the subroutine. Assuming the 65C02, that would put six bytes on the stack each time, four for the registers and two for the return address. If the stack pointer starts out at $FF, 42 such subroutine calls would be feasible.

It's even less a worry with the 65C816 when in native mode, even though a total preservation of MPU state prior to a subroutine call would push 13 bytes in total, 10 bytes for the registers themselves and 3 bytes for the return address, assuming the subroutine was called with the JSL instruction. If the top-of-stack pointer were at $CFFF, for example, and it was decreed that the bottom of the stack was at $C000, a total of 315 such subroutine calls could be made without stack underflow.

A major concern in any interrupt service routine is performance, especially during interaction with I/O hardware. I cover some of this in my 65C816 interrupts article in the software engineering section.

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


Last edited by BigDumbDinosaur on Sun Aug 23, 2015 9:01 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 23, 2015 9:00 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8390
Location: Midwestern USA
floobydust wrote:
A couple other bits.... for entry into your service routine, do a LDA VIA_IFR first, then do the BPL exitISR. This way you have the ISR contents in the A reg and can just do an AND #%00010000 to test for CB1...Also, as you're using a 65C02, use a BRA exitISR instruction instead of the JMP exitISR at the bottom to exit the routine... saves another byte.

In fact, the 65C02 supports a BIT immediate test, which means that AND #%00010000 can be BIT #%00010000, preserving the content of .A for additional tests if needed.

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


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 24, 2015 4:44 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1382
Hi BDD,

The 65C02 immediate BIT test... yup, I use that in my BIOS for testing the VIA timer interrupts, but he's planning to use the old NMOS 6502. Nowadays, I can't really see any reason to use the original 6502, but to each their own. I've focused solely on the 65C02 since the late 80's and leverage many of the new instructions and addressing modes as required. Needless to say, nothing I've written since 1988 will work on the old 6502.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 24, 2015 7:32 pm 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
floobydust wrote:
Hi BDD,

The 65C02 immediate BIT test... yup, I use that in my BIOS for testing the VIA timer interrupts, but he's planning to use the old NMOS 6502. Nowadays, I can't really see any reason to use the original 6502, but to each their own. I've focused solely on the 65C02 since the late 80's and leverage many of the new instructions and addressing modes as required. Needless to say, nothing I've written since 1988 will work on the old 6502.


I'm using the WDC 65C02S, not the old NMOS one. Otherwise getting to >10MHz is going to be challenging! :mrgreen:

Thanks to both of you for the info with regard to BIT and also reminding me about BRA (I always forget that one - too used to not having it from when I initially played around with 6502 assembly in the late 80's).
I'm looking forward to having a go at the 65C816 once I complete my current 65C02 project (mark 2) - never used that CPU before so should be interesting. The trouble is, is that I keep becoming distracted by new stuff (most of everything falls into this category!) such as my h/w bus monitor series, keyboards, learning about timers, etc., etc.. :D


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 24, 2015 7:57 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8510
Location: Southern California
Quote:
I'm using the WDC 65C02S, not the old NMOS one.

In that case you can also replace LDA#0, STA VIA_DDRB with STZ VIA_DDRB too. See the differences between NMOS and CMOS 6502 at http://wilsonminesco.com/NMOS-CMOSdif/ .

_________________
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 Aug 25, 2015 5:43 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8390
Location: Midwestern USA
banedon wrote:
I'm looking forward to having a go at the 65C816 once I complete my current 65C02 project (mark 2) - never used that CPU before so should be interesting.

Stepping up to the 65C816 is trivial. Just about everything you know about the 65C02 circuitry is transferable, with a few minor hardware differences that shouldn't pose a problem for you. The 65C816 assembly language is a superset of the 65C02, so the learning curve is not at all steep. If an old dinosaur like me can do it, so can you. :lol:

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


Top
 Profile  
Reply with quote  
PostPosted: Tue Aug 25, 2015 7:36 am 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
GARTHWILSON wrote:
Quote:
I'm using the WDC 65C02S, not the old NMOS one.

In that case you can also replace LDA#0, STA VIA_DDRB with STZ VIA_DDRB too. See the differences between NMOS and CMOS 6502 at http://wilsonminesco.com/NMOS-CMOSdif/ .


Again, that's another one I keep forgetting about lol. Thanks. (I'll have another look at that link).


BigDumbDinosaur wrote:
banedon wrote:
I'm looking forward to having a go at the 65C816 once I complete my current 65C02 project (mark 2) - never used that CPU before so should be interesting.

Stepping up to the 65C816 is trivial. Just about everything you know about the 65C02 circuitry is transferable, with a few minor hardware differences that shouldn't pose a problem for you. The 65C816 assembly language is a superset of the 65C02, so the learning curve is not at all steep. If an old dinosaur like me can do it, so can you. :lol:


I already have the CPUs, so it's just a matter of getting the time. Always the case!


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 13 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: