Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are all disk sectors iterated in assembly?

In the process of learning assembly, I am writing an OS. I have successfully written the code necessary for appending a second 512 byte sector to the initial 512 byte bootloader:

%define KBDINT  0x16
%define VIDINT  0x10
%define DISKINT 0x13
%define TTYOUT  0x0E
%define VIDMODE 0x0000
%define NUL 0x00
%define CR  0x0D
%define LF  0x0A
%define START   0x7C00

%macro  PRINT   1
    mov si, %1
    call    print
%endmacro

    bits    16          ; 16 bit real mode
    org START           ; loader start in memory
start:  jmp main

print:  jmp .init
.loop:  mov bx, VIDMODE
    mov ah, TTYOUT
    int VIDINT
    inc si
.init:  mov al, [si]
    cmp al, NUL
    jne .loop
    ret

main:   cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    sti
    PRINT   welcome

    mov ah, NUL
    int DISKINT
    mov al, 0x01        ; sector count
    mov ah, 0x02        ; read function
    mov bx, kernel
    mov cl, 0x02
    mov ch, 0x00        ; cylinder number
    mov dh, 0x00        ; head number
    int DISKINT
    jc  fail
    jmp kernel

fail:   PRINT   failure
;   jmp halt

halt:   PRINT   halting
    cli
    hlt
    PRINT   imprbbl
    jmp halt

    welcome db "moose os", CR, LF, NUL
    failure db "failed disk load", CR, LF, NUL
    halting db "halting", CR, LF, NUL
    imprbbl db "but that's impossible!", CR, LF, NUL

times 0x0200 - ($ - $$) - 2 db 0x00
    end dw 0xAA55

kernel: PRINT   yay
    yay db "kernel", CR, LF, NUL
    jmp halt

times 0xFFFF db 0x00

I compile the file with: nasm -f bin -o boot.bin boot.asm && qemu boot.bin:

binary execution

I am curious how heads and cylinders are used:

  • How are the sectors iterated through?
  • How does iteration differ between emulation and direct execution?
like image 937
motoku Avatar asked Jun 22 '15 22:06

motoku


People also ask

How many sectors are in a disk?

A sector is the smallest physical storage unit on the disk, and on most file systems it is fixed at 512 bytes in size. A cluster can consist of one or more consecutive sectors – commonly, a cluster will have four or eight sectors.

How is data stored on a track sector?

Storing the Data Tracks are concentric circles that surround the entire platter. Sectors are wedges that, when grouped together, form a track. Sectors contain a fixed number of bytes – usually 256 or 512 – and are grouped into clusters. Before a drive can be used, it must be properly formatted.

How many sectors and tracks are in a hard drive?

There can be more than a thousand tracks on a 3.5-inch hard disk. Sections within each track are called sectors. A sector is the smallest physical storage unit on a disk, and is almost always 512 bytes (0.5 kB) in size. The figure below shows a hard disk with two platters.

What is the typical disk sector size?

Each sector stores a fixed amount of user data, traditionally 512 bytes for hard disk drives. But because of better data integrity at higher densities and robust error correction capabilities newer HDDs now store 4096 bytes (4K) in each sector.


1 Answers

•How are the sectors iterated through?

To iterate over many sectors using CHS CylinderHeadSector notation, we first have to retrieve the actual limits on these parameters. BIOS has function 08h on int 13h that gives us the maximum values as well as some extra info that we don't need for now.

The sector number in CL ranges from 1 to 63.
The head number in DH ranges from 0 to 255, although 255 is seldom used.
The cylinder number in CL ranges from 0 to 1023. Since this cannot be held in a single byte, the 2 highest bits of this 10-bit number are stored in bits 6 and 7 of the CL register!

How the iteration works

Think of the CHS notation as if it were some kind of number where C is the most significant part and S is the least significant part.
To get to the next sector on the disk we start our incrementation of this number at its least significant end.
If by incrementing the S part we overflow its range, we reset it to its smallest value (1) and start incrementing the next more significant part which is H in this case.
If by incrementing the H part we overflow its range, we reset it to its smallest value (0) and start incrementing the most significant part which is C in this case.
If by incrementing the C part we overflow its range, we reset it to its smallest value (0). This will make for a wraparound on the disk. If on input a correct SectorCount was given then normally at this point reading will have stopped.

; INPUT:  DL=Drive
;         CH=Cylinder
;         DH=Head
;         CL=Sector
;         AX=SectorCount
;         ES:BX=Buffer
; OUTPUT: CF=0 AH       = 0
;              CH,DH,CL = CHS of following sector
;         CF=1 AH       = Error status
;              CH,DH,CL = CHS of problem sector
ReadDiskSectors:
  push    es
  push    di
  push    bp
  mov     bp,sp           ;Local variables:
  push    ax              ;[bp-2]=SectorCount
  push    cx              ;[bp-4]=MaxSector
  push    dx              ;[bp-6]=MaxHead
  push    bx              ;[bp-8]=MaxCylinder

  push    es
  mov     ah,08h
  int     13h             ;ReturnDiskDriveParameters
  pop     es
  jc      NOK
  mov     bx,cx           ;10-bit cylinder info -> BX
  xchg    bl,bh
  shr     bh,6
  xchg    [bp-8],bx       ;Store MaxCylinder and get input BX back
  movzx   dx,dh           ;8-bit head info -> DX
  xchg    [bp-6],dx       ;Store MaxHead and get input DX back
  and     cx,003Fh        ;6-bit sector info -> CX
  xchg    [bp-4],cx       ;Store MaxSector and get input CX back

ReadNext:
  mov     di,5            ;Max 5 tries per sector
ReadAgain:
  mov     ax,0201h        ;Read 1 sector
  int     13h             ;ReadDiskSectors
  jnc     OK
  push    ax              ;Save error status byte in AH
  mov     ah,00h
  int     13h             ;ResetDiskSystem
  pop     ax
  dec     di
  jnz     ReadAgain
  stc
  jmp     NOK
OK:
  dec     word ptr [bp-2] ;SectorCount
  jz      Ready
  call    NextCHS
  mov     ax,es           ;Move buffer 512 bytes up
  add     ax,512/16
  mov     es,ax
  jmp     ReadNext

Ready:
  call    NextCHS         ;Return useful CHS values to support reads
  xor     ah,ah           ; -> CF=0 ... that are longer than memory
NOK:
  mov     sp,bp
  pop     bp
  pop     di
  pop     es
  ret

NextCHS:
  mov     al,cl            ;Calculate the 6-bit sector number
  and     al,00111111b
  cmp     al,[bp-4]        ;MaxSector
  jb      NextSector
  cmp     dh,[bp-6]        ;MaxHead
  jb      NextHead
  mov     ax,cx            ;Calculate the 10-bit cylinder number
  xchg    al,ah
  shr     ah,6
  cmp     ax,[bp-8]        ;MaxCylinder
  jb      NextCylinder
DiskWrap:
  mov     cx,1             ;Wraparound to very first sector on disk
  mov     dh,0
  ret
NextCylinder:
  inc     ax
  shl     ah,6             ;Split 10-bit cylinder number over CL and CH
  xchg    al,ah
  mov     cx,ax
  mov     dh,0
  inc     cl
  ret
NextHead:
  inc     dh
  and     cl,11000000b
NextSector:
  inc     cl
  ret

A note on sector size

Although it's perfectly alright to have sectors that are not 512 bytes in length, it's a save assumption that they will be that size. After decades of programming I've never seen any disk that hadn't 512-byte sectors.
If you insisted on supporting different sizes, you can look at the 4th byte of the DisketteParameterTable for which you recieved a pointer in ES:DI from the BIOS function ReturnDiskDriveParameters.

•How does iteration differ between emulation and direct execution?

I suppose that by direct execution you understand real hardware.
For real hardware BIOS will return you the geometry in CHS notation and the sectors exist ... well just because they are real!
Under emulation the emulator will do its best to also provide you will these geometry values but it will be up to you to make sure that enough sectors exist on the concerned drive. This is exactly what @Jester said when he asked you: "Do you even have that sector in the image file?" You resolved this issue by enlarging the image file using times 0xFFFF db 0x00

Some extra advice

You didn't setup the stack. Since you load your kernel above the bootsector at 7C00h, I suggest you initialize SS:SP to 0000h:7C00h for a stack beneath the bootsector.

main:
  cli
  xor ax, ax
  mov ds, ax
  mov es, ax
  mov ss, ax
  mov sp, 7C00h
  sti
  PRINT   welcome

As @Fifoernik commented you would better put the jmp halt before the yay db "kernel", CR, LF, NUL to prevent the execution of this data!

kernel:
  PRINT   yay
  jmp halt
  yay db "kernel", CR, LF, NUL
like image 69
Sep Roland Avatar answered Oct 31 '22 15:10

Sep Roland