Una implementación de switch / case en Ensamblador
A menudo, al programar en ensamblador, nos encontramos con situaciones en las que nos gustaría implementar una construcción de programación desde un lenguaje de alto nivel, como la declaración de switch de C. C encuentra muchos usos en aplicaciones integradas, pero dicha construcción no existe en lenguaje ensamblador. Sin embargo, es bastante fácil recrear la funcionalidad usando las operaciones fundamentales del microcontrolador junto con una macro característica del ensamblador que se utiliza con poca frecuencia y algunos viejos trucos de álgebra booleana que muchos de nosotros hemos olvidado hace tiempo.
El código
En este fragmento de código, SWITCH es un registro (variable) que contiene el valor que intentamos emparejar con cada uno de los casos. Las etiquetas CASE1, CASE2 y CASE3 son constantes y son los valores que estamos comprobando contra el almacenado en SWITCH. Las etiquetas LABEL1, LABEL2 y LABEL3 son los nombres de las subrutinas a las que queremos saltar para cada condición de coincidencia.
Análisis paso a paso
1. movwf SWITCH, w
2. xorlw CASE1
Esto realiza una operación OR exclusiva en modo bit (en tiempo de ejecución) entre el registro W y la CASE1 constante. El resultado se almacena en W. Matemáticamente, esto se puede escribir como:
W = W ⊕ CASO 1
Sustituyendo el valor original de W establecido en la línea 1:
W = SWITCH ⊕ CASE1
3. btfsc STATUS,Z
Esto prueba el resultado de la operación anterior. Si el resultado de SWITCH ⊕ CASE1 es cero, entonces se establecerá el bit Z en el registro STATUS. Entonces, lo que esta línea está diciendo es que si el bit Z esta limpio (la operación anterior no dio como resultado un cero), saltee la próxima instrucción. La razón por la que realizamos esta prueba se basa en la propiedad de no indefensión: A ⊕ A = 0. Entonces, si el valor en SWITCH es el mismo valor que CASE1, entonces SWITCH ⊕ CASE1 = 0. Si ese es el caso, encontramos nuestro punto y queremos ejecutar la siguiente instrucción ...
4. goto LABEL1
Esta línea nos lleva a una subrutina con el nombre LABEL1 para manejar la situación cuando SWITCH ⊕ CASE1 = 0. Si el bit Z en el registro STATUS no estaba configurado arriba, entonces esta instrucción se saltaría y probaremos la siguiente condición.
5. xorlw CASE1 ^ CASE2
Esta línea es donde ocurre la mayor parte de la magia. Estamos jugando varios trucos a la vez. Lo primero que hay que señalar es que el símbolo '^' es el operador XOR en el lenguaje de macros del ensamblador. Los operadores macro realizan cálculos en la computadora en tiempo de compilación y nunca se ejecutan en el microcontrolador PIC®. Por lo tanto, este operador solo se puede usar con constantes u otros valores que se conocen en tiempo de compilación. En este ejemplo, CASE1, CASE2 y CASE3 son todas constantes definidas en nuestro código y, por lo tanto, conocidas en tiempo de compilación. Por lo tanto, esta línea de código XOR el valor en el registro W con el valor calculado CASE1 ^ CASE2. Matemáticamente, esto se puede escribir como:
W = W ⊕ (CASO1 ⊕ CASO2)
Sin embargo, W contiene el resultado de la operación anterior SWITCH ⊕ CASE1. Sustituyendo el valor anterior de W:
W = (SWITCH ⊕ CASE1) ⊕ (CASE1 ⊕ CASE2)
En este punto, podemos aprovechar algunas de las propiedades de XOR. Primero, usamos las propiedades asociativas y conmutativas para reescribir la ecuación:
W = (SWITCH ⊕ CASE2) ⊕ (CASE1 ⊕ CASE1)
A continuación, usamos la propiedad de no indefensión (A ⊕ A = A = 0):
W = (SWITCH ⊕ CASE2) ⊕ 0
Y finalmente, usamos la propiedad de identidad (A ⊕ 0 = A):
W = SWITCH ⊕ CASE2
Que es exactamente lo que queremos probar para ver si SWITCH = CASE2! Ahora esto es exactamente como lo que hicimos en la línea 2. De aquí en adelante, el código simplemente se repite con diferentes valores.
El código puede repetirse para tantos valores de CASEn que desee usar.
El operador XOR
Para crear la construcción del switch / case en ensamblador, necesitamos revisar algunas de las propiedades fundamentales del operador XOR.
Para crear la construcción del switch / case en ensamblador, necesitamos revisar algunas de las propiedades fundamentales del operador XOR.
Todas las propiedades anteriores se usan en combinación entre sí para realizar un pequeño truco de lógica que se describirá paso a paso a continuación. Sin un conocimiento práctico de estas propiedades, el código no tendrá mucho sentido.
El código
En este fragmento de código, SWITCH es un registro (variable) que contiene el valor que intentamos emparejar con cada uno de los casos. Las etiquetas CASE1, CASE2 y CASE3 son constantes y son los valores que estamos comprobando contra el almacenado en SWITCH. Las etiquetas LABEL1, LABEL2 y LABEL3 son los nombres de las subrutinas a las que queremos saltar para cada condición de coincidencia.
El fragmento de código anterior aprovecha las propiedades del operador XOR y la capacidad del ensamblador para realizar cálculos en constantes en tiempo de compilación (más en el análisis paso a paso a continuación). Hay varias variaciones flotando alrededor de la red, por lo que esta no es ciertamente la única forma de implementar una construcción tipo switch en ensamblador. El código anterior debería funcionar en cualquier microcontrolador PIC® de 8 bits. Con modificaciones menores a la sintaxis, también debería funcionar en un microcontrolador PIC de 16 bits y controladores de señal digital dsPIC®.
Análisis paso a paso
1. movwf SWITCH, w
Esto simplemente mueve el valor del registro etiquetado SWITCH al registro W. Matemáticamente, podemos escribir esto como:
W = SWITCH
W = SWITCH
2. xorlw CASE1
Esto realiza una operación OR exclusiva en modo bit (en tiempo de ejecución) entre el registro W y la CASE1 constante. El resultado se almacena en W. Matemáticamente, esto se puede escribir como:
W = W ⊕ CASO 1
Sustituyendo el valor original de W establecido en la línea 1:
W = SWITCH ⊕ CASE1
3. btfsc STATUS,Z
Esto prueba el resultado de la operación anterior. Si el resultado de SWITCH ⊕ CASE1 es cero, entonces se establecerá el bit Z en el registro STATUS. Entonces, lo que esta línea está diciendo es que si el bit Z esta limpio (la operación anterior no dio como resultado un cero), saltee la próxima instrucción. La razón por la que realizamos esta prueba se basa en la propiedad de no indefensión: A ⊕ A = 0. Entonces, si el valor en SWITCH es el mismo valor que CASE1, entonces SWITCH ⊕ CASE1 = 0. Si ese es el caso, encontramos nuestro punto y queremos ejecutar la siguiente instrucción ...
4. goto LABEL1
Esta línea nos lleva a una subrutina con el nombre LABEL1 para manejar la situación cuando SWITCH ⊕ CASE1 = 0. Si el bit Z en el registro STATUS no estaba configurado arriba, entonces esta instrucción se saltaría y probaremos la siguiente condición.
5. xorlw CASE1 ^ CASE2
Esta línea es donde ocurre la mayor parte de la magia. Estamos jugando varios trucos a la vez. Lo primero que hay que señalar es que el símbolo '^' es el operador XOR en el lenguaje de macros del ensamblador. Los operadores macro realizan cálculos en la computadora en tiempo de compilación y nunca se ejecutan en el microcontrolador PIC®. Por lo tanto, este operador solo se puede usar con constantes u otros valores que se conocen en tiempo de compilación. En este ejemplo, CASE1, CASE2 y CASE3 son todas constantes definidas en nuestro código y, por lo tanto, conocidas en tiempo de compilación. Por lo tanto, esta línea de código XOR el valor en el registro W con el valor calculado CASE1 ^ CASE2. Matemáticamente, esto se puede escribir como:
W = W ⊕ (CASO1 ⊕ CASO2)
Sin embargo, W contiene el resultado de la operación anterior SWITCH ⊕ CASE1. Sustituyendo el valor anterior de W:
W = (SWITCH ⊕ CASE1) ⊕ (CASE1 ⊕ CASE2)
En este punto, podemos aprovechar algunas de las propiedades de XOR. Primero, usamos las propiedades asociativas y conmutativas para reescribir la ecuación:
W = (SWITCH ⊕ CASE2) ⊕ (CASE1 ⊕ CASE1)
A continuación, usamos la propiedad de no indefensión (A ⊕ A = A = 0):
W = (SWITCH ⊕ CASE2) ⊕ 0
Y finalmente, usamos la propiedad de identidad (A ⊕ 0 = A):
W = SWITCH ⊕ CASE2
Que es exactamente lo que queremos probar para ver si SWITCH = CASE2! Ahora esto es exactamente como lo que hicimos en la línea 2. De aquí en adelante, el código simplemente se repite con diferentes valores.
El código puede repetirse para tantos valores de CASEn que desee usar.
Fuente: A switch/case Implementation in Assembly
Comentarios
Publicar un comentario