Ml and ml64 command-line reference
Содержание:
- Программа HelloWorld
- 32-Bit-Adress Modus (Adressgröße außer Kraft Setzung)32-Bit Address Mode (Address Size Override)
- Версии MASM
- История
- Aggiungere un file in linguaggio assembler a un progetto di C++ Visual StudioAdd an assembler-language file to a Visual Studio C++ project
- Установка и запуск виртуальной машины Bochs
- Первая программа на ассемблере
- Вторая программа на ассемблере
- Создание образа дискеты
- Modalità Indirizzo 32 bit (override delle dimensioni degli indirizzi)32-Bit Address Mode (Address Size Override)
- 32-разрядный режим адреса (переопределение размера адреса)32-Bit Address Mode (Address Size Override)
- Что такое ассемблер?
- Добавление файла ассемблерного языка в проект Visual Studio C++Add an assembler-language file to a Visual Studio C++ project
- Коэффициенты
- Объяснение исходного кода программы HelloWorld
Программа 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 |
.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 |
.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):
- include win64a.inc
- include msvcrt.inc
- includelib gdi32.lib
- includelib msvcrt.lib
- IMAGE_BASE equ 400000h
- cdXPos equ 131
- cdYPos equ 217
- cdYSize equ 430
- cdXSize equ 640
- movr macro x,y
- mov x,
- org $-4
- dd y
- endm
- .code
- WndProc proc hWndQWORD,MsgQWORD,wParamQWORD,lParamQWORD
- local psPAINTSTRUCT
- local hdcqword
- local hOldBmpqword
- local hGDITmpqword
- local hOldDIBqword
- local hBackDCqword
- local hMainDIBqword
- local bufBMPqword
- local idword
- local xdword
- local ydword
- local adword
- local bdword
- local cdword
- local ddword
- local edword
- local fdword
- local NewXqword
- local NewYqword
- local XCCqword
- local YCCqword
- push rbp
- mov ebp, esp
- sub esp,(50h+sizeof PAINTSTRUCT+11*8+9*4+15)and(-16)
- mov hWnd,rcx
- cmp edx, WM_DESTROY
- jz wmDESTROY
- cmp edx, WM_PAINT
- jz wmPAINT
- cmp edx, WM_CREATE
- jz wmCREATE
- leave
- jmp DefWindowProc
- ; —————————————————————————
- wmDESTROY::mov rdx,hOldBmp
- mov rcx,hBackDC
- call SelectObject
- mov hGDITmp,rax
- mov rcx,bufBMP
- call DeleteObject
- mov rcx,hBackDC
- call DeleteDC
- mov rdx,hOldDIB
- mov rcx,bufDIBDC
- call SelectObject
- mov hGDITmp,rax
- mov rcx,bufDIBDC
- call DeleteDC
- mov rcx,hMainDIB
- call DeleteObject
- mov rcx,hWnd
- call DestroyWindow
- xor ecx,ecx ; nExitCode
- call ExitProcess
- ; —————————————————————————
- wmPAINTlea edx,ps
- call BeginPaint
- mov hdc,rax
- mov qword ptr rsp+40h,SRCCOPY; rop
- xor edx,edx ; x
- mov rsp+38h,rdx ; y1
- mov rsp+30h,rdx ; x1
- mov rax,bufDIBDC ; hdcSrc
- mov rsp+28h,rax
- mov rax,cdYSize
- mov rsp+20h,rax
- mov r9,cdXSize ; cx
- xor r8d,r8d ; cy
- mov rcx,hdc ; hdc
- call BitBlt
- lea edx,ps
- mov rcx,hWnd
- call EndPaint
- jmp wmBYE
- ; —————————————————————————
- wmCREATEmov rcx,hWnd
- call GetDC
- mov hdc,rax
- mov rcx,rax ; HDC
- call CreateCompatibleDC
- mov bufDIBDC,rax
- mov rsp+28h,rbx
- mov rsp+20h,rbx
- mov r9d,offset pMainDIB
- mov r8d,DIB_RGB_COLORS
- mov edx,offset bi
- mov rcx,hdc
- call CreateDIBSection
- mov hMainDIB,rax
- mov rdx,rax; hMainDIB
- mov rcx,bufDIBDC
- call SelectObject
- mov rax,hOldDIB
- mov rdx,hdc
- mov rcx,hWnd
- call ReleaseDC; // Libera device context
- ;Init———————————————————
- ;a b c d e f p
- ;0 0 0 0.50 0 0 0.05 500
- ;0.42 -0.42 0.42 0.42 0 0.2 0.4 4000
- ;0.42 0.42 -0.42 0.42 0 0.2 0.4 4000
- ;0.1 0 0 0.1 0 0.2 0.15*10000= 1500
- mov XCC,rbx
- mov YCC,rbx
- mov i,150000
- bucle call rand
- mov ecx,10000
- xor edx,edx
- div ecx
- cmp edx,500
- jg @f
- mov a,ebx
- mov b,ebx
- mov c,ebx; c = 0
- movr d,0.5
- mov f,ebx
- jmp fin
- @@ cmp edx,4500
- jg @f
- movr a,0.42
- movr b,-0.42
- movr c,0.42
- movr d,0.42
- movr f,0.2
- jmp fin
- @@ cmp edx,8500
- jg @f
- movr a,0.42
- movr b,0.42
- movr c,-0.42
- movr d,0.42
- movr f,0.2
- jmp fin
- @@ movr a,0.1
- mov b,ebx
- mov c,ebx
- movr d,0.1
- movr f,0.2
- fin fld a ;st(0) = a
- fmul XCC ;st(0) = a * XCC
- fld b ;st(0) = b
- fmul YCC ;st(0) = b * YCC
- faddp ;st(0) = a * XCC + b * YCC
- fst NewX ;NewX = a * XCC + b * YCC
- fmul Scale ;st(0) = NewX * 900
- fadd ShiftX ;st(0) = NewX * 900 + 320
- fistp x ;x = (int) (NewX * 900 + 320)
- mov eax,x
- cmp eax,cdXSize
- jae @f
- fld c ;st(0) = c
- fmul XCC ;st(0) = c * XCC
- fld d ;st(0) = d
- fmul YCC ;st(0) = d * YCC
- faddp ;st(0) = c * XCC + d * YCC
- fadd f ;st(0) = c * XCC + d * YCC + f
- fst NewY ;NewY = c * XCC + d * YCC + f
- fst YCC ;YCC = NewY
- fmul Scale;st(0) = NewY * 900
- fsubr ShiftY;st(0) = -NewY * 900 + 405
- fistp y ; y = (int) (-NewY * 900 + 405)
- mov ecx, y
- cmp ecx,cdYSize
- jae @f
- imul ecx,cdXSize*4
- add rcx,pMainDIB
- mov dword ptr rcx+rax*4,0FF00h ;*(pMainDIB + y*cdXSize*4 + x*4) = black color
- @@ fld NewX ;XCC = NewX
- fstp XCC
- dec i
- jnz bucle
- wmBYE leave
- retn
- WndProc endp
- WinMain proc
- local msgMSG
- push rbp
- mov ebp,esp
- sub esp,sizeof MSG
- xor ebx,ebx
- mov esi,IMAGE_BASE
- mov edi,offset ClassName
- push rbx ;hIconSm
- push rdi ;lpszClassName
- push rbx ;lpszMenuName
- push COLOR_WINDOWTEXT; hbrBackground
- push rbx ;hCursor
- push rbx ;hIcon
- push rsi ;hInstance
- push rbx ;cbClsExtra & cbWndExtra
- db 68h
- dd WndProc ;lpfnWndProc
- push sizeof WNDCLASSEX ;cbSize & style
- mov ecx,esp ;addr WNDCLASSEX
- call RegisterClassEx
- push rbx
- push rsi ;rsi=400000h
- push rbx
- push rbx
- push cdYSize
- push cdXSize
- push cdYPos
- push cdXPos
- mov r9d,WS_VISIBLE
- mov r8,rdi ;offset ClassName
- mov edx,edi ;offset ClassName
- xor ecx,ecx
- sub esp,20h ; dwExStyle
- call CreateWindowEx
- @@ lea ecx,msg
- xor edx,edx
- xor r8d,r8d
- xor r9d,r9d
- call GetMessage
- cmp msg.wParam,VK_ESCAPE;user press ‘Esc’?
- je wmDESTROY
- lea ecx,msg
- call DispatchMessage
- jmp @b
- WinMain endp
- .data
- ClassName db ‘Tree #1 Fractal’,
- bi BITMAPINFO <<28h,cdXSize,-cdXSize,1,32,,,,,,>>
- bufDIBDC dq ?
- pMainDIB dq ?
- Scale dd 900.0
- ShiftX dd 320.0
- ShiftY dd 405.0
- 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.