How to use inline assembly language in Visual C++

For squeezing the most speed out of your code, the best optimizer is the one atop your shoulders.  Visual C++ does a reasonable job of optimizing C code, but because the C language is not well suited for manipulating bits and bytes, it often makes sense to write inner loop code in assembly language.  Microsoft has done a reasonable job with their support of inline assembly in VC++.  Shown below is a function which will draw a character which unfortunately needs its colors translated pixel by pixel.  I've left the equivalent C code in place to demonstrate what's going on in the assembly language.   I've found the following general rules apply when working with the inline assembler in MSVC++:

1) Errors are off by one line - When an error is found by the compiler, it erroneously reports it as being on the next line (VC++ 5.0 & 6.0 behave this way).

2) Use EBP carefully - Since EBP points to the local variable heap, you can modify it, but always restore it before the end of your code block and while it is not pointing to the local variables you will not be able to access any.

3) Using EBX,ESI,EDI - You are free to use these registers, but they are used by the compiler for register vars.  If your ASM code is in the middle of code which uses register vars, then these must be preserved, if not then don't worry about them.

4) Accessing structure variables - The compiler does not allow accessing structure variables indirectly through a pointer since it can't get to the pointer with an absolute address.  The solution is to put the value in a global or local var before entering your routine so it can be accessed within the ASM code.

Here's the code:

iAddr - The emulated video offset (0-3ff)
cColorPROM - A 256 color translation table to turn the character colors into palette colors
iPitch - Destination video buffer width in bytes
iChar - Character to draw
iColor - Color to draw the character
pCharData - Image data for characters, 8x8 bytes or 64 bytes per character

void EMUDrawChar(int iAddr, unsigned char *cColorPROM, int iPitch, int iChar, int iColor, unsigned char *pCharData)
{
int x, y;
unsigned char *s, *d; /* Source and destination pointers */

  x = iCharX[iAddr]; /* Translate video address into x,y coordinate */
  y = iCharY[iAddr];
  if (x < 0 || y < 0) /* Non-visible, don't draw it */
     return;
  d = &pBitmap[y * iPitch + x]; /* Calculate screen address from x,y */
  s = &pCharData[iChar * 64];
#ifdef PORTABLE
   for (y=0; y<8; y++)
      for (x=0; x<8; x++)
         d[y*iPitch+x] = cColorPROM[iColor + *s++];
#else /* Do it fast */
iPitch -= 8; /* Use to advance to next line in asm code */
   _asm {
        mov esi,s
        mov edi,d
        dec edi               /* Start back by 1 for better instruction interleave */
        mov edx,cColorPROM    /* color lookup table */
        add edx,iColor        /* Add fixed color offset */
        mov ecx,iPitch
        mov bl,8              /* y count */
        xor eax,eax
drwc0: mov bh,8               /* x count */
drwc1: mov al,[esi]
        inc edi
        inc esi
        mov al,[edx+eax]      /* translate the color */
        dec bh
        mov [edi],al
        jnz drwc1
        add edi,ecx           /* Skip to next line */
        dec bl
        jnz drwc0
        }
#endif /* PORTABLE */

} /* EMUDrawChar() */

Back