Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability. |
EAX | Multipurpose. Return values from a function are usually stored in EAX. Low 16 bits are referenced as AX. AX can be further subdivided into AL (the low 8 bits), and AH (the upper 8 bits of AX). |
EBX | Multipurpose. Low 16 bits are referenced as BX. BX can be further subdivided into BL (the low 8 bits), and BH (the upper 8 bits of BX). |
ECX | Multipurpose. Often used as a counter, for example, to hold the number of loop iterations that should be performed. Low 16 bits are referenced as CX. CX can be further subdivided into CL (the low 8 bits), and CH (the upper 8 bits of CX). |
EDX | Multipurpose. Low 16 bits are referenced as DX. DX can be further subdivided into DL (the low 8 bits), and DH (the upper 8 bits of DX). |
ESI | Multipurpose. In certain operations that move or compare memory, ESI contains the source address. Low 16 bits are referenced as SI. |
EDI | Multipurpose. In certain operations that move or compare memory, EDI contains the destination address. Low 16 bits are referenced as DI. |
ESP | Stack pointer. Implicitly changed by PUSH, POP, CALL, and RET instructions. |
EBP | Base pointer. Usually points to the current stack frame for a procedure. Procedure parameters are usually at positive offsets from EBP (for example, EBP+8). Local variables are usually at negative offsets (for example, EBP-16). Sometimes, optimizing compilers won't use a stack frame, and use EBP as a multipurpose register. |
EFLAGS | Rarely directly referenced. Instead, instructions implicitly set or clear bitfields within the EFLAGS register to represent a certain state. For example, when the result of a mathematical operation is zero, the Zero flag is toggled on in the EFLAGS register. The conditional jump instructions make use of the EFLAGS register. |
FS | 16-bit. Under Win32, the FS register points to a data structure with information pertaining to the current thread. FS is a segment register (segment registers are beyond the scope of this discussion). Intel CPUs have six segment registers, but the operating system sets them up and maintains them. Win32 compilers only need to explicitly refer to the FS segment register, which is used for things like structured exception handling and thread local storage. |
Figure 2 InstructionDemo.CPP
//========================================== // Matt Pietrek // Microsoft Systems Journal, February 1998 // Program: InstructionDemo.CPP // FILE: InstructionDemo.CPP //========================================== #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdlib.h> #include <stdio.h> // Force these functions inline (/O2 would normally do this #pragma intrinsic( memset, strlen, strcmp ) __declspec(thread) int tlsVariable = 0; // Make a thread local variable int g_myGlobalVariable; // Make a global variable void MySubProcedure( void ); int main( int argc, char *argv[] ) { char szBuffer[128]; char *pszString = "Hello"; unsigned long localUnsignedLong = 2; unsigned char localUnsignedChar = 2; long localSignedLong = 2; char localSignedChar = 2; int i; g_myGlobalVariable = 0x12345678; // Assignment to global localSignedLong = localSignedChar; // signed type promotion // Conditional execution if ( localUnsignedLong == 2 ) localSignedLong = 1; else localSignedLong = 2; // Using TEST if ( localUnsignedLong & 0x00040008 ) i = 3; // AND'ing off bitfields localUnsignedLong &= 0x01020304; // OR'ing on bitfields localSignedLong |= 0x05060708; // LOOP code for ( i = 0; i < 4; i++ ) localUnsignedLong += i; // Procedure invocation printf( "%u %u %08X %s", localUnsignedLong, argc, &argc, szBuffer ); // Using STOSD / STOSB memset( szBuffer, 0, sizeof(szBuffer) ); // Using SCASB i = strlen( szBuffer ); MySubProcedure( ); return 0; } void MySubProcedure( void ) { tlsVariable = 2; // Use of try/except code __try { g_myGlobalVariable = 2; } __except( EXCEPTION_EXECUTE_HANDLER ) { g_myGlobalVariable = 4; } }
Figure 3 InstructionDemo Mixed Source and Assembly
int main( int argc, char *argv[] ) { 401000: PUSH EBP 401001: MOV EBP,ESP 401003: SUB ESP,00000098 401009: PUSH EDI char *pszString = "Hello"; 40100A: MOV DWORD PTR [EBP-0000008C],00406030 unsigned long localUnsignedLong = 2; 401014: MOV DWORD PTR [EBP-00000088],00000002 unsigned char localUnsignedChar = 2; 40101E: MOV BYTE PTR [EBP-00000094],02 long localSignedLong = 2; 401025: MOV DWORD PTR [EBP-00000084],00000002 char localSignedChar = 2; 40102F: MOV BYTE PTR [EBP-00000098],02 g_myGlobalVariable = 0x12345678; // Assignment to global 401036: MOV DWORD PTR [004088E8],12345678 localSignedLong = localSignedChar; // signed type promotion 401040: MOVSX EAX,BYTE PTR [EBP-00000098] 401047: MOV DWORD PTR [EBP-00000084],EAX // Conditional execution if ( localUnsignedLong == 2 ) 40104D: CMP DWORD PTR [EBP-00000088],02 401054: JNE 00401062 localSignedLong = 1; 401056: MOV DWORD PTR [EBP-00000084],00000001 else 401060: JMP 0040106C localSignedLong = 2; 401062: MOV DWORD PTR [EBP-00000084],00000002 // Using TEST if ( localUnsignedLong & 0x00040008 ) 40106C: MOV ECX,DWORD PTR [EBP-00000088] 401072: AND ECX,00040008 401078: TEST ECX,ECX 40107A: JE 00401086 i = 3; 40107C: MOV DWORD PTR [EBP-00000090],00000003 // AND'ing off bitfields localUnsignedLong &= 0x01020304; 401086: MOV EDX,DWORD PTR [EBP-00000088] 40108C: AND EDX,01020304 401092: MOV DWORD PTR [EBP-00000088],EDX // OR'ing on bitfields localSignedLong |= 0x05060708; 401098: MOV EAX,DWORD PTR [EBP-00000084] 40109E: OR EAX,05060708 4010A3: MOV DWORD PTR [EBP-00000084],EAX // LOOP code for ( i = 0; i < 4; i++ ) 4010A9: MOV DWORD PTR [EBP-00000090],00000000 4010B3: JMP 004010C4 4010B5: MOV ECX,DWORD PTR [EBP-00000090] 4010BB: ADD ECX,01 4010BE: MOV DWORD PTR [EBP-00000090],ECX 4010C4: CMP DWORD PTR [EBP-00000090],04 4010CB: JNL 004010E1 localUnsignedLong += i; 4010CD: MOV EDX,DWORD PTR [EBP-00000088] 4010D3: ADD EDX,DWORD PTR [EBP-00000090] 4010D9: MOV DWORD PTR [EBP-00000088],EDX 4010DF: JMP 004010B5 // Procedure invocation printf( "%u %u %08X %s", localUnsignedLong, argc, &argc, szBuffer ); 4010E1: LEA EAX,[EBP-80] 4010E4: PUSH EAX 4010E5: LEA ECX,[EBP+08] 4010E8: PUSH ECX 4010E9: MOV EDX,DWORD PTR [EBP+08] 4010EC: PUSH EDX 4010ED: MOV EAX,DWORD PTR [EBP-00000088] 4010F3: PUSH EAX 4010F4: PUSH 00406038 4010F9: CALL 004011C0 4010FE: ADD ESP,14 // Using STOSD / STOSB memset( szBuffer, 0, sizeof(szBuffer) ); 401101: MOV ECX,00000020 401106: XOR EAX,EAX 401108: LEA EDI,[EBP-80] 40110B: REP STOSD // Using SCASB i = strlen( szBuffer ); 40110D: LEA EDI,[EBP-80] 401110: OR ECX,FF 401113: XOR EAX,EAX 401115: REPNE SCASB 401117: NOT ECX 401119: ADD ECX,FF 40111C: MOV DWORD PTR [EBP-00000090],ECX MySubProcedure( ); 401122: CALL 0040112E return 0; 401127: XOR EAX,EAX } 401129: POP EDI 40112A: MOV ESP,EBP 40112C: POP EBP 40112D: RET void MySubProcedure( void ) { 40112E: PUSH EBP 40112F: MOV EBP,ESP 401131: PUSH FF 401133: PUSH 00405058 401138: PUSH 004012F8 40113D: MOV EAX,FS:[00000000] 401143: PUSH EAX 401144: MOV DWORD PTR FS:[00000000],ESP 40114B: SUB ESP,08 40114E: PUSH EBX 40114F: PUSH ESI 401150: PUSH EDI 401151: MOV DWORD PTR [EBP-18],ESP tlsVariable = 2; 401154: MOV EAX,[004088EC] 401159: MOV ECX,DWORD PTR FS:[0000002C] 401160: MOV EDX,DWORD PTR [ECX+EAX*4] 401163: MOV DWORD PTR [EDX+00000004],00000002 __try { 40116D: MOV DWORD PTR [EBP-04],00000000 g_myGlobalVariable = 2; 401174: MOV DWORD PTR [004088E8],00000002 40117E: MOV DWORD PTR [EBP-04],FFFFFFFF 401185: JMP 004011A1 __except( EXCEPTION_EXECUTE_HANDLER ) 401187: MOV EAX,00000001 40118C: RET 40118D: MOV ESP,DWORD PTR [EBP-18] g_myGlobalVariable = 4; 401190: MOV DWORD PTR [004088E8],00000004 } 40119A: MOV DWORD PTR [EBP-04],FFFFFFFF } 4011A1: MOV ECX,DWORD PTR [EBP-10] 4011A4: MOV DWORD PTR FS:[00000000],ECX 4011AB: POP EDI 4011AC: POP ESI 4011AD: POP EBX 4011AE: MOV ESP,EBP 4011B0: POP EBP 4011B1: RET