This part I’ll be writing about ripping the music from the game (‘hacking, ‘cracking’, ‘ripping’ the vocabulary of the scene sounds a bit like a pro-wrestling pre-match-rant, doesn’t it?).
One of the reasons the C64 was so popular, was because of the great soundchip and the music that was produced for it. Hackers ripped the music so it could be played on it’s own or as part of a demo. By adding some metadata, the C64 standalone music program can even be converted to a SID file (named after the C64 sound chip) and played by programs developed for modern PC’s.
Ripping music means: extracting the code that plays the music and the accompanying music data and putting it in a single package, so it can be played by itself, without the game.
As it is a game from 1985, the music has been ripped already a long time ago by others. You can find it as part of the High Voltage Sid collection (you can do a search for ‘willow pattern’ on their site).
I use a lot of text to explain stuff. To accompany the stream of words I offer a link to the files that are the result of this ‘session’:
Files included are the assembly code (kickassembler format), the resulting basic-executable program and the music data as binary that is used in the code. Also a resulting SID file is made.
It’s only a few months ago I ripped the music, so I could use some modern tools. The tools I’ve used for this are Vice, an IDE with Assembler a Hex Editor (I’m using HxD editor) and a tool I made myself in Excel.
Finding the beginning of the thread
Most music routines on a C64 have the same basic idea. At regular intervals a counter is decreased . When the counter reaches zero a new musical’event like ‘play a note’ is read and the counter gets the value for the time till the next musical event. There can be all kinds of other things going on, but this is the general method.
The best way to get a regular interval in C64 assembly is by using a Interrupt.
Interrupts are ways of the ‘outside world’ (outside for the CPU that is, so a signal from another chip for instance) to interrupt the regular flow of an executing program. The CPU has a special bit on its status register to deal with interrupts.
A simplified way to describe the working of the CPU of a C64:
- read a instruction from memory and execute it
- alter the current memory position based on that instruction
- read the next instruction from that address
But before each instruction-read the CPU checks if the interrupt bit in the CPU’s status register is set. If so, the normal flow is suspended and the CPU starts executing from the address that is coded in a certain part of memory (the interrupt pointer).
A commonly used type of Interrupt for the C64 has its pointer in address $0314/$0315. Scanning memory for the two following bytes $14 and $03 (the C64 is ‘little endian)’ can give you locations where that memory is used or set.
Or you can see what the current values are of that pointer. In Willow Pattern Adventure (WPA) it points to location $0E00.
Where does it lead to, what does it do?
Interrupts can be used for several types of functionality. Music is one, updating graphics or scanning the keyboard another. WPA has a pretty simple interrupt: it is only used for the music.
If I import all 64k of memory to my Excel program I can trace what parts of memory are used by giving it the start address ($0E00). It will trace the code, take all jumps and all subroutines. Every address tracked will be designated as code, the rest of memory as data.
That way you’ll end up with a very comprehensive view of the code, every byte that has nothing to do with the music is not disassembled but rendered as bytes.
Here’s the beginning of the code, remember this will be called about every 50th of a second (one tick) on a PAL system.
JSR L0F66 ; this subroutine is called every tick: it slightly changes the frequency of the notes played: vibrato DEC L0ED5 ; this is the counter I mentioned before BEQ L0E0B ; if the counter reaches zero goto L0E0B, the main music player routine, to get a new note JMP $EA31 ; return to the standard normal C64 Kernal Interrupt routine which does things like checking the keyboard L0E0B LDA #0C ; set the counter to a standard value. Unusual: the same amount of time between all events! STA L0ED5 ; it is used so you can have the same piece of music in different speeds by changing this value LDA $FA ; [.....]
I now have the routine that plays the music. But I also need the routine that does the initial sound setup. It is likely to be the same part where the interrupt pointer is set.
Using the Vice Monitor I find a few promising parts of memory:
The second address is where the pointer is set to $EA31 which is the standard C64 Interrupt address. I can ignore that. The first address is where the interrupt is set to $0E00 and looks like a small subroutine where some sound things are set and the interrupt pointer.
Now I need to know where the music data is stored.
I can see in the code that indirect addressing is used for reading from memory:
L0E3A LDA(FA),Y ; an address between parenthesis means A is not read from $FA but the address referenced in $FA/$FB with Y value addded to it BEQ L0E57 ; if the note value is zero, no new note is played, a pretty wasteful method, specially because it still uses two bytes STA L0ECF ; the note is also stored for vibrato purposes STA $D400 ; store note value in low frequency register of Voice 1 of the SID-Chip
You could look up the current value in memory but it could change to all values.
I use a plugin for Vice, ICU64. It can show what parts of memory are updated in real time. This is a graphic representation of the memory. Bytes that are read or written to, light up and fade out.
The opening screen is a static screen, so probably all that changes is the music. I guess the green parts are the music data. It moves at a constant speed which is what you would expect from the music routine: I saw in the code it reads notes at a constant interval of n-ticks.
You can zoom in on the green dots and read the actual addresses.
The in-game music is different, so probably occupies a different part of memory, but you can use the same method for that. Just start the game and watch the memory.
Now I have all parts for making a standalone musicplayer. I save the memory containing the music as binary data and export the code as a text file. My Excel program can automatically create labels for addresses used in my code so I can easily relocate the code.
Putting it together
- I import the code in my IDE Relaunch64
- change the assembly to KickAssembler format, which I now use
- import the music data binary
- add a basic loader
- create some code that calls the setup routine.
The result is a single file program that can be started from basic and plays the music. But I would like to hear the intro and in-game music, so I add some code that if you run the setup again, the other music starts to play.
The beginning of the code now looks like this:
.pc = $0801 //start of the memory for a basic program .var music1_speed=$0e//$10 //some variables .var music2_speed=$0a//$0c :BasicUpstart($0810) // a way to easily add a basic program that runs machine code from address in parenthesis .pc = $0810 ldx #$00 lda l0f4e //if this address is two, change to zero cmp #$02 beq !+ ldx #$02 //otherwise set to two !: stx l0f4e //this address controls which music is played jsr r0edb // call music setup rts //return from subroutine, back to basic
This all works and can easily be incorporated into another program like an intro.
I know cracking a game is not so special anymore and I’m not too sure if my home-made SID tunes will impress anyone. Also the Willow Pattern tune uses very few computer cycles, which might be useful if I use it in my intro.
But using the same music for the intro and the game sounds a bit lame.
What can I do to make it just a bit special?
I’ll just reverse the music! Really, I have no idea why I came up with that but at least it probably has not been done before.
I have to make a few adjustments to the code:
- The startaddress of the tunes must now point to the end of the tunes
- The pointer must now count backwards
- The routine uses a byte of ’01’ as a sign the end of the music is reached, The ’01’ must come at the beginning.
This all worked pretty well. But to give it that ‘real backwards feeling’ I also have to reverse the way the notes sound.
Sound is synthesized on a C64 using the ADSR Attack Decay Sustain Release technique.
A certain note increases to maximum volume during an Attack phase, drops to a Sustain level in the Decay phase and when triggered will drop to volume 0 in the Release phase. Those four values can be set for each note.
The original tune has a pretty ‘staccato’ sound, caused by an attack time of 0. Almost immediately the note goes to maximum volume.
To create a ‘backwards’ effect you can make a note slowly ‘swell’ to maximum volume.
It does have the disadvantage that notes take longer to be heard. In the backwards tune some notes disappear. But since it doesn’t really resemble the original tune anymore that doesn’t matter much.
Well, that’s almost it for now. As a finalcz touch I would like to convert the tune to .SID format so it could be played with a SID player on a modern PC. Not that the reversed tune sounds that great, but I never made a SID before.
The changes in the code are easy: you have to have an initialization routine that is called with the song number in the A register.
This will do:
[... I removed the basic loader, that will not be used ...] .pc = $1000 / I relocate it to address $1000, not strictly neccesary. asl //a*2, multiply the A register by two because the original routine uses 0 and 2 as song numbers sta l0f4e // store that value in the memory that is already used in the original code for song number jsr r0edb // call the already existing initialization rts //... done!
Now I have to add some meta data to the file. On hvsc.com there is a good description of the file format, so with a Hex editor and that description the file is changed in a jiffy.
I’ve added the resulting SID file to the dropbox folder that is linked in the beginning of this post (so long ago!)
Not sure what I’ll be focussing on next time. I think I will be writing about trainer functionality.