
                               ASSEMBLY
                             Codigos e Codificaes
                                Aula nr. 13 de 26

    Algumas pessoas,  depois  de  verem  o  cdigo-exemplo  do texto
anterior, desenvolvido  para  ser  compilado  em  TURBO  PASCAL,  me
perguntaram:  "E quanto ao C?!".  Well...  aqui vo algumas tcnicas
para codificao mixta em C...

    Antes de comearmos a dar uma olhada nas tcnicas, quero  avisar
que  meu  compilador preferido  o BORLAND C++ 3.1.  Ele tem algumas
caractersticas que no esto presentes  do  MicroSoft C++ 7.0 ou no
MS Visual C++!  Por  exemplo,  O  MSC++  ou  o MS-Visual C++ no tem
"pseudo"-registradores (que ajudam um  bocado  na mixagem de cdigo,
evitando os "avisos" do compilador).

    Mesmo com algumas diferenas, voc poder usar as tcnicas  aqui
descritas...  As regras podem ser usadas  para  qualquer  compilador
que no gere aplicaes em modo protegido para o MS-DOS.

  Regras para a boa codigificao assembly em C

    Assim como no TURBO PASCAL, devemos:

        * Nunca alterar CS, DS, SS, SP, BP e IP.
        * Podemos alterar com muito cuidado ES, SI e DI
        * Podemos alterar sempre que quisermos AX, BX, CX, DX

    O registrador DS  sempre  aponta  para  o  segmento  de dados do
programa...  Se a sua funo assembly acessa alguma varivel global,
e voc tiver alterado DS, a  varivel  que  voc  pretendia  acessar
no estar disponvel!  Os registradores SS, SP e BP so usados pela
linguagem  para  empilhar  e  desempilhar  os parametros e variveis
locais da funo na  pilha...   altera-los pode causar problemas!  O
par de registradores CS:IP nao deve ser  alterado  porque  indica  a
prxima  posio  da  memria  que contm uma instruo assembly que
ser executada...  Em qualquer  programa "normal" esses ltimos dois
registradores so deixados em paz.

    No caso dos registradores ES, SI e DI, o compilador  os  usa  na
manipulao  de  pointers  e  quando precisa manter uma varivel num
registrador  (quando  se  usa   a  palavra-reservada  "register"  na
declarao de uma varivel, por exemplo!).   Dentro  de  uma  funo
escrita puramente em assembly, SI e DI podem ser alterados a vontade
porque  o compilador trata de salva-las na pilha (via PUSH SI e PUSH
DI) e, ao trmino da funo, as  restaura  (via POP DI e POP SI).  A
melhor  forma  de  se  saber  se  podemos  ou  no  usar  um  desses
registradores em um cdigo mixto  compilando o programa  e  gerando
uma listagem assembly (no BORLAND C++ isso  feito usando-se a chave
-S  na  linha  de comando!)...  faa a anlize da funo e veja se o
uso  desses  registradores  vai  prejudicar  alguma  outra  parte do
cdigo!

    Se voc no quer ter essa dor de cabea,  simplesmente  salve-os
antes de usar e restaure-os depois que os usou!


  Modelamento de memria:

    O  mais  chato  dos  compiladores   C/C++  para  o  MS-DOS    o
modelamento de memria, coisa que no existe no TURBO PASCAL!   Digo
"chato"  porque esse recurso, QUE  MUITO UTIL, nos d algumas dores
de cabea de vez em quando...

    Os modelos COMPACT, LARGE e  HUGE usam, por default, pointers do
tipo  FAR  (segmento:offset).  Os modelos TINY, SMALL e MEDIUM usam,
por default, pointers do  tipo  NEAR  (apenas  offset, o segmento de
dados  assumido!).

    A "chatisse" est  em  criarmos  cdigos  que  compilem  bem  em
qualquer  modelo  de  memria.  Felizmente isso  possvel graas ao
pre-processador:

Ŀ
#if defined(__TYNY__) || defined(__SMALL__) || defined(__MEDIUM__)
/* Processamento de pointers NEAR */                              
#else                                                             
/* Processamento dos mesmos pointers... mas, FAR! */              
#endif                                                            


    Concorda comigo que   meio  chato  ficar enchendo a listagem de
diretivas do pr-processador?... C'est la vie!

  C + ASM

    Os compiladores da BORLAND possuem a  palavra  reservada  "asm".
Ela  diz  ao compilador que o que a segue deve ser interpretado como
uma instruo  assembly.   Os  compiladores  da  MicroSoft possuem o
"_asm" ou o "__asm".  A  BORLAND  ainda  tem  uma  diretiva  para  o
pr-processador  que  usada para indicar ao compilador que o cdigo
deve ser montado pelo TURBO ASSEMBLER ao invs do compilador C/C++:

Ŀ
 #pragma inline                                                   


    Voc pode usar isto ou ento  a  chave -B da linha de comando do
BCC...  funciona da mesma forma!  Voc  deve  estar  se  perguntando
porque  usar  o  TURBO  ASSEMBLER se o prprio compilador C/C++ pode
compilar o cdigo...   Ahhhhh,  por  motivos de COMPATIBILIDADE!  Se
voc pretende que o seu cdigo seja compilvel no TURBO C  2.0,  por
exemplo,  deve  incluir a diretiva acima!!  Alm do mais, o TASM faz
uma checagem mais detalhada do cdigo assembly do que o BCC...

    Eis um exemplo de uma funozinha escrita em assembly:

 Ŀ
   int f(int X)                                                  
   {                                                             
       asm mov     ax,X    /* AX = parametro X */                
       asm add     ax,ax   /* AX = 2 * AX */                     
       return _AX;         /* retorna AX */                      
   }                                                             
 

    Aqui segue mais uma regra:

     Se a sua funo pretende  devolver  um valor do tipo "char" ou
      "unsigned char", coloque o valor  no  registrador  AL  e  (nos
      compiladores da BORLAND) use "return _AL;"

      Se  a  sua funo pretende devolver um valor do tipo "int" ou
      "unsigned int", coloque o  valor  no  registrador AX e (tambm
      nos compiladores da BORLAND) use "return _AX;"

    A ltima linha da funo acima ("return _AX;") no  necessria,
mas  se  no  a colocarmos teremos um aviso do compilador, indicando
que "a funo precisa retornar  um  'int'".  Se voc omitir a ltima
linha   (  o  caso  dos  compiladores  da  MicroSoft  que  no  tem
pseudo-registradores) e no ligar  pros  avisos, a coisa funciona do
mesmo jeito.

    Agora voc deve estar querendo  saber  como  devolver  os  tipos
"long", "double", "float", etc...  O tipo "long" (bem como "unsigned
long")  simples:

     Se a sua funo pretende  devolver  um valor do tipo "long" ou
      "unsigned  long", coloque os 16 bits mais significativos em DX
      e os 16 menos significativos em AX.

    No existe uma forma de devolvermos  DX  e  AX  ao  mesmo  tempo
usando  os pseudo-registradores da Borland, ento prepare-se para um
"aviso" do compilador...

    Os demais tipos no so  inteiros...   so  de  ponto-flutuante,
portanto, deixe que o compilador tome conta deles.


  Trabalhando com pointers e vetores:

    D uma olhada na listagem abaixo:

Ŀ
   unsigned ArraySize(char *str)                                   
   {                                                               
#if defined(__TYNY__) || defined(__SMALL__) || defined(__MEDIUM__) 
       asm mov     si,str  /* STR = OFFSET apenas */               
#else                                                              
       asm push    ds                                              
       asm lds     si,str  /* STR = SEGMENTO:OFFSET */             
#endif                                                             
                                                                   
       asm mov     cx,-1                                           
ContinuaProcurando:                                                
       asm inc     cx                                              
       asm lodsb                                                   
       asm or      al,al                                           
       asm jnz     ContinuaProcurando                              
       asm mov     ax,cx                                           
                                                                   
#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__)
       asm pop     ds          /* Restaura DS */                   
#endif                                                             
                                                                   
       return _AX;                                                 
   }                                                               


    A rotina acima  equivalente a funo strlen() de <string.h>.

    Como disse antes, nos modelos COMPACT, LARGE e HUGE  um  pointer
tem o formato SEGMENTO:OFFSET  que    armazenado  na memria em uma
grande varivel de 32 bits (os 16 mais significativos so o SEGMENTO
e os 16 menos significativos so o OFFSET).  Nos modelos TINY, SMALL
e MEDIUM apenas o OFFSET  fornecido no pointer  (ele  tem  16  bits
neste  caso),  o SEGMENTO  o assumido em DS (no devemos alter-lo,
neste caso!).

    Se voc compilar  essa  listagem  nos  modelos COMPACT, LARGE ou
HUGE o cdigo coloca em DS:SI o pointer (lembre-se: pointer  s  um
outro  nome  para "endereo de memria!").  Seno, precisamos apenas
colocar em SI o OFFSET (DS j est certo!).

    Ao sair da funo, DS deve  ser  o  mesmo de antes da funo ser
chamada...  Portanto, nos modelos "LARGOS" (hehe) precisamos  salvar
DS ANTES de us-lo e restaura-lo  DEPOIS de usado!  O compilador no
faz isso automaticamente!

    No se preocupe com SI  (neste caso!)...  este sim, o compilador
salva sozinho...

    Um macete com o  uso  de  vetores  pode ser mostrado no seguinte
cdigo exemplo:

 Ŀ
   char a[3];                                                     
   int b[3], c[3];                                                
   long d[3];                                                     
                                                                  
   void init(void)                                                
   {                                                              
       int i;                                                     
                                                                  
       for (i = 0; i < 3; i++)                                    
           a[i] = b[i] = c[i] = d[i] = 0;                         
   }                                                              
 

    O compilador gera a seguinte funo equivalente em assembly:

 Ŀ
   void init(void)                                                
   {                                                              
       asm xor     si,si           /* SI = i */                   
       asm jmp     short @1@98                                    
   @1@50:                                                         
       asm mov     bx,si           /* BX = i */                   
       asm shl     bx,1                                           
       asm shl     bx,1            /* BX = BX * 4 */              
       asm xor     ax,ax                                          
                                                           
