Unless I'm reading the manual incorrectly, the following should produce $11 when D404 = $10 (I'm turning on the gate for voice1 which has been previously set to triangle wave %00010000/$10)

00153  5198  A9 01                 LDA #01
00154  519A  0D 04 D4              ORA D404
00155  519D  8D 04 D4              STA D404

So %00010000 OR %00000001 = %00010001 right?

But in debugger, you can see the accumulator is staying as %00000001. It's loading $01 in to the accumulator, D404 = $10, and current step is AFTER the ORA line... LDA still = #01 so my STA will store the wrong result

3 Answers 3


I think your ORA D404 instruction is trying to read from the SID registers, which is not possible. You can't rely on the value read from that address. Register 04 is write-only.

If you want to do read-modify-store on a SID register, you will need to keep a copy of the SID registers in RAM. That way, you load the old value from RAM, do your operations, and then write the new value to both the RAM and the SID.

From the SID datasheet, retrieved from https://www.waitingforfriday.com/?p=661 . Notice that almost all the registers are write-only. (Also a good list is this Commodore 64 memory map reference https://sta.c64.org/cbm64mem.html)

enter image description here

  • 1
    Thanks. I'll keep that in mind: i can see a few tutorials showing writing directly to d404 etc but maybe that method you describe is safer? In the end i think it was just a debugger error... The code is "kinda" working now (well, broken for a different reason lol) i'm going to delete the question (on seconds thoughts maybe not)
    – Simon
    Oct 10, 2021 at 7:02
  • 7
    Your question is fine. :) You can write directly to D404, but you can't read from it. So if you want to toggle GATE with xor for example, you have to store the last value you wrote to D404 in RAM as well.
    – knol
    Oct 10, 2021 at 7:02
  • 1
    apologies i missread your response... so the reads are the problem hmm OK
    – Simon
    Oct 10, 2021 at 7:22
  • 1
    hurray, problem solved... Thanks heaps. Sorry, frustration make brain no work :D
    – Simon
    Oct 10, 2021 at 7:45
  • 1
    @Simon: As you dive deeper into peripherals, you will often see they show unexpected behaviours. You always have to study the datasheet carefully.
    – Janka
    Oct 10, 2021 at 20:53

Other answers note that most SID chip registers are write-only, but didn't really go into why.

When connecting storage elements to a bus, separate circuitry is required to read and write them. If a device has many storage elements whose state need not be made available to outside electronics (as would be the case for a RAM), all of the storage elements can be placed close together and each equipped with a "select" signal, and one can then use a single set of read circuitry and a single set of write circuitry to handle accesses to the entire array. If the state of each storage element needs to be forwarded continuously to outside circuitry which is supposed to do something with it, however, practicality will often dictate that each storage element be placed near the circuitry it controls, which would effectively preclude the sharing of read-write circuitry. Some devices attach read-write support circuitry to every register, but in the 1980s where signals generally had to be routed between active circuits rather than over them, the cost of doing so would have often been significant. Some device designers tolerated the cost, while others avoided it.

In some ways, the selection of read registers on the SID is a bit surprising. If the SID had included a simple software-controlled DAC that would be mixed with the final output, along with a single-bit analog comparator to determine if the output with the DAC mixed in was above or below a particular threshold, a single-bit read register from the analog comparator would have made it possible to write a software-only test program that could confirm the functionality of everything else out to the final output amplifier stage. If e.g. one of the bits in the channel 1 frequency output control wasn't working, a test program could recognize that the SID wasn't producing the correct pattern of highs and lows coming back on the comparator. From what I can tell, the SID's readable registers would be insufficient to determine whether any of the channel 0 and channel 1 registers are working.

I suspect that the readback feature for channel 2's output was intended for use in chip development, so that if the channel hardware design had a problem it would be possible to see what was going on. If e.g. the carry chain in the frequency generation circuitry was unreliable, being able to program a sawtooth wave and watch whether the values count in consistent order would be easier than trying to figure out what's going on based upon the output signal alone.

Further, if the designers of the SID had more time available, they could have saved chip area by using a time-shared architecture which would have used the same circuitry to read and write the registers from all four voices. This would have reduced the amount of circuitry required to support full readback of all registers, thus making it more likely that readback would be supported. Indeed, depending upon the design chosen, the simplest way to allow registers to be updated at arbitrary times in a time-sharing system might have been to use a small RAM to hold them, and have on-chip logic use half of each system clock cycle to read the settings for the current voice and half of each cycle to expose the RAM to the CPU. Getting such a system working, however, would probably have required more effort than getting a single-voice tone generator working and plopping on the chip as many copies as would fit, and given the time constraints that wasn't practical.

  • "I suspect that the readback feature for channel 2's output was intended for use in chip development" - that's speculation of course, but FWIW, already in the preliminary datasheet it was advertised as a feature for generating random numbers or providing a modulation source.
    – Retrograde
    Oct 12, 2021 at 17:52
  • @Retrograde: Marketing is weird. Using the readback as an RNG may be handy, but doesn't offer any huge advantage over an LFSR implemented in software, and use for modulation is hampered by the lack of any way to directly set a channel's amplitude. When I first started programming the C64, I thought the ADSR envelopes were cool, but I've since come to the conclusion that simply having an 8-bit amplitude register for each channel would have been more useful when playing music in anything other than BASIC.
    – supercat
    Oct 13, 2021 at 19:22
  • I think the RNG use case has a lot of merit, it's continuously running out of the box and trivial to read even from BASIC. Being able to read out the current envelope value seems more obscure. When doing music and play routines on the C64 I also missed a way to directly control each voice amplitude, however without the automatic envelopes it would be hard to achieve consistent timing of an envelope without burning clock cycles as the envelopes needs updating a lot faster than say the frame rate, which were the typical update unit for play routines.
    – Retrograde
    Oct 13, 2021 at 21:18
  • @Retrograde: The RNG use case requires that one forego use of channel 3 for anything other than a high-frequency noise waveform. As for envelopes, music programs for a lot of systems perform attack/decay envelope shaping using once-per-frame updates and they sound fine. Otherwise, hardware envelope support would have been more useful if the four 4-bit values were replaced with two eight-bit ones: a target amplitude and a slope rate, with a rate value of 0 causing amplitude adjustments to be applied immediately.
    – supercat
    Oct 13, 2021 at 21:44

A lot of memory-mapped I/O is NOT read-write.

You are imagining the I/O port is like a byte of memory that you could read and write.

No it's not. (unless the manufacturer spends some extra silicon to make it so).

For instance, I'm just making up a hypothetical here, but A808-9 might be

  • A808-9 reading: value 0-255 for analog joystick #1 input, X and Y axis, respectively
  • A808 writing: bits 0-3 select video mode, bit 7 select NTSC/PAL
  • A809 writing: pause system clock until horizontal blank, value irrelevant

See? They have absolutely nothing to do with each other, you don't want to read a joystick value and set your video mode to that!

In the Commodores, particularly...

They were notorious for doing stuff like this. As a result, for several registers, it was more efficient to just store a local copy of what you were setting the register to. For instance

 LDA F4     ; get our mirror of port D404 from zero page
 ORA #01    ; set bit 0 
 STA F4     ; save our mirror
 STA D404   ; set port to match mirror

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .