EL COMPILADOR DE C

5.1 Introducción 

Prosiguiendo con nuestro estudio sobre las herramientas de programación para la gama media  hemos buscado distintas herramientas gratuitas que ofrezcan soporte para el lenguaje C. Microchip se limita a ofrecer tan solo los MPLAB-C17 y MPLAB-C18, limitadas a la gama alta (PIC17CXX y PIC18CXX, respectivamente). 

El entorno MPLAB, es del tipo container, y carece de lenguajes en su código, dejando la tarea de compilación para otros programas, requiere que estos creen, a su fin, determinados ficheros para atender sus propias necesidades, como la de mostrar errores del programa.

5.2 El primer programa en C  

Veamos un sencillo programa:    

char aa, bb, cc, dd;        // Creamos cuatro variables de tipo char (caracteres, tamaño 1 byte, lo usual) que al estar declaradas

                                      // fuera del main() son globales

main()                           // Proceso principal

char kk                       // Nuevo char, utilizable solo en main, variable local

 

   aa = 3;                       // Damos un par de valores, y calculamos una multiplicación

   bb = 5;

   cc = aa*bb;

 

   kk = cc+1;

   dd = myfunc( kk );     // Llamada a la función myfunc, con el parámetro kk.

                                    // Su valor de vuelta es asignado a la variable dd

 }

 

 

myfunc(char  t )

{

char c

 

    c = t+4                       // Calculamos la función deseada

    return( c );                // Devolvemos el valor c.

 

}

 

 

 No es nuestro deseo tampoco hacer un estudio profundo del lenguaje c, del que es abundante la bibliografía y con el que pensamos que se hallará familiarizado cualquier lector que se aventure en estas páginas, pero sí hablaremos de sus ventajas, sus desventajas y hasta dónde llega su desarrollo como lenguaje (qué encontraremos en sus librerías). 

Por lo pronto, se tiene  aquí la comparación directa con el resultado de la compilación. Es fácil apreciar lo simple, escueto, legible y fácil de modificar que resulta el código en c. 

;Small C PIC16C84;

; Coder (1.0 2/10/95)

; Version 0.002

 

; Front End (PIC Ver 1.0 2/19/95)

 

  include '16c84.h'

 

; **************code segment cseg*******************

 

  org _CSEG

 

 

; Begin Function

  

 

main_

  sub _stackptr, #1

  mov _primary, #3

  mov aa_          , _primary

  mov _primary, #5

  mov bb_           , _primary

  mov _primary, aa_

  call _push_

  mov _primary, bb_

  call _pop_

  call _mul_

  mov cc_           , _primary

  mov _primary, #0

  add _primary, _stackptr

  call _push_

  mov _primary, cc_

  call _push_

  mov _primary, #1

  call _pop_

  add _primary, _secondary

  call _pop_

  call _putstk_

  mov _primary, #0

  add _primary, _stackptr

  call _indr_

  call _push_

  ;;(# args passed) mov W, #1

  call myfunc_

  add _stackptr, #1

  mov dd_           , _primary

_1_

  add _stackptr, #1

  ret

 

 

; Begin Function

 

 

 

myfunc_

  sub _stackptr, #1

  mov _primary, #0

  add _primary, _stackptr

  call _push_

  mov _primary, #2

  add _primary, _stackptr

  call _indr_

  call _push_

  mov _primary, #4

  call _pop_

  add _primary, _secondary

  call _pop_

  call _putstk_

  mov _primary, #0

  add _primary, _stackptr

  call _indr_

  jmp _2_

_2_

  add _stackptr, #1

  ret

 

;*******need mult/div standard library******** 

  include 'mlibpic.h' 

; **************data segment dseg*******************

 

  org _DSEG

 

aa_        ds        1

bb_         ds        1

cc_         ds        1

dd_         ds        1

 

;0 error(s) in compilation

; literal pool:0

; global pool:112

; Macro pool:51

  end

 

 Pocos comentarios quedan al respecto. De hecho es prácticamente indescifrable el resultado. Sin embargo, como siempre, una buena programación directa en ensamblador reducirá código, y será de ejecución más rápida. Muchas veces, incluso, resultará imprescindible para determinados módulos. Sin embargo, también necesitará un mayor tiempo de estudio, desarrollo e implementación que en el caso del c. 

5.3 ¿ Qué podemos usar del c convencional? 

Pues, por ejemplo, como apreciaremos del ejemplo, los punteros

char val;

 

main()

char aa,bb,cc,dd;

 

   load( &aa, &bb, &cc, &dd);   // El carácter & referencia a la dirección de la variable

   val = aa+bb+cc+dd;

  

}

  

load(char *a, char *b, char *c, char *d )

 {                                              // Y aquí a, b, c y d son direcciones de memoria, con lo que,

     *a = 1;                                // para aludir a sus valores, las precedemos de *.

     *b = 2;

     *c = 3;

     *d = 4;

}

 

 Otro ejemplo que muestra las ventajas de un bucle while, o de los operadores lógicos, como igual, distinto, >=, etc. 

#define TRUE 1

#define FALSE 0

 

char ad, d1, d2, d3;

 

main()

 char cnt;

 

   while(TRUE) {  

     ad = GetAD();

     cnt = 0;

     while (ad>=100) {

       ad =a d-100;

       cnt++;

     }                                      // while

     d1 = cnt;

     cnt = 0;

     while (ad>=10) {

       ad = ad-10;

       cnt++;

     }                                      // while

     d2 = cnt;

     cnt = 0;

     while (ad>0) {

       ad = ad-1;

       cnt++;

     }                                      // while

     d3 = cnt;

   }                                       // while principal

}

 

 

char GetAD(void)

{

char v;

 

   v = 0xff;   

   return(v);

 

}

 

Ejemplo del uso de switch, para escoger entre distintos valores de una variable.

char z;

 

main()

{ 

char i;

 

   for (i=0; i<5; i++) {

     switch(i) {

     case 0:

             z = f1();

             break;

     case 1:

             z = f2();

             break;

     case 2:

             z = f3();

             break;

     case 3:

             z = f4();

             break;

     case 4:

            z = f5();

             break;

     }

   }

}

 

f1()

 {

 return(1);

 }

f2()

 {

 return(2);

}

f3()

{

return(3);

}

f4()

{

return(4);

}

f5()

{

return(5);

}

 

Ejemplo de la  sentencia condicional if

char z;

 

main()

char i;

 

   i = 0;

   while(i<5) {

     if (i==0)

       z = f1();

     if (i==1)

       z = f2();

     if (i==2)

       z = f3();

     if (i==3)

       z = f4();

     if (i==4)

       z = f5();

     i++;

   }

}

 

f1()

 {

 return(1);

 }

f2()

 {

 return(2);

}

f3()

{

return(3);

}

f4()

{

return(4);

}

f5()

{

return(5);

}

 

 Ejemplo  del bucle for, y de las cadenas de caracteres (la sentencia GetChar será explicada en el siguiente apartado): 

/* ejemplo de inclusión en el compilador */

 

char a, b, c, d;

 

main() {

 

    a = "hello";

    b = "goodbye";

    for (d=0; d<7; d++)

      c = GetChar(b,d);      /* Indice a travéz del string */

    for (d=0; d<5; d++)    /* ditto */

      c = GetChar(a,d);

}

 

#include "getchar.c"      // inclusión explicita del codigo correspondiente a getchar()

 

 En este caso, hemos vuelto a comentarlo para la más sencilla comprensión: 

/* test i/o port  */

 

#include "io.c"

 

char a, b;

 

main()

{

 

   SetP_A(0x03);                      // Se establece como puerto A la dirección 0x03

   SetP_B(0x0f);                       // Se establece como puerto B la dirección 0x0F

   a = RdPortA();                      // El valor del puerto A se guarda en la variable a

   b = RdPortB();                     // El valor del puerto B se guarda en la variable b

   WrPortA(0x1);                      // Escribimos en el puerto A el valor 0x01

   WrPortB(0x55);                    // Escribimos en el puerto B el valor 0x55

 

}

 

 En realidad es posible asignar como puerto A o B cualquier dirección física de memoria de datos, aunque esta no sea propiamente un puerto. Esto puede ser útil para manejar directamente registros como STATUS o INTCON. 

Pero dejamos para el último apartado lo no descrito en los ejemplos, es decir, aquellas funciones incluidas en las librerías. 

5.4 Librerías y funciones 

5.4.1 La librería GETCHAR 

Esta librería sólo contiene la función GetChar (cadena, índice), que, como pudimos ver en el ejemplo, toma de una cadena de caracteres (definida, por ejemplo, como a = ”cadena”) el carácter situado en la posición índice. Por ejemplo, b = GetChar (a,2) nos daría b = “d”, ya que se empieza a contar desde 0. 

5.4.2 La librería IO 

Esta librería se encarga de la gestión de puertos, y contiene varias funciones: 

SetP_A(valor) asigna como dirección del puerto A valor. 

SetP_B(valor) asigna como dirección del puerto B valor. 

RdPortA() y RdPortB() devuelven el valor leído en ambos puertos (siempre habrá que definir sus direcciones antes, o tendremos resultados imprevistos). 

WrPortA(valor) y WrPortB(valor), respectivamente, enviarán valor a las salidas de los puertos A y B. Como en el caso de las anteriores, deben estar previamente definidas sus direcciones para no sufrir imprevistos. 

5.4.3 Librería EE_READ 

Contiene la función ee_read (addr), que leerá un valor de la memoria EEPROM interna del microprocesador, sito en la dirección addr. 

5.4.4 Librería EE_WRITE 

Contiene la función ee_write (addr), que escribirá un valor de la memoria EEPROM interna del microprocesador, sito en la dirección addr. 

Estas dos últimas funciones no han sido detalladas en el apartado de ensamblador por no aparecer en la bibliografía asociada. Consultando el Databook en el apartado correspondiente al PIC16C84 se nos mostrará la manera apropiada de hacerlo, a través de los registros EECON1 (dirección 0x08 en la página 0), EECON2 (dirección 0x09 en la página 0), EEDATA (dirección 0x08 en la página 1, 0x88 como dirección absoluta) y EEADR (dirección 0x09 en la página 1, 0x89 como dirección absoluta). 

 

5.4.5 También conviene saber 

Este C asume la directiva #asm, a continuación de la cuál seguirá un trozo de código en ensamblador. Para regresar a la programación en C deberemos incluir, tras la secuencia de código,  #endasm. Puede ver ejemplos editando cualquiera de las librerías, sitas en el directorio Pic_lib. Podrá, a su vez, hacer referencia a los argumentos de la rutina en la que esté el código mediante #0, #1 ... #n, siendo el número la posición del argumento en la llamada a la rutina, comenzando por 0. 

Las definiciones de constantes en ensamblador están en Pic_rt\16c84. _portA y _portB, por ejemplo, referencian a esos puertos, así como eedata, eeaddr, eecon1 y eecon2. También contiene macros como _push_, _pop_, _swap_, _swaps_, o _indr_, que son usados internamente por el compilador. Puesto que manejan los cuatro registros reservados para el C (estos son _primary, _secondary, _temp y _stackptr) no conviene que los use si no los comprende muy bien, ya que podría alterar el buen funcionamiento de su programa. Las direcciones de esto cuatro registros son 0x2f, 0x2e, 0x2c y 0x2b. Procure no usarlos en su código en ensamblador. 

Hosting by WebRing.
Navigation by WebRing.