1; // TODO: This code only works for the 286+ so you must detect for 8088's somewhere in your code.
 
    3[bits 32] ; Function for reading disk contents to memory
 
    4global read_disk ; Function using legacy PIO mode + polling, should be used early during boot only!
 
    7read_disk: ; First parameter (pointer): [ESP + 8] - LBA address of the first sector to read.
 
    8           ; Second parameter (boolean): [ESP + 12] - Master or slave drive to use. // TODO: make use of it, setup drive check!
 
    9           ; Third parameter (word): [ESP + 16] - Bytes number to read (must be even).
 
   10           ; Fourth parameter (word): [ESP + 20] - Offset to read (must be even).
 
   11           ; Last parameter (pointer): [ESP + 24] - Pointer to RAM where to load data from disk.
 
   12    push ebp ; Save stack base.
 
   13    mov ebp, esp ; Set register base to register top.
 
   14                 ; Function won't be able to interfere with previous stack entries.
 
   15    pusha ; Save all registers. They will be restored afterwards.
 
   17    ; Calculate number of sectors to read (ceil).
 
   18    mov ebx, [ebp+16] ; Store offset in ebx.
 
   19    add ebx, [ebp+20] ; Add length to ebx - ceil(ebx) will be number of sectors to read.
 
   20    shl ebx, 9 ; Divide ebx by sector size.
 
   21    test ebx, 0x1ff ; Calculate eax by sector size division remainder. // TODO: check sector size!
 
   22    jz .setup_reading ; Setup reading if remainder equals zero.
 
   23    add ebx, 1 ; Add 1 to ceil otherwise.
 
   27    mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA.
 
   28    mov eax, [ebp+8] ; Copy LBA to eax.
 
   29    shr eax, 24 ; Get bit 24 - 27 in al.
 
   30    or al, 0xe0 ; Set bit 6 in al for LBA mode.
 
   31    out dx, al ; Send eax value to port.
 
   33    mov edx, 0x01f2 ; Port to send number of sectors.
 
   34    mov al, bl ; Get number of sectors from ebx.
 
   37    mov edx, 0x1f3 ; Port to send bit 0 - 7 of LBA.
 
   38    mov eax, [ebp+8] ; Copy LBA to eax.
 
   41    mov edx, 0x1f4 ; Port to send bit 8 - 15 of LBA.
 
   42    mov eax, [ebp+8] ; Copy LBA to eax.
 
   43    shr eax, 8 ; Get bit 8 - 15 in al.
 
   46    mov edx, 0x1f5 ; Port to send bit 16 - 23 of LBA.
 
   47    mov eax, [ebp+8] ; Copy LBA to eax.
 
   48    shr eax, 16 ; Get bit 16 - 23 in al.
 
   51    mov edx, 0x1f7 ; Command port.
 
   52    mov al, 0x20 ; Read with retry command.
 
   57    in al, dx ; Read status port.
 
   58    test al, 0x80 ; Check for BSY status flag.
 
   59    jz .ready ; Start reading if flag isn't set.
 
   61    test al, 0x1 ; Check for ERR flag.
 
   62    jnz .general_error ; Jump to general error handler if it's set.
 
   63    test al, 0x20 ; Check for DF flag.
 
   64    jnz .drive_fault_error ; Jump to drive fault handler if it's set.
 
   66    jmp .busy ; Loop otherwise.
 
   69    mov dx, 0x1f1 ; Port to receive error message.
 
   70    in ax, dx ; Read error code from port.
 
   74    mov ax, 0x10 ; Drive fault doesn't have error code, so we'll set it to first bit of second byte.
 
   79    in al, dx ; Read status port.
 
   80    test al, 0x8 ; Check for DRQ status flag.
 
   81    jz .ready ; Loop some more if flag isn't set.
 
   82    mov dx, 0x1f0 ; Data port, data comes in and out of here.
 
   83                  ; NB! This is a word port only. We can't read a single byte from here. Only a word.
 
   85    mov ecx, [ebp+20] ; Copy offset to ecx.
 
   86    shr ecx, 1 ; Divide offset by 2 - calculate offset in words.
 
   87    cmp ecx, 0 ; If offset equals zero start reading data.
 
   91    in ax, dx ; If offset doesn't equal zero, drain offset data.
 
   95    mov ecx, [ebp+16] ; Copy data length to ecx.
 
   96    shr ecx, 1 ; Divide data length by 2 - calculate data length in words.
 
   97    mov edi, [ebp+24] ; Copy data loading address to edi.
 
   98    rep insw ; Copy all data there.
 
  100    mov ecx, ebx ; Copy number of sectors from ebx to ecx.
 
  101    shr ecx, 9 ; Multiply ecx by sector size.
 
  102    sub ecx, [ebp+16] ; Substract offset from ecx.
 
  103    sub ecx, [ebp+20] ; Substract data length from ecx.
 
  104                      ; Remaining data length in the last read sector is stored in ecx.
 
  105    shr ecx, 1 ; Divide remaining data length by 2 - calculate remaining data length in words.
 
  106    cmp ecx, 0 ; If remaining data length equals zero return.
 
  110    in ax, dx ; If remaining data length doesn't equal zero, drain remaining data.
 
  113    mov ax, 0 ; Move zero (successful code) to eax.
 
  117    mov [esp+14], ax ; Change saved eax to current.
 
  118                     ; This allows us to return error or successful execution code.
 
  119    popa ; Restore all registers.
 
  120    mov esp, ebp ; Restore stack.
 
  121    pop ebp ; Restore stack base.