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.

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:

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 	jz

Assembly Directives

The following assembly directives are allowed in Turbo C++ inline assembly statements:

	db 	dd 	dw 	extrn

C 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.