INT 3

Interrupt call 3 (#BP, breakpoint)

8086

int 3

CD ib

INT imm8

Interrupt call imm8

8086

int 13

INTO

Call interrupt 4 (#OF, overflow) if EFLAGS.OF=1

8086

into

Description:

Team INT 3 is intended to generate interrupt 3 and is identical to the INT n instruction in the description of the operation, except that the interrupt number is not directly present in the opcode here, but is implicitly set to 3.

This interrupt is intended to be used by a debugger that places a special one-byte instruction INT 3(code CCh) instead of the first byte of commands or instead of single-byte commands.

There is a second way to call this interrupt using the two-byte code INT 3 (code CD03h). However this method not used in practice, all x86 assemblers interpret mnemonics by default INT 3 as a one-byte command with the CCh code (but this does not exclude the possibility of manually programming a two-byte code). In addition to the size of the code, the processing of single- and double-byte commands is also different. INT 3. An interrupt generated by a single-byte instruction in EV86 mode (CR4.VME = 1) is not redirected by the interrupt redirection map (as described for Mode 2 , Mode 3 , Mode 5) and is always handled by the protected mode handler via a descriptor in the IDT table. In addition, in V86 mode, no IOPL field checks are performed for this interrupt and, accordingly, a #GP general protection error cannot be generated when EFLAGS.IOPL< 3, то есть однобайтная команда не является IOPL-чувствительной .

Operation:

The algorithm presented here describes not only the behavior of the processor when executing an instruction INT 3 external interrupt or exception generation.

THEN GOTO REAL-ADDRESS-MODE;

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

(CR4.VME = 0 OR CR4.VME = 1 AND IRB[n] = 1)

) (* IRB[n] is the bit corresponding to interrupt n in the interrupt redirect map *)

#GP(0); (Software interrupt INT n in mode: (1) V86 at EFLAGS.IOPL< 3, (2) EV86 Режим 2 *)

ELSE (* Protected mode or V86/EV86 mode *)

IF (EFLAGS.VM = 1 AND CR4.VME = 1 AND

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

ELSE GOTO PROTECTED-MODE; (* Hardware interrupts, exceptions; software interrupts INT n in mode: (1) Protected mode, (2) V86 with EFLAGS.IOPL = 3, (3) EV86 Mode 1 or Mode 4 *)

REAL-ADDRES-MODE:

IF ((Interrupt number * 4) + 3 out of segment when accessing the IVT interrupt vector table) THEN #GP; FI;

IF (No space on stack for 6 bytes) THEN #SS; FI;

EFLAGS.IF = 0; (* Reset interrupt flag *)

EFLAGS.TF = 0; (* Reset trap flag *)

EFLAGS.AC = 0; (* Reset alignment control mode flag *)

CS = IVT[Interrupt Number * 4].selector;

EIP = IVT[Interrupt number * 4].offset AND 0x0000FFFFh;

(* Continued in real addressing mode... *)

EV86-MODE: (* CR0.PE = 1, EFLAGS.VM = 1, CR4.VME = 1, EV86 mode - software interrupt INT n, with IRB[n] = 0 - Mode 3 or Mode 5 *)

IF (V86 task stack has no room for 6 bytes) THEN #SS(0); FI;

tempFLAGS = FLAGS;

tempFLAGS.NT = 0;

THEN EFLAGS.IF = 0; (* Reset interrupt enable flag *)

tempFLAGS.IF = EFLAGS.VIF;

EFLAGS.VIF = 0; (* Reset virtual interrupt flag *)

EFLAGS.TF = 0; (* Reset trap flag *)

Push(tempFLAGS);

(* No error codes are pushed on the stack *)

CS = IVT_V86[Interrupt Number * 4].selector; (* The IVT_V86 interrupt vector table is located at the beginning of the V86 task address space *)

EIP = IVT_V86[Interrupt number * 4].offset AND 0x0000FFFFh; (* upper 16-bits of the EIP register are set to zero *)

(* Continued in EV86 mode ... *)

PROTECTED MODE: (* CR0.PE = 1, Hardware interrupts, exceptions; software interrupts INT n in mode: (1) Protected mode, (2) V86 with EFLAGS.IOPL = 3, (3) EV86 Mode 1 or Mode 4 *)

IF ((Interrupt number * 8) + 7 does not fall within the IDT table) THEN #GP(interrupt number * 8 + 2 + EXT); FI;

(* Hereinafter, in the error code parameters, the term +2 means setting the IDT bit of the error code, and the term +EXT means setting the EXT bit of the error code in accordance with whether the interrupt that caused the error was software EXT = 0 or external EXT = 1 * )

The AR byte of the descriptor must specify the interrupt gate, trap gate, or task gate, otherwise #GP(interrupt number * 8 + 2 + EXT);

IF (Software Interrupt or Exception) (* i.e. one of the cases INT n, INT 3, INT01, BOUND, or INTO *)

IF (CPL > Gateway DPL)

#GP(interrupt number * 8 + 2); (* CR0.PE = 1, Gateway DPL< CPL, программное прерывание *)

Gateway must be present otherwise #NP(interrupt number * 8 + 2 + EXT);

IF (Task Gateway)

THEN GOTO TASK-GATE;

GOTO TRAP-OR-INT-GATE; (* CR0.PE = 1, interrupt or trap gate *)

TRAP-OR-INT-GATE: (* Protected mode or V86/EV86 mode, trap or interrupt gateway *)

Checking the new CS selector specified in the gateway descriptor and its corresponding descriptor from LDT or GDT:

The selector must be non-null, otherwise #GP(EXT);

The selector index must fall within the descriptor table, otherwise #GP(selector + EXT);

The selected descriptor must be a code segment descriptor, else #GP(selector + EXT);

The segment must be present (P = 1), otherwise #NP(selector + EXT);

IF (Code segment inconsistent) AND (DPL of code segment< CPL)

IF EFLAGS.VM = 0

THEN GOTO INT-TO-INTER-PRIV; (* CR0.PE = 1, EFLAGS.VM = 0, interrupt or trap gate, mismatched code segment, code segment DPL< CPL *)

ELSE(*EFLAGS.VM=1*)

IF (DPL of new code segment ≠ 0) THEN #GP(Code segment selector + EXT); FI;

GOTO INT-FROM-V86-MODE;(* CR0.PE = 1, EFLAGS.VM = 1, interrupt or trap gate, code segment DPL = 0, CPL = 3 *)

ELSE (* CR0.PE = 1, interrupt or trap gate, matched code segment or mismatched code segment with DPL = CPL *)

IF EFLAGS.VM = 1 THEN #GP(Code Segment Selector + EXT); FI;

IF ((code segment negotiated) OR (code segment DPL = CPL))

THEN GOTO INT-TO-INTRA-PRIV; (* CR0.PE = 1, interrupt or trap gate, code segment DPL ≤ CPL for matched segment, code segment DPL = CPL for inconsistent segment *)

ELSE #GP(Code Segment Selector + EXT); (* DPL > CPL for a matched segment or DPL ≠ CPL for a non-matched segment *)

INT-TO-INTER-PRIV: (* Protected mode, interrupt or trap gate, mismatched code segment, code segment DPL< CPL *)

IF (Current TSS 32-bit)

TSSstackAddress = (new code segment DPL * 8) + 4

IF ((TSSstackAddress + 5) > TSS limit) (* (TSSstackAddress + 7) >

NewSS = [Base TSS + TSSstackAddress + 4]; (*load 2 bytes*)

(* 4 bytes are loaded *)

ELSE (* Current TSS 16-bit *)

TSSstackAddress = (new code segment DPL * 4) + 2

IF ((TSSstackAddress + 3) > TSS limit) (* (TSSstackAddress + 4) > TSS limit - for some processor models *)

THEN #TS(Current TSS selector + EXT);

NewESP = [Base TSS + TSSstackAddress]; (*load 2 bytes*)

NewSS = [Base TSS + TSSstackAddress + 2]; (*load 2 bytes*)

The RPL of the selector must be equal to the DPL of the new code segment, else #TS(SS selector + EXT);

The DPL of the stack segment must be equal to the DPL of the new code segment, else #TS(SS selector + EXT);

IF (32-bit gateway)

THEN The new stack must have room for 20 bytes (24 bytes if there is an error code), else #SS(EXT)

ELSE New stack must have room for 10 bytes (12 bytes if there is an error code), else #SS(EXT)

SS:ESP = TSS(NewSS:NewESP); (* Download new SS and eSP values ​​from TSS *)

IF (32-bit gateway)

THEN

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

Load the SS descriptor into the hidden part of the SS register;

IF (32-bit gateway)

Push(Long pointer to old stack - SS:ESP);

Push(Long pointer to return point - CS:EIP); (*3 words padded to 4*)

Push(Error code);

Push(Long pointer to old stack - SS:SP); (*2 words*)

Push(Long pointer to return point - CS:IP); (*2 words*)

Push(Error code);

CPL = DPL of the new code segment;

IF (Interrupt Gateway) THEN EFLAGS.IF = 0 FI; (* Reset interrupt flag *)

EFLAGS.RF = 0;

(* Continue running in protected mode at high privilege level... *)

INT-FROM-V86-MODE: (* V86/EV86 mode, interrupt or trap gate, DPL = 0, CPL = 3 *)

(* Current TSS is always 32-bit in V86 mode *)

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

THEN #TS(Current TSS selector + EXT);

NewSS = [Base TSS + 8]; (*load 2 bytes*)

NewESP = [Base TSS + 4]; (* 4 bytes are loaded *)

Checking the selector of the new NewSS stack segment and its corresponding descriptor from LDT or GDT:

The selector must be non-null, otherwise #TS(EXT);

The selector index must fall within the descriptor table, otherwise #TS(SS selector + EXT);

The RPL of the selector must be zero, otherwise #TS(SS selector + EXT);

The DPL of the stack segment must be zero, otherwise #TS(SS selector + EXT);

The descriptor must be in the format of a writable data segment descriptor (W = 1), otherwise #TS(SS selector + EXT);

The segment must be present (P = 1), otherwise #SS(SS selector + EXT);

IF (32-bit gateway)

The new stack must have room for 36 bytes (40 bytes if there is an error code), else #SS(EXT)

The new instruction pointer must fall within the new code segment, otherwise #GP(EXT); (* the instruction pointer is determined by the value of the OFFSET field from the gateway descriptor *)

TempEflags = EFLAGS;

EFLAGS.VM = 0; (* Processor exits V86 mode to handle interrupt in protected mode *)

IF (Interrupt Gateway)

THEN EFLAGS.IF = 0;

CPL=0; (* Switch to privilege level zero *)

SS:ESP = TSS(NewSS:NewESP); (* Load SS0 and ESP0 values ​​from TSS *)

Push(GS);

Push(FS); (*expands to two words*)

Push(DS); (*expands to two words*)

Push(ES); (*expands to two words*)

GS = 0; (* Segment registers are zeroed. Subsequent use of null selectors in protected mode is not allowed *)

Push(TempSS); (*expands to two words*)

Push(TempEflags);

Push(CS); (*expands to two words*)

Push(Error code); (* If present, 4 bytes *)

CS:EIP = Gate(SELECTOR:OFFSET); (* Load selector:offset from 32-bit gateway handle *)

ELSE (*16-bit gateway*)

The new stack must have room for 18 bytes (20 bytes if there is an error code), else #SS(EXT)

(* saving 16-bit register values ​​on the stack is similar to a 32-bit gateway *)

(* Returning from an interrupt with an IRET instruction back to V86 mode from a 16-bit segment will not be possible, since the VM flag will not be saved on the stack and will not be restored from the EFLAGS image on return *)

(* Continue running in protected mode with privilege level zero... *)

INT-TO-INTRA-PRIV: (* CR0.PE = 1, DPL = CPL or matched segment with DPL ≤ CPL *)

IF (32-bit gateway)

THEN The new stack must have room for 12 bytes (16 bytes if there is an error code), else #SS(EXT)

ELSE New stack must have room for 6 bytes (8 bytes if there is an error code), else #SS(EXT)

The new instruction pointer must fall within the new code segment, otherwise #GP(EXT);

IF (32-bit gateway)

Push(Long pointer to return point); (*3 words padded to 4*)

CS:EIP = Gate(SELECTOR:OFFSET); (* Load selector:offset from 32-bit gateway handle *)

Push(Error code); (* If present, 4 bytes *)

Push(Long pointer to return point); (*2 words*)

CS:IP = Gate(SELECTOR:OFFSET); (* Load selector:offset from 16-bit gateway descriptor *)

Push(Error code); (* If present, 2 bytes *)

Load the CS descriptor into the hidden part of the CS register;

IF (Interrupt Gateway) THEN EFLAGS.IF = 0; FI;

(* Continue running in protected mode without changing the privilege level... *)

TASK-GATE: (* CR0.PE = 1, task gate *)

Checking the TSS selector from the task gateway handle:

Selector must specify GDT (TI bit = 0), else #GP(TSS selector + EXT);

The selector index must fall within the GDT table, otherwise #GP(TSS selector + EXT);

Checking the corresponding TSS descriptor for the selected selector:

The TSS descriptor must have a free TSS type (TYPE.B = 0), otherwise #GP(TSS selector + EXT);

TSS must be present (P = 1), else #NP(TSS selector + EXT);

SWITCH-TASKS(With Attachment) to TSS; (* here "With nesting" means the fact that when the context is initialized new task flag EFLAGS.NT = 1 will be set, and the TSS selector of the interrupted (old) task will be copied into the LINK field of its TSS segment - see Addressing and multitasking: Multitasking support *)

IF (Interrupt is a special situation with an error code)

There must be room on the stack for the error code, otherwise #SS(EXT);

push(error code);

The EIP instruction pointer must fall within the CS segment, else #GP(EXT);

(* Loading a new task context comes with additional checks as described in Addressing and Multitasking: Multitasking Supports *)

(* Continued in the context of the new task... *)

Protected mode exceptions:

INT 3, but also when any external interrupt or exception is generated. Bit EXT in the external interrupt error code).

  • descriptor vector(index) of the interrupt is not within the interrupt descriptor table (IDT) ;
  • handle corresponding to the one being processed vector(index) of an interrupt, is not a handle to a trap gate, interrupt gate, or task gate;
  • a software interrupt or software exception occurs (that is, one of the cases: INT n , INT 3 , INT01, BOUND, or INTO) and current privilege level(CPL) tasks more privilege level(DPL) gateway descriptor from IDT table −
  • segment selector in the corresponding processing vector(index) of the interrupt handle to the trap gate, interrupt gate, or task gate is a null selector;
  • the selector index of the segment descriptor of the trap gate or the descriptor of the interrupt gate does not fall within the limits of the corresponding table of descriptors;
  • TSS selector index from interrupt-corresponding task-gate descriptor is out of bounds global descriptor table(GDT);
  • descriptor new code segment is not a code segment descriptor ;
  • (DPL) new negotiated code segment is greater current privilege level(CPL) tasks -
  • handle privilege level(DPL) of the new mismatched code segment is not equal to current privilege level(CPL) tasks -
  • the TSS selector from the interrupt-corresponding task-gate descriptor points to local descriptor table(LDT);
  • the TSS handle of the new task is marked as busy- TYPE.B ≠ 1.
  • the address where the new value should be read for stack pointer(SS:eSP), out of TSS segment ;
  • the selector of the new stack segment is a null selector;
  • the selector index of the new stack segment does not fall within the corresponding descriptor table ;
  • requested privilege level(RPL) selector of new stack segment is not equal to (DPL) of a new code segment -
  • handle privilege level(DPL) of the new stack segment is not equal to handle privilege level(DPL) of a new code segment -
  • the new stack segment is not a writable data segment -
  • the corresponding interrupt handle to the trap gate , interrupt gate , task gate , or TSS handle is marked as absentee(the P bit of the descriptor is clear);
  • new code segment is not present ( bit P segment descriptor is reset).
  • when writing to the stack (including to a new stack if a stack switch took place) of values return addresses, stack pointer, flags or an error code, the stack segment is out of bounds;
  • the new stack segment is not present (the P bit of the segment descriptor is clear).

Special situations of real addressing mode:

The list of exceptions presented here characterizes the behavior of the processor not only when executing an instruction INT 3, but also when any external interrupt or exception is generated.

Special situations of V86 mode:

The list of exceptions presented here characterizes the behavior of the processor not only when executing an instruction INT 3, but also when any external interrupt or exception is generated. Bit EXT in the error code is used to indicate an event external to the interrupted program (

MASM, TASM and WASM assemblers differ from each other. However, creating simple programs for them has practically no differences, with the exception of the assembly and linking itself.

So our first program for MASM, TASM and WASM, which outputs English letter"A" at the current cursor position, that is, at the left upper corner screen:

Model tiny .code ORG 100h start: MOV AH,2 MOV DL,41h INT 21h INT 20h END start text editor- for example, in a NOTEBOOK (NotePad) from WINDOWS (but not in Word and not in another "fancy"). However, I recommend an "advanced" text editor with syntax highlighting such as PSPad (see section ). Then we save this file with the .asm extension, for example, in the MYPROG folder. Let's name the file atest. So we got: C:\MYPROG\atest.asm.

NOTE
Note that in the first command we wrote 2 instead of 02h. MASM, TASM and WASM, like Emu8086, allow such "liberties". Although you can write 02h - there will be no error.

Explanations for the program:

.model tiny- 1st line. The .model directive defines the memory model for a particular file type. In our case, this is a COM file, so we choose the tiny model, which combines code, data, and stack segments. The tiny model is designed to create COM files.

.code- 2nd line. This directive starts a code segment.

ORG 100h- 3rd line. This command sets the program counter to 100h, because when loading a COM file into memory, DOS allocates the first 256 bytes for the PSP data block ( decimal number 256 is equal to hexadecimal 100h). The program code is located only after this block. All programs that compile to COM files must start with this directive.

start: MOV AH, 02h- 4th line. The start label is placed before the first command in the program and will be used in the END directive to indicate which command the program starts with. The MOV instruction places the value of the second operand into the first operand. That is, the value 02h is placed in register AH. What is it for? 02h is a DOS function that prints a character to the screen. We are writing a program for DOS, so we use the commands of this operating system(OS). And we write this function (or rather, its number) in the AH register, because interrupt 21h uses this particular register.

MOV DL, 41h- 5th line. The character code "A" is entered into the DL register. The ASCII character code for "A" is the number 41h.

INT 21h- 6th line. This is the same interrupt 21h - a command that calls the DOS system function specified in the AH register (in our example, this is function 02h). The INT 21h command is the main means of interaction between programs and the OS.

INT 20h- 7th line. This is an interrupt that tells the operating system to exit the program and transfer control to the console application. In the event that the program has already been compiled and run from the OS, the INT 20h command will return us to the OS (for example, to DOS).

END start- 8th line. The END directive terminates the program, at the same time indicating at what label its execution should begin.

What is an assembler

Assembler is a low-level programming language. Each processor has its own assembler. By programming in assembler you are directly working with the computer hardware. Assembly language source text consists of instructions (mnemonics) that, after compilation, are converted into processor instruction codes.

Developing programs in assembler is a very difficult thing. In return for the time spent, you get an effective program. Assembler programs are written when every cycle of the processor is important. In assembler, you give specific commands to the processor and no unnecessary garbage. This is what ensures the high speed of your program execution.

To correctly use the assembler, you need to know the program model of the microprocessor system. From a programmer's point of view, a microprocessor system consists of:

  1. microprocessor
  2. memory
  3. I/O devices.

The programming model is well described in the literature.

Assembly Syntax

The general format of a program line in assembler

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

Label field. The label can consist of symbols and underscores. Labels are used in conditional and unconditional jump operations.

Operator field. This field contains the command mnemonic. For example mnemonic mov

Operand field. Operands can only be present if Operator (operator field) is present. There may be no operands, or there may be several. Operands can be data on which you need to perform some action (send, add, etc.).

Comment field. The commentary is needed for the verbal accompaniment of the program. Everything behind the symbol ; considered a comment.

First assembly language program

This article will use assembler for the i80x86 processor and use the following software:

  • TASM - Borland Turbo Assembler - Compiler
  • TLINK - Borland Turbo Linker - link editor (linker)

To be specific, Tasm 2.0.

By tradition, our first program will print the string "Hello world!" to the screen.

sample.asm file

Model small ; Memory model.stack 100h ; Setting the stack size.data ; Beginning of program data segment HelloMsg DB "Hello World!",13,10,"$" .code ; Beginning of code segment mov ax,@DATA ; Move data segment address to AX register mov ds,ax ; Set DS register to data segment mov ah,09h ; DOS function to print a string to the screen mov dx,offset HelloMsg ; Set the offset to the beginning of the string int 21h ; Output string mov ax,4C00h ; DOS program exit function int 21h ; Exiting the program end

As you can see, the program is divided into segments: a data segment, a code segment, and there is also a stack segment.

Let's consider everything in order.

The .model small directive specifies the memory model. The small model is 1 segment for code, 1 segment for data and stack i.e. data and stack are in the same segment. There are other memory models, for example: tiny, medium, compact. Depending on the memory model you choose, segments of your program may overlap or may have separate segments in memory.

The .stack 100h directive sets the stack size. The stack is necessary to save some information with its subsequent restoration. In particular, the stack is used for interrupts. In this case, the contents of the FLAGS register, the CS register, and the IP register are stored on the stack. Next comes the execution of the interrupt program, and then the values ​​of these registers are restored.

  • The FLAGS register contains signs that are generated after the execution of an instruction by the processor.
  • The CS (Code Segment) register contains the address of the code segment.
  • Register IP (Instruction Pointer) - instruction pointer. It contains the address of the instruction to be executed next (Address relative to the CS code segment).

More detailed description goes beyond a simple article.

The .data directive defines the beginning of your program's data segment. The data segment defines "variables" i.e. there is a memory reservation for the necessary data. After .data there is a line
HelloMsg DB "Hello World!",13,10,"$"

Here HelloMsg is the symbolic name that matches the beginning of the string "Hello World!" (without quotes). That is, this is the address of the first character of our string relative to the data segment. The DB (Define Byte) directive defines the memory area available byte by byte. 13,10 - character codes New line and Carriage return, and the $ character is required for DOS function 09h to work correctly. So, our string will occupy 15 bytes in memory.

The .code directive defines the beginning of the code segment (CS - Code Segment) of the program. Next are the lines of the program containing command mnemonics.

Let's talk about the mov command.

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

The mov command is a move command. It sends the contents of the source to the destination. Transfers can be register-register, register-memory, memory-register, but there is no memory-memory transfer, i.e. everything goes through the registers of the processor.

To work with data, you need to set up a data segment register. The setup is that we write the address of the @DATA data segment to the DS (Data Segment) register. It is impossible to directly write the address to this register - this is the architecture, so we use the AX register. In AX we write the address of the code segment

and then we send the contents of the AX register to the DS register.

After that, the DS register will contain the address of the beginning of the data segment. The address DS:0000h will contain the character H. I'm assuming you know about segments and offsets.

The address is made up of two parts.<Сегмент>:<Смещение>where Segment is 2 bytes and offset is 2 bytes. It turns out 4 bytes for access to any memory cell.

mov ah,09h
mov dx,offset HelloMsg
int 21h

Here we write the number 09h into the AH register - the number of the function of the 21st interrupt, which displays the line on the screen.

In the next line, we write the address (embarrassment) to the beginning of our line into the DX register.

Next, we call interrupt 21h - this is an interrupt for DOS functions. Interrupt - when a running program is interrupted and the interrupting program starts executing. The interrupt number determines the address of a DOS subroutine that prints a string of characters to the screen.

You will probably have a question: Why do we write the function number 09h in the AH register? And why is the offset to the line written to the DX register?
The answer is simple: for each function, specific registers are defined that contain the input data for this function. You can see which registers are needed for specific functions in help "e.

movax,4C00h
int 21h

mov ax,4C00h - send the function number to the AX register. Function 4C00h - exit from the program.

int 21h - perform an interrupt (actually exit)

end - end of the program.

After the end directive, the compiler ignores everything, so you can write whatever you want there :)

If you have read to the end, then you are a hero!

Maiko G.V. Assembler for IBM PC: - M.: "Business-Inform", "Sirin" 1999 - 212 p.