0

There are various calculations we do while working with boot sector of a floppy, like

XOR DX,DX
DIVW 0x7C18
INC DL
MOV 0x7C3B, DL
XOR DX, DX
DIVW 0x7C1A
MOV 0x7C2A,DL
MOV 7C39,AX
RET

This is just a sample of code, as obvious there is much more calculations as we go through the code, we find various DIV and MUL instructions, My question is what we are trying to calculate, and how it will be calculated, Thanks

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • What syntax is this? Is `0x7C18` a memory operand, like `[0x7C18]` in NASM syntax? I assume `divw` is word operand-size `div`, like AT&T syntax. But this must have destination on the left, because mov-immediate or load into DL would make no sense right before xor-zeroing it. Anyway, it's just dividing something by 2 different divisors, and storing the 1-byte remainders. (The first one incremented). Also the quotient of the second one. Single-step it in a debugger, and/or look up the instructions in an instruction-set reference like https://www.felixcloutier.com/x86/ – Peter Cordes Jul 27 '22 at 03:30
  • Hi Peter, thanks for your reply, as I already mentioned that this is just a portion of the code, many instructions written by me may be with improper syntax, here I just want to know that by using instructions like these what we are trying to calculate and how will it be calculated. As i guess it may calculate floppy disk geometry, but same question again what and how it will be calculated. – Shankar Tiwari ji Jul 27 '22 at 03:42
  • 1
    If you wrote these instructions, what did you *intend* them to calculate? You seem to know how DIV works, since you zeroed DX (the high half of the dividend) before each one, to do a normal 16/16 => 16-bit division. And yeah, certainly could be a geometry calculation; that would make sense of dividing twice and taking the remainder at each step. (Where the divisors are memory operands at `0x7C18` and `0x7C1A`.) https://www.felixcloutier.com/x86/div / [Why should EDX be 0 before using the DIV instruction?](https://stackoverflow.com/q/38416593) – Peter Cordes Jul 27 '22 at 04:03
  • 1
    I am curious if any answers helped you or solved your problem. I see you are relatively new to Stackoverflow and you've asked question (about half a dozen) but haven't accepted any answers. You may be unaware that there is an "accept an answer" feature that lets future readers know it has been solved. More on how (and why) to do that can be found here: https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work – Michael Petch Jul 30 '22 at 20:57

2 Answers2

2

That specific piece of code is converting an LBA address into a CHS address.

An LBA address (or Linear Block Address) is where each sector on the disk is given a number from 0 to max. (e.g. from 0 to 2879 for a 1.44 KB floppy disk that has a total of 2880 sectors). A CHS address ("Cylinder, Head, Sector") is closer to how a floppy disk works mechanically (and is used by old BIOS disk functions) but is less convenient for higher level software.

The calculation is:

sector = LBA % sectors_per_cylinder + 1
head = (LBA / sectors_per_cylinder) % total_heads
cylinder = (LBA / sectors_per_cylinder) / total_heads

With common sub-expression elimination this becomes:

sector = LBA % sectors_per_cylinder + 1
temp = LBA / sectors_per_cylinder;

head = temp % total_heads
cylinder = temp / total_heads

What we are trying to calculate by various mathematical instructions in boot sector code?

For other calculations, it's impossible to guess - I'd expect a few more pieces for disk IO (e.g. calculating how many sectors are needed from the size of a file in bytes); maybe some calculations for memory management (aligning things to page boundaries, converting real mode "segment:offset" into 32-bit physical addresses); maybe some for keeping track of time, ...

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • @Shankar I tried to reverse-engeneer [MS-DOS boot sector procedure](https://euroassembler.eu/probin/boot16.htm) and completed it with comments at the label **LBAtranslate**, which is a procedure that converts LBA from `DX:AX` to the floppy geometry. – vitsoft Jul 27 '22 at 06:11
2

The code translates the Logical Block Address (LBA) passed in register AX and converts it to Cylinder/Head/Sector (CHS). I explain the general calculation in an answer to a related Stackoverflow question in the section Translation of LBA to CHS.


Translation of LBA to CHS

Definitions:

LBA is the logical block address
HPC is the maximum number of heads per cylinder (reported by 
    disk drive, typically 16 for 28-bit LBA)
SPT is the maximum number of sectors per track (reported by
    disk drive, typically 63 for 28-bit LBA)


MOD is the modulo operation, i.e. the remainder
÷   is integer division, i.e. the quotient of the division
    where any fractional part is discarded

What you need is a proper LBA to CHS conversion routine. Since you will need such a function for different aspect of navigating FAT12 file structures it is best to create a function. We'll call it lba_to_chs.

Before we write such code we should revisit the equation earlier:

C = LBA ÷ (HPC × SPT)
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1

We could implement this as is, but if we rework the equation for cylinders we can reduce the amount of work we have to do. C = LBA ÷ (HPC × SPT) can be rewritten as:

C = LBA ÷ (HPC × SPT)
C = LBA ÷ (SPT × HPC)
C = (LBA ÷ SPT) × (1 ÷ HPC)
C = (LBA ÷ SPT) ÷ HPC

If we now look at the revised formula we have:

C = (LBA ÷ SPT) ÷ HPC
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1

Now we should notice that (LBA ÷ SPT) is duplicated in two places. We only have to do that equation once. As well since x86 DIV instruction computes the remainder and quotient at the same time we also end up computing LBA mod SPT for free when we do (LBA ÷ SPT). The code would follow this structure:

  1. Compute LBA DIV SPT . This yields:
    • (LBA ÷ SPT) in the quotient
    • (LBA mod SPT) in the remainder
  2. Take the remainder from step (1) and put in temporary register
  3. Add 1 to the temporary in step (2). That register now contains the sector as computed by S = (LBA mod SPT) + 1
  4. Take quotient from step (1) and divide by HPC.
    • Cylinder number will be the quotient
    • Head will be the remainder.

We have reduced the equation down to a couple DIV instructions and an increment/add. We can simplify things more. If we assume we are using well known IBM Compatible Disk formats then we can also say that Sectors per Track (SPT), Heads(HPC), Cylinder, Head, and Sector will always be less than 256. When the maximum LBA on any well known floppy disk format is divided by SPT the result will always be less than 256. Knowing this allows us to avoid bit twiddling the top two bits of the cylinder and placing them in the top two bits of CL. We can also use DIV instructions that do 16-bit by 8-bit unsigned division.


Your assembly code is very similar in nature.

Function inputs:

  • The parameter LBA for the function is in AX
  • Word at address 0x7C18 is the Sectors Per Track stored in the disk's BIOS Parameter Block (BPB).
  • Word at address 0x7C1A is the Number Of Heads as stored in the BPB.

Function outputs (after the calculations):

  • Byte at address 0x7C3B is where the computed Sector # is stored.
  • Byte at address 0x7C2A is where the computed Head # is stored.
  • Word at address 0x7C39 is where the computed Cylinder # is stored.
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Michael Petch
  • 46,082
  • 8
  • 107
  • 198