The SIDPLAY Home Page counter

Ripping sidtunes

Ripping a sidtune is the process of detaching the code and data of a music player from an enclosing program, and getting the isolated music to play.

Overview Specifics

This section is not meant to be a full guide on ripping C64 musics. It is not even meant to be an introductory guide. Individual pieces of music may be very difficult and time consuming to rip for a beginner. And there must clearly be distinguished between just a rip and a so-called clean rip. In this and subsequent paragraphs the term ripper refers to a human person, unless specified in any other way.

Clean rips

Ripping sometimes can be as easy as finding the start of two machine code sub-routines and saving a small memory area.

Clean rips often are much harder to achieve. Their aim is to strip down the source program to nothing else than the music player and its data, thereby making the ripped fragment as short as possible. Additionally, rippers often copy the remaining fragments of data into a more compact form. Sometimes they even relocate the player. This may be required if one wants to merge all subsongs (perhaps each with a separate player) while considering the C64's memory size of 64 KiB only. Concerning SIDPLAY, this is easier, as the whole data are reloaded before a subsong is started, permitting the ripper to use always the full memory regardless of whether he likes to overwrite unused parts of the music data or not. The entire sidtune data will be recovered upon restarting a song or starting another song (note, that this makes it impossible to leave previously used parts of the memory untouched).

It is a crucial case if the player and its complete data (all subsongs) are spread throughout the whole memory. It is then difficult to determine whether certain regions of memory actually belong to the music player. Proving the integrity of a fragmented song can only be done by listening to it till the end and comparing it to the output of its original program. This might be the most boring part upon performing a rip if you don't want to fully read and analyze the music player (which, by the way, would be the only true way to track down the location of all belonging data; except using a self-written utility). Consider a long song. You would have to listen to it entirely to find some parts or its end to be broken. The effect of only a bit of data missing might not be as obvious as you would guess. A single instrument might be altered only slightly. You probably would not even notice a difference, a weaker vibrato effect for instance. Better save a wider region of the memory rather than cutting off any parts by accident.

Consider some of the required music data (such as vibrato tables, instrument definitions, or track and pattern data) to be put into a memory region, which you would not even think of. For instance, sensitive data might have been put into the stack address space or into the zero-page. Zero-page pointers sometimes are only initialized once by some code at the early start of the program. It can be required to write an own music player initialization, most often based on available code fragments throughout the entire memory.

Usually only musics in demonstration programs are likely to be protected. Game musics that are hard to rip generally are protected by accident (one exception is Arkanoid by Martin Galway), which either means the game programmer mixed parts of the music player code with his own code, or he did the music and player himself, or he had a very confusing coding style (socalled spaghetti-code), making the code unnecessarily difficult to read and understand, and making it almost impossible to detach music player code from program code without much effort. More on this further below.

As long as a (partially) ripped music can be determined to be obviously damaged, you can still decide to leave it in its most basic form. For instance, if the code of the application and the code of the music player are nested, saving the entire memory might be a hint. Making the rip a clean rip is just an optimization and could be done later. Also, consider asking another ripper whether he can help. Often four eyes see more than two.


The availability of special software or external hardware tools is of much help. Try to get one of the following (sorted by recommended priority):

  • Any hardware modul with monitoring functions: Memory dump, search, fill, disassembler, assembler, load and save of any memory region, ability to restore the screen buffer and the stack.
  • A hardware freezer cardridge for the C64 expansion port. For instance, this will enable you to freeze the running program at any point you like. This is especially helpful, since it gives the possibility to repeatedly access a song at the end of a game or demo without having to run the program from the very beginning.
  • A self-made EPROM module, which provides at least a subset of the monitoring functions listed above.
  • A software monitor, which provides any of the above functions; usually sufficient enough to take a close look at those parts of memory, which remain unmodified after a hardware reset.
  • A ripping utility. This chapter does not cover any of these.
  • A utility, that can read/write C64 disks with the C64 floppy drive connected to your PC. Or a utility that can transfer data via a serial cable from the C64 to a different platform.

If you don't have access to any of the tools listed above, you are definitely out of luck. At least I can't think of any other equivalent tools. However, nowadays you do not need a real C64 for ripping music (a real SID for listening to music is appreciated, though). Some of the existing C64 emulators provide a built-in emulation of good monitoring and freezing functions, e.g. the freely available C64 emulator VICE [1]. As a side note, it is also possible to rip directly from linearly written *.D64 emulator disk images provided that you know how to follow tracks and sectors.

Of course, you need to know of the capabilities of your tools and how to use them efficiently. Finally, you certainly need to know the C64's hardware and the 6502/6510 machine language. A bit of knowledge of hexadecimal machine codes would be useful but is rarely necessary as you will almost never have to read plain hex machine code. You don't need to be a programmer either, though you should be able to read and understand (and thus be able to follow) disassemblings.

A quick guide on ripping

The method presented here is just one particular way to rip a sidtune. It may not be directly applicable to every sidtune and its enclosing program. The overall ability to rip a specific sidtune depends mainly on experience and instinct as well as a bit of machine code programming skills. You will certainly build your own individual ripping algorithm based upon the experiences you make during trial-and-error ripping. Most of the examples and recommendations in this section are based upon questions me and others on Usenet have been asked. For the weird case that you want to test this guide on some program, please take into account that intro/demo tunes generally are much more easy to rip than game tunes. If you want to rip a very easy game tune, check out the game Commando. It is probably the most famous tune and very easy to rip due to the simplicity of Rob Hubbard's modularized music player. Note, that a lot of other music player are modularized as well.

Have you ever tried to rip a sidtune from a game or demo? Maybe you have always wanted to be able to listen to your favourite music without having to load and run a larger program? If you've ever tried to rip, did you succeed? If not, what was the main problem? Maybe you didn't know where to start? Maybe you erratically browsed through hundreds of lines of machine code and randomly displayed kilobytes of C64 memory without a clue? Or maybe you did not even come that far?

Depending on the tools which aid you on your ripping-task, make a hardware reset to stop the running program and start the monitor (here the term monitor always refers to the monitoring utility and not the screen :). Making a reset does not mean turning off the computer and then on again. By doing that you erase its memory! Inside a C64 emulator you often have the choice between soft and hard reset. Soft reset should be the recommended one unless the application is protected against resetting. Without an expansion module the easiest way to reset the C64 is to connect pins 1+3 (GND and RESET) of the user port (starting to count the pins from the left while looking at the back of the C64) for a short time. For the case that you don't have an external hardware module installed and need to load and start a software monitor, you will overwrite a part of the memory by loading the monitor. Some monitors are relocatable. Make and save a couple of versions for different memory regions, e.g. $1000, $6000 and $C000. Unless you find out a memory region that is definitely not used by the music player and data, you will likely have to repeat this procedure:

  1. optionally clear the whole C64 memory by filling every RAM address with a zero (if the program moves memory blocks, the zeroes can serve as a visible hint on unused memory areas)
  2. load and start the program which contains music
  3. make a hardware reset
  4. (load and) start your monitor

You already had come so far, but didn't know any further? Did you think about the simple fact that SID music can only be produced by machine code that accesses the SID chip? That is the most trivial hint. The following steps in trying to rip the tune will be:

  1. search for machine code that writes values into the SID chip registers
  2. determine the part of the program where most of the code is stored which accesses the SID (most likely the music player or sound fx driver)
  3. search the whole memory for code that branches to the music player area
  4. determine the main subroutines of the music player (most common as the Init and Play addresses)

Unless the ROM and the I/O address space are turned off, the SID chip registers are found at a constant place in memory, i.e. address $D400 to (and including) $D41D. On the contrary, all subsequent chunks of 32-bytes from $D420 to $D800 are mapped to the SID chip, too. But only a very few tunes access the SID chip via these mirrored registers.

So, first we need to find the music player code. We assume the music player uses the absolute addressing mode to access at least one SID register. This is most common, but must not be true for a specific player. Basically, you could search for the appearance of any valid SID address. But starting with the Master Volume and Filter Type Register $D418 next to the Oscillator 1 Control Register $D404 is a good hint. In your monitor enter something like the following at the prompt to search the C64 memory for the appearance of each value:

    > H 0800 D000 18,D4

This searches the memory from address $0800 to $d000 (where the VIC chip starts) for appearance of the consecutive bytes $18 and $D4. Notice the little-endian order of the low and high byte of the 16-bit address word. Please also take into account, that if you want to search other parts of the C64 memory, like the RAM under the ROM $d000 - $FFFF, you first have to enable/disable the desired memory bank. Further, superior monitoring tools are able to search for full valid machine code instructions instead of just hex values. Consider the following output:

    > H 0800 D000 18,D4
    087C 127A 15F4 CFEA 
    > H 0800 D000 04,D4
    117A 1208 13F8 1410 15FE

Searching for alternate $D4?? values would probably also hit the area at $1???. Avoid searching for the SID registers of voice two and three, as most players access these via indices and the SID base address $D400. Since repeatedly searching the C64 memory takes some time, you can narrow down the region once you had a couple of hits. In this example there is most likely some SID sound code between $1000 and $1800. Examining the code in that area might be enough. Keep in mind, that unless you are able to directly search for full valid machine instructions, these hits might only be binary data. Manually searching for possible instructions could be a hint. Consider the instruction STA $D418:

    > H 1000 2000 8D,18,D4
    1279 15F3

Further, keep in mind, that there might be more than one player in memory as well as additional SID code outside a compact music player. Hence you might find your searches to hit two completely distinct memory regions.

Next the often more difficult task of finding the initialization and play addresses of the music player, although the overall procedure is similar:

  • search for the appearance of interrupt vector $0314/$0315 or vector $FFFE
    /$FFFF and their set-up routines; then follow the interrupt handler to find a call into the music player area; likely to be the official player entry
  • track down a decompressor on the stack for the starting address of the program; then follow the code to find a call of a subroutine in the music player area; likely to be the official initialization entry; search the whole program for the number and range of parameters used to initialize the different subsongs or sound effects
  • search for the use of Kernal-ROM subroutines (like CLR-screen) and hardware initialization code; likely at the start of the program; follow it to see what the original code does

Special problems need special treatment:

  • no initialization subroutine available: reset as early as possible or try to write an own one; determine dynamic pointers and indices, which are increased during playback
  • music code and data start inside screen memory $0400 and no hardware module available: insert code into the program to save the data to disk after it was moved into the screen memory; insert code into the program to not let the program move the data, but terminate (reset)
  • multiple init/play addresses
  • separate music players
  • separate init functions for each subsong

A universal timer interrupt handler can be used to verify the found play and init addresses of a ripped sidtune.

Finally, you just need to save the tune to disk. Worth knowing is, that the music data may be either stored in front of or behind the player. Or it is stored at a more distant location. The safest way to search for the true location of the data is to follow the initialization subroutine and try to determine where pointers, arrays, note tables and such, are kept. But generally it is enough to do a quick memory dump of the full player environment, searching for boundaries, which often consist out of a larger number of zero padded bytes. With a bit of experience you will be able to hit the right region just by looking at a hex dump.

Bank switching

Without bank-switching it is nearly impossible for an emulator to determine which memory region should be accessed. There are some sidtunes that do not only use the memory under the address space of the Basic-ROM and Kernal-ROM, but also the memory under the I/O address space. To have all sidtunes in a unique format, it would be preferred to use valid machine code, which would also run on a real C64, except for the PlaySID-compatible sample-player modifications of course.

Please don't neglect bank-switching! It doesn't make the code of a ripped sidtune much longer. Where necessary, it generally takes only 2*12 bytes of additional code in front of the initialization and main player routines.

SIDPLAY's built-in machine code interpreter provides default bank settings for the C64 music player starting addresses:

  • $0000 to $9FFF -> $01 = #$07
  • $A000 to $CFFF -> $01 = #$06
  • $D000 to $DFFF -> $01 = #$00
  • $E000 to $FFFF -> $01 = #$05

The main aim of these default values is to provide a sane initial state prior to executing the music player machine code. Further, these defaults make it possible to jump directly from SIDPLAY to any RAM area without turning off too many components. The single value $05 is not used for all the memory banks because, for instance, it would be necessary to turn back on the Kernal area to use the end-of-irq functions.

The quickest way to add C64 bank-switching code to a sidtune is to append the following code and adjust the initialization and player address accordingly. This example switches off Basic-ROM and Kernal-ROM, but leaves on the I/O address space:

New <init>/<play> subroutine:

    LDX #$35
    STX $01
    JSR <init>/<play>
    LDX #$37
    STX $01

Or something like:

    LDA $01
    LDA #$35
    STA $01
    JSR <init>/<play>
    STA $01

Note: Keep in mind, that some PlaySID-specific rips assume all registers to contain a zero prior to executing the player subroutine (and the X and Y-Register to contain a zero prior to executing the init subroutine). Thus, when modifying those rips to use bank-switching, the additional code does not satisfy the PlaySID-specific assumption. To satisfy such a rip, add LDX #$00, LDY #$00 in front of the music player call.

Variable playing speed

Variable replaying-speed for a song can be set up via CIA 1 Timer A:

  • Upon song initialization or during playback set up the CIA timer speed via the I/O registers $DC04/$DC05.
  • Configure the sidtune info file (or header) to enable timer speed support. On how to do that refer to the documentation on file formats.

Consult your C64 programmers reference book for additional information. The clock speed of a PAL C64 is 985248.4 Hz. The CIA 1 Timer A value is calculated with this formula:

     985248.4 Hz
    ---------------- = timer_value
    song_speed in Hz

    Normal double-speed:
    985248.4 Hz / 100 Hz ~= $267C

Some music composers demand subsequent player calls to be done after a specific number of scan-lines. This way effect updates are affected and, according to the composers, result in better sound. Usually it is wrong to simply call the music player via several JSR instructions one after each other in order to increase the speed of playback. That technique lacks any delays between subsequent player calls.

Without the availability of emulation of scan-line interrupts you can nevertheless achieve an accurate timing by dynamically updating the timer speed for each player call. You can divide the normal speed timer value into small chunks that would equal the system time for one scan-line:

On a PAL system:

    $4CF8 (50 Hz) / 312 scan-lines ~= 63.15 cycles per scan-line
    63.15 * X scan-lines = timer value for one particular
                        player call

Normal double speed example

New <init> subroutine:

    LDX #$26                ; plain 100 Hz
    LDY #$7C
    STX $DC05
    STY $DC04
    LDA #$00
    STA <counter>
    JMP <init>

Timed double speed example

New <play> subroutine for double-speed and $48 scan-lines delay:

    LDA <counter>           ; decide which timer value to set
    INC <counter>
    AND #$01                ; 0=1st or 1=2nd player call
    ASL                     ; two bytes per timer value
    LDA <timer_table>,X     ; $11D4 (duration of $48 scan-lines)
    STA $DC05               ; $3B24 (the rest of the screen)
    LDA <timer_table+1>,X   ; ($11D4 + $3B24 = $4CF8 full screen)
    STA $DC04
    LDA $DC0E               ; read CIA 1 CRA
    ORA #$10                ; set STROBE bit
    STA $DC0E               ; write CIA 1 CRA
    JMP <play>/<play2>      ; maybe call different player entries
Note that the <counter> can be the immediate operand of the LDA instruction (self-modifying code).


A lot of C64 music players contain a built-in instance of an interrupt handler and a corresponding interrupt set-up routine. It was probably used to provide a small test player, making it able to quickly start the music for demonstration purposes. And it often serves as an example of what is required to correctly initialize a song, its speed and where the music player has to be called to produce continous music. A certain simplicity of such a built-in handler provided, it is very easy to convert such a sidtune to SIDPLAY compatible form. Specifying the play address $0000 makes SIDPLAY search and use an installed interrupt vector in either $0314/$0315 or $FFFE/$FFFF.

A universal C64 Timer Interrupt Handler at address $6000 is set up like this and can be used to verify the found play and init addresses of a rip:

;6000    78          SEI                ; disable interrupts

;6001    ad 0e dc    LDA $dc0e          ; load CIA 1 CR A
;6004    29 fe       AND #$fe           ; stop timer A
;6006    8d 0e dc    STA $dc0e          ; write back

;6009    a9 4d       LDA #$4d           ; PAL clock speed / $4D00
;600b    a2 00       LDX #$00           ; ~= 50 Hz
;600d    8d 05 dc    STA $dc05          ; set CIA 1 timer A
;6010    8e 04 dc    STX $dc04

;6013    a9 60       LDA #$60           ; point IRQ vector to
;6015    a2 40       LDX #$40           ; interrupt handler at $6040
;6017    8d 15 03    STA $0315
;601a    8e 14 03    STX $0314

;601d    a9 00       LDA #$00           ; disable VIC interrupts
;601f    8d 1a d0    STA $d01a          ; via IMR

;6022    a5 01       LDA $01            ; load and save bank-select
;6024    48          PHA                ; register on stack

;6025    a9 36       LDA #$35           ; disable Basic-ROM
;6027    85 01       STA $01            ; and Kernal-ROM

;6029    a9 0f       LDA #$0f           ; set SID Master Volume
;602b    8d 18 d4    STA $d418          ; to maximum

;602e    a9 00       LDA #$00           ; select sub-song #1
;6030    20 00 50    JSR <INIT>         ; call music player

;6033    68          PLA                ; restore bank-select
;6034    85 01       STA $01            ; register via stack

;6036    ad 0e dc    LDA $dc0e          ; load CIA 1 CR A
;6039    09 01       ORA #$01           ; start timer A
;603b    8d 0e dc    STA $dc0e          ; write back

;603e    58          CLI                ; enable interrupts
;603f    60          RTS

;6040    a5 01       LDA $01
;6042    48          PHA
;6043    a9 36       LDA #$35
;6045    85 01       STA $01
;6047    20 12 50    JSR <PLAY>         ; call music player
;604a    68          PLA
;604b    85 01       STA $01
;604d    4c 31 ea    JMP $ea31          ; jump to Kernal, EOI()

Or this one which also kills the Kernal run-time system and is located in the default screen address space:

;0400    78          SEI                ; disable interrupts

;0401    a9 05       LDA #$35           ; disable Basic-ROM
;0403    85 01       STA $01            ; and Kernal-ROM

;0405    a9 04       LDA #$04           ; point IRQ vector
;0407    a2 51       LDX #$44           ; to interrupt handler at $0444
;0409    8d ff ff    STA $ffff
;040c    8e fe ff    STX $fffe

;040f    a9 04       LDA #$04           ; point NMI vector
;0411    a2 7d       LDX #$65           ; to RTI instruction at $0465
;0413    8d fb ff    STA $fffb
;0416    8e fa ff    STX $fffa

;0419    a9 00       LDA #$00           ; disable VIC interrupts
;041b    8d 1a d0    STA $d01a          ; via IMR

;041e    ad 0e dc    LDA $dc0e          ; load CIA 1 CR A
;0421    29 fe       AND #$fe           ; stop timer A
;0423    8d 0e dc    STA $dc0e          ; write back

;0426    a9 81       LDA #$81           ; CIA 1 ICR, enable
;0428    8d 0d dc    STA $dc0d          ; underflow timer A

;042b    ad 0f dc    LDA $dc0f          ; load CIA 1 CR B
;042e    29 fe       AND #$fe           ; stop timer B
;0430    8d 0f dc    STA $dc0f          ; write back

;0433    a9 00       LDA #$00           ; select sub-song #1
;0435    20 00 4c    JSR <INIT>         ; call music player

;0438    58          CLI                ; enable interrupts

;0439    ad 0e dc    LDA $dc0e          ; load CIA 1 CR A
;043c    09 01       ORA #$01           ; start timer A
;043e    8d 0e dc    STA $dc0e          ; write back

;0441    4c 41 04    JMP $0441          ; endless jump

;0444    48          PHA                ; save MPU's registers
;0445    8a          TXA 
;0446    48          PHA 
;0447    98          TYA 
;0448    48          PHA 
;0449    a5 01       LDA $01
;044b    48          PHA 
;044c    a9 05       LDA #$05
;044e    85 01       STA $01
;0450    ad 0d dc    LDA $dc0d          ; acknowledge interrupt
;0453    ee 20 d0    INC $d020          ; (or 3x NOP)
;0456    20 89 ec    JSR <PLAY>         ; call music player
;0459    ce 20 d0    DEC $d020          ; (or 3x NOP)
;045c    68          PLA 
;045d    85 01       STA $01
;045f    68          PLA                ; restore MPU's registers
;0460    a8          TAY 
;0461    68          PLA 
;0462    aa          TAX 
;0463    68          PLA 
;0464    58          CLI 
;0465    40          RTI

ROM routines

Avoid any usage of Basic-ROM or Kernal-ROM routines. Not necessarily because SIDPLAY does not include the original ROM images and does not otherwise emulate any ROM routines, but because ROM functions generally imply any sort of I/O usage. A jump into the ROM memory is considered equal to an ``RTS'' instruction. Some specific versions may include faked emulation of a few often used and interrupt related ROM routines. If you don't want to break the compatibility to other SID emulators, don't use these routines either.

Converting sample players

Some sidtunes play back 4-bit samples (or 3-bit, or less precision) by changing the SID's master volume register $D418 in quick succession (other techniques involve pulse width modulation, but are not covered here). This method of playing low-quality samples in sidtunes has been referred to as volume samples, fake samples, or just playback of digis (short form of digitized sounds). Usually, such a music player uses a Non-Maskable-Interrupt (NMI, vectors $0318/19 or $FFFA/FB) next to the standard player subroutine to copy sample values into the master volume register continuously. Sample frequencies of 4 kHz to 8 KHz have been very common.

PlaySID introduced a set of new SID registers, called the Extended Sound Interface Device, which requires a modification of the original C64 sample players in order to activate the samples in SID emulators which don't support emulation of unmodified C64 sample players, but do support PlaySID's extensions. The possibility to modify sample players comes at the cost of the necessity to disable the original machine code routines that would play samples using the SID chip normally.


The modified sidtunes no longer play on a real C64 and neither in any emulator that doesn't support PlaySID's extensions. Once modified, they are emulator-specific. But often it is possible to leave most of the original player untouched and add the modifications in a way that one can re-activate (or re-rip) the old player without much effort. You are advised to keep the unmodified sidtune anyway.

Basically, the main aim of these sample registers has been in aiding PlaySID in directly replaying the C64 samples via one of the AMIGA's audio channels. Using these extended registers is not as flexible and powerful as full-featured interrupt emulation would be. But it allows sample playback at maximum quality (well, sort of :).

SIDPLAY provides compatibility with these registers, but also adds a second set of registers for a second sample channel (at $D500). This section only deals with setting up an examplary sample player for SIDPLAY or PlaySID. If you want more examples, consult the already converted sidtunes, or even better, contact people who are familiar with these PlaySID extensions.

Modify the sample player to do the following once for each sampled waveform you want to start. Usually you must search for that particular part of the original music player which communicates with its sample player interrupt handler (or separate processor loop), and starts a new sample, or modifies the interrupt speed (i.e. sample period). $D41D must be the last register written to.

  1. Set the average master volume in $D418. This is usually around $08 for 4-bit sample players. Find the original value, and don't try to compensate for lack of loudness in the emulation by setting a higher value.
  2. Set the sampling frequency in $D45D/$D45E in number of cycles. This is usually the NMI refresh speed of the sample player interrupt handler, e.g. the CIA 1 Timer A value in $DC04/$DC05.
  3. Set the sample data start address in $D41E/$D41F.
  4. Set the sample data end address in $D43D/$D43E, which is (!) the address of the last byte to be played +1 (end-start = number of samples to be played).
  5. Set the sample data repeat address in $D47E/$D47F.
  6. Set the number of repeats in $D43F: $00 = none, $FF = endless, $01 to $FE = else.
  7. Set the octave in $D45F: $00 = 1st, $01 = 2nd, $02 = 3rd, $04 = 4th, and so on.
  8. Set the sample order in $D47D: $00 = low-nibble/high-nibble (the most common), $01 = high-nibble/low-nibble.
  9. Enable sample replay in $D41D: $FF = 4-bit samples, $FE = 3-bit samples, $FC = 2-bit samples. This depends on what range of values originally was written to the master volume register.
  10. Disable sample replay with $D41D = $FD on demand.

Set all registers newly each time you start a sample. There is no guarantee that any register preserves its value once the sample playback has been started via $D41D. The emulator sets $D41D to zero after is has started a sample. This way it can detect when the next sample is to be started.

Ripping in disassemblings

A powerful way of ripping is to make a disassembling of the complete memory using modern tools, and then search the code listing within a normal text editor. This allows you to search for pieces of text rather than hexadecimal numbers. For instance, you can search for LDA $C70 if you are in search of all LDA-instructions that access memory at $C70?. Or you can search for $D400,X easily. This way way you can even use regular expressions if the editor has them.


Just to mention the availability of other C64 emulators, too, CCS64, PC64, C64S, ALEC64, Frodo, Power64, Win64, A64.