Ml and ml64 command-line reference

Программа HelloWorld

Ниже показан исходный код программы HelloWorld на языке ассемблера Flat Assembler (FASM), которая будет стартовать на голом железе в реальном режиме работы x86-совместимого процессора (комментарии пишу на английском — привычка, которая возникла у меня в связи с проблемами с кодировками кириллицы).

; HelloWorld real modeuse16               ; generate 16-bit code
org 7C00h           ; the code starts at 0x7C00 memory addressstart
    jmp far dword 0x0000entr ; makes CS=0, IP=0x7c00
entr
    xor  ax, ax     ; ax = 0
    mov  ds, ax     ; setup data segment ds=ax=0
    cli             ; when we set up stack we need disable interrupts because stack is involved in interrupts handling
    mov  ss, ax     ; setup stack segment ss=ax=0
    mov  sp, 0x7C00 ; stack will grow starting from 0x7C00 memory address
    sti             ; enable interrupts
 
    mov  si, message
    cld             ; clear direction flag (DF is a flag used for string operations)
    mov  ah, 0Eh    ; BIOS function index (write a charachter to the active video page)
puts_loop
    lodsb           ; load to al the next charachter located at address in memory (si is incremented automatically because the direction flag DF = 0)
    test al, al     ; zero in al denotes the end of the string
    jz   puts_loop_exit
    int  10h        ; call BIOS standard video service’s function
    jmp  puts_loop
puts_loop_exit
    cli             ; disable interrupts before halting the processor
    hlt             ; halt the processor
    ;jmp  $         ; alternatively to hlt we could run an infinite loop
 
message db ‘Hello World!’,
finish
    ; The size of a disk sector is 512 bytes. Boot sector signature occupies the two last bytes.
    ; The gap between the end of the source code and the boot sector signature is filled with zeroes.
    times 510-finish+start db
    db 55h, 0AAh    ; boot sector signature

32-Bit-Adress Modus (Adressgröße außer Kraft Setzung)32-Bit Address Mode (Address Size Override)

MASM gibt die Adress Größen Überschreitung von 0x67 aus, wenn ein Speicher Operand 32-Bit-Register umfasst.MASM emits the 0x67 address size override if a memory operand includes 32-bit registers. In den folgenden Beispielen wird beispielsweise die außer Kraft setzung der Adressgröße ausgegeben:For example, the following examples cause the address size override to be emitted:

MASM geht davon aus, dass eine 64-Bit-Adressierung beabsichtigt ist, wenn eine 32-Bit-Verschiebung allein als Speicher Operand angezeigt wird.MASM assumes that if a 32-bit displacement appears alone as a memory operand, 64-bit addressing is intended. Es gibt zurzeit keine Unterstützung für die 32-Bit-Adressierung mit diesen Operanden.There is currently no support for 32-bit addressing with such operands.

Zum Schluss generiert das Mischen von Register Größen in einem Arbeitsspeicher Operanden, wie im folgenden Code gezeigt, einen Fehler.Finally, mixing register sizes within a memory operand, as demonstrated in the following code, generates an error.

Версии MASM

Хотя MASM больше не является коммерческим продуктом, Microsoft продолжает поддерживать исходный код, используемый и в других продуктах Microsoft. С тех пор как Microsoft прекратила продавать MASM отдельно, было выпущено несколько обновлений к производственной линии MASM 6.x (последнее обновление — версия 6.15, которая была включена в Visual C++ 6.0), а после этого — MASM 7.0 в составе Visual C++ .NET 2002, MASM 7.1 в составе Visual C++ .NET 2003, MASM 8.0 в составе Visual C++ 2005 и MASM 9.0 в составе Visual C++ 2008, поддерживающие платформу x64. С версии 11 MASM снова выходит как независимый продукт, имеется 2 среды разработки обычный (qedit) и для юникода (uniedit).

История

В начале 1990-х годов альтернативные ассемблеры, вроде Borland TASM и свободного ассемблера NASM, начали отбирать часть доли рынка MASM. Однако, два события в конце 1990-х позволили MASM сохранить большую часть своей доли: сначала Microsoft прекратила продавать MASM как коммерческий продукт и начала распространять его бесплатно как часть DDK (англ. Device Driver Kit — набор для создания драйверов). Во-вторых, благодаря пакету MASM32 и обучающим программам Iczelion’а Win32 оказалось, что программирование на MASM возможно и в среде Microsoft Windows. В 2000 году MASM 6.15 был выпущен как часть пакета разработки Visual C++ и все версии Visual C++ после 6.0 включали в себя версию MASM, равную версии Visual C++. Позже в Visual C++ 2005 появилась 64-разрядная версия MASM. Вместе с большим сообществом программистов MASM эти события помогли остановить снижение популярности MASM по сравнению с другими ассемблерами. Сегодня MASM продолжает использоваться на платформе Win32, несмотря на конкуренцию с новыми продуктами, такими как NASM, fasm, TASM, HLASM.

Aggiungere un file in linguaggio assembler a un progetto di C++ Visual StudioAdd an assembler-language file to a Visual Studio C++ project

Il sistema di progetto di Visual Studio supporta i file in linguaggio Assembler compilati C++ usando MASM nei progetti.The Visual Studio project system supports assembler-language files built by using MASM in your C++ projects. È possibile creare file di origine in linguaggio Assembler x64 e compilarli in file oggetto utilizzando MASM, che supporta completamente x64.You can create x64 assembler-language source files and build them into object files by using MASM, which supports x64 fully. È quindi possibile collegare questi file oggetto al codice C++ compilato per le destinazioni x64.You can then link these object files to your C++ code built for x64 targets. Questo è un modo per ovviare alla mancanza di un assembler inline x64.This is one way to overcome the lack of an x64 inline assembler.

Per aggiungere un file in linguaggio assembler a un progetto di Visual C++ Studio esistenteTo add an assembler-language file to an existing Visual Studio C++ project

Установка и запуск виртуальной машины Bochs

Я имел дело всего с тремя виртуальными машинами: Oracle VM VirtualBox, VMware Workstation Player и Bochs. Bochs хотя и обладает очень скромным графическим интерфейсом, хорош тем, что он легкий и может выполнять машинный код пошагово, т. е. с ним можно производить отладку исходного кода программы. Скачиваем с официального сайта программу установки (файл с расширением .exe) и запускаем его (все настройки я оставлял по-умолчанию). После установки запускаем Bochs. Возникает диалоговое окно Bochs Start Menu. В списке Edit Options выбираем Disk & Boot и нажимаем кнопку Edit. Открывается диалог Bochs Disk Options. На вкладке Floppy Options в группе First Floppy Drive устанавливаем следующие настройки:

Type of floppy drive 3.5 1.44M
First floppy image/device жмем Browse и выбираем ранее созданный нами файл floppy.img
Type of floppy media 1.44M
Write Protection галка снята
Status inserted

Жмем кнопку OK. В окне Bochs Start Menu жмем кнопку Start (предварительно можно сохранить сделанные нами настройки в текстовом файле с расширением .bxrc нажав кнопку Save, впоследствии их можно будет загрузить, нажав кнопку Load). Открывается окно, в котором мы видим надпись

Hello World!

Первая программа на ассемблере

Cтандартный «Hello World». Для вывода результатов я решила сразу использовать окна windows, а не консоль. По простой причине — так красивее.

.386
.model flat, stdcall
option casemap: none

include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc

includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

.data
msg_title db «Title», 0
msg_message db «Hello world», 0

.code
start:
invoke MessageBox, 0, addr msg_message, addr msg_title, MB_OK
invoke ExitProcess, 0
end start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

.386

.model flat,stdcall

option casemapnone

includemasm32includewindows.inc

includemasm32includeuser32.inc

includemasm32includekernel32.inc

includelibmasm32libuser32.lib

includelibmasm32libkernel32.lib

.data

msg_title db»Title»,

msg_message db»Hello world»,

.code

start

invoke MessageBox,,addr msg_message,addr msg_title,MB_OK

invoke ExitProcess,

endstart

Запускаем редактор C:/masm32/qeditor.exe . Вводим текст программы. Сохраняем. При сохранении обязательно надо указать вместе с именем файла расширение «.asm» . Выбираем пункт меню «Project» -> «Build All». Если в коде нет ошибок, программа будет скомпилирована и рядом с файлом исходного кода появится симпатичный exe-файл.

Вторая программа на ассемблере

Эта программа суммирует значение переменных и выводит результат.

.486
.model flat, stdcall
option casemap: none

include /masm32/include/windows.inc
include /masm32/include/user32.inc
include /masm32/include/kernel32.inc

includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

include /masm32/macros/macros.asm
uselib masm32, comctl32, ws2_32

.data
msg_title db «Title», 0
A DB 1h
B DB 2h
buffer db 128 dup(?)
format db «%d»,0

.code
start:

MOV AL, A
ADD AL, B

invoke wsprintf, addr buffer, addr format, eax
invoke MessageBox, 0, addr buffer, addr msg_title, MB_OK

invoke ExitProcess, 0

end start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

.486

.model flat,stdcall

option casemapnone

includemasm32includewindows.inc

includemasm32includeuser32.inc

includemasm32includekernel32.inc

includelibmasm32libuser32.lib

includelibmasm32libkernel32.lib

includemasm32macrosmacros.asm

uselib masm32,comctl32,ws2_32

.data

msg_title db»Title»,

ADB1h

BDB2h

buffer db128dup(?)

format db»%d»,

.code

start

MOV AL,A

ADD AL,B

invoke wsprintf,addr buffer,addr format,eax

invoke MessageBox,,addr buffer,addr msg_title,MB_OK

invoke ExitProcess,

endstart

Создание образа дискеты

Операционная система должна загружаться с какого-то носителя (жесткого диска, CD-ROM, флешки, дискеты и т. д.). Создадим образ загрузочной дискеты с нашим машинным кодом. Виртуальная машина сможет загрузиться с виртуальной дискеты, используя этот образ. Создавать образ дискеты умеет unix’овая утилита под названием dd. Существует версия этой утилиты под Windows. Если вы пользуетесь средой Cygwin, то утилита dd есть в ее составе. Итак, запускаем командную строку:

dd if=»/dev/zero» of=»floppy.img» bs=1024 count=1440
dd if=»HelloWorld.bin» of=»floppy.img» conv=notrunc

1-я команда создает образ дискеты floppy.img и заполняет его нулями, 2-я — записывает в самое начало образа нашу программу.

Modalità Indirizzo 32 bit (override delle dimensioni degli indirizzi)32-Bit Address Mode (Address Size Override)

MASM genera l’override della dimensione dell’indirizzo 0x67 se un operando di memoria include registri a 32 bit.MASM emits the 0x67 address size override if a memory operand includes 32-bit registers. Gli esempi seguenti, ad esempio, determinano la creazione dell’override delle dimensioni dell’indirizzo:For example, the following examples cause the address size override to be emitted:

MASM presuppone che se lo spostamento a 32 bit viene visualizzato da solo come operando di memoria, è previsto l’indirizzamento a 64 bit.MASM assumes that if a 32-bit displacement appears alone as a memory operand, 64-bit addressing is intended. Attualmente non è disponibile alcun supporto per l’indirizzamento a 32 bit con tali operandi.There is currently no support for 32-bit addressing with such operands.

Infine, combinando le dimensioni del registro all’interno di un operando di memoria, come illustrato nel codice seguente, viene generato un errore.Finally, mixing register sizes within a memory operand, as demonstrated in the following code, generates an error.

32-разрядный режим адреса (переопределение размера адреса)32-Bit Address Mode (Address Size Override)

Компилятор MASM выдает переопределение размера адреса 0x67, если операнд памяти включает 32-разрядные регистры.MASM emits the 0x67 address size override if a memory operand includes 32-bit registers. Например, в следующих примерах вызывается переопределение размера адреса.For example, the following examples cause the address size override to be emitted:

В компиляторе MASM предполагается, что если 32-разрядное смещение отображается как операнд в памяти, то используется 64-разрядная адресация.MASM assumes that if a 32-bit displacement appears alone as a memory operand, 64-bit addressing is intended. В настоящее время не поддерживается 32-разрядная адресация с такими операндами.There is currently no support for 32-bit addressing with such operands.

Наконец, смешивание размеров регистров в пределах операнда памяти, как показано в следующем коде, приводит к ошибке.Finally, mixing register sizes within a memory operand, as demonstrated in the following code, generates an error.

Что такое ассемблер?

Само слово ассемблер (assembler) переводится с английского как «сборщик». На самом деле так называется программа-транслятор, принимающая на входе текст, содержащий условные обозначения машинных команд, удобные для человека, и переводящая эти обозначения в последовательность соответствующих кодов машинных команд, понятных процессору. В отличие от машинных команд, их условные обозначения, называемые также мнемониками, запомнить сравнительно легко, так как они представляют собой сокращения от английских слов. В дальнейшем мы будем для простоты именовать мнемоники ассемблерными командами. Язык условных обозначений и называется языком ассемблера.

На заре компьютерной эры первые ЭВМ занимали целые комнаты и весили не одну тонну, имея объем памяти с воробьиный мозг, а то и того меньше. Единственным способом программирования в те времена было вбивать программу в память компьютера непосредственно в цифровом виде, переключая тумблеры, проводки и кнопочки. Число таких переключений могло достигать нескольких сотен и росло по мере усложнения программ. Встал вопрос об экономии времени и денег. Поэтому следующим шагом в развитии стало появление в конце сороковых годов прошлого века первого транслятора-ассемблера, позволяющего удобно и просто писать машинные команды на человеческом языке и в результате автоматизировать весь процесс программирования, упростить, ускорить разработку программ и их отладку. Затем появились языки высокого уровня и компиляторы (более интеллектуальные генераторы кода с более понятного человеку языка) и интерпретаторы (исполнители написанной человеком программы на лету). Они совершенствовались, совершенствовались — и, наконец, дошло до того, что можно просто программировать мышкой.

Таким образом, ассемблер — это машинно ориентированный язык программирования, позволяющий работать с компьютером напрямую, один на один. Отсюда и его полная формулировка — язык программирования низкого уровня второго поколения (после машинного кода). Команды ассемблера один в один соответствуют командам процессора, но поскольку существуют различные модели процессоров со своим собственным набором команд, то, соответственно, существуют и разновидности, или диалекты, языка ассемблера. Поэтому использование термина «язык ассемблера» может вызвать ошибочное мнение о существовании единого языка низкого уровня или хотя бы стандарта на такие языки. Его не существует. Поэтому при именовании языка, на котором написана конкретная программа, необходимо уточнять, для какой архитектуры она предназначена и на каком диалекте языка написана. Поскольку ассемблер привязан к устройству процессора, а тип процессора жестко определяет набор доступных команд машинного языка, то программы на ассемблере не переносимы на иную компьютерную архитектуру.

Поскольку ассемблер всего лишь программа, написанная человеком, ничто не мешает другому программисту написать свой собственный ассемблер, что часто и происходит

На самом деле не так уж важно, язык какого именно ассемблера изучать. Главное — понять сам принцип работы на уровне команд процессора, и тогда не составит труда освоить не только другой ассемблер, но и любой другой процессор со своим набором команд

Добавление файла ассемблерного языка в проект Visual Studio C++Add an assembler-language file to a Visual Studio C++ project

Система проектов Visual Studio поддерживает файлы ассемблерного языка, созданные с помощью MASM в ваших C++ проектах.The Visual Studio project system supports assembler-language files built by using MASM in your C++ projects. Вы можете создавать файлы исходного кода на языке ассемблера x64 и создавать их в объектных файлах с помощью MASM, который поддерживает 64-разрядную версию.You can create x64 assembler-language source files and build them into object files by using MASM, which supports x64 fully. Затем эти файлы объектов можно связать с C++ кодом, созданным для платформ x64.You can then link these object files to your C++ code built for x64 targets. Это один из способов преодоления отсутствия встроенного ассемблера x64.This is one way to overcome the lack of an x64 inline assembler.

Добавление файла ассемблерного языка в существующий проект Visual Studio C++To add an assembler-language file to an existing Visual Studio C++ project

Коэффициенты

a
b
c
d
e
f
p
0.50

0.05*100000=500
0.42
-0.42
0.42
0.42

0.2
0.4*100000=4000
0.42
0.42
-0.42
0.42

0.2
0.4*100000=4000
0.1

0.1

0.2
0.15*10000=1500

Код (ASM):

  1. include win64a.inc
  2. include msvcrt.inc
  3. includelib gdi32.lib
  4. includelib msvcrt.lib
  5. IMAGE_BASE equ 400000h
  6. cdXPos          equ 131
  7. cdYPos          equ 217
  8. cdYSize         equ 430
  9. cdXSize         equ 640
  10. movr macro x,y
  11. mov x,
  12. org $-4
  13. dd y
  14. endm
  15. .code
  16. WndProc proc hWndQWORD,MsgQWORD,wParamQWORD,lParamQWORD
  17. local psPAINTSTRUCT
  18. local hdcqword
  19. local hOldBmpqword
  20. local hGDITmpqword
  21. local hOldDIBqword
  22. local hBackDCqword
  23. local hMainDIBqword
  24. local bufBMPqword
  25. local idword
  26. local xdword
  27. local ydword
  28. local adword
  29. local bdword
  30. local cdword
  31. local ddword
  32. local edword
  33. local fdword
  34. local NewXqword
  35. local NewYqword
  36. local XCCqword
  37. local YCCqword
  38. push rbp
  39. mov ebp, esp
  40. sub esp,(50h+sizeof PAINTSTRUCT+11*8+9*4+15)and(-16)
  41. mov hWnd,rcx
  42. cmp edx, WM_DESTROY
  43. jz wmDESTROY
  44.         cmp edx, WM_PAINT
  45. jz wmPAINT
  46.         cmp edx, WM_CREATE
  47. jz wmCREATE
  48. leave
  49.         jmp DefWindowProc
  50. ; —————————————————————————
  51. wmDESTROY::mov rdx,hOldBmp
  52. mov rcx,hBackDC
  53. call SelectObject
  54. mov hGDITmp,rax
  55. mov rcx,bufBMP
  56. call DeleteObject
  57. mov rcx,hBackDC
  58. call DeleteDC
  59. mov rdx,hOldDIB
  60. mov rcx,bufDIBDC
  61. call SelectObject
  62. mov hGDITmp,rax
  63. mov rcx,bufDIBDC
  64. call DeleteDC
  65. mov rcx,hMainDIB
  66. call DeleteObject
  67. mov rcx,hWnd
  68. call DestroyWindow
  69. xor ecx,ecx ; nExitCode
  70. call ExitProcess
  71. ; —————————————————————————
  72. wmPAINTlea edx,ps
  73. call BeginPaint
  74. mov hdc,rax
  75. mov qword ptr rsp+40h,SRCCOPY; rop
  76.         xor edx,edx ; x
  77. mov rsp+38h,rdx ; y1
  78. mov rsp+30h,rdx ; x1
  79. mov rax,bufDIBDC ; hdcSrc
  80.         mov rsp+28h,rax
  81. mov rax,cdYSize
  82. mov rsp+20h,rax
  83. mov r9,cdXSize ; cx
  84. xor r8d,r8d ; cy
  85. mov rcx,hdc ; hdc
  86. call BitBlt
  87. lea edx,ps
  88. mov rcx,hWnd
  89. call EndPaint
  90. jmp wmBYE
  91. ; —————————————————————————
  92. wmCREATEmov rcx,hWnd
  93. call GetDC
  94. mov hdc,rax
  95.         mov rcx,rax ; HDC
  96. call CreateCompatibleDC
  97. mov bufDIBDC,rax
  98. mov rsp+28h,rbx
  99. mov rsp+20h,rbx
  100. mov r9d,offset pMainDIB
  101. mov r8d,DIB_RGB_COLORS
  102. mov edx,offset bi
  103. mov rcx,hdc
  104.         call CreateDIBSection
  105. mov hMainDIB,rax
  106. mov rdx,rax; hMainDIB
  107. mov rcx,bufDIBDC
  108. call SelectObject
  109.         mov rax,hOldDIB
  110. mov rdx,hdc
  111. mov rcx,hWnd      
  112.         call ReleaseDC;   // Libera device context      
  113. ;Init———————————————————
  114. ;a     b     c    d    e f   p
  115. ;0     0     0    0.50 0 0   0.05         500
  116. ;0.42 -0.42  0.42 0.42 0 0.2 0.4         4000
  117. ;0.42  0.42 -0.42 0.42 0 0.2 0.4         4000
  118. ;0.1   0     0    0.1  0 0.2 0.15*10000= 1500
  119. mov XCC,rbx
  120. mov YCC,rbx
  121. mov i,150000
  122. bucle  call rand
  123. mov ecx,10000
  124. xor edx,edx
  125. div ecx
  126. cmp edx,500
  127. jg @f
  128. mov a,ebx
  129. mov b,ebx
  130. mov c,ebx; c = 0
  131. movr d,0.5
  132. mov f,ebx
  133. jmp fin
  134. @@ cmp edx,4500
  135. jg @f
  136. movr a,0.42
  137. movr b,-0.42
  138. movr c,0.42
  139. movr d,0.42
  140. movr f,0.2  
  141. jmp fin
  142. @@ cmp edx,8500
  143. jg @f
  144. movr a,0.42
  145. movr b,0.42
  146. movr c,-0.42
  147. movr d,0.42
  148. movr f,0.2
  149. jmp fin
  150. @@ movr a,0.1
  151. mov b,ebx
  152. mov c,ebx
  153. movr d,0.1
  154. movr f,0.2
  155. fin fld a       ;st(0) = a
  156. fmul XCC     ;st(0) = a * XCC
  157. fld b       ;st(0) = b
  158. fmul YCC     ;st(0) = b * YCC
  159. faddp        ;st(0) = a * XCC + b * YCC
  160. fst NewX    ;NewX  = a * XCC + b * YCC
  161. fmul Scale ;st(0) = NewX * 900
  162.         fadd ShiftX ;st(0) = NewX * 900 + 320
  163. fistp x       ;x = (int) (NewX * 900 + 320)
  164.         mov eax,x
  165. cmp eax,cdXSize
  166. jae @f
  167. fld c       ;st(0) = c
  168. fmul XCC     ;st(0) = c * XCC
  169. fld d       ;st(0) = d
  170. fmul YCC     ;st(0) = d * YCC
  171. faddp        ;st(0) = c * XCC + d * YCC
  172. fadd f       ;st(0) = c * XCC + d * YCC + f
  173. fst NewY    ;NewY  = c * XCC + d * YCC + f
  174. fst YCC     ;YCC = NewY
  175. fmul Scale;st(0) = NewY * 900
  176. fsubr ShiftY;st(0)  = -NewY * 900 + 405
  177. fistp y       ; y = (int) (-NewY * 900 + 405)
  178. mov ecx, y
  179. cmp ecx,cdYSize
  180. jae @f
  181. imul ecx,cdXSize*4
  182. add rcx,pMainDIB
  183. mov dword ptr rcx+rax*4,0FF00h ;*(pMainDIB + y*cdXSize*4 + x*4) = black color
  184. @@ fld NewX  ;XCC = NewX
  185. fstp XCC
  186. dec i
  187. jnz  bucle
  188. wmBYE leave
  189. retn
  190. WndProc endp
  191. WinMain proc
  192. local msgMSG
  193. push rbp
  194. mov ebp,esp
  195. sub esp,sizeof MSG
  196. xor ebx,ebx
  197. mov esi,IMAGE_BASE
  198. mov edi,offset ClassName
  199. push rbx ;hIconSm
  200. push rdi ;lpszClassName
  201. push rbx ;lpszMenuName
  202. push COLOR_WINDOWTEXT; hbrBackground
  203. push rbx ;hCursor
  204. push rbx         ;hIcon
  205. push rsi ;hInstance
  206. push rbx         ;cbClsExtra & cbWndExtra
  207. db 68h
  208. dd WndProc       ;lpfnWndProc
  209. push sizeof WNDCLASSEX ;cbSize & style
  210. mov ecx,esp ;addr WNDCLASSEX
  211. call RegisterClassEx
  212. push rbx
  213. push rsi ;rsi=400000h
  214. push rbx
  215. push rbx
  216. push cdYSize
  217. push cdXSize
  218. push cdYPos
  219. push cdXPos
  220. mov r9d,WS_VISIBLE
  221. mov r8,rdi ;offset ClassName
  222. mov edx,edi ;offset ClassName
  223. xor ecx,ecx
  224. sub esp,20h ; dwExStyle
  225. call CreateWindowEx
  226. @@     lea ecx,msg
  227. xor edx,edx
  228. xor r8d,r8d
  229. xor r9d,r9d
  230. call GetMessage
  231. cmp msg.wParam,VK_ESCAPE;user press ‘Esc’?
  232. je wmDESTROY
  233. lea ecx,msg
  234. call DispatchMessage
  235. jmp @b
  236. WinMain endp
  237. .data
  238. ClassName db ‘Tree #1 Fractal’,
  239. bi BITMAPINFO <<28h,cdXSize,-cdXSize,1,32,,,,,,>>
  240. bufDIBDC dq ?
  241. pMainDIB dq ?
  242. Scale dd 900.0
  243. ShiftX dd 320.0
  244. ShiftY dd 405.0
  245. end

Результат

Объяснение исходного кода программы HelloWorld

Теперь в двух словах о том, как работает программа HelloWorld. Когда процессор выполняет нашу программу, он считывает машинные команды из оперативной памяти. Откуда в памяти возьмется наша программа? Ответ: с одного из носителей, коими могут быть жесткий диск, дискета, флешка, компакт-диск или локальная сеть. Кто загрузит программу с носителя в оперативную память? Ответ: процессор, который сразу после включения начинает выполнять программу, записанную в BIOS — микросхеме памяти, которая хранит программу, которая проецируется на адресное пространство процессора и которая заставляет процессор перебирать все носители и искать на них т. н. загрузочный сектор — блок данных размером 512 байт, в последних двух байтах которого содержится т. н. сигнатура загрузочного сектора. Найдя загрузочный сектор, процессор копирует его с носителя в оперативную память по адресу 0x7C00 и переходит к выполнению машинной инструкции, расположенной по этому адресу. Чтобы рассчитать адреса меток, компилятор должен знать, по какому адресу будет расположена наша программа — для этого мы используем директиву

org 7C00h

Заметим, что директива никак не влияет на расположение кода внутри двоичного файла, она влияет только на адреса, на которые указывают метки.
Чтобы поместить в последние два байта (всего в секторе 512 байт) сектора сигнатуру загрузочного сектора (байты 55h, AAh), используем в конце файла директиву times:

finish
    times 510-finish+start db
    db 55h, 0AAh ; boot sector signature

После включения процессор работает в 16-разрядном режиме (т. н. реальный режим работы процессора). Две ассемблерные команды, имеющие одно и то же мнемоническое обозначение, будут иметь разные машинный код в зависимости от режима работы процессора (режимов на сегодняшний день существует три: реальный (16-разрядный), защищенный (32-разрядный) и длинный (64-разрядный)). Поэтому компилятор должен знать, какой машинный код ему генерировать, 16-, 32- или 64-разрядный — для этого мы используем директиву

use16

Настройка сегментных регистров:

start
    jmp far dword 0x0000entr ; makes CS=0, IP=0x7c00
entr
    xor  ax, ax     ; ax = 0
    mov  ds, ax     ; setup data segment ds=ax=0
    mov  ss, ax     ; setup stack segment ss=ax=0
    mov  sp, 0x7C00 ; stack will grow starting from 0x7C00 memory address

Зачем помещать нули в сегментные регистры? Дело в том, что мы не знаем, каковы значения этих регистров в момент запуска программы. Единственное, что мы знаем — это что наша программа будет загружена по адресу 0x7C00 и что первая машинная команда будет выполнена. В то же время, наша программа рассчитана на то, что в сегментных регистрах cs и ds будут нули, т. е. все метки в программе обозначают смещения относительного базового адреса, равного нулю. Стек мы пока не используем, но это пока — нелишним будет проинициализировать и регистры ss и sp.

Как вывести текстовую строку на экран. После старта процессора в реальном режиме в оперативную память из BIOS загружена таблица прерываний и загружены обработчики прерываний. Среди них есть программные прерывания — те, обработчики которых можно вызвать командой int. Эти прерывания по сути являются функциями, которые BIOS предоставляет нашей программе, и эти функции позволяют осуществлять ввод-вывод, в том числе на экран компьютера. Функции BIOS могут принимать параметры через регистры. Например функция int 10h принимает два параметра: параметр ah=0Eh уточняет задачу функции — «записать символ в видеопамять»; параметр al — ASCII-код символа, который надо записать в видеопамять. И мы последовательно в цикле помещаем в регистр al символы строки «Hello World!» и вызываем прерывание int 10h:

puts_loop
    lodsb           ; load to al the next charachter located at address in memory (si is incremented automatically because direction flag DF = 0)
    test al, al     ; zero in al means the end of the string
    jz   puts_loop_exit
    int  10h        ; call BIOS standard video service’s function
    jmp  puts_loop
puts_loop_exit

Некоторые функции BIOS приведены на странице OsDev.org — BIOS.
Заканчивается программа инструкцией , которая останавливает работу процессора. Из этого состояния он может быть выведен только прерыванием (как немаскируемым, так и маскируемым) или перезагрузкой (reset).

В следующей заметке я расскажу о настройке проекта osdevlearning, который размещен на BitBucket, и в который я буду помещать наши эксперименты по изучению архитектуры Intel x86-64.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector