Jump to content
  • 0

CursorKeys
 Share

Question

Posted (edited)

Hi!
 

I have been playing with interupts a little, in kickc, and it all works nice.  But now I tried to incooperate it in a game I hit a snag.

It seems when I setup an interupt, for scanline, my keyboard functions stop working.

I am probably doing something really basic wrong.
The weird thing is also, when I restore the interupt to it's previous state, the keyboard is still not working.

ps. the keyboard routing I am using is kickc "kbhit", which seems to call kernal routine "GETIN" at $FFE4, I also tried directly calling kernal routines, which works fine, but not when my interupt is enabled.
ps2. I enable the interupt, something like this.

 

Quote

__interrupt void irq_line() {

  //do stuff

}

void main() {
   //kbhit function works fine here

  //store old interupt values
    old_irq = *KERNEL_IRQ;
    old_ien = *VERA_IEN;

  SEI();
  *KERNEL_IRQ = &irq_line;
  *VERA_IEN = VERA_LINE;
  *VERA_IRQLINE_L = 4;
  CLI();


   //kbhit function stopped working


  //try to restore the interupt
   SEI();
  *KERNEL_IRQ = old_irq;
  *VERA_IEN = old_ien;
  CLI();

  //kbhit function is not restored. 

}

 

What I am asking is.

How does the keyboard normally work?

1- Is there an interupt when a key is pressed?  Why does that interup stop working when I set my scanline interupt. How are they related? Do I need to do some extra "magic" at the end of my scanline interupt, to keep the key handling interupt working?

2- Can you somehow check the keyboard in a loop, without interupt, by polling a memory address or maybe a vram address, and if so which one?

 

Also anyone could point me to the correct sections in the documentation, I will be thankfull!

The most accurate I have found an answer is the section in the manual called "'Custom keyboard scan code handler".  But I somehow  don't get the full picture.

Thanks
CC

 

Edited by CursorKeys
Link to comment
Share on other sites

Recommended Posts

  • 0

You need to enable the VSYNC IRQ, too. Enabling only the LINE IRQ messes up the kernal, which is expecting that VSYNC 60 times a second to keep it regular. It should only be disabled for short periods where you need to maximize CPU usage on some task, and don't care about I/O until it's done.

  • Like 1
Link to comment
Share on other sites

  • 0
1 hour ago, CursorKeys said:

How does the keyboard normally work?

1- Is there an interupt when a key is pressed?

A number of system functions (keyboard, mouse, and controller scanning, updating the jiffy clock, and blinking the cursor) are performed by the kernal IRQ handler every time the VSYNC interrupt occurs. I believe the best way to restore these functions would be to enable both VSYNC and LINE interrupts, and to check which interrupt occurred in your code. If it was a VSYNC interrupt, jump to the original IRQ handler. If not, execute the line interrupt code.

1 hour ago, CursorKeys said:

Also anyone could point me to the correct sections in the documentation, I will be thankfull!

Currently, most of the things that the X16 inherited from the C64 (BASIC commands, kernal routines, vectors) are completely undocumented.

  • Like 1
Link to comment
Share on other sites

  • 0
20 minutes ago, Elektron72 said:

I believe the best way to restore these functions would be to enable both VSYNC and LINE interrupts,

......and to check which interrupt occurred in your code. If it was a VSYNC interrupt, jump to the original IRQ handler. If not, execute the line interrupt code.

 


Thanks for pointing me in the right direction. But how do you check the cause of the interupt?  Is it in the acumulator, or how else is it passed to the interupt routine?

(I haven't done interupts in much detail before)

Link to comment
Share on other sites

  • 0
Posted (edited)

It took me a bit of time to find a free timeslot to look at, but I managed to "sort of" make it work, Thanks to SlithyMatt excelent videos, and Elektron72

 

Below is my code.

So what works is that I have two interupts, VSYNC and LINE.  Line I like to have so I can figure out where on the screen I am, and when I want to scroll. VSYNC, I like to have for timing.

The code below does 3 things. 

-Update left top character 1, each time you press space.

-Updater left to character 2, each time the main loop detects the line interrupt is executed

-Updater left to character 3, each time the main loop loops

 

It works. Sort of.

It only works, if I make my variables "volatile" inside the main loop.

This to me indicates that there is something ongoing with the registers each time the interrupt runs, that breaks the code in the main loop.   I have tried with only VSYNC interrupt and there is no issue then.

From SlithyMatts code examples, I took as and example the code

{

        ply
        plx
        pla
        rti

}

To end the line interrupt nicely.  Some how it not working.

Any ideas?

 

Quote

// Example program for the Commander X16
// Displays raster bars in the border

#pragma target(cx16)
#include <cx16.h>
#include <6502.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <veralib.h>


volatile char vbcounter=0;
volatile void *default_irq_vector;

__interrupt void myInterupt() {

  unsigned char intbits = *VERA_ISR;

  if( intbits & VERA_LINE ) {

    vbcounter++;

    *VERA_ISR = VERA_LINE | VERA_VSYNC;
    *VERA_IEN = VERA_LINE | VERA_VSYNC;
    asm {

        ply
        plx
        pla
        rti
    }

  }

  if( intbits & VERA_VSYNC ) {
    *VERA_ISR = VERA_LINE | VERA_VSYNC;
    *VERA_IEN = VERA_LINE | VERA_VSYNC;

    asm {

        jmp (default_irq_vector)
    }
  }

  *VERA_IEN = VERA_LINE | VERA_VSYNC;
  *VERA_IRQLINE_L = 100;

}


void initMain() {

  volatile unsigned char ll = 0, mm=0;

  SEI();
  default_irq_vector = *KERNEL_IRQ;
  *KERNEL_IRQ = &myInterupt;
  *VERA_IEN = VERA_LINE | VERA_VSYNC;
  *VERA_IRQLINE_L = 4;
  CLI();

  vpoke( 0,0, 0 ); //set an "@" left top screen
  vpoke( 0,1, 0x12 ); //set an "@" left top screen

  char c=0;
  while(1==1) {

    unsigned char c = kbhit();
    if( c== 32 ) {

      vpoke( 0,0, ++mm ); //set an "@" left top screen
      vpoke( 0,0+1, 0x12 ); //set an "@" left top screen

    }

    vpoke( 0,2, vbcounter ); //set an "@" left top screen
    vpoke( 0,3, 0x12 ); //set an "@" left top screen

    vpoke( 0,4, ll++ ); //set an "@" left top screen
    vpoke( 0,5, 0x12 ); //set an "@" left top screen

  }


}

 


void main() {

    initMain();

}

 

And the interupt code in assembly.

 

Quote

    lda VERA_ISR
    sta.z intbits

//line checking and handling

    lda #VERA_LINE
    and.z intbits
    cmp #0
    beq __b1
    inc.z vbcounter
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    ply
    plx
    pla
    rti
  __b1:

//vsynch checking and handling

    lda #VERA_VSYNC
    and.z intbits
    cmp #0
    beq __b2
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    jmp (default_irq_vector)

//other (is there other to deal with?)

  __b2:
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_IEN
    lda #$64
    sta VERA_IRQLINE_L
    jmp $e049
 

 

Edited by CursorKeys
Link to comment
Share on other sites

  • 0
Posted (edited)

Thanks. I think it was auto generated code.

So I added the jump, and it looks like this on the generated assembly now.

But no change. It works fine, when I have the variables as volatile (store them in memory instead of loaded in registers), but when I remove the volatile keyword, it stops  working.  
Or a more exact description is, main loop indicator and the keyboard loop indicator on the screen stop cycling, but the behavior is erratic, when I print a number on the screen in the main loop, suddenly there is no issues in the main loop anymore.

 

Quote

.label intbits = 7

    lda VERA_ISR
    sta.z intbits

//line

    lda #VERA_LINE
    and.z intbits
    cmp #0
    beq __b1
    inc.z vbcounter
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    ply
    plx
    pla
    rti

//vsync


  __b1:
    lda #VERA_VSYNC
    and.z intbits
    cmp #0
    beq __b2
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    jmp (default_irq_vector)

//other

  __b2:
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_IEN
    lda #$64
    sta VERA_IRQLINE_L
    jmp (default_irq_vector)

 

 

Edited by CursorKeys
Link to comment
Share on other sites

  • 0
7 minutes ago, CursorKeys said:

main loop indicator and the keyboard loop indicator on the screen stop cycling

Those are both indications that the default IRQ handler isn't running. Seems like it would be easier to have your whole interrupt handling code in assembly, then you could have C functions that you jump to for certain cases.

Link to comment
Share on other sites

  • 0
20 minutes ago, SlithyMatt said:

Those are both indications that the default IRQ handler isn't running. Seems like it would be easier to have your whole interrupt handling code in assembly, then you could have C functions that you jump to for certain cases.

Maybe, but how would it explain, that the key-counter works fine on first key-press, and then stops working.  And works fine when it is ensured that the keycounter is in a volatile variable?

Link to comment
Share on other sites

  • 0

No idea without seeing the difference in assembly output. It seems like cc65 does some weird things while it tries to work around the limitations of the 6502. Again, it's why I would not use C for low-level stuff like interrupt handling. Keep it confined to algorithms that can be called from assembly when the state is appropriate. If you have to twist yourself up in knots to make C generate the assembly you're looking for, then you should just write the assembly and be done with it.

Link to comment
Share on other sites

  • 0

I hear your argument for 100% assembly 🙂  and maybe I will eventually be convinced.

But for me now it is either "use c and make cool stuff on the x16", or "do assembly and need plenty of time, and end up not coding for the x16"

Anyway, we'll see where it ends... To start out it is KickC.  I did not really get a liking for cc65 too much.  Maybe a question of taste, but if a simple thing like "Don't start the program in lowercase", had to be dealt with as a workaround, I felt that was an bad indicator for me.

For the complete assembly code, I'll be thank full to know what you think, if you want to comment, so I publish it below.   Maybe I learn something 🙂

Anyway, I added many comments, but most code is generated by kickc.  My after kickc did it's "thing" comments start with "/////".

 

Quote

////////////// AUTO CODE

 

// Example program for the Comander X16
// Displays raster bars in the border
.cpu _65c02
  // Commodore 64 PRG executable file
.file [name="vlinterupt.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(__start)
  .const VERA_ADDRSEL = 1
  .const VERA_LINE = 2
  .const VERA_VSYNC = 1
  // $9F20 VRAM Address (7:0)
  .label VERA_ADDRX_L = $9f20
  // $9F21 VRAM Address (15:8)
  .label VERA_ADDRX_M = $9f21
  // $9F22 VRAM Address (7:0)
  // Bit 4-7: Address Increment  The following is the amount incremented per value value:increment
  //                             0:0, 1:1, 2:2, 3:4, 4:8, 5:16, 6:32, 7:64, 8:128, 9:256, 10:512, 11:40, 12:80, 13:160, 14:320, 15:640
  // Bit 3: DECR Setting the DECR bit, will decrement instead of increment by the value set by the 'Address Increment' field.
  // Bit 0: VRAM Address (16)
  .label VERA_ADDRX_H = $9f22
  // $9F23    DATA0    VRAM Data port 0
  .label VERA_DATA0 = $9f23
  // $9F25    CTRL Control
  // Bit 7: Reset
  // Bit 1: DCSEL
  // Bit 2: ADDRSEL
  .label VERA_CTRL = $9f25
  // $9F26    IEN        Interrupt Enable
  // Bit 7: IRQ line (8)
  // Bit 3: AFLOW
  // Bit 2: SPRCOL
  // Bit 1: LINE
  // Bit 0: VSYNC
  .label VERA_IEN = $9f26
  // $9F27    ISR     Interrupt Status
  // Interrupts will be generated for the interrupt sources set in the lower 4 bits of IEN. ISR will indicate the interrupts that have occurred.
  // Writing a 1 to one of the lower 3 bits in ISR will clear that interrupt status. AFLOW can only be cleared by filling the audio FIFO for at least 1/4.
  // Bit 4-7: Sprite Collisions. This field indicates which groups of sprites have collided.
  // Bit 3: AFLOW
  // Bit 2: SPRCOL
  // Bit 1: LINE
  // Bit 0: VSYNC
  .label VERA_ISR = $9f27
  // $9F28    IRQLINE_L    IRQ line (7:0)
  // IRQ_LINE specifies at which line the LINE interrupt will be generated.
  // Note that bit 8 of this value is present in the IEN register.
  // For interlaced modes the interrupt will be generated each field and the bit 0 of IRQ_LINE is ignored.
  .label VERA_IRQLINE_L = $9f28
  // $0314    (RAM) IRQ vector - The vector used when the KERNAL serves IRQ interrupts
  .label KERNEL_IRQ = $314
  .label vbcounter = 5
  .label default_irq_vector = 6
.segment Code
__start: {
    lda #0
    sta.z vbcounter
    sta.z default_irq_vector
    sta.z default_irq_vector+1
    jsr main
    rts
}

////////////// MY INTERRUPT


myInterupt: {
    .label intbits = 8
    lda VERA_ISR
    sta.z intbits
    lda #VERA_LINE
    and.z intbits
    cmp #0
    beq __b1
    inc.z vbcounter
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    lda #$64
    sta VERA_IRQLINE_L
    ply
    plx
    pla
    rti
  __b1:
    lda #VERA_VSYNC
    and.z intbits
    cmp #0
    beq __b2
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    jmp (default_irq_vector)
  __b2:
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_ISR
    sta VERA_IEN
    lda #$64
    sta VERA_IRQLINE_L
    jmp (default_irq_vector)
}

////////////// MAIN
main: {
    jsr initMain
    rts
}

 

////////////// REAL MAIN
initMain: {

 

////////////// MAIN INIT

 

///// MAIN LOOP COUNTER

    .label ll = 2

///// NOTES: "ll" is in memory, keyboard counter seem to be optimized and not stored in memory (we have ll = mainloop, keycounter, and vbcounter)

///// forcing keycounter in memory solves it, but make me unsure of what other issues would arise, for rest a pretty big c program

///// which I am using as my base program, so forcing things to memory is not wanted, and also it kills optimisation.

 

///// SET INTERRUPTS
    sei
    lda KERNEL_IRQ
    sta.z default_irq_vector
    lda KERNEL_IRQ+1
    sta.z default_irq_vector+1
    lda #<myInterupt
    sta KERNEL_IRQ
    lda #>myInterupt
    sta KERNEL_IRQ+1
    lda #VERA_LINE|VERA_VSYNC
    sta VERA_IEN
    lda #4
    sta VERA_IRQLINE_L
    cli

///// POKE INITIAL VALUES ON SCREEN


    ldx #0
    txa
    sta.z vpoke.vaddr
    sta.z vpoke.vaddr+1
    jsr vpoke
  //set an "@" left top screen
    ldx #$12
    lda #<1
    sta.z vpoke.vaddr
    lda #>1
    sta.z vpoke.vaddr+1
    jsr vpoke
    lda #0
    sta.z ll
    tay

 

////////////// MAIN LOOP


  __b2:

 

///// CHECK KB
    jsr kbhit

///// IS SPACEBAR??
    cmp #$20
   bne __b3

    iny

    tya
    tax
    lda #<0
    sta.z vpoke.vaddr
    sta.z vpoke.vaddr+1
    jsr vpoke

    ldx #$12
    lda #<1
    sta.z vpoke.vaddr
    lda #>1
    sta.z vpoke.vaddr+1
    jsr vpoke

/////NO SPACE KEY PRESSED
  __b3:
    ldx.z vbcounter
    lda #<2
    sta.z vpoke.vaddr
    lda #>2
    sta.z vpoke.vaddr+1
    jsr vpoke
  //set an "@" left top screen
    ldx #$12
    lda #<3
    sta.z vpoke.vaddr
    lda #>3
    sta.z vpoke.vaddr+1
    jsr vpoke
    ldx.z ll
  //set an "@" left top screen
    lda #<4
    sta.z vpoke.vaddr
    lda #>4
    sta.z vpoke.vaddr+1
    jsr vpoke
    inc.z ll
  //set an "@" left top screen
    ldx #$12
    lda #<5
    sta.z vpoke.vaddr
    lda #>5
    sta.z vpoke.vaddr+1
    jsr vpoke
    jmp __b2
}

 

////////////// FROM KICK C LIBRARY FOR VERA


// Put a single byte into VRAM.
// Uses VERA DATA0
// - bank: Which 64K VRAM bank to put data into (0/1)
// - addr: The address in VRAM
// - data: The data to put into VRAM
// vpoke(byte* zp(3) vaddr, byte register(X) data)
vpoke: {
    .label vaddr = 3
    // Select DATA0
    lda #VERA_ADDRSEL^$ff
    and VERA_CTRL
    sta VERA_CTRL
    lda.z vaddr
    // Set address
    sta VERA_ADDRX_L
    lda.z vaddr+1
    sta VERA_ADDRX_M
    lda #0
    sta VERA_ADDRX_H
    // Set data
    stx VERA_DATA0
    rts
}

 

////////////// FROM KICK C LIBRARY FOR CONSOLE


// Return true if there's a key waiting, return false if not
kbhit: {
    .label chptr = ch
    .label IN_DEV = $28a
    // Current input device number
    .label GETIN = $ffe4
    .label ch = 9
    lda #0
    sta.z ch
    // CBM GETIN API
    jsr _kbhit
        bne L3

        jmp continue1

        .var via1 = $9f60                  //VIA#1
        .var d1pra = via1+1

    _kbhit:
        ldy     d1pra       // The count of keys pressed is stored in RAM bank 0.
        stz     d1pra       // Set d1pra to zero to access RAM bank 0.
        lda     $A00A       // Get number of characters from this address in the ROM of the CX16 (ROM 38).
        sty     d1pra       // Set d1pra to previous value.
        rts

    L3:
        ldy     IN_DEV          // Save current input device
        stz     IN_DEV          // Keyboard
        phy
        jsr     GETIN           // Read char, and return in .A
        ply
        sta     chptr           // Store the character read in ch
        sty     IN_DEV          // Restore input device
        ldx     #>$0000
        rts

    continue1:
        nop
     
    rts
}

 





 

Link to comment
Share on other sites

  • 0
21 minutes ago, CursorKeys said:

I hear your argument for 100% assembly 🙂  and maybe I will eventually be convinced.

I believe SlithyMatt is suggesting that you write the interrupt handler in assembly, and the rest of the program in C. On a system like the X16, a language like C should be used when it makes the code less complicated; if C begins to make the code more complicated, use assembly for that part of the program.

  • Like 1
Link to comment
Share on other sites

  • 0
4 minutes ago, Elektron72 said:

I believe SlithyMatt is suggesting that you write the interrupt handler in assembly, and the rest of the program in C. On a system like the X16, a language like C should be used when it makes the code less complicated; if C begins to make the code more complicated, use assembly for that part of the program.

Ok, that is cool, that was my aim from the start.

 

The weird thing here is that, the is change in the non-interupt part. The main code behaviour seems modified by the interrupt.  Or the change is the main code breaks the interrupt.  So it's the combination of the two that does not go too well.  Anyways..... more investigations seem to be needed... kickc seems to have build in debugger, that can talk to the emulator, or such I understood. Maybe I give that a spin, but I'm not sure how that would look, with interupts twirling about. 

Link to comment
Share on other sites

  • 0

Thanks, @Elektron72, that was my point. 100% assembly is not for everybody, but until cc65 for the X16 is more mature, you need to be careful what you use C for. You can still make a program that's mostly in C, but still with some assembly to hold it together. What would be nice if somebody made a C application framework for cc65 that did things like set up interrupts and let you register callback functions. Then you could focus your C code on the particular processing of your app and not have to deal with boilerplate stuff, and then only inject assembly where it suits your app's particular needs, if at all.

Link to comment
Share on other sites

  • 0
Posted (edited)
17 minutes ago, SlithyMatt said:

What would be nice if somebody made a C application framework for cc65 that did things like set up interrupts and let you register callback functions. 

Hi!

More or less this is why I went to KickC, since (maturity aside), it has build in functions for enabling interrupts. SEI() and CLI().  It's like a minimal super super minimal "framework" to get it done.   
Anyway when I am more or less happy with my code working in both world (ASM and C) without the inexplicable behaviour, a framework sounds like a cool project.

But yeah, first it needs to work properly 🙂

Sorry for the spam, thanks for the feedback so far guys, I do appreciate it.

ps. Now I think of it, I don't see a "libraries and frameworks" section under downloads, so not sure where to post it, if I'd ever get around to doing something like that.

Edited by CursorKeys
Link to comment
Share on other sites

  • 0
6 minutes ago, CursorKeys said:

Now I think of it, I don't see a "libraries and frameworks" section under downloads, so not sure where to post it, if I'd ever get around to doing something like that.

There is a "Dev Tools" section

  • Like 1
Link to comment
Share on other sites

  • 0

I ended up using one instance of "asm("jmp _DefaultIRQ");" in my 99.999% C program. (that and asm("SEI"); c code; asm("CLI"); during my IRQ install routine)

Sometimes being close to the metal in C, but having the metal itself under glass can be pretty frustrating. But at the end of the day, I got it all done. It's not just the results - it's the friends we made along the way. (right?)

  • Like 2
Link to comment
Share on other sites

  • 0
Posted (edited)

Your program does too much!  Instead, it should follow these rules:

  • Ignore interrupts that aren't important to the program.  Your test program cares about only the raster line.  Let Kernal handle the other interrupt.
  • Don't constantly re-enable the interrupt.  Set it once, at the beginning (it will stay enabled).
  • After you handle the interrupt, clear only that interrupt!
  • Always exit through the saved vector.  Let Kernal do its things.
#pragma target(cx16)
#include <cx16.h>
#include <6502.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <veralib.h>

volatile char vbcounter=0;
volatile void *default_irq_vector;

__interrupt void myInterrupt(void)
{
  if (*VERA_ISR & VERA_LINE) {
    vbcounter++;
    *VERA_IRQLINE_L = 100;

    *VERA_ISR = VERA_LINE;
  }

  asm {
    jmp (default_irq_vector)
  }
}

void initMain(void)
{
  volatile unsigned char ll = 0, mm=0;

  default_irq_vector = *KERNEL_IRQ;
  SEI();
  *KERNEL_IRQ = &myInterrupt;
  CLI();

  *VERA_IRQLINE_L = 4;
  *VERA_IEN |= VERA_LINE;

  vpoke( 0,0, 0 ); // set "@" left top screen
  vpoke( 0,1, 0x12 ); // set "R" next to it

  while (1) {
    unsigned char c = kbhit();

    if (c == 32) {
      vpoke( 0,0, ++mm ); // increment left top screen
      vpoke( 0,1, 0x12 ); // set "R" next to it
    }

    vpoke( 0,2, vbcounter );
    vpoke( 0,3, 0x12 ); // set "R" next to it

    vpoke( 0,4, ll++ );
    vpoke( 0,5, 0x12 ); // set "R" next to it
  }
}

void main(void)
{
  initMain();
}
Edited by Greg King
Syntax-highlight the C program.
  • Like 1
Link to comment
Share on other sites

  • 0
1 hour ago, Greg King said:

Always exit through the saved vector.  Let Kernal do its things.

It doesn't make sense to jump through the original vector when a line interrupt is handled, as that would cause the kernal interrupt handler to run multiple times in one frame. I may be wrong, but it looks like the program above jumps to the kernal IRQ handler every time an interrupt occurs.

  • Like 1
Link to comment
Share on other sites

  • 0
Posted (edited)

Yeah, it should do something more like this:
 

__interrupt void myInterrupt(void)
{
  if (*VERA_ISR & VERA_LINE) {
    vbcounter++;
    *VERA_IRQLINE_L = 100;

    *VERA_ISR = VERA_LINE;
  }

  asm {
    jmp (default_irq_vector)
  }
}

(I can't figure out how to get my own "code" block on this thing, so just imagine these changes:
After *VERA_ISR = VERA_LINE;
asm {
  ply
  plx
  pla
  rti
}
and put the asm{jmp(default_irq_vector)} into an else{} clause

p.s. it kind of sucks that they didn't design the 6502 with a dedicated vector for BRK in addition to reset, IRQ, and NMI.

 

Edited by ZeroByte
  • Like 1
Link to comment
Share on other sites

  • 0
Posted (edited)
18 hours ago, Elektron72 said:

It doesn't make sense to jump through the original vector when a line interrupt is handled, as that would cause the kernal interrupt handler to run multiple times in one frame. I may be wrong, but it looks like the program above jumps to the kernal IRQ handler every time an interrupt occurs.

He's right.  I had assumed that Kernal checks for the vertical sync interrupt -- but, it doesn't!!  Therefore, our programs are forced to do it.  Your handler must have some additional code.

__interrupt void myInterrupt(void)
{
  if (*VERA_ISR & VERA_LINE) {
    vbcounter++;
    *VERA_IRQLINE_L = 100;

    *VERA_ISR = VERA_LINE;
  } else if (*VERA_ISR & VERA_VSYNC) {
    asm {
      jmp (default_irq_vector)
    }
  }
  // Other interrupts are ignored.
}

Note that the __interrupt qualifier makes this function return by jumping to R38 Kernal's $E049, which pulls the registers and does RTI.

Edited by Greg King
Syntax-highlight the C code.
  • Like 1
Link to comment
Share on other sites

  • 0
6 hours ago, Greg King said:

Note that the __interrupt qualifier makes this function return by jumping to R38 Kernal's $E049, which pulls the registers and does RTI.

Ugh... if that's what cc65's interrupt handler does, then in my opinion it ought to be changed. First, there's no guarantee that the kernal's interrupt exit routine will always exist at that address, until the kernal is finished and the final X16 is released. Even then, creating that dependency makes it possible to break programs later on if the kernal needs to be patched. Also, if there's logic that was interrupted and depends on a particular ROM bank being loaded, that bank gets clobbered if you let the kernal trampoline into ROM bank 0 and then fail to let it trampoline back to the appropriate ROM bank that had been loaded before the IRQ fired.

The kernal loads a default interrupt handler address at $0314, and a default brk handler address at $0316. It's fine if someone wants to clobber those to run their own handlers, but to return control to the kernal they should be jumping to whatever the kernal had originally loaded at those addresses, not to the location of ROM that happens to perform the PLY, PLX, PLA, and RTI. If you really intend to immediately terminate the interrupt, expert-style, you should just go ahead and do it yourself. (Unless or until the kernal gets an official API function to do it on your behalf, saving your program the... 1 byte difference between a jsr and "PLY, PLX, PLA, RTI".)

  • Like 3
Link to comment
Share on other sites

  • 0

Thanks for all the feedback,

So today I cam back to this, since I forgot about it for a while, as everything that made sense just ended up not working.  And I needed to get some other things done.


Now I noticed that when I disabled the interrupt it was not working either 😛, I noticed the combination of kbhit and vpoke, as defined in KickC, at least the version I have, crashed.


So when I found this out, then I replaced the vpoke by my own implementation, and suddenly the version without interupt was all ok, now one more try WITH interupt.

What I did get working on the end, is more or less described by the following minimal code snippet.
 

Quote

..

volatile char vbcounter=0;
volatile char linecounter=0;


__interrupt( rom_sys_cx16 ) void myInterupt() {

  if( *VERA_ISR & VERA_LINE ) {

    //DO Line interupt stuff

    linecounter++;

    //end interupt
    *VERA_ISR &= VERA_LINE;
    asm {
      ply
      plx
      pla
      rti
    }

  }
  else if( VERA_ISR & VERA_VSYNC ) {
    //Do sync interupt stuff

    vbcounter++;
  }
}

..

RTFM helped me a bit as well, seeing that KickC has some support build in for X16 interrupts, with or without keyboard (system or minimal). 

All worked fine for none line interupts, but for line interrupts, I still needed a bit of assembly, as you see above, to exit correctly. (ply, plx, pla, rti )

This is probably the best version I have right now, with the least amount of assembly code in it, and it works nicely together with keyboard.

 

Link to comment
Share on other sites

  • 0

Thanks Greg, that is correct.  So then the final code snippet for KickC compiler, vsync AND line interupts AND keyboard handling:

 

Quote

..

volatile char vbcounter=0;
volatile char linecounter=0;


__interrupt( rom_sys_cx16 ) void myInterupt() {

  if( *VERA_ISR & VERA_LINE ) {

    //DO Line interupt stuff

    linecounter++;

    //end interupt
    *VERA_ISR = VERA_LINE;
    asm {
      ply
      plx
      pla
      rti
    }

  }
  else if( VERA_ISR & VERA_VSYNC ) {
    //Do sync interupt stuff

    vbcounter++;
  }
}

..



 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use