INT 3

Прекъсване на повикване 3 (#BP, точка на прекъсване)

8086

int 3

CD ib

ИНТР imm8

Прекъснете разговора imm8

8086

int 13

INTO

Прекъсване на повикване 4 (#OF, препълване), ако EFLAGS.OF=1

8086

в

Описание:

Екип INT 3е предназначено да генерира прекъсване 3 и е идентично с инструкцията INT n в описанието на операцията, с изключение на това, че номерът на прекъсването не присъства директно в кода на операцията тук, но е имплицитно зададен на 3.

Това прекъсване е предназначено да се използва от дебъгер, който поставя специална еднобайтова инструкция INT 3(код CCh) вместо първия байт на командите или вместо еднобайтови команди.

Има втори начин за извикване на това прекъсване с помощта на двубайтовия код INT 3 (код CD03h). въпреки това този методне се използва на практика, всички x86 асемблери интерпретират мнемониката по подразбиране INT 3като еднобайтова команда с CCh код (но това не изключва възможността за ръчно програмиране на двубайтов код). В допълнение към размера на кода, обработката на еднобайтови и двубайтови команди също е различна. INT 3. Прекъсване, генерирано от еднобайтова инструкция в режим EV86 (CR4.VME = 1), не се пренасочва от картата за пренасочване на прекъсвания (както е описано за режим 2, режим 3, режим 5) и винаги се обработва от манипулатора на защитен режим чрез дескриптор в IDT таблицата. В допълнение, в режим V86 не се извършват проверки на IOPL поле за това прекъсване и, съответно, #GP обща грешка на защитата не може да бъде генерирана, когато EFLAGS.IOPL< 3, то есть однобайтная команда не является IOPL-чувствительной .

Операция:

Алгоритъмът, представен тук, описва не само поведението на процесора при изпълнение на инструкция INT 3генериране на външно прекъсване или изключение.

ТОГАВА МИНЕТЕ РЕЖИМ НА РЕАЛЕН АДРЕС;

IF (EFLAGS.VM = 1 И EFLAGS.IOPL< 3 AND

(CR4.VME = 0 ИЛИ CR4.VME = 1 И IRB[n] = 1)

) (* IRB[n] е битът, съответстващ на прекъсване n в картата за пренасочване на прекъсване *)

#GP(0); (Софтуерно прекъсване INT n в режим: (1) V86 при EFLAGS.IOPL< 3, (2) EV86 Режим 2 *)

ДРУГО (* Защитен режим или режим V86/EV86 *)

АКО (EFLAGS.VM = 1 И CR4.VME = 1 И

(INT n) И IRB[n] = 0)

ИНАЧЕ МИНЕТЕ ЗАЩИТЕН РЕЖИМ; (* Хардуерни прекъсвания, изключения; софтуерни прекъсвания INT n в режим: (1) Защитен режим, (2) V86 с EFLAGS.IOPL = 3, (3) EV86 Режим 1 или Режим 4 *)

РЕЖИМ НА РЕАЛЕН АДРЕС:

IF ((Номер на прекъсване * 4) + 3 извън сегмента при достъп до IVT векторната таблица на прекъсванията) THEN #GP; FI;

IF (Няма място в стека за 6 байта) THEN #SS; FI;

EFLAGS.IF = 0; (* Нулирайте флага за прекъсване *)

EFLAGS.TF = 0; (* Нулиране на флага за прихващане *)

EFLAGS.AC = 0; (* Нулиране на флага за контрол на подравняването *)

CS = IVT[Номер на прекъсване * 4].селектор;

EIP = IVT[Номер на прекъсване * 4].отместване И 0x0000FFFFh;

(* Продължава в режим на реално адресиране... *)

EV86-РЕЖИМ: (* CR0.PE = 1, EFLAGS.VM = 1, CR4.VME = 1, режим EV86 - софтуерно прекъсване INT n, с IRB[n] = 0 - Режим 3 или Режим 5 *)

IF (стекът със задачи на V86 няма място за 6 байта) THEN #SS(0); FI;

tempFLAGS = ФЛАГОВЕ;

tempFLAGS.NT = 0;

ТОГАВА EFLAGS.IF = 0; (* Нулирайте флага за разрешаване на прекъсване *)

tempFLAGS.IF = EFLAGS.VIF;

EFLAGS.VIF = 0; (* Нулирайте флага за виртуално прекъсване *)

EFLAGS.TF = 0; (* Нулиране на флага за прихващане *)

Натискане (tempFLAGS);

(* В стека не се изпращат кодове за грешки *)

CS = IVT_V86[Номер на прекъсване * 4].селектор; (* Векторната таблица на прекъсванията IVT_V86 се намира в началото на адресното пространство на задачите V86 *)

EIP = IVT_V86[Номер на прекъсване * 4].отместване И 0x0000FFFFh; (* горните 16 бита на EIP регистъра са зададени на нула *)

(* Продължава в режим EV86 ... *)

ЗАЩИТЕН РЕЖИМ: (* CR0.PE = 1, Хардуерни прекъсвания, изключения; софтуерни прекъсвания INT n в режим: (1) Защитен режим, (2) V86 с EFLAGS.IOPL = 3, (3) EV86 Режим 1 или Режим 4 *)

IF ((Номер на прекъсване * 8) + 7 не попада в таблицата IDT) THEN #GP(номер на прекъсване * 8 + 2 + EXT); FI;

(* По-нататък, в параметрите на кода за грешка, терминът +2 означава настройка на бита IDT на кода на грешката, а терминът +EXT означава настройка на бита EXT на кода на грешката според това дали прекъсването, което е причинило грешката, е софтуерно EXT = 0 или външен EXT = 1 * )

AR байтът на дескриптора трябва да указва вратата за прекъсване, вратата за прихващане или вратата на задачата, в противен случай #GP(номер на прекъсване * 8 + 2 + EXT);

IF (софтуерно прекъсване или изключение) (* т.е. един от случаите INT n, INT 3, INT01, BOUND или INTO *)

IF (CPL > Gateway DPL)

#GP(номер на прекъсване * 8 + 2); (* CR0.PE = 1, шлюз DPL< CPL, программное прерывание *)

В противен случай шлюзът трябва да присъства #NP(номер на прекъсване * 8 + 2 + EXT);

IF (шлюз за задачи)

ТОГАВА МИНЕТЕ TASK-GATE;

ОТМИНЕТЕ TRAP-OR-INT-GATE; (* CR0.PE = 1, прекъсване или прихващане *)

TRAP-OR-INT-GATE: (* Защитен режим или режим V86/EV86, шлюз за прихващане или прекъсване *)

Проверка на новия CS селектор, указан в дескриптора на шлюза и съответния му дескриптор от LDT или GDT:

Селекторът трябва да е различен от нула, в противен случай #GP(EXT);

Индексът на селектора трябва да попада в дескрипторната таблица, в противен случай #GP(селектор + EXT);

Избраният дескриптор трябва да бъде дескриптор на кодов сегмент, в противен случай #GP(селектор + EXT);

Сегментът трябва да присъства (P = 1), в противен случай #NP(селектор + EXT);

IF (Кодовият сегмент е несъвместим) И (DPL на кодовия сегмент< CPL)

АКО EFLAGS.VM = 0

ТОГАВА МИНЕТЕ INT-TO-INTER-PRIV; (* CR0.PE = 1, EFLAGS.VM = 0, прекъсване или врата за прихващане, несъответстващ кодов сегмент, кодов сегмент DPL< CPL *)

ELSE(*EFLAGS.VM=1*)

IF (DPL на нов кодов сегмент ≠ 0) THEN #GP(Селектор на кодов сегмент + EXT); FI;

ОТМИНЕТЕ ИНТЕР-ОТ-V86-РЕЖИМ;(* CR0.PE = 1, EFLAGS.VM = 1, врата за прекъсване или прихващане, кодов сегмент DPL = 0, CPL = 3 *)

ДРУГО (* CR0.PE = 1, прекъсване или врата за прихващане, съответстващ кодов сегмент или несъответстващ кодов сегмент с DPL = CPL *)

IF EFLAGS.VM = 1 THEN #GP(Селектор на кодов сегмент + EXT); FI;

IF ((договорен кодов сегмент) ИЛИ (кодов сегмент DPL = CPL))

ТОГАВА МИНЕТЕ INT-TO-INTRA-PRIV; (* CR0.PE = 1, прекъсване или врата за прихващане, кодов сегмент DPL ≤ CPL за съответстващ сегмент, кодов сегмент DPL = CPL за непоследователен сегмент *)

ELSE #GP(Селектор на кодов сегмент + EXT); (* DPL > CPL за съответстващ сегмент или DPL ≠ CPL за несъвпадащ сегмент *)

INT-TO-INTER-PRIV: (* Защитен режим, прекъсване или прихващане, несъответстващ кодов сегмент, кодов сегмент DPL< CPL *)

IF (Текущ TSS 32 бита)

TSSstackAddress = (нов кодов сегмент DPL * 8) + 4

IF ((TSSstackAddress + 5) > TSS ограничение) (* (TSSstackAddress + 7) >

NewSS = [Базов TSS + TSSstackAddress + 4]; (*зареди 2 байта*)

(* 4 байта са заредени *)

ДРУГО (* Текущ TSS 16-bit *)

TSSstackAddress = (нов кодов сегмент DPL * 4) + 2

IF ((TSSstackAddress + 3) > TSS ограничение) (* (TSSstackAddress + 4) > TSS ограничение - за някои модели процесори *)

THEN #TS(Текущ TSS селектор + EXT);

NewESP = [Базов TSS + TSSstackAddress]; (*зареди 2 байта*)

NewSS = [Базов TSS + TSSstackAddress + 2]; (*зареди 2 байта*)

RPL на селектора трябва да бъде равен на DPL на новия кодов сегмент, в противен случай #TS(SS селектор + EXT);

DPL на стековия сегмент трябва да бъде равен на DPL на новия кодов сегмент, в противен случай #TS(SS селектор + EXT);

IF (32-битов шлюз)

ТОГАВА Новият стек трябва да има място за 20 байта (24 байта, ако има код за грешка), в противен случай #SS(EXT)

ИНАЧЕ Новият стек трябва да има място за 10 байта (12 байта, ако има код за грешка), в противен случай #SS(EXT)

SS:ESP = TSS(Нов SS:Нов ESP); (* Изтеглете нови SS и eSP стойности от TSS *)

IF (32-битов шлюз)

ТОГАВА

ELSE CS:IP = Gate(SELECTOR:OFFSET);

Заредете SS дескриптора в скритата част на SS регистъра;

IF (32-битов шлюз)

Push(Дълъг указател към стар стек - SS:ESP);

Push(Дълъг указател към точка за връщане - CS:EIP); (*3 думи, подплатени до 4*)

Push (код на грешка);

Push(Дълъг указател към стар стек - SS:SP); (*2 думи*)

Push(Дълъг указател към точка за връщане - CS:IP); (*2 думи*)

Push (код на грешка);

CPL = DPL на новия кодов сегмент;

IF (шлюз за прекъсване) THEN EFLAGS.IF = 0 FI; (* Нулирайте флага за прекъсване *)

EFLAGS.RF = 0;

(* Продължете да работите в защитен режим на високо ниво на привилегия... *)

INT-FROM-V86-MODE: (* V86/EV86 режим, прекъсване или капан, DPL = 0, CPL = 3 *)

(* Текущият TSS винаги е 32-битов в режим V86 *)

IF (TSS ограничение< 9) (*TSS ограничение< 11 - для некоторых моделей процессоров *)

THEN #TS(Текущ TSS селектор + EXT);

NewSS = [Базов TSS + 8]; (*зареди 2 байта*)

NewESP = [Базов TSS + 4]; (* 4 байта са заредени *)

Проверка на селектора на новия сегмент на стека на NewSS и съответния му дескриптор от LDT или GDT:

Селекторът трябва да е различен от нула, в противен случай #TS(EXT);

Индексът на селектора трябва да попада в дескрипторната таблица, в противен случай #TS(SS селектор + EXT);

RPL на селектора трябва да е нула, в противен случай #TS(SS селектор + EXT);

DPL на сегмента на стека трябва да бъде нула, в противен случай #TS(SS селектор + EXT);

Дескрипторът трябва да бъде във формат на записваем дескриптор на сегмент от данни (W = 1), в противен случай #TS(SS селектор + EXT);

Сегментът трябва да присъства (P = 1), в противен случай #SS(SS селектор + EXT);

IF (32-битов шлюз)

Новият стек трябва да има място за 36 байта (40 байта, ако има код за грешка), в противен случай #SS(EXT)

Новият указател на инструкция трябва да попада в новия кодов сегмент, в противен случай #GP(EXT); (* указателят на инструкция се определя от стойността на полето OFFSET от дескриптора на шлюза *)

TempEflags = EFLAGS;

EFLAGS.VM = 0; (* Процесорът излиза от режим V86, за да обработи прекъсване в защитен режим *)

IF (шлюз за прекъсване)

ТОГАВА EFLAGS.IF = 0;

CPL=0; (* Превключване към ниво на привилегия нула *)

SS:ESP = TSS(Нов SS:Нов ESP); (* Заредете SS0 и ESP0 стойности от TSS *)

Натискане (GS);

Натискане (FS); (*разширява се до две думи*)

Натискане (DS); (*разширява се до две думи*)

Push(ES); (*разширява се до две думи*)

GS = 0; (* Сегментните регистри са нулирани. Последващото използване на нулеви селектори в защитен режим не е разрешено *)

Натискане (TempSS); (*разширява се до две думи*)

Push(TempEflags);

Натискане (CS); (*разширява се до две думи*)

Push (код на грешка); (* Ако има, 4 байта *)

CS:EIP = Gate(SELECTOR:OFFSET); (* Селектор за зареждане: отместване от 32-битов манипулатор на шлюза *)

ELSE (*16-битов шлюз*)

Новият стек трябва да има място за 18 байта (20 байта, ако има код за грешка), в противен случай #SS(EXT)

(* запазването на 16-битови регистрови стойности в стека е подобно на 32-битов шлюз *)

(* Връщане от прекъсване с инструкция IRET обратно към режим V86 от 16-битов сегмент няма да е възможно, тъй като VM флагът няма да бъде записан в стека и няма да бъде възстановен от изображението EFLAGS при връщане *)

(* Продължете да работите в защитен режим с ниво на привилегия нула... *)

INT-TO-INTRA-PRIV: (* CR0.PE = 1, DPL = CPL или съответстващ сегмент с DPL ≤ CPL *)

IF (32-битов шлюз)

ТОГАВА Новият стек трябва да има място за 12 байта (16 байта, ако има код за грешка), в противен случай #SS(EXT)

ИНАЧЕ Новият стек трябва да има място за 6 байта (8 байта, ако има код за грешка), в противен случай #SS(EXT)

Новият указател на инструкция трябва да попада в новия кодов сегмент, в противен случай #GP(EXT);

IF (32-битов шлюз)

Push(Дълъг показалец към точката за връщане); (*3 думи, подплатени до 4*)

CS:EIP = Gate(SELECTOR:OFFSET); (* Селектор за зареждане: отместване от 32-битов манипулатор на шлюза *)

Push (код на грешка); (* Ако има, 4 байта *)

Push(Дълъг показалец към точката за връщане); (*2 думи*)

CS:IP = Gate(SELECTOR:OFFSET); (* Зареждане на селектор: отместване от 16-битов дескриптор на шлюза *)

Push (код на грешка); (* Ако има, 2 байта *)

Заредете CS дескриптора в скритата част на CS регистъра;

IF (шлюз за прекъсване) THEN EFLAGS.IF = 0; FI;

(* Продължете да работите в защитен режим, без да променяте нивото на привилегия... *)

TASK-GATE: (* CR0.PE = 1, врата за задача *)

Проверка на TSS селектора от манипулатора на шлюза на задачата:

Селекторът трябва да посочи GDT (TI бит = 0), в противен случай #GP(TSS селектор + EXT);

Индексът на селектора трябва да попада в GDT таблицата, в противен случай #GP(TSS селектор + EXT);

Проверка на съответния TSS дескриптор за избрания селектор:

TSS дескрипторът трябва да има свободен TSS тип (TYPE.B = 0), в противен случай #GP(TSS селектор + EXT);

TSS трябва да присъства (P = 1), в противен случай #NP(TSS селектор + EXT);

ЗАДАЧИ ЗА ПРЕВКЛЮЧВАНЕ (С прикачен файл) към TSS; (* тук „С влагане“ означава факта, че когато контекстът се инициализира нова задачаще бъде зададен флаг EFLAGS.NT = 1 и TSS селекторът на прекъснатата (стара) задача ще бъде копиран в полето LINK на нейния TSS сегмент - вижте Адресиране и многозадачност: Поддръжка на многозадачност *)

IF (Прекъсването е специална ситуация с код за грешка)

В стека трябва да има място за кода на грешката, в противен случай #SS(EXT);

натискане (код на грешка);

Указателят на EIP инструкция трябва да попада в CS сегмента, в противен случай #GP(EXT);

(* Зареждането на нов контекст на задача идва с допълнителни проверки, както е описано в Адресиране и многозадачност: Поддържа многозадачност *)

(* Продължава в контекста на новата задача... *)

Изключения в защитен режим:

INT 3, но също и когато се генерира външно прекъсване или изключение. малко ВЪНШв кода за грешка на външното прекъсване).

  • дескриптор вектор(индексът) на прекъсването не е в таблицата на дескрипторите на прекъсванията (IDT);
  • дръжка, съответстваща на тази, която се обработва вектор(индекс) на прекъсване, не е манипулатор на trap gate, interrupt gate или task gate;
  • възниква софтуерно прекъсване или софтуерно изключение (т.е. един от случаите: INT n, INT 3, INT01, BOUND или INTO) и текущо ниво на привилегия(CPL) задачи повече ниво на привилегия(DPL) дескриптор на шлюзаот IDT таблица −
  • селектор на сегмент в съответната обработка вектор(индекс) на манипулатора за прекъсване към вратата за прихващане, вратата за прекъсване или вратата на задачата е нулев селектор;
  • индексът на сегментния селектор на дескриптора на вратата за прихващане или дескриптора на вратата за прекъсване не попада в границите на съответната дескрипторна таблица;
  • Индексът на TSS селектор от дескриптора на вратата на задачата, съответстващ на прекъсване, е извън границите глобална дескрипторна таблица(GDT);
  • дескрипторнов кодов сегмент не е дескриптор на кодов сегмент;
  • (DPL) нов договорен кодов сегмент е по-голям текущо ниво на привилегия(CPL) задачи -
  • ниво на привилегия за управление(DPL) на новия несъответстващ кодов сегмент не е равно на текущо ниво на привилегия(CPL) задачи -
  • TSS селекторът от съответния на прекъсването дескриптор на вратата на задачата сочи към локална дескрипторна таблица(LDT);
  • манипулаторът на TSS на новата задача е маркиран като зает- ТИП.B ≠ 1.
  • адреса, за който трябва да се прочете новата стойност указател на стека(SS:eSP), извън TSS сегмента;
  • селекторът на новия стеков сегмент е нулев селектор;
  • индексът на селектора на новия стеков сегмент не попада в съответната дескрипторна таблица;
  • поискано ниво на привилегия(RPL) селектор на нов сегмент от стека не е равен на (DPL) на нов кодов сегмент -
  • ниво на привилегия за управление(DPL) на новия сегмент на стека не е равно на ниво на привилегия за управление(DPL) на нов кодов сегмент -
  • новият стеков сегмент не е записваем сегмент от данни -
  • съответният манипулатор на прекъсване към trap gate, interrupt gate, task gate или TSS манипулатор се маркира като неприсъствен(битът P на дескриптора е чист);
  • нов кодов сегмент не присъства (малко P сегментният дескриптор е нулиран).
  • при запис в стека (включително в нов стек, ако е извършено превключване на стека) на стойности обратни адреси, указател на стека, знаменаили код за грешка, сегментът на стека е извън границите;
  • новият сегмент на стека не присъства (битът P на дескриптора на сегмента е чист).

Специални ситуации на реален режим на адресиране:

Списъкът с изключения, представен тук, характеризира поведението на процесора не само при изпълнение на инструкция INT 3, но също и когато се генерира външно прекъсване или изключение.

Специални ситуации на режим V86:

Списъкът с изключения, представен тук, характеризира поведението на процесора не само при изпълнение на инструкция INT 3, но също и когато се генерира външно прекъсване или изключение. малко ВЪНШв кода за грешка се използва за обозначаване на събитие, външно за прекъснатата програма (

Асемблерите MASM, TASM и WASM се различават един от друг. Създаването на прости програми за тях обаче практически няма разлики, с изключение на самото сглобяване и свързване.

Така че първата ни програма за MASM, TASM и WASM, която извежда английско писмо„A“ в текущата позиция на курсора, тоест вляво горен ъгълекран:

Модел малък .код ORG 100h начало: MOV AH,2 MOV DL,41h INT 21h INT 20h END начало текстов редактор- например в NOTEBOOK (NotePad) от WINDOWS (но не в Word и не в друга "фантазия"). Въпреки това препоръчвам "разширен" текстов редактор с подчертаване на синтаксиса като PSPad (вижте раздел ). След това запазваме този файл с разширение .asm, например в папката MYPROG. Нека назовем файла atest. Така че имаме: C:\MYPROG\atest.asm.

ЗАБЕЛЕЖКА
Обърнете внимание, че в първата команда написахме 2 вместо 02h. MASM, TASM и WASM, като Emu8086, позволяват такива "свободи". Въпреки че можете да напишете 02h - няма да има грешка.

Обяснения към програмата:

.модел мъничък- 1-ви ред. Директивата .model дефинира модела на паметта за определен тип файл. В нашия случай това е COM файл, така че избираме малкия модел, който съчетава код, данни и сегменти от стека. Малкият модел е предназначен за създаване на COM файлове.

.код- 2-ри ред. Тази директива стартира кодов сегмент.

ORG 100ч- 3-ти ред. Тази команда задава програмния брояч на 100h, тъй като при зареждане на COM файл в паметта, DOS разпределя първите 256 байта за PSP блока данни ( десетично число 256 е равно на шестнадесетично 100h). Програмният код се намира само след този блок. Всички програми, които компилират в COM файлове, трябва да започват с тази директива.

начало: MOV AH, 02ч- 4-ти ред. Стартовият етикет се поставя преди първата команда в програмата и ще се използва в директивата END, за да посочи с коя команда стартира програмата. Инструкцията MOV поставя стойността на втория операнд в първия операнд. Тоест стойността 02h се поставя в регистър AH. За какво е? 02h е функция на DOS, която отпечатва знак на екрана. Пишем програма за DOS, така че използваме командите на това операционна система(ОПЕРАЦИОННА СИСТЕМА). И ние записваме тази функция (или по-скоро нейния номер) в регистъра AH, защото прекъсването 21h използва този конкретен регистър.

MOV DL, 41ч- 5-ти ред. Кодът на знака "A" се въвежда в DL регистъра. ASCII символният код за "A" е числото 41h.

INT 21ч- 6-ти ред. Това е самото прекъсване 21h - команда, която извиква системната функция на DOS, посочена в регистъра AH (в нашия пример това е функция 02h). Командата INT 21h е основното средство за взаимодействие между програмите и операционната система.

INT 20ч- 7-ми ред. Това е прекъсване, което казва на операционната система да излезе от програмата и да прехвърли управлението на конзолното приложение. В случай, че програмата вече е компилирана и стартирана от ОС, командата INT 20h ще ни върне в ОС (например в DOS).

КРАЙ начало- 8-ми ред. Директивата END прекратява програмата, като в същото време указва от кой етикет трябва да започне нейното изпълнение.

Какво е асемблер

Assembler е език за програмиране на ниско ниво. Всеки процесор има свой собствен асемблер. Програмирайки на асемблер, вие директно работите с компютърния хардуер. Изходният текст на асемблерния език се състои от инструкции (мнемоники), които след компилация се преобразуват в кодове на инструкции на процесора.

Разработването на програми на асемблер е много трудно нещо. В замяна на отделеното време получавате ефективна програма. Програмите за асемблер се пишат, когато всеки цикъл на процесора е важен. В асемблера давате конкретни команди на процесора и няма излишни боклуци. Именно това гарантира високата скорост на изпълнение на вашата програма.

За да използвате правилно асемблера, трябва да знаете програмния модел на микропроцесорната система. От гледна точка на програмиста микропроцесорната система се състои от:

  1. микропроцесор
  2. памет
  3. I/O устройства.

Моделът на програмиране е добре описан в литературата.

Синтаксис на сглобяване

Общият формат на програмен ред в асемблер

<Метка>: <Оператор> <Операнды> ; <Комментарий>

Поле за етикет. Етикетът може да се състои от символи и долни черти. Етикетите се използват в условни и безусловни операции за прескачане.

Поле за оператор. Това поле съдържа командата мнемоника. Например мнемоника mov

Операндно поле. Операндите могат да присъстват само ако присъства Operator (операторно поле). Може да няма операнди или може да има няколко. Операндите могат да бъдат данни, върху които трябва да извършите някакво действие (изпращане, добавяне и т.н.).

Поле за коментар. Коментарът е необходим за словесния съпровод на програмата. Всичко зад символа ; счита за коментар.

Първата програма на асемблерния език

Тази статия ще използва асемблер за i80x86 процесор и ще използва следния софтуер:

  • TASM - Borland Turbo Assembler - Компилатор
  • TLINK - Borland Turbo Linker - редактор на връзки (линкер)

За да бъдем конкретни, Tasm 2.0.

По традиция първата ни програма ще отпечата низа "Hello world!" към екрана.

файл sample.asm

Модел малък ; Memory model.stack 100h; Задаване на стека size.data; Начало на сегмент от програмни данни HelloMsg DB "Hello World!",13,10,"$" .code ; Начало на кодов сегмент mov ax,@DATA; Преместване на адреса на сегмент от данни в AX регистър mov ds,ax; Задайте DS регистър на сегмент от данни mov ah,09h; DOS функция за отпечатване на низ на екрана mov dx,offset HelloMsg; Задайте отместването в началото на низа int 21h ; Изходен низ mov ax,4C00h; Функция за изход от DOS програма int 21h ; Излизане от края на програмата

Както можете да видите, програмата е разделена на сегменти: сегмент от данни, сегмент от код и има също сегмент от стека.

Нека разгледаме всичко в ред.

Малката директива .model указва модела на паметта. Малкият модел е 1 сегмент за код, 1 сегмент за данни и стек, т.е. данни и стек са в един и същи сегмент. Има и други модели памет, например: малка, средна, компактна. В зависимост от модела на паметта, който изберете, сегменти от вашата програма може да се припокриват или да имат отделни сегменти в паметта.

Директивата .stack 100h задава размера на стека. Стекът е необходим за запазване на информация с последващото й възстановяване. По-специално, стекът се използва за прекъсвания. В този случай съдържанието на регистъра FLAGS, регистъра CS и регистъра IP се съхранява в стека. Следва изпълнението на програмата за прекъсване и след това стойностите на тези регистри се възстановяват.

  • Регистърът FLAGS съдържа знаци, които се генерират след изпълнение на инструкция от процесора.
  • Регистърът CS (кодов сегмент) съдържа адреса на кодовия сегмент.
  • Регистър IP (Instruction Pointer) - указател на инструкции. Той съдържа адреса на инструкцията, която трябва да бъде изпълнена следващата (адрес спрямо сегмента на CS кода).

| Повече ▼ Подробно описаниенадхвърля обикновена статия.

Директивата .data дефинира началото на сегмента с данни на вашата програма. Сегментът с данни дефинира "променливи", т.е. има резервация на памет за необходимите данни. След .data има ред
HelloMsg DB "Здравей свят!",13,10,"$"

Тук HelloMsg е символичното име, което съответства на началото на низа "Hello World!" (без кавички). Тоест, това е адресът на първия знак от нашия низ спрямо сегмента с данни. Директивата DB (Define Byte) дефинира наличната област на паметта байт по байт. 13,10 - кодове на символи Нова линияи връщане на каретка, а символът $ е необходим, за да работи DOS функцията 09h правилно. Така нашият низ ще заема 15 байта в паметта.

Директивата .code дефинира началото на кодовия сегмент (CS - Code Segment) на програмата. Следват редовете на програмата, съдържащи командна мнемоника.

Нека поговорим за командата mov.

mov<приёмник>, <источник>

Командата mov е команда за преместване. Той изпраща съдържанието на източника до дестинацията. Трансферите могат да бъдат регистър-регистър, регистър-памет, памет-регистър, но няма трансфер памет-памет, т.е. всичко минава през регистрите на процесора.

За да работите с данни, трябва да настроите регистър на сегменти от данни. Настройката е, че записваме адреса на сегмента от данни @DATA в регистъра DS (сегмент от данни). Невъзможно е директно да се запише адресът в този регистър - това е архитектурата, затова използваме регистъра AX. В AX записваме адреса на кодовия сегмент

и след това изпращаме съдържанието на регистъра AX към регистъра DS.

След това регистърът DS ще съдържа адреса на началото на сегмента с данни. Адресът DS:0000h ще съдържа знака H. Предполагам, че знаете за сегменти и отмествания.

Обръщението се състои от две части.<Сегмент>:<Смещение>където сегментът е 2 байта, а отместването е 2 байта. Получават се 4 байта за достъп до всяка клетка от паметта.

mov ah,09h
mov dx,offset HelloMsg
вътрешен 21ч

Тук записваме числото 09h в регистъра AH - номерът на функцията на 21-вото прекъсване, което показва линията на екрана.

В следващия ред записваме адреса (смущението) в началото на нашия ред в DX регистъра.

След това извикваме прекъсване 21h - това е прекъсване за DOS функции. Прекъсване - когато работеща програма бъде прекъсната и прекъсващата програма започне да се изпълнява. Номерът на прекъсването определя адреса на DOS подпрограма, която отпечатва низ от знаци на екрана.

Вероятно ще имате въпрос: Защо записваме номер на функция 09h в регистъра AH? И защо отместването на линията се записва в DX регистъра?
Отговорът е прост: за всяка функция се дефинират специфични регистри, които съдържат входните данни за тази функция. Можете да видите кои регистри са необходими за конкретни функции в помощ "e.

movax,4C00h
вътрешен 21ч

mov ax,4C00h - изпраща номера на функцията в регистъра AX. Функция 4C00h - изход от програмата.

int 21h - извършване на прекъсване (всъщност изход)

край - край на програмата.

След директивата end, компилаторът игнорира всичко, така че можете да пишете каквото искате там :)

Ако сте прочели до края, значи сте герой!

Майко Г.В. Асемблер за IBM PC: - М.: "Бизнес-информ", "Сирин" 1999 г. - 212 с.