BenLunt wrote:
After a quick look over, I think it is that you are assuming that the interrupt call preserves BX. The Interrupt call is not required to preserve the BX register. It may be altered during a call, possibly after reading the last sector of the first track.
An older version of my code has an example
conversion routine.
Like *most* BIOS calls in the absence of a documented bug (and none that I am aware of with Int 13h/ah=2 involving BX), BX is preserved.
I looked at your code and noticed it relies on a 386+ processor and won't run on anything before that. It might be a caveat you might want to put in the header documentation.
I have a version that works with 16-bit code (and should run on an 8086) where the 32-bit LBA is passed in SI:DI and then sets up the registers as expected by int 13h/ah=2 etc:
Code:
; Function: lba_to_chs
; Description: Translate a 32-bit Logical block address (LBA)
; to CHS (Cylinder, Head, Sector).
;
; 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
; https://stackoverflow.com/q/47118827/3857942
; http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_9/CH09-4.html
;
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HEADS
; Cylinder = (LBA / SPT) / HEADS
;
; Inputs: SI = Lower 16-bits of LBA
; DI = Upper 16-bits of LBA
; DI:SI = 32 bit LBA number
;
; 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
; This routine should work on an 8086 processor.
lba_to_chs:
push bx ; Save temporary registers
push ax
xor dx, dx ; Set up 32-bit by 16-bit DIV to determine high order
mov ax, di ; of Quotient (HOQ), DX:AX = (0x0000:DI)
div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 1 (HOQ)
mov bx, ax ; Save high order of Quotient (HOQ)
mov ax, si ; Do division to compute low order of Quotient (LOQ)
div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 2 (LOQ)
mov cl, dl ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
mov dx, bx ; Move saved HOQ to DX (Upper 16-bits of DX:AX)
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
ror ah, 1 ; Rotate upper 2 bits of 10-bit Cylinder
ror ah, 1 ; into top of AH
and ah, 0xC0 ; set lower 6 bits to 0
or cl, ah ; Place upper 2 bits of 10-bit Cylinder
; Into the upper 2 bits of the sector number
pop ax ; Restore temporary registers
pop bx
ret
This can be dramatically simplified for anything that would support FAT 12 media where you only need a 16-bit LBA:
Code:
; 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
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
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
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
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
ror ah, 1 ; Rotate upper 2 bits of 10-bit Cylinder
ror ah, 1 ; into top of AH
and ah, 0xC0 ; set lower 6 bits to 0
or cl, ah ; Place upper 2 bits of 10-bit Cylinder
; Into the upper 2 bits of the sector number
pop ax ; Restore scratch registers
ret
This would be simplified a bit more if the media is well known IBM-PC floppy format.