Sometimes while doing SID music covers I wish I had a way of playing SID tunes with a slow speed. That would be very helpful to guess notes better, especially when a tune goes at a fast pace. So, yesterday I decided to make a fast and simple player by using BASIC language. This is not always adviceable for many reasons, including the fact that a tune may require a memory arrangement where BASIC is not present. BASIC intepreter speed is also of great concern. But, with some tunes it will work just fine.
I am currently studying the amazing song “Monty on the Run” by Rob Hubbard, so the choice of which tune to be used with the player was obvious.
The first thing to do is to obtain the rip of the music. All game musics are avaliable on the High Voltage Sid Collection, and a SID file just contains all the data we need to play a song in our own player.
A SID file is not a proper music file. It is rather a music program with music data. More programs and more data may be available on a single SID file (for example, there are SID files with various tunes and sound effects).
We need three information:
- the initialization routine address;
- the play routine address;
- where music data is (including music routines).
The initialization routine is called once just before playing the tune. It takes care of initialization tasks, like resetting the starting point of music data and selecting which tune to play (in case of a SID file with multiple tunes).
The play routine is usually called via a raster interrupt, every 1/50th of a second on PAL C64 machines. An interrupt handler must be set up, so that when the rasterbeam reaches a given raster line, an interrupt is triggered and the play routine is executed.
Let’s have a look at the Monty on the Run SID file with a hex editor:
From the “SID file format description”, we know that “loadAddress” can be found starting from position +7C. Low byte and high byte of this address are selected in the above picture.
So, the starting location for music data is $8000 (32768 decimal). This is the location the file containing music data and music routines must be loaded from on Commodore 64 memory. So, we must select all the bytes from position +7C till the end of the file, and paste them on a new hex editor file (monty data.prg). This file MUST contain the loading address in the low byte / high byte format at the very beginning.
At this point, we can think of the file “monty data.prg” as the song to be played.
Looking back at the .SID file, we can easily obtain the initialization routine address: it can be found starting from position +0A. It is expressed in the high byte / low byte format, so the init address is $9554 (38228 decimal).
As for the play routine address, it can be found starting from position +0C. It is also given in the high byte / low byte format, so the play routine address is $8012 (32786 decimal).
I was in a hurry and I decided to write just a simple BASIC program that would allow me to play this famous theme at a very slow rate on certain points, so that I could easily guess the musical notes even on those “running” parts.
At first, I just set up things the easier way. I saved on a .d64 1541 disk file image the file “monty data.prg”: this can be loaded by the BASIC player program and then played.
Then, to make things clean, I created a onefiled version.
Let’s have a look at the BASIC code.
Line 12 makes use of the old-school technique of loading data from inside a BASIC program. This actually takes advantage of the chaining feature. When a BASIC program is loaded and run within a BASIC program, BASIC variables are not deleted. This was useful for old PET computers: since you only had 8K of RAM, you could split your program in two parts, and load the second part when needed. By using this technique, the second program could make use of the variables created by the first program. On our program, since we are loading data, the already existing BASIC program is run after loading issued from within the program itself it’s complete.
The first time you RUN the BASIC program, F variable is 0. When the program is runned from within the program itselft, F contains 1, so no loading happens again and the BASIC program continues.
On line 35, please note that before calling the init routine, a 0 is stored on the accumulator. Rather, it is stored on location 780 decimal. When a SYS call is performed, the value contained in this location is passed to the accumulator. So, just before initialization the accumulator is zeroed. On this case, that means: “play song one”, the main theme.
The BASIC program calls the Play Routine roughly each 1/50th of a second by using the instruction WAIT 53265,128. As we know, it waits the rasterbeam to be in the non visible area. This is the way we can make synchronization with the rasterbeam in BASIC. The variables R and Q are used in that statement to gain speed.
The code may seem odd, but it has been taylored so that the tune can be played with exact or nearly 1X speed. That asks for short loops in the code. No more than three different keys can be read on the 1X playing loop, otherwise the song will slow down even when it should be at normal speed.
Using redundant loops like this allows for better performances of the program with less conditional code. Also at higher speeds, the song is played with no unwanted speed changes. And key response is better than by using “rolled” code. That’s why I used several SYS PL instructions instead of a SYS PL inside an iteration.
Using the player is easy. The “S” key slows down the song. If you press it more times, the song will be slower… until another press will set the song to its normal speed.
Pressing F plays the song fast. Pressing F more times plays it even faster. While the song is playing faster, pressing S puts it back at normal speed. This key acts as a handy fast/forward. Sadly, the rewind function would be very difficult to implement in general and it is just impossible with BASIC.
Due to code efficiency reasons, current playing speed is not displayed. But, while the song is played slowly, pressing F and then S sets it quickly to normal speed. That somehow compensates for that.
Pressing P makes a pause. At this stage, if you press R the song will be restarted. I had to put this option here in order not to slow down things. Pressing P again resumes playing.
The little delay caused by holding down the space bar is just an added bonus from BASIC slowness.
RUN/STOP + RESTORE will allow you to exit the program.
The .d64 version allows for easier modifications of the BASIC program, should you need to fiddle around with delay values to change playing speeds.
To create the one-filed version, I merged the BASIC program and the song data file with the hex editor, then I used the cross-platform tool “Exomizer” to dramatically reduce file size.
I am learning to play this song with my music keyboard. I think the ability to play it slow will help. I may also come up with a .sid cover of this song in the future.