Error detection and correction (EDC/ECC)

Optical discs are read with a laser through scratched, dusty, fingerprinted plastic. Raw read errors are routine. What makes the media usable is several layers of error handling, each built on the one below. This page explains every layer. It gives exact parameters and working algorithms for the sector-level codes that image files store, because a converter needs those to verify a dump or regenerate a raw sector from 2048 bytes of user data.

Layer Applies to Where it's stored Handled by In image files?
1. CIRC (C1 + C2 Reed–Solomon, interleaved) every CD (audio and data) inside the EFM frames the drive, always never
2. EDC (CRC-32) CD Mode 1, Mode 2 Form 1 (and optionally Form 2) sector bytes 0x810 / 0x818 / 0x92C drive (cooked reads) or software yes, in raw (2352-byte) sectors
3. ECC (Reed–Solomon product code, "P" and "Q" parity), a.k.a. L-EC CD Mode 1, Mode 2 Form 1 sector bytes 0x81C–0x92F drive (cooked reads) or software yes, in raw sectors
– Q subchannel CRC-16 Q bytes 10–11 drive / software when subchannel is stored
DVD: IED, EDC, RS-PC (PI/PO) every DVD sector ID field, data frame, ECC block the drive, always no (images store only the 2048 user bytes)

Some terminology first:

  • Detection means knowing that data is wrong. Correction means repairing it.
  • An erasure is an error whose position is known (for example, because a lower layer flagged it). Reed–Solomon codes can correct twice as many erasures as unknown-position errors.
  • A Reed–Solomon code written RS(n, k) turns k data symbols into n symbols by adding n − k parity symbols. It can correct up to ⌊(n − k)/2⌋ symbol errors, or n − k erasures. On optical discs a symbol is one byte, in the finite field GF(2⁸) generated by the polynomial x⁸ + x⁴ + x³ + x² + 1 (0x11D).

1. Why so many layers? #

Audio can tolerate an occasional uncorrectable error: the player interpolates (estimates a missing sample from its neighbours) or briefly mutes, and nobody notices. Program code cannot tolerate a single wrong byte. So:

  • the Red Book (audio) defined only CIRC, plus interpolation as the last resort;
  • the Yellow Book (CD-ROM) added a second, independent layer for data sectors (EDC + ECC) on top of CIRC. Everything CIRC leaves behind gets another chance;
  • DVD was designed for data from the start and uses one strong product code instead.

2. Layer 1: CIRC #

CIRC (Cross-Interleaved Reed–Solomon Code) protects every 24-byte F1 frame (encoding §2):

  1. Delay and scramble. The 24 bytes are split into even and odd samples, which are delayed relative to each other by 2 frames. Neighbouring audio samples are therefore never damaged together, which makes interpolation possible.
  2. C2 encoder: RS(28, 24). Adds 4 parity bytes (called "Q" parity in the CD standard; not to be confused with the Q subchannel or the sector-level Q parity below).
  3. Cross-interleave. The 28 bytes are spread out by delay lines of 0, 4, 8, … 108 frames, so each byte of a C2 code word ends up in a different F2 frame, up to 108 frames (about 1/7 second of audio) away.
  4. C1 encoder: RS(32, 28). Adds 4 parity bytes ("P" parity) over 28 bytes that are now consecutive on the disc.
  5. A final 1-frame delay on alternate bytes and inversion of the parity bytes.

Decoding runs in reverse. C1 catches small random errors (it can correct up to 2 bytes per frame) and marks the rest as erasures. After de-interleaving, a long burst has been spread into one or two bytes per C2 word, which C2 can correct as erasures (up to 4 per word). If C2 fails, the drive can report C2 error pointers for the affected bytes (MMC READ CD with C2 flags; 294 bytes per sector, one bit per byte).

Rough capability: CIRC corrects bursts of about 3,500 bits (≈ 2.4 mm of track). With interpolation, audio can conceal bursts of about 12,000 bits (≈ 8.5 mm).

CIRC is entirely inside the drive. No image format stores CIRC parity. "Raw" 2352-byte sectors are what remains after CIRC decoding.


3. Layer 2: EDC (CD sectors) #

The EDC is a 32-bit CRC over the start of a data sector. It lets the drive, or software, check whether the ECC correction step produced a correct sector. It cannot locate or fix errors itself.

3.1 Parameters #

Property Value
Polynomial P(x) = (x¹⁶ + x¹⁵ + x² + 1) · (x¹⁶ + x² + x + 1) = x³² + x³¹ + x¹⁶ + x¹⁵ + x⁴ + x³ + x + 1
Normal form 0x8001801B
Reflected form (for LSB-first table) 0xD8018001
Bit order reflected (least-significant bit first)
Initial value 0
Final XOR none
Storage 4 bytes, little-endian

This is not the familiar CRC-32 used by zip/Ethernet. The polynomial is different, there is no initial 0xFFFFFFFF, and there is no final inversion.

3.2 Coverage #

Sector type EDC covers EDC stored at
Mode 1 0x000–0x80F (sync + header + 2048 data) 0x810
Mode 2 Form 1 0x010–0x817 (subheader + 2048 data) 0x818
Mode 2 Form 2 0x010–0x92B (subheader + 2324 data) 0x92C (optional; 00 00 00 00 means "not calculated")
Mode 0, Mode 2 formless, audio – –

3.3 Implementation #

/// CD-ROM EDC (ECMA-130 §14.3). Returns the value to store little-endian.
fn cd_edc(data: &[u8]) -> u32 {
    let mut edc: u32 = 0;
    for &b in data {
        edc ^= b as u32;
        for _ in 0..8 {
            edc = (edc >> 1) ^ if edc & 1 != 0 { 0xD801_8001 } else { 0 };
        }
    }
    edc
}

// Mode 1:          let e = cd_edc(&sector[0x000..0x810]); sector[0x810..0x814].copy_from_slice(&e.to_le_bytes());
// Mode 2 Form 1:   let e = cd_edc(&sector[0x010..0x818]); sector[0x818..0x81C].copy_from_slice(&e.to_le_bytes());
// Mode 2 Form 2:   let e = cd_edc(&sector[0x010..0x92C]); sector[0x92C..0x930].copy_from_slice(&e.to_le_bytes());

A test value, computed with this algorithm (which matches libmirage's table-driven version): a Mode 1 sector at LBA 0 (header 00 02 00 01) with 2048 zero bytes of user data has EDC 0x2B6813C5, stored as C5 13 68 2B.

For speed, real implementations use a 256-entry lookup table built from the same reflected polynomial. libmirage's mirage_helper_init_crc32_lut(0xD8018001, …) builds one; it also has a "slicing" variant that processes several bytes per step.


4. Layer 3: ECC (CD sectors): the P and Q parity #

The 276 bytes from 0x81C to 0x92F hold a Reed–Solomon Product-like Code (ECMA-130 calls it RSPC; it is also called L-EC, layered error correction). It is made of two interleaved sets of short RS codes laid over the sector like a grid.

4.1 The grid #

Take the 2064 bytes from 0x00C to 0x81B: header (4) + user data (2048) + EDC (4) + zero (8) for Mode 1. Number them 0..2063 and view them as 1032 16-bit words (each word = an MSB byte and an LSB byte) arranged in 24 rows × 43 columns. The MSB bytes and the LSB bytes form two independent byte planes, each protected separately. That is why the code is described in terms of 86 "columns" of bytes (43 words × 2 planes).

          col 0     col 1     ...   col 42
row 0     w0        w1              w42           (word = 2 bytes; byte index = 2*word + plane)
row 1     w43       w44             w85
...
row 23    w989      ...             w1031
row 24    P parity (43 words = 86 bytes)          ← computed first
row 25    P parity (43 words = 86 bytes)

4.2 P parity: 86 × RS(26, 24) over columns #

Each column (24 bytes in one byte plane) gets 2 parity bytes, making an RS(26, 24) code word. 86 columns × 2 bytes = 172 bytes of P parity, stored at 0x81C–0x8C7. They form rows 24 and 25 of the grid.

4.3 Q parity: 52 × RS(45, 43) over diagonals #

The grid is now 26 rows × 43 words (2236 bytes, data + P parity). Each diagonal (start at a row, then step one row down and one column right, wrapping around) holds 43 bytes per plane. There are 26 diagonals × 2 planes = 52 of them. Each gets 2 parity bytes, making an RS(45, 43) code word: 52 × 2 = 104 bytes of Q parity, stored at 0x8C8–0x92F.

Because every byte is in one P code word (column) and one Q code word (diagonal), a decoder can iterate: fix what P can, then what Q can, then P again, and so on. Between them they correct errors that neither could correct alone. Afterwards the EDC confirms whether the result is right.

4.4 Mode 2 Form 1: the header is zeroed #

For Mode 2 Form 1, the 4 header bytes (0x00C–0x00F) are set to zero while computing P and Q parity, and put back afterwards. The grid is still the same 2064 bytes starting at 0x00C; for Form 1 it holds subheader (8) + data (2048) + EDC (4) instead of data + EDC + zero. The parity is therefore independent of the sector's address. Mode 1 includes the real header.

Mode 2 Form 2 sectors have no P/Q parity at all.

4.5 Implementation #

libmirage's routine (derived from Neill Corlett's ECM tool) is a compact way to compute both parities. It treats the bytes from 0x00C as one array and walks it with different strides:

/// GF(2^8) helper tables, generator x^8 + x^4 + x^3 + x^2 + 1 (0x11D).
/// F[i] = i * 2 in GF(256).  B[i ^ F[i]] = i  (used to solve for the 2 parity bytes).
fn ecc_tables() -> ([u8; 256], [u8; 256]) {
    let mut f = [0u8; 256];
    let mut b = [0u8; 256];
    for i in 0..256usize {
        let j = ((i << 1) ^ if i & 0x80 != 0 { 0x11D } else { 0 }) as u8;
        f[i] = j;
        b[i ^ j as usize] = i as u8;
    }
    (f, b)
}

/// Computes one parity layer (P or Q) over `src` (= sector[0x00C..]) into `dest`.
/// P: major_count=86, minor_count=24, major_mult=2,  minor_inc=86  → dest = sector[0x81C..0x8C8]
/// Q: major_count=52, minor_count=43, major_mult=86, minor_inc=88  → dest = sector[0x8C8..0x930]
fn ecc_block(f: &[u8; 256], b: &[u8; 256], src: &[u8],
             major_count: usize, minor_count: usize, major_mult: usize, minor_inc: usize,
             dest: &mut [u8]) {
    let size = major_count * minor_count;
    for major in 0..major_count {
        let mut index = (major >> 1) * major_mult + (major & 1);
        let (mut ecc_a, mut ecc_b) = (0u8, 0u8);
        for _ in 0..minor_count {
            let temp = src[index];
            index += minor_inc;
            if index >= size { index -= size; }
            ecc_a ^= temp;
            ecc_b ^= temp;
            ecc_a = f[ecc_a as usize];
        }
        ecc_a = b[(f[ecc_a as usize] ^ ecc_b) as usize];
        dest[major] = ecc_a;
        dest[major + major_count] = ecc_a ^ ecc_b;
    }
}

Note that the Q pass reads the P parity just written: src for the Q pass spans 0x00C..0x8C8, which is 52 × 43 = 2236 bytes. A complete Mode 1 regeneration looks like this:

fn regenerate_mode1(sector: &mut [u8; 2352], lba: i32) {
    const SYNC: [u8; 12] = [0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
    sector[0..12].copy_from_slice(&SYNC);
    let abs = (lba + 150) as u32;                       // see lba.md
    let bcd = |v: u32| (((v / 10) << 4) | (v % 10)) as u8;
    sector[12] = bcd(abs / 4500);
    sector[13] = bcd((abs / 75) % 60);
    sector[14] = bcd(abs % 75);
    sector[15] = 0x01;                                  // mode 1
    // user data is already at 0x010..0x810
    let edc = cd_edc(&sector[0x000..0x810]);
    sector[0x810..0x814].copy_from_slice(&edc.to_le_bytes());
    sector[0x814..0x81C].fill(0);
    let (f, b) = ecc_tables();
    let (head, tail) = sector.split_at_mut(0x81C);     // P: read 0x00C.., write 0x81C..
    ecc_block(&f, &b, &head[0x00C..], 86, 24, 2, 86, &mut tail[..172]);
    let (head, tail) = sector.split_at_mut(0x8C8);     // Q: read 0x00C..0x8C8, write 0x8C8..
    ecc_block(&f, &b, &head[0x00C..], 52, 43, 86, 88, &mut tail[..104]);
}

For Mode 2 Form 1, save bytes 0x00C..0x010, zero them, compute P and Q exactly as above, then restore the header. The EDC is computed over 0x010..0x818 as in §3.2.

ecc_block only computes parity. Actually correcting errors with it needs a full Reed–Solomon decoder (syndromes, error locator, Forney). Drives do that in hardware. Software usually only needs to compute parity, for regenerating and verifying sectors.

4.6 What these codes are used for in practice #

  • Verifying a raw dump: recompute EDC (and optionally ECC) for every data sector. A mismatch means a read error, or deliberate damage.
  • Converting cooked → raw: an .iso or MODE1/2048 track can be expanded to 2352-byte sectors exactly, because sync, header, EDC and ECC are all functions of the address and the data. (Mode 2 Form 1/Form 2 also needs the subheader, which a 2048-byte image doesn't keep. Converters usually assume Form 1 with a zeroed subheader.)
  • ECM compression ("Error Code Modeler", .ecm) strips EDC/ECC from sectors where it can be regenerated, and keeps it only where it doesn't match. That typically saves about 12% of a raw image.
  • Copy protection. Some schemes (e.g. SafeDisc, some SecuROM versions, and others) master sectors with intentionally wrong EDC/ECC and check that the drive reports a read error there. A cooked image, or a careless conversion that "fixes" the sector by regenerating EDC/ECC, breaks the protection. Only a raw image (BIN, IMG, MDF at 2352 bytes) preserves it.

5. Q subchannel CRC #

The Q subchannel has its own 16-bit CRC (CCITT 0x1021, inverted). It only detects errors. Details and code are on the Subchannels page. It is mentioned here for completeness: a dump with mismatched Q CRCs either has read errors in the subchannel or deliberately corrupted Q data (PlayStation LibCrypt, some SecuROM).


6. DVD: RS product code #

DVD uses one large, strong code per ECC block of 16 sectors instead of a stack of smaller layers. (Frame layout and modulation are on the encoding page.)

Component Code Purpose
IED (ID Error Detection) RS(6, 4) over the 4-byte ID Lets the drive trust a sector's address (PSN) before decoding the whole ECC block, which is essential for seeking
EDC CRC-32, polynomial x³² + x³¹ + x⁴ + 1, over ID + IED + CPR_MAI + main data (2060 bytes) Final check on each decoded sector
PO (Parity Outer) RS(208, 192) down each of 172 columns 16 bytes per column; corrects up to 8 errors (or 16 erasures) per column
PI (Parity Inner) RS(182, 172) along each of 208 rows 10 bytes per row; corrects up to 5 errors per row, and flags rows it can't fix as erasures for PO

The 16 PO rows are interleaved, one into each of the 16 sectors, so a long burst removes at most a row or so from each code word.

Decoding order: PI (rows) first, marking failed rows as erasures; then PO (columns) using those erasures; optionally iterate. This lets DVD correct bursts of roughly 6 mm of track, far more than CD's CIRC, while spending about 13% of the channel on parity (37,856 bytes recorded for 32,768 user bytes, including headers).

Disc-quality tools report DVD error rates as PI errors (PIE, rows needing correction), PI failures (PIF, rows PI could not fix), and PO failures (POF, uncorrectable, meaning lost data). For CD they report C1 and C2 errors and CU (uncorrectable).

The drive handles all DVD error correction internally. No image format stores DVD parity, IED or EDC: a DVD image is just the 2048-byte main data of each sector (after descrambling). Protections that depend on DVD-level errors can only be represented by side-channel information, such as a list of bad sectors.


Sources #