UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Interfaz C-Assembler. Código de arranque: Se compila el siguiente programa: void main(void) { ; } La memoria flash de 60 KB se inicia en 0x01100. Los vectores de interrupción, están en la zona alta de la memoria flash, desde 0xFFE0 hasta 0xFFFF. Cuando se le da energía al controlador, o se genera una señal de reset externo, se coloca en PC la dirección almacenada en el vector asociado a RESET (0x0FFFE). La RAM, de 2 KB, ocupa desde: 0x0200 hasta 0x09FF. El stack se inicia en el tope de la RAM+2; es decir en 0x0A00. De este modo al efectuar un llamado a subrutina, se decrementa en 2 el valor almacenado en el stack, y se deposita la dirección de PC de retorno en el tope del stack. En el caso de rutinas de servicio de interrupción además de empujar la dirección de retorno, se almacena el valor del registro de estado del controlador (SR). El código de autoarranque del compilador IAR: setea el stack, el vector de interrupción para reset, y define el la rutina exit, cuya finalidad es detener la ejecución de instrucciones después de ejecutada la rutina principal. En este caso simple, el programa principal es traducido a la instrucción de máquina ret. #include "msp430x14x.h" ;-----------------------------------------------------------------------------ORG 01100h ; Programa en Flash ;-----------------------------------------------------------------------------RESET: mov.w #0A00h, SP ; Inicia stackpointer call #main call #exit _exit: br #__exit __exit jmp #__exit main: ret exit: br Prof. Leopoldo Silva Bijit. #_exit ; 23-02-2004 1 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 ;-----------------------------------------------------------------------------; Vector de interrupción asociado a Reset ;-----------------------------------------------------------------------------ORG 0FFFEh ; MSP430 RESET Vector DW RESET ; END Variables locales en registros. El siguiente programa define una variable local dentro de la función main: void main(void) { int i1=2; i1+=1; } Se dispone de 12 registros, comienzan a usarse desde el número 15. Se ilustra ahora sólo el código assembler de main: main: MOV.W ADD.W RET #0x2, R15 #0x1, R15 ; i1=R15 = 2 ; i1+=1; El símbolo # indica direccionamiento inmediato Uso de registros para variables locales. Si se definen dos variables, se ocupan el R15 para i1, y el registro R14 para i2. void main(void) { int i1=2, i2=-1; i1+=1; i2-=1; } Se traduce a: main: MOV.W MOV.W ADD.W ADD.W RET Prof. Leopoldo Silva Bijit. #0x2, R15 #0xffff, R14 #0x1, R15 #0xffff, R14 ; i1 = R15 = 2 ; i2 = R14 = -1; ; i1+=1 ; i2-=1 23-02-2004 2 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Empleo de registros temporales. Si se siguen empleando registros vemos que el R10 y el R11 son preservados. Y que emplea el R11 como temporal. Como se verá el compilador salva el contenido de los registros R4 a R11. Los registros R12 a R15 no son salvados. void main(void) { int i1=-1; int i2=0; int i3=1; int i4 = 5+1; int i5=1<<5; i1+=1; i2-=1; i3=i1+i2+i4; i4=i3 | i2; i5=i5+2*3*4; } Para evitar warnings por definir variables que no se empleen, las variables se leen y escriben. main: PUSH.W PUSH.W MOV.W MOV.W MOV.W MOV.W MOV.W ADD.W ADD.W MOV.W ADD.W ADD.W MOV.W MOV.W BIS.W MOV.W ADD.W POP.W POP.W RET Prof. Leopoldo Silva Bijit. R10 R11 #0xffff, R15 #0x0, R14 #0x1, R12 #0x6, R13 #0x20, R10 #0x1, R15 #0xffff, R14 R15, R11 R14, R11 R13, R11 R11, R12 R12, R11 R14, R11 R11, R13 #0x18, R10 R11 R10 ;se salvan registros ; i1 = R15 = -1 ; i2 = R14 = 0; ; i3 = R12 = 1; ; i4 = R13 = 5+1 = 6 ; i5 = R10 = 1<<5 = 32 = 0x20 ; i1+=1 ; i2-=1 ; R11 se emplea como temporal. ; i3=i1+i2+i4 ; i4=i3 | i2 ; i5=i5+2*3*4; 23-02-2004 3 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Variables locales en el frame. Cuando el número de locales es mayor que los registros disponibles, se almacenan en el frame de la función. Se emplean registros temporales para la compilación de expresiones que requieren almacenamientos intermedios. void main(void) { int i1 = 1, i2 = 2, i3 = 3, i4 = 4; int i5 = 5, i6 = 6, i7 = 7, i8 = 8; int i9 = 9, i10 = 10, i11 = 11, i12 = 12; i1= i2+i3; i2= i1-i2; i3= i1 & i4; i4= i3 | i2; i5= i3 ^i4; } main: PUSH.W R10 ;salva registros tipo save PUSH.W R11 PUSH.W R8 PUSH.W R9 PUSH.W R6 PUSH.W R7 PUSH.W R4 PUSH.W R5 SUB.W #0x2, SP ; crea frame MOV.W #0x1, R4 ; i1 = R4 = 1; MOV.W #0x2, R5 ; i2 = R5 = 2; MOV.W #0x3, R14 ; i3 = R14 = 3; MOV.W #0x4, R12 ; i4 = R12 = 4; MOV.W #0x5, R13 ; i5 = R13 = 5; MOV.W #0x6, 0x0(SP) ; tope = 6; Direccionamiento indexado. MOV.W #0x7, R10 ; i7 = R10 = 7; MOV.W #0x8, R11 ; i8 = R11 = 8; MOV.W #0x9, R8 ; i9 = R8 = 9; MOV.W #0xa, R9 ; i10 = R9 = 10; MOV.W #0xb, R6 ; i11 = R6 = 11; MOV.W #0xc, R7 ; i12 = R7 = 12; MOV.W R5, R15 ; emplea R15 como temporal ADD.W R14, R15 MOV.W R15, R4 ; i1= i2+i3; MOV.W R4, R15 SUB.W R5, R15 MOV.W R15, R5 ; i2= i1-i2; MOV.W R4, R15 Prof. Leopoldo Silva Bijit. 23-02-2004 4 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 ?Epilogue8: ?Epilogue7: ?Epilogue6: ?Epilogue5: ?Epilogue4: ?Epilogue3: AND.W MOV.W MOV.W BIS.W MOV.W MOV.W XOR.W MOV.W ADD.W BR pop.w pop.w pop.w pop.w pop.w pop.w pop.w pop.w ret R12, R15 R15, R14 R14, R15 R5, R15 R15, R12 R14, R15 R12, R15 R15, R13 #0x2, SP #?Epilogue8 R5 R4 R7 R6 R9 R8 R11 R10 ; i3= i1 & i4; ; i4= i3 | i2; ; i5= i3 ^ i4; ; desarma el frame ; rótulos automáticos ; restaura registros que deben preservarse ; por ser modificados dentro de la función. Variables globales inicializadas. Consideremos las siguientes definiciones de variables globales inicializadas. int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int i6 = 6; int i7 = 7; int i8 = 8; int i9 = 9; int i10 = 10; int i11 = 11; int i12 = 12; void main(void) { i1= i2+i3; i2= i1-i2; i3= i1 & i4; i4= i3 | i2; i5= i3 ^i4; } Prof. Leopoldo Silva Bijit. 23-02-2004 5 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Las variables globales (definidas fuera de las funciones) son almacenadas en RAM, a partir de la dirección 0x200. Los inicializadores se almacenan en la memoria flash, a continuación de la última instrucción del programa. Antes de invocar a main, además de iniciar el stack pointer, se inicializan las variables estáticas en RAM, mediante la función memcpy que es automáticamente insertada por el código de arranque. #include "msp430x14x.h" ;-----------------------------------------------------------------------------ORG 01100h ; Programa en Flash ;-----------------------------------------------------------------------------RESET: mov.w #0A00h, SP ; Inicia stackpointer mov.w #0x200, R12 ; inicio RAM mov.w #Inicializadores, R14 ; valores iniciales de globales push.w #0xA ; se pasa en el tope el número call #memcpy incd.w SP ;pop _exit: __exit call call br jmp #main #exit #__exit #__exit El código en C, para la función estándar memcpy es el siguiente: void *memcpy(void * destino, const void * fuente, register int n) { register char *d = (char *)destino; register const char *s= (char *)fuente; while(n--) *d++ = *s++; return destino; } memcpy Copia un bloque de n bytes desde la dirección fuente hacia la dirección destino. La siguiente implementación assembler de memcpy, copia n bytes desde la dirección fuente (que se pasa en R14, la cual apunta a la zona de los valores iniciales de las variables globales que se escriben en la memoria flash, inmediatamente después de la última instrucción del programa) hacia la dirección destino (que se pasa en R12, la cual apunta al inicio del segmento de memoria RAM). Entonces memcpy queda: memcpy: cpy: push.w mov.w mov.w jmp mov.w Prof. Leopoldo Silva Bijit. R10 0x4(SP), R13 ; R13 =número de bytes R12, R10 testn R10, R15 23-02-2004 6 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 inc.w mov.b testn: mov.w add.w tst.w jne pop.w ret Inicializadores: DW DW DW DW DW R10 ;d++ @R14+,0x0(R15) ;de flash a ram. s++ R13, R15 #0xFFFF, R13 ; n-R15 cpy R10 ;última instrucción del programa. 0100 0200 0300 0400 0500 ;valor inicial de i1 ;inicializador de i2 El direccionamiento @R14 es indirecto. Usa R14 como puntero. Nótese que no se crea el espacio para variables globales que no se emplean en el programa, es el caso de las variables i6 a i12, en el ejemplo que se está desarrollando. ;-----------------------------------------------------------------------------; Segmento RAM, después de memcpy ;-----------------------------------------------------------------------------ORG 00200h ; MSP430 Inicio RAM i1: DW 00001h ; 200 i2: DW 00002h ; 202 i3: DW 00003h ; 204 i4: DW 00004h ; 206 i5: DW 00005h ; 208 END El código de main ilustra el direccionamiento empleando variables en la zona de RAM. También se muestra operadores de suma y resta, y operadores al bit. main: mov.w add.w mov.w &i2, R15 &i3, R15 R15, &i1 ; i1= i2+i3; mov.w sub.w mov.w &i1,R15 &i2,R15 R15,&i2 ; i2= i1-i2; mov.w and.w mov.w &i1, R15 &i4, R15 R15, &i3 ; i3= i1 & i4 Prof. Leopoldo Silva Bijit. ;El & es direccionamiento absoluto. 23-02-2004 7 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 mov.w bis.w mov.w &i3,R15 &i2,R15 R15,&i4 ; i4= i3 | i2; mov.w xor.w mov.w ret &i3,R15 &i4,R15 R15,&i5 ; i5= i3 ^i4; Variables globales no inicializadas. Consideremos las siguientes definiciones de variables globales no inicializadas. Se ilustra además el tratamiento de arreglos. int i1; int arr[4]; int main(void) { i1=-1; arr[0]=0; arr[1]=1; arr[2]=2; arr[3]=3; arr[0]=arr[1]+i1; return(0) ; } Se crean las globales en RAM y se las inicia automáticamente con valores nulos, antes de invocar a main. Lo anterior se logra invocando a la rutina memset, que inserta el código de arranque. #include "msp430x14x.h" ;-----------------------------------------------------------------------------ORG 01100h ; Programa en Flash ;-----------------------------------------------------------------------------RESET: mov.w #0A00h, SP ; Inicia stackpointer mov.w #0x200, R12 ; inicio RAM clr.w R14 ; valor a setear en RAM push.w #0xA ; se pasa en el tope el número de bytes a setear call #memset incd.w SP ;pop call Prof. Leopoldo Silva Bijit. #main 23-02-2004 8 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 _exit: __exit call br jmp #exit #__exit #__exit Se puede describir en C, el código de memset: void memset(void * destino, const int valor, register int n) { register char *d = (char *)destino; while(n--) *d++ = valor; } El código de memset que genera IAR: memset: push.w R10 push.w R11 mov.w 0x6(SP), R13 mov.w R12, R10 jmp testn setram: mov.b R14,R15 mov.w R10,R11 inc.w R10 mov.b R15,0x0(R11) testn: mov.w R13, R15 add.w #0xFFFF,R13 tst.w R15 jne setram pop.w R11 pop.w R10 ret ;número queda en frame ;en R10 inicio de RAM ;d++ ;n-- ;-----------------------------------------------------------------------------; Segmento RAM, después de memset ;-----------------------------------------------------------------------------ORG 00200h ; MSP430 Inicio RAM i1: DW 00000h ; 200 arr: DW 00000h ; 202 arr[0] DW 00000h ; 204 arr[1] DW 00000h ; 206 arr[2] DW 00000h ; 208 arr[3] END El código de main ilustra la manipulación de componentes de un arreglo: Prof. Leopoldo Silva Bijit. 23-02-2004 9 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 main: mov.w clr.w mov.w mov.w mov.w #0xFFFF,&i1 &arr #0x1,&0x204 #0x2,&0x206 #0x3,&0x208 ; i1=-1; ; arr[0]=0; ; arr[1]=1; ; arr[2]=2; ; arr[3]=3; mov.w add.w mov.w &0x204,R15 &i1,R15 R15,&arr ; arr[0]=arr[1]+i1; clr.w ret R12 ; return(0) ; En este ejemplo, la función main retorna un valor entero. El valor de retorno de la función se efectúa a través de R12. Definición de punteros. En el siguiente código se definen dos globales iniciadas, y un puntero a entero sin iniciar. int *pi; int x=1; int y=2; int main(void) { pi=&x; y = *pi; return(0) ; } El código de arranque invoca a memcpy para iniciar en RAM las variables enteras y a memset para definir en RAM el puntero a entero, iniciado con valor nulo. ;-----------------------------------------------------------------------------; Segmento RAM, después de memcpy y memset ;-----------------------------------------------------------------------------ORG 00200h ; MSP430 Inicio RAM x: DW 00001h ; 200 y: DW 00002h ; 202 pi: DW 00000h ; 204 END Prof. Leopoldo Silva Bijit. 23-02-2004 10 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 La función main: main: mov.w mov.w mov.w clr.w ret #0x200, &pi ; pi = &x &pi, R15 ; temp=pi @R15, &y ; y = *temp; R12 ; return(0) ; En los ejemplos a continuación se emplearán variables globales, ya que el direccionamiento hace referencia a los nombres de las variables, y de este modo es más sencillo seguir los ejemplos. Si se emplearan locales, habría que asociar variables a registros, y considerar que algunos pueden ser usados como variables temporales. Alternativas. Ilustra el uso de la instrucción compare y los saltos condicionales. ;if (v8>v7) v6++; else v5--; cmp.w jge inc.w jmp else: add.w endif: &v8, &v7 else ; va a else si: &v8 <=&v7 &v6 endif #0xFFFF,&v5 Luego de compilar la condición, mediante un salto condicional se genera el salto al bloque de acciones asociados al else. ;if (v8>=v7) v6++; else v5--; cmp.w &v7,&v8 jl else ; va a else si: &v7 > &v8 inc.w &v6 jmp endif else: add.w #0xFFFF,&v5 endif: ;if (v8<v7) v6++; else v5--; cmp.w jge inc.w jmp else: add.w endif: Prof. Leopoldo Silva Bijit. &v7,&v8 else ; va a else si: &v7 <=&v8 &v6 endif #0xFFFF,&v5 23-02-2004 11 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 ;if (v8<=v7) v6++; else v5--; cmp.w &v8,&v7 jl else ; va a else si: &v8 > &v7 inc.w &v6 jmp endif else: add.w #0xFFFF,&v5 endif: ;if (v8==v7) v6++; else v5--; cmp.w &v7,&v8 jne else ; va a else si: &v7 != &v8 inc.w &v6 jmp endif else: add.w #0xFFFF,&v5 endif: ;if (v8!=v7) v6++; else v5--; cmp.w jeq inc.w jmp else: add.w endif: &v7,&v8 else ; va a else si: &v7 == &v8 &v6 endif #0xFFFF,&v5 Iteraciones. ;while (v8>v0) v0++; lazo: cmp.w jge inc.w jmp endwhile: &v8,&v0 endwhile &v0 lazo ;termina iteración si &v8 <= &v0 ;do {v1++;} while (v8>v1); repeat: inc.w cmp.w jl Prof. Leopoldo Silva Bijit. &v1 &v8,&v1 repeat ;repite si &v8 > &v1 23-02-2004 12 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 ; for(v8=0;v8<v7;v8++) v0--; clr.w &v8 testfor: cmp.w &v7,&v8 jge endfor ;termina si &v7 <= &v8 add.w #0xFFFF,&v0 inc.w &v8 jmp testfor endfor: Condicional. ;v8= (v7>v6) ? v7 : v6; cmp.w jge mov.w jmp else: mov.w fuera: &v7,&v6 else &v7,&v8 fuera &v6,&v8 Switch. switch (v7) { case 1: case 2: v2++; break; case 3: v3--; break; default: v3=++v5; } ;switch (v7) { mov.w dec.w jeq dec.w jeq dec.w jeq jmp &v7,R15 R15 caso1 R15 caso2 R15 caso3 default ;si es caso 1 ;si es 2 ;si es 3 caso1: Prof. Leopoldo Silva Bijit. 23-02-2004 13 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 caso2: caso3: default: inc.w jmp add.w jmp inc.w mov.w &v2 ; v2++; siga #0xFFFF,&v3 ; v3--; siga &v5 ; v3=++v5; &v5,&v3 ;} siga: Break y Continue en iteraciones. ;for(v8=0 ;v8<10 ;v8++ ) { v6++; if(v6>8) break; v5++;} clr.w testfor: &v8 cmp.w jge inc.w cmp.w jge inc.w inc.w jmp #0xA,&v8 endfor &v6 #0x9,&v6 endfor ; es el break. Fuera del lazo. &v5 &v8 testfor endfor: ;for(v8=0 ;v8<10 ;v8++ ) { v6++; if(v6>8) continue; v5++;} clr.w &v8 testfor: cmp.w #0xA,&v8 jge endfor inc.w &v6 cmp.w #0x9,&v6 jge incfor ; es el continue. Al incremento del lazo inc.w &v5 incfor: inc.w &v8 jmp testfor endfor: Ejemplos de diseños de funciones. Ejemplo1. Sea una función f1 de tres variables. Prof. Leopoldo Silva Bijit. 23-02-2004 14 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 El llamado a la función en C, v5=f1(v8,v7,v6); Asumiendo que las variables locales están asociadas a registros: v5 con R9, v6 con R8, v7 con R11 y v8 con R10. Se pasa el valor de v6 en el tope del stack. Luego en R14 el valor de v7 y en R12 el valor de v8. La forma de pasar los argumentos es una política del compilador, que en el caso de pasar paso por registros depende de los que ya estén en uso. Es indispensable conocer ese orden para el diseño de la función. La compilación del llamado queda como sigue: ;v5=f1(v8,v7,v6); push.w mov.w mov.w call incd.w dirret: mov.w R8 R11,R14 R10,R12 #f1 sp R12,R9 ;empuja tercer argumento ;pasa en R14 segundo argumento ;pasa en R12 primer argumento ;recupera posición de sp ;se asume valor de retorno en R12 Después de invocar a f1, los frames en el stack pueden visualizarse según: sp dirret valor v6 Fondo y tope del frame de f1 Tope del frame de función que llama a f1 Para compilar el llamado lo único que se necesita conocer es el prototipo de la función f1: int f1(int , int , int ); Si se desea incorporar el código de la función f1 en assembler, debe indicarse en el prototipo que su definición está en otro archivo. Esto se logra anteponiendo la palabra reservada extern, según se indica: exter int f1(int , int , int ); Esta declaración debe figurar, en el texto en C, antes del llamado. Para compilar la función se requiere su definición, y la forma en que se han pasado los argumentos. Prof. Leopoldo Silva Bijit. 23-02-2004 15 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 int f1(int a0, int a1, int a2) { a0=a1+a2; a1=a0+a2; return(a0); } f1: mov.w mov.w add.w mov.w mov.w add.w mov.w ret 0x2(SP),R15 R14,R13 R15,R13 R13,R12 R12,R13 R15,R13 R13,R14 ;deja en R15 el valor del argumento a2 ;en R14 se tiene a1 ; R13 se usa como registro temporal ; en R12 se tiene a0 Ejemplo 2. Si aumentamos el número de argumentos de la función, y también el número de variables locales en registros, tendremos una visualización de la política de asignación que el compilador utiliza para la asignación de registros. int f1(int a0,int a1,int a2,int a3, int a4) { a0=a1+a2+a3+a4; a1=a0+a2; return(a0); } int main(void) { register int v8=8,v7=7,v6=6,v5=5,v4=4,v3=3,v2=2,v1=1; v3=f1(v8,v7,v6,v5,v4); return(0) ; } main: push.w push.w push.w push.w push.w push.w R10 R11 R8 R9 R6 R7 Prof. Leopoldo Silva Bijit. ;salva registros que se usan en locales. 23-02-2004 16 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 dr1: push.w push.w mov.w mov.w mov.w mov.w mov.w mov.w mov.w mov.w push.w push.w push.w mov.w mov.w call mov.w clr.w add.w pop.w pop.w pop.w pop.w pop.w pop.w pop.w pop.w ret R4 R5 #0x8,R7 #0x7,R10 #0x6,R11 #0x5,R8 #0x4,R9 #0x3,R6 #0x2,R5 #0x1,R4 R9 R8 R11 R10,R14 R7,R12 #f1 R12,R6 R12 #0x6,SP R5 R4 R7 R6 R9 R8 R11 R10 ;inicio de locales ;paso de argumentos en frame. a4 ;a3 ;a2 ;al menos dos argumento se pasan en registros. a1 ;a0 ; retorno de f1 en R12 ;retorno de main. ;desarma frame de la función main ;restaura registros usado en locales. Frame de main en el stack, inmediatamente después de la invocación a f1. sp Prof. Leopoldo Silva Bijit. dr1 a2 a3 a4 R5 R4 R7 R6 R9 R8 R11 R10 dr0 Fondo del frame de f1 Tope del frame de main Fondo del frame de main 23-02-2004 17 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Frame de f1, luego de salvar los registros R10 y R11(tipo save): sp 6 8 A R11 R10 dr1 a2 a3 a4 R5 R4 R7 R6 R9 R8 R11 R10 dr0 Tope del frame de f1 Fondo del frame de f1 Tope del frame de main Fondo del frame de main f1: push.w push.w mov.w mov.w mov.w mov.w add.w add.w add.w mov.w mov.w add.w mov.w pop.w pop.w ret R10 R11 0x6(SP),R15 0x8(SP),R13 0xA(SP),R10 R14,R11 R15,R11 R13,R11 R10,R11 R11,R12 R12,R11 R15,R11 R11,R14 R11 R10 Prof. Leopoldo Silva Bijit. ; salva R10 que ocupa para a4 ; salva R11 que usa como temporal ; deja en R15 el argumento a2 ; deja en R13 el argumento a3 ; deja en R10 el argumento a4 ; a0=a1+a2+a3+a4; ; a1=a0+a2; ;restaura salvados 23-02-2004 18 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Corrimientos aritméticos. Asumiendo variables globales, la traducción de los corrimientos por constante y por variable se ilustra a continuación: v8=v7<<3; v8=v7>>6; v8=v7>>v6; v8=v7<<v6; /*corrimiento por constante */ /*corrimiento por variable */ El assembler implementa algunas rotaciones como sumas. Además sólo rota un bit a la vez. Por ejemplo, empleando R12 como temporal, el desplazamiento con signo a la izquierda en tres posiciones puede compilarse según: mov.w rla.w rla.w rla.w mov.w &v7,R12 R12 R12 R12 R12,&v8 También pueden desarrollarse rutinas más generales, para corrimientos de 0 a 16 posiciones. Se ilustran los códigos, a continuación, generados por el compilador IAR. mov.w call mov.w &v7,R12 #?ShiftLeft16_3 R12,&v8 ; v8=v7<<3; mov.w call mov.w &v7,R12 #?ShiftRight16s_6 R12,&v8 ; v8=v7>>6; mov.w mov.w call mov.w &v7,R12 &v6,R14 #?ShiftRight16s R12,&v8 ; v8=v7>>v6; mov.w mov.w call mov.w &v7,R12 &v6,R14 #?ShiftLeft16 R12,&v8 ; v8=v7<<v6; Las rutinas tienen diversos puntos de entrada, de acuerdo al número de corrimientos que deben efectuarse. Prof. Leopoldo Silva Bijit. 23-02-2004 19 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 ; R12=R12>>R14 ?ShiftLeft16: lazoshlf: skipshlf ?ShiftLeft16_7: ?ShiftLeft16_6: ?ShiftLeft16_5: ?ShiftLeft16_4: ?ShiftLeft16_3: tst.b jeq rla.w dec.b jne ret rla.w rla.w rla.w rla.w rla.w rla.w rla.w ret A la derecha con signo: ?ShiftRight8s: sxt ?ShiftRight16s: tst.b jeq lazoshrt: rra.w dec.b jne skipshrt: ret ?ShiftRight16s_7: rra.w ?ShiftRight16s_6: rra.w ?ShiftRight16s_5: rra.w ?ShiftRight16s_4: rra.w ?ShiftRight16s_3: rra.w rra.w rra.w ret R14 skipshlf R12 R14 lazoshlf R12 R12 R12 R12 R12 R12 R12 R12 R14 skipshrt R12 R14 lazoshrt R12 R12 R12 R12 R12 R1 R12 Si las variables son sin signo, los llamados para corrimientos a la izquierda no cambian; pero los corrimientos a la derecha deben convertirse en rotaciones lógicas. Para corrimiento sin signo, se limpia el carry. A la derecha sin signo: ?ShiftRight8u: and.b ?ShiftRight16u: tst.b jeq lazosru: clrc rrc.w Prof. Leopoldo Silva Bijit. #0xFF,R12 R14 skipsru R12 23-02-2004 20 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 skipsru: ?ShiftRight16u_7: ?ShiftRight16u_6: ?ShiftRight16u_5: ?ShiftRight16u_4: dec.b jne ret clrc rrc.w clrc rrc.w clrc rrc.w clrc rrc.w R14 lazosru R12 R12 R12 R12 ?ShiftRight16u_3: clrc rrc.w R12 rra.w R12 rra.w R12 ret Si el número de corrimientos es mayor que 15 el compilador debería generar un cero (cuestión que IAR realiza). También deberían tratarse en forma especial situaciones que podrían producir pérdidas del signo del número. División de enteros sin signo y con signo. La división de enteros sin signo se implementa mediante algoritmos usuales. El siguiente segmento ilustra el llamado mov.w &v7,R12 mov.w &v6,R14 call #?DivMod16u mov.w R12,&v8 ; v8=v7/v6; ?DivMod8u: and.b and.b #0xFF,R12 #0xFF,R14 ;para enteros sin signo de 8 bits mov.w clr.w mov.w bit.w rlc.w rlc.w cmp.w R14,R15 R14 #0x1,R13 #0x1,R13 R12 R14 R15,R14 ;R12/R14 ?DivMod16u: rot1: Prof. Leopoldo Silva Bijit. 23-02-2004 ;R14-R15 21 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 rot2: jnc sub.w rlc.w jnc rra.w rlc.w ret rot2 R15,R14 R13 rot1 R13 R12 La división con signo, determina el signo del resultado (dejando positivos los operandos) y efectúa la división sin signo. Luego coloca el signo del cuociente y el resto. ?DivMod8s: sxt sxt R12 R14 ?DivMod16s: test1: test2: set1: set2: push.w R9 clr.w R9 tst.w R14 jge test1 inv.w R14 inc.w R14 bis.w #0x1,R9 tst.w R12 jge test2 inv.w R12 inc.w R12 inv.w R9 call #?DivMod16u bit.w #0x1,R9 jeq set1 inv.w R12 inc.w R12 bit.w #0x2,R9 jeq set2 inv.w R14 inc.w R14 pop.w R9 ret Prof. Leopoldo Silva Bijit. 23-02-2004 22 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 Multiplicación de enteros con signo. El microcontrolador tiene una unidad aritmética que efectúa multiplicación de enteros. En caso de multiplicaciones por potencias de dos, es más eficiente realizar la multiplicación mediante sumas que utilizar el hardware de multiplicación. ;v8=v7*2; mov.w rla.w mov.w &v7,R15 R15 R15,&v8 ; v8=v7*v6; ?Mul8Hw: mov.w mov.w call mov.w &v7,R12 &v6,R14 #?Mul16Hw R12,&v8 and.b and.b #0xFF,R12 #0xFF,R14 push.w dint nop mov.w mov.w mov.w mov.w reti SR ;operandos de 8 bits ?Mul16Hw: ;no se puede interrumpir R12,&MPY R14,&OP2 &RESLO,R12 &RESHI,R13 ;retorno en R12 y R13 PC ;recupera SR Operaciones lógicas. Se implementan con cortocircuitos y asignan valores 0 y 1. ; v8=v7&&v6; tst.w &v7 jeq esfalso ;primer operando falso tst.w &v6 jeq esfalso mov.b #0x1,R15 ;ambos verdaderos jmp esverdad Prof. Leopoldo Silva Bijit. 23-02-2004 23 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA Assembler MSP430 esfalso: esverdad: clr.b and.w mov.w R15 #0xFF,R15 R15,&v8 ;v7=v6||v5; yaesverdad: esfalso: esverdad: tst.w jne tst.w jeq mov.b jmp clr.b and.w mov.w &v6 yaesverdad &v5 esfalso #0x1,R15 esverdad R15 #0xFF,R15 R15,&v7 tst.w jne mov.b jmp clr.b and.w mov.w &v6 argverdad #0x1,R15 setvalor R15 #0xFF,R15 R15,&v7 ;ambos son falsos v7=!v6; argverdad: setvalor: ;setea a uno Se ilustra la composición de una expresión. ;v8=(v7&&v6)&&v5; tst.w &v7 jeq esfalso tst.w &v6 jeq esfalso tst.w &v5 jeq esfalso mov.b #0x1,R15 jmp setvalor esfalso: clr.b R15 setvalor: and.w #0xFF,R15 mov.w R15,&v8 Prof. Leopoldo Silva Bijit. 23-02-2004 24