Referència | Trucs | Perifèrics | Recursos CITCEA | |
Tutorial | Exemples | Projectes | Inici |
A vegades pot ser interessant fer parts d'un programa en assemblador. En llenguatge assemblador podem generar codi més compacte i que s'executa de manera més ràpida. Abans de fer servir instruccions en assemblador dins el programa, caldrà que coneguem com es programa en llenguatge assemblador.
Una de les qüestions a destacar quan posem trossos en assemblador dins d'un programa en C és que no coneixem l'adreça exacta de les variables. Però tenim una manera de fer-les servir. La següent taula ens mostra un parell d'exemples de variables:
Tipus | Nom en C | Bytes | Nom en ASM | Comentari |
Char | Var1 | 1 | _Var1 | |
Unsigned short | Var2 | 2 | _Var2 | Byte menys significatiu |
_Var2+1 | Byte més significatiu | |||
Char | Var3[2] | 2 · 1 | _Var3 | Var3[0] |
_Var3+1 | Var3[1] |
Atès que no sabem on s'ha guardat la variable, cal que ens assegurem que estem en el banc correcte. Com que no sabem quin és, fem servir la instrucció banksel. Només podem accedir a variables globals, les locals no són accessibles.
Cada instrucció en assemblador les posarem, com a cadena de caràcters (és a dir entre cometes), com a paràmetre de la funció asm. En instruccions en les que hi posem números, aquests s'interpretaran en decimal:
asm("movlw 80"); // Valor 80 decimal asm("movwf _Var1");
Quan el programa té més variables que els que caben al banc 0, n'hi haurà que estaran al banc 1 o, potser, al 2. En aquests casos podem obtenir un error perquè l'adreça de la variable serà de vuit (banc 1) o nou (banc 2) bits i, en canvi, l'espai que té la instrucció per guardar l'adreça és de set bits. Atès que els bits vuitè i novè ja estan implícits en el banc, el problema no és a l'hora de trobar la variable sinó només a l'hora de guardar l'adreça a la instrucció. Per evitar l'error, tenim la possibilitat de forçar al compilador a posar al banc 0 les variables a les que volem accedir des de l'assemblador, com en aquest cas:
char Var0; // Variable que no fem servir des de l'assemblador __bank(0) char Var1; // Variable que forcem perquè estigui al banc 0
Però si el compilador creu que és millor que aquella variable estigui al banc 1 (o al 2) serà per algun motiu. Una alternativa més flexible és fer un emmascarament per tal que les adreces que posem en les instruccions d'assemblador siguin sempre de set bits, com en l'exemple següent:
asm("movlw 80"); // Valor 80 decimal asm("movwf (_Var1&7fh)"); // Equival a 0b01111111
Fent això amb totes les variables (no ho cal fer amb les adreces dels registres) no tindrem errors per culpa d'adreces de vuit o nou bits. Per tant, podem escriure la taula anterior d'aquesta forma:
Tipus | Nom en C | Bytes | Nom en ASM | Comentari |
Char | Var1 | 1 | (_Var1&7fh) | |
Unsigned short | Var2 | 2 | (_Var2&7fh) | Byte menys significatiu |
((_Var2+1)&7fh) | Byte més significatiu | |||
Char | Var3[2] | 2 · 1 | (_Var3&7fh) | Var3[0] |
((_Var3+1)&7fh) | Var3[1] |
Tenim dues maneres de posar les etiquetes per a les instruccions goto.
asm("Bucle:"); // Etiqueta sola asm("movf ADRESH,w"); ... asm("goto Bucle");
asm("Bucle: movf ADRESH,w"); // Etiqueta abans de la instrucció ... asm("goto Bucle");
En programes llargs, pot passar que el nostre tros de programa en assemblador caigui a la segona pàgina de la memòria de programa. Si és així, el compilador ens pot donar un error si intenta posar una adreça de la segona pàgina com a paràmetre de les instruccions goto o call. Per això, és convenient emmascarar l'adreça de l'etiqueta.
Si la instrucció on cal emmascarar forma part de la mateixa funció, és quasi segur que les dues instruccions estaran a la mateixa pàgina i, per tant, només cal emmascarar:
asm("Bucle:"); asm("movf ADRESH,w"); ... asm("goto (Bucle&7ffh)");
Si, en canvi, les instruccions estan en funcions diferents es farà imprescindible seleccionar el banc abans de fer el salt:
asm("pagesel Func"); asm("call (Func&7ffh)");
A continuació hem posat, a tall d'exemple, el programa de l'exemple EA però amb un petit tros en assemblador.
#pragma config FOSC = INTRCIO, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = OFF #pragma config CPD = OFF, BOREN = OFF, IESO = OFF, FCMEN = OFF #include <xc.h> // Carrega el fitxer de funcions necessari per al compilador XC8
char Valor; // Valor llegit
void main (void){ TRISC = 0b00000000; // Posa el port C com a sortida TRISAbits.TRISA0 = 1; // La pota RA0 és d'entrada ANSEL = 0; // Desactiva totes les entrades analògiques ANSELH = 0; ANSELbits.ANS0 = 1; // I ara activa la del potenciòmetre (AN0) ADCON1 = 0b00010000; // Posa el conversor a 1/8 de la freqüència ADCON0 = 0b00000001; // Activa el conversor connectat a AN0 // amb el resultat justificat per l'esquerra while (1){ // Bucle infinit ADCON0bits.GO = 1; // Posa en marxa el conversor while (ADCON0bits.GO == 1) // Mentre no acabi ; // ens esperem asm("banksel ADRESH"); // Banc on està ADRESH asm("movf ADRESH,w"); // Agafem la lectura del conversor asm("banksel _Valor"); // Banc on està la variable asm("movwf (_Valor&7fh)"); // Ho guarda a la variable PORTC = Valor/16; // Copiem el resultat, dividit per 16, als LED _delay(200000); // Retard per permetre la visualització } }
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.