How the block-sync and byte-sync signals of a Commodore 1541 drive work

If, like myself, you decide to delve into the mythical world of disk copy protections for the Commodore 1541 drive, you might find out that useful reference documentation is a tad scattered all over the place.

In fact, I haven’t been able to find a single reference that gives enough elements of electrical theory and the corresponding registers for the second VIA, the one responsible for controlling the drive motor and head of the the 1541 drive, side by side. Therefore I decided to assemble one myself and also share my observations with my readers.

For all topics I also reported the references where you can find further information, as appropriate.

Bear in mind that in WordPress I used the syntax-highlighted code block as it is the only one that seems to preserve the original formatting of text without removing blank spaces. So bear with me if you see blocks with line numbers but no code in them 🙂

Other than that a big thank you goes to Flavioweb for putting up with me and helping with testing and ideas.

Flavioweb also recommended doing research on two additional topics:

  • Why exactly do weird things happen when there are more than three empty bit cells in a track stream (reason for which GCR encoding was introduced), but nothing seems to go wrong if all bit cells are empty in a whole track? I shall leave this one for another article on the very subject.
  • Explain the operations of the erase coil. I shall leave this one for another article when we go through methods to write fat tracks on different 1541 compatible disk drives.

Without further delay, let’s delve into the theory!

The block-sync signal

The /block-sync signal is the output of a NAND port whose inputs are the last ten bits read off the surface of a disk (eight come from an 8-bit serial-to-parallel converter, and two from two additional latches).
It is therefore an active-low signal and it is connected to PB7 (pin 17) of the VIA2, which is mapped to bit 7 at $1C00.

Simply put, the /block-sync signal goes from 1 (logic low) to 0 (logic high) when at least 10 consecutive one bits are read from disk. It also stays high until the first bit 0 comes in.

Here’s a timing diagram:

Decoder clock cycles: |0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|0123|
Under the read head:  |    |I   |    |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |I   |    |I   |    |I   |    |    |I   |    |
Decoded bit:          |0   |1   |0   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |1   |0   |1   |0   |1   |0   |0   |1   |0   |
Bit 7 at $1C00:       |1111|1111|1111|1111|1111|1111|1111|1111|1111|1111|1111|1111|X000|0000|0000|0000|0000|0000|0000|X111|1111|1111|1111|1111|1111|1111|1111|

Legend:
|    | = bit cell (4 decoder clock cycles long, density adjusted: see "Encoder/decoder clock rates per zone" table below)
I = magnetic flux reversal
X = not sure if bit has changed yet

The /block-sync signal is used for framing purposes, i.e. to understand where the first byte of the payload starts.
In fact, what the read circuitry makes available out of the edge detector is a continuous stream of bits and without a synchronization mechanism we would not know how to separate them into individual bytes. The /block-sync signal circuitry was designed to address this synchronization requirement: when the /block-sync signal goes from 0 (logic high) back to 1 (logic low), it means that the first payload bit is coming through the read circuitry and is being assembled into a byte by the 8-bit serial-to-parallel converter.

Because of the above, the first bit of the first payload byte has to be 0. In fact, the payloads in DOS sectors start with either a 0x52 or a 0x55, which are the first eight bits of the GCR equivalent of the block type identifier (0x07 for DATA and 0x08 for HEADER).

In order to wait for a block sync (typically 40 one bits in a sequence instead of just 10) with a 20 ms timeout the 1541 ROM uses the following code:

TIMER1=$1805

DSKCNT=$1C00
DATA2 =$1C01
ERR    JMP ERRR
SYNC
;
        LDA #$80+80     ; WAIT 20 MS FOR SYNC MAX
        STA TIMER1
;
        LDA #3          ; ERROR CODE FOR NO SYNC
;
SYNC10  BIT TIMER1      ; TEST FOR TIME OUT
        BPL ERR
        BIT DSKCNT      ; TEST FOR SYNC
        BMI SYNC10
;
;
        LDA DATA2       ; RESET PA LATCH
        CLV
        LDY #0          ; CLEAR POINTER
        RTS

You can find the above code in the SYNC routine of the 1541 ROM at $F556.
You can also browse a hyperlinked version.

The code that writes block syncs when formatting a disk is as per below:

WRTSYN  LDA #$FF        ; WRITE SYNC
        STA DATA2
;
        LDX #NUMSYN     ; WRITE 5 SYNCS (<- this comment was corrected as it read 4)
;
WRTS10  BVC *
        CLV
;
        DEX
        BNE WRTS10

You can find the above code in the WRTSYN routine of the 1541 ROM at $FCB1.
You can also browse a hyperlinked version.

The reason Commodore engineers used five sync bytes (i.e. 40 one bits) for marking header and data blocks is two-fold. They had to ensure that:

  • enough sync bits would be written correctly for the /block-sync signal circuitry to catch ten of them, and
  • enough sync bits would be present so that the /block-sync signal would stay at 0 (logic high) for long enough to poll its state in the loop at SYNC10 without missing the logic high level due to jitter.

Testing in VICE’s x64sc with a drive wobble value set to 0 suggests that at least 12 (twelve) one bits in each block sync are required for the loop at SYNC10 to reliably catch the /block-sync transition from 1 (logic low) to 0 (logic high). Commodore engineers could probably have got away with 16 or 24 one bits, but I think they were trying to play safe and account for the worst case scenario by choosing 40 one bits.

References:

  • SAMS, 1541 Troubleshooting and repair guide, Mike Peltier, ISBN-10 : 9996563758, ISBN-13 : 978-9996563751:
    • Section 2.4.7 – Read Circuit
    • Section 2.4.8 – Encoder/Decoder Circuit
    • Section 7.2.8 – Read Circuit Electrical Theory
    • Section 7.2.9 – Encoder/Decoder Electrical Theory
  • 1541 source code
  • 1541 hyperlinked ROM listing

The byte-sync signal

When the /block-sync signal transitions from 0 (logic high) to 1 (logic low), the /byte-sync signal is also re-synchronized and will therefore pulse every time eight bits are read from disk.
In practice, the synchronization resets the bit counter circuitry, effectively discarding what the bit count was until that point. Hence the /byte-sync signal can be used to find out whether a full payload byte was read from the disk.

Each read byte is made available on port A of the VIA2, at $1C01. In fact, the output of the 8-bit serial-to-parallel converter is not only hooked into the /block-sync NAND gate but also into port A of the VIA2, which is set as input during disk read operations.

The /byte-sync signal is mapped to the oVerflow flag of the 6502, being routed to its SO pin (pin 38), so its state can be checked by simply checking the state of the oVerflow flag.

The following code shows how the 1541 ROM code waits for a byte to be available and reads it:

DATA2 =$1C01

SRCH25  BVC *           ; WAIT FOR BYTE
        CLV
;
        LDA DATA2

It’s important to clear the oVerflow flag after detecting that a byte is ready, so it can be used for the following byte.

You can find the above code in the SRCH routine of the 1541 ROM at $F510.
You can also browse a hyperlinked version.

It is worth noting that /byte-sync signal has to be activated by setting the CA2 Control bits (bits 1, 2, and 3) in $1C0C to 111. Doing so configures the CA2 pin, which is the source of what is referred to as the byte-sync enable signal, as an output and sets it to a high level.
This is done by the initialization routine in the ROM of the 1541 (see below, under “VIA2 control registers”). The byte-sync enable signal is connected to one of the inputs of a NAND port (another input is the /byte-sync signal itself) so that it can enable or disable the /byte-sync signal.

Finally, it is worth noting that the /byte-sync signal is also routed to the CA1 pin (pin 40) of the VIA2, which is configured to trigger an interrupt on a negative edge in the initialization code of the 1541 (see below, under “VIA2 control registers”).
The corresponding interrupt flag is at bit 7 of $1C0D (VIA2 Interrupt Flag Register). When a negative edge is detected on the CA1 pin, in order for an interrupt request to be generated, the corresponding interrupt has to be enabled, using bit 7 of $1C0E (VIA2 Interrupt Enable Register).

References:

  • SAMS, 1541 Troubleshooting and repair guide, Mike Peltier, ISBN-10 : 9996563758, ISBN-13 : 978-9996563751:
    • Section 2.4.7 – Read Circuit
    • Section 2.4.8 – Encoder/Decoder Circuit
    • Section 7.2.8 – Read Circuit Electrical Theory
    • Section 7.2.9 – Encoder/Decoder Electrical Theory
  • 1541 source code
  • 1541 hyperlinked ROM listing

Encoder/decoder clock rates per zone

We start by noting that 300 rpm equals five rotations per second. In zone 4, every second is 1000000 encoder/decoder clock cycles long, therefore each rotation is 1000000 / 5 = 200000.0 clock cycles long.
Each bit cell is 4 encoder/decoder clock cycles, therefore, upon each rotation, 200000.0 / 4 bits pass under the read/write head in zone 4, that’s 50000.0 bits per rotation.
Equivalently, in one second 1000000 / 4 bits pass under the read/write head in zone 4, that’s 250000.0 bits/s.

For zones 1 to 3, the below properties apply proportionally:

ZoneTracksNominal clock freq (MHz)DivisorBits/rotationClock rate (bits/s)
11-171.2307691361538.4307692.3
218-241.1428571457142.9285714.2
325-301.0666671553333.3266666.7
431-351.0000001650000.0250000.0

References:

  • DATAMOST, Inside Commodore DOS, Gerald G. Neufeld; Richard C. Immers; Diane M. Corralejo, ISBN 10: 0881903663, ISBN 13: 9780881903669:
    • Section 3.1 – Layout of Tracks and Sectors

Block diagram of the encoder/decoder circuit

Block diagram of the encoder/decoder circuit by Luigi Di Fraia
Block diagram of the encoder/decoder circuit

References:

  • SAMS, 1541 Troubleshooting and repair guide, Mike Peltier, ISBN-10 : 9996563758, ISBN-13 : 978-9996563751:
    • Section 2.4.8 – Encoder/Decoder Circuit

VIA2 control register selection

 $1C0C/7180/VIA2+12:   Peripheral Control Register

   +----------+-----------------------------------------------------+
   | Bits 7-5 |   CB2 Control:                                      |
   |          |     000 = Input negative active edge                |
   |          |     001 = Independent interrupt input negative edge |
   |          |     010 = Input positive active edge                |
   |          |     011 = Independent interrupt input positive edge |
   |          |     100 = Handshake output                          |
   |          |     101 = Pulse output                              |
   |          |     110 = Low output                                |
   |          |     111 = High output                               |
   | Bit  4   |   CB1 Interrupt Control: 0 = Negative active edge   |
   |          |                          1 = Positive active edge   |
   | Bit  3-1 |   CA2 Control: see Bits 7-5                         |
   | Bit  0   |   CA1 Interrupt Control: see Bit 4                  |
   +----------+-----------------------------------------------------+

   CA1 (Input):  BYTE-READY
   CA2 (Output): SOE (High = activate BYTE-READY)
   CB2 (Output): Head Mode (Low = Write, High = Read)

Note: Only 110 and 111 are relevant for controlling the operating mode of the pins CA2 (pin 39) and CB2 (pin 19) of the VIA2 in a 1541.

Hence, in the 1541 ROM, the initialization code is as per below:

CNTINT  LDA #%01101111  ; DATA DIRECTION
        STA DDRB2
        AND #$FF-$08-$04-$03 ; TURN MOTOR OFF,SET PHASE A, LED OFF
        STA DSKCNT
;
;
        LDA PCR2        ; SET EDGE AND LATCH MODE
        AND #$FF-$01    ; NEG EDGE PLEASE
;
;
; CA2: SOE OUTPUT HI ENABLE S.O. INTO 6502 (<- this comment was corrected as it read DISABLE)
;
        ORA #$0E
;
;
; CB1 INPUT ONLY
;
; CB2 MODE CONTROL R/W
;
        ORA #$E0
        STA PCR2

You can find the above code in the CNTINT routine of the 1541 ROM at $F556.
You can also browse a hyperlinked version.

It’s worth noting that:

  • AND #$FE ; Set CA1 to trigger on the negative edge of the incoming /byte-sync signal
  • ORA #$0E ; Activate the /byte-sync signal, which in turn is connected to the oVerflow flag of the 6502, through the SO pin (pin 38)
  • ORA #$E0 ; Set head mode to read

References:

About Luigi Di Fraia

I am a Senior DevOps Engineer so I get to work with the latest technologies and open-source software. However, in my private time I enjoy retro-computing.
This entry was posted in Retrocomputing, Reverse Engineering, Technical and tagged , , , , . Bookmark the permalink.

8 Responses to How the block-sync and byte-sync signals of a Commodore 1541 drive work

  1. Zibri says:

    Reading your posts it is incredible how you are following step-by-step the same path me and a friend of mine “walked” about 6 months ago. I started by doing the same analys and then went on finding some more obscure things about the 1541. Together we also debunked a few misconceptions and a lot of confusion (even in the commented sources) for example about density (which is a consequence and not a setting, since you can set a framing clock). Very well written article. If you need a had, let me know (even if I know that finding things by yourself is what is fun in all this)

    • Thanks ZIbri. I reckon you know how I feel. And yes, it’s fun to go through this sort of details, in preparation for what comes next.
      I’ve also been partly debunking misconceptions, e.g. the fat track protection in my previous post, and I don’t exclude debunking more in future!

  2. Sébastien Mametz says:

    Thank you for this nice and accurate article.

    I’m facing an issue with interrupts on VIA2 CA1 pin. Schematics show that this pin is connected to BYTE_SYNC. As you write :

    “Finally, it is worth noting that the /byte-sync signal is also routed to the CA1 pin (pin 40) of the VIA2, which is configured to trigger an interrupt on a negative edge in the initialization code of the 1541 (see below, under “VIA2 control registers”).”

    While writing an emulator, I found out that VIA1 CA1 pin is configured to rise an interrupt on ACK rising edge, interrupt enabled at ($EB32 – $EB37 of the reset routine). And in the initialization of the disk controller, all VIA2 interrupts are disabled ($F281 – $F283), but the timer 1 timeout interrupt, enabled at ($F286 – $F28B). Could you please point out where you found in rom the setting of VIA2 CA1 interrupt enable ?

    • > “Could you please point out where you found in rom the setting of VIA2 CA1 interrupt enable ?”

      I haven’t looked for it. I just wrote that “When a negative edge is detected on the CA1 pin, in order for an interrupt request to be generated, the corresponding interrupt has to be enabled, using bit 7 of $1C0E (VIA2 Interrupt Enable Register).”

  3. Pingback: Encoding details for Vorpal later: fully documented | Luigi Di Fraia's e-Footsteps

  4. Pingback: Luigi Di Fraia's e-Footsteps

  5. Nynfus says:

    I assume byte_n is also asserted when the first 8 bits of a sync is detected, or if there are 8 1’s in the middle of a sector, right?

Leave a comment