Как я могу JMP переместить код в мою MBR?

Я пытаюсь написать чрезвычайно простой MBR, чтобы начать учиться писать MBR / ядро. Это то, что у меня есть (созданное из частей других MBR). Бинарный файл, который я получаю при использовании nasm, а затем ld для связывания, немного отличается от простого использования nasm для обоих, но, похоже, это не проблема.

Сначала я начал с jmp 0:continue, но он, похоже, перескакивает на 0000:7c22 (или 001d с одним nasm ... я полагаю, что не указывал, что он начинается с 7c00), но я хочу перейти к :7a22 или :7a1d, адресу перемещенного кода. Я пробовал использовать только jmp continue, а затем, как показано ниже без комментариев, добавлял указатель стека к указателю продолжения, нажимал его и возвращал. Все, что я получаю, - это мигающий курсор, когда я попадаю в мой 1-й сектор. Любая помощь приветствуется.

                            ; nasm+ld       nasm            comment
global _start
_start:
    xor    cx, cx           ; 6631c9        31c9            Set segment registers to zero
    mov    es, cx           ; 8ec1          8ec1
    mov    ds, cx           ; 8ed9          8ed9
    mov    ss, cx           ; 8ed1          8ed1
    mov    sp, 0x7A00       ; 66bc007a      bc007a          Stack
    mov    di, sp           ; 6689e7        89e7            Bottom of relocation point
    mov    esi, _start      ; be007c0000    66be00000000
    cld                     ; fc            fc
    mov    ch, 1            ; b501          b501            cx = 256
    rep movsw               ; f366a5        f3a5            Copy self to 0:7A00

;----------------------------------------------------------------------------------------------------------------------
    xor    eax,eax
    mov    ax, sp
    add    ax, continue

    ;jmp    0:continue      ; ea227c00000000    ea1d000000      near JMP to copy of self
                            ; or
    ;jmp    continue        ; (eb00)
    push eax
    ret
;----------------------------------------------------------------------------------------------------------------------

continue:
    sti                     ; fb            fb

ERROR:
    mov esi, errormsg       ; be3b7c0000 (be36) 66be36000000        Error Message loc
    mov ah, 0x0E            ; b40e          b40e
    mov bx, 7               ; 66bb          bb0700
disp:
    lodsb                   ; ac            ac
    cmp ah, 0x00            ; 80fc00        80fc00
    je end                  ; 7404          7404
    int 10h                 ; cd10          cd10
    jmp disp                ; ebf6          ebf6

end:
    nop                     ; 90            90
    jmp end                 ; ebfd          ebfd            infinte loop

errormsg db 10,'YOU MESSED UP.',13,0

times (0x1b8 - ($-$$)) nop  ; 90            90          Padding

UID db 0xf5,0xbf,0x0f,0x18                                         ;Unique Disk ID

BLANK times 2 db 0

PT1 db 0x80,0x20,0x21,0x00,0x0C,0x50,0x7F,0x01,0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D ;First Partition Entry
PT2 times 16 db 0                                      ;Second Partition Entry
PT3 times 16 db 0                                      ;Third Partition Entry
PT4 times 16 db 0                                      ;Fourth Partition Entry

BOOTSIG dw 0xAA55                                      ;Boot Signature[/code]

person Willdorf    schedule 18.05.2016    source источник
comment
Похоже, ваш компоновщик создает 32-битный код, который не подходит для загрузочного сектора. (Хотя я никогда не слышал, чтобы ld на самом деле вставлял префиксы размера перед инструкциями ... все это выглядит странно). Какие настоящие команды вы используете для сборки и компоновки? Как ты в обоих случаях сваливаешь результат?   -  person davmac    schedule 18.05.2016
comment
@davmac Компоновщик этого не делает. Это NASM. Ему нужна директива BITS 16, чтобы сделать это явным.   -  person Ross Ridge    schedule 18.05.2016
comment
@RossRidge в порядке, но коды операций в столбце nasm в его комментариях выглядят как правильный 16-битный код. Конечно, я не знаю, откуда он на самом деле их берет.   -  person davmac    schedule 18.05.2016
comment
Я не уверен, почему вы думаете, что jmp 0:continue может поступить правильно. Как ассемблер или компоновщик может узнать, куда вы скопировали код?   -  person Ross Ridge    schedule 18.05.2016
comment
nasm -f bin -o mbr.dd mbr.asm или nasm -f elf mbr.asm; ld mbr.o -o mbr.bin -Ttext 0x00007C00 --oformat binary, и я просто скопировал файлы, чтобы получить коды операций   -  person Willdorf    schedule 18.05.2016
comment
@davmac NASM выбирает значение по умолчанию для BITS на основе используемого формата объекта. С elf по умолчанию 32, с bin по умолчанию 16.   -  person Ross Ridge    schedule 18.05.2016
comment
@Willdorf можно использовать 32-битные инструкции, но код должен быть сгенерирован в 16-битном режиме. (32-битные инструкции будут сгенерированы с соответствующими префиксами размера операндов). Сейчас вы видите префиксы размера операндов (66h) там, где их быть не должно. (Процессор может быть в 16-битном или 32-битном режиме; в любом режиме префикс 66h переключится на альтернативный размер операнда. Загрузочный сектор будет выполняться в 16-битном режиме).   -  person davmac    schedule 18.05.2016
comment
Итак, nasm (компиляция / ссылка) генерирует хороший 16-битный код, но как я могу получить код для перехода на новый (7axx) адрес?   -  person Willdorf    schedule 18.05.2016
comment
Думаю, @Willdorf использует соответствующую директиву org. Или измените -Ttext 0x00007C00 в своей команде компоновщика. Вы утверждаете, что код находится по адресу 0x7C00, и он делает именно то, что вы просите ...   -  person davmac    schedule 18.05.2016
comment
Большое спасибо, ребята, я отправлю ответ с обновленным кодом   -  person Willdorf    schedule 18.05.2016


Ответы (3)


Как вы обнаружили, вы можете установить исходную точку ORG 0x7A00 для всего загрузчика. Это прекрасно работает. Код, копирующий загрузочный сектор в 0x7A00, не полагается ни на какие абсолютные метки, а только на родственные. Этот ответ - скорее мысленный эксперимент и другой подход к нему.

Что бы произошло, если бы мы хотели отобразить строку перед копией в качестве примера? Какие есть возможные варианты?

  1. NASM позволяет формату BIN (-f bin) иметь разделы, которые принимают виртуальная отправная точка (исходная точка) и физический адрес (начало). Этот метод слишком ограничивает расположение загрузчика.
  2. Используйте LD скрипт компоновщика. для определения макета загрузчика.
  3. Реорганизуйте код для использования ORG (исходной точки) 0x0000 и соответствующим образом установите регистры сегмента. См. Мой другой ответ на этот вопрос.

В этом ответе основное внимание уделяется варианту 2. Объяснение того, как работают сценарии LD компоновщика, слишком широко для Stackoverflow. LD manual - это лучший источник информации, и у него есть примеры. Идея в том, что мы позволяем размещать загрузчик внутри скрипта компоновщика. Мы можем настроить LMA (Load Memory Address), чтобы указать адрес памяти, по которому раздел будет загружен в память. VMA - это исходная точка для раздела. Все метки и адреса в разделе будут разрешены относительно его VMA.

Для удобства мы можем использовать раздел с определенным LMA, чтобы поместить загрузочную сигнатуру непосредственно в выходной файл, а не указывать ее в коде сборки. Мы также можем определить символы из сценария компоновщика, к которым можно получить доступ из кода сборки с помощью директивы NASM extern.

Одним из преимуществ всего этого является то, что вы можете определять разделы в своем коде сборки в любом порядке, а сценарий компоновщика будет переупорядочивать вещи. Вы также можете связать вместе несколько объектных файлов. Первым должен быть указан объектный файл, содержащий загрузочный код, который должен отображаться первым.

Макет этого скрипта компоновщика примерно выглядит следующим образом:

Non-relocatable portion of boot code (boot.text) Relative to an origin of 0x7c00
Non-relocatable portion of boot data (boot.data)
--------------------------------------- Word aligned
Relocatable portion of boot code (rel.text) - Relative to an origin of 0x7a00
Relocatable portion of boot data (rel.data)
Relocatable portion of partition data at offset 0x1b8 (partition.data)
---------------------------------------
Boot signature at offset 0x1fe

Скрипт компоновщика, который будет компоновщик этого загрузчика, может выглядеть примерно так:

ENTRY(_start);
OUTPUT(elf_i386);

SECTIONS
{
    /* Set the base of the main bootloader offsets */
    _bootbase = 0x7c00; /* Where bootloader initially is loaded in memory */
    _relbase  = 0x7a00; /* Address entire bootsector will be copied to
                           This linker script expects it to be word aligned */
    _partoffset = 0x1b8; /* Offset of UID and Partition data */
    _sigoffset  = 0x1fe; /* Offset of the boot signature word */


    /* SUBALIGN(n) in an output section will override the alignment
     * of any input section that is encontered */

    /* This is the boot loader code and data that is expected to run from 0x7c00 */
    .bootinit _bootbase : SUBALIGN(2)
    {
        *(boot.text);
        *(boot.data);
    }

    /* Note that referencing any data in the partition table will
     * only be usable from the code that is in the .bootrel section */

    /* Partition data */
    .partdata _relbase + _partoffset :
        AT(_bootbase + _partoffset) SUBALIGN(0)
    {
        *(partition.data);
    }

    /* Boot signature */
    .bootsig :
        AT(_bootbase + _sigoffset) SUBALIGN(0)
    {
        SHORT(0xaa55);
    }
    /* Length of region to copy in 16-bit words */
    _rel_length = 256;
    /* Address to copy to */
    _rel_start = _relbase; /* Word aligned start address */

    /* Code and data that will expect to run once relocated
     * is placed in this section. Aligned to word boundary.
     * This relocateable code and data will be placed right
     * after the .bootinit section in the output file */
    .bootrel _relbase + SIZEOF(.bootinit) :
        AT(_bootbase + SIZEOF(.bootinit)) SUBALIGN(2)
    {
        *(rel.text);
        *(rel.data);
    }
}

Отредактированная копия вашего кода с использованием этого сценария компоновщика и определенных в нем символов может выглядеть так:

BITS 16

extern _bootbase
extern _relbase
extern _rel_length
extern _rel_start

section boot.text
                            ; comment
global _start
_start:
    xor    cx, cx           ; Set segment registers to zero
    mov    es, cx
    mov    ds, cx
    mov    ss, cx
    mov    sp, 0x7A00       ; Stack
    cld

.copymsg:
    mov si, copymsg         ; Copy message
    mov ah, 0x0E            ; 0E TTY Output
    mov bx, 7               ; Page number
.dispcopy:
    lodsb                   ; Load next char
    test al, al             ; Compare to zero
    jz .end                 ; If so, end
    int 10h                 ; Display char
    jmp .dispcopy           ; Loop
.end:
    mov    di, _rel_start   ; Beginning of relocation point
    mov    si, _bootbase    ; Original location to copy from
    mov    cx, _rel_length  ; CX = words to copy
    rep movsw               ; Copy self to destination

    jmp    0:rel_entry      ; far JMP to copy of self

section rel.text
rel_entry:
    sti                     ; Enable interrupts

    mov si, successmsg      ; Error Message location
    mov ah, 0x0E            ; 0E TTY Output
    mov bx, 7               ; Page number
.disp:
    lodsb                   ; Load next char
    test al, al             ; Compare to zero
    je .end                 ; If so, end
    int 10h                 ; Display char
    jmp .disp               ; Loop

    cli                     ; Disable interrupts
.end:
    hlt                     ; CPU hlt
    jmp .end                ; infinte loop

section rel.data
successmsg db 10,'Success!',13,0

section boot.data
copymsg db 10,'Before copy!',13,0

section partition.data
UID db 0xf5,0xbf,0x0f,0x18  ;Unique Disk ID

BLANK times 2 db 0

PT1 db 0x80,0x20,0x21,0x00,0x0C,0x50,0x7F,0x01
    db 0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D
PT2 times 16 db 0
PT3 times 16 db 0
PT4 times 16 db 0

В качестве эксперимента, чтобы убедиться, что код в разделе boot.text может получить доступ к данным в boot.data, я показываю строку перед копией. Затем я выполняю FAR JMP для перемещенного кода. Перемещенный код отображает строку успеха.

Я изменил код, чтобы не использовать 32-битные регистры, такие как ESI, поскольку вы будете выполнять этот код в реальном режиме. Я также изменил ваш бесконечный цикл, чтобы использовать инструкцию HLT .

Код и сценарий компоновщика можно изменить, чтобы копировать только с начала перемещенных данных до 512-го байта, но это выходит за рамки этого ответа.


Взгляд на разборку

Раздел .bootinit, имеющий исходную точку 0x7c00, представлен ниже. Это фрагмент OBJDUMP этого раздела (без данных для краткости):

Disassembly of section .bootinit:

00007c00 <_start>:
    7c00:       31 c9                   xor    cx,cx
    7c02:       8e c1                   mov    es,cx
    7c04:       8e d9                   mov    ds,cx
    7c06:       8e d1                   mov    ss,cx
    7c08:       bc 00 7a                mov    sp,0x7a00
    7c0b:       fc                      cld

00007c0c <_start.copymsg>:
    7c0c:       be 2e 7c                mov    si,0x7c2e
    7c0f:       b4 0e                   mov    ah,0xe
    7c11:       bb 07 00                mov    bx,0x7

00007c14 <_start.dispcopy>:
    7c14:       ac                      lods   al,BYTE PTR ds:[si]
    7c15:       84 c0                   test   al,al
    7c17:       74 04                   je     7c1d <_start.end>
    7c19:       cd 10                   int    0x10
    7c1b:       eb f7                   jmp    7c14 <_start.dispcopy>

00007c1d <_start.end>:
    7c1d:       bf 00 7a                mov    di,0x7a00
    7c20:       be 00 7c                mov    si,0x7c00
    7c23:       b9 00 01                mov    cx,0x100
    7c26:       f3 a5                   rep movs WORD PTR es:[di],WORD PTR ds:[si]
    7c28:       ea 3e 7a 00 00          jmp    0x0:0x7a3e

Все адреса VMA в левом столбце выглядят правильно настроенными относительно исходной точки 0x7c00. FAR JUMP (jmp 0x0:0x7a3e) также переместился в то место, где все было перемещено (скопировано). Аналогичный сокращенный дамп раздела .bootrel выглядит так:

Disassembly of section .bootrel:

00007a3d <rel_entry-0x1>:
        ...

00007a3e <rel_entry>:
    7a3e:       fb                      sti
    7a3f:       be 54 7a                mov    si,0x7a54
    7a42:       b4 0e                   mov    ah,0xe
    7a44:       bb 07 00                mov    bx,0x7

00007a47 <rel_entry.disp>:
    7a47:       ac                      lods   al,BYTE PTR ds:[si]
    7a48:       3c 00                   cmp    al,0x0
    7a4a:       74 05                   je     7a51 <rel_entry.end>
    7a4c:       cd 10                   int    0x10
    7a4e:       eb f7                   jmp    7a47 <rel_entry.disp>
    7a50:       fa                      cli

00007a51 <rel_entry.end>:
    7a51:       f4                      hlt
    7a52:       eb fd                   jmp    7a51 <rel_entry.end>

VMA в левом столбце соответствует началу 0x7A00, что правильно. Инструкция mov si,0x7a54 - это абсолютный адрес, близкий к памяти, и он правильно закодирован для ссылки на successmsg адрес (я вырезал данные для краткости, чтобы они не отображались).

Записи:

00007a3d <rel_entry-0x1>:
        ...

Информация, связанная с выравниванием раздела .bootrel по четной границе слова. С этим скриптом компоновщика rel_entry всегда будет иметь четный адрес.


Компиляция и связывание этого загрузчика

Самый простой способ - использовать эти команды:

nasm -f elf32 -o boot.o boot.asm
ld -melf_i386 -Tlinker.ld -o boot.bin --oformat=binary boot.o

Следует отметить, что мы используем формат ELF32 с NASM, а не BIN. Затем LD используется для создания двоичного файла boot.bin, который должен быть 512-байтовым образом загрузочного сектора. linker.ld - это имя файла сценария компоновщика.

Если вы хотите получить дамп объекта, вы можете использовать эти команды для сборки и связывания:

nasm -f elf32 -o boot.o boot.asm
ld -melf_i386 -Tlinker.ld -o boot.elf boot.o
objcopy -O binary boot.elf boot.bin

Отличие от первого метода в том, что мы не используем параметр --oformat=binary с LD. В результате будет сгенерировано изображение ELF32, которое будет помещено в выходной файл boot.elf. Мы не можем использовать boot.elf напрямую в качестве загрузочного образа, поэтому мы используем OBJCOPY для преобразования файла ELF32 в двоичный файл с именем boot.bin. Полезность этого способа можно увидеть, если мы воспользуемся такой командой для сброса содержимого и дизассемблирования файла ELF:

objdump boot.elf -Mintel -mi8086 -Dx
  • -D вариант разобрать все
  • -x выводить заголовки
  • -mi8086 дизассемблировать как 16-битный код 8086
  • -Mintel дизассемблирование должно выполняться с использованием синтаксиса INTEL, а не синтаксиса по умолчанию ATT.
person Michael Petch    schedule 19.05.2016
comment
Спасибо, что есть много полезной информации о скриптах компоновщика, компиляции и дизассемблировании! - person Willdorf; 19.05.2016

Скомпилировано и связано с использованием: nasm -f bin -o mbr.bin mbr.asm

[BITS 16]
ORG 0x00007a00
                            ; opcodes       comment
global _start
_start:
    xor    cx, cx           ; 31c9          Set segment registers to zero
    mov    es, cx           ; 8ec1
    mov    ds, cx           ; 8ed9
    mov    ss, cx           ; 8ed1
    mov    sp, 0x7A00       ; bc007a        Stack
    mov    di, sp           ; 89e7          Bottom of relocation point
    mov    esi, 0x00007C00  ; 66be007c0000  Original location
    cld                     ; fc
    mov    ch, 1            ; b501          CX = 256
    rep movsw               ; f3a5          Copy self to 0:7A00
    jmp    0:continue       ; ea1d7a0000    near JMP to copy of self

continue:
    sti                     ; fb

ERROR:
    mov esi, errormsg       ; 66be357a0000  Error Message location
    mov ah, 0x0E            ; b40e          0E TTY Output
    mov bx, 7               ; bb0700        Page number
disp:
    lodsb                   ; ac            Load next char
    cmp al, 0x00            ; 3c00          Compare to zero
    je end                  ; 7404          If so, end
    int 10h                 ; cd10          Display char
    jmp disp                ; ebf6          Loop

end:
    nop                     ; 90            Do Nothing
    jmp end                 ; ebfd          infinte loop

errormsg db 10,'YOU MESSED UP!',13,0

times (0x1b8 - ($-$$)) nop  ; 90909090...   Padding

UID db 0xf5,0xbf,0x0f,0x18  ;Unique Disk ID

BLANK times 2 db 0

PT1 db 0x80,0x20,0x21,0x00,0x0C,0x50,0x7F,0x01
PT1more db 0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D
PT2 times 16 db 0
PT3 times 16 db 0
PT4 times 16 db 0

BOOTSIG dw 0xAA55           ;Boot Signature

Вывод hexdump -C mbr.bin:

00000000  31 c9 8e c1 8e d9 8e d1  bc 00 7a 89 e7 66 be 00  |1.........z..f..|
00000010  7c 00 00 fc b5 01 f3 a5  ea 1d 7a 00 00 fb 66 be  ||.........z...f.|
00000020  35 7a 00 00 b4 0e bb 07  00 ac 3c 00 74 04 cd 10  |5z........<.t...|
00000030  eb f7 90 eb fd 0a 59 4f  55 20 4d 45 53 53 45 44  |......YOU MESSED|
00000040  20 55 50 21 0d 00 90 90  90 90 90 90 90 90 90 90  | UP!............|
00000050  90 90 90 90 90 90 90 90  90 90 90 90 90 90 90 90  |................|
*
000001b0  90 90 90 90 90 90 90 90  f5 bf 0f 18 00 00 80 20  |............... |
000001c0  21 00 0c 50 7f 01 00 08  00 00 b0 43 f9 0d 00 00  |!..P.......C....|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200
person Willdorf    schedule 18.05.2016
comment
Нет причин использовать ESI вместо SI, поскольку 16-битные инструкции MOVSW и LODSB используют только 16-битный регистр SI. - person Ross Ridge; 19.05.2016
comment
Почему вы устанавливаете ORG на 0x7a00. Это должно быть 0x7c00. - person David Hoelzer; 19.05.2016
comment
Кроме того, вы также можете установить sp в 0x7c00. Поскольку стек растет вниз, на самом деле нет причин не делать этого. Не нужно ставить между ними ловушку. - person David Hoelzer; 19.05.2016
comment
@DavidHoelzer: Он установил для него значение 0x7a00, потому что код, который делает копию, можно перемещать (не имеет значения, какая организация находится в этот момент). После завершения копирования остальная часть его кода теперь копируется в 0x7a00, поэтому он должен работать должным образом. Это довольно необычно, но в этом случае должно работать. - person Michael Petch; 19.05.2016
comment
@DavidHoelzer: по поводу его SP. Поскольку он скопировал 512 байтов загрузчика из 0x7c00 в 0x7a00, похоже, он решил разместить стек чуть ниже места назначения, куда он скопировал - что кажется разумным. Я предполагаю, что он строит цепной погрузчик. - person Michael Petch; 19.05.2016
comment
Вы правы, @MichaelPetch - Честно говоря, я не смотрел дальше странных корректировок ORG и SP. - person David Hoelzer; 19.05.2016

Другая альтернатива для перемещения MBR / загрузчика, которая не включает сценарий компоновщика, но позволяет перемещать ВСЕ код и данные в загрузчике в любое место, кроме пары сегмент: смещение = сегмент: 0x0000. Это не ограничивает копирование загрузчика в 64 КБ памяти. Это включает в себя использование математики сегментов и несколько незначительных изменений кода. Я собираюсь использовать ваш исходный код с некоторой очисткой плюс инструкции, которые я добавил в свой первый ответ, который выводит строку перед JMP.

Помните, что в сегментации в реальном режиме x86 физический адрес определяется содержимым регистра сегмента. и смещение. Расчет выглядит следующим образом:

Physical address = (segment << 4) + offset

В случае загрузчиков многие люди помещают 0x0000 в DS и ES, что вы и сделали в своем коде. В исходном коде использовалось ORG 0x7c00 (что фактически является смещением). Когда вы берете сегмент и смещение и вычисляете физический адрес, вы получаете:

Physical address = (0x0000 << 4) + 0x7c00 = 0x07c00

0x07c00 - это физический адрес загрузчика, который был помещен в память BIOS. Однако обычно существует более одного способа выразить один физический адрес в виде пары сегмент: смещение.

В качестве примера, если мы используем сегмент 0x07c0 и ORG (смещение) 0x0000, мы обнаружим, что уравнение дает:

Physical address = (0x07c0 << 4) + 0x0000 = 0x07c00

Что, если установить исходные сегментные регистры на 0x07c0 и использовать директиву ORG 0x0000 в загрузчике. Это сработает? Да, было бы. Компоновщик и NASM действительно не имеют представления о том, как пользователь устанавливает регистры сегментов. Когда NASM создает ссылки на NEAR абсолютные адреса, предполагается, что сегменты будут правильно настроены разработчиком во время выполнения.

Если мы используем ORG 0x0000, то для копирования из 0x7c00 в 0x7a00 мы можем поместить 0x07c0 в DS и SI в 0 (0x07c0: 0x0000). Мы можем установить ES равным 0x07a0 и DI равным 0 (0x07a0: 0x0000). CX будет иметь значение 256 (мы будем копировать слова). Это скопировало бы 512 байт памяти с физического адреса 0x07c00 на 0x07a00. После копирования всего загрузчика мы используем FAR JMP, чтобы установить CS на сегмент, указанный в переходе, и установить указатель инструкции (IP) на смещение метки.

Как только мы переходим в память (область, начинающуюся с физического адреса 0x7a00), куда был скопирован загрузчик, мы соответствующим образом настраиваем сегментные регистры. Итак, в нашем случае мы установили регистры DS и ES после перехода на 0x07a0. Это должно позволить всему коду и данным загрузчика работать должным образом в новом месте.

Ниже приведен код, который будет делать что-то похожее на то, что было предложено выше:

BITS 16
ORG 0x0000

global _start
_start:
    xor    bp, bp           
    mov    ss, bp           ; Set SS segment register to zero
    mov    sp, 0x7A00       ; Set stack to SS:SP=0x0000:0x7A00
    mov    dx, 0x07C0
    mov    ds, dx           ; DS = 0x07C0 - Segment:Offset = 0x07c0:0x0000 = 0x07C00
    sub    dx, 0x20         ; DX = 0x07C0-0x20=0x07A0
    mov    es, dx           ; ES = 0x07A0 - Segment:Offset = 0x07a0:0x0000 = 0x07A00
    cld

    ; Print message before the copy
    mov si, copymsg         ; Copy message (DS:[copymsg])
    call outputstr

    ; Copy 256 words of memory from 0x07c00 to 0x07a00
    mov    di, bp           ; Destination is ES:DI = 0x07a0:0x0000 = 0x07a00
    mov    si, bp           ; Source is DS:SI = 0x07c0:0x0000 = 0x07c00
    mov    cx, 256          ; CX = 256 words to copy
    rep movsw               ; Copy self to destination

    jmp    0x07A0:rel_entry
                            ; far JMP jumps to phys address 0x07a00, sets
                            ; CS = 0x07A0 and IP = rel_entry.

rel_entry:
    sti                     ; Enable interrupts
    mov    ds, dx           ; DS=ES=0x07A0
                            ; ES already 0x07A0 before the jump, no need to set again
.success:
    ; Print the "Success!" message
    mov si, successmsg      ; Success Message location (DS:[successmsg])
    call outputstr

    cli                     ; Disable interrupts
.end:
    hlt                     ; CPU hlt
    jmp .end                ; infinite loop

; Function: outputstr
; Inputs:
;    SI = address of string
; Outputs:
;    None
; Registers destroyed:
;    AX, BX, SI
;
outputstr:
    mov ah, 0x0E            ; 0E TTY Output
    mov bx, 7               ; Page number
.disp:
    lodsb                   ; Load next char from DS:[SI], SI+=2
    test al, al             ; Compare to zero
    je .end                 ; If so, end
    int 0x10                ; Display char
    jmp .disp               ; Loop
.end:
    ret

successmsg db 10,'Success!',13,0
copymsg db 10,'Before copy!',13,0

times (0x1b8 - ($-$$)) db 0x00 ; Padding

UID     db 0xf5,0xbf,0x0f,0x18 ; Unique Disk ID
BLANK   times 2 db 0
PT1     db 0x80,0x20,0x21,0x00,0x0C,0x50,0x7F,0x01
PT1more db 0x00,0x08,0x00,0x00,0xb0,0x43,0xF9,0x0D
PT2     times 16 db 0
PT3     times 16 db 0
PT4     times 16 db 0

BOOTSIG dw 0xAA55              ;Boot Signature

Этот загрузчик вызывает функцию outputstr для вывода сообщений на консоль как в коде перед копированием, так и после перехода к перемещенному коду. Это работает, потому что в обоих случаях все метки по-прежнему с одинаковым смещением, изменился только сегмент.

Загрузчик, указанный выше, можно собрать с помощью такой команды:

nasm -f bin boot.bin boot.asm
person Michael Petch    schedule 22.05.2016