Why is my root directory not loading? (FAT12)

I am writing a stage 1 bootloader in the assembly with which I am trying to load the FAT12 file system into memory so that I can load my stage 2 bootloader. I managed to load the FAT into memory, but I struggle to load the root directory into memory.

I am currently using this for reference and have produced the following:

.load_root: ;es is 0x7c0 xor dx, dx ; blank dx for division mov si, fat_loaded ; inform user that FAT is loaded call print mov al, [FATcount] ; calculate how many sectors into the disk must be loaded mul word [SectorsPerFAT] add al, [ReservedSectors] div byte [SectorsPerTrack] mov ch, ah ; Store quotient in ch for cylinder number mov cl, al ; Store remainder in cl for sector number xor dx, dx xor ax, ax mov al, ch ; get back to "absolute" sector number mul byte [SectorsPerTrack] add al, cl mul word [BytesPerSector] mov bx,ax ; Memory offset to load to data into memory after BOTH FATs (should be 0x2600, physical address should be 0xA200) xor dx, dx ; blank dx for division mov ax, 32 mul word [MaxDirEntries] div word [BytesPerSector] ; number of sectors root directory takes up (should be 14) xor dh, dh ; head 0 mov dl, [boot_device] ; boot device mov ah, 0x02 ; select read mode int 13h cmp ah, 0 je .load_OS mov si, error_text call print jmp $ 

However, if I check the memory in 0xA200 with gdb, I just see 0. There is a file in my root directory - I put a file called OS.BIN in the root directory for verification.

Using info registers in gdb after a read operation gives the following output:

 eax 0xe 14 ecx 0x101 257 edx 0x0 0 ebx 0x2600 9728 esp 0x76d0 0x76d0 ebp 0x0 0x0 esi 0x16d 365 edi 0x0 0 eip 0x7cdd 0x7cdd eflags 0x246 [ PF ZF IF ] cs 0x0 0 ss 0x53 83 ds 0x7c0 1984 es 0x7c0 1984 fs 0x0 0 gs 0x0 0 

The operation status is 0, the number of sectors read is 14, and es:bx points to 0xA200, but x/32b 0xa200 shows 32 0 when I expect to see data for OS.BIN.

EDIT I did info registers before the interruption, and I got the following output:

 eax 0x20e 526 ecx 0x101 257 edx 0x0 0 ebx 0x2600 9728 esp 0x76d0 0x76d0 ebp 0x0 0x0 esi 0x161 353 edi 0x0 0 eip 0x7cc8 0x7cc8 eflags 0x246 [ PF ZF IF ] cs 0x0 0 ss 0x53 83 ds 0x7c0 1984 es 0x7c0 1984 fs 0x0 0 gs 0x0 0 

The same as after, except that the function request number has been replaced by a status code.

Where am I going wrong? Am I reading from the wrong CHS address? Or some other simple mistake? And how can I fix this?

I use fat_imgen to create a disk image. The command to create the disk image is fat_imgen -c -f floppy.flp -F -s bootloader.bin , and the command to add OS.BIN to the image is fat_imgen -m -f floppy.flp -i OS.BIN


I have a BIOS parameter block (BPB) that represents a 1.44 MB diskette using FAT12:

 jmp short loader times 9 db 0 BytesPerSector: dw 512 SectorsPerCluster: db 1 ReservedSectors: dw 1 FATcount: db 2 MaxDirEntries: dw 224 TotalSectors: dw 2880 db 0 SectorsPerFAT: dw 9 SectorsPerTrack: dw 18 NumberOfHeads: dw 2 dd 0 dd 0 dw 0 BootSignature: db 0x29 VolumeID: dd 77 VolumeLabel: db "Bum'dOS ",0 FSType: db "FAT12 " 

I have another function that works, which loads the FAT12 table at memory address 0x7c0: 0x0200 (physical address 0x07e00):

 ;;;Start loading File Allocation Table (FAT) .load_fat: mov ax, 0x07c0 ; address from start of programs mov es, ax mov ah, 0x02 ; set to read mov al, [SectorsPerFAT] ; how many sectors to load xor ch, ch ; cylinder 0 mov cl, [ReservedSectors] ; Load FAT1 add cl, byte 1 xor dh, dh ; head 0 mov bx, 0x0200 ; read data to 512B after start of code int 13h cmp ah, 0 je .load_root mov si, error_text call print hlt 
+5
source share
2 answers

I ended up losing the root directory after loading FAT. In the end, I modified my .load_fat procedure to load both the FAT and the root directory at the same time (essentially reading 32 sectors after the boot sector, but allowing me to easily change the disk geometry).

The code for this is below:

 .load_fat: mov ax, 0x07c0 ; address from start of programs mov es, ax mov al, [SectorsPerFAT] ; how many sectors to load mul byte [FATcount] ; load both FATs mov dx, ax push dx xor dx, dx ; blank dx for division mov ax, 32 mul word [MaxDirEntries] div word [BytesPerSector] ; number of sectors for root directory pop dx add ax, dx ; add root directory length and FATs length -- load all three at once xor dh,dh mov dl, [boot_device] xor ch, ch ; cylinder 0 mov cl, [ReservedSectors] ; Load from after boot sector add cl, byte 1 xor dh, dh ; head 0 mov bx, 0x0200 ; read data to 512B after start of code mov ah, 0x02 ; set to read int 13h cmp ah, 0 je .load_root mov si, error_text call print hlt 

Although not the way I decided to solve the problem, it does the job, and I can move from this to continue development.

EDIT

I think I finally decided that the old code was wrong. I enlarged the cylinder after sector 18, when I had to enlarge my head. This is CHS, not HCS, for some reason!

+3
source

Problem analysis

The problem with your code is that you are not reading from the point on the disk that you expect. Despite the fact that the reading from your disk was successful, the wrong sectors were loaded into the memory.

If we look at the Ralph Brown interrupt list for Int 13h / AH = 2 , we see that the input looks like this:

DISC - READ SECTOR IN MEMORY

 AH = 02h AL = number of sectors to read (must be nonzero) CH = low eight bits of cylinder number CL = sector number 1-63 (bits 0-5) high two bits of cylinder (bits 6-7, hard disk only) DH = head number DL = drive number (bit 7 set for hard disk) ES:BX -> data buffer 

If we look at your registers before you do int 13h in .load_root , we will see these registers with the following contents:

 eax 0x20e ecx 0x101 edx 0x0 ebx 0x2600 es 0x7c0 

So ES: BX is 0x7c0: 0x2600, which is the physical address of 0xA200. It's right. AH (0x02) - read from disk, and the number of sectors to read in AL - 14 (0x0e). That seems reasonable. The problem occurs in ECX and EDX. If we look at your code, then it looks like you are trying to find the sector (logical block address) on the disk where the root directory begins:

 mov al, [FATcount] ; calculate how many sectors into the disk must be loaded mul word [SectorsPerFAT] add al, [ReservedSectors] 

In your BIOS parameter block, you have SectorsPerFat = 9, ReservedSectors = 1 and FATCount = 2. If we look at the FAT12 project document that shows this configuration, it will look like:

enter image description here

Your calculation is correct. 2 * 9 + 1 = 19. The first 19 logical blocks start from LBA 0 to LBA 18. On LBA 19, your root directory starts. We need to convert this to Cylinders / Heads / Sectors (CHS). Logic block address for calculating CHS :

 CHS tuples can be mapped to LBA address with the following formula: LBA = (C × HPC + H) × SPT + (S - 1) where C, H and S are the cylinder number, the head number, and the sector number 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) LBA addresses can be mapped to CHS tuples with the following formula ("mod" is the modulo operation, ie the remainder, and "÷" is integer division, ie the quotient of the division where any fractional part is discarded): C = LBA ÷ (HPC × SPT) H = (LBA ÷ SPT) mod HPC S = (LBA mod SPT) + 1 

In your code, SPT = 18, HPC = 2. If we use LBA 19, we calculate CHS C = 0, H = 1, S = 2. If we look at the values ​​you passed to the registers (CL, CH, DH) above, we find that you used CHS C = 1, H = 0, S = 1. This is LBA 36, not 19. The problem is that your calculations are incorrect. In particular .load_root :

 div byte [SectorsPerTrack] mov ch, ah ; Store quotient in ch for cylinder number mov cl, al ; Store remainder in cl for sector number [snip] xor dh, dh ; head 0 mov dl, [boot_device] ; boot device mov ah, 0x02 ; select read mode int 13h 

Unfortunately, this is the wrong way to calculate CHS from the LBA. You have a similar problem with .load_fat , but you are lucky that you have calculated the correct value. You are reading from the wrong sectors on the disk, and this leads to loading data into 0xA200, which you do not expect.


LBA to CHS Translation

What you need is the correct procedure for converting LBA to CHS. Since you need such a function for various aspects of navigating FAT12 file structures, it is best to create a function. We will call it lba_to_chs .

Before writing such code, we must return to 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 redo the equation for the cylinders, we can reduce the amount of work that 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) duplicated in two places. We must do this equation only once. In addition, since the x86 DIV instruction calculates the remainder and quotient at the same time, we also end up calculating the LBA mod SPT for free when we do (LBA ÷ SPT) . The code will follow this structure:

  1. Calculate LBA DIV SPT. This gives:
    • (LBA ÷ SPT) in private
    • (LBA mod SPT) in the remainder
  2. Take the remainder of step (1) and put in a temporary register
  3. Add 1 to the temporary in step (2). This register now contains the sector computed S = (LBA mod SPT) + 1
  4. Take the ratio from step (1) and divide by HPC.
    • Cylinder number will be private
    • The head will remain.

We reduced the equation to a couple of DIV instructions and added / added. We can simplify things more. If we assume that we use the well-known IBM Compatible Disk formats, then we can also say that the number of sectors per track (SPT), heads (HPC), cylinder, head and sector will always be less than 256. When the maximum LBA value for any well The well-known floppy disk format is divided into SPT, the result will always be less than 256. Knowing this allows us to avoid shifting the bits of the upper two bits of the cylinder and placing them in the upper two bits of CL. We can also use DIV instructions that perform 16-bit, 8-bit unsigned division.


Transfer code

If we take the pseudo code above, we can create a rather small function lba_to_chs that accepts LBA and converts it to CHS and works for all known IBM compatible floppy disk formats.

 ; Function: lba_to_chs ; Description: Translate Logical block address to CHS (Cylinder, Head, Sector). ; Works for all valid FAT12 compatible disk geometries. ; ; Resources: http://www.ctyme.com/intr/rb-0607.htm ; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion ; https://stackoverflow.com/q/45434899/3857942 ; Sector = (LBA mod SPT) + 1 ; Head = (LBA / SPT) mod HEADS ; Cylinder = (LBA / SPT) / HEADS ; ; Inputs: SI = LBA ; Outputs: DL = Boot Drive Number ; DH = Head ; CH = Cylinder (lower 8 bits of 10-bit cylinder) ; CL = Sector/Cylinder ; Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL ; Sector in lower 6 bits of CL ; ; Notes: Output registers match expectation of Int 13h/AH=2 inputs ; lba_to_chs: push ax ; Preserve AX mov ax, si ; Copy LBA to AX cwd ; Upper 16-bit of 32-bit value set to 0 for DIV ; (works on floppy media because AX < 0x8000) div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT mov cl, dl ; CL = S = LBA mod SPT inc cl ; CL = S = (LBA mod SPT) + 1 cwd ; Upper 16-bit of 32-bit value set to 0 for DIV ; (works on floppy media because AX < 0x8000) div word [NumberOfHeads] ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS mov dh, dl ; DH = H = (LBA / SPT) mod HEADS mov dl, [boot_device] ; boot device, not necessary to set but convenient mov ch, al ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS shl ah, 6 ; Store upper 2 bits of 10-bit Cylinder into or cl, ah ; upper 2 bits of Sector (CL) pop ax ; Restore scratch registers ret 

You can use these functions lba_to_chs and integrate them into the code .load_fat and .load_root . Your code might look like this:

 ;;;Start loading File Allocation Table (FAT) .load_fat: mov ax, 0x07c0 ; address from start of programs mov es, ax mov ah, 0x02 ; set to read mov al, [SectorsPerFAT] ; how many sectors to load mov si, [ReservedSectors] ; Load FAT1 into SI for input to lba_to_chs call lba_to_chs ; Retrieve CHS parameters and boot drive for LBA mov bx, 0x0200 ; read data to 512B after start of code int 13h cmp ah, 0 je .load_root mov si, error_text call print hlt ;;;Start loading root directory .load_root: mov si, fat_loaded call print xor ax, ax mov al, [FATcount] mul word [SectorsPerFAT] add ax, [ReservedSectors] ; Compute LBA of oot directory entries mov si, ax ; Copy LBA to SI for later call to lba_to_chs mul word [BytesPerSector] mov bx,ax ; Load to after BOTH FATs in memory mov ax, 32 cwd ; Zero dx for division ; (works since AX(32) < 0x8000) mul word [MaxDirEntries] div word [BytesPerSector] ; number of sectors to read call lba_to_chs ; Retrieve CHS values and load boot drive mov ah, 0x02 int 13h cmp ah, 0 je .load_OS mov si, error_text call print jmp $ 
+5
source

Source: https://habr.com/ru/post/1270423/


All Articles