A mi me gusta mucho tanto C como Assembler, y reconozco que uso C más cuando programo algoritmos o programas para CPUs, o cuando para una MCU que no conozco en profundidad (porque básicamente si tienes muchos ejemplos en C que hacen algo parecido a lo que quieres, es más rápido modificar un ejemplo para que haga lo que quieres, que hacer entero el código).
Pero si quiero que un programa funcione EXACTAMENTE como espero de él, prefiero el ASM. Quizá es más jaleo a la hora de programar, pero después tengo menos dolores de cabeza a la hora de arreglar cosas, y es porque en C se produce un efecto que no se produce en ASM y es el "Lost in translation". Por supuesto, si conoces todas las idiosincrasias del compilador en C, puedes sobrellevar el asunto bastante bien pero de vez en cuando aparece algún imprevisto, que no aparecería si se escribiera directamente en ASM.
Supongamos una instrucción sencilla para codigo en PIC:
Código:
if (VAR.5==TRUE) PORTB.3=0; else PORTB.3=1;
Una forma de traducir esto por algún compilador para optimizar el tiempo de ejecución sería:
Código:
BSF PORTB,3
BTFSC VAR,5
BCF PORTB,3
Este código es muy rápido, pero es un desastre, porque asume primero el "else" y luego cambia la condición si se cumple el "if". Esto implica que aunque sea por unos nanosegundos, PORTB.3 va a presentar un pulso de nivel alto. Un compilador podría usar esta optimización cuando se trate de variables internas, que no manejen ningún tipo de hardware, ya que por ejemplo si en lugar de PORTB.3 se tratara de ADCON0bits.GO, el ADC siempre se accionaría ya que basta sólo una escritura de ese bit a 1 para que el ADC comience. Incluso con variables internas se podría dar el caso de que una interrupción saltase entre la primera y segunda instrucción, causando un estropicio.
Lo habitual sería encontrar esta forma en la mayoría de compiladores:
Código:
BTFSC VAR,5
BCF PORTB,3
BTFSS VAR,5
BSF PORTB,3
Aquí el bit 3 de portB nunca asume un valor, de forma que cambiaría de estado acorde con la condición del "if" y no antes del "if".
Pero no es la más adecuada en algunos casos. Esta es la forma que traduce la ejecución tal cual la leemos en el código en C, pero una ejecución de una sentencia "if" es simultanea, es decir, no se ejecuta primero la condición "if" y luego la condición "else", sino que o se ejecuta el "if", o en el mismo tiempo que se ejecutaría el "if" se ejecuta el "else". Esto en un bus de comunicaciones puede suponer una diferencia de fases en los pulsos que haga perder la comunicación, o introducir un error de magnitud en una modulación PWM, etc.
La forma más lenta, que según que casos sería la más correcta, sería esta:
Código:
MOVF PORTB,W
BTFSC VAR,5
ANDLW 0xF7
BTFSS VAR,5
IORLW 0x08
MOVWF PORTB
PORTB.3, si cambia, lo hace en el mismo instante sin depender de la condición, respetando el tiempo de ejecución.
Curiosamente la optimización primera ahora se podría hacer, ya que W es un registro interno que no se ve afectado por el hardware, ni debería verse afectado por las interrupciones. Así este código sería viable:
Código:
MOVF PORTB,W
IORLW 0x08
BTFSC VAR,5
ANDLW 0xF7
MOVWF PORTB
En resumen, una simple sentencia de C, puede traducirse por el compilador de varias maneras, y dependiendo del caso unas pueden ser más apropiadas que otras (para encender o apagar un LED, no tiene mayor importancia si se usa la primera de todas).