Ok, so looking through the cc65 folders and Googling, I have it(mostly) figured out to write an IRQ routine for a home brewed board in 'C'. I don't know if i have it 100% correct, but i have it working for my board. As I've seen mentioned here, and elsewhere, it's always best to write your IRQ routine in assembler as "set_irq()" has quite a bit of overhead (ie. saves zero page), but if you want to use 'C' then...
Using my board's locations as an example, on boot, the bios $FFFE(IRQ) redirects the IRQ to $00FD and inits $00FD with an RTI.
1) In your .cfg, "FEATURES" section, make sure you have:
Code:
CONDES: segment = STARTUP,
type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
import = __CALLIRQ__;
Placing it to the desired(code type) segment. I just pointed it to my STARTUP segment
2) The "set_irq()" method requires defining "initirq", "doneirq" methods. So i made an IRQinit.s:
Code:
; IRQinit.s
;
; IRQ init
.export initirq, doneirq ; needed for set_irq call
.import callirq
; IRQ location
IRQVec := $00FD
; ------------------------------------------------------------------------
.segment "ONCE" ; ("ONCE" is after my "STARTUP" segment and before my "CODE" segment)
initirq:
sei ; disable IRQ (take care of SEI/CLI in the 'C' code, but keep this here to be safe)
lda #$4C ; JMP
sta IRQVec
lda #<IRQStub ; get the stub addr
ldx #>IRQStub
sta IRQVec+1 ; save the stub addr in the redirected IRQ adr
stx IRQVec+2
; cli ; enable IRQ
rts
; ------------------------------------------------------------------------
.segment "CODE"
doneirq:
sei ; disable IRQ
lda #$40 ; RTI
sta IRQVec
; cli ; enable IRQ <-- may need to do this if the original IRQ is to get triggered
rts
; ------------------------------------------------------------------------
IRQStub:
cld ; Just to be sure
jsr callirq ; Call the functions
rts
IRQStub will be called by "set_irq()", so in "initirq", I set a JMP at $00FD, then the adr to jump to(IRQStub) on an IRQ. Make sure you end IRQStub with RTS, the caller of IRQStub will end it with RTI. My board doesn't use any other IRQs, so on "doneirq", I just put an RTI back at $00FD. Depending on the complexity of your board, you may need to save/restore the adr at your redirected IRQ location, ie. looking at the C64 irq.s(cc65\libsrc\c64), it saves it's current IRQ adr to $0000, on "doneirq", it will restore it.
3) Now the 'C' code:
Code:
unsigned char IRQ(void);
#define STACK_SIZE 64 // vary this depending how busy your IRQ method is
unsigned char TempStack[STACK_SIZE];
int main ()
{
SEI(); // disable interrupts
set_irq(&IRQ, TempStack, STACK_SIZE); // note: needs "initirq", "doneirq"
...other inits
CLI(); // enable interrupts
...
}
unsigned char IRQ()
{
...your IRQ code
return IRQ_NOT_HANDLED;
}
I tested it enabling the counter interrupt on a 6522, a scope on the IRQ line confirmed it was working.