A project about playing C64-era "SIDtunes" with a Teensy4, a 6502 processor and a SID (clone) chip
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
So based on the past two logs, i decided to revisit the Debugger and take a closer look at the C64 Schematics. To figure out the relationship of the CIA timers and the VIC-II
In the schematic i can indeed see that the IRQ input has three potential sources: The CIA1 chip, the VIC-II chip and the Expansion slot. So it would seem that the Vertical Blanking Interval is used at times as a Interrupt source.
I went into the debugger and started running a variety of SID files and note the interrupt configurations.
NAME | TYPE | NOTES |
2400AD | RealSID | Raster Interrupt enabled |
Amazing Discoveries | RealSID | T1B and T2A active. Raster enabled |
A waste of space | RealSID | Same as Amazing Discovers |
Hi Fi Sky | RealSID | Same, |
My Life | RealSID | Same |
$11 Heaven | PlaySID | Raster Interrupt enabled |
Crystal Dawn | PlaySID | Raster Interrupt enabled |
Echofied 6581 | RealSID | Raster Interrupt enabled, 1A,1B and 2A active. |
Haven | PlaySID | 4x, 1A configured as IRQ source (4x as fast) |
Rotten Soul | PlaySID | 6x. 1A configured as IRQ source. (6x as fast) |
Age We Aceed | PlaySID | 4x. 1A configured as IRQ source (4x as fast). |
So. Turns out that during my initial looking into behaviour, i did not pay attention to the VIC-II's behaviour and just assumed everything was done via the timers. It was not. Apparantly depending on the SID file used. One can expect either:
This complicates things a little. As it means besides the CIA timers. The VIC's Raster interrupt needs to be handled as well.
Brief look at the VIC IRQ.
In practice. Without looking to actually output video. The Raster Interrupt can be seen as another timer. Though compared to others. This timer just runs non-stop up to 0x0137 (311) with the IRQ output turned on/off as needed.
Messing with the specific raster interrupt number while it is running in a debugger does not seem to cause any odd behaviour outside of graphics shifting and no active reading of the raster value has been noted for music itself. So it does not appear to require cycle exact behaviour, Functioning mostly as a periodic interval timer whose exact timing changes depending on the video format (NTSC/PAL).
In regards of knowing which one to activate. The SID format file specifies "Default" settings for both PSID and RSID, which combined with the header can allow for accurately configuring a player in advance. Any modifications are expected to be performed by the SID file itself during initialization.
Good thing this was figured out sooner, rather than later. Could have been a major headache later on!
YES. I know its been two years since my last blog, but this project ain't dead. I was just busy dealing with College and bagging myself a Bachelor in Electrical Engineering. Which i now hold!! So hopefully i can revive and work on this project proper from now on!!
The High Voltage SID collection stores the SID code in specialized .sid files. These files consist of a Header, followed by the actual 6502 Machine code. For proper playback a player needs to read the header in order to figure out what kind of SID music is being played and how it has to be prepared. Luckily the archive comes with a file format text file that goes through all the data that can be found in the header. For convenience i compressed it into a table with the relevant fields and their offset from the start.
+00 | four character String containing either "PSID" or "RSID". Informs the player what kind of file is being dealt with. |
+04 | 16-bit field containing Version of 1 to 4. Depending on the version, there may be more data in the header. |
+06 | 16-bit field containing the DataOffset. Points the where the machine code portion begins. Should be static depending on the version |
+08 | 16-bit field containing the LoadAddress. This is the location in the RAM from which the machine code has to be loaded into. If the LoadAddress is 0. The first two bytes of the machine code are expected to indicate LoadAddress. |
+0A | 16-bit field containing the initAddress. This is the address of the data subroutine that must be run by the processor to start playback. Special note is that when the initAddress is called the file will switch song based on the number stored in the accumulator register. If the initAddress is 0. The LoadAddress is expected to double as init. |
+0C | 16-bit field containing the playAddress. The address that is to be called repeatedly for music to play. Normally the address called by the IRQ handler. If 0. The code is expected to set up the interrupt service handler itself during init. |
+0E | 16-bit field containing the number of songs. Specifies the number of songs available in the file. Used to know what kind of accumulator values would be accepted when calling the initAddress Is always at least 1 (never 0) |
+10 | 16-bit field containing the default Starting Song. Specifies the default song to be played one load |
+12 | 32-bit field containing the Speed setting. Each bit corresponding with a song number and indicating whether the playback is based on the vertical blank interrupt (50hz/60hz based on PAL or NTSC) if bit=0 and based on the CIA1 timer interrupt (60hz) if bit=1 |
+76 | Starting point of Data for Version 1 SID files. |
This is the extent of data found in V1 headers and for basic operation of old PSIDs it is enough, but it does lack a bit in regards of specifics. For example it doesn't tell you whether the file is meant for a PAL or an NTSC device, which has dramatic effects on how it sounds. Other versions (2,3 and 4) include an expansion on the header which provides more data:
+76 | A 32-bit "Flags" value. Stores bitwise data: b0 : Indicates built-in player [0] or Compute! player [1] b1 : Specifies C64 compatible PSID [0] or PlaySID specific/C64 Basic [1] b2-3: Specifies Video standard: 01=PAL, 10=NTSC, 11=Both, 00=Unknown b4-5: Specifies SID chip used: 00=unknown, 01=6581, 10=8580, 11=both b6-7: Specifies second chip. same as above. Only used in Version 3 and 4 b8-9: Specifies third chip. Same as above. Only used in Version 4 Rest is reserved and should be set to 0 |
+78 | 8-bit field containing startPage Indicates if there is free space for a music driver. If 0. Anywhere outside of the data block is accessible. If 0xff: No space exists. Anything else indicates the high-bytes of a memory page that has space. |
+79 | 8-bit field containing pageLength This informs how many pages of memory are available from the address indicated by the startPage. Every page is a byte of... |
Before i could get started it was imperative that i looked at the golden target. The playing of RSIDs and what makes them so special compared to the regular SID music referred to as PSIDs. Most projects around SID playback make assumption on how they could do it that as far as i know: never panned out only ever playing PSID. My hunch is that RSID playback has something to do with the "Complex Interface Adapter" (CIA) chips. These are massive chips in charge of general purpose I/O, but also Timing via a pair of Periodic Interval Timers (PIT1 and PIT2). These are also what control the processor's interrupts (CIA1->IRQ, CIA2->NMI)
To investigate this I used a "C64 Debugger". A Variant of the popular VICE emulator that shows information about the peripheral chips like the SID, CIA and VICs, a live memory map and can be run clock by clock if desired. I then proceeded to load up various SID files including a couple RSIDs and observe how they use the C64 hardware.
By default the C64 is configured with a regular IRQ signal coming from CIA1-PIT1. This IRQ goes of at a frequency of 50hz. Most PSID use this regular IRQ to call the "PlayRoutine" (an address that when jumped to starts the routine for the next step in playback) 50x per second. Some PSIDs deviate and reconfigure the CIA1-PIT1 to instead trigger IRQ more often. The other Timer and CIA2 are left alone.
RSIDs though start to use more elaborate tricks. With the SID chip they start to manipulate the volume register at times to pull off PCM sample playback. Occasionally using the NMI connected CIA2 or even forgoing the timers altogether and just running at full speed. Then I got to play "Hi-Fi Sky" by LMAN which showed the most devious exploit I had seen thus far.
This one did this weird thing where CIA1 was not creating any IRQ, but had its second timer running at an incredibly small interval while the first was standing still at a specific value. The processor kept passing by it. On closer inspection it turned out that the first timer's value matched a JMP opcode. The processor was reading the registers of the first timer which had part of a JMP command. The last byte it read for the target address being the low-byte of Timer2 that was constantly changing. The RSID was using the CIA1 Timers a a time-sensitive JMP Table!!
This is why most players failed and why you had to use a "Cycle Exact" emulator. Even when using a genuine SID chip, most players only assumed the IRQ would get used for a regular signal and any offset in timer value would break the playback if you didn't do it exactly.
So if i want a real chance at playing all the RSIDs. I need to figure out how the substitute the CIAs Timers. Which will be the subject of a future log.
Create an account to leave a comment. Already have an account? Log In.
Become a member to follow this project and never miss any updates
By using our website and services, you expressly agree to the placement of our performance, functionality, and advertising cookies. Learn More