Commodore 64 Programming #9: Interrupts and music

image

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. Smilefjes (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! Smilefjes

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. Smilefjes
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:
image

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.

    processor    6502
org    $0810

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. Smilefjes

             lda #$00
tax
tay
jsr $1000

Now we are going to initiate the interrupts. First we need to turn off the interrupts:
             sei

Then we put the value 7f into $dc0d and $dd0d to disable the CIA I, CIA II and VIC interrupts (timer, keyboard,…, interrupts)
             lda #$7f
sta $dc0d
sta $dd0d

We also need to enable raster interrupts. We do this by inserting 01 into $d01a.
             lda #$01
sta $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).
             lda #$1b
ldx #$08
ldy #$14
sta $d011
stx $d016
sty $d018

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:
             lda #<irq
ldx #>irq
sta $0314
stx $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.
             ldy #$7e
sty $d012

Then we clear pending interrupts (the CIA 1, CIA 2 and VIC interrupts).
             lda $dc0d
lda $dd0d
asl $d019

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:
             cli
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.
             asl $d019

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):
             jmp    $ea81

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.

    org $1000-$7e
INCBIN “music.sid”

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

processor    6502
org    $0810

             lda #$00
tax
tay
jsr $1000
sei
lda #$7f
sta $dc0d
sta $dd0d
lda #$01
sta $d01a
lda #$1b
ldx #$08
ldy #$14
sta $d011
stx $d016
sty $d018
lda #<irq
ldx #>irq
ldy #$7e
sta $0314
stx $0315
sty $d012
lda $dc0d
lda $dd0d
asl $d019
cli
loop:    jmp loop
irq:      jsr $1006
asl $d019
jmp    $ea81

    org $1000-$7e
INCBIN “music.sid”

Downloads
Download the source from GitHub:
https://github.com/petriw/Commodore64Programming/tree/master/9-InterruptsMusic

This entry was posted in Commodore 64. Bookmark the permalink.

19 Responses to Commodore 64 Programming #9: Interrupts and music

  1. Pingback: Windows Client Developer Roundup 067 for 5/1/2011 - Pete Brown's 10rem.net

  2. cynic0xff says:

    Hello,

    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:
    processor 6502
    org $1000

    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
    INCBIN “energy.sid”

    When compiling I receive:

    segment: INITIAL CODE SEGMENT 0782 vs current org: 101a
    energy.asm (20): error: Origin Reverse-indexed.
    Aborting assembly

    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?

    Thank you,

  3. Artur says:

    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!

  4. paxman says:

    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!

  5. paxman says:

    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?

  6. Dave Hartman says:

    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!

    – S0RC3R0R

  7. Dave Hartman says:

    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?

  8. Dave Hartman says:

    processor 6502
    org $0810; run with sys 2064

    sei
    lda #$00
    tax
    tay
    jsr $1000

    ; i have no idea how this routine works!

    again:

    lda #$40
    cmp $d012
    bne *-3
    inc $d020
    jsr $1006
    dec $d020

    ; check if space hit to exit play routine

    lda $dc01 ;check keyboard
    cmp #$ef ;spacebar pressed?
    beq exit

    jsr again ; else continue to play

    exit: jsr $1000 ; reset the SID chip
    rts

    ; dasm psuedo-op codes to load SID file at $1000

    org $1000-$7e
    INCBIN “music.sid”

  9. UX-admin says:

    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.

  10. Wrong code here in the large listing:
    lda #$1b
    ldx #$08
    ldy #$14
    sta $d011
    stx $d016
    sty $d014

    Should say $d018 instead of $d014.

  11. MANU says:

    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.

  12. MANU says:

    There’s also a second error
    stx $0315
    ldy #$7e ; this was one the tuto and not on large code. It doesn’t seem to do different with my pic 🙂
    sty $d012

    I think this part of the tuto worth to be redo a little better

  13. MANU says:

    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

  14. MANU says:

    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

  15. MANU says:

    arg the parser doesn’t like it…
    is high part …

  16. MANU says:

    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!

  17. Pingback: ΚΑΤΑΣΚΕΥΗ C64 IRQ Status LED | iamretro

  18. Paolo says:

    Guess what? link to music.sid is broken… 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.