Sei sulla pagina 1di 34

1.5.

Basics of How Operating


Systems Work
1.5.1. Role of Interrupts
Interrupts are signals sent to the CPU by external devices, normally I/O devices. They
tell the CPU to stop its current activities and execute the appropriate part of the operating
system.

There are three types of interrupts:

1. Hardware Interupts are generated by


hardware devices to signal that they need
some attention from the OS. They may
have just received some data (e.g.,
keystrokes on the keyboard or an data on
the ethernet card); or they have just
completed a task which the operating
system previous requested, such as
transfering data between the hard drive
and memory.
2. Software Interupts are generated by
programs when they want to request
a system call to be performed by the
operating system.
3. Traps are generated by the CPU itself to
indicate that some error or condition
occured for which assistance from the
operating system is needed.

Interrupts are important because they give the user better control over the computer.
Without interrupts, a user may have to wait for a given application to have a higher priority
over the CPU to be ran. This ensures that the CPU will deal with the process immediately.

1.5.2. CPU Execution Mode


There are two modes of execution, known as user mode and kernel or supervisor mode.
User mode is restricted in that certain instructions cannot be executed, certain registers
cannot be accessed, and I/O devices can not be accessed. Kernel mode has none of
these restrictions. A system call will set the CPU to kernel mode, as will traps and
interrupts. Application programs cannot do this.

Mode bit: Supervisor or User mode

 Supervisor mode
o Can execute all machine instructions
o Can reference all memory locations
 User mode
o Can only execute a subset of instructions
o Can only reference a subset of memory
locations

1.5.3. CPU Response to Interrupts


A key point towards understanding how operating systems work is to understand what
the CPU does when an interrupt occurs. The hardware of the CPU does the exact same
thing for each interrupt, which is what allows operating systems to take control away from
the current running user process. The switching of running processes to execute code
from the OS kernel is called a context switch.

CPUs rely on the data contained in a couple registers to correctly handle interrupts. One
register holds a pointer to the process control block of the current running process. This
register is set each time a process is loaded into memory. The other register holds a
pointer to a table containing pointers to the instructions in the OS kernel for interrupt
handlers and system calls. The value in this register and contents of the table are set
when the operating system is initialized at boot time.

The CPU performs the following actions in response to an interrupt:

1. Using the pointer to the current process control


block, the state and all register values for the
process are saved for use when the process is
later restarted.
2. The CPU mode bit is switched
to supervisory mode.
3. Using the pointer to the interrupt handler table
and the interrupt vector, the location of the kernel
code to execute is determined. The interrupt
vector is the IRQ for hardware interrupts (read
from an interrupt controller register) and an
argument to the interrupt assembly language
instruction for software interrupts.
4. Processing is switched to the appropriate portion
of the kernel.

The CPU uses a table and the interrupt vector to find OS the code to execute in
response to interrupts. A software interrupt is shown here.
As the computer runs, processing switches between user processes and the operating
system as hardware and software interrupts are received.
Introduction of Assembler
Assembler is a program for converting instructions written in low-level assembly code
into relocatable machine code and generating along information for the loader.

It generates instructions by evaluating the mnemonics (symbols) in operation field and


find the value of symbol and literals to produce machine code. Now, if assembler do all
this work in one scan then it is called single pass assembler, otherwise if it does in
multiple scans then called multiple pass assembler. Here assembler divide these tasks
in two passes:
 Pass-1:
1. Define symbols and literals and remember them in symbol table and literal
table respectively.
2. Keep track of location counter
3. Process pseudo-operations
 Pass-2:
1. Generate object code by converting symbolic op-code into respective numeric
op-code
2. Generate data for literals and look for values of symbols
Firstly, We will take a small assembly language program to understand the working in
their respective passes. Assembly language statement format:
[Label] [Opcode] [operand]

Example: M ADD R1, ='3'


where, M - Label; ADD - symbolic opcode;
R1 - symbolic register operand; (='3') - Literal

Assembly Program:
Label Op-code operand LC value(Location counter)
JOHN START 200
MOVER R1, ='3' 200
MOVEM R1, X 201
L1 MOVER R2, ='2' 202
LTORG 203
X DS 1 204
END 205
Let’s take a look on how this program is working:
1. START: This instruction starts the execution of program from location 200 and
label with START provides name for the program.(JOHN is name for program)
2. MOVER: It moves the content of literal(=’3′) into register operand R1.
3. MOVEM: It moves the content of register into memory operand(X).
4. MOVER: It again moves the content of literal(=’2′) into register operand R2 and its
label is specified as L1.
5. LTORG: It assigns address to literals(current LC value).
6. DS(Data Space): It assigns a data space of 1 to Symbol X.
7. END: It finishes the program execution.
Working of Pass-1: Define Symbol and literal table with their addresses.
Note: Literal address is specified by LTORG or END.
Step-1: START 200 (here no symbol or literal is found so both table would be empty)
Step-2: MOVER R1, =’3′ 200 ( =’3′ is a literal so literal table is made)
LITERAL ADDRESS

=’3′ –––

Step-3: MOVEM R1, X 201


X is a symbol referred prior to its declaration so it is stored in symbol table with blank
address field.
SYMBOL ADDRESS

X –––

Step-4: L1 MOVER R2, =’2′ 202


L1 is a label and =’2′ is a literal so store them in respective tables
SYMBOL ADDRESS

X –––

L1 202

LITERAL ADDRESS

=’3′ –––

=’2′ –––
Step-5: LTORG 203
Assign address to first literal specified by LC value, i.e., 203
LITERAL ADDRESS

=’3′ 203

=’2′ –––

Step-6: X DS 1 204
It is a data declaration statement i.e X is assigned data space of 1. But X is a symbol
which was referred earlier in step 3 and defined in step 6.This condition is called
Forward Reference Problem where variable is referred prior to its declaration and can
be solved by back-patching. So now assembler will assign X the address specified by
LC value of current step.
SYMBOL ADDRESS

X 204

L1 202

Step-7: END 205


Program finishes execution and remaining literal will get address specified by LC value
of END instruction. Here is the complete symbol and literal table made by pass 1 of
assembler.
SYMBOL ADDRESS

X 204

L1 202

LITERAL ADDRESS

=’3′ 203

=’2′ 205

Now tables generated by pass 1 along with their LC value will go to pass-2 of assembler
for further processing of pseudo-opcodes and machine op-codes.
Working of Pass-2:
Pass-2 of assembler generates machine code by converting symbolic machine-opcodes
into their respective bit configuration(machine understandable form). It stores all
machine-opcodes in MOT table (op-code table) with symbolic code, their length and
their bit configuration. It will also process pseudo-ops and will store them in POT
table(pseudo-op table).
Various Data bases required by pass-2:
1. MOT table(machine opcode table)
2. POT table(pseudo opcode table)
3. Base table(storing value of base register)
4. LC ( location counter)
x86 Assembly Guide
Contents: Registers | Memory and Addressing | Instructions | Calling Convention

This guide describes the basics of 32-bit x86 assembly language programming,
covering a small but useful subset of the available instructions and assembler
directives. There are several different assembly languages for generating x86 machine
code. The one we will use in CS216 is the Microsoft Macro Assembler (MASM)
assembler. MASM uses the standard Intel syntax for writing x86 assembly code.

The full x86 instruction set is large and complex (Intel's x86 instruction set manuals
comprise over 2900 pages), and we do not cover it all in this guide. For example,
there is a 16-bit subset of the x86 instruction set. Using the 16-bit programming model
can be quite complex. It has a segmented memory model, more restrictions on register
usage, and so on. In this guide, we will limit our attention to more modern aspects of
x86 programming, and delve into the instruction set only in enough detail to get a
basic feel for x86 programming.

Resources

 Guide to Using Assembly in Visual Studio — a tutorial on building and


debugging assembly code in Visual Studio
 Intel x86 Instruction Set Reference
 Intel's Pentium Manuals (the full gory details)

Registers

Modern (i.e 386 and beyond) x86 processors have eight 32-bit general purpose
registers, as depicted in Figure 1. The register names are mostly historical. For
example, EAX used to be called the accumulator since it was used by a number of
arithmetic operations, and ECX was known as the counter since it was used to hold a
loop index. Whereas most of the registers have lost their special purposes in the
modern instruction set, by convention, two are reserved for special purposes — the
stack pointer (ESP) and the base pointer (EBP).

For the EAX, EBX, ECX, and EDX registers, subsections may be used. For example, the
least significant 2 bytes of EAX can be treated as a 16-bit register called AX. The least
significant byte of AX can be used as a single 8-bit register called AL, while the most
significant byte of AX can be used as a single 8-bit register called AH. These names
refer to the same physical register. When a two-byte quantity is placed into DX, the
update affects the value of DH, DL, and EDX. These sub-registers are mainly hold-overs
from older, 16-bit versions of the instruction set. However, they are sometimes
convenient when dealing with data that are smaller than 32-bits (e.g. 1-byte ASCII
characters).

When referring to registers in assembly language, the names are not case-sensitive.
For example, the names EAX and eax refer to the same register.

Figure 1. x86 Registers

Memory and Addressing Modes


Declaring Static Data Regions
You can declare static data regions (analogous to global variables) in x86 assembly
using special assembler directives for this purpose. Data declarations should be
preceded by the .DATA directive. Following this directive, the directives DB, DW,
and DD can be used to declare one, two, and four byte data locations, respectively.
Declared locations can be labeled with names for later reference — this is similar to
declaring variables by name, but abides by some lower level rules. For example,
locations declared in sequence will be located in memory next to one another.

Example declarations:
.DATA

var DB 64 ; Declare a byte, referred to as location var, containing the value 64.

var2 DB ? ; Declare an uninitialized byte, referred to as location var2.

DB 10 ; Declare a byte with no label, containing the value 10. Its location is var2 + 1.

X DW ? ; Declare a 2-byte uninitialized value, referred to as location X.

Y DD 30000 ; Declare a 4-byte value, referred to as location Y, initialized to 30000.

Unlike in high level languages where arrays can have many dimensions and are
accessed by indices, arrays in x86 assembly language are simply a number of cells
located contiguously in memory. An array can be declared by just listing the values,
as in the first example below. Two other common methods used for declaring arrays
of data are the DUP directive and the use of string literals. The DUP directive tells the
assembler to duplicate an expression a given number of times. For example, 4
DUP(2) is equivalent to 2, 2, 2, 2.

Some examples:

; Declare three 4-byte values, initialized to 1, 2, and 3. The value of location Z +


Z DD 1, 2, 3
8 will be 3.

bytes DB 10 DUP(?) ; Declare 10 uninitialized bytes starting at location bytes.

DD 100
arr ; Declare 100 4-byte words starting at location arr, all initialized to 0
DUP(0)

; Declare 6 bytes starting at the address str, initialized to the ASCII character values
str DB 'hello',0
for hello and the null (0) byte.

Addressing Memory
Modern x86-compatible processors are capable of addressing up to 232 bytes of
memory: memory addresses are 32-bits wide. In the examples above, where we
used labels to refer to memory regions, these labels are actually replaced by the
assembler with 32-bit quantities that specify addresses in memory. In addition to
supporting referring to memory regions by labels (i.e. constant values), the x86
provides a flexible scheme for computing and referring to memory addresses: up to
two of the 32-bit registers and a 32-bit signed constant can be added together to
compute a memory address. One of the registers can be optionally pre-multiplied by
2, 4, or 8.
The addressing modes can be used with many x86 instructions (we'll describe them
in the next section). Here we illustrate some examples using the mov instruction that
moves data between registers and memory. This instruction has two operands: the
first is the destination and the second specifies the source.
Some examples of mov instructions using address computations are:
mov eax, [ebx] ; Move the 4 bytes in memory at the address contained in EBX into EAX

; Move the contents of EBX into the 4 bytes at memory address var. (Note, var is a
mov [var], ebx
32-bit constant).

mov eax, [esi-4] ; Move 4 bytes at memory address ESI + (-4) into EAX

mov [esi+eax], cl ; Move the contents of CL into the byte at address ESI+EAX

mov edx,
; Move the 4 bytes of data at address ESI+4*EBX into EDX
[esi+4*ebx]

Some examples of invalid address calculations include:


mov eax, [ebx-ecx] ; Can only add register values

mov [eax+esi+edi], ebx ; At most 2 registers in address computation

Size Directives
In general, the intended size of the of the data item at a given memory address can
be inferred from the assembly code instruction in which it is referenced. For
example, in all of the above instructions, the size of the memory regions could be
inferred from the size of the register operand. When we were loading a 32-bit
register, the assembler could infer that the region of memory we were referring to
was 4 bytes wide. When we were storing the value of a one byte register to memory,
the assembler could infer that we wanted the address to refer to a single byte in
memory.
However, in some cases the size of a referred-to memory region is ambiguous.
Consider the instruction mov [ebx], 2. Should this instruction move the value 2 into
the single byte at address EBX? Perhaps it should move the 32-bit integer
representation of 2 into the 4-bytes starting at address EBX. Since either is a valid
possible interpretation, the assembler must be explicitly directed as to which is
correct. The size directives BYTE PTR, WORD PTR, and DWORD PTR serve this
purpose, indicating sizes of 1, 2, and 4 bytes respectively.
For example:
mov BYTE PTR [ebx], 2 ; Move 2 into the single byte at the address stored in EBX.

; Move the 16-bit integer representation of 2 into the 2 bytes starting at the
mov WORD PTR [ebx], 2
address in EBX.

mov DWORD PTR [ebx], ; Move the 32-bit integer representation of 2 into the 4 bytes starting at the
2 address in EBX.

Instructions
Machine instructions generally fall into three categories: data movement,
arithmetic/logic, and control-flow. In this section, we will look at important examples
of x86 instructions from each category. This section should not be considered an
exhaustive list of x86 instructions, but rather a useful subset. For a complete list, see
Intel's instruction set reference.
We use the following notation:
<reg32> Any 32-bit register (EAX, EBX, ECX, EDX, ESI, EDI, ESP, or EBP)

<reg16> Any 16-bit register (AX, BX, CX, or DX)

<reg8> Any 8-bit register (AH, BH, CH, DH, AL, BL, CL, or DL)

<reg> Any register

<mem> A memory address (e.g., [eax], [var + 4], or dword ptr [eax+ebx])

<con32> Any 32-bit constant

<con16> Any 16-bit constant

<con8> Any 8-bit constant

<con> Any 8-, 16-, or 32-bit constant

Data Movement Instructions


mov — Move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...)
The mov instruction copies the data item referred to by its second operand (i.e.
register contents, memory contents, or a constant value) into the location referred
to by its first operand (i.e. a register or memory). While register-to-register moves
are possible, direct memory-to-memory moves are not. In cases where memory
transfers are desired, the source memory contents must first be loaded into a
register, then can be stored to the destination memory address.
Syntax
mov <reg>,<reg>
mov <reg>,<mem>
mov <mem>,<reg>
mov <reg>,<const>
mov <mem>,<const>

Examples
mov eax, ebx — copy the value in ebx into eax
mov byte ptr [var], 5 — store the value 5 into the byte at location var

push — Push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)
The push instruction places its operand onto the top of the hardware supported
stack in memory. Specifically, push first decrements ESP by 4, then places its operand
into the contents of the 32-bit location at address [ESP]. ESP (the stack pointer) is
decremented by push since the x86 stack grows down - i.e. the stack grows from
high addresses to lower addresses.
Syntax
push <reg32>
push <mem>
push <con32>

Examples
push eax — push eax on the stack
push [var] — push the 4 bytes at address var onto the stack

pop — Pop stack


The pop instruction removes the 4-byte data element from the top of the hardware-
supported stack into the specified operand (i.e. register or memory location). It first
moves the 4 bytes located at memory location [SP] into the specified register or
memory location, and then increments SP by 4.

Syntax
pop <reg32>
pop <mem>

Examples
pop edi — pop the top element of the stack into EDI.
pop [ebx] — pop the top element of the stack into memory at the four bytes starting
at location EBX.
lea — Load effective address
The lea instruction places the address specified by its second operand into the
register specified by its first operand. Note, the contents of the memory location are
not loaded, only the effective address is computed and placed into the register. This
is useful for obtaining a pointer into a memory region.

Syntax
lea <reg32>,<mem>

Examples
lea edi, [ebx+4*esi] — the quantity EBX+4*ESI is placed in EDI.
lea eax, [var] — the value in var is placed in EAX.
lea eax, [val] — the value val is placed in EAX.

Arithmetic and Logic Instructions


add — Integer Addition
The add instruction adds together its two operands, storing the result in its first
operand. Note, whereas both operands may be registers, at most one operand may
be a memory location.
Syntax
add <reg>,<reg>
add <reg>,<mem>
add <mem>,<reg>
add <reg>,<con>
add <mem>,<con>
Examples
add eax, 10 — EAX ← EAX + 10
add BYTE PTR [var], 10 — add 10 to the single byte stored at memory address var
sub — Integer Subtraction
The sub instruction stores in the value of its first operand the result of subtracting
the value of its second operand from the value of its first operand. As with add
Syntax
sub <reg>,<reg>
sub <reg>,<mem>
sub <mem>,<reg>
sub <reg>,<con>
sub <mem>,<con>
Examples
sub al, ah — AL ← AL - AH
sub eax, 216 — subtract 216 from the value stored in EAX
inc, dec — Increment, Decrement
The inc instruction increments the contents of its operand by one.
The dec instruction decrements the contents of its operand by one.

Syntax
inc <reg>
inc <mem>
dec <reg>
dec <mem>

Examples
dec eax — subtract one from the contents of EAX.
inc DWORD PTR [var] — add one to the 32-bit integer stored at location var

imul — Integer Multiplication


The imul instruction has two basic formats: two-operand (first two syntax listings
above) and three-operand (last two syntax listings above).
The two-operand form multiplies its two operands together and stores the result in
the first operand. The result (i.e. first) operand must be a register.
The three operand form multiplies its second and third operands together and stores
the result in its first operand. Again, the result operand must be a register.
Furthermore, the third operand is restricted to being a constant value.
Syntax
imul <reg32>,<reg32>
imul <reg32>,<mem>
imul <reg32>,<reg32>,<con>
imul <reg32>,<mem>,<con>

Examples

imul eax, [var] — multiply the contents of EAX by the 32-bit contents of the memory
location var. Store the result in EAX.
imul esi, edi, 25 — ESI → EDI * 25
idiv — Integer Division
The idiv instruction divides the contents of the 64 bit integer EDX:EAX (constructed
by viewing EDX as the most significant four bytes and EAX as the least significant four
bytes) by the specified operand value. The quotient result of the division is stored
into EAX, while the remainder is placed in EDX.

Syntax
idiv <reg32>
idiv <mem>

Examples

idiv ebx — divide the contents of EDX:EAX by the contents of EBX. Place the quotient
in EAX and the remainder in EDX.
idiv DWORD PTR [var] — divide the contents of EDX:EAX by the 32-bit value stored at
memory location var. Place the quotient in EAX and the remainder in EDX.
and, or, xor — Bitwise logical and, or and exclusive or
These instructions perform the specified logical operation (logical bitwise and, or,
and exclusive or, respectively) on their operands, placing the result in the first
operand location.

Syntax
and <reg>,<reg>
and <reg>,<mem>
and <mem>,<reg>
and <reg>,<con>
and <mem>,<con>
or <reg>,<reg>
or <reg>,<mem>
or <mem>,<reg>
or <reg>,<con>
or <mem>,<con>

xor <reg>,<reg>
xor <reg>,<mem>
xor <mem>,<reg>
xor <reg>,<con>
xor <mem>,<con>

Examples
and eax, 0fH — clear all but the last 4 bits of EAX.
xor edx, edx — set the contents of EDX to zero.

not — Bitwise Logical Not


Logically negates the operand contents (that is, flips all bit values in the operand).

Syntax
not <reg>
not <mem>

Example
not BYTE PTR [var] — negate all bits in the byte at the memory location var.

neg — Negate
Performs the two's complement negation of the operand contents.

Syntax
neg <reg>
neg <mem>

Example
neg eax — EAX → - EAX

shl, shr — Shift Left, Shift Right


These instructions shift the bits in their first operand's contents left and right,
padding the resulting empty bit positions with zeros. The shifted operand can be
shifted up to 31 places. The number of bits to shift is specified by the second
operand, which can be either an 8-bit constant or the register CL. In either case,
shifts counts of greater then 31 are performed modulo 32.

Syntax
shl <reg>,<con8>
shl <mem>,<con8>
shl <reg>,<cl>
shl <mem>,<cl>

shr <reg>,<con8>
shr <mem>,<con8>
shr <reg>,<cl>
shr <mem>,<cl>

Examples

shl eax, 1 — Multiply the value of EAX by 2 (if the most significant bit is 0)
shr ebx, cl — Store in EBX the floor of result of dividing the value of EBX by
2n wheren is the value in CL.
Control Flow Instructions
The x86 processor maintains an instruction pointer (IP) register that is a 32-bit value
indicating the location in memory where the current instruction starts. Normally, it
increments to point to the next instruction in memory begins after execution an
instruction. The IP register cannot be manipulated directly, but is updated implicitly
by provided control flow instructions.

We use the notation <label> to refer to labeled locations in the program text. Labels
can be inserted anywhere in x86 assembly code text by entering a label name followed
by a colon. For example,
mov esi, [ebp+8]
begin: xor ecx, ecx
mov eax, [esi]
The second instruction in this code fragment is labeled begin. Elsewhere in the code,
we can refer to the memory location that this instruction is located at in memory
using the more convenient symbolic name begin. This label is just a convenient way
of expressing the location instead of its 32-bit value.

jmp — Jump
Transfers program control flow to the instruction at the memory location indicated
by the operand.

Syntax
jmp <label>

Example
jmp begin — Jump to the instruction labeled begin.

jcondition — Conditional Jump


These instructions are conditional jumps that are based on the status of a set of
condition codes that are stored in a special register called the machine status word.
The contents of the machine status word include information about the last
arithmetic operation performed. For example, one bit of this word indicates if the
last result was zero. Another indicates if the last result was negative. Based on these
condition codes, a number of conditional jumps can be performed. For example,
the jz instruction performs a jump to the specified operand label if the result of the
last arithmetic operation was zero. Otherwise, control proceeds to the next
instruction in sequence.

A number of the conditional branches are given names that are intuitively based on
the last operation performed being a special compare instruction, cmp (see below).
For example, conditional branches such as jle and jne are based on first performing a
cmp operation on the desired operands.

Syntax
je <label> (jump when equal)
jne <label> (jump when not equal)
jz <label> (jump when last result was zero)
jg <label> (jump when greater than)
jge <label> (jump when greater than or equal to)
jl <label> (jump when less than)
jle <label> (jump when less than or equal to)

Example
cmp eax, ebx
jle done
If the contents of EAX are less than or equal to the contents of EBX, jump to the
label done. Otherwise, continue to the next instruction.
cmp — Compare
Compare the values of the two specified operands, setting the condition codes in the
machine status word appropriately. This instruction is equivalent to
the sub instruction, except the result of the subtraction is discarded instead of
replacing the first operand.

Syntax
cmp <reg>,<reg>
cmp <reg>,<mem>
cmp <mem>,<reg>
cmp <reg>,<con>

Example
cmp DWORD PTR [var], 10
jeq loop

If the 4 bytes stored at location var are equal to the 4-byte integer constant 10, jump
to the location labeled loop.
call, ret — Subroutine call and return
These instructions implement a subroutine call and return. The call instruction first
pushes the current code location onto the hardware supported stack in memory (see
the push instruction for details), and then performs an unconditional jump to the
code location indicated by the label operand. Unlike the simple jump instructions,
the call instruction saves the location to return to when the subroutine completes.

The ret instruction implements a subroutine return mechanism. This instruction first
pops a code location off the hardware supported in-memory stack (see
the pop instruction for details). It then performs an unconditional jump to the retrieved
code location.

Syntax
call <label>
ret

Calling Convention
To allow separate programmers to share code and develop libraries for use by many
programs, and to simplify the use of subroutines in general, programmers typically
adopt a common calling convention. The calling convention is a protocol about how
to call and return from routines. For example, given a set of calling convention rules,
a programmer need not examine the definition of a subroutine to determine how
parameters should be passed to that subroutine. Furthermore, given a set of calling
convention rules, high-level language compilers can be made to follow the rules, thus
allowing hand-coded assembly language routines and high-level language routines to
call one another.
In practice, many calling conventions are possible. We will use the widely used C
language calling convention. Following this convention will allow you to write
assembly language subroutines that are safely callable from C (and C++) code, and
will also enable you to call C library functions from your assembly language code.
The C calling convention is based heavily on the use of the hardware-supported
stack. It is based on the push, pop, call, and ret instructions. Subroutine parameters
are passed on the stack. Registers are saved on the stack, and local variables used by
subroutines are placed in memory on the stack. The vast majority of high-level
procedural languages implemented on most processors have used similar calling
conventions.
The calling convention is broken into two sets of rules. The first set of rules is
employed by the caller of the subroutine, and the second set of rules is observed by
the writer of the subroutine (the callee). It should be emphasized that mistakes in
the observance of these rules quickly result in fatal program errors since the stack
will be left in an inconsistent state; thus meticulous care should be used when
implementing the call convention in your own subroutines.
>
Stack during Subroutine Call
[Thanks to Maxence Faldor for providing a correct figure and to James Peterson for finding and fixing the bug in
the original version of this figure!]

A good way to visualize the operation of the calling convention is to draw the
contents of the nearby region of the stack during subroutine execution. The image
above depicts the contents of the stack during the execution of a subroutine with three
parameters and three local variables. The cells depicted in the stack are 32-bit wide
memory locations, thus the memory addresses of the cells are 4 bytes apart. The first
parameter resides at an offset of 8 bytes from the base pointer. Above the parameters
on the stack (and below the base pointer), the call instruction placed the return
address, thus leading to an extra 4 bytes of offset from the base pointer to the first
parameter. When the ret instruction is used to return from the subroutine, it will jump
to the return address stored on the stack.

Caller Rules
To make a subrouting call, the caller should:

1. Before calling a subroutine, the caller should save the contents of certain
registers that are designated caller-saved. The caller-saved registers are EAX,
ECX, EDX. Since the called subroutine is allowed to modify these registers, if
the caller relies on their values after the subroutine returns, the caller must
push the values in these registers onto the stack (so they can be restore after
the subroutine returns.
2. To pass parameters to the subroutine, push them onto the stack before the
call. The parameters should be pushed in inverted order (i.e. last parameter
first). Since the stack grows down, the first parameter will be stored at the
lowest address (this inversion of parameters was historically used to allow
functions to be passed a variable number of parameters).
3. To call the subroutine, use the call instruction. This instruction places the
return address on top of the parameters on the stack, and branches to the
subroutine code. This invokes the subroutine, which should follow the callee
rules below.

After the subroutine returns (immediately following the call instruction), the caller
can expect to find the return value of the subroutine in the register EAX. To restore
the machine state, the caller should:

1. Remove the parameters from stack. This restores the stack to its state before
the call was performed.
2. Restore the contents of caller-saved registers (EAX, ECX, EDX) by popping
them off of the stack. The caller can assume that no other registers were
modified by the subroutine.

Example
The code below shows a function call that follows the caller rules. The caller is calling
a function _myFunc that takes three integer parameters. First parameter is in EAX,
the second parameter is the constant 216; the third parameter is in memory
location var.
push [var] ; Push last parameter first
push 216 ; Push the second parameter
push eax ; Push first parameter last

call _myFunc ; Call the function (assume C naming)

add esp, 12
Note that after the call returns, the caller cleans up the stack using
the add instruction. We have 12 bytes (3 parameters * 4 bytes each) on the stack,
and the stack grows down. Thus, to get rid of the parameters, we can simply add 12
to the stack pointer.

The result produced by _myFunc is now available for use in the register EAX. The
values of the caller-saved registers (ECX and EDX), may have been changed. If the
caller uses them after the call, it would have needed to save them on the stack before
the call and restore them after it.

Callee Rules
The definition of the subroutine should adhere to the following rules at the
beginning of the subroutine:

1. Push the value of EBP onto the stack, and then copy the value of ESP into EBP
using the following instructions:
2. push ebp
3. mov ebp, esp

This initial action maintains the base pointer, EBP. The base pointer is used by
convention as a point of reference for finding parameters and local variables
on the stack. When a subroutine is executing, the base pointer holds a copy of
the stack pointer value from when the subroutine started executing.
Parameters and local variables will always be located at known, constant
offsets away from the base pointer value. We push the old base pointer value
at the beginning of the subroutine so that we can later restore the appropriate
base pointer value for the caller when the subroutine returns. Remember, the
caller is not expecting the subroutine to change the value of the base pointer.
We then move the stack pointer into EBP to obtain our point of reference for
accessing parameters and local variables.

4. Next, allocate local variables by making space on the stack. Recall, the stack
grows down, so to make space on the top of the stack, the stack pointer
should be decremented. The amount by which the stack pointer is
decremented depends on the number and size of local variables needed. For
example, if 3 local integers (4 bytes each) were required, the stack pointer
would need to be decremented by 12 to make space for these local variables
(i.e., sub esp, 12). As with parameters, local variables will be located at known
offsets from the base pointer.
5. Next, save the values of the callee-saved registers that will be used by the
function. To save registers, push them onto the stack. The callee-saved
registers are EBX, EDI, and ESI (ESP and EBP will also be preserved by the
calling convention, but need not be pushed on the stack during this step).

After these three actions are performed, the body of the subroutine may proceed.
When the subroutine is returns, it must follow these steps:
1. Leave the return value in EAX.
2. Restore the old values of any callee-saved registers (EDI and ESI) that were
modified. The register contents are restored by popping them from the stack.
The registers should be popped in the inverse order that they were pushed.
3. Deallocate local variables. The obvious way to do this might be to add the
appropriate value to the stack pointer (since the space was allocated by
subtracting the needed amount from the stack pointer). In practice, a less
error-prone way to deallocate the variables is to move the value in the base
pointer into the stack pointer: mov esp, ebp. This works because the base
pointer always contains the value that the stack pointer contained
immediately prior to the allocation of the local variables.
4. Immediately before returning, restore the caller's base pointer value by
popping EBP off the stack. Recall that the first thing we did on entry to the
subroutine was to push the base pointer to save its old value.
5. Finally, return to the caller by executing a ret instruction. This instruction will
find and remove the appropriate return address from the stack.

Note that the callee's rules fall cleanly into two halves that are basically mirror
images of one another. The first half of the rules apply to the beginning of the
function, and are commonly said to define the prologue to the function. The latter
half of the rules apply to the end of the function, and are thus commonly said to
define the epilogue of the function.
Example
Here is an example function definition that follows the callee rules:
.486
.MODEL FLAT
.CODE
PUBLIC _myFunc
_myFunc PROC
; Subroutine Prologue
push ebp ; Save the old base pointer value.
mov ebp, esp ; Set the new base pointer value.
sub esp, 4 ; Make room for one 4-byte local variable.
push edi ; Save the values of registers that the function
push esi ; will modify. This function uses EDI and ESI.
; (no need to save EBX, EBP, or ESP)

; Subroutine Body
mov eax, [ebp+8] ; Move value of parameter 1 into EAX
mov esi, [ebp+12] ; Move value of parameter 2 into ESI
mov edi, [ebp+16] ; Move value of parameter 3 into EDI

mov [ebp-4], edi ; Move EDI into the local variable


add [ebp-4], esi ; Add ESI into the local variable
add eax, [ebp-4] ; Add the contents of the local variable
; into EAX (final result)

; Subroutine Epilogue
pop esi ; Recover register values
pop edi
mov esp, ebp ; Deallocate local variables
pop ebp ; Restore the caller's base pointer value
ret
_myFunc ENDP
END
The subroutine prologue performs the standard actions of saving a snapshot of the
stack pointer in EBP (the base pointer), allocating local variables by decrementing the
stack pointer, and saving register values on the stack.

In the body of the subroutine we can see the use of the base pointer. Both parameters
and local variables are located at constant offsets from the base pointer for the
duration of the subroutines execution. In particular, we notice that since parameters
were placed onto the stack before the subroutine was called, they are always located
below the base pointer (i.e. at higher addresses) on the stack. The first parameter to
the subroutine can always be found at memory location EBP + 8, the second at EBP +
12, the third at EBP + 16. Similarly, since local variables are allocated after the base
pointer is set, they always reside above the base pointer (i.e. at lower addresses) on the
stack. In particular, the first local variable is always located at EBP - 4, the second at
EBP - 8, and so on. This conventional use of the base pointer allows us to quickly
identify the use of local variables and parameters within a function body.

The function epilogue is basically a mirror image of the function prologue. The
caller's register values are recovered from the stack, the local variables are
deallocated by resetting the stack pointer, the caller's base pointer value is
recovered, and the ret instruction is used to return to the appropriate code location
in the caller.
Credits: This guide was originally created by Adam Ferrari many years ago,
and since updated by Alan Batson, Mike Lack, and Anita Jones.
It was revised for 216 Spring 2006 by David Evans.

x86 Assembly Registers[All Types Explained]


As a whole, there are fourteen(14) x86 registers and 35 MIPS registers. They are classified into
three parts:

 Data Registers: Holds data for operations.


 Address Register: Holds Address of instruction.
 Status Registers: Store the current status of the processor.
Table X86 Registers
DATA REGISTERS ADDRESS REGISTERS STATUS REGISTERS

32 16 Status Control
bit bit 8+8 bit Segment Pointer Index Flags Flags

EAX AX AL+AH CS SP SI CF SF TF

EBX BX BL+BH DS BP DI PF OF IF

ECX CX CL+CH SS AF ZF DF

EDX DX DL+DH ES

Now we gonna explain these registers step by step:

Contents
 1 Data Registers In x86 Assembly
o 1.1 Accumulator Register(AX)
o 1.2 Base Register(BX)
o 1.3 Count Register(CX)
o 1.4 Data Register(DX)
 2 Address Registers
o 2.1 Segment Registers
 2.1.1 Code Segment(CS)
 2.1.2 Data Segment(DS)
 2.1.3 Stack Segment(SS)
 2.1.4 Extra Segment(ES, FS, GS)
o 2.2 Pointer Registers
 2.2.1 Stack Pointer(SP)
 2.2.2 Base Pointer(BP)
 2.2.3 Instruction Pointer(IP)
o 2.3 Index Registers
 2.3.1 Source Index(SI)
 2.3.2 Destination Index(DI)
 3 Status Registers
o 3.1 Status Flags
 3.1.1 Carry Flag(CF)
 3.1.2 Parity Flag(PF)
 3.1.3 Auxiliary Carry Flag(AF)
 3.1.4 Zero Flag(ZF)
 3.1.5 Sign Flag(SF)
 3.1.6 Overflow Flag(OF)
o 3.2 Control Flags
 3.2.1 Trap Flag(TF)
 3.2.2 Interrupt Flag(IF)
 3.2.3 Direction Flag(DF)

Data Registers In x86 Assembly


Four sets of registers contain in the x86 assembly are for general data manipulation. However, a
processor can operate on data stored in memory, but processor can perform data manipulation at
the much faster rate when data is in registers. Read further to find out the functionalities of these
registers.

Data registers are divided into three parts according to sizes:

 EAX, EBX, ECX, EDX are 32-bit data registers.


 Further lower part of EAX, EBX, ECX, EDX are used as 16-bit data registers which are AX,
BX, CX, DX.
 Furthermore, higher and lower parts of AX, BX, CX, DX can be used as 8-bit data registers
which are AL, BL, CL, DL and AH, BH, CH, DH whereas H is used for the higher part and L is
used for the lower part.

Accumulator Register(AX)
Accumulator register is preferred to use in arithmetic, logic, and data transfer operations. i.e. In
division and multiplication, one of the numbers must be in AX(32-bit) or AL(16-bit).

Base Register(BX)
Base Register is the register, hold the address of the base storage location from where the data were
stored continuously. Base register value is used to find the data that is required.

Count Register(CX)
Count register is preferred to use as a loop counter. i.e. while shifting and rotating bits CX is used as
counter.

Data Register(DX)
The data register is used in I/O operations as well as preferred in division and multiplication while
execution of large values.(DX and DL)

Address Registers
Address registers typically store the memory address from where data will be fetched into CPU or
address to which data will be sent or stored. Address registers are further divided into three parts:
Segment Registers
To understand the use of segment registers you have to understand the logic of memory
segmentation. In 8086 we have an address of size 20-bits whereas the size of the processor is 16-
bit, so we need to divide the address into segments. We divide it into the segment and offset
address and by the combination of both we get the actual address. Here comes the use of segment
registers which are:
Code Segment(CS)
As in code segment, we write the instructions that need to be executed. So Code Segment(CS)
register contain all the instruction that are to be executed and also has the starting address of code
segment.

Data Segment(DS)
It stores the starting address of data segment as well as the complete data.

Stack Segment(SS)
There is a data structure called stack used by the processor to implement the subroutine calls. The SS
register store the address of subroutine or procedure.

Extra Segment(ES, FS, GS)


If a program needs to access second data segment, it can use the extra segment register.

Pointer Registers
Pointer registers of 32-bit are ESP, EBP, EIP whereas the 16-bit pointer registers are as SP, BP, IP.
These registers point to the memory location, but they can also be used in arithmetic and other
registers, unlike segment registers.

Stack Pointer(SP)
The stack pointer is used in combination with stack segment(SS) as SS: SP for accessing the stack
segment. It contains the top address of the stack.

Base Pointer(BP)
Unlike stack pointer, the base pointer contains the base address of the stack. It is used to access the
data on a stack. It is combined with stack segment (SS:BP).

Instruction Pointer(IP)
Sometimes called as index pointer store the offset address of next instruction to be executed. It is
combined with code segment (CS:IP).
Index Registers
32-bit index registers are ESI and EDI whereas the 16-bit rightmost are SI and DI. They are
mentioned below:

Source Index(SI)
The SI 1source index) register is used to point to memory locations in the data segment addressed
by OS. By incrementing the contents of SI, we can eaSily access Consecutive memory locations. I

Destination Index(DI)
The DI (destination Index) register performs the same functions as SI. There is a class of instructions,
called string operations, that use DI to access memory locations addressed by ES.

Status Registers
Status registers are called as flags which represent the state of a processor. It is done by setting the
bits called flags.

There are two types of flags described below:

Status Flags
The status flags are used by the processor to reflect the result of an operation. I.e. let an instruction
SUB AX, AX has executed the zero flag become 1 which return zero results. Status flags are discussed
below:

Status Flags Table


NAME SYMBOL BIT

Carry Flag CF 0

Parity Flag PF 2

Auxiliary Carry Flag AF 4

Zero Flag ZF 6

Sign Flag SF 7

Overflow Flag OF 11
Carry Flag(CF)
On addition if there is a carry-out from the most significant bit or there is a borrow into msb on
subtraction then carry flag will be:

[code]CF=1[/code]

Otherwise:

[code]CF=0[/code]

Parity Flag(PF)
If low Bytes of result has an even number of one, then Parity flag is:

[code]PF=1[/code]

If low bytes has an odd number of one then parity flag is:

[code]PF=0[/code]

For Example: If a result of a addition in 10101000, it ha 3 one bits, so parity flag is 0.

Auxiliary Carry Flag(AF)


If there is carry out from bit number 3 on addition or carry into bit number three then the value of
Auxiliary Carry flag is:

[code]AF=1[/code]

An auxiliary flag is used in binary coded decimal.

Zero Flag(ZF)
If a result is zero then zero flag is:

[code]ZF=1[/code]

and if a result is non-zer0 then zero flag is:

[code]ZF=0[/code]

Sign Flag(SF)
If the most significant bit of a result is 1, it means its a negative value so then sign flag will be:

[code]SF=1[/code]
If MSB is zero then the value of sign flag will be:

[code]SF=0[/code]

Overflow Flag(OF)
If signed overflow is occurred then the value of overflow flag is:

[code]OF=1[/code]

otherwise it will be:

[code]OF=0[/code]

Control Flags
There are three main control flags. See table below:

Control Flags Table


NAME SYMBOL BIT

Trap Flag TF 8

Interrupt Flag IF 9

Direction Flag DF 10

Trap Flag(TF)
It allows setting the operation of the processor in single-step mode. The DEBUG program we used
sets the trap flag, so we could step through the execution one instruction at a time.

Interrupt Flag(IF)
If decides whether the external interrupts are to be ignored or processed. It disables the external
interrupt when the value is 0 and enables interrupts when the value is 1.

Direction Flag(DF)
It determines left or right direction for moving or comparing string data. When the DF value is 0, the
string operation takes left-to-right direction and when the value is set to 1, the string operation takes
right-to-left direction.
Assembly Programming Tutorial

Potrebbero piacerti anche