Welcome

Monday, January 27, 2020

Interrupts Explained

Interrupts  Explained


Why do you need to worry about interrupts when writing a simple C64 game? Interrupts are  only needed for for fancy raster bars and demos right?

Well no, raster interrupts perform two very important jobs. 

The first is they give us strict timing control, without some kind of timing interrupt, games would run as fast as they can all the time. Which would mean that they would constantly run faster and slower depending on what's on the screen and how much processing the game uses.  

The second reason is we need to make sure all screen updates are as smooth as possible, with CRT screen the picture is drawn from the top left to the bottom right. Its quite like a type writer when the scan line gets all the way to the right, it drops one line and returns to the left before resuming the screen update. The problem comes when the program draws something to the screen if the CRT has passed the point on the screen where the graphic is to appear. in this situation the graphics wont be drawn until the next screen refresh (50 times/second PAL 60 times NTSC) This doesn't sound important but this is why graphics flicker on screen. To stop this flicker it is important to draw all the graphics before the raster reaches them, the easiest way to do this is to start drawing the next frame as soon as the raster reaches the bottom of the viewable screen, this is where the interrupts come into play; we trigger the interrupt at the end of the last line of the screen and wait at that point.

Raster interrupts are generated by the VIC chip when the CRT gets to a certain line down the screen.

The best way to think of interrupts, for someone who is used to modern languages, is as an event. When an event is triggered the code associated with that event is run and that’s exactly how an interrupt works but it’s the hardware that causes the event not a user.

There are two sets to getting interrupts up and working, the initial config code and the interrupt (or event) handler code.

Let’s take a look at the setup code:

interrupts: {

            init: {

                sei                                                      // turn off maskable interupts

                lda #$7f                                             // kill CIA interupts
                sta $dc0d
                sta $dd0d

                lda $dc0d                                          // read interupts to clear them
                lda $dd0d

                lda #$01                                            // get the vic II to do raster interupts
                sta $d01a


                lda #$1b                                           // set the inturpt line
                sta $d011

                lda #$EA
                sta $d012

                lda #<rasterHandler                          //this is how we set up
                sta $fffe                                           //the address of our interrupt code
                lda #>rasterHandler
                sta $ffff
                
                cli                                                       // turn on the interupts

                rts
            }

Ok so most of this code is boiler plate code and doesn’t really need to be understood in detail;

The sei command disables all interrupts, if we get an interrupt whilst setting up the interrupts it will probably crash the computer so temporally turn them off.

The CIA interrupts are not used usually during a game, they are used to monitor the keyboard inputs and let the kernal buffer them which in-game we don’t want so we turn them off and clear any pending interrupts.

The two green lines tell the VIC chip that you want it to generate interrupts at a given point, these lines just enable that function.

The yellow lines are the first important ones and the ones that may change program to program. They tell the VIC chip which line to generate the interrupt on, this is important for timing. Usually you want the interrupt to fire after all the screen updates are complete but leave as much time as possible before the next screen refresh. I usually set mine to the bottom of the visible screen area ie at the start of the bottom border.

Next the blue code tells the computer what address to call when the interrupt is triggered, think of it as a jsr rasterHandler. When the interrupt fires this subroutine is called.

Now the interrupt is set up you need to give it some code to run.

            rasterHandler: {                      

                        dec rasterCounter
                        
                        lsr $d019                                             // acknowledge the interupt 
                        rti

             }

This is about the simplest code you will ever use, all it does is decrease memory location called rasterCounter. The variable rasterCounter should be defined in zero page see later.

We also have to acknowledge the interrupt, that is to tell the VIC that we saw the interrupt and to reset it for the next cycle. If the interrupt is not acknowledged then the VIC chip will constantly spam the interrupt which means that only the code in the interrupt will run.


Ok so now you have an interrupt that every 1/50th of a second will decrease a variable, not a lot of use right? Well …..


*=2 virtual
rasterCounter:
            .byte 0
*=$1000
Main: {
            Jsr interrupts.init

!gameLoop:

            Lda #1
            Sta rasterCounter
!waitForInterrupt:
            Lda rasterCount
            Bne !waitForInterupt-

// game code here

            Jmp !gameLoop-

}

// interrupt code from above goes here.

Right so when you run this program logically the section is an infinite loop and should never break out, you set rasterCounter to 1 then keep checking to see if its zero which it never will be right? Well every 50th of a second the interrupt event happens and decrease the counter so after a 50th of a second the rasterCounter isn’t 1 any more and the loop ends.

If you want to make it 25th of a second set the initial raster count to 2 etc. this way. You can create a pause as long as you want provided its in 1/50 of a second.

Now you will notice in the interrupt code I didn’t save any of the registers, well that’s because I didn’t use any of them, you only need to save the registers you change in the interrupt handler.

2 comments:

  1. Could you tell me a little more about lsr $d019 in the above rasterHandler code? Is bit 1 of $d019 always set on an interrupt, so you cna just move it to bit 0 to acknowledge the interrupt?

    ReplyDelete
  2. Furthermore, he expanded the window opening from one to a few strains to add another factor to entice folks to play, as gamers may now see how shut they were to profitable. Many gamers try to land their wins on penny slots, probably the most cheap slot machine video games in online playing. If you might be} one of them, consideration to|take observe of} the subsequent slot tips – particularly when you think {you have|you've|you may 토토사이트 have} discovered gold the moment you found a penny slot machine with a progressive jackpot. Computer systems have made slot machines a lot more adaptable.

    ReplyDelete

Setting up Mega65 Connect for LAN

The latest Mega65 Core (0.96) now supports remote access from the  M65Connect using Jtag and now ethernet. This guide will explain how to se...