Turbo C User's Guide:
Embedded Assembly Language. Part I - Basic Principles
BASM.DOC is on online file that tells you how to use the Turbo C++ built-in inline assembler (BASM) to include assembly language routines in your C and C++ programs without any need for a separate assembler. Such assembly language routines are called inline assembly, because they are compiled right along with your C routines, rather than being assembled separately, then linked together with modules produced by the C compiler.
Turbo C++ lets you write assembly language code right inside your C and C++ programs. This is known as inline assembly and has certain restrictions.
- It cannot use assembler macros
- It cannot handle 80386 or 80486 instructions
- It does not permit Ideal mode syntax
- It allows only a limited set of assembler directives
To invoke the in-line assembler, you need only use the keyword asm to introduce an inline assembly language instruction. The format is
asm opcode operands
where opcode is a valid 80x86 instruction, and operands contains the operand(s) acceptable to the opcode, and can reference C constants, variables, and labels.
To include a number of asm statements, surround them with braces: The initial brace must appear on the same line as the asm keyword.
Semicolons are not used to start comments (as they are in TASM). When commenting asm statements, use C-style comments, like this:
asm mov ax,ds; /* This comment is OK */
asm {pop ax; pop ds; iret;} /* This is legal too */
asm push ds ;THIS COMMENT IS INVALID!!The assembly language portion of the statement is copied straight to the output, embedded in the assembly language that Turbo C++ is generating from your C or C++ instructions. Any C symbols are replaced with ap-propriate assembly language equivalents.
Because the inline assembly facility is not a complete assembler, it may not accept some assembly language constructs. If this happens, Turbo C++ will issue an error message. You then have two choices. You can simplify your inline assembly language code so that the assembler will accept it, or you can use an external assembler such as TASM. However, TASM might not identi-fy the location of errors, since the original C source line number is lost.
You can include any of the 80x86 instruction opcodes as Opcodes inline assembly statements. There are four classes of instructions allowed by the Turbo C++ compiler:
- normal instructions--the regular 80x86 opcode set
- string instructions--special string-handling codes
- jump instructions--various jump opcodes
- assembly directives--data allocation and definition
Note that all operands are allowed by the compiler, even if they are erroneous or disallowed by the assembler. The exact format of the operands is not enforced by the compiler.
Prefixes
The following prefixes can be used:
lock rep repe repne repnz repz
Jump Instructions
Jump instructions are treated specially. Since a label cannot be included on the instruction itself, jumps must go to C labels. The allowed jump instructions are given in the next table.
ja jge jnc jns loop jae jl jne jnz loope jb jle jng jo loopne jbe jmp jnge jp loopnz jc jna jnl jpe loopz jcxz jnae jnle jpo je jnb jno js jg jnbe jnp jzAssembly Directives
The following assembly directives are allowed in Turbo C++ inline assembly statements:
db dd dw extrnC Symbols
You can use C symbols in your asm statements; Turbo C++ Inline assembly automatically converts them to appropriate assembly references to data language operands and appends underscores onto and functions identifier names. You can use any symbol, including automatic (local) variables, register variables, and function parameters.
In general, you can use a C symbol in any position where an address operand would be legal. Of course, you can use a register variable wherever a register would be a legal operand. If the assembler encounters an identifier while parsing the operands of an inline assembly instruction, it searches for the identifier in the C symbol table. The names of the 80x86 registers are excluded from this search. Either uppercase or lowercase forms of the register names can be used.
Inline Assembly and Register Variables
Inline assembly code can freely use SI or DI as scratch registers. If you use SI or DI in inline assembly code, the compiler won't use these registers for register variables.
When programming, you don't need to be concerned with the exact offsets of local variables. Simply using the name will include the correct offsets. However, it may be necessary to include appropriate WORD PTR, BYTE PTR, or other size overrides on assembly instruction. A DWORD PTR override is needed on LES or indirect far call instructions.
Examples of Inline Assembly Language
1. Example of window scrolling routines with keyboard IO.
#include <stdio.h> void screen( void ); void scroll( void ); void get_char( void ); char char_in; // variable defs unsigned char row, col, row_up, row_down, col_up, col_down; void main() { row = 0; col = 0; row_up = 3; // define a 10 line by 40 col row_down = 13; // window for scrolling col_up = 20; col_down = 60; screen(); // erase the screen scroll(); // scroll the screen get_char(); // get char to terminate... } void screen() { asm{ mov cx,50h /* 80 interations */ } disp_loop: asm{ mov ah,02h /* cursor position */ mov bh,00h /* BIOS call */ mov dh,row and dh,0Fh /* limit 32 lines */ mov row,dh /* restore row value */ mov dl,col int 10h inc row /* incr row,col ptrs */ inc col mov ah,02h /* DOS char output */ mov dl,'x' /* output the X char */ int 21h loop disp_loop /* loop until done */ } } // ............................................ void scroll() { asm{ mov ah,06h /* BIOS scroll function */ mov al,10h /* 16 lines */ mov bh,00h mov ch,row_up /* set window limits */ mov cl,col_up mov dh,row_down mov dl,col_down int 10h } } // ............................................ void get_char() { asm{ mov ah,02h /* cursor pos BIOS call */ mov bh,00h mov dh,06d mov dl,25d int 10h mov ah,02h /* DOS char output call */ mov dl,'?' int 21h mov ah,01h /* keyboard in, no echo */ int 21h } }2. Array Manipulation
#include <stdio.h> #include <graph.h> void main() { int array1[64], array2[64], i, *a1, *a2; char ch; for( i=1; i<64; i++ ) array1[i] = i; // fill array a1 = &array1[0]; // set up pointers to the a2 = &array2[0]; // first elements in each array { asm{ mov si,a1 /* get first pointer */ mov di,a2 /* get second pointer */ mov cx,64d /* set up count */ } here: asm{ mov ax,[si] /* get source value */ mov [di],ax /* into dest location */ add si,2 /* increment pointers */ add di,2 loop x } } for( i=0; i<20; i++ ) printf("\t%d \t%d \n", array1[i], array2[i] ); }
Developed and maintained by F. J. Looft, fjlooft@ee.wpi.edu.