HC12tutorial Tasm

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 150

Tutorial PK-HCS12E128 Estructura de Computadores

TUTORIAL DE LABORATORIO PARA LA PLACA DE


EVALUACIÓN PK-HCS12E128 Y ENTORNO
METROWORKS CODEWARRIOR

1 Entorno de desarrollo
El entorno de desarrollo que utilizaremos a lo largo de las sesiones de
prácticas es el CodeWarrior IDE.

Este software nos permitirá llevar a cabo los diversos pasos necesarios para
la creación y ejecución de nuestros programas. Dispondremos de las herramientas
típicas de desarrollo como un editor de código, un compilador, facilidades para la
descarga en el microcontrolador, etc. La interfaz gráfica es muy “amigable” e
“intuitiva” por lo que no tendréis mayores problemas al trabajar con ella.

2 Iniciando el CodeWarrior
El programa se encuentra en el escritorio o en
Inicio>Programas>Metrowerks CodeWarrior>CW12 v3.0>CodeWarrior IDE. La
figura siguiente enseña la ventana principal del programa.

3 Metodología de desarrollo
La metodología a seguir es la siguiente:

Creación/Edición del código fuente utilizando CodeWarrior IDE;

Compilación del programa utilizando CodeWarrior IDE;

Descarga del programa a la placa de evaluación PK-HCS12E128;

Verificación del correcto funcionamiento utilizando el monitor de la placa de


evaluación;

1
Tutorial PK-HCS12E128 Estructura de Computadores

Modificación de parámetros mediante el monitor.

3.1 Creando un proyecto

Suponiendo que el CodeWarrior IDE ya se encuentra activo (en caso


contrario, activarlo), iniciar un proyecto nuevo siguiendo las siguientes instrucciones.

1º) Seleccione File>New.

2º) En la ventana que muestra el paso anterior, seleccione “HC(S)12 New


Project Wizard” y el directorio donde será creado el fichero

3º) En la opción “Location>Set” elija la carpeta y escriba el nombre del


fichero del proyecto, en nuestro caso Tut1.

4º) En la ventana “New Project Wizard - Page 1” seleccione el dispositivo


“MC9S12E128”

5º) En la ventana “New Project Wizard - Page 2” seleccione sólo la opción


“Assembly” (desmarque todas las otras)

6º) En la ventana “New Project Wizard - Page 3” elija “Absolute


Assembly”

7º) En la ventana “New Project Wizard - Page 4” seleccione las opciones


“Metrowerks Full Chip Simulator” y “inDART-HCS12 Hardware
Debugging”

8º) Abra la carpeta “Source” y pulse dos veces en el fichero “main.asm”

Al final, aparecerá la siguiente ventana enseñando el fichero main.asm. Este


programa es un esqueleto que servirá de base para que el alumno pueda escribir
sus propios programas.

2
Tutorial PK-HCS12E128 Estructura de Computadores

3.2 Compilando el proyecto

Para compilar utilizamos la opción Project>Compile. En caso de errores


aparecerá una ventana con mensajes que nos ayudarán a solucionar los problemas.

3.3 Probando el proyecto

Antes de descargar el ejecutable para el Hardware o para depurar el código,


podremos simularlo con la opción Project>Debug. Esa opción compila el código
fuente y activa el Debugger.

Obs.: Si aparece la ventana “HI-WAVE” simplemente pulse en “Aceptar”.

A continuación aparecerá la ventana “True-Time Simulator & Real-Time


Debugger”.

3
Tutorial PK-HCS12E128 Estructura de Computadores

3.3.1 Estructura del programa

El programa para el HC12 deberá estar estructurado con dos regiones


distintas de memoria: La región para las variables y otra para el segmento de código.
Para definir las regiones de memoria, se utiliza, en general, los símbolos RAMStart
y ROMStart. En estos símbolos, definimos la dirección absoluta del inicio de cada
región.
RAMStart EQU $0500
ROMStart EQU $5000
.

ORG RAMStart
.
. segmento de variables
.

ORG ROMStart
.
. segmento de codigo
.

En el ejemplo de arriba, hemos definido que nuestra dirección de inicio del


segmento de las variables es 0x500 mientras que la dirección de inicio del segmento
de código es 0x5000. La directiva EQU (equate) es utilizada para definir el valor de
los símbolos.

4
Tutorial PK-HCS12E128 Estructura de Computadores

Para que tenga efecto estas direcciones, utilizamos la directiva ORG (origin)
para definir cada una de nuestras regiones de memoria.

Otra importante directiva es la directiva ABSENTRY. Esta directiva es la


responsable de especificar la dirección de inicio de ejecución del programa cuando
éste se encuentra en modo absoluto. Por ejemplo, “ABSENTRY Start”, significa que
el programa va comenzar en la etiqueta “Start”.

A diferencia de las instrucciones, las directivas no generan código y son


utilizadas en la etapa de pré-procesamiento. Para el buen funcionamiento del
programa, las directivas tendrán que tener un espacio adelante.

3.3.1.1 Sintaxis

Las directivas y las instrucciones no son “case sensitive” mientras que las
etiquetas y símbolos son.

Los comentarios son utilizados para explicar el significado de alguna parte del
código y se definen mediante la utilización del “;” (e.j., DEC Var01 ; decremento de
la variable var01).

Las etiquetas son símbolos que son seguidos de un “:” pegados a ellos. Son
utilizadas para marcar algun punto del código que sea necesario acceder
directamente (e.j., LOOP:).

Los “strings” son una cadena de caracteres delimitados por comillas simples o
bien comillas dobles. Por ejemplo:

Var01 dc.b “Pepito” ; Pepito

Var02 dc.b ‘“Pepito”’ ; “Pepito”

3.3.2 Depurando el proyecto

En la ventana del Debugger, encontramos siete sub-ventanas donde


podemos obtener todas las informaciones posibles para depurar nuestro programa.
Todas las ventanas son auto-intuitivas, pero las principales son:

Memory: se puede mirar la memoria del controlador. Pulsando con el botón


derecho del ratón en esta ventana aparecerá un menú de contexto.

Register: se puede mirar el contenido de los registros del controlador y


cambiar el formato de los valores (i.e., Hex, Bin, Dec, etc…) del mismo.

Assembly, se puede mirar la codificación del programa, sus instrucciones,


posición en la memoria y otras informaciones adicionales (botón derecho del ratón).

5
Tutorial PK-HCS12E128 Estructura de Computadores

Pregunta:

1) Indique la dirección de memoria en donde empieza el código.

2) ¿Qué cambio será necesario si queremos que el programa comience


en la dirección 4300?

3) ¿Cuántos bytes tiene el programa ejecutable?

4) ¿Cuál es la dirección de memoria de la variable temp_byte?

3.4 Ejecución del programa en el simulador

Seleccione la opción Run>Start/Continue. El programa será simulado en el


entorno. Mire la información CPU Cycles en la ventana Register.

Pregunta:

5) ¿Qué instrucción es necesaria para que el programa incremente la


variable temp_byte a cada ejecución del lazo?

Continúe observando la memoria de datos (pregunta 4). Para que podamos


observar las actualizaciones de la memoria será necesario cambiar la opción de
refresco. Para eso, en la ventana “Memory” utilizamos el menú de contexto y
seleccionamos la opción “Mode…>Periodical…”. Ponga 5 en la opción “Update
Rate” y pulse OK.

Pregunta:

6) Ejecute el programa. ¿Por qué la variable no cambia de uno en uno?

3.5 Visualización de Memoria

Existen diversos comandos para la manipulación de la memoria. Estas


funciones van desde la visualización y modificación de posiciones de memoria
específicas hasta el movimiento de bloques de memoria a otras zonas en el mapa
de memoria.

Pregunta:

7) Sabiéndo que el botón derecho del ratón sirve para activar el menú de
contexto, experimente las posibilidades de este menú en la ventana
“Assembly” y escriba el código binario de las 3 primeras instrucciones del
programa

6
Tutorial PK-HCS12E128 Estructura de Computadores

8) Si se añade la definición de la variable var1 dc.w 400 en el código, qué


cambios se aprecian en la memoria?

9) Explicar la diferencia entre la declaración de temp_byte y var1

3.6 Control de ejecución.

3.6.1 Ejecución paso-a-paso

Una forma sencilla de controlar la ejecución consiste en ejecutar las


instrucciones de una en una. Para eso utilice las opciones del menú Run.

Pregunta:

10) Altere el programa para que éste se quede en un bucle infinito


incrementando la variable temp_byte hasta que ésta llegue a 0xFF y, a partir de
ahí, decrementar temp_byte hasta que llegue a 0.

Mirar instrucciones condicionales(BNE,BRA…)

11) Ejecute el programa paso a paso. ¿Qué flags están activados al


llegar a la instrucción BNE?

3.6.2 Puntos de ruptura (Breakpoints).

Los puntos de ruptura se utilizan para interrumpir la ejecución / simulación de


un programa en una determinada instrucción. Cuando el programa llega al
breakpoint se interrumpe momentáneamente la ejecución, de forma que se pueda
consultar el estado del procesador y decidir si se continúa depurando el programa o
se reanuda la ejecución hasta el siguiente breakpoint.

Para insertar un breakpoint pulsa el botón derecho del ratón y elige la opción
Set Breakpoint.

Pregunta:

12) ¿En qué punto se debe poner el breakpoint para que el programa
pare siempre que temp_byte sea 0?

3.7 Descarga del programa en el microcontrolador.

Antes de descargar el programa en el microcontrolador debemos verificar que


la placa este conectada al ordenador mediante el puerto USB y que el indicador
POWER este encendido.

En la ventana del proyecto Tut1.mcp modifique la opción de salida para


SofTec_inDART-HCS12 (ver la marca de la figura siguiente).

7
Tutorial PK-HCS12E128 Estructura de Computadores

Seleccionar la opción Project>Debug (F5). Aparecerá la ventana para


configuración de la placa de simulación. En la opción HW code seleccione PK-
HCS12E128 y pulse OK (ver figura siguiente).

El programa será compilado y descargado para la placa de experimentos.

4.- Ejercicios prácticos.

4.1. Estructura condicionales:


La programación en el entorno HCS12 no nos proporciona instrucciones
condicionales directas pero nos permite por medio de las instrucciones condicionales

8
Tutorial PK-HCS12E128 Estructura de Computadores

crear las estructuras condicionales que hagan falta. En el manual de instrucciones


tenéis las instrucciones condicionales para implementar las estructuras.

4.1.1 Basándonos en el programa del tutorial, realizar las siguientes


modificaciones:
a) Inicializar la variable temp_byte a 1
b) Incrementar su valor
c) Cuando temp_byte=10 finalizar el programa

4.1.2 En el programa del punto 5.1.1 realizar el siguiente cambio:


d) Cuando temp_byte=10 volver al paso a)

4.2. Estructura de datos:

La declaración de constantes y variables en el codificador del HCS12 es muy


sencillo y se hace de la siguiente forma:
Para declarar una constante, se utiliza la directiva DC (define constant in
memory), se utiliza la siguiente expresión:
Para declarar un byte…:
Constante01 dc.b $FC
Vector 01 dc.b 1,2,3,5,6,8
String01 dc.b ‘Hola’
Para declarar un Word (2 bytes)…:
Constante02 dc.w $FB1A
Vector02 dc.w 1,2,3,5,6,8

Para declarar variables, se utiliza la directiva DS. Esta directiva reserva


posiciones consecutivas de memoria, pero no las inicializa!
Por ejemplo,
Variable01 ds.b 1
Vector02 ds.b 5
Vector03 ds.w 3
Los elementos siempre se almacenan contiguos en memoria (esto siempre es
así, independientemente del lenguaje que se use), de forma que se puede acceder a
ellos usando la dirección base del array, que apunta al primer elemento, y el

9
Tutorial PK-HCS12E128 Estructura de Computadores

desplazamiento desde la dirección base al elemento al que se quiere acceder. Por


tanto, para acceder a un elemento de un array se hará de la siguiente forma:
Dirección_Elemento = Dirección_Base + Indice * Tamaño_Elemento
Estamos suponiendo que el primer elemento del array tiene el índice 0 (como
en lenguaje C).
Aunque esta forma de enumerar los elementos del array depende del
programador, es aconsejable que se haga así para simplificar el código.
• Dirección_Base: Es la dirección del primer elemento del array y siempre
aparece en la dirección de memoria más pequeña. El segundo elemento sigue
directamente al primero en memoria, y así sucesivamente. La Dirección_Base se
obtiene usando el nombre de la etiqueta que encabeza la definición del array (del
espacio de memoria donde se almacena). El ensamblador trata las etiquetas como
direcciones a memoria, aquélla donde se encuentra el dato referenciado por la
etiqueta.
• Indice: Es el índice del elemento del array al cual se quiere acceder.
• Tamaño_Elemento: Es el tamaño en bytes de un elemento del array.

4.2.1 Crear un programa que realice el siguiente algoritmo:


- Vector01 dc.b 1,2,3,4,5
- Crear Vector02
- Cargar Vector02 con los mismos valores que Vector01.
- Vector03=Vector01+Vector02

NOTA: Durante la implementación de este programa, se debe verificar su


correcto funcionamiento a través de la información que proporciona el debuger.

4.2.2 Subrutinas
Las subrutinas son muy útiles y ayudan a localiza fácilmente los
problemas. Para crear una subrutina, se hace igual que con las etiquetas pero
la forma de acceso es a través de llamadas a subrutinas. Por ejemplo: BSR y
RTS. En el manual del HSC12 podrán encontrar referencias detalladas.

4.2.2 Entrada y salida


En el microcontrolador HCS12, podemos programar entradas y salidas
a distintos periféricos. En este tutorial nos vamos a centrar en los pulsadores y en
los leds del microcontrolador. Para poder utilizar las entradas y salidas del
microcontrolador es necesario configurar sus puertos para que actúen como se
desea (entrada o salida).

10
Tutorial PK-HCS12E128 Estructura de Computadores

Por ejemplo: en el trozo de codigo abajo, programamos el puerto DAO (led)


para que funcione como puerto de salida, y el pulsador PAD04 para funcionar como
entrada. Observe los comentarios de cada instrucción.

ABSENTRY Entry
PTM EQU $0250 ; Puerto M (controla el LED DA0)
DDRM EQU $0252 ;Define la configuración del puerto M (si es entrada ó salida)
ATDDIEN1 EQU $008D ; Habilita entrada en el puerto AD bits (7..0)
PTADLo EQU $0271 ; Byte menos significativo de AD (bits 7-0)
DDRADLo EQU $0275 ;Define la configuración del puerto AD (si es entrada ó salida)
PERADLo EQU $0279 ; Define alimentacion de entrada
; (0= desabilitada / 1= habilitada)
RAMStart EQU $0400 ; Inicio de la area de variables
ROMStart EQU $4000 ; Inicio del codigo
ORG RAMStart ; data section
ORG ROMStart ; code section
Entry:
LDS #$1FFF ; Carga la dirección de la pila
LDAB #$FF ; envia FF al registro B
STAB DDRM ; define el modo del PTM. PTM es salida

STAB PERADLo ; Habilita alimentación de entrada

STAB ATDDIEN1 ; ATDDIENLo set to input


LDAB #0 ; carga 0 en el registro B
STAB DDRADLo ; Bits 7..0 del PTAD son entrada
LOOP:
BSR testPDA04 ; Mira si el pulsador4 esta activado
BEQ OUTPUT ; Salta si hay cambio en el pulsador
BRA LOOP ; Bucle infinito
OUTPUT:
ldab #1 ; Envia 1 a registro b
STAb PTM ; Enciende el LED DA0 (PTM=B)
BRA LOOP ; Bucle infinito
testPDA04:
ldab #0 ; Envia 0 a registro b
STAb PTM ; Enciende el LED DA0 (PTM=B)
LDAA PTADLo ; Lee el puerto AD
ANDA #$10 ; Testea el bit relativo a PAD04 (0= activo)
RTS ; Retorna a la siguiente instrucción después de la
llamada correspondiente del BSR

11
Tutorial PK-HCS12E128 Estructura de Computadores

4.3. Cree un nuevo proyecto e introduzca el siguiente código:

00) ABSENTRY Entry ; for absolute assembly: mark this as application entry point
01) RAMStart EQU $0400 ; absolute address to place my variables
02) ROMStart EQU $4000 ; absolute address to place my code/constant data
03) PTT EQU $0240 ; Direccion del puerto T= LEDS
04) DDRT EQU $0242 ; Direccion que define se PT es entrada o salida
05) ; variable/data section
06) TMP EQU $FFFF ; Time to delay
07)
08)
09) ORG RAMStart; Insert here your data definition.

10) ORG ROMStart ; code section

11) Entry:
12) CLI ; enable interrupts
13) LDAB #$FF ; PT es salida
14) STAB DDRT
15) LDAB #0
16) for1:
17) STAB PTT ; Encende los leds
18) INCB
19) LDX #TMP
20) for2:
21) DEX
22) BNE for2
23) BRA for1 ; bucle infinito

Al realizar los ejercicios de esta sección, es imprescindible verificar el correcto


funcionamiento de los diversos programas mediante la utilización de la placa de
evaluación.

Pregunta:

13) ¿Explique qué hace el programa?

14) Utilizando la tabla A-1 ( Instruction Set Summary ) del manual de


referencia, pp. 340-357, escriba las operaciones, modo de direccionamiento y
el código de las instrucciones de las líneas: 12, 13, 14, 18, 19, 21, 22 y 23.

15) Modifique el programa para que presente una secuencia inversa.

16) Altere el programa original para que presente los resultados de


manera más lenta.

12
Tutorial PK-HCS12E128 Estructura de Computadores

4.4. Cree un nuevo proyecto e introduzca el siguiente código:

ABSENTRY Entry ; for absolute assembly: mark this as


; application entry point

RAMStart EQU $0400 ; absolute address to place my variables


ROMStart EQU $4000 ; absolute address to place my code/constant data
STACKAdress EQU $6000

ORG RAMStart

time ds.b 1
var ds.b 1
res ds.b 1

ORG ROMStart

Entry:
lds STACKAdress

Main:
movb #$5,res
movb #$4,var
movb #01,time
for:
ldaa var
ldab res
MUL

STAB res

BSR SHOW
movb #01,time

SHOW:
ldab res
stab PTT
RTS

Como se observa en el código, se definen las variables del programa en la posición de


memoria 400, el código en la 4000 y la pila en la 6000.

El programa contiene errores y nosotros, mediante el debuger, vamos a depurar el


programa en busca de los errores.

17) Intuitivamente, ¿qué hace el programa?


18) Utilizar el debuger y ejecutar el programa. Explicar que ocurre.
19) ¿Qué registro del procesador provoca este error? Ayuda: debugar el
programa paso a paso y observar qué ocurre con la pila.
20) Con la ventana “memory” del debuger visualizar el contenido de la pila.
¿Cuál es la dirección de la pila y el contenido de la memoria donde ésta
apunta la primera vez que se entra en la subrutina? . ¿Y la segunda vez que
se entra a la subrutina?
21) Proponer una solución a los problemas encontrados

13
Tutorial PK-HCS12E128 Estructura de Computadores

4.5. Realice un programa que se base en la siguiente siguiente tabla:

Pulsador1 Incremento registro B

Pulsador2 Decremento registro B

El programa debe empezar encendiendo los leds de forma incremental y debe


continuar así en un bucle infinito, hasta que el usuario pulse uno de los pulsadores. Los
pulsadores seran los responsables de cambiar el valor del registro B, que por su vez deberá
ser enviado para el puerto de los leds.
La secuencia de encendido de leds se debe conseguir mediante el incremento de una
variable de 0 hasta 255. El decremento de 255 hasta 0.
Para conocer si se ha apretado algún pulsador, el programa realizará el método de
encuesta (polling). Es importante programar bien las entradas y salidas de los pulsadores y de
los leds para que el programa funcione. El fichero OrdenLeds.asm se encuentra en el campus
virtual y tendrá que ser completado para ejercicio propuesto.

14
Tutorial PK-HCS12E128 Estructura de Computadores

TUTORIAL DE LABORATORIO PARA LA PLACA DE


EVALUACIÓN PK-HCS12E128 Y ENTORNO
METROWORKS CODEWARRIOR

1 Entorno de desarrollo
El entorno de desarrollo que utilizaremos a lo largo de las sesiones de
prácticas es el CodeWarrior IDE.
Este software nos permitirá llevar a cabo los diversos pasos necesarios para
la creación y ejecución de nuestros programas. Dispondremos de las herramientas
típicas de desarrollo como un editor de código, un compilador, facilidades para la
descarga en el microcontrolador, etc. La interfaz gráfica es muy “amigable” e
“intuitiva” por lo que no tendréis mayores problemas al trabajar con ella.

2 Iniciando el CodeWarrior
El programa se encuentra en el escritorio o en
Inicio>Programas>Metrowerks CodeWarrior>CW12 v3.0>CodeWarrior IDE. La
figura siguiente enseña la ventana principal del programa.

3 Metodología de desarrollo
La metodología a seguir es la siguiente:
1º) Creación/Edición del código fuente utilizando CodeWarrior IDE;
2º) Compilación del programa utilizando CodeWarrior IDE;
3º) Descarga del programa a la placa de evaluación PK-HCS12E128;
4º) Verificación del correcto funcionamiento utilizando el monitor de la placa de
evaluación;
5º) Modificación de parámetros mediante el monitor.

3.1 Creando un proyecto


Suponiendo que el CodeWarrior IDE ya se encuentra activo (en caso
contrario, activarlo), iniciar un proyecto nuevo siguiendo las siguientes instrucciones.

1
Tutorial PK-HCS12E128 Estructura de Computadores

1º) Seleccione File>New.


2º) En la caja de dialogo, selecione “HC(S)12 New Project Wizard” y el
directorio donde será creado el fichero
3º) En la opción “Location>Set” elija la carpeta y escriba el nombre del
fichero del proyecto, en nuestro caso Tut1.
4º) En la ventana “New Project Wizard - Page 1” seleccione el dispositivo
“MC9S12E128”
5º) En la ventana “New Project Wizard - Page 2” seleccione sólo la opción
“Assembly” (desmarque todas las otras)
6º) En la ventana “New Project Wizard - Page 3” elija “Absolute
Assembly”
7º) En la ventana “New Project Wizard - Page 4” seleccione las opciones
“Metrowerks Full Chip Simulator” y “inDART-HCS12 Hardware
Debugging”
8º) Abra la carpeta “Source” y pulse dos veces en el fichero “main.asm”

Al final, aparecerá la siguiente ventana enseñando el fichero main.asm. Este


programa es un esqueleto que servirá de base para que el alumno pueda escribir
sus propios programas.

3.2 Compilando el proyecto


Para compilar utilizamos la opción Project>Compile. En caso de errores
aparecerá una ventana con mensajes que nos ayudarán a solucionar los problemas.

2
Tutorial PK-HCS12E128 Estructura de Computadores

3.3 Probando el proyecto


Antes de descargar el ejecutable para el Hardware o para depurar el código,
podremos simularlo con la opción Project>Debug. Esa opción compila el código
fuente y activa el Debugger.
Obs.: Si aparece la ventana “HI-WAVE” simplemente pulse en “Aceptar”.
A continuación aparecerá la ventana “True-Time Simulator & Real-Time
Debugger”.

Pregunta:
1) Indique la dirección de memoria en donde empieza el código.
2) ¿Qué cambio será necesario si queremos que el programa comience
en la dirección 4300?
En la ventana “Memory” se puede mirar la memoria del controlador. Pulsando
con el botón derecho del ratón en esta ventana aparecerá un menú de contexto. Elija
la opción “Address…” y ponga el valor 4000. Mire lo que pasa en la ventana
“Memory”.
Pregunta:
3) ¿Cuántos bytes tiene el programa ejecutable?
4) ¿Cuál es la dirección de memoria de la variable temp_byte?

3
Tutorial PK-HCS12E128 Estructura de Computadores

3.4 Ejecución del programa en el simulador


Seleccione la opción Run>Start/Continue. El programa será simulado en el
entorno. Mire la información CPU Cycles en la ventana Register.
Pregunta:
5) ¿Qué instrucción es necesaria para que el programa incremente la
variable temp_byte a cada ejecución del lazo?
Continúe observando la memoria de datos (pregunta 4). Para que podamos
observar las actualizaciones de la memoria será necesario cambiar la opción de
refrescar. Para eso, en la ventana “Memory” utilizamos el menú de contexto y
seleccionamos la opción “Mode…>Periodical…”. Ponga 5 en la opción “Update
Rate” y pulse OK.
Pregunta:
6) Ejecute el programa. ¿Por qué la variable no cambia de uno en uno?

3.5 Visualización de Memoria


Existen diversos comandos para la manipulación de la memoria. Estas
funciones van desde la visualización y modificación de posiciones de memoria
específicas hasta el movimiento de bloques de memoria a otras zonas en el mapa
de memoria.
Pregunta:
7) Sabiendo que el botón derecho del ratón sirve para activar el menú de
contexto, experimente las posibilidades de este menú en la ventana
“Assembly” y escriba el código binario de las 3 primeras instrucciones del
programa

3.6 Control de ejecución.

3.6.1 Ejecución paso-a-paso


Una forma sencilla de controlar la ejecución consiste en ejecutar las
instrucciones de una en una. Para eso utilizase las opciones del menú Run.
Pregunta:
8) Modifique el programa para que se quede en un bucle infinito
incrementando la variable temp_byte hasta que ésta llegue a 0xFF y, a partir de
ahí, decrementar temp_byte hasta que llegue a 0.
9) Ejecute el programa paso a paso. ¿Qué flags están activados al llegar
a la instrucción BNE?

3.6.2 Puntos de ruptura (Breakpoints).


Los puntos de ruptura se utilizan para interrumpir la ejecución / simulación de
un programa en una determinada instrucción. Cuando el programa llega al

4
Tutorial PK-HCS12E128 Estructura de Computadores

breakpoint se interrumpe momentáneamente la ejecución, de forma que se pueda


consultar el estado del procesador y decidir si se continúa depurando el programa o
se reanuda la ejecución hasta el siguiente breakpoint.
Para insertar un breakpoint pulsa el botón derecho del ratón y elige la opción
Set Breakpoint.
Pregunta:
10) ¿En qué punto se debe poner el breakpoint para que el programa
pare siempre que temp_byte sea 0?

3.7 Descarga del programa en el microcontrolador.


Antes de descargar el programa en el microcontrolador debemos verificar que
la placa este conectada al ordenador mediante el puerto USB y que el indicador
POWER este encendido.
En la ventana del proyecto Tut1.mcp altere la opción de salida para
SofTec_inDART-HCS12 (ver destaque abajo).

Seleccionar la opción Project>Debug (F5). Aparecerá la ventana para


configuración de la placa de simulación. En la opción HW code seleccione PK-
HCS12E128 y pulse OK (ver figura siguiente).

5
Tutorial PK-HCS12E128 Estructura de Computadores

El programa será compilado y descargado para la placa de experimentos.

4 Ejercicios
Cree un nuevo proyecto e introduzca el siguiente código:

ABSENTRY Entry ; for absolute assembly


RAMStart EQU $0400 ; absolute address to place my variables
ROMStart EQU $4000 ; absolute address to place my code/constant data
PTT EQU $0240 ; Direccion del puerto T= LEDS
DDRT EQU $0242 ; Direccion que define se PT es entrada o salida
TIME EQU $FFFF ; Tiempo del bucle

ORG RAMStart ; Data section

ORG ROMStart ; Code section

Entry:
CLI ; Habilita interrupciones
LDAB #$FF ; PT es salida
STAB DDRT ; Programa PT
LDAB #0
for1: ; Bucle de contaje
STAB PTT ; Enciende los leds
INCB ; Próximo valor

LDX #TIME
for2: ; Bucle de tempo
DEX
BNE for2

BRA for1 ; Bucle infinito

6
Tutorial PK-HCS12E128 Estructura de Computadores

Al realizar los ejercicios de esta sección, es imprescindible verificar el correcto


funcionamiento de los diversos programas mediante la utilización de la placa de
evaluación.

Pregunta:
11) ¿Explique qué hace el programa?
12) Modifique el programa para que presente una secuencia inversa.

5 Trabajos Finales
13) Modifique el programa original para que presente los resultados de
manera más lenta.

14) Basándose en el programa anterior, cree un nuevo programa que permita


controlar la secuencia de los LEDS bajo el control de los pulsadores PAD04 y
PAD05, conforme la siguiente tabla:
PAD04 Para la secuencia
PAD05 Reinicia la secuencia

7
Tutorial HC12Sim _____

TUTORIAL DE LABORATORIO PARA EL


SIMULADOR HC12SIM
1.- Instalando el simulador.
Si estas en el laboratorio pasad al punto 2 del tutorial. Si estas en casa y quieres instalar el
simulador en tu PC sigue leyendo.
Para poder ejecutar el simulador es necesario disponer del propio simulador, un ensamblador
para el microprocesador 68HC12 y una maquina virtual de Java. Este software se puede
descargar a partir de la siguiente web: http://almy.us/68hc12.html.
Para instalar el simulador se puede descargar cualquiera de las versiones disponibles en la
página web mencionada anteriormente y seguir las explicaciones correspondientes. El
simulador necesita que exista una máquina virtual de Java instalada en el computador. Lo más
simple en estos casos es instalar la versión que incluye máquina virtual de Java.
Otra alternativa sería realizar la instalación por separado del simulador y de la máquina
virtual de Java. Los pasos a seguir serían:
a) Instalar la maquina virtual de Java, si no la tenemos inslatada. Realizar un doble-click
sobre el ejecutable descargado de la Web de Sun (www.sun.com) y seguir las
instrucciones de los diálogos que van apareciendo a medida que el proceso de
instalación progresa.
b) Descomprimir el fichero del simulador en el directorio que preferías (por ejemplo:
C:\soft\hc12sim).
Por último es necesario instalar el programa ensamblador necesario para compilar nuestros
programas. Hay que descargarse el archivo correspondiente y descomprimir los archivos en
otro directorio, por ejemplo C:\soft\hc12Asm.

2.- Arrancando el simulador.


Encended el ordenador y entrar en Windows.
Una vez en Windows es necesario abrir una ventana de explorador de archivos para ejecutar
el simulador. Situaros en el directorio en donde está instalado el simulador (en el laboratorio
el directorio es C:\soft\Mc68hc12\simulador). A continuación hay que ejecutar el simulador.
El método de ejecución del simulador depende de la versión del intérprete de Java (JRE) que
se haya instalado:
• Si la versión del Java instalado en el sistema es inferior a la 1.2, la forma más fácil de
ejecutar el simulador es utilizar el archivo: “RUNSIM.BAT” ó desde línea de
comandos mediante:
> jre –cp SimHC12.jar SimHC12
• Si la versión del Java instalado en el sistema es igual ó superior a la 1.2, entonces el
simulador se puede lanzar directamente realizando un doble-click sobre el archivo
SimHC12.jar desde un explorador de archivos de Windows (Laboratorio).
Si todo va bien deberías ver en vuestra pantalla la interfase del simulador mostrada en la
página 3.
Ir haciendo los diferentes ejercicios indicados a lo largo de este tutorial y escribir las
respuestas en la parte correspondiente de la última que se incluye en este documento. La hoja
de respuestas deberá entregarse debidamente cumplimentada al finalizar la sesión de
laboratorio.

1
Tutorial HC12Sim _____

3.- Metodología de simulación.


La metodología a seguir para la simular un programa en el SimHC12 consta de los siguientes
pasos:
1. Editar el programa ensamblador.
2. Compilar el programa ensamblador.
3. Cargar el programa en el simulador.
4. Simular la ejecución del programa y depurarlo.

3.1.- Edición del programa.

La edición del programa ensamblador se puede realizar con cualquier editor de texto. En
Windows podremos utilizar el notepad y en DOS podemos utilizar el programa edit.
Los ficheros de código fuente debería tener la extensión asm para utilizar una nomenclatura
común (aunque el ensamblador que vamos a utilizar no requiere una extensión específica).

Editar el siguiente código y salvarlo con el nombre Tut1.asm. Conviene crear un


directorio temporal de trabajo y grabar en disquete todos los archivos al final de la
sesión. (El ensamblador del MC68hc12 es un ensamblador tabulado, donde las
etiquetas y símbolos deben estar situados en la primera columna y las órdenes para el
ensamblador y las instrucciones deben estar situadas más allá de la primera columna
(el uso del tabulador contribuirá a cumplir esta regla y a mejorar la legibilidad del
programa, por otra parte).

P1: ¿Qué hace este programa?

DBasePrograma: EQU $4000


DBasePila: EQU $7FFF
DMemoriaDatos: EQU $5000
; Codigo Principal.
ORG DBasePrograma
LDS #DBasePila
LDAA #1
LDAB #20
LDX #DMemoriaDatos
DEX
EtOtraIter:
STAA 1,x
ADDA 1,x+
DECB
BNE EtOtraIter
; Fin programa.
SWI ; Esta instrucción solo funciona en el.
; microprocesador real.

Tut1.asm

3.2.- Generación del programa ejecutable.

2
Tutorial HC12Sim _____

Para ensamblar nuestros ficheros fuentes vamos a utilizar el ensamblador AS12 desde una
ventana de DOS. Este programa recibe un fichero fuente, genera el código ensamblado en un
fichero con formato s19 y un listado por pantalla.
Si no se especifica lo contrario el fichero s19 tiene el mismo nombre que el fichero original
pero con la extensión s19 (Esto implica que nunca se puede utilizar la extensión s19 si no
queremos perder el código fuente cuando este se compile).

Ensamblar el código anterior mediante el siguiente comando (en el laboratorio el


programa ensamblador se encuentra en C:\soft\Mc68hc12\as12. Verificad que la
variable PATH apunta correctamente a este directorio si tenéis problemas al ejecutar
el comando de ensamblaje):
As12 Tut1.asm

Si queremos almacenar el listado generado en un fichero, utilizar el comando:


As12l Tut1.asm > Tut1.lst

El ensamblador nos indica las líneas donde hay errores leves (warnings) o graves. Los errores
graves deben corregirse editando el fichero original y volviendo a ensamblar. Es conveniente
corregir también los errores leves si los hubiese.

3.3.- Cargar el programa en el simulador.

Una vez se ha ensamblado el código correctamente este se tiene que que cargar en el
simulador para poder simular su ejecución. La carga de programas se realiza utilizando el
menú file del simulador y seleccionando el fichero de entrada S19 que queramos simular.
Cargar el fichero compilado anterior (Tut1.s19) en el simulador mediante el menú
File Load.

3.4.- Simulación

Una vez tenemos nuestro programa cargado en el simulador podemos realizar algunas de las
siguientes funciones:
• Ejecutar paso a paso cada una de sus instrucciones.
• Ejecutar el programa de forma continua hasta una determinada instrucción, utilizando
breakpoints.
• Visualizar el contenido de los registros y puertos del procesador.
• Comprobar el contenido de la memoria.
• Etc.
Esta funcionalidad nos permite verificar nuestro código y localizar errores de forma que
podamos comprobar que realiza correctamente la función para la cual ha sido diseñado.
En el siguiente apartado veremos cómo utilizar todas estas características del simulador de
forma que la depuración del código se pueda realizar de forma fácil y sin necesidad de
disponer del microprocesador para poder probar y depurar nuestro programas.

3
Tutorial HC12Sim _____

4.- Simulador.

En la siguiente imagen podemos ver la interfase gráfica del HC12SIM.

En la parte superior de la ventana de la aplicación podemos ver tres menús: File, View y
Help.
El menú File nos permite cargar el fichero s19 del código a simular (como hemos visto en el
apartado anterior), inicializar el estado del microprocesador (Reset), generar un log de la
simulación en un fichero (Log) ó cerrar el simulador (Exit).

El menú View nos permite visualizar diversa información de la simulación que esta ubicada
en ventanas independientes como puede ser: el código del programa (Code view), un
emulador de un terminal (SCI Viewer), los puertos paralelos (Parallel Ports), el generador de
señales (Signal Generator), las entradas analógicas (ADC Analog to Digital Converter) y
capturar instantáneas de cierta información de la simulación (Snapshot).
Por último, el menú Help nos permite acceder a la ayuda del simulador.

4.1. Visor de código.

El visor de código nos permite visualizar el código de nuestro programa, ya que a medida que
simulación avanza, en la ventana principal únicamente se nos muestra la siguiente instrucción
que se va a ejecutar.

4
Tutorial HC12Sim _____

Esta ventana se abre mediante la opción View Code View. Dispone de un campo (Address)
en donde se puede especificar a partir de que dirección de memoria se quiere realizar el
desensamblado (convertir código maquina en código ensamblador) del código.

Mostrar la ventana de código para el programa anteriormente cargado en el


simulador.
P2: ¿En que posición de memoria se encuentra la ultima línea de nuestro programa?

4.2. Registros de la CPU.

La siguiente zona del simulador nos permite ver y modificar los valores del los registros y
los flags del microprocesador. Para modificar el valor de un registro hay que posicionarse
en el campo correspondiente e introducir el valor deseado en formato hexadecimal. Los
flags se pueden modificar mediante un clic en el campo del flag que queramos cambiar, si
esta activado (valor =1) se desactivará (valor =0) y viceversa.

Antes de poder empezar a simular la ejecución de un programa es necesario inicializar el


PC con la posición de memoria en donde queremos que empiece la ejecución
(normalmente se inicializar con la dirección de memoria en donde empieza nuestro
programa).

5
Tutorial HC12Sim _____

4.3. Controlando la simulación.

Mediante los siguientes botones se puede controlar la ejecución de la simulación:

El botón de step permite ejecutar 1 ó n instrucciones de golpe (n depende del número


introducido en el campo count).
El botón de step-over permite ejecutar las siguientes instrucciones pero sin detallar la
ejecución de las instrucciones de las subrutinas.
El botón Go permite ejecutar de golpe todo el programa hasta que finalice ó hasta que se
encuentre un punto de ruptura de la ejecución (breakpoint).
El botón “View Breakpoints” permite abrir la ventana de gestión de los puntos de ruptura
de la ejecución. Esta ventana se explica con más detalle en el apartado 4.4.
En el campo “Next Instruction” se visualiza la siguiente instrucción que se va a simular.

Simular la ejecución del programa anterior paso a paso hasta ejecutar la instrucción
STAA 1,x.
P3: ¿Qué valores tenemos en los diferentes registros y que flags están activados?

4.4. Puntos de ruptura (Breakpoints).

Los puntos de ruptura se utilizan para interrumpir la ejecución / simulación de un


programa en una determinada instrucción. Cuando el programa llega al breakpoint se
interrumpe momentáneamente la ejecución de forma que se pueda consultar el estado del
procesador y decidir si se continúa depurando el programa ó se reanuda la ejecución hasta
el siguiente breakpoint.
La gestión de los puntos de ruptura en el simulador se realiza mediante la siguiente
ventana, la cual aparece al presionar el botón “View Breakpoints”:

6
Tutorial HC12Sim _____

El botón de Add nos permite añadir un punto de ruptura en la posición de memoria


introducida en el campo de entrada.
El botón de “Delete” borra el breakpoint seleccionado, mientras que con el botón “Delete
All” podemos borrar todos los breakpoints de golpe.
Por ultimo el botón “Close” cierra la ventana.
La metodología utiliza para trabajar con los puntos de ruptura es la siguiente:
1) Seleccionar los puntos de ruptura que queremos añadir a la ejecución.
2) Arrancar la ejecución del programa, mediante la presión del botón Go, para que se
ejecute de forma continua hasta el primer breakpoint.
3) Cuando se llegue a un punto de ruptura, depurar el código y reanudar la ejecución
si es necesario.

Utilizando los breakpoints, simular la ejecución del programa Tut1 hasta el final de la
iteración 8.
P4: a) ¿En qué punto se debe situar el breakpoint?
b) ¿Qué valores tienen los registros A y X al final de cada iteración?

4.5. Display de Memoria.

La zona de “Memory Display” nos permite consultar y modificar el contenido de una


zona de memoria del micro.

La zona de visualización se compone de tres zonas diferentes:


• A la izquierda tenemos las direcciones de la memoria que se esta visualizando, en
formato hexadecimal.
• En el centro tenemos el contenido de las distintas posiciones de memoria en
hexadecimal, en donde cada posición que se muestra representa un byte de la
memoria.
La zona de visualización, en total, tiene capacidad para 4 filas cada una con 16
posiciones de memoria de un byte. La posición de cada uno de estos bytes, se
puede calcular fácilmente a sumando la dirección de inicio (a la izquierda de la
fila) más el desplazamiento (situado en la cabecera de la columna).

7
Tutorial HC12Sim _____

• En el lado derecho se muestra el contenido de la misma área de memoria pero en


formato ASCII.

Para seleccionar la zona de memoria que se quiere visualizar se tiene que presionar el
botón de “Show” y a continuación insertar la dirección de inicio de la zona de memoria
que se quiere consultar.

Situar la ventana de visualización en la posición 4000h,


P5: ¿Qué significado tiene el contenido de las direcciones 4000h, 4001h, 4002h? Usar
el archivo Tut1.lst para responder.

Ejecutar el programa Tut1.asm hasta el final.


P6: ¿ Cual es el contenido del bloque de memoria 5000-500Fh, después de la ejecución
del programa?

Para modificar una posición de memoria hay que rellenar el campo de “Fill start” con la
dirección de inicio del bloque de memoria, el campo de “Fill end” con la dirección final y el
campo “Value” con el valor que se va escribir. A continuación apretando el botón “Fill” se
escribirá el valor en todas las posiciones del bloque de memoria seleccionado.

Inicializar todas las posiciones del bloque de memoria 5000-5020h a 11h y volver a
ejecutar el programa Tut1.asm hasta el final.
P7: ¿ Que resultado obtenemos en el registro A?

5.- Ejercicios prácticos.

Al realizar los ejercicios de esta sección, es imprescindible verificar el correcto


funcionamiento de los diversos programas mediante la utilización del simulador HC12Sim.
Al inicio de la siguiente sesión, se tendrá que entregar un pequeño informe con las respuestas
obtenidas a las preguntas del tutorial (utilizando la ultima hoja del mismo) y un listado de
todos los programas pedidos. En el caso de que durante la primera sesión no se pueda acabar
los ejercicios estos se tienen que finalizar en casa.

P8: Modificar el programa anterior de forma que el final de la posición a rellenar se


obtenga mediante una constante en memoria inicializada a 30 en vez de un valor
constante (#20).
¿Cuál es la dirección de la constante en memoria?

P9: Modificar el programa Tut1.asm, de forma que en vez utilizar un bloque de memoria
preestablecido para guardar los resultados se utilice una variable que ocupe 20 bytes.
¿Cuál es la dirección de la variable?

P10: Simular el siguiente y simular código.

DBasePrograma: EQU $4000


DBasePila: EQU $6000
DMemoriaDatos: EQU $5000

8
Tutorial HC12Sim _____

; Codigo Principal.
ORG DBasePrograma

LDS #DBasePila
LDA #$11
LDB #$22
LDX #$3333
LDY #$4444

;Guardar en la pila.
PSHA
PSHB
PSHX
PSHY
PSHD
EjercicioA:
NOP

;Restaurar de la pila.
PULB
PULA
PULX
PULY
PULD

; Fin programa.
SWI ; Esta instrucción solo funciona en el.
; microprocesador real.

Tut2.asm

a) el contenido de la pila después de ejecutar las instrucciones hasta la etiqueta


EjercicioA.
b) Mostrar el contenido de los registro del procesador después de ejecutar el programa.
c) Queremos que después de finalizar de restaurar los registros de la pila, el valor de
estos sea el mismo que antes de ponerlos en la pila. ¿Se consigue este objetivo con el
programa anterior? ¿En el caso de que la respuesta sea negativa modificar el código
para que se logre dicho objetivo?
P11: El programa Tut1.asm sufre un problema grave debido a que utiliza datos cuyo
tamaño es de un solo byte. Eso provoca que los resultados sean erróneos a partir de
una determinada iteración. Realizar un programa que solucione dicho problema
utilizando datos de dos bytes de tamaño.
Comprobar el resultado, ejecutando el programa y viendo cual es la nueva secuencia
de valores que se generan en memoria. Este ejercicio debe entregarse al inicio de la
siguiente sesión de prácticas.

9
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

TUTORIAL de Programación Básica en Ensamblador


del Microprocesador HCS12
Objetivos:
ƒ Editar un programa, ensamblarlo, cargarlo en el procesador, y ejecutarlo paso a paso,
visualizando los cambios que produce cada instrucción en los registros del procesador y
en la memoria.
ƒ Entender el funcionamiento de instrucciones sencillas del repertorio de la familia de
procesadores HCS12. Modificar el programa para variar su funcionalidad.
ƒ Revisar la documentación y aprender a buscar la información técnica.

Material:
ƒ Placa de desarrollo SofTec Microsystems PK-HCS12E128.
ƒ Computador Personal (MS-Windows) con un puerto USB disponible y cable USB
ƒ Metrowerks CodeWarrior HC(S)12 (http://www.codewarrior.com/MW/download)
ƒ SofTec Microsystems PK-HCS12E128 “System Software” (Campus Virtual)
ƒ Documentación técnica resumida y completa (Campus Virtual)

1. Programas en Ensamblador de HCS12


Un programa es un fichero de texto escrito según la sintaxis y la semántica del lenguaje
ensamblador. Se puede crear un programa a partir de múltiples ficheros, aunque este tema
queda fuera de los objetivos de este tutorial. A continuación se describirá la sintaxis de los
programas y posteriormente se describirá la semántica de los programas.

1.1 Sintaxis
Por regla general, cada línea de texto de un programa debe contener una única instrucción, o la
definición de una variable, o una directiva (o seudo-operación). En la siguiente sección se
describirán estos tres elementos que determinan la semántica de los programas. Una línea
puede contener una etiqueta y un comentario.
La sintaxis de una línea consiste en hasta cuatro campos separados por caracteres “whitespace”
(el carácter espacio o el carácter de tabulación): una etiqueta, seguida de una operación (que
puede ser una instrucción, una directiva, o una macro), seguida de operandos opcionales
separados por comas, y seguidos de un comentario opcional que comienza con el carácter ‘;’.
Ejemplos:
Etiqueta: instr1 op1, op2 ; comentario
instr2 ; comentario
Una etiqueta es una serie de caracteres alfanuméricos que comienza en la primera columna del
texto y que finaliza en un carácter espacio, en un carácter de tabulación, o en el carácter ‘:’. Si no
se quiere etiquetar una línea, se debe comenzar la línea con un espacio o un carácter de
tabulación. Una etiqueta representa la dirección de memoria en la que se coloca la instrucción o
la variable definidos en esa línea (→ver más adelante la definición). De este modo, el programa
no necesita hacer referencia explícita a direcciones de memoria, sino que puede utilizar etiquetas
simbólicas, para así facilitar la escritura del programa. Las etiquetas son case sensitive
(sensibles a mayúsculas o minúsculas). Por ejemplo, no es lo mismo “Start” que “START”.

-1-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

Una operación es un mnemotécnico que consiste en una serie de caracteres alfabéticos, donde
no se distingue entre mayúsculas y minúsculas. El mnemotécnico representa o bien una única
instrucción del repertorio de instrucciones (ver manual del HCS12 para conocer la lista completa
de todas las instrucciones, p.e. ADCA, ó bge), o bien una directiva (→ver más adelante la
definición).
Tras el mnemotécnico que indica la operación se pueden incluir cero, uno o varios operandos,
separados por comas (p. e., 0, #!17). Los operandos son una mezcla de constantes numéricas
(ver Tabla 1), nombres de registros (A, B, D, X, Y, SP), y signos ‘+’, ‘-‘, ‘[‘, ‘]’, ‘#’. Las cadenas de
caracteres (strings) son constantes que también se pueden utilizar como operandos al definir el
contenido de variables de texto. Se representan delimitadas por comillas simples ‘’ o bien por
comillas dobles ””.
Base Prefijo Sufijo
Binaria (2) % Q
Decimal (10) T
Hexadecimal (16) $ H
Tabla 1. Representación de constantes. Por defecto se supone que las constantes del programa están en
decimal, aunque las que se muestran en el depurador están en hexadecimal.

Un comentario se utiliza para documentar el código. Puede ocupar toda una línea si el primer
carácter de la línea es ‘*’ o ‘;’. Generalmente se ponen al final de la línea tras la operación y los
operandos y el carácter “;”.
Cuidado! En ocasiones se comete el error de escribir la operación en la primera columna,
de modo que el ensamblador la considera como una etiqueta y por tanto desaparece
misteriosamente de nuestro programa!

1.2 Semántica Básica


Una instrucción se compone de una operación junto con sus operandos. Cada instrucción se
codifica en uno o varios bytes, que se almacenarán en la memoria del microprocesador y que
constituirán el programa binario que el microprocesador ejecutará. La dirección de una
instrucción se define como la dirección de memoria en la que se almacena el primer byte de una
instrucción. El microprocesador lee la información binaria en memoria y la interpreta para saber
qué secuencia de operaciones debe realizar y qué datos debe utilizar. El manual del HCS12
documenta la lista completa de todas las instrucciones, indicando su sintaxis y semántica (cómo
modifican el estado del procesador), la forma de codificarlas, y el número de ciclos de reloj
necesarios para ejecutarlas.
Una directiva es una instrucción que se le da al lenguaje ensamblador. En muchas ocasiones no
genera código, y por tanto no ocupa lugar en la memoria del microprocesador. Existen directivas
que se utilizan para declarar y definir variables, que sí que generan información para ser
almacenada en la memoria del microprocesador. Para conocer las directivas es necesario un
manual del ensamblador (y no un manual del procesador). En este tutorial aprenderemos el
significado de las directivas más importantes. A continuación explicaremos las dos directivas
dedicadas a declarar y definir variables (DS y DC).
Las variables son posiciones de memoria que se utilizan para contener información que es
utilizada por el programa. Las variables pueden ocupar uno o más bytes. La dirección de una
variable se define como la dirección de memoria en la que se almacena el primer byte de una
variable. En el lenguaje ensamblador del microprocesador HCS12 se pueden definir variables de
un byte de tamaño (indicado con el sufijo “.b”, de byte) o de dos bytes de tamaño (indicado con el

-2-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

sufijo “.w”, de word, o palabra). Al definir una variable se le puede asignar un valor inicial
predefinido, o no darle ningún valor, y por tanto al inicio de la ejecución del programa tendrá un
valor indefinido.
Para declarar una variable con un valor indefinido (sin inicializar) se utiliza la directiva DS
(declare storage). Esta directiva reserva posiciones consecutivas de memoria, pero no las
inicializa. Por ejemplo:
Variable1 ds.b 1
Vector2 ds.b 5
Vector3 ds.w 3
Las tres líneas anteriores declaran tres variables, la primera de un byte de tamaño, la segunda
de 5 bytes de tamaño, y la tercera de 3 palabras de 2 bytes cada una (es decir, 6 bytes en total).
Las tres etiquetas representan las direcciones de cada una de las variables. Tal como se indicó
previamente, los sufijos “.b” y “.w” indican el tamaño de cada elemento de la variable (un byte, o
una palabra de 2 bytes). Los números finales (1, 5 y 3) indican el número de elementos a
reservar para la variable. Una variable compuesta de más de un elemento (como en el caso de
Vector2 y Vector3), se denomina vector, y los elementos de él se almacenan continuos en
memoria. Más adelante se propondrá un ejercicio para manejar los elementos de un vector.
Para declarar una variable con un valor inicial predefinido se utiliza la directiva DC (define
constant). Esta directiva reserva una posición (o varias consecutivas) de memoria y la inicializa
con un valor constante. De todos modos, si la variable está definida en la dirección de memoria
que corresponde a la memoria RAM (→ver más adelante la definición), el contenido de la
variable podrá ser modificado dinámicamente por el programa, y por tanto la palabra “constante”
no se debe interpretar de forma estricta.
Las declaraciones anteriores que usaban DS se pueden sustituir por las siguientes declaraciones
de variables con valores iniciales:
Variable1 dc.b $FC
Vector2 dc.b ‘adios’
Vector3 dc.w %4,$18,!24
Fijarse en cómo se puede declarar una lista de constantes separadas por comas o una cadena
de caracteres (cada carácter codificado con un byte).

1.3 Estructura General de los Programas


Los programas en lenguaje ensamblador para el microprocesador de la familia HC12 suelen
definir al menos dos regiones distintas de memoria: una para las variables y otra para el código.
Las variables siempre se colocan en direcciones correspondientes a memoria RAM (de memoria
volátil, con operaciones de lectura y escritura rápidas), mientras que el código se suele colocar
en la memoria EPROM (flash ROM, de memoria no volátil, con operaciones de lectura rápidas
pero operaciones de escritura mucho más lentas). Para indicar las direcciones de memoria de
inicio de estas regiones se suelen usar los símbolos RAMStart y ROMStart.
El siguiente código de ejemplo define la dirección inicial de las zonas de memoria RAM y ROM
como 400h y 4000h respectivamente. Para ello se utiliza la directiva EQU (equate), que asocia
una etiqueta o nombre (un símbolo alfanumérico, en este caso RAMstart y ROMStart) con un
valor constante (el formato de esta directiva es: “Etiqueta EQU valor_constante”). Pero es la
directiva ORG (origin) la que indica al ensamblador la dirección de memoria a partir de la cual se
deben colocar las instrucciones o datos que vienen a continuación en el programa (el formato de
esta directiva es: “ORG dirección_de_memoria”).

-3-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

XDEF Entry
ABSENTRY Entry

RAMStart EQU $0400


ROMStart EQU $4000

ORG RAMStart
temp_byte ds.b 1

ORG ROMStart
Entry:
CLI ; enable interrupts
MOVB #1,temp_byte ; just some demonstration code
NOP ; insert here you own code
BRA Entry ; endless loop

ORG $FFFE
fdb Entry ; Reset

Otra directiva importante es ABSENTRY. Esta directiva le dice al ensamblador la dirección de


inicio de ejecución del programa. Por ejemplo, “ABSENTRY Entry”, significa que el programa va
a comenzar en la posición de memoria que sigue a la etiqueta Entry. La directiva XDEF sirve
para que la etiqueta Entry sea pública, lo cual permite crear programas complejos compuestos
de múltiples ficheros. Finalmente, las dos últimas líneas del programa sirven para introducir la
dirección de inicio del programa, que corresponde con la etiqueta Entry, en la posición de
memoria asignada a la función de reset. De este modo, cada vez que se haga un reset del
procesador, éste comenzará a ejecutar de forma automática el programa que se encuentra en la
dirección Entry.
PREGUNTAS de PRE-LABORATORIO (deben traerse contestadas al laboratorio y entregarlas
al profesor al comenzar la sesión): Analizar el anterior programa ejemplo y contestar.
1) ¿Por qué se deja un espacio al principio de muchas de las líneas?
2) ¿Cuántos símbolos (ó etiquetas) se definen en el programa y que valor tiene asociado
cada símbolo?
3) Mirando el manual del HCS12, indicar el tamaño del programa en bytes, incluyendo
tanto el espacio de memoria de variables como el de código.

2. Entorno de desarrollo CodeWarrior


El entorno de desarrollo CodeWarrior IDE nos permitirá llevar a cabo los pasos necesarios para
la creación y ejecución de nuestros programas. Dispondremos de las herramientas típicas de
desarrollo: editor de código, compilador, descarga del programa en el microcontrolador, etc. La
interfaz gráfica es muy “amigable” e “intuitiva” por lo que no tendréis mayores problemas al
trabajar con ella.
Para instalarlo en vuestro propio ordenador debéis obtener el software Metrowerks CodeWarrior
HC(S)12 directamente de http://www.codewarrior.com/MW/download. Esto os permitirá editar,
compilar y simular programas, para así poder verificar su correcto funcionamiento. Para poder
utilizar la placa de evaluación PK-HCS12E128, después de instalar CodeWarrior, se debe
instalar también SofTec Microsystems PK-HCS12E128 “System Software” (disponible en el
Campus Virtual).

-4-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

2.1 Metodología de desarrollo de Programas


La metodología a seguir es la siguiente:
ƒ Creación/Edición del código fuente utilizando CodeWarrior IDE
ƒ Compilación del programa utilizando CodeWarrior IDE
ƒ Verificación del correcto funcionamiento y depuración de errores utilizando el simulador.
ƒ Descarga del programa a la placa de evaluación PK-HCS12E128 y ejecución.
ƒ Depuración de errores relacionados con los periféricos que no se hayan podido simular,
y utilizando el entorno real (parámetros de entrada diferentes a los utilizados durante la
simulación pueden dar a luz errores no detectados anteriormente).
2.2 Iniciando CodeWarrior
El programa se encuentra en el escritorio o en Inicio>Programas>Metrowerks CodeWarrior> ….
La figura siguiente muestra la ventana principal del programa.

Figura 1. Ventana Principal del programa de desarrollo CodeWarrior


2.3 Creando un proyecto
Suponiendo que CodeWarrior IDE ya se encuentra activo, iniciar un proyecto nuevo siguiendo
las siguientes instrucciones.
ƒ Seleccionar File>New.
ƒ En la ventana que muestra el paso anterior, seleccionar “HC(S)12 New Project Wizard” y
el directorio donde serán creado los múltiples ficheros de los que consta el proyecto
(incluso un programa muy simple genera múltiples ficheros). En la opción “Location>Set”
elija la carpeta y escriba el nombre del fichero del proyecto, por ejemplo Tut1. Presionar
el botón Aceptar.
ƒ Aparecerán una serie de 4 ventanas, etiquetadas como “New Project Wizard - Page K”
en las que hay que seleccionar una serie de parámetros:
1. seleccionar el dispositivo “MC9S12E128”
2. seleccionar sólo la opción “Assembly” (desmarque todas las otras)
3. elegir “Absolute Assembly”
4. seleccionar las opciones “Metrowerks Full Chip Simulator” y “inDART-HCS12
Hardware Debugging” (en versiones nuevas de CodeWarrior es posible que la
opción a seleccionar sea “Softec Microsystems Hardware Debugging")
ƒ Aparecerá una nueva ventana. Se debe abrir la carpeta “Source” y pulsar dos veces en
el fichero “main.asm”. De este modo aparecerá la ventana de la Figura 2, que muestra el
contenido del fichero que contiene el programa (en versiones nuevas de CodeWarrior es
posible que el programa varíe ligeramente)

-5-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

El programa es el mismo que se presentó en la sección anterior (si no lo es, utilizar la nueva
versión, en la que es posible que los valores RAMStart y ROMStart sean diferentes), y se trata
de un esqueleto que servirá de base para que el alumno pueda escribir sus propios programas.
De momento se utilizará tal y como está, y más adelante se realizarán modificaciones sobre él.

Figura 2. Entorno de Edición. Programa ejemplo que aparece por defecto


2.4 Compilando el programa
Para compilar utilizamos la opción Project>Compile. En caso de errores aparecerá una ventana
con mensajes que nos ayudarán a solucionar los problemas. De momento no deberían aparecer
errores, ya que el ejemplo que aparece por defecto es correcto. En caso contrario, volver a
cargar el fichero y asegurarse de no modificar nada.
2.5 Depurando el programa
Una opción interesante para depurar el código de errores semánticos es simular la ejecución. De
este modo no es necesario disponer de la placa hardware, o se puede tener apagada. Por
defecto, el entorno arranca con la opción de la simulación activada. Más adelante se propondrá
el uso del hardware, de momento se utilizará el modo de simulación. La opción Project>Debug
activa el Depurador en modo simulación (ignorar las ventanas intermedias que puedan aparecer
a continuación, y simplemente pulsar en “Aceptar”). La ventana “True-Time Simulator & Real-
Time Debugger” que se puede ver en la Figura 3 es la que se utilizará para analizar el código, su
funcionamiento, y el estado del procesador. Ampliar al máximo la ventana para disponer del
máximo espacio para visualizar los datos.
En la ventana del Depurador, encontramos siete sub-ventanas donde podemos obtener mucha
de la información necesaria para analizar y depurar nuestro programa. Todas las ventanas son
auto-intuitivas, pero las principales son:
ƒ Assembly: permite interpretar y visualizar en lenguaje ensamblador el contenido de la
memoria, visualizar la codificación de las instrucciones (en hexadecimal), encontrar la
dirección inicial de memoria de cada instrucción. Pulsando con el botón derecho del
ratón sobre esta ventana aparecerá un menú de contexto que nos permite variar la

-6-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

información visualizada, y que también permite poner y quitar puntos de parada (→ver
más adelante la definición).
ƒ Register: permite visualizar el contenido de los registros del microcontrolador y cambiar
el formato de visualización de los valores (p.e., Hexadecimal, Binario, Decimal, etc…).
Las modificaciones también se hacen invocando el menú de contexto con el botón
derecho del ratón.
ƒ Memory: permite visualizar la memoria del microcontrolador. El menú de contexto nos
permite variar la dirección de memoria a visualizar, el formato de los datos visualizados,
etc. Haciendo un doble clic sobre algún dato podemos modificar directamente la
memoria. Hay que tener en cuenta que el comportamiento de la memoria puede diferir
según estemos en el modo simulación o utilizando el hardware real. Por ejemplo, en
modo simulación, muchas posiciones de memoria tienen un valor indefinido (u:
undefined). Pulsando sobre un dato en memoria que corresponde a la dirección de un
registro de entrada/salida, en la parte superior de la ventana se muestra el nombre
específico de ese registro de Entrada/Salida.

Figura 3. Entorno de Depuración. En este caso se está simulando el procesador HC12.

2.6 Control de la ejecución del programa


El depurador permite ejecutar las instrucciones de una en una de forma controlada para poder
ver los efectos de cada instrucción de forma individual. La ejecución paso a paso se puede
realizar usando el menú Run, un botón específico, o la tecla <F11>.
La ejecución continua de las instrucciones del programa se puede invocar usando el menú Run,
un botón específico, o la tecla <F5>. Se puede detener la ejecución continua en cualquier
momento con la opción Halt del menú Run, un botón específico, o la tecla <F6>.

-7-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

Si lo que se desea es detener la ejecución del programa en un determinado momento, se


pueden utilizar puntos de ruptura de la ejecución, bien asociados a instrucciones (Breakpoints) o
bien asociados a variables (Watchpoints). Se pueden definir múltiples puntos de ruptura
utilizando la opción Control Points del menú Run, o con el menú de contexto sobre la ventana
Assembly o sobre la ventana Data. Cada punto de ruptura se asocia a una instrucción o una
variable (a la dirección de memoria de la instrucción o de la variable). Cuando la ejecución del
programa llega a la instrucción o cuando el programa realiza un acceso a la variable (para leer o
para escribir) se interrumpe la ejecución, de forma que se puede consultar el estado del
procesador y decidir si se continúa depurando el programa o se reanuda la ejecución hasta el
siguiente punto de parada. Es posible complicar la definición de puntos de parada indicando el
número de ocurrencias antes de detener la ejecución u otro tipo de condiciones sobre variables y
registros.

PREGUNTAS de LABORATORIO (resolverlas en el laboratorio y resolver las dudas con el


profesor durante la sesión): Utilizar el simulador para obtener la siguiente información sobre el
anterior programa.
4) Comprobar que las respuestas a las preguntas 2) y 3) preparadas previamente son
correctas, e indicar la dirección de memoria y la codificación en hexadecimal de las cuatro
instrucciones del programa (CLI, MOVB, NOP y BRA).
5) ¿Cuál es inicialmente el contenido de la dirección $400 de memoria? Ejecutar paso a
paso las cuatro instrucciones y mostrar los valores que toma el registro IP y los bits de condición
del registro CCR que se activan después de la ejecución de cada una de las cuatro
instrucciones. ¿Cuál es ahora el contenido de la dirección $400 de memoria?
6) ¿Cuál es el nombre de los registros de Entrada/Salida que se encuentran en las
direcciones de memoria $240 y $242? Modificar el contenido de estos registros para que ambas
direcciones contengan el valor $FF.
7) Modificar el contenido de la dirección de memoria $4008, cambiando el valor $20 por el
valor $22. ¿Qué ocurre?

2.7 Utilización del Hardware Real


Para finalizar el análisis del entorno CodeWarrior se utilizará la placa con el hardware real.
Disponéis en la documentación de las especificaciones técnicas del microprocesador y de la
placa (elementos que componen el microprocesador y la placa, conexiones entre los elementos,
nombres de las señales físicas y posición en el chip, direcciones de memoria asociadas a los
dispositivos, etc.) Antes de venir al laboratorio debéis dedicar 5 minutos a revisar esta
documentación por encima, de modo que tengáis una cierta idea del tipo de información del que
disponéis y de dónde ir a buscarla.
Antes de descargar el programa en el microcontrolador se debe verificar que la placa esté
conectada al ordenador a través del puerto USB y que el indicador POWER de la placa se
encienda (la placa toma su alimentación a través del puerto USB).
Cerrar la ventana “True-Time Simulator & Real-Time Debugger” con la que se había estado
simulando el programa (y que se mostró en la Figura 3). Ahora sobre la ventana principal del
proyecto Tut1.mcp, que se mostró en la Figura 2, se debe indicar al entorno CodeWarrior que se
desea utilizar el hardware real y no el modo simulación. Para ello se debe modificar la opción
que se remarca en la figura 4 para que sea SofTec_inDART-HCS12. Si ahora se selecciona la
opción Project>Debug (<F5>) aparecerá una ventana para configurar la placa de simulación. En
nuestro caso la opción HW code debe ser PK-HCS12E128.

-8-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

Figura 4. Modificar la opción indicada en la figura para usar el Hardware (en lugar de la simulación)

Tras finalizar los pasos anteriores el programa será compilado y descargado en la memoria de la
placa de experimentos. A continuación aparecerá una ventana de depuración similar a la que se
mostró en la Figura 3. La diferencia es que ahora el programa obtiene su información
directamente del hardware, a través del bus USB y usando el módulo interno BDM (Background
Debug Mode).

PREGUNTAS de LABORATORIO (resolverlas en el laboratorio y resolver las dudas con el


profesor durante la sesión): Utilizar el hardware para contestar las siguientes preguntas:
8) Contestar de nuevo a las preguntas 5), 6) y 7) del apartado anterior, pero ahora usando
el hardware real. Indicar las diferencias con el modo simulación.
9) Realizar un reset de la placa pulsando el botón de reset. ¿Qué valores se encuentran en
las direcciones de memoria $240, $400 y $4008? ¿Qué valores han cambiado y cuáles no?
Explicarlo. Realizar el mismo experimento pero desconectando la placa físicamente
(desenchufando uno de los extremos del cable USB), de modo que se interrumpa la alimentación
de forma momentánea.

3. Vectores y Entrada/Salida simple


El siguiente programa utiliza un vector de 10 elementos, una estructura de control iterativa, y
enciende y apaga LEDs (una forma muy sencilla de salida a un periférico). Se trata de
comprender el funcionamiento del programa y de CADA INSTRUCCIÓN del programa. Para ello
se ejecutará el programa paso a paso, observando con atención los cambios que produce cada
instrucción en el estado del procesador. También puede ser necesario revisar la documentación
sobre el repertorio de instrucciones del HCS12. Si alguna duda, la más mínima, queda sin
resolver, se debe preguntar al profesor de prácticas.

3.1 Análisis del Programa


Cortar el código siguiente de la versión electrónica de este documento y pegarlo en el editor de
CodeWarrior para probar el programa. Ejecutarlo de forma controlada para comprender su
funcionamiento. Contestar a las siguientes preguntas que tratan de guiar el proceso de análisis.
ABSENTRY Entry

RAMStart EQU $0400


ROMStart EQU $4000
PTT EQU $240
DDRT EQU $242

ORG RAMStart
Vector dc.b $01,$03,$07,$0F,$1F,$3F,$7F,$FF

-9-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

ORG ROMStart
Entry
LDAB #$FF
STAB DDRT
LDY #Vector
LDAA #0

Bucle0:
LDX #50000

Bucle1:
DEX
BNE Bucle1

LDAB 0,Y
STAB PTT
INY
INCA
CMPA #8
BNE Bucle0

STOP

ORG $FFFE
fdb Entry

PREGUNTAS de LABORATORIO (resolverlas en el laboratorio y resolver las dudas con el


profesor durante la sesión): Utilizar el hardware para contestar las siguientes preguntas:
10) Visualizar los valores que se encuentran inicialmente (antes de la ejecución) a partir de
la dirección de memoria $400. ¿Dónde están declarados estos datos dentro del programa?
11) En la instrucción “LDX #50000”, ¿con qué base de numeración se interpreta la constante
50000? Mirar la ventana Assembly para visualizar la codificación de la instrucción.
12) La instrucción “BNE Bucle1”, ¿qué bit de condición del registro CCR verifica para decidir
si salta o no salta? La instrucción justo anterior “DEX”, ¿para qué valores del registro X pone un
valor en el bit de condición que hace que la instrucción “BNE” salte?
13) ¿Qué efecto tiene sobre el hardware la ejecución de la instrucción “STAB PTT”?
14) Usar la documentación para buscar el nombre de las señales del microcontrolador a las
que se conectan los 8 LEDS que utiliza el programa anterior. Buscar también el número de pin
del procesador (entre el 1 y el 112) al que se conecta cada LED. Finalmente, mirar en la tabla 1-
1 (Device Register Map Overview) a qué dispositivo corresponde la dirección de memoria $240.
15) ¿Qué efecto tiene sobre el hardware la ejecución de la instrucción “STAB DDRT”? Para
poderlo deducir usando el depurador será necesario modificar la instrucción previa (LDAB #$FF)
para que cargue diferentes valores en el registro B (por ejemplo: $0F ó $00) y a partir de aquí,
ejecutar el programa de una sola vez, fijándose en cómo varía el encendido de los LEDs con el
cambio. AVISO: el efecto de la instrucción “STAB DDRT” no es inmediato, sino que sólo se
puede comprobar durante la ejecución posterior del programa. Si no se encuentra la solución en
5 minutos, no dudéis en pedir ayuda al profesor.

-10-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

3.2 Estructuras de Datos en forma de Vector o Matriz


En el apartado 1.2 se definió un vector como una variable compuesta de más de un elemento.
Los elementos del vector se almacenan contiguos en memoria (esto siempre es así, con
independencia del lenguaje que se use) de forma que se puede acceder a cada uno de los
elementos usando la dirección base del vector, que apunta al primer elemento del vector, y el
desplazamiento desde la dirección base al elemento al que se quiere acceder.
Suponiendo (tal y como se hace en lenguaje C) que el primer elemento del vector tiene el índice
0, entonces el cálculo que hay que realizar para obtener la dirección del elemento con índice i, es
el siguiente:
Dirección Elemento i-ésimo = DirBase + i * TamElemento

ƒ DirBase: es la dirección del primer elemento (elemento 0) del vector (la dirección más
pequeña). El segundo elemento sigue directamente al primero en memoria, y así
sucesivamente. DirBase se obtiene usando el nombre de la etiqueta que encabeza la
definición del vector.
ƒ TamElemento: es el tamaño en bytes de un elemento del vector (.b indica 1 bytes, .w
indica 2 bytes).
Si el vector en realidad pretende representar una estructura de dos o más dimensiones,
entonces esta estructura de datos se denomina matriz de n dimensiones. En todo caso, una
matriz siempre se representa como una secuencia de elementos, y la fórmula para acceder a los
elementos dependerá de cómo se hagan corresponder las dimensiones de la matriz en una
estructura lineal.
LABORATORIO (resolver las dudas con el profesor durante la sesión):
16) Crear un programa que declare tres vectores: V1, V2 y V3. El vector V1 contiene 10
elementos de tamaño de 1 byte, y se inicializa con los valores 1, 2, 3, 4, 5, 6, 7, 8, 9 y 10. El
vector V2 contiene 10 elementos de tamaño 2 bytes, y se inicializa con los valores $1000, $1001,
$1002, $1003, $1004, $1005, $1006, $1007, $1008 y $1009. El vector V3 contiene 10 elementos
de tamaño 2 bytes, y no se debe inicializar. El programa debe sumar los vectores V1 y V2 y dejar
el resultado en V3. Comprobar el funcionamiento correcto del programa ejecutándolo de forma
controlada: NUNCA se DEBE PROBAR un PROGRAMA ejecutándolo de golpe!

4. Entrada/Salida simple
Se debe realizar un programa que controle dos pulsadores (PAD04 y PAD05), y cuando se
detecte una pulsación encienda o apague un LED (DAO0). En concreto, el funcionamiento se
puede describir con esta tabla, o con el diagrama de estados siguiente:
Evento Acción Pulsación PAD05 Pulsación PAD04 Pulsación PAD04
Pulsación de PAD04 Encender LED DAO0 / nada / encender DAO0 / nada

Pulsación de PAD05 Apagar LED DAO0


Estado nº 1 Estado nº 2
DAO0 apagado DAO0 encendido

Pulsación PAD05
/ apagar DAO0

Figura 5. Definición del funcionamiento del programa.

-11-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

LABORATORIO (resolver las dudas con el profesor durante la sesión):


17) Usar la documentación para buscar el LED DAO0 y los pulsadores PAD04 y PAD05 en
el mapa del circuito. Buscar las señales del microcontrolador a donde se conectan estos
elementos y sus números de pin (entre el 1 y el 112). El dispositivo periférico que controla los
tres elementos se denomina PIM (Port Integration Module) y sus registros internos de
Entrada/Salida se encuentran a partir de una cierta dirección inicial de memoria: buscarla en la
tabla 1-1 (Device Register Map Overview). En la documentación del PIM (Block Guide —
S12E128PIMV1/D V01.00, que se incluye como documentación básica de referencia adjunta a
este enunciado de prácticas), en la página 15 del manual del PIM, se puede ver la tabla 3-1 que
contiene el mapa de la memoria del PIM_9E128. En este mapa se debe encontrar la dirección
del puerto M (PTM y DDRM) y del puerto AD (PTAD, DDRADH), teniendo en cuenta que estas
direcciones son relativas al inicio de los registros internos de Entrada/Salida del PIM.
Comprobarlo con la ventana Memory del Depurador.
18) Como el puerto AD es de 16 bits, cada registro asociado al puerto consta de dos bytes.
Para utilizar sólo uno de los dos bytes del registro, es necesario conocer dónde se almacena la
parte baja del registro y donde se almacena la parte alta. Hacer una prueba con la ventana
Memory del Depurador para descubrirlo.
19) A partir de la página 18 del manual del PIM se puede encontrar una descripción de los
registros de Entrada/Salida del PIM, la posición de cada campo de bits dentro del registro, y el
significado de cada campo. Con esta información, y usando como base el programa que se
muestra a continuación, implementar el programa que se pide en esta sección. Construir la
implementación en varias etapas (diseño, implementación, verificación). En todos los casos de
verificación el programa se debe ejecutar de forma controlada: NUNCA se DEBE PROBAR un
PROGRAMA ejecutándolo de golpe!
ABSENTRY Entry
RAMStart EQU $0400
ROMStart EQU $4000

PTM EQU ?? ; Puerto M


DDRM EQU ?? ; Configuración del puerto M
ATDDIEN1 EQU $008D ; Habilita entrada en Puerto AD(7-0)
PTADLo EQU ?? ; Puerto AD (bits 7-0)
DDRADLo EQU ?? ; Configuración del puerto AD (bits 7-0)
PERADLo EQU ?? ; Habilita Pull-Device de Puerto AD

ORG RAMStart
ORG ROMStart
Entry:
LDAB #$FF
STAB DDRM ; configura puerto M de salida
STAB PERADLo ; Habilita alimentación entrada a PADLo
STAB ATDDIEN1 ; Habilita Puerto AD como entrada
LDAB #0
STAB DDRADLo ; Configura PTAD (bits 7-0) de entrada

LOOP:
BSR testPDA04 ; Mira si pulsador4 pulsado
BEQ OUTPUT ; Salta si hay cambio en el pulsador
BRA LOOP

-12-
Tutorial de Programación Básica en Ensamblador del Microprocesador HCS12

OUTPUT:
LDAB #??
STAB PTM ; Enciende el LED DAO0
BRA LOOP

testPDA04: ; SUBRUTINA
LDAA PTADLo
ANDA #$10 ; Testea el bit 4 de PAD04 (0= activo)
RTS ; Retorna de subrutina

-13-
Interrupciones con HC12 Estructura de computadores

Introducción
La sincronización con los periféricos puede realizarse mediante 2 técnicas diferentes: el polling y las
interrupciones. En la primera práctica hemos utilizado la primera técnica, el polling, también llamado
“encuesta”, que consistía en muestrear periódicamente los periféricos de entrada (en nuestro caso los
pulsadores). Esta técnica tiene varios inconvenientes. El primero, ya observado en la práctica anterior,
consiste en que los pulsadores no funcionan con precisión. Es decir, las pulsaciones no son siempre
detectadas por el programa, o por el contrario se interpretan varias veces. Aunque este problema podía
haberse solucionado realizando un programa que observara los flancos de los pulsadores, la manera
correcta es utilizar interrupciones. El segundo inconveniente al utilizar la técnica del polling consiste
en que el programa principal desconoce cuando va a producirse un cambio de estado en los pulsadores.
Ese desconocimiento nos obliga a realizar programas poco eficientes que dedican tiempo a preguntar a
los periféricos si han cambiado de estado. Por todo esto, parece más lógico que sea el dispositivo quien
avise al programa en caso de cambio de estado. Este mecanismo es el conocidísimo sistema de
interrupciones.

¿Cómo funciona un sistema de interrupciones?


Básicamente, un sistema de interrupciones funciona de igual manera en cualquier microprocesador.
Podemos encontrar pequeñas diferencias en cuanto al sistema de prioridades y también respecto a
cuáles son las acciones que realiza el sistema y cuáles el usuario (programador). En el Espai Virtual
podeis encontrar la bibliografía necesaria para comprender el sistema de interrupciones del HC12. Leer
con atención la sección 7 del “HC12 Reference Manual”, y la sección 5 del “Mc9S12E-Family Device
User Guide V01.04”.

La sincronización con los periféricos mediante interrupciones es un mecanismo, soportado por los
procesadores, que permite “programar” la ejecución de un código (la Rutina de Servicio a la
Interrupción o RSI) cuando se produce un acontecimiento en el periférico (por ejemplo, se presiona un
pulsador, un timer llega a 0, etc). Veamos, paso a paso, los mecanismos necesarios para poner en
práctica un sistema de interrupciones.

1.- Los vectores de interrupción.


Para asociar una rutina a la interrupción de un dispositivo periférico disponemos de la tabla de
vectores de interrupción. Esta tabla contiene la dirección de inicio de cada una de las rutinas asociadas
con los diferentes periféricos. Por lo tanto, si queremos escribir una rutina para dar servicio en caso de
que un pulsador sea presionado, no debemos olvidar poner la dirección de inicio de esa rutina en el
vector correspondiente (el de los pulsadores). En el caso del HC12 la tabla de vectores se encuentra
alojada en el espacio de memoria que va desde $FF80 a $FFFF. Cada vector ocupa 2 bytes (una
dirección). Sólo hay que conocer el vector que corresponde a cada periférico (ver tabla 5-1) del
“Mc9S12E-Family Device User Guide V01.04”.

1
Interrupciones con HC12 Estructura de computadores

2.- Tipos de interrupciones.


En cualquier procesador siempre existen diferentes tipos de interrupciones. Normalmente, y es el caso
del HC12, hay 2 tipos bien diferenciados: las enmascarables y las no enmascarables. Las primeras,
las que nosotros vamos a programar, pueden inhabilitarse mediante un flag en el registro de estado del
procesador (el I-bit en el caso del HC12). Esto es, que desactivando ese flag prohibimos que se atienda
ninguna interrupción aunque algún dispositivo periférico así lo requiera. El segundo tipo, las no
enmascarables, agrupa aquellas interrupciones de sistema que no pueden ser desatendidas bajo ningún
concepto. Un ejemplo es el pulsador de reset de la placa.

Dentro del grupo de las enmascarables podemos distinguir 2 tipos de interrupciones, las internas y las
externas. Las internas son originadas por dispositivos que residen dentro del procesador y que no
requieren de interacción con el usuario (timers y puertos serie). Las externas son originadas por
dispositivos externos al procesador. Estos dispositivos utilizan la línea de Interrupt Request (IRQ) para
llamar la atención del procesador.

3.- Protocolo de sucesos y acciones desde que se reconoce una interrupción hasta
que finaliza la RSI asociada.
Existen varios requisitos para que una petición de interrupción sea atendida correctamente:

1. El flag I-bit del registro de estado debe estar habilitado. Este flag habilita/inhibe todas las
interrupciones.
2. El bit de enable del registro de control del periférico debe estar habilitado. Este bit habilita/inhibe
las interrupciones del periférico en cuestión.
3. El vector de interrupción que da servicio al periférico debe estar bien inicializado.

Si todos los requisitos anteriores se dan a la vez, una petición de interrupción será reconocida de
inmediato si no hay otra en curso. A continuación se detallan los sucesos (los ejecuta el sistema) y
acciones (las ejecuta el usuario) que se producen una vez reconocida una interrupción:

1. (Suceso) Se salva la dirección de retorno. Es decir, la dirección de la siguiente instrucción a


ejecutar cuando finalice la RSI.
2. (Suceso) Se salva el estado del procesador en la pila. Es decir, el estado de los registros X,Y,D,
y CCR. De no hacerlo el sistema lo debería hacer el programador. Hay que tener en cuenta que una
interrupción se produce en el momento menos esperado. Seguramente, dentro de la RSI
utilizaremos registros del procesador que a la vez estaba utilizando el programa interrumpido. Hay
que ser muy cuidadoso y dejar esos registros como estaban antes de finalizar la RSI.
3. (Suceso) Se inhabilita el I-bit para que ninguna otra interrupción interrumpa a la actual.
4. (Suceso) Se hace apuntar el PC (contador de programas) a la dirección que hay en el vector de
interrupción correspondiente.
5. (Acción dentro RSI) Se limpia el flag del dispositivo que ha provocado la interrupción. Todos
los dispositivos llevan asociado un flag que se activa cuando producen interrupción. Este flag hay
que borrarlo antes de finalizar la RSI, porqué de lo contrario o no se producirá ninguna otra
interrupción de ese tipo, en el caso de Intel, o se repetirá la misma interrupción en el caso del
HC12.
2
Interrupciones con HC12 Estructura de computadores

… Código de la RSI …

6. (Acción dentro RSI) Ejecutar la instrucción RTI. Esta instrucción se utiliza para salir de la RSI
y se diferencia de RTS en que debe rescatar de la pila más información.
7. (Suceso derivado de RTI) Se habilita el I-bit para permitir nuevas interrupciones.
8. (Suceso derivado de RTI) Se restaura el estado del procesador. Es decir, se rescata de la pila
los valores de X, Y, D y CCR anteriores a la interrupción.
9. (Suceso derivado de RTI) Se restaura en el PC la dirección de retorno, que se extrae de la pila.
10. (Acción fuera RSI) Desinstalar vector de interrupción antes de acabar el programa.

La relación de sucesos y acciones que se han descrito son necesarios en todos los procesadores. Lo que
puede suceder es que en algunos casos haya acciones que deba ejecutarlas el usuario ya que el sistema
no lo hace.

4.- ¿Cómo se depura una interrupción?


Lo primero que hay que aprender es cómo se depuran las rutinas de interrupción. Hay que tener en
cuenta que la RSI se ejecuta fuera del control de nuestro programa principal y por lo tanto es muy
difícil de depurar. El procedimiento de depuración es el siguiente:

1.- Poner un breakpoint dentro de la RSI (en la 1ª instrucción, p.e.).


2.- Ejecutar RUN.
3.- Provocar la interrupción (apretar un botón si es el caso). Si todo está bien programado (siguiendo
los tres pasos descritos al principio del apartado 3) entraremos en la RSI y pararemos en el breakpoint.
¡Enhorabuena!
4.- Ejecutar paso a paso dentro de la RSI para depurar el código.
5.- Antes del RTI ejecutar RUN de nuevo. El comportamiento del depurador al intentar ejecutar paso a
paso la instrucción RTI es impredecible. Se debe tener en cuenta que la ejecución paso a paso también
implica la generación de interrupciones internas asociadas al depurador.

3
Interrupciones con HC12 Estructura de computadores

Tutorial 2: Interrupciones con HC12


El tutorial que ahora se presenta puede realizarse en una única sesión. A lo más tardar es obligatoria su
resolución y entrega antes de finalizar la 8ª sesión (última sesión del 1er cuatrimestre). Es obligatorio
entregar el pre-laboratorio antes del inicio de la sesión. Su falta implica suspender las prácticas.

SESIÓN 1

Pre-laboratorio:
1. El alumno leerá con atención la sección 7 del “HC12 Reference Manual” y la sección 5 del
“Mc9S12E-Family Device User Guide V01.04”, además del capítulo introductorio que viene con
este tutorial.
2. Repasar el significado de las instrucciones BSET, BCLR, BRSET y BRCLR. Si existe alguna duda
preguntar al profesor.
3. El alumno entregará al profesor, al inicio de la sesión, un documento escrito con la respuesta a las
siguientes preguntas:

• ¿En qué registro se encuentra alojado el I-Bit y para qué sirve? ¿Qué lógica utiliza?
• ¿Es necesario ejecutar CLI en el programa para permitir las interrupciones? Razona tu
respuesta
• ¿Es necesario tener habilitado el I-Bit para que funcione el botón de reset de la placa y la
instrucción SWI? Razona tu respuesta.
• Cuándo una instrucción está en medio de su ejecución y se produce una interrupción, ¿Qué
sucede? ¿Se interrumpe esa instrucción o se deja acabar? Razona tu respuesta.

Laboratorio:
En esta sesión se realizan las acciones indicadas por el tutorial y se contestan las preguntas que en él se
formulan, siendo obligatorio la entrega de dichas respuestas. Para ello se partirá de un programa
ejemplo (ver Ejemplo 1), un programa que contabiliza las veces que se presiona PAD04, a partir del
cual se irá aprendiendo los entresijos de las interrupciones. Este programa configura de entrada el
puerto AD, habilita las interrupciones, y espera a que se pulse 5 veces PAD04 para finalizar el
programa. El programa principal es un bucle de 3 instrucciones (LDAA, CMP, BPL) que analiza
permanentemente el estado de la variable contador. La RSI incrementa esa variable cada vez que se
pulsa PAD04. Esta es la manera de sincronizar un programa principal con una RSI, a través de
variables globales.

4
Interrupciones con HC12 Estructura de computadores

A) Crear un proyecto con el programa que se os da como ejemplo nº 1. Poner un breakpoint en la RSI
(instrucción marcada en rojo). Ejecutar RUN. Apretar el pulsador PAD04. ¿Ha entrado en la RSI? No.
¿Por qué? ¿Por qué no funciona el botón? NO es el caso. Comprobar usando el depurador cómo el flag
se ha activado después de apretar el pulsador (ver dirección 27F). Lo que sucede es que alguno de los 3
requisitos vistos en el protocolo de sucesos y acciones no se ha realizado correctamente. Repásalos y
con ayuda del debugger encuentra los errores. Modifica el programa con la solución a los errores.

B) Ahora que la RSI ya se ejecuta, se ha parado la ejecución en el breakpoint de la RSI, ¿qué valores
han sido escritos en la pila como resultado de atender la interrupción? Responder a esta pregunta
utilizando una tabla donde aparezcan 3 columnas: la dirección de la pila, el contenido que allí se ha
almacenado (dirección de retorno, X, Y, D o CCR), y el valor que encontramos. Si ejecutamos varias
veces esta acción (resetear programa y empezar de nuevo en cada acción), ¿La dirección de retorno es
siempre la misma? Razona tu respuesta.

C) Nos encontramos parados en el breakpoint de la RSI. Ejecutemos RUN. ¿Qué sucede? ¿Vuelve a
entrar en la RSI? ¿Por qué? No hemos pulsado PAD04, ¿no? Razonar la respuesta y añade la
instrucción que falta para que eso no suceda.

D) Ahora que la RSI funciona correctamente y sólo se ejecuta una vez por pulsación de PAD04, ¿Qué
estado tiene el I-Bit antes de entrar en la RSI? ¿Qué estado tiene cuando nos encontramos dentro de la
RSI? ¿Qué estado tiene al salir de la RSI y volver al programa principal? Para contestar estas
preguntas habrá que añadir otro breakpoint en el bucle principal (p.e en BPL). ¿Coinciden los
resultados con lo visto en el protocolo de sucesos y acciones?

E) Comprobemos si el programa funciona correctamente. Manteniendo los 2 breakpoints, ejecutar


RUN y presionar PAD04 combinadamente para comprobar cómo la variable “Contador_PAD04” se va
incrementando. ¿Lo veis? ¿No? Poner el ‘mode’ en ‘periodical’. Comprobar también como el programa
acaba al llegar a 5.

Añadir en el programa la funcionalidad necesaria para tratar el pulsador PAD05, de manera que este
nuevo pulsador decremente la variable “Contador_PAD04”.

Memoria:
El alumno deberá entregar una memoria donde se recojan las soluciones a las preguntas realizadas,
tanto en el pre-laboratorio como en el tutorial, una vez adquirido el conocimiento resultado de la
sesión.

5
Interrupciones con HC12 Estructura de computadores

XDEF Entry ; export 'Entry' symbol


ABSENTRY Entry ; for absolute assembly: mark this as application entry point

RAMStart EQU $0400 ; absolute address to place my variables


ROMStart EQU $4000 ; absolute address to place my code/constant data
ATDDIEN1 EQU $008D ; Habilita entrada en el puerto AD (parte baja)
PTADLo EQU $0271 ; Puerto AD (parte baja)
DDRADLo EQU $0275 ; Define la dirección del puerto AD (entrada/salida)
PERADLo EQU $0279 ; Define alimentación de entrada
PIEADLo EQU $027D ; Registro de Enable del Puerto AD
PIFADLo EQU $027F ; Registro de flags del Puerto AD
PAD04 EQU %00010000 ; Máscara pulsador 4

; variable/data section
ORG RAMStart
; Insert here your data definition.
Contador_PAD04 dc.b 0

; code section
ORG ROMStart
CLI ;Habilitar todas las interrupciones enmascarables
Entry:
LDS #$1FFF

;Configurar el puerto AD (parte baja) de entrada


BSET PERADLo,#$FF
BSET ATDDIEN1,#$FF
BCLR DDRADLo,#$FF

;Limpiar posible flag de interrupción PADLo anterior


BSET PIFADLo,PAD04

;Habilitar interrupciones PAD04


BSET PIEADLo,PAD04

espera_infinita:
LDAA #$5
CMPA Contador_PAD04
BPL espera_infinita

;Se ha pulsado 5 veces PAD04. Acabamos el programa


SWI

RSI_PAD04:
;Código de la RSI
INC Contador_PAD04
RTI

;**************************************************************
;* Interrupt Vectors *
;**************************************************************
ORG $FFFE
fdb Entry ; Reset
ORG $FFD0
fdb RSI_PAD04 ; PAD04

Ejemplo nº1

6
Interrupciones con HC12 Estructura de computadores

Gestión de interrupciones con HC12


Estructura de computadores

Introducción
La sincronización con los periféricos puede realizarse mediante 2 técnicas diferentes: el polling y las
interrupciones. En la primera práctica hemos utilizado la primera técnica, el polling, también llamado
“encuesta”, que consistía en muestrear periódicamente los periféricos de entrada (en nuestro caso los
pulsadores). Esta técnica tiene varios inconvenientes. El primero, ya observado en la práctica anterior,
consiste en que los pulsadores no funcionan con precisión. Es decir, las pulsaciones no son siempre
detectadas por el programa, o por el contrario se interpretan varias veces. Aunque este problema podía
haberse solucionado realizando un programa que observara los flancos de los pulsadores, la manera
correcta es utilizar interrupciones. El segundo inconveniente al utilizar la técnica del polling consiste
en que el programa principal desconoce cuando va a producirse un cambio de estado en los pulsadores.
Ese desconocimiento nos obliga a realizar programas poco eficientes que dedican tiempo a preguntar a
los periféricos si han cambiado de estado. Por todo esto, parece más lógico que sea el dispositivo quien
avise al programa en caso de cambio de estado. Este mecanismo es el conocidísimo sistema de
interrupciones.

¿Cómo funciona un sistema de interrupciones?


Un sistema de interrupciones funciona básicamente de igual manera en cualquier microprocesador.
Podemos encontrar pequeñas diferencias en cuanto al sistema de prioridades y también respecto a
cuáles son las acciones que realiza el sistema y cuáles el usuario (programador). En el Espai Virtual
podéis encontrar la bibliografía necesaria para comprender el sistema de interrupciones del HC12. Leer
con atención la sección 7 del “HC12 Reference Manual”, y la sección 5 del “Mc9S12E-Family Device
User Guide V01.04”.

La sincronización con los periféricos mediante interrupciones es un mecanismo, soportado por los
procesadores, que permite “programar” la ejecución de un código (la Rutina de Servicio a la
Interrupción o RSI) cuando se produce un acontecimiento en el periférico (por ejemplo, se presiona un
pulsador, un timer llega a 0, etc). Veamos, paso a paso, los mecanismos necesarios para poner en
práctica un sistema de interrupciones.

1.- Los vectores de interrupción.


Para asociar una rutina a la interrupción de un dispositivo periférico disponemos de la tabla de
vectores de interrupción. Esta tabla contiene la dirección de inicio de cada una de las rutinas asociadas
con los diferentes periféricos. Por lo tanto, si queremos escribir una rutina para dar servicio en caso de
que un pulsador sea presionado, no debemos olvidar poner la dirección de inicio de esa rutina en el
vector correspondiente (el de los pulsadores). En el caso del HC12 la tabla de vectores se encuentra
1
Interrupciones con HC12 Estructura de computadores

alojada en el espacio de memoria que va desde $FF80 a $FFFF. Cada vector ocupa 2 bytes (una
dirección). Sólo hay que conocer el vector que corresponde a cada periférico (ver tabla 5-1) del
“Mc9S12E-Family Device User Guide V01.04”.

2.- Tipos de interrupciones.


En cualquier procesador siempre existen diferentes tipos de interrupciones. Normalmente, y es el caso
del HC12, hay 2 tipos bien diferenciados: las enmascarables y las no enmascarables. Las primeras,
las que nosotros vamos a programar, pueden inhabilitarse mediante un flag en el registro de estado del
procesador (el I-bit en el caso del HC12). Esto es, que desactivando ese flag prohibimos que se atienda
ninguna interrupción aunque algún dispositivo periférico así lo requiera. El segundo tipo, las no
enmascarables, agrupa aquellas interrupciones de sistema que no pueden ser desatendidas bajo ningún
concepto. Un ejemplo es el pulsador de reset de la placa.

Dentro del grupo de las interrupciones enmascarables del HC12 podemos distinguir 2 tipos de
interrupciones, las internas y las externas. Las internas son originadas por dispositivos que residen
dentro del procesador y que no requieren de interacción con el usuario (timers y puertos serie). Las
externas son originadas por dispositivos externos al procesador. Estos dispositivos utilizan la línea de
Interrupt Request (IRQ) para llamar la atención del procesador.

3.- Protocolo de sucesos y acciones desde que se reconoce una interrupción hasta
que finaliza la RSI asociada.
Existen varios requisitos para que una petición de interrupción sea atendida correctamente:

1. El flag I-bit del registro de estado debe estar habilitado. Este flag habilita/inhibe todas las
interrupciones.
2. El bit de enable del registro de control del periférico debe estar habilitado. Este bit habilita/inhibe
las interrupciones del periférico en cuestión.
3. El vector de interrupción que da servicio al periférico debe estar bien inicializado.

Si todos los requisitos anteriores se dan a la vez, una petición de interrupción será reconocida de
inmediato si no hay otra en curso. A continuación se detallan los sucesos (los ejecuta el sistema) y
acciones (las ejecuta el usuario) que se producen una vez reconocida una interrupción:

1. (Suceso) Se salva la dirección de retorno. Es decir, la dirección de la siguiente instrucción a


ejecutar cuando finalice la RSI.
2. (Suceso) Se salva el estado del procesador en la pila. Es decir, el estado de los registros X,Y,D,
y CCR. De no hacerlo el sistema lo debería hacer el programador. Hay que tener en cuenta que una
interrupción se produce en el momento menos esperado. Seguramente, dentro de la RSI
utilizaremos registros del procesador que a la vez estaba utilizando el programa interrumpido.
Antes de finalizar la RSI, se debe dejar esos registros como estaban al principio.
3. (Suceso) Se inhabilita el I-bit para que ninguna otra interrupción interrumpa a la actual.
4. (Suceso) Se hace apuntar el PC (contador de programas) a la dirección que hay en el vector de
interrupción correspondiente.
5. (Acción dentro RSI) Se limpia el flag del dispositivo que ha provocado la interrupción. Todos
2
Interrupciones con HC12 Estructura de computadores

los dispositivos llevan asociado un flag que se activa cuando producen interrupción. Este flag hay
que borrarlo antes de finalizar la RSI, porqué de lo contrario o no se producirá ninguna otra
interrupción de ese tipo, en el caso de Intel, o se repetirá la misma interrupción en el caso del
HC12.
… Código de la RSI …

6. (Acción dentro RSI) Ejecutar la instrucción RTI. Esta instrucción se utiliza para salir de la RSI
y se diferencia de RTS en que debe rescatar de la pila más información.
7. (Suceso derivado de RTI) Se habilita el I-bit para permitir nuevas interrupciones.
8. (Suceso derivado de RTI) Se restaura el estado del procesador. Es decir, se rescata de la pila
los valores de X, Y, D y CCR anteriores a la interrupción.
9. (Suceso derivado de RTI) Se restaura en el PC la dirección de retorno, que se extrae de la pila.
10. (Acción fuera RSI) Desinstalar vector de interrupción antes de acabar el programa.

La relación de sucesos y acciones que se han descrito son necesarios en todos los procesadores. Lo que
puede suceder es que en algunos casos haya acciones que deba ejecutarlas el usuario ya que el sistema
no lo hace, como por ejemplo, salvar y recuperar el estado del procesador.

4.- ¿Cómo se depura una interrupción?


Lo primero que hay que aprender es cómo se depuran las rutinas de interrupción. Hay que tener en
cuenta que la RSI se ejecuta fuera del control de nuestro programa principal y por lo tanto requiere una
técnica específica para depurar. El procedimiento de depuración es el siguiente:

1.- Poner un breakpoint dentro de la RSI (en la 1ª instrucción, p.e.).


2.- Ejecutar RUN.
3.- Provocar la interrupción (apretar un botón si es el caso). Si todo está bien programado entraremos
en la RSI y pararemos en el breakpoint.
4.- Ejecutar paso a paso dentro de la RSI.
5.- Antes del RTI ejecutar RUN.

3
Interrupciones con HC12 Estructura de computadores

Tutorial 2: Interrupciones con HC12


El tutorial que ahora se presenta puede realizarse en una única sesión. A lo más tardar es obligatoria su
resolución y entrega antes de finalizar la 8ª sesión (última sesión del 1er cuatrimestre). Es obligatorio
entregar el pre-laboratorio antes del inicio de la sesión. Su falta implica suspender las prácticas.

SESIÓN 1

Pre-laboratorio:
1. El alumno leerá con atención la sección 7 del “HC12 Reference Manual” y la sección 5 del
“Mc9S12E-Family Device User Guide V01.04”, además del capítulo introductorio que viene con
este tutorial.
2. Repasar el significado de las instrucciones BSET, BCLR, BRSET y BRCLR. Si existe alguna duda
preguntar al profesor.
3. El alumno entregará al profesor, al inicio de la sesión, un documento escrito con la respuesta a las
siguientes preguntas:

• ¿En qué registro se encuentra alojado el I-Bit y para qué sirve? ¿Qué lógica utiliza?
• ¿Es necesario ejecutar CLI en el programa para permitir las interrupciones? Razona tu
respuesta
• ¿Es necesario tener habilitado el I-Bit para que funcione el botón de reset de la placa y la
instrucción SWI? Razona tu respuesta.
• Cuándo una instrucción está en medio de su ejecución y se produce una interrupción, ¿Qué
sucede? ¿Se aborta la ejecución de esa instrucción o se deja acabar? Razona tu respuesta.

Laboratorio:
En esta sesión se realizan las acciones indicadas por el tutorial y se contestan las preguntas que en él se
formulan, siendo obligatorio la entrega de dichas respuestas. Para ello se partirá de un programa
ejemplo (ver Ejemplo 1), un programa que contabiliza las veces que se presiona PAD04, a partir del
cual se irá aprendiendo los entresijos de las interrupciones. Este programa configura de entrada el
puerto AD, habilita las interrupciones, y espera a que se pulse 5 veces PAD04 para finalizar el
programa. El programa principal es un bucle de 3 instrucciones (LDAA, CMP, BPL) que analiza
permanentemente el estado de la variable contador. La RSI incrementa esa variable cada vez que se
pulsa PAD04. Esta es la manera de sincronizar un programa principal con una RSI, a través de
variables globales.

4
Interrupciones con HC12 Estructura de computadores

A) Crear un proyecto con el programa que se os da como ejemplo nº1. Poner un breakpoint en la RSI
(instrucción marcada en rojo). Ejecutar RUN. Apretar el pulsador PAD04. ¿Ha entrado en la RSI? No.
¿Por qué? ¿Porqué no funciona el botón? NO es el caso. Comprobar como el flag se activa después de
apretar el pulsador (ver dirección 27F). Lo que sucede es que alguno de los 3 requisitos vistos en el
protocolo de sucesos y acciones no se ha realizado correctamente. Repásalos y con ayuda del debugger
encuentra los errores. Modifica el programa con la solución a los errores.

B) Ahora que la RSI ya se ejecuta, se ha parado la ejecución en el breakpoint de la RSI, ¿qué valores
han sido escritos en la pila como resultado de atender la interrupción? Responder a esta pregunta
utilizando una tabla donde aparezcan 3 columnas: la dirección de la pila, el contenido que allí se ha
almacenado (dirección de retorno, X, Y, D o CCR), y el valor que encontramos. Si ejecutamos varias
veces esta acción (resetear programa y empezar de nuevo en cada acción), ¿La dirección de retorno es
siempre la misma? Razona tu respuesta.

C) Nos encontramos parados en el breakpoint de la RSI. Ejecutemos RUN. ¿Qué sucede? ¿Vuelve a
entrar en la RSI? ¿Porqué? No hemos pulsado PAD04, ¿no? Razonar la respuesta y añade la
instrucción que falta para que eso no suceda.

D) Ahora que la RSI funciona correctamente y sólo se ejecuta una vez por pulsación de PAD04, ¿Qué
estado tiene el I-Bit antes de entrar en la RSI? ¿Qué estado tiene cuando nos encontramos dentro de la
RSI? ¿Qué estado tiene al salir de la RSI y volver al programa principal? Para contestar estas
preguntas habrá que añadir otro breakpoint en el bucle principal (p.e en BPL). ¿Coinciden los
resultados con lo visto en el protocolo de sucesos y acciones?

E) Comprobemos si el programa funciona correctamente. Manteniendo los 2 breakpoints, ejecutar


RUN y presionar PAD04 combinadamente para comprobar cómo la variable “Contador_PAD04” se va
incrementando. ¿Lo veis? ¿No? Poner el ‘mode en periodical’. Comprobar también como el programa
acaba al llegar a 5.

Añadir en el programa la funcionalidad necesaria para tratar el pulsador PAD05, de manera que este
nuevo pulsador decremente la variable “Contador_PAD04”.

Memoria:
El alumno deberá entregar una memoria donde se recojan las soluciones a las preguntas realizadas,
tanto en el pre-laboratorio como en el tutorial, una vez adquirido el conocimiento resultado de la
sesión.

5
Interrupciones con HC12 Estructura de computadores

XDEF Entry ; export 'Entry' symbol


ABSENTRY Entry ; for absolute assembly: mark this as application entry point

RAMStart EQU $0400 ; absolute address to place my variables


ROMStart EQU $4000 ; absolute address to place my code/constant data
ATDDIEN1 EQU $008D ; Habilita entrada en el puerto AD (parte baja)
PTADLo EQU $0271 ; Puerto AD (parte baja)
DDRADLo EQU $0275 ; Define la dirección del puerto AD (entrada/salida)
PERADLo EQU $0279 ; Define alimentación de entrada
PIEADLo EQU $027D ; Registro de Enable del Puerto AD
PIFADLo EQU $027F ; Registro de flags del Puerto AD
PAD04 EQU %00010000 ; Máscara pulsador 4

; variable/data section
ORG RAMStart
; Insert here your data definition. For demonstration, temp_byte is used.
Contador_PAD04 dc.b 0

; code section
ORG ROMStart
CLI ;Habilitar todas las interrupciones enmascarables
Entry:
LDS #$1FFF

;Configurar el puerto AD (parte baja) de entrada


BSET PERADLo,#$FF
BSET ATDDIEN1,#$FF
BCLR DDRADLo,#$FF

;Limpiar posible flag de interrupción PADLo anterior


BSET PIFADLo,PAD04

;Habilitar interrupciones PAD04


BSET PIEADLo,PAD04

espera_infinita:
LDAA #$5
CMPA Contador_PAD04
BPL espera_infinita

;Se ha pulsado 5 veces PAD04. Acabamos el programa


SWI

RSI_PAD04:
;Código de la RSI
INC Contador_PAD04
RTI

;**************************************************************
;* Interrupt Vectors *
;**************************************************************
ORG $FFFE
fdb Entry ; Reset
ORG $FFD0
fdb RSI_PAD04 ; PAD04
Ejemplo nº1

6
Tutorial de Programación en C con el Microprocesador HCS12

TUTORIAL de Programación en Lenguaje C con el


Microprocesador HCS12
Objetivos:
ƒ Editar un programa en Lenguaje C, ensamblarlo, cargarlo en el procesador, y ejecutarlo
paso a paso, visualizando los cambios que produce cada instrucción en los registros del
procesador y en la memoria.
ƒ Aprender a intercalar instrucciones en ensamblador dentro de un programa en lenguaje
C.

Material:
ƒ Computador Personal (MS-Windows) con un puerto USB disponible y cable USB
ƒ Metrowerks CodeWarrior HC(S)12 (http://www.codewarrior.com/MW/download)
ƒ SofTec Microsystems PK-HCS12E128 “System Software” (Campus Virtual)

1. Programas en C
Un programa es un fichero de texto escrito según la sintaxis y la semántica del lenguaje C. Se
puede crear un programa a partir de múltiples ficheros, aunque este tema queda fuera de los
objetivos de este tutorial. Se supone conocida tanto la sintaxis como la semántica del lenguaje C
(al menos de las instrucciones más comunes).
El siguiente código ejemplo en lenguaje C servirá de base para este tutorial. En él se incluyen
dentro del código instrucciones en ensamblador que utilizan variables definidas en C (la variable
e). También se utilizan las variables predefinidas DDRT y PTT (que se encuentran definidas en
el fichero mc9s12e128.h).
#include <hidef.h> /* common defines and macros */
#include <mc9s12e128.h> /* derivative information */

#pragma LINK_INFO DERIVATIVE "SampleS12"

int a=1000, b=2000, c;

void main(void) {
char e;

EnableInterrupts;
c = a+b;
e = c;
asm {
LDAA 0xFF ;
STAA 0x242
LDAA e ;
STAA 0x240 ;
}
DDRT = 0xff;
PTT = e;
for(;;) {} /* wait forever */
}

-1-
Tutorial de Programación en C con el Microprocesador HCS12

PREGUNTAS de PRE-LABORATORIO (deben traerse contestadas al laboratorio y entregarlas


al profesor al comenzar la sesión): Analizar el anterior programa ejemplo y contestar.
1) ¿Qué valores tomarán las variables c y e tras ejecutar el programa?

2. Entorno de desarrollo CodeWarrior


El entorno de desarrollo CodeWarrior IDE permite llevar a cabo los pasos necesarios para la
creación y ejecución de nuestros programas. El uso básico de este entorno está descrito en el
“Tutorial de programación básica en ensamblador del microprocesador HCS12” y se remite ha
este documento. En este tutorial sólo se presentarán aquellas características propias del uso del
lenguaje C.
2.1 Iniciando CodeWarrior y Creando un Proyecto en C
El programa se encuentra en el escritorio o en Inicio>Programas>Metrowerks CodeWarrior> ….
Suponiendo que CodeWarrior IDE ya se encuentra activo, iniciar un proyecto nuevo siguiendo
las siguientes instrucciones.
ƒ Seleccionar File>New.
ƒ En la ventana que muestra el paso anterior, seleccionar “HC(S)12 New Project Wizard” y
el directorio donde serán creado los múltiples ficheros de los que consta el proyecto
(incluso un programa muy simple genera múltiples ficheros). En la opción “Location>Set”
elija la carpeta y escriba el nombre del fichero del proyecto, por ejemplo Tut1. Presionar
el botón Aceptar.
ƒ Aparecerán una serie de 7 ventanas, etiquetadas como “New Project Wizard - Page K”
en las que hay que seleccionar una serie de parámetros:
1. seleccionar el dispositivo “MC9S12E128”
2. seleccionar sólo la opción “C” (desmarque todas las otras)
3. indicar que NO se quiere usar Processor Expert
4. indicar que NO se quiere usar PC-lint
5. indicar que NO se quiere usar floating-point support
6. elegir “small memory model” el código y los datos deben ocupar un máximo de
64 KB.
7. seleccionar las opciones “Metrowerks Full Chip Simulator” y “inDART-HCS12
Hardware Debugging”
ƒ Aparecerá una nueva ventana. Se debe abrir la carpeta “Source” y pulsar dos veces en
el fichero “main.c”. De este modo aparecerá la ventana de la Figura 1, que muestra el
contenido del fichero que contiene el programa.
El programa NO es el mismo que se presentó en la sección anterior, y se debe modificar para
que COINCIDA EXACTAMENTE con el que hay en la página 1 de este tutorial (lo más sencillo
es cortar de la versión electrónica y pegar sobre la ventana del código).

-2-
Tutorial de Programación en C con el Microprocesador HCS12

Figura 1. Entorno de Edición. Programa ejemplo que aparece por defecto: SE DEBE SUSTITUIR POR EL
CÓDIGO de la página 1.
2.2 Compilando y Depurando el programa
Para compilar y activar el depurador utilizamos directamente la opción Project>Debug. En caso
de errores aparecerá una ventana con mensajes que nos ayudarán a solucionar los problemas.
Comprobar que habéis copiado correctamente el código, y en caso de no saber continuar
preguntar al profesor. Por defecto, el entorno arranca con la opción de la simulación activada, y
de este modo no es necesario disponer de la placa hardware, o se puede tener apagada. Tras
seguir todos los pasos, la ventana del depurador (True-Time Simulator & Real-Time Debugger)
permitirá analizar el código, su funcionamiento, y el estado del procesador. Ampliar al máximo
esta ventana para disponer del máximo espacio para visualizar los datos. En ella se encuentran
ocho sub-ventanas donde podemos obtener mucha de la información necesaria para analizar y
depurar nuestro programa. Las ventanas Assembly, Register y Memory están descritas en el
“Tutorial de Ensamblador”. En este tutorial analizaremos la ventana:
ƒ Source: permite visualizar el programa C original y controlar la ejecución de
instrucciones en lenguaje C. Pulsando con el botón derecho del ratón sobre esta
ventana aparecerá un menú de contexto que nos permite variar la información
visualizada.
Los programas en lenguaje C necesitan ejecutar un código de inicialización (startup) antes de
ceder el control a la función principal de nuestro programa (función main). Hasta que no se
ejecuta este código, el depurador no es capaz de conocer cierta información. En la ventana
Source poner un punto de parada sobre la línea que contiene la instrucción “EnableInterrupts”
(→ver Tutorial de Ensamblador para recordar cómo hacerlo). A continuación invocar la ejecución
continua de las instrucciones del programa usando el menú Run, o la tecla <F11>. La ejecución
se detendrá al inicio de la instrucción “EnableInterrupts” y las dos ventanas inferiores de la

-3-
Tutorial de Programación en C con el Microprocesador HCS12

izquierda se actualizarán con la información adecuada. La figura 2 muestra el estado de la


ventana del depurador una vez realizados los pasos anteriores.

ƒ Data:1: permite visualizar y modificar las variables GLOBALES.


ƒ Data:2: permite visualizar y modificar las variables LOCALES.

Figura 2. Entorno de Depuración: se está simulando el procesador HCS12.

PREGUNTAS de LABORATORIO (resolverlas en el laboratorio y resolver las dudas con el


profesor durante la sesión): Utilizar el simulador para obtener la siguiente información sobre el
anterior programa.
2) Usar el botón derecho del ratón sobre las ventanas de datos para hacer aparecer el
menú de contexto, y usar la opción “Show location” para encontrar la posición de memoria en las
que se encuentran las variables a, b, c y e.
3) ¿En qué zona de la memoria se encuentra la variable e? PISTA: Observar el valor que
contiene el registro SP.

-4-
Tutorial de Programación en C con el Microprocesador HCS12

4) Ejecutando paso a paso las instrucciones en ensamblador (ctrl.+<F11>) encontrar cómo


se traduce a lenguaje ensamblador cada una de las instrucciones en lenguaje C, y en qué
posiciones de memoria se encuentran cada una de estas instrucciones.
5) ¿Cuál es el valor final de las variables c y e?

3. Entrada/Salida simple
Se debe realizar un programa que controle dos pulsadores (PAD04 y PAD05), y cuando se
detecte una pulsación encienda o apague un LED (DAO0). En concreto, el funcionamiento se
puede describir con esta tabla, o con el diagrama de estados siguiente:
Evento Acción Pulsación PAD05 Pulsación PAD04 Pulsación PAD04
Pulsación de PAD04 Encender LED DAO0 / nada / encender DAO0 / nada

Pulsación de PAD05 Apagar LED DAO0


Estado nº 1 Estado nº 2
DAO0 apagado DAO0 encendido

Pulsación PAD05
/ apagar DAO0

Figura 5. Definición del funcionamiento del programa.

LABORATORIO (resolver las dudas con el profesor durante la sesión):


6) Usar la documentación para buscar la información necesaria para manejar el LED DAO0
y los pulsadores PAD04 y PAD05. El tutorial de ensamblador indica de forma más precisa cómo
y dónde obtener esta información.
7) Implementar el programa que se pide en esta sección utilizando únicamente el lenguaje
C y variables predefinidas PTAD, DDRAD, … . Construir la implementación en varias etapas
(diseño, implementación, verificación). En todos los casos de verificación el programa se debe
ejecutar de forma controlada: NUNCA se DEBE PROBAR un PROGRAMA ejecutándolo de
golpe!
8) Sustituir alguna instrucción en lenguaje C por instrucciones en ensamblador utilizando la
instrucción asm.

-5-
Tutorial de Programación en Assembler Bajo
Linux

Diego Echeverri Saldarriaga

10 de julio de 2006
2
Índice general

Introducción III

1. Programación, Ensamblado, Enlazado y Depuración 1


1.1. Estructura básica de un programa . . . . . . . . . . . . . . . . . 1
1.1.1. Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2. Secciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.3. Directiva .global . . . . . . . . . . . . . . . . . . . . . . 2
1.1.4. Fin del Programa . . . . . . . . . . . . . . . . . . . . . . . 2
1.2. Un Primer Problema: . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1. Solución en Pseudocódigo . . . . . . . . . . . . . . . . . . 4
1.2.2. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.3. Implementando el Algoritmo . . . . . . . . . . . . . . . . 5
1.2.4. Ejecutando el algorı́tmo . . . . . . . . . . . . . . . . . . . 6
1.2.5. Haciendo Debugging . . . . . . . . . . . . . . . . . . . . . 7
1.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.1. Manejo de variables . . . . . . . . . . . . . . . . . . . . . 8
1.3.2. Invertir . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.3. Operaciones aritméticas . . . . . . . . . . . . . . . . . . . 8
1.3.4. Operaciones lógicas . . . . . . . . . . . . . . . . . . . . . . 8

2. Control de flujo y Estructuras Indexadas 9


2.1. Control de Flujo . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.1. Instrucción Loop . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.2. Comparación y saltos no Condicionales . . . . . . . . . . 10
2.2. Un Primer Problema . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.1. Solución en Pseudocódigo . . . . . . . . . . . . . . . . . . 12
2.2.2. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.3. Implementando el Algoritmo . . . . . . . . . . . . . . . . 12
2.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.1. Problema del Programa Anterior . . . . . . . . . . . . . . 13
2.3.2. Indexación de varias dimensiones . . . . . . . . . . . . . . 13
2.3.3. Búsqueda de un Elemento . . . . . . . . . . . . . . . . . . 14
2.3.4. Control de Flujo . . . . . . . . . . . . . . . . . . . . . . . 14

i
ii ÍNDICE GENERAL

3. Funciones y Modularidad 15
3.1. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2. Resolviendo Un Primer Problema . . . . . . . . . . . . . . . . . . 17
3.2.1. Solución en Pseudocódigo . . . . . . . . . . . . . . . . . . 18
3.2.2. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.3. Implementando el Algoritmo . . . . . . . . . . . . . . . . 18
3.2.4. Ensamblando y Enlazando . . . . . . . . . . . . . . . . . . 19
3.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.1. Paso de Parámetros . . . . . . . . . . . . . . . . . . . . . 20
3.3.2. Parámetros por Referencia . . . . . . . . . . . . . . . . . 20
3.3.3. Funciones Recursivas . . . . . . . . . . . . . . . . . . . . . 20
Introducción

La programación bajo Assembler es una parte fundamental del curso de Ar-


quitectura ST-039. La idea de este tutorial es que sea suficientemente fácil y
explicito como para poder realizarse sin supervisión, ası́ como también hacer la
introducción a dicho lenguaje mediante una aproximación desde el Pseudocódi-
go.

Capı́tulo 1 Muestra como esta estructurado un programa en Assembler bajo


Intel. Se explican las directivas mas utilizadas del ensamblador as y los
comandos del gdb, como también, el manejo de los registros y tamaños
de variables utilizadas por el procesador. Esta sección deberá durar como
máximo dos sesiones.
Capı́tulo 2 Este capitulo explicara el control de flujo (If, While, For) a par-
tir de las instrucciones de Loop y saltos que provee Intel. La Duración
estimada es de 1 sesión.
Capı́tulo 3 Este capitulo estará dedicado al manejo de funciones y modulari-
dad de un programa realizado en Assembler. Se utilizará una sesión para
esta parte.
Capı́tulo 4 En esta parte se explicará SPARC mediante la comparación del
ya conocido ensamblador de Intel.

iii
iv INTRODUCCIÓN
Capı́tulo 1

Programación, Ensamblado,
Enlazado y Depuración

En esta sección se explicará como esta organizado un programa en Intel. A


veces se hace referencia a algunos aspectos de la arquitectura (como es el caso
del sistema de registros) con el fı́n de que aquel que posea ya conocimientos en
programación bajo Intel con herramientas como TASM y Visual Basic pueda
de una vez adaptarse al entorno de as.
Para aquel que no esté familiarizado, seria útil dar una lectura rápida de la
primera parte, con el objetivo de al menos generar dudas que serán solucionadas
en la segunda parte, la solucion del primer problema en Assembler.

1.1. Estructura básica de un programa


Este es el esqueleto simplificado de un Programa en Assembler de Intel

1. /*
2. Este es un tipo de comentario
3. */
4. #Otro tipo de comentario
5. #nombre de archivo (opcional)
6. .file "Ejemplo1.s"
7. #secciones
8. .section .data
9. # <- datos
10. # <- datos
11. #PROGRAMA
12. .section .text
13. .global _start
14. _start:
15. #FINALIZA PROGRAMA

1
2CAPÍTULO 1. PROGRAMACIÓN, ENSAMBLADO, ENLAZADO Y DEPURACIÓN

16. xorl %eax, %eax


17. incl %eax
18. xorl %ebx, %ebx
19. int $0x80
20. .end

1.1.1. Comentarios
Realizar comentarios en los programas de Assembler pasa de ser una buena
costumbre de programación a una necesidad vital. En as se permite realizar
comentarios de 2 formas:

’#’ para comentarios de una sola lı́nea (4).

“/ ∗ ∗/” para comentarios de varias lı́neas (1-3).

La sección de comentarios esta en [1] (sección 3.3)

1.1.2. Secciones
El código de Assembler debe ser relocalizado en la fase de enlazado con el fin
de que cada parte ocupe la sección que le corresponde. Existen básicamente 5
secciones de enlazado pero, por razones prácticas, nos ocuparemos únicamente
de la de datos .data y la de “texto”.text, para ver las otras secciones se puede
recurrir a [1] (sección 4).
Las secciones .text y .data (que aparecen en las lı́neas 8 y 10) son muy
parecidas, la única diferencia es que la sección .text es comúnmente compartida
entre procesos, además, contiene instrucciones y constantes (el código es .text).
la sección .data es usualmente alterable1 .

1.1.3. Directiva .global


La directiva .global hace visible el simbolo que se le pase al ld. En el caso de
nuestro ejemplo, se le esta pasando el nombre del label (Ver: 3) donde comienza
el programa.

1.1.4. Fin del Programa


Las lineas (15-20) muestran como debe terminar un programa en Linux,
también nos sirven Como primer ejemplo de instrucciones de Assembler
Una instrucción de Assembler consta de:

<label>: <código de instrucción> <parametro 1>, <parametro 2> ...


1 Para efectos de lo que se realizará durante el curso, la diferencia no es muy importante.
1.2. UN PRIMER PROBLEMA: 3

Label

La idea de un label, es “guardar”la ubicación donde se encuentra, ya sea para


una instrucción o para hacer referencia al área de datos (lo que se suele llamar el
left-hand side de la variable). Un label es un identificador seguido de ’:’. Cuando
se vaya a hacer referencia a este se nombra solo el identificador. Este concepto
quedará mas claro una vez resolvamos nuestro primer problema. Los Labels son
opcionales.

Código de Instrucción y Parámetros

Los códigos de instrucción y sus respectivos parámetros, están dados por


por los manuales especı́ficos del procesador en el que se esté trabajando, como
también del Ensamblador que se utilice. Para el caso de Intel, se puede con-
sultar [2] disponible en linea en (http://www.intel.com/design/pentium4/
manuals/index_new.htm). Las diferencias de sintaxis (Sintaxis AT&T) dadas
por el ensamblador son básicamente las siguientes, y pueden ser consultadas
para mayor detalle en [1] (Sección 8.8).

Los registros se escriben de la forma %<registro>.

Las números (valores inmediatos) deben estar acompañados del prefijo $.

Los códigos de instrucción que operen sobre 32 bits se les debe agregar el
sufijo “l”

Las referencias “source, destination”de Intel, se invierten.

Para el caso del fin del programa, la secuencia de instrucciones es la siguiente.

xorl %eax, %eax: Realiza un or exclusivo de eax con eax. Como eax es igual a
eax, el resultado de esta operación, deja eax en ’0’.

incl %eax: Ésta instrucción incrementa eax

xorl %ebx, %ebx: Igual que la primera instrucción.

int $0x80: Esta es una llamada a una interrupción, le dice al sistema operativo
que el programa terminó su ejecución.

1.2. Un Primer Problema:


Hallar la sumatoria de 5 números alojados en memoria.
4CAPÍTULO 1. PROGRAMACIÓN, ENSAMBLADO, ENLAZADO Y DEPURACIÓN

1.2.1. Solución en Pseudocódigo


Por mas trivial que paresca el problema, siempre resulta útil hacer el es-
quema de ejecución a modo de pseudocódigo. De esta forma garantizamos, en
primer lugar, una buena lógica de programa, y en segundo, reducir la labor de
programación a una simple traducción de un lenguaje al cual ya estamos famil-
iarizados. Como primer intento podriamos hablar de un pseudocódigo de alto
nivel de este tipo.

Input: N1 , N2 , N3 , N4 , N5
Output: Sumatoria
return N1 + N2 + N3 + N4 + N5 ;
Algoritmo 1: Primera Aproximación
Ahora bien, si tenemos en cuenta que Assembler es un lenguaje donde las
operaciones son predominantemente binarias, valdrı́a la pena convertir nuestro
algorı́tmo a uno que solo requiera operaciones de éste tipo.

Input: N1 , N2 , N3 , N4 , N5
Output: Sumatoria
Acumulador ←− 0;
Acumulador += N1 ;
Acumulador += N2 ;
Acumulador += N3 ;
Acumulador += N4 ;
Acumulador += N5 ;
return Acumulador

Algoritmo 2: Ahora con operaciones binarias

Ya con el algorı́tmo de operaciones binarias, podemos comenzar la solución


desde Assembler.

1.2.2. Variables
Algo importante en la programación en Assembler es tener claras las vari-
ables que se utilizarán, Para esto, se debe tener en cuenta lo siguiente:

1. Nombre de la Variable: Toda variable debe tener nombre. Debe ser de


fácil recordación ya que el orden es muy importante en la programación
en Assembler.

2. Tamaño de la variable: Definir de que tamaño es la variable (64 bits, 32


bits, 16 bits, 8 bits o un espacio de memoria de N Bytes)

Según nuestro algoritmo necesitaremos 6 espacios de memoria. Cinco para


los números que necesitamos sumar y un espacio para el acumulador.
1.2. UN PRIMER PROBLEMA: 5

El Procesador Intel posee espacios para almacenar variables, estos espacios


se denominan Registros. Las ventajas que nos ofrece la utilización de registros
es la velocidad de acceso, por esto es preferible que los datos que se utilicen
con mayor frecuencia se alojen en registros. Los registros de Intel que podemos
utilizar se organizan de esta forma:

Registros de 32 bits EAX, EBX, ECX, EDX

Registros de 16 bits Corresponden a la mitad derecha de los registros de 32


bits, y son: AX, BX, CX, DX

Registros de 8 bits Corresponden a cada una de las mitades de los registros


de 16 (H para High, L para Low) y son: AL, AH, BL, BH, CL, CH, DL,
DH

Conociendo esto, alojaremos nuestras variables de la siguiente forma.

Nombre Tamaño Lugar


N1 , N2 ...N5 32 bits Memoria
Acumulador 32 bits EAX

Las Variables en memoria se definen en el área de datos asi:

<label>: <tama~
no> <valor inicial>

Aqui el label sirve como el nombre de la variable, es decir, el nombre con el


cual haremos referencia al valor guardado.
El tamaño se define como .long para 32 bits,.word para 16 bits y .byte
para 8 bits.
Los valores iniciales equivalen a los números que tendrá el programa. Por
defecto se leen en decimal pero pueden escribirse en binario (inicializando la
cadena con “0b”), en hexadecimal (inicializando la cadena con “0x”) o en octal
inicializandola en “0”.
Ya con esto podemos escribir nuestra sección .data para alojar las variables
a las que hallaremos la sumatoria (alojaremos 100, 100, 100, 100, -100).

.section .data
Numero1: .long 100
Numero2: .long 0144
Numero3: .long 0x64
Numero4: .long 0b1100100
Numero5: .long -100

1.2.3. Implementando el Algoritmo


Para la implementación del algorı́tmo se debe buscar si alguna instrucción
del procesador hace lo que nosotros necesitamos, de lo contrario, será necesario
descomponer aquella instruccion en una secuencia que ya exista. Para consultar
6CAPÍTULO 1. PROGRAMACIÓN, ENSAMBLADO, ENLAZADO Y DEPURACIÓN

las instrucciones se puede buscar en [2] o también existe una hoja de referencia
con las instrucciones más utilizadas en http://www.jegerlehner.ch/intel/
IntelCodeTable_es.pdf.
La primera instrucción, pone el acumulador (EAX) en 0. La instrucción
clrl %eax nos realizará esta tarea.
Para sumar utilizaremos (add origen, destino). Asi nos quedará el código del
programa de esta forma:

1. .file "Sumatoria.s"
2. .section .data
3. Numero1: .long 100
4. Numero2: .long 0144
5. Numero3: .long 0x64
6. Numero4: .long 0b1100100
7. Numero5: .long -100
8. .global _start
8.1. .text
9. _start:
10. clrl %eax
11. addl Numero1,%eax
12. addl Numero2,%eax
13. addl Numero3,%eax
14. addl Numero4,%eax
15. addl Numero5,%eax
16. xorl %eax, %eax
17. incl %eax
18. xorl %ebx, %ebx
19. int $0x80
20. .end
2

1.2.4. Ejecutando el algorı́tmo


Para ejecutar el algoritmo, debemos primero ensamblarlo con as.
En general, las herramientas de GNU siguen el formato:

<Programa> <opciones> <archivo(s) fuente(s)>

Las opciones que nos interesan de as son las siguientes (para mas información
ver [1] Sección 1).

[-o] Con ésta opción se especifica el archivo de salida.

[–gstabs] Con esta opción se genera la información para hacer el debbuging


2 Nótese la linea extra 8.1, se debe poner esta lı́nea para garantizar que las instrucciones

sean tomadas por gdb


1.2. UN PRIMER PROBLEMA: 7

Nos queda el comando de esta forma:

as --gstabs -o Sumatoria.o Sumatoria.s

Cuando tenemos el programa objeto (Sumatoria.o), debemos enlazarlo. Para


esto usamos el enlazador de GNU, ld.

ld -o Sumatoria Sumatoria.o

Sumatoria es el ejecutable.

1.2.5. Haciendo Debugging


Para hacer el debug del programa, utilizamos la heramienta gdb. Para lla-
marlo, utilizamos:

gdb <ejecutable>

Lo cual nos abre una ventana de éste tipo


$ gdb Ejemplo1
GNU gdb 6.3.50_2004-12-28-cvs (cygwin-special)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General
Public License, and you are welcome to change it
and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type
"show warranty" for details.
This GDB was configured as "i686-pc-cygwin"...
(gdb)
En este punto podemos utilizar los diferentes comandos de gdb. Estos son
los mas importantes, para verlos se puede consultar [3].

l ist: muestra el código fuente, es útil para tener las lineas exactas donde se
ponen los puntos de parada (break).
break <linea>: pone un punto de parada en la lı́nea indicada.
break *<dir>: pone un punto de parada en la dirección indicada.
r un: ejecuta el programa
k ill: termina el programa
q uit: sale de gdb
i nfo r egisters: muestra el estado de los registros.
i nfo variables: muestra el estado de las variables.
8CAPÍTULO 1. PROGRAMACIÓN, ENSAMBLADO, ENLAZADO Y DEPURACIÓN

print <Variable>: muestra el valor de la variable.


step n: avanza n “instrucciones”. Cuando n no esta definido, avanza un paso
next n: avanza n “instrucciones”. Cuando n no esta definido, avanza un paso,
pero no entra dentro de los calls.

1.3. Ejercicios
1.3.1. Manejo de variables
Cambiar el programa de 1.2.3 para que trabaje con variables de 16 bits y de
8 bits.

1.3.2. Invertir
Hacer un programa que dados 5 números alojados en memoria N1 , N2 , N3 , N4 , N5 ,
invierta el orden de los valores asignados en un principio. es decir: N1 −→ N5
N2 −→ N4 N3 −→ N3 N4 −→ N2 N5 −→ N1

1.3.3. Operaciones aritméticas


Dados 2 números en memoria A y B, hacer un programa que al final deje
almacenado en EAX A + B, en EBX A − B, en ECX A ∗ B y en EDX A/B

1.3.4. Operaciones lógicas


Dados 2 números en memoria A y B, hacer un programa que al final deje
almacenado en EAX A ∨ B, en EBX A ∧ B, en ECX ∼ A y en EDX A ⊗ B
Capı́tulo 2

Control de flujo y
Estructuras Indexadas

2.1. Control de Flujo


El control de flujo en los programas de alto nivel se realiza con estructuras If,
While, For. En Assembler dichas estructuras no existen, en cambio, existen los
saltos condicionales, una especie de goto condicionados, con los cuales se pueden
escribir cada una de las rutinas que utilizan los leguajes de alto nivel. En esta
sección veremos las instrucciones de Assembler de Intel que nos permiten hacer
esto.

2.1.1. Instrucción Loop


?? La instrucción Loop es muy útil para hacer secuencias un determinado
número de veces. Es decir, resulta útil para algoritmos de esta forma

Input: Entrada
Output: Salida
for i ←− n to 1 do
//Secuencia de pasos;
end

El Loop funciona de la siguiente manera:

1. Se hace dec %ECX

2. Si ECX es igual a cero, pasa a la siguiente instrucción. Sinó, salta a la


etiqueta que se le pasa por parametro

Siendo asi, una secuencia loop será de la forma:

9
10 CAPÍTULO 2. CONTROL DE FLUJO Y ESTRUCTURAS INDEXADAS

1. mov n, %ecx
2. bucle:
3. ##SECUENCIA
4. loop bucle

Una cosa importante a tener en cuenta es que dado que el valor que controla
el número de saltos está en ECX, dentro de la secuencia es importante que el
valor no se altere, o si se altera, conservarlo en una variable, para cuando se
ejecute la instrucción Loop, restaurarlo.

2.1.2. Comparación y saltos no Condicionales


En Assembler existen las instrucciones cmp y los saltos jnz, jz, jmp, etc, las
cuales son útiles para la implementación del resto de las secuencias de control.

CMP: Sirve para comparar. Internamente es una resta entre los 2 operandos
que se comparan

JE: Éste es un salto que se ejecuta cuando la comparación anterior dio como
resultado que los 2 operandos son iguales.

JNE: Salta si no es igual.

JG: Salta si es mayor.

JGE: Salta si es mayor o igual.

JL: Salta si es menor.

JLE: Salta si es menor o igual.

JMP: Salta siempre.

Implementando el If Then Else

Input: Entrada
Output: Salida
if A operador B then
#SECUENCIA DE PASOS SI SE CUMPLE LA CONDICIÓN;
else
#SECUENCIA DE PASOS SI NO SE CUMPLE LA CONDICIÓN;
end
Algoritmo 3: operador es cualquiera de (=, 6=, ≤, ≥, <, >)

Una secuencia If then else de la forma del Algoritmo 3, se puede implementar


de la siguiente manera:
2.2. UN PRIMER PROBLEMA 11

1. cmp A,B
2. <Salto> else
3. #SECUENCIA DE PASOS SI SE CUMPLE LA CONDICIÓN}
4. jmp fin
5. else:
6. #SECUENCIA DE PASOS SI \textbf{NO} SE CUMPLE LA CONDICIÓN
7. fin:

Donde <Salto>, es una instrucción cuya condición de salto sea contraria al


operador utilizado, es decir:

Operador Instrucción de Salto


= JNE
6= JE
≤ JG
≥ JL
< JGE
> JLE

Implementando el While

Siguiendo exactamente la misma manera que utilizamos para generar el If,


podemos implementar el While.

Input: Entrada
Output: Salida
while A operador B do
#SECUENCIA DE PASOS DENTRO DEL WHILE;
end
Algoritmo 4: operador es cualquiera de (=, 6=, ≤, ≥, <, >)

1. while:
2. cmp a,b
3. <Salto> salir
4. #SECUENCIA DE PASOS DENTRO DEL WHILE\
5. jmp while
6. salir:

2.2. Un Primer Problema


Encontrar el número mayor en un arreglo no ordenado.
12 CAPÍTULO 2. CONTROL DE FLUJO Y ESTRUCTURAS INDEXADAS

Input: Arreglo[], T am
Output: M ayor
M ayor ←− Arreglo[T am];
for i ←− T am to 0 do
if Arreglo[i] > M ayor then
M ayor ←− Arreglo[i]
end
end
return Mayor
Algoritmo 5: Número Mayor en Arreglo

2.2.1. Solución en Pseudocódigo


2.2.2. Variables
Otra vez utilizaremos variables de 32. Siendo asi, una forma de acomodar
las variables es de esta forma.
Nombre Tamaño Lugar
T am 32 bits Memoria
Arreglo1 32 bits*Tam Memoria
M ayor 32 bits EAX
i 32 bits ECX2
Ya con esto podemos definir nuestra sección .data. Iniciamente comenzare-
mos con un arreglo de tamaño 5. Para definir un arreglo utilizaremos la directiva
.int

.section .data
Tam: .long 5
Arreglo: .int 0,2,4,8,10

2.2.3. Implementando el Algoritmo


Para realizar algoritmos “grandes”lo mas útil casi siempre es implementar
de a pequeños fragmentos. Por esto, primero implementaremos la parte interna
del for, es decir, el if siguiendo lo que vimos en la sección 2.1.2.

1. cmpl %eax, prueba


2. JLE else
3. movl prueba, %eax
4. jmp fin
5. else:
6. #En este caso no hay Else
7. fin:
1 Arreglo viene siendo un apuntador a donde comienza la estructura de datos
2 Se coloca en ECX a propósito, para seguir la estructura de 2.1.1
2.3. EJERCICIOS 13

Por ahora utilizaremos una variable en memoria llamada prueba, con el fin
de garantizar que el fragmento este correcto, despues reemplazaremos esto por
la correspondiente posición.
Una vez hayamos probado la parte del If según lo indicado en 1.2.4, podemos
implementar el for según lo visto en 2.1.1.

.file "MayorenArreglo.s"
.section .data
Tam: .long 5
Arreglo: .int 0,2,4,8,10
prueba: .long 4
.text
.global _start
_start:
movl Tam, %ecx
loop1:
cmpl %eax, Arreglo(,%ecx,4)
JLE else
movl Arreglo(,%ecx,4), %eax
jmp fin
else:
#En este caso no hay Else
fin:
loop loop1
xorl %eax, %eax
incl %eax
xorl %ebx, %ebx
int $0x80
.end

El manejo indexado funciona de la siguiente manera. Cuando llamamos Arreg-


lo(, %ecx,4)estamos usando la dirección base del Arreglo, estamos indexando
con ecx (por lo cual nos iterará todas las posiciones), y con el cuatro le decimos
que el tamaño de los datos es de 4 Bytes (una palabra).

2.3. Ejercicios
2.3.1. Problema del Programa Anterior
En la solución de la busqueda del número mayor hay un grave error, ¿Cuál
és? ¿Cómo se soluciona?

2.3.2. Indexación de varias dimensiones


Realice el mismo programa, pero que haga la busqueda en una matriz de 2
dimensiones.
14 CAPÍTULO 2. CONTROL DE FLUJO Y ESTRUCTURAS INDEXADAS

2.3.3. Búsqueda de un Elemento


Realice una busqueda de un elemento en un arreglo, y retorne el ı́ndice donde
se encuentre

2.3.4. Control de Flujo


Realice cualquier tipo de busqueda en el arreglo pero utilizando el control
de flujo While y Do While.
Capı́tulo 3

Funciones y Modularidad

3.1. Funciones
A medida que un programa aumenta de complejidad, se detectan ciertas
porciones de código que realizan lo mismo basados en 0 o más parámetros. A
estos fragmentos, podemos agruparlos en funciones.
Independiente de la arquitectura utilizada, una función está constituida por
dos componentes: El Llamador y el Llamado

El Llamador (Caller ) y el Llamado (Called)


El Llamador1 es la parte del código que tiene estos objetivos:

Colocar Los parámetros en el sitio adecuado

Guardar la dirección de retorno

Invocar el Llamado

El Llamado, es la parte que se encarga de:

Garantizar que todo quede como estaba.

Localizar (si los hay) los parámetros.

Definir (si las hay) las variables locales.

Recuperar la dirección de retorno

Retornar el control al llamador.


1 Para la descripción del Llamador y Llamado se han utilizado ciertas notas textuales del
Curso Programación Digital de Jose Luis Montoya Pareja

15
16 CAPÍTULO 3. FUNCIONES Y MODULARIDAD

Una Aproximación al Llamador y el Llamado desde C


Dado un Código de éste tipo en C:
1. int main(void) {
2. int a, b;
3. a = 10;
4. b = 20;
5. sumar(a,b);
6. return 0;
7. }
8. void sumar(int x, int y){
9. int z;
10. z = x + y;
11. }
Podemos comenzar a diferenciar que partes del código de la función sumar
son responsabilidad de que parte de la función (Llamador o Llamado).
En la lı́nea 5 se puede ver que le estamos pasando los parámetros a y b, es
decir, estamos cumpliendo con la primera responsabilidad del Llamador. Tam-
bién podemos decir que ya que después de ejecutarse esta lı́nea, C pasará a la
siguiente (lı́nea 6) se cumple también con la segunda responsabilidad Llamador,
es decir, esta instrucción despues de ejecutarse, sabe donde debe continuar. En
tercer lugar, sabe tambien que esta linea ejecuta el código que escribimos para
sumar, lo cual quiere decir que cedimos el control al Llamado.
La función sumar reconoce dentro de su ámbito, las variables x y y. Esto
corresponde a Localizar los parámetros. En la lı́nea 9 definimos una variable
local, lo cual también definimos como responsabilidad del Llamado.

Una Aproximación al Llamador y el Llamado desde Asm


Dado el código Asm:
1. .section .data
2.
3. a: .long 4
4. b: .long 5
5.
6. .section .text
7. .global sumar
8. sumar:
9. pushl %ebp
10. movl %esp, %ebp
11. movl 8(%ebp), %ebx
12. movl 12(%ebp), %eax
13. addl %ebx, %eax
14. leave
15. ret
3.2. RESOLVIENDO UN PRIMER PROBLEMA 17

16.
17. .global _start
18. _start:
19.
20. # "Main"
21. pushl a
22. pushl b
23. call sumar
24. popl %ebx
25. popl %ebx
26.
27. # Finalizo el programa
28. xorl %eax, %eax
29. incl %eax
30. xorl %ebx, %ebx
31. int $0x80
32. .end

Como pudimos ver en el Llamador de C, una sola lı́nea tenı́a a su cargo


todas las responsabilidades. En Asm, somos nosotros los que debemos pasarle
los parámetros.
Existen varias formas de pasar parámetros, en este caso los pasaremos por
Pila. Para esto, tenemos simplemente que hacerle push a cada uno como se ve
en las lı́neas 21 y 22.
La utilización del call realiza varias cosas. En primer lugar, guarda la di-
rección donde se encuentra, para que cuando se termine de ejecutar la función,
esta sepa a que dirección debe retornar. También, funciona como una especie
de salto incondicional, es decir, irá a la primera posición de memoria donde se
encuentra ubicada la función.
Las lı́neas 9 y 10 sirven para la creación del marco de pila, que es la forma
como Intel maneja funciones. en las lı́neas 11 y 12 estamos recuperando los
parámetros que pasamos por pila, mediante un direccionamiento indexado.
Con las instrucciones leave y ret(Lı́neas 14 y 15), regresamos a la lı́nea
posterior donde hicimos el call.
Después de haber realizado el call, ponemos 2 pop (lı́neas 24 y 25) para
desocupar los parámetros que pasamos dentro de la función. Esto con el objetivo
de cumplir la responsabilidad que tiene el llamado de “dejar las cosas como
estaban”.

3.2. Resolviendo Un Primer Problema


Dado un arreglo de tamaño N y el apuntador a éste, hallar la sumatoria
utilizando la función suma de la sección anterior. Esta vez llamaremos la Función
suma desde un archivo Externo.
18 CAPÍTULO 3. FUNCIONES Y MODULARIDAD

3.2.1. Solución en Pseudocódigo


Ya que utilizaremos la función que anteriormente implementamos (suma),
podemos hacer referencia a esta, sin tener que implementarla de nuevo (real-
mente se trata de una sola lı́nea Asm, ası́ que no hay diferencia, pero para
problemas grandes esto resulta muy conveniente).

Input: Arreglo[], T am
Output: Sumatoria
Sumatoria ←− 0;
i ←− 0;
while i < T am do
Sumatoria ←− sumar(Arreglo[i], Sumatoria);
i + +;
end
return Sumatoria
Algoritmo 6: Sumatoria de N números en Arreglo

3.2.2. Variables
Nombre Tamaño Lugar
T am 32 bits Memoria
Arreglo[] 32 bits Memoria
Sumatoria 32 bits EAX
i 32 bits ECX

3.2.3. Implementando el Algoritmo


Para aumentar el nivel de “orden”, utilizaremos un archivo separado para la
función suma que ya habı́amos implementado. De esta forma:

1. .file "Suma.s"
2. .section .text
3. .global suma
4. suma:
5. pushl %ebp
7. movl %esp, %ebp
8. movl 8(%ebp), %ebx
9. movl 12(%ebp), %eax
10. addl %ebx, %eax
11. leave
12. ret

El archivo “Principal”, será de la siguiente manera:


3.2. RESOLVIENDO UN PRIMER PROBLEMA 19

.file "Principal.s"
.section .data
Tam: .long 5
Arreglo: .int 0,2,4,8,10
.section .text
.global _start
.extern suma
_start:

movl $0, %ecx


movl $0, %eax
while1:
cmpl Tam, %ecx
jge finwhile1

pushl Arreglo(,%ecx,4)
pushl %eax
call suma
pop %ebx
pop %ebx
inc %ecx
jmp while1

finwhile1:
# Finalizo el programa
xorl %eax, %eax
incl %eax
xorl %ebx, %ebx
int $0x80
.end

Donde la directiva .extern es aquella que le dice al enlazador, que se recur-


rirá a una función que se encuentra en otro archivo.

3.2.4. Ensamblando y Enlazando


Se debe tener en cuenta que en el proceso de ensamblado y enlazado se debe
realizar de la siguiente manera.
Primero se debe ensamblar cada fuente:

$ as -gstabs -o Suma.o Suma.s


$ as -gstabs -o Principal.o Principal.s
Después se enlaza de la siguiente forma:
$ ld -o Principal Suma.o Principal.o
20 CAPÍTULO 3. FUNCIONES Y MODULARIDAD

3.3. Ejercicios
3.3.1. Paso de Parámetros
Cambie la función para trabajar con otras formas de paso de parámetros
(por registros y area de memoria)

3.3.2. Parámetros por Referencia


Cambie el programa de sumatoria a una función que reciba como parámetros
N y el apuntador (sugerencia: La instrucción LEA puede ser útil)

3.3.3. Funciones Recursivas


Implemente una función recursiva que halle factorial.
Bibliografı́a

[1] Dean Elsner and Jay Fenlason. Using as, Enero 1994.

[2] Software Developer’s Manual IA-32 Intel Architecture, Marzo 2006.


[3] Richard M. Stallman and Roland H. Pesch. Debugging with GDB, Enero
1994.

21
EE 371 LAB 1 - F05 Name ___________________
MC68HCS12 Programming - Introduction
Why is it your feet smell and your nose runs? Partner_________________
Meeting Day _____
References: EE371 texts, Hitchhiker's Guide to CodeWarrior HR ______
Schedule: 9/7, 8 Do the lab Demo:
9/14, 15 Programs demonstrated (3)__________
(8) __________
1. Pre-Lab:Read
Extra credit (9) __________
2. Follow steps 1-18 in the Hitchhiker's Guide to CodeWarrior to enter and make the
following program.
a. Type the following code into main.asm (step 10) after the comment ; Your program code goes here.

;********************************
; Your program code goes here
; Initialize I/O
main_loop:
; DO
nop ; No operation
ldx #main_loop ; Initialize pointer
ldab #$02 ; Load Reg B
loop:
ldaa 0,x ; Load Reg A
staa VarData ; Save it
ldaa 2,x ; Load Reg A again
staa VarData+1 ; Save it again
inx ; Increment pointer
decb ; Decrement counter
bne loop ; If counter not zero loop
nop ; Do nothing again
; FOREVER
bra main_loop
MyConst:SECTION
; Place constant data here
;********************************
MyData: SECTION
; Place variable data here
VarData: DS.B 2 ; Two bytes of storage

After you have successfully compiled (assembled!) and made the program, go to step 16 and 17 to use the True-Time Simulator
and P&E ICD Target.
After the simulator has been launched, have a look at the display.
The Source window shows your source program.
The Assembly window shows the program as it looks in memory. Right Click in the Assembly window and check Display
Code to make your display show the code in the memory locations $C000 - $C025. Right Click again and check Display
Symbolic to show symbolic instead of decimal addresses.
The Registers window shows the current status/contents of all registers.
The Data window will show the current value of variable data in your program. Click on the +VarData to show the two variable
bytes. Right Click on VarData and select Format… Hex.
The Procedure window shows what procedure you are currently in and the Command window allows you to enter
simulator/debugger commands. We will see more of these windows in future labs.
The Memory window allows the display of memory contents. Right Click in the Memory window and select Address . . . and
in the Display Address window enter C000.

In the Source window position the cursor pointer over the nop instruction and Right Click and select Set Breakpoint.
Click the Green Run Arrow or click Run > Start/Continue. The program should run to your breakpoint and stop. (Note: A
breakpoint stops the program before it executes that statement.)

1
Now, step through your program one assembly language statement at a time by clicking on the single-
step button or pressing F11 until you reach the nop instruction at the beginning of the program you
entered. (We will see what the LDS instruction is all about later.)

At each step inspect the Register window to see what is happening to the registers. Try to predict what will happen at each step
BEFORE you click on the step button.
3. Explain to your lab instructor what you see on the screen. In particular, explain what is happening to each register and to the
condition code register.

4. Close the simulator to return to the CodeWarrior project manager.

5. Create a new project by entering the following program. To save some typing, you can get the source file from the “N” drive. (If
you haven’t done so all ready, you can map drive N to ohm\\ececourses$.) The source file is N:\EE371\lab01_f05\lab_01_f05.asm.
In the project manager, File > Open, browse to the file, and then after opening the file do File > Save As … and be sure to position
to your project folder in the Sources folder. You can also rename the file if you wish. Then Project > Add lab_01_f05.asm to
Project…

; EE371, Lab 1, Fall 2005


; Blink LED1 and LED2 on the CSM-12C32 board
;********************************
; Define the entry point for the main program
XDEF Entry, main
XREF __SEG_END_SSTACK ; Note double underbar
;********************************
; Include files
;********************************
; Register definitions
BASE: EQU 0 ; Base address for registers
PORTA: EQU BASE+0 ; Port A
DDRA: EQU BASE+2 ; Data Dir Reg A
PORTB: EQU BASE+1 ; Port B
DDRB: EQU BASE+3 ; Data Dir Reg B
LED1: EQU %00000001 ; LED1 on Port A-0
LED2: EQU %00010000 ; LED2 on Port B-4
;********************************
; Constants definitions
DELAY1: EQU $ffff ; Inner loop counter
DELAY2: EQU $04 ; Outer loop counter
;********************************
; Constants definitions
;********************************
; Code Section
MyCode: SECTION
Entry:
main:
;********************************
; Initialize stack pointer register
lds #__SEG_END_SSTACK
;********************************
; Your program code goes here
; Initialize I/O
bset DDRA,LED1 ; Make Port A-0 output
bset DDRB,LED2 ; Make Port B-4 output
ldaa #LED1 ; Initial LED display
ldab #~LED2 ; ~ = 1's complement
main_loop:
; DO
; Display the current pattern on the LEDs

2
staa PORTA
stab PORTB
; Delay some time
jsr delay_sub ; Use a subroutine
; Complement the display values
coma ; Complement A register
comb ; Complement B register
; FOREVER
bra main_loop
;********************************
; Simple Delay Subroutine
; Registers modified: CCR
;********************************
delay_sub:
pshx ; Save the registers
pshy
ldy #DELAY2 ; Initialize outer loop counter
outer:
; WHILE the Y register is not zero
; Decrement the outer loop counter
dey
beq all_done ; Branch when Y equal to zero
; DO the inner loop
ldx #DELAY1 ; Initialize inner loop counter
inner:
; WHILE the X register is not zero
; DO Decrement the inner loop counter
dex
bne inner ; Branch when X NOT equal to zer0
; ENDWHILE X not zero
bra outer
; ENDWHILE Y not zero
all_done:
puly ; Restore the registers
pulx
rts
;********************************
MyConst:SECTION
; Place constant data here
;********************************
MyData: SECTION
; Place variable data here

6. Run the program. You should see the two LEDs on the CSM-12C32 microcontroller board flashing.
7. Step through the program to see how it works.
8. Modify the program so that it flashes at about ½ the rate.
9. Extra Credit: Modify the program so that it flashes LED1 twice while LED2 is off and then LED2 twice while LED1 is off and repeats.
10. Grading: Program demo s 5 points each, extra credit 5 points

3
EE 308 Spring 2006

Writing Assembly Language Programs — Use Flowcharts to Help Plan Program Structure

Flow chart symbols:

START

LABEL:

OPERATION

YES
CONDITIONAL BRANCH

NO

END

1
EE 308 Spring 2006

IF-THEN Flow Structure

if (C)
{
A;
}

FALSE
C?

TRUE
L1:

L2:

EXAMPLE:

IF (A<10) CMPA #10


{ BLT L1
var = 5; BRA L2
} L1: LDAB #5
STAB var
L2: next instruction

OR:
CMPA #10
BGE L2
LDAB #5
STAB var
L2: next instruction

2
EE 308 Spring 2006

IF-THEN-ELSE Flow Structure

if (C)
{
A;
}
else
{
B;
}

FALSE
B C?
TRUE

L1:

L2:

if (A<10) CMPA #10


{ BLT L1
var = 5; CLR VAR
} BRA L2
else L1: LDAB #5
{ STAB var
var = 0; L2: next instruction
}

3
EE 308 Spring 2006

DO WHILE Flow Structure

do
{
A;
}
while (C);

L1:

TRUE
C?

FALSE

EXAMPLE:
i = 0; LDX #table
do CLRA
{ L1: ASR 1,X+
table[i] = table[i]/2; INCA
i = i+1; CMPA #LEN
} BLE L1
while (i <= LEN);

4
EE 308 Spring 2006

WHILE Flow Structure

while (C)
{
A;
}

L1:

TRUE
A C?
L2:

FALSE

L3:

EXAMPLE:

i = 0; LDX #table
while (i <= LEN) CLRA
{ L1: CMPA #LEN
table[i] = table[i]*2; BLT L2
i = i + 1; BRA L3
} L2: ASL 1,X+
INCA
BRA L1
L3: next instruction

5
EE 308 Spring 2006

Use Good Structure When Writing Programs — Do Not Use


Spaghetti Code

SPAGHETTI CODE
DO NOT USE

6
EE 308 Spring 2006

Example Program: Divide a table of data by 2

Problem: Start with a table of data. The table consists of 5 values. Each
value is between 0 and 255. Create a new table whose contents are the original
table divided by 2.

1. Determine where code and data will go in memory.


Code at $1000, data at $2000.
2. Determine type of variables to use.
Because data will be between 0 and 255, can use unsigned 8-bit numbers.
3. Draw a picture of the data structures in memory:

$2000 table1:

COUNT

table2:

7
EE 308 Spring 2006

4. Strategy: Because we are using a table of data, we will need pointers to


each table so we can keep track of which table element we are working
on.
Use the X and Y registers as pointers to the tables.
5. Use a simple flow chart to plan structure of program.

START

table1 X Init
Pointers

COUNT
Get
Entry

table2 Y Divide
by 2

Store
Result

Inc
Pointers

8
EE 308 Spring 2006

6. Need a way to determine when we reach the end of the table.


One way: Use a counter (say, register A) to keep track of how many
elements we have processed.

START

Init
Counter

table1 X Init
Pointers
L1:
COUNT
Get
Entry

table2 Y Divide
by 2

Store
Result

Inc
Pointers

Dec
Counter

YES
More?

NO

STOP

9
EE 308 Spring 2006

7. Add code to implement blocks:

START

Init
Counter LDAA #COUNT

table1 X Init LDX #TABLE1


Pointers LDY #TABLE2
L1:
COUNT
Get
Entry
LDAB 0,X

table2 Y Divide LSRB ; unsigned divide


by 2

Store
Result STAB 0,Y

Inc INX
Pointers INY

Dec
Counter DECA

YES BNE L1
More?

NO

STOP SWI

10
EE 308 Spring 2006

8. Write program:

; Program to divide a table by two


; and store the results in memory

prog: equ $1000


data: equ $2000

count: equ 5

org prog ;set program counter to 0x1000


ldaa #count ;Use A as counter
ldx #table1 ;Use X as data pointer to table1
ldy #table2 ;Use Y as data pointer to table2
l1: ldab 0,x ;Get entry from table1
lsrb ;Divide by two (unsigned)
stab 0,y ;Save in table2
inx ;Increment table1 pointer
iny ;Increment table2 pointer
deca ;Decrement counter
bne l1 ;counter != 0 => more entries to divide
swi ;Done

org data
table1: dc.b $07,$c2,$3a,$68,$F3
table2: ds.b count

11
EE 308 Spring 2006

9. Advanced: Optimize program to make use of instructions set efficiencies:

; Program to divide a table by two


; and store the results in memory

prog: equ $1000


data: equ $2000

count: equ 5

org prog ;set program counter to 0x1000


ldaa #count ;Use B as counter
ldx #table1 ;Use X as data pointer to table1
ldy #table2 ;Use Y as data pointer to table2
l1: ldab 1,x+ ;Get entry from table1; then inc pointer
lsrb ;Divide by two (unsigned)
stab 1,y+ ;Save in table2; then inc pointer
dbne a,l1 ;Decrement counter; if not 0, more to do
swi ;Done

org data
table1: dc.b $07,$c2,$3a,$68,$F3
table2: ds.b count

12
EE 308 Spring 2006

TOP-DOWN PROGRAM DESIGN

• PLAN DATA STRUCTURES IN MEMORY


• START WITH A LARGE PICTURE OF PROGRAM STRUCTURE
• WORK DOWN TO MORE DETAILED STRUCTURE
• TRANSLATE STRUCTURE INTO CODE
• OPTIMIZE FOR EFFICENCY —
DO NOT SACRIFICE CLARITY FOR EFFICIENCY

13
Programación con HC12 Estructura de computadores

ESTRUCTURAS DE DATOS COMPLEJAS Y MÉTODO DE ENCUESTA

Se dispone de 4 sesiones de prácticas para realizarla. Es obligatorio entregar el prelaboratorio antes del
inicio de cada sesión, su falta implica suspender las prácticas. Al finalizar cada sesión debe mostrarse
el funcionamiento de la práctica al profesor.
A continuación, se define el trabajo a realizar para cada sesión.

SESIÓN 1

Prelaboratorio:

1. Explicar cómo realizar el acceso indexado a la matriz de la figura 1 a través del array de la figura 2.
Implementar el código de la función que realiza este tipo de acceso a la matriz.

Práctica:

1. Estructura de datos complejas


Se define la matriz mostrada en la figura 1

matriz dc.b $80, $40, $20, $10


$08, $04, $02, $01
$80, $40, $20, $10
$08, $04, $02, $01
figura 1

y el array mostrado en la figura 2

acceso_indexado dc.b 0 ,7, 1, 14, 10, 5, 3, 4

figura 2

Implementar un programa que acceda a los elementos de la matriz mediante los diferentes métodos:
a) Por filas
b) Por columnas
c) Indexado a través del array definido en la figura 2

El programa debe visualizar el valor obtenido de la matriz, para cada método de acceso, encendiendo
los leds correspondientes.

1
Programación con HC12 Estructura de computadores

SESIÓN 2

Prelaboratorio:

2. Mostrar un organigrama y el pseudocódigo del programa que se pide en el apartado 2.1 y 2.2 de la
práctica.

Práctica:

2. Método de encuesta (polling)

Se implementarán los siguientes apartados:


2.1 El usuario selecciona el método de acceso a la matriz de la figura 1 a través de los pulsadores.
Por defecto, al inicio del programa, se selecciona la opción a). Si el usuario desea modificarla, debe
apretar el pulsador PAD04. Esta modificación debe visualizarse mediante los leds según muestra la
tabla 1. Cuando el usuario finaliza su selección, pulsa PAD05.

programa opción seleccionada visualización leds


inicio programa a) X0000000
PAD04 b) 0X000000
PAD04 c) 00X00000
PAD04 a) X0000000

tabla 1
Nota: X=led encendido
0 =led apagado

2.2 Una vez seleccionado el método de acceso a la matriz, guardar el identificador del método en
una variable global, mostrar el valor de la matriz mediante los leds, esperar 1 segundo
aproximadamente y mostrar el siguiente valor de la matriz.
Nota: Cuando se accede a la última posición de la matriz, volver a la posición inicial.

2
Programación con HC12 Estructura de computadores

SESIÓN 3

Prelaboratorio:

3. Implementar la rutina retardo de 0.5 seg. Mostrar un organigrama y el pseudocódigo de la rutina


retardo de 1 seg.

Práctica:

3. Diferentes retardos gestionados por polling y finalización del programa

3.1 Durante la ejecución del apartado 2.2 pueden apretarse los pulsadores. La tabla 2 define los
cambios a realizar en el programa al apretarse alguno de los pulsadores

programa opción seleccionada


PAD04 decrementa el retardo 1/2 seg
PAD05 incrementa el retardo 1/2 seg

tabla 2

Nota: el retardo estará comprendido entre medio segundo y 3 segundos aproximadamente.

3.2 Durante la ejecución del programa pueden apretarse los 2 pulsadores (PAD04 y PAD05). En este
caso, finaliza el programa

SESIÓN 4

Finalización y depuración de todo el trabajo pendiente de las sesiones anteriores

3
Interrupciones con HC12 Estructura de computadores

Introducción
Al final del primer cuatrimestre introdujimos las interrupciones del HC12 como 2º método de
sincronización con los periféricos, método que solucionaba los inconvenientes del anterior, el polling,
poco eficiente, impreciso en la detección de los pulsadores, etc. Para ello realizamos un tutorial que nos
sirvió para aprender:

• Conceptos como RSI, vector de interrupción, I-Bit, …


• Cómo escribir una RSI completa (la que trataba los pulsadores).
• Cómo depurar una RSI.

Uno de los objetivos de las prácticas de este 2º cuatrimestre es profundizar en el conocimiento del
sistema de interrupciones. Para ello estudiaremos cómo funciona el temporizador del HC12 (práctica 2)
y cómo funcionan las comunicaciones serie y paralela (práctica 3).

Introducción al Timer del HC12


En el 1er cuatrimestre hemos utilizado rutinas que gastaban ciclos de CPU cuando queríamos contar un
determinado tiempo. De manera aproximada habíamos añadido instrucciones sin sentido dentro de 2
bucles anidados, con el único objetivo de perder tiempo. Este método no es eficiente ya que ocupa la
CPU en tareas no productivas. Además no es preciso y depende de la velocidad de la CPU. La manera
más correcta de contra tiempo es utilizar los timers del sistema. Con esta práctica se pretende estudiar
el funcionamiento básico del timer del HC12, dejando fuera de dicho estudio características avanzadas
del timer como son la medición de señales de entrada y la generación de señales de salida (Input
Capture y Output Compare).

El timer del HC12 consiste básicamente en un registro contador de 16 bits (TCNT) que se auto-
incrementa en cada ciclo de reloj (16 Mhz). ¿Cómo se cuenta tiempo? Observando cuántas veces el
TCNT da la vuelta (pasa de FFFF a 0000). Para ayudar al programador el HC12 provee de un flag que
nos avisa de ese hecho. Contar es entonces tan sencillo como conocer cuánto tiempo necesita TCNT
para dar una vuelta y multiplicar ese tiempo por el número de avisos del flag (nº de veces que TCNT da
la vuelta). Para añadir más flexibilidad al sistema, el HC12 incorpora un divisor de frecuencia
(prescaler), que divide la frecuencia inicial (16 Mhz) por 2, 4, 8, 16, …, 256, según se programe.

1
Interrupciones con HC12 Estructura de computadores

Práctica 2: El Timer del HC12


La práctica que a continuación se propone debe entregarse al final de la 4ª sesión, a lo más tardar. Es
obligatorio entregar el pre-laboratorio antes del inicio de cada sesión. Es obligatorio también la entrega
del informe de la práctica. La no consecución de estos prerrequisitos implica suspender las prácticas.

El objetivo de esta práctica es resolver los problemas detectados en la práctica desarrollada en el 1er
cuatrimestre:

1. Medición de tiempos poco precisa (los pulsadores no incrementaban/disminuían la velocidad de


display con la precisión requerida).
2. Los pulsadores no siempre funcionaban correctamente. El funcionamiento por niveles hacía que
detectáramos varias veces una misma pulsación y en otros casos no detectáramos ninguna.

¿Cómo vamos a resolver estos problemas? Convirtiendo el polling en Interrupciones. El objetivo final
es contener todo nuestro código en 2 RSIs, la del timer y la de los pulsadores. La función principal
únicamente debería contener la inicialización del sistema y la finalización.

En definitiva, esta segunda práctica mantiene la misma funcionalidad que la primera. La diferencia es
que el código se reorganiza para eliminar el polling y crear las interrupciones. Por lo tanto, es
condición necesaria venir a clase con los fuentes de la práctica del primer cuatrimestre.

2
Interrupciones con HC12 Estructura de computadores

SESIÓN 1

Pre-laboratorio:
1. Repasar el tutorial sobre interrupciones visto en el cuatrimestre anterior.
2. El alumno leerá con atención la sección 1 y 3 del “ECT_16B8C Block User Guide V01.06”, además
del capítulo introductorio que viene con esta práctica.
3. El alumno entregará al profesor, al inicio de la sesión, un documento escrito de manera legible con
la respuesta a las siguientes preguntas:

• (En segundos) ¿Cuál es el mínimo tiempo necesario para que TCNT de una vuelta? ¿Y el
máximo? Tener en cuenta la frecuencia del reloj base y la programación del prescaler.
• ¿En qué registro se programa el divisor de frecuencia? ¿Con cuántos bits se implementa?
• ¿Cómo se llama el flan que avisa de que TCNT ha dado una vuelta? ¿En qué registro se
encuentra?
• Para que el registro contador (TCNT) empiece a contar, o lo que es lo mismo, funcione en
modo normal, debe habilitarse un bit de un registro de control. ¿Cuál es y dónde se encuentra?

Laboratorio:
El objetivo de esta sesión es medir los tiempos con precisión. Para ello utilizaremos el timer del HC12
en su versión polling. Organizaremos el trabajo de la siguiente manera:

1. Escribir una rutina (retardo_250) que genere un retardo de 250 milisegundos, utilizando el
temporizador. Calcular correctamente el prescaler necesario para que TCNT de una sola vuelta
en ese tiempo.
2. Modificar la práctica nº1 (1er cuatrimestre) para que los tiempos de retardo sean contados
mediante la rutina anterior, es decir, para contar 1 segundo llamar 4 veces a la rutina
retardo_250. Será tan fácil como almacenar en una variable global el nº de veces que se llama a
esta rutina. Los pulsadores (PAD4 y PAD5) incrementarán o disminuirán dicha variable. Como
resultado de esto eliminaremos de la práctica la rutina de pérdida de tiempo.

3
Interrupciones con HC12 Estructura de computadores

SESIÓN 2

Pre-laboratorio:
1. Repasar el tutorial sobre interrupciones visto en el cuatrimestre anterior.
2. El alumno leerá con atención la sección 1 y 3 del “ECT_16B8C Block User Guide V01.06”, además
del capítulo introductorio que viene con esta práctica.
3. El alumno entregará al profesor, al inicio de la sesión, un documento escrito de manera legible con
la respuesta a las siguientes preguntas:
• En esta sesión pretendemos eliminar el polling de TOF y controlar el temporizador a través de
su RSI. ¿Qué acciones debemos realizar para que la RSI se ejecute? (Recordar los 3
prerrequisitos vistos en el tutorial. Nombrar los bits y registros involucrados en las acciones)

Laboratorio:
El objetivo de esta sesión es escribir la RSI del timer donde se incluya todo el código que controla el
cambio de configuración de los leds. Organizaremos el trabajo de la siguiente manera:

Versión 1
1. RSI timer: Convertir la rutina retardo_250 en RSI del timer. Esta rutina será llamada por el
sistema cada vez que TOF se active, siempre y cuando hayamos sido capaces de implementar
los 3 prerrequisitos. La RSI debe contabilizar (incrementando la var. global “contador_250”)
el nº de interrupciones producidas (1 cada 250 mseg.). Implementar en la RSI todo lo
necesario para su buen funcionamiento (recordar cómo se implementaba la RSI de los
pulsadores en el tutorial).
2. Programa principal: Se encarga de la fase inicial de selección del método de acceso al array,
de inicializar el sistema de interrupciones y el temporizador. Controla (haciendo polling sobre
la variable global) si ha transcurrido el tiempo de retardo y cambia a la siguiente configuración
de leds. Controla los pulsadores. Controla también la condición de salida del programa.

Versión 2
1. RSI timer: Añadir a la RSI anterior el control del encendido de los leds. Es decir, las
instrucciones que controlan el cambio de configuración de los leds se trasladan del programa
principal a la RSI. Ojo! No podemos implantar bucles dentro de la RSI. Se ha de salir
rápidamente.
2. Programa principal: Se encarga de la fase inicial de selección del método de acceso al array,
de inicializar el sistema de interrupciones y el temporizador. Controla los pulsadores. Controla
también la condición de salida del programa.

Nota importante: En esta sesión aparece la necesidad de crear 2 vars. globales, “contador_250” y
“total_250”. La primera contabiliza el nº de interrupciones del timer producidas hasta el momento
mientras la segunda contiene el número de interrupciones necesarias para contar un determinado
tiempo. Es importantísimo entender cómo se interrelacionan las 2 variables y cómo se actualizan.

4
Interrupciones con HC12 Estructura de computadores

SESIÓN 3

Pre-laboratorio:
1. El alumno entregará al profesor, al inicio de la sesión, un documento escrito de manera legible con
la siguiente información:

• Escribir en pseudocódigo la Rutina de Servicio de Interrupción que controla los pulsadores.

Laboratorio:
El objetivo de esta sesión es escribir la RSI de los pulsadores donde se incluya todo el código que
controla el cambio de velocidades de display. Partiendo del programa de la sesión anterior, crear la RSI
de los pulsadores y obtener la siguiente estructura:

1. RSI timer: No sufre cambios respecto a la versión 2 de la sesión anterior.


2. RSI pulsadores: Debe averiguar qué pulsador se ha pulsado y actualizar la variable global
“total_250” que controla la velocidad del movimiento de los leds.
3. Programa principal: Se encarga de la fase inicial de selección del método de acceso al array,
de inicializar el sistema de interrupciones y el temporizador, espera en un bucle a que se
produzca la condición de salida, y sale del programa.

5
Interrupciones con HC12 Estructura de computadores

SESIÓN 4

Pre-laboratorio:
1. El alumno entregará al profesor, al inicio de la sesión, un documento escrito de manera legible con
la siguiente información:

• Identificar los siguientes pines del microprocesador HC12 (número y nombre):


- Los relacionados con el cristal oscilador (16 Mhz).
- El de la señal Interrupt Request.

(Buscar información en los diagramas de bloques del manual “MC9S12E-Family Device User
Guide V01.04”)

Laboratorio:
El objetivo de esta sesión es manejar el osciloscopio/analizador lógico.

1. Cristal Oscilador. Demostrar gráficamente al profesor la frecuencia del reloj base de la placa
de evaluación.
2. Interrupt request: Demostrar gráficamente al profesor la frecuencia con la que se producen las
interrupciones. Variar la velocidad mediante los pulsadores y demostrar con el osciloscopio
cómo varia dicha frecuencia.

Memoria:
El grupo de alumnos deberá entregar al profesor un informe de la práctica antes de empezar la siguiente
sesión de prácticas. Dicho informe deberá cumplir con la normativa de la asignatura a tal efecto.

6
Comunicaciones con HC12 Estructura de computadores

Objetivos
El objetivo principal de esta práctica consiste en conocer cómo funcionan las comunicaciones, tanto
serie como paralelas. Para ello utilizaremos los medios que nos proporciona el HC12. Otro de los
objetivos es gestionar las comunicaciones mediante interrupciones. Por último sería interesante
comprobar con el analizador lógico el cambio de alguna de las señales involucradas.

Introducción a las comunicaciones


En esta práctica vamos a estudiar los protocolos de comunicaciones más utilizados (hasta no hace
mucho) entre PCs y periféricos. Hasta la llegada de los conocidos USB, Bluetooth, etc, los protocolos
de comunicaciones más utilizados entre este tipo de dispositivos eran los conocidísimos serie y
paralelo. Ambos protocolos se caracterizan por sincronizar emisor y receptor después de cada byte
enviado, aunque cada uno de manera distinta. Si bien es cierto que la mayoría de estudiantes los
diferencian por el número de bits que se transmiten a la vez por el canal de comunicaciones (1 en el
serie y 8 en el paralelo), la característica diferenciadora más importante es el método de
sincronización utilizado. A continuación vamos a estudiar cómo se implementan cada uno de los
protocolos con el HC12 haciendo énfasis en el método de sincronización utilizado.

Comunicaciones Serie (protocolo RS232)


El protocolo RS232 standard define la comunicación serie asíncrona entre procesadores. En este
protocolo los bits de la información se envían uno detrás del otro a través de un único hilo (un bit a la
vez). Como la comunicación es asíncrona, ésta puede empezar en cualquier momento, y por lo tanto el
receptor del mensaje tiene que detectar de algún modo cuando empieza la transmisión del mensaje y
cuando se acaba. Para ello, el protocolo RS232 define un método por el cual el mensaje a enviar se
divide en palabras, las cuales están formadas por los siguientes campos:

bit de start: Indica el comienzo de la palabra a emitir.


bits de datos: Contienen la información a transmitir.
bit de paridad: Usado en la detección de errores.
bits de stop: Indican el final de la palabra.

1
Comunicaciones con HC12 Estructura de computadores

Tanto el emisor como el receptor necesitan saber la duración de cada bit transmitido. Para ello se utiliza
el concepto de tasa de transferencia medida en BAUDIOS. Dicha tasa define el número de bits que se
transmiten por segundo. Así, con una tasa de transferencia de 9600 Baudios, el tiempo entre el envío de
dos bits consecutivos, y el tiempo entre el muestreo de un bit y del siguiente será 1/9600 seg. = 100
useg.

Observamos como el método de sincronización (bits estart/stop) se entremezcla con los datos en el
protocolo serie. Otro punto a tener en cuenta es la necesidad de negociar emisor y receptor la tasa
de transferencia, antes de iniciar una comunicación (tiene que ser la misma). Veremos como en el
protocolo paralelo esto no es necesario.

Comunicaciones paralelas

En el protocolo paralelo los datos y las señales de sincronismo viajan por caminos separados. Por
un lado tenemos un bus de 8 bits para los datos (Data) y 2 señales de control para implementar el
método de sincronización (Avail y Busy). ¿Cómo funciona el protocolo?

1. Emisor (Microcontroler) envía un dato a Receptor (Output Device) poniendo el dato (1 byte) en
el bus de datos (Data).
2. Emisor avisa a Receptor, activando la señal ‘dato disponible’ (Avail), de que ya puede leer del
bus el nuevo dato.
3. Receptor recibe la señal ‘dato disponible’ enviada por Emisor.
4. Receptor lee el dato del bus de datos.
5. Receptor avisa a Emisor, activando la señal ‘dato recibido’ (Busy), de que ya puede enviar otro

2
Comunicaciones con HC12 Estructura de computadores

dato cuando quiera.


6. Emisor recibe la señal ‘dato recibido’ enviada por Receptor. Emisor sabe que puede empezar de
nuevo el protocolo para enviar un nuevo dato.

Como hemos podido observar en el protocolo paralelo no es necesario negociar la tasa de


transferencia, la comunicación será tan rápida como emisor sea capaz de producir los datos y receptor
sea capaz de leerlos. En el ejemplo anterior sólo aparecen comunicaciones en un sentido, de Emisor a
Receptor. Es importante resaltar que las comunicaciones pueden ser bidireccionales, siendo válido
todo lo explicado para los 2 sentidos de la comunicación. En el caso bidireccional habría que añadir 2
señales de control más y el bus de datos debería ser bidireccional. Claro está que emisor/receptor
podrían enviar datos a la vez que los reciben.

A continuación se detallan las tareas a realizar en la siguiente práctica y se os ofrecen


extractos/resúmenes de los manuales técnicos disponibles en el “Espai Virtual” de la asignatura. Para
que os hagáis una idea rápida de en qué consiste la práctica, pensar en la funcionalidad de la recién
acabada práctica 2 y añadir comunicaciones.

3
Comunicaciones con HC12 Estructura de computadores

Práctica 2: Comunicaciones serie y paralelas con el HC12


La práctica que a continuación se propone debe entregarse al final de la 8ª sesión, a lo más tardar. Para
una mejor realización de la misma se proponen unos objetivos parciales llamados sesiones. Por lo
tanto, es obligatorio entregar el pre-laboratorio antes del inicio de cada sesión así como el laboratorio
antes de finalizar la sesión. Es obligatorio también la entrega del informe de la práctica. La no
consecución de estos prerrequisitos implica suspender las prácticas. Es condición necesaria venir
a clase con los fuentes de la anterior práctica, ya que nos basaremos totalmente en ella.

Vamos a recordar, de manera sintética, la funcionalidad de la anterior práctica. Finalmente habíamos


conseguido tener:

ticks_a_contar ; var. global


ticks_contados ; var. global
salir ; var. global

1. Programa Principal.
- Fase inicial (pooling) donde se pregunta al jugador por el método de acceso a la matriz
seleccionado.
- Inicialización del Timer.
- Inicialización del Sistema de Interrupciones.
- Bucle de espera hasta que se produzca la condición de salida (var. global).

2. RSI Timer.
incrementar ticks_contados.
SI ticks_contados >= ticks_a_contar ENTONCES
ticks_contados = 0
Realizar cambio de luces (acceder a la siguiente posición de la matriz según el método de
acceso seleccionado en fase inicial).
FINSI
Limpiar flag que ha provocado la interrupción.

3. RSI Pulsadores.
SI (han sido pulsados los 2 pulsadores a la vez) ENTONCES salir = 1
SINO SI (se ha pulsado PAD4) ENTONCES ticks_a_contar +++
SINO SI (se ha pulsado PAD5) ENTONCES ticks_a_contar ---
Limpiar flag que ha provocado la interrupción.

¿Qué pretendemos en esta tercera práctica? Mantener la funcionalidad anterior e integrar en ella las
comunicaciones. ¿Cómo lo vamos a hacer? Supondremos que cada grupo tendrá 2 placas de evaluación
MC9S12E128 (HC12) conectadas por un canal de comunicaciones (serie o paralelo). La idea es que el
programa corra en las 2 HC12 a la vez. ¿Cómo? Cargando el programa (download) en una y luego en

4
Comunicaciones con HC12 Estructura de computadores

otra. Recordar que la placa guarda el programa en memoria no volátil, hecho que nos permite trabajar
con ellas aunque no estén conectadas al PC. ¿Qué debe hacer el programa? Básicamente lo mismo que
en la práctica anterior (ver pseudocódigo) salvo el siguiente matiz:

• El cambio de luces en los leds será provocado por el HC12 remoto. Esto quiere decir que
nuestro programa enviará la nueva codificación de leds al HC12 remoto, mediante el canal de
comunicaciones.

Este cambio provocará las siguientes curiosidades:

1. El método seleccionado en el HC12A se verá representado en los leds del HC12B.


2. Los cambios de velocidad que programemos en el HC12A (mediante los pulsadores) los
observaremos en los leds del HC12B.

Aunque el enunciado de la práctica podría formularse de manera más exigente pidiendo que la
comunicación fuera bidireccional en todo momento (los 2 HC12s se intercambiarían sus
codificaciones), nos conformaremos con que funcione en un solo sentido a la vez. Por otro lado,
desconocemos si se dispondrá finalmente de 2 placas HC12 por grupo. Si no es posible, el grupo
trabajará con una sola placa HC12 emulando que tiene 2. ¿Cómo? Conectando la salida (canal
de comunicaciones) de la placa a la entrada de la misma. De esta manera lo que el HC12 envíe será
recibido por él mismo. Aunque parezca mentira la funcionalidad es equivalente.

5
Comunicaciones con HC12 Estructura de computadores

Práctica 2 (1ª parte): Comunicaciones serie con el HC12


En la página anterior os hemos explicado la funcionalidad de la nueva práctica. Para poder realizarla es
necesario que aprendáis cómo se programan los puertos serie en el HC12. Para ello os ofrecemos a
continuación un breve resumen de las secciones 3 y 4 del “SCI Block Guide V03.02”.

¿Cómo se implementa un canal de comunicaciones serie? Muy sencillo. Necesitamos 1 puerto serie
(SCI) en cada extremo y un cable que los conecte (ver figura 1). Este sería el esquema a utilizar en caso
de que los grupos de prácticas tuvieran a su alcance 2 placas HC12.

TXD RXD
HC12A SCI SCI HC12B

Figura 1: Esquema con 2 placas HC12 (MC9S12E128).

En el caso de que los grupos sólo dispongan de 1 placa de evaluación HC12, teniendo en cuenta que
cada HC12 contiene 3 puertos serie bidireccionales (SCI0, SCI1, SCI2), esto es, puertos que pueden
enviar datos a la vez que reciben, varias son las alternativas de conexionado que se nos presentan (ver
figuras 2 y 3):

TXD0 TXD0
HC12A SCI0 HC12B SCI0

RXD2 RXD0
SCI2

Figura 2: Esquema con 1 HC12 y 2 puertos serie Figura 3: Esquema con 1 HC12 y 1 puerto serie

6
Comunicaciones con HC12 Estructura de computadores

Puertos serie en el HC12


La siguiente figura muestra el diagrama de bloques del puerto serie (SCI) del HC12. Observar que hay
sólo cuatro señales que entren o salgan en el puerto serie. Por un lado, las señales RXD y TXD sirven
para realizar la comunicación. Por otro lado, la señal BusClk controla la velocidad de funcionamiento
del puerto, y se corresponde con la señal de reloj del procesador. Finalmente, el puerto es capaz de
interrumpir al procesador HC12 mediante una línea de interrupción dedicada (SCI Interrupt Request).

El micro HC12 define 3 puertos series llamados SCI0, SCI1, SCI2. Cada uno de los puertos serie tiene
unos registros asociados, que sirven para controlar su funcionamiento. La siguiente tabla presenta el
nombre de estos registros, indica su posición dentro del espacio de memoria (relativa a una dirección
base asociada a cada uno de los puertos serie), e indica también si el registro es de entrada, de salida, o
de entrada/salida.

7
Comunicaciones con HC12 Estructura de computadores

Las direcciones reales de los registros de control empiezan en:

• $00C8 para SCI0BDH.


• $00D0 para SCI1BDH.
• $00E8 para SCI2BDH.

La siguiente figura muestra el diagrama de bloques detallado del puerto serie (SCI) del HC12. En él se
muestran todos los elementos del puerto serie que pueden ser programados, y que se describen con
detalle más adelante.

8
Comunicaciones con HC12 Estructura de computadores

9
Comunicaciones con HC12 Estructura de computadores

Los registros y bits de estado y de control más importantes se describen a continuación:

SCIDRL: Registro de datos del puerto serie, en el se escribe el dato (8 bits) a enviar o del que se
lee el dato recibido
SCIDBH y SCIDBL: Registros de control que permiten definir la tasa de trasferencia (Baudios) de
la comunicación serie. Esta tasa se define con 13 bits [SBR12:SBR0], donde los 5 bits más
significativos se corresponden con los bits [4:0] del registro SCIDBH, y los 8 bits menos
significativos se corresponden con los bits [7:0] del registro SCIDBL. La siguiente formula indica
el valor decimal que hay que utilizar:

SBR[12:0]= SCI module clk /(16* SCI Baud rate).

SCICR1: Registro 1 de control del puerto serie. Los siguientes bits tienen funciones específicas:
- bit M (SCICR1[4]): Longitud de los datos, 8 o 9 bits (usaremos 8 bits)
- bit PE (SCICR1[1]): Activación de la paridad (usaremos sin paridad)
- bit PT (SCICR1[0]): Tipo de paridad Al usar el protocolo sin paridad, no tiene efecto).

SCICR2: Registro 2 de control del puerto serie. Contiene el bit que se habilita la transmision de
datos (bit SCICR2[3] =1, TE, Transmit Enable), el bit que habilita la recepción de datos (bit
SCICR2[2] =1, RE, Receive Enable), y el bit que activa la interrupción de la recepción de datos (
bit SCICR2[5] =1, RIE, Receive Interrupt Enable).

SCISR1: Registro 1 de estado del puerto serie. Los bits de condición (flags) de este registro
informan del estado del puerto serie. Los bits más importantes son:
- bit RDRF (SCISR1[5]): Se activa cuando se ha completado la recepción de un dato
completo. Para desactivar el bit de condición hay que leer el registro SCISR1 y
posteriormente el registro de datos (SCIDRL)
- bit TDRE (SCISR1[7]): Se activa cuando ha acabado la transmisión de un dato y se puede transmitir
el siguiente dato. Para desactivar el bit de condición hay que leer el registro SCISR1 y posteriormente
escribir en el registro de datos (SCIDRL).

10
Comunicaciones con HC12 Estructura de computadores

La siguiente tabla muestra la posición de cada uno de los bits de condición, y del resto de campos de
los registros asociados al puerto serie.

Para más información es interesante (y en algunos casos aconsejable) consultar los manuales:
Puerto Serie (SCI's): HCS12 Serial Communications Interface (SCI) Block Guide.
Registros puertos e interrupciones HCS12: MC9S12E-Family Device User Guide.

11
Comunicaciones con HC12 Estructura de computadores

SESIONES 1 y 2

Pre-laboratorio:
1. Repasar los fuentes de la práctica anterior y no olvidar traerlos a las sesiones de laboratorio.
2. Leer atentamente toda la documentación adjunta al enunciado de esta práctica. Preguntar al profesor,
el día del laboratorio, las dudas aparecidas en esa lectura.
3. El alumno leerá con atención las secciones 3 y 4 del “SCI Block Guide V03.02”.
4. El alumno entregará al profesor, al inicio de la sesión, un documento escrito de manera legible con
la respuesta a las siguientes preguntas:

• Supongamos que queremos implementar las opciones representadas por la figura 2 y 3 de la


página 6. ¿Qué pines conectaréis para establecer el canal de comunicación en los 2 casos?
Responder nombrando el número de los pines afectados (consultar el diagrama de bloques de la
placa de evaluación MC9S12E128).
• Si leéis con atención la información adjunta relacionada con el diseño de los puertos serie
deberíais preguntaros cómo puede ser que 1 SCI tenga 2 registros de datos (1 para la emisión y
otro para la recepción) y en cambio sólo exista 1 registro accesible por el programador
(SCIDRH-SCIDRL). ¿Cómo se explica eso? Explicar también para qué se utilizan los registros
de datos desde el punto de vista del programador.
• Supongamos que la frecuencia base del reloj (SCI Module clk) es de 16 MHz. Si queremos
programar un Baud Rate = 8192 baudios, ¿Cuál es el valor que debo programar en los registros
de control preparados para este fin? Escribir también el valor inicial de cada uno de los
registros de la figura 3-1 (hacer una fotocopia de la figura y escribir dentro de cada bit el valor
que programaríais en la fase de inicialización, poniendo 0, 1 o X si tanto da.
• En la figura 3 pág. 6 habéis observado como se conecta de manera externa (con un cable) la
salida del puerto serie 0 (TXD0) con la entrada del mismo (RXD0). Existe la manera de hacer
esa misma conexión de manera interna, es decir, sin cable. Explicar cómo puede hacerse esto
describiendo cómo debe programarse un registro de control (leer sección 3 “SCI Block Guide
V03.02”).
• ¿Cuál es la dirección del vector de interrupción para SCI0?

12
Comunicaciones con HC12 Estructura de computadores

Laboratorio:
Aunque el objetivo de la práctica ha quedado ya explicado en las introducciones anteriores a
continuación se describe, mediante pseudocódigo, la funcionalidad que debe cumplir esta entrega (en
rojo encontrareis las nuevas funcionalidades respecto a la práctica anterior).

ticks_a_contar ; var. global


ticks_contados ; var. global
salir ; var. global

1. Programa Principal.
- Fase inicial (pooling) donde se pregunta al jugador por el método de acceso a la matriz
seleccionado.
- Inicialización del Timer.
- Inicialización del Sistema de Interrupciones.
- Bucle de espera hasta que se produzca la condición de salida (var. global).
- Inicialización del puerto/puertos serie (SCI).

2. RSI Timer.
incrementar ticks_contados.
SI ticks_contados >= ticks_a_contar ENTONCES
ticks_contados = 0
Cambio de luces (acceder a la siguiente posición de la matriz según el método de acceso
seleccionado en fase inicial). Hay que eliminar la escritura en los leds locales y cambiar
esta acción por una escritura en el registro de datos de envío del puerto serie.
FINSI
Limpiar flag que ha provocado la interrupción.

3. RSI Pulsadores.
SI (han sido pulsados los 2 pulsadores a la vez) ENTONCES salir = 1
SINO SI (se ha pulsado PAD4) ENTONCES ticks_a_contar +++
SINO SI (se ha pulsado PAD5) ENTONCES ticks_a_contar ---
Limpiar flag que ha provocado la interrupción.

4. RSI SCI0.
SI hay varios motivos de interrupción asociados al SCI= controlarlos.
Leer del registro de datos de recepción del puerto serie.
Displayar en los leds el valor recibido.
Limpiar flag que ha provocado la interrupción.

13
Comunicaciones con HC12 Estructura de computadores

El alumno deberá entregar funcionando una práctica con esta funcionalidad según los esquemas
siguientes:

• 1 HC12 con 1 puerto serie sin cablear.


• 1 HC12 con 1 puerto serie cableado.
• (si hubiera disponibilidad de placas) 2 placas HC12 cableadas.

14
Comunicaciones con HC12 Estructura de computadores

Práctica 2 (2ª parte): Comunicaciones paralelas con el HC12


Desde el punto de vista del programador es mucho más fácil implementar un protocolo paralelo que
uno serie. Si nos fijamos bien en el layout (diagrama de bloques) del MC9S12E128 podremos observar
como todos los puertos de E/S son paralelos de 8 bits y bidireccionales bit a bit (PTA, PTB, PTE,
PTK, PTP, PTQ, PTS, PTT, PTU, PTM y PAD). Ojo con lo de “bidireccionales”! Así como en el
puerto serie existe la posibilidad de enviar y recibir a la vez, debido a que existen 2 pines diferentes
(uno para entrada y otro para salida), en los puertos paralelos esto no es así. Sólo existen 8 pines. Debe
configurarse de antemano que role jugará cada pin, entrada o salida. Como ya sabéis, esto se hace
mediante el registro DDR correspondiente.

¿Cómo se implementa un canal de comunicaciones paralelo? Muy sencillo. Necesitamos 1 puerto


paralelo en cada extremo y un cable que los conecte (ver figura 1). Además, para implementar la
sincronización entre emisor y receptor (handshaking), necesitamos 2 líneas más: “dato disponible” y
“dato recibido”. En el caso de que ambas partes pudieran enviar y recibir deberíamos añadir 2 líneas
más de handshaking y convertir el bus de datos (cable) en bidireccional. Este sería el esquema a
utilizar en caso de que los grupos de prácticas tuvieran a su alcance 2 placas HC12.

PTA Bus Datos PTA


HC12A HC12B

PTS PTS
Dato enviado
PAD1 PAD6
Dato recibido (ack)
PAD2 PAD7
PAD7 PAD2
PAD6 PAD1

Figura 1: Esquema con 2 placas HC12 (MC9S12E128).

En el caso de que los grupos sólo dispongan de 1 placa de evaluación HC12 realizaremos el siguiente
conexionado (ver figura 2). Como puede observarse la comunicación no es bidireccional!

15
Comunicaciones con HC12 Estructura de computadores

Datos
PTA
HC12

PTS

PAD2
Dato enviado
PAD1
PAD6
PAD7
ACK

Figura 2: Esquema con 1 placa HC12 (MC9S12E128).

Información de utilidad (direcciones de puertos y otros detalles)

Puerto A
• PORTA (Datos): 0000H
• DDRA (Dirección datos): 0002H

Puerto S
• PORTS (Datos): 0248H
• DDRS(Dirección datos): 024AH

Puerto AD
• ATDDIEN1 (Habilita entrada): 008DH
• PTADLo (Datos): 0271H
• DDRADLo (Dirección datos ): 0275H
• PERADLo (Alimentación de entrada): 0279H
• PIEADLo (Habilita interrupciones): 027DH
• PIFADLo (Registro de flags): 027FH

Recordatorio para programar la dirección de los datos:


DDRX[i]=1 --> PORTX[i] es de salida
DDRX[i]=0 --> PORTX[i] es de entrada.
Donde X identifica al puerto (A,B,S,..) e i es el bit de dicho puerto.

¿Cómo activar una señal de sincronización? (generar un pulso en la señal ACK)


PAD7 = 0;
NOP;
NOP;
PAD7 = 1; Fijémonos que la lógica utilizada es negada.
16
Comunicaciones con HC12 Estructura de computadores

SESIONES 3 y 4

Pre-laboratorio:
1. Leer atentamente toda la documentación adjunta al enunciado de esta práctica. Preguntar al profesor,
el día del laboratorio, las dudas aparecidas en esa lectura.
2. El alumno entregará al profesor, al inicio de la sesión, un documento escrito de manera legible con
la respuesta a las siguientes preguntas:

• En una configuración con 1 placa de evaluación, ¿Qué pines conectaréis para establecer el
canal de comunicación? Responder nombrando el número de los pines afectados (consultar el
diagrama de bloques de la placa de evaluación MC9S12E128).
• En la versión anterior, la serie, habíamos tenido 3 RSIs diferentes. ¿Cuántas necesitamos en la
versión paralela? ¿Cuáles son?
• Una de las RSIs tendrá 4 motivos de interrupción. ¿Cuál es? Describe cada uno de los motivos.
• ¿Creéis que el handshaking es necesario en nuestra práctica? ¿Por qué? Razonar la respuesta de
manera detallada (observar cómo funciona el flujo de los datos!).

Laboratorio:
La funcionalidad de la entrega que se os pide es equivalente a la realizada con el protocolo de
comunicación serie, sólo que modificada convenientemente para utilizar ahora el protocolo paralelo en
vez del serie. A continuación se describe, mediante pseudocódigo, la funcionalidad que debe cumplir
esta entrega:

ack ; var. global


ticks_a_contar ; var. global
ticks_contados ; var. global
salir ; var. global

1. Programa Principal.
- Fase inicial (pooling) donde se pregunta al jugador por el método de acceso a la matriz
seleccionado.
- Fase de Inicialización (Timer, puertos de comunicaciones, etc)
- Inicialización del Sistema de Interrupciones.
- Bucle de espera hasta que se produzca la condición de salida (var. global).
ack = 0

2. RSI Timer.
incrementar ticks_contados.
SI ticks_contados >= ticks_a_contar ENTONCES
ticks_contados = 0
SI ack = =0 ENTONCES

17
Comunicaciones con HC12 Estructura de computadores

ack=1
leer siguiente codificación de luces y enviar por el canal de comunicaciones.
activar señal de control “Dato enviado”.
desactivar señal de control “Dato enviado”.
FINSI
FINSI
Limpiar flag que ha provocado la interrupción.

3. RSI PAD.
SI (han sido pulsados los 2 pulsadores a la vez) ENTONCES salir = 1
SI (se ha pulsado PAD4) ENTONCES ticks_a_contar +++
SI (se ha pulsado PAD5) ENTONCES ticks_a_contar ---
SI (se ha activado PAD6) ENTONCES
leer byte recibido y displayarlo en los leds
activar señal de control “ack”.
desactivar señal de control “ack”.
FINSI
SI (se ha activado PAD2) ENTONCES ack = 0
Limpiar flag que ha provocado la interrupción.

Memoria:
El grupo de alumnos deberá entregar al profesor un informe de la práctica antes de la fecha indicada.
Dicho informe deberá cumplir con la normativa de la asignatura a tal efecto.

18
EE 308 Spring 2006

THE STACK AND THE STACK POINTER

• Sometimes it is useful to have a region of memory for temporary storage,


which does not have to be allocated as named variables.
• When we use subroutines and interrupts it will be essential to have such
a storage region.
• Such a region is called a Stack.
• The Stack Pointer (SP) register is used to indicate the location of the
last item put onto the stack.
• When you put something onto the stack (push onto the stack), the SP is
decremented before the item is placed on the stack.
• When you take something off of the stack (pull from the stack), the SP
is incremented after the item is pulled from the stack.
• Before you can use a stack you have to initialize the Stack Pointer to
point to one value higher than the highest memory location in the stack.
• For the HC12 use a block of memory from about $3B00 to $3BFF for the
stack.
• For this region of memory, initialize the stack pointer to $3C00.
• Use the LDS (Load Stack Pointer) instruction to initialize the stack
point.
• The LDS instruction is usually the first instruction of a program which
uses the stack.
• The stack pointer is initialized only one time in the program.
• For microcontrollers such as the HC12, it is up to the programmer to
know how much stack his/her program will need, and to make sure
enough space is allocated for the stack. If not enough space is allo-
cated the stack can overwrite data and/or code, which will cause the
program to malfunction or crash.

1
EE 308 Spring 2006

The stack is an array of memory dedicated to temporary storage

SP points to location last item


placed in block

SP decreases when you put item on stack

SP increases when you pull item from stack


0x3AF5

0x3AF6 For HC12 EVBU, use 0x3C00 as initial SP:

0x3AF7 STACK: EQU $3C00


LDS #STACK
0x3AF8

0x3AF9
A B
0x3AFA
D
0x3AFB

0x3AFC X

0x3AFD
Y
0x3AFE

0x3BFF SP

0x3B00
PC
0x3B01

0x3B02 CCR

0x3B03

2
EE 308 Spring 2006

An example of some code which uses the stack

Stack Pointer:

Initialize ONCE before first use (LDS #STACK)

Points to last used storage location

Decreases when you put something on stack

Increases when you take something off stack

STACK: equ $3C00

0x3BF5
CODE: section .text
0x3BF6 org 0x1000

0x3BF7 lds #STACK


0x3BF8 ldaa #$2e
ldx #$1254
0x3BF9 psha
pshx
0x3BFA
clra
0x3BFB ldx #$ffff

0x3BFC CODE THAT USES A & X

0x3BFD pulx
pula
0x3BFE

0x3BFF A

0x3C00
X

SP

3
EE 308 Spring 2006

Core User Guide — S12CPU15UG V1.2

PSHA Push A onto Stack PSHA


Operation (SP) – $0001 ⇒ SP
(A) ⇒ MSP
Decrements SP by one and loads the value in A into the address to which SP points.
Push instructions are commonly used to save the contents of one or more CPU registers at
the start of a subroutine. Complementary pull instructions can be used to restore the saved
CPU registers just before returning from the subroutine.

CCR
Effects S X H I N Z V C
– – – – – – – –

Code and
CPU Source Form Address Machine CPU Cycles
Mode Code (Hex)
Cycles
PSHA INH 36 Os

439

4
EE 308 Spring 2006

Subroutines

• A subroutine is a section of code which performs a specific task, usually


a task which needs to be executed by different parts of a program.
• Example:
– Math functions, such as square root
• Because a subroutine can be called from different places in a program,
you cannot get out of a subroutine with an instruction such as

jmp label

because you would need to jump to different places depending upon


which section of code called the subroutine.
• When you want to call the subroutine your code has to save the address
where the subroutine should return to. It does this by saving the return
address on the stack.
– This is done automatically for you when you get to the subroutine by
using the JSR (Jump to Subroutine) or BSR (Branch to Subroutine)
instruction. This instruction pushes the address of the instruction
following the JSR (BSR) instruction on the stack.
• After the subroutine is done executing its code it needs to return to the
address saved on the stack.
– This is done automatically for you when you return from the sub-
routine by using the RTS (Return from Subroutine) instruction. This
instruction pulls the return address off of the stack and loads it into
the program counter, so the program resumes execution of the pro-
gram with the instruction following that which called the subroutine.
The subroutine will probably need to use some HC12 registers to do its
work. However, the calling code may be using its registers for some rea-
son — the calling code may not work correctly if the subroutine changes
the values of the HC12 registers.

5
EE 308 Spring 2006

– To avoid this problem, the subroutine should save the HC12 registers
before it uses them, and restore the HC12 registers after it is done
with them.

6
EE 308 Spring 2006

Core User Guide — S12CPU15UG V1.2

BSR Branch to Subroutine BSR


Operation (SP) – $0002 ⇒ SP
RTNH:RTNL ⇒ MSP:MSP + 1
(PC) + $0002 + rel ⇒ PC
Sets up conditions to return to normal program flow, then transfers control to a subroutine.
Uses the address of the instruction after the BSR as a return address.
Decrements the SP by two, to allow the two bytes of the return address to be stacked.
Stacks the return address (the SP points to the high byte of the return address).
Branches to a location determined by the branch offset.
Subroutines are normally terminated with an RTS instruction, which restores the return
address from the stack.

CCR
Effects S X H I N Z V C
– – – – – – – –

Code and
CPU Source Form Address Machine CPU Cycles
Mode Code (Hex)
Cycles
BSR rel8 REL 07 rr SPPP

333

7
EE 308 Spring 2006

Core User Guide — S12CPU15UG V1.2

RTS Return from Subroutine RTS


Operation (MSP):(MSP + 1) ⇒ PCH:PCL
(SP) + $0002 ⇒ SP
Restores the value of PC from the stack and increments SP by two. Program execution
continues at the address restored from the stack.

CCR
Effects S X H I N Z V C
– – – – – – – –

Code and
CPU Source Form Address Machine CPU Cycles
Mode Code (Hex)
Cycles
RTS INH 3D UfPPP

463

Example of a subroutine to delay for a certain amount of time

; Subroutine to wait for 100 ms

8
EE 308 Spring 2006

delay: ldaa #250


loop2: ldx #800
loop1: dex
bne loop1
deca
bne loop2
rts

• Problem: The subroutine changes the values of registers A and X

• To solve, save the values of A and X on the stack before using them, and
restore them before returning.

; Subroutine to wait for 100 ms

delay: psha ; Save regs used by sub on stack


pshx
ldaa #250
loop2: ldx #800
loop1: dex
bne loop1
deca
bne loop2
pulx ; Restore regs in opposite
pula ; order
rts

9
EE 308 Spring 2006

; Program to make a binary counter on LEDs


;
; The program uses a subroutine to insert a delay
; between counts

prog: equ $1000


STACK: equ $3C00 ;Stack ends of $3BFF
PORTA: equ $0000
PORTB: equ $0001
DDRA: equ $0002
DDRB: equ $0003

org prog

lds #STACK ; initialize stack pointer


ldaa #$ff ; put all ones into DDRA
staa DDRA ; to make PORTA output
clr PORTA ; put $00 into PORTA
loop: jsr delay ; wait a bit
inc PORTA ; add one to PORTA
bra loop ; repeat forever

; Subroutine to wait for 100 ms

delay: psha
pshx
ldaa #250
loop2: ldx #800
loop1: dex
bne loop1
deca
bne loop2
pulx
pula
rts

10
EE 308 Spring 2006

JSR and BSR place return address on stack


RTS returns to instruction after JSR or BSR

STACK: EQU $3C00


ORG $1000
1000 CF 3C 00 LDS #STACK
1003 16 10 07 JSR MY_SUB
0x3AF5
1006 7F SWI
0x03A6 1007 CE 12 34 MY_SUB: LDX #$1234
0x3AF7 100A 3D RTS

0x3AF8

0x3AF9
A B
0x3AFA
D
0x3AFB

0x3AFC X

0x3AFD
Y
0x3AFE

0x3AFF SP

0x3B00
PC
0x3B01

0x3B02 CCR

0x3B03

Another example of using a subroutine

Using a subroutine to wait for an event to occur, then take an action.

11
EE 308 Spring 2006

• Wait until bit 7 of address $00C4 is set.


• Write the value in ACCA to address $00C7.

; This routine waits until the HC12 serial


; port is ready, then sends a byte of data
; to the HC12 serial port

putchar: brclr $00CC,#$80,putchar


staa $00CF
rts

• Program to send the word hello to the HC12 serial port

; Program fragment to write the word "hello" to the


; HC12 serial port

ldx $str
loop: ldaa 1,x+ ; get next char
beq done ; char == 0 => no more
jsr putchar
bra loop
swi

str: dc.b "hello"


fc.b $0A,$0D,0 ; CR LF

12
EE 308 Spring 2006

Here is the complete program to write a line to the screen:

prog: equ $1000


data: equ $2000
stack: equ $3c00

org prog
lds #stack
ldx #str
loop: ldaa 1,x+ ; get next char
beq done ; char == 0 => no more
jsr putchar
bra loop
done: swi

putchar: brclr $00CC,$80,putchar


staa $00CF
rts

org data
str: fcc "hello"
dc.b $0a,$0d,0 ; CR LF

13
EE 308 Spring 2006

Using DIP switches to get data into the HC12

• DIP switches make or break a connection (usually to ground)

DIP Switches on Breadboard

+5V

14
EE 308 Spring 2006

• To use DIP switches, connect one end of each switch to a resistor


• Connect the other end of the resistor to +5 V
• Connect the junction of the DIP switch and the resistor to an input port
on the HC12

Using DIP Switches

+5V +5V +5V +5V

PB1
PB0

+5V

• When the switch is open, the input port sees a logic 1 (+5 V)
• When the switch is closed, the input sees a logic 0 (0 V)

15
EE 308 Spring 2006

Looking at the state of a few input pins

• Want to look for a particular pattern on 4 input pins


– For example want to do something if pattern on PB3-PB0 is 0110
• Don’t know or care what are on the other 4 pins (PB7-PB4)
• Here is the wrong way to do it:

ldaa PORTB
cmpa #b0110
beq task

• If PB7-PB4 are anything other than 0000, you will not execute the task.
• You need to mask out the Don’t Care bits before checking for the pattern
on the bits you are interested in

ldaa PORTB
anda #b00001111
cmpa #b00000110
beq task

• Now, whatever pattern appears on PB7-4 is ignored

16
EE 308 Spring 2006

Using an HC12 output port to control an LED

• Connect an output port from the HC12 to an LED.

Using an output port to control an LED

PA0

Resistor, LED, and


ground connected
internally inside
breadboard

When a current flows


through an LED, it
emits light

17
EE 308 Spring 2006

Making a pattern on a seven-segment LED

• Want to generate a particular pattern on a seven-segment LED:

f b
g

e c
d

• Determine a number (hex or binary) which will generate each element of


the pattern
– For example, to display a 0, turn on segments a, b, c, d, e and
f, or bits 0, 1, 2, 3, 4 and 5 of PTH. The binary pattern is 00111111,
or $3f.
– To display 0 2 4 6 8, the hex numbers are $3f, $5b, $66, $7d,
$7f.
• Put the numbers in a table
• Go through the table one by one to display the pattern
• When you get to the last element, repeat the loop

18
EE 308 Spring 2006

Flowchart to display a pattern of lights on a set of LEDs

0x3f X START
table
0x5b

0x66 PORTA ldaa #$ff


Output staa DDRA
l1:
0x7d
Point to ldx #table
table_end 0x7f
first entry
l2:

Get entry ldaa 0,x

Output to
PORTA staa PORTA

Inc
Pointer inx

cpx #table_end
X < end? bls l2

bra l1

19
EE 308 Spring 2006

; Program using subroutine to make a time delay

prog: equ $1000


data: equ $2000
stack: equ $3C00
PTH: equ $0260
DDRH: equ $0262

org prog

lds #stack ; initialize stack pointer


ldaa #$ff ; Make PTH output
staa DDRH ; 0xFF -> DDRH
l1: ldx #table ; Start pointer at table
l2: ldaa 1,x+ ; Get value; point to next
staa PTH ; Update LEDs
jsr delay ; Wait a bit
cpx #table_end ; More to do?
bls l2 ; Yes, keep going through table
bra l1 ; At end; reset pointer

delay: psha
pshx
ldaa #250
loop2: ldx #8000
loop1: dex
bne loop1
deca
bne loop2
pulx
pula
rts

org data
table: dc.b $3f
dc.b $5b
dc.b $66
dc.b $7d
table_end: dc.b $7F

20

También podría gustarte