Суббота, 23.11.2024, 02:09
Приветствую Вас Гость | RSS
Главная | | Регистрация | Вход
Меню сайта
Форма входа
Поиск
Календарь
«  Март 2012  »
ПнВтСрЧтПтСбВс
   1234
567891011
12131415161718
19202122232425
262728293031
Архив записей
Наш опрос
Оцените мой сайт
Всего ответов: 20
Мини-чат
Друзья сайта
  • Заказ Художник
  • Рыбалка
  • Новости
  • Наш университет io-96
  • Железо и другие
  • Наш Counter-Strike
  • Google ot Turbokherson
  • Google ot Turbokherson87
  • Android
  • Программы и Софт
  • Программирование
  • Лечение здоровья
  • О Ремонт компьютер
  • Наш сайт Turbokherson
  • Программирование ICQ 378204653 от Turbokherson
    Главная » 2012 » Март » 30 » Пишем свой загрузочный сектор в Ассемблере
    09:04
    Пишем свой загрузочный сектор в Ассемблере

    Мы будем писать загрузочный сектор для трехдюймовой дискеты с файловой системой FAT12. После окончания начальной загрузки программа POST находит активное устройство и загружает с него короткую программу загрузки ОС - загрузочный сектор. Загрузочный сектор это первый физический сектор устройства, в данном случае дискеты и его размет равен всего ничего 512 байт. С помощью этих 512 байт кода мы должны найти основную часть загрузчика операционной системы, загрузить его в память и передать ему управление. Заголовок файловой системы FAT находится в первом секторе дискеты, благодаря чему этот заголовок, содержащий всю необходимую информацию о файловой системе, загружается вместе нашим загрузчиком.

    Наш загрузочный сектор будет искать в корневом каталоге некоторый файл - загрузчик, загрузит его в память и передаст ему управление на его начало. А загрузчик уже сам разберется, что ему делать дальше. Я использую NASM, т.к. считаю, что он больше подходит для наших целей.

    И так, приступим. Как я уже говорил, в начале нашего загрузочного сектора располагается заголовок FAT, опишем его:

    ; Общая часть для всех типов FAT
    BS_jmpBoot:
    jmp short BootStart ; Переходим на код загрузчика
    nop
    BS_OEMName db '*-v4VIHC' ; 8 байт, что было на моей дискете, то и написал
    BPB_BytsPerSec dw 0x200 ; Байт на сектор
    BPB_SecPerClus db 1 ; Секторов на кластер
    BPB_RsvdSecCnt dw 1 ; Число резервных секторов
    BPB_NumFATs db 2 ; Количектво копий FAT
    BPB_RootEntCnt dw 224 ; Элементов в корневом катологе (max)
    BPB_TotSec16 dw 2880 ; Всего секторов или 0
    BPB_Media db 0xF0 ; код типа устройства
    BPB_FATsz16 dw 9 ; Секторов на элемент таблицы FAT
    BPB_SecPerTrk dw 18 ; Секторов на дорожку
    BPB_NumHeads dw 2 ; Число головок
    BPB_HiddSec dd 0 ; Скрытых секторов
    BPB_TotSec32 dd 0 ; Всего секторов или 0
    ; Заголовок для FAT12 и FAT16
    BS_DrvNum db 0 ; Номер дика для прерывания int 0x13
    BS_ResNT db 0 ; Зарезервировано для Windows NT
    BS_BootSig db 29h ; Сигнатура расширения
    BS_VolID dd 2a876CE1h ; Серийный номер тома
    BS_VolLab db 'X boot disk' ; 11 байт, метка тома
    BS_FilSysType db 'FAT12   ' ; 8 байт, тип ФС
    ; Структура элемента каталога
    struc DirItem
    DIR_Name: resb 11
    DIR_Attr: resb 1
    DIR_ResNT: resb 1
    DIR_CrtTimeTenth resb 1
    DIR_CrtTime: resw 1
    DIR_CrtDate: resw 1
    DIR_LstAccDate: resw 1
    DIR_FstClusHi: resw 1
    DIR_WrtTime: resw 1
    DIR_WrtDate: resw 1
    DIR_FstClusLow: resw 1
    DIR_FileSize: resd 1
    endstruc ;DirItem

    Большинство полей мы использовать не будем, и так мало места для полета. Загрузчик BIOS передает нам управление на начало загрузочного сектора, т.е. на BS_jmpBoot, поэтому в начале заголовка FAT на отводится 3 байта для короткой или длинной инструкции jmp. Мы в данном случае использовали короткую, указав модификатор short, и в третьем байте просто разместили однобайтовую инструкцию nop.

    По инструкции jmp short BootStart мы переходим на наш код. Проведем небольшую инициализацию:

    ; Наши не инициализированные переменные
    ; При инициализации они затрут не нужные нам
    ; поля заголовка FAT: BS_jmpBoot и BS_OEMName
    struc NotInitData
    SysSize: resd 1 ; Размер системной области FAT
    fails: resd 1 ; Число неудачных попыток при чтении
    fat: resd 1 ; Номер загруженного сектора с элементами FAT
    endstruc ;NotInitData
    ; По этому адресу мы будем загружать загрузчик
    %define SETUP_ADDR 0x1000
    ; А по этому адресу нас должны были загрузить
    %define BOOT_ADDR 0x7C00
    %define BUF 0x500
    BootStart:
    cld
    xor cx, cx
    mov ss, cx
    mov es, cx
    mov ds, cx
    mov sp, BOOT_ADDR
    mov bp, sp
    ; Сообщим о том что мы загружаемся
    mov si, BOOT_ADDR + mLoading
    call print

    Все сегментные регистры настраиваем на начало физической памяти. Вершину стека настраиваем на начало нашего сектора, стек растет вниз (т.е. в сторону младших адресов), так что проблем быть не должно. Туда же указывает регистр bp - нам нужно обращаться к полям заголовка FAT и паре наших переменных. Мы используем базовую адресацию со смещением, для чего используем регистр bp т.к. в этом случае можно использовать однобайтовые смещения, вместо двухбайтовых адресов, что позволяет сократить код. Процедуру print, выводящую сообщение на экран, рассмотрим позже.

    Теперь нам нужно вычислить номера первых секторов корневого каталога и данных файлов.

     mov al, [byte bp+BPB_NumFATs]
    cbw
    mul word [byte bp+BPB_FATsz16]
    add ax, [byte bp+BPB_HiddSec]
    adc dx, [byte bp+BPB_HiddSec+2]
    add ax, [byte bp+BPB_RsvdSecCnt]
    adc dx, cx
    mov si, [byte bp+BPB_RootEntCnt]
    ; dx:ax - Номер первого сектора корневого каталога
    ; si - Количество элементов в корневом каталоге
    pusha
    ; Вычислим размер системной области FAT = резервные сектора +
    ; все копии FAT + корневой каталог
    mov [bp+SysSize], ax ; осталось добавить размер каталога
    mov [bp+SysSize+2], dx
    ; Вычислим размер корневого каталога
    mov ax, 32
    mul si
    ; dx:ax - размер корневого каталога в байтах, а надо в секторах
    mov bx, [byte bp+BPB_BytsPerSec]
    add ax, bx
    dec ax
    div bx
    ; ax - размер корневого каталога в секторах
    add [bp+SysSize], ax ; Теперь мы знаем размер системной
    adc [bp+SysSize+2], cx ; области FAT, и начало области данных
    popa
    ; В dx:ax - снова номер первого сектора корневого каталога
    ; si - количество элементов в корневом каталоге

    Теперь мы будем просматривать корневой каталог в поисках нужного нам файла

    NextDirSector:
    ; Загрузим очередной сектор каталога во временный буфер
    mov bx, 700h ; es:bx - буфер для считываемого сектора
    mov di, bx ; указатель текущего элемента каталога
    mov cx, 1 ; количество секторов для чтения
    call ReadSectors
    jc near DiskError ; ошибка при чтении
    RootDirLoop:
    ; Ищем наш файл
    ; cx = 0 после функции ReadSectors
    cmp [di], ch ; byte ptr [di] = 0?
    jz near NotFound ; Да, это последний элемент в каталоге
    ; Нет, не последний, сравним имя файла
    pusha
    mov cl, 11 ; длина имени файла с расширением
    mov si, BOOT_ADDR + LoaderName ; указатель на имя искомого файла
    rep cmpsb ; сравниваем
    popa
    jz short Found ; Нашли, выходим из цикла
    ; Нет, ищем дальше
    dec si ; RootEntCnt
    jz near NotFound ; Это был последний элемент каталога
    add di, 32 ; Переходим к следующему элементу каталога
    ; bx указывает на конец прочтенного сектора после call ReadSectors
    cmp di, bx ; Последний элемент в буфере?
    jb short RootDirLoop ; Нет, проверим следующий элемент
    jmp short NextDirSector ; Да последний, загрузим следующий сектор

    Из этого кода мы можем выйти одну из трех точек: ошибка при чтении DiskError, файл наден Found или файл не найден NotFound.


    Загрузка файла в память 

    Если файл найден, то загрузим его в память и передадим управление на его начало.

    Found:
    ; Загрузка загрузчика (извените, калабур)
    mov bx, SETUP_ADDR
    mov ax, [byte di+DIR_FstClusLow] ; Номер первого кластера файла
    ; Загружаем сектор с элемнтами FAT, среди которых есть FAT[ax]
    ; LoadFAT сохраняет значения всех регистров
    call LoadFAT
    ReadCluster:
    ; ax - Номер очередного кластера
    ; Загрузим его в память
    push ax
    ; Первые два элемента FAT служебные
    dec ax
    dec ax
    ; Число секторов для чтения
    ; cx = 0 после ReadSectors
    mov cl, [byte bp+BPB_SecPerClus] ; Секторов на кластер
    mul cx
    ; dx:ax - Смещение кластера относительно области данных
    add ax, [byte bp+SysSize]
    adc dx, [byte bp+SysSize+2]
    ; dx:ax - Номер первого сектора требуемого кластера
    ; cx еще хранит количество секторов на кластер
    ; es:bx - конец прошлого кластера и начало нового
    call ReadSectors ; читаем кластер
    jc near DiskError ; Увы, ошибка чтения
    pop ax ; Номер кластера
    ; Это конец файла?
    ; Получим значение следующего элемента FAT
    pusha
    ; Вычислим адрес элемента FAT
    mov bx, ax
    shl ax, 1
    add ax, bx
    shr ax, 1
    ; Получим номер сектора, в котором находится текущий элемент FAT
    cwd
    div word [byte bp+BPB_BytsPerSec]
    cmp ax, [bp+fat] ; Мы уже читали этот сектор?
    popa
    je Checked ; Да, читали
    ; Нет, надо загрузить этот сектор
    call LoadFAT
    Checked:
    ; Вычислим адрес элемента FAT в буфере
    push bx
    mov bx, ax
    shl bx, 1
    add bx, ax
    shr bx, 1
    and bx, 511 ; остаток от деления на 512
    mov bx, [bx+0x700] ; а вот и адрес
    ; Извлечем следующий элемент FAT
    ; В FAT16 и FAT32 все немного проще :(
    test al, 1
    jnz odd
    and bx, 0xFFF
    jmp short done
    odd:
    shr bx, 4
    done:
    mov ax, bx
    pop bx
    ; bx - новый элемент FAT
    cmp ax, 0xFF8 ; EOF - конец файла?
    jb ReadCluster ; Нет, читаем следующий кластер
    ; Наконец-то загрузили
    mov ax, SETUP_ADDR>>4 ; SETUP_SEG
    mov es, ax
    mov ds, ax
    ; Передаем управление, наше дело сделано :)
    jmp SETUP_ADDR>>4:0

    LoadFAT ;proc
    ; Процедура для загрузки сектора с элементами FAT
    ; Элемент ax должен находится в этом секторе
    ; Процедура не должна менять никаких регистров
    pusha
    ; Вычисляем адрес слова содержащего нужный элемент
    mov bx, ax
    shl ax, 1
    add ax, bx
    shr ax, 1
    cwd
    div word [byte bp+BPB_BytsPerSec]
    ; ax - смещение сектора относительно начала таблицы FAT
    mov [bp+fat], ax ; Запомним это смещение, dx = 0
    cwd ; dx:ax - номер сектора, содержащего FAT[?]
    ; Добавим смещение к первой копии таблицы FAT
    add ax, [byte bp+BPB_RsvdSecCnt]
    adc dx, 0
    add ax, [byte bp+BPB_HiddSec]
    adc dx, [byte bp+BPB_HiddSec+2]
    mov cx, 1 ; Читаем один сектор. Можно было бы и больше, но не быстрее
    mov bx, 700h ; Адрес буфера
    call ReadSectors
    jc DiskError ; Ошибочка вышла
    popa
    ret
    ;LoadFAT endp

    В FAT12 на каждый элемент FAT отводится по 12 бит, что несколько усложняет нашу работу, в FAT16 и FAT32 на каждый элемент отводится по 16 и 32 бита соответственно и можно просто прочесть слово или двойное слово, а в FAT12 необходимо прочесть слово содержащее элемент FAT и правильно извлечь из него 12 бит.


    Процедура загрузки секторов

    Теперь разберем процедуру загрузки секторов. Процедура получает номер сектора в dx:ax (нумерация с нуля) и преобразует его к формату CSH (цилиндр, сектор, сторона), используемому прерыванием BIOS int 0x13.

    <>; *************************************************
     ; *          Чтение секторов с диска              *
     ; *************************************************
     ; * Входные параметры:                            *
     ; * dx:ax       - (LBA) номер сектора             *
     ; * cx          - количество секторов для чтения  *
     ; * es:bx       - адрес буфера                    *
     ; *************************************************
     ; * Выходные параметры:                           *
     ; * cx       - Количество не прочтенных секторов  *
     ; * es:bx    - Указывает на конец буфера          *
     ; * cf = 1   - Произошла ошибка при чтении        *
     ; *************************************************
     ReadSectors ;proc
     next_sector:
     ; Читаем очередной сектор
     mov byte [bp+fails], 3 ; Количество попыток прочесть сектор
     try:
     ; Очередная попытка
     pusha
     ; Преобразуем линейный адрес в CSH
     ; dx:ax = a1:a0
     xchg ax, cx ; cx = a0
     mov ax, [byte bp+BPB_SecPerTrk]
     xchg ax, si ; si = Scnt
     xchg ax, dx ; ax = a1
     xor dx, dx
     ; dx:ax = 0:a1
     div si ; ax = q1, dx = c1
     xchg ax, cx ; cx = q1, ax = a0
     ; dx:ax = c1:a0
     div si ; ax = q2, dx = c2 = c
     inc dx ; dx = Sector?
     xchg cx, dx ; cx = c, dx = q1
     ; dx:ax = q1:q2
     div word [byte bp+BPB_NumHeads] ; ax = C (track), dx = H
     mov dh, dl ; dh = H
     mov ch, al
     ror ah, 2
     or cl, ah
     mov ax, 0201h ; ah=2 - номер функции, al = 1 сектор
     mov dl, [byte bp+BS_DrvNum]
     int 13h
     popa
     jc Failure ; Ошибка при чтении
     ; Номер следующего сектора
     inc ax
     jnz next
     inc dx
     next:
     add bx, [byte bp+BPB_BytsPerSec]
     dec cx ; Все сектора прочтены?
     jnz next_sector ; Нет, читаем дальше
     return:
     ret
     Failure:
     dec byte [bp+fails] ; Последняя попытка?
     jnz try ; Нет, еще раз
     ; Последняя, выходим с ошибкой
     stc
     ret
     ;ReadSectors endp
    >

    Осталось всего ничего:

    ; Сообщения об ошибках
    NotFound: ; Файл не найден
    mov si, BOOT_ADDR + mLoaderNotFound
    call print
    jmp short die
    DiskError: ; Ошибка чтения
    mov si, BOOT_ADDR + mDiskError
    call print
    ;jmp short die
    die: ; Просто ошибка
    mov si, BOOT_ADDR + mReboot
    call print
    _die: ; Бесконечный цикл, пользователь сам нажмет Reset
    jmp short _die
    ; Процедура вывода ASCIIZ строки на экран
    ; ds:si - адрес строки
    print: ; proc
    pusha
    print_char:
    lodsb ; Читаем очередной символ
    test al, al ; 0 - конец?
    jz short pr_exit ; Да конец
    ; Нет, выводим этот символ
    mov ah, 0eh
    mov bl, 7
    int 10h
    jmp short print_char ; Следующий
    pr_exit:
    popa
    ret
    ;print endp
    ; Перевод строки
    %define endl 10,13,0
    ; Строковые сообщения
    mLoading db 'Loading...',endl
    mDiskError db 'Disk I/O error',endl
    mLoaderNotFound db 'Loader not found',endl
    mReboot db 'Reboot system',endl
    ; Выравнивание размера образа на 512 байт
    times 499-($-$$) db 0
    LoaderName db 'BOOTOR     ' ; Имя файла загрузчика
    BootMagic dw 0xAA55 ; Сигнатура загрузочного сектора


    Компиляция

    Ну вот вроде бы и все. Компилируется все это до безобразия просто:

    > nasm -f bin boot.asm -lboot.lst -oboot.bin

    Осталось только как-то записать этот образ в загрузочный сектор вашей дискеты и разместить в корне этой дискеты файл загрузчика BOOTOR. Загрузочный сектор можно записать с помощью такой вот простой программы на Turbo (Borland) Pascal. Эта программа будет работать как в DOS, так и в Windows - пробовал на WinXP - работает как ни странно, но только с floopy. Но все же я рекомендую запускать эту утилиту из-под чистого DOS'а, т.к. WinXP обновляет не все поля в заголовке FAT и загрузочный сектор может работать некорректно.

    var
     
    fn:string;
      f:file;
      buf:array[0..511] of byte;
      ok:boolean;
    begin
     
    fn:=ParamStr(1);
      if fn='' then writeln('makeboot bootsect.bin')
      else
      begin
       
    writeln('Making boot floppy');
        {$I-}
       
    assign(f,fn);
        reset(f,sizeof(buf));
        BlockRead(f,buf,1);
        close(f);
        {$I+}
       
    if IOResult<>0 then
        begin
         
    Writeln('Failed to read file "',fn,'"');
          Halt(1);
        end;
        ok:=false;
        asm
          mov       ax, 0301h
         
    mov       cx, 1
         
    mov       dx, 0
         
    mov       bx, seg buf
          mov       es, bx
          mov       bx, offset buf
          int       13h
         
    jc        @error
          mov       ok, true
        @error:
       
    end;
        if ok then writeln('Done :)')
        else begin
         
    writeln('Makeboot failed :(');
          Halt(1);
        end;
      end;
    end.


    http://www.cyberguru.ru/programming/assembler/assembler-boot-sector-page4.html


    Просмотров: 1599 | Добавил: Turbokherson | Рейтинг: 0.0/0
    Всего комментариев: 0
    Имя *:
    Email *:
    Код *:
    Создать бесплатный сайт с uCozCopyright MyCorp © 2024