Update: All needed files for this tutorial can be found in the GitHub repo linked in the bottom of this page.
In this tutorial we will take a look at one of the most important topic when it comes to C-64 programming – Interrupts.
Interrupts are used to pause whatever the machine is doing at a given condition, and do another task. When the interrupt is complete, your program will continue to run where it was interrupted.
An interrupt might be a timer interrupt that happens when a given amount of cycles has passed, a raster interrupt, a collision interrupt and so on. You can have multiple interrupts during a screen refresh. If you have interrupts enabled, and don’t use them to time your program, your application will be jerky and flickering.
There are two types if interrupts. One is Interrupt Request (IRQ), the one we are going to program in this tutorial, and then another one named Non-Maskable Interrupt (NMI). The difference between these is only that you can turn off the IRQ’s, but not the NMI’s (unless you do a trick).
We are also going to introduce two new instructions, SEI and CLI:
– SEI (SEt I(nterrupt) flag) instruction does disable interrupts.
– CLI (CLear I(nterrupt) flag) instruction does enable interrupts.
There is an initialization process when creating interrupts. During this, it’s very important to disable interrupts just to make sure another interrupt won’t ruin the init process. (If not, your application MIGHT crash)
The example in this tutorial will use interrupts to play music. Playing music in a program is very simple. Only a music file (.sid) and 5 lines of code is required. You also need to find some numbers that will be used to locate, init and play the music using a SID-player tool.
You can create your own music, but since I’m nothing near a musician, I downloaded a music and used this in our example. The file is named music.sid, and the title of the song is “Masses Zak”. I got the file from High Voltage SID Collection, a page with a huge archive of C-64 music. Be sure to take a look!
Before we start, download this music file:
Source at GitHub
You also need a tool that can play music files, just to find out some information about the file, like where in the memory it will be stored. Most files will be at $1000, but not all.
The SID player I use is named Sidplay2 for Windows and can be downloaded here.
Now, download and start Sidplay 2, open “music.sid” (can be downloaded above) and the music will start playing. Now click File->Properties to see a long list with information regarding the SID-file:
What’s important to note is the Load range, Init address and Play address. These will be used when we init and play our song (d’Oh).
Let the programming begin!
As usual, we start by telling the compiler what processor we are programming for, and where in the memory our program should start.
But wait, we are starting on $0810 instead of $1000? Why is this? Well, our music file will be loaded into $1000 (Load range), so we simply move the start address for our program to $0810. To run our program, start it in the emulator and write SYS 2064 (decimal of 0810 hex)
Next we initiate the music. This is done but putting the value 00 into the x- and y-registers, and call the subroutine that resets the SID-chip. The properties in the SID file stated that the init routine for the music is at $1000, so that’s what we want to do.
Now we are going to initiate the interrupts. First we need to turn off the interrupts:
Then we put the value 7f into $dc0d and $dd0d to disable the CIA I, CIA II and VIC interrupts (timer, keyboard,…, interrupts)
We also need to enable raster interrupts. We do this by inserting 01 into $d01a.
Next we tell the VIC that we want to enter single-color text mode. Inserting 1b into $d011 means “Enter text-mode”, and inserting 08 into $d016 means “Use single-color”. We also tell the VIC that our screen RAM is at $0400 and that we want to use the default charset by inserting 14 into $d018 (see earlier tutorials for more information about how this works).
Now we are at the meat of this tutorial. What we will do next is to load the interrupt handlers codes lower and high part into the interrupt vector at $0314-$0315. “irq” is the label where the code for our interrupt is located, so all we do is to insert a pointer to this into $0314-$0315:
Then we need to create the trigger for out interrupt at “irq”. We want a raster interrupt at any line (in this example $7e) to trigger the interrupt.
Then we clear pending interrupts (the CIA 1, CIA 2 and VIC interrupts).
Now that the interrupt is initiated, we can enable interrupts and start with the program logic. In this example, we are only running an infinite loop:
loop: jmp loop ; infinite loop
Next is the code for our interrupt. What this does is to first run a sub routine at $1006 (the play SID-file routine for music.sid (remember the properties of the sid file)):
irq: jsr $1006
Then we ACK the interrupt with asl $d019. This is done because we don’t want the interrupt to be called again right after we return from it.
Then we jump to a subroutine that restores the stack and returns from the interrupt. If you want to save 3 cycles, you could write this manually, but for simplicity, we jump to $ea81 (see below for what you can replace this with if you want to write the code yourself):
The last thing we do is loading the music into $1000. But a SID file got an offset of $7e so we need to subtract this from $1000 so the file is correctly placed in memory.
That’s if for basic interrupt. We will be more advanced in a later tutorial as interrupts are really important when it comes to C-64 programming.
A complete listing of our example is in listing 9.1.
Listing 9.1 – Interrupts and music
loop: jmp loop
irq: jsr $1006
Download the source from GitHub:
Pingback: Windows Client Developer Roundup 067 for 5/1/2011 - Pete Brown's 10rem.net
Excellent tutorials. I have 2 questions that I hope you can answer. I am trying to play the following but I am obviously doing something wrong.
Name: Energy Warriors
Author: Andy Grimson
Released: 1987 Mastertronic
Load range: $0800-$1391
Init address: $1389
Play address: $0801
Below is the code I am using:
lda #$00 ;load $0 into the accumulator
tax ;transfer the accumulator into the x register
tay ;transfer the accumulator into the y register
jsr $1389 ;initialize music (Init Address)
mainloop: lda $d012 ;load the current position of the raster
cmp #$38 ;the raster trigger point on the screen
bne mainloop ;if != $38 then mainloop
inc $d020 ;inc the border colour
jsr $0801 ;jump to the music play routine (Play Address)
dec $d020 ;dec the border colour
jmp mainloop ;keep looping
org $800-$7e ;I know this can’t be correct due to the memory mapping
When compiling I receive:
segment: INITIAL CODE SEGMENT 0782 vs current org: 101a
energy.asm (20): error: Origin Reverse-indexed.
So my questions are, can someone please correct me in the above code and repost please? Also, the load range on the above sid is $0800-$1391, I am assuming any code written needs to above $1391?
Excellent C64 series! I’m really impressed haw smooth and pleasant to read your posts are. Thanks to you I discovered C-64 once again. 🙂 Big thanks again!
Great tutorial, I really hope you will continue with it since we still haven’t learned how to make those smooth Scrollers and other goodies yet 🙂 Thanks!
By the way.. I experienced that the sid music I used in your code example plays at least at half the speed than it did originally using Sidplay2 at the 1x setting… Why is this happening do you think and how can I speed up the tune using your code?
Absolutely awesome tutorial man!!! Kudos. 🙂
I had some issues with compiling as others did, but when I read the replies, I found the solution with the spacing issue as well was the fix! I never thought I would be able to code the loader that I just made with BASIC/ML that loads ML programs AND a BASIC (compiled with BLITZ) program as an overlay (thanks to Jim Butterfield’s tutorial on how to use overlays and a bootstrap).
I have everything I need except the SCROLLER with a message at the bottom. I will have to check out the code to display the image created with Koala Paint you gave in a tutorial to do this instead of the BASIC DATA/ML code program I found to do it in the back of the Koala Painter user manual.
I am using HERMIT’s SID-Wizard v1.0 and SID-Maker v1.0 programs to create my OWN SID tracks, which is an AWESOME software combo to do it.
DASM is cool. Thank you for the info man. You rock!
How can we mod this code to look for a SPACE key press to STOP playing music? LOL
Can you load programs normally while the interrupt plays a SID in memory?
org $0810; run with sys 2064
; i have no idea how this routine works!
; check if space hit to exit play routine
lda $dc01 ;check keyboard
cmp #$ef ;spacebar pressed?
jsr again ; else continue to play
exit: jsr $1000 ; reset the SID chip
; dasm psuedo-op codes to load SID file at $1000
What about using CIA interrupt(s) to play the music at the same speed regardless of PAL or NTSC? Please write a tutorial on how to do that.
Wrong code here in the large listing:
Should say $d018 instead of $d014.
I’ve been looinkg for a post like this forever (and a day)
Indeed, somehow I could remove that part, I used a pic 🙂
Thx people, the press space snipe works perfect.
Thanks Digitalerr0r. though this part could have been a litlle more clear. it’s nice to play a tune, but the interrupts should have been the main focus.Also I must rethink about this $7e offset, not really 100% undestand the tuto.
There’s also a second error
ldy #$7e ; this was one the tuto and not on large code. It doesn’t seem to do different with my pic 🙂
I think this part of the tuto worth to be redo a little better
Okay I’ll retry tommorrow 😀
I was puzzled by the lda #< … then I used google!
For the noobs that are me it should help even if it was wrote " load the interrupt handlers codes lower and high part into the interrupt vector at $0314-$0315" brain (or my breadbin haha)
Yep I didn't made the LDA/load connexion in my
lda #irq ; high part of address of interrupt handler code
sorry, netbook here, my cheap pad is crazy when I type keys, so it ruined the cursor focus, I need to disable it, I use a mouse…
lda #int ; high part of address of interrupt handler code
arg the parser doesn’t like it…
is high part …
Ok sorry for the roman. Today I think I got it.
More help for newbies
$7e offset = it’s where the header of a sid file end. It can be 7c with version 2 of sid. We read here the raw datas to play the tune.
jmp $ea81, is like a return, otherwise it doesn’t quit the routine
I wonder if it’s also where you can get the values or any datas from the external fonction (if that foncion is like a dll). I don’t know what we get, maybe that’s not enough code, but probably there’s a way to sync music ? Ok that’s not for asm beginners anyway. Just noticed it
What I need now, before read the last tuto is few info about CIA…. what’s that!
Pingback: ΚΑΤΑΣΚΕΥΗ C64 IRQ Status LED | iamretro
Guess what? link to music.sid is broken… 🙂