X 86 Notes
X 86 Notes
The Intel x86 line of CPUs use the accumulator machine model.
Registers
Note that each register has 32 bit, 16 bit and 8 bit names. We will usually use just the 32 bit names for
the registers. See the diagrams of the registers on the following pages.
• The primary accumulator register is called EAX. The return value from a function call is saved in
the EAX register. Secondary accumulator registers are: EBX, ECX, EDX.
• The EBP register is the stack frame pointer. It is used to facilitate calling and returning from
functions.
• ESI and EDI are general purpose registers. If a variable is to have register storage class, it is
often stored in either ESI or EDI. A few instructions use ESI and EDI as pointers to source and
destination addresses when copying a block of data. Most compilers preserve the value of ESI and
EDI across function calls — not generally true of the accumulator registers.
• The ESP register is the stack pointer. It is a pointer to the “top” of the stack.
• The EFLAGS register is sometimes also called the status register. Several instructions either set or
check individual bits in this register. For example, the sign flag (bit 7) and the zero flag (bit 6) are
set by the compare (cmp) instruction and checked by all the conditional branching instructions.
• The EIP register holds the instruction pointer or program counter (pc), which points to the next
instruction in the text section of the currently running program.
1
31 0
EAX
(Accumulator)
AH AL
AX
EBX
(Base)
BH BL
BX
ECX
(Count)
CH CL
CX
EDX
(Data)
DH DL
DX
EBP
(Base Pointer)
BP
ESI
(Source Index)
SI
EDI
(Destination Index)
DI
ESP
(Stack Pointer)
SP
General Register Designations for x86 CPUs.
2
0 Carry Flag
Parity Flag Zero Flag (zf)
Auxiliary Flag
Sign Flag (sf)
6
7
Trap Flag
Interrupt Enable
Direction Flag
Overflow Flag
I/O Privilege Level
Nested Task Flag
18 Resume Flag
Virtual 8086 Mode
31
EFLAGS Register.
Segment register
Memory
base address (4 MS bits)
address
Segment
1111111111
0000000000
0000000000
1111111111
3
Segment register
GDTR or Memory
LDTR TI Index
GDT
access limit
base address
access limit
base address
access limit
base address
access limit
base address Segment
...
+ 1111111111
0000000000
0000000000
1111111111
address
the data into another, and the stack is placed into a third segment. Each segment is given its own address
space of up to 64 Kbytes in length. The 16–bit addresses used by the program are actually an offset from
a segment base address. This is called real mode, segmented memory model and instructions and data are
referenced relative to a base address held in the segment register (see diagram). The segment registers
are CS (code segment), SS (stack segment), DS, ES, FS, GS (all data segments). The segmented model
increases the addressable memory size to 220 = 1M byte. The segment and offset registers are combined
in an unusual manner. The two registers are offset by four bits and added together to come up with a
20-bit address. This is the memory model used by DOS.
The only advantage to this mode was that it was very easy for developers to write their own device
drivers. Once DOS loaded a program, it stayed out of the way and the program had full control of the
CPU. The program can either let the BIOS handle the interrupts or handle them itself. This worked
great for small programs which could fit into the available memory and did not require multi-tasking.
BIOS: Software in read–only–memory of the computer with basic device drivers and interrupt han-
dlers for I/O devices (keyboard, drives, monitor, printer, mouse). BIOS is used when the computer is
turned on to load the operating system. Modern operating systems (Unix, Linux, Windows) do not use
the BIOS drivers once the operating system is running (booted).
For more demanding applications, the limitations of the real mode scheme were prohibitive. So
beginning with the Intel 80286 processor, a protected mode was also available. In protected mode, these
processors provide the following features:
Protection: Each program can be allocated a certain section of memory. Other programs cannot use
4
this memory, so each program is protected from interference from other programs.
Extended memory: Enables a single program to access more than 640K of memory.
Virtual memory: Expands the address space to 16 MB for 16–bit processors and 4 GB for 32–bit
processors (80386 and later).
Multitasking: Enables the microprocessor to switch from one program to another so the computer can
execute several programs at once.
In the protected mode, segmented memory model, the code segment contains an offset into the global
descriptor table, where more details about the base address and memory protection / limits are stored. A
special register called the GDTR points to the location of the GDT and the segment registers hold offsets
pointing to the desired entry called a segment descriptor in the GDT (see diagram). The Minix OS uses
a protected mode, segmented memory model. Minix boots into this mode and stays in protected mode.
Very complicated articles can be found in literature and on the Internet describing how a DOS program
can switch the processor to protected mode and then return to real mode when the program exits.
Modern x86 based operating systems (Windows and Linux) use a protected mode, flat memory model
where the base memory addresses in the segment descriptors in the GDT are all set to the same value. This
mode greatly simplifies things, making segmentation and memory protection a non-issue for programmers.
Summary
4004 First Intel CPU - 4 bit.
8088 16 bit CPU with 8 bit external data bus. DOS ran in real mode with segments.
80186 Used mainly with embedded systems. Added some new instructions.
80286 Added protected mode. Some versions of Unix (SC0 Xenix, minix) used protected mode with
segments.
80386 32 bit CPU. Windows 3.0, Linux used protected mode flat memory model.
Addressing Modes
The addressing mode refers to how operands are referenced in an assembly language instruction. We
will use the mov instruction here to describe the available addressing modes of the x86 family of processors.
The mov instruction copies data between two locations. It’s syntax is shown below — dest and source
represent the operands. Data is copied from the source to the destination.
5
mov dest, source
Register Mode A register mode operand simply names a register. Both operands use register mode
below. Here we copy the contents of register ECX to register EAX. Note that register names are
not case sensitive in the assembly code.
mov EAX, ECX
Immediate Mode An immediate mode operand is a constant listed directly in the code. Below, we
use immediate mode with the second operand to store the value 10 in the EAX register. The
immediate mode operand must be the source operand.
mov EAX, 10
Register Indirect (On SPARC, this same mode is called Register direct.) Here we use a register to
hold a pointer (address in main memory) of where data can be moved to or from. Both operands
of an instruction can not be register indirect — one of the operands must be either register mode
or immediate mode. Brackets are placed around the operand to indicate register indirect. In C
language terminology, brackets may be viewed as the dereference operator. Some compilers use
square brackets, others use parentheses.
mov [EAX], EDX ; contents of edx goes to address pointed to by eax.
mov ebx, [edx] ; data at address pointed to by edx goes to ebx.
Listed here are only the most commonly used instructions. Information on additional instructions
can be found from the Intel manual (/pub/cis450/Pentium.pdf or /pub/cis450/x86Instructions.ps)
7
Instruction Operands Notes
add reg, reg two’s complement addition
reg, immed first operand is used as source and overwritten as destination
reg, mem
sub reg, reg two’s complement subtraction
reg, immed first operand is used as source and overwritten as destination
reg, mem
inc reg increment the value in register
dec reg decrement the value in register
neg reg additive inverse
mul EAX, reg Unsigned multiply
EAX, immed Some compilers tend to use imul instead
EAX, mem
imul reg Signed multiply, EAX*reg → EAX
reg, reg
reg, immed
reg, mem
div reg Unsigned divide
mem EAX / reg,mem; EAX = quotient, EDX = remainder,
idiv reg Signed divide
mem EAX / reg,mem; EAX = quotient, EDX = remainder,
Segment declaration
There are four different assembly segments: text, rom, data and bss. Segments are declared and selected
by the sect pseudo-op. It is customary to declare all segments at the top of an assembly file like this:
Then within the body of the code, segment declarations are used to begin the declarations for each
segment. Note that the ’.’ symbol refers to the location in the current segment.
Labels
There are two types: name and numeric. Name labels consist of a name followed by a colon (:).
The numeric labels are single digits. The nearest 0: label may be referenced as 0f in the forward
direction, or 0b backwards.
8
Statement Syntax
Each line consists of a single statement. Blank or comment lines are allowed.
The most general form of an instruction is
label: opcode operand1, operand2 ! comment
function parameters
return address
old frame pointer ←− frame pointer (ebp)
local variables ←− stack pointer (esp)
To set up the frame pointer at the beginning of each function (including main), the following two
lines of assembly code are used.
push ebp
mov ebp,esp
So first, the old frame pointer is pushed onto the stack for use when the function returns to the calling
(parent) function. Then, since the old frame pointer is now at the top of the stack, we can use the pointer
value in the esp register to copy a pointer to where the old frame pointer was stored to the ebp register,
making this the new frame pointer.
Here is a simple example of how local variables in the stack are managed. Try to draw a memory map
of the stack.
Recall that C compilers implement a restriction that each function may only access (i.e. scope) those
elements on the stack which are within the function’s Activation Record. The Activation Record for
each function includes the following:
function parameters
return address
old frame pointer ←− frame pointer (ebp)
local variables ←− stack pointer (esp)
The steps for a function are the same for every C function. It should be pointed out that this is the
scheme used by compilers. Some assembly programmers follow this scheme for hand written assembly
code. But many assembly programmers never worry about setting the frame pointer.
1. The calling function pushes the function parameters onto the stack prior to the function call.
2. The call instruction pushes the return address (EIP register) onto the stack which is used on function
exit by the ret (return) instruction which loads the EIP register with this address.
3. The function (assembly code) pushes the old frame pointer onto the stack and sets the EBP register
to point to this location on the stack.
push ebp
mov ebp,esp
4. During the execution of the function, the frame pointer is used as a reference point to the rest of
the memory in the activation record. On function exit, the leave instruction loads the EBP register
from this saved value so that when control returns to the calling function, the frame pointer is still
correct.
10
5. Local variables are stored on the stack and are removed from the stack when the function exits.
6. If the function returns data to the calling function, the return value is placed in the EAX register.
7. The calling function removes and discards the function parameters when control is returned from
the function.
8. The calling function looks to the EAX register for a return value.
int main(void)
{
... k | c |
f(a, b, c); j | b |
... i | a |
} | ret addr |
| old fp | <--- fp (ebp)
void f(int i, int j, int k) | x |
{ | y |
int x, y, z; | z | <--- sp (esp)
...
}
Here is a more extensive example, again try to draw a memory map. Check your memory map with
the memory map posted on the class web page for ar.c. This example includes examples of global and
static data which are saved in the bss and data section of memory.
11
#include <stdio.h>
int gbss;
int gdata = 5;
int main(void)
{
int lauto1, lauto2, lauto3;
static int lbss;
gbss = 10;
lbss = 20;
lauto1 = f( gdata, gbss, lbss );
lauto2 = 5;
lauto3 = 15;
printf( "%d %d %d\n", lauto1, lauto2, lauto3 );
printf( "%d\n", f( lauto3, lauto2, 5 ));
return 0;
}
d += a + b + c;
e = d*a;
return e;
}
12
12 push esi ! lauto3 = esi -- note: register without asking
13 push edi ! lauto2 = edi
14 .sect .bss
15 .comm I_1,4 ! 4 bytes in bss (I_1) for static int lbss
16 .sect .text
17 mov (_gbss),10 ! gbss = 10
18 mov edx,20
19 mov (I_1),edx ! lbss (I_1) = edx = 20
20 push edx
21 push (_gbss) ! push params in reverse order
22 push (_gdata)
23 call _f
24 add esp,12 ! remove params from stack
25 mov -4(ebp),eax ! lauto1 = f(...)
26 mov edi,5 ! lauto2 = 5
27 mov esi,15 ! lauto3 = 15
28 push esi
29 push edi ! push params in reverse order
30 push -4(ebp)
31 push I_2 ! format ... "%d %d %d\n"
32 call _printf
33 add esp,16 ! remove params
34 push 5
35 push edi
36 push esi
37 call _f
38 add esp,12 ! remove params
39 push eax ! push return value to stack
40 push I_3 ! format ... "%d\n"
41 call _printf
42 pop ecx
43 pop ecx ! remove params, alternate to ‘add esp,8’
44 xor eax,eax ! return 0
45 pop edi
46 pop esi ! restore registers
47 leave ! restore old frame pointer from stack
48 ret ! return address comes from stack
49 .sect .rom ! rom is part of text
50 I_3:
51 .data4 680997 ! format ... "%d\n"
52 I_2: ! format ... "%d %d %d\n"
53 .data4 622879781
54 .data4 1680154724
55 .extern _f
56 .data4 10
13
57 .sect .text
58 _f:
59 push ebp ! save old frame pointer
60 mov ebp,esp ! new frame pointer goes to ebp
61 sub esp,4 ! e = -4(ebp)
62 .sect .bss
63 .comm I_4,4 ! 4 bytes in bss (I_4) for static int d
64 .sect .text
65 mov edx,12(ebp)
66 add edx,8(ebp) ! add parameters (a, b, c)
67 add edx,16(ebp)
68 add edx,(I_4) ! d += a + b + c
69 mov (I_4),edx
70 imul edx,8(ebp) ! edx = d*a
71 mov eax,edx ! return e; note -- no need to save edx to -4(ebp)
72 leave ! restore old frame pointer from stack
73 ret ! return address comes from stack
74 .extern _gbss
75 .sect .bss
76 .comm _gbss,4 ! 4 bytes in bss for global int lbss
77 .sect .text
Additional Instructions
Logical Instructions
14
0
A logical shift moves the bits a set number of positions to the right or left. Positions which are not
filled by the shift operation are filled with a zero bit. An arithmetic shift does the same, except the sign
bit is always retained. This variation allows a shift operation to provide a quick mechanism to either
multiply or divide 2’s–complement numbers by 2.
15
Example: Multiply and Divide by multiple of 2
Control Instructions
The following instructions are used to implement various control constructs (if, while, do while, for).
Conditional branch instructions follow a cmp or test instruction and evaluate the sign and zero flag (SF,
ZF) bit in the EFLAGS register. For each of these instructions, the operand is the name of a label found
in the assembly code.
See the notes below on control flow for examples of how they are used.
Iterative Instructions
The above control instructions can be used to implement looping constructs, but there are also some
special instructions just for the purpose of looping.
16
Instruction Notes
movs move one byte from [esi] to [edi]
movsb
movsw move one word (2 bytes) from [esi] to [edi]
movsd move one double word (4 bytes) from [esi] to [edi]
Miscellaneous Instructions
Instruction Notes
cld Clear the direction flag; used with string movement instructions
std Set the direction flag; used with string movement instructions
cli Clear or disable interrupts; Reserved for the OS
sti Set or enable interrupts; Reserved for the OS
nop no operation, used to make a memory location addressable
17
Input/Output Instructions
Control Flow
In assembly language, the instructions used to implement control constructs is the various forms of the
jump instructions. This is usually accomplished with a comparison (cmp) instruction to evaluate a logical
expression following a conditional jump instruction.
if block
false
expr
23
46 $L24: 8
47 9 push ebp
48 ; 11 : } 10 mov ebp, esp
49 11 sub esp, 12
50 pop edi 12 push ebx
51 pop esi 13 push esi
52 pop ebx 14 push edi
53 leave 15
54 ret 0 16 ; 3 : int i;
55 _main ENDP 17 ; 4 : int j;
56 _TEXT ENDS 18 ; 5 :
57 END 19 ; 6 : switch(i) {
20
switch 21 mov eax, DWORD PTR _i$[ebp]
22 mov DWORD PTR -12+[ebp], eax
Switch statements are implemented differently de- 23 jmp $L27
pending on the number of branches (case state- 24 $L31:
ments) in the switch structure. 25
In the following example, the number of 26 ; 7 : case 1: j = 1; break;
branches is small and the compiler puts the test 27
variable on the stack at -12[ebp] and uses a sequence 28 mov DWORD PTR _j$[ebp], 1
of cmp and jump statements. 29 jmp $L28
30 $L32:
main() 31
{ 32 ; 8 : case 2: j = 2; break;
int i; 33
int j; 34 mov DWORD PTR _j$[ebp], 2
35 jmp $L28
switch(i) { 36 $L33:
case 1: j = 1; break; 37
case 2: j = 2; break; 38 ; 9 : case 3: j = 3; break;
case 3: j = 3; break; 39
default: j = 4; 40 mov DWORD PTR _j$[ebp], 3
} 41 jmp $L28
} 42 $L34:
43
44 ; 10 : default: j = 4;
45
46 mov DWORD PTR _j$[ebp], 4
1 PUBLIC _main
47
2 _TEXT SEGMENT
48 ; 11 : }
3 _i$ = -4
49
4 _j$ = -8
50 jmp $L28
5 _main PROC NEAR
51 $L27:
6
52 cmp DWORD PTR -12+[ebp], 1
7 ; 2 : {
24
53 je $L31
54 cmp DWORD PTR -12+[ebp], 2
1 PUBLIC _main
55 je $L32
2 ; COMDAT _main
56 cmp DWORD PTR -12+[ebp], 3
3 _TEXT SEGMENT
57 je $L33
4 _i$ = -4
58 jmp $L34
5 _j$ = -8
59 $L28:
6 _main PROC NEAR
60 $L24:
7
61
8 ; 2 : {
62 ; 12 : }
9
63
10 push ebp
64 pop edi
11 mov ebp, esp
65 pop esi
12 sub esp, 76
66 pop ebx
13 push ebx
67 leave
14 push esi
68 ret 0
15 push edi
69 _main ENDP
16 lea edi, DWORD PTR [ebp-76]
70 _TEXT ENDS
17 mov ecx, 19
71 END
18 mov eax, -858993460 ; ccccccccH
19 rep stosd
20
21 ; 3 : int i;
The following example, which has a few more 22 ; 4 : int j;
branches, uses a simple jump table to determine 23 ; 5 :
which branch to take. This code also fills an area of 24 ; 6 : switch(i) {
the stack from -76[ebp] to -13[ebp] with alternating 25
ones and zeros (0xcccccccc). I do not know why this 26 mov eax, DWORD PTR _i$[ebp]
is done. It does not appear to accomplish anything. 27 mov DWORD PTR -12+[ebp], eax
28 mov ecx, DWORD PTR -12+[ebp]
int main() 29 sub ecx, 1
{ 30 mov DWORD PTR -12+[ebp], ecx
int i; 31 cmp DWORD PTR -12+[ebp], 7
int j; 32 ja SHORT $L44
33 mov edx, DWORD PTR -12+[ebp]
switch(i) { 34 jmp DWORD PTR $L49[edx*4]
case 1: j = 1; break; 35 $L37:
case 3: j = 3; break; 36
case 8: j = 8; break; 37 ; 7 : case 1: j = 1; break;
case 6: j = 6; break; 38
case 2: j = 2; break; 39 mov DWORD PTR _j$[ebp], 1
case 7: j = 7; break; 40 jmp SHORT $L34
case 4: j = 4; break; 41 $L38:
default: j = 9; break; 42
} 43 ; 8 : case 3: j = 3; break;
} 44
25
45 mov DWORD PTR _j$[ebp], 3 90 pop ebp
46 jmp SHORT $L34 91 ret 0
47 $L39: 92 $L49:
48 93 DD $L37 ; case 1
49 ; 9 : case 8: j = 8; break; 94 DD $L41 ; case 2
50 95 DD $L38 ; case 3
51 mov DWORD PTR _j$[ebp], 8 96 DD $L43 ; case 4
52 jmp SHORT $L34 97 DD $L44 ; case 5 - default
53 $L40: 98 DD $L40 ; case 6
54 99 DD $L42 ; case 7
55 ; 10 : case 6: j = 6; break; 100 DD $L39 ; case 8
56 101 _main ENDP
57 mov DWORD PTR _j$[ebp], 6 102 _TEXT ENDS
58 jmp SHORT $L34 103 END
59 $L41:
60 In the next example, the values in the the case
61 ; 11 : case 2: j = 2; break; statements are not are not close together, so the
62 compiler uses a two stage jump table. One table
63 mov DWORD PTR _j$[ebp], 2 hold an index into the second table which lists the
64 jmp SHORT $L34 location to jump to.
65 $L42:
66 int main()
67 ; 12 : case 7: j = 7; break; {
68 int i;
69 mov DWORD PTR _j$[ebp], 7 int j;
70 jmp SHORT $L34
71 $L43: switch(i) {
72 case 10: j = 1; break;
73 ; 13 : case 4: j = 4; break; case 33: j = 3; break;
74 case 85: j = 8; break;
75 mov DWORD PTR _j$[ebp], 4 case 66: j = 6; break;
76 jmp SHORT $L34 case 20: j = 2; break;
77 $L44: case 79: j = 7; break;
78 case 41: j = 4; break;
79 ; 14 : default: j = 9; break; default: j = 9; break;
80 }
81 mov DWORD PTR _j$[ebp], 9 }
82 $L34:
83
84 ; 16 : }
85
86 pop edi
1 PUBLIC _main
87 pop esi
2 ; COMDAT _main
88 pop ebx
3 _TEXT SEGMENT
89 mov esp, ebp
4 _i$ = -4
26
5 _j$ = -8 50
6 _main PROC NEAR 51 ; 9 : case 85: j = 8; break;
7 52
8 ; 2 : { 53 mov DWORD PTR _j$[ebp], 8
9 54 jmp SHORT $L34
10 push ebp 55 $L40:
11 mov ebp, esp 56
12 sub esp, 76 57 ; 10 : case 66: j = 6; break;
13 push ebx 58
14 push esi 59 mov DWORD PTR _j$[ebp], 6
15 push edi 60 jmp SHORT $L34
16 lea edi, DWORD PTR [ebp-76] 61 $L41:
17 mov ecx, 19 62
18 mov eax, -858993460 ;ccccccccH 63 ; 11 : case 20: j = 2; break;
19 rep stosd 64
20 65 mov DWORD PTR _j$[ebp], 2
21 ; 3 : int i; 66 jmp SHORT $L34
22 ; 4 : int j; 67 $L42:
23 ; 5 : 68
24 ; 6 : switch(i) { 69 ; 12 : case 79: j = 7; break;
25 70
26 mov eax, DWORD PTR _i$[ebp] 71 mov DWORD PTR _j$[ebp], 7
27 mov DWORD PTR -12+[ebp], eax 72 jmp SHORT $L34
28 mov ecx, DWORD PTR -12+[ebp] 73 $L43:
29 sub ecx, 10 74
30 mov DWORD PTR -12+[ebp], ecx 75 ; 13 : case 41: j = 4; break;
31 cmp DWORD PTR -12+[ebp], 75 76
32 ja SHORT $L44 77 mov DWORD PTR _j$[ebp], 4
33 mov eax, DWORD PTR -12+[ebp] 78 jmp SHORT $L34
34 xor edx, edx 79 $L44:
35 mov dl, BYTE PTR $L49[eax] 80
36 jmp DWORD PTR $L50[edx*4] 81 ; 14 : default: j = 9; break;
37 $L37: 82
38 83 mov DWORD PTR _j$[ebp], 9
39 ; 7 : case 10: j = 1; break; 84 $L34:
40 85
41 mov DWORD PTR _j$[ebp], 1 86 ; 16 : }
42 jmp SHORT $L34 87
43 $L38: 88 pop edi
44 89 pop esi
45 ; 8 : case 33: j = 3; break; 90 pop ebx
46 91 mov esp, ebp
47 mov DWORD PTR _j$[ebp], 3 92 pop ebp
48 jmp SHORT $L34 93 ret 0
49 $L39: 94 $L50:
27
95 DD $L37 ; entry 0 - case 10 140 DB 7
96 DD $L41 ; case 20 141 DB 7
97 DD $L38 ; case 33 142 DB 7
98 DD $L43 ; case 41 143 DB 7
99 DD $L40 ; case 66 144 DB 7
100 DD $L42 ; case 79 145 DB 7
101 DD $L39 ; case 85 146 DB 7
102 DD $L44 ; entry 7, default 147 DB 7
103 $L49: 148 DB 7
104 DB 0 ; 10 149 DB 7
105 DB 7 150 DB 7
106 DB 7 151 DB 7
107 DB 7 152 DB 7
108 DB 7 153 DB 7
109 DB 7 154 DB 7
110 DB 7 155 DB 7
111 DB 7 156 DB 7
112 DB 7 157 DB 7
113 DB 7 158 DB 7
114 DB 1 ; 20 159 DB 7
115 DB 7 160 DB 4 ; 66
116 DB 7 161 DB 7
117 DB 7 162 DB 7
118 DB 7 163 DB 7
119 DB 7 164 DB 7
120 DB 7 165 DB 7
121 DB 7 166 DB 7
122 DB 7 167 DB 7
123 DB 7 168 DB 7
124 DB 7 169 DB 7
125 DB 7 170 DB 7
126 DB 7 171 DB 7
127 DB 2 ; 33 172 DB 7
128 DB 7 173 DB 5 ; 79
129 DB 7 174 DB 7
130 DB 7 175 DB 7
131 DB 7 176 DB 7
132 DB 7 177 DB 7
133 DB 7 178 DB 7
134 DB 7 179 DB 6 ; 85
135 DB 3 ; 41 180 _main ENDP
136 DB 7 181 _TEXT ENDS
137 DB 7 182 END
138 DB 7
139 DB 7
28
break, continue 20 ; 7 : for (i = 1; i <= 17; i++) {
21
void main()
22 mov DWORD PTR _i$[ebp], 1
{
23 jmp $L28
int a, b;
24 $L29:
int i;
25 inc DWORD PTR _i$[ebp]
26 $L28:
for (i = 1; i <= 17; i++) {
27 cmp DWORD PTR _i$[ebp], 17
if (a == 0) continue;
28 jg $L30
if (b == 0) break;
29
}
30 ; 8 : if (a == 0) continue;
31
while (i <= 17) {
32 cmp DWORD PTR _a$[ebp], 0
if (a == 0) continue;
33 jne $L31
if (b == 0) break;
34 jmp $L29
}
35 $L31:
36
do {
37 ; 9 : if (b == 0) break;
if (a == 0) continue;
38
if (b == 0) break;
39 cmp DWORD PTR _b$[ebp], 0
} while (i <= 17);
40 jne $L32
}
41 jmp $L30
42 $L32:
43
44 ; 10 : }
45
46 jmp $L29
1 PUBLIC _main
47 $L30:
2 _TEXT SEGMENT
48 $L34:
3 _a$ = -4
49
4 _b$ = -8
50 ; 11 :
5 _i$ = -12
51 ; 12 : while (i <= 17) {
6 _main PROC NEAR
52
7
53 cmp DWORD PTR _i$[ebp], 17
8 ; 3 : {
54 jg $L35
9
55
10 push ebp
56 ; 13 : if (a == 0) continue;
11 mov ebp, esp
57
12 sub esp, 12
58 cmp DWORD PTR _a$[ebp], 0
13 push ebx
59 jne $L36
14 push esi
60 jmp $L34
15 push edi
61 $L36:
16
62
17 ; 4 : int a, b;
63 ; 14 : if (b == 0) break;
18 ; 5 : int i;
64
19 ; 6 :
29
65 cmp DWORD PTR _b$[ebp], 0 88 jne $L42
66 jne $L37 89 jmp $L40
67 jmp $L35 90 $L42:
68 $L37: 91 $L39:
69 92
70 ; 15 : } 93 ; 20 : } while (i <= 17);
71 94
72 jmp $L34 95 cmp DWORD PTR _i$[ebp], 17
73 $L35: 96 jle $L38
74 $L38: 97 $L40:
75 98 $L24:
76 ; 16 : 99
77 ; 17 : do { 100 ; 21 : }
78 ; 18 : if (a == 0) continue; 101
79 102 pop edi
80 cmp DWORD PTR _a$[ebp], 0 103 pop esi
81 jne $L41 104 pop ebx
82 jmp $L39 105 leave
83 $L41: 106 ret 0
84 107 _main ENDP
85 ; 19 : if (b == 0) break; 108 _TEXT ENDS
86 109 END
87 cmp DWORD PTR _b$[ebp], 0
30
Floating Point Arithmetic Instructions
31
26 jmp .L1 31 .Lfe1:
27 .p2align 4,,7 32 .size main,.Lfe1-main
28 .L1: 33 .ident
29 leave 34 "GCC: (GNU) egcs-2.91.66 19990314/Linux"
30 ret
32