Timer interrupts with 65C22

Building your first 6502-based project? We'll help you get started here.
Post Reply
FlimsyFlipFlop
Posts: 3
Joined: 22 Jun 2025
Location: Belgium

Timer interrupts with 65C22

Post by FlimsyFlipFlop »

Hello everyone

I've built a simple SBC with a 65C02, 65C22, SST39SF020 for ROM, AS6C22256, and some NAND gates for address decoding.
I've been trying to output a sine lookup table on port A of the 65C22.
The weirdest thing is that when I reset the computer, some stuff happens on port A, but then at seemingly random intervals after reset, it just stops. I have the PB7 toggle enabled on the 65C22 to make sure the timer is running, which doesn't stop when port A does, so at least the timer is running I think.

What could be wrong here?

Here is the source code:

SIENINDEX = $00
DDRA = $4003
PORTA = $4001
TCNT1L = $4004
TCNT1H = $4005
OCR1L = $4006
OCR1H = $4007
ACR = $400B
IER = $400E

.org $c000
RESET:
LDX #$FF ;stack init
TXS ;yoooo
LDX #$00
STX SIENINDEX
LDA #$FF ;poort A on
STA DDRA ;yoooo
LDA #$C0 ;Timer1 continues interrupt with PB7 toggle
STA ACR ;yooooo
LDA #$C0 ;Timer1 and interrupts in general
STA IER ;yoooooo
LDA #$FF ;decimal 255 to count down from
STA OCR1L ;yooooo
LDA #$00 ;decimal 0 high order byte
STA OCR1H ;yooooo
LDA #$FF ;decimal 255 to count from
STA TCNT1L ;yoooo
LDA #$00 ;decimal 0 low order byte
STA TCNT1H ;yoooo
CLI ;enable them interrupts

MAIN:
NOP
JMP MAIN

INT_VECT:
LDA TCNT1L ; to clear interrupt flag in 65C22
LDX SIENINDEX
LDA SIENTABEL, X
STA PORTA
INX
STX SIENINDEX
RTI

SIENTABEL:
.byte 128, 131, 134, 137, 140, 143, 146, 149
.byte 152, 155, 158, 162, 165, 167, 170, 173
.byte 176, 179, 182, 185, 188, 190, 193, 196
.byte 198, 201, 203, 206, 208, 211, 213, 215
.byte 218, 220, 222, 224, 226, 228, 230, 232
.byte 234, 235, 237, 238, 240, 241, 243, 244
.byte 245, 246, 248, 249, 250, 250, 251, 252
.byte 253, 253, 254, 254, 254, 255, 255, 255
.byte 255, 255, 255, 255, 254, 254, 254, 253
.byte 253, 252, 251, 250, 250, 249, 248, 246
.byte 245, 244, 243, 241, 240, 238, 237, 235
.byte 234, 232, 230, 228, 226, 224, 222, 220
.byte 218, 215, 213, 211, 208, 206, 203, 201
.byte 198, 196, 193, 190, 188, 185, 182, 179
.byte 176, 173, 170, 167, 165, 162, 158, 155
.byte 152, 149, 146, 143, 140, 137, 134, 131
.byte 128, 124, 121, 118, 115, 112, 109, 106
.byte 103, 100, 97, 93, 90, 88, 85, 82
.byte 79, 76, 73, 70, 67, 65, 62, 59
.byte 57, 54, 52, 49, 47, 44, 42, 40
.byte 37, 35, 33, 31, 29, 27, 25, 23
.byte 21, 20, 18, 17, 15, 14, 12, 11
.byte 10, 9, 7, 6, 5, 5, 4, 3
.byte 2, 2, 1, 1, 1, 0, 0, 0
.byte 0, 0, 0, 0, 1, 1, 1, 2
.byte 2, 3, 4, 5, 5, 6, 7, 9
.byte 10, 11, 12, 14, 15, 17, 18, 20
.byte 21, 23, 25, 27, 29, 31, 33, 35
.byte 37, 40, 42, 44, 47, 49, 52, 54
.byte 57, 59, 62, 65, 67, 70, 73, 76
.byte 79, 82, 85, 88, 90, 93, 97, 100
.byte 103, 106, 109, 112, 115, 118, 121, 124

.org $FFFC
.word RESET
.org $FFFE
.word INT_VECT
Counting every cycle
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Timer interrupts with 65C22

Post by BigEd »

Welcome!

One thing I'd try to do is simplify. What if you drop the interrupts idea, and just put your SIN loop into MAIN? The output won't be at the audio frequency you wanted but maybe you will see if the problem comes from interrupts.

Otherwise, some kind of marginality in your logic design, or perhaps electrical noise in your construction, would be worth looking into.

A photo of your build would be good. Do you at least have solid power distribution and bypass capacitors near each chip?
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Timer interrupts with 65C22

Post by BigDumbDinosaur »

Welcome to 6502-land.

When you post code, you can surround it with CODE tags so it is rendered in monospaced text for ease of reading, e.g.:

Code: Select all

SIENINDEX = $00
DDRA      = $4003
PORTA     = $4001
TCNT1L    = $4004
TCNT1H    = $4005
OCR1L     = $4006
OCR1H     = $4007
ACR       = $400B
IER       = $400E

.org $c000
RESET:
	LDX #$FF                   ;stack init
	TXS                        ;yoooo
	LDX #$00
	STX SIENINDEX
	LDA #$FF                   ;poort A on
	STA DDRA                   ;yoooo
	LDA #$C0                   ;Timer1 continues interrupt with PB7 toggle
	STA ACR                    ;yooooo
	LDA #$C0                   ;Timer1 and interrupts in general
	STA IER                    ;yoooooo
	LDA #$FF                   ;decimal 255 to count down from
	STA OCR1L                  ;yooooo
	LDA #$00                   ;decimal 0 high order byte
	STA OCR1H                  ;yooooo
	LDA #$FF                   ;decimal 255 to count from
	STA TCNT1L                 ;yoooo
	LDA #$00                   ;decimal 0 low order byte
	STA TCNT1H                 ;yoooo
	CLI                        ;enable them interrupts

MAIN:
	NOP
	JMP MAIN

INT_VECT:
	LDA TCNT1L                 ; to clear interrupt flag in 65C22
	LDX SIENINDEX
	LDA SIENTABEL, X
	STA PORTA
	INX
	STX SIENINDEX
	RTI

SIENTABEL:
	.byte 128, 131, 134, 137, 140, 143, 146, 149
	.byte 152, 155, 158, 162, 165, 167, 170, 173
	.byte 176, 179, 182, 185, 188, 190, 193, 196
	.byte 198, 201, 203, 206, 208, 211, 213, 215
	.byte 218, 220, 222, 224, 226, 228, 230, 232
	.byte 234, 235, 237, 238, 240, 241, 243, 244
	.byte 245, 246, 248, 249, 250, 250, 251, 252
	.byte 253, 253, 254, 254, 254, 255, 255, 255
	.byte 255, 255, 255, 255, 254, 254, 254, 253
	.byte 253, 252, 251, 250, 250, 249, 248, 246
	.byte 245, 244, 243, 241, 240, 238, 237, 235
	.byte 234, 232, 230, 228, 226, 224, 222, 220
	.byte 218, 215, 213, 211, 208, 206, 203, 201
	.byte 198, 196, 193, 190, 188, 185, 182, 179
	.byte 176, 173, 170, 167, 165, 162, 158, 155
	.byte 152, 149, 146, 143, 140, 137, 134, 131
	.byte 128, 124, 121, 118, 115, 112, 109, 106
	.byte 103, 100, 97, 93, 90, 88, 85, 82
	.byte 79, 76, 73, 70, 67, 65, 62, 59
	.byte 57, 54, 52, 49, 47, 44, 42, 40
	.byte 37, 35, 33, 31, 29, 27, 25, 23
	.byte 21, 20, 18, 17, 15, 14, 12, 11
	.byte 10, 9, 7, 6, 5, 5, 4, 3
	.byte 2, 2, 1, 1, 1, 0, 0, 0
	.byte 0, 0, 0, 0, 1, 1, 1, 2
	.byte 2, 3, 4, 5, 5, 6, 7, 9
	.byte 10, 11, 12, 14, 15, 17, 18, 20
	.byte 21, 23, 25, 27, 29, 31, 33, 35
	.byte 37, 40, 42, 44, 47, 49, 52, 54
	.byte 57, 59, 62, 65, 67, 70, 73, 76
	.byte 79, 82, 85, 88, 90, 93, 97, 100
	.byte 103, 106, 109, 112, 115, 118, 121, 124

	.org $FFFC
	.word RESET
	.org $FFFE
	.word INT_VECT

You might want to revisit your interrupt handler (ISR).  I would write it thusly:

Code: Select all

IFR       = $400D              ; *** interrupt flag register ***
;
INT_VECT:
         LDA IFR               ; VIA IRQ?
         BPL CRTI              ; no...
;
;			———————————————————————————————————————————————————————————————————
;			If the VIA isn’t interrupting & there is no other IRQ source in the
;			system, you have a spurious IRQ, which suggests a hardware issue.
;			———————————————————————————————————————————————————————————————————
;
         STA IFR               ; clear VIA IRQ
         BIT #%01000000        ; timer IRQ? (CMOS only — use AND with NMOS)
         BEQ CRTI              ; no, also spurious...
;
;			————————————————————————————————————————————————————————————
;			The VIA did interrupt, but not because of timer-A underflow.
;			————————————————————————————————————————————————————————————
;
         LDX SIENINDEX
         LDA SIENTABEL,X
         STA PORTA
         INC SIENINDEX         ; replaces two-instruction sequence
;
CRTI:
         RTI

Note in the above that I did not read timer-A to clear the pending IRQ, as you were doing.  Until you read the VIA’s IFR, you don’t even know if the device is interrupting, let alone what in it is causing it to interrupt.  If bit 7 in the IFR isn’t set, the VIA is not interrupting, which means you have some other device interrupting, or a spurious IRQ problem.

Once it is known the VIA is interrupting, the IFR is written back to immediately clear all pending IRQs, which is a form of defensive programming.  In a wired-OR interrupt circuit, a finite amount of time is needed for the circuit to return to a logic 1 condition after all IRQ sources have been cleared.  Writing back the IFR early in the ISR causes the VIA to immediately release the interrupt circuit, hence giving the circuit more time to become quiescent before processing is returned to the foreground.  If quiescence is not achieved by the time the RTI instruction has been executed, the MPU will think it is still being interrupted.

Following the fetching of the IFR’s content, you can use shifts and/or Boolean operations to determine what in the VIA is interrupting.  Also, note the slight change I made in handling the SIENINDEX variable in the reworked ISR.  Good practice in writing ISRs is to use the minimum number of instructions needed to do the job, unless more instructions ultimately reduces total Ø2 cycles consumed.

In general, it is not wise to make assumptions about IRQ sources, as a spurious IRQ could ultimately lead to deadlock.  Defensive programming limits the chances of anomalous behavior bringing down your system or causing it to produce dubious results.

Also, note Ed’s comments.  You may have external influences injecting noise into the circuit, thus causing your unit to misbehave in subtle ways and possibly leading to spurious interrupts.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
Dr Jefyll
Posts: 3524
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Timer interrupts with 65C22

Post by Dr Jefyll »

Welcome, FlimsyFlipFlop :)

Ed has given you good advice. In particular, I endorse the notion that the problem has to do with the computer, not the program. Your remark that, "at seemingly random intervals after reset, it just stops," tends to support this.

Lack of attention to power distribution and bypass capacitors can very plausibly cause the symptom you describe. Another common problem is the use solderless breadboards, many of which are of low quality and are incapable of maintaining secure connections. (Even a momentary lapse -- as short as a microsecond, say :shock: -- is enough to send your program off into the weeds.)

-- Jeff

ETA: I see BDD posted while I was typing this, and he mentioned spurious interrupts. To be clear, construction issues can cause all sorts of unexpected behavior -- not just spurious interrupts.
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
FlimsyFlipFlop
Posts: 3
Joined: 22 Jun 2025
Location: Belgium

Re: Timer interrupts with 65C22

Post by FlimsyFlipFlop »

Thank you for all the replies.

The construction is all wire wrapped (with no bypass caps), so spurious signals isn't out of the question.
All suggestions are appreciated and will be tried, starting with those darned bypass caps.

I will let you know the results thusly

Much thanks,
FFF
Counting every cycle
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Timer interrupts with 65C22

Post by BigDumbDinosaur »

FlimsyFlipFlop wrote:
The construction is all wire wrapped (with no bypass caps), so spurious signals isn't out of the question.

Bypass capacitors in digital circuits are a lot like money: no such thing as “too much.”  I can’t begin to count how many times someone has asked for help in taming a malfunctioning machine, only to find out said contraption has no bypass caps.  :D  The ones I recommend are 0.01 µF, 50 volt, X7R MLCCs.  Each chip should have one, with the cap’s leads as short and direct as possible to minimize inductance.  Beware of capacitor quality!

It is important to understand that in digital logic, it is output state transition speed that causes a lot of trouble, not the MPU’s clock rate.  74HC logic, the slowest logic usually recommended for 65C02 builds (use of NMOS parts in new designs is discouraged), has a very rapid transition speed, which can generate switching noise with effective frequencies in excess of 100 MHz.  If not correctly bypassed, such noise can sneak into all sorts of places and in the case of a wired-OR interrupt circuit, can result in transients that may be seen by the MPU as interrupts.  You will go around the bend trying to troubleshoot such misbehavior.

Also, as Jeff noted, robust power and ground connections are de rigueur.  Ground bounce, in particular, can result in some really difficult-to-identify hardware problems, and as he points out, not just spurious IRQs.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
FlimsyFlipFlop
Posts: 3
Joined: 22 Jun 2025
Location: Belgium

Re: Timer interrupts with 65C22

Post by FlimsyFlipFlop »

Hello all

I have tried the suggestions, with success.
The timeline is as follows:
- my code completely still, but with bypass caps: no change
- without interrupts, just main doing the outputting: there it is.
- with the defensive programming as demonstrated by BigDumbDinosaur: it works, interrupts and all.

I've attached some photos to show the amount of spaghetti my computer has.
I tried to use a colour scheme when I started (yellow I/O data and control, purple address bus, orange data bus, blue clock, and so on),
but ended up just using whatever spool I had by hand towards wiring up the op-amp, DAC and bypass caps, because by that point
my workspace was thoroughly full of insulation and messed up wires.

The DAC is another can of worms because I've never used the DAC0800 IC before, my op-amp output is nonexistent and the current output of the DAC
looks like the sine's been bit-reversed (I knew having that contraption work from the first time would be too good to be true).

Thank you all very much for your help.
20250623_013542.jpg
20250623_013505.jpg
Regards,
FFF
Counting every cycle
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Timer interrupts with 65C22

Post by GARTHWILSON »

Welcome.  You've brought up a lot of things that are explained in the 6502 primer at http://wilsonminesco.com/6502primer/ and the 6502 interrupts primer at http://wilsonminesco.com/6502interrupts/ .  I think you'll meet with good success if you go through those.
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: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Timer interrupts with 65C22

Post by BigDumbDinosaur »

FlimsyFlipFlop wrote:
I have tried the suggestions, with success.
The timeline is as follows:
- my code completely still, but with bypass caps: no change
- without interrupts, just main doing the outputting: there it is.
- with the defensive programming as demonstrated by BigDumbDinosaur: it works, interrupts and all.

Always satisfying when a stubborn bit of gear is finally made to work.  When convenient, please post a monochrome schematic of your gadget.

Be sure to go over Garth’s primer, as well as his interrupts exposition.  There is a lot of practical information to be had there.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Timer interrupts with 65C22

Post by BigEd »

Well done FFF, and thanks for the pictures.
Post Reply