Logical Block Addressing (LBA)
Sector addresses come in several flavours: MSF times, logical block addresses, DVD physical sector numbers, and byte offsets inside image files. They look similar and are off by fixed amounts from one another, so mixing them up is the most common source of off-by-150 (or off-by-0x30000) bugs. This page defines each one and shows how to convert between them.
1. Units: sectors, frames and blocks #
- A sector is the smallest addressable unit of a disc (see encoding).
- On a CD, a sector is 1/75 s of playing time. In time notation it is called a frame: "frame" in
mm:ss:ffmeans a whole sector. It is not the 588-bit small frame from the encoding page. - Block is the SCSI/MMC word for the same thing. Logical block emphasises that it is the unit the host addresses.
On a CD a block is 2352 bytes raw (2048 bytes of user data for Mode 1). On a DVD it is 2048 bytes.
2. MSF: minutes, seconds, frames #
CDs grew out of audio, so their native address is a time: mm:ss:ff, where
mm= minutes (0–99),ss= seconds (0–59),ff= frames (0–74), 75 per second.
Converting a time to a sector count is simple arithmetic:
sectors = (mm × 60 + ss) × 75 + ff
mm = sectors / 4500 ss = (sectors / 75) % 60 ff = sectors % 75
Example: 12:07:45 = (12 × 60 + 7) × 75 + 45 = 54,570 sectors.
There are two different MSF times on a CD:
- Absolute time (A-time; AMIN/ASEC/AFRAME in Q): counted from the start of the program area. Data sector headers carry this time, and drives seek with it.
- Relative time (MIN/SEC/FRAME in Q): counted within a track, from its index 1. It counts down through the pregap. Only players use it.
When a document just says "MSF address", it means absolute time.
On the disc (in data sector headers and in the Q subchannel) MSF values are BCD: each decimal digit takes one nibble, so 59 is stored as 0x59. In MMC command responses and in most image descriptors they are plain binary (59 = 0x3B). The MDS and CCD formats store binary values. The CUE format stores them as decimal text.
3. LBA: logical block address #
An LBA is just a sector number. It is what an operating system or file system uses (ISO 9660 extents are LBAs), and what MMC READ commands take.
For CDs, LBA 0 is defined to be absolute time 00:02:00, the normal start of track 1's data. The 150 sectors (2 seconds) before it are track 1's mandatory pregap and are addressed as LBA −150 to −1:
LBA = (mm × 60 + ss) × 75 + ff − 150 (for mm < 90; see §4)
MSF of LBA n: total = n + 150; mm = total / 4500; ss = (total / 75) % 60; ff = total % 75
| Absolute MSF | LBA | What's normally there |
|---|---|---|
00:00:00 |
−150 | Start of program area; track 1 pregap (index 0) |
00:01:74 |
−1 | Last pregap sector |
00:02:00 |
0 | Track 1, index 1. ISO 9660's "logical sector 0" |
00:02:16 |
16 | ISO 9660 Primary Volume Descriptor |
74:00:00 |
332,850 | Typical end of a 74-min disc |
79:59:74 |
359,849 | Last sector before 80:00:00 |
Why the 2-second offset? The Red Book requires every disc to begin with a 2-second pause, and data standards chose not to give user data addresses inside it. Many image formats begin at LBA 0 and do not store the first 150 sectors. A reader re-creates them as silence or zero-filled sectors.
4. Negative addresses and the 90-minute rule #
The lead-in comes before 00:00:00, so its sectors need negative LBAs. MSF has no sign. Instead, the Q subchannel of the lead-in counts up through MSF values in the 90s and wraps from 99:59:74 to 00:00:00 where the program area begins. The convention (used by MMC and by libmirage) is:
Any MSF with minutes ≥ 90 is treated as negative: subtract 100 minutes (450,000 sectors).
if mm >= 90: LBA = (mm × 60 + ss) × 75 + ff − 150 − 450000
libmirage's helpers, in lib/cdemu/libmirage/mirage/utils.c:
gint mirage_helper_msf2lba (guint8 m, guint8 s, guint8 f, gboolean diff) {
gint lba = (m*60+s)*75 + f;
if (diff) lba -= 150; // "diff" = absolute address, apply the 2-second offset
if (m >= 90) lba -= 450000; // lead-in: wrap negative
return lba;
}
void mirage_helper_lba2msf (gint lba, gboolean diff, guint8 *m, guint8 *s, guint8 *f) {
if (diff) lba += 150;
if (lba < 0) lba += 450000;
*m = lba/(75*60); *s = (lba/75) % 60; *f = lba % 75;
}
The diff flag separates addresses, which need the ±150, from lengths, which don't. Forgetting this distinction is a classic bug.
Examples:
| MSF | Interpretation | LBA |
|---|---|---|
99:59:74 |
last lead-in sector | −151 |
97:26:10 |
inside a typical lead-in | −11,690 |
90:00:00 |
earliest possible lead-in time | −45,150 |
This is also why discs longer than about 90 minutes are trouble. The program area of a 99-minute disc runs into minute values that drives may interpret as negative lead-in addresses.
CloneCD's ALBA and PLBA fields are these LBAs, and can be negative (an ALBA of −150, or lead-in values, are common). Aaru's CloneCD writer uses a slightly different wrap rule (if lba > 405000 then lba = −(lba − 405000 + 300)). Readers should accept both conventions.
5. Track-relative, session-relative and file-relative addresses #
Several formats store addresses relative to something other than the start of the disc:
| Where | Address type | Zero point |
|---|---|---|
| Q subchannel MIN/SEC/FRAME | relative time | index 1 of the current track (counts down in the pregap) |
CUE INDEX nn mm:ss:ff |
file-relative sector count, written as MSF | start of the current FILE. No 150 offset: INDEX 01 00:00:00 is byte 0 of the file |
CUE PREGAP / POSTGAP |
length | n/a |
MDS "PLBA" (data block 24h) |
absolute LBA | 00:02:00 |
| MDS session start/end | absolute LBA | 00:02:00 (first session starts at −150) |
MDS track start offset (28h) |
byte offset in the MDF | start of the MDF file |
CCD PLBA, ALBA |
absolute LBA (from PMSF/AMSF) | 00:02:00 |
CCD [TRACK n] INDEX k |
LBA | 00:02:00 (libmirage notes it may be session-relative on later sessions) |
| ISO 9660 extent locations | logical sector number | LBA 0 in practice. On multisession discs the newest volume descriptor is at (start of the last session's first track + 16), but extents in it are absolute disc LBAs, so files from earlier sessions can still be referenced |
The CUE case trips people up. A CUE time of 03:20:15 does not mean absolute time 03:20:15. It means "sector 15,015 of this file". For a single-file BIN whose first track starts at byte 0, the absolute LBA equals that file sector number if the file includes every pregap. Any PREGAP lines (gaps not stored in the file) push later tracks further out on the disc than their file position suggests. See BIN/CUE.
6. From LBA to a byte offset in an image file #
Each format needs its own formula. With S = stored sector size in bytes:
| Format | Byte offset of LBA n |
|---|---|
.iso |
n × 2048 (or n × S for raw ISO variants) |
.img (CloneCD), single session |
n × 2352 (the file starts at LBA 0 and includes all later pregaps) |
.sub (CloneCD) |
n × 96 |
.img, multisession |
as above, but subtract the lead-out + lead-in + pregap gap(s) between sessions, which are not stored |
.bin (single file, all gaps stored as INDEX 00) |
n × S if every track has the same S. Otherwise sum length × S track by track |
.bin (one file per track) |
(n − track_start_lba_in_file) × S within that track's file |
.mdf |
track.start_offset + (n − track.PLBA) × S for the track containing n (S includes 96 bytes when subchannel is stored) |
The MDF formula is the reliable one, because the MDS gives every track's start offset explicitly. Do not assume the MDF is a contiguous dump from LBA 0: some pregaps are omitted (see MDS/MDF).
7. DVD addresses: PSN and LBA #
DVD sectors carry a 24-bit Physical Sector Number (PSN) in their ID field (encoding §6.1). It is binary, not BCD. The data area of layer 0 begins at PSN 0x030000, which is LBA 0. Lead-in sectors have smaller PSNs.
Single layer, or layer 0 #
LBA = PSN − 0x30000 PSN = LBA + 0x30000
Parallel track path (PTP) dual layer #
Each layer has its own data area starting at 0x030000. Layer 1 sectors are told apart by the layer bit in the ID's sector information byte. LBAs continue from the end of layer 0.
Opposite track path (OTP) dual layer #
Layer 1's PSNs are the 24-bit complement of layer 0's at the same radius. If layer 0's data area ends at E0 (stored in the PFI as "end sector of layer 0"):
L0_sectors = E0 − 0x30000 + 1
L1_first_PSN = ~E0 & 0xFFFFFF (= 0xFFFFFF − E0)
For LBA n:
if n < L0_sectors: PSN = n + 0x30000 (layer 0)
else: PSN = L1_first_PSN + (n − L0_sectors) (layer 1)
Worked example, Empire Earth III (values from the PFI in its MDS; see MDS/MDF):
| Quantity | Value |
|---|---|
| Data area start | 0x030000 |
End of layer 0, E0 |
0x151CDF |
| Layer 0 sectors | 0x151CDF − 0x030000 + 1 = 0x121CE0 = 1,187,040 |
| Layer 1 first PSN | 0xFFFFFF − 0x151CDF = 0xEAE320 |
| End of data area (last L1 PSN) | 0xFCFFEF |
| Layer 1 sectors | 0xFCFFEF − 0xEAE320 + 1 = 0x121CD0 = 1,187,024 |
| Total (LBAs 0 … 2,374,063) | 2,374,064 |
| Layer break | between LBA 1,187,039 and LBA 1,187,040 |
| Check: last LBA → PSN | 0xEAE320 + 1,187,023 = 0xFCFFEF ✓ |
The layers differ by 16 sectors (one ECC block). In an image the sectors are simply stored in LBA order, layer 1 straight after layer 0. The layer break position is metadata, kept in the PFI (MDS) or in a sidecar file (.dvd files used with ImgBurn and similar tools). A plain ISO loses it.
8. How the host asks for an address (MMC) #
- READ(10)/READ(12)/READ CD take a 32-bit LBA. READ CD also accepts negative LBAs, which lets some drives read the track 1 pregap (−150..−1) and sometimes a little of the lead-in.
- READ CD MSF takes absolute MSF start and end times (binary, not BCD).
- READ TOC/PMA/ATIP returns addresses either as LBAs or as MSF (the "MSF" bit in the command picks which). Formats 0 and 1 give track start addresses. The Full TOC format always gives MSF (binary) values, the same values MDS and CCD store.
- For DVD, READ DISC STRUCTURE returns the PFI with data area start, end and end-of-layer-0 as 24-bit PSNs (stored in 4-byte fields).
9. Quick reference #
sectors(m,s,f) = (m*60 + s)*75 + f
LBA(abs MSF) = sectors − 150, and − 450000 more if m ≥ 90
abs MSF(LBA) = from (LBA + 150), adding 450000 first if negative
CUE INDEX time = file-relative sector count; no 150
DVD LBA(PSN, L0) = PSN − 0x30000
DVD OTP L1 start PSN = 0xFFFFFF − end_of_layer0_PSN
74 min = 333,000 sectors; 80 min = 360,000 sectors; 99:59:74 = 449,999 sectors
Sources #
- ECMA-130, §14.2 (sector address in header, absolute time) and Q subchannel time definitions. https://ecma-international.org/publications-and-standards/standards/ecma-130/
- ECMA-267, §3 / §16 (PSN, data area start
30000h, OTP numbering). https://ecma-international.org/publications-and-standards/standards/ecma-267/ - T10 MMC-6 draft: LBA ↔ MSF conversion table (including the negative range), READ CD, READ TOC MSF bit. https://www.t10.org/drafts.htm
- libmirage
lib/cdemu/libmirage/mirage/utils.c(mirage_helper_msf2lba,mirage_helper_lba2msf). - Aaru
lib/Aaru/Aaru.Images/CloneCD/Write.cs(ALBA/PLBA wrap rule). - Wikipedia, "Logical block addressing" (general background): https://en.wikipedia.org/wiki/Logical_block_addressing