The starting cluster number is offset 26 bytes from the start of the directory entry, so that should be 26, or 0x1A, not 15.
But apart from that, I can't really see anything wrong with the code. It seems to work the same as mine, and mine works.
Here's my code anyway which loads the image. Perhaps somebody else can spot the difference.
Notes:
- The cluster variable is calculated like so:
Code: Select all
; The following table shows the format of a single FAT12 directory entry
;
; Bytes | Meaning
; ----------------------------------------
; 1 - 8 | File Name
; 9 - 11 | File Extension
; 12 | Attributes
; 13 | Reserved
; 14 | Creation Time - Milliseconds
; 15 - 16 | Creation Time (hh:mm:ss)
; 17 - 18 | Creation Date
; 19 - 20 | Last Accessed Date
; 21 - 22 | Unused
; 23 - 24 | Last modified Time
; 25 - 26 | Last modified Date
; 27 - 28 | Starting Cluster
; 29 - 32 | File Size
; Examining the table reveals that the 27th and 28th bytes contain the
; starting cluster, which we want. So if we add on 26 (0x1A) to di, which
; points to the directory entry, then we get a pointer to the starting
; cluster
; Save the starting cluster in the cluster variable.
mov dx, word [di + 0x001A]
mov word [cluster], dx
- The cluster_lba function looks like this:
Code: Select all
cluster_lba:
sub ax, 0x0002 ; Zero base cluster number
xor cx, cx
mov cl, byte [SectorsPerCluster] ; Convert byte to word
mul cx
add ax, word [dataSector] ; Base data sector
ret
- LOADER_ADDRESS_SEGMENT:LOADER_ADDRESS_OFFSET is the address to load the image file (loader.bin) at
- END_OF_FILE is defined to be 0xFF0
Code: Select all
; Store LOADER_ADDRESS_SEGMENT in the es register
mov ax, LOADER_ADDRESS_SEGMENT
mov es, ax
; Store LOADER_ADDRESS_OFFSET in the bx register and save it on the stack
mov bx, LOADER_ADDRESS_OFFSET
push bx
.load_image:
; read_sectors reads cx sectors starting at ax into memory location es:bx
;
; ax = Sector to read (calculated from cluster)
; cx = Sectors per cluster
; es = LOADER_ADDRESS_SEGMENT
; bx = LOADER_ADDRESS_OFFSET
; ax = Sector to read (calculated from cluster)
mov ax, word [cluster]
call cluster_lba
; bx = LOADER_ADDRESS_OFFSET
pop bx
; cx = Sectors per cluster
xor cx, cx
mov cl, byte [SectorsPerCluster]
; Now read the actual sectors
; Note that read_sectors will also increment bx by the number of sectors
; read so we don't need to do that.
call read_sectors
; And save bx (LOADER_ADDRESS_OFFSET) again
push bx
; Compute the location of the next cluster in the FAT and store it in
; bx using the following algorithm:
;
; bx = end_boot + (cluster + (cluster >> 1))
;
; Which is multiplying cluster by 1.5 (remember >> 1 is the same
; as dividing by 2) and adding it on to end_boot, where the FAT
; is located
mov ax, word [cluster] ; ax = cluster
mov cx, ax ; cx = cluster
mov dx, ax ; dx = cluster
shr dx, 0x0001 ; dx = dx >> 1
add cx, dx ; cx = cx + dx
mov bx, end_boot ; bx = end_boot
add bx, cx ; bx = bx + cx
; Now bx contains a pointer to the entry in the FAT containing the
; next cluster so load the next cluster into dx
mov dx, word [bx]
; At this point we have an entire word from the FAT instead of only
; the 12 bits we want. We use the following algorithm to get the 12 bits
; we're interested in:
;
; if the cluster (ax) is even then
; dx = dx AND 0x0FFF (mask off lower 12 bits)
; else
; dx = dx >> 4 (get the upper 12 bits)
; end if
test ax, 0x0001
jnz .odd_cluster
.even_cluster:
and dx, 0xFFF
jmp .check_cluster
.odd_cluster:
shr dx, 0x0004
; Now we have a pointer to the next cluster stored in dx
; We need to check if this is the end of the file, denoted by a cluster
; greater than or equal to the END_OF_FILE marker, in which case we keep
; looping back to .load_image, otherwise we keep loading sectors
.check_cluster
mov word [cluster], dx
cmp dx, END_OF_FILE
jb .load_image
I attached the entire boot loader (note: it doesn't turn on A20 address line or switch to pmode)