6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Sep 22, 2024 11:30 pm

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 
Author Message
PostPosted: Sun Jul 05, 2020 9:05 pm 
Offline

Joined: Fri Jul 03, 2020 2:44 pm
Posts: 8
Hi,

Back in 1988/89 I made my own 65C02 board for my own learning/experiments. Fast forward 30 years, i just recently came across cc65. i dusted off my micro board and low and behold, it still works :) It has 32K of ram, so it's fine for writing C code. I first made a C# PC app to talk to the board. For cc65 to work, i modded a cfg file to match my board, and changed crt0.s to jump to the monitor after exiting main(). It's working fine so far ...cool.

For this board, on boot, the monitor(eprom) sets location $FFFA(NMI) to $00FA, and for $FFFE(IRQ) to $00FD. During boot, the monitor inits these locations($00FA and $00FD) with $40(RTI). Sooooo, back in the day when i'd write my asm apps (i used TASM back then), for IRQs, i'd put a $4C (abs JMP) at $00FD followed by the address of my IRQ routine at $00FE/$00FF. My question after all this(whew!), is how do i tell cc65 where to put the IRQ address of my C method? My guess is right now if i write:

#define STACK_SIZE 256
unsigned char TempStack[STACK_SIZE];

int main(void)
{
SEI(); // disable IRQ
set_irq(&IRQ_Routine, TempStack, STACK_SIZE);
CLI(); // enable IRQ

...
return EXIT_SUCCESS;
}

// IRQ handler
unsigned char IRQ_Routine(void)
{
...
return (IRQ_NOT_HANDLED);
}

...then cc65 is going to try to put the address of "IRQ_Routine" at $FFFE? ...which i don't want. I've Googled and come up with bits and pieces, but still unsure what to do.

For starter is guess i have to add the following to my cfg?
CONDES: type = interruptor,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__,
segment = RODATA,
import = __CALLIRQ__;

..then add some kind of stub code in crt0? I'd like this to work for IRQ routines written in C and asm.

Any help, with examples, appreciated. Thanks!


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 13, 2020 9:44 pm 
Offline

Joined: Fri Jul 03, 2020 2:44 pm
Posts: 8
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. :)


Top
 Profile  
Reply with quote  
PostPosted: Thu Jul 30, 2020 3:30 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
(Oh, must have missed this... can't help, but welcome anyway!)


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

All times are UTC


Who is online

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