by Daniel Rowell Faulkner

This is a (very) simple LBA to CHS tutorial. I assume that you know how to Add, Subtract, Divide and multiply and that you know what Assembly is and possibly the basics of it. (though I go over the basics to remind you in case you have forgotten)

Brief idea of what the physical drive is like:

A normal floppy drive (which I will use for this example) contains 2 main parts.
Sector: The area on the disk
Cylinder: aka Track, one circle at the same radius from the center.
Head: The top or bottom side of the disk? (In hard disks you have multiple magnetic disks) Also it is the head that contains the mechanism to read and write to the disk.

So to address any part of the drive you have to say:
1. Top or bottom
2. How far to move the head from the center
3. How far to move the disk round.

Ok so what is LBA?

LBA is logical addressing of your physicial drive.
Or in simple terms you refer to sectors on the floppy or hard disk as 1,2,3,4,5,.....

Ok so what is CHS?

CHS is the method the drive uses to load sectors from the floppy or hard disk.
This composes of a Sector, Cylinder, Head.


If you are anything like me you will shudder at the notion of maths. In order to understand this you will have to know at least basic division, which is not to hard.

If you look at most sites they will give you this set of formulas:
Sector = (LBA mod SectorsPerTrack)+1
Cylinder = (LBA/SectorsPerTrack)/NumHeads
Head = (LBA/SectorsPerTrack) mod NumHeads

Is there a different way?

Well this is the only formula which works so don't go looking for any other way to do this, as an easier doesn't exist.

So what does it all mean?

mod stands for modulus in the formulas I presented.
/ stands for divide.

An easy way I've decided to look at the mod's is as points to take the remainder value rather than the quotient value.

Lets re-write this taking that into effect:
Sector = (LBA/SectorsPerTrack) Remainder value + 1
Cylinder = (LBA/SectorsPerTrack)/NumHeads (Take Remainder value)
Head = (LBA/SectorsPerTrack)/NumHeads (Take quotient value)

(The plus 1 on the sectors is because you need a sector to read at least, else you won't be reading anything if theres a 0 anywhere, for some reason they don't start at 0 or something like that, but miss it out and out and you will have interesting things happen)

Ok still confused?

LBA/Sectors Per Track:
Quotient - NumberOfTracks     Remainder + 1 - Sector

But you can't just have the number of tracks that has to be broken down into cylinders and heads!

Number Of Tracks/Number Of Heads:
Quotient - Cylinder     Remainder - Head

Cylinder is half the number of tracks, head value will alternate (0,1 on a floppy) with each increase of the NumberOfTracks value.

Ok I don't think I can make the formulas any simpler I'm afraid. So I'm going to move on to the next section.

The vague algorithm:

(SPT = Sectors Per Track)
(All divides return: Quotient, Remainder in this algorithm)

Assembly Language introduction:

ASM terms:
Registers - In simple terms memory/variables of a fixed size in the CPU
   - In the code I'm writing the size is 16 bytes for each register.

ASM commands used:
The DIV command is very important in this function.
DIV ; Divides register AX by the register you enter as reg
   ; Output value AX = Quotient DX = Remainder.
The MOV command is simple but used often.
MOV ; Move the Source to the destination.
The INC command is again simple but is needed.
INC ; Adds 1 to the register you pass to the command.
The XOR command is mostly used for turning a register to zero's. It compares each bit of two registers and if they are set the same outputs zero as the result.
XOR ax, ax ; This will zero ax (will work with any register)
The RET command is used to return to the main program.
RET ; Returns to the main program
PUSH ; Puts the register's value onto the stack
POP ; Restores a registers value using the value on the stack

In order to learn more about Asm I suggest looking at the Art Of Asm website (The location changes and this may not be updated regularly so best to look for it in a search engine) The book 'Assembly Language Step By Step' By Jeff Duntemann is also very usefull for beginners to Assembly Language programming.

And to find out more about the commands I put up look at the Intel Reference manual, also the NASM documentation has a reference section. (Quite likely similar documentation is also around else where)

Simple Assembly example:

(This is not likely to work if you cut and paste it and is here to show the principle only, look at the next section for a complete example)

; Set up the registers ready for the divide
MOV ax, [LBAvalue]	; []'s means value at memory location LBAvalue.
; Make the divide
DIV [SectorsPerTrack]	; Carry out the division of ax.
; Put the returned Number Of Tracks some where
MOV [NumTracks], ax	  ; Put the quotient into a memory variable
; Sort out the sector value
INC dx		      ; Add 1 to the remainder
MOV [Sector], dx     ; Put the remainder into a memory variable

; Set up the registers ready for the divide
MOV ax, [NumTracks]    ; Put the number of tracks in to ax
; Make the divide
DIV [NumHeads]		; Divide NumTracks (ax) by NumHeads
; Stash the results in some memory locations
MOV [Cylinder], ax     ; Quotient value, the Number of heads to be moved from ax
MOV [Head], dx	       ; Remainder value, the cylinder value to be oved from dx

Advanced Assembly example:

(This will most likely work, but like any of my code I can't say for certain, being to lazy to test it at the time of writing this tutorial)

; Compile using NASM compiler (Again look for it using a search engine)
; Input: ax - LBA value
; Output: ax - Sector
;	  bx - Head
;	  cx - Cylinder

 PUSH dx			; Save the value in dx
 XOR dx,dx		; Zero dx
 MOV bx, [SectorsPerTrack]	; Move into place STP (LBA all ready in place)
 DIV bx			; Make the divide (ax/bx -> ax,dx)
 inc dx			; Add one to the remainder (sector value)
 push dx			; Save the sector value on the stack

 XOR dx,dx		; Zero dx
 MOV bx, [NumHeads]	; Move NumHeads into place (NumTracks all ready in place)
 DIV bx			; Make the divide (ax/bx -> ax,dx)

 MOV cx,ax		; Move ax to cx (Cylinder)
 MOV bx,dx		; Move dx to bx (Head)
 POP ax			; Take the last value entered on the stack off.
			; It doesn't need to go into the same register.
			; (Sector)
 POP dx			; Restore dx, just in case something important was
			; originally in there before running this.
 RET			; Return to the main function

I hope this has helpped anyone with LBA to CHS translation.

If this has helpped you please send me an e-mail saying so. (I like compliments)

If you want to see new things in here please say, if you want to translate this into an other language please send me the new version so I can host that as an alternative. (I can translate copy's of this if requested but the altavista translater isn't quite perfected for large documents like this, and I would rather spend my time working on something else)

This tutorial is here with the permission of Daniel Faulkner, if you wish to put this tutorial on another site, please contact Daniel for permission.
&copy All Rights Reserved Bona Fide OS development 2001-2006. We Disclaim responsibility for all bad things, good things okay.


Report issues via Bona Fide feedback.

2001 - 2024 © Bona Fide OS Development | The Goal | Contributors | How To Help |