ComputerArchitecture-HC9S08-Uribe-d.pdf

March 25, 2018 | Author: Cesar Fernandez | Category: Bit, Computer Memory, Central Processing Unit, Technology, Computing


Comments



Description

ING. LUIS G. URIBE C.Computer Architecture: The MC9S08 Primera Edición Septiembre de 2014, V1.3d ING. LUIS GUILLERMO URIBE CATAÑO Computer Architecture: The MC9S08  2014 Ing. Luis G. Uribe C. Caracas, Venezuela i Contenido PRÓLOGO ........................................................................................................................................................................... 5   ABSTRACT............................................................................................................................................................. 5 INTRODUCCIÓN ................................................................................................................................................... 6 ARQUITECTURA DEL COMPUTADOR: QUÉ DEBEMOS ESTUDIAR .................................................................. 6 Enfoque ......................................................................................................................................................................... 7 Observaciones sobre algunos términos empleados ...................................................................................................... 7 VON NEUMANN COMPUTERS....................................................................................................................................... 8  INTRODUCCIÓN ................................................................................................................................................... 8 QUÉ ES UNA INSTRUCCIÓN ..................................................................................................................................... 8  MÁQUINAS DE 3, 2, 1 Y 0 DIRECCIONES ................................................................................................................... 10  HCS08 ARCHITECTURE: PROGRAMMING MODEL...................................................................................... 11 MODOS DE DIRECCIONAMIENTO EN EL MC9S08 (CPU08RM Reference Manual)........................................... 11 VARIACIONES Y OPTIMIZACIONES ....................................................................................................................... 12  CODIDAC, VON URIBE’S COMPUTER.................................................................................................................... 14 LA ARQUITECTURA ................................................................................................................................................. 14 LAS INSTRUCCIONES............................................................................................................................................... 14 LOS MODIFICADORES ............................................................................................................................................ 14 EL SUBSISTEMA DE ENTRADA Y SALIDA.............................................................................................................. 14 CÓDIGOS DE OPERACIÓN ..................................................................................................................................... 14 EL EDITOR ................................................................................................................................................................ 15 EL FORMATO DE LOS NÚMEROS .......................................................................................................................... 15 LA CALCULADORA................................................................................................................................................... 15 LA ESTRUCTURA ...................................................................................................................................................... 15 DATOS ESTADÍSTICOS............................................................................................................................................. 15 CIRCUITOS IMPRESOS ............................................................................................................................................ 15 FOTOS........................................................................................................................................................................ 16 ASSEMBLER HCS08 ........................................................................................................................................................ 37  EJERCICIOS BÁSICOS EN ASSEMBLY LANGUAGE .......................................................................................... 37 1) Primer Programa para el HCS08: MINIMUM Program for HCS08..................................................................... 37 COMENTARIOS a ["Laboratorios\Lab1\00a_L1.asm"]:........................................................................................... 38 2) Segundo Programa para el HCS08: Init COP & STACK ...................................................................................... 40 COMENTARIOS a ["Laboratorios\Lab1\00c_L1.asm"]:........................................................................................... 41 3) Programación en Lenguaje de Máquina (HEX) para el HCS08:........................................................................... 42 COMENTARIOS a ["Laboratorios\Lab1\01a_L1.asm"]:........................................................................................... 43 4) Invente su propio Instruction Set para el HCS08:.................................................................................................. 46 COMENTARIOS a ["Laboratorios\Lab1\01b_L1.asm"]:........................................................................................... 47 5) Usando los OpCodes del Fabricante para el HCS08:............................................................................................ 47 COMENTARIOS a ["Laboratorios\Lab1\01c_L1.asm"]:........................................................................................... 48       6) Forma DEFINITIVA para programar el HCS08: .................................................................................................. 49 COMENTARIOS a ["Laboratorios\Lab1\01d_L1.asm"]:........................................................................................... 50 7) CODE SECTION, DATA SECTION, en el HCS08:................................................................................................ 50 COMENTARIOS a ["Laboratorios\Lab1\01e_L1.asm"]:........................................................................................... 51 PROGRAMAS AVANZADOS EN ASSEMBLY LANGUAGE, PARTE I ......................................................... 54 8) Serie de Fibonacci.................................................................................................................................................. 54 COMENTARIOS a ["Laboratorios\Lab1\02Fibonacci.asm"]:................................................................................... 56 9) Empleo de Subrutinas; Identificación de Modos de Direccionamiento.................................................................. 56 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03a_L1_M.asm"]:....................................................................... 58 10) Introducción a las INTERRUPCIONES ............................................................................................................... 58 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03b_L1_M.asm"]:....................................................................... 61 11) Ejercicio con más Aspectos Nuevos: .................................................................................................................... 63 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03c_L1_M.asm"]:....................................................................... 65 12) Ejercicio con más Aspectos Nuevos aún: ............................................................................................................. 67 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03d_L1_M.asm"]:....................................................................... 70 13) Aspectos Cosméticos en el trato de los Vectores de Interrupción:....................................................................... 71 14) Ejemplo: Rutina de MULTIPLICAR, por Sumas Sucesivas: ................................................................................ 72 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\05a_L1_M.asm"]:....................................................................... 73 15) "SUBRUTINA" de MULTIPLICAR, por sumas sucesivas:................................................................................... 74 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\05b_L1_M.asm"]:....................................................................... 75 16) Ejemplo PRIMORDIAL del uso de Variables Dinámicas: ................................................................................... 76 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\05d_L1_M.asm"]:....................................................................... 80 PROGRAMAS AVANZADOS EN ASSEMBLY LANGUAGE, PARTE II........................................................ 84 17) Torres de Hanoi, MÍNIMO, en C: ........................................................................................................................ 84 COMENTARIOS a ["Temarios\Ex#1-HanoiMin\HanoiMin--.c"]: .......................................................................... 85 18) Torres de Hanoi, MÍNIMO, en ASSEMBLY LANGUAGE: .................................................................................. 86 A) Programa HanoiMin--.asm: ................................................................................................................................. 87 B) Include File HanoiMin_.inc:................................................................................................................................. 88 COMENTARIOS a ["Temarios\Ex#1-HanoiMin\HanoiMin--.asm"]: ........................................................................ 90 COMENTARIOS a ["Temarios\Ex#1-HanoiMin\hanoiMin_.inc"]: ........................................................................... 91 TIMERS.................................................................................................................................................................. 93 NÚMERO DE TIMERS: ............................................................................................................................................. 93 OPERACIÓN Y USO DE LA BIBLIOTECA DE TIMERS:......................................................................................... 94 19) Timers for Windows (timers.cpp): ........................................................................................................................ 95 COMENTARIOS a ["Laboratorios\Lab2\_New\timers_.h"]: ..................................................................................... 99 COMENTARIOS a ["Laboratorios\Lab2\_New\timers.cpp"]: ................................................................................. 100 COMENTARIOS a ["Laboratorios\Lab2\_New\timtst2.cpp"]: ................................................................................ 101 20) Librería de TIMERS para el HC9S08 (timers8HS.inc): ..................................................................................... 101 COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\timers8HS.inc"]:................................................................... 104 21) Ejemplo del uso de la Librería de TIMERS (timer8HS.asm): ............................................................................ 108 COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\timer8HS.asm"]: .................................................................. 111 22) "WaitISR_on" Interactuando con la "IRQISR" .................................................................................................. 111 COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\tim8_IRQ-HS.asm"]:............................................................ 114 23) Enciende los 8 LEDs con Diferentes Intervalos ................................................................................................. 116 COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\TimedFlash8.asm"]:............................................................. 119 24) Enciende ORDENADAMENTE los 8 LEDs con Diferentes Intervalos .............................................................. 120 Finalización Tema TIMERS...................................................................................................................................... 123 PROGRAMAS AVANZADOS EN ASSEMBLY LANGUAGE, PARTE III .................................................... 123 COLAS DE DATOS .............................................................................................................................................. 126 25) BIBLIOTECA de COLAS: Que.inc ..................................................................................................................... 129 COMENTARIOS a ["Laboratorios\Lab3\SciComm\Que.inc"]: ............................................................................... 133 "STRUCT" in ASSEMBLY LANGUAGE................................................................................................................... 134 26) Programa Elemental para Probar la Librería de COLAS ................................................................................. 139 COMENTARIOS a ["Laboratorios\Lab3\SciComm\4quetst.asm"] .......................................................................... 141 COMUNICACIONES SERIALES......................................................................................................................... 143 27) LIBRERÍA de COMUNICACIONES SERIALES: SciComm.inc ......................................................................... 143 COMENTARIOS a ["Laboratorios\Lab3\SciComm\SciComm.inc"]:....................................................................... 147 28) Transmisión. ....................................................................................................................................................... 158 COMENTARIOS a ["Laboratorios\Lab3\SciComm\1tstXmt.asm"].......................................................................... 160 29) ECHO. ................................................................................................................................................................ 160 COMENTARIOS a ["Laboratorios\Lab3\SciComm\2echo1.asm"]:......................................................................... 162 30) Transmisión por INTERRUPCIONES. ............................................................................................................... 163 COMENTARIOS a ["Laboratorios\Lab3\SciComm\3echoInt-1.asm"]: ................................................................... 165 31) Cuarto Programa de COMUNICACIONES: Flash LEDs.................................................................................. 168 COMENTARIOS a ["Laboratorios\Lab3\SciComm\3echoInt-1Leds.asm"]:............................................................ 170 32) Programas de COMUNICACIONES que USTED Debe Hacer: ........................................................................ 173  PROGRAMAS VARIOS EN ASSEMBLY LANGUAGE .................................................................................. 174 33) BITCOPY, MACRO y Ejemplo. .......................................................................................................................... 174 COMENTARIOS a ["Laboratorios\Proy\Buzzer\BitCopy.asm"]: ............................................................................ 178 34) USO Básico del ADC, y Display en LEDs.......................................................................................................... 180 COMENTARIOS a ["Laboratorios\Proy\Buzzer\ADC.asm"]................................................................................... 182 35) Uso Básico del ADC, Reversar Datos Antes de ir a los LEDs. .......................................................................... 184 COMENTARIOS a ["Laboratorios\Proy\Buzzer\ADC_Reverse.asm"]:................................................................... 187 36) Contador de UNOS............................................................................................................................................. 189 COMENTARIOS a ["Books\Interfacing-HCS08\Examples\bitcount3.asm"]: .......................................................... 190 37) Programación de un TONO en el BUZZER. ...................................................................................................... 191 COMENTARIOS a ["Laboratorios\Proy\Buzzer\Buzzer.asm"]: .............................................................................. 193 38) Un "WALKMAN" Elemental............................................................................................................................... 195 COMENTARIOS a ["Laboratorios\Proy\Buzzer\Walkman.asm"]: .......................................................................... 199 39) "SWITCH", vía "COMPUTED GOTO".............................................................................................................. 202 COMENTARIOS a ["Laboratorios\FSM-FiniteStateMachines\ComputedGoTo.asm"]:.......................................... 204 40) La FÁBRICA de CHOCOLATES........................................................................................................................ 207 41) TABLAS de DATOS; Implementación del "FOR". ............................................................................................. 209 COMENTARIOS a ["Laboratorios\Tables\Tables0.asm"] ....................................................................................... 211 42) Last, but not Least: 8 Bits Rotate Left. ............................................................................................................... 212 COMENTARIOS a ["Evaluaciones\2013-01Ene\Evaluaciones\Ex#1\Rotate8.asm"]:............................................. 213 PROGRAMACIÓN EN "C"........................................................................................................................................... 215    NOTAS INICIALES ............................................................................................................................................ 215 DÓNDE ESTAMOS ............................................................................................................................................ 215 GENERALIDADES ............................................................................................................................................ 216 43) Programa INTRODUCTORIO en C, para HCS08, Comparativo con ASM. ..................................................... 216 COMENTARIOS a ["Labs-C\Fibonacci\060Fibonacci.c"]:..................................................................................... 217 ["Labs-C\Fibonacci\Fibonacci_.h"]: ....................................................................................................................... 219 COMENTARIOS a ["Labs-C\Fibonacci\Fibonacci_.h"]: ........................................................................................ 220 44) FIBONACCI PROGRAMADO EN C, NORMALMENTE, PARA COMPARAR ................................................. 221 COMENTARIOS a ["Labs-C\Fibonacci\090FibonacciOK.c"]: ............................................................................... 221 45) EXPONENCIACIÓN POR MULTIPLICACIONES Y SUMAS SUCESIVAS: WHILE ....................................... 222 COMENTARIOS a ["Labs-C\Lab1\010Lab0e-1.c"]: ............................................................................................... 222 46) EXPONENCIACIÓN, MULTIPLICACIONES Y SUMAS SUCESIVAS: FUNCIONES ..................................... 222 COMENTARIOS a ["Labs-C\Lab1\020Lab0e-2Sub.c"]: ......................................................................................... 223 47) EXPONENCIACIÓN, FUNCIONES: PARA VISUAL STUDIO ......................................................................... 223 COMENTARIOS a ["Labs-C\Lab1\030Lab0e-2SubVisualStudio.c"]: ..................................................................... 224 48) PRIMER PROGRAMA PARA HCS08: MINIMUM C PROGRAM..................................................................... 224 COMENTARIOS a ["Labs-C\lab1\00a_l1.c"]:......................................................................................................... 224 49) PROGRAMA EN C PARA HCS08, UN POCO MÁS ÚTIL ................................................................................ 225 COMENTARIOS a ["Labs-C\Lab1\01c_L1.c"]:....................................................................................................... 226 50) "INTERRUPTS" EN EL LENGUAJE C.............................................................................................................. 227 COMENTARIOS a ["Labs-C\Lab1\03b_L1_M.c"]: ................................................................................................. 229 EL INCLUDE FILE "SEVERAL_U.H":.................................................................................................................... 232 COMENTARIOS a ["Labs-C\Lab1\several_U.h"]: .................................................................................................. 232 OBSERVACIÓN: ...................................................................................................................................................... 233 51) "Interrupts" en el lenguaje C, variación............................................................................................................. 234  MANEJO DE TIMERS ......................................................................................................................................... 234 52) LIBRERÍA DE TIMERS: timersJ.c ..................................................................................................................... 234 COMENTARIOS a ["Labs-C\Lab2\TimersJ\timersJ.c"]: ......................................................................................... 238 COMENTARIOS a ["Labs-C\Lab2\TimersJ\timersJ_.h"]:....................................................................................... 242 53) EJEMPLO #1 USANDO LIBRERÍA DE TIMERS .............................................................................................. 243 COMENTARIOS a ["Labs-C\Lab2\TimersJ\100TimedFlash8.c"] ........................................................................... 245 54) EJEMPLO #2 USANDO LIBRERÍA DE TIMERS .............................................................................................. 247 COMENTARIOS a ["Labs-C\Lab2\TimersJ\110TimedFlash8X4.c"]: ..................................................................... 249  SERIAL COMMUNICATIONS & DATA QUES IN C............................................................................................ 250 55) LIBRERÍA DE COMUNICACIONES SERIALES............................................................................................... 250 COMENTARIOS a ["Labs-C\Lab3\SciComm\SciComm.h"]:................................................................................... 253 56) Send 'A' to 'z' letters for ever, to PC ................................................................................................................... 257 COMENTARIOS a ["Labs-C\Lab3\SciComm\1tstXmt.c"]: ...................................................................................... 258 57) SEND STEP BY STEP (IRQ) 'A' TO 'Z' LETTERS TO PC & SHOW IN LEDS ................................................. 258 COMENTARIOS a ["Labs-C\Lab3\SciComm\1tstXmt-Leds.c"]: ............................................................................. 260 58) ECHO ................................................................................................................................................................. 262 COMENTARIOS a ["Labs-C\Lab3\SciComm\2echo1.c"]:....................................................................................... 263 59) ECHO USANDO INTERRUPCIONES............................................................................................................... 263 COMENTARIOS a ["Labs-C\Lab3\SciComm\3echoInt-1.c"]: ................................................................................. 265 60) ECHO CON INTERRUPCIONES, ASCIZ STRINGS ......................................................................................... 266 COMENTARIOS a ["Labs-C\Lab3\SciComm\3EchoInt-2Z.c"]: .............................................................................. 268 61) COLAS DE DATOS ............................................................................................................................................ 268 COMENTARIOS a ["Labs-C\Lab3\SciComm\Que.h"]: ........................................................................................... 270 62) COLAS DE DATOS: DEMOSTRACIÓN............................................................................................................ 274 COMENTARIOS a ["Labs-C\Lab3\SciComm\4quetst.c"]:....................................................................................... 276 63) CHAT POR INTERRUPCIONES, USANDO COLAS......................................................................................... 277 COMENTARIOS a ["Labs-C\Lab3\SciComm\6ChatInt.c"]: .................................................................................... 280 COMENTARIOS a ["Labs-C\Lab3\SciComm\6ChatInt_.h"]:.................................................................................. 282 COMENTARIOS a ["Labs-C\Lab3\SciComm\4InputPushButtons_.h"]:.................................................................. 283 64) BIG CHAT, INTERRUPCIONES, COLAS ......................................................................................................... 284 COMENTARIOS a ["Labs-C\Lab3\SciComm\6ChatInt-4.c"]:................................................................................. 286 FIN DE LA OBRA..................................................................................................................................................... 288 PRÓLOGO CoDiDac, Computador Digital Didáctico: In Memoriam.  ABSTRACT H ABIENDO dictado clases de Arquitectura del Computador a Ingenieros, sin solución de continuidad desde 1971 (43 años y contando) y –especialmente desde 2003 en la USB– donde se emplea para las prácticas la familia de 8 bits de Motorola/Freescale, he acumulado notas y ejercicios que resultan de utilidad en mis clases, pues mi propósito es el de ir más allá de los conocimientos teóricos impartidos en los planes de estudio, que suelen tratar estos temas a nivel de sistemas, y omiten referirse en estricto detalle y en profundidad, al hardware, al software, a los algoritmos y a los problemas prácticos. No hay forma tan eficaz como Aprender Haciendo (¡ah!), pero ese método no está exento de problemas; por ejemplo, para aplicarlo al estudio de los microcontroladores se requiere manejar con precisión una enorme cantidad de detalles. Esa es la diferencia fundamental entre el enfoque del aula y la aproximación del laboratorio; y entre comprender los conceptos u ostentar destrezas de Maestro. Estas notas pretenden mostrar una pequeña pero importante fracción de esos detalles, que se necesitan para la lograr Maestría en la praxis, y que no se consiguen con facilidad ni en claustros ni en bibliotecas. De hecho, en relación a los últimos exponentes de la familia de microcontroladores que nos ocupa –la MCS08– solo se ha identificado en el mercado un de libro de texto, de calidad precaria. Desde luego, siendo ilimitado el ámbito de lo que desconocemos, este aporte siempre resultará insuficiente, con independencia de su extensión… y otro autor habrá, que aparezca calificando también nuestro esfuerzo de insuficiente. De eso estoy conciente; este es un trabajo en evolución. Habrá que añadir la utilización de más periféricos (aunque los tiempos en mis asignaturas no dan para más dispositivos…) y técnicas avanzadas que incluyan la aproximación a los Sistemas Operativos… 5 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08  INTRODUCCIÓN ARQUITECTURA DEL COMPUTADOR: QUÉ DEBEMOS ESTUDIAR Es bastante conocida mi polémica opinión en relación al objetivo de nuestro plan de estudios. Para comenzar, permítaseme incluir una paráfrasis: ¿Qué diríamos de un médico eminente que  aplica los últimos desarrollos de la instrumentación electrónica para diagnosticar acertadamente la plétora de pacientes que lo agobia,  y que prescribiendo los más modernos antibióticos de última generación logra mantener a raya difíciles y dolorosas enfermedades;  que interviene mediante técnicas no invasivas a quienes no responden a los medicamentos apropiadamente, para rescatarlos de la enfermedad y devolverles la salud…  pero que no tiene sino una superficial – aunque a todas vistas suficiente– idea de los increíbles principios físicos que hacen funcionar el “scanner”…  y sus conocimientos de la maravillosa química que se sintetiza en las cápsulas no le permitirían dirigir un laboratorio químico,  y que es completamente lego en relación al genial concurso que hacen la micromecánica, la óptica y la electrónica, aplicadas a las miniaturas que usa como utensilios quirúrgicos? Casi todos podríamos convenir en que es un excelente médico; sus pacientes lo veneran, los enfermos hacen lo imposible por ser atendidos por él pero, probablemente… de sus manos no salga el próximo CAT ni el tan esperado antibiótico final, y tampoco lo contratarán para diseñar el Escalpelo del Futuro. No será un investigador, ni electrónico, ni químico, ni micro-mecánico. Eso sí, está al tanto de conseguir y emplear las últimas y más certeras herramientas que se ofrecen para el ejercicio de su profesión Médica. Bueno, subyace aquí un dilema que en realidad parece sencillo: Qué queremos como egresado, ¿un científico, o un Ingeniero? Es claro que el título que concedemos es el de Ingeniero... Un ingeniero resuelve problemas en la industria (ojalá que los de la USB lo hagan en Venezuela), empleando dispositivos especializados y complejos, como el Core i7 de Intel, por ejemplo, que cuando lo aplicamos no tenemos sino una muy aproximada idea de cómo está hecho un quadra-core. Un ingeniero hace eso: aprende a usar la tecnología como herramienta en su trabajo diario, y sé que no hay muchos de nosotros que pudiéramos diseñar un Core i7 de Intel, ni ello resultaría tampoco en gran beneficio, pues Intel no adolece de profesionales en esa área… y Venezuela no tiene como prioridad preparar ingenieros para trabajar en Intel… Quiero hacer las siguiente citas: "A good Scientist is a person with original ideas. A good ENGINEER is a person who makes a design that works with as few original ideas as possible. There are no prima donnas in engineering. - Freeman Dyson (Nanotechnology father)" cfr en.wikipedia.org/wiki/Freeman_Dyson Uno puede estar de acuerdo con esta afirmación o rechazarla, pero todos debemos convenir en que, sí, es cierto que el ámbito de acción de un científico no es el mismo que el de un Ingeniero, y que nuestra carrera es la de Ingeniero Electrónico, no la de científico; ni siquiera la de profesor o docente. Parafraseando a Martin Grimheden, del Royal Institute of Technology, que habla de la “legitimidad” de la profesión: “The question of legitimacy is defined as ‘the relation between the actual outcome of the educational efforts undertaken by the university, and the actual demands that are put on the students’ abilities by the society and/or industry at the end of the students’ education.’ To simplify: ‘Why teach [electronics]?’ and ‘What does the industry want?’ ” (Y yo preguntaría: qué quiere NUESTRA industria, para empezar...) El artículo profundiza sobre “knowledge and skills” (teoría y práctica): “industries hiring [electronics] engineers, search for functional skills rather than formal knowledge” 6 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 En nuestro país, y en Latinoamérica, probablemente también sea así. Y, a lo mejor, ¡con los INGENIEROS, en general, ocurra de la misma manera en todas partes! Solo cuando pretendemos que los egresados funjan más bien como Docentes o Investigadores, todo queda medio al revés. O cuando ignoramos el patrón que –para Ingenieros– usan tan prestigiosas instituciones como Stanford y el MIT. En nuestra profesión NO se puede conocer un poco de todo; hay que saber USAR ese “todo”. En 40 años de ejercicio profesional, y en mi transición desde profesor hasta el área de proyectos: ingeniero, gerente, vicepresidente de R&D, y en el perfil de empleado y de empresario (! y, de nuevo, a profesor !), en las compañías en las que me desenvolví, contraté –o trabajé con ellos– a más de 100 ingenieros, casi todos electrónicos, la mayoría de la USB (NB: Cuando la profesora Trina Adrián de Pérez hizo la última encuesta externa para cambio de currículum, en mi empresa yo empleaba, simultáneamente, 44 ingenieros electrónicos, 40 de la USB)… Algo conozco del tema, y mi reales que me ha costado. OBSERVACIÓN: Yo podría ser el profesor activo de la USB, que más egresados de electrónica ha contratado…) Para mí, el que diseñaba amplificadores operacionales, microprocesadores, modems, e incluso dispositivos de más alto nivel… no estaba capacitado para lo que yo necesitaba. Yo requería siempre al que sabía ENCONTRAR y EMPLEAR las herramientas más apropiadas de la tecnología. p Como integrador de sistemas, nunca reinventé la rueda, y me fue bastante bien... ¡en Venezuela! Con lo anterior como premisa, yo abogo por un estudio de la Arquitectura (y… los Sistemas Operativos, y TODO lo que enseñamos) orientado hacia la APLICACIÓN de la tecnología a la solución de problemas. Si alguien quiere diseñar (micro)procesadores en Intel, se encontrará con que las plazas ya están copadas, que ese desarrollo lo hacen científicos –rara vez Ingenieros– y que son como 1,000 especialistas en todo el mundo. Enfoque Este libro no estudia cómo diseñar una computadora, y hace énfasis en cómo están diseñadas, no en por qué están diseñadas así. Observaciones sobre algunos términos empleados Siendo el Inglés la lingua mater de la investigación electrónica, resulta en ocasiones difícil elaborar en esa materia un texto en Castellano que no incurra en anglicismos, y aquellos que lo hacen pueden terminar en un galimatías. Recuerdo algún impreso que traducía “Master Clear” como Limpieza Maestra o, peor, “buffer” como Memorias Tampón, y “flip-flops” por básculas. Además, he empleado la primera forma verbal en vez de la tercera, clásica y neutra, porque esto no es una tesis ni un reporte de investigación, y puedo darme el lujo de acercarme al lector hablando en primera persona. El estudiante se abstendrá de imitarme hasta que tenga tantos años como yo. Ing. Luis G. Uribe C. Caracas, marzo de 2014. 7 1 Capítulo I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 VON NEUMANN COMPUTERS “Quien no conoce la historia está condenado a repetirla”, Jorge Santayana.  INTRODUCCIÓN L AS computadoras actuales (a mí me encanta nombrarlas en femenino) pueden separarse en términos prácticos en dos clases: Harvard y Von Neumann. Muchas otras divisiones coexisten, como CISC y RISC, siendo las más utilizadas en la actualidad, por su cantidad, las CISC tipo Von Neumann, aunque yo he trabajado con algunos RISC tipo Harvard: los Microchip de la familia 16Fxxx. La familia HC9S08 que nos ocupa es CISC de tipo Von Neumann, con instrucciones primordialmente de una dirección, orientadas a Acumulador, como ya veremos. QUÉ ES UNA INSTRUCCIÓN A las computadoras hay que programarlas, o instruirlas para que realicen sus actividades. Entienden, para ese fin, un idioma propio de cada una de ellas, o Lenguaje de Máquina (Machine Language). El programador emplea INSTRUCCIONES, compuestas de: a) una Voz Imperativa o COMANDO, que le indica a la computadora qué operación hacer en un instante determinado y b) la identificación de los operandos que deben participar en la acción. Desde que estudiábamos aritmética aprendimos que un cálculo largo, que incluía una cierta cantidad de valores (operandos) y distintas operaciones aritméticas sobre ellos, podía descomponerse en una secuencia más simple de operaciones que incluyeran, como máximo, dos (2) operandos; así, por ejemplo:  F = A*B‐C/D se realiza como:  T1 = A*B; T2 = C/D y F = T1 ‐ T2.  o:   F = A*B;  T = C/D y F =  F ‐ T  (se emplea una variable temporal menos)  A las operaciones aritméticas elementales se las conoce como “binarias”, o “diádicas”, porque emplean dos operandos sobre los cuales actúan para producir un (1) resultado. Lo mismo ocurre con los operadores booleanos. Y aquellos que requieren un solo operando, como “-5”, o “~B” (unarias o monádicas) son un subconjunto y pueden recomponerse como binarios o diádicos así: “0-5” o “~(0|B)”. 8 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Una computadora tiene una unidad Lógico Aritmética (ALU: Arithmetic-Logic Unit) cuyos circuitos realizan operaciones de suma y resta; otras incluyen multiplicación y división y, las menos, soportan -además de números Enteros– representación en “punto flotante” para números Reales. Para identificar los Comandos hay que codificarlos. Eso significa que si hubiera, por ejemplo, solo dos operaciones (dos Comandos), podrían representarse con un bit que, estando en 0 designaría al primero de ellos, y si valiera 1 señalaría al otro. Si la Unidad de Control debe identificar 250 comandos dentro de instrucciones (esa es la cantidad que tenía el antecesor del HCS08) se necesitarían 8 bits, lo cual resulta muy conveniente pues 8 son los bits que definen UN Byte, que es la cantidad mínima de bits que se manipulan de una sola vez en las computadoras modernas. Cuando tenemos procesadores con una cantidad superior a 256 Comandos (tal es el caso del nuevo HCS08) podrían codificarse con dos (2) bytes pero, como con 16 bits pueden individualizarse 65,536 entidades diferentes, se ve que habría una holgura innecesaria (dado que los computadores actuales no tienen más de alrededor de 1,000 y pico de códigos de operación, como orden de magnitud). Entonces se usa un truco que codifica los Comandos más empleados con un (1) byte, y para el resto usa dos (2): uno llamado “Escape”, que advierte al procesador que, a continuación, viene otro byte que es el código que en realidad identifica ese comando que por tanto requiere 2 bytes. Esta es la aproximación que se usa en el HCS08, y desde luego también en Intel. Así que para ver cuántos bits conforman una Instrucción tenemos que, para el Código de Operación, o Comando (OpCode), se necesita un (1) byte. Veamos ahora qué se requiere para codificar los operandos, que residen en la Memoria. La Unidad de Control debe instruir a la memoria para que le suministre a la ALU cada operando, y para que reciba y almacene el resultado. La Memoria o unidad de almacenamiento puede caracterizarse como un dispositivo con dos dimensiones: qué se va a guardar (el Valor de la variable, consideradas ellas en el sentido que se les atribuye al estudiar algún lenguaje de programación), y dónde se va a almacenar ese valor: Valor y Dirección. Sobre el Valor ya nos hacemos una idea de a qué corresponde. En cuanto a la Dirección, las celdas o posiciones de memoria se numeran normalmente comenzando desde 0, en incrementos comúnmente de a uno, y cada una resulta así identificada mediante ese número, que se conoce como la Dirección de la celda. Cuando decimos que debemos leer una variable (para hacer un cálculo), en lenguajes de programación solemos identificarla mediante un Nombre, pero a la computadora hay que señalársela mediante un número, la Dirección sobre la cuál la memoria debe operar para entregarnos dicho Valor. Así, una Variable identificada por el programador mediante un Nombre, desde el punto de vista de la computadora equivale a una Dirección y un Valor. Entonces, ¿cuántos bits se requieren para identificar el Valor, y la Dirección? Ese número no tiene por qué ser el mismo para ambos casos. Para el Valor, la mayoría de los MCUs usan un (1) byte. Máquinas más nuevas emplean dos (2) bytes (16 bits), cuatro (4) bytes (32 bits); ahora se van imponiendo ocho (8) bytes (64 bits) y ya llegarán hasta 16 bytes (128 bits). Ya eso me parece muy improbable. Ahora, con una máquina común de 32 bits, si cada dato es un número, éste puede representar enteros hasta 2^32 = 4,294,967,296 o, si llevan signo: de +2,147,483,647 a –2,147,483,648. 9 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 En el caso de las Direcciones se necesitan 4 bytes. Si tenemos dos (2) operandos que suministran valores, y uno (1) para el resultado, y cada uno de ellos precisa cuatro (4) bytes para su identificación, el número de bytes de una instrucción sería de: 1 (OpCode) + 3 * 4 (Addresses) = trece (13) bytes. Note que como son tres (3) operandos, a este tipo de computadoras se las conoce como Máquinas de Tres Direcciones. Hay Instrucciones que tienen menos direcciones, como “BORRE” (Clear; V=0), que tiene UNA sola dirección; y “HALT”, que no tiene ninguna.  MÁQUINAS DE 3, 2, 1 Y 0 DIRECCIONES T RECE (13) bytes son demasiados. Cada vez que se ejecuta una Instrucción, el CPU necesita leer todos los bytes que la componen. Para obtener un alto rendimiento se requiere que se lea la Instrucción con la menor cantidad de pasos posible; ojalá UNA sola operación. Para esto, el “Bus” en el que se transfiere la información tendría un ancho exagerado, lo que resultaría costoso, además de oponerse a los espacios pequeños que requieren los dispositivos modernos. Por tanto, es importante ver cómo pueden eliminarse “campos” o elementos de la Instrucción. Recuerden que con la codificación del Comando hay poco que hacer, según acabamos de describir. El primer paso en este sentido se dio haciendo que uno de los 3 operandos de la Instrucción fuera, al mismo tiempo, fuente de información y resultado; algo así como: A = A*B. Eso, en realidad, elimina un operando (cuatro [4] bytes). Para que el sistema funcione hay que agregar una instrucción que permita inicializar una variable con el contenido de otra; esta es un MOVE. F = A*B‐C/D se realiza ahora como:  T = C (MOVE TC; una sola Dirección); T = T/D; F = A (MOVE FA); F = F*B; F = F‐T  A este tipo de computadora bien podría llamársela Máquina de Dos Direcciones. El segundo paso para eliminar de la Instrucción otro operando consistió en inventarse un dispositivo de almacenamiento que al mismo tiempo fuera fuente de información y albergue del resultado; algo así como: Acc  =  Acc  *  B en donde Acc, conocido como Accumulator, W [Working Register], H [Holding Register…] es un registro dentro del CPU, y es un elemento TÁCITO, que no requiere identificarse porque es el único que hay, con lo cual, la instrucción anterior sólo tiene UNA Dirección. A estas computadoras se las conoce como Máquinas de Acumulador, o, naturalmente, Máquinas de UNA Dirección. Para que funcione el sistema hay que hacer que el Move anterior pueda llevar datos DESDE la memoria hasta el Acumulador, y desde éste HACIA la memoria. Como ahora son dos operaciones, reciben nombres diferentes: LOAD (AccMemory) y STORE (Memory Acc). Nuestro ejemplo de siempre quedaría resuelto de la siguiente manera: F = A*B‐C/D, en una máquina de Acumulador (Una Dirección):  LOAD     C; (Acc = C). Obsérvese que no se menciona el Acc; solo “C”  DIVIDE   D; (Acc = Acc/D: C/D)  STORE    T; (T   = Acc: C/D)  LOAD     A; (Acc = A)  MULTIPLY B; (Acc = Acc*B: A*B)  SUBTRACT T; (Acc = Acc‐T: A*B‐C/D)  STORE    F; (F   = Acc:   A*B‐C/D)  10 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Éste último resultó ser el sistema más eficiente en relación a los parámetros referidos al principio, y es el que más se usa en la actualidad: Instrucciones de Una Dirección; Máquinas de Acumulador. Finalmente, se forzó la barra y existen Máquinas de Cero Direcciones. Los computadores HP fueron sus exponentes más sobresalientes. Una aplicación muy generalizada de esta aproximación de Cero Direcciones la encontramos en la evaluación automática de expresiones, principalmente como parte de los Compiladores. La notación más divulgada se conoce como RPN, Reverse Polish Notation. Los tipos generales de Direcciones: inmediata, directa, indirecta, modo registro, indirección con registros, desplazamiento, stack, basadas, segmentadas, deben consultarse en las referencias genéricas.  HCS08 ARCHITECTURE: PROGRAMMING MODEL E L correspondiente diagrama puede verse en el Reference Manual del microcomputador: CPU08RM.pdf, página 15. Se tiene el Acumulador A, un registro de 8 bits con las características que acabamos de ver para las Máquinas de Una Dirección. Existe un registro Índice (H:X) de 16 bits, que fundamentalmente sirve como apuntador (pointer, como en C); es decir, alberga: no el valor de una variable, sino la dirección de esa variable (exactamente como en C). Se pueden realizar operaciones que mimeticen las equivalentes de alto nivel, tales como: (*p se refiere al H:X):   *p = Acc; Acc = *p; *p++ = Acc; Acc = *p++; p = *p; jump *p; ... y similares.  Está también el Stack Pointer SP de 16 bits, que permite realizar el protocolo para la ejecución de Subrutinas o Funciones, así como el de las Interrupciones, y sirve para establecer la existencia de variables dinámicas, como en el C, que se materializan con la llamada de una función y desaparecen a su finalización. Finalmente se encuentran otros 2 registros de uso más específico que los 3 anteriores: el PC o Program Counter, de 16 bits, que en principio identifica la instrucción que habrá de ejecutarse a continuación, y el Processor Status Word, cuyo nombre fue cambiado a CCR: Condition Code Register; es de 16 bits y almacena banderas (flags) individuales que identifican el estado del procesador en un instante dado, y facilitan el control del flujo del programa (saltos condicionales, habilitación de interrupciones). El uso en detalle de todos estos elementos del MCU lo veremos más adelante como parte del análisis y desarrollo de la secuencia de programas, objetivo principal de este texto. MODOS DE DIRECCIONAMIENTO EN EL MC9S08 (CPU08RM Reference Manual) El microprocesador HCS08 que se escogió en la USB para incluirlo como elemento de trabajo en sus laboratorios de electrónica es casi perfecto para estudiar la Arquitectura de las Computadoras, por sus 11 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 enriquecidas características, casi todas tomadas de las minicomputadoras DEC de los 70s y siguientes. (Motorola no fue la única en adoptar tan bella tecnología de las PDP-11; Intel incluye más conceptos aún en sus microcomputadores). Ya vimos que, a diferencia de muchos dispositivos similares de sus competidores, tienen un repertorio de instrucciones poderoso, variado y numeroso (no como las 35 instrucciones de los 16F8Xx). En cuanto a los modos en que emplea las direcciones, hay 17 distintos. Aquí solo se enumeran; enfóquese para su estudio en el Manual de Referencia del CPU: “CPU08RM”. El uso en detalle de todos los modos de direccionamiento lo veremos más adelante como parte del análisis y desarrollo de la secuencia de programas que presentaremos. 1) Inherent     (no addresses)  2) Immediate,  8 bit  constants  3) Immediate, 16 bits constants  4) Direct      8 bits (ZERO page)  5) Extended   16 bits; "NATURAL" addressing mode: ABSOLUTE  6) Indexed      no offset  7) Indexed,   8‐bit offset  8) Indexed,  16‐bit offset  9) Indexed      no offset  with (Index register) post increment  10) Indexed,   8‐bit offset with (Index register) post increment   11) SP,        8‐bit offset  12) SP,       16‐bit offset (no existe SIN offset)  13) Relative    (not for DATA! Only for CODE Branches)  Memory‐to‐Memory, M‐M (Only advanced++ ):  14) M‐M: Immediate to Direct  15) M‐M: Direct    to Direct  16) M‐M: Indexed   to Direct,  with (Index register) post increment  17) M‐M: Direct    to Indexed, with (Index register) post increment  VARIACIONES Y OPTIMIZACIONES Para optimizar el rendimiento del HCS08, maximizando velocidad y minimizando uso de memoria, se incluyeron varias excepciones a la arquitectura convencional; primero, en el manejo de la memoria. Normalmente la memoria de una computadora tipo Von Neumann consiste, como ya indicamos, en una colección de celdas o elementos almacenadores, de información y programas, colocados en Secuencia Lineal. (La unidad de almacenamiento de las máquinas tipo Harvard tiene la particularidad de que la sección que podemos usar para guardar datos emplea su propio conjunto de direcciones, separado del grupo de direcciones de la memoria en la que se guardar el programa. Cada una de ellas puede verse como un arreglo lineal, pero no están contiguos el uno junto al otro. Es decir, que si por ejemplo, se recorre la RAM o memoria de almacenamiento para variables, y se llega hasta el final, la próxima posición NO es la del código del programa. En las máquinas tipo von Neumann hay UN solo 12 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 campo de direcciones. Al recorrerse todas las posiciones en secuencia, se habrán visitado tanto las variables como el programa). Si hay 2^16 = 65536 posiciones (64 Kbytes) necesitamos desde luego 16 bits de Direccionamiento para cada unidad elemental de almacenamiento, que en nuestro caso es el Byte (8 bits). Motorola dividió la memoria, y a la primera sección, compuesta de 256 posiciones (0x0000 a 0x00FF) la llamó Direct Page (Z RAM). Desde luego, si únicamente hubiera este grupo de unidades de memoria, sólo se necesitaría 1 Byte (8 bits) para identificarlas, en lugar de los 16 bits que se requerirían si la memoria fuera mayor. El truco consiste en multiplicar por 2 los códigos de operación, de tal manera que se use, por ejemplo, Add0 para identificar la suma (Add) de una variable que está en Direct Page, y que por lo tanto puede identificarse con un (1) solo Byte, y Add1 para indicar la suma de una variable que no está localizada en Direct Page, y para la cual se necesitan dos (2) Bytes. Así, si se coloca en Direct Page la información más utilizada, se puede lograr una economía en el tamaño del programa, que requiere menos bytes para accesar esas posiciones, y tiene mayor velocidad, porque hay que cargar un byte menos. Si normalmente las instrucciones tienen un (1) byte para el OpCode y dos (2) para la dirección (total: 3), que demorarían en su manipulación “3 tiempos de lectura de Byte, tB”; el ahorro, al llevarlo a solo 2 bytes es de 1/3 en “tB”; así la velocidad aumenta en 50%: en el mismo intervalo de tiempo se ejecuta un 50% más instrucciones, si todos los accesos son en Direct Page. Así que podríamos decir que dentro del Código de Operación para el Comando Add, hay un bit que vale 0 o 1 e identifica si a continuación de ese OpCode vienen 1 o 2 bytes de dirección. Es interesante notar que el programa que nos ayuda a codificar a tan bajo nivel (inferior al C), llamado el Assembler, se encarga de averiguar la posición de las variables, de forma que el programador pueda usar un solo código, Add en este caso, y el Ensamblador incluye Add0 o Add1, según corresponda a la ubicación de la información, en Page-0 o no. El programador queda así liberado de llevar la pista a las variables. 13 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08  CODIDAC, VON URIBE’S COMPUTER LA ARQUITECTURA Consola de operador, con teletipo o télex (Olivetti, Siemens), para el suministro de datos y programa mediante el teclado, e impresión de resultados en papel. Automatización vía lector adjunto de cinta de papel. Tablero para despliegue de información interna: registros, direcciones, instrucciones, botonera para encendido y demás funciones no realizables desde el teletipo. LAS INSTRUCCIONES 14 instrucciones: Comandos para lectura de datos, impresión, 4 operaciones aritméticas, toma de decisiones y bifurcación en el flujo de control, desplazamientos de información a derecha e izquierda, movimiento de datos entre diversas posiciones de memoria, borrado, espera y parada… codificadas en castellano. El CODIDAC debía realizar operaciones similares a las del FORTRAN. El CODIDAC se diferencia de otros computadores porque tiene Clases de Información definidas a nivel de máquina. En los computadores tipo Von Neumann la naturaleza de un elemento de información no le es inherente; el significado se lo proporcionan las instrucciones que lo manipulan. Así, al sumar A con B, la instrucción de suma les otorga significación numérica a las variables; si hubiera a lo mejor texto en ellas, el computador no tendría forma de saberlo… Por ejemplo, para el CODIDAC la instrucción que desplaza información lo hace de una manera si el operando es numérico y de otra muy diferente si representa un texto. En caso de números, el resultado siempre es un número; desplazar un número negativo produce siempre otro número negativo. En caso de textos, el resultado siempre es un texto. ¡Incluso las Instrucciones pertenecen a una Clase! lo que permite realizar sobre ellas operaciones aritméticas, en los campos de las direcciones de los operandos (numéricos), para facilitar el manejo de subíndices, estructuras de datos, etc. LOS MODIFICADORES Un modificador cambia las operaciones, de simples a Repetitivas. Una sola instrucción de máquina, como IM: IMprima, arroja un único valor, o un Rango de resultados (IR: Imprima Repetitivamente). EL SUBSISTEMA DE ENTRADA Y SALIDA Forma parte esencial de la Arquitectura del CPU, con instrucciones específicas, más poderosas que el INPUT o PRINT del Basic y superiores al IN y al OUT de los microprocesadores. CÓDIGOS DE OPERACIÓN Como el aspecto didáctico es preeminente, los códigos tienen una base mnemónica de fácil comprensión y recordación: Cuando dos palabras designan un comando, se toma la primera letra de cada una para conformar dicho código. Por ejemplo, "Acepte Información" se codifica como "AI", y las letras 'A' e 'I' se almacenan en la memoria, en código baudot de 5 bits. 14 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Si el comando tiene una sola voz imperativa, como "SUME", el código se conforma como "SU", utilizando sus 2 primeras letras. Si se requiere el modificador de iteración, se reemplaza la segunda letra por una R: AR es Acepte información de manera Repetitiva (desde, hasta), SR totaliza un área completa de memoria, etc. EL EDITOR Para cargar el programa hay un editor de texto compuesto por circuitos (no es software). Para invocarlo existe el interruptor "Función Programa", y el "Botón de Corrección" para corregir errores tipográficos. EL FORMATO DE LOS NÚMEROS La información numérica se representa en BCD, en “signo y magnitud”, y es de "formato fijo": El número de decimales se preestablece mediante selectores en la consola: desde 0 hasta 11 decimales. Cada palabra de memoria almacena 12 dígitos BCD, o 16 letras. La unidad de salida imprime la “Coma” de los números en la posición decimal designada, y obviaba la escritura de ceros no significativos, es decir, ¡el FORMAT está también incluido en la electrónica! LA CALCULADORA El CODIDAC puede operarse como calculadora, gracias al selector de Operaciones Inmediatas. Así, no hay que elaborar un programa para realizar operaciones aritméticas. Ningún otro computador tiene esta característica. LA ESTRUCTURA La máquina es del tipo serial, con lógica secuencial convencional, no microprogramada. Posee selectores que permiten trabajar a máxima velocidad (¡600 KHz!) o en animación lenta para permitir el seguimiento del flujo de información. Tiene un interruptor de paso a paso; uno para Detener la operación del programa en cualquier instante, otro para Continuarla, y el Master Reset. DATOS ESTADÍSTICOS Está compuesto por más de 4.000 circuitos integrados TTL, 300 transistores, 200 tarjetas de circuito impreso; 160.000 puntos de soldadura, 155.000 agujeros perforados con taladro de mano; 16.000 cablecitos de alambre telefónico (2.500 metros) y más de 5 kilos de soldadura de estaño. Diez fuentes de alimentación, de 5 Voltios a 10 amperios, más fuentecitas variadas para manejar las luces de neón, el teleimpresor, etc., alojadas en un gabinete independiente. Dos disipadores principales tienen como 1,70 metros de altura c/u. El consumo está por encima de los 3 kilovatios y para controlar la generación de calor hay una pequeña turbina eléctrica que fuerza la circulación de aire en el gabinete principal, así como un poderoso ventilador situado en la base del gabinete de alimentación. CIRCUITOS IMPRESOS En los primeros dibujos para obtener los negativos fotográficos se emplearon "rapidógrafos"; luego se usaron cintas adhesivas, como “pistas”, y “ojetes” para las perforaciones. 15 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 FOTOS 1) VISTA GENERAL A la izquierda se ve la consola formada por la mesa, el teclado y el impresor (télex o teletipo) al que se le alcanza a ver el rollo de papel de impresión y el perforador de cinta de papel, adosado al lado izquierdo. Además, el lector de papel. A la derecha se observa el panel de control (botonera) y despliegue de información (luces). El gabinete principal es el que está a la derecha, con 4 racks (se ven dos, uno arriba del otro). En el de abajo alcanza a verse la memoria de núcleos magnéticos de ferrita. El tercer gabinete, pequeño, situado entre los otros dos, alberga las fuentes de alimentación. 16 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 2) VISTA GENERAL DESDE OTRO ÁNGULO En el gabinete de las fuentes se ve un panel de control, con interruptores y borneras de interposición, así como las protecciones para sobrevoltaje, tipo Crowbar (un SCR quema un fusible). 17 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 3) ACERCAMIENTO En esta foto no aparece el lector de cinta de papel. 18 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 4) MAYOR ACERCAMIENTO A LA CONSOLA 19 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 5) PRESENTACIÓN Le enseño el proyecto a mi padre, y a mi hermano que es el fotógrafo. 20 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 6) CONSOLA DE CONTROL Y TELETIPO 21 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 7) CONSOLA DE CONTROL, VISTA DE FRENTE Abajo a la izquierda, los contactores para encender el computador, y una luz piloto. A la derecha, en dos grupos, los interruptores; los 7 primeros de la izquierda sirven para determinar el número de decimales (0 a 11) con que trabajará un programa. A la derecha, la botonera de control: De izquierda a derecha, los principales son  Inicie;  Función Programa;  Corrija una línea (borra y repite la entrada);  Arranque;  Pare;  Continúe;  Función Calculadora. Alcanzan a verse un botón para máxima velocidad, otro para baja velocidad, y uno para paso a paso (el ícono es una persona caminando). En la parte baja del panel vertical, el despliegue de bits de cada registro. Con el selector de la izquierda se escoge entre los registros A, B o C, o el de Instrucciones, para presentarlo mediante las luces de neón. La información entra por la derecha y se desplazaba secuencialmente hacia la izquierda. Más arriba se indica el número del paso del micro-secuenciador. La instrucción más larga tiene 18 pasos. La siguiente franja tiene los íconos que representan los catorce comandos:  Acepte Información,  Compare por Igualdad,  Compare por Magnitud,  Lleve,  Sume,  Reste,  Divida,  Multiplique,  Desplace a la Derecha,  Desplace a la Izquierda,  Imprima,  Borre,  Espere y  Pare. Los cuatro íconos de la izquierda muestran errores de overflow, división por cero y demás. Y a la derecha, la dirección de memoria que se está usando (000-999. Se está visualizando un 022) 22 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 8) GABINETE PRINCIPAL, VISTA ANTERIOR Cuatro Racks con conectores (peines). Caben hasta 17 por fila, pero no todas están completamente pobladas Algunas tarjetas son de doble altura (dos conectores). El rack inferior derecho es el de la memoria: los bastidores con los núcleos (entre láminas protectoras de acrílico), las tarjetas controladoras (corrientes y sensores), y una fila con 100 palabras de almacenamiento semiconductor, hechas con registros de desplazamiento normales que quedaron sin utilizar al terminar el diseño. 23 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 9) GABINETE PRINCIPAL, VISTA POSTERIOR Indicativo de la cantidad de cables de interconexión (no se utilizó “Mother Board”). Resaltan los sensores remotos (cinco y cinco, en acrílico blanco) que leen a distancia el valor de las 10 alimentaciones y los llevan hasta los reguladores. Así se garantizan el valor de los voltajes en el destino, a pesar de la considerable separación entre el gabinete de las fuentes y el de la electrónica. 24 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 10) GABINETE DE MEMORIA Los núcleos están localizados en cuatro (4) planos. Cada bastidor se alambró a mano, cruzando un hilo de Excitación (corriente) horizontal, llamado X, otro de Excitación vertical, Y, uno de Inhibición, para cancelar la excitación en las posiciones no seleccionadas, y el de Lectura. Los núcleos magnéticos son de 20 mils (milésimas de pulgadas de diámetro)… 25 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 11) ACERCAMIENTO A LA MEMORIA DE NÚCLEOS MAGNÉTICOS 26 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 12) DETALLE DE LA UNIDAD DE CONTROL Rack situado arriba a la izquierda en el gabinete. 27 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 13) DETALLE (POSTERIOR) 28 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 14) GABINETE DE FUENTES (VISTA ANTERIOR) Se ven abajo los transformadores (¡nada pequeños!); un poco más arriba, el ventilador para forzar el aire y, a continuación, las 10 fuentes. 29 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 15) GABINETE DE FUENTES (ACERCAMIENTO; VISTA ANTERIOR) 30 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 16) GABINETE DE FUENTES (VISTA POSTERIOR) Se aprecia uno de los dos disipadores, con 5 transistores de potencia (de los 10 que eran en total). Detrás de él se encuentra el segundo disipador. Puede observarse sus tamaños… 31 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 17) MÉTODO DE ELABORACIÓN DE CIRCUITOS IMPRESOS (1) Como ejemplo puede verse una sección de la Unidad de Control. 32 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 18) MÉTODO DE ELABORACIÓN DE CIRCUITOS IMPRESOS (2) Se escoge una parte de la sección de la Unidad de Control, la que va a colocarse en una tarjeta de circuito impreso. Se muestra la parte seleccionada, el dibujo tinta china y plumilla, hecho sobre lámina flexible de acrílico (de las que se usan para el retroproyector), y el negativo fotográfico que se aplica sobre la tarjeta de cobre para realizar la exposición, utilizando lámparas de luz muy fuerte. 33 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 19) MÉTODO DE ELABORACIÓN DE CIRCUITOS IMPRESOS (3) Ejemplo de una tarjeta ya poblada de componentes (master clock, 16 fases…). Vista anterior. 34 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 20) MÉTODO DE ELABORACIÓN DE CIRCUITOS IMPRESOS (4) Master clock, vista posterior. Todas las tarjetas son de dos caras. 35 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 21) FORMATO DE LAS INSTRUCCIONES Puede verse la codificación de algunas instrucciones (así se escriben en el programa y así quedan en la memoria): Acepte Información (AI), Acepte información de manera Repetitiva (AR), Imprima (IM), Imprima Repetitivamente (IR), Sume (SU), Sume Repetitivamente (SR, que totaliza un bloque de datos contiguos), Reste (RE), Multiplique (MU), Divida (DV; es la única excepción de la codificación, porque DI es Desplace a la Izquierda…). Ing. Luis Guillermo Uribe Cataño Revisión de Diciembre de 2012. Resumen de lo publicado por la Javeriana, 2011, y por Tekhne, en la UCAB. 36 2 Capítulo I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ASSEMBLER HCS08 "Perfection is finally attained, not when there is no longer anything to add, but when there is no longer anything to take away..." (Antoine De Saint-Exupéry)  EJERCICIOS BÁSICOS EN ASSEMBLY LANGUAGE P ARA comprender los aspectos más importantes de una computadora es imprescindible dar una mirada a la manera como el programador le transmite lo que ella debe hacer, cómo se la programa en forma nativa: su Conjunto de Instrucciones, cómo direcciona la memoria, cómo usa el Stack, qué recursos tiene, tales como registros índice, Stack. En nuestro empeño por apoyarnos en el MC9S08QE128 de Freescale instalado en la tarjeta de desarrollo DEMOQE128, hemos elaborado una serie de programas ilustrativos que presentamos a continuación. Estos ejercicios se dividen en 4:  Introductorios;  referentes a Timers, en donde explico la librería que suministro siempre con mis cursos, para estudiarla y usarla;  Comunicaciones con el PC, usando el SCI (RS-232); aquí también analizamos una librería que desarrollé para mis cursos, la estudiamos con detalle y también debemos poder usarla en nuestros ejercicios y proyectos. Finalmente,  ejercicios varios. 1) Primer Programa para el HCS08: MINIMUM Program for HCS08 Este  es  el  programa  más  pequeño  que  puede  escribirse  en  el  ambiente  de  desarrollo  CodeWarrior (la versión que uso en la actualidad es: CodeWarrior 10.5. Tiene sus Bugs,  algunos  reportados  por  mí  [enero  de  2014],  sin  corregir,  pero  es  lo  que  hay...  NB:  MUCHOS ambientes de desarrollo, en el área de MCUs, son de JUGUETE)  El objetivo de este programa es verificar que su instalación del ambiente de desarrollo  funciona, y los mínimos aspectos que debe incluir en sus programas.     ["Laboratorios\Lab1\00a_L1.asm"]  01 ;********************************************************************  02 ; 00a_L1.asm, MINIMUM Program for HCS08, Rev. F, V08J2012 M29E2013  03 ; Luis G. Uribe C., D27Y2007 J09A2009. For CodeWarrior 10.2  04 ; ===================================================================  05 ; Begin of Code Section  06        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  07  08        ORG     $2080       ; ORG? Origin. Next code (bra) is located  09                            ; ..at this address!  37 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 10                            ; $2080 is HCS08 ROMStart (Flash)  11                            ; Main: symbolic for address $2080 (bra)  12 Main:  bra     *   ; Branch to himself (BRA: Branch Always; *: here!)  13  14        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  15        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  16                            ; This 'nop' MAY be removed for CW 6.3 ...  17 ;********************************************************************  18 ; Interrupt Vectors  19  20        ORG     $FFFE       ; VECTOR holding address for HCS08 startup  21        DC.W    $2080       ; ...(RESET)    DC.W: Define CONSTANT Word  22    END                     ; END and next lines, removed by Assembler  23  24 Note: Nothing is read after END... (cfr. 'main.dbg')    COMENTARIOS a ["Laboratorios\Lab1\00a_L1.asm"]:   La FORMA y la PRESENTACIÓN del Código en nuestra área de trabajo o en cualquier otra,  es FUNDAMENTAL para la vida de los proyectos. Los programas deben poder entenderse, por  quien  los  hace,  cuando  vuelva  a  mirarlos,  y  por  las  personas  que  vengan  después  a  modificarlos o repararlos.  Para eso se requieren comentarios útiles, resaltantes y claros.  Lo  primero  que  hay  que  incluir  siempre,  en  un  programa,  es  el  nombre  del  mismo,  la  fecha  o  fechas  en  los  que  se  hizo,  o  modificó  el  programa.  En  este  libro  empleo  abreviaturas y como las variaciones han sido cosméticas casi nunca las relaciono:  01 ;********************************************************************  02 ; 00a_L1.asm, MINIMUM Program for HCS08, Rev. F, V08J2012 M29E2013  03 ; Luis G. Uribe C., D27Y2007 J09A2009. For CodeWarrior 10.2    En la primera línea:  ; 00a_L1.asm, MINIMUM Program for HCS08, Rev. F,    se  coloca  el  nombre  del  programa,  propósito  del  mismo,  para  qué  plataforma  y  qué  revisión  (identificación  que  permite  llevar  la  pista  de  las  modificaciones  que  se  le  han hecho)  Además,  las  fechas  indican  el  comienzo  del  programa,  cuándo  se  lo  modificó  y  la  datación  de  la  revisión  actual.  Cuando  es  importante,  hay  que  agregar  una  lista  de  fechas y cosas que se añadieron, quitaron o modificaron. También, las iniciales de la  persona que hizo cambios en el código, para saber a quién preguntar si fuera necesario.  02 ; ..V08J2012 M29E2013  03 ; ..D27Y2007 J09A2009. For CodeWarrior 10.2    Este programa se comenzó el Domingo 27 de maYo de 2007; el jueves 09 de Abril de 2009  se  lo  adaptó  al  CodeWarrior  10.2  (ahora  se  sabe  que  funciona  bien  bajo  la  versión  10.5). Luego, el viernes 08 de Junio de 2012 se le hizo alguna modificación cosmética  (porque no hay mayor reporte), y la última edición fue el martes 29 de Enero de 2013,  hace ya un año.  Nótese  también  la  columna  casi  perfecta  donde  comienzan  los  comentarios.  En  Assembly  Language se recomienda que el ";" que marca el comienzo de los comentarios se coloque  EN  la  columna  32.  A  veces  esto  no  es  factible  y  hay  que  moverla,  hacia  atrás  o  38 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 adelante, pero en general debe utilizarse esa regla. SUS PROGRAMAS TIENEN QUE HACERSE  SIGUIENDO ESTE Y TODOS LOS LINEAMIENTOS.    Cuando un programa se va a ejecutar (o, como todo el mundo dice, se va a CORRER), hay  que  indicarle  al  CPU  dónde  comienza  el  código.  Cada  procesador  tiene  su  propio  protocolo  para  esto.  Por  ejemplo,  cuando  se  energizan  los  procesadores  Intel,  el  PC  (Program Counter) asume el valor 0xFFFF, para los micros de 16 bits, y 0xFFFFFFFF para  los de 32 bits, como parte de la definición de su Estado Inicial (un valor de 0x0000,  puros  ceros,  o  0xFFFF,  puros  unos,  son  cómodos  de  lograr,  mediante  las  líneas  apropiadas de los registros correspondientes: Clear o Preset, respectivamente). De esa  dirección toma el CPU LA PRIMERA INSTRUCCIÓN que ha de ejecutarse. Como esa ubicación  corresponde  a  la  última  posición  de  la  memoria,  seguramente  que  allí  tiene  el  programador que colocar un JUMP hacia su verdadera primera instrucción.  (Cómo puede colocarse una instrucción en la memoria, si recién se está energizando la  máquina, y el estado inicial de la RAM es normalmente indeterminado, forma parte de la  solución  al  problema  denominado  IPL:  Initial  Program  Loader.  Revise  en  la  literatura  del curso en qué consiste con exactitud ese problema, y cuáles son las soluciones. Una  de  ella  consiste  en  que  en  la  parte  superior  de  la  memoria  de  trabajo,  volátil,  se  coloca  una  ROM,  o  memoria  de  sólo  lectura,  que  contiene  su  programa,  o  un  código  destinado a “buscar” su programa y localizarlo en un sitio adecuado)  En el HCS08, usted tiene que decidir dónde va a colocar la primera instrucción de su  programa. Eso se hace así:  Se le indica al CodeWarrior que la primera dirección de su programa es 'Main' (tiene  que estar seguro de que esa sea la etiqueta apropiada dentro de su código)  06        ABSENTRY Main       ; Export symbol (DEBUGGER entry)    Luego  se  le  dice  que  coloque  el  código  de  su  primera  instrucción,  en  la  dirección  Hexadecimal $2080:    08        ORG     $2080       ; ORG? Origin. Next code (bra) is located    ORG, por Origin, le indica al programa Assembler que el código que a continuación usted  va a escribir, debe colocarlo de manera secuencial a partir de la dirección indicada;  $2080 en nuestro caso (que para el HC9S08QE128 que empleamos, es la PRIMERA dirección  de  la  memoria  FLASH,  que  es  su  almacenamiento  no  volátil.  Para  una  identificación  precisa de las áreas ocupadas por la RAM y la Flash, remítase al manual MC9S08QE128RM  (Reference Manual)  Luego, el programador tiene que colocar su 'Main' (usted escoge a su gusto el nombre;  no tiene que ser Main, siempre que use el mismo en todas aquellas partes en las que yo  lo incluí en este ejemplo...)  11                            ; Main: symbolic for address $2080 (bra)  12 Main:  bra     *   ; Branch to himself (BRA: Branch Always; *: here!)    Note  que  se  enfatiza  en  que  el  símbolo  mnemónico,  alfabético,  'Main',  tiene  una  equivalencia  con  el  número  $2080.  Usted  emplea  'Main';  el  CPU  usa  $2080  (16  BITS  EN  BINARIO, ¡QUE ES LO ÚNICO QUE LAS COMPUTADORAS ENTIENDEN!)  Ahora observe la línea 15; se ha colocado ese NOP porque se requiere en las versiones  10.x (al menos hasta la 10.5; NO era necesario en la versión 6.x y no recuerdo haberlo  usado en versiones anteriores, las que comencé a usar por allá por la 3.x, hace eones)  15        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  16                            ; This 'nop' MAY be removed for CW 6.3 ...    39 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Si esta línea se omite, el Debugger produce un error al tratar de ejecutar el 'bra *'  (salto infinito a sí mismo) de la línea 12. Esto es un error del CodeWarrior actual.  Finalmente vienen  los Interrupt  Vectors  de  la  línea  20.  Resulta  que,  a  diferencia  de  Intel que, como ya dijimos, inicializa sus PC para leer LA PRIMERA INSTRUCCIÓN de su  programa SIEMPRE desde la última dirección de memoria, el Motorola/Freescale inicializa  también su PC hacia la última dirección WORD (16 bits) de la Flash (16 bits que ocupan  $FFFE  y  $FFFF),  PERO  ALLÍ  LO  QUE  ESPERA  encontrar  es  LA  "DIRECCIÓN"  DE  LA  PRIMERA  INSTRUCCIÓN DE SU PROGRAMA. El mecanismo que se emplea para establecer este vínculo se  forma de la siguiente manera:  06        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  08        ORG     $2080       ; ORG? Origin. Next code (bra) is located  ...  12 Main:  bra     *   ; Branch to himself (BRA: Branch Always; *: here!)  ...  20        ORG     $FFFE       ; VECTOR holding address for HCS08 startup  21        DC.W    $2080       ; ...(RESET)    DC.W: Define CONSTANT Word    En 06 usted define la Etiqueta (Main) en la que va a comenzar su programa, e indica en  qué  posición  (válida)  de  Flash  la  va  a  colocar  (el  resto  de  su  código  va  a  continuación!); luego en la 12 está su 'Main' y, en la 20 y siguientes se culmina el  vínculo:  cuando  el  PC  asume  su  valor  inicial,  $FFFF,  el  CPU,  que  está  en  la  fase  de  inicialización,  usa  ese  PC  para  cargar  un  valor  de  16  bits,  que  corresponde  a  la  dirección de su primera instrucción. Reemplaza su $FFFF por lo que está almacenado allí  (el número $2080 en el ejemplo) y de esa posición toma la primera instrucción que va a  ejecutar, de SU programa.    Al  espacio  en  donde  se  colocan  los  valores  de  arranque  (o  RESET),  y  otros  que  luego  estudiaremos,  se  lo conoce  como el  área  de  Vectores  de  Interrupción.  Comienzan  en  la  ÚLTIMA  posición  de  memoria,  y  va  creciendo  HACIA  ABAJO,  de  a  dos  posiciones,  porque  cada elemento de información allí almacenado corresponde a direcciones (de arranque, o  de rutinas de interrupción: ISR), y las direcciones siempre tienen 16 bits.    Todo programa finaliza con un END:    22    END                     ; END and next lines, removed by Assembler  24 Note: Nothing is read after END... (cfr. 'main.dbg')    Se  ha  incluido  ex  professo  la  línea  24,  con  texto  común  y  corriente,  para  enfatizar  que, después del END del programa principal, el Ensamblador NO lee absolutamente nada  más. Puede usarse, por tanto, esa área para incluir texto cualquiera, como comentarios.    Tenga CUIDADO: Algunos ensambladores suspenden el análisis cuando encuentran CUALQUIER  END.  A  veces,  si  a  un  INCLUDE  FILE,  similar  al  que  ya  conoce  del  C,  se  le  coloca  inadvertidamente  un  END,  el  programa  queda  procesado  hasta  allí,  y  con  mucha  probabilidad eso no es lo que el programador desea...  2) Segundo Programa para el HCS08: Init COP & STACK Hay  un  código  mínimo  que  SIEMPRE  debe  incluir  en  sus  programas,  y  se  indica  a  continuación:      40 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ["Laboratorios\Lab1\00c_L1.asm"]  01 ;********************************************************************  02 ; 00c_L1.asm, MINIMUM Program for HCS08, V08J2012  03 ; Luis G. Uribe C., J09A2009. For CodeWarrior 10.2  04 ; ===================================================================  05 ; Begin of Code Section  06        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  07  08        ORG     $2080       ; HCS08 ROMStart (Flash)  09        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10        ; *** ALWAYS *** include the following 4 instructions  11  12 Main:  lda    #$42         ; $42(%0100_0010)? COPE(SOPT1b7)=0 ($1802)  13         sta    $1802       ; (SOPT1 Pup init to $C2 = %1100_0010)  14        ldhx   #$1800       ; Init Stack Pointer SP (HCS08)  15         txs                ; $1800? RAMEnd + 1.  SP = RAMEnd: $17FF  17        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  18        bra     *   ; Branch to himself (BRA: Branch Always; *: here!)  19  20        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  21        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  22                            ; This 'nop' MAY be removed for CW 6.3 ...  23 ;********************************************************************  24 ; Interrupt Vectors  25  26        ORG     $FFFE       ; "Vector" holding starting address,  27        DC.W    $2080       ; ...(RESET)    DC.W: Define CONSTANT Word  28    END    COMENTARIOS a ["Laboratorios\Lab1\00c_L1.asm"]: Cuatro líneas se han añadido por las siguientes dos razones:    PRIMERA:  Casi  todos  los  microcontroladores  (MCU)  incluyen  un  dispositivo  que  por  hardware lo inicializan (resetean) si no se ha cumplido cierto protocolo con él; se lo  conoce como el "Watch Dog". Su propósito es el de restaurar la máquina, tal como si se  hubiera activado la señal de RESET (o se hubiera encendido el MCU en ese momento). Bien  instrumentado,  puede  evitar  costosísimos  desplazamientos  a  reinicializar  nuestros  equipos, en caso de que por alguna fluctuación en la energía eléctrica, o impacto con  un rayo gamma o partículas alpha, el flujo del programa tome un derrotero no previsto.  En el MC9S08QE128 al Watch Dog se lo conoce como COP: Cpu Operating Properly. En los  programas  que  se  cubren  en  esta  publicación  NO  se  utiliza  el  COP,  pero  hay  que  DESACTIVARLO  porque  el  MCU  lo  tiene  activo  al  aplicársele  la  energía.  Ese  es  el  propósito del código de las líneas:  12 Main:  lda    #$42         ; $42(%0100_0010)? COPE(SOPT1b7)=0 ($1802)  13         sta    $1802       ; (SOPT1 Pup init to $C2 = %1100_0010)    Se han incluido valores en Hexadecimal, pero luego veremos que hay un mecanismo que nos  permite hacer referencias SIMBÓLICAS, que ofrecen mayor claridad a quien codifica y a  quien luego quiere entender el programa.  41 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 En el registro SOPT1 (SYSTEM OPTION 1, ubicado en la posición $1802) se deshabilita el  COP:  COPE=0:  COP  Enable=0.  Como  esta  es  una  máquina  de  Acumulador,  se  necesitan  dos  instrucciones para hacer el MOVE de un dato a una variable: LOAD Accumulator con #$42 y  STORE Accumulator a la posición $1802 (líneas 12 y 13).  Nótese la sangría de un espacio que he incluido en la línea 13 con respecto a la 12.  Sirve  para  enfatizar  que  las  dos  instrucciones  físicas  realizan  una  sola  operación  lógica. Usted debe tratar de hacer lo mismo. Luego aprenderemos un mecanismo: MACROs,  que  le  permiten  definir  sus  propias  instrucciones,  como  MOVE,  que  reemplazan  a  la  secuencia de LDA y STA señaladas.  SEGUNDA  razón:  Al  encender  el  MCU  el  Stack  Pointer  (SP),  al  igual  que  el  PC,  se  inicializa en un valor útil para todos los modelos de MCU de las familias HC05 y HC08,  algunos de los cuales que pueden tener muy pequeña RAM. En nuestro caso no es así; en  el  HC9S08QE128  hay  mucha  RAM  (8064  bytes  reporta  el  Reference  Manual),  así  que  el  manufacturante recomienda inicializar el stack (vía el SP) así:  14        ldhx   #$1800       ; Init Stack Pointer SP (HCS08)  15         txs                ; $1800? RAMEnd + 1.  SP = RAMEnd: $17FF    Observen de nuevo la sangría.    Debido  a  ciertas  peculiaridades  de  la  Arquitectura  de  este  MCU,  la  instrucción  TXS  (Transfer indeX to Sp) RESTA un UNO al registro índice H:X ANTES de transferir el valor  original al SP. Por eso, dado que la última posición de RAM es la $17FF, y que el Stack  arrancará ahí (y crecerá hacia ABAJO), hay que colocar en H:X el valor $1800.    3) Programación en Lenguaje de Máquina (HEX) para el HCS08: Se  presenta  la  forma  más  elemental  en  la  que  se  le  pueden  dar  las  instrucciones  al  Microprocesador. Como ya dijimos, los comandos de las instrucciones se individualizan y  diferencian  unos  de  otros,  identificándolos  mediante  la  asignación  de  un  número,  o  Código de Operación (OpCode). El diseñador del MCU hace la asignación, y hay una tabla  en la que se colocan los comandos y su Código de Operación. Así, si queremos borrar una  variable que hemos localizado en la primera posición de la Memoria de Acceso Directo o  Página 0 (ZRam) necesitaremos buscar el código del CLEAR y escribir algo así:    Main:  DC.B    $3F         ; Define Constant Byte. $3F $80? CLR $80         DC.B    $80         ; $80? Direct Address    Por suerte el programa Assembler permite referirnos a los OpCode mediante los SÍMBOLOS  que el manufacturante definió para ellos (CLR para BORRAR memoria, etc.), y no tenemos  ni qué memorizar sus códigos hexadecimales ni buscar en tablas las equivalencias.    Se trata de emplear, al MÁXIMO, símbolos alfanuméricos, los de las instrucciones, y a  las  variables  y  ciertas  posiciones  de  código  también  les  damos  NOMBRES,  ojalá  con  significado claro y preciso, tal como se hace en C. El objetivo es el de hacernos más  fácil  la  composición,  lectura  e  interpretación  del  código,  y  ese  mecanismo  pretende  ELEVAR EL VALOR SEMÁNTICO DEL LENGUAJE, que ya de por sí, es bastamte BAJO…    Puede verse completo este tercer ejemplo a continuación:    42 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ["Laboratorios\Lab1\01a_L1.asm"]  01 ;********************************************************************  02 ; 01a_L1.asm, MINIMUM HCS08 Program Rev.E, V08J2012 >> HEXADECIMAL <<  03 ; Luis G. Uribe C., D27Y2007 J09A2009. For CodeWarrior 10.2  L21N2013  04 ; Minimum aspects needed to write a program:  05 ; 1.‐ Define program ORG ($2080 and up)  06 ; 2.‐ Write the program (look up for each instruction code in Hex)  07 ; 3.‐ Define RESET Address (vector $FFFE)  08 ; NOTE: Look up "SortedHCS08_ISet2013‐U.pdf" for Op. Codes  09 ; ===================================================================  10 ; Begin of Code Section  11        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  12  13        ORG     $2080       ; HCS08 ROMStart (Flash)  14 Main:  DC.B    $3F         ; Define Constant Byte. $3F $80? CLR $80  15        DC.B    $80         ; $80? Direct Address    16        DC.B    $3C         ; LOOP: $3C,$80? INC $80 (direct variable)  17        DC.B    $80         ; $80? Direct Address    18        DC.B    $20         ; BRA LOOP  19        DC.B    $FC         ; $FC? Relative offset. BRA *: $20‐2 ($FE)  20                            ; $FC?‐4. PC points to NOP. PC‐4 is 'LOOP'  21        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  22        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  23                            ; This 'nop' MAY be removed for CW 6.3 ...  24 ;********************************************************************  25 ; Interrupt Vectors    26        ORG     $FFFE    27        DC.B    $20         ; Reset Vector HIGH  28        DC.B    $80         ; Reset Vector LOW (HCS08 is BIG ENDIAN)  29  30    END  31 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32      Byte‐addressable processors may be categorized as big endian,  33    little endian, or bi‐endian.  35      A multibyte numerical value stored with the MOST significant  36    byte in the LOWEST numerical address is stored in big‐endian  37    fashion.  39      The little‐endian style stores the MOST significant byte in the  40    HIGHEST numerical address. Intel microprocessor are LITTLE ENDIAN.  41  42    A bi‐endian processor can handle both styles (Motorola's PowerPC).    COMENTARIOS a ["Laboratorios\Lab1\01a_L1.asm"]: Hay  varios  documentos  en  los  que  pueden  encontrar  la  asociación  entre  Comandos  y  OpCodes. Por ejemplo:    08 ; NOTE: Look up "SortedHCS08_ISet2013‐U.pdf" for Op. Codes  43 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Primero se define, como siempre, la asociación para el vector de RESET:    11        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  13        ORG     $2080       ; HCS08 ROMStart (Flash)  14 Main ...    El  resto  del  programa  inicializa  la  variable  en  cero,  la  incrementa  y  retorna  a  incrementarla por siempre. Puede verificar el comportamiento del programa mediante el  Debugger,  que  permite  correrlo  paso  a  paso,  e  ir  viendo  cada  vez  el  contenido  de  la  variable en la posición $80:    14 Main:  DC.B    $3F         ; Define Constant Byte. $3F $80? CLR $80  15        DC.B    $80         ; $80? Direct Address  16        DC.B    $3C         ; LOOP: $3C,$80? INC $80 (direct variable)  17        DC.B    $80         ; $80? Direct Address  18        DC.B    $20         ; BRA LOOP  19        DC.B    $FC         ; $FC? Relative offset. BRA *: $20‐2 ($FE)  20                            ; $FC?‐4. PC points to NOP. PC‐4 is 'LOOP'    En  cuanto  a  las  líneas  18  y  19,  que  codifican  un  salto  incondicional  (BRA:  Branch  Always) al "Loop" de Incremento, es interesante notar lo siguiente, que es de uso común  entre casi todo los procesadores:    Para realizar un salto a alguna posición de código dentro de la memoria, el OpCode es  el que corresponda, en nuestro caso un BRA, y luego hay que identificar la posición a  la cual saltar. Como el programa en una máquina de Von Neumann puede estar en cualquier  parte  de  la  memoria,  se  necesita  identificar  una  dirección  de  16  bits,  en  nuestro  micro, que direcciona en forma natural, 64KBytes. De esa manera, las instrucciones de  ramificación, que modifican el flujo del programa, y que forman uno de los grupos más  usados, tendrían 3 bytes: uno para el OpCode y dos para la dirección.    Una forma de optimización en esta materia se logra mediante la siguiente observación:  la  gran  mayoría  de  saltos  se  realizan  en  la  "LOCALIDAD",  que  es  un  concepto  que  identifica  de  manera  genérica,  las  posiciones  cercanas  a  la  dirección  del  salto;  sus  VECINAS.  Y  esa  localidad  es  muy  numerosa  en  un  rango  de  127  posiciones  adelante,  y  hasta  128  atrás.  Lo  cual  es  muy  conveniente,  ya  que  esa  información  es  la  que  se  codifica mediante bytes con signo (signed chars). Así, se logra que las instrucciones  de  ramificación  que  más  se  emplean  tengan  solo  DOS  bytes:  uno  para  el  OpCode,  y  uno  para  el  DESPLAZAMIENTO  (no  se  indica  a  dónde,  sino  que  se  señalan  cuántas  posiciones  adelante o atrás, mediante un Offset). Desde luego, hay un grupo de instrucciones que  permiten  saltar  a  cualquier  lado.  En  esta  máquina,  los  BRANCHES  son  RELATIVOS,  es  decir,  emplean  Desplazamientos  que  indican  el  offset  en  RELACIÓN  a  la  instrucción.  Y  los JUMPS son absolutos, emplean direcciones de 16 bits.    Lo interesante es el mecanismo para calcular la dirección efectiva, destino del salto:  A la posición actual (registrada siempre mediante el PC), se le debe sumar el Offset  con signo para obtener así la dirección de la próxima instrucción.    Se dice bien pero hay dos cosas sutiles que aclarar. La primera es en relación al uso  del PC. Lo segundo, ver cómo las direcciones, que son números SIN signo, suman bien con  los “Offset”, que son números CON signo.    44 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La operación del PC, en todos los micros que conozco, es como sigue: se comienza con el  PC  apuntando  a  la  próxima  instrucción  que  se  va  a  ejecutar.  Cuando  se  trata  de  instrucciones de varios bytes, como es el caso del BRANCH, apunta al OpCode. Cuando se  comienza  a  ejecutar  la  instrucción,  en  el  ciclo  de  Fetch,  el  CPU  lee,  usando  la  dirección almacenada en el PC, el OpCode, y apunta hacia el siguiente elemento. Esto lo  hace  incrementando  el  PC.  Es  decir,  para  cada  lectura  se  produce  la  siguiente  secuencia:       InstructionRegister = *PC++;    // se carga el OpCode y se apunta                                     // ..al siguiente byte: el Offset     *DataBuf = *PC++;   // se carga el Offset y el PC, en este caso,                         // ..apunta al próximo byte: Next OpCode.    Ahora, si la dirección del comienzo de la instrucción es N, al finalizar la secuencia  anterior el PC alberga N + 2. Así que una instrucción como: BR *, en la que se desea  hacer un ciclo infinito de saltos a sí mismo, el Offset que hay que colocar es... ‐2.  De esa manera el próximo PC será PC = N + 2 ‐ 2 = N, que era la dirección original.    O  sea,  es  importante  al  calcular  el  Offset,  recordar  que  para  el  momento  de  la  aplicación de PC + Offset, el PC apunta dos bytes hacia abajo.    ESTA  ES  LA  FUENTE  DEL  ERROR  EN  EL  CODEWARRIOR,  para  solucionar  el  cual  tuve  que  agregar el NOP del que ya hemos hablado. Cuando se ejecuta el BRANCH, el PC apunta  MÁS ALLÁ DE LA MEMORIA QUE LE HA SIDO ASIGNADA AL PROGRAMA. Allí NO hay código, y  el  Assembler  cree  que  se  ha  cometido  un  error,  cuando  en  realidad  no  es  así.  De  hecho, la versión 6.3 funciona muy bien sin ese NOP.    En relación al segundo punto, uno de los problemas del cuestionario pide demostrar que  a un número sin signo (una dirección) se le puede sumar uno CON signo (el Offset), y el  resultado  es  perfecto:  la  dirección  requerida,  adelante  o  atrás,  de  la  dirección  original.    ESTA PROPIEDAD DE LOS NÚMEROS CON SIGNO, CODIFICADOS EN COMPLEMENTO A DOS, ES LA RAZÓN  PRIMORDIAL  POR  LA  CUAL  SE  USA  ESTA  REPRESENTACIÓN  NUMÉRICA,  y  no  se  emplean  el  complemento a 1 (ni mucho menos otras más engorrosas, como Signo y Magnitud).    En la parte final del ejercicio se ejemplifica el orden en que una información de 16  bits (2 bytes) debe representarse en una máquina BIG ENDIAN, como es el caso del HC08:  Yendo desde las direcciones inferiores hasta las superiores (de $FFFE a $FFFF), primero  se  coloca  el  byte  de  Mayor  valor  y  a  continuación,  de  último,  va  el  final:  la  parte  Menos  significativa  del  número.  La  información  termina  (END)  en  la  dirección  mayor  (BIG); de ahí el término ”BIG ENDIAN”.    (Si  este  fuera  un  microprocesador  Intel,  en  la  misma  secuencia  de  direcciones  los  valores  almacenados  serían,  primero,  $80  y  luego,  $20:  LITTLE  ENDIAN;  es  decir  el  número termina [END; $80] en la parte mas baja de memoria [LITTLE]):    25 ; Interrupt Vectors  26        ORG     $FFFE  27        DC.B    $20         ; Reset Vector HIGH  28        DC.B    $80         ; Reset Vector LOW (HCS08 is BIG ENDIAN)    45 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 4) Invente su propio Instruction Set para el HCS08: Invente SU propio Instruction Set. Esto casi nunca se usa, pero ilustra la manera como  trabaja el Assembler al traducir los códigos que usted le suministra.    Además hay al menos una manera más avanzada de hacer lo que este ejercicio nos muestra,  a base de MACROs, pero eso vendrá después.    ["Laboratorios\Lab1\01b_L1.asm"]  01 ;********************************************************************  02 ; 01b_L1.asm Symbolic Program (User defined) Rev. F V08J2012 M29E2013  03 ; Luis G. Uribe C., J09A2009  04 ; Aspectos mínimos necesarios al escribir un programa:  05 ; 1.‐ Definir posición inicial del código. $8000 up  06 ; 2.‐ Escribir el programa (buscar código HEX de c/instrucción)  07 ;     Hacer SU propio conjunto de instrucciones en Simbólico.  08 ; 3.‐ Definir Dirección de inicio de ejecución luego  09 ;     de Reset (vector $FFFE)  11 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  12 ; Parameter Definitions (User defined SYMBOLS via 'EQU')  13 ; You may define whatever symbols you like... Your own Assembly Language!  14  15 START: EQU     $2080  16 STARTh:EQU       $20  17 STARTl:EQU       $80    18 XCLR:  EQU       $3F       ; Instruction clr, tomada del Reference Manual  19 XOP1:  EQU       $80       ; Operand (1st variable) for clr and inc  20 XINC:  EQU       $3C       ; Instruction inc, del Reference Manual  21 XBRA:  EQU       $20       ; Instruction bra, del Reference Manual  22 XOFF:  EQU        ‐4       ; Offset for instruction bra  23 RESET: EQU     $FFFE       ; Vector de Interrupciones  24  25 ; ===================================================================  26 ; Begin of Code Section  27        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  28  29        ORG     $2080       ; HCS08 ROMStart (Flash)  30 Main:  DC.B    XCLR        ; Later TRAY THIS: Step some, Insert BREAK  31        DC.B     XOP1       ; ..POINT in Main. >>RUN<<  CW will stop..  32 loop:  DC.B    XINC        ; ..here. WHY? **BECAUSE COP is ENABLED**!  33        DC.B     XOP1       ; loop  34        DC.B    XBRA  35        DC.B     XOFF  36        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  37        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  38                            ; This 'nop' MAY be removed for CW 6.3 ...  39 ;********************************************************************  40 ; Interrupt Vectors  42        ORG     RESET  44        DC.B    STARTh      ; Reset Vector HIGH  45        DC.B    STARTl      ; Reset Vector LOW (HCS08 is BIG ENDIAN)  46 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 46  47 ; NOTE: If you remove ALL references to RESET, STARTh and STARTl...  48 ;       the program continues to be "debugged" fine!  49 ;       The DEBUGGER uses the 'ABSENTRY Main' instead...  50 ;       The actual CPU NEEDs the RESET Vector (Vreset)    COMENTARIOS a ["Laboratorios\Lab1\01b_L1.asm"]: Lo  primero  es  generar  SUS  propios  OpCodes.  Los  hemos  comenzado  con  una  'X'  para  ayudarnos con la diferenciación entre sus códigos y los del manufacturante.    18 XCLR:  EQU       $3F       ; Instruction clr, tomada del Reference Manual  19 XOP1:  EQU       $80       ; Operand (1st variable) for clr and inc  20 XINC:  EQU       $3C       ; Instruction inc, del Reference Manual  21 XBRA:  EQU       $20       ; Instruction bra, del Reference Manual  22 XOFF:  EQU        ‐4       ; Offset for instruction bra  23 RESET: EQU     $FFFE       ; Vector de Interrupciones    La parte siguiente es muy similar al ejercicio anterior: 01a_L1.asm:    29        ORG     $2080       ; HCS08 ROMStart (Flash)  30 Main:  DC.B    XCLR        ; Later TRAY THIS: Step some, Insert BREAK  31        DC.B     XOP1       ; ..POINT in Main. >>RUN<<  CW will stop..  32 loop:  DC.B    XINC        ; ..here. WHY? **BECAUSE COP is ENABLED**!  33        DC.B     XOP1       ; loop  34        DC.B    XBRA  35        DC.B     XOFF    40 ; Interrupt Vectors  42        ORG     RESET  44        DC.B    STARTh      ; Reset Vector HIGH  45        DC.B    STARTl      ; Reset Vector LOW (HCS08 is BIG ENDIAN)    5) Usando los OpCodes del Fabricante para el HCS08:   El mismo ejercicio anterior, 01a_L1.asm, todo escrito en lenguaje SIMBÓLICO. Ya usted  no usa SUS propios OpCodes, sino los de Motorola.    Usted (casi) NUNCA debe emplear números; los símbolos alfanuméricos, como en el C, son  más  o  menos  descriptivos,  dependiendo  de  lo  bien  que  usted  se  aplique  a  hacer  las  definiciones.  Pero,  un  número  como  $40,  difícilmente  será  recordado  como  la  cantidad  numérica que se usa para deshabilitar el COP, cuando se lo almacena en el $1802 (SOPT1)    ["Laboratorios\Lab1\01c_L1.asm"]  01 ;********************************************************************  02 ; 01c_L1.asm Symbolic Program (User defined), Rev. E V08J2012  03 ; Luis G. Uribe C., J09A2009  04 ; Aspectos mínimos necesarios al escribir un programa:  05 ; 1.‐ Definir posición inicial del código. $2080 up  06 ; 2.‐ Escribir el programa (buscar código SIMBÓLICO de c/instrucción)  07 ; 3.‐ Definir Dirección de inicio de ejecución luego  47 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 08 ;     de RESET (vector $FFFE)  09  10 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  11 ; Parameter Definitions  12  13 START: EQU     $2080       ; comienzo de la Flash  14 RESET: EQU     $FFFE       ; vector para indicar comienzo del código  15  16 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  17 ; Definición de variables  18  19 Var:   EQU     $80         ; una forma de identificar variables  20  21 ;====================================================================  22 ; Begin of Code Section  23        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  24  25        ORG     START       ; HCS08 ROMStart (Flash)  26 Main:  clr     Var         ; Ensayar: clr VAR (mayúsculas. Da error)  27 loop:  inc     Var  28        bra     loop  29        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  30        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  31                            ; This 'nop' MAY be removed for CW 6.3 ...  32 ;********************************************************************  33 ; Interrupt Vectors  34  35        ORG     RESET  36        DC.W    Main        ; RESET: HCS08 Power On (PON) procedure.  37    END    COMENTARIOS a ["Laboratorios\Lab1\01c_L1.asm"]: Los elementos importantes son:    11 ; Parameter Definitions  13 START: EQU     $2080       ; comienzo de la Flash  14 RESET: EQU     $FFFE       ; vector para indicar comienzo del código    Asocian números a símbolos alfanuméricos. Dice mucho más RESET, que $FFFE.    17 ; Definición de variables  19 Var:   EQU     $80         ; una forma de identificar variables  Mucho mejor el símbolo de alto nivel 'Var', y no tener que recordar que su dirección es  la $80, que además puede cambiar si se agregan o eliminan variables.    25        ORG     START       ; HCS08 ROMStart (Flash)  26 Main:  clr     Var         ; Ensayar: clr VAR (mayúsculas. Da error)  27 loop:  inc     Var  28        bra     loop    48 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Luego  usaremos  el  símbolo  ROMStart,  que  es  el  definido  por  el  manufacturante.  Las  etiquetas de programa lucen como en C. (Con ustedes es un problema hacer referencias al  C, cuando con toda seguridad NINGUNO usó nunca etiquetas para identificar partes de su  código en C. Así, no puedo basarme en sus (des)conocimientos de C para ayudarme en las  explicaciones de este curso. Lástima...)    33 ; Interrupt Vectors  35        ORG     RESET  36        DC.W    Main        ; RESET: HCS08 Power On (PON) procedure.    Todo  en  simbólico.  Tampoco  usaremos  'RESET',  pues  hay  una  serie  de  símbolos  para  la  tabla de Interrupt Vectors, definidos por el manufacturante.    6) Forma DEFINITIVA para programar el HCS08:   FULL Use of Motorola's Symbolic Language    Ahora sí; esta es LA forma definitiva que usted debe usar para escribir sus programas.    ["Laboratorios\Lab1\01d_L1.asm"]  01 ;********************************************************************  02 ; 01d_L1.asm Use of Motorola's Symbolic Language, Rev. E V08J2012  03 ; Luis G. Uribe C., J09A2009  04 ; Aspectos mínimos necesarios al escribir un programa:  05 ; 1.‐ Definir posición inicial del código. $8000 up  06 ; 2.‐ Escribir el programa (buscar código SIMBÓLICO de c/instrucción)  07 ; 3.‐ Definir Dirección de inicio de ejecución luego  08 ;     de RESET (vector $FFFE)  09  10 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  11 ; Include files: Work exactly like in "C"; define processor symbols:  12 ; .. Z_RAMStart, ROMStart, INT_RESET, registers, I/O ports...  13 ; ..derivative.inc is prepared by the Project Wizzard, for each CPU.  14  15        INCLUDE 'derivative.inc'  16 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  17 ; Definición de variables  18  19        ORG     Z_RAMStart  ; $80  20  21 Var:   DS.B    1           ; DS.B? 1 byte variables Name&Data Storage  22  23 ; ===================================================================  24 ; Begin of Code Section  25        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  26  27        ORG     ROMStart    ; HCS08 ROMStart (Flash)  28 Main:  clr     Var         ; ensayar: clr VAR (mayúsculas. Da error)  29 loop:  inc     Var  30         bra    loop  31        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  49 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 32        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  33                            ; This 'nop' MAY be removed for CW 6.3 ...  34 ;********************************************************************  35 ; Interrupt Vectors  36  37        ORG     Vreset  38        DC.W    Main        ; RESET: HCS08 Power On (PON) procedure.  39    END  40  41 SEE 'MC9S08QE128.inc' for Symbols Definitions like:  42 ‐ Z_RAMStart  ; $80  43 ‐ ROMStart    ; HCS08 ROMStart (Flash)  44 ‐ Vreset  45  46 'MC9S08QE128‐U.inc' was ***COMPLETLY*** REARRANGED and BEAUTIFIED by  47 Luis G. Uribe C., J07J2012, for DOCUMENTATION PURPOSES ONLY. Do NOT  48 REPLACE the original...    COMENTARIOS a ["Laboratorios\Lab1\01d_L1.asm"]: Este código no necesita explicación adicional.    7) CODE SECTION, DATA SECTION, en el HCS08: Ya hemos visto cómo, para comenzar a escribir nuestro programa, comenzamos definiendo  un  ORG  START  (START,  o  su  equivalente  que  cada  vez  usaremos  menos:  $2080).  En  CodeWarrior esto no recibe una identificación particular, pero en otros ensambladores,  y dado que allí se está definiendo código, se lo denomina CODE SECTION. Y hay, por lo  mismo,  un  DATA  SECTION,  cuando  queremos  definir  variables  de  manera  simbólica  (que  todavía no hemos visto). Un uso muy atractivo es que nos permite comenzar escribiendo  código en el CODE SECTION y luego, cuando identificamos la necesidad de usar una o más  variables, cambiamos a DATA SECTION, definimos la variable, y nos devolvemos al sitio  donde  íbamos  con  otro  CONTROL  SECTION.  (Estas  parejas  reciben  el  nombre  genérico  de  PROGRAM SECTIONS. Un lenguaje de alto nivel muy famoso que hizo uso de estas secciones  fue el Cobol, copiado luego por el PL‐1 de IBM, en el que yo trabajé cuando hacía mi  maestría en Sistemas, hay como un millón de años...)    Si  quisiéramos  tener  facilidad  de  hacer  lo  mismo  en  CodeWarrior  (lo  cual  está  en  un  nivel un tanto más avanzado de lo que corresponde a este curso, que NO es un curso de  Lenguajes,  sino  de  ARQUITECTURA),  la  forma  es  la  que  se  indica  en  el  ejercicio  a  continuación, único en tocarse este tema.    ["Laboratorios\Lab1\01e_L1.asm"]  01 ;********************************************************************  02 ; 01e_L1.asm Symbolic Assembly Language, Rev. E V08J2012  03 ; Luis G. Uribe C., J09A2009  04 ; ADVANCED way to define variables...  05 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  06 ; Include files  08        INCLUDE 'derivative.inc'  09 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10 ; Parameter definitions  50 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 12 ram:   set     Z_RAMStart  ; Set 'ram' to $80  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; Variables Definición (ADVANCED)  15  16        ORG     ram         ; Start in $80  17  18 Var:   DS.B    1           ; DS.B? 1 byte variables Name&Data Storage  19 Varend:  20 ram:   SET     Varend      ; $81  21  22        ORG     ram         ; Re‐start in $81  23 Var2:  DS.B    1  24 Var2end:  25 ram:   SET     Var2end     ; $82  26  27    ORG     ram             ; Re‐start in $82  28 Var3:  DS.B    1  29 Var3end:  30 ram:   SET     Var3end     ; $83... and SO ON.  31  32 ;====================================================================  33 ; Begin of Code Section  34        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  36        ORG     ROMStart    ; HCS08 ROMStart (Flash)  37 Main:  ldhx   #Var  38        ldhx   #Var2  39        ldhx   #Var3  40        bra     *  41        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  42        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  43                            ; This 'nop' MAY be removed for CW 6.3 ...  44 ;********************************************************************  45 ; Interrupt Vectors  46  47        ORG     Vreset  48        DC.W    Main        ; RESET: HCS08 Power Up (Pup) procedure.  49    END    COMENTARIOS a ["Laboratorios\Lab1\01e_L1.asm"]: La primera novedad es el uso de INCLUDE files:    06 ; Include files  08        INCLUDE 'derivative.inc'    Esta  parte  deberá  incluirse  SIEMPRE  en  sus  ejercicios,  de  ahora  en  adelante.  El  ambiente  de  desarrollo  particulariza  (customize)  o  parametriza  una  serie  de  definiciones, a fin de hacer lo más portable posible los programas. Y nuestra interfaz  con ese resultado es a través de esa línea 08: INCLUDE 'derivative.inc'    Luego, la definición de variables, empleando un ORG, tal como para el código, pero en  las direcciones correspondientes a las variables: la Random Acces Memory, RAM:  51 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08      ORG ram:    10 ; Parameter definitions  12 ram:   set     Z_RAMStart  ; Set 'ram' to $80  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; Variables Definición (NORMAL, LA QUE UD. DEBE EMPLEAR)  16        ORG     ram         ; Start in $80  18 Var:   DS.B    1           ; DS.B? 1 byte variables Name&Data Storage  23 Var2:  DS.B    1  28 Var3:  DS.B    1    Hasta allí la parte común de la definición de variables, que usted SIEMPRE usará en sus  programas.    Ahora, si se deseara definir una variable, y más adelante otra, y luego otra, el truco  consiste  en  REDEFINIR  la  posición  'RAM'  para  que  lleve  la  cuenta  hasta  adónde  hemos  llegado definiendo variables. Por eso, en el programa que estamos analizando el código  NO  va  como  las  8  líneas  anteriores,  sino  que  en  realidad  es  como  se  ilustra  a  continuación.    Comencemos diciendo que en el HC9S08QE128, las áreas de RAM son:  Z_RAMStart:        equ $00000080  Z_RAMEnd:          equ $000000FF    RAMStart:          equ $00000100  RAMEnd:            equ $000017FF    RAM1Start:         equ $00001880  RAM1End:           equ $0000207F    NOTE que hay un AGUJERO, un espacio no definido dentro de ese campo de direcciones; lo  emplean  REGISTROS  usados  en  periféricos.  En  la  $1800,  por  ejemplo,  se  encuentra  el  registro       SRS ‐System Reset Status Register; 0x00001800  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  Entonces, el código bajo análisis es, en realidad, como sigue:    10 ; Parameter definitions  12 ram:   set     Z_RAMStart  ; Set 'ram' to $80  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; Variables Definición (ADVANCED)  16        ORG     ram         ; Start in $80  17  18 Var:   DS.B    1           ; DS.B? 1 byte variables Name&Data Storage  19 Varend:  20 ram:   SET     Varend      ; $81  21  22        ORG     ram         ; Re‐start in $81  23 Var2:  DS.B    1  24 Var2end:  52 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 25 ram:   SET     Var2end     ; $82  26  27    ORG     ram             ; Re‐start in $81  28 Var3:  DS.B    1  29 Var3end:  30 ram:   SET     Var3end     ; $83... and SO ON.    En  realidad,  en  esta  secuencia  contigua  SOLO  HACE  FALTA  lo  que  ya  se  ilustró  en  las  líneas inmediatamente anteriores: 10, 12, 14, 16, 18, 23, 28.       Pero usted puede hacer algo como esto:              ORG     ram         ; Start in $80     Var:   DS.B    1           ; DS.B? 1 byte variables Name&Data Storage     Varend:     ram:   SET     Varend      ; ram = $81              ORG     rom         ; Start in $2080     ; ... CÓDIGO, CÓDIGO, CÓDIGO     ActualRom:     rom:   SET     ActualRom   ; rom = $20XX (donde su programa vaya)              ORG     ram         ; Re‐start in $81, en este ejemplo     Var2:  DS.B    1     Var2end:     ram:   SET     Var2end     ; ram = $82              ORG     rom         ; Start in $20XX (donde su programa iba)     ; ... MÁS_CÓDIGO1, MÁS_CÓDIGO1, MÁS_CÓDIGO1     ActualRom1:     rom:   SET     ActualRom1  ; rom=$20XX (donde su programa vaya ahora)              ORG     ram         ; Re‐start in $82     Var3:  DS.B    1     Var3end:     ram:   SET     Var3end     ; $83... and SO ON.              ORG     rom         ; Start in $20XX (donde su programa iba)     ; ... MÁS_CÓDIGO2, MÁS_CÓDIGO2, MÁS_CÓDIGO2     ActualRom2:     rom:   SET     ActualRom2  ; $20XX (donde su programa vaya ahora...)       y ya se hacen una idea. "THE POOR'S MAN PROGRAM SECTION aproach"...    El resto del programa no tiene, en este ejemplo, mayores dificultades:    36        ORG     ROMStart    ; HCS08 ROMStart (Flash)  37 Main:  ldhx   #Var  38        ldhx   #Var2  39        ldhx   #Var3  40        bra     *    53 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Trate  usted  de  cambiar  el  programa  para  incluir  código  de  verdad,  entremezclado  con  definición de variables.    Aun cuando le parezca arrevesado este modo de programación (y, en realidad, así es), lo  que  ocurre  es  que  cuando  estamos  haciendo  uso  de  MACROs,  que  son  rutinas  que  se  escriben en otras partes, o librerías, y en donde puede necesitarse agregar variables,  que usted no ha previsto en el programa principal, esas MACROs pueden hacer uso de este  mecanismo,  si  se  establece  un  protocolo  (ojalá  de  aplicación  automática)  que  permita  marcar  el  sitio  en  donde  el  programa  principal  iba  al  momento  de  llamar  a  la  MACRO,  definir  las  variables  a  las  que  hubiera  lugar,  y  luego  dejar  el  CODE  SECTION  en  el  preciso valor en donde el programa lo necesita.    Programa y conceptos muy importantes, pero que al nivel de este curso, en donde NO se  está estudiando en realidad el Assembler Language, no es tan relevante. Pero para mí,  que debo escribirles rutinas y bibliotecas de ellas, es fundamental.     PROGRAMAS AVANZADOS EN ASSEMBLY LANGUAGE, PARTE I   8) Serie de Fibonacci.   Este programa lo adapté del manual del Assembler Eclipse (la nueva versión 10.x se basa  en Eclipse, un IDE gratuito, construido por IBM, y regalado a la comunidad. Es usado  por  Microchip,  y  ahora  por  Freescale,  y  otros.  Está  siendo  adaptado  en  muchas  plataformas. Por ejemplo, el desarrollo para celulares ANDROID se basa en la plataforma  Eclipse...)    No  hay  mayores  comentarios  sobre  este  programa.  Usted  debe  ser  capaz  de  decir  cómo  funciona y, en caso de duda, acuda al Debugger del CodeWarrior, siga el programa paso a  paso, y podrá hacerse una idea del funcionamiento).    ["Laboratorios\Lab1\02Fibonacci.asm"]  01 ;********************************************************************  02 ; 02Fibonacci.asm, Luis G. Uribe C., V10A2009 V08J2012  03 ; ADAPTED from: HCS08‐RS08_Assembler_MCU_Eclipse.pdf, Listing 4.1  04 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  05 ; Include files  06        NOLIST  07          INCLUDE 'derivative.inc'  08        LIST  09 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10 ; Parameter definitions  11  12 ram:           SET     Z_RAMStart  ; $80  13 initStack:     EQU     $1800  14 COP_Disable:   EQU     %01000010   ; $42  15 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 ; Definición de variables (ds: byte por defecto)  54 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 17        ORG     ram  18  19 Counter:   DS.B    1  20 FiboRes:   DS.B    1       ; Aquí va la respuesta  21 ; ===================================================================  22 ; Begin of Code Section  23        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  24  25        ORG     ROMStart    ; HCS08 ROMStart (Flash)  26 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  27 ; *** ALWAYS *** include the following 4 instructions  28  29 Main:  lda    #COP_Disable  30         sta    SOPT1       ; System Options 1  31        ldhx   #initStack   ; Init SP  32         txs  33 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  34 mainLoop:  35        clra                ; Use Accumulator as a counter  36    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  37    cntLoop:  38        inca  39        cbeqa  #14, mainLoop; Larger values cause overflow  40        sta     Counter     ; Update global variable  41        bsr     CalcFibo  42         sta    FiboRes     ; Store result  43        lda     Counter     ; ..Activate BREAKPOINT here to see...  44                            ; .. 1 2 3 5 8 13 21 34 55 89 144 233  45    bra         cntLoop     ; Next round  46 ;====================================================================  47 ; Function to compute Fibonacci numbers. Argument is in A  48  49 CalcFibo:  50        dbnza   fiboDo      ; Fibonacci Do  51        inca  52    rts  53 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  54 fiboDo:  55        psha                ; The counter  56        clrx                ; Second last = 0  57        lda    #$01         ; Last = 1  58    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  59    FiboLoop:  60        psha                ; Push last  61        txa  62        add     1, sp  63        pulx  64    dbnz        1, sp, FiboLoop  65  66 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  67 FiboDone:  68        pulh                ; Release counter  55 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 69    rts                     ; Result in A  70        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  71        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  72                            ; This 'nop' MAY be removed for CW 6.3 ...  73 ;********************************************************************  74 ; Interrupt Vectors  75  76        ORG     Vreset  77        DC.W    Main        ; RESET: HCS08 Power Up (Pup)  78    END    COMENTARIOS a ["Laboratorios\Lab1\02Fibonacci.asm"]: Un aspecto a resaltar:    05 ; Include files  06        NOLIST  07          INCLUDE 'derivative.inc'  08        LIST    NOLIST  es  una  Pseudo  operación  que  indica  al  Assembler  que  el  contenido  del  include  file  NO  lo  incluya  en  los  listados  que  se  producen  como  resultado  del  proceso  de  ensamblaje.  De  esta  manera,  sólo  queda  en  esos  listados,  para  su  documentación,  información que usted considere relevante.    Debajo  del  include  file  es  menester  rehabilitar  la  generación  de  los  listados,  con  LIST, ya que de lo contario, TAMBIÉN SU CÓDIGO será eliminado.    9) Empleo de Subrutinas; Identificación de Modos de Direccionamiento.   Este programa enfatiza en el uso elemental de llamadas a funciones, o subrutinas (BSR).  Además,  se  identifican  algunos  modos  de  direccionamiento  empleados.  La  tabla  de  Vectores  de  Interrupción  se  ha  ampliado  para  incluir  IRQ  (Interrupt  ReQuest,  un  pin  externo  del  MCU  que  al  activarlo  genera  una  interrupción),  SWI  (una  instrucción  diseñada  para  servir  de  interfaz  con  el  Sistema  Operativo:  SoftWare  Interrupt,  muy  valiosa) y el consabido RESET. Se vuelve a mencionar el tema de suspensión del PROGRAM  SECTION para volver después.    IRQ y SWI en realidad NO juegan ningún papel en este programa; se escribió su entrada  en la tabla de Interrupt Vectors simplemente para ver cómo se incluirían.    ["Laboratorios\Lab1\Lab1_M\03a_L1_M.asm"]  01 ;********************************************************************  02 ; Programa 03a_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  03 ;  04 ; Nuevos Aspectos:  05 ;  06 ; Empleo de una SUBRUTINA dentro del programa, mediante "bsr" para el  07 ; ..llamado y "rts" para el retorno. Se usan los direccionamientos:  08 ; ‐ Inherente   ‐ Directo   ‐ Inmediato   ‐ Relativo  09 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  56 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 10 ; Include files  11        NOLIST  12          INCLUDE 'derivative.inc'  13        LIST  14  15 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 ; Parameter definitions  17 ram:           SET     Z_RAMStart  ; $80. Cfr. 'MC9S08QE128‐U.inc'  18 rom:           SET     ROMStart    ; $2080  19 initStack:     EQU     $1800  20 COP_Disable:   EQU     $42  21  22 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  23 ; Definición de variables (DS: DATA STORATE; Byte por defecto)  24        ORG ram  25  26 var:   DS  1               ; Equivalent to DS.B  27  28 ;====================================================================  29 ; Begin of Code Section  30        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  31  32        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  33  34    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  35    ; *** ALWAYS *** include the following 4 instructions  36 Main:  lda    #COP_Disable  37         sta    SOPT1       ; System Options 1  38        ldhx   #initStack   ; Init SP  39         txs                ; ..  40    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  41 loop:  clr     var         ; Direct   Addressing Mode  42        bsr     rutina      ; Relative Addressing Mode  43        bra     loop        ; ..  44  45 ;====================================================================  46 ; Subrutina de ejemplo  47 rutina:  48        lda     var  49         cbeqa   #10, fin   ; Termina cuando var llega a 10  50         inc     var  51        bra     rutina  52 fin:   rts                 ; COLOCAR BREAKPOINT AQUÍ (Debugger)  53  54  55 ;********************************************************************  56 ;* Spurious Interrupt Service Routine (Unwanted Interrupt)          *  57 ;********************************************************************  58 spurious:  59        rti  60 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  61 ; Si va a cambiar de ORG, y luego debe continuar con el Código:  57 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 63 endrom1:  64 rom:   SET     endrom1  66 ;********************************************************************  67 ;*                Interrupt Vectors                                 *  68 ;********************************************************************  69  70        ORG     Virq  71         DC.W   spurious    ; IRQ  72         DC.W   spurious    ; SWI  73         DC.W   Main        ; RESET  74  75 ;********************************************************************  76 ; IF you need to continue with the program...:  77  78        ORG     rom         ; rom points to: endrom1. Not needed here  79  80        END    COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03a_L1_M.asm"]: Las  pocas  cosas  nuevas  serían,  una  rutina  DUMMY  (no  hace  nada),  para  efectos  del  ejemplo:    58 spurious:  59        rti    La marca para cambiar de CODE SECTION:    61 ; Si va a cambiar de ORG, y luego debe continuar con el Código:  63 endrom1:  64 rom:   SET     endrom1    La asignación para la tabla de Vectores de Interrupción:    67 ;*                Interrupt Vectors                                 *  70        ORG     Virq  71         DC.W   spurious    ; IRQ  72         DC.W   spurious    ; SWI  73         DC.W   Main        ; RESET    Y,  por  si  necesitara  seguir  escribiendo  el  programa,  aquí  está  la  continuación  del  manejo del CODE SECTION:    76 ; IF you need to continue with the program...:  78        ORG     rom         ; rom points to: endrom1. Not needed here  ... (Continúa...)    10) Introducción a las INTERRUPCIONES Este  es  nuestro  primer  ejemplo,  aunque  muy  simple,  en  el  que  se  toca  el  tema  de  las  Interrupciones,  que  son  una  gran  adición  al  modelo  del  computador,  sin  las  cuales  serían impensables las computadoras modernas, y que a pesar de ello son el PEOR DOLOR  58 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 DE CABEZA de un programador de sistemas Embebidos... Así que HAY QUE ANDARSE CON MUCHO  CUIDADO cuando incluyamos Interrupciones en nuestros códigos. Tiene que hacerse de una  manera MUY DISCIPLINADA. Los Sistemas Operativos así lo hacen.    ["Laboratorios\Lab1\Lab1_M\03b_L1_M.asm"]  001 ;********************************************************************  002 ; Programa 03b_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013 V22N13  003 ;  004 ; Nuevos Aspectos:  005 ;  006 ; ‐ Uso de Rutinas de Interrupción:  007 ;   a) Inclusión del código de la rutina de Interrupción (Interrupt  008 ;      Service Routine, ISR), usando "RTI" para retornar de ésta.  009 ;   b) Definición de los Vectores de Interrupción de acuerdo con la  010 ;      fuente de la interrupción; en este caso: "IRQ". Su vector de  011 ;      interrupciones está en Virq ($FFFA‐$FFFB). Se ha usado la  012 ;      etiqueta "IRQISR" para designar la dirección de la rutina  013 ;   c) Habilitación del periférico para Interrumpir  014 ;   d) Habilitación del CPU (CLI) para que acepte Interrupciones  015 ;      (aparte de la del RESET)  016  017 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  018 ; Include files  019        NOLIST  020          INCLUDE 'derivative.inc'  021        LIST  022  023 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  024 ; Parameter definitions  025 ram:           SET     Z_RAMStart  ; $80. Cfr. 'MC9S08QE128‐U.inc'  026 rom:           SET     ROMStart    ; $2080  027 initStack:     EQU     $1800  028 COP_Disable:   EQU     $42  029  030 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  031 ; Definición de variables (ds: byte por defecto)  032        ORG ram  033  034 var:   DS.B 1               ; Equivalent to DS  035  036 ; ===================================================================  037 ; Begin of Code Section  038        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  039  040        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  041  042    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  043    ; *** ALWAYS *** include the following 4 instructions  044 Main:  lda    #COP_Disable  045         sta    SOPT1       ; System Options 1  046        ldhx   #initStack   ; Init SP  047         txs                ; ..  59 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 048  049        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  050        ; Enable Interrupts for IRQ  051        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  052        BSET    IRQSC_IRQPE,  IRQSC ; Enable IRQ PIN  053        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  054        ; .. Clear any possible previous, false, interrupts by Setting  055        ; .. IRQACK bit in IRQSC (IRQ Status and Control register)  056        BSET    IRQSC_IRQACK, IRQSC  057        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  058        BSET    IRQSC_IRQIE,  IRQSC ; IRQ pin Interrupt Enable  059        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  060  061        cli                 ; CPU Interrupt ENABLE  062  063 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  064 loop:  clr     var         ; Direct   Addressing Mode  065        bsr     rutina      ; Relative Addressing Mode  066        bra     loop  067  068 ;====================================================================  069 ; Subrutina de ejemplo  070 rutina:  071        lda     var  072         cbeqa   #10, fin   ; Termina cuando llega a 10  073         inc     var  074        bra     rutina  075 fin:   rts                 ; COLOCAR BREAKPOINT AQUÍ (Debugger)  076  077  078 ;====================================================================  079 ; EJEMPLO de Rutina de Interrupción (ISR for IRQ signal)  080 ; NOTE: Es necesario realizar un ACKNOWLEDGE para que la interrupción  081 ;       vuelva a suceder (Interrupt Handshaking... different for each  082 ;       peripheral equipment. Need to read Manual for each one... :‐(  083 IRQISR:  084        clr     var  085        BSET    IRQSC_IRQACK, IRQSC ; Acnowledge IRQ Interrupt  086                                    ; .. (rearm IRQ Interrupts)  087        rti  088  089 ;********************************************************************  090 ;*                Interrupt Vectors                                 *  091 ;********************************************************************  092 ; (Esta parte es más o menos estándar,  093 ; ..excepto que normalmente se incluyen TODOS los periféricos...)  094 spurious:  095        rti  097        ORG     Virq        ; Change ORG  098         DC.W   IRQISR      ; <<< IRQ Defined  099         DC.W   spurious    ; SWI  100         DC.W   Main        ; RESET  60 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 101  102        END  103  104 NOTE: If in CW 10.2, IRQ does NOT have any effect if you are  105 debugging in Full Chip Simulation (FCS) and you are using step by  106 step (F5). You need to make a break point in the ISR and then RUN at  107 full speed: F8. Now, IRQ works...  108  109 GURIBE, M29E2013    COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03b_L1_M.asm"]: Cuando  se  trata  del  MCU  hay  varios  pasos  que  se  deben  seguir  al  trabajar  con  periféricos; a lo mejor hay dispositivos a los que no se aplican todos los pasos... Lo  primero es, a veces, definir ciertos valores o escoger, por ejemplo, la fuente de CLOCK  (como en el caso de las comunicaciones, SCI: Serial Communication Interface; periférico  al  que  hay  que  definirle  primero  las  características  del  canal  de  comunicaciones:  velocidad, número de bits, paridad, número de stop bits... ANTES de hacer nada más).    Luego, hay que "ENERGIZAR" el periférico. Muchos dispositivos no están conectados a la  alimentación eléctrica, dentro del MCU, mientras no se los necesite, con el propósito  de  ahorrar  en  consumo  de  energía.  Esto  es  lo  que  yo  denominé  "ACTIVATE"  en  el  tratamiento  del  SCI.  Nótese  que  cuando  operamos  con  periféricos  de  comunicaciones  en  realidad cada uno de ellos puede verse como DOS componentes: el de transmisión y el de  recepción. A lo mejor hay que ACTIVAR cada uno de ellos por separado (Revisar vez por  vez  el  manual).  En  el  caso  del  SCI  es  así,  y  en  nuestros  ejemplos  yo  ACTIVO  ambos  ‐‐transmisor y receptor‐‐ al mismo tiempo, mediante la operación 'XmtRcvActivate'.  Después,  hay  que  habilitar  las  interrupciones  (Peripheral  Interrupt  Enable)  de  los  dispositivos necesarios, que ya han sido apropiadamente inicializados y ACTIVADOS.    Por  último  se  debe  habilitar  al  CPU  para  que  procese  interrupciones.  Esa  es  la  secuencia  genérica  de  actividades  para  habilitar  periféricos  para  que  interrumpan  el  procesamiento lineal de datos del CPU.    Además de todo, hay que establecer la identificación de: QUÉ rutina debe atender CUÁL  interrupción.  El  mecanismo  que  se  emplea  es  mediante  un  link  en  el  Vector  de  Interrupciones,  que  tiene  una  posición  reservada  para  cada  periférico,  o  parte  del  dispositivos que esté en capacidad de interrumpir. Por ejemplo, en el caso del SCI, el  transmisor  tiene  su  propio  vector  de  interrupciones;  también  lo  tiene  el  receptor,  y  asimismo  lo  posee  el  Control  de  Errores  de  Recepción.  En  otros  equipos,  como  los  PC  compatibles IBM/AT, hay un vector adicional para el manejo del Modem, o dispositivo de  comunicación con el canal telefónico convencional.    Finalmente,  hay  que  escribir  por  aparte  la  rutina  ISR  que  atenderá  cada  dispositivo  habilitado  para  hacerlo.  Cada  ISR  tiene  que  responder  de  la  manera  convencional;  por  ejemplo, una rutina de interrupción de Recepción en el SCI, tiene al menos que leer el  dato que está llegando, y que ha ocasionado la interrupción, colocarlo en el área de  memoria dispuesto para eso (normalmente una COLA, o QUEUE), indicarle al periférico que  ya se lo atendió (Interrupt Acknowledge; esta funcionalidad en equipos menos primitivos  la  suministra  el  hardware,  pero  en  la  mayoría  de  los  microcontroladores,  el  software  tiene  que  participar  en  mayor  o  menor  grado  en  el  protocolo  de  Aceptación  de  Interrupciones).  61 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Los conceptos novedosos aquí son: el Enable del IRQ (que NO de sus Interrupts, sino del  PIN de IRQ):    052        BSET    IRQSC_IRQPE,  IRQSC ; Enable IRQ PIN  054        ; .. Clear any possible previous, false, interrupts by Setting  055        ; .. IRQACK bit in IRQSC (IRQ Status and Control register)  056        BSET    IRQSC_IRQACK, IRQSC    Note  que  se  le  está  dando,  de  entrada,  un  Acknowledge  al  periférico,  por  si  alguien  había activado el interruptor ANTES de que lo hubiésemos habilitado para el proceso...    Esta  es  una  de  las  partes  MAS  COMPLICADAS  en  los  sistemas  reales  (no  de  laboratorio).  Por  ejemplo,  cuando  conectamos  una  Estación  Maestra  (un  PC)  a  un  grupo  de  dispositivos  remotos  que  están  operando  (RTUs,  Remote  Terminal  Units),  probablemente,  al  habilitar  la  interfaz  de  comunicaciones  del  PC,  ¡ya  las  RTUs  estén  comunicándose!  Comenzar  una  comunicación  desde  el  PC,  a  la  mitad  de  una  transmisión de la RTU, puede dar lugar a los más espinosos problemas...    Luego viene la habilitación de las INTERRUPCIONES, propiamente dicha:    058        BSET    IRQSC_IRQIE,  IRQSC ; IRQ pin Interrupt Enable    Y, finalmente, la habilitación del CPU para que acepte las interrupciones externas:    061        cli                 ; CPU Interrupt ENABLE    ESTOS  SON  LOS  PASOS,  EN  ESE  ORDEN,  QUE  DEBEN  SEGUIRSE  SIEMPRE  PARA  ACTIVAR  LAS  INTERRUPCIONES DE LOS PERIFÉRICOS QUE NECESITEMOS.    La rutina principal es muy simple en este ejercicio, con el ánimo de no oscurecer el  problema de las interrupciones; consiste en un ciclo que incrementa la variable 'var'  de 0 a 10, y repite...    064 loop:  clr     var         ; Direct   Addressing Mode  065        bsr     rutina      ; Relative Addressing Mode  066        bra     loop  068 ;====================================================================  069 ; Subrutina de ejemplo  070 rutina:  071        lda     var  072         cbeqa   #10, fin   ; Termina cuando llega a 10  073         inc     var  074        bra     rutina  075 fin:   rts                 ; COLOCAR BREAKPOINT AQUÍ (Debugger)    Cuando  se  interrumpe  el  proceso  oprimiendo  el  botón  de  IRQ,  la  IRQISR  (rutina  de  interrupción  del  periférico  IRQ)  en  este  sencillo  ejemplo,  simplemente  coloca  la  variable 'var' a cero:    083 IRQISR:  084        clr     var    62 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Después,  le  indica  al  periférico  IRQ  que  ha  sido  atendido  (protocolo  de  Ack  de  Interrupciones),  de  tal  manera  que  el  dispositivo  periférico  pueda  volver  a  interrumpir,  si  fuera  necesario  (lo  que  se  conoce  como  REARMAR  la  interrupción),  y  retorna al flujo principal del programa (RTI):    085        BSET    IRQSC_IRQACK, IRQSC ; Acnowledge IRQ Interrupt  086                                    ; .. (rearm IRQ Interrupts)  087        rti    Por  último  se  culmina  con  la  parte  estándar  de  las  definiciones  de  los  vectores  de  interrupción, ya vista varias veces con anterioridad.    11) Ejercicio con más Aspectos Nuevos: ‐ Definición de constantes en la ROM (Flash, memoria de programa)  ‐ Direccionamiento Indexado con Post‐incremento (X+) en KBI1 para    leer consecutivamente de 'Table'.    ["Laboratorios\Lab1\Lab1_M\03c_L1_M.asm"]  001 ;********************************************************************  002 ; Programa 03c_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  003 ;  004 ; Nuevos Aspectos:  005 ;  006 ; ‐ Definición de constantes en la ROM (Flash, memoria de programa):  007 ;   Una tabla de valores consecutivos en una zona de la memoria de  008 ;   programa DIFERENTE a la del código (instrucciones). Para esto se  009 ;   ha utilizado DC (define Constant), Bytes por default. Los valores  010 ;   corresponden al código ASCII de los caracteres 'Hola', seguidos  011 ;   de un "nul byte" (0)  012 ;  013 ; ‐ Direccionamiento Indexado con Post‐incremento (X+) en KBI1 para  014 ;   leer consecutivamente de 'Table'.  015 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  016 ; Include files  017        NOLIST  018          INCLUDE 'derivative.inc'  019        LIST  020  021 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  022 ; Parameter definitions  023 ram:           SET     Z_RAMStart  ; $80. Cfr. 'MC9S08QE128‐U.inc'  024 rom:           SET     ROMStart    ; $2080  025 initStack:     EQU     $1800  026 COP_Disable    EQU     $42  027 ROMTable:      EQU     $3000   ; Posición arbitraria, encima del  028                                ; ..programa; debajo del area ocupada  029                                ; ..(VER Mapa de Memoria)  030  031 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  032 ; Definición de variables (ds: byte por defecto)  033        ORG ram  63 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 034 TPtr:      DS.W    1  035 out:       DS      1           ; 'DS' y 'DS.B' son sinónimos  036 var:       DS.B    1           ; Verifique comparando Project.abs.s19  037  038 ; ===================================================================  039 ; Begin of Code Section  040        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  041  042        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  043    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  044    ; *** ALWAYS *** include the following 4 instructions  045 Main:  lda    #COP_Disable  046         sta    SOPT1       ; System Options 1  047        ldhx   #initStack   ; Init SP  048         txs                ; ..  049  050        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  051        ; Enable Interrupts for IRQ  052        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  053        BSET    IRQSC_IRQPE,  IRQSC ; Enable IRQ PIN  054        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  055        ; .. Clear any possible previous, false, interrupts by Setting  056        ; .. IRQACK bit in IRQSC (IRQ Status and Control register)  057        BSET    IRQSC_IRQACK, IRQSC  058        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  059        BSET    IRQSC_IRQIE,  IRQSC ; IRQ pin Interrupt Enable  060        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  061        cli                     ; CPU Interrupt ENABLE  062  063 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  064 loop:  clr     var             ; Direct   Addressing Mode  065        bsr     rutina          ; Relative Addressing Mode  066        bra     loop  067  068 ;====================================================================  069 ; Subrutina de ejemplo  070 rutina:  071        lda     var  072         cbeqa   #10, fin       ; Termina cuando llega a 10  073         inc     var  074        bra     rutina  075 fin:   rts                 ; COLOCAR BREAKPOINT AQUÍ (Debugger)  076  077 ;====================================================================  078 ; Transfer ALL table complete to 'out', with each IRQ activation.  079 ; NOTE: En una rutina de interrupciones **JAMÁS** use un registro sin  080 ;       ..SALVAR previamente su valor en el stack. Al finalizar, debe  081 ;       ..RESTAURAR su valor desde el stack.  082 IRQISR:  083        pshh                    ;Save H reg; Hardware does NOT do this  084        ldhx   #Table           ; Index reg. points to 'Table' start.  085                                ; (Cuánto vale 'Table'? Y '#Table'?)  64 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 086 loop2: mov     X+, out  087         beq    done            ; Activate BREAKPOINT here to see...  088        bra     loop2  089  090 done:  pulh  091        BSET    IRQSC_IRQACK, IRQSC ; Acnowledge IRQ Interrupt  092                                    ; .. (rearm IRQ Interrupts)  093        rti  094  095 ;====================================================================  096 ; Table  097        ORG     ROMTable  098 Table: DC.B    'H'  099        DC.B    'O'  100        DC.B    'L'  101        DC.B    'A'  102        DC.B    0  104 ;********************************************************************  105 ;*                Interrupt Vectors                                 *  106 ;********************************************************************  107 ; (Esta parte es más o menos estándar,  108 ; ..excepto que normalmente se incluyen TODOS los periféricos...)  109 spurious:  110        rti  112        ORG     Virq        ; Change ORG  113         DC.W   IRQISR      ; <<< IRQ Defined  114         DC.W   spurious    ; SWI  115         DC.W   Main        ; RESET  116  117        END  118  119 NOTE: If in CW 10.2, IRQ does NOT have any effect if you are  120 debugging in Full Chip Simulation (FCS) and you are using step by  121 step (F5). You need to make a break point in the ISR and then RUN at  122 full speed: F8. Now, IRQ works...  123  124 GURIBE, M29E2013    COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03c_L1_M.asm"]: El programa comienza de la manera ordinaria. Defina la ROMTable arbitrariamente en la  posición $3000, que queda bien por encima del área de Flash utilizada por el programa:    027 ROMTable:      EQU     $3000   ; Posición arbitraria    Define un APUNTADOR a la tabla que, por tanto, tiene que tener 16 bits (DS.W: Define  Storage WORD):    032 ; Definición de variables (ds: byte por defecto)  033        ORG ram  034 TPtr:      DS.W    1    65 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Hace luego la habilitación que ya hemos realizado para el IRQ, y el CLI para habilitar  las  interrupciones  del  CPU.  La  rutina  de  ejemplo  es  el  mismo  código  simple  que  empleamos en el ejercicio anterior.    La parte novedosa consiste en transferir TODA la tabla (que en el ejemplo contiene solo  las letras HOLA y un '\0' que estamos usando como terminador de strings, igual que en  C), de un solo golpe, cada vez que se interrumpa el CPU mediante el botón de IRQ.    La advertencia más importante es:    079 ; NOTE: En una rutina de interrupciones **JAMÁS** use un registro sin  080 ;       ..SALVAR previamente su valor en el stack. Al finalizar, debe  081 ;       ..RESTAURAR su valor desde el stack.    Analicemos la IRQISR:    082 IRQISR:  083        pshh                    ;Save H reg; Hardware does NOT do this  084        ldhx   #Table           ; Index reg. points to 'Table' start.  085                                ; (Cuánto vale 'Table'? Y '#Table'?)    Lo  primero  es  decir  que  el  protocolo  de  atención  de  interrupciones  de  este  CPU,  almacena los cinco registros, con excepción de la parte superior del H:X (o sea, el H).  Así que es INDISPENSABLE SALVAR ESE REGISTRO POR PROGRAMA; de ahí el PSHH (Push H)    Luego se coloca la dirección de la tabla en el registro índice H:X  A continuación viene el ciclo que mueve toda la tabla, letra por letra, a la variable  'out' (que, como veremos luego, si no fuera una variable sino el registro de salida del  periférico de comunicaciones, ¡enviaría el mensaje al PC!)    086 loop2: mov     X+, out  087         beq    done            ; Activate BREAKPOINT here to see...  088        bra     loop2    Para finalizar, HAY QUE RESTAURAR POR PROGRAMA LA PARTE ALTA DEL REGISTRO ÍNDICE H:X (  o  sea,  H,  que  resguardó  en  Stack  al  comienzo).  Luego  se  culmina  con  el  protocolo  ya  conocido  de  Interrupt  Acknowledge  para  este  periférico,  y  la  rutina  retorna  de  la  interrupción. Si se vuelve a oprimir el botón, vuelve a trasmitirse la tabla:    090 done:  pulh  091        BSET    IRQSC_IRQACK, IRQSC ; Acnowledge IRQ Interrupt  092                                    ; .. (rearm IRQ Interrupts)  093        rti    Definición de la tabla:    096 ; Table  097        ORG     ROMTable  098 Table: DC.B    'H'  099        DC.B    'O'  100        DC.B    'L'  101        DC.B    'A'  66 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 102        DC.B    0    ...que también puede escribirse como:  Table: DC.B    "HOLA", 0    La parte final, de los vectores de interrupción, es la misma que ya hemos visto.      12) Ejercicio con más Aspectos Nuevos aún:   Este programa es similar al anterior, pero no envía la tabla completa cada vez que se  oprime el IRQ, sino que mueve una letra a la vez, por cada interrupción de IRQ.    ["Laboratorios\Lab1\Lab1_M\03d_L1_M.asm"]  001 ;********************************************************************  002 ; Programa 03d_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  003 ;  004 ; Nuevos Aspectos:  005 ;  006 ; ‐ Uso de una variable 'pointer' (TPtr) para transferir la tabla  008 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  009 ; Include files  010        NOLIST  011          INCLUDE 'derivative.inc'  012        LIST  014 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  015 ; Parameter definitions  016 ram:           SET     Z_RAMStart  ; $80. Cfr. 'MC9S08QE128‐U.inc'  017 rom:           SET     ROMStart    ; $2080  018 initStack:     EQU     $1800  019 COP_Disable    EQU     $42  020 ROMTable:      EQU     $3000   ; Posición arbitraria, encima del  021                                ; ..programa; debajo del area ocupada  022                                ; ..(VER Mapa de Memoria)  023  024 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  025 ; Definición de variables (ds: byte por defecto)  026        ORG ram  027 TPtr:      DS.W    1  028 out:       DS      1           ; 'DS' y 'DS.B' son sinónimos  029 var:       DS.B    1           ; Verifique comparando Project.abs.s19  030  031 ; ===================================================================  032 ; Begin of Code Section  033        ABSENTRY Main           ; Export symbol (DEBUGGER entry)  034  035        ORG     rom             ; $2080: HCS08 ROMStart (Flash)  036    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  037    ; *** ALWAYS *** include the following 4 instructions  038 Main:  lda    #COP_Disable  039         sta    SOPT1           ; System Options 1  67 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 040        ldhx   #initStack       ; Init SP  041         txs                    ; ..  042        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  043  044        ldhx   #Table           ; Init pointer to Table  045        sthx    TPtr            ; ..  046  047        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  048        ; Enable Interrupts for IRQ  049        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  050        BSET    IRQSC_IRQPE,  IRQSC ; Enable IRQ PIN  051        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  052        ; .. Clear any possible previous, false, interrupts by Setting  053        ; .. IRQACK bit in IRQSC (IRQ Status and Control register)  054        BSET    IRQSC_IRQACK, IRQSC  055        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  056        BSET    IRQSC_IRQIE,  IRQSC ; IRQ pin Interrupt Enable  057        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  058        cli                     ; CPU Interrupt ENABLE  059  060 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  061 loop:  clr     var             ; Direct   Addressing Mode  062        bsr     rutina          ; Relative Addressing Mode  063        bra     loop  064    065 ;====================================================================  066 ; Subrutina de ejemplo  067 rutina:  068        lda     var  069         cbeqa   #10, fin       ; Termina cuando llega a 10  070         inc     var  071        bra     rutina  072 fin:   rts  073  074 ;====================================================================  075 ; Transfer ALL table to 'out', CHAR BY CHAR with each IRQ activation.  076 ; NOTE: En una rutina de interrupciones **JAMÁS** use un registro sin  077 ;       ..SALVAR previamente su valor en el stack. Al finalizar, debe  078 ;       ..RESTAURAR su valor desde el stack.  079 IRQISR:  080        pshh                    ; Salvar H (el Hardware no lo hace !)  081        ldhx    TPtr            ; Cargar registro índice con Pointer  082        mov     X+, out         ; Mueve letra; apunta a la siguiente  083        beq     resetPtr  084        sthx    TPtr            ; ..Actualiza el pointer en memoria  085        bra     done  086  087 resetPtr:  088        ldhx   #Table           ; Re‐inicializa el pointer de Table  089        sthx    TPtr            ; ..  090 done:  pulh  68 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 091        BSET    IRQSC_IRQACK, IRQSC ; Acnowledge IRQ Interrupt  092                                    ; .. (rearm IRQ Interrupts)  093        rti                     ; Activate BREAKPOINT here to see...  094  095 ;====================================================================  096 ; Table  097        ORG     ROMTable  098 Table: DC      "HOLA", 0       ; QUÉ ES EL '0' QUE APARECE AL FINAL?  099                                ; (DC es lo mismo que DC.B)  100 ;********************************************************************  101 ; Interrupt Vectors  102 ;   ALWAYS INCLUDE VERBATIM COPY, EXCEPTO FOR VECTORS YOU USE...  103 dummy_isr:                     ; This must be placed in ROM Space  104        rti  105  106       org    Vtpm3ovf          ; Increasing priority from bottom up  107       DC.W        dummy_isr    ;Vtpm3ovf:  108       DC.W        dummy_isr    ;Vtpm3ch5:  109       DC.W        dummy_isr    ;Vtpm3ch4:  110       DC.W        dummy_isr    ;Vtpm3ch3:  111       DC.W        dummy_isr    ;Vtpm3ch2:  112       DC.W        dummy_isr    ;Vtpm3ch1:  113       DC.W        dummy_isr    ;Vtpm3ch0:  114       DC.W        dummy_isr    ;Vrtc:  115       DC.W        dummy_isr    ;Vsci2tx:  116       DC.W        dummy_isr    ;Vsci2rx:  117       DC.W        dummy_isr    ;Vsci2err:  118       DC.W        dummy_isr    ;Vacmpx:  119       DC.W        dummy_isr    ;Vadc:  120       DC.W        dummy_isr    ;Vkeyboard:  121       DC.W        dummy_isr    ;Viicx:  122       DC.W        dummy_isr    ;Vsci1tx:  123       DC.W        dummy_isr    ;Vsci1rx:  124       DC.W        dummy_isr    ;Vsci1err:  125       DC.W        dummy_isr    ;Vspi1:  126       DC.W        dummy_isr    ;Vspi2:  127       DC.W        dummy_isr    ;Vtpm2ovf:  128       DC.W        dummy_isr    ;Vtpm2ch2:  129       DC.W        dummy_isr    ;Vtpm2ch1:  130       DC.W        dummy_isr    ;Vtpm2ch0:  131       DC.W        dummy_isr    ;Vtpm1ovf:  132       DC.W        dummy_isr    ;Vtpm1ch2:  133       DC.W        dummy_isr    ;Vtpm1ch1:  134       DC.W        dummy_isr    ;Vtpm1ch0:  135       DC.W        dummy_isr    ;Vlvd:  136       DC.W   IRQISR            ;Virq:  137       DC.W        dummy_isr    ;Vswi:  138       DC.W   Main              ;Vreset:  139  140       END      69 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\03d_L1_M.asm"]: Usa  una  variable  'pointer'  (TPtr)  para  transferir  la  tabla,  letra  por  letra.  En  el  ejercicio 11) también se usó la variable, pero se transfería TODA la tabla por cada IRQ  interrupt.    La inicialización de TPtr se hace ahora en el programa principal (Main):    044        ldhx   #Table           ; Init pointer to Table  045        sthx    TPtr            ; ..    La habilitación de las interrupciones para el IRQ queda igual. La 'rutina' y el 'loop'  siguen siendo los mismos que antes.    La IRQISR cambia un poco, para transferir letra por letra, no tabla por tabla:    079 IRQISR:  080        pshh                    ; Salvar H (el Hardware no lo hace !)  081        ldhx    TPtr            ; Cargar registro índice con Pointer    El  Pointer  TPtr  fue  inicializado  en  Main.  Ahora,  usando  el  modo  de  direccionamiento  indexado con autoincremento:    se mueve una letra  082        mov     X+, out         ; Mueve letra; apunta a la siguiente    se  aprovecha  la  propiedad  de  Conjunto  de  Instrucciones  Enriquecido,  que  fundamentalmente hace que muchas instrucciones realicen más funciones de las que indica  su nombre. Fundamentalmente, la transferencia de información, como en la línea  082:  mov  X+,  out,  aprovecha  para  realizar  una  comparación  TÁCITA  con  el  operando  movido, lo que permite tomar una decisión si lo que acaba de transferirse es un '\0',  que es el símbolo que estamos usando para marcar el final del string, como en C:    083        beq     resetPtr    Si no se ha terminado, se guarda el nuevo valor de Pointer, que ha sido incrementado  para apuntar a la próxima letra que debe transferirse:    084        sthx    TPtr            ; ..Actualiza el pointer en memoria    y se termina la interrupción    085        bra     done    Si  la  instrucción  083  beq  resetPtr,  determina  que  ya  se  transfirió  el  NULL  Byte,  se  reinicializa  el  pointer  para  comenzar  de  nuevo  la  transferencia  de  la  tabla,  cuando  haya más interrupciones:    087 resetPtr:  088        ldhx   #Table           ; Re‐inicializa el pointer de Table  089        sthx    TPtr            ; ..    70 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La parte final es la misma háyase terminado la transmisión o no. Se restaura el valor  del registro H, no salvado por el mecanismo de interrupciones y se ejecuta el protocolo  de Ack para la interrupción y la rutina retorna al programa principal:    090 done:  pulh  091        BSET    IRQSC_IRQACK, IRQSC ; Acnowledge IRQ Interrupt  092                                    ; .. (rearm IRQ Interrupts)  093        rti                     ; Activate BREAKPOINT here to see...    La tabla tiene la misma estructura de antes:  097        ORG     ROMTable  098 Table: DC      "HOLA", 0       ; QUÉ ES EL '0' QUE APARECE AL FINAL?  099                                ; (DC es lo mismo que DC.B)    Como ejemplo, se incluyen TODOS los vectores de interrupción, con los nombres definidos  por el manufacturante. No la repito aquí para conservar el espacio.    13) Aspectos Cosméticos en el trato de los Vectores de Interrupción:   La tabla con todos los valores de los vectores de interrupción, que se incluyó en el  ejercicio  anterior,  se  acomoda  dentro  de  un  INCLUDE  FILE  que  se  invoca  al  final  del  problema. El ejercicio es muy simple y no desarrolla ningún código especial.    ["Laboratorios\Lab1\Lab1_M\04b_L1_M.asm"]  01 ;********************************************************************  02 ; Programa 04b_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  03 ;  04 ; Nuevos Aspectos:  06 ; ‐ Se emplea un Include File: 'InitU_M.inc' (ALL Interrupt Vectors)  07  08 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  09 ; Include files  10        NOLIST  11          INCLUDE 'derivative.inc'  12        LIST  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; Parameter definitions  15  16 ram:         SET   Z_RAMStart  ; $80  17 rom:         SET   ROMStart    ; $2080  18 initStack:   EQU   $1800  19 COP_Disable: EQU   $42  20 ROMTable:    EQU   $3000       ; Posición arbitraria, encima del  21                                ; ..programa; debajo del area ocupada  22                                ; ..(VER Mapa de Memoria)  23 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  24 ; Definición de variables (ds: byte por defecto)  25  26        ORG ram  27  28 TPtr:      DS.W    1  71 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 29 out:       DS.B    1  30 var:       DS.B    1  31  32 ; ===================================================================  33 ; Begin of Code Section  34        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  35  36        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  37  38    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  39    ; *** ALWAYS *** include the following 4 instructions  40 Main:  lda    #COP_Disable  41         sta    SOPT1       ; System Options 1  42        ldhx   #initStack   ; Init SP  43         txs                ; ..  44  45 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  46 ; Branch for ever  47  48        bra     *  49  50 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  51 ; InitU  52        NOLIST  53          INCLUDE 'InitU_M.inc' ; Interrupt Vectors; Restart in 'Main'  54        LIST  55  56        END      14) Ejemplo: Rutina de MULTIPLICAR, por Sumas Sucesivas:   El  HCS08  TIENE  una  instrucción  de  multiplicar...,  así  que  no  se  precisaría  hacer  la  operación mediante sumas. Es solo un ejemplo:    ["Laboratorios\Lab1\Lab1_M\05a_L1_M.asm"]  01 ;********************************************************************  02 ; Programa 05a_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  03 ; Luis G. Uribe C., D12A2009  04 ;  05 ; EJEMPLO: Rutina de MULTIPLICAR, por sumas sucesivas  07 ; NOTE: El HCS08 TIENE una instrucción de multiplicar...  08 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  09 ; Include files  10        NOLIST  11          INCLUDE 'derivative.inc'  12        LIST  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; Parameter definitions  15 ram:           SET     Z_RAMStart  ; $80  16 rom:           SET     ROMStart    ; $2080  72 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 17 initStack:     EQU     $1800  18 COP_Disable:   EQU     $42  19 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  20 ; Definición de variables (DS: Define Storage BYTE, por defecto)  21        ORG ram  22 m_ando:    DS.B      1           ; Multiplicando  23 m_ador:    DS        1           ; Multiplicador  24 prod:      DS.B      1           ; Producto (RESULTADO)  25 cnt:       DS        1           ; Contador  26 ; ===================================================================  27 ; Begin of Code Section  28        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  29        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  31    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32    ; *** ALWAYS *** include the following 4 instructions  33 Main:  lda    #COP_Disable  34         sta    SOPT1       ; System Options 1  35        ldhx   #initStack   ; Init SP  36         txs                ; ..  37 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  38 ; Inicialización de parámetros  39        mov    #5, m_ando       ; Main: Etiqueta del programa ppal.  40        mov    #3, m_ador       ; Pasar parámetros a la rutina  41 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  42 ; Rutina de Multiplicación por sumas sucesivas  43        clr     prod  44        clr     cnt  45 mply:  lda     m_ador  46        cbeq    cnt, fin  47        lda     prod  48        add     m_ando  49        sta     prod  50        inc     cnt  51        bra     mply  52  53 fin:   bra *                   ; Simula un HALT  54        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  55        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  56                            ; This 'nop' MAY be removed for CW 6.3 ...  57  58 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  59 ; InitU  60 ;       NOLIST  61          INCLUDE 'InitU_M.inc' ; Interrupt Vectors; Restart in 'Main'  62 ;       LIST  63  64        END    COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\05a_L1_M.asm"]: Las partes nuevas comienzan con la Inicialización de parámetros. Se multiplicarán, como  ejemplo, 5 * 3 (constantes):  73 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 39        mov    #5, m_ando       ; Main: Etiqueta del programa ppal.  40        mov    #3, m_ador       ; Pasar parámetros a la rutina    La Rutina (que aún NO Subrutina) de Multiplicación por sumas sucesivas es como sigue:    43        clr     prod  44        clr     cnt  45 mply:  lda     m_ador  46        cbeq    cnt, fin  47        lda     prod  48        add     m_ando  49        sta     prod  50        inc     cnt  51        bra     mply  53 fin:   bra *                   ; Simula un HALT  54        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  55        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  56                            ; This 'nop' MAY be removed for CW 6.3 ...    El  ejercicio  termina  con  en  la  línea  53  fin:  bra  *,  que  simula  un  HALT,  para  permitirnos mirar el resultado con el Debugger.    15) "SUBRUTINA" de MULTIPLICAR, por sumas sucesivas:   Es  un  ejemplo  muy  parecido  al  anterior,  pero  el  código  se  ha  colocado  dentro  de  una  SUBRUTINA  que  el  programa  principal  llama.  Esta  es  la  forma  más  corriente  de  codificación.    ["Laboratorios\Lab1\Lab1_M\05b_L1_M.asm"]  01 ;********************************************************************  02 ; Programa 05b_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  03  04 ; EJEMPLO: Rutina de MULTIPLICAR, por sumas sucesivas  06 ; Nuevos Aspectos:  07 ;  08 ; ‐ Incluye una Subrutina  09 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10 ; Include files  11        NOLIST  12          INCLUDE 'derivative.inc'  13        LIST  14 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  15 ; Parameter definitions  16 ram:           SET     Z_RAMStart  ; $80  17 rom:           SET     ROMStart    ; $2080  18 initStack:     EQU     $1800  19 COP_Disable:   EQU     $42  20 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  21 ; Definición de variables (DS: Define Storage BYTE, por defecto)  22        ORG ram  24 m_ando:    DS.B      1           ; Multiplicando  74 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 25 m_ador:    DS        1           ; Multiplicador  26 prod:      DS.B      1           ; Producto (RESULTADO)  27 ; ===================================================================  28 ; Begin of Code Section  29        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  30        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  31    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32    ; *** ALWAYS *** include the following 4 instructions  33 Main:  lda    #COP_Disable  34         sta    SOPT1       ; System Options 1  35        ldhx   #initStack   ; Init SP  36         txs                ; ..  37 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  38 ; Inicialización de parámetros  39        mov     #5, m_ando      ; Main: Etiqueta del programa ppal.  40        mov     #3, m_ador      ; Pasar parámetros a la rutina  41  42        bsr     mply            ; Branch to Subroutine mply  43 final: bra     final           ; Halt  44  45 ; ===================================================================  46 ; Rutina de Multiplicación por sumas sucesivas  47 mply:  clr     prod  48        lda     m_ador          ; RICH Instruction Set: Load and make  49                                ; ..tacit & automatic COMPARE with '0'  50         beq    fin  51    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  52    ; Loop  53    mply2:  lda     prod  54            add     m_ando  55            sta     prod  56            dec     m_ador  57            beq     fin  58            bra     mply2  59 fin:   rts  60        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  61        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  62                            ; This 'nop' MAY be removed for CW 6.3 ...  63  64 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  65 ; InitU  66        NOLIST  67          INCLUDE 'InitU_M.inc' ; Interrupt Vectors; Restart in 'Main'  68        LIST  70        END    COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\05b_L1_M.asm"]: Las novedades comienzan aquí:    42        bsr     mply            ; Branch to Subroutine mply  43 final: bra     final           ; Halt  75 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se trasladó el código del programa principal a la SUBRUTINA MPLY:    47 mply:  clr     prod  48        lda     m_ador          ; RICH Instruction Set: Load and make  49                                ; ..tacit & automatic COMPARE with '0'  50         beq    fin  52    ; Loop  53    mply2:  lda     prod  54            add     m_ando  55            sta     prod  56            dec     m_ador  57            beq     fin  58            bra     mply2  59 fin:   rts    16) Ejemplo PRIMORDIAL del uso de Variables Dinámicas: Las  variables  dinámicas,  en  C,  son  aquellas  que  no  existen  antes  de  llamarse  la  subrutina en donde se van a utilizar; se generan como parte del mecanismo de llamada a  la  función (Stack  FRAME),  duran  hasta  que  la  subrutina termine,  y desaparecen  con  la  finalización de la función, liberando el espacio utilizado.    Este mecanismo tiene dos aspectos importantes:    Primero,  emplea  de  una  manera  muy  eficiente el  espacio,  según  acabo  de  explicar.  Las  variables  estáticas  o  globales,  conservan  el  espacio  desde  su  asignación  hasta  la  terminación del programa. Nunca lo liberan hasta entonces.  Segundo,  permiten  la  codificación  de  FUNCIONES  RECURSIVAS,  al  estilo  de  FACTORIAL,  o  las TORRES DE HANOI y similares.    Es MUY importante entender y poder aplicar las técnicas que aquí expongo.    El  ejemplo  que  usaremos  como  vehículo  para  aplicar  y  demostrar  la  forma  de  definir  variables  dinámicas  será  otra  vez  el  Ejemplo  de  EXPONENCIACIÓN,  por  multiplicaciones  sucesivas que a su vez se hacen por sumas sucesivas... Pero TODAS las variables estarán  en el Stack.    Es importante, para su comprensión, que se haga paso a paso un dibujo del Stack y el  valor  del  SP,  a  medida  que  se  ejecuta  el  programa.  Puede  ayudarse  con  el  Debugger,  siguiendo el programa paso a paso y monitoreando el SP y el área asignada al Stack.    ["Laboratorios\Lab1\Lab1_M\05d_L1_M.asm"]  001 ;********************************************************************  002 ; Programa 05d_L1_M.asm, HCS08_CPU, Luis G. Uribe C., M29E2013  003  004 ; EJEMPLO: EXPONENCIACIÓN, por multiplicaciones sucesivas;  005 ; ..que a su vez se hacen por sumas sucesivas...  006 ;  007 ; Nuevos Aspectos:  008 ;  009 ; ‐ **USA SOLO EL STACK; NO VARIABLES LOCALES**  010 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  76 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 011 ; Include files  012        NOLIST  013          INCLUDE 'derivative.inc'  014        LIST  016 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  017 ; Parameter definitions  018 mantisa:     EQU 2               ; Resultado: 2^5=32 (0x20 en el ACC)  019 exponente:   EQU 5               ; Cambie aquí si se desa más valores  020 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  021 ram:         SET     Z_RAMStart  ; $80  022 rom:         SET     ROMStart    ; $2080  023 initStack:   EQU     $1800  024 COP_Disable: EQU     $42  025  026 STD:         EQU 4               ;Posición STandarD de la 1a variable  027  028 ; ===================================================================  029 ; DEFINICION DE VARIABLES LOCALES:  031 ; En lugar de las variables (globales), se especifica el "Offset"  032 ; ..que cada una tendrá en el Stack (Frame) al momento de llamar a  033 ; ..una subrutina. De esta manera, tal como hace el IJVM, cada  034 ; ..subrutina o función puede direccionar "simbólicamente" sus  035 ; ..variables locales, y de idéntica forma, sus parámetros. Se emplea  036 ; ..para ello, de manera extensiva, el direccionamiento indexado.  037 ;  038 ; Observe que este direccionamiento puede hacerse en relación al SP,  039 ; ..pero resulta preferible en función del H:X, ya que el SP va  040 ; ..moviéndose cada vez que se hace un PUSH, un llamado de subrutina,  041 ; ..y esto complica el seguimiento de la posición de cada variable.  042 ;  043 ; Los números, tales como: "result EQU STD", indican a cuántos bytes  044 ; ..se encuentra la variable equivalente. Así, "result" está situada  045 ; ..4 bytes por encima de X, al llamar la subrutina 'exp'  046 ;  047 ; Háganse un gráfico que señale la posición de c/variable en el stack  048 ; ..Eso ayuda bastante a la comprensión de este código  049 ;  050 ; ==========================  051 ; subrutina 'exp' (exponenciar)  052 ;  053 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  054 ; (1) Variables LOCALES (En el STACK):  055  056 result:    EQU STD         ; Única variable "local" de 'exp'  057 ; Otras variables se definirían aquí. Por ejemplo:  058 ; var2:    EQU result+1    ; 1 es el tamaño en bytes de 'result'  059 ; var3:    EQU var2+1      ; 1 es el tamaño en bytes de 'var2', etc.  060  061 EXP_Vars:  EQU result‐STD+1  ; Cantidad de variables locales en 'exp'  062                            ; La expresión es: PRIMERA_variable‐STD+1  063 ; (*) NOTA:  064 ; Si fuera 'result' la que midiera 2 bytes, var2 sería:  77 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 065 ; var2:   EQU result+2     ; etc.  066  067 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  068 ; (2) PARÁMETROS (EN EL STACK, en órden INVERSO a como se apilan.  069 ; ..Si se hace *Push BASE* y luego *Push EXP*, el orden es el que se  070 ; ..indica a continuación; primero EXP y luego BASE):  071  072 exp:       EQU result+1    ; segundo parámetro  073 base:      EQU exp+1       ; PRIMER parámetro. Aplica (*) NOTA arriba  074  075 EXP_Stack: EQU base‐STD+1  ; uso del stack (vars + param)  076                            ; La expresión es ÚLTIMO_parámetro‐STD+1  077  078 ; ==========================  079 ; subrutina 'mply'  080 ; (1) VARIABLES LOCALES (En el STACK):  081  082 prod:     EQU STD          ; Única variable "local" de 'mply'  083  084 MUL_Vars: EQU prod‐STD+1   ; Cantidad de variables locales en 'mply'  085  086 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  087 ; (2) PARÁMETROS (EN EL STACK):  088  089 m_ador:   EQU prod+1       ; segundo parámetro  090 m_ando:   EQU m_ador+1     ; primer  parámetro  091  092 MUL_Stack: EQU m_ando‐STD+1 ; uso del stack (vars + param)  093  094 ; ===================================================================  095 ; Begin of Code Section  096        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  097  098        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  099  100    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  101    ; *** ALWAYS *** include the following 4 instructions  102 Main:  lda    #COP_Disable  103         sta    SOPT1       ; System Options 1  104        ldhx   #initStack   ; Init SP  105         txs                ; ..  106  107 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  108 ; InitU  109 ;       NOLIST  110          INCLUDE 'InitU_M.inc' ; Interrupt Vectors; Restart in 'Main'  111 ;       LIST  112  113        ; ============================================================  114        ; Pasa parámetros a la rutina "expo"  115        lda     #mantisa    ; base  116         psha  78 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 118        lda    #exponente   ; exp (2^5 = 32: 0x20)  119         psha  120  121        ais    #‐EXP_Vars   ; reserva VARIABLES locales en "expo"  122         bsr    expo        ; JSR expo.           Resulado en ACC  123         ais   #EXP_Stack   ; recupera stack  *** Resulado en ACC ***  124  125        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  126        bra     *           ; Simula un HALT. 2^5 = 32: $20 (see ACC)  127  128 ; ===================================================================  129 ; Rutina de Exponenciación por multiplicaciones sucesivas  130 expo:  pshx                ; guarda stack frame anterior  131         pshh  132        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  133        tsx                 ; Mark Variable Frame in stack with H:X  134  135        lda    #1  136         sta    result, x  137  138        lda     exp, x  139         beq    ex_fin  140    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  141    ; Pasar parámetros a la rutina "mply"  142    expo2:  lda     result, x   ; pasa parámetros a la rutina "mply":  143             psha               ;.. m_ando  144  145            lda     base, x     ; m_ador  146             psha  147            ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  148            ais    #‐MUL_Vars   ; reserva area para VARIABLES locales  149             bsr    mply        ; *** Resulado en ACC  150             ais   #MUL_Stack   ; recupera stack  151            ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  152            sta     result, x  153            lda     exp, x  154             deca  155             sta    exp, x  156             beq    ex_fin  157            bra     expo2  158 ex_fin:  159        lda     result, x       ; Result in ACC  160        pulh                    ; recupera stack frame anterior  161        pulx  162        rts  163 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  164 ; Rutina de Multiplicación por sumas sucesivas  165 mply:  pshx                    ; guarda stack frame anterior  166        pshh  167        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  168        tsx                     ; Mark Variable Frame in stack with HX  169        clra  79 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 170         sta    prod, x  171        lda     m_ador, x  172        beq     s_fin  173    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  174    ; Loop  175    mply2:  lda     prod, x  176             add    m_ando, x  177             sta    prod, x  178            lda     m_ador, x  179             deca  180             sta    m_ador, x  181             beq    s_fin  182            bra     mply2  183 s_fin: lda     prod, x         ; Result in ACC  184        pulh                    ; recupera stack frame anterior  185        pulx  186        rts  187  188        END    COMENTARIOS a ["Laboratorios\Lab1\Lab1_M\05d_L1_M.asm"]: La  primera  modificación  al  ejercicio  que  ya  hemos  presentado  con  anterioridad,  es  la  definición  de  los  valores  que  van  a  multiplicarse.  Hasta  ahora  se  usaron  como  constantes numéricas: #3 y #5. Aquí se les da una identificación simbólica:    017 ; Parameter definitions  018 mantisa:     EQU 2               ; Resultado: 2^5=32 (0x20 en el ACC)  019 exponente:   EQU 5               ; Cambie aquí si se desa más valores    Y luego viene la diferencia fundamental: DEFINICION DE VARIABLES LOCALES:    En  lugar  de  las  variables  (globales)  con  las  que  se  trabajó  en  los  ejercicios  anteriores, se especifica aquí el "Offset" que cada una tendrá en el Stack (Frame) al  momento  de  llamar  a  una  subrutina.  De  esta  manera,  cada  subrutina  o  función  puede  direccionar  "simbólicamente"  sus  variables  locales,  y  de  idéntica  forma,  sus  parámetros. Se emplea para ello, de manera extensiva, el direccionamiento indexado.    El hecho de que variables y parámetros de una función, puedan manipularse DE LA MISMA  MANERA  (en  este  caso,  ambos  con  NOMBRES),  fue  una  de  las  cosas  importantes  de  los  lenguajes de alto nivel, como el C. Cuando hacemos algo así como:    int example( int a, int b)  {  int tmp;     tmp = a * b; // mirando esta líne no puede diferenciarse entre los                  // ..parámetros a y b y la variable local tmp. Es el  ...             // ..mismo tratamiento para variables y parámetros!  }    Este  direccionamiento de  variables  en  el  Stack  puede  hacerse  en  relación  al SP,  pero  resulta preferible en función del registro índice H:X, ya que el SP va moviéndose cada  vez que se hace un PUSH, o un llamado de subrutina, y esto complica el seguimiento de  80 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 la  posición  de  cada  variable.  En  cambio,  una  vez  marcado  el  STACK  FRAME,  con  el  registro  índice,  el  H:X  queda  estático  y  los  desplazamientos  en  consecuencia  no  se  mueven.    En el ejercicio, los números como el de la línea 056:  056 result:    EQU STD         ; Única variable "local" de 'exp'    indican a cuántos bytes se encuentra la variable equivalente dentro del Stack (o dentro  del STACK FRAME, que es el área marcada por el registro índice H:X). Así, "result" está  situada 4 bytes por encima de H:X, al llamar y ejecutar la subrutina 'exp'. Recuerde  que STD ha sido definido como:    026 STD:         EQU 4               ;Posición STandarD de la 1a variable    Haga  un  gráfico  que  señale  la  posición  de  c/variable  y  cada  parámetro  en  el  stack,  siguiendo  el  orden  de  los  push  de  los  parámetros,  la  reserva  del  área  de  trabajo  en  stack,  las  posiciones  que  se  lleva  guardar  el  PC,  según  el  protocolo  de  llamada  a  subrutinas y, finalmente, el espacio que se requiere para salvar el valor anterior del  registro índice: H:X; eso ayuda bastante a la comprensión de este código.    La definición de variables y parámetros para la primera subrutina, 'exp' (exponenciar)  es como sigue:    051 ; subrutina 'exp' (exponenciar)  054 ; (1) Variables LOCALES (En el STACK):  056 result:    EQU STD         ; Única variable "local" de 'exp'       Si hubiera más variables se definirían aquí así:       058 ; var2:    EQU result+1    ; 1 es tamaño de 'result'     059 ; var3:    EQU var2+1      ; 1 es tamaño de 'var2', etc.       Se ha asumido que var2 y var3 tendrían un tamaño de un byte. Si las variables fueran  WORDS, de 2 bytes, las definiciones serían así:       058 ; var2:    EQU result+1    ; 1 es tamaño de 'result'     059 ; var3:    EQU var2+2      ; 2 es tamaño de 'var2'    Medición del espacio de Stack ocupado por la subrutina 'exp':    061 EXP_Vars: EQU result‐STD+1 ; Cantidad de VARIABLES locales en 'exp'  062                            ; La expresión es: PRIMERA_variable‐STD+1    A continuación se definen los PARÁMETROS (es costumbre ponerlos en el Stack, en orden  INVERSO  a  como  se  apilan.  Así  se  hace  en  C,  de  tal  manera  que  una  expresión  como  printf(  "%d",  i  );  introduce  'i'  en  el  Stack  y  luego  el  FORMAT:  %d.  De  esta  manera,  como  el  número  de  parámetros  de  printf  es  VARIABLE,  se  puede  averiguar  con  cuántos  parámetros se la llamó, inspeccionando el String de FORMAT, que convenientemente es el  que está en el techo del Stack (apuntado directamente por el SP‐1. Recuerde que el SP  en  esta  máquina  se  diseñó  para  que  apuntara  a  la  próxima  posición  VACÍA  dentro  del  Stack, a diferencia de todas las demás máquinas del orbe...)    81 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Recuerde que STD ha sido definido como:    026 STD:         EQU 4               ;Posición STandarD de la 1a variable    Repito por claridad la línea 56:    056 result:    EQU STD         ; Única variable "local" de 'exp'  060  061 EXP_Vars:  EQU result‐STD+1; Cantidad de variables locales en 'exp'  062                            ; La expresión es: PRIMERA_variable‐STD+1    067 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  068 ; (2) PARÁMETROS (EN EL STACK, en órden INVERSO a como se apilan.  069 ; ..Si se hace *Push BASE* y luego *Push EXP*, el orden es el que se  070 ; ..indica a continuación; primero EXP y luego BASE):  072 exp:       EQU result+1    ; segundo parámetro.  073 base:      EQU exp+1       ; PRIMER parámetro. Aplica (*) NOTA arriba  075 EXP_Stack: EQU base‐STD+1  ; uso del stack (vars + param)    El segundo símbolo auxiliar para la liberación de espacio en esta subrutina es:    075 EXP_Stack: EQU base‐STD+1  ; uso del stack (vars + param)    En la expresión de la línea 072:  072 exp: EQU result+1 ;    'result' es la  ÚLTIMA VARIABLE del método, ANTES de los parámetros. (Aquí, además de  ser la última, también es la única...)    Para la subrutina 'mply' se sigue el mismo procedimiento:    079 ; subrutina 'mply'  080 ; (1) VARIABLES LOCALES (En el STACK):  082 prod:     EQU STD          ; Única variable "local" de 'mply'  084 MUL_Vars: EQU prod‐STD+1   ; Cantidad de variables locales en 'mply'    La línea 084 mide el número de variables locales en esta subrutina,    084 MUL_Vars: EQU prod‐STD+1    Luego se definen los parámetros, similarmente a como se hizo en la subrutina anterior:    087 ; (2) PARÁMETROS (EN EL STACK):  089 m_ador:   EQU prod+1       ; segundo parámetro  090 m_ando:   EQU m_ador+1     ; primer  parámetro  092 MUL_Stack: EQU m_ando‐STD+1 ; uso del stack (vars + param)    Este valor de la línea 092 mide el USO del stack para esta subrutina:  092 MUL_Stack: EQU m_ando‐STD+1 ; uso del stack (vars + param)    Note: Los vectores de Interrupción están definidos para arrancar en Main, en el INCLUDE  file: 'InitU_M.inc', en:  82 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 110          INCLUDE 'InitU_M.inc' ; Interrupt Vectors; Restart in 'Main'    Observemos  cómo  se  pasan  los  parámetros  a  'expo',  y  cómo  se  reservan  las  variables  locales:    113        ; ============================================================  114        ; Pasa parámetros a la rutina "expo"  115        lda     #mantisa    ; base  116         psha  117  118        lda    #exponente   ; exp (2^5 = 32: 0x20)  119         psha    A estas alturas, se han acomodado los dos parámetros en el Stack. Ahora se reserva el  espacio para las variables locales, decrementando el SP por la cantidad definida arriba  como la cantidad de bytes de las variables locales:    121        ais    #‐EXP_Vars   ; reserva VARIABLES locales en "expo"    Luego se salta a la subrutina, lo cual aleja nuestra área de almacenamiento, DOS (2)  bytes del SP (porque el salto a subrutina implica guardar el PC de retorno en el Stack,  y el PC mide 2 bytes)    122         bsr    expo        ; JSR expo.           Resulado en ACC    Al retornar, de una sola operación se recupera todo el Stack, tanto el correspondiente  a los parámetros como el ocupado por las variables:    123         ais   #EXP_Stack   ; recupera stack  *** Resulado en ACC ***    Ahora analicemos la primera subrutina, 'expo'. Lo primero que hace es guardar el Stack  Frame  Anterior,  que  se  encuentra marcado  por  el  registro  índice:  H:X.  La  cantidad  de  bytes  que  ocupa  el  registro  índice  guardado  en  el  Stack  (2  bytes)  nos  aleja  justo  2  posiciones más de nuestros datos, PARA UN TOTAL DE CUATRO (4) que es el valor definido  arriba para el símbolo 'STD'.    130 expo:  pshx                ; guarda stack frame anterior  131         pshh    Ahora procede a generar un NUEVO STACK FRAME:    132        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  133        tsx                 ; Mark Variable Frame in stack with H:X    De ahí en adelante la secuencia de operaciones es bastante legible:    135        lda    #1  136         sta    result, x ; definido en 056 result: EQU STD  138        lda     exp, x    ; definido en 072 exp: EQU result+1  139         beq    ex_fin    83 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Recuerden que el modo de direccionamiento indexado con desplazamiento de 8 bits, suma  el registro índice H:X con el desplazamiento para obtener la posición de la variable,  por  encima  de  los  4  bytes  (STD)  que  están  ocupando  el  PC  y  el  antiguo  H:X.  Recuerde  también  que  cada  push  DECREMENTA  el  SP.  Por  eso  para  reservar  el  espacio  de  las  variables, se le sumó a SP un número negativo (repito la línea 121):    121        ais    #‐EXP_Vars   ; reserva VARIABLES locales en "expo"    El comportamiento de la siguiente subrutina, "mply", es tan similar al anterior, que no  voy a comentar más sobre él.       PROGRAMAS AVANZADOS EN ASSEMBLY LANGUAGE, PARTE II   A  continuación  voy  a  presentar  un  ejemplo  clásico  de  la  literatura  computacional,  en  donde  se  muestra  el  poderío  de  la  PROGRAMACIÓN  RECURSIVA  a  la  solución  de  aquellos  problemas  que  pueden  expresarse  de  esa  manera,  como  son  el  cálculo  de  factoriales,  búsqueda de Palíndromos, exploración de árboles de información (como los directorios de  un sistema de archivos), la generación de combinaciones y permutaciones entre elementos  (no el cálculo de cuántas son, sino cuáles son), el cálculo del Máximo Común Divisor,  según el algoritmo de Euclides, por mencionar algunos de los más conocidos.    En primer lugar mostraré el ejemplo en C, para Windows (compilar con Visual Studio 10+)  y  luego  en  Assembly  Language  para  el  HCS08.  Cuando  veamos  todos  estos  programas  reescritos en C para el HC9S08QE128, volveremos a verlo.    El  ejercicio  de  las  Torres  de  Hanoi  supone  3  torres,  en  las  que  se  han  colocado  N  discos,  comenzando  por  el  más  grande  abajo,  y  colocando  encima  discos  cada  vez  más  pequeños.  El  ejercicio  requiere  pasar  todos  los  discos  de  la  torre  1  a  la  torre  3,  empleando la torre 2 como auxiliar. La única regla es que no se puede colocar nunca un  disco grande sobre uno más pequeño.    La  solución  es  muy  simple:  pasar  N‐1  discos  de  la  torre  1  a  la  2;  pasar  EL  disco  restante de la torre 1 a la 3, y finalmente pasar los N‐1 que están en la torre 2, a la  3. LISTO. Desde luego, para pasar N‐1 discos de una torre a otra, se pasan N‐2 a la que  sirva ahora de torre auxiliar, empleando el MISMO procedimiento... de manera recursiva.  Es decir, cuando sólo haya UN disco, habrá que pasarlo desde la fuente hasta el destino  final. En caso contrario, llamar el procedimiento de manera recursiva.    17) Torres de Hanoi, MÍNIMO, en C: A)  Programa HanoiMin‐‐.c    ["Temarios\Ex#1‐HanoiMin\HanoiMin‐‐.c"]  01 #include "hanoimin_.h"     // hanoimin‐‐.c LGUribeC L12D2011 J12D2013  02 void main ( int argc, char ** argv )   /* HANOImin‐‐.C */      /*()*/  03 {  uint N;  04    get( NDisks );  84 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 05    hanoi( N, 1, 3 );           // MOVE N disks from tower 1 to 3.  06 }  /* main() */                // ..That's all!    07 // ==================================================================  08 void hanoi ( uint N, uint a, uint b )                          /*()*/  09 {  10    if( N == 1 )        // GOT ONLY 1 DISK? IT'S EASY! JUST MOVE IT ON  11       PrintW( "Pase el disco de la torre '%d' a la '%d'", a, b );  12    hanoi( N ‐ 1,    a,  TowerTmp ); // SEVERAL DISKS? CALL ITSELF RECURSIVELY:  13    hanoi(   1  ,    a,     b );  14    hanoi( N ‐ 1, TowerTmp, b );  15 }  /* hanoi() */    B)  Include File hanoiMin_.h:    ["Temarios\Ex#1‐HanoiMin\hanoiMin_.h"]  // HANOImin_h.h, Luis G. Uribe C.: Play with paper disks!  // S12N2005 J23M2006 L29G2011  J12D2013  16 #include <assert.h>  17 #include <stdio.h>  18 #include <stdlib.h>    19 enum { DEFAULT = 3, MAX = 10 };  20 typedef unsigned int uint;    21 void hanoi( uint n, uint a, uint b );    22 #define get(NDisks)  {if( argc < 2 ) N = DEFAULT;   \  23                       else if((N = atoi(argv[1])) == 0) N = DEFAULT;\  24                       if( N > MAX ) {                               \  25                          puts("#Discos debe ser <= 10. Asume 3\n"); \  26                          N = DEFAULT; }  }    27 // Printf message, and Wait the user to hit ENTER, to continue  28 #define PrintW(s, a, b)    { printf( s, a, b ); getchar(); return; }    29 // Magic Number! ie: for a=1 & b=3, => TowerTmp=2 ETC!Sleep on it...  30 #define TowerTmp   ( 6 ‐ a ‐ b )    COMENTARIOS a ["Temarios\Ex#1-HanoiMin\HanoiMin--.c"]: Desde luego, me esforcé en presentar un algoritmo MÍNIMO. Difícilmente pueden colocarse  menos instrucciones.    De 'main' podemos olvidarnos: NO forma parte del algoritmo; sirve para leer del usuario  el  número  de  discos,  N,  y  llamar  a  la  respectiva  función  'hanoi()',  que  es  la  que  realmente  materializa el  algoritmo:  Mover  N  discos  desde  la  torre  'a'  hasta  la  torre  'b':    08 void hanoi ( uint N, uint a, uint b )                          /*()*/  10    if( N == 1 )        // GOT ONLY 1 DISK? IT'S EASY! JUST MOVE IT ON  11       PrintW( "Pase el disco de la torre '%d' a la '%d'", a, b );  85 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Como  ya  dijimos,  si  en  un  momento  dado  hay  que  mover  UN  disco  (N==1),  se  lo  pasa  simplemente de la torre FUENTE (SRC) al la torre DESTINO (DST). Esa parte es fácil: 'a'  y 'b' SON las torres SRC y DST.    Ahora,  si  hay  más  de  un  disco  para  mover  entre  la  torre  'a'  y  la  torre  'b',  pues  también es muy sencillo, como se expresa a continuación:    12    hanoi( N ‐ 1,    a,  TowerTmp ); // SEVERAL DISKS? CALL ITSELF RECURSIVELY:  13    hanoi(   1  ,    a,     b );  14    hanoi( N ‐ 1, TowerTmp, b );    ...mover  N  ‐  1,  de  la  torre  'a'  a  la  'TowerTmp',  mover  el  disco  que  queda  sólo  (1  disco), obviamente de la torre SRC, 'a', a la DST, 'b' y, finalmente mover las N ‐ 1  que teníamos en la temporal, desde esa 'TowerTmp' hasta la 'b'.    LISTO. ESO ES TODO. Juéguenlo con 12 cartas (4095 movimientos) y verán que inventarse  un algoritmo NO RECURSIVO, no es nada sencillo...    B)  Include File    En  este  archivo  se  han  colocado  las  cosas  accesorias,  que  no  forman  parte  del  algoritmo. Lo único resaltante es:    // HANOImin_h.h, Luis G. Uribe C.: Play with paper disks!  J12D2013  29 // Magic Number! ie: for a=1 & b=3, => TowerTmp=2 ETC!Sleep on it...  30 #define TowerTmp   ( 6 ‐ a ‐ b )    El Magic Number, según se señala, se obtiene de una fórmula sencilla que nos permite  saber:    ‐ Si vamos de la torre 1 a la 3, la temporal será la 2.  ‐ Para ir  de la       1 a la 2, la temporal será la 3.  ‐ Y,       de la       2 a la 3, la temporal será la 1.    Esa fórmula produce el número de la torre TEMPORAL, dados los otros dos números: SRC y  DST.      18) Torres de Hanoi, MÍNIMO, en ASSEMBLY LANGUAGE:   Es  un  tema  recursivo  de  mi  tratamiento  acerca  de  este  curso,  la  separación  que  debe  hacerse  entre  POLÍTICA  y  MECANISMOS.  Las  POLÍTICAS  definen  LO  QUE  HAY  QUE HACER,  sin  hacer énfasis en el CÓMO, que son precisamente los MECANISMOS.    Cuando  veamos  los  Timers,  las  rutinas  de  comunicaciones,  este  ejercicio...  siempre  resalto la definición de las POLÍTICAS y, aparte, la implementación de los mismos: los  MECANISMOS.    Otro factor importante que debe tenerse en cuenta es el diseño: TOP‐DOWN, es decir, las  funcionalidades deben incluirse desde el nivel más alto del SISTEMA, e ir bajando hasta  llegar  a  definir  lo  más  elemental  que  se  requiere.  Y  luego,  probablemente  la  86 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 IMPLEMENTACIÓN  se  realice  de  abajo  hacia  arriba:  BUTTOM‐UP,  codificando  primero  las  cosas  más  sencillas,  o  elementales,  que  quedaron  colocadas  en  la  base  jerárquica  del  diseño,  y  luego  se  va  subiendo  con  los  módulos  más  complejos,  hasta  llegar  a  los  niveles superiores.    A) Programa HanoiMin--.asm:   ["Temarios\Ex#1‐HanoiMin\HanoiMin‐‐.asm"]  001 ; hanoimin‐‐.asm, Luis G. Uribe C. Hanoi Minimum  V27D2013  002 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  003 ; Include files; Parameter definitions  004        NOLIST  005          INCLUDE 'derivative.inc'  006          INCLUDE 'hanoimin_.inc'  007        LIST  008 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐; <<< <<< Change here 'ND', Number of Disks  009 ND     EQU 3  ; TODO: Get "ND" from push buttons, or from PC, via SCI  010 ;====================================================================  011 ; GLOBAL Variables  012        ORG ram  013 tmpAcc:    DS  1               ; helper to calculate Magic Number  014 ;********************************************************************  015 ; MAIN PROGRAM HEADER:  016        ABSENTRY Main           ; Export 'Main' symbol  017        ORG rom  018 Main:  InitSys    019        HanoiM #ND, #1, #3  ; MOVE ND disks from tower 1‐>3 THAT'S IT!    020    BRA   *                     ; Simulate "C" EXIT from Main.  021 ;====================================================================  022 ;byte Hanoi_ (byte N,byte Ta,byte Tb)  // N from Tower_a to Tb /*()*/  023 Hanoi_:    ; Called from HanoiM (Main), and Hanoi (below) recursively  024        CreateNewStackFrame  025        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  026        lda       N, X          ; Acc = "N", number of discs (N is ND  027         cmp     #1             ; ..pushed by HanoiM above)  028         bne      Several       ; ..if( N != 1 ) goto Several  029        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  030 OnlyOne:   ; Only 1 disk to move from a‐>b? MOVE IT, show the ANSWER:  031        MoveOneFromSrcToDst     ; Acc = 00Ta_00Tb  032         bra      fin           ; <<< Break here to see RESULTS in Acc  033                                ;ND=3 Seq: 1_3 1_2 3_2 1_3 2_1 2_3 1_3  034        ; TODO: Display ACC in LEDs or show it somehow in the PC (SCI)  035        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  036 Several:               ; Got Several disks? Call itself recursively!  037        EvalTowerTmp  Src, Dst  ; Acc = Temp. Tower number: 6‐a‐b  038         sta      TowerTmp, X   ; ..TowerTmp = 6‐a‐b    039        lda       N, X          ; n_1  = n ‐ 1  040         deca                   ; ..  87 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 041         sta      n_1, X        ; ..  042        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  043         Hanoi    n_1,    Src,  TowerTmp  044         HanoiK  #1,      Src,    Dst  045         Hanoi    n_1, TowerTmp,  Dst  046        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  047 fin:   RestoreOldStackFrame  048    RTS  049        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  050        nop ; <<<NEEDED by CodeWarrior 10.1‐5 (not 6.3). INCREDIBLE<<<  051                            ; This 'nop' MAY be removed for CW 6.3 ...  052 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  053 ; Interrupt Vectors  054        ORG     Vreset  055         DC.W   Main            ;  RESET. Maximum priority. Asynch.  056        END    B) Include File HanoiMin_.inc:   ["Temarios\Ex#1‐HanoiMin\HanoiMin_.inc"]  057 ; HANOImin_.inc, Luis G. Uribe C.: V27D2013  M27Y2014  058 rom:       SET     ROMStart    ; $2080  059 ram:       SET     RAMStart    ; Put THIS in Main program  060 initStack:     EQU     $1800  061 COP_Disable:   EQU    %01000010; $42  062 STD            EQU 4           ; 1st variable position in Stack  063 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  064 ; Macros  065 InitSys    MACRO  066        lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  067         sta    SOPT1           ; ..System Options 1  068        ldhx   #initStack       ; Set up SP (and H:X for Stac Frame)  069         txs                    ; ...  070    ENDM  071 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  072 CreateNewStackFrame MACRO  073        pshx                    ; Save previous Stack Frame, order X,H  074         pshh  075        tsx                     ; use H:X to point to Stack Frame  076    ENDM  077 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  078 RestoreOldStackFrame MACRO     ; into HX. Order: H,X  079        pulh                    ; *RECOVER* old Stack Frame  080         pulx  081    ENDM  082 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  083 EvalTowerTmp   MACRO   A, B    ; "ACC=6‐A‐B": MAGIC NUMBER, i.e, for  084        lda     \1, X           ; tmpAcc = A  085         sta      tmpAcc        ; ..  086        lda     \2, X           ; Acc = B  087         add      tmpAcc        ; Acc = B + A  88 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 088        sub      #6             ; Acc = B + A ‐ 6  089         nega                   ; Acc = 6 ‐ A ‐ B  090    ENDM  091 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  092 Push   MACRO   PushValue       ; i.e: Push #3 (Acc <= #3; push Acc)  093        lda     \1  094         psha  095    ENDM  096 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  097 Pop    MACRO   Var             ; i.e: Pop var: pop Acc; var <= Acc  098        pula  099         sta    \1  100    ENDM  101 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  102 PushH  MACRO   PushValue       ; i.e: Push dst (Acc <= dst; push Acc)  103        lda     \1, X  104         psha  105    ENDM  106 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  107 PopH  MACRO   Var              ; i.e: Pop var: pop Acc; var <= Acc  108        pula  109         sta   \1, X  110    ENDM  111 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  112 HanoiM MACRO   n, SrcTower, DstTower   ; Use in Main  113        Push    \3              ; Dst Tower  114        Push    \2              ; Src Tower  115        Push    \1              ; n Disks  116        ais    #‐HANOI_Vars     ; *RESERVA* VARS. locales en "Hanoi_"  117        bsr     Hanoi_  118        ais    #HANOI_Stack     ; Restore Stack. *** Result in ACC ***  119    ENDM  120 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  121 Hanoi  MACRO   n, SrcTower, DstTower   ; Use Recursively  122        PushH   \3              ; Dst Tower  123        PushH   \2              ; Src Tower  124        PushH   \1              ; n Disks  125        ais    #‐HANOI_Vars     ; *RESERVA* VARS. locales en "Hanoi_"  126        bsr     Hanoi_  127        ais    #HANOI_Stack     ; Restore Stack. *** Result in ACC ***  128    ENDM  129 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  130 HanoiK MACRO   n, SrcTower, DstTower   ; 1st parameter is Constant #1  131        PushH   \3              ; Dst Tower  132        PushH   \2              ; Src Tower  133        Push   #1               ; ONE Disk  134        ais    #‐HANOI_Vars     ; *RESERVA* VARS. locales en "Hanoi_"  135        bsr     Hanoi_  136        ais    #HANOI_Stack     ; Restore Stack. *** Result in ACC ***  137    ENDM  138 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐    89 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 139 MoveOneFromSrcToDst    MACRO  140        lda       Src, X        ; ..(Acc) and RETURN.  Acc = 0000_00Ta  141         nsa                    ; ..Nibble Swap Acc.:  Acc = 00Ta_0000  142         sta      tmpAcc        ;                      Tmp = 00Ta_0000  143        lda       Dst, X        ;                      Acc = 0000_00Tb  144         ora      tmpAcc        ; ..                   Acc = 00Ta_00Tb  145    ENDM  146 ;==============================  147 ; Subroutine 'hanoi_'  148 ; In "C" it would be: BYTE hanoi_( BYTE N, BYTE Src, BYTE Dst );  149 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  150 ; (1) LOCAL Variables (in STACK):  151 TowerTmp   EQU     STD         ; remember number of temporal tower  152 n_1        EQU     TowerTmp+1  ; store "n ‐ 1"  153 HANOI_Vars EQU     n_1‐STD+1   ; # de BYTES locales en 'hanoi_'    154 ; (2) PARAMETERS (IN STACK, in >>REVERSE<< order to stack.  155 ; ..If you make *Push DST*, *Push SRC* and then *Push N*, order is as  156 ; ..follows: first N, then SRC and last DST  157 N      EQU     n_1+1           ; tercer parámetro  (1 byte)  158 Src    EQU     N+1             ; segundo parámetro (1 byte)  159 Dst    EQU     Src+1           ; PRIMER parámetro. (1 byte)  160 HANOI_Stack  EQU   Dst‐STD+1   ; Uso del Stack (vars + param)    COMENTARIOS a ["Temarios\Ex#1-HanoiMin\HanoiMin--.asm"]: Bueno,  analicemos  primero  el  programa  principal.  Esta  es  la  parte  donde  se  define  cuántos discos van a jugar (alambrado dentro del código, para hacerlo simple...)    009 ND     EQU 3  ; TODO: Get "ND" from push buttons, or from PC, via SCI    Ya hemos dicho que los métodos recursivos se basan fundamentalmente en variables tipo  dinámicas (como en el C), que son las que se definen en el Stack (ya hicimos ejercicios  de  eso).  Pero  aquí  se  necesita  una  variable  global,  visible  por  todas  las  instancias  del procedimiento recursivo, y que sirve para definir cuál es la torre temporal, en un  instante dado:    013 tmpAcc:    DS  1               ; helper to calculate Magic Number    El programa se define expresando al máximo SÓLO las políticas. Veamos Main, que como en  el ejemplo codificado en C, sirve solo como inicialización y llamado a la rutina que  realiza de manera recursiva, todo el trabajo:    018 Main:  InitSys  019        HanoiM #ND, #1, #3  ; MOVE ND disks from tower 1‐>3 THAT'S IT!    Y, ahora sí, veamos la rutina recursiva:    021 ;====================================================================  022 ;byte Hanoi_ (byte N,byte Ta,byte Tb)  // N from Tower_a to Tower_Tb /*()*/  023 Hanoi_:    ; Called from HanoiM (Main), and Hanoi (below) RECURSIVELY  024        CreateNewStackFrame  90 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se  requiere  definir  un  nuevo  Stack  Frame  cada  vez  que  llamamos  la  rutina.  Ese  es  el  propósito de la Macro 'CreateNewStackFrame' (ver su implementación en el INCLUDE file).    Ahora, mira si la cantidad de discos que tiene que transferir es UNO. Recuerde que ese  caso es el sencillo; si hay un solo disco para ser movido de la torre 1 a la 3, LISTO:  Moverlo... con 'MoveOneFromSrcToDst'    026        lda       N, X          ; Acc = "N", number of discs (N is ND  027         cmp     #1             ; ..pushed by HanoiM above)  028         bne      Several       ; ..if( N != 1 ) goto Several  029        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  030 OnlyOne:   ; Only 1 disk to move from a‐>b? MOVE IT, show the ANSWER:  031        MoveOneFromSrcToDst     ; Acc = 00Ta_00Tb  032         bra      fin           ; <<< Break here to see RESULTS in Acc  033                                ;ND=3 Seq: 1_3 1_2 3_2 1_3 2_1 2_3 1_3    De lo contrario, aplicar el mismo procedimiento de manera recursiva. Primero obtenga la  identificación  de  la  torre  temporal  (EvalTowerTmp),  dadas  las  actuales  torres  SRC  y  DST:    036 Several:               ; Got Several disks? Call itself RECURSIVELY!  037        EvalTowerTmp  Src, Dst  ; Acc = Temp. Tower number: 6‐a‐b  038         sta      TowerTmp, X   ; ..TowerTmp = 6‐a‐b    Luego obtenga el actual N‐1, que son los discos que deben moverse a la torre temporal    039        lda       N, X          ; n_1  = n ‐ 1  040         deca                   ; ..  041         sta      n_1, X        ; ..    Y, finalmente, aplique el procedimiento recursivo:    043         Hanoi    n_1,    Src,  TowerTmp  044         HanoiK  #1,      Src,    Dst  045         Hanoi    n_1, TowerTmp,  Dst    Por último, reestablezca el valor del antiguo Stack Frame, y RETORNE:    047 fin:   RestoreOldStackFrame  048    RTS    B)  Include File hanoiMin_.inc:    COMENTARIOS a ["Temarios\Ex#1-HanoiMin\hanoiMin_.inc"]: 057 ; HANOImin_.inc, Luis G. Uribe C.:  V27D2013    Las  primeras  líneas  no  necesitan  especial  mención.  Push  y  Pop  se  entiende  con  facilidad.    Están las equivalentes que usan el registro índice H:X para apuntar al valor al que se  requiere hacer push o pop:  91 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 102 PushH  MACRO   PushValue       ; i.e: Push #3 (Acc <= #3; push Acc)  103        lda     \1, X  104         psha  105    ENDM  106 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  107 PopH  MACRO   Var              ; i.e: Pop var: pop Acc; var <= Acc  108        pula  109         sta   \1, X  110    ENDM    La  próxima  Macro  guarda  los  parámetros:  DST,  SRC  y  N  en  es  stack,  reserva  (AIS)  el  espacio  para  las  variables,  llama  a  la  subrutina  y  al  regresar,  libera  el  espacio  ocupado  en  el  Stack  (AIS).  Esta  se  diseñó  para  su  uso  en  MAIN  (HanoiM):  (hace  referencia a los parámetros en forma directa: Push):    112 HanoiM MACRO   n, SrcTower, DstTower   ; Use in Main  113        Push    \3              ; Dst Tower  114        Push    \2              ; Src Tower  115        Push    \1              ; n Disks  116        ais    #‐HANOI_Vars     ; *RESERVA* VARS. locales en "Hanoi_"  117        bsr     Hanoi_  118        ais    #HANOI_Stack     ; Restore Stack. *** Result in ACC ***    Hanoi es la que se usa de manera recursiva (hace referencia a los parámetros empleando  el registro índice: PushH):    121 Hanoi  MACRO   n, SrcTower, DstTower   ; Use Recursively  122        PushH   \3              ; Dst Tower  123        PushH   \2              ; Src Tower  124        PushH   \1              ; n Disks  125        ais    #‐HANOI_Vars     ; *RESERVA* VARS. locales en "Hanoi_"  126        bsr     Hanoi_  127        ais    #HANOI_Stack     ; Restore Stack. *** Result in ACC ***  128    ENDM    Finalmente, cuando se requiere pasar el número 1 (#1) como disco, uno de los PushH de  'Hanoi' tiene que cambiarse por Push simple:    130 HanoiK MACRO   n, SrcTower, DstTower   ; 1st parameter is Constant #1  131        PushH   \3              ; Dst Tower  132        PushH   \2              ; Src Tower  133        Push   #1               ; ONE Disk  134        ais    #‐HANOI_Vars     ; *RESERVA* VARS. locales en "Hanoi_"  135        bsr     Hanoi_  136        ais    #HANOI_Stack     ; Restore Stack. *** Result in ACC ***  137    ENDM    Estas  3  Macros  se  necesitan  porque  al  programa  Assembler  del  CodeWarrior  le  falta  EXPRESIVIDAD.  En  otros  ensambladores,  bastaría  una  Macro,  y  ella  determinaría  con  facilidad  el  curso  de  acción,  si  es  el  que  se  requiere  en  Main,  o  en  las  llamadas  recursivas, o si se va a incluir una constante como el número #1.    92 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La Macro MoveOneFromSrcToDst es la que realiza el movimiento (que, en nuestro ejemplo,  consiste  simplemente  en  mostrar  en  el  ACUMULADOR  los  números  de  las  torres  que  participan  en  cada  jugada.  (Esta  parte  debería  modificarse  para  presentar  la  información  en  los  LEDs  de  la  tarjeta  DEMOQE128,  o  enviarla  por  el  canal  de  comunicación serial al PC del laboratorio, para su presentación por pantalla.    139 MoveOneFromSrcToDst    MACRO  140        lda       Src, X        ; ..(Acc) and RETURN.  Acc = 0000_00Ta  141         nsa                    ; ..Nibble Swap Acc.:  Acc = 00Ta_0000  142         sta      tmpAcc        ;                      Tmp = 00Ta_0000  143        lda       Dst, X        ;                      Acc = 0000_00Tb  144         ora      tmpAcc        ; ..                   Acc = 00Ta_00Tb  145    ENDM    Además  de  las  Macros,  está  la  subrutina,  que  es  el  VERDADERO  MOTOR  DEL  PROCESO  RECURSIVO, gracias a su manejo de variables dinámicas en Stack:    147 ; Subroutine 'hanoi_'  148 ; In "C" it would be: BYTE hanoi_( BYTE N, BYTE Src, BYTE Dst );  149 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  150 ; (1) LOCAL Variables (in STACK):  151 TowerTmp   EQU     STD         ; remember number of temporal tower  152 n_1        EQU     TowerTmp+1  ; store "n ‐ 1"  153 HANOI_Vars EQU     n_1‐STD+1   ; # de BYTES locales en 'hanoi_'    154 ; (2) PARAMETERS (IN STACK, in >>REVERSE<< order to stack.  155 ; ..If you make *Push DST*, *Push SRC* and then *Push N*, order is as  156 ; ..follows: first N, then SRC and last DST  157 N      EQU     n_1+1           ; tercer parámetro  (1 byte)  158 Src    EQU     N+1             ; segundo parámetro (1 byte)  159 Dst    EQU     Src+1           ; PRIMER parámetro. (1 byte)  160 HANOI_Stack  EQU   Dst‐STD+1   ; Uso del Stack (vars + param)    Si usted entiende la importancia del ejercicio de las Torres de Hanoi, y es capaz de  reescribirlo  por  su  cuenta,  habrá  logrado  culminar  la  parte  genérica  del  Curso  de  Arquitectura del Computador.       TIMERS   Gran  cantidad  de  problemas  requieren  'temporizadores'  que  permitan  establecer  en  un   momento determinado, si transcurrió o no cierto lapso. Los "timers" son el corazón  de  los Sistemas Operativos y de los sistemas Embebidos...    NÚMERO DE TIMERS:   ¿Por qué no hacer una rutina que active y desactive el Timer: RTC, TPM (o TIMER0 en los  PIC) de acuerdo a la cantidad de milisegundos que se quiere medir?    93 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Pues porque, como normalmente sólo hay *UN* módulo temporizador básico (RTC), y siempre  se requieren VARIOS (o MUCHOS) contadores, se necesitarían ¡más recursos de hardware de  los que existen! La solución común ofrece varios contadores por software, basados en un  sólo dispositivo de hardware.    Anexo hay un conjunto de rutinas para manejar 8 temporizadores diferentes (que son un  número  razonable  pero,  si  no  le  alcanzan,  ¡usted  puede  aumentarlos  con  relativa  facilidad para ajustarlos a sus necesidades!)    El  conjunto  de  Rutinas  de  Soporte  para  8  Timers  está  en  el  archivo  'timers8HS.inc',  versión para CodeWarrior 10.6, MC9S08QE128, DEMOQE128 (H es la secuencia, contada desde  la versión A que hice en Noviembre del 2003. S es la actualización para el MC9S08.    Estas  rutinas  fueron  publicadas  en  EDN,  en  la  versión  que  realicé  para  MICROCHIP  PIC16F84A, operando a 4 MHz.    En Internet:  http://edn.com/design/URIBE:Use‐eight‐timers‐with‐PIC16Fxxx‐microcontrollers     OPERACIÓN Y USO DE LA BIBLIOTECA DE TIMERS:   1. Se activa el proceso de los 8 temporizadores empleando la Macro:    Init8Timers    La manera como operan es la siguiente: 'Init8Timers' genera 8 variables, desde 'Timer0'  hasta 'Timer7', que ocupan DOS (2) bytes cada una, (16 bits, sin signo).    Cada  'Timer'  cronometrará  una  cierta  cantidad  de  tiempo,  en  milisegundos.  Por  tanto,  podrán medirse intervalos entre 1 ms y 65,536 segundos (un poco más de un minuto; si  necesita extenderlo más aún, usted tiene que programar contadores de minutos...)    Existe también un byte en el que CADA BIT representa el estado de UN timer, del 0 al 7:  la variable llamada 'TimerFlags', y 8 SÍMBOLOS que facilitan la lectura del estado de  cada uno de esos bits: 'Timer0Ready' a 'Timer7Ready'    2. Para inicializar un temporizador (en MiliSegundos) se usa la macro  SetimerMS. Uno puede decir:      SetimerMS 0, #2  ;active temporizador 0 con la constante #2: 2 milisegundos, o:      SetimerMS  0,  var  ;active  el  timer          0  con  el  contenido  de  la  variable  var,  en  milisegundos    En el programa de prueba timer8HS.asm, incluido más adelante, se emplean así:         SetimerMS 0, #50  ; Timer0: 50 mSeconds through CONSTANT       SetimerMS 1, var  ; Timer1: 25 mSeconds through VARIABLE (var holds #25)      94 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 3. La macro SetimerMS inicializa un timer, pero el flujo de programa NO SE DETIENE A  ESPERAR  a  que  pase  el  tiempo  programado.  La  macro  WaitMS_on  hace  ambas  cosas:  Inicializa un timer _Y_ espera hasta que transcurra el lapso indicado en MiliSegundos:  es una rutina BLOQUEANTE.      4.  En  el primer  caso (SetimerMS),  para  averiguar  subsecuentemente  si  ya  se cumplió  o  no,  el  tiempo  deseado,  podrían  emplearse  las  instrucciones  'brset'  o  'brclr',  de  la  siguiente forma:      brset Timer0Ready, TimerFlags, labelYES    ; Si el bit está SET, es porque YA se cumplió el tiempo      brclr Timer1Ready, TimerFlags, labelNOT    ; Si el bit está CLR, es porque NO se ; cumplió el tiempo  Sin  embargo  usted  no  tiene  que  hacer  necesariamente  eso,  ya  que  he  incluido  esta  funcionalidad, perfectamente encapsulada en las dos macros adicionales, que se muestran  a continuación:    TimeOut  timer, gotoAddress  ; (JUMP to gotoAddress if “timer” expired. Return with jump/br)    TimeOutS timer, bsrAddress  ; (BRANCH TO SUBROUTINE bsrAddress if “timer” expired. Return with rts)    Estas  Macros  inspeccionan  el  temporizador  “timer”  y,  en  caso  de  haber  expirado  (Timeout), ejecutan la rutina correspondiente, saltando con BRANCH a gotoAddress, o con  BSR a bsrAddress, según corresponda.    También se incluyen las Macros Complementarias, para su conveniencia:    NTimeOut timer, gotoAddress  ; .. gotoAddress IF timer has *NOT* expired    NTimeOutS timer, callAddress  ; .. branch to Subroutine callAddress IF timer has NOT expired    Antes  de  mostrar  los  programas  en  Assembly  Language,  voy  a  ilustrar  el  tema  en  "C"  (Windows).  Luego  tendrán  la  oportunidad  de  ver  que  las  funcionalidades  definidas  en  Ensamblador son EXACTAMENTE LAS MISMAS. En esencia lo único que cambia es el lenguaje  de programación.      19) Timers for Windows (timers.cpp):   Como ya hemos visto la aproximación a la inclusión de funcionalidades, en forma de Top‐ Down Design, voy a presentar en primer lugar el include file timers_h.h, que define los  MECANISMOS.  Después,  el  programa  que  materializa  la  librería,  Timers.cpp,  y  que  implementa  los  MECANISMOS.  Por  último,  un  ejemplo  para  que  se  pueda  visualizar  el  empleo de las diferentes funcionalidades. timtst2.cpp:    95 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ["Laboratorios\Lab2\_New\timers_h.h"]  001 /* *************************************************************** */  002 // timers_h.h: Windows, time usage; Luis G. Uribe C.  003 // L13M2006 L16O06 MSVC J01Y08 C31G11 V09S11 S05E2013  L21O2013  004 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  005 // Include Files  006 #include <windows.h>  007 #include <conio.h>  008 #include <stdio.h>  009 using namespace System;  010 using namespace System::Threading;    011 #define NTIMERS    8   // This MUST be a power of 2: 2, 4, 8, 16...    012 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  013 // Function Prototypes    014            #define Timeout TimeouT // Avoid System::Timeout conflict    015 void IniTimers( int ntimers );  016 void SetimerMS(  int timer, int timeMS );  017 int  Timeout( int timer );  018 void WTimer( int timer );  019 void WaitMS_on(  int timer, int timeMS );    ["Laboratorios\Lab2\_New\timers.cpp"]:  020 /* *************************************************************** */  021 // Timers.cpp, Windows Luis G. Uribe C.  S21S2013  022 // .. L13M2006 L16O6 VC C31G2011 J17E2013 (_kbhit) V08F2013    023 // MSVisual Studio: Change config. properties, general, to include:  024 // .. Common Language Runtime Support (/clr)  025 // .. Program File Extensions MUST be CPP  026 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  027 // Include Files  028 #include "timers_h.h"    029 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  030 // Global Variables      031 static volatile long timersMS[ NTIMERS ];    032 /* =============================================================== */  033 void IniTimers ( int ntimers )                                 /*()*/  034 {  // 'ntimers' must be 8 in this implementation. It is NOT used...    035    if( NTIMERS & (NTIMERS ‐ 1) ) {  036        fprintf( stderr,  037                 "NTIMERS (%d) *MUST* be a Power of 2", NTIMERS );  038        abort();  039    }  96 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 040    // LINK init ALL timers ( timersMS[ NTIMERS ] ) to ZERO    041 }  /* IniTimers() */    042 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  043 void SetimerMS ( int timer, int timeMS )                       /*()*/  044 {  045    long tc = (long)Environment::TickCount;    // ticks (milliSeconds)    046    timersMS[ timer & ( NTIMERS ‐ 1 ) ] =  047        tc + (long)timeMS;    // ticks (milliSeconds)    048 }      /* SetimerMS() */    049 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  050 int Timeout ( int timer )                                      /*()*/  051 {  052    long tc = (long)Environment::TickCount;    // ticks (milliSeconds)    053    _kbhit();                           // Enable Ctrl‐C testing!  054    if( timersMS[ timer & ( NTIMERS ‐ 1 ) ] >= ( tc ) )  055        return( 0 );  056    return( 1 );    057 }  /* Timeout() */    058 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  059 void WTimer ( int timer )                                      /*()*/  060 {  061    long tc = (long)Environment::TickCount;    // ticks (milliSeconds)    062    if( timersMS[ timer & ( NTIMERS ‐ 1 ) ] ‐ tc > 0 )  063        Thread::Sleep( timersMS[ timer & ( NTIMERS ‐ 1 ) ] ‐ tc );  064 }      /* WTimer() */  065 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  066 void WaitMS_on ( int timer, int timeMS )                       /*()*/  067 {  068    SetimerMS( timer, timeMS );  069    WTimer( timer );    070 }      /* WaitMS_on() */    ["Laboratorios\Lab2\_New\timtst2.cpp"]:  071 char *_id =  072 "timtst2.cpp testing routines (Lite, WINDOWS). Luis G. Uribe C.,"  073 " C07S2011 L31O2013\n";    074 // char *_usage = "timtest2 \n";  075 // cc timtst2.c timers.c    076 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  077 /* Include Files */  97 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 078 #include <stdio.h>  079 #include <stdlib.h>    080 #include "timers_h.h"    081 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  082 /* Global Variables */    083 char text[] = "1234567890";    084 /* =============================================================== */  085 void main ( void )                                             /*()*/  086 {  char *p = text;    087    puts( _id );  088    IniTimers( 8 );             // only 8 timers allowed at this time    089    fputs( "\n0) Press 'ENTER' to print w/o delays________ ",stdout );  090    _getch();  091    for( p = text; *p; p ++ ) {         // Impresión SIN retardos  092        _putch( *p );           // putchar may be buffered; use _putch  093    }    094    /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  095    // RTC Test: Show 3 ways for calling Timer Routines. See: // <<<    096    fputs( "\n1) Press 'ENTER' to continue at 300 ms/char_ ",stdout );  097    _getch();  098    for( p = text; *p; p ++ ) { // Impresión con Wait_on a 300 mS.  099        _putch( *p );  100        WaitMS_on( 0, 300 );        // <<< BLOCK CPU for 300 ms  101    }  102    fputs( "\n2) Press 'ENTER' to continue at 750 ms/char_ ",stdout );  103    _getch();  104    for( p = text; *p; p ++ ) { // Impresión con while a 750 mS.  105        _putch( *p );  106        SetimerMS( 7, 750 );        // <<< Setup timer    107        // <<< Do here any process you need    108        WTimer( 7 );                // <<< ..wait for remaining time  109    }    110    fputs( "\n3) Press 'ENTER' to continue at 200 ms/char_ ",stdout );  111    _getch();    112    p = text;                       // Impresión con while a 200 mS.  113 ploop:  114    while( *p ) {  115        _putch( *p ++ );  116        SetimerMS( 5, 200 );        // <<< Setup timer    98 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 117        // <<< Do here any process you need    118        while( 1 ) {  119            if( Timeout( 5 ) ) {    // <<< Test condition later...  120                goto ploop;         // <<< ..finish? print next char  121            }                       // <<< You won't need GOTOs !  122        }  123    }  124    fputs( "\n4) Press 'ENTER' to continue at 500 ms/char_ ",stdout );  125    _getch();  126    for( p = text; *p; p ++ ) { // Impresión con while a 500 mS.  127        _putch( *p );  128        WaitMS_on( 0, 500 );        // <<<  129    }  130    fputs( "\n5) Press 'ENTER' to continue at 1000 ms/char ",stdout );  131    _getch();  132    for( p = text; *p; p ++ ) { // Impresión con while a 500 mS.  133        _putch( *p );  134        WaitMS_on( 0, 1000 );       // <<<  135    }  136    fputs( "\n6) Press 'ENTER' to Return to full speed____ ",stdout );  137    _getch();  138    puts( text );    139    puts( "\nEnd... (Press 'ENTER' to finish)" );  140    getchar();  141    exit( 0 );    142 }      /* main() */    COMENTARIOS a ["Laboratorios\Lab2\_New\timers_.h"]:   El  include  file  no  tiene  nada  extraño;  básicamente  define  las  funciones  que  se  implementarán a continuación.    011 #define NTIMERS    8   // This MUST be a power of 2: 2, 4, 8, 16...    El  número  de  Timers  se  ha  fijado  a  8  en  esta  implementación,  pero  puede  cambiarse  y  recompilar la librería. Los valores tienen que ser potencias de 2.    Las  funcionalidades  que  se  ofrecen  son  casi  idénticas  a  las  que  mencionamos  en  la  introducción, y a las que implementaremos en lenguaje ensamblador:    015 void IniTimers( int ntimers );  016 void SetimerMS(  int timer, int timeMS );  017 int  Timeout( int timer );  018 void WTimer( int timer );  019 void WaitMS_on(  int timer, int timeMS );    99 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Laboratorios\Lab2\_New\timers.cpp"]:   La variable timersMS es global, por conveniencia:    031 static volatile long timersMS[ NTIMERS ]; // Global Variable    IniTimers( 8 ) (el parámetro no interesa; pero se valida que NTIMERS sea potencia de 2)    033 void IniTimers ( int ntimers )                                 /*()*/  034 {  // 'ntimers' must be 8 in this implementation. It is NOT used...  035    if( NTIMERS & (NTIMERS ‐ 1) ) {  036        fprintf( stderr,  037                 "NTIMERS (%d) *MUST* be a Power of 2", NTIMERS );  038        abort();  039    }    No  se  inicializan  los  timers  en  esta  implementación,  porque  el  LINKER  realiza  esta  función para programas en C.    040    // LINK init ALL timers ( timersMS[ NTIMERS ] ) to ZERO  041 }  /* IniTimers() */    Para activar un timer:    043 void SetimerMS ( int timer, int timeMS )                       /*()*/  044 {  long tc = (long)Environment::TickCount;    // ticks (milliSeconds)  046    timersMS[ timer & ( NTIMERS ‐ 1 ) ] =  047        tc + (long)timeMS;    // ticks (milliSeconds)  048 }      /* SetimerMS() */    Se toma una de las representaciones de la hora de Windows: TickCount (en Milisegundos);  se le suma el tiempo requerido, timeMS, en milisegundos también, y se lo almacena en la  variable correspondiente al temporizador deseado, timer.  La  funcionalidad  para  establecer  si  ya  venció  el  plazo  seleccionado  para  algún  temporizador, timer, es 'Timeout':    050 int Timeout ( int timer ) {  052    long tc = (long)Environment::TickCount;    // ticks (milliSeconds)  053    _kbhit();                           // Enable Ctrl‐C testing!  054    if( timersMS[ timer & ( NTIMERS ‐ 1 ) ] >= ( tc ) )  055        return( 0 );  056    return( 1 );  057 }  /* Timeout() */    Timeout() lee en 'tc' la representación de la hora, TickCount en MS, vigente para el  momento  en  que  se  lo  ejecuta;  y  compara  si  el  valor  almacenado  para  el  temporizador  'timer'  es  mayor  o  igual  a  'tc'.  En  caso  de  ser  así  significa  que  el  tiempo  actual,  'tc', no ha sobrepasado aún al memorizado para el temporizador 'timer'. En ese caso, se  retorna un '0', que indique que aún no está Listo ese temporizador.    100 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Si, por el contrario, el tiempo almacenado NO es superior al tiempo actual, 'tc', es  porque  el  tiempo  ya  ha  sobrepasado  el  valor  esperado  por  el  temporizador  deseado,  'timer', y se devuelve un '1' indicando que ese lapso YA EXPIRÓ.    WTimer  consiste  en  detener  el  flujo  del  programa  (bloquearlo)  y  esperar  hasta  que  el  tiempo  del  temporizador  'timer',  que  ha  sido  previamente  activado  mediante  un  SetimerMS,  expire.  Emplea  una  funcionalidad  ad‐hoc  del  Windows:  Sleep().  Esta  implementación  tiene  la  ventaja  de  que  al  programa  lo  suspende  el  sistema  operativo  hasta que se cumpla la condición, y Windows puede adjudicar el CPU y otros recursos,  como la memoria, el disco, etc., a algún proceso que se encuentre en disponibilidad de  trabajar (esté Ready).  059 void WTimer ( int timer )                                      /*()*/  060 {  long tc = (long)Environment::TickCount;    // ticks (milliSeconds)  062    if( timersMS[ timer & ( NTIMERS ‐ 1 ) ] ‐ tc > 0 )  063        Thread::Sleep( timersMS[ timer & ( NTIMERS ‐ 1 ) ] ‐ tc );  064 }   /* WTimer() */    Cuando  hay  que  activar  un  temporizador,  y  el  flujo  del  programa  no  puede  continuar  hasta  que  transcurra  el  intervalo  seleccionado,  la  funcionalidad  BLOQUEANTE  que  se  emplea es WaitMS_on:    066 void WaitMS_on ( int timer, int timeMS )                       /*()*/  067 {  SetimerMS( timer, timeMS );  069    WTimer( timer );  070 }      /* WaitMS_on() */    COMENTARIOS a ["Laboratorios\Lab2\_New\timtst2.cpp"]: El programa de prueba, timtst2.cpp, se explica por sí mismo.        20) Librería de TIMERS para el HC9S08 (timers8HS.inc):   La  librería  'timers8HS.inc'  es  FUNDAMENTAL  para  desarrollar  sus  proyectos.  Entender  cómo está hecha es imprescindible, y usted tiene que ser capaz de codificar sus propias  funciones, como demostración de que entendió todo el proceso.    ["Laboratorios\Lab2\Timers8HS\timers8HS.inc"]  001 ; Timers8HS.inc: Luis G. Uribe C., Basic 8 Timers Support  L30D2013  002 ;====================================================================  003 ; THE FOLLOWING MACROS ARE DEFINED IN THIS LIBRARY  004 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  005 ; Init8Timers:  Init 8Timers library  006 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  007 ; SetimerMS:  MACRO timer, time ; time is either CONSTANT or VARIABLE  008 ; .. Set timer and continue to work  009 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  010 ; WaitMS_on:  MACRO timer, time ; time is either CONSTANT or VARIABLE  011 ; .. Set timer and BLOCK until timer expires  012 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  013 ; TimeOut:    MACRO timer, gotoAddress  ; timer is always a CONSTANT  014 ; .. gotoAddress IF timer has expired  101 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 015 ; NTimeOut:   MACRO timer, gotoAddress  ; timer is always a CONSTANT  016 ; .. gotoAddress IF timer has *NOT* expired  018 ; TimeOutS:   MACRO timer, callAddress  ; timer is always a CONSTANT  019 ; .. branch to Subroutine callAddress IF timer has expired  020 ; NTimeOutS:  MACRO timer, callAddress  ; timer is always a CONSTANT  021 ; .. branch to Subroutine callAddress IF timer has NOT expired  022 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  023 ; WaitISR_on: MACRO timer, time ; time is either CONSTANT or VARIABLE  024 ; .. Set timer and BLOCK until timer expires, INSIDE some ISR  025 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  026 ; TIMERS8ISR:   ISR routines to update 8 timers (user need to INCLUDE  027 ;               ..this Macro in MAIN but it is of no further concern)  028 ;====================================================================  029 ; DATA DEFINITION.  030 ; Define 8 timers Global Variables  031 ; Note: Macro directives (DW, $IF...) cannnot go into macros... GOK!  033 ; NOTE: REQUIRES  'ORG ram'  **BEFORE**  INCLUDE 'timers8HS.inc'  034 ; ..in main program, to make this data definitions work!  035 ; *** ALL following variables  >>>MUST<<<  reside on ZERO page! ***  036 ; ..(Timerflags use bset and bclr...)  037 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  038 ; TimerDef: MACRO and Variables DEFINITION  039 TimerDef:  MACRO   timer  040 Timer\1:   DS.W    1           ; 16 bit counters: 65536 mS: more than  041 Timer\1.Rdy EQU    \1          ; .. one minute each timer (1mS Tick)  042    ENDM  043        TimerDef    0  044        TimerDef    1  045        TimerDef    2  046        TimerDef    3  047        TimerDef    4  048        TimerDef    5  049        TimerDef    6  050        TimerDef    7  051 TimerFlags: DS.B   1           ; 8 bit flags: Timer0,Timer1..  052 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  053 ; DEFINES  054 ;ram:      SET Z_RAMStart ; <<<TIMERS8: Put THIS into Main program<<<  055 rom:       SET ROMStart        ; $2080  056 initStack: EQU RAMEnd + 1      ; $1800=$17FF+1. Stack begins on $17FF  057 COP_Disable:   EQU $42  058 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  059 ; Defines Bits for RTC Programming  060 ; ..RTCSC ($1830) bits. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, p.246  061 RTCPS_BY_ONE EQU mRTCSC_RTCPS3 ; ..Bit #8 Divide by 1, gives 1mS Tick  062 RTCIE:     EQU RTCSC_RTIE      ; ..Bit #4 RealTime Interrupt  Enable  063 RTCIE.bit  EQU mRTCSC_RTIE     ; ..Bit #4 RealTime Clock Int. Enable  064 NOT_RTCIE.bit  EQU (~RTCIE.bit & $00FF)  065 RTCIF:     EQU RTCSC_RTIF      ; ..Bit #7 RealTime Interrupt  Enable  066 RTCIF.bit  EQU mRTCSC_RTIF     ; ..Bit #7 RealTime Clock Int. Enable  067 ONE_KHZ    EQU 0               ; Use internal default 1Khz clock src.  068 RTC_FLAGS  EQU (RTCIF.bit | ONE_KHZ | RTCIE.bit | RTCPS_BY_ONE)  102 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 069 ;====================================================================  070 ; More MACRO DEFINITIONS  071 TimeOut:   MACRO   timer, gotoAddress  ; timer is always a CONSTANT  072        brset   \1, TimerFlags, \2  073    ENDM  074 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  075 NTimeOut:  MACRO   timer, gotoAddress  ; timer is always a CONSTANT  076        brclr   \1, TimerFlags, \2  077    ENDM  078 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  079 TimeOutS:  MACRO   timer, callAddress  ; timer is always a CONSTANT  080        brclr   \1, TimerFlags, \@cont  081        bsr     \2  082 \@cont:  083    ENDM  084 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  085 NTimeOutS: MACRO   timer, callAddress  ; timer is always a CONSTANT  086        brset   \1, TimerFlags, \@cont  087        bsr     \2  088 \@cont:  089    ENDM  090 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  091 SetimerMS: MACRO   timer, time ; time is either CONSTANT or VARIABLE  092        bset    \1, TimerFlags  ; Make timer X Ready... Mutex !!!  093        pshh                    ; save H:X  094         pshx                   ; ..  095         ldhx   \2              ; time in milliseconds (50)        <<<  096         sthx   Timer\1         ; ..                               <<<  097        pulx                    ; restore H:X  098        pulh                    ; ..  099        bclr    \1, TimerFlags  ; Make timer X Not Ready...  100    ENDM  101 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  102 WaitMS_on:  MACRO   timer, time ; time is Constant or Variable  103        SetimerMS \1, \2  104        brclr   \1, TimerFlags, * ; Wait 'time' mseconds on 'timer'  105    ENDM  106 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  107 WaitISR_on: MACRO  timer, time ; time is Constant or Variable  108        psha                    ; save ACC  109         tpa                    ; push CCR Saves IMask status  110         psha                   ; ..  111        sei                     ; DISABLE INTERRUPTS (IMask=1): Let  112         SetimerMS \1, \2       ; ..Setimer used inside Int. Routines  113        cli                     ; REENABLE INTERRUPTS: Let RTC Count  114        brclr   \1, TimerFlags, * ; Wait 'time' mseconds on 'timer'  115        pula                    ; Restore Interrupt Flag Mask  116         tap                    ; ..to its Saved value  117        pula                    ; Restore ACC  118    ENDM  119 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  120 ; Ancillary MACRO DEFINITIONS  103 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 121 UpdateTmr: MACRO  timer  122        brset   Timer\1.Rdy, TimerFlags, \@Next ; Mutex !!!  123        ldhx    Timer\1         ; load H:X  124        aix    #‐1              ; decrement TimerXX  125         sthx   Timer\1         ; ..  126        bne     \@Next  127         bset   Timer\1.Rdy, TimerFlags  ; 0?: Mark TimerXX READY  128 \@Next:  129    ENDM  130 ;====================================================================  131 ; RTC INIT. This is the base for Timer Support  132 Init8Timers:   MACRO  133        clrx  134        clrh  135         sthx   Timer0          ; Init Timer0 to 0  136         sthx   Timer1          ; Init Timer1 to 0  137         sthx   Timer2          ; Init Timer2 to 0  138         sthx   Timer3          ; Init Timer3 to 0  139         sthx   Timer4          ; Init Timer4 to 0  140         sthx   Timer5          ; Init Timer5 to 0  141         sthx   Timer6          ; Init Timer6 to 0  142         sthx   Timer7          ; Init Timer7 to 0  143        mov    #$FF, TimerFlags ; Set ALL 8 bit timers flags: Done!  144        lda    #RTC_FLAGS       ; Use 1KHz internal clock/1: 1mS tick  145         sta    RTCSC           ; ..Clear RTCIF; IntEnable RTC  146    ENDM  147 ;====================================================================  148 ; RTC Interrupt Service Routine (ISR). 90 cycles...  149 TIMERS8ISR:    MACRO  150        pshh                    ; H Not saved by Interrupt process  151        UpdateTmr   0           ; Protected from Setimers by Mutex  152        UpdateTmr   1           ; ...  153        UpdateTmr   2  154        UpdateTmr   3  155        UpdateTmr   4  156        UpdateTmr   5  157        UpdateTmr   6  158        UpdateTmr   7  159 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  160 RTCISRexit:  161        lda    #RTC_FLAGS       ; Use 1KHz internal clock/1: 1mS tick  162         sta    RTCSC           ; ..Clear RTCIF; IntEnable RTC  163        pulh                    ; H not restored by RTI process  164        rti                     ; ..Return from Interrupt  165    ENDM    COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\timers8HS.inc"]: Los aspectos más sobresalientes son:    003 ; THE FOLLOWING MACROS ARE DEFINED IN THIS LIBRARY  005 ; Init8Timers:  Init 8Timers library  104 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 007 ; SetimerMS:  MACRO timer, time ; time is either CONSTANT or VARIABLE  008 ; .. Set timer and continue to work    010 ; WaitMS_on:  MACRO timer, time ; time is either CONSTANT or VARIABLE  011 ; .. Set timer and BLOCK until timer expires    013 ; TimeOut:    MACRO timer, gotoAddress  ; timer is always a CONSTANT  014 ; .. gotoAddress IF timer has expired    015 ; NTimeOut:   MACRO timer, gotoAddress  ; timer is always a CONSTANT  016 ; .. gotoAddress IF timer has *NOT* expired    018 ; TimeOutS:   MACRO timer, callAddress  ; timer is always a CONSTANT  019 ; .. branch to Subroutine callAddress IF timer has expired    020 ; NTimeOutS:  MACRO timer, callAddress  ; timer is always a CONSTANT  021 ; .. branch to Subroutine callAddress IF timer has NOT expired    023 ; WaitISR_on: MACRO timer, time ; time is either CONSTANT or VARIABLE  024 ; .. Set timer and BLOCK until timer expires, INSIDE some ISR    026 ; TIMERS8ISR:   ISR routines to update 8 timers (user need to INCLUDE  027 ;               ..this Macro in MAIN but it is of no further concern)    029 ; DATA DEFINITION.  033 ; NOTE: REQUIRES  'ORG ram'  **BEFORE**  INCLUDE 'timers8HS.inc'  034 ; ..in main program, to make this data definitions work!    Esta  es  el  primer  REQUISITO  IMPORTANTE  para  emplear  esta  librería:  en  Main,  ANTES  de  INCLUIR  'timers8HS.inc',  debe  haberse  hecho  un:  'ORG  ram'  Esto  se  debe  a  que  esta  librería define sus propias variables, y requiere encontrarse en el DATA SEGMENT a la  hora de hacerlo.    039 TimerDef:  MACRO   timer  040 Timer\1:   DS.W    1           ; 16 bit counters: 65536 mS: more than  041 Timer\1.Rdy EQU    \1          ; .. one minute each timer (1mS Tick)  043        TimerDef    0  ... etc., hasta 7.    Observe  algo  con  detenimiento.  A  veces  algún  usuario  de  la  librería  necesita  emplear  únicamente  un  pequeño  valor  en  sus  temporizadores,  y  decide  usar  sólo  un  byte  para  representar  sus  retardos...  Cuando  van  a  cambiar  el  valor  del  WORD  que  representa  el  contador, lo almacenan simplemente en la variable TimerN (digamos, Timer0). Y ESTÁ MAL.  Tienen que recordar que esta máquina es BIG ENDIAN, y que el valor Terminal de un WORD  está en "Timer0+1" (en el segundo byte del WORD).    Una aproximación mejor sería cargar el valor en el registro índice H:X (su valor de 8  bits  quedaría  en  la  parte  inferior  de  índice)  y  mover  sus  16  bits  a  Timer0;  el  procesador lo almacena como corresponde, dejando el byte que representa su información,  en la parte apropiada de la variable.    059 ; Defines Bits for RTC Programming  105 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Lo mejor es que verifique TODOS los bits empleados aquí, con el Reference Manual del  MC9S08QE128,  en  donde  están  claramente  definidos  todos  y  cada  uno  de  los  bits  aquí  utilizados.    Al leer las Macros y funciones definidas en esta librería, esté pendiente de caer en  cuenta de la implementación Down‐Up, de lo más elemental hasta lo más sofisticado.    Las  Macros  TimeOut,  NTimeOut,  TimeOutS  y  NTimeOutS  son  de  confección  más  bien  elemental.    La  Macro  SetimerMS  requiere  alguna  aclaración  explicativa;  recibe  dos  parámetros,  el  timer que ha de programarse, y el valor de 'time' en Milisegundos.    091 SetimerMS: MACRO   timer, time ; time is either CONSTANT or VARIABLE    Lo primero que hace, antes de comenzar a jugar con las variables asociadas, es marcar  ese  timer  como  READY  (tiempo  expirado).  Lo  hace  colocando  un  uno  (1)  en  el  correspondiente bit de TimerFlags:    092        bset    \1, TimerFlags  ; Make timer X Ready... Mutex !!!    Luego  resguarda  el  registro  índice,  que  va  a  usar  para  cargar  el  valor  del  tiempo  (time) y almacenarlo en la variable asociada (TimerX). Observe la sangría en el texto,  que resalta varias instrucciones para una sola operación. Finalmente, recupera el valor  del registro índice:    093        pshh                    ; save H:X  094         pshx                   ; ..  095         ldhx   \2              ; time in milliseconds (50)        <<<  096         sthx   Timer\1         ; ..                               <<<  097        pulx                    ; restore H:X  098        pulh                    ; ..  099        bclr    \1, TimerFlags  ; Make timer X Not Ready...    Para la Macro WaitMS_on resulta también innecesario algún otro comentario.    En  cambio,  se  ha  incluido  una  funcionalidad  (WaitISR_on)  que  permite  utilizar  la  librería de timers8, que están basados en las interrupciones del reloj, DENTRO DE OTRAS  rutinas de interrupción. Esto se hizo por comodidad, y como ejemplo de que los manuales  de  Motorola/Freescale están  equivocados  al  decir  que,  habilitar  interrupciones  DENTRO  de interrupciones, es más conveniente NO HACERLO, que porque dizque es complicado, y no  trae ningún provecho ni mejora en el comportamiento de los programas.    Complicado,  y  de  cuidado,  sí  es.  Pero  si  le  hicieran  caso  al  manual,  los  manufacturantes de sistemas operativos no tendrían trabajo.    WaitISR_on,  en  términos  generales,  equivale  a  Wait_ON,  pero  está  especialmente  programada  para  funcionar  DENTRO  de  las  rutinas  de  interrupción.  NUNCA  USE  Wait_ON  dentro de una rutina de interrupciones.    TAMPOCO USE EL MISMO NÚMERO DE TIMER DENTRO DE LAS RUTINAS DE INTERRUPCIONES Y EN EL  PROGRAMA PRINCIPAL.  106 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 107 WaitISR_on: MACRO  timer, time ; time is Constant or Variable    Resguarda  en  el  Stack  el  valor  del  acumulador;  salva  también  el  valor  del  CCR  en  el  Stack, para guardar el estado actual del CPU y FUNDAMENTALMENTE SI ESTÁ HABILITADO PARA  INTERRUPCIONES, O DESHABILITADO (IFlag)    Esto  es  importante  porque,  al  momento  de  usar  esta  Macro  WaitISR_on,  la  rutina  debe  deshabilitar  las  interrupciones  para  poder  trabajar  “toqueteando”  las  variables  del  timer involucrado. Luego de eso habría la tendencia a REHABILITAR las interrupciones...  pero  lo  que  corresponde  es  DEJARLAS  COMO  ESTABAN  cuando  WaitISR_on  comenzó  a  trabajar... De lo contrario, se generará un DESASTRE.    108        psha                    ; save ACC  109         tpa                    ; push CCR Saves IMask status  110         psha                   ; ..    Ahora se deshabilitan las interrupciones (SEI) pues, como acabo de decir, no se puede  manipular la estructura de datos de un timer, si hay la posibilidad de que otra rutina,  fuera  de  ésta  interrupción,  también  trate  de  hacerle  cambios.  Lo  apropiado  es  DESHABILITARLAS...    111        sei                     ; DISABLE INTERRUPTS (IMask=1): Let    Luego, se modifica la estructura de datos del temporizador (SetimerMS) y se REHABILITAN  LAS  INTERRUPCIONES  (CLI),  pues  de  lo  contrario  el  timer  no  funcionaría,  ya  que  opera  por interrupciones. Se hace un bucle de espera (que no necesariamente es BLOQUEANTE, ya  que ¡las interrupciones están ACTIVAS!).    112         SetimerMS \1, \2       ; ..Setimer used inside Int. Routines  113        cli                     ; REENABLE INTERRUPTS: Let RTC Count  114        brclr   \1, TimerFlags, * ; Wait 'time' mseconds on 'timer'    Al terminar el 'brclr', habrá expirado el timer; y es hora de recomponer todo.    115        pula                    ; Restore Interrupt Flag Mask  116         tap                    ; ..to its Saved value  117        pula                    ; Restore ACC    Es importante notar en UdateTmr, que NO TOCA NADA (dado que entra por interrupciones)  si el bit asociado al timer está en uno:    122        brset   Timer\1.Rdy, TimerFlags, \@Next ; Mutex !!!  (y, note que Setimer lo primero que hace, antes de manipular nada más, es colocar ese  bit  en  uno.  Así,  si  Setimer  ha  comenzado  a  actualizar  la  estructura  de  datos  de  un  temporizador  dado,  la  rutina  de  interrupciones  NO  LO  ACTUALIZARÁ,  gracias  a  ese  bit  que,  además  de  servir  de  indicador  de  READY,  sirve  como  Semáforo para  la  apropiación  Mutuamente Exclusiva de ese recurso: MUTEX)    120 ; Ancillary MACRO DEFINITIONS  121 UpdateTmr: MACRO  timer  122        brset   Timer\1.Rdy, TimerFlags, \@Next ; Mutex !!!  123        ldhx    Timer\1         ; load H:X  124        aix    #‐1              ; decrement TimerXX  107 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 125         sthx   Timer\1         ; ..  126        bne     \@Next  127         bset   Timer\1.Rdy, TimerFlags  ; 0?: Mark TimerXX READY  128 \@Next:  La  rutina  de  inicialización  de  las  variables,  Init8Timers,  no  necesita  mayores  aclaratorias. Solo que en las líneas 144, 145 se escoge un reloj para el RTC, que es de  1KHz (1 milisegundo por tick), muy conveniente para nuestros propósitos. Hay toda una  gama  diferente  para  escoger;  a  usted  le  toca  estudiarla,  en  caso  de  que  sus  temporizadores requieran ser mucho más rápidos, o más lentos.    132 Init8Timers:   MACRO  ...  144        lda    #RTC_FLAGS       ; Use 1KHz internal clock/1: 1mS tick  145         sta    RTCSC           ; ..Clear RTCIF; IntEnable RTC      La  rutina  de  interrupción  del  RTC  (Real  Time  Clock:  TIMERS8ISR),  que  atiende  las  actualizaciones de los diversos temporizadores, se basa en la Macro UpdateTmr que, como  ya vimos, respeta a un temporizador si fuera de esta rutina de interrupciones, alguien  lo está manipulando, vía Setimer:    148 ; RTC Interrupt Service Routine (ISR). 90 cycles...  149 TIMERS8ISR:    MACRO  150        pshh                    ; H Not saved by Interrupt process  151        UpdateTmr   0           ; Protected from Setimers by Mutex  152        UpdateTmr   1           ; ...  153        UpdateTmr   2  154        UpdateTmr   3  155        UpdateTmr   4  156        UpdateTmr   5  157        UpdateTmr   6  158        UpdateTmr   7    160 RTCISRexit:  161        lda    #RTC_FLAGS       ; Use 1KHz internal clock/1: 1mS tick  162         sta    RTCSC           ; ..Clear RTCIF; IntEnable RTC  163        pulh                    ; H not restored by RTI process  164        rti                     ; ..Return from Interrupt    21) Ejemplo del uso de la Librería de TIMERS (timer8HS.asm):   Las cosas que hay que recordar para emplear correctamente la librería de timers, están  todas marcadas en los ejemplos con el símbolo: <<<    No deje de mirar esas marcas en los ejemplos, y atender a las recomendaciones...    ["Laboratorios\Lab2\Timers8HS\timer8HS.asm"]  01 ;********************************************************************  02 ; Programa Timer8HS.asm: TST Timer Support  03 ; Luis G. Uribe C., L24N2003 L08N4 M27F7 D08L7 (timeOut) J16M2009  04 ; V31Y9 V08J2012 C04D2013  L09D2013  108 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 05 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  06 ; Include files  07 ;  08 ; CHECK‐LIST: Conserve el orden Y la posición relativa de:  09 ; .. derivative.inc, timers8HS.inc  e  Init8Timers  10 ;  11 ; Si usa Init8Timers, *TIENE* que inicializar el vector: INT_RTC  12 ; ..con DC.W  RTC_INTERRUPT    13        NOLIST  14          INCLUDE 'derivative.inc'  15        LIST    16 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  17 ; 2) DEFINES  18 ram:   SET     Z_RAMStart  ;   <<<TIMERS8: <<<  19 ;====================================================================  20 ; 3) Global Variables    21        ORG     ram         ; <<<TIMERS8: *BEFORE* 'timers8HS.inc'<<<  22        NOLIST  23          include 'timers8HS.inc'  ; <<<TIMERS8: Code, Vars & Macros  24        LIST    25 var:   DS.W    1  26 var2:  DS.W    1    27 ;********************************************************************  28 ; Begin of Code Section  29        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  30        ORG rom  31 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32 ; Always include the following 4 instructions    33 Main:  lda    #COP_Disable  34         sta    SOPT1       ; System Options 1  35        ldhx   #initStack   ; Init SP  36         txs    37        Init8Timers    38 ;====================================================================  39 ; Main Loop (Your Code)    40        ldhx   #300             ; Begin with 300 mS delay, via 'Var'  41         sthx   var             ; ..    42        ldhx   #2               ; 2 mS delay, via Var2  43         sthx   var2            ; ..  44 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  45        cli                     ; Enable interrupts now  46 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  109 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 47 again:  48        Setimer 2, #200         ; Begin 200 mSeconds via Constant  49        Setimer 5,  var         ; 300 mSeconds via VARIABLE  50 loop:  TimeOut 2,  Two_on      ; If TimeOut 2 goto Two_on  51        TimeOutS 5, Five_on     ; else if TimeOut 5, brs to Five_on  52        bra     loop            ; else 'loop'  53 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  54 Five_on:                       ; From BRS (TimeOutS); return via RTS  55        lda     #5              ; ACC=5: identify THIS routine  56        Setimer  5, var         ; Reinstall timer5 w/300 mS  57        WaitMS_on  7, var2      ; 2 mSeconds from Variable (var2)  58        ldhx     var  59        bne      Five_cont  60        rts  61 Five_cont:  62        aix     #‐1  63         sthx    var  64        rts  65 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  66 Two_on:                        ; From BRA (TimeOut); return via BRA  67        lda     #2              ; ACC=2: identify THIS routine  68        Setimer  2, #200        ; Reinstall timer w/200 mS  69        WaitMS_on  0, #150      ; Test WaitMS_on constant  70        bra      loop    71 ;====================================================================  72 ; PROGRAM TRAILER:  73 ;  74 ; 1) TIM8INT Macro  75 ; 2) Interrupt Vectors  76 ;  77 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  78 ; RTC Interrupt Service Routine, si usa Init8Timers...    79 RTC_INTERRUPT:  80        TIMERS8ISR    81 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  82 ; Interrupt Vectors    83 dummy_isr:                     ; <<< Must be placed in ROM Space  84        rti    85        ORG     Vrtc            ; Increasing priority from bottom up  86         DC.W   RTC_INTERRUPT   ; (Si usa Init8Timers...)    87        ORG     Virq            ; Virq, Vswi and Vreset  88         DC.W   dummy_isr       ; IRQ  89         DC.W   dummy_isr       ; SWI  90         DC.W   Main            ; RESET. Maximum priority. Asynch.  91    END    110 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\timer8HS.asm"]: Aspectos novedosos:    18 ram:   SET     Z_RAMStart  ;   <<<TIMERS8: <<<    20 ; 3) Global Variables  21        ORG     ram         ; <<<TIMERS8: *BEFORE* 'timers8HS.inc'<<<  22        NOLIST  23          include 'timers8HS.inc'  ; <<<TIMERS8: Code, Vars & Macros  24        LIST    33 Main:  lda    #COP_Disable  ...  37        Init8Timers    40        ldhx   #300             ; Begin with 300 mS delay, via 'Var'  41         sthx   var             ; ..  42        ldhx   #2               ; 2 mS delay, via Var2  43         sthx   var2            ; ..    44 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  45        cli                     ; Enable interrupts now    Suele olvidarse este último paso, y nada funciona. Está todo activo, pero el procesador  ignora todas las interrupciones.    Lo mismo suele ocurrir con la rutina de interrupciones del RTC. Hay que incluirla de  acuerdo al siguiente código:    79 RTC_INTERRUPT:  80        TIMERS8ISR    82 ; Interrupt Vectors    85        ORG     Vrtc            ; Increasing priority from bottom up  86         DC.W   RTC_INTERRUPT   ; (Si usa Init8Timers...)  ...    22) "WaitISR_on" Interactuando con la "IRQISR"   ["Laboratorios\Lab2\Timers8HS\tim8_IRQ‐HS.asm"]  001 ;********************************************************************  002 ; Programa Tim8_IRQ‐HS.asm: Timer calls inside IRQISR J01M2007 D12A09  003 ; Luis G. Uribe C., J16A09 V29Y09 V08J2012 C20F2013  S21D2013  004 ; C20F2013: Use 'WaitISR_on 7,delay' on IRQISR, instead of WaitMS_on  005 ;  006 ; Program showS interactions between RTC 'timers8' interrupt routines  007 ; ..and 'IRQ' interrupts, when it is needed to request timer delays  008 ; ..from IRQISR. This is a typical case when you need to rearm global  009 ; ..interrupts DESPITE THE ADVISE IN CONTRARY FROM MOTOROLA TECHNICAL  111 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 010 ; ..DOCUMENTS... (cfr. 01CPU08RM!ReferenceManual‐U.pdf, page 30)  011 ;  012 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  013 ; Include files    014        NOLIST  015          INCLUDE 'derivative.inc'  016        LIST    017 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  018 ; Parameter definitions    019 IRQACK:    EQU IRQSC_IRQACK  020 IRQIE:     EQU IRQSC_IRQIE  021 IRQPE:     EQU IRQSC_IRQPE    022 ram:   SET     Z_RAMStart  ; $80  023 ;====================================================================  024 ; Global Variables    025        ORG ram             ; Put this *BEFORE* 'timers8HS.inc'    026        NOLIST  027          include 'timers8HS.inc'  ; <<<TIMERS8: Code, Vars & Macros  028        LIST  029 delay:     DS.W    1  030 counter:   DS.B    1  031 ;********************************************************************  032 ; MAIN PROGRAM HEADER:  033        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  034        ORG rom    035 Main:  lda    #COP_Disable ; RSTE=0: PTA5 is NOT for ~RESET  036         sta    SOPT1       ; ..System Options 1  037        ldhx   #initStack   ; Init SP  038         txs  039 ;====================================================================  040 ; External Interrupt Initialization (>>>IRQ<<<)  041 ; 1. Mask interrupts by clearing IRQIE in IRQSC.  042 ;    (IRQIE=0 on Power on Reset, by Default)  043 ; 2. Select the pin polarity by setting the appropriate IRQEDG bits  044 ;    in IRQSC (IRQEDG=0 is falling edge triggering)  045 ;    (IRQEDG=0 on Power on Reset, by Default)  046 ; 3. If using internal pull‐up/pull‐down device, clear the IRQPDD bit  047 ;    in IRQSC (IRQPDD=0 on Power on Reset, by Default)  048 ; 4. Enable the IRQ pin by setting the appropriate IRQPE bit in IRQSC  049 ;    (IRQPE=0 by Default on Power on Reset >>>WE MUST set IRQPE=1<<<)  050 ; NOTE: IRQSC: equ $000F? Zero Page Register. Use Direct addressing!!  051 ; 5. Write to IRQACK in IRQSC to clear any false interrupts.  052 ; 6. Set IRQIE in IRQSC to enable interrupts      112 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 053        bset    IRQPE,  IRQSC   ; 4. above  054        bset    IRQACK, IRQSC   ; 5. above. IRQACK=1 to clear (!) IRQF  055        bset    IRQIE,  IRQSC   ; 6. above  056 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  057        mov    #$FF, PTCDD      ; Set Ports C & D all bits for output  058        mov    #$FF, PTEDD      ; ..(they drive the LEDs on DEMOQE128)  059        ldhx   #20              ; time in milliseconds (20)  060         sthx   delay           ; ..    061        mov    #$FF, counter    ; Decrement!!! instead of increment...  062        mov    #$FF, PTCD       ; Leds have negative logic: 0 to light  063        mov    #$FF, PTED       ; ..  064        Init8Timers  065 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  066 ; Main Loop  067        cli                     ; Global Enable interrupts NOW  068 again: bra     again  069 ;====================================================================  070 ; PROGRAM TRAILER:  071 ;  072 ; 1) TIM8INT Macro  073 ; 2) Interrupt Vectors  074 ;  075 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  076 ; RTC Interrupt Service Routine, si usa Init8Timers...    077 RTC_INTERRUPT:  078        TIMERS8ISR    079 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  080 ; IRQ INTERRUPT SERVICE ROUTINE  081 ; IRQ has one of the highest priorities in the HCS08, and the higher  082 ; ..hardware priority, bellow Reset and SWI. That is ok for  083 ; ..responsiveness, but care must be taken to assure that no  084 ; ..>>>PRIORITY INVERSION<<< ocurrs through long ISR times, for  085 ; ...example by using debouncing delays. If this is the case, you  086 ; ..MUST auto‐disable the IRQ interrupts and re‐enable global  087 ; ..interrupts, to permit other lower peripherals access...    088 IRQISR:  089        bclr    IRQIE, IRQSC; **SELF‐DISABLE IRQ Interrupts**    090        pshh                ; Not saved by interrupt proc  091        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  092        ; 20mS delay processing for IRQISR: Help to avoid multiple  093        ; ..interrupts due to contact bouncing from IRQ pin...  094        WaitISR_on 7, delay ; 20 MSeconds for debouncing. This only  095                            ; ..works if RTC is enabled to interrupt..  096                            ; ..You could have included NOP loops to  097                            ; ..delay: but you CANNOT block all other  098                            ; ..peripheral interrupts! So, you must  099                            ; ..follow this same strategy...  113 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 100        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  101        ; *** NOTE *** Timer  7  >>>MUST NOT<<< BE USED ANYWHERE ELSE!  102        ; ..It belongs EXCLUSIVELY to this 'IRQISR'  103        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐    104        dec     counter         ; Drive LEDs using negative logic, is  105        mov     counter, PTCD   ; 6 bits to drive LEDs  106        mov     counter, PTED   ; 2 bits more to drive higher LEDs    107 IRQISRexit:  108        bset    IRQACK, IRQSC   ; IRQACK=1 to clear (!) IRQF  109        bset    IRQIE,  IRQSC   ; Re‐enable IRQ interrupts    110        pulh                    ; Not restored by interrupt process  111        rti                     ; Return from Interrupt    112 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  113 ; Interrupt Vectors    114        ORG     Vrtc            ; Increasing priority from bottom up  115         DC.W   RTC_INTERRUPT   ; (Si usa Init8Timers...)    116        ORG     Virq  117         DC.W   IRQISR          ; IRQ    118        ORG     Vreset  119         DC.W   Main            ; RESET. Maximum priority. Asynch.    120    END    COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\tim8_IRQ-HS.asm"]:   Aspectos novedosos:    Protocolo completo de Habilitación del IRQ: External Interrupt Initialization  (>>>IRQ<<<)      041 ; 1. Mask interrupts by clearing IRQIE in IRQSC.  042 ;    (IRQIE=0 on Power on Reset, by Default)  043 ; 2. Select the pin POLARITY by setting the appropriate IRQEDG bits  044 ;    in IRQSC (IRQEDG=0 is falling edge triggering)  045 ;    (IRQEDG=0 on Power on Reset, by Default)  046 ; 3. If using internal pull‐up/pull‐down device, clear the IRQPDD bit  047 ;    in IRQSC (IRQPDD=0 on Power on Reset, by Default)  048 ; 4. Enable the IRQ pin by setting the appropriate IRQPE bit in IRQSC  049 ;    (IRQPE=0 by Default on Power on Reset >>>WE MUST set IRQPE=1<<<)  050 ; NOTE: IRQSC: equ $000F? Zero Page Register. Use Direct addressing!!  051 ; 5. Write to IRQACK in IRQSC to clear any false interrupts.  052 ; 6. Set IRQIE in IRQSC to enable interrupts    114 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 053        bset    IRQPE,  IRQSC   ; 4. above  054        bset    IRQACK, IRQSC   ; 5. above. IRQACK=1 to clear (!) IRQF  055        bset    IRQIE,  IRQSC   ; 6. above    Programación de los LEDs:    057        mov    #$FF, PTCDD      ; Set Ports C & D all bits for output  058        mov    #$FF, PTEDD      ; ..(they drive the LEDs on DEMOQE128)  059        ldhx   #20              ; time in milliseconds (20)  060         sthx   delay           ; ..    Los LEDs están alambrados para ENCENDER con un CERO:    061        mov    #$FF, counter    ; Decrement!!! instead of increment...  062        mov    #$FF, PTCD       ; Leds have negative logic: 0 to light  063        mov    #$FF, PTED       ; ..    080 ; IRQ INTERRUPT SERVICE ROUTINE  081 ; IRQ has one of the highest priorities in the HCS08, and the higher  082 ; ..hardware priority, bellow Reset and SWI. That is ok for  083 ; ..responsiveness, but care must be taken to assure that no  084 ; ..>>>PRIORITY INVERSION<<< ocurrs through long ISR times, for  085 ; ...example by using debouncing delays. If this is the case, you  086 ; ..MUST auto‐disable the IRQ interrupts and re‐enable global  087 ; ..interrupts, to permit other lower peripherals access...    La ISR del periférico que vaya a emplear WaitISR_on, tiene particularidades necesarias  para  funcionar.  En  primer  lugar,  dado  que  va  a  rehabilitarse  la  atención  de  interrupciones,  ANTES  de  terminar  esa  IRQ  (en  el  ejemplo,  ISRIRQ),  es  INDISPENSABLE  AUTO DESHABILITAR las interrupciones:    088 IRQISR:  089        bclr    IRQIE, IRQSC; **SELF‐DISABLE IRQ Interrupts**    En este ejemplo sencillo, se llama a continuación a WaitISR_on (que si ya leyeron su  código, hace varias cosas, entre ellas, HABILITAR LAS INTERRUPCIONES DEL CPU. (De otra  forma, no operarían las interrupciones del timer, RTC)    094        WaitISR_on 7, delay ; 20 MSeconds for debouncing.  101        ; *** NOTE *** Timer  7  >>>MUST NOT<<< BE USED ANYWHERE ELSE!  102        ; ..It belongs EXCLUSIVELY to this 'IRQISR'  103        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐    En el ejemplo, cada vez que se activa el IRQ, se incremental los LEDs. Como encienden  al revés, en lugar de incrementar, decrementamos... (truco)    104        dec     counter         ; Drive LEDs using negative logic, is  105        mov     counter, PTCD   ; 6 bits to drive LEDs  106        mov     counter, PTED   ; 2 bits more to drive higher LEDs    Para finalizar IRQISR, se da el Acknowledge correspondiente al hardware, y se retorna  al programa principal:  115 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 107 IRQISRexit:  108        bset    IRQACK, IRQSC   ; IRQACK=1 to clear (!) IRQF  109        bset    IRQIE,  IRQSC   ; Re‐enable IRQ interrupts  ...  111        rti                     ; Return from Interrupt      23) Enciende los 8 LEDs con Diferentes Intervalos   ["Laboratorios\Lab2\Timers8HS\TimedFlash8.asm"]  001 ;********************************************************************  002 ; Program TimedFlash8.asm: Luis G Uribe C. D10J2012 C20F2013 C04D2013  003 ; Flashes all 8 LEDs at different intervals each one. Push IRQ to  004 ; ..toggle program OFF/ON  005 ; C20F2013: Do NOT use timer 7 inside IRQISR AND in Main program...  006 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  007 ; Include files  008        NOLIST  009          INCLUDE 'derivative.inc'  010        LIST  011 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  012 ; Parameter definitions    013 IRQACK:    EQU IRQSC_IRQACK  014 IRQIE:     EQU IRQSC_IRQIE  015 IRQPE:     EQU IRQSC_IRQPE    016 ram:   SET     Z_RAMStart      ; $80  017 ;====================================================================  018 ; Global Variables    019        ORG ram             ; <<<Put this *BEFORE* 'timers8HS.inc'<<<    020        NOLIST  021          include 'timers8HS.inc'   ; <<<TIMERS8: Code, Vars & Macros  022        LIST  023 LEDsMask:  DS.B    1           ; Ram bank #0 (direct addressing)  024 Toggle:    DS.B    1  025 ;********************************************************************  026 ; MAIN PROGRAM HEADER:  027        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  028        ORG rom    029 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  030         sta    SOPT1           ; ..System Options 1  031        ldhx   #initStack       ; Init SP  032         txs  033 ;====================================================================  034 ; External Interrupt Initialization (>>>IRQ<<<) (see Tim8_IRQ‐HS.asm)      116 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 035        bset    IRQPE,  IRQSC   ; 4. above  036        bset    IRQACK, IRQSC   ; 5. above. IRQACK=1 to clear (!) IRQF  037        bset    IRQIE,  IRQSC   ; 6. above  038 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  039 ; Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7)    040        mov    #$FF, PTCDD      ; Set Ports C & D all bits for output  041        mov    #$FF, PTEDD      ; ..(they drive the LEDs on DEMOQE128)    042        Init8Timers  043 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  044 ; Main Loop  045        cli                     ; Global Enable interrupts NOW  046        Setimer 0, #673  >> 1  047        Setimer 1, #150  >> 1  048        Setimer 2, #2755 >> 1  049        Setimer 3, #1398 >> 1  050        Setimer 4, #511  >> 1  051        Setimer 5, #1002 >> 1  052        Setimer 6, #3000 >> 1  053        Setimer 7, #355  >> 1  054        mov    #$01, Toggle     ; Begin in RUN state  055    loop:  056        brclr   0, Toggle, *    ; Wait for Bit0 (RUN/~STOP)  057        clr     LEDsMask    058        NTimeOut    0,  ET0  059        bset        0,  LEDsMask  060        Setimer     0, #673  >> 1  061      ET0:  062        NTimeOut    1,  ET1  063        bset        1,  LEDsMask  064        Setimer     1, #150  >> 1  065      ET1:  066        NTimeOut    2,  ET2  067        bset        2,  LEDsMask  068        Setimer     2, #2755 >> 1  069      ET2:  070        NTimeOut    3,  ET3  071        bset        3,  LEDsMask  072        Setimer     3, #1398 >> 1  073      ET3:  074        NTimeOut    4,  ET4  075        bset        4,  LEDsMask  076        Setimer     4, #511  >> 1  077      ET4:  078        NTimeOut    5,  ET5  079        bset        5,  LEDsMask  080        Setimer     5, #1002 >> 1  081      ET5:  082        NTimeOut    6,  ET6  083        bset        6,  LEDsMask  117 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 084        Setimer     6, #3000 >> 1  085      ET6:  086        NTimeOut    7,  ET7  087        bset        7,  LEDsMask  088        Setimer     7, #355  >> 1  089      ET7:  090        lda     PTCD  091        eor     LEDsMask  092         sta    PTCD  093         sta    PTED  094    jmp loop  095 ;====================================================================  096 ; PROGRAM TRAILER:  097 ; 1) TIM8INT Macro  098 ; 2) Interrupt Vectors  099 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  100 ; RTC Interrupt Service Routine, si usa Init8Timers...    101 RTC_INTERRUPT:  102        TIMERS8ISR  103 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  104 ; IRQ INTERRUPT SERVICE ROUTINE    105 IRQISR:  106        bclr    IRQIE, IRQSC; **SELF‐DISABLE IRQ Interrupts**    107        pshh                ; Not saved by interrupt proc  108        lda     Toggle  109        eor    #%00000001  110         sta    Toggle  111 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  112 IRQISRexit:  113        sei  114        bset    IRQACK, IRQSC   ; IRQACK=1 to clear (!) IRQF  115        bset    IRQIE,  IRQSC   ; Re‐enable IRQ interrupts    116        pulh                    ; Not restored by interrupt process  117        rti                     ; Return from Interrupt  118 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  119 ; Interrupt Vectors  120 dummy_isr:                     ; <<<< Must be placed in ROM Space  121        rti    122        ORG     Vrtc            ; Increasing priority from bottom up  123         DC.W   RTC_INTERRUPT   ; (Si usa Init8Timers...)    124        ORG     Virq            ; Virq, Vswi and Vreset  125         DC.W   IRQISR          ; IRQ  126         DC.W   dummy_isr       ; SWI  127         DC.W   Main            ; RESET. Maximum priority. Asynch.    128    END  118 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Laboratorios\Lab2\Timers8HS\TimedFlash8.asm"]: Este ejercicio resulta simpático de observar en la tarjeta DEMOQE128:    044 ; Main Loop  045        cli                     ; Global Enable interrupts NOW    Activa los 8 timers, cada uno con un valor que obtuve de un programa que genera números  al  azar. El  número  es  dividido  por  dos  (>>  1)  para que  dure  la  mitad  encendido  y  la  mitad apagado (50% Dutty Cycle)    046        Setimer 0, #673  >> 1  047        Setimer 1, #150  >> 1  048        Setimer 2, #2755 >> 1  049        Setimer 3, #1398 >> 1  050        Setimer 4, #511  >> 1  051        Setimer 5, #1002 >> 1  052        Setimer 6, #3000 >> 1  053        Setimer 7, #355  >> 1  054        mov    #$01, Toggle     ; Begin in RUN state    Toggle es una variable que sirve para detener o continuar el parpadeo, al oprimir el  botón de IRQ.    El  proceso  es  muy  sencillo.  Se  trata  de  un  lazo  infinito,  en  el  que  se  pregunta  por  cada temporizador para ver si ya expiró. Cuando alguno ya cumplió, se debe cambiar el  estado del correspondiente LED.    La  rutina  no  afecta  directamente  los  LEDs,  sino  que,  a  partir  de  una  máscara  (LEDsMask),  que  comienza  en  ceros  al  principio  del  ciclo,  se  coloca  en  1  el  bit  correspondiente  a  cada  LED  que  tenga  que  CAMBIAR.  Al  finalizar  el  ciclo,  se  hace  un  EXOR del LEDsMask con el valor actual de los LEDs (línea 091 eor). Así, se cambia el  valor, de encendido a apagado, o viceversa, para aquellos a los cuales se les venció el  tiempo.  Desde  luego,  cada  vez  que  para  alguno  expira  el  intervalo,  se  lo  vuelve  a  inicializar, con Setimer...    091        eor     LEDsMask  055    loop:  056        brclr   0, Toggle, *    ; Wait for Bit0 (RUN/~STOP)  057        clr     LEDsMask    058        NTimeOut    0,  ET0  059        bset        0,  LEDsMask  060        Setimer     0, #673  >> 1  061      ET0:  062        NTimeOut    1,  ET1  063        bset        1,  LEDsMask  064        Setimer     1, #150  >> 1  ...  089      ET7:  090        lda     PTCD  091        eor     LEDsMask  119 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 092         sta    PTCD  093         sta    PTED  094    jmp loop    La 105 IRQISR básicamente alterna la variable Toggle (su bit #0), de esta manera, el  programa principal puede detener todo el proceso, o reanudarlo, de acuerdo al valor de  la variable Toggle:    056        brclr   0, Toggle, *    ; Wait for Bit0 (RUN/~STOP)  ...  108        lda     Toggle  109        eor    #%00000001  110         sta    Toggle    24) Enciende ORDENADAMENTE los 8 LEDs con Diferentes Intervalos   Los LEDs más lentos están a un lado de la tarjeta; los más rápidos al otro extremo. Es  una variación sencilla del ejercicio anterior, y no necesita mayores comentarios:    ["Laboratorios\Lab2\Timers8HS\TimedFlash8Ordered.asm"]  001 ;********************************************************************  002 ; TimedFlash8Ordered.asm: Luis G. Uribe C. D10J2012 C20F2013 S22G13  003 ; C04D2013, J19D2013 cosmetics  004 ; Flashes all 8 LEDs at different intervals each one. Push IRQ to  005 ; ..toggle program OFF/ON  006 ; C20F2013: Do NOT use timer 7 inside IRQISR AND in Main program...  007 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  008 ; Include files  009        NOLIST  010          INCLUDE 'derivative.inc'  011        LIST  012 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  013 ; Parameter definitions        014 IRQACK:    EQU IRQSC_IRQACK  015 IRQIE:     EQU IRQSC_IRQIE  016 IRQPE:     EQU IRQSC_IRQPE        017 ram:   SET     Z_RAMStart      ; $80  018 ;====================================================================  019 ; Global Variables        020        ORG ram             ; <<<Put this *BEFORE* 'timers8HS.inc'<<<        021        NOLIST  022          include 'timers8HS.inc'   ; <<<TIMERS8: Code, Vars & Macros  023        LIST        024 LEDsMask:  DS.B    1           ; Ram bank #0 (direct addressing)  025 Toggle:    DS.B    1  026 ;********************************************************************  120 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 027 ; MAIN PROGRAM HEADER:  028        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  029        ORG rom        030 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  031         sta    SOPT1           ; ..System Options 1  032        ldhx   #initStack       ; Init SP  033         txs  034 ;====================================================================  035 ; External Interrupt Initialization (>>>IRQ<<<) (see Tim8_IRQ‐HS.asm)        036        bset    IRQPE,  IRQSC   ; 4. above  037        bset    IRQACK, IRQSC   ; 5. above. IRQACK=1 to clear (!) IRQF  038        bset    IRQIE,  IRQSC   ; 6. above  039 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  040 ; Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7)        041        mov    #$FF, PTCDD      ; Set Ports C & D all bits for output  042        mov    #$FF, PTEDD      ; ..(they drive the LEDs on DEMOQE128)        043        Init8Timers  044 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  045 ; Main Loop  046        cli                     ; Global Enable interrupts NOW  047        SetimerMS 0, # 150 >> 1  048        SetimerMS 1, # 355 >> 1  049        SetimerMS 2, # 511 >> 1  050        SetimerMS 3, # 673 >> 1  051        SetimerMS 4, #1002 >> 1  052        SetimerMS 5, #1398 >> 1  053        SetimerMS 6, #2755 >> 1  054        SetimerMS 7, #3000 >> 1  055        mov    #$01, Toggle     ; Begin in RUN state  056    loop:  057        brclr   0, Toggle, *    ; Wait for Bit0 (RUN/~STOP)  058        clr     LEDsMask        059        NTimeOut    0,  ET0  060        bset        0,  LEDsMask  061        SetimerMS   0, #150  >> 1  062      ET0:  063        NTimeOut    1,  ET1  064        bset        1,  LEDsMask  065        SetimerMS   1, #355  >> 1  066      ET1:  067        NTimeOut    2,  ET2  068        bset        2,  LEDsMask  069        SetimerMS   2, #511  >> 1  070      ET2:  071        NTimeOut    3,  ET3  072        bset        3,  LEDsMask  073        SetimerMS   3, #673  >> 1  121 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 074      ET3:  075        NTimeOut    4,  ET4  076        bset        4,  LEDsMask  077        SetimerMS   4, #1002 >> 1  078      ET4:  079        NTimeOut    5,  ET5  080        bset        5,  LEDsMask  081        SetimerMS   5, #1398 >> 1  082      ET5:  083        NTimeOut    6,  ET6  084        bset        6,  LEDsMask  085        SetimerMS   6, #2755 >> 1  086      ET6:  087        NTimeOut    7,  ET7  088        bset        7,  LEDsMask  089        SetimerMS   7, #3000 >> 1  090      ET7:  091        lda     PTCD  092        eor     LEDsMask  093         sta    PTCD  094         sta    PTED  095    jmp loop        096 ;====================================================================  097 ; PROGRAM TRAILER:  098 ; 1) TIM8INT Macro  099 ; 2) Interrupt Vectors  100 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  101 ; RTC Interrupt Service Routine, si usa Init8Timers...  102 RTC_INTERRUPT:  103        TIMERS8ISR  104 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  105 ; IRQ INTERRUPT SERVICE ROUTINE  106 IRQISR:  107        bclr    IRQIE, IRQSC; **SELF‐DISABLE IRQ Interrupts**  108        pshh                ; Not saved by interrupt proc  109        lda     Toggle  110        eor    #%00000001  111         sta    Toggle  112 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  113 IRQISRexit:  114        sei  115        bset    IRQACK, IRQSC   ; IRQACK=1 to clear (!) IRQF  116        bset    IRQIE,  IRQSC   ; Re‐enable IRQ interrupts        117        pulh                    ; Not restored by interrupt process  118        rti                     ; Return from Interrupt  119 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  120 ; Interrupt Vectors  121 dummy_isr:                     ; <<< Must be placed in ROM Space  122        rti  123        ORG     Vrtc            ; Increasing priority from bottom up  122 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 124         DC.W   RTC_INTERRUPT   ; (Si usa Init8Timers...)  125        ORG     Virq            ; Virq, Vswi and Vreset  126         DC.W   IRQISR          ; IRQ  127         DC.W   dummy_isr       ; SWI  128         DC.W   Main            ; RESET. Maximum priority. Asynch.  129    END    Finalización Tema TIMERS Antes  de  finalizar  el  tema  de  los  Timers,  y  después  de  haber  leído  la  documentación  hasta  aquí,  haberla  estudiado  y  entenderla...  haga  un  examen  de  conciencia  que  le  permita decir con certeza, si no hay uno o más detalles que usted hubiera pasado por  alto, o que ni siquiera entendió.  Este es el nivel de detalle al que hay que acostumbrarse cuando trabaja en ésta área de  los sistemas Embebidos, donde todo depende de todo, y cuando no pensar en un bit que  nos  sirva  como  Mutex  (tal  como  lo  manejan  la  rutina  de  interrupciones  del  timer,  y  otras como el Setimer...) puede dar al traste con todo un proyecto.  METICULOSIDAD,  MUCHO  ANÁLISIS  Y  REFLEXIÓN  EN  CADA  PASO,  MUCHAS  PRUEBAS  (Test‐Driven  Development, TDD) son menester en un proyecto.  No  crea  que  esta  dificultad  es  inherente  sólo  a  nuestro  campo.  La  parte  electrónica  sólo es más fácil porque hay una gran cantidad de funcionalidades de muy alto nivel y  gran complejidad incluida en circuitos integrados, tanto analógicos como digitales; de  frecuencia bajas y muy altas.  Por eso sostengo que hay que aceptar que nuestra forma de diseño ha cambiado: ahora hay  que saber INTEGRAR componentes, que pueden ser tarjetas completas, como la DEMOQE128, o  los Credit Card Computers, que por $100 nos colocan en las manos todo el poder de un PC  IBM/AT compatible, como los que compro en CompuLab http://compulab.co.il/   “The 486CORE you sent to us has arrived at this very moment to our office. I must  congratulate you and CompuLab: This is truly a beautiful piece of hardware.”  Luis G. Uribe, Vessing 2000     PROGRAMAS AVANZADOS EN ASSEMBLY LANGUAGE, PARTE III Hemos  cubierto  hasta  ahora  una  sección  introductoria,  donde  aprendimos  a  usar  el  conjunto  de  instrucciones  del  HCS08,  sus  modos  de  direccionamiento,  y  allí  hicimos  nuestras primeras aproximaciones a la programación de periféricos, encendiendo algunos  LEDs y leyendo los interruptores de la tarjeta DEMOQE128.  Aprendimos a usar el ambiente de desarrollo CodeWarrior y su debbuger, que nos permite  seguir paso a paso el programa directamente DENTRO del MCU, lo cual se fundamenta en la  123 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 nueva  versión  de  esta  familia  de  microcontroladores,  que  incluye  un  acceso  directo  a  los  registros  internos  del  MCU,  y  una  infraestructura  de  Debugging  que  permite  a  un  programa  externo  (CodeWarrior)  interactuar  con  los  recursos  internos  del  micro,  SIN  detener su funcionamiento ni casi interferir con él. Para hacer un trabajo similar en  otras familias de microcontroladores, se necesita adquirir una interfaz conocida como  ICE  (In  Circuit  Emulator)  que,  por  un  lado  reemplaza  al  micro  en  la  tarjeta  del  cliente, mediante un enchufe que tiene la misma forma del MCU y que por el otro extremo  se  conecta al  PC;  allí,  un  programa  SIMULA  el  comportamiento  del  micro,  genera  todos  los valores de voltaje en los pines que produciría el MCU original, lee las variables  externas, digitales o analógicas como si fuera el MCU y, desde luego que interactúa con  el  usuario,  puesto  que  no  hay  tal  MCU,  sino  una  versión  simulada  del  mismo.  Un  dispositivo como éste, dependiendo del manufacturante, puede costar entre $600 y $2000  que, como se verá, no necesariamente resulta barato.  Pues  ésta  familia  del  HCS08  incluye  dentro  del  mismo  chip  todos  los  recursos  que  permiten hacer exactamente lo mismo que con el ICE, y a un costo nulo.  En  los  ejercicios  un  poco  más  avanzados  aprendimos  a  crear  variables  dinámicas  en  el  stack, al estilo del C; probamos a descomponer los programas en subrutinas, y conocimos  el protocolo de llamado de las mismas:  CREAR EL STACK FRAME PARA SUBRUTINAS:  ‐ Guardar los parámetros en el Stack;  ‐ Reservar el espacio para las variables locales de la subrutina;  ‐ Hacer la llamada;  ‐ Inicializar las variables locales, si es el caso;  ‐ Ejecutar el código;  ‐ Almacenar el valor resultante (casi siempre un Byte) en el Acumulador, o en una  variable: externa, o Global, o definida mediante un apuntador.  REMOVER EL STACK FRAME ACTUAL Y RECUPERAR EL ANTERIOR  ‐ Eliminar tanto variables locales como parámetros  RETORNAR.  Empleando  variables  en  el  stack  pudimos  hacer  con  facilidad  programas  recursivos,  al  estilo de las torres de Hanoi, cálculo de factorial y similares.    Y... comenzamos la parte avanzada, donde aprendimos a programar una batería de Timers,  que además de resultarnos muy beneficiosos en nuestros laboratorios y en el proyecto,  nos sirvió para ver con detenimiento la forma como se diseña y construye SOFTWARE: Una  definición del problema, descomponiéndolo en funciones desde el nivel más alto, bajando  hasta  llegar  a  definir  las  operaciones  más  elementales  (Top  Down  Design);  y  la  codificación, que hicimos desde éstas funciones elementales hacia arriba, hasta llegar  a implementar el código de más alto nivel (Buttom Up Coding). Hicimos mucho esfuerzo en  mostrar el desarrollo de esa librería, descomponiéndola en Políticas y Mecanismos, para  definir e implementar por una lado, código que sólo tiene que ver con LO QUE SE VA A  124 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 HACER,  sin  tener  en  cuenta  cómo  se  lo  va  a  hacer  (si  van  a  hacerse  los  timers  con  retrasos,  con  temporizadores,  empleando  o  no  interrupciones...).  Y  por  último,  la  codificación de los Mecanismos, que sí dependen de cómo se va a hacer cada operación.  Pero  aún  allí,  puede  obligarse  al  programador  a  establecer  una  separación  entre  las  rutinas que TOCAN el hardware, y aquellas que no. Windows, por ejemplo, tiene una capa  de  software  que  separa  el  hardware  de  otro  software,  y  se  la  denomina  HAL:  Hardware  Abstraction Layer. Es muy importante lograr al máximo esa separación, de manera que si  se cambia el hardware no haya que tirar a la basura todo el software. Recuerdan haber  oído cada rato que el micro era un 486, un Pentium, un PRO, un MMX, un Quadracore? Y  eso ocurre con una velocidad pasmosa. Y también, que sus discos eran de 560 Mb, de 1Gb,  de 100 Gb, de 1 Terabyte, de estado sólido...  Así  que:  NO  separar  el  hardware  específico,  del  resto  del  código,  es  GARANTÍA  DE  SUICIDIO EN UNA EMPRESA.  Llegamos ahora al último aspecto que puede cubrirse en las horas reservadas para este  curso de Arquitectura del Computador, que ha hecho énfasis primordial en la UTILIZACIÓN  de los microcontroladores, más que en su diseño.  El periférico de comunicación serial (SCI en el MC9S08QE128) tiene varias ventajas para  el aprendizaje, por las cuales lo hemos incluido aquí.  En  PRIMER  lugar,  una  de  las  principales  funciones  que  realizan  los  MCUs  en  sistemas  embebidos  consiste  en  intercambiar  información  con  unos  con  otros,  y  con  sistemas  de  mayor jerarquía, como servidores, etc. Estudiar este periférico se enmarca dentro de la  importancia de la función.  SEGUNDO, cubre la gran mayoría de aspectos básicos que se deben considerar siempre al  utilizar  una  función  de  éstas:  manejo  de  los  periféricos  propiamente  dichos  (encenderlos,  apagarlos),  control  de  sus  líneas  de  habilitación  para  interrupción,  y  protocolo de intercambio de información: Data Ready, Done, donde el periférico produce  una  señal  de  que  hay  datos  listos  para  leerse,  y  el  MCU  le  da  un  reconocimiento  (Acknowledge) al periférico para que él pueda proceder correctamente con otro dato. Lo  mismo ocurre con las interrupciones: su habilitación, procesamiento, reconocimiento. Se  tocan  otros  aspectos  como  la  habilitación  de  interrupciones  DENTRO  de  rutinas  de  interrupciones,  a  pesar  de  todo lo  que  los  manuales  de  Freescale  hablan  en  contra...  Hay que aprender todo el protocolo de interrupción: qué guarda el micro en el Stack,  qué no (y que por lo tanto tiene que guardar el programador a mano), en qué orden se  guardan y se recuperan los valores almacenados, cómo se desactivan automáticamente las  interrupciones a nivel general del CPU, etc.    TERCERO:  Como  parte  del  protocolo  de  atención  se  aprende  a  manejar  el  vector  de  interrupciones, y se hace mucho énfasis en NO hacer la mayor parte del proceso dentro  de la rutina de interrupciones, sino en ‐‐por ejemplo‐‐ recibir un dato, almacenarlo en  algún lugar reservado e idóneo para ello, informar al programa principal de ese EVENTO,  quizás  activando  una  bandera  (flag),  y  retornar  rápidamente  al  código  al  que  se  interrumpió.  En  este  sentido  es  conveniente  incluir  el  manejo  de  Colas  de  Datos,  lo  cual  se  hace  precisamente aquí y ahora, para sincronizar los dos eventos, de por sí asíncronos entre  125 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ellos, que son los datos entrantes de un equipo o usuario externo, independiente, y el  MCU. Se indican variantes del método, mediante TABLAS, que pueden funcionar como Double  Buffers  cuando  se  procesan  datos  que  llegan  en  ráfagas,  como  es  el  caso  de  la  manipulación de discos, etc.  Todo lo anterior se integra, pues, en la siguiente sección.   COLAS DE DATOS Cuando  dos  sistemas  de  diferentes  velocidades  se  comunican  entre  sí,  de  manera  asíncrona, es decir, cuando no existe relación temporal definida, clara y precisa entre  ellos, es indispensable sincronizarlos o de lo contrario se perderán datos durante la  transferencia de información. Un ejemplo cotidiano es una persona empleando el teclado  para enviar información a su PC; no podrían ser más diferentes sus velocidades, y no  existe sincronía ni por asomo entre el usuario y el PC.  La  forma  de  sincronizarlos  varía  de  acuerdo  a  la  naturaleza  de  la  información  transmitida. Si el sistema más rápido puede darse el lujo de emplear buena parte de su  tiempo esperando que el usuario le transmita una letra, quizás basta con un elemento de  memoria en el que reposará la letra mientras se la procesa, antes de venir a leer el  siguiente  símbolo.  El  dispositivo  elemental,  unitario,  de  memoria,  suele  ser  un  registro  (Data  Buffer)  interpuesto  entre  el  dispositivo  Productor  de  la  información  (canal de comunicación con el usuario), y el Consumidor de la misma, en este caso, el  CPU del PC (Esta unidad de acople recibe el nombre de INTERFAZ; en inglés Interface. NO  SE TRADUCE AL CASTELLANO COMO INTERFACE!  Para  facilitar  la  vida  del  PC,  se  agrega  a  la  Interfaz  una  bandera,  el  bit  de  Data  Ready, que se activa cuando ha llegado una letra por el canal de información, producida  desde afuera del PC por el usuario al oprimir una tecla. Al leer la tecla, se repone a  cero el valor de la bandera, de tal manera que la unidad de comunicación sepa que ya  leyeron el dato que ella aportó.  A este protocolo, realizado manipulando ordenadamente la bandera de Data Ready, se lo  conoce como Hand‐Shaking (en inglés: apretón de manos).  El saludo o Hand‐Shaking verbalizado sería algo así como:  "Hola, tengo un dato". "Ah, un momento, ya lo recibo". "Listo, gracias y ciao". "Ciao".  Las  dificultades  comienzan  cuando  el  CPU  no  puede  bloquearse  esperando  a  que  llegue  cada  símbolo,  porque  eso  lo  pondría  a  trabajar  más  o  menos  a  la  velocidad  del  subsistema lento, en este caso, el usuario. Una alternativa es lograr que el programa  que el CPU va a estar corriendo entre letra y letra, se comporte como una especie de  CICLO de longitud indefinida que, entre las cosas que tiene que realizar, está la de  pasar a revisar si llegó o no una letra.  ¿Cuánto tiempo tiene el CPU para dar la vuelta y preguntar por una letra a la entrada,  antes de que comience a llegar OTRA LETRA, porque sobrescribiría parcial o totalmente  la  letra  anterior?  Pues  si  el  registro  de  entrada  es  un  "Shift  Register"  que  va  recibiendo uno a uno los N bits de cada letra, el CPU tiene que atender al periférico  ANTES de que transcurra UN TIEMPO DE BIT de comunicaciones. Así, llega el último bit de  126 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 un símbolo producido por el usuario en el teclado y se activa el bit de STATUS, Data  Ready. El CPU tiene que leerlo y actuar ANTES de que comience a llegar el primer bit  del segundo símbolo. Si se está en una transferencia continua de datos, se calcula el  tiempo necesario de reacción del CPU, como se lo conoce, infiriendo, de la velocidad de  comunicación,  el  tiempo  de  cada  bit.  Ese  es  la  cota  superior  o  límite  en  tiempo  que  puede pasar entre atención y atención al periférico, para garantizar que no se pierden  ningún símbolo recibido.  Una modificación que hace más llevadera la vida de los sistemas consiste en agregarle  un  Registro  Adicional  al  Serializador  de  Entrada  (el  serializador  que  es  el  que  va  recibiendo uno a uno los bits de la letra, y cuando tiene N ‐‐normalmente 8 bits para  nuestros ejemplos, siendo 8 el número más empleado en la actualidad‐‐ el controlador de  la interfaz lo transfiere a ese Data Buffer. Ahora el CPU tiene TODO UN TIEMPO DE BYTE  TRANSMITIDO,  entre  atención  y  atención  al  periférico.  Como  cada  byte  (8  bits)  puede  durar  10  tiempos  de  bit  en  transmitirse,  se  ha  disminuido  en  un  orden  de  magnitud  (dividido  por  10)  el  tiempo  permitido  al  CPU  para  atender  los  símbolos.  A  esta  combinación  de  serializador  alimentando  a  otro  registro,  que  es  el  registro  que  en  definitiva  OPERA  el  CPU,  se  lo  conoce  como  Double  Buffer  (porque  hay  2  registros  involucrados, o buffers).  Cuando la velocidad con la que la unidad de comunicaciones le entrega los datos al CPU  en algunas oportunidades, es mayor del tiempo libre, o de holgura, del procesador, éste  se ve en la necesidad de atender los datos con mayor agilidad. Y para facilitar este  intercambio,  se  inventó  el  mecanismo  conocido  como  INTERRUPCIONES,  que  consiste  en  lograr que el CPU responda rápidamente a cada letra pero, si por algún motivo no tiene  tiempo para procesar la información de entrada, tiene que moverla del double buffer a  la memoria.  Cuando la memoria de trabajo tiene un principio y un fin, como una tabla, se la conoce  como BUFFER de Entrada. Desde luego puede haber BUFFERS de Salida también (tablas). Si  la velocidad de transferencia de información es muy elevada, por ejemplo en la entrada,  se emplean DOS BUFFERS, y se los conmuta de tal manera de estar adquiriendo información  sobre  un  BUFFER,  mientras  se  está  procesando  el  otro.  A  esta  técnica  se  la  conoce  también  como  DOUBLE  BUFFERING,  y  se  distingue  de  la  anterior  por  la  cantidad  de  elementos  de  información  que  conforma  cada  buffer.  En  el  caso  mencionado  antes,  eran  solo  dos  elementos  o  registros:  el  serializador,  y  el  registro  buffer,  propiamente  dicho.  En  este  segundo  caso  (que  desafortunadamente  recibe  el  mismo  nombre,  Double  Buffer, o Buffering) el tamaño del buffer suele ser grande, entre 64 bytes para canales  de comunicación muy rápidos, hasta 512 bytes para unidades adaptadoras de discos, que  cuando dicen transferir información lo hacen de manera muy veloz.  Pero  hay  otra  alternativa,  frente  al  Double  Buffering,  que  consiste  en  el  empleo  de  COLAS o QUEUEs; algunas de ellas realizadas a nivel de circuitos integrados (FIFOs).  Una cola puede verse como una tabla de tamaño fijo, pero CIRCULAR, lo cual quiere decir  que si se van colocando datos en ella y se llega al final, el próximo dato SE COLOCARÁ  AL PRINCIPIO de la tabla otra vez. Esto produce la ilusión de que esa área de memoria  es  CIRCULAR,  pues  el  final  de  la  tabla  está  contiguo  al  principio,  como  si  fuera  un  anillo (o círculo) que no tiene fin.  El  mayor  uso  de  las  Colas  se  consigue  cuando  el  proceso  Productor  de  datos  puede  generar  a  veces,  ráfagas  de  información,  como  picos  de  transmisión,  pero  que  normalmente está por debajo de lo que podríamos llamar Comunicación Ininterrumpida, o  127 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 continua. Es así que la comunicación va a un paso dado, pero de pronto se acelera en  una ráfaga de una duración acotada, inferior a X caracteres. Si además el Consumidor va  extrayendo con cierta periodicidad los datos de la cola, y cuando lo hace lo hace muy  rápidamente (de la misma cola), la Cola presenta la imagen virtual de que es de mayor  capacidad operativa de su real capacidad física.  Uno de los aspectos oscuros del empleo de Colas es el establecimiento del tamaño de las  mismas, ya que su capacidad depende de la diferencia de velocidades entre Productor y  Consumidor.  Una  alternativa  consiste  en  simular  la  cola  en  el  PC,  alimentándola  y  extrayendo  información  de  ella  al  azar,  e  ir  incrementando  un  contador  que  determine  cuándo,  al  tratar  de  agregarse  un  elemento  a  la  cola,  ésta  estaba  llena,  lo  cual  conforma un error, y también, dependiendo del caso, si se quiso retirar un elemento de  información de la cola, y no se pudo debido a que ésta se encontraba vacía.  Una Cola, pues, es una Estructura de Datos que está materializada en la memoria como  una tabla, y emplea varios elementos accesorios para su correcta operación.  Se  requiere  una  variable  que  determine  el  tamaño  definido  para  la  cola,  'SIZE'(1)  en  nuestra descripción. (Nota: los números entre paréntesis corresponden a cada elemento  identificado de la misma manera en el próximo dibujo)  Es  cómodo  tener  un  pointer  que  señale  siempre  a  dónde  comienza  la  tabla:  BASE,  que  apunta a "BUF"(2).  Se  necesita  un  apuntador  que  indique  en  cuál  posición  de  la  tabla  se  colocará  la  próxima letra que llegue en secuencia, y haya que almacenarla. Este es el PUT pointer,  o simplemente "PUT"(3).  Se  necesita  otro  apuntador,  al  que  le  corresponde  señalar  cuál  es  la  letra  que  debe  salir  en  secuencia  de  la  cola,  cuando  se  haga  una  lectura  de  allí.  Este  es  el  GET  pointer, o sencillamente "GET"(4).  Es  cómodo  tener  otro  pointer  que  identifique  el  final  de  la  tabla:  LIMIT,  y  una  variable "N"(5), que indique la cantidad de bytes que almacena la Cola en un instante  dado. Para llevar la pista de cuántos bytes están almacenados en la cola, cada vez que  se incluya uno debe incrementarse el contador, y al momento de extraer un dato, debe  decrementarse  dicho  valor.  Así,  siempre  "N"(5)  contiene  la  cantidad  de  Símbolos  almacenados en un momento determinado.  En 'C', un elemento de información como el que acabamos de describir se representa de  manera natural como una STRUCT; la nuestra sería (tomada de mis rutinas de Colas para  el PC):  typedef  struct  {      byte  *put;                /*              n (5)               */      byte  *get;                /* (2) buf‐‐‐‐> +‐‐‐+               */      word   n;                  /*              |(1)| ‐‐‐> (4) get  */      word   c;                  /* (3) put ‐‐‐> | s |               */      byte  *base;               /*              | i |               */      byte  *limit;              /*              | z |               */      word   size;               /*              | e |               */  } QUE;                         /*              +‐‐‐+               */    128 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 En esta definición de QUE están los dos apuntadores a byte: *put y *get; el contador de  elementos que, para esta implementación es un word n (16 bits, con lo que la capacidad  máxima de las colas es de 64Kbytes); los apuntadores a byte, que indican el comienzo y  al final de la tabla: byte *base y byte *limit; una constante para cada cola, que es su  tamaño: word size; y finalmente una variable auxiliar para almacenar un símbolo de la  tabla temporalmente, word c (es word, no byte, porque igual que el símbolo EOF en la  librería estándar de C, se necesita un word para almacenarlo).  Una ilustración de los rangos de valores que pueden alcanzar las diferentes variables,  y la definición de cola llena y vacía es la siguiente:  Sea  'size'(1)  de  'buf'(2)  igual  a  5,  como  ejemplo.  Los  pointers  'put'(3)  y  'get'(4)  sólo pueden tener los valores:       buf, buf + 1, buf + 2, buf + 3, buf + 4;  es decir, los pointers deben permanecer en el rango:       buf  <=  pointer  <  (buf+size), o, lo que es lo mismo:       buf  <=  pointer  <  limit    'n'(5) puede valer:       0 (vacío), 1, 2, 3, 4 and 5 (lleno).  Note que 'n' es UN SEMÁFORO que impone las siguientes reglas de tráfico:       NO  'deQue' cuando n sea  <=  0       NO  'enQue' cuando n sea  >=  size    Con  estos  antecedentes  en  mente,  analicemos  mi  librería  de  colas  para  el  HC9S08,  escritas en Assembly Language:  25) BIBLIOTECA de COLAS: Que.inc   ["Laboratorios\Lab3\SciComm\Que.inc"]  001 ;********************************************************************  002 ; Que.inc: Queue Support (for SCI)  003 ; Luis G. Uribe C., C14M2007 C15A09 L08J09 J14J2012 (HCS08)  M05M2013  004 ; S30N2013 (defineQue & initQue now begin with Lower Case)  C18D2013  005 ;====================================================================  006 ; THE FOLLOWING MACROS ARE DEFINED IN THIS LIBRARY  007 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  008 ;defineQue: MACRO   size1;   ===>> Use 'defineQue' in **RAMstart ORG*  009 ;initQue:   MACRO   name1, size2; ==>>Use 'initQue' in *ROMstart ORG*  010 ;enQue:     MACRO   name1       ; enQ Accumulator into Que: 'name1'  011 ;deQue:     MACRO   name1       ; deQ Accumulator from Que: 'name1'  012 ;====================================================================  013 ; EXAMPLES OF USE  014 ;...  015 ; Global Variables  016 ;      ORG ram  129 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 017 ;inpQ: defineQue   6           ; Use macro 'defineQue' in 'ORG ram'  018 ;outQ: defineQue   5  019 ;...  020 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  021 ;      ORG rom  022 ;...  023 ;      initQue inpQ, 6         ; Use macro 'initQue' in 'ORG rom'  024 ;      initQue outQ, 5  025 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  026 ;      enQue   inpQ  027 ;       bcs    full_inpQ  028 ;...  029 ;      deQue   inpQ  030 ;       bcs    empty_inpQ  031 ;      enQue   outQ  032 ;       bcs    full_outQ  033 ;...  034 ;====================================================================  035 ; DATA DEFINITION.  036 ;  037 ; NOTE: This is ONE way to work with >>>STRUCTURES<<< in Assembler...  038 ; References to internal RAM struct positions are done via symbols:  039 ; The following EQUs are defined to help us in locating the different  040 ; ..variables used in Queue's struct; i.e:  041 ; _QN:      BYTE: unsign number of chars stored any time in one Queue  042 ; _QPUT:    WORD: pointer used to store the next char, vía enQue;  043 ; _QGET:    WORD: pointer to deQue one char.  044 ; _QSIZE:   BYTE: static space reserved for Queue;  045 ; _QBASE:   WORD: address of the first char to be stored in the  046 ;                 ..reserved memory of each Queue;  047 ; _QLIMIT:  WORD: last element's address in a Queue; used in wrap  048 ;                 ..around process, when a pointer crosses the border  049 ; _QBUF:   BYTES: here begins RAM space for characters to be stored  050 _QN:       EQU  0              ;  BYTE: data bytes counter N  051 _QPUT:     EQU  1        ;  WORD: pointer for storing data into Queue  052 _QGET:     EQU  3        ;  WORD: pointer for getting data from Queue  053 _QSIZE:    EQU  5              ;  BYTE: sizeof  054 _QBASE:    EQU  6              ;  WORD: base address of Queue struc  055 _QLIMIT:   EQU  8              ;  WORD: last address of buffer  056 _QBUF:     EQU 10              ; BYTES: reserve 'size' data bytes    057 ;====================================================================  058 ; "defineQue" is used with 1 parameter: SIZE of the Queue.  059 ; ..defineQue RESERVES RAM space, and *MUST* be used in RAMspace ORG.  060 ;                                ===>> ^^^^ <<<===  061 ; To use "defineQue", you  *MUST*  begin it with a LABEL: This *IS*  062 ; ..the NAME of your QUEUE; i.e:  063 ;  064 ;**********************  065 ;inpQ:    defineQue 6 *  066 ;**********************  067 ; NOTE: It is clever to define data bytes ram space, the last...  130 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 068 defineQue: MACRO   size1;  ===>> Use 'defineQue' in **RAMstart ORG**  069 ;                          ===>>===>===>===>===>===>> ^^^^^^^^ <<<===  070        DS  _QPUT    ‐  _QN     ;     n: data bytes counter N  071        DS  _QGET    ‐  _QPUT   ;   put: ptr for STORE data into Queue  072        DS  _QSIZE   ‐  _QGET   ;   get: ptr for GET   data from Queue  073        DS  _QBASE   ‐  _QSIZE  ;  size: sizeof  074        DS  _QLIMIT  ‐  _QBASE  ;  base: base address of Queue struc  075        DS  _QBUF    ‐  _QLIMIT ; limit: last address of buffer  076        DS  \1                  ;   buf: reserve 'size' data bytes  077    ENDM    078 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  079 ; "initQue" is used with 2 parameters: NAME of the Queue, AND SIZE.  080 ; initQue STORES information in the Queue's RAM space, and  081 ; .. *MUST* be used in ** ROMspace ORG **  082 ;==>> ^^^^ <<<===  083 ; For initQue you must write SAME parameters used on defineQue; i.e:  084 ;  085 ;******************  086 ; initQue inpQ, 6 * 'inpQ' was Queue's NAME in defineQue; size was 6  087 ;******************  088 ;  089 ; ALL references to internal RAM positions are done via the symbols  090 ; .. _QN,  _QPUT,  _QGET,  _QSIZE,  _QBASE,  _QLIMIT  and  _QBUF  091 ; ..Note that this is ONE way to work with STRUCTURES in assembler...    092 initQue:   MACRO   name1, size2; ==>> Use 'initQue' in *ROMstart ORG*  093                                ; ==>>====>====>======>> ^^^^^^^^ <<==  094        clra                    ; mark Queue as empty (n = 0)  095         sta    (\1 + _QN)      ; 'sta', not 'clr OP' cause OP could  096                                ; ..reside in 16 bit address space  097        ldhx   #(\1 + _QBUF)  098         sthx   (\1 + _QBASE)  099         sthx   (\1 + _QPUT)  100         sthx   (\1 + _QGET)  101        lda    #\2  102         sta    (\1 + _QSIZE)  103        aix    #\2  104         sthx   (\1 + _QLIMIT)  105    ENDM  106 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  107 ; "enQue" stores the content of ACCumulator in the designated QUEUE  108 ;  109 ;********************************************************************  110 ;* You may program other usefull macros such as: enQueK for dealing *  111 ;* ..with constants, enQueV to enQue variables, etc It's up to you  *  112 ;********************************************************************  113 ;  114 ; If there is NO space for storing some character, the macro signals  115 ; ..it by setting the C bit on CCR, so the invoking program may  116 ; ..know that the Queue is full, and take actions...  117 ;  131 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 118 ; NOTE: enQue is NOT a blocking action...  119 ;  120 ;******************  121 ;   enQue   outQ; *    enQ Accumulator into Queue named outQ  122 ;******************  123 enQue:     MACRO   name1       ; enQ Accumulator INTO Queue: 'name1'  124        psha  125        lda     (\1 + _QN)      ; Verify that there is room (n < SIZE)  126        cmp     (\1 + _QSIZE)  127        pula  128        blo     \@ok  129        sec                     ; c = 1 signal error: Queue full  130        bra     \@exitQ  131 \@ok:  ldhx    (\1 + _QPUT )   ; *put++ = accumulator  132        sta     ,x              ; ..  133        aix    #1               ; ..  134        sthx    (\1 + _QPUT )   ; ..  135        cphx    (\1 + _QLIMIT)  ; ..  136        blo     \@incn          ; put pointer inside que? ok  137        ldhx    (\1 + _QBASE)   ; else: rollover: reasign put to BASE  138        sthx    (\1 + _QPUT )   ; ..  139 \@incn:  140        clc                     ; c = 0: enQue was succesfull  141        ldhx   #(\1 + _QN)      ; n++. This is the LAST thing to be  142        inc     ,x              ; <<< <<< ..done: 'n' IS the semaphore  143 \@exitQ:  144    ENDM  145 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  146 ; "deQue" retreives data from the designated Queue and stores it in  147 ; ..the ACCumulator.  148 ;  149 ;********************************************************************  150 ;* You may program other macros, such as: deQueV for retreive       *  151 ;* ..data and store it on variables, etc. It is up to you           *  152 ;********************************************************************  153 ;  154 ; If there is NO data for retrieving, the macro signals it by SETting  155 ; ..the C bit on CCR, so the invoking program may know that the Queue  156 ; ..is empty, and take actions...  157 ;  158 ; NOTE: deQue is NOT a blocking action...  159 ;  160 ;******************  161 ;   deQue   inpQ; *    deQue from Queue named inpQ into Accumulator  162 ;******************    163 deQue:     MACRO   name1       ; deQ: Accumulator FROM Queue: 'name1'  164        lda     (\1 + _QN)      ; Verify that que is not empty (n > 0)  165        bne     \@ok  166        sec                     ; c = 1 signal error: Queue full  167        bra     \@exitQ  168 \@ok:  ldhx    (\1 + _QGET )   ; accumulator = *get++  132 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 169        lda     ,x              ; ..  170        aix    #1               ; ..  171        sthx    (\1 + _QGET )   ; ..  172        cphx    (\1 + _QLIMIT)  ; ..  173        blo     \@decn          ; get pointer inside que? ok  174        ldhx    (\1 + _QBASE)   ; else: rollover: reasign get to BASE  175        sthx    (\1 + _QGET )   ; ..  176 \@decn:  177        clc                     ; c = 0: enQue was succesfull  178        ldhx   #(\1 + _QN)      ; n‐‐. This is the LAST thing to be  179        dec     ,x              ; <<< <<< ..done: 'n' IS the semaphore  180 \@exitQ:  181    ENDM    COMENTARIOS a ["Laboratorios\Lab3\SciComm\Que.inc"]:   Se le ofrecen al usuario 4 funciones principales:    Para cada Cola se precisa hacer una definición ANTES de invocar cualquier otra función  relacionada:    008 ;defineQue: MACRO   size1;   ===>> Use 'defineQue' in **RAMstart ORG*    Ejemplo:    inpQ: defineQue   6    Se define una Cola de 6 elementos (en esta implementación todos los elementos son BYTES  pero,  desde  luego,  usted  puede  emplear  otros  valores,  como  enteros,  Longs,  y  otros  Tipos de Datos. Pero esto es preferible (como casi todo) programarlo en C.    NOTA IMPORTANTE:    'defineQue' debe usarse habiendo previamente abierto un DATA SECTION: 'ORG ram', porque  'defineQue' reserva espacio en RAM para los apuntadores y demás variables relacionadas,  así como también para la tabla circular propiamente dicha.    Para  cada  cola  se  precisa,  además,  inicializar  apropiadamente  sus  apuntadoras  y  variables relacionadas; esto se hace con:    Ejemplo:           initQue     inpQ, 6    NOTA IMPORTANTE:    'InitQue'  sólo  debe  emplearse  estando  en  CODE  SECTION,  es  decir,  habiendo  hecho  previamente un 'ORG rom', ya que es la parte que genera código ajustado a cada cola en  particular.    133 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Finalmente están las funciones de uso rutinario, para almacenar y extraer datos de una  cola, las Macros enQue y deQue:    010 ;enQue:     MACRO   name1       ; enQ Accumulator into Que: 'name1'  011 ;deQue:     MACRO   name1       ; deQ Accumulator from Que: 'name1'    Cada  Macro  hace  uso  del  dato  que  está  previamente  guardado  en  el  Acumulador.  A  continuación, un ejemplo que cubre todo lo anterior:    Para definir las Colas, hay que abrir un DATA SECTION (org RAM) y luego llamar la Macro  defineQue, tantas veces como Colas quieran definirse:    015 ; Global Variables  016 ;      ORG ram  017 ;inpQ: defineQue   6           ; Use macro 'defineQue' in 'ORG ram'  018 ;outQ: defineQue   5    Note que el NOMBRE de las colas, inpQ y outQ, son ETIQUETAS de la Macro defineQue.    Cuando van a inicializarse las colas, hay que definir un CODE SECTION (org ROM):    019 ;...  021 ;      ORG rom  022 ;...  023 ;      initQue inpQ, 6         ; Use macro 'initQue' in 'ORG rom'  024 ;      initQue outQ, 5    Note que el nombre de las Colas NO es un nuevo "Label", sino que las etiquetas que se  emplearon en la Definición de las Colas, se usan ahora como PARÁMETROS de las macros de  Inicialización: inpQ y outQ.    El ejemplo encola un char, almacenado en el Acumulador, en la cola de entrada, inpQ;  más adelante, otra sección del código (seguramente una rutina que transmite los datos  que  se  han  encolado),  saca  elementos  almacenados,  posiblemente  los  preprocesa  y,  finalmente, los encola en outQ para que, probablemente, la rutina de interrupciones que  se encarga de transmitir información, la envíe al PC, por ejemplo.    025 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  026 ;      enQue   inpQ  027 ;       bcs    full_inpQ  028 ;...  029 ;      deQue   inpQ  030 ;       bcs    empty_inpQ  031 ;      enQue   outQ  032 ;       bcs    full_outQ  033 ;...    "STRUCT" in ASSEMBLY LANGUAGE Primero hay que volver a mirar los elementos que componen este elemento de información,  porque no todos sus componentes son exactamente como en el ejemplo en C. Los tamaños en  nuestra implementación actual son:  134 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   041 ; _QN:      BYTE: unsign number of chars stored any time in one Queue    Fíjense que en esta implementación, una Cola puede identificar máximo 256 caracteres,  pues 'N' mide solo un byte.    042  ;  _QPUT:        WORD:  pointer  used  to  store  the  next  char,  via  enQue;  043  ;  _QGET:     WORD: pointer to deQue one char.    044 ; _QSIZE:   BYTE: static space reserved for Queue;    Aquí, de nuevo, el tamaño no corresponde al que puede definirse en un entero, sino en  un byte.    045  ;  _QBASE:      WORD:  address  of  the  first  char  to  be  stored  in  the  046  ;             ..reserved  memory  of  each  Queue;  047  ;  _QLIMIT:    WORD:  last  element's  address  in  a  Queue; used in wrap 048 ;                 ..around process, when a pointer crosses the  border 049 ; _QBUF:   BYTES: here begins RAM space for characters to be stored    Con estas definiciones concretas, definimos ahora el valor de cada símbolo, utilizando  la pseudo instrucción EQU:    050 _QN:       EQU  0              ;  BYTE: data bytes counter N    _QN representa un Offset, o desplazamiento de 0, en nuestra 'STRUCT'.    Como _QN mide un BYTE (es 'N', el contador de bytes que hay en un momento determinado  en la Cola), y todo se mide en Bytes, el siguiente elemento está UN (1) BYTE después de  _QN; por eso el EQU 1:    051 _QPUT:     EQU  1        ;  WORD: pointer for storing data into Queue  _QPUT, como es un apuntador, tiene que medir 16 bits (DOS [2] bytes), pues en el HC9S08  todas  las  direcciones  deben  ser  capaces  de  discriminar  entre  65536  posiciones  de  memoria.  Por  eso,  el siguiente  elemento  está  separado de  _QPUT  por  DOS  (2) unidades.  Como ya llevábamos UNO (1), el siguiente es 1+2=3:    052 _QGET:     EQU  3        ;  WORD: pointer for getting data from Queue    Igual ocurre con el siguiente, que se separa por DOS (2) del anterior, y 3+2=5:    053 _QSIZE:    EQU  5              ;  BYTE: sizeof    _QSIZE  mide  UN  (1)  byte,  así  que  el  próximo,  _QBASE,  se  separa  de  éste  por  UNA  (1)  posición, lo que da: 5+1=6:    054 _QBASE:    EQU  6              ;  WORD: base address of Queue struc    El próximo se separa por DOS (2), que es lo que mide _QBASE, dando 6+2=8:    055 _QLIMIT:   EQU  8              ;  WORD: last address of buffer    135 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 y,  finalmente,  el  buffer  COMIENZA  separado  por  DOS  (2),  que  es  lo  que  mide  _QLIMIT,  dando 8+2=10:    056 _QBUF:     EQU 10              ; BYTES: reserve 'size' data bytes    OBSERVACIÓN NOTABLE:    La posición en que coloqué el comienzo de la tabla, _QBUF, ES LA MEJOR POSICIÓN PARA  ALOJAR  LA  TABLA  en  esta  definición.  ¿Qué  pasaría  si  la  hubiéramos  incluido  entre  los  símbolos _QGET y _QSIZE, o entre otros cualesquiera? ¡PIÉNSELO! "It is clever to define  data bytes ram space, the last..."    Hechas todas estas parametrizaciones se las emplea para finalmente DEFINIR LA COLA EN  RAM, mediante la:    MACRO DEFINEQUE:    068 defineQue: MACRO   size1;  ===>> Use 'defineQue' in **RAMstart ORG**  069 ;                          ===>>===>===>===>===>===>> ^^^^^^^^ <<<===  070        DS  _QPUT    ‐  _QN     ;     n: data bytes counter N  071        DS  _QGET    ‐  _QPUT   ;   put: ptr for STORE data into Queue  072        DS  _QSIZE   ‐  _QGET   ;   get: ptr for GET   data from Queue  073        DS  _QBASE   ‐  _QSIZE  ;  size: sizeof  074        DS  _QLIMIT  ‐  _QBASE  ;  base: base address of Queue struc  075        DS  _QBUF    ‐  _QLIMIT ; limit: last address of buffer  076        DS  \1                  ;   buf: reserve 'size' data bytes    Fíjense  cómo  cada  variable  usa  el  número  de  bytes  apropiado,  empleando  DS  (Data  Storage) y la cantidad que resulta de calcular el número de bytes que resulta haciendo:  siguiente símbolo menos actual, como en:    070        DS  _QPUT ‐ _QN     ;     n: data bytes counter N  Se habrían podido colocar estas cantidades a mano, pero me tomé la molestia de hacerlo  mediante parámetros que me permiten, si quiero cambiar algo como el tamaño de la cola  (pero NO queremos) para que fuera de hasta 64 Kbytes, con sólo modificar un símbolo (o  dos...) el resto se reacomoda automáticamente.    Es  EVIDENTE  que  en  una  máquina  tan  pequeña,  NO  vamos  a  usar  colas  de  tamaño  mayor  a  256, que además son suficientes en MUCHOS casos, incluso para máquinas más grandes. Así  que este ejemplo sólo está codificado así, para que ustedes vean la manera profesional  de hacerlo: PARAMETRIZADO.    Así, espero que hayan aprendido LA MANERA de trabajar estructuras en Assembly Language.      MACRO INITQUE:    092 initQue:   MACRO   name1, size2; ==>> Use 'initQue' in *ROMstart ORG*  094        clra                    ; mark Queue as empty (n = 0)  095         sta    (\1 + _QN)      ; 'sta', not 'clr OP' cause OP could  096                                ; ..reside in 16 bit address space    136 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Cuando se comienza a trabajar con una Cola, los apuntadores a la BASE y también los de  PUT y GET deben señalar todos al PRINCIPIO DE LA TABLA Circular. Note que la expresión  #(\1  +  _QBUF)  representa:  una  CONSTANTE  (#),  igual  a  la  base  de  la  Estructura  de  la  Cola  ("\1",  el  primer  parámetro)  MÁS  el  desplazamiento  a  donde se  comienza  la  tabla:  _QBUF)    097        ldhx   #(\1 + _QBUF)  098         sthx   (\1 + _QBASE)  099         sthx   (\1 + _QPUT)  100         sthx   (\1 + _QGET)    La  inicialización  de  los  otros  dos  parámetros  es  trivial  (¿o  no?  Si  no  está  claro,  escriba  TODAS  las  definiciones,  y  verá  lo  que  hace  el  AIX  #\2;  verifique  lo  que  se  cargó antes en el registro índice H:X):    101        lda    #\2  102         sta    (\1 + _QSIZE)  ... ... ...  103        aix    #\2  104         sthx   (\1 + _QLIMIT)      Finalmente, revisemos cómo se codificaron las Macros para el manejo normal de la Colas:  enQue y deQue (encolar y decolar...).    "enQue": Almacena el contenido del Acumulador en la Cola requerida:  El problema cuando se va a encolar algo, es que NO haya espacio para recibir un nuevo  valor. Si fuera así, la Macro lo SEÑALA colocando en UNO el bit de carry, C, en el CCR;  así,  el  programa  que  invoca  la  Macro  puede  saber  si  la  cola  estaba  llena,  y  tomar  acciones.  Nótese  que  la  manera  como  codifiqué  la  Macro  enQue  NO  ES  DE  MANERA  BLOQUEADORA, lo cual resulta muy conveniente... Usted simplemente encola algo y luego  pregunta si se hizo bien, mirando el bit de Carry, C. Ahí usted toma la decisión que  corresponda.    123 enQue:     MACRO   name1       ; enQ Accumulator INTO Queue: 'name1'    Guarda el valor del Acumulador, en el Stack:    124        psha    Verifica que haya espacio (que n sea < SIZE):    125        lda     (\1 + _QN)      ; Verify that there is room (n < SIZE)  126        cmp     (\1 + _QSIZE)  127        pula  128        blo     \@ok    Si SÍ hay espacio en la Cola, continua en la etiqueta marcada como: \@ok; de NO haber  espacio, se activa la bandera C (Carry), y termina:    129        sec                     ; c = 1 signal error: Queue full  130        bra     \@exitQ  137 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   Habiendo  verificado  que  SÍ  hay  espacio,  carga  la  dirección  para  ENCOLAR  (según  el  pointer PUT), en el registro índice H:X, y usando el direccionamiento indexado almacena  el nuevo dato, tomándolo del Acumulador:    131 \@ok:  ldhx    (\1 + _QPUT )   ; *put++ = accumulator  132        sta     ,x              ; ..    Una  vez  guardado  el  dato,  se  incrementa  el  apuntador  (aix  #1),  para  que  señale  a  la  próxima posición que recibirá un dato, y se guarda ese nuevo valor, incrementado, en la  posición de memoria que reservada para almacenar ese apuntador:    133        aix    #1               ; ..  134        sthx    (\1 + _QPUT )   ; ..    Compara ese NUEVO valor, para ver si se apunta fuera del área asignada:    135        cphx    (\1 + _QLIMIT)  ; ..  136        blo     \@incn          ; put pointer inside que? OK    Si  el  apuntador  contiene  una  dirección  que  queda  FUERA  del  área  asignada,  hay  que  reiniciarlo, para que apunte a la BASE (ROLLOVER; este mecanismo es el que la cola sea  CIRCULAR):    137        ldhx    (\1 + _QBASE)   ; else: rollover: reasign put to BASE  138        sthx    (\1 + _QPUT )   ; ..    Si el apuntador señala correctamente dentro del área asignada, se borra la bandera C,  Carry, para indicar así que TODO ESTÁ BIEN:    139 \@incn:  140        clc                     ; c = 0: enQue was succesfull      Luego viene la parte quizás MÁS DIFÍCIL DE COMPRENDER, CONCEPTUALMENTE:    Como "N" es el MUTEX (semáforo binario),    === lo >>>ÚLTIMO<<< que hay que hacer al almacenar algo en la Cola es >>>INCREMENTAR  "N"<<<;    hacerlo  ANTES  puede  deshacer  la  magia  del  semáforo,  que  probablemente  ya  no  nos  resguarde contra inconsistencias.    Supongamos, por ejemplo, que la Cola está VACÍA y "Main" estuviera en el proceso del  "enQue", tratando de ENCOLAR un símbolo. Si alguna rutina irrumpe tratando de DECOLAR  algo para, por ejemplo, transmitirlo, y "N" le indica que SÍ hay información en la Cola  (porque  se  incrementó  "N",  por  ejemplo,  al  principio  de  la  Macro,  ANTES  de  que  en  realidad se hubiera almacenado nada), pues el "deQue" de la ISR LEERÍA esa información,  ¡QUE NO EXISTE!    138 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 CAOS garantizado...    Esto  es  sólo  un  DETALLE,  pero  es...  ¡FUNDAMENTAL!  No  es  SENCILLO  de  analizar,  y  es  MUCHO MÁS DIFÍCIL DE... CODIFICAR.    141        ldhx   #(\1 + _QN)      ; n++. This is the LAST thing to be  142        inc     ,x              ; <<< <<< ..done: 'n' IS the semaphore    La  explicación  sobre  "deQue"  resulta  algo  así  como  el  DUAL  de  la  que  acabo  de  hacer  sobre  "enQue",  y  por  eso  no  voy  a  presentar  aquí  más  comentarios.  Se  sigue  la  misma  norma con relación al bit C: Carry, que si está EN 0 representa que todo fue BIEN, y EN  1, indica que hubo un ERROR (fue a extraerse algo de la cola, y ésta estaba VACÍA...).    El problema en esta parte es similar al anterior: Supongamos, por ejemplo, que la Cola  está  LLENA  y  "Main"  estuviera  en  el  proceso  del  "deQue",  tratando  de  DECOLAR  un  símbolo. Si alguna rutina irrumpe tratando de ENCOLAR algo, por ejemplo lo que acaba de  leer  del  receptor,  y  "N"  le  indica  que  SÍ  hay  espacio  en  la  Cola  (porque  Main  incrementó  "N",  por  ejemplo,  al  principio  de  la  Macro  deQue,  ANTES  de  en  realidad  haberlo leído (cambió el orden que en realidad hay que seguir), pues el "enQue" de la  ISR PERMITIRÍA ALMACENAR SU información, EN UNA POSICIÓN QUE AÚN NO HA SIDO LEÍDA POR  MAIN!    CAOS garantizado de nuevo...    Este  es  otro  DETALLE  pequeño  pero  igualmente...  ¡FUNDAMENTAL!  Complicado  de  analizar  y... MUCHO MÁS DIFÍCIL DE CODIFICAR.    Verifique  con  MUCHO  detenimiento  en  el  código,  que  tampoco  aquí  existe  JAMÁS  un  comportamiento  indebido,  como  que  la  rutina  de  interrupciones  almacene  algo  sin  que  haya espacio dónde colocarlo...  NOTA: La codificación del bit de Carry, C, se usa en el lenguaje "C" desde el comienzo  de  los  tiempos,  para  retornar  CERO  si  una  función  SÍ  operó  bien,  y  UNO  si  NÓ  operó  normalmente. Así se comportan, por ejemplo, las funciones de bajo nivel de la sección  correspondiente a <io.h>.    Esas cosas usted ha debido aprenderlas en sus cursos de Programación. Si no fue así, lo  estafaron.    Cuando termine de ponderar la nitidez del código, trate de hacer nuevamente un Examen  de Conciencia, y diga si usted hubiera podido pensar en toda la complejidad subyacente  en  las  interacciones  que  pueden  ocurrir  asincrónicamente,  entre  un  Main  tratando  de  Encolar, y una rutina de interrupciones tratando de Decolar... o viceversa, SIN QUE SE  PUEDA JAMÁS EXTRAER DE LA COLA ALGO QUE NO HAY, O ENCOLAR ALGO EN UNA POSICIÓN DE LA  QUE AÚN NO SE HA DESOCUPADO!      26) Programa Elemental para Probar la Librería de COLAS Programa muy sencillo, que encola en "inpQ" una secuencia de caracteres consecutivos,  comenzando con la letra 'A', y finaliza cuando "inpQ" se llena. A continuación procede  a  extraerlos  de  "inpQ"  y  los  va  trasladando  a  "outQ",  hasta  que  "inpQ"  se  vacíe  o  139 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 "outQ"  se  llene,  lo  primero  que  ocurra.  El  comportamiento  debe  verificarse  con  el  Debugger, siguiendo paso a paso el programa.    ["Laboratorios\Lab3\SciComm\4quetst.asm"]  01 ;********************************************************************  02 ; Programa 4QueTst.asm: Test Que Support (for SCI)  03 ; Luis G. Uribe C., M13M2007 C15A09 L08J09 S16J2012 (HCS08)  04 ;  05 ; See in this program: The use of Que.inc, defineQue, initQue, enQue  06 ; ..and deQue. Tray and code the program using RCV interrupts, using  07 ; ..a 'Queue' for temporally storing the incoming data.  08 ; You must be carefully and not exceed the free RAM on the chip...  09 ;  10 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  11 ; Include files that do not define ram  12 ;  13 ; CHECK‐LIST: Conserve this order, and the relative position of:  14 ; ..'derivative.inc' and 'Que.inc'  15 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 ; Include files that does not define ram  17        NOLIST  18          INCLUDE 'derivative.inc'  19          INCLUDE 'Que.inc'     ; <<<=== Que.inc file does NOT use RAM  20        LIST                    ; ..storage, and THIS is it's place  21 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  22 ; 2) DEFINES    23 rom:   SET         ROMStart    ; $2080  24 ram:   SET         Z_RAMStart  ; $80  25 initStack:     EQU RAMEnd + 1  ; $1800=$17FF+1. Stack begins on $17FF  26 COP_Disable:   EQU $42  27 ;====================================================================  28 ; 3) Global Variables  29        ORG ram    30 inpQ:  defineQue   6           ; Use macro 'defineQue' in 'ORG ram'  31 outQ:  defineQue   5  32 ;********************************************************************  33 ; 4) MAIN PROGRAM HEADER:  34        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  35        ORG rom    36 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  37         sta    SOPT1           ; ..System Options 1  38        ldhx   #initStack       ; Set up SP  39         txs                    ; ...  40 ;====================================================================  41        initQue     inpQ, 6     ; Use macro 'InitQue' in 'ORG rom'  42        initQue     outQ, 5    43        lda    #'A'  44    cont:   enQue   inpQ  140 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 45            bcs     full  46            inca  47            bra     cont    48 full:  49    cont2:  deQue   inpQ  50            bcs     empty  51            enQue   outQ  52            bcs     full2  53            bra     cont2  54 empty:  55 full2:     bra     *  56        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  57        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  58                            ; This 'nop' MAY be removed for CW 6.3 ...  59 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  60 ; Interrupt Vectors    61        ORG     Vreset  62         DC.W   Main            ; RESET. Maximum priority. Asynch.  63        END    COMENTARIOS a ["Laboratorios\Lab3\SciComm\4quetst.asm"] El que estudia este texto hará bien en la recomendación de codificar el programa usando  interrupciones RCV y una 'Queue' para almacenar temporalmente la información que llega.    Como siempre, vienen:    11 ; Include files that do not define ram  Esto es IMPORTANTE:    13 ; CHECK‐LIST: Conserve this ORDER, and the relative position of:  14 ; ..'derivative.inc' and 'Que.inc'    Este ejercicio sólo tiene las siguientes (Global) Variables en RAM (defineQue). Usted  DEBE  jugar  y  cambiar  los  tamaños  de  cada  cola  para  ver  cómo  se  modifica  el  comportamiento del programa.    29        ORG ram  30 inpQ:  defineQue   6           ; Use macro 'defineQue' in 'ORG ram'  31 outQ:  defineQue   5    Después de la DEFINICIÓN, al comienzo se hace la inicialización:    41        initQue     inpQ, 6     ; Use macro 'InitQue' in 'ORG rom'  42        initQue     outQ, 5    El programa enviará letras consecutivas, comenzando en la 'A', hasta que la cola "inpQ"  se llene.    Inicializa el acumulador:  141 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 43        lda    #'A'    El ciclo que encola hasta que inpQ se llene:  44    cont:   enQue   inpQ  45            bcs     full    Incrementar una letra produce la siguiente: La 'A' + 1, produce 'B':  46            inca  47            bra     cont    Cuando  la  cola  "inpQ" se  llene se  extraen otra  vez  de "inpQ"  y se  los  traslada  a  la  cola  "outQ",  hasta  que  "inpQ"  se  vacíe  o  hasta  que  "outQ"  se  llene.  Verificar  el  comportamiento del programa con el Debugger, siguiéndolo paso a paso.    48 full:  49    cont2:  deQue   inpQ  50            bcs     empty  51            enQue   outQ  52            bcs     full2  53            bra     cont2    Se finaliza con la consabida imitación del HALT:    55 full2:     bra     *    La  modificación  que  se  sugiere  hacer  al  programa  consiste  en  colocar  una  Cola  para  recibir por interrupciones lo que llega del PC. La rutina de interrupciones (a la cual  se  llega  luego  de  las  habilitaciones  respectivas,  y  porque  un  símbolo  llegó  por  la  línea  de  comunicaciones,  y  así  generó  una  interrupción  de  RCV),  toma  la  letra,  la  ENCOLA y se devuelve (RTI).    El programa principal, Main, se la pasa leyendo de la cola mientras ésta se encuentra  vacía;  y,  cuando  llega  a  encontrarla  NO  vacía,  DECOLA  una  letra  y  la  devuelve  al  PC  (INCREMENTADA como ya vimos), usando la Macro PUTCHAR; luego regresa al ciclo infinito.    AGREGO: Cuando eso funcione, trate de colocar dos (2) colas, una para recibir lo que  llega del PC, como acabamos de ver, y otra para almacenar lo que se va a transmitir; es  decir,  la  parte  de  código  que  antes  hacía  PUTCHAR,  ahora  hace  "enQ  outQ"  y,  LUEGO,  HABILITA LAS INTERRUPCIONES de salida.    La rutina de interrupciones de salida, a la cual se llega cuando se pueda, si es que el  periférico transmisor está disponible (previas TODAS las habilitaciones necesarias)...  DESENCOLA  ("deQ  outQ")  si  ve  que  NO  ESTABA  VACÍA  transmite  vía  PUTCHAR.  Si  la  cola  estaba vacía, la rutina de transmisión se AUTO‐DESHABILITA para interrupciones. ESTO ES  ¡IMPORTANTE!    Y en esas se la pasan.    La idea es que usted aprenda a usar los mecanismos de recepción y transmisión, tanto de  manera  programada  como  por  interrupciones,  usando  como  vehículo  para  aprender,  las  comunicaciones seriales con el PC. Así, estará preparado para sus proyectos... hoy, y  luego como graduado.  142 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08  COMUNICACIONES SERIALES   ESTE ES EL CULMEN DE LA PRESENTACIÓN; AQUÍ CONFLUYEN TODAS LAS TÉCNICAS APRENDIDAS; el  periférico de comunicaciones no es TAN simple de estudiar pero, desde luego, es mucho  más  sencillo  que  los  stacks  TCP/IP  y  Bluethoot,  o  algunos  otros  periféricos  de  comunicaciones sincrónicas, o el I2C y el SPI (Serial Peripheral Interface, with full‐ duplex  or  single‐wire  bidirectional;  double‐buffered  transmit  and  receive;  master  or  slave mode; MSB‐first or LSB‐first shifting...)    Este NO ES UN TRATADO DE COMUNICACIONES SERIALES. El lector está avisado de que debe  revisar  documentos  publicados  en  relación  al  tema.  Yo  mismo  incluí  el  PDF:  "SerialComm16F84A(UribeChap14RS232)" en la Página de mi curso en Asignaturas de la USB.  Y en el Internet hay multitud de documentos relacionados.    Usted  tiene  que  aprender  por  su  cuenta,  en  otra  parte,  cuáles  son  los  voltajes  empleados en las transacciones RS‐232F; las velocidades; la distancia de los cables y  el  tipo,  los  conectores;  el  nombre  y  significado  de  las  señales  como  DTR,  DSR,  RTS,  CLS,  Carrier  Detect,  TX,  RX,  Modem,  Null  Modem,  dónde  van  los  conectores  hembra,  y  donde  los  conectores  macho;  Gender  Exchanger;  lo  que  es  comunicación  Full  y  Half  Duplex;  Simplex.  Start  bit,  Stop  bit,  técnicas  de  lectura  sobre  la  señal  de  entrada  para garantizar 5% de holgura en el oscilador local de recepción, etc.    Tiene que leer algún texto para esto, como ya dije.     27) LIBRERÍA de COMUNICACIONES SERIALES: SciComm.inc Repito mi objetivo primordial: Presentar la Metodología de Programación que define lo  que va a codificarse, de forma "Top‐Down Design", y la materialización del software de  manera  "Buttom‐Up".  Observen  y  evalúen  primordialmente,  en  esta  biblioteca  que  les  obsequio, el uso de esas técnicas de diseño y programación.    ["Laboratorios\Lab3\SciComm\SciComm.inc"]  001 ;********************************************************************  002 ; SciComm.inc: SCI Comm. Support  M28E2014  003 ; Luis G. Uribe C., D11M2007 L12M07 J16A09 L08J09  004 ; ..S16J2012 (HCS08) D24J2012 M26F2013 J12D2013 (putcX)  C18D2013  005 ; J12D2013: Modified 'putcX' to enable any possible following 'BEQ'  006 ; M10D2013: Changed 'XmtRcvEnable' to 'XmtRcvActivate'  007 ; M26F2013: Fixed: Include Commas in List of ALL Macros (comments!)  008 ; J03E2013: List ALL Macros, for reference, in this header... AddresS  009 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  010 ; ** REFER to >>> >> 02MC9S08QE128RM(ReferenceManual)‐U.pdf << <<< **  012 ;====================================================================  013 ; THE FOLLOWING MACROS ARE DEFINED IN THIS LIBRARY  014 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  015 ;SCI9600N8: MACRO              ; Program SCI @9600, 8, N, 1  016 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  017 ;getchar:   MACRO              ; MAY rearm Rcv interrupts, inside ISR  018 ; getcX:    MACRO   Ptr1 ;..MAY rearm interrupts, inside ISR. *Ptr1++  019 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  143 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 020 ;putchar:   MACRO              ; MAY rearm Xmt interrupts, inside ISR  021 ; putc:     MACRO   #K or V    ; MAY rearm Xmt interrupts, inside ISR  022 ; putcX:    MACRO   Ptr1 ;..MAY rearm interrupts, inside ISR; *Ptr1++  023 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  024 ;RcvChar:   MACRO  025 ;XmtChar:   MACRO  026 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  027 ;XmtRcvActivate: MACRO  028 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  029 ; Ancillary Macros  030 ;  031 ;IfRcvRdy:  MACRO   brAddressIfReady1  032 ;IfXmtRdy:  MACRO   brAddressIfReady1  033 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  034 ;XmtIntEn:  MACRO  035 ;XmtIntDsb: MACRO  036 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  037 ;RcvIntEn:  MACRO  038 ;RcvIntDsb: MACRO  039 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  040 ;ComIntEn:  MACRO              ; Interrupt Enable for Communications  041 ;  042 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  043 ;*TODO*: Define  IfNotRcvRdy, IfNotXmtRdy, IfNotRcvRdyS, IfNotXmtRdyS  044 ; ..(cfr. timers8) if you ever need them.  045 ; CREATE kbhit, Nkbhit & equivalents for output. getc var  046 ;====================================================================  047 ; DEFINE REGISTERS: (in 'MC9S08QE128.inc'), AND REQUIRED BITS  048 ; ..(here), for SCI Serial Communications Interface    049 ;********************************************************************  050 ; SCI1BD: EQU $0020            ; Baude Rate register  051 ; LOOPMODE!!!    052 Bauds9600Value: EQU   $001A    ; 16 bit: 4000000/(16*9600)=26.04=0x1A  053 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  054 ; SCI1D: EQU $27  055 ; >>> >>> ALIAS <<< <<< (Other names for same address. BE CAREFULL !)    056 XMTBUF:        EQU     SCI1D   ; Transmiter Buffer  057 RCVBUF:        EQU     SCI1D   ; Receiver   Buffer  058 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  059 ; SCI1C2:  EQU $23  060 ; >>> >>> ALIAS <<< <<< (Other names for same address. BE CAREFULL !)    061 XMTCTRLREG:    EQU     SCI1C2  062 RCVCTRLREG:    EQU     SCI1C2    063 XMTEN:         EQU    mSCI1C2_TE   ; TE:  Transmiter Enable %00001000  064 XMTIEN:        EQU    mSCI1C2_TIE  ; TIE: Xmt INT    Enable %10000000  065 XMTIEN.bit:    EQU     SCI1C2_TIE  ; 7    144 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 066 RCVEN:         EQU    mSCI1C2_RE   ; RE:  Receiver Enable   %00000100  067 RCVIEN:        EQU    mSCI1C2_RIE  ; RIE: Rcv INT  Enable   %00100000  068 RCVIEN.bit:    EQU     SCI1C2_RIE  ; 5    069 XmtRcvEnab:    EQU     (XMTEN | RCVEN) ;Enable BOTH devices %00001100  070 ;====================================================================  071 ; SCI1S1: EQU $24  072 ; >>>>>>ALIAS<<<<<<  (Other names for the SAME ADDRESS. BE CAREFULL!)    073 XMTSTATREG:    EQU     SCI1S1  074 RCVSTATREG:    EQU     SCI1S1  075 ; Relevant bits:    076 XMTRDY:        EQU    mSCI1S1_TDRE ; Transmiter Data Register Empty  077 XMTRDY.bit:    EQU     SCI1S1_TDRE ; 7 (mSCI1S1_TDRE: %10000000)    078 RCVRDY:        EQU    mSCI1S1_RDRF ; Receive   Data Register Full  079 RCVRDY.bit:    EQU     SCI1S1_RDRF ; 5 (mSCI1S1_RDRF: %00100000)    080 XMTEMPTY:      EQU    mSCI1S1_TC   ; Transmission Complete Flag  081 XMTEMPTY.bit:  EQU     SCI1S1_TC   ; 6 (mSCI1S1_TC: %01000000): last!  082 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  083 ; Receiver:  084 OVERRUNERR:    EQU    mSCI1S1_OR   ; OR: Overrun           %00001000  085 NOISERR:       EQU    mSCI1S1_NF   ; NF: Noise Flag        %00000100  086 FRAMERR:       EQU    mSCI1S1_FE   ; FE: Framing Error     %00000010  087 PARERR:        EQU    mSCI1S1_PF   ; PE: Parity  Error     %00000001    088 RCVErrs:       EQU     (OVERRUNERR | NOISERR | FRAMERR | PARERR)  089 ;********************************************************************  090 ; MACRO DEFINITIONS  091 ; NOTE: Accumulator, H:X, CCR are NOT preserved through Comm. Macros    092 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  093 ; No need for previous enabled SCI peripherals  094 SCI9600N8: MACRO               ; Program SCI @9600, 8, N  095        ldhx   #Bauds9600Value  ; program 9600bps value  096         sthx   SCI1BD          ; ..  097    ENDM  098 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  099 XmtRcvActivate: MACRO  100        mov    #XmtRcvEnab, SCI1C2  ; ACTIVATE  BOTH units: xmt & rcv  101    ENDM  102 ;====================================================================  103 IfRcvRdy:  MACRO   brAddressIfReady1  104        brset   RCVRDY.bit, RCVSTATREG, \1  105    ENDM  106 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  107 getchar:   MACRO               ; MAY rearm Rcv interrupts, inside ISR  108        brclr   RCVRDY.bit, RCVSTATREG, *   ; rearm..  109        lda     RCVBUF          ; ..rearm  110    ENDM  145 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 111 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  112 getcX:     MACRO   Ptr1  ;..MAY rearm interrupts, inside ISR. *Ptr1++  113        ldhx    \1  114        brclr   RCVRDY.bit, RCVSTATREG, *   ; rearm..  115        mov     RCVBUF, x+      ; ..rearm  116        tpa                     ; save CCR to enable any following BEQ  117        sthx    \1  118        tap                     ; ..restore CCR: enable following BEQ  119    ENDM  120 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  121 RcvChar:   MACRO  122        lda     RCVBUF  123    ENDM  124 ;====================================================================  125 IfXmtRdy:  MACRO   brAddressIfReady1  126        brset   XMTRDY.bit, XMTSTATREG, \1  127    ENDM  128 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  129 putchar:   MACRO               ; MAY rearm Xmt interrupts, inside ISR  130        brclr   XMTRDY.bit, XMTSTATREG, *   ; rearm..  131        sta     XMTBUF          ; ..rearm  132    ENDM  133 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  134 putc:      MACRO   #K or V     ; MAY rearm Xmt interrupts, inside ISR  135        lda     \1  136        brclr   XMTRDY.bit, XMTSTATREG, *   ; rearm..  137        sta     XMTBUF          ; ..rearm  138    ENDM  139 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  140 putcX:     MACRO   Ptr1  ;..MAY rearm interrupts, inside ISR; *Ptr1++  141        ldhx    \1  142        brclr   XMTRDY.bit, XMTSTATREG, *   ; rearm..  143        mov     x+, XMTBUF      ; ..rearm  144        tpa                     ; save CCR to enable any following BEQ  145        sthx    \1  146        tap                     ; ..restore CCR: enable following BEQ  147    ENDM  148 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  149 XmtChar:   MACRO  150        sta     XMTBUF  151    ENDM  152 ;====================================================================  153 XmtIntEn:  MACRO  154        bset    XMTIEN.bit, XMTCTRLREG  155    ENDM  156 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  157 XmtIntDsb: MACRO  158        bclr    XMTIEN.bit, XMTCTRLREG  159    ENDM  160 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  161 RcvIntEn:  MACRO  162        bset    RCVIEN.bit, RCVCTRLREG  146 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 163    ENDM  164 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  165 RcvIntDsb: MACRO  166        bclr    RCVIEN.bit, RCVCTRLREG  167    ENDM  168 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  169 ComIntEn:  MACRO               ; Interrupt Enable for Communications  170        bset    RCVIEN.bit, RCVCTRLREG  ; Just only RCV...  171                                ; Must Enable XMT *ONLY* when needed  172        lda     RCVSTATREG      ; Clear any possibly pending  173        lda     RCVBUF          ; ..RCV Ready Flag  174    ENDM    COMENTARIOS a ["Laboratorios\Lab3\SciComm\SciComm.inc"]: Comencemos,  pues,  identificando  las  funcionalidades  que  requiere  el  usuario  cuando  necesita manipular un periférico de comunicaciones seriales, RS232. No olviden revisar  el Manual de Referencia que publiqué: 02MC9S08QE128RM(ReferenceManual)‐U.pdf.    Primero  se  presenta  la  funcionalidad  que  empleamos  para  definir  los  parámetros  de  comunicación. En principio, en un canal de comunicación serial, del tipo definido como  RS‐232, hay variables que se definen FUERA del protocolo. Eso quiere decir que si usted  tiene que comunicarse por ejemplo con CANTV, empleando este método, tiene que LLAMARLOS  por teléfono, preguntarles qué valores tienen para: Número de bits de cada símbolo (8  es  el  más  común,  pero  NO  el  único);  la  velocidad  en  bits  por  segundo  (9600  bps,  en  nuestra librería, pero hay MUCHOS otros valores que se emplean, llegando hasta 115,200  bps,  y  más,  sobre  todo  en  comunicación  LOCAL,  la  que  no  se  transmite  vía  Modem  o  Radio... Luego, está un bit llamado "Parity", o paridad, que servía, al principio más  que ahora, para ayudar a identificar errores de recepción. Hay uno más: el número de  STOP bits, que hace mucho tiempo se usa como UN (1) Stop bit.    Nuestra Macro para inicializar el canal de comunicaciones seriales es:    015 ;SCI9600N8: MACRO              ; Program SCI @9600, 8, N, 1    Es decir: 9600 bps, NO parity, 8 bits per char, ONE (1) stop bit.    Para  leer  y  transmitir  caracteres,  el  usuario  AMARÍA  poder  tener  funcionalidades  equivalentes  a  las  de  "getchar"  y  "putchar"  del  C...  Por  eso  aquí  están,  a  continuación:    017 ;getchar:   MACRO              ; MAY rearm Rcv interrupts, inside ISR  018 ; getcX:    MACRO   Ptr1 ;..MAY rearm interrupts, inside ISR. *Ptr1++    "getchar" funciona igual que en C: toma un símbolo de entrada (En C vienen del teclado;  aquí,  del  puerto  serial)  y  nos  lo  retorna  en  el  Acumulador.  "getcX"  lee  datos  y  los  almacena,  no  en  el  Acumulador,  sino  empleando  el  registro  índice  H:X;  muy  útil  para  almacenar en una tabla los caracteres recibidos.    020 ;putchar:   MACRO              ; MAY rearm Xmt interrupts, inside ISR  021 ; putc:     MACRO   #K or V    ; MAY rearm Xmt interrupts, inside ISR  022 ; putcX:    MACRO   Ptr1 ;..MAY rearm interrupts, inside ISR; *Ptr1++  147 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 "putchar", transmite lo que está en el Acumulador, empleando la línea de comunicaciones  seriales.  "putc"  se  usa  cuando  desean  transmitirse  CONSTANTES  o  VARIABLES;  y  "putcX"  para  enviar  información  que  está  almacenada  en  TABLAS,  "apuntadas"  empleando  el  registro índice H:X.    Las próximas 2 Macros son de más bajo nivel:    024 ;RcvChar:   MACRO  025 ;XmtChar:   MACRO    Dije con anterioridad que los periféricos del MC9S08QE128 tienen, en primer lugar, que  ENERGIZARSE: muchos de esos periféricos NO están conectados permanentemente a la fuente  de alimentación, para disminuir el consumo de energía. Los dos periféricos que tienen  que ver con la comunicación serial, RS‐232 (SCI), que son el de Transmisión (XMT) y el  de Recepción (RCV), se energiza cada uno por aparte. Yo activo los dos de una sola vez,  porque en mis ejemplos estamos empleando AMBOS: XMT and RCV "Activate":    027 ;XmtRcvActivate: MACRO    Las funcionalidades de más bajo nivel aún, son:    029 ; Ancillary Macros  031 ;IfRcvRdy:  MACRO   brAddressIfReady1  032 ;IfXmtRdy:  MACRO   brAddressIfReady1  que  sirven  para  ver  si  el  dispositivo  de  recepción  (RCV)  o  el  de  transmisión  (XMT)  están disponibles (READY), a fin de saber si se los puede emplear.    Para  habilitar  o  deshabilitar  cada  dispositivo,  independientemente,  para  que  interrumpan:    ‐ Para el Transmisor (XMT), activar y desactivar las interrupciones:    034 ;XmtIntEn:  MACRO  035 ;XmtIntDsb: MACRO    ‐ Para el Receptor (RCV), activar y desactivar las interrupciones:    037 ;RcvIntEn:  MACRO  038 ;RcvIntDsb: MACRO    Y, finalmente, una forma conveniente de activar AMBOS periféricos simultáneamente para  trabajar con ellos:    040 ;ComIntEn:  MACRO              ; Interrupt Enable for Communications    La  definición  de  registros  usted  tiene  que  analizarla  JUNTO  a  los  manuales  de  referencia para el SCI, Serial Communications Interface:    050 ; SCI1BD: EQU $0020            ; Baude Rate register  052 Bauds9600Value: EQU   $001A    ; 16 bit: 4000000/(16*9600)=26.04=0x1A    148 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 NOTA:  Para  aumentar  la  compresión  del  código,  he  utilizado  varios  ALIAS,  que  son  NOMBRES  ALTERNATIVOS  para  unas  mismas  direcciones  o  variables.  Hay  que  ser  MUY  CUIDADOSOS cuando se emplean ALIAS, para no caer en olvidos que nos lleven a creer que  los Alias SON ¡DIFERENTES VARIABLES, O REGISTROS, O POSICIONES DE MEMORIA! (Recuerde:  Son  DISTINTOS  nombres  para  LAS  MISMAS  COSAS  ENTIDADES:  registros,  posiciones  de  memoria, ETC.)    056 XMTBUF:        EQU     SCI1D   ; Transmiter Buffer  057 RCVBUF:        EQU     SCI1D   ; Receiver   Buffer    INTERESANTE notar que AMBOS de registros, el buffer de transmisión y el de recepción,  corresponden  a  la  MISMA  ENTIDAD  FÍSICA:  SCI1D.  Esto  es  así  porque  el  campo  de  direcciones  es  un  recurso  MUY  LIMITADO;  y  para  optimizar,  se  definen  AMBOS  registros  con la misma dirección.    ¿Cómo sabe el CPU cuál de los dos periféricos, el de entrada o el de salida, estamos  direccionando?  Pues,  si  estamos  LEYENDO  de  esa  única  dirección,  seguro  estamos  intentando  RECIBIR  desde  el  periférico  RECEPTOR.  Y  si  estamos  ESCRIBIENDO  sobre  ese  MISMO  recurso,  con  seguridad  estamos  tratando  de  TRANSMITIR  por  el  periférico  TRANSMISOR.    Con seguridad coincidirá conmigo en que es MUCHO más inteligible un símbolo como RCVBUF  (Receiver Buffer, o registro de datos del receptor), que SCI1D. En el código hay otros  ejemplos  en  los  que  la  inteligibilidad  de  los  símbolos  por  mí  definidos  resulta  con  mucho superior a la de las definiciones de Freescale!  Registros de Control del Transmisor:  061 XMTCTRLREG:    EQU     SCI1C2  062 RCVCTRLREG:    EQU     SCI1C2    RECUERDE:  hay  que  HABILITAR  el  transmisor  (ENABLE:  XMTEN),  para  trabajar  con  él;  además, si debe interrumpir, hay que habilitarlo también para interrupción (XMTIEN):    Bits de uso frecuente:  063 XMTEN:         EQU    mSCI1C2_TE   ; TE:  Transmiter Enable %00001000  064 XMTIEN:        EQU    mSCI1C2_TIE  ; TIE: Xmt INT    Enable %10000000  065 XMTIEN.bit:    EQU     SCI1C2_TIE  ; 7    Vuelvo a recalcar: Una cosa es el XMTEN (Transmiter ENABLE), que ACTIVA al periférico  transmisor (lo ENERGIZA),    ..y otra muy distinta XMTIEN, que es el Interrupt ENable para el XMT (Transmiter). Y el  XMTIEN que yo introduzco, es mucho mejor nombre que mSCI1C2_TIE.    El número binario a la derecha de mis definiciones, identifica CUÁL bit es el referido:      TIE: Xmt INT Enable %10000000 (bit 7) del registro de control asociado, XMTCTRLREG.  Es importante saber cuáles son los bits, para poderlos ver con el Debugger.      Este número (7) identifica en decimal, el correspondiente bit:    065 XMTIEN.bit:    EQU     SCI1C2_TIE  ; 7    149 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Definiciones equivalentes para el Receptor:    066 RCVEN:         EQU    mSCI1C2_RE   ; RE:  Receiver Enable   %00000100  067 RCVIEN:        EQU    mSCI1C2_RIE  ; RIE: Rcv INT  Enable   %00100000    El bit para habilitar las interrupciones de transmisión se define por aparte:    068 RCVIEN.bit:    EQU     SCI1C2_RIE  ; 5    Finalmente,  tenemos  una  forma  de  activar  AMBOS  dispositivos  (para  que  reciban  alimentación eléctrica); un símbolo que cubre los dos periféricos:    069 XmtRcvEnab:    EQU     (XMTEN | RCVEN) ;Enable BOTH devices %00001100    Antes  se  definieron  los  bits  de  los  registros  de  Control;  ahora  les  corresponde  el  turno a los Status Registers:    073 XMTSTATREG:    EQU     SCI1S1  074 RCVSTATREG:    EQU     SCI1S1    Los bits más usados en ellos, que el usuario nunca verá si está empleando mis Macros:    076 XMTRDY:        EQU    mSCI1S1_TDRE ; Transmiter Data Register Empty  077 XMTRDY.bit:    EQU     SCI1S1_TDRE ; 7 (mSCI1S1_TDRE: %10000000)    078 RCVRDY:        EQU    mSCI1S1_RDRF ; Receive   Data Register Full  079 RCVRDY.bit:    EQU     SCI1S1_RDRF ; 5 (mSCI1S1_RDRF: %00100000)    080 XMTEMPTY:      EQU    mSCI1S1_TC   ; Transmission Complete Flag  081 XMTEMPTY.bit:  EQU     SCI1S1_TC   ; 6 (mSCI1S1_TC: %01000000): last!    ¿Por qué para recepción hay un solo bit de READY, y para transmisión hay dos? Bueno,  los dos periféricos son CASI DUALES el uno del otro, pero no exactamente.    Al comienzo, cuando hablamos de Colas, indicamos una modificación para el dispositivo  de  entrada,  que  TAMBIÉN  se  le  hizo  al  periférico  DE  SALIDA:  Se  le  agregó  un  segundo  Registro  Adicional  al  Serializador  de  Salida  (el  serializador  que  es  el  que  va  transmitiendo uno a uno los bits de la letra) y cuando éste ha transmitido 8 bits, el  controlador  de  la  interfaz  toma  otro  símbolo  del  Data  Buffer  y  lo  lleva  al  serializador,  si  es  que  hay  algún  dato  ya  en  el  Data  Buffer.  A  esta  combinación  de  Serializador alimentado por otro registro, que es aquel de donde en definitiva el CPU  ESCRIBE  los  datos,  se  lo  conoce,  al  igual  que  en  el  caso  del  receptor,  como  Double  Buffer (porque hay 2 registros involucrados, o buffers).    El protocolo es similar al del caso de entrada: cada vez que hay un Bit de Ready activo  en la transmisión, el CPU puede mover el siguiente dato al Transmiter Buffer. En algún  momento  ese  dato  pasa  al  serializador,  y  como  el  Buffer  de  Transmisión  ha  quedado  libre, se vuelve a activar el Bit de Ready de Transmisión, lo que permite al programa  principal  colocar  un  nuevo  valor  en  el  Transmiter  Buffer,  mientras  se  está  transmitiendo el dato anterior.    150 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Ahora,  cuando  ya  no  hay  nada  más  que  transmitir,  la  última  vez  que  el  subsistema  de  salida activa un Transmiter Ready, el programa principal podría CORTAR la comunicación  en ese momento, pero PERDERÍA la transmisión del último byte porque, aunque el buffer  de  salida  está  disponible,  el  periférico  de  salida  NO  ha  terminado  de  serializar  el  último dato que tiene que transmitir por el canal de comunicaciones. Esta situación se  trató  en  los  primeros  UARTS,  mediante  un  retardo  que  el  programa  principal  activaba  cuando  transfería  el  último  dato  al  Transmiter  Buffer.  Y  NO  cerraba  el  canal  de  comunicaciones  (¿Modem?  ¿Radio?)  hasta  que  no  hubiera  transcurrido  ese  tiempo,  calculado como mayor al necesario para enviar un byte.    En los UARTs modernos se colocó un SEGUNDO bit de Ready para el transmisor; que sólo  usamos antes de cerrar el canal de comunicaciones después de haber transmitido nuestro  último byte. En nuestros ejercicios, éste no es el caso, pero en la vida real, sí.    Un  último  grupo  de  bits  dentro  del  SCI,  que  también  pueden  INTERRUMPIR  y  tienen  su  propio  vector  de  interrupciones,  es  el  de  los  ERRORES.  Note  que  NO  hay  errores  de  TRANSMISIÓN,  porque  no  tenemos  control  ni  conocimiento  de  lo  que  pasa,  una  vez  que  hemos enviado un símbolo al canal de comunicaciones. Sólo hay errores de RECEPCIÓN:    084 OVERRUNERR:    EQU    mSCI1S1_OR   ; OR: Overrun           %00001000    El  OVERRUNERR  lo  detecta  la  interfaz  de  entrada,  cuando  va  a  almacenar  en  el  Data  Buffer un valor que acaba de serializar, y ese registro de entrada NO HA SIDO LEÍDO por  el  procesador.  La  unidad  controladora  de  entrada  lo  sabe,  porque  el  protocolo  de  recepción  ordena  que  cuando  el  CPU  lee  un  dato,  debe  borrar  la  bandera  de  Ready  de  entrada. Si esta bandera está en uno, para el momento en que se debe colocar un nuevo  dato  en  el  registro  de  entrada,  se  sabe  que  el  nuevo  dato  BORRARÁ  el  valor  viejo,  y  como el CPU no ha indicado que lo leyó, se ESTÁN PERDIENDO DATOS en la recepción. El  canal de comunicaciones está alimentando información a una velocidad superior a la que  el CPU la está procesando...    085 NOISERR:       EQU    mSCI1S1_NF   ; NF: Noise Flag        %00000100    El  Error  de  Ruido  lo  infiere  el  receptor  si,  cuando  está  realizando  el  proceso  de  muestreo de los bits de entrada, encuentra que hay cambios en la información, más allá  de los previsibles en los bordes de la señal de muestreo. El manual de referencia tiene  la descripción exacta.    086 FRAMERR:       EQU    mSCI1S1_FE   ; FE: Framing Error     %00000010    Un Framing Error se produce cuando el receptor comienza a muestrear nuevos bits, porque  ha detectado un bit de ARRANQUE (START) y, al llegar al fin NO ENCUENTRA UN STOP BIT.  Esto puede deberse a excesivo ruido en la línea, que hizo creer a la interfaz que había  un START bit, o a una pérdida de sincronismo por alguna otra causa, y que en todo caso  produce que la información de entrada no tenga el Marco o FRAME correcto. Note que si,  habiéndose salido de sincronismo, por mala suerte el último bit, que se lee, erróneo,  es un UNO, la interfaz NO puede detectar ningún Framing Error...    087 PARERR:        EQU    mSCI1S1_PF   ; PE: Parity  Error     %00000001    El  bit  de  paridad  (PAR  o  IMPAR)  se  inventó  al  comienzo  cuando  los  dispositivos  de  comunicaciones  seriales  eran  electromecánicos  (TÉLEX),  y  había  MUCHOS  errores  en  la  151 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 transmisión.  La  idea era  añadir un  bit  a la  información,  codificándola  de  tal  manera  que hubiera una mitad de símbolos POSIBLES, y una mitad de valores IMPOSIBLES. Así, si  al receptor le llega un símbolo del grupo equivocado, sabe que ha habido UN error de  comunicaciones. Si el dato es del grupo válido, se supone que ha llegado bien (aunque  si  hay  DOS  errores,  o  un  número  PAR  de  ellos,  el  receptor  no  es  capaz  de  identificarlo).  Una  manera  fácil  de  añadir  el  bit  extra,  consiste  en  hacerlo  de  tal  manera que TODOS los bits de la variable que se está transmitiendo, sean PARES (EVEN  PARITY) o IMPARES (ODD PARITY). Un dispositivo muy sencillo suma el número de bits de  la  información  (sumador;  cadena  de  EXORs),  y  si  el  número  unos  ya  coincide  con  la  paridad, no agrega nada, pero si el número de unos no coincide, se agrega un uno, a fin  de obligar al símbolo para que siempre su cantidad coincida con la paridad, número par  o impar de unos.    Finalmente, se hace una combinación para identificar CUALQUIER condición detectable de  error:    088 RCVErrs:       EQU     (OVERRUNERR | NOISERR | FRAMERR | PARERR)    Unidades de comunicación serial más avanzadas, como las que se emplean en el PC IBM/AT  compatible, tienen un CUARTO elemento que puede también generar interrupciones, en un  vector independiente. Se trata del manejador de Línea (LINE CONTROLER) encargado de ver  si el Modem se activó o se desactivó, si se perdió la portadora o regresó, si hay un  inconveniente con el Modem (DSR inválido), o si en algún momento no puede aceptar un  símbolo nuevo (CTS inválido).    Escribir  un  paquete  completo  de  comunicaciones,  que  considere  los  cuatro  elementos  disponibles en las interfaces industriales de comunicaciones, no resulta nada sencillo.  Pero se compran por menos de $60, con garantía, soporte, y documentación impecable para  su uso.    El  resto  de  la  biblioteca  casi  no  necesita  mayor  comentario,  si  se  analizan  con  detenimiento las instrucciones de cada Macro.    Bueno;  la  primera  Macro  le  coloca  al  dispositivo  SCI  (tanto  al  transmisor  como  al  receptor) los valores apropiados de BPS (bits por segundo: 9600 para nuestros ejemplos  [¡SIEMPRE FUNCIONAN!]), 8 bits para cada elemento transferido, NO parity y un (1) Stop  bit. El registro en el que se programan esos parámetros es el SCI1BD: Baud Rate. Véase  el Reference Manual.    094 SCI9600N8: MACRO               ; Program SCI @9600, 8, N  095        ldhx   #Bauds9600Value  ; program 9600bps value  096         sthx   SCI1BD          ; ..    La ACTIVACIÓN de ambos dispositivos, transmisor y receptor (energizarlos):    099 XmtRcvActivate: MACRO  100        mov    #XmtRcvEnab, SCI1C2  ; ACTIVATE  BOTH units: xmt & rcv    Macro auxiliar para determinar si el receptor está listo:    103 IfRcvRdy:  MACRO   brAddressIfReady1  104        brset   RCVRDY.bit, RCVSTATREG, \1  152 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Si llega a necesitar la macro opuesta (IfRcvNotRdy), es así:    If_NOT_RcvRdy: MACRO   brAddressIfNotReady1             brclr   RCVRDY.bit, RCVSTATREG, \1    >>>LA<<< Macro principal, que lee como si se estuviera haciendo en C:    107 getchar:   MACRO               ; MAY rearm Rcv interrupts, inside ISR  108        brclr   RCVRDY.bit, RCVSTATREG, *   ; rearm..  109        lda     RCVBUF          ; ..rearm    ¿Sencillo, no? Observe que esta Macro es BLOQUEANTE, igual que en el C... Además, si  está  leyendo  Strings, que  son  elementos  de  información terminados en  un  byte  ZERO,  a  continuación  de  un  "getchar"  usted  puede  simplemente  hacer  un  BEQ,  o  BNE,  y  estas  instrucciones  hacen  referencia  al  ÚLTIMO  byte  transferido  por  getchar  (LDA  hace  una  comparación tácita del valor que transfiere, contra Cero).    Ojo  que  ambas  Macros  de  RECEPCIÓN,  Y  LAS  SIMILARES  PARA  TRANSMISIÓN  (putchar,  putc,  putcX),  HAN  SIDO  PROGRAMADAS  de  tal  manera  que  ‐‐además‐‐,  "REARMEN"  las  interrupciones de cada periférico y, así, ¡PUEDEN USARSE TAMBIÉN dentro de las rutinas  de interrupción!    Eso me quedó MUUUY BIEN.    Eso  es  así  porque  el  protocolo  de  Acknowledge  de  Interrupción,  que  le  dice  al  periférico de Recepción, o al de Transmisión, que ya fueron atendidos, es la misma cosa  que  llevarles  la  bandera  de  Ready  a  Cero  a  dichos  dispositivos,  y  consiste  en  lo  siguiente (tomado directamente del Manual de Referencia):    "To clear RCVRDY, read RCVSTATREG with RCVRDY = 1 and THEN read the SCI data register  (RCVBUF)". Esto es exactamente lo que hace getchar:    107 getchar:   MACRO               ; MAY rearm Rcv interrupts, inside ISR  108        brclr   RCVRDY.bit, RCVSTATREG, *; Espera a que RCVRDY = 1  109        lda     RCVBUF          ; Luego LEE del RCVBUF    Una observación para el lector avispado: En las rutinas de interrupción puede suponerse  que NO HABRÍA necesidad de incluir:    108        brclr   RCVRDY.bit, RCVSTATREG, *; Espera a que RCVRDY = 1    pues únicamente se llega a la rutina de interrupción porque el periférico ESTÁ READY!  Parecería superfluo. Pero, esos dos pasos son los que se requieren, según el texto en  inglés que acabo de copiar directamente del Reference Manual.    NOTA: El manual es INEXACTO e IMPRECISO, porque cuando dice: "read RCVSTATREG with  RCVRDY = 1 and THEN read RCVBUF", NO DICE SI ENTRE ESAS DOS INSTRUCCIONES PUEDEN, O  NO, HABER OTRAS INSTRUCCIONES (que sería el caso en que una interrupción se colara  en  medio  de  ellas,  y  la  cosa  quedaría  como:  a)  "read  RCVSTATREG  with  RCVRDY=1",  b) a  continuación  un  número  indeterminado  de  instrucciones  (las  de  la  rutina  de  interrupción) y finalmente, c) el "read RCVBUF".    153 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La única manera de esclarecer esto es mediante investigación, incluyendo instrucciones  a mano y viendo que, en realidad, sí funciona con instrucciones extras incluidas entre  las otras dos.. Pero es poco profesional que para saber lo que hace un dispositivo, no  sea suficiente con leer la documentación.    "To  clear  XMTRDY,  read  XMTSTATREG  with  XMTRDY  =  1  and  then  write  to  the  SCI  data  register (XMTBUF)". Esto es exactamente lo que hace putchar:    129 putchar:   MACRO               ; MAY rearm Xmt interrupts, inside ISR  130        brclr   XMTRDY.bit, XMTSTATREG, *; Espera a que XMTRDY = 1  131        sta     XMTBUF          ; Luego ESCRIBE en el XMTBUF    NOTA: En dispositivos más avanzados, como la familia Intel que se usa en los PC IBM/AT  compatibles, el protocolo de Acnowledge de entrada y salida lo realiza el hardware: la  misma instrucción que LEE el dato del buffer de entrada, BORRA LA BANDERA de Ready; la  misma instrucción que escribe sobre el periférico de salida BORRA LA BANDERA de Ready.    No era algo tan complicado, como para obligar al programador a hacer esta operación por  software.    Mais c'est la vie...      Note  que  al  igual  de  su  equivalente  en  C,  getchar  es  BLOQUEANTE:  una  vez  invocada,  esperará a que llegue una letra, ANTES de continuar adelante con el programa.    Si en algún momento decide que usted no debe bloquearse (un programa CASI NUNCA debe  bloquearse,  porque  se  pierde  casi  todo  el  control  sobre  el  sistema),  usted  puede  preguntar si hay letras en el receptor ANTES de llamar a getchar para leerlas. Este es  el equivalente a usar kbhit() en C pero, muy probablemente ustedes jamás oyeron hablar  de kbhit() antes.    NotKbhit (más útil aquí que kbhit) puede codificarse así (es igual a If_NOT_RcvRdy):    NotKbhit EQU If_NOT_RcvRdy    El código quedaría así:           NotKbhit  cont  ; Si no hay datos, no los lee         getchar         ; ..getchar no se bloquea, porque sí hay datos  cont:    Una última funcionalidad muy útil en el receptor consiste en no leer los datos sobre el  acumulador, sino sobre una tabla, que se supone que vamos a llenar con los diferentes  símbolos que lleguen. También es BLOQUEANTE:    112 getcX:     MACRO   Ptr1  ;..MAY rearm interrupts, inside ISR. *Ptr1++    Se carga el registro índice con el Apuntador hacia el próximo elemento disponible en la  tabla:    113        ldhx    \1  154 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se espera a que llegue una letra, al igual que en getchar:    114        brclr   RCVRDY.bit, RCVSTATREG, *   ; rearm..    Al llegar un nuevo dato se lo lleva a la tabla con el MOV, SE INCREMENTA el apuntador  en  un  solo  golpe,  gracias  al  lujo  de  instrucciones  y  modos  de  direccionamiento  que  tiene este excepcional microcontrolador:    115        mov     RCVBUF, x+      ; ..rearm    Ahora,  una  funcionalidad  prometida  en  estas  macros  es  que,  una  vez  transferido  el  elemento  de  información  principal  (el  DATO  que  llegó),  el  programador  pueda  hacer  un  salto condicional, aprovechando el valor de la bandera Z, activado por el conjunto de  instrucciones ENRIQUECIDO del MCU.    Pero, a pesar de que YA transferimos el dato a su sitio, y que el MOV ya colocó el bit  Z  en  su  estado  apropiado,  todavía  no  hemos  llevado  el  nuevo  valor  del  pointer  incrementado a su sitio. Al hacer la transferencia del apuntador, se van a alterar las  banderas del CCR, en particular la Z. Así que hay que preservar el valor actual del CCR  (tpa) ANTES de guardar el apuntador (sthx), y recobrarlo después (tap):      116        tpa                     ; save CCR to enable any following BEQ  117        sthx    \1  118        tap                     ; ..restore CCR: enable following BEQ    Observe que "tpa" y "tap" fueron pensadas de tal manera que NO modifican las banderas  del CCR!    NOTE: INCREÍBLEMENTE, este es el comentario que figura en el Manual de Referencia, en  relación a la instrucción TAP:    * NOTE: The TAP instruction was added to improve testability of * the CPU08, and so few  practical applications of the * instruction exist.    The  author  of  this  infamous  paragraph  HAS  NO  IDEA  OF  WHAT  HE  IS  TALKING  ABOUT.  This  instruction was NOT added to improve testability; it is one of the most important Op‐ Codes in the entire HC08 instruction set, and one that deserves a full example on using  it!    For example, to write some generic routine, one that could be used inside an ISR, if  you need to disable interrupts, later you can not simply enable them: You must restore  interrupts to the state they where at the beginning, something like this:  ...  ;     TPA    ; Transfer CCR to Acc (TPA is TAP’s twin opcode)     PSHA   ; ..Save Acc (CCR) into stack  ;...Later you may recover the interrupt state, as follows:     PULA   ; Pop (er... Pull) Acc     TAP    ; ..Transfer Acc to CCR        ; ..(saved flags, including “I” Flag)  ;...  155 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 By the way, TAP stands for: “Transfer Accumulator to PROCESSOR STATUS WORD”. You know,  they begun calling the flags: PSW and, then, change their mind and renamed the PSW like  Condition  Code  Register  (CCR)  but,  the  instruction  codes  remain  using  the  old  nomenclature (TAP, TPA).    Usted  tiene  que  pensar  en  todo;  la  programación  es  una  actividad  MUY  MINUCIOSA.  Por  eso, hay que dividir en problema en zonas muy muy pequeñas, que nos permitan VER todas  las interacciones. Si usted hace un código de mil líneas, y luego comienza a ver por  qué no funciona, está perdido.    Las  Macros  para  el  transmisor  son  las  DUALES  del  receptor;  por  eso  los  comentarios  serán mínimos:    129 putchar:   MACRO               ; MAY rearm Xmt interrupts, inside ISR  130        brclr   XMTRDY.bit, XMTSTATREG, *   ; rearm..  131        sta     XMTBUF          ; ..rearm    "putchar" es bloqueante.  putc  no  tiene  equivalente  en  el  dispositivo  receptor:  Se  usa  putc  cuando  se  van  a  transmitir CONSTANTES O VARIABLES; nos permite no tener que cargar explícitamente esas  constantes o variables en el acumulador:    134 putc:      MACRO   #K or V     ; MAY rearm Xmt interrupts, inside ISR  135        lda     \1  136        brclr   XMTRDY.bit, XMTSTATREG, *   ; rearm..  137        sta     XMTBUF          ; ..rearm    putcX es muy similar a getcX. Para poder hacer uso de las instrucciones enriquecidas,  tal  como  en  getcX,  antes  de  almacenar  el  apuntador  incrementado,  hay  que  guardar  el  valor de Z (CCR), cargado por el dato que se ha transmitido, y recuperarlo después de  mover el apuntador:    140 putcX:     MACRO   Ptr1  ;..MAY rearm interrupts, inside ISR; *Ptr1++  141        ldhx    \1  142        brclr   XMTRDY.bit, XMTSTATREG, *   ; rearm..  143        mov     x+, XMTBUF      ; ..rearm  144        tpa                     ; save CCR to enable any following BEQ  145        sthx    \1  146        tap                     ; ..restore CCR: enable following BEQ    Las macros XmtIntEn, XmtIntDsb, RcvIntEn, RcvIntDsb son simplemente encender o apagar  el bit correspondiente.    ComIntEn  hace  "Interrupt  Enable  for  Communications".  Es  decir,  una  vez  colocados  los  valores: SCI9600N8, y energizados los dos periféricos: XmtRcvActivate, se desea Activar  el Subsistema Completo para trabajar por interrupciones:    169 ComIntEn:  MACRO               ; Interrupt Enable for Communications  170        bset    RCVIEN.bit, RCVCTRLREG  ; Just only RCV...  171                                ; Must Enable XMT *ONLY* when needed  172        lda     RCVSTATREG      ; Clear any possibly pending  173        lda     RCVBUF          ; ..RCV Ready Flag  156 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Nótese  que  SÓLO  SE  ACTIVA  LA  RECEPCIÓN  para  que  interrumpa.  Esto  es  así  porque  el  comportamiento de los periféricos de entrada es tal que NUNCA ellos están READY (nunca  interrumpen)  hasta  que  llega  una  letra  o  algún  otro  elemento  por  la  correspondiente  entrada.    Los periféricos de salida (impresoras, transmisor RSR‐232, y cualquier otro), cuando se  los  Activa  es  más  que  seguro  que  no  están  haciendo  nada  (nadie  los  ha  mandado  A  transmitir nada, o no les han dado nada para imprimir).    Así que, para comenzar, ESTÁN READY.    Si  se  los  activa  para  que  interrumpan,  como  están  Ready,  van  a  interrumpir  INMEDIATAMENTE.  Por  eso  hay  que  posponer  la  habilitación  para  interrupciones  de  los  periféricos  de  salida,  hasta  cuando  haya  salidas  disponibles,  como  una  tabla  para  transmitir  por  interrupciones,  o  una  cola,  o  incluso  una  letra,  si  es  que  no  se  la  desea enviar de manera programada, sino por interrupciones.      NÓTESE que, además de la activación del bit de RCVIEN.bit, leyendo el RCVSTATREG y el  RCVBUF:    172        lda     RCVSTATREG      ; Clear any possibly pending  173        lda     RCVBUF          ; ..RCV Ready Flag    ..se  BORRA  cualquier  posible  "RCV  Ready  Flag"  pendiente,  si  es  que  hubiera  llegado  algún símbolo ANTES de que activáramos el ComIntEn.    (Y,  esto...  puede  NO  ser  suficiente  para  activar  confiablemente  un  dispositivo  de  Recepción,  cuando  los  equipos  externos  NOS  ESTÁN  ENVIANDO  INFORMACIÓN,  de  manera  Asincrónica... Sobre los periféricos externos ¡solemos NO tener control!).    Piense  bien  sobre  las  implicaciones  que  existen  si  queremos  habilitar  nuestra  recepción,  y  no  lo  podemos  hacer  sin  estar  seguros  de  que  ¡NO  NOS  HAN  COMENZADO  A  TRANSMITIR TODAVÍA!      El comportamiento de los periféricos de salida es así: la rutina, probablemente Main,  llena una tabla o usa una, pregrabada en ROM (Flash), y habilita las interrupciones de  salida.  La  ISR  de  transmisión:  XMTISR,  se  activa  cuando  está  disponible,  si  se  encuentra habilitada para interrumpir. Así, saca un elemento de la tabla, lo transmite  y retorna con RTI (Return from Interrupt). Cuando vuelva a quedar en estado de Ready,  vuelve a interrumpir.    Dos mecanismos suelen emplearse para identificar que ya no hay nada que procesar: o se  acaba  de  enviar  EL  señalizador  de  final,  que  suele  ser  un  byte  NULL,  lleno  de  ceros  (éste  es el  mismo  método  que  usa  el  C  para  terminar  sus  Strings),  o  llega a  cero  un  contador  que  identifica  cuántos  caracteres  faltan  para  terminar,  y  que  se  lo  ha  ido  decrementando cada vez que se transmite una letra.    Cualquiera  que  sea  el  método,  cuando  XMTISR  determina  que  ya  no  hay  nada  más  que  enviar, SE AUTO DESHABILITA PARA INTERRUMPIR, y retorna con RTI.    157 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 NOTA:  Si  ha  "encolado"  una  tabla  para  su  transmisión,  y  quiere  enviar  otra,  o  reutilizar  la  misma  área  para  enviar  otros  datos,  tiene  que  estar  seguro  de  MANTENER LA COHERENCIA.    Mucho  cuidado  con  comenzar  una  acción  antes de  terminar  la  otra. Puede  hacerse,  pero  garantizando  que  jamás  se  trancará  el  juego.  Por  ejemplo,  supongamos  que  se  está  empleando una COLA: Si Main hace "enQue" de algo y habilita las interrupciones, y la  XMTISR  hace  "deQue"  y  lo  transmite,  y  si  en  un  momento  Main  está  "enQueing"  algo  y  quiere  volver  a  habilitar  las  interrupciones,  tiene  que  asegurarse  que  no  ocurra  una  situación de DEAD‐LOCK, en la que Main acaba de habilitar las interrupciones y XMTISR  las  desactiva  y  se  llegue  a  un  estado  en  que  a  Main  le  aparece  la  cola  llena,  y  no  puede hacer un ulterior envío, y XMTISR NO interrumpe, porque está desactivada, y por  tanto no saca caracteres de la cola. Eso es: SU PROGRAMA ESTÁ MUERTO: DEAD‐LOCK. Nada  hace ningún proceso; cada entidad espera por la otra, y ninguna de las dos trabaja...    Yo les dije que las interrupciones son una gran adición al modelo del computador, sin  las cuales serían impensables las computadoras modernas, y que a pesar de ello, son el  PEOR DOLOR DE CABEZA de un programador de sistemas Embebidos...    El análisis se complica muchísimo, porque se tiende a creer que 2 instrucciones que se  escribieron una a continuación de la otra, se ejecutarán una inmediatamente después de  la  otra.  Cuando  hay  interrupciones  activas,  esto  no  necesariamente  es  así.  Por  eso,  cuando  esté  haciendo  un  análisis  de  coherencia,  que  le  permita  garantizar  que  su  programa jamás quedará en DEAD‐LOCK, para cada instrucción tiene que suponer que todas  las  ISR  lo  interrumpen,  hacen  su  trabajo,  y  que  todo  sigue  bien  cuando  su  programa  continúe a ejecutar la siguiente instrucción de su la listado. Es muy laborioso.    NOTA FINAL:    Usted  debe  haber  observado  que  se  ha  empleado  la  instrucción  MOV  en  varias  partes;  incluso  aprovechando  su  modo  auto  incrementado  para  el  registro  Índice  H:X.  Pero  la  instrucción MOV requiere que al menos una de sus direcciones se encuentren en la página  0  (ZPage), es  decir,  dentro  de  las  256  primeras  posiciones.  Por  tanto,  estas rutinas  PRECISAN  la  definición  de  sus  tablas  EN  ZRam.  Si  ese no  va  a  ser  su  caso,  tiene  que  reescribirlas para usar parejas de LDA y STA que reemplacen los MOV.      Espero  que  les  haya  gustado  mi  implementación  de  la  librería  de  comunicaciones  seriales, RS‐232, empleando el dispositivo SCI del MC9S08QU128. Llevo al menos 30 años  implementando las mismas rutinas para el PC, para mis propios equipos de Supervisión y  Control, a nivel de Maestras, y de RTUs (Remote Terminal Units), trabajando en sistemas  tan diversos como QNX y DOS. No es que me senté esta semana y se las codifiqué....    28) Transmisión. Se envían permanentemente al PC las letras de la 'A' mayúscula a la 'z' minúscula. En  el  PC  se  ejecuta  un  programa  de  comunicaciones  tal  como  "Hyperterminal"  (antigua  utilidad  nativa  en  Windows,  y  ya  periclitada...),  el  "TeraTerm"  (que  es  el  que  yo  siempre uso), el "Putty" ("poty", como se pronuncia en Internet), el "RealTerm", que es  el  programa  instalado  en  los  PCs  del  Laboratorio  C,  y  cualquier  otro  que  ustedes  conozcan para realizar esta actividad de interactuar en Windows con un dispositivo vía  canal de comunicaciones RS‐232, en lo que se conoce una CONSOLA REMOTA.  158 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ["Laboratorios\Lab3\SciComm\1tstXmt.asm"]  01 ;********************************************************************  02 ; Programa 1TstXmt.asm: Test SCI Comm. Support  03 ; Luis G. Uribe C., D11M2007 J16A09 L08J09 S16J2012 (HCS08)  M10D2013  04 ;  05 ; ..Send 'A' to 'z' letters for ever, to Hyperterminal  06 ;  07 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  08 ; Include files that no define ram  09        NOLIST  10          INCLUDE 'derivative.inc'  11          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM  12        LIST                    ; ..storage and THIS is it's place  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; 2) DEFINES    15 ram:           SET Z_RAMStart  ; $80  16 rom:           SET ROMStart    ; $2080  17 initStack:     EQU RAMEnd + 1  ; $1800=$17FF+1. Stack begins on $17FF  18 COP_Disable:   EQU $42  19 ;====================================================================  20 ; MAIN PROGRAM HEADER:  21        ABSENTRY Main  22        ORG rom    23 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  24         sta    SOPT1           ; ..System Options 1  25        ldhx   #initStack       ; Set up SP  26         txs                    ; ...  27 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  28        SCI9600N8               ; Setup Serial Communications Inter‐  29        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits  30 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  31 Forever:  32        lda    #'A'             ; Ascii 'A'  33 XmitLoop:  34        putchar  35        inca                    ; 'A' + 1 is 'B'; 'Z' + 1 is 'a'  36        cmp    #'z'  37        bls     XmitLoop    38        putc   #$0D             ; Carriage Return (CR: '\r' in C)  39        putc   #$0A             ; Line Feed (LF: '\n')    40        bra     Forever         ; To come to this end you'l need  41                                ; ..217,191 cycles @4,194,304Hz  42        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  43        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  44                            ; This 'nop' MAY be removed for CW 6.3 ...  45 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  46 ; Interrupt Vectors    159 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 47        ORG     Vreset  48         DC.W   Main            ; RESET. Maximum priority. Asynch.  49        END    COMENTARIOS a ["Laboratorios\Lab3\SciComm\1tstXmt.asm"] Así, nuestro primer programa "1TstXmt.asm" incluye la librería 'SciComm.inc' en un área  que NO emplea RAM, a continuación de 'derivative.inc':    10          INCLUDE 'derivative.inc'  11          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM    No olvide que ésta es la posición donde DEBE incluir mi librería de comunicaciones.  Luego,  se  definen  los  parámetros  de  comunicación  (SCI9600N8)  y  se  activan  AMBOS  periféricos: XmtRcvActivate (Activate Xmt & Rcv):    28        SCI9600N8               ; Setup Serial Communications Inter‐  29        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits    El programa en sí es muy sencillo; consiste de un ciclo indefinido que transmite todas  las letras entre la 'A' mayúscula y la 'z' minúscula:    31 Forever:  32        lda    #'A'             ; Ascii 'A'  33 XmitLoop:  34        putchar  35        inca                    ; 'A' + 1 is 'B'; 'Z' + 1 is 'a'  36        cmp    #'z'  37        bls     XmitLoop    Cada  vez  que  se  termina  una  serie,  se  imprime  el  símbolo  que  representa,  para  la  pantalla, el devolverse hacia el lado izquierdo de la misma: Carriage Return ('\r' in  C), y luego el que obliga al cursor de la pantalla a bajar una línea: Line Feed (LF:  '\n'):    38        putc   #$0D             ; Carriage Return (CR: '\r' in C)  39        putc   #$0A             ; Line Feed (LF: '\n')      29) ECHO. Este  programa  implementa  una  funcionalidad  muy  empleada  entre  los  profesionales  que  tienen que interactuar con comunicaciones seriales, del tipo RS‐232. Consiste en hacer  un ECO (ECHO) a los caracteres que le llegan a nuestro equipo, probablemente desde una  estación Maestra, remota.    Ustedes  simularán  la  estación  remota  con  el  PC;  su  dispositivo  local  será  nuestra  tarjeta DEMOQE128. Ahora, resulta que en ocasiones, los programas de comunicación que  se  ejecutan  en  la  estación  maestra  (o  en  el  PC),  tienen  una  función  que  a  veces  el  operador olvida, y que se llama LOCAL ECHO. Esto significa que todo lo que se envía,  por ejemplo desde el teclado del PC, se ve en la pantalla del mismo. Es claro que, si  160 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 está  activa  esa  característica,  es  difícil  saber  si  nos  estamos  comunicando  con  el  periférico remoto, o no.    Por eso el siguiente programa contesta con la letra que recibe, INCREMENTADA en uno. Se  sabe que, en la codificación de los caracteres (clásicamente, ASCII: American Standard  Code for Information Interchange), una letra del alfabeto es igual a la anterior, MAS  UNO. Es decir, que la letra 'B' es igual a la letra 'A' MÁS 1. Así que nuestro programa  de  ECHO,  toma  cada  símbolo  que  recibe,  y  devuelve  el  siguiente:  Símbolo  Recibido  MÁS  UNO.  Ahora  sí  es  fácil  en  el  PC  verificar  que  la  comunicación  se  ha  establecido.  Si  escribo un 'A', recibo una 'B', y así para todas las letras.    Si usted escribe HAL, como se llamaba la supercomputadora de la película "2001 Odisea  del Espacio", aparece en la pantalla... IBM. ¿Qué tal?    ["Laboratorios\Lab3\SciComm\2echo1.asm"]  01 ;********************************************************************  02 ; Programa 2Echo1.asm: Test SCI Comm. Support  03 ; Luis G. Uribe C., D11M2007 J16A09 L08J09 S16J2012 (HCS08)  04 ; M10D2013 cosmetics (XmtRcvActivate)  05 ; To be sure that there is "ECHO", return back 'letter+1',  06 ; ..i.e: if Hyperterminal sends 'A', HC908 will return 'B'...  07 ; ..for the range 'A' <= c < 'z'.  08 ; Any other chars are returned back without modification.    09 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10 ; Include files that no define ram  11        NOLIST  12          INCLUDE 'derivative.inc'  13          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM  14        LIST                    ; ..storage and THIS is it's place  15 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 ; 2) DEFINES    17 ram:           SET     Z_RAMStart  ; $80  18 rom:           SET     ROMStart    ; $2080  19 initStack:     EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  20 COP_Disable:   EQU $42  21 ;====================================================================  22 ; MAIN PROGRAM HEADER:  23        ABSENTRY Main  24        ORG rom    25 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  26         sta    SOPT1           ; ..System Options 1  27        ldhx   #initStack       ; Set up SP  28         txs                    ; ...  29 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  30 ; SCI9600N8: No need for previous enabled SCI peripherals  31        SCI9600N8               ; Setup Serial Communications Inter‐  32        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits  33 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  34 Prompt:  161 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 35        lda    #'A'             ; Ascii 'A'  36 XmitLoop:  37        putchar  38        inca  39        cmp    #'z'  40        bls     XmitLoop    41        putc    #$0D            ; Carriage Return (CR: '\r' in C)  42        putc    #$0A            ; Line Feed (LF: '\n')  43                                ; BE PATIENT! To come here you'l need  44                                ; ..217,191 cycles @4,194,304Hz  45 ;************ NOTE: ***************  46 ; To SIMULATE SCI *inputs* use PEMicro SCI1 debugger command.  48 ; It will be instructive to display SCI1S1 (status register), and see  49 ; ..most important flags, bits 7, 6 & 5: XMTRDY, XMTEMPTY & RCVRDY  50 ; While debugging, see how reading Status and writing BUF, clears  51 ; ..the RCV Ready flag; see also how XMT Status work (RDY & EMPTY)    52 inpLoop:  53        getchar                 ; Letters in range 'A' .. 'z' will  54        cmp     #'A'            ; ..be incremented before returning,  55        bhs     inRange         ; ..to be sure that what we see in  56                                ; ..the PC isn't a LOCAL ECHO!  57 Xmit:  putchar  58        bra     inpLoop         ; Stay in this loop, for ever    59 inRange:  60        cmp     #'z'  61        bhs     Xmit            ; Don't increment 'z' either  62 incLetter:  63        inca                    ; Increment this received char  64        bra     Xmit            ; ..and return back it to PC  65        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  66        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  67                            ; This 'nop' MAY be removed for CW 6.3 ...  68 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  69 ; Interrupt Vectors  70        ORG     Vreset  71         DC.W   Main            ; RESET. Maximum priority. Asynch.  72        END    COMENTARIOS a ["Laboratorios\Lab3\SciComm\2echo1.asm"]: No olvide la posición donde DEBE incluir mi librería de comunicaciones:    12          INCLUDE 'derivative.inc'  13          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM    A continuación, como en el ejemplo anterior, las Macros:    31        SCI9600N8               ; Setup Serial Communications Inter‐  32        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits  162 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Primero el programa envía LA MISMA TRAMA del ejercicio anterior. Se llama "PROMPT" a un  texto de advertencia que el computador le envía al usuario para recordarle que está en  posición  de  recibir  los  datos  que  se  le  envíen.  Por  eso  esta  trama  la  llamamos  aquí  Prompt:    34 Prompt:  35        lda    #'A'             ; Ascii 'A'  36 XmitLoop:  37        putchar  38        inca  39        cmp    #'z'  40        bls     XmitLoop  41        putc    #$0D            ; Carriage Return (CR: '\r' in C)  42        putc    #$0A            ; Line Feed (LF: '\n')    Luego recibe lo que le envían desde el PC, verifica que los símbolos se encuentren en  el rango 'A' .. 'z'; si no, retransmite lo mismo que recibió...    52 inpLoop:  53        getchar                 ; Letters in range 'A' .. 'z' will  54        cmp     #'A'            ; ..be incremented before returning,  55        bhs     inRange         ; ..to be sure that what we see in  56                                ; ..the PC isn't a LOCAL ECHO!  57 Xmit:  putchar  58        bra     inpLoop         ; Stay in this loop, for ever    59 inRange:  60        cmp     #'z'  61        bhs     Xmit            ; Don't increment 'z' either    Y  en  caso  afirmativo,  de  que  el  rango  sea  el  apropiado,  retransmite  el  símbolo  recibido, INCREMENTADO:  62 incLetter:  63        inca                    ; Increment this received char  64        bra     Xmit            ; ..and return back it to PC    30) Transmisión por INTERRUPCIONES. Este  programa  es  igual  al  anterior,  pero  usa  las  interrupciones  de  transmisión  para  enviar el 'Message':    ["Laboratorios\Lab3\SciComm\3echoInt‐1.asm"]  01 ;********************************************************************  02 ; Programa 3EchoInt‐1.asm: Test SCI Comm. Support  03 ; Luis G. Uribe C., M13M2007 J16A09 L08J09 L18J2012 (HCS08) D24J2012  04 ; M10D2013 cosmetics (XmtRcvActivate)  05 ; ..Same 2Echo1.asm program but using INTERRUPTS for XMT 'Message'  06 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  07 ; Include files that no define ram  08        NOLIST  09          INCLUDE 'derivative.inc'  10          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM  163 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 11        LIST                    ; ..storage and THIS is it's place  12 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  13 ; 2) DEFINES  14 ram:           SET     Z_RAMStart  ; $80  15 rom:           SET     ROMStart    ; $2080  16 initStack:     EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  17 COP_Disable:   EQU     $42  18 ;====================================================================  19 ; GLOBAL VARIABLES  20        ORG ram  21 XmtNchars:     DS  1           ; Soft Flag for Main: 0 Nothing to Xmt  22 XmtPtr:        DS  2           ; char pointer of next data to Xmt  23 ;********************************************************************  24 ; MAIN PROGRAM HEADER:  25        ABSENTRY Main  26        ORG rom  27 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  28         sta    SOPT1           ; ..System Options 1  29        ldhx   #initStack       ; Set up SP  30         txs                    ; ...  31 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32 ; SCI9600N8: No need for previous enabled SCI peripherals    33        SCI9600N8               ; Setup Serial Communications Inter‐  34        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits  35 ;====================================================================  36 ; Transmit your message: Enque at once full 'Message' Table for Xmt:  37 Prompt:  38        ldhx   #Message         ; Init XmtPtr with Message address;  39         sthx   XmtPtr          ; ..init counter with # of chars  40        mov    #LOW(MessageSize), XmtNchars ; ...  41        XmtIntEn                ; XmtISR will send the message and  42                                ; ..clear 'XmtNchars' when finished  43        cli                     ; <<<DON'T FORGET TO GLOBAL ENABLE INTs  44 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  45 XmtLoop:  46        tst     XmtNchars       ; Wait until done (tst: cmp with 0)  47        bne     XmtLoop         ; (Not needed here. Just: Go to BRA *)  48        bra     *  49 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  50 Message:   DC  '12345', $0D, $0A ; Short message. DC? Define Constant    51 ;;Message: DC  "Este es el mensaje de prueba, "  52 ;;         DC  "primero en transmitirse al PC por interrupciones"  53 ;;         DC  $0D, $0A        ; Carriage Return, Line Feed (Newline)  55 MessageSize:   EQU * ‐ Message    56 ;====================================================================  57 XmtISR:  58        pshh                    ; Not saved by interrupt proc  59        tst     XmtNchars       ; See if done  60         beq    XmtDone         ; 0 chars? Goto Dsb Xmt Int  164 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 61 Xmt:   putcX   XmtPtr          ; NOTE: putcX clears XMTRDY.bit  62        dec     XmtNchars       ; Adjust char counter  63        bra     XmtIsrExit    64 XmtDone:  65        XmtIntDsb  66 XmtIsrExit:  67        pulh                    ; Not restored by interrupt proc  68        rti  69        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  73 ; Interrupt Vectors (ELIMINADAS LÍNEAS 70,71,72 por espacio. Ver SRC ORG)  74        ORG     Vsci1tx  75         DC.W   XmtISR          ; SCI Transmit  76        ORG     Vreset  77         DC.W   Main            ;  RESET. Maximum priority. Asynch.  78        END    COMENTARIOS a ["Laboratorios\Lab3\SciComm\3echoInt-1.asm"]: Como de costumbre, aquí van los Include Files que no definen ram:    09          INCLUDE 'derivative.inc'  10          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM    Se  incluyen  dos  variables  GLOBALES:  un  contador  de  los  caracteres  que  faltan  por  enviar, XmtNchars, y el apuntador a la próxima letra del mensaje que se transmitirá:    20        ORG ram  21 XmtNchars:     DS  1           ; Soft Flag for Main: 0 Nothing to Xmt  22 XmtPtr:        DS  2           ; char pointer of next data to Xmt    La  consabida  definición  de  los  parámetros  de  comunicación,  y  la  Activación  de  ambos  dispositivos, el de transmisión y el de recepción:    33        SCI9600N8               ; Setup Serial Communications Inter‐  34        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits    El  mensaje  se  encuentra  completo  en  la  tabla  'Message'.  Se  origina  cargando  la  dirección del mensaje en la variable que usamos como apuntador:    38        ldhx   #Message         ; Init XmtPtr with Message address;  39         sthx   XmtPtr          ; ..init counter with # of chars    Obsérvese  que  la  dirección  de  la  tabla  'Message'  es:  #Message.  Si  usted  mueve  simplemente 'Message', estará copiando la PRIMERA LETRA del mismo, no su dirección.    A  continuación  se  inicializa  la  variable  XmtNchars  con  el  número  que  representa  el  tamaño  del  mensaje:  #LOW(MessageSize).  Nótense  aquí  dos  cosas;  en  PRIMER  lugar,  el  tamaño es el símbolo MessageSize, que cuando analicemos en seguida la constitución de  la  tabla  veremos  en  detalle  cómo  logramos  que  el  Assembler  calcule  ese  valor  por  nosotros, para que no seamos nosotros los que tengamos que hacer JAMÁS el trabajo de la  computadora. Luego, ese valor MessageSize puede engañar al Assembler, de tal manera que  165 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 éste  no  sepa  con  seguridad  si  ese  símbolo  representa  8  bits  o  16.  Para  evitar  toda  ambigüedad  se  usa  el  operador  LOW,  que  escoge  los  8  bits  inferiores  del  símbolo  en  cuestión.  Y  el  numeral  (#)  se  le  agrega  porque  estamos  usando  la  parte  baja  de  (MessageSize) como una constante (el valor del tamaño reservado para esa tabla):    40        mov    #LOW(MessageSize), XmtNchars ; ...    De  los  dos  procedimientos  que  suelen  emplearse  para  verificar  que  la  transmisión  se  terminó (ZStrings, terminados en NULL, o un Contador indicando la cantidad de símbolos  que falta por transmitir), hemos escogido este último, un contador de letras.  Después  de  esto  estamos  listos  para  transmitir;  para  esto  habilitamos  las  interrupciones del transmisor y, POR ÚLTIMO, TAMBIÉN habilitamos la bandera GLOBAL del  CPU, para que éste acepte y procese interrupts:    41        XmtIntEn                ; XmtISR will send the message and  42                                ; ..clear 'XmtNchars' when finished  43        cli                     ; <<<DON'T FORGET TO GLOBAL ENABLE INTs    El  ciclo  final  consiste  en  esperar  a  que  se  termine  la  transmisión,  analizando  la  variable compartida (global) XmtNchars hasta que se haga cero, en cuyo caso llegamos al  bra * que simula un HALT:    45 XmtLoop:  46        tst     XmtNchars       ; Wait until done (tst: cmp with 0)  47        bne     XmtLoop         ; (Not needed here. Just: Go to BRA *)  48        bra     *    Se ve con claridad que bastaría reemplazar esas 3 instrucciones con el "bra *", porque  después  de  transmitir  los  datos,  no  hay  ningún  proceso  ulterior  en  este  ejercicio.  También se ve que no se necesitan las interrupciones, porque el CPU no está haciendo  nada  mientras  la  ISR  transmite  el  texto...  porque  éste  es  un  ejemplo  muy  sencillo.  Cuando  usted  haga  su  proyecto  tendrá  oportunidad  de  introducir  en  Main,  código  que  trabaje CONCOMITANTEMENTE con la ISR.    Viene la definición de la Tabla:    50 Message:   DC  '12345', $0D, $0A ; Short message. DC? Define Constant  55 MessageSize:   EQU * ‐ Message    El  texto,  muy  corto  en  este  ejercicio,  corresponde  a  los  números  12345  y  luego,  el  Carriage  Return  (\r:  0x0D:  $0D)  y  el  New  Line  (\n:  0x0A:  $0A).  El  DC  es  por  "Define  Constant"; debe utilizarse en el CODE SECTION, pues define espacio en RAM (Flash).    Y  para  que  sea  el  Assembler  el  que  calcule  el  tamaño  del  texto,  a  fin  de  que  si  lo  cambiamos NO TENGAMOS QUE VOLVER A CONTARLO, el truco es generar un símbolo, definido  ahora por la pseudo instrucción EQU, así:    55 MessageSize:   EQU * ‐ Message    Recuerde  que  el  LOCATION  COUNTER  (que  usted  debió  aprender  al  hacer  una  de  las  monografías  de  la  parte  teórica  del  curso)  se  identifica  en  el  CodeWarrior  con  el  símbolo  '*'.  Y  de esa  manera  es  claro  que  el  número  de  bytes es  igual a  la  posición  166 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 siguiente al texto, identificada por el '*', menos la etiqueta que señala el comienzo  de la tabla, Message:    Si quiere ensayar con textos más largos, aquí hay un ejemplo:    51 Message: DC  "Este es el mensaje de prueba, "  52          DC  "primero en transmitirse al PC por interrupciones"  53          DC  $0D, $0A        ; Carriage Return, Line Feed (Newline)  55 MessageSize:   EQU * ‐ Message    La rutina de interrupciones para el transmisor, XmtISR, comienza guardando en el Stack  el valor de la parte H del registro índice: H:X, porque el hardware NO salva ese byte  automáticamente.  Y  como  putcX  emplea  el  registro  índice,  pues  hay  que  resguardar  su  valor al inicio de la ISR, y restaurarlo para finalizar.    57 XmtISR:  58        pshh                    ; Not saved by interrupt proc    Lo primero que se hace es verificar si aún hay algo por transmitir. Si no, terminamos  saltando a la etiqueta XmtDone:    59        tst     XmtNchars       ; See if done  60         beq    XmtDone         ; 0 chars? Goto Dsb Xmt Int    Si  aún  no se  ha  terminado  la  transmisión,  ENVIAMOS  la próxima  letra  empleando  'putcX  XmtPtr' que, como ya dijimos, de paso BORRA el XMTRDY.bit.    61 Xmt:   putcX   XmtPtr          ; NOTE: putcX clears XMTRDY.bit    Se le resta uno al contador de letras, XmtNchars, y se retorna de la interrupción.    62        dec     XmtNchars       ; Adjust char counter  63        bra     XmtIsrExit    Cuando el contador determina que ya se terminó toda la transmisión, se AUTODESHABILITA  la rutina de interrupción:    64 XmtDone:  65        XmtIntDsb    y  se  ejecuta  la  parte  final,  recuperando  el  valor  de  H  que  se  había  guardado  al  principio, y retornando de interrupción:    66 XmtIsrExit:  67        pulh                    ; Not restored by interrupt proc  68        rti    Los Interrupt Vectors son sólo el Vsci1tx y el Vreset:    74        ORG     Vsci1tx  75         DC.W   XmtISR          ; SCI Transmit  76        ORG     Vreset  167 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 77         DC.W   Main            ;  RESET. Maximum priority. Asynch.    31) Cuarto Programa de COMUNICACIONES: Flash LEDs. Este  programa  es  similar  al  anterior,  pero  hace  parpadear  los  LEDs  con  cada  byte  enviado. Además de la librería 'SciComm.inc', en este ejercicio se incluye la librería  'timers8HS.inc':        ["Laboratorios\Lab3\SciComm\3echoInt‐1Leds.asm"]  001 ;********************************************************************  002 ; Programa 3EchoInt‐1Leds.asm: Test SCI Comm. Support  003 ; Luis G. Uribe C., M13M2007 J16A09 L08J09 L18J2012 (HCS08) C27J2012  004 ; ..J03E2013  M10D2013 cosmetics (XmtRcvActivate)  005 ; ..Same 3echoInt‐1.asm, but Flash LEDs with each byte sent.  006 ; In DEMOQUE128, DO ***NOT*** USE PTC5's ASSOCIATED LED, as it is  007 ; ..also the pin used to DISABLE COMM1 !!!, via Jumper J8, THANKS!  008 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  009 ; Include files that no define ram  010        NOLIST  011          INCLUDE 'derivative.inc'  012          INCLUDE 'SciComm.inc' ;  SciComm INC file does NOT use RAM  013        LIST                    ; ..storage and THIS is it's place  014 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  015 ; 2) DEFINES    016 ram:           SET     Z_RAMStart  ; $80  017 rom:           SET     ROMStart    ; $2080  018 ;;;initStack:  EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  019 ;;;COP_Disable: EQU    $42  020 ;====================================================================  021 ; GLOBAL VARIABLES  022        ORG ram    023        NOLIST  024          include 'timers8HS.inc'   ; <<<TIMERS8: Code, Vars & Macros  025        LIST    026 XmtNchars:     DS  1           ; Soft Flag for Main: 0 Nothing to Xmt  027 XmtPtr:        DS  2           ; char pointer of next data to Xmt  028 ;********************************************************************  029 ; MAIN PROGRAM HEADER:  030        ABSENTRY Main  031        ORG rom    032 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  033         sta    SOPT1           ; ..System Options 1  034        ldhx   #initStack       ; Set up SP  035         txs                    ; ...  036 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  037 ; SCI9600N8: No need for previous enabled SCI peripherals  168 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   038        SCI9600N8               ; Setup Serial Communications Inter‐  039        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits  040 ;====================================================================  041 ; Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7)  042        mov    #%11110000, PTCD ; Turn on 4 leds, to show program  043        mov    #%11110000, PTED ; ..start (0? Led ON. 1? Led OFF)  044        mov    #%00011111, PTCDD ; Set Ports bits for output: C=5, D=2  045        mov    #%11000000, PTEDD ; ..(they drive the LEDs on DEMOQE128  046 ; NOTE: PTC5 is programmed for INPUT so writting to PTC5 does NOTHING  047 ;********************************************************************  048 ; NOTE: Genius working at PEMicro use same PTC5 pin to both, drive a  049 ;       LED, AND FOR DISABLING COMMUNICATIONS, via Jumper J8, THANKS!  050 ; So, if you use BOTH COMM1 and Leds, do NOT use PTC5 associated LED.  051 ;********************************************************************    052        Init8Timers  053        cli                     ; <<<DON'T FORGET TO GLOBAL ENABLE INTs  054 Loop:  WaitMS_on 7, #750       ; Show the lights...  055 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  056 ; Transmit your message: Enque at once full 'Message' Table for Xmt:  057 Prompt:  058        ldhx   #Message         ; Init XmtPtr with Message address;  059         sthx   XmtPtr          ; ..init counter with # of chars  060        mov    #LOW(MessageSize), XmtNchars ; ...  061        XmtIntEn                ; XmtISR will send the message and  062                                ; ..clear 'XmtNchars' when finished  063 XmtLoop:  064        tst     XmtNchars       ; Wait until done (tst: cmp with 0)  065        bne     XmtLoop         ; ..    066        WaitMS_on 7, #750       ; Show the lights...  067        ldhx   #Message2        ; Init XmtPtr with Message address;  068         sthx   XmtPtr          ; ..init counter with # of chars  069        mov    #LOW(MessageSize), XmtNchars ; ...  070        XmtIntEn                ; XmtISR will send the message and    071 XmtLoop2:  072        tst     XmtNchars       ; Wait until done (tst: cmp with 0)  073        bne     XmtLoop2        ; ..  074        bra     Loop  075 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  076 Message:   DC  '1234567890', $0D ; Short message. DC? Define Constant  077 MessageSize:   EQU * ‐ Message  078 Message2:  DC  '6789012345', $0D ; Short message. DC? Define Constant    079 ;;Message: DC  "Este es el mensaje de prueba, "  080 ;;         DC  "primero en transmitirse al PC por interrupciones"  081 ;;         DC  $0D, $0A        ; Carriage Return, Line Feed (Newline)  082 ;;MessageEnd:    083 ;====================================================================  169 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 084 XmtISR:  085        pshh                    ; Not saved by interrupt proc  086        tst     XmtNchars       ; See if done  087         beq    XmtDone         ; 0 chars? Goto Dsb Xmt Int    088        lda    #$FF             ; Flash Leds to show program progress  089        eor     PTCD            ; ..  090        eor     PTED            ; ..  091        sta     PTCD            ; ..  092        sta     PTED            ; ..  093 Xmt:   putcX   XmtPtr          ; NOTE: putcX clears XMTRDY.bit  094        dec     XmtNchars       ; Adjust char counter  095        bra     XmtIsrExit    096 XmtDone:  097        XmtIntDsb  098 XmtIsrExit:  099        pulh                    ; Not restored by interrupt proc  100        rti  101        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  102        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  103                            ; This 'nop' MAY be removed for CW 6.3 ...  104 ;====================================================================  105 ; RTC Interrupt Service Routine, si usa Init8Timers...    106 RTC_INTERRUPT:  107        TIMERS8ISR  108 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  109 ; Interrupt Vectors  110        ORG     Vrtc            ; Increasing priority from bottom up  111         DC.W   RTC_INTERRUPT   ; (Si usa Init8Timers...)  112        ORG     Vsci1tx  113         DC.W   XmtISR          ; SCI Transmit  114        ORG     Vreset  115         DC.W   Main            ;  RESET. Maximum priority. Asynch.  116        END    COMENTARIOS a ["Laboratorios\Lab3\SciComm\3echoInt-1Leds.asm"]:   Lo primero es comentar que en la tarjeta DEMOQUE128, NO se puede usar el LED asociado  con  PTC5's  ASSOCIATED  LED,  porque  ese  mismo  pin  se  emplea  en  esa  tarjeta  para  DESHABILITAR LAS COMUNICACIONES !!!, vía Jumper J8. Eso... era INNECESARIO. ¡Gracias a  PEMICRO!    009 ; Include files that no define ram  012          INCLUDE 'SciComm.inc' ; SciComm INC file does NOT use RAM    Incluir la librería timers8HS.inc, que SÍ define variables en RAM:    022        ORG ram  024          include 'timers8HS.inc'   ; <<<TIMERS8: Code, Vars & Macros  170 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   El contador de caracteres y el apuntador a la próxima letra que será transmitida:    026 XmtNchars:     DS  1           ; Soft Flag for Main: 0 Nothing to Xmt  027 XmtPtr:        DS  2           ; char pointer of next data to Xmt    Parámetros de comunicación, y Activación (Activate) de XMT y RCV:    038        SCI9600N8               ; Setup Serial Communications Inter‐  039        XmtRcvActivate          ; ..face: 9600 bps, No parity, 8 bits  INICIALIZACIÓN DE LOS LEDs. Los LEDs en la tarjeta DEMOQE128 son 8, pero NO provienen  de  un  MISMO  registro  de  8  bits  (lo  que  no  le  hubiera  costado  nada  a  la  gente  de  PEMicro), sino que vienen 6 y dos, así: de PTCD: bits 0‐5, y de PTED; bits 6‐7.  Eso...  era INNECESARIO. Gracias, PEMICRO!    Lo primero que se hace cuando se van a programar uno o más bits para SALIDA como es  el caso de los LEDs en este ejercicio, es inicializarlos con el valor que para el  ejercicio sea el más conveniente, ANTES DE DEFINIRLOS COMO SALIDA (Recuerde que, al  encender el MCU, TODOS los puertos son de ENTRADA).    Los  puertos  de  entrada  y  salida  digital  tienen  varios  registros  asociados.  El  más  importante  es  el  de  datos:  PTCD  para  el  puerto  "C",  PTED  para  el  puerto  "E",  y  así  sucesivamente (cfr. el respectivo Reference Manual)    041 ; Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7)  042        mov    #%11110000, PTCD ; Turn on 4 leds, to show program  043        mov    #%11110000, PTED ; ..start (0? Led ON. 1? Led OFF)    Después de que ya tienen un valor de salida asignado, pueden ahora sí programarse para  SALIDA:    044        mov    #%00011111, PTCDD ; Set Ports bits for output: C=5, D=2  045        mov    #%11000000, PTEDD ; ..(they drive the LEDs on DEMOQE128  046 ; NOTE: PTC5 is ‐‐^‐‐‐‐‐ programmed for INPUT: Writting to it does NOTHING      ADVERTENCIA:    A  algunos  GENIOS  que  trabajan  en  PEMicro,  se  les  ocurrió  usar  el  MISMO  pin  PTC5  para manejar simultáneamente un LED Y TAMBIÉN PARA DESHABILITAR LAS COMUNICACIONES,  vía el Jumper J8, Gracias, PEMicro!    Así que si su proyecto usa simultáneamente el puerto de comunicaciones SCI1 y LEDs,  NO use el LED5, asociado con PTC5.    Ahora, inicializa los timers, como siempre se ha hecho, y habilita las interrupciones  globales del CPU:    052        Init8Timers  053        cli                     ; <<<DON'T FORGET TO GLOBAL ENABLE INTs    171 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Primero espera un poco menos de un segundo para que se vean los LEDs:    054 Loop:  WaitMS_on 7, #750       ; Show the lights...    Transmita el mensaje; esta parte es idéntica a la del ejercicio anterior:    057 Prompt:  058        ldhx   #Message         ; Init XmtPtr with Message address;  059         sthx   XmtPtr          ; ..init counter with # of chars  060        mov    #LOW(MessageSize), XmtNchars ; ...  Después de cargar todos los parámetros para el mensaje #1, habilita las interrupciones  de transmisión:    061        XmtIntEn                ; XmtISR will send the message and    Y luego viene el ciclo de espera hasta que se termine de transmitir:    063 XmtLoop:  064        tst     XmtNchars       ; Wait until done (tst: cmp with 0)  065        bne     XmtLoop         ; ..    Cuando haya terminado (XmtNchars vale cero), vuelva a esperar, a fin de que se vean los  LEDs:    066        WaitMS_on 7, #750       ; Show the lights...    Ahora  carga  la  dirección  del  nuevo  mensaje,  Message2.  Note  que  como  ambos  mensajes  tienen la misma longitud, solo se ha empleado UNA longitud para ellos dos. Eso no tiene  por qué ser así. Si sus mensajes tienen diferente longitud, repita el mismo cálculo que  se está haciendo para el mensaje uno, en el mensaje 2, y el resto sigue igual:    067        ldhx   #Message2        ; Init XmtPtr with Message address;  068         sthx   XmtPtr          ; ..init counter with # of chars  069        mov    #LOW(MessageSize), XmtNchars ; ...    Vuelta a habilitar las interrupciones de salida, que se AUTODESHABILITARON al terminar  de transmitir el mensaje anterior:    070        XmtIntEn                ; XmtISR will send the message and    071 XmtLoop2:  072        tst     XmtNchars       ; Wait until done (tst: cmp with 0)  073        bne     XmtLoop2        ; ..  074        bra     Loop    Y repite...    Los mensajes:    076 Message:   DC  '1234567890', $0D ; Short message. DC? Define Constant  077 MessageSize:   EQU * ‐ Message  078 Message2:  DC  '6789012345', $0D ; Short message. DC? Define Constant  172 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La  rutina  de  interrupciones  para  el  transmisor.  La  primera  parte  es  idéntica  al  ejercicio anterior:    084 XmtISR:  085        pshh                    ; Not saved by interrupt proc  086        tst     XmtNchars       ; See if done  087         beq    XmtDone         ; 0 chars? Goto Dsb Xmt Int    Las próximas líneas niegan los bits de los registros C y E, que alimentan a los LEDs:    088        lda    #$FF             ; Flash Leds to show program progress  089        eor     PTCD            ; ..  090        eor     PTED            ; ..  091        sta     PTCD            ; ..  092        sta     PTED            ; ..    Luego se transmite la siguiente letra; se decrementa el contador y se termina la ISR:    093 Xmt:   putcX   XmtPtr          ; NOTE: putcX clears XMTRDY.bit  094        dec     XmtNchars       ; Adjust char counter  095        bra     XmtIsrExit    Cuando  se  transmitido  todo  el  mensaje,  se  AUTODESHABILITA  la  rutina  para  interrupciones, y termina:    096 XmtDone:  097        XmtIntDsb  098 XmtIsrExit:  099        pulh                    ; Not restored by interrupt proc  100        rti    La RTC Interrupt Service Routine:    106 RTC_INTERRUPT:  107        TIMERS8ISR    Y los Interrupt Vectors, uno para mi librería de Timers, Vrtc, otro para el transmisor  de información serial, Vsci1tx, y el siempre necesario, Vreset.    32) Programas de COMUNICACIONES que USTED Debe Hacer: Haga  un  programa  que  reciba  información  por  interrupciones,  que  la  RCVISR  la  encole,  habilite las interrupciones de salida, que la XMTISR la saque de la cola y retransmita  lo  que  'recibe  +  1',  tal  como  hizo  el  programa  de  ECHO  que  ya  describimos  SIN  interrupciones.    Debe poder hacer un programa que simule un CHAT: En usuario escribe desde el PC, el MCU  le  hace  ECHO  normal  (si  recibe  una  A  retransmite  una  A),  y  para  simular  que  el  otro  usuario  escribe  desde  el  MCU,  donde  no  hay  muchos  recursos...  podemos  pegar  varios  mensajes, uno a cada push button de entrada; el MCU lee el botón que se oprimió, encola  el correspondiente mensaje, y realiza la transmisión por interrupciones.    173 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Tiene que tomar previsiones para que no haya conflicto entre el mensaje que usted está  transmitiendo desde el MCU, con el ECO que se le está haciendo a cada letra que reciba  del  PC.  (Por  ejemplo,  si  está  transmitiendo  su  mensaje,  puede  ignorar  por  completo  hacerle  Eco  a  la  información  que  de  pronto  le  puedan  enviar  simultáneamente  desde  el  PC).    De  los  demás  periféricos,  el  PWM  se  usará  en  los  proyectos  en  los  que  haya  sonidos  (tipo Walkman, o piano). El ADC (Analog to Digital Converter) se empleará en proyectos  donde, por ejemplo, quiera cambiarse el volumen del sonido, o la frecuencia del piano,  o si se desea hacer un Voltímetro Digital (el potenciómetro alimenta directamente uno  de los canales del ADC en la tarjeta DEMOQE128)    Una aplicación del PWM es la de hacer que la tarjeta HABLE: Se graba la voz y se la  reproduce vía PWM, de tal manera que la cornetica de la tarjeta suene, lo cual hará con  muy  mala  calidad,  porque  la  respuesta  del  piezoeléctrico,  que  hace  de  transductor  de  sonido, es muy pobre. Pero si usted puede reproducir una palabra, será un buen logro.    Y los otros dispositivos no pueden usarse en el DEMOQE128 sin tocar los conectores, lo  que está prohibido en los cursos de Arquitectura‐I en nuestros laboratorios. Ya tendrán  oportunidad de emplearlos a fondo en los cursos más avanzados de Arquitectura, y en los  de Laboratorios de Proyectos.       PROGRAMAS VARIOS EN ASSEMBLY LANGUAGE   33) BITCOPY, MACRO y Ejemplo. En “Ingeniería Digital”, Cap. 3, se hace una introducción a uno de los más apasionantes  y  antiguos  métodos  de  detección  y  corrección  de  error:  El  Código  Hamming.  No  voy  a  repetir lo que allí escribí para su referencia; bástenos con explorar un ejemplo.    Si se tiene una trama de información de 4 bits, llamados I7I6I5I3, y se quiere detectar  la  presencia  o  ausencia  de  UN  error,  se  le  agrega  UN  BIT  DE  PARIDAD,  del  cual  ya  hablamos  con  antelación  al  explicar  los  aspectos  más  relevantes  de  la  LIBRERÍA  DE  COMUNICACIONES SERIALES: SciComm.inc.    Richard Wesley Hamming hizo en 1950 una codificación simple, pura, clara, eficiente y  eficaz, que permite DETECTAR la aparición de UN ERROR y CORREGIRLO, y detectar (pero NO  corregir) la existencia de DOS errores.    Para  nuestro  caso  de  ejemplo,  Hamming  dispuso  los  bits  en  la  siguiente  TRAMA  (Trama  Hamming): I7I6I5P4I3P2P1, en donde, además de los bits de Información, I7I6I5I3, se han  entrelazado  3  bits  adicionales,  de  paridad:  P4P2P1  (note  que  su  identificación  corresponde a potencias de 2)    En esa trama, Hamming hizo el siguiente planteamiento: Los 3 bits extras, de paridad,  pueden establecer 8 combinaciones entre ellos. La combinación 000 indicará que NO hay  error. Y las restantes 7 combinaciones, del 001 al 111, indicarán la posición en donde  aparece el error, de la posición 1 a la 7, en decimal.    Su  codificación  para  cumplir  con  esta  tan  extraordinaria  aproximación  consistió  en  establecer, para la transmisión, el siguiente cálculo binario:    174 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 P1  =  I3  XOR  I5  XOR  I7,  el  XOR  entre  los  bits  situados  en  las  posiciones  correspondientes a los números impares de la tabla de abajo:    P2 = I3 XOR I6 XOR I7 (observe el por qué en la tabla)    P4 = I5 XOR I6 XOR I7 (observe el por qué en la tabla)    TABLA HAMMING:              Error_Column#   P  P  P   |  I I I I   4  2  1   |  7 6 5 3  ‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐   0  0  0   0 (NO ERROR)   0  0  1   1  . . . .   0  1  0   2  . . . .   0  1  1   3  . . . 1    P1 = I3 XOR I5 XOR I7   1  0  0   4  . . . .    P2 = I3 XOR I6 XOR I7   1  0  1   5  . . 1 .    P4 = I5 XOR I6 XOR I7   1  1  0   6  . 1 . .   1  1  1   7  1 . . .    Donde  haya  un  1,  para  cada  Pn,  se  toma  la  Im  que  tenga  1  (si  es  que  hay)  y  se  la  incluye en la ecuación.    Ver la explicación para un caso de N bits de información esté en Ingeniería Digital.    Supongamos que tenemos la Información de 4 bits del ejemplo y queremos calcular las 3  Paridades. Cómo haría usted los 6 XORs de las 3 ecuaciones booleanas?    Una  forma  inmediata  de  hacer  los  XORs  sería  colocando  los  bits  en  dos  variables  del  CPU, H1 y H2, en las siguientes posiciones, y emplear la instrucción EOR (XOR para el  S08):    H1|I5 I3 I3  H2|I6 I6 I5 XOR  ‐‐+‐‐‐‐‐‐‐‐  H2|p4 p2 p1 (Respuesta Parcial)  H1|I7 I7 I7 XOR  ‐‐+‐‐‐‐‐‐‐‐  H2|P4 P2 P1 (RESPUESTA DEFINITIVA)    A  partir  de  los  bits  dispuestos  en  Trama  Hamming,  I7I6I5P4I3P2P1,  resulta  un  tanto  laborioso reasignar los bits hasta las posiciones requeridas en la tabla para hacer los  XORs, y del resultado de vuelta a la variable que aloja la Trama Hamming:    b7 b6 b5 b4 b3 b2 b1 b0  .. I7 I6 I5 P4 I3 P2 P1 (Variable de 7 bits; aloja la Trama Hamming)    b7 b6 b5 b4 b3 b2 b1 b0  H1|            I5 I3 I3  H2|            I6 I6 I5  175 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 H1|            I7 I7 I7  y de aquí otra vez a la trama:  H2|            P4 P2 P1 (RESPUESTA)    Y si los bits de Información comienzan juntos: I7I6I5I3, hay que moverlos, al comienzo,  para formar la Trama Hamming.    Se transmite la trama así elaborada y en el destino se aplica un cálculo similar, que  usted tiene que revisar en Ingeniería Digital. Al transmitir, se comienza con 4 bits de  información y se forma una trama de 7 bits; al receptor le llegan los 7 bits, se los  procesa y se producen los 4 bits de información, libres de error, mientras no ocurra  más de un error en cada trama de 7.    Finalmente,  se  pueden  detectar  y  corregir  cualquier  cantidad  necesaria  de  errores,  calculando  las  tramas  como  acabo  de  indicar,  y  transmitiéndolas  VERTICALMENTE.  El  receptor las reacomoda HORIZONTALMENTE, y corrige todos los errores anticipados, según  explico en la referencia mencionada.    El  objetivo  de  este  ejercicio  es  mostrar  un  procedimiento,  la  MACRO  BITCOPY,  y  un  ejemplo. A usted le vendría bien aplicarla al cálculo de Hamming, tanto para Generación  del Código Hamming como para la Detección y Corrección del error.    Para un examen o evaluación, los ejercicios suelen tener más de 3 bits de información,  lo que produce tramas que no caben en un BYTE. Sin embargo el procedimiento es similar  al anterior.    En  Ingeniería  Digital  se  encuentra  la  generalización  a  N  bits  de  Información,  y  el  agregado  de  un  bit  de  paridad  extra,  que  permite  DETECTAR  dos  errores,  aunque  el  algoritmo no permita corregirlos.      ["Laboratorios\Proy\Buzzer\BitCopy.asm"]  01 ;********************************************************************  02 ; BitCopy.asm; Luis G. Uribe C., D01L2012  03 ; MACRO BITCOPY and test program.  04 ;    To implement "C" statments like this:  05 ;      PTCD_PTCD0 = PTBD_PTBD5;  06 ;             // Display PWM output, pin PTB5, into LED wired to PTC0  07 ;********************************************************************  08        NOLIST  09          INCLUDE 'derivative.inc'  10        LIST  11 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  12 ; 2) DEFINES  13  14 ram:           SET     Z_RAMStart  ; $80  15 rom:           SET     ROMStart    ; $2080  16 initStack:     EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  17 COP_Disable:   EQU     $42  18  19 ;====================================================================  20 BITCOPY    MACRO   SrcAdd, SrcBit, DstAdd, DstBit  176 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 21 ; Example:  BITCOPY  Src, 3, Dst, 5 ; Use any variables: DIR or EXT  22        lda     \1  23        bit    #1<<(\2&$07) ; If SrcAdd[SrcBit] == 0  24         beq  \@Clr_DstAdd  ; ..goto Clr_DstAdd[DstBit]  25 ;Set_DstAdd[DstBit]:       ; Else,  Set_DstAdd[DstBit]  26        lda     \3  27         ora   #1<<(\4&$07)  28        bra \@cont  29 ;Clr_DstAdd[DstBit]:  30 \@Clr_DstAdd:  31        lda     \3  32         and     #~(1<<(\4&$07))  33 \@cont:  34         sta    \3  35    ENDM  36  37 ;********************************************************************  38 ; MAIN PROGRAM HEADER:  39        ORG ram  40 Src:   DS  1  41 Dst:   DS  1  42  43        ABSENTRY Main  44        ORG rom  46 Main:  lda    #COP_Disable ; RSTE=0: PTA5 is NOT for ~RESET  47         sta    SOPT1       ; ..System Options 1  48        ldhx   #initStack   ; Set up SP  49         txs                ; ...  50  51        lda    #0  52         sta    Src  53        lda    #$FF  54         sta    Dst  55        BITCOPY  Src, 3, Dst, 5 ; copy a 0  56  57        lda    #$FF  58         sta    Src  59        lda    #$0  60         sta    Dst  61        BITCOPY  Src, 2, Dst, 6 ; copy a 1  62  63        bra     *  64        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  65        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  66                            ; This 'nop' MAY be removed for CW 6.3 ...  67 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  68 ; Interrupt Vectors  70        ORG     Vreset  71         DC.W   Main        ;  RESET. Maximum priority. Asynch.  73        END    177 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Laboratorios\Proy\Buzzer\BitCopy.asm"]: En  C  es  relativamente  fácil  expresar  estos  movimientos  de  bits,  de  un  lugar  a  otro,  tales como:    05 ;      PTCD_PTCD0 = PTBD_PTBD5;  06 ;             // Display PWM output, pin PTB5, into LED wired to PTC0    En realidad están asignados a una Struct de BITS, en Union con el Byte (en este caso,  de los registros C y B).  20 BITCOPY    MACRO   SrcAdd, SrcBit, DstAdd, DstBit  21 ; Example:  BITCOPY  Src, 3, Dst, 5 ; Use any variables: DIR or EXT    La Macro tiene 4 parámetros: la dirección (1) de la variable FUENTE (SRC), de donde se  va a tomar el bit (2) para transportarlo a la variable (3) DESTINO, DST, en la posición  de bit (4)    OBSERVACIONES:    A) Las variables pueden estar colocadas en cualquier posición de la RAM, porque se usan  LDA y STA en lugar de MOV.    B)  Los  parámetros  2  y  4,  correspondientes  a  las  posiciones  de  los  bits,  NO  SON  VARIABLES: SON CONSTANTES, porque constantes del 0 al 7 son las que hay que darle a la  BIT del S08 que aquí se usa. Además, ese número NO lleva el símbolo '#': son números  decimales SIMPLES, del 0 al 7, sin ningún otro modificador agregado. >>> IMPORTANTE <<<    22        lda     \1    Se carga en el acumulador el primer parámetro: La variable SRC.    23        bit    #1<<(\2&$07) ; If SrcAdd[SrcBit] == 0    Se hace una comparación del bit SRC deseado, indicado por el parámetro \2, para ver si  está en cero o en uno. Para hacer la comparación se usa la instrucción BIT (Bit Test),  que hace un AND entre el Acumulador y el Operando, y compara el resultado contra cero.    Obsérvese  que  el  Operando  es  un  uno  colocado  en  la  posición  de  bit  indicada  por  el  número \2; esto que acabo de decir se escribe así: #1<<(\2&$07)    ...un  uno  (#1)  colocado  en  la  posición  (<<)  de  bit  indicada  por  el  número  \2  (<<(\2&$07).  Al  parámetro  \2  se  le  ha  hecho  un  AND  (&)  con  el  número  $07  (binario  %111), a fin de que si por error el usuario llama la Macro pasándole un número MAYOR a  7, éste queda automáticamente confinado al rango permitido de 0 a 7.    Claro?    24        beq   \@Clr_DstAdd  ; ..goto Clr_DstAdd[DstBit]    Si el bit es Cero se salta (BEQ) a BORRAR el bit en la dirección destino: Clr_DstAdd.  Como ésta es una dirección que se repetiría cada vez que se empleara la Macro (lo cual  daría error, porque no puede haber más de una definición para una dirección, ni para  178 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 una variable), el truco consiste en anteceder la dirección que se indica dentro de la  Macro, con un \@. Esto le indica al Assembler que genere una dirección diferente, cada  vez que vea ese símbolo \@Clr_DstAdd.    A continuación viene el código que se ejecuta si el bit era UNO, y por tanto hay que  colocar  en  uno  (SET)  el  bit  en  el  destino.  Primero  se  carga  el  acumulador  con  la  variable DST:    26        lda     \3    y, para colocar un uno en el bit deseado de DST, se le hace un OR (ora) así:    27         ora   #1<<(\4&$07)    Aquí, de nuevo, la posición es: un 1 (#1), desplazado a la izquierda (<<) tantos pasos  cuantos indique el último parámetro (\4), al que se lo ha “confinado” automáticamente  al rango de 0 a 7 mediante el AND (\4&$07).    Finalmente  sale  de  la  Macro,  no  sin  antes  guardar  el  valor  del  Acumulador  en  la  variable DST, indicada por el parámetro \3.    28        bra \@cont    Si  el  bit  estuvo  en  cero,  hay  que  colocar  en  cero  el  bit  correspondiente  de  DST,  haciendo  un  AND  del  valor  (del  Acumulador)  con  un  número  formado  por  TODOS  UNOS,  Excepto un CERO en la posición que se quiere borrar:    29 ;Clr_DstAdd[DstBit]:  30 \@Clr_DstAdd:  31        lda     \3  32         and     #~(1<<(\4&$07))    La expresión "and #~(1<<(\4&$07))" hace exactamente eso: un AND (and) con una máscara  de puros UNOS que se consigue haciendo el NEGADO booleano de UN uno en la posición que  se quiere borrar: ~: Negado booleano; la posición que se quiere borrar: (1<<(\4&$07)).    Claro?    Ejemplo:    Se carga un cero en SRC, y se llena de puros unos el DST:    51        lda    #0  52         sta    Src  53        lda    #$FF  54         sta    Dst    Se copia el bit 3 de SRC, al bit 5 de DST:    55        BITCOPY  Src, 3, Dst, 5 ; copy a 0    Con el Debugger puede verse el valor resultante.  179 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se carga ahora al revés, puros UNOS en SRC, y puros CEROS en DST:    57        lda    #$FF  58         sta    Src  59        lda    #$0  60         sta    Dst        Y se copia el bit 2 de SRC, al bit 6 de DST. Véase con el Debugger.    61        BITCOPY  Src, 2, Dst, 6 ; copy a 1    34) USO Básico del ADC, y Display en LEDs. El  siguiente  programa  habilita  el  Analog  to  Digital  Converter  para  realizar  conversiones continuamente (free wheeling) y presentar los resultados en los LEDs. La  entrada  se  toma  del  potenciómetro  del  DEMOQE128.  Sirve  como  ejemplo  para  cuando  necesite hacer conversiones de análogo a digital, programar y manejar los LEDs.    01 ["Laboratorios\Proy\Buzzer\ADC.asm"]  02 ;********************************************************************  03 ; ADC.asm; Luis G. Uribe C., D01L2012 L11M2013  04 ; L11M2013: Clarification about ADC pins ENABLE (APCTLx)  05 ; DESCRIPTION: The ADC module is configured in continuous conversion  06 ; mode, every obtained value is display in port C&E (8‐LEDs).  07 ; PTA0 is the blue  POTENCIOMETER  in  DEMOQE128 !!!  08 ;********************************************************************  09        NOLIST  10          INCLUDE 'derivative.inc'  11        LIST                ; ..storage and THIS is it's place  12 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  13 ; 2) DEFINES  14 ram:           SET     Z_RAMStart  ; $80  15 rom:           SET     ROMStart    ; $2080  16 initStack:     EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  17 COP_Disable:   EQU     $42  18 ;********************************************************************  19 ; MAIN PROGRAM HEADER:  20        ABSENTRY Main  21        ORG rom  22 Main:  lda    #COP_Disable ; RSTE=0: PTA5 is NOT for ~RESET  23         sta    SOPT1       ; ..System Options 1  24        ldhx   #initStack   ; Set up SP  25         txs                ; ...  26 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  27 ; MCU_Init  28 ;;     lda    #$23         ; Enable Bus Clock to the ADC module  29 ;;      sta    SCGC1  30 ;;     clra                ; Disable unused peripherals Bus clock  31 ;;      sta    SCGC2  32 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  180 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 33 ; GPIO_Init  34 ; Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7)  35        mov    #%11110000, PTCD ; Turn on 4 leds, to show program  36        mov    #%11110000, PTED ; ..start (0? Led ON. 1? Led OFF)  37        mov    #%00011111, PTCDD ; Set Ports bits for output: C=5, D=2  38        mov    #%11000000, PTEDD ; ..(they drive the LEDs on DEMOQE128  39 ; NOTE: PTC5 is programmed for INPUT so writting to PTC5 does NOTHING  40 ;********************************************************************  41 ; NOTE: Genius working at PEMicro use same PTC5 pin to both, drive a  42 ;       LED, AND FOR DISABLING COMMUNICATIONS, via Jumper J8, THANKS!  43 ; So, if you use BOTH COMM1 and Leds, do NOT use PTC5 associated LED.  44 ;********************************************************************    45 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  46 ; ADC_configuration  47                            ; Int. disable. Continuous conversion  48        mov    #$20, ADCSC1 ; ..mode and channel 0 active  49        clr     ADCSC2      ; Software trigger selected  50        mov    #$30, ADCCFG ; Input clock/2. Long Sample time  51                            ; ..config. 8‐bit conversion  52 ;====================================================================  53 ; See "02MC9S08QE128RM(ReferenceManual)‐U.pdf", 10.5.1 ADC Module  54 ; ..Initialization Example, 10.5.1.2 Pseudo‐Code Example:  55 ; APCTL1 = 0x02 means: AD1 pin I/O control disabled.  56 ; ..All other AD pins remain general purpose I/O pins  57 ; Manual says: A "1" in APCTLx: Pin I/O control DISABLED.  58 ; ..It means:  A "1" in APCTLx: ADC pin ENABLED !!! <<< <<< <<< <<<  59 ;  60 ; These people can not even write a decent manual...    61        clr     APCTL1      ; ALL ADC pins DISABLE; they work as GPIO  62         bset   APCTL1_ADPC0, APCTL1   ; ENABLE channel0 for ADC input  63        bset    ADCSC1_AIEN, ADCSC1    ; Enable ADC interrupt  64        cli  65        bra     *    66 ;********************************************************************  67 ; interrupt VectorNumber_Vadc ADC_ISR  68 ADC_ISR:  69 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  70 ; NOTE: Reading ADCRL clear COCO Flag, AS IT SHOULD BE DONE ON ALL  71 ; ..PERIPHERALS THAT HAVE DATA BUFFERS TO READ OR WRITE!!!    72        lda     ADCRL       ; Negate before display  73         coma               ; ..(LEDs turn on with 0's)  74         sta    PTCD        ; Move ADC value to port C  75         sta    PTED        ; ..and to port E  76        rti  77        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  78        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  79                            ; This 'nop' MAY be removed for CW 6.3 ...  80 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  181 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 81 ; Interrupt Vectors    82        ORG     Vadc  83         DC.W   ADC_ISR  84        ORG     Vreset  85         DC.W   Main        ; RESET. Maximum priority. Asynch.  86        END  COMENTARIOS a ["Laboratorios\Proy\Buzzer\ADC.asm"]   Importante  notar  que  el  Potenciómetro  Azul  del  DEMOQE128  se  conecta  al  ADC  por  la  entrada PTA0:    07 ; PTA0 is the blue  POTENCIOMETER  in  DEMOQE128 !!!    El  siguiente  código  no  se  ejecuta,  pero  se  lo  ha  incluido  de  todas  maneras  para  ilustrar  la  configuración  del  MCU  cuando  hay  que  habilitar  el  módulo  de  ADC.  Está  comentado porque las acciones que aquí se tomarían, se han ejecutado durante el Power  On Reset, y como no las hemos modificado, no necesitamos repetirlas.    PERO, si se cambiara alguno de estos "settings", la forma de VOLVER a configurarlos es  ejecutando estas  4  líneas  de  código.  Para  mayor  información  hay  que  revisar, como  de  costumbre, el Reference Manual.    26 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  27 ; MCU_Init  28 ;;     lda    #$23         ; Enable Bus Clock to the ADC module  29 ;;      sta    SCGC1  30 ;;     clra                ; Disable unused peripherals Bus clock  31 ;;      sta    SCGC2    La explicación de la inicialización de los 8 LEDs es exactamente igual a la que se hizo  en: 30) CUARTO PROGRAMA DE COMUNICACIONES, y no la vamos a repetir aquí.    Como  la  salida  del  potenciómetro alimenta  el  terminal  PTA0,  en  donde  se  encuentra  el  canal 0 del ADC, su configuración consiste en activar el canal 0 de entrada Analógica,  y  poner  el  ADC  en  modo  de  Conversión  Continua,  activado  por  Software  (cuando  podría  activarse por Hardware). También se selecciona conversión de 8 bits, porque el ADC del  MC9S08QE128 puede trabajar con otras longitudes para el resultado de la conversión (así  se obtiene mayor resolución). El código:    46 ; ADC_configuration  47                            ; Int. disable. Continuous conversion  48        mov    #$20, ADCSC1 ; ..mode and channel 0 active  49        clr     ADCSC2      ; Software trigger selected  50        mov    #$30, ADCCFG ; Input clock/2. Long Sample time  51                            ; ..config. 8‐bit conversion    La  siguiente  es  una  aclaratoria  sobre  uno  de  los  tópicos  peor  descritos  en  el  "02MC9S08QE128RM(ReferenceManual)‐U.pdf", 10.5.1.    Hay un ejemplo de inicialización, 10.5.1.2, escrito en Pseudo‐Código y comentado:  182 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Primero  se  deshabilita  el  control  de  I/O  para  el  pin  o  terminal  AD1;  los  demás  terminales permanecen como pines de I/O de propósitos generales:    55 ; APCTL1 = 0x02 means: AD1 pin I/O control disabled.  56 ; ..All other AD pins remain general purpose I/O pins    El  manual,  bien  oscurecido,  dice  que  un  UNO  en  un  pin  x  del  APCTL  (bit  APCTLx)  le  DESHABILITA a ese pin el CONTROL DE I/O.    POR  TANTO,  ESE  pin  x  del  APCTL,  por  haberlo  programado  a  UNO,  hace  que  el  correspondiente pin del ADC SE HABILITE (para funciones de ADC)    Mucho  más  sencillo  hubiera  sido  expresar  esta  funcionalidad  de  la  siguiente  manera  (redactado lo más en POSITIVO que es posible):    "Para HABILITAR un pin x del conversor ADC, para que funcione como ENTRADA ANALÓGICA,  se coloca un UNO en el correspondiente bit x del registro Analógico para el Control de  Pines: APCTL (bit APCTLx).    "Para  HABILITAR  como  I/O  digital  un  pin  x,  de  los  que  pueden  actuar  como  entrada  analógica del ADC (24 canales analógicos en el MC9S08QE128), se coloca un CERO en el  correspondiente bit x del APCTL, APCTLx.    "Después de Power On Reset (POR), todos los pines que potencialmente pueden servir como  entradas analógicas del ADC, son I/O DIGITALES GENÉRICOS y así, pueden programarse para  entrada  o  salida,  colocándoseles,  o  no,  resistencias  de  Pull‐up,  y  todas  las  demás  funcionalidades  definidas  para  las  entradas  y  salidas  digitales  genéricas.  Por  eso  nuestros ejemplos nunca habilitaron esos pines mediante un CERO en los bits APCTLx: es  el valor estándar.    57 ; Manual says: A "1" in APCTLx: Pin I/O control DISABLED.  58 ; ..It means:  A "1" in APCTLx: ADC pin ENABLED !!! <<< <<< <<< <<<    Se  programa  el  canal  cero  del  ADC  para  que  manipule  entradas  analógicas;  los  otro  7  bits de ESE específico grupo de 24, en el MC9S08QE128, quedan en CERO, y por lo tanto  siguen siendo Entradas y Salidas Digitales Genéricas, GPIO.    61        clr     APCTL1      ; ALL ADC pins DISABLE; they work as GPIO  62         bset   APCTL1_ADPC0, APCTL1   ; ENABLE channel0 for ADC input    En nuestro caso, podríamos haber hecho solamente el BSET ya que, como hemos explicado,  los  otros  7  bits  del  APCTL1  están  en  CERO  (no  necesitan  un  CLR).  Desde  luego,  este  código se deja acá, por si usted tiene necesidad de revertir en algún otro ejercicio,  las entradas de Analógicas, a GPIO.    FINALMENTE se habilita el ADC para que Interrumpa, lo mismo que al CPU, y se simula un  HALT mediante el BRA *:    63        bset    ADCSC1_AIEN, ADCSC1    ; Enable ADC interrupt  64        cli  65        bra     *    183 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 NOTA: Como no fue mi intención establecer en este documento un subsistema para el ADC,  programado con todas las reglas de Top‐Down Design y Bottom‐Up Coding que se emplearon  en las librerías que desarrollé para TEMPORIZADORES y COMUNICACIONES SERIALES, no usé  nombres  de  un  valor  semántico  más  elevado,  establecidos  mediante  ALIAS  a  las,  para  nuestros  propósitos,  oscuras  identificaciones  que  definieron  los  diseñadores del  MCU.  Hubieran sido definiciones algo así como "AdcIntEn" (parangón del "XmtIntEn" empleado  en anteriores ejercicios...)    Cada vez que se produce una conversión, se coloca en UNO la bandera de READY, que en el  conversor ADC se llama COCO: COnversion COmplete. Lo interesante del periférico ADC de  este  MCU  es  que  BASTA  CON  LEER  EL  REGISTRO  DE  DATOS,  ADCRL,  para  AUTOMÁTICAMENTE  generar el Acknowledge de la Interrupción (reposicionando COCO en CERO).    Esto,  aunque  opera  como  las  minicomputadoras  Digital  (PDP,  VAX)  lo  hacían,  o  como  funciona  el  PC/AT  Compatible,  arquitectura  desarrollada  por  la  IBM  y  aún  en  uso  actualmente,  es  decir,  que  OPERA  COMO  TODOS  LOS  PERIFÉRICO  DEBÍAN  HACERLO,  introduce  una nueva IRREGULARIDAD:    ‐ Periféricos a los que el INTACK se les da por software, leyendo la bandera de READY  en UNO y, a continuación, leyendo el DATA BUFFER, talo como está definido el periférico  SCI de Comunicaciones Seriales.    ‐  Periféricos  que  reconocen  un  INTACK  con  el  solo  hecho  de  leer  el  vector  de  interrupciones  asociado  (la  dirección  de  la  IRQISR.  Este  modo  se  usó  en  el  ancestro  directo del MC9S08, el MC908, sin la S).    ‐ El ADC sólo requiere que se lea el DATABUF para aceptar un ACK (COCO retorna a CERO)    En la rutina de interrupción, ADC_ISR, se lee el valor del BUFFER, ADCRL (Register LOW,  que  proporciona  los  8  bits  requeridos).  Esto  produce,  como  ya  dijimos,  un  ACK  que  resetea el COCO. Invertimos el valor negándolo (con COMA, 1's Complement Accumulator),  porque como ya sabemos, los LEDs en la tarjeta de desarrollo DEMOQE128 se encienden con  CEROS.  El  valor  lo  llevamos  a  los  dos  registros  en  que  los  genios  de  PE  Micro  descompusieron los 8 LEDs (6 en PTC y 2 en PTE). Por último, la rutina hace un RTI.    68 ADC_ISR:  72        lda     ADCRL       ; Negate before display  73         coma               ; ..(LEDs turn on with 0's)  74         sta    PTCD        ; Move ADC value to port C  75         sta    PTED        ; ..and to port E  76        rti    NOTE: No se ha salvado por programa el registro H al comienzo, porque esta rutina no  usa para nada el registro índice, H:X.    35) Uso Básico del ADC, Reversar Datos Antes de ir a los LEDs. Suponga que va a alojar dentro de una caja su tarjeta de desarrollo, y que quiere dejar  una ventanilla por donde puedan verse desde el exterior. Si mira bien, notará que los  bits quedan al revés, con el bit0 a la izquierda. Así que la secuencia 1, 2, 3, 4, 5,  6, 7 se verá como: 1000.., 0100.., 0110.., 0001.., 1001.., 0011.., 0111    184 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Eso me da la base a decir que PE Micro ALAMBRÓ LOS LEDs AL REVÉS.    Este ejercicio es parecido al anterior, pero a los bits del resultado de la conversión  deL ADC se los reordena para que la información en binario que se presenta mediante los  LEDs, quede AL DERECHO.    ["Laboratorios\Proy\Buzzer\ADC_Reverse.asm"]  001 ;********************************************************************  002 ; ADC_Reverse.asm; Luis G. Uribe C., D01L2012  003 ; DESCRIPTION: The ADC module is configured in Continuous Conversion  004 ; ..Mode, every obtained value is display in port C&E (8‐LEDs).  005 ; PTA0 is the blue  POTENCIOMETER  in  DEMOQE128 !!!  006 ;  007 ;********************************************************************  008 ; NOTE from Luis G. Uribe C. Desktop:  009 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  010 ; Genius working at PEMicro not only used same PTC5 pin to both drive  011 ; an LED, AND FOR DISABLING COMMUNICATIONS, via Jumper J8,  !THANKS!,  012 ; but REVERSE WIRED the LEDs with Bit0 at the LEFT!!        !THANKS!,  013 ; and REVERSE DRIVE the LEDs so they light with a '0'       !THANKS!,  014 ; and REVERSE WIRED the POTENCIOMETER, to provide 0 Volts when fully  015 ; .. turned CLOCKWISE, and 3 Volts when turned BACKWARDS    !THANKS!.  016 ; .. Did they ever use a KNOB to raise the music volume aloud  ???  017 ;********************************************************************  018        NOLIST  019          INCLUDE 'derivative.inc'  020        LIST                ; ..storage and THIS is it's place  021 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  022 ; 2) DEFINES    023 ram:           SET     Z_RAMStart  ; $80  024 rom:           SET     ROMStart    ; $2080  025 initStack:     EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  026 COP_Disable:   EQU     $42    027 ;====================================================================  028 BITCOPY    MACRO   SrcAdd, SrcBit, DstAdd, DstBit  029 ; Example:  BITCOPY  Src, 3, Dst, 5 ; Use any variables: DIR or EXT  030        lda     \1  031        bit    #1<<(\2&$07) ; If SrcAdd[SrcBit] == 0  032         beq  \@Clr_DstAdd  ; ..goto Clr_DstAdd[DstBit]  033 ;Set_DstAdd[DstBit]:       ; Else,  Set_DstAdd[DstBit]  034        lda     \3  035         ora   #1<<(\4&$07)  036        bra \@cont  037 ;Clr_DstAdd[DstBit]:  038 \@Clr_DstAdd:  039        lda     \3  040         and     #~(1<<(\4&$07))  041 \@cont:  042         sta    \3  043    ENDM  185 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 044 ;====================================================================  045 ; Global Variables    046        ORG ram  047 ADC_tmp1       DS.B    1  048 ADC_tmp2       DS.B    1    049 ;********************************************************************  050 ; MAIN PROGRAM HEADER:  051        ABSENTRY Main  052        ORG rom    053 Main:  lda    #COP_Disable ; RSTE=0: PTA5 is NOT for ~RESET  054         sta    SOPT1       ; ..System Options 1  055        ldhx   #initStack   ; Set up SP  056         txs                ; ...  057 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  058 ; This is the 'MCU_Init' from the Original C program. However, I am  059 ; ..using POR (Power On Reset) defaults: Clock to EVERY peripheral.  060 ;  061 ; MCU_Init  062 ;;     lda    #$23         ; Enable Bus Clock to the ADC module  063 ;;      sta    SCGC1  064 ;;     clra                ; Disable unused peripherals Bus clock  065 ;;      sta    SCGC2  066 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  067 ; GPIO_Init  068 ; Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7)  069 ;  070 ; NOTE: For OUTPUT Pins, always Set‐up first the initial value you  071 ;       desire for them, and THEN program the pin for Output.    072        mov    #%11110000, PTCD ; Turn on 4 leds, to show program  073        mov    #%11110000, PTED ; ..start (0? Led ON. 1? Led OFF)  074        mov    #%00011111, PTCDD; Set Ports bits for output: C=5, D=2  075        mov    #%11000000, PTEDD; ..(they drive the LEDs on DEMOQE128  076 ; NOTE: PTC5 is programmed for INPUT so writting to PTC5 does NOTHING    077 ;********************************************************************  078 ; NOTE: Genius working at PEMicro use same PTC5 pin to both, drive a  079 ;       LED, AND FOR DISABLING COMMUNICATIONS, via Jumper J8, THANKS!  080 ; So, if you use BOTH COMM1 and Leds, do NOT use PTC5 associated LED.  081 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  082 ; ADC_configuration  083                            ; Int. disable. Continuous conversion  084        mov    #$20, ADCSC1 ; ..mode and channel 0 active  085        clr     ADCSC2      ; Software trigger selected  086        mov    #$30, ADCCFG ; Input clock/2; Long Sample time  087                            ; ..config; 8‐bit conversion  088        clr     APCTL1      ; ADC0 pin disable; it works as GPIO...  089 ; ===================================================================  090        bset    APCTL1_ADPC0, APCTL1; Select channel for ADC0 input  186 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 091        bset    ADCSC1_AIEN, ADCSC1 ; Enable ADC interrupt  092        cli                 ; Global Enable CPU Interrupts.  093        bra     *    094 ;********************************************************************  095 ; interrupt VectorNumber_Vadc ADC_ISR  096 ADC_ISR:  097        ;*******************************  098        ; NOTE: Reading ADCRL clear COCO Flag, AS IT SHOULD BE DONE ON  099        ; ..ALL PERIPHERALS THAT HAVE DATA BUFFERS TO READ OR WRITE!!!  100        ;*******************************    101        lda     ADCRL       ; Negate before display, THANKS PE Micro!  102         coma               ; ..(LEDs turn on with 0's)  103         sta    ADC_tmp1    104        ; Exchange bit positions, THANKS PE Micro!  105        BITCOPY ADC_tmp1, 0, ADC_tmp2, 7  106        BITCOPY ADC_tmp1, 1, ADC_tmp2, 6  107        BITCOPY ADC_tmp1, 2, ADC_tmp2, 5  108        BITCOPY ADC_tmp1, 3, ADC_tmp2, 4  109        BITCOPY ADC_tmp1, 4, ADC_tmp2, 3  110        BITCOPY ADC_tmp1, 5, ADC_tmp2, 2  111        BITCOPY ADC_tmp1, 6, ADC_tmp2, 1  112        BITCOPY ADC_tmp1, 7, ADC_tmp2, 0    113        mov    ADC_tmp2, PTCD   ; Move ADC value to port C  114        mov    ADC_tmp2, PTED   ; ..and to port E  115        rti  116        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  117        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  118                            ; This 'nop' MAY be removed for CW 6.3 ...  119 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  120 ; Interrupt Vectors    121        ORG     Vadc  122         DC.W   ADC_ISR    123        ORG     Vreset  124         DC.W   Main        ; RESET. Maximum priority. Asynch.    125        END    COMENTARIOS a ["Laboratorios\Proy\Buzzer\ADC_Reverse.asm"]: Se incluye la Macro BITCOPY, anteriormente explicada y usada:  028 BITCOPY    MACRO   SrcAdd, SrcBit, DstAdd, DstBit    Dos variables Globales temporales (Bytes) para manipular el valor de la conversión:  046        ORG ram  047 ADC_tmp1       DS.B    1  048 ADC_tmp2       DS.B    1  187 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se  programan  los  LEDs  y  el  ADC  como  en  el  ejercicio  anterior;  y  se  culmina  con  una  espera eterna, mediante el BRA *.    La ISR del conversor comienza como antes, leyendo el BUFFER: ADCRL y negándolo (COMA:  1's COMPLEMENT ACCUMULATOR), y lo almacena en ADC_tmp1:    101        lda     ADCRL       ; Negate before display, THANKS PE Micro!  102         coma               ; ..(LEDs turn on with 0's)  103         sta    ADC_tmp1    Usa ADC_tmp1 como variable SRC para la Macro BITCOPY, y cruza los bits almacenándolos  en la segunda variable temporal, ADC_tmp2. Note cómo copia el bit 0 al 7, el 1 al 6, 2  al 5, 3 al 4, 4 al 3, 5 al 2, 6 al 1 y finalmente, el bit 7 al bit 0.    104        ; Exchange bit positions, THANKS PE Micro!  105        BITCOPY ADC_tmp1, 0, ADC_tmp2, 7  106        BITCOPY ADC_tmp1, 1, ADC_tmp2, 6  107        BITCOPY ADC_tmp1, 2, ADC_tmp2, 5  108        BITCOPY ADC_tmp1, 3, ADC_tmp2, 4  109        BITCOPY ADC_tmp1, 4, ADC_tmp2, 3  110        BITCOPY ADC_tmp1, 5, ADC_tmp2, 2  111        BITCOPY ADC_tmp1, 6, ADC_tmp2, 1  112        BITCOPY ADC_tmp1, 7, ADC_tmp2, 0    Terminada la reasignación de bits, los presentamos en los 8 LEDs y terminamos con RTI:    113        mov    ADC_tmp2, PTCD   ; Move ADC value to port C  114        mov    ADC_tmp2, PTED   ; ..and to port E  115        rti    Dado que esta IRQ no resguarda por programa el registro H, usted tiene que asegurarse  de que la Maco BITCOPY TAMPOCO USE el registro Índice H:X.    DETALLES, DETALLES...    UN PUNTO MÁS:    Como el POTENCIÓMETRO TAMBIÉN SE CABLEÓ AL REVÉS: significa que al estar completamente  a  la  izquierda  entrega  el  mayor  voltaje  (3V),  y  al  rotarlo  hacia  la  derecha  baja  el  voltaje  linealmente  hasta  llegar a  CERO  V, una  truco  para  arreglar  ese  problema,  por  software, es convertir las medidas que en hexadecimal van de $FF a $00 (de izquierda a  derecha), en medidas que vayan de $00 a $FF. Se ve que basta con NEGAR la medida del  conversor para solucionar el problema del alambrado ilógico del potenciómetro.    Pero, YA HAY UN 'COMA' para resolver el problema de que los LEDs encienden al revés, en  CERO:    102         coma               ; ..(LEDs turn on with 0's)  Recuerde que negar dos veces equivale a NO NEGAR.    Por  tanto,  NO  HAY  NECESIDAD  del  'COMA  de  la  línea  102;  basta  con  eliminarla,  o  comentarla, y ahora todo parecerá funcionar bien.  188 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 36) Contador de UNOS. Contar  los  bits  que  están  en  uno,  o  en  cero,  en  una  variable,  es  un  clásico  en  los  cursos de programación.    En C dos son las soluciones más publicitadas; la primera consiste en tener una variable  auxiliar que vale ...001 y se va desplazando a la izquierda, N veces (8 para un byte):  ...001  ...010  ...100.  Cada  vez  se  hace  un  AND  con  el  operando  y  si  el  resultado  es  diferente de cero se incrementa el contador de unos.    Una, más interesante aún, consiste en aplicar la expresión x &= (x ‐ 1); que ELIMINA de  'x' el UNO que está más a la izquierda:    while( x ) {      cnt ++;      x &= ( x ‐ 1 );  }    Una ventaja de esta última aproximación consiste en que sólo hay que repetir el ciclo  mientras haya UNOS, no necesariamente 8 veces (8, para Bytes)      El ejemplo que incluyo a continuación, hace uso de la instrucción de LEFT SHIFT, que  desplaza la variable en cuestión, una posición a la izquierda y el bit que SALE queda  almacenado en la bandera C (Carry). Se incrementa el contador cada vez que C == 1. Si  se repite el código 8 veces (para Bytes), habremos contado el número de unos.    Un atajo consiste en disponer de una segunda condición, para detener el procedimiento  si  la  variable  llega  a  cero  (antes  de  terminar  los  8  ciclos).  Esto  agrega  la  misma  ventaja del último ejemplo en C, y también permite repetir el ciclo sólo mientras haya  UNOS, no necesariamente 8 veces (8, para Bytes)    Este  ejercicio  está  pensado  para  ejecutarse  con  el  DEBUGGER,  paso  a  paso,  a  fin  de  poder  introducir  las  entradas  a  mano  (simuladas),  vía  PTAD.  Usted  puede  emplear  los  interruptores  de  la  tarjeta  DEMOQE128  que  usan,  por  ejemplo,  el  PTC,  o  usar  otra  variable puesta en el programa para ese propósito.    ["Books\Interfacing‐HCS08\Examples\bitcount3.asm"]  01 ;********************************************************************  02 ; BitCount3.asm, HCS08_CPU, Luis G. Uribe C., M05F2013  03 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  04 ; Include files  05        NOLIST  06          INCLUDE 'derivative.inc'  07        LIST  08 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  09 ; Parameter definitions  10 ram:           SET     Z_RAMStart  ; $80. Cfr. 'MC9S08QE128‐U.inc'  11 rom:           SET     ROMStart    ; $2080  12 initStack:     EQU     RAMEnd + 1  ; $17FF + 1 = $1800  13 COP_Disable:   EQU     $42  14 ; ===================================================================  189 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 15 ; Begin of Code Section  16        ABSENTRY Main       ; Export symbol (DEBUGGER entry)  17        ORG     rom         ; $2080: HCS08 ROMStart (Flash)  18    ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  19    ; *** ALWAYS *** include the following 4 instructions  20 Main:  lda    #COP_Disable  21         sta    SOPT1       ; System Options 1  22        ldhx   #initStack   ; Init SP. H:X <‐ $1800  23         txs                ; .. SP <= $17FF  24 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  25        clra                ; Initialize A to 0  26        ldx     PTAD        ; Load operand into X  27 Loop:  lslx                ; Shift out a bit of operand  28         beq    Zero  29        adc    #0           ; Accumulate it  30        bra     Loop  31 Zero:  adc    #0           ; Accumulate it  32        bra     *  33        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  34        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  35                            ; This 'nop' MAY be removed for CW 6.3 ...  36 ;********************************************************************  37 ; Interrupt Vectors    38        ORG     Vreset  39         DC.W   Main        ; RESET: HCS08 Power On (PON) procedure.  40    END    COMENTARIOS a ["Books\Interfacing-HCS08\Examples\bitcount3.asm"]:   El contador será el Acumulador, y se lo inicializa en cero. Sólo para el ejercicio, se  supone que la entrada que funciona como el operando a la que se le contarán los unos se  toma del Puerto A (PTAD); como ya se dijo, usted puede colocar otra variable si así lo  desea.  El  operando  se  lee  desde  PTAD  y  se  almacena  en  la  parte  BAJA  del  registro  Índice: X    25        clra                ; Initialize A to 0  26        ldx     PTAD        ; Load operand into X    El  ciclo  Desplaza  el  operando  (X)  un  bit  a  la  Izquierda;  si  encuentra  que  esta  operación produjo Cero (Conjunto de Instrucciones Enriquecido, que compara el resultado  de la mayoría de las operaciones contra CERO), se termina la acción.    27 Loop:  lslx                ; Shift out a bit of operand  28         beq    Zero    Si aún la variable no vale Cero, se suma el Carry al Acumulador; como cada bit que sale  del  operando  al  hacerse  un  desplazamiento,  se  almacena  en  el  bit  de  Carry,  C,  éste  quedará  en  Cero  o  en  Uno  según  corresponda.  Si  se  suma  el  Carry  a  Acumulador,  se  produce  un  Incremento  si  lo  que  se  desplazó  era  un  UNO;  si  era  un  CERO,  la  suma  no  190 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 genera ningún incremento. De esta manera se van contabilizando cuántos unos había en el  operando. Luego repite el ciclo.    Note que no hay una instrucción simple para sumar el Carry al Acumulador; lo que hay es  un compuesta, ADC, que suma un operando al Acumulador, y a la vez le suma el valor del  Carry.  Como  lo  único  que  queremos  es  sumar  el  Carry,  y  nada  más,  usamos  ADC  con  un  operando de CERO: ADC #0    29        adc    #0           ; Accumulate it  30        bra     Loop    Si la variable sí llegó a Cero, se termina el procedimiento, sumando antes el Carry al  Acumulador, lo que produce un Incremento si lo que se desplazó era un UNO.    31 Zero:  adc    #0           ; Accumulate it  32        bra     *    37) Programación de un TONO en el BUZZER. Recuerde que estamos en una sección de "Misceláneos"; estos programas se incluyen como  EJEMPLO  para  cuando  necesiten  emplear  funcionalidades  similares.  No  están  construidos  con  todas  las  técnicas  profesionales  que  se  emplearon  en  las  primeras  secciones,  que  culminaron con las Colas y las Comunicaciones Seriales.    Sin embargo, si llega a necesitar programar el Buzzer que trae la tarjeta de desarrollo  DEMOQE128, esta es una GUÍA ELEMENTAL.    NOTA:  Este  programa  en  Assembly  Language  lo  adapté  tomándolo  de  uno  de  Freescale:  PWM.c.  FUNCIONA  BIEN  y  ha  sido  empleado  durante  varios  trimestres  como  base  para  proyectos tales como Morse, Piano, Walkman, generación de sonidos diversos, etc.    Sin embargo, ahora que fui a escribir la explicación línea por línea, encuentro que en  PWM.c, y por tanto aquí también, se programa como salida la línea PTC0, sin que en este  momento yo encuentre una justificación apropiada para esto.    Ustedes pueden eliminar esa línea, y agradeceré me reporten si todo funciona bien, para  removerlas del código en futuras ediciones.    ["Laboratorios\Proy\Buzzer\Buzzer.asm"]  01 ;********************************************************************  02 ; Buzzer.asm; Luis G. Uribe C., D01L2012  03 ; DESCRIPTION: This project uses the PWM functionality of the TPM1  04 ; module. A PWM signal is generated and when the MCU is interrupted.  05 ; PTB5 is connected to BUZZER in DEMOQE128 !!  06 ;********************************************************************  07        NOLIST  08          INCLUDE 'derivative.inc'  09        LIST                    ; ..storage and THIS is it's place  10 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  11 ; 2) DEFINES    12 ram:           SET     Z_RAMStart  ; $80  191 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 13 rom:           SET     ROMStart    ; $2080  14 initStack:     EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  15 COP_Disable:   EQU     $42    16 ;********************************************************************  17 ; MAIN PROGRAM HEADER:  18        ABSENTRY Main  19        ORG rom    20 Main:  lda    #COP_Disable     ; RSTE=0: PTA5 is NOT for ~RESET  21         sta    SOPT1           ; ..System Options 1  22        ldhx   #initStack       ; Set up SP  23         txs                    ; ...  24 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  25 ; MCU_Init  26        lda    #$20             ; Enable Bus Clock to the TPWM1 module  27         sta    SCGC1  28        clra                    ; Disable unused peripherals Bus clock  29         sta    SCGC2  30 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  31 ; GPIO_Init  32        clr     PTCD            ; Put 0's in PTC0 port  33        mov    #$01, PTCDD      ; Configure PTC0 pin as output  34 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  35 ; TPM_configuration  36        mov    #$68, TPM1C1SC   ; Chan.1 IntEn; PWM Edge Align. Or $24  37 ;*******************************  38 ; PIANO FREQUENCY TABLE FROM DO (C)... SI (B), DO (C)  39 ; C 1  D 1  E 1  F 1  G 1  A 1  B 1  C 2  40 ; 2094 1976 1760 1661 1480 1319 1175 1047    41 FREQ   EQU     2094  42 FREQ_4 EQU     FREQ >> 2       ; 25% Dutty Cycle    43        ldhx   #FREQ            ; Really... it is PERIOD...  44         sthx   TPM1MOD  45        ldhx   #FREQ_4          ; 25% Dutty Cycle  46         sthx   TPM1C1V  47        mov    #%1000, TPM1SC   ; Bus_rate_clock/1  = TPM Clock Source  48 ; More Dividers: Table 16‐4 in 02MC9S08QE128RM(ReferenceManual)‐U.pdf  49 ;      mov    #%1111, TPM1SC   ; Bus_rate_clock/128= TPM Clock Source  50 ;      mov    #%1110, TPM1SC   ; Bus_rate_clock/64 = TPM Clock Source  51 ;      mov    #%1000, TPM1SC   ; Bus_rate_clock/1  = TPM Clock Source    52 ;====================================================================  53        cli  54        bra     *    55 ;********************************************************************  56 ; interrupt VectorNumber_Vtpm1ch1 TPM_ISR  57 TPM_ISR:  58        lda     TPM1C1SC        ; Clear TPWM flags  192 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 59        bclr    7, TPM1C1SC     ; Two‐step flag acknowledgement  60        rti  61        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  62        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  63                            ; This 'nop' MAY be removed for CW 6.3 ...  64 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  65 ; Interrupt Vectors    66        ORG     Vtpm1ch1  67         DC.W   TPM_ISR  68        ORG     Vreset  69         DC.W   Main            ; RESET. Maximum priority. Asynch.  70        END    COMENTARIOS a ["Laboratorios\Proy\Buzzer\Buzzer.asm"]: Los generadores de frecuencia que empleamos en este MCU, son fundamentalmente los PWM,  que generan señales "moduladas" por amplitud de pulso, Pulse Width Modulation.    En particular, se usa la funcionalidad PWM del módulo TPM1.    04 ; A PWM signal is generated and when the MCU is interrupted.  05 ; PTB5 is connected to BUZZER in DEMOQE128 !!      La  inicialización  del  MCU  incluye  habilitar  el  Bus  Clock  para  el  módulo  TPM1;  a  los  periféricos  no  utilizados  se  les  deshabilita  el  Bus Clock.  Usted  debe  revisar  con  el  Reference Manual, si los valores aquí programados para habilitar el TPMWM1 son o no los  estándar cuando hay un POR. De ser así, puede eliminar (como ya se hizo en el caso del  ADC) este código sin ningún problema    25 ; MCU_Init  26        lda    #$20             ; Enable Bus Clock to the TPWM1 module  27         sta    SCGC1  28        clra                    ; Disable unused peripherals Bus clock  29         sta    SCGC2      Lo siguiente es programar el terminal PTC0 como salida.    Como dije anteriormente, NO encuentro ahora una JUSTIFICACIÓN apropiada para esto.    Luego  de  que  usted  logre  que  el  programa  funcione  (¡TODOS  funcionan!),  eliminen  esas  dos (2) líneas y reporte si todo funciona bien, para proceder a removerlas del código  en futuras ediciones. O si alguien encuentra algún motivo plausible para habilitar PTC0  como salida...    Yo imagino que es una imprecisión del programa original PWM.c  31 ; GPIO_Init  32        clr     PTCD            ; Put 0's in PTC0 port  33        mov    #$01, PTCDD      ; Configure PTC0 pin as output    193 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La  configuración  del  TPM  consiste  en  programar  las  interrupciones  del  Canal  1,  y  selecciona  "alineación  por  Eje"  para  el  PWM  (Edge  Align).  Revise  el  Reference  Manual  para ver qué otras alternativas existen.    35 ; TPM_configuration  36        mov    #$68, TPM1C1SC   ; Chan.1 IntEn; PWM Edge Align. Or $24      Los valores que coloco en TPM1MOD (cfr. Reference Manual) y en TPM1C1V para generar UNA  escala del piano (8 notas BLANCAS, no hay NEGRAS), SON EMPÍRICOS (la noche anterior a  la primera vez que íbamos a usar la escala musical, me la pasé cambiando valores hasta  lograr esta lista. En nuestro DEMOQE128, no es sólo cuestión de conocer las frecuencias  de  la  escala;  hay  que  ver  la  RESPUESTA  del  transductor  de  sonido,  o  BUZZER).  Usted  puede  ajustar  estas  cantidades  a  sus  necesidades  pero...  todos  los  proyectos,  hasta  ahora, los han usado con éxito relativo.    Recuerden que estos valores son de Período, no de Frecuencia.    38 ; PIANO FREQUENCY TABLE FROM DO (C)... SI (B), DO (C)  39 ; C 1  D 1  E 1  F 1  G 1  A 1  B 1  C 2  40 ; 2094 1976 1760 1661 1480 1319 1175 1047    Esa es la escala completa. En este ejercicio se GENERARÁ UNA SÓLA NOTA.    41 freq   equ     2094    Al generar PWM, un valor fundamental es la Frecuencia de la onda; el otro es el llamado  Dutty  Cycle,  que  es  el  porcentaje,  de  0%  a  100%,  que  la  señal  debe  estar  en  UNO.  Después de probar durante algún tiempo, encontré experimentalmente que el Dutty Cycle,  para  obtener  la  mayor  intensidad  sonora  (volumen)  sobre  el  Buzzer  de  la  tarjeta  de  demostración,  DEMOQE128,  era  del  25%  (1/4).  Por  eso  el  cálculo  de  FREQ  /  4.  Esto  es  absolutamente  arbitrario  y,  a  lo  mejor,  lo  que  funciona  bien  en  mi  transductor  piezoeléctrico, puede no ser lo más apropiado para el de su tarjeta de desarrollo. Hay  que experimentar.    42 FREQ_4 EQU     FREQ >> 2       ; 25% Dutty Cycle    NOTA: Recuerde que aquellos los cálculos tales como el definido en la línea 42 (FREQ >>  2), se realizan en Assembly Time, no en Run Time; es decir, lo ejecuta el Assembler, y  no el MCU.    Se programa entonces el Período en el registro TPM1MOD (cfr. el Reference Manual para  toda esta explicación)    43        ldhx   #FREQ            ; Really... it is PERIOD...  44         sthx   TPM1MOD    Y en el registro TPM1C1V, en donde se define el Dutty Cycle, colocamos 1/4 del valor  del Período.    45        ldhx   #FREQ_4          ; 25% Dutty Cycle  46         sthx   TPM1C1V  194 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Escogemos la Fuente de Reloj para el TMP:    47        mov    #%1000, TPM1SC   ; Bus_rate_clock/1  = TPM Clock Source    Una  lista  adicional  de  posibles  divisores  es  la  siguiente,  según  la  Tabla  16‐4  del  02MC9S08QE128RM(ReferenceManual)‐U.pdf    49 ;      mov    #%1111, TPM1SC   ; Bus_rate_clock/128= TPM Clock Source  50 ;      mov    #%1110, TPM1SC   ; Bus_rate_clock/64 = TPM Clock Source  51 ;      mov    #%1000, TPM1SC   ; Bus_rate_clock/1  = TPM Clock Source    Finalmente se activan las interrupciones en el CPU, y se simula un HALT con el BRA *:    53        cli  54        bra     *    La  Rutina  de  Interrupciones  (TPM_ISR)  realiza  el  PROTOCOLO  de  Reconocimiento  de  Interrupciones (Interrupt Acknowledge) para este periférico, que tiene dos (2) pasos,  siempre  según  el  Reference  Manual:  Se  lee  el  registro  TPM1C1SC,  y  a  continuación  se  borra (BCLR) en ese mismo registro, el bit 7 (CH1F: Ready Flag del Canal 1)    56 ; interrupt VectorNumber_Vtpm1ch1 TPM_ISR  57 TPM_ISR:  58        lda     TPM1C1SC        ; Clear TPWM flags  59        bclr    7, TPM1C1SC     ; Two‐step flag acknowledgement  60        rti    38) Un "WALKMAN" Elemental. Hay  una  gran  cantidad  de  aspectos  que  habría  que  programar  para  interpretar  automáticamente una melodía en un dispositivo electrónico, tipo Walkman, pero los dos  esenciales son altura (frecuencia) y duración.    Por simplicidad, este ejercicio pretende interpretar una canción de Una Sola Voz, con  un  solo  instrumento  musical  (a  diferencia  de  las  obras  en  general,  que  emplean  múltiples  voces  e  instrumentos).  Se  basa  en  el  programa  anterior,  al  que  le  han  agregado  varias  cosas:  una  tabla  que  represente  precisamente  cada  nota,  o  punto  de  sonido, mediante los dos aspectos referidos, frecuencia y duración.    En realidad, como vimos en el ejercicio anterior, en lugar de la frecuencia se colocará  el  período,  y  se  incluyen  sólo  algunas  de  las  Duraciones  estándar  de  la  notación  musical  (Redonda,  Blanca  con  Puntillo,  Blanca,  Negra),  que  se  han  definido  en  milisegundos, mediante una asignación aproximada y arbitraria, y que puede modificarse.    Una  melodía  se  interpretará  de  la  siguiente  manera:  Se  comienza  al  principio  de  la  tabla,  se  lee  el  valor  de  altitud  (que  en  nuestro  caso,  como  ya  dijimos,  no  es  frecuencia, sino su inverso: período), se programa el oscilador que está conectado a la  pequeña cornetica del DEMOQE128, o Buzzer, y se emplea el parámetro correspondiente a  la  duración  para  llamar  una  de  las  rutinas  de  Esperar  Milisegundos,  incluida  en  la  librería de Timers que ya estudiamos.    195 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Al expirar el lapso programado, se procede con la siguiente entrada (de dos posiciones)  de la tabla y así hasta su terminación.    ["Laboratorios\Proy\Buzzer\Walkman.asm"]  001 ;********************************************************************  002 ; Walkman.asm; Luis G. Uribe C., D01L2012  L09D2013  003 ; DESCRIPTION: This project uses the PWM functionality of the TPM1  004 ; module. A PWM signal is generated and when the MCU is interrupted,  005 ; the Dutty cycle is incremented in 1. PTB5 is BUZZER in DEMOQE128 !!  006 ;********************************************************************  007        NOLIST  008          INCLUDE 'derivative.inc'  009        LIST  010 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  011 ; 2) DEFINES  012 ram:           SET     Z_RAMStart  ; $80  013 rom:           SET     ROMStart    ; $2080  014 ;;initStack:   EQU     RAMEnd + 1  ; $1800=$17FF+1. SP = $17FF  015 ;;COP_Disable: EQU     $42  016 ;====================================================================  017 ; Global Variables  018        ORG ram             ; <<<Put this *BEFORE* 'timers8HS.inc'<<<  019        NOLIST  020          include 'timers8HS.inc'   ; <<<TIMERS8: Code, Vars & Macros  021        LIST  022 SongPtr:   DS.W    1  023 NNotes:    DS.W    1  024 NoteTime:  DS.W    1  025 DCycle:    DS.W    1  026 ;********************************************************************  027 ; MAIN PROGRAM HEADER:  028        ABSENTRY Main  029        ORG rom  030 ;*******************************  031 ; PIANO FREQUENCY TABLE FROM DO (C)... SI (B), DO (C)    032 ; C 1  D 1  E 1  F 1  G 1  A 1  B 1  C 2  033 ; 2094 1976 1760 1661 1480 1319 1175 1047  034 C1:    EQU     2094  035 D1:    EQU     1976  036 E1:    EQU     1760  037 F1:    EQU     1661  038 G1:    EQU     1480  039 A1:    EQU     1319  040 B1:    EQU     1175  041 C2:    EQU     1047    042 Div:   EQU     0  043 ; Standard Durations in Music:  044 ;  045 r:     EQU     2000 >> Div     ; Redonda  046 b_:    EQU     1500 >> Div     ; Blanca con Puntillo  196 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 047 b:     EQU     1000 >> Div     ; Blanca  048 n:     EQU      500 >> Div     ; Negra    049 ;;;Song:  DC.W    C1,b_, D1,n  050 ;;;       DC.W    E1,b_, G1,b  051 ;;;       DC.W    C2,n, B1,n, A1,n, G1,n  052 ;;;       DC.W    A1,b, G1,b  053 ;;;       DC.W    F1,n, A1,n, E1,n, G1,n  054 ;;;       DC.W    F1,n, D1,n, C1,b  055 ;;;SongSize   EQU (* ‐ Song) / 4    056 ; Song table is formed by NOTE & DURATION pair of 16‐bit values  057 ;  058 Song:  DC.W    C1,b, D1,b  059        DC.W    E1,b, F1,b  060        DC.W    G1,b, A1,b, B1,b, C2,b  061        DC.W    C2,b, B1,b, A1,b, G1,b  062        DC.W    F1,b, E1,b  063        DC.W    D1,b, C1,r  064        DC.W    0,1  065 SongSize   EQU (* ‐ Song) / 4  066 ;*******************************  067 Main:  lda    #COP_Disable ; RSTE=0: PTA5 is NOT for ~RESET  068         sta    SOPT1       ; ..System Options 1  069        ldhx   #initStack   ; Set up SP  070         txs                ; ...  071 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  072 ; This is the 'MCU_Init' from the Original C program. However, I am  073 ; ..using POR (Power On Reset) defaults: Clock to EVERY peripheral.  074 ;  075 ; MCU_Init  076 ;;       lda    #$20       ; Enable Bus Clock to the TPWM1 module  077 ;;        sta    SCGC1  078 ;;       clra              ; Disable unused peripherals Bus clock  079 ;;        sta    SCGC2  080 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  081 ; GPIO_Init  082        clr     PTCD        ; Put 0's in PTC0 port  083        mov    #$01, PTCDD  ; Configure PTC0 pin as output  084 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  085 ; TPM_configuration  086        mov    #$68, TPM1C1SC   ; Chan.1 IntEn; PWM Edge Align. Or $24  087        mov    #%1000, TPM1SC   ; Bus_rate_clock/1  = TPM Clock Source  088 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  089        Init8Timers         ; Init Timers & Gloabal Enable CPU inter‐  090        cli                 ; ..rupts (TPM & RTC are already enabled)  091 ;====================================================================  092        ldhx   #SongSize    ; Get SongSize, constant representing # of  093         sthx   NNotes      ; ..NOTES; store it into variable 'NNotes'  094          beq   EndSong     ; If NNotes is 0, finish the song...  095        ldhx   #Song        ; Get Address of the Song: '#Song' it is!  096 Loop:   sthx   SongPtr     ; Store Song's Address into SongPointer &  197 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 097        ldhx    , X         ; ..using it, load the next NOTE into H:X  098         sthx   TPM1MOD     ; Store into TPM1MOD to fix TPM1's PERIOD  099         sthx   DCycle      ; Store into DCycle too  100        ldhx   #DCycle      ; Get DCycle ADDRESS into H:X to be used  101         lsr    , X         ; ..as a pointer to DCycle. Divide DCycle  102         lsr    , X         ; ..by 4 to get 25% Dutty Cycle  103        ldhx    , X         ; Using DCycle address in H:X, get DCycle  104         sthx   TPM1C1V     ; ..value into H:X, and move it to TPM1C1V  105                            ; ..to set the Dutty Cycle...  106        ldhx    SongPtr     ; Get the Song Pointer;  107         aix   #2           ; ..add #2 to point to next DURATION  108         sthx   SongPtr     ; ..and save new value.  109        ldhx    , X         ; Using this new address, get the next  110         sthx   NoteTime    ; ..Note TIME from Song Table  111        WaitMS_on 0, NoteTime ; Now TPM1 generates PWM pulses to drive  112                            ; ..buzzer; Wait here for the desired time  113        ldhx    NNotes      ; At end get NNotes, decrease by 1 and see  114         aix   #‐1          ; ..if the Song is done.  115         sthx   NNotes      ; ..  116          beq   EndSong     ; ..  117        ldhx    SongPtr     ; If not, load address of next Song table  118         aix   #2           ; ..possition, point it to the next NOTE  119    bra     Loop            ; ..and Loop again    120 ;====================================================================  121 EndSong:  122        clr     TPM1C1SC    ; Chan.1 IntDisable  123        bra     *           ; ..and Wait Forever    124 ;********************************************************************  125 ; Interrupt VectorNumber_Vtpm1ch1 TPM_ISR  126 TPM_ISR:  127        lda     TPM1C1SC    ; Clear TPWM flags in this Two‐step flag  128        bclr    7, TPM1C1SC ; ..acknowledgement to reenable TPM1 INTs.  129        rti                 ; Return from Interrupt    130 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  131 ; RTC Interrupt Service Routine  132 RTC_INTERRUPT:  133        TIMERS8ISR  134        ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  135        nop ; <<<NEEDED by CodeWarrior 10.1&2 (not 6.3). INCREDIBLE<<<  136                            ; This 'nop' MAY be removed for CW 6.3 ...  137 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  138 ; Interrupt Vectors  139        ORG     Vtpm1ch1  140         DC.W   TPM_ISR  141        ORG     Vrtc  142         DC.W   RTC_INTERRUPT  143        ORG     Vreset  144         DC.W   Main        ; RESET. Maximum priority. Asynch.  145    END  198 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Laboratorios\Proy\Buzzer\Walkman.asm"]: Recordar siempre el sitio apropiado para cada INCLUDE:    017 ; Global Variables  018        ORG ram             ; <<<Put this *BEFORE* 'timers8HS.inc'<<<  019        NOLIST  020          include 'timers8HS.inc'   ; <<<TIMERS8: Code, Vars & Macros  021        LIST    Hay cuatro (4) variables de 16 bits (WORDS):    ‐  SongPtr  con  el  apuntador  a  la  canción  (a  la  tabla  que  la  representa,  y  que  está  compuesta por PAREJAS de valores: Período y Duración)    ‐ NNotes, que lleva la cantidad de notas (de DOS valores) faltantes para finalizar la  canción    ‐ NoteTime, donde se almacena temporalmente la duración de cada nota, para emplear con  facilidad la función de la librería de Timers    ‐  DCycle,  en  donde  se  calcula  el  Dutty  Cycle  para  cada  nota  (empleando  la  misma  aproximación del ejercicio anterior)    022 SongPtr:   DS.W    1  023 NNotes:    DS.W    1  024 NoteTime:  DS.W    1  025 DCycle:    DS.W    1      Las  definiciones  de  las  notas  (sus  ALTURAS,  aquí:  sus  períodos)  son  las  mismas  introducidos en el ejercicio anterior:    031 ; PIANO FREQUENCY TABLE FROM DO (C)... SI (B), DO (C)  032 ; C 1  D 1  E 1  F 1  G 1  A 1  B 1  C 2  033 ; 2094 1976 1760 1661 1480 1319 1175 1047  034 C1:    EQU     2094  035 D1:    EQU     1976  036 E1:    EQU     1760  037 F1:    EQU     1661  038 G1:    EQU     1480  039 A1:    EQU     1319  040 B1:    EQU     1175  041 C2:    EQU     1047  A continuación se definen algunas de las Duraciones estándar de la notación musical (r:  Redonda, b_: Blanca con Puntillo, b: Blanca, n: Negra), en milisegundos, como se dijo  antes, mediante asignación aproximada y arbitraria.    Para modificar con cierta facilidad estas duraciones, se ha incluido un parámetro, Div,  mediante el cual se las puede dividir por 2, 4, etc. Así se logra que la canción vaya  más rápido, si es lo deseado:    199 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 042 Div:   EQU     0  043 ; Standard Durations in Music:  044 ;  045 r:     EQU     2000 >> Div     ; Redonda  046 b_:    EQU     1500 >> Div     ; Blanca con Puntillo  047 b:     EQU     1000 >> Div     ; Blanca  048 n:     EQU      500 >> Div     ; Negra      Como ejemplo, no hemos incluido una verdadera canción sino una ESCALA que sube desde la  nota más baja, C1, hasta la más alta C2, y luego se regresa hasta el principio. Para  finalizar, hemos agregamos a la tabla, luego de la canción (o escala), una entrada con  un  período  de  CERO  y  una  duración  de  un  Milisegundos.  Su  propósito  es  que  NO  quede  sonando indefinidamente el Buzzer (CERO Período lo silencia).    058 Song:  DC.W    C1,b, D1,b  059        DC.W    E1,b, F1,b  060        DC.W    G1,b, A1,b, B1,b, C2,b  061        DC.W    C2,b, B1,b, A1,b, G1,b  062        DC.W    F1,b, E1,b  063        DC.W    D1,b, C1,r  064        DC.W    0,1  065 SongSize   EQU (* ‐ Song) / 4    Como se ha sugerido, el NÚMERO DE NOTAS (en la tabla) lo calcula el Assembler empleando  el símbolo SongSize que se coloca LUEGO de la tabla y que en este caso tiene el valor:       SongSize = (* ‐ Song) / 4;    es  decir,  SongSize  es  igual  a  la  posición  ACTUAL  (*),  que  es  justo  la  POSICIÓN  posterior a la tabla, menos el PRINCIPIO de la tabla (Song), dividido por cuatro (4),  pues cada NOTA tiene cuatro (4) BYTES.    La inicialización: GPIO_Init y TPM_configuration, ya las hemos repasado varias veces.    Se inicializan los Timers:    089        Init8Timers         ; Init Timers & Global Enable CPU inter‐  090        cli                 ; ..rupts (TPM & RTC are already enabled)      Y ahora sí comienza el código nuevo, para interpretar una canción:    Primero  se  inicializa  la  variable  NNotes,  que  indica  cuántas  notas  faltan  para  finalizar la canción:    092        ldhx   #SongSize    ; Get SongSize, constant representing # of  093         sthx   NNotes      ; ..NOTES; store it into variable 'NNotes'    Si NNotes fuera CERO, se terminaría la interpretación de la canción:    094          beq   EndSong     ; If NNotes is 0, finish the song...  200 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Luego se almacena el PRINCIPIO de la canción, es decir, la DIRECCIÓN de la tabla Song,  en la variable APUNTADORA para ese efecto: SongPtr    095        ldhx   #Song        ; Get Address of the Song: '#Song' it is!  096 Loop:   sthx   SongPtr     ; Store Song's Address into SongPointer &    Para  comenzar,  ese  será  el  PRINCIPIO  de  la  canción  (o  de  la  Tabla);  luego  será  la  posición ACTUAL de la tabla; por eso, ahí se establece el "Loop".    Empleando el APUNTADOR, se lee el "siguiente" valor de la NOTA, AL MISMO registro H:X.    097        ldhx    , X         ; ..using it, load the next NOTE into H:X    NOTA: Es GENIAL poder tener en H:X la dirección de lo que se quiere cargar EN EL  PROPIO H:X !!!    Se almacena, después, el valor de H:X: en el registro TPM1MOD, que define el PERÍODO, y  en la variable DCycle, para calcular el Dutty Cycle:    098         sthx   TPM1MOD     ; Store into TPM1MOD to fix TPM1's PERIOD  099         sthx   DCycle      ; Store into DCycle too    Luego se copia LA DIRECCIÓN de DCycle (#DCycle: NOTE EL # !!) en H:X, y con dos LSR,  Logical  Shift  Right,  se  divide  el  valor  inicial  por  CUATRO  (4)  (que  es  el  valor  EXPERIMENTAL con el cual conseguí que sonara MEJOR el Buzzer en MI tarjeta DEMOQE128):    100        ldhx   #DCycle      ; Get DCycle ADDRESS into H:X to be used  101         lsr    , X         ; ..as a pointer to DCycle. Divide DCycle  102         lsr    , X         ; ..by 4 to get 25% Dutty Cycle    Después  de  dividir  el  Período  por  4,  se  lo  almacena  en  el  registro  TPM1C1V,  que  es  donde se determina, finalmente, el Dutty Cycle:    103        ldhx    , X         ; Using DCycle address in H:X, get DCycle  104         sthx   TPM1C1V     ; ..value into H:X, and move it to TPM1C1V  105                            ; ..to set the Dutty Cycle...    Luego cargamos en el registro Índice, la dirección de la SIGUIENTE nota de la canción  (que, al comenzar, es la PRIMERA nota):    106        ldhx    SongPtr     ; Get the Song Pointer;  Como  cada  nota  está  compuesta  de dos  valores:  Altura  (representada  por  su  período:  2  BYTES) y por su Duración, 2 BYTES también, una vez que se Apunta a la primera nota (su  Período), hay que apuntar a la Duración; por eso se incrementa el H:X en DOS (2), que  es lo que mide el PERÍODO:    107         aix   #2           ; ..add #2 to point to next DURATION  108         sthx   SongPtr     ; ..and save new value.    201 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se almacena temporalmente ese nuevo valor en SongPtr y, usando esa nueva dirección, que  corresponde  a  la  DURACIÓN  (TIME),  se  carga  ese  valor  en  H:X  (Genial  modo  de  Direccionamiento) y se lo almacena donde corresponde: en NoteTime:    109        ldhx    , X         ; Using this new address, get the next  110         sthx   NoteTime    ; ..Note TIME from Song Table    Y, usando ese valor NoteTime, se llama mi rutina WaitMS_on (timer 0); así hemos logrado  activar el valor de Período, y activar el Timer 0 para que espere durante NoteTime:    111        WaitMS_on 0, NoteTime ; Now TPM1 generates PWM pulses to drive  112                            ; ..buzzer; Wait here for the desired time    Al finalizar, se decrementa el valor de NNotes; se lo almacena (en NNotes), y si ya se  llegó a CERO se sale del "Loop" (BEQ) y se termina la canción: EndSong:    113        ldhx    NNotes      ; At end get NNotes, decrease by 1 and see  114         aix   #‐1          ; ..if the Song is done.  115         sthx   NNotes      ; ..  116          beq   EndSong     ; ..    Si NNotes NO llegó aún a Cero, y por tanto la canción NO se ha terminado, se vuelve a  cargar en el registro Índice la dirección de la PRÓXIMA nota, H:X; se hacer apuntar esa  dirección a la SIGUIENTE nota y se vuelve al "Loop":    117        ldhx    SongPtr     ; If not, load address of next Song table  118         aix   #2           ; ..possition, point it to the next NOTE  119    bra     Loop            ; ..and Loop again    Para terminar, se deshabilitan las interrupciones de TMP1, y se simula un HALT:    121 EndSong:  122        clr     TPM1C1SC    ; Chan.1 IntDisable  123        bra     *           ; ..and Wait Forever    La  rutina  de  interrupciones,  TPM_ISR,  opera  igual  que  en  el  ejercicio  anterior  y  no  comentaremos aquí más sobre ella.    La  rutina  de  interrupciones  de  la  librería  de  Timers  también  funciona  como  de  costumbre.    39) "SWITCH", vía "COMPUTED GOTO". Una  manera  muy  eficiente  de  ejecutar  la  funcionalidad  equivalente  al  SWITCH  del  lenguaje  C,  consiste  en  tomar  la  variable  de  Control  del  Switch,  y  en  base  a  ella  saltar al código del correspondiente CASE. Hay ciertas variaciones al respecto; en el  siguiente ejemplo, los CASEs tienen que ser CONTIGUOS, del 0 en adelante: case 0: case  1:  case  2:,  etc.,  lo  cual  sirve  en  una  gran  cantidad  de  ocasiones,  en  específico,  cuando se están implementando Finite State Machines, FSM.    202 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 En  este  ejemplo,  también,  se  ha  supuesto  que  el  número  de  posiciones  o  estados  será  siempre  POTENCIA  DE  DOS.  Si  ese  no  es  el  caso,  hay  que  reestructurar  un  poco  el  programa para acomodarlo.    Otras  veces  los  CASEs  pueden  tener  valores  que  no  comienzan  en  Cero  (0),  o  que  sean  números cualesquiera, no necesariamente consecutivos como en el ejercicio más simple.  Esto puede solucionarse de muchas maneras, por ejemplo, mediante una serie de CBEQ. Lo  cual es menos elegante que la solución del presente ejercicio, pero más general...    Este  ejercicio  está  pensado  para  ejecutarse  con  el  DEBUGGER,  paso  a  paso,  a  fin  de  poder introducir las entradas a mano, vía el puerto SIMULADO PTAD. Usted puede emplear  los interruptores de la tarjeta DEMOQE128 que usan, por ejemplo, el PTC, o usar otra  variable puesta en el programa para ese propósito.      ["Laboratorios\FSM‐FiniteStateMachines\ComputedGoTo.asm"]  01 ;************************* ComputedGoTo.asm *************************  02 ; Luis G. Uribe C., Implement "SWITCH"; D17F2013  J05D2013  03 ; 1) Use COMPUTED GOTO (Cases ARE sequential: 0, 1, 2, ...)  04 ;  05 ; This example is only meant to be Debugged/Simulated. For real, you  06 ; ..will need to debounce inputs, perhaps include some 'InputReady'  07 ; ..signal (debounced), use Timers and produce some required outputs.  08 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  09 ; Include files  10        NOLIST  11          INCLUDE 'derivative.inc'  12        LIST  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; 2) DEFINES  15 ram:           SET     Z_RAMStart  ;   <<<TIMERS8: <<<  16 rom:           SET     ROMStart    ; HCS08 ROMStart (Flash)    17 initStack:     EQU     $1800  18 COP_Disable:   EQU     %01000010   ; $42  19 ;====================================================================  20 ; 3) Global Variables  21        ORG     ram  22 STATE:     DS.W    1  23 ;********************************************************************  24 ; Begin of Code Section  25        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  26        ORG rom  27 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  28 ; *** ALWAYS *** include the following 4 instructions    29 Main:  lda    #COP_Disable  30         sta    SOPT1       ; System Options 1  31        ldhx   #initStack   ; Init SP  32         txs  33 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  34        clr     STATE       ; HIGH end always will be 0 in the example  203 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 35 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  36 Forever:  37        lda     PTAD  38         and   #LOW(Table_Mask) ; Make input < 8 (0..7), no mater what  39         lsla               ; Multiply by 2 (Table is 2 bytes/entry)  40         sta    STATE + 1   ; >>> BIG ENDIAN... REMEMBER ??? <<<  41        ldhx    STATE  42        ldhx    Table, X    ; o = Table[ ix ];  43        jmp     , X    44 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  45 ; THIS IS THE WAY TO DECLARE *TABLES OF ADDRESSES* (Constants):  46 ; 1: They will go in ROM‐Flash (constants...)  47 ; 2: They have a NAME ('Table' in this example)  48 ; 3: You populate tables with values, using DC.W assembler directive,  49 ;    because addresses are 16 bits long.  50 ; 4: NORMALLY, you need to know the LENGHT of the table. So you  51 ;    put a marker at the end: 'Table_End:' in this example, the  52 ;    assembler calculates lenght as: ( Table_End ‐ Table_Begin ) / 2  53 ;    (You do NOT shall calculate anything the assembler will !)    54 Table:  55    DC.W    START, S1, S2, S3   ; Any sequence for this example  56    DC.W    S2,    S3, S2, START  57 Table_End:  58 TSIZE:         EQU     ( Table_End ‐ Table ) / 2  59 Table_Mask:    EQU     TSIZE ‐ 1   ; TSIZE *IS* a power of 2  60 START: nop                     ; 'nop': Replace it with YOUR code!  61        bra     Forever         ; 'bra': use JMP if too far away  62 S1:    nop  63        bra     Forever  64 S2:    nop  65        bra     Forever  66 S3:    nop  67        bra     Forever  68 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  69 ; Interrupt Vectors  70 dummy_isr:                     ; <<< Must be placed in ROM Space  71        rti  72        ORG     Vswi            ; Vswi and Vreset  73         DC.W   dummy_isr       ; SWI  74         DC.W   Main            ; RESET. Maximum priority. Asynch.  75    END    COMENTARIOS a ["Laboratorios\FSM-FiniteStateMachines\ComputedGoTo.asm"]: Al principio se INICIA la variable de Control: STATE, de 16 bits (2 Bytes), de la cual,  en este ejercicio que sólo tiene 8 estados o posiciones en la tabla, sólo se utilizarán  los  3  bits  MENOS  significativos  y,  en  general,  sólo  se  empleará  el  BYTE  MENOS  significativo de STATE. Por eso, se borra (CLR) SU BYTE **MÁS** SIGNIFICATIVO:    34        clr     STATE       ; HIGH end always will be 0 in the example    204 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Recuerden:  Esta  máquina  es  BIG  ENDIAN.  Para  borrar  EL  BYTE  MÁS  SIGNIFICATIVO  de  una  variable  de  16  bits, como  nuestra  variable 'STATE,  hay  que  borrar  la  posición  STATE;  por eso', el 'CLR STATE' de la línea 34.    OJO:     34 clr STATE ; HIGH end always will be 0 in the example  NO BORRA TODA LA VARIABLE 'STATE', solo su parte MÁS SIGNIFICATIVA.    El ciclo infinito para este ejercicio comienza leyendo la variable de Control, PTAD, en  el acumulador, y asegurándose que está dentro del rango válido, que para este ejercicio  es entre 0 y 7, inclusive:    36 Forever:  37        lda     PTAD  38         and   #LOW(Table_Mask) ; Make input < 8 (0..7), no mater what    Como es un requisito para este ejemplo, que la tabla sea siempre potencia de DOS, se ha  calculado más abajo el valor 'Table_Mask', que es igual a la potencia de 2 que define  el tamaño (OCHO en este ejercicio), MENOS 1: SIETE, que en binario es: 00..111. Así, el    38  AND #LOW(Table_Mask)    toma  (LOW)  los  8  bits  menos  significativos  de  'Table_Mask'  (00000111)  y  hace  un  AND  entre esa Constante (el NUMERAL: #) y el Acumulador, con lo que, no importa cuál sea el  valor alimentado, siempre quedará confinado a números entre 0 y 7, ambos inclusive.    Luego que tenemos la variable de entrada apropiadamente VALIDADA, la multiplicamos por  DOS  (LSLA)  porque  la  tabla  es  de  DIRECCIONES  (a  dónde  Saltar)  y  en  esta  máquina  las  direcciones son de 16 bits (DOS bytes)    39         lsla               ; Multiply by 2 (Table is 2 bytes/entry)    Ese  valor,  que  es  la  Entrada,  confinada,  multiplicada  por  2,  se  la  almacena  en  la  Variable  STATE.  Como  sólo  estamos  usando  el  Byte  MENOS  significativo  de  STATE,  se  la  almacena  en  'STATE+1',  porque  esta  máquina  es  BIG  ENDIAN,  y  la  posición  MENOS  SIGNIFICATIVA está DE ÚLTIMO:    40         sta    STATE + 1   ; >>> BIG ENDIAN... REMEMBER ??? <<<    Ahora, se toma el valor de STATE (16 bits, de los cuales ya hemos garantizado que el  MSB es Cero), se lo carga en el registro Índice, H:X:    41        ldhx    STATE  Y empleando ese índice cargamos, en el mismo registro Índice, H:X, el valor 'Table +  STATE'. Ese valor que se carga (observe bien!), corresponde a la DIRECCIÓN, sacada de  la Tabla, de la rutina a donde hay que Saltar para ejecutar lo que corresponde a ese  Número de Case, identificado por la variable de Control! Finalmente, se salta a dicha  rutina.  Se  emplea  a  fondo  el  direccionamiento  Indexado.  Pocos  MCUs  de  8  bits  tienen  tanta exuberancia de Arquitectura!    42        ldhx    Table, X    ; o = Table[ ix ];  43        jmp     , X  205 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Veamos ahora cómo se estructura la Tabla de Direcciones. En primer lugar, las entradas  en  la  Tabla  son  Direcciones  de  las  diferentes  Rutinas  que  han  de  materializar  la  funcionalidad  de  cada  Case;  así  que  la  Tabla  normalmente  irá  en  ROM  (Flash).  Podría  pensarse  en  tablas  dinámicas,  que  recibieran  sus  valores  en  RUN  TIME,  pero  no  es  el  caso  de  este  ejemplo. Esas  Tablas  irían  en RAM,  y  habría  que  inicializarlas mediante  código. Las Tablas en ROM (Flash) las inicializa el Assembler, y el QUEMADOR del código  en el MPU las copia inicializadas a la Flash de Programa.    Segundo,  las  Tablas  tienen  una  identificación,  o  nombre,  que  en  nuestro  ejemplo  es  "Table".    En  caso  de  tablas  de  Direcciones,  se  inicializan  con  valores  declarados  mediante  la  directiva de Assembler DC.W (Define Constant Word: 16 bits).    Casi siempre es útil conocer el tamaño o longitud de la tabla, lo que hemos calculado  siempre  de  una  manera  simple  y  automática:  se  coloca  una  etiqueta  en  la  posición  siguiente a la última de la tabla, se le resta la dirección de la posición de arranque,  lo  cual  nos  da  el  tamaño  en  BYTES.  Si  se  desea  calcular  el  tamaño  en  Cantidad  de  ELEMENTOS  (en  nuestro  ejemplo,  el  número  de  direcciones  almacenadas  en  la  tabla),  se  divide por el número de bytes que mide la entidad almacenada, en este caso, se divide  por 2, pues dos bytes tiene cada dirección: (Table_End ‐ Table_Begin)/2    Usted NUNCA tiene que hacer cálculos; para eso está trabajando en una COMPUTADORA.    La tabla para nuestro ejemplo, que contiene una secuencia inventada, es la siguiente:    54 Table:  55    DC.W    START, S1, S2, S3   ; Any sequence for this example  56    DC.W    S2,    S3, S2, START  57 Table_End:  58 TSIZE:         EQU     ( Table_End ‐ Table ) / 2  59 Table_Mask:    EQU     TSIZE ‐ 1   ; TSIZE *IS* a power of 2    En  otra  localidad  se  colocan  las  rutinas  que  materializarán  cada  CASE.  Pueden  estar  colocadas antes o después de esta posición, pueden estar separadas por todo el programa  (lo cual, si bien es posible, NO parece conveniente) o ir en cualquier orden:    60 START: nop                     ; 'nop': Replace it with YOUR code!  61        bra     Forever         ; 'bra': use JMP if too far away  62 S1:    nop  63        bra     Forever  64 S2:    nop  65        bra     Forever  66 S3:    nop  67        bra     Forever  Hemos  reemplazado  todo  lo  que  sería  el  código  necesario  para  implementar  cada  uno  de  los CASEs, por NOPs, que usted puede cambiar a voluntad por las rutinas necesarias para  sus programas particulares, según sea el caso.    NOTAS:    En caso que la etiqueta 'Forever' (en el ejemplo) esté muy apartada, por la cantidad de  código necesario para los CASEs, en lugar de BRA Forever usted puede colocar JMP.    206 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Si  el  número  de  elementos  de  la  tabla,  NO  es  potencia  de  2,  habrá  que  cambiar  la  metodología que aquí se aplicó, de confinar las variables de Control a números entre 0  y 7, mediante un AND con el #7. Una forma puede ser Comparando la variable de control  con la parte Baja y la Alta del RANGO permitido para su ejemplo.    Es  IMPORTANTE  garantizar  que  su  programa,  aunque  sea  por  error  en  la  información  de  entrada,  NO  saltará  a  ejecutar  código  que  no  sea  apropiado:  Si  la  tabla  tiene  5  entradas, saltar a la posición 6 o a la 7... ES un error GRAVE.    Pero observe: aunque la Información de Entrada no debe sobrepasar cierto rango; si se  EXTRALIMITA  por  error  en  su  adquisición,  procesarla  mediante  un  AND,  o  empleando  comparaciones, para obligarla a permanecer confinada dentro de ese rango, PUEDE NO SER  LA SOLUCIÓN.    Es decir, en la vida real, ambas cosas son errores: a) ejecutar una rutina que se sabe  que  NO  se  corresponde  con  la  entrada,  y  que  seguramente  NI  EXISTE,  y  b)  ejecutar  la  rutina que resulta de confinar obligadamente a la variable a que permanezca dentro del  rango acordado.    Por ejemplo, si la tabla tiene 8 valores, de las posiciones 0 a la 7, y la variable de  Control indica, por error de lectura o por equivocación del operador, etc., que se debe  ejecutar la RUTINA "10" (%1010), sería un ERROR GRAVE tratar de ejecutar dicha RUTINA  "10" de la tabla, PORQUE NO EXISTE! pero también puede estar MUY MAL ejecutar la rutina  'DOS', que es en nuestro ejemplo lo que resulta al hacer un AND entre %1010 y %0111:  %0010.    Así que en un ejercicio real, usted tiene que decidir qué hacer si se produce un error;  por ejemplo, avisar al operador y que él tome la decisión, tomar un valor "estimado"  por un algoritmo apropiado, tomar un valor mínimo si es que la variable está por debajo  de él, o máximo si se ubica por encima del techo, etc. Cada caso será distinto. Pero NO  puede dejar que su variable de Control obligue a su CPU a ejecutar una rutina que usted  ni siquiera escribió.    40) La FÁBRICA de CHOCOLATES. Este  ejercicio  está  definido  en  el  libro  "Ingeniería  Digital",  que  ha  sido  publicado  como parte del curso de Arquitectura‐I. Allí puede ver de qué se trata. A continuación  se incluye la solución.    Observe que MUCHOS ejercicios de Redes Combinatorias, como la solución al problema del  EDP CENTER, que también se encuentra en el libro "Ingeniería Digital", se resuelven de  una manera Similar o IGUAL al problema de La Fábrica de Chocolates.    01 ["Laboratorios\Tables\FabricaDeChocolates.asm"]  02 ;********************************************************************  03 ; FabricaDeChocolates.asm, Luis G. Uribe C., V15F2013  04 ; See how to read a Table. Use HX register as index to travel 'Table'  05 ; ‐ input comes from PTAD. You need to MASK out all not used bits.  06 ;   Use AND with 0x07 [%00000111; #LOW(Table_Mask)] to Mask 'input'.  07 ; Main loop reads PTAD values (you will fake inputs from debugger);  08 ; ..read RESPONSES from Table, and output them to 'output' variable  09 ; ..(in real life, you will output this values to the output PORT)  207 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 10 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  11 ; Include files  12        NOLIST  13          INCLUDE 'derivative.inc'  14        LIST  15 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 ; 2) DEFINES       17 ram:           SET     Z_RAMStart  ;   <<<TIMERS8: <<<  18 rom:           SET     ROMStart    ; HCS08 ROMStart (Flash)       19 initStack:     EQU     $1800  20 COP_Disable:   EQU     %01000010   ; $42  21 ;====================================================================  22 ; 3) Global Variables  23        ORG     ram  24 input:     DS.W    1       ; DS.W: HX register needs 16 bits values..  25 output:    DS.B    1  26 ;********************************************************************  27 ; Begin of Code Section  28        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  29        ORG rom  30 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  31 ; *** ALWAYS *** include the following 4 instructions  32 Main:  lda    #COP_Disable  33         sta    SOPT1       ; System Options 1  34        ldhx   #initStack   ; Init SP  35         txs  36 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  37         clr    input       ; HIGH end always will be 0 in the example  38 Forever:  39        lda     PTAD        ; Be sure: input < 8 (0..7)  40         and   #LOW(Table_Mask)  41         sta    input + 1   ; >>> BIG ENDIAN... REMEMBER ??? <<<  42        ldhx    input  43        lda     Table, X    ; o = Table[ ix ];  44        sta     output      ; ..  45        bra     Forever  46 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  47 ; THE FOLLOWING IS THE WAY TO DECLARE *TABLES OF CONSTANTS*:  48 ; 1: They go in ROM‐Flash (constants...)  49 ; 2: They have a NAME ('Table' in this example)  50 ; 3: You populate tables with values, using DC assembler directive  51 ; 4: NORMALLY, you need to know the LENGHT of the table. So you  52 ;    put a marker at the end: 'Table_End:' in this example, the  53 ;    assembler calculates the lenght as: 'Table_End ‐ Table_Begin'  54 ;    (You do NOT like to calculate anything the assembler can !)       55 Table:  56    DC.B    %100, %100, %100, %100  ; Stop,Half,Full are bits  57    DC.B    %100, %010, %010, %001  ; ..b2, b1, b0 on 'output'  58 Table_End:  208 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 59 TSIZE:         EQU     Table_End ‐ Table  60 Table_Mask:    EQU     TSIZE ‐ 1   ; TSIZE *IS* a power of 2       61 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  62 ; Interrupt Vectors  63 dummy_isr:                     ; <<< Must be placed in ROM Space  64        rti       65        ORG     Vswi            ; Vswi and Vreset  66         DC.W   dummy_isr       ; SWI  67         DC.W   Main            ; RESET. Maximum priority. Asynch.       68    END    A  estas  alturas,  ustedes  deben  poder  comprender  por  completo  la  solución  que  aquí  presento, sin comentarios adicionales.    41) TABLAS de DATOS; Implementación del "FOR". Se muestra como leer una tabla para efectuar consecutivamente la expresión en C: o =  Table[ ix ]; dentro de un ciclo FOR.    En muchas aplicaciones la variable de salida ("o", output) será un Puerto del MCU, o  una interfaz de comunicaciones seriales. Usted sabrá cambiar el ejercicio para acomodar  sus necesidades específicas.    Aproveché para definir la variable "o" en el Stack, cosa que ya aprendimos a hacer.    Además, la variable 'ix', índice dentro de "Tabla", se ha definido como UNSIGNED. Usted  puede  designar  en  sus  programas  el  calificativo  SIGNED.  Es  importante  resaltar  que  usted debe saber aplicar las Ramificaciones (Branches) APROPIADOS PARA CADA OCASIÓN. De  lo contrario usted habrá cometido la misma TORPEZA que Andrew S. Tanenbaum al diseñar  su fallido MIC (1, 2 y 3) para su libro de arquitectura.    01 ["Laboratorios\Tables\Tables0.asm"]  02 ;********************************************************************  03 ; Tables0.asm, Luis G. Uribe C., V15F2013  04 ; See how to read a Table. Use HX register as index to travel 'Table'  05 ; ‐ "o" var has space reserved in Stack  06 ; ‐ See how to implement a FOR LOOP  07 ; ‐ In this example, 'ix' var is UNSIGNED char       08 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  09 ; Include files  10        NOLIST  11          INCLUDE 'derivative.inc'  12        LIST  13 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  14 ; 2) DEFINES       15 ram:           SET     Z_RAMStart  ;   <<<TIMERS8: <<<  16 rom:           SET     ROMStart    ; HCS08 ROMStart (Flash)  209 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08      17 initStack:     EQU     $1800  18 COP_Disable:   EQU     %01000010   ; $42  19 ;====================================================================  20 ; Begin of Code Section  21        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  22        ORG rom  23 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  24 ; *** ALWAYS *** include the following 4 instructions       25 Main:  lda    #COP_Disable  26         sta    SOPT1       ; System Options 1  27        ldhx   #initStack   ; Init SP  28         txs  29 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  30        pshh                ; Reserve space for "o" var stored in 1,SP  31 For01_Init:                ; for( ix = 0; ix < TSIZE; ix ++ )  32 For01:                     ; I PREFER LABEL 'For01:', not For01_Init  33        clrx                ; INIT FOR: ix = 0;  34        clrh                ; ..  35 For01_Tst:                 ; In the example, ix IS 'unsigned' char;  36        cphx   #TSIZE       ; ..if( ! (ix < TSIZE ) goto For01_End;  37                            ; ..NOTE: HERE (ix<8) you may use 'cpx'...  38        bhs     For01_Exit  ; ..Dont use 'BGT'; use 'BHS': ix IS uchar  39 For01_Code:  40        lda     Table, X    ; o = Table[ ix ];  41        sta     1, SP       ; ..  42 For01_End:  43        aix    #1           ; ix ++. HERE (ix<8) you may use 'incx'...  44        bra     For01_Tst  45 For01_Exit:  46        pulh                ; restore Stack      }  //end main  47        bra     *       48 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  49 ; THE FOLLOWING IS THE WAY TO DECLARE *TABLES OF CONSTANTS*:  50 ; 1: They go in ROM‐Flash (constants...)  51 ; 2: They have a NAME ('Table' in this example)  52 ; 3: You populate tables with values, using DC assembler directive  53 ; 4: NORMALLY, you need to know the LENGHT of the table. So you  54 ;    put a marker at the end: 'Table_End:' in this example, the  55 ;    assembler calculates the lenght as: 'Table_End ‐ Table_Begin'  56 ;    (You do NOT like to calculate anything the assembler can !)  57 Table:  58        DC.B    7, 6, 5, 4, 3, 2, 1, 0  59 Table_End:  60 TSIZE:     EQU     Table_End ‐ Table  61 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  62 ; Interrupt Vectors  63 dummy_isr:                     ; <<< Must be placed in ROM Space  64        rti  65        ORG     Vswi            ; Vswi and Vreset  210 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 66         DC.W   dummy_isr       ; SWI  67         DC.W   Main            ; RESET. Maximum priority. Asynch.  68    END    COMENTARIOS a ["Laboratorios\Tables\Tables0.asm"] Después de la rutinaria inicialización, se pasa a reservar espacio en el Stack para la  variable "o"; esto se hace decrementando el SP, mediante un push de un byte:    30        pshh                ; Reserve space for "o" var stored in 1,SP    A continuación el código genérico para los FOR, que como se recordará, tienen a) una  parte  de  Inicialización  de  variables,  b)  un  Cuerpo  con  las  instrucciones  que  usted  quiere repetir dentro del ciclo y c) una parte de Finalización, luego de la cual está  la Salida del FOR.    La Inicialización aquí [ for( ix = 0; ix < TSIZE; ix ++ ) ] consiste en colocar en CERO  la variable índice, que está en el Stack:    31 For01_Init:                ; for( ix = 0; ix < TSIZE; ix ++ )  32 For01:                     ; I PREFER LABEL 'For01:', not For01_Init  33        clrx                ; INIT FOR: ix = 0;  34        clrh                ; ..    A continuación se verifica (TST) que todavía hay trabajo que hacer, [ ix < TSIZE; ]; si  ya se terminó se va a la sección de salida, Exit:    35 For01_Tst:                 ; In the example, ix IS 'unsigned' char;  36        cphx   #TSIZE       ; ..if( ! (ix < TSIZE ) goto For01_End;  37                            ; ..NOTE: HERE (ix<8) you may use 'cpx'...  38        bhs     For01_Exit  ; ..Dont use 'BGT'; use 'BHS': ix IS uchar    Observe  dos  cosas  importantes:  en  este  ejemplo  usted  puede  emplear  simplemente  comparaciones con X, el LSB del registro índice H:X, porque 'ix' solo asumirá valores  inferiores a 8.    Lo  segundo  es  que  no  se  pueden  usar  Ramificaciones  (BRANCHES)  de  las  definidas  para  números  SIGNED,  como  BGT;  tiene  que  revisar  y  emplear  solamente  las  definidas  para  UNSIGNED, como BHS (o, si fuera el caso, hay un grupo de ellas definidas para AMBOS:  Signed y Unsigned, como BEQ, BNE, etc.)  La  parte  del  Cuerpo  de  su  programa  en  este  caso  es  simplemente  cargar  de  Table  el  elemento correspondiente a 'ix' y almacenarlo (como ejemplo) en la variable de salida  'o':    39 For01_Code:  40        lda     Table, X    ; o = Table[ ix ];  41        sta     1, SP       ; ..    En la parte de finalización del FOR se incrementa el índice, [ix ++] y se continúa en  la parte de TST del FOR, y el ciclo continúa:    42 For01_End:  211 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 43        aix    #1           ; ix ++. HERE (ix<8) you may use 'incx'...  44        bra     For01_Tst    Se  suele  usar  AIX  para  incrementar  (#1)  el  índice  H:X  directamente,  pero  en  este  ejemplo en particular, en que el valor del índice no sobrepasará de 8, también podría  haberse empleado INCX, que incrementa sólo X, la parte LSB de H:X    Para finalizar el FOR, se deja balanceado el Stack (COSA FUNDAMENTAL) mediante el PULH,  y se simula un HALT:    45 For01_Exit:  46        pulh                ; restore Stack      }  //end main  47        bra     *       En  este  sencillo  ejemplo,  la  tabla  está  compuesta  de  8  números.  Para  diferenciarlos  perfectamente del 'ix', he escogido los 8 números en REVERSA. Usted puede emplear los  que le resulten más convenientes:       57 Table:  58        DC.B    7, 6, 5, 4, 3, 2, 1, 0  59 Table_End:  60 TSIZE:     EQU     Table_End ‐ Table       POR ÚLTIMO, si su variable no estuviera en Stack, usted tendría que definirla en RAM  Space y el Código cambiaría sutilmente de STA 1, SP en la línea 41, a STA VAR:    Esto corresponde a un extracto del programa:    ["Laboratorios\Tables\Tables1.asm"]  no listado:    39 For01_Code:  40        lda     Table, X    ; o = Table[ ix ];  41        STA     VAR         ; ..    Aplica igual para un puerto de salida. Si fuera a transmitir los valores de la tabla  por un canal de comunicación serial, tendría que reemplazar la línea 41 por el llamado  a  una  rutina  de  transmisión  (lo  que  ya  hicimos  en  los  ejercicios  dedicados  a  COMUNICACIONES)    42) Last, but not Least: 8 Bits Rotate Left. Como recordará, las instrucciones de ROTATE (izquierda y derecha) dirigen al Carry, C,  el bit que sale de la variable, y alimentan el otro extremo con C; esto producen una  rotación de NUEVE (9) bits.    Este ejercicio muestra cómo realizar la misma instrucción pero dentro de OCHO (8) bits.  Corresponde a un Rotate LEFT, pero basta con cambiar las instrucciones ROLA y ROL por  las  equivalentes  hacia  la  derecha,  RORA  y  ROR,  para  invertir  el  sentido  del  giro  de  rotación:    01 ["Evaluaciones\2013‐01Ene\Evaluaciones\Ex#1\Rotate8.asm"]  02 ;********************************************************************  212 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 03 ; Rotate8.asm, Luis G. Uribe C., D17F2013  04 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  05 ; Include files  06        NOLIST  07          INCLUDE 'derivative.inc'  08        LIST  09 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10 ; 2) DEFINES  11 ram:           SET     Z_RAMStart  ;   <<<TIMERS8: <<<  12 rom:           SET     ROMStart    ; HCS08 ROMStart (Flash)  13 initStack:     EQU     $1800  14 COP_Disable:   EQU     %01000010   ; $42  15 ;====================================================================  16 ; 3) Global Variables  17        ORG     ram  18 var:   DS.B    1  19 ;********************************************************************  20 ; Begin of Code Section  21        ABSENTRY Main       ; Export symbol (ABSOLUTE Assembly select)  22        ORG rom  23 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  24 ; *** ALWAYS *** include the following 4 instructions  25 Main:  lda    #COP_Disable  26         sta    SOPT1       ; System Options 1  27        ldhx   #initStack   ; Init SP  28         txs  29 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  30        mov    #%10101010, var  31 ; ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32        lda     var  33        rola  34        rol     var  35        bra     *  36 ;‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  37 ; Interrupt Vectors  38 dummy_isr:                     ; <<< Must be placed in ROM Space  39        rti  40        ORG     Vswi            ; Vswi and Vreset  41         DC.W   dummy_isr       ; SWI  42         DC.W   Main            ; RESET. Maximum priority. Asynch.  43    END    COMENTARIOS a ["Evaluaciones\2013-01Ene\Evaluaciones\Ex#1\Rotate8.asm"]: Variable que estará sujeta a la rotación de 8 bits:    17        ORG     ram  18 var:   DS.B    1    Inicialización con un valor arbitrario:                bits 76543210  30        mov    #%10101010, var  213 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se  carga  'var'  en  el  Acumulador  y  se  rota  el  Acumulador  a  la  izquierda,  lo  cual  almacena el bit 7 de 'var' en el carry, C:    32        lda     var  33        rola    Resultando después del ROLA en:    Acc = %0101010x  C = 1  (original bit7 de 'var')    La  'x'  en  el  bit0  del  Acc  es  porque  el  valor  del  carry  C,  es  indeterminado  para  comenzar.    Ahora  se  rota  'var'  a  la  izquierda,  con  lo  cual  el  C,  que  era  el  bit7  de  'var',  alimenta su bit0:    34        rol     var  35        bra     *    Resultando en:    var = %01010101 (ROTATE LEFT en OCHO BITS)  C = 1 (pero ya no se usa más)  214 3 Capítulo I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 PROGRAMACIÓN EN "C" Unix, Linux, iOS, Windows, QNX... hacen del lenguaje “C” el más usado desde 1970 (44 años). Ningún otro ostenta esa marca.  NOTAS INICIALES S ugiero encarecidamente que cada vez que encuentre algún aspecto del Lenguaje C en  los ejercicios que a continuación presento, que usted NO ENTIENDA, lo ANOTE por  aparte,  Y  ME  HAGA  LLEGAR  LA  LISTA  COMPLETA  AL  FINALIZAR  la  lectura  de  esta  sección correspondiente al Lenguaje C.    Servirá  para  QUEJARME  NUEVAMENTE  a  ver  si  logramos  que  CAMBIEN  DE  UNA  VEZ  LOS  DOS  CURSOS DE PROGRAMACIÓN.  Y a usted le dará una idea DE MUCHAS COSAS QUE LE FALTAN POR APRENDER DEL LENGUAJE. En  su  profesión,  ES  MUY  IMPORTANTE  MANEJAR  EL  "C",  y  aquí  YA  NO  LE  ENSEÑARÁN  MÁS  PROGRAMACIÓN.  Así  que  le  corresponde  a  usted  estar  DESCONTENTO  y...  ¡ESTUDIAR  POR  SU  CUENTA!   DÓNDE ESTAMOS U na  vez  que  hemos  aprendido  los  aspectos  más  importantes  de  la  Arquitectura,  que  resumo  a  continuación,  para  estudiar  las  cuales  nos  hemos  auxiliado  con  ejercicios  hechos  en  el  Lenguaje  Ensamblador,  se  justifica  poco  trabajar  en  Assembler,  y  menos  como  profesionales.  Sólo  utilizamos  el  ASM  como  vehículo  para comprender y aplicar los siguientes conceptos importantes:     Configuraciones de las máquinas de Von Neumman vs. las Harvard   Clases y relevancia de conjuntos de instrucciones CISC y RISC   Poderío y abundancia de los modos de direccionamiento   Importancia de los métodos de Entrada y Salida   Elementos de información como el Stack   Definición de variables locales, en el Stack   Uso de métodos recursivos   Direccionamientos indexados, vía SP y vía registro índice H:X   Interrupciones   Interrupciones anidadas   Inversión de prioridades  215 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08  Habilitación de interrupciones a cierta altura de las ISR   Aplicación de instrucciones tan importantes como el SWI   Uso de TPA y TAP para guardar el Processor Status Word    En el área de la aplicación de Microcontroladores a los sistemas EMBEBIDOS, el uso del  lenguaje "C" está muy extendido. Otros que se emplean en menor escala son Java, ciertos  BASICs, FORTH...  Por eso introduje la programación de MC9S08QE128 empleando el "C", que es el lenguaje  usado en los cursos más avanzados de Arquitectura y Laboratorio de Proyectos.   GENERALIDADES D ividiremos los ejercicios de C en tres partes: Introducción, con los programas  más  simples,  comparados  con  los  similares  en  Assembler;  el  manejo  de  temporizadores, en la que presento mi librería de Timers, con funcionalidades  casi exactas a las de el capítulo anterior; la librería de de Comunicaciones  Seriales,  iguales  a  su  contraparte  en  ASM,  y  la  librería  para  el  manejo  de  Colas,  imprescindible para operar apropiadamente las Comunicaciones con flexibilidad.    Las carpetas en donde se encuentran los ejercicios introductorios son:   Labs‐C\Fibonacci y   Labs‐C\Lab1    43) Programa INTRODUCTORIO en C, para HCS08, Comparativo con ASM. Consulte  el  programa  Laboratorios\Lab1\02Fibonacci.asm  como  referencia  para  nuestro  programa introductorio en C. Al principio, las diferencias entre los dos programas son  mínimas,  según  se  dará  cuenta,  pero  a  partir  de  'mainLoop',  los  dos  son  (casi)  IDÉNTICOS.    ["Labs‐C\Fibonacci\060Fibonacci.c"]  01 // Fibonacci.c (02Fibonacci.asm), Luis G. Uribe C.,  M10D2013  02 // ******************************************************************  03 // 02Fibonacci.asm, Luis G. Uribe C., V10A2009 V08J2012  04 // ADAPTED from: HCS08‐RS08_Assembler_MCU_Eclipse.pdf, Listing 4.1  05 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  06 // Include files  07 #include "derivative.h"        // Include peripheral declarations  08 #include "Fibonacci_.h"    09 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  10 // Definición de variables (ds: byte por defecto)    11 byte       Counter;  12 byte       FiboRes;            // Aquí va la respuesta    13 void main ( void )                                             /*()*/  14 {  15 mainLoop:  16        clra;  216 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 17    cntLoop:  18        inca;  19        cbeqa(14, mainLoop);    // Larger values cause overflow  20        sta(Counter);           // Update global variable  21        bsr(CalcFibo);  22         sta(FiboRes);          // Store result  23        lda(Counter);           // ..Activate BREAKPOINT here to see..  24                                // .. 1 2 3 5 8 13 21 34 55 89 144 233  25    bra(cntLoop);               // Next round    26    for( ;; ) { /* EMPTY FOR */  }  27 }  //end main    28 // ==================================================================  29 // Function to compute Fibonacci numbers. Argument is in A    30 void CalcFibo ()  31 {  32        dbnza(fiboDo);          // Fibonacci Do  33        inca;  34    rts;  35 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  36 fiboDo:  37        psha;                   // The counter  38        clrx;                   // Second last = 0  39        lda(0x01);              // Last = 1  40    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  41    FiboLoop:  42        psha;                   // Push last  43        txa;  44        add(1,sp);  45        pulx;  46    dbnz(1, sp, FiboLoop);    47 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  48 FiboDone:  49        pulh;                   // Release counter  50    rts;                        // Result in A  51 }  // end CalcFibo()    COMENTARIOS a ["Labs-C\Fibonacci\060Fibonacci.c"]: La magia que nos permite hacer un programa en C, que se VÉ casi IGUAL al equivalente en  Assembler,  y  que  CORRE  "IGUAL"  (puede  simularlo...)  se  encuentra  en  el  include  file:  Fibonacci_.h,  que  tiene  las  definiciones  que  mimetizan  el  ASM  en  C.  Lo  analizaremos  posteriormente.    Adelante,  cuando  comencemos  en  serio,  hablaremos  en  detalle  acerca  de  los  Include  files; por el momento omito comentarlos.    La definición de variables, que en ASM eran: "ds", byte por defecto:    217 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 11 byte       Counter;  12 byte       FiboRes;            // Aquí va la respuesta    ¿Sabe qué es "byte"? LAS SUPOSICIONES NUNCA PUEDEN CONVERTIRSE EN INFORMACIÓN.    SI USTED AVANZA SIN ESCLARECER LAS COSAS QUE NO SABE, NO LE VA A IR BIEN EN LA VIDA    La  definición  de  (casi)  todo  lo  que  tiene  que  ver  con  este  MCU  se  encuentra  en  "mc9s08qe128.h". Yo tengo una versión REDUCIDA: "mc9s08qe128‐U‐.h", fácil de consultar.  En particular, allí figuran:    ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  typedef unsigned char byte;  typedef unsigned int word;  ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐    El comienzo en ASM era en 'Main'; aquí:    13 void main ( void )                                             /*()*/    El ciclo principal se llama igual en ASM, y las instrucciones aquí lo MIMETIZAN de una  manera  casi  IGUAL.  Inclusive,  como  los  punto  y  comas  se  usan  en  ASM  para  comenzar  COMENTARIOS,  y  en  C  para  terminar  'SENTENCES',  podemos  rearreglarlos  (;)  para  que  se  vean como el comienzo de los comentarios en ASM:    15 mainLoop:  16        clra                 ; // comienzo de comentario  17    cntLoop:  18        inca                 ; // .. tanto en ASM como en ... ¡C!    Aquí  viene  una  DIFERENCIA:  aunque  no  es  imposible  evitar  los  paréntesis  que  se  necesitan en C, pero no en ASM, para el llamado de las subrutinas, no es fácil, por lo  que los hemos dejado...:    19        cbeqa(14, mainLoop)  ; // Larger values cause overflow  20        sta(Counter)         ; // Update global variable  21        bsr(CalcFibo)        ;  22         sta(FiboRes)        ; // Store result  23        lda(Counter)         ; // ..Activate BREAKPOINT here to see..  25    bra(cntLoop)             ; // Next round    Por eso decimos que el resultado es CASI idéntico.    La función también está mimetizada de manera CASI igual, así:    30 void CalcFibo () {  32        dbnza(fiboDo);          // Fibonacci Do  33        inca;  34    rts;      36 fiboDo:  218 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 37        psha;                   // The counter  38        clrx;                   // Second last = 0  39        lda(0x01);              // Last = 1    41    FiboLoop:  42        psha;                   // Push last  43        txa;  44        add(1,sp);  45        pulx;  46    dbnz(1, sp, FiboLoop);    48 FiboDone:  49        pulh;                   // Release counter  50    rts;                        // Result in A    ["Labs-C\Fibonacci\Fibonacci_.h"]: Analicemos ahora el include file:    ["Labs‐C\Fibonacci\Fibonacci_.h"]  01 // ==================================================================  02 // Fibonacci_.h (02Fibonacci.asm), Luis G Uribe C, S09N2013  M10D2013    03 // Mimic CPU Registers  04 byte       A;                  // Use A as a counter  05 byte       X;  06 byte       H;    07 #define    clra        A = 0  08 #define    inca        A++  09 #define    cbeqa(v,l)  if(A == v) goto l  10 #define    sta(v)      v = A  11 #define    bsr(l)      l()  12 #define    lda(v)      A = v  13 #define    bra(l)      goto l  14 #define    dbnza(l)    if(‐‐A) goto l  15 #define    dbnz(c,v,l) if( stack[v+c] ‐= 1 )  goto l  16 #define    rts         return  17 #define    MAX_SP      16  18 #define    clrx        X = 0  19 #define    txa         A = X  20 #define    add(n,v)    A = A + stack[v+n]  21 #define    psha        stack[ sp‐‐ ] = A  22 #define    pulx        X = stack[ ++sp ]  23 #define    pulh        H = stack[ ++sp ]    24 word       stack[ MAX_SP ];  25 word       sp = MAX_SP ‐ 1;    26 void CalcFibo();    219 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Fibonacci\Fibonacci_.h"]: ["Labs‐C\Fibonacci\Fibonacci_.h"]:  Como  en  C  no  tenemos  acceso  a  los  registros  internos  del  CPU,  los  definimos  como  variables:    04 byte       A;                  // Use A as a counter  05 byte       X;  06 byte       H;    Una serie de MACROS (en C), definen las instrucciones del HCS08, mediante operaciones  en C que simulen el comportamiento original. Por ejemplo, 'clra' en C sería: A = 0    07 #define    clra        A = 0    El  BSR  indica  como  parámetro,  cuál  es  el  nombre  de  la  subrutina  a  la  que  se  quiere  saltar,  bsr(l);  la  codificación  es  simplemente  el  "Label",  o  nombre  de  la  rutina,  seguido por un grupo de paréntesis: l()    11 #define    bsr(l)      l()    Además escogí un ejercicio que emplea recursión, así que verán que simulé el Stack con  un  arreglo  de  Words.  Para  decrementar  la  variable  que  está  en  el  Techo  del  Stack,  y  saltar a un "Label":    15 #define    dbnz(c,v,l) if( stack[v+c] ‐= 1 )  goto l    El tamaño del Stack lo defino arbitrariamente en 16:    17 #define    MAX_SP      16    Sumarle al acumulador una variable dinámicamente definida en el Stack:    20 #define    add(n,v)    A = A + stack[v+n]    Manipular el Stack (push y pop) para el Acumulador y el registro índice H:X    21 #define    psha        stack[ sp‐‐ ] = A  22 #define    pulx        X = stack[ ++sp ]  23 #define    pulh        H = stack[ ++sp ]    La definición del Stack, y la definición e inicialización del Stack Pointer SP:    24 byte       stack[ MAX_SP ];  25 byte       sp = MAX_SP ‐ 1;    El prototipo de la función recursiva:  26 void CalcFibo();    ¿NO ENTIENDE ALGUNO DE LOS #define? ¿OTRAS DECLARACIONES?    TIENE QUE ESTUDIAR "C"  220 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 44) FIBONACCI PROGRAMADO EN C, NORMALMENTE, PARA COMPARAR ["Labs‐C\Fibonacci\090FibonacciOK.c"]  01 // ******************************************************************  02 // FibonacciOK.c (090FibonacciOK.c) Luis G Uribe C, D10N2013 M10D2013  03 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  04 // Include files  05 #include "derivative.h"        // Include peripheral declarations  06 #include "FibonacciOK_.h"  07  08 // ******************************************************************  09 void main ( void )                                             /*()*/  10 {  word    i, first = 0, last = 14;  11    volatile word fibonacci;  12    volatile byte tmp;  13  14    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  15    // >>> ALWAYS include the following 2 lines  16    // Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  17  18    #define COP_Disable 0x42  19    SOPT1 = COP_Disable;        // System Options 1  20  21    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  22    // Main loop  23  24    for( i = first; i < last; i ++ ) {  25        fibonacci = fib( i );  26        tmp ^= tmp;  27    }   // BREAKPOINT here to see: 1 2 3 5 8 13 21 34 55 89 144 233  28  29    for( ;; ) { /* EMPTY FOR */  }  30  31 }  //end main()  32  33 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  34 word fib ( word n )                                            /*()*/  35 {  36 // return n < 2 ? n : fib( n ‐ 2 ) + fib( n ‐ 1 ); // Extended  37    return n < 2 ? 1 : fib( n ‐ 2 ) + fib( n ‐ 1 ); // Conventional  38 }    COMENTARIOS a ["Labs-C\Fibonacci\090FibonacciOK.c"]: Este programa es C estándar y no amerita más comentarios.    ¿NO ENTIENDE EL OPERADOR TERNARIO? ¿NI SABE SEÑALAR CUÁL ES?    TIENE QUE ESTUDIAR "C"      221 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 45) EXPONENCIACIÓN POR MULTIPLICACIONES Y SUMAS SUCESIVAS: WHILE ["Labs‐C\Lab1\010Lab0e‐1.c"]  01 // ******************************************************************  02 // 010Lab0e‐1.c: EXPONENCIACIÓN. Luis G. Uribe C.  M10D2013  03 //               ** Usa EL STACK; >>>SOLO<<< VARIABLES DINÁMICAS **  04 // ******************************************************************  05 // Include Files  06 #include <hidef.h>             // For EnableInterrupts macro  07 #include "derivative.h"        // Include peripheral declarations    08 #include "several_U.h"         // Def: CpuIntEn, CpuIntDsb, EOF, Wait    09 // ******************************************************************  10 void main ( void )                                             /*()*/  11 {  byte base = 2, exponent=5;  // EXAMPLE 2^5=32. See 3^5=243  12    byte resultExp = 1;  13    byte multiplicand, multiplier, resultMpy;    14    while( exponent‐‐ ) {       // ...by repeated multiplications  15        multiplier   = base;  16        multiplicand = resultExp;  17        resultMpy = 0;  18        while( multiplier‐‐ ) { // ...by repeated additions  19            resultMpy += multiplicand;  20        }  21        resultExp = resultMpy;  22    }    23    Wait( 0 );                  // Wait for Ever  24 }    COMENTARIOS a ["Labs-C\Lab1\010Lab0e-1.c"]: Este programa es C estándar, por tanto no amerita más comentarios.    46) EXPONENCIACIÓN, MULTIPLICACIONES Y SUMAS SUCESIVAS: FUNCIONES ["Labs‐C\Lab1\020Lab0e‐2Sub.c"]  01 // ******************************************************************  02 // 020Lab0e‐2Sub.c: EXPONENCIACIÓN. Luis G. Uribe C. J21N2013  03 //                  ** Usa EL STACK >>>SOLO<<< VARIABLES DINÁMICAS **  04 // ******************************************************************  05 // Include Files  06 #include <hidef.h>             // For EnableInterrupts macro  07 #include "derivative.h"        // Include peripheral declarations    08 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  09 // Functions Prototypes  10 byte expo( byte base, byte exponent );  11 byte mply( byte multiplicand, byte multiplier );    222 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 12 // ******************************************************************  13 void main ( void )                                             /*()*/  14 {  byte base = 2, exponent=5;  // EXAMPLE 2^5=32. See 3^5=243  15    volatile byte resultExp;    16    resultExp = expo( base, exponent );    17    Wait( 0 );                  // Wait for Ever  18 }    19 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  20 byte expo ( byte base, byte exponent )                         /*()*/  21 {  byte resultExp = 1;    22    while( exponent‐‐ ) {       // ...by repeated multiplications  23        resultExp = mply( resultExp, base );  24    }  25    return resultExp;  26 }    27 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  28 byte mply ( byte multiplicand, byte multiplier )               /*()*/  29 {  byte resultMpy = 0;    30    while( multiplier‐‐ ) {     // ...by repeated additions  31        resultMpy += multiplicand;  32    }  33    return resultMpy;  34 }    COMENTARIOS a ["Labs-C\Lab1\020Lab0e-2Sub.c"]: Este programa es C estándar y no amerita más comentarios.    47) EXPONENCIACIÓN, FUNCIONES: PARA VISUAL STUDIO ["Labs‐C\Lab1\030Lab0e‐2SubVisualStudio.c"]  01 // ******************************************************************  02 // 030Lab0e‐2SubVisualStudio.c EXPONENCIACIÓN  LG. Uribe C.  J21N2013  03 //                 ** Usa EL STACK; >>>SOLO<<< VARIABLES DINÁMICAS **  04 // ******************************************************************  05 #define  Wait( e )   while(!(e))  06 typedef unsigned char byte;    07 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  08 // Functions Prototypes  09 byte expo( byte base, byte exponent );  10 byte mply( byte multiplicand, byte multiplier );    11 // ******************************************************************  12 void main ( void )                                             /*()*/  13 {  byte base = 3, exponent=5;  // EXAMPLE 2^5=32. See 3^5=243  223 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 14    volatile byte resultExp;    15    resultExp = expo( base, exponent );    16    Wait( 0 );                  // Wait for Ever  17 }    18 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  19 byte expo ( byte base, byte exponent )                         /*()*/  20 {  byte resultExp = 1;    21    while( exponent‐‐ ) {       // ...by repeated multiplications  22        resultExp = mply( resultExp, base );  23    }  24    return resultExp;  25 }    26 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  27 byte mply ( byte multiplicand, byte multiplier )               /*()*/  28 {  byte resultMpy = 0;    29    while( multiplier‐‐ ) {     // ...by repeated additions  30        resultMpy += multiplicand;  31    }  32    return resultMpy;  33 }    COMENTARIOS a ["Labs-C\Lab1\030Lab0e-2SubVisualStudio.c"]: Este  programa  es  C  estándar  para  ser  procesado  por  Visual  Studio,  y  no  amerita  más  comentarios.    48) PRIMER PROGRAMA PARA HCS08: MINIMUM C PROGRAM ["Labs‐C\lab1\00a_l1.c"]  01 // ******************************************************************  02 // 00a_L1.c, MINIMUM 'C' Program for HCS08, J21N2013  03 // Luis G. Uribe C., For CodeWarrior 10.3  04 // ==================================================================  05  06 void main ( void )                                             /*()*/  07 {  08    for( ;; ) {  09        /* EMPTY FOR */  10    }  11 }    COMENTARIOS a ["Labs-C\lab1\00a_l1.c"]: Como  ya  se  indicó,  FORMA  y  PRESENTACIÓN  de Código  en  nuestro  trabajo,  es  FUNDAMENTAL  para  la  vida  de  los  proyectos.  Los  programas  deben  poder  entenderse,  por  quien  los  hace, cuando vuelva a mirarlos, y por las personas que vengan después a modificarlos o  repararlos.  224 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Para eso se requieren, entre otras cosas, comentarios útiles, resaltantes y claros.    Igual que en programas escritos para otros Lenguajes, en las primeras líneas se coloca  el  Nombre  del  programa,  su  propósito,  para  qué  plataforma  y  qué  revisión  (identificación que permite llevar la pista de las modificaciones que se le han hecho);  hay  que  incluir  siempre  fechas  que  indiquen  el  comienzo  del  programa,  cuándo  se  lo  modificó y la datación de la revisión actual. Cuando es importante, hay que agregar una  lista  de  fechas  e  indicar  explícitamente  qué  cosas  se  añadieron,  se  quitaron  o  se  modificaron.  También, las  iniciales  de  la persona  que hizo  cambios  en  el  código,  que  permita identificarlas para saber a quién preguntar si fuera necesario.    02 // 00a_L1.c, MINIMUM 'C' Program for HCS08, J21N2013  03 // Luis G. Uribe C., For CodeWarrior 10.3    Programa  desarrollado  el  Jueves  21  de  Noviembre  de  2013,  para  el  CodeWarrior  10.2  (ahora se sabe que funciona también en CW 10.5).    SIEMPRE  hay  que  indicarle  al  CPU  dónde  comienza  su  código  pero,  a  diferencia  del  trabajo que ya hicimos anteriormente en Assembler, aquí es el compilador de C, y las  demás  herramientas  asociadas  a  él  y  a  su  entorno  de  desarrollo,  como  el  Linker,  el  Absolute Loader, etc.) los que determinan la dirección de arranque (manejo automático  de los Vectores de Interrupción). Gracias a Dios.    No hay mucho más que se pueda agregar para explicar este ejercicio tan sencillo.    06 void main ( void )                                             /*()*/  07 {  08    for( ;; ) {  09        /* EMPTY FOR */  10    }  11 }    ¿NO COMPRENDE ALGUNO DE LOS ELEMENTOS DE ESTE PROGRAMA?  ¡DESISTA!    ¿NO ENTIENDE EL FOR?  TIENE QUE ESTUDIAR "C"    49) PROGRAMA EN C PARA HCS08, UN POCO MÁS ÚTIL El  programa  anterior  compila  bien  (aun  cuando  no  hace  nada  más;  es  una  forma  equivalente  al  "BRA  *"  que  hemos  usado  ya  antes),  hay  un  par  de  COSAS  QUE  SIEMPRE  TENEMOS QUE AGREGAR EN NUESTROS PROGRAMAS, y las revisaremos a continuación:    ["Labs‐C\Lab1\01c_L1.c"]  01 // ******************************************************************  02 // 01c_L1.c, Small C program M10D2013  03 // Luis G. Uribe C., For CodeWarrior 10.3  04  05 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  06 // Include Files  07 #include <hidef.h>             // For EnableInterrupts macro  08 #include "derivative.h"        // Include peripheral declarations  09  225 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 10 // ******************************************************************  11 void main ( void )                                             /*()*/  12 {  13    byte var;                   // Depending on optimization options,  14                                // ..C compiler may learn that nothing  15    var = 0;                    // ..is realy being done, and replace  16    while( 1 ) {                // ..var, or all your code for empty/  17        var ++;                 // ..null, block. If var is not seen  18    }                           // ..in Debug remove some optimization  19 }                              // ..declaring:   volatile byte var;    COMENTARIOS a ["Labs-C\Lab1\01c_L1.c"]: Los dos "include files" listados a continuación, casi siempre se incluyen.    07 #include <hidef.h>             // For EnableInterrupts macro  08 #include "derivative.h"        // Include peripheral declarations    En  "derivative.h"  CodeWarrior  incluye  la  particularización  del  MCU  SUYO.  Así,  los  programas NO cambian si son para uno u otro MCU; y CW parametriza en este Include File  todo lo que tiene que ver con cada MCU en particular.    Por ejemplo, para nuestro micro, la sección más importante en "derivative.h" es:    #include <mc9s08qe128.h>    <hidef.h> (Hardware Interface Definitions) se usa menos, ya que fundamentalmente lo que  incluye son definición de Macros para la habilitación de Interrupciones. Más adelante  veremos cómo manejamos nosotros estas funcionalidades.    El ejercicio lo que trata de hacer, en su simplicidad, es incrementar permanentemente  una variable 'var', de tipo 'unsigned char' (byte).    La aclaratorio que hay que hacer, para que ustedes recuerden sus CONOCIMIENTO DE C, es  que el Compilador puede darse cuenta, dependiendo de las opciones de optimización, que  no se hace NADA con 'var': a pesar de incrementarla, nadie la usa. Así que, como buen  "optimizador",  puede  quitar  la  variable  por  completo,  y  si  yo  hubiera  diseñado  el  Optimizador, eliminaría TODO EL CÓDIGO.    Como  ustedes  deben  saber,  de  sus  cursos  de  C,  pueden  deshabilitarse  las  opciones  de  OPTIMIZACIÓN  para  que  el  compilador  NO  elimine  ni  la  variable  inútil,  ni  el  código  INÚTIL.    Otra  forma  que  ustedes  debieron  aprender  en  sus  cursos  del  lenguaje,  es  que  si  se  declara esa variable como "volatile byte var;", el compilador asumirá que, sin que él  entienda cómo, esa variable será usada en alguna otra parte que él desconoce (como, por  ejemplo, si así se hace referencia a un Puerto Digital de Salida: El entorno externo  USARÁ  los  valores  calculados  DENTRO  del  programa,  aunque  no  se  haga  evidente  para  el  compilador que esto sea así.    13    byte var;                   // Depending on optimization options,  14                                // ..C compiler may learn that nothing  226 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 15    var = 0;                    // ..is realy being done, and replace  16    while( 1 ) {                // ..var, or all your code for empty/  17        var ++;                 // ..null, block. If var is not seen  18    }                           // ..in Debug remove some optimization  19 }                              // ..declaring:   volatile byte var;    Note en los programas en C, similarmente a lo que hicimos en los códigos en Assembler,  la muy ajustada línea en donde se colocan los comentarios. En principio, // comienza en  la  columna  32.  Y  aquí  también  puede  haber  ocasiones  en  las  que  haya  que  mover  los  comentarios, pero mientras más estandarizado esté la forma de presentación del código,  mejor para todos.    50) "INTERRUPTS" EN EL LENGUAJE C Las  Interrupciones  NO  forman  parte  del  lenguaje  C,  ni  tampoco  una  gran  cantidad  de  Extensiones, como la manipulación de apuntadores, identificándolos como 'far' o 'near',  o  los  Vectores  de  Interrupción,  o  el  manejo  de  memoria  Segmentada  mediante  la  aplicación  de  Modelos:  Short,  Tiny,  Large,  Medium,  Data.  (¿SABE  USTED  LO  QUE  SIGNIFICAN?)    Muchos  dispositivos  dividen  su  memoria  en  Segmentos;  por  ejemplo,  los  modelos  de  la  escala baja de la familia 0x86 de Intel. Ellos usan 16 bits de dirección, con lo cual  pueden acceder a un máximo de 64 KBytes (65536). Mediante un registro que indica dónde  comienza un Segmento (y cuyo valor puede alterarse de manera dinámica, para seleccionar  en  Run  Time  VARIOS  Segmentos  de  memoria),  se  puede  llegar  hasta  posiciones  mucho  más  allá de los 64KB del modelo. El PC original tenía 640 KBytes de RAM (no llegaba ni al  Megabyte).    En  Intel  hay  4  registros  de  Segmento,  con  una  función  similar,  llamados  CS,  Code  Segment;  DS,  Data  Segment,  SS,  Stack  Segment  y  EX,  Extra  Segment.  El  CS  confina  el  código del programa, que ha de ser capturado mediante el CS y el PC (Program Counter).    En Assembler, si el programa es más grande que 64KB, se programa el CS y, en el momento  en que se desea saltar a una posición que está fuera de ese Segmento, se reprograma el  CS para utilizar otros 64KB. La manipulación suele no ser muy sencilla, ya que si se  cambia el CS, DESAPARECEN las instrucciones que están haciendo la reprogramación, pero  hay trucos que se acostumbran.    Lo cierto es que para trabajar en C, si el programa es más grande que un Segmento, una  manera de indicarle al compilador que él se haga cargo de ese trabajo de reprogramación  del CS es, por ejemplo, definiendo apuntadores que puedan barrer mucho más de los 64KB  (por ejemplo, TODA la memoria). La forma es mediante el indicativo 'far', así:      far char *ptr;    Así, el compilador se encarga de ir haciendo el trabajo que en Assembler correspondería  al programador, de verificar si cada dirección está, o no, confinada a un segmento. En  caso  de  no  estarlo,  el  compilador  incluye  código  para  la  reprogramación  del  CS  y  la  transición, invisible para el programador, a otro Segmento.    Algo parecido ocurre con el DS, o Segmento de Datos. El de Código verifica que algún  salto (JMP) o llamado a subrutina (JSR), se comporte bien no importa en qué parte de la  227 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 memoria se encuentre el código. El de Dato, DS, opera de la misma manera, pero ya no  para  código,  sino  para  manipular aquellas  áreas  de  memoria  que  están  en  una posición  más allá de las fronteras de 64KB impuestas por el Data Segment.    Este  ejercicio  muestra  cómo  manejar  las  Interrupciones,  con  la  Extensión  para  ese  propósito  definida  por  Motorola,  Freescale,  CodeWarrior,  mediante  un  código  sencillo  que hace una manipulación cualquiera sobre una variable 'var', y cada vez que se recibe  una  interrupción  del  botón  IRQ  (external  Interrupt  Request),  se  niega  una  variable  global, GlobalVar.      ["Labs‐C\Lab1\03b_L1_M.c"]  01 // ******************************************************************  02 // Programa 03b_L1_M.C, HCS08_CPU, Luis G. Uribe C, M10D2013  03 //  04 // Uso de Rutinas de Interrupción:  05 // A) Habilitación del periférico para Interrumpir  06 // B) Habilitación del CPU (cli) para que acepte Interrupciones  07 //    (aparte del RESET, que **NO** es enmascarable)  08 // C) Inclusión del código de la rutina de Interrupción (Interrupt  09 //    Service Routine, ISR).    10 // ******************************************************************  11 // Include Files  12 #include <hidef.h>             // For EnableInterrupts macro  13 #include "derivative.h"        // Include peripheral declarations    14 #include "several_U.h"         // Def: CpuIntEn, CpuIntDsb, EOF, Wait    15 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 // Functions Prototypes  17 byte rutina( byte var );    18 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  19 // Global Variables  20 volatile byte GlobalVar;    21 // ******************************************************************  22 void main ( void )                                             /*()*/  23 {  volatile byte var = 30;    24    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  25    // >>> ALWAYS include the following 2 lines  26    // Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  27    #define COP_Disable 0x42  28    SOPT1 = COP_Disable;        // System Options 1    29    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  30    // Enable Interrupts for IRQ  31    //  32    // 1‐a) ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  33    IRQSC_IRQPE = 1;        // BSET IRQSC_IRQPE, IRQSC: Enable IRQ PIN  228 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   34    // 1‐b) ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  35    // .. Clear any possible previous, false, interrupts by Setting  36    // .. IRQACK bit in IRQSC (IRQ Status and Control register)  37    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK, IRQSC    38    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  39    // >> >>> Try: IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK <<< <<<  40    // 2) ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  41    //  42    IRQSC_IRQIE = 1;        // BSET IRQSC_IRQIE,IRQSC // IRQ pin IntEn    43    // ===============================================================  44    cli;                        // CPU Interrupt ENABLE    45 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  46 loop:  47    while( 1 ) {  48        var = rutina( var ‐ 10 );  49        var = var;              // COLOCAR BREAKPOINT AQUÍ (Debugger)  50    }    51    Wait( 0 );                  // Wait for Ever  52 }    53 // ==================================================================  54 byte rutina ( byte var )       // Subrutina de ejemplo         /*()*/  55 {  56    while( var < 30 ) {  57        var++;  58    }  59    return var;  60 }    61 // ==================================================================  62 // EJEMPLO de Rutina de Interrupción (ISR for IRQ signal)  63 // NOTE: Es necesario realizar un ACKNOWLEDGE para que vuelva a suce‐  64 //       der la interrupción (Interrupt Handshaking... different for  65 //       each peripheral: You need to read Manual for each one... :‐(    66 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  67 {  68    GlobalVar ^= (byte)‐1;  69    IRQSC_IRQACK = 1;   // BSET IRQSC_IRQACK, IRQSC: ACK IRQ Interrupt  70 }                      // ..(Rearm IRQ Interrupts)    COMENTARIOS a ["Labs-C\Lab1\03b_L1_M.c"]: Los Include Files normales y uno extra, "several_U.h" (LOS PRESENTO MÁS ADELANTE):    12 #include <hidef.h>             // For EnableInterrupts macro  13 #include "derivative.h"        // Include peripheral declarations  229 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 14 #include "several_U.h"         // Def: CpuIntEn, CpuIntDsb, EOF, Wait    El prototipo de la función 'rutina', que retorna un 'byte' y a la cual se le pasa un  también byte, ( byte var ):    17 byte rutina( byte var );    Variable global. Nótese que se la ha definido como 'volatile', para que el OPTIMIZADOR  del compilador NO LA OMITA, ni al código que también sería detectado como inútil (este  programa es bastante inane):    20 volatile byte GlobalVar;    Comienzo del programa y definición de 'var', también como 'volatile':    22 void main ( void )                                             /*()*/  23 {  volatile byte var = 30;    En  los  programas  que  escribimos  en  lenguaje  ensamblador,  se  incluían  siempre  al  comienzo 4 instrucciones, dos para inicializar el SP, y dos para detener el COP, CPU  Operating Properly. En C, el compilador incluye código invisible para el programador,  mediante  el  cual  inicializa  apropiadamente  el  Stack,  por  lo  que  nos  desentendemos  de  eso.    Pero el COP sí hay que deshabilitarlo por programa. Un posible equivalente a:    Main: lda    #$42          ; $42(%0100_0010)? COPE(SOPT1b7)=0 ($1802)         sta    $1802        ; (SOPT1 Pup init to $C2 = %1100_0010)    es el siguiente:    27    #define COP_Disable 0x42  28    SOPT1 = COP_Disable;        // System Options 1    Ahora,  para  habilitar las  interrupciones  del  IRQ,  éste  es  el  código  en  Assembler  que  siempre hemos usado,          BSET    IRQSC_IRQPE,  IRQSC      ; Enable IRQ PIN        BSET    IRQSC_IRQACK, IRQSC        BSET    IRQSC_IRQIE,  IRQSC      ; IRQ pin Interrupt Enable        cli                              ; CPU Interrupt ENABLE    en C el que corresponde es el siguiente:    33    IRQSC_IRQPE  = 1;        // BSET IRQSC_IRQPE, IRQSC: Enable IRQ PIN  37    IRQSC_IRQACK = 1;        // BSET IRQSC_IRQACK, IRQSC  42    IRQSC_IRQIE = 1;         // BSET IRQSC_IRQIE,IRQSC // IRQ pin IntEn  44    cli;                     // CPU Interrupt ENABLE    Usted puede ensayar también (mucho MEJOR):    39    IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK  230 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   Funciona igual, pero es más simple.    El ciclo principal del programa es muy sencillo, y en realidad corresponde a una espera  artificial hasta que ocurra la interrupción. Este código debe ser seguido empleando el  Debugger (colocando un Breakpoint donde se indica):    46 loop:  47    while( 1 ) {  48        var = rutina( var ‐ 10 );  49        var = var;              // COLOCAR BREAKPOINT AQUÍ (Debugger)  50    }    51    Wait( 0 );                  // Wait for Ever    La 'rutina' llamada, también es artificial:    54 byte rutina ( byte var )       // Subrutina de ejemplo         /*()*/  55 {  56    while( var < 30 ) {  57        var++;  58    }  59    return var;    La  rutina  de  Interrupciones  tiene  varios  ASPECTOS  NOVEDOSOS.  Como  dijimos  que  las  funcionalidades  no  definidas  por  el  estándar  ANSI  se  manejan  como  Extensiones  al  lenguaje,  y  al  compilador,  las  define  cada  proveedor,  y  si  bien  es  cierto  que  se  parecen bastante entre uno y otro, ¡no son iguales! Por tanto... NO SON PORTABLES. Y  hay que aprenderlas para cada suplidor.    La  definición  introduce  el  modificador  no  estándar  'interrupt';  lo  tienen  casi  TODOS  los que hacen compiladores.    A  continuación  se  agrega  una  constante,  'VectorNumber_Virq',  definida  de  manera  simbólica mediante la inclusión de <mc9s08qe128.h> (que a su vez, ha sido incluida por  "derivative.h",  junto  con  las  declaraciones  de  los  símbolos  de  los  periféricos).  Obviamente  este  'VectorNumber_Virq'  define  la  posición  de  la  ISR  en  la  tabla  de  Vectores de Interrupción (muy similar a lo que hicimos en ASM)    Luego viene el TIPO de información que la rutina retorna; las rutinas de Interrupción,  ISR, JAMÁS DEVUELVEN NADA y su tipo SIEMPRE es 'void'. Si una ISR, Interrupt Service  Routine, debe comunicarse con el programa principal, tiene que hacerlo intercambiando  valores mediante variables globales, como 'GlobalVar' en el presente ejemplo.    Las  ISR  JAMÁS  RECIBEN  PARÁMETROS,  como  las  subrutinas  convencionales;  por  eso  el  '(void)'; si la ISR tiene que leer valores que 'main' o alguien más produce, tienen que  intercambiarse TAMBIÉN (leerse, escribirse) en variables globales:    66 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  67 {    231 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 IRQISR  (Rutina  de  Servicio  de  Interrupciones,  ISR,  para  la  Interrupción  del  botón  de  Interrupt  Externo,  IRQ),  hace  en  este  ejercicio  algo  muy  simple:  negar  la  variable  GlobalVar (de manera booleana: cambiando ceros por unos y viceversa):    68    GlobalVar ^= (byte)‐1;    ¿ENTIENDE LA EXPRESIÓN? SI NO, TIENE QUE ESTUDIAR "C"    Luego,  ejecuta  el  protocolo  de  reconocimiento  (Acknowledge)  de  Interrupción  del  IRQ,  que le indica al harware que el software YA atendió su solicitud de interrupción. Como  hemos dicho a todo lo largo de este documento, en Freescale cada periférico tiene su  propio protocolo de IntACK, que hay que estudiar en los manuales de referencia. En el  caso del IRQ es muy simple (refiérase también a los ejercicios en Assembler):    69    IRQSC_IRQACK = 1;   // BSET IRQSC_IRQACK, IRQSC: ACK IRQ Interrupt  70 }                      // ..(Rearm IRQ Interrupts)    NOTA:  En  la  familia  anterior:  HC08  (sin  la  'S'),  el  IRQ  tenía  dos  diferencias  fundamentales con la familia actual, HCS08:    1)  Las  interrupciones  de  IRQ  estaban  ¡HABILITADAS  SOLO  CON  ENCENDER  EL  MCU!  Esto  es  **MUY POCO ORTODOXO**, y fue eliminado en el HCS08...    2)  Bastaba  con  que  el  PC  cargara  el  contenido  del  Vector  de  Interrupciones  del  IRQ,  como  parte  del  proceso  de  atender  esa  interrupción,  para  que  automáticamente  el  Hardware tuviera un Acknowledge.    Esto bien podrían haberlo dejado, pero lo cambiaron: ahora hay que darle explícitamente  un ACK, según la línea 69 arriba.    EL INCLUDE FILE "SEVERAL_U.H": 01 // ******************************************************************  02 // several_U.h, Luis G. Uribe C, S30N2013  03  04 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  05 // Several Defines  06  07 #undef  Wait  08 #define Wait(e)   while( !(e))  09 #undef  CpuIntEn  10 #define CpuIntEn  __asm CLI  11 #undef  CpuIntDsb  12 #define CpuIntDsb __asm SEI  13 #undef  EOF  14 #define EOF ((word)‐1)    COMENTARIOS a ["Labs-C\Lab1\several_U.h"]: Primero  se  define  un  'Wait',  empleando  un  'while'.  Wait  es  muy  conveniente  expresado  cosas  como:  Wait(  kbhit()  ):  espere  hasta  que  llegue  una  tecla.  Empleando  un  while  habría que escribir  232 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08     while ( ! (kbhit() );    Como esta es una proposición en NEGATIVO, se hace más difícil su comprensión.    En  general  nunca  uso  expresiones  en  negativo;  las  convierto  en  positivo,  como  es  el  caso del Wait, pero también, en Assembler, no uso BNE sino que introduzco un mnemónico  nuevo:  BDIFF.  Es  lo  mismo,  pero  la  comprensión  es  mucho  mejor  con  la  instrucción  en  positivo: SALTE SI SON DIFERENTES.    Mucho mejor que SALTE SI *NO* SON IGUALES.    Antes  de  definir  el  Wait,  lo  '#undef'  ('undefine'),  por  si  este  compilador  tiene  definido  su  propio  Wait  (y  como NO  voy  a usar  ese  Wait  del  compilador,  si  es  que  lo  tuviera,  aquí  puedo  cambiarlo  por  EL  MÍO.  El  Visual  Studio  de  Microsoft,  ¡tiene  UN  WAIT!, y algunos otros compiladores también)    07 #undef  Wait  08 #define Wait(e)   while( !(e))    Para  habilitar  las  interrupciones  del  CPU  se  emplea  una  instrucción  de  lenguaje  ensamblador, que ¡NO puede expresarse en lenguaje C!    Pero  una  de  las  extensiones  de  CASI  todos  los  compiladores  modernos,  permite  incluir  instrucciones de Assembler, EN LÍNEA con el código en C. Esto es exactamente lo que se  hace a continuación con el 'asm':    10 #define CpuIntEn  __asm CLI    Lo mismo se hace para la deshabilitación de interrupciones:    12 #define CpuIntDsb __asm SEI    (Además,  que  mis  nombres,  CpuIntEn  y  CpuIntDsb,  con  seguridad  que  son  ¡MUCHO  más  INTELIGENTES, DIGO: INTELIGIBLES, que los originales de Freescale!)    Y,  finalmente,  hay  un  símbolo  equivalente  a  su  homónimo  en  el  ANSI  C  que  ya  hemos  empleado, y que yo lo defino a mi conveniencia aquí (en el presente ejercicio, NO se  emplea):    14 #define EOF ((word)‐1)    ¿NO ENTIENDE EL #define DE LA LÍNEA 14?    TIENE QUE ESTUDIAR "C"    OBSERVACIÓN: Ni hemos comenzado los ejercicios en C, y les pregunto:    ‐ ¿Sabían algo de #undef?    233 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ‐ ¿Qué valor inicial tiene GlobalVar (línea 20)?    ‐ ¿Por qué en la línea  48        var = rutina( var ‐ 10 );  'rutina' está PEGADA al paréntesis izquierdo, en tanto que en:  54 byte rutina ( byte var )       // Subrutina de ejemplo         /*()*/  'rutina' está SEPARADA del paréntesis izquierdo?    ‐ ¿Para qué puede servir el 'moñito': /*()*/ ?    Imagino, pero estoy desconfiado, que ¿sí habían usado #define?    51) "Interrupts" en el lenguaje C, variación El problema ["Labs‐C\Lab1\03b_L1_M2.c"] es similar al anterior y utiliza simplemente:    39    IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK    Así:       // Enable Interrupts for IRQ     IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;       IRQSC_IRQIE = 1;            // BSET IRQSC_IRQIE,IRQSC // IRQ pin IntEn       cli;                        // CPU Interrupt ENABLE     MANEJO DE TIMERS A quí  les  presento  mi  primera  librería  en  C,  el  manejador  de  Temporizadores.  Primero  les  incluyo  una  breve  reseña  acerca  de  las  diversas  versiones,  en  Lenguaje Ensamblador y ahora en Lenguaje C.    Refiérase a los ejercicios que están en la carpeta: Carpetas Labs‐C\Lab2\TimersJ    52) LIBRERÍA DE TIMERS: timersJ.c 1) Esta segunda etapa de mi docencia en la USB comenzó en Abril de 2003 con el curso de  Circuitos Digitales. La primera versión de la biblioteca de Timers en ASM, Timers8, la  hice  para  mi  primer  curso  de  Arquitectura  del  Computador,  el  Lunes  24Nov2003,  seguramente la terminé a las 3 am, antes de comenzar mi clase de ese día a las 9:30. El  microcontrolador  que  se  empleaba  en  aquella  época  era  el  Motorola  68HC908GP32,  con  memoria  Flash  (el  "9"  significa  eso)  y  32  KBytes  de  Memoria  (toda  Flash,  menos  512  Bytes  de  RAM).  El  ensamblador  tenía  por  nombre  WinIDE  y  era  gratuito.  Tenía  serias  deficiencias en el tratamiento de Macros, lo que me obligó, por ejemplo, a definir DOS  tipos  de  'Setimer'  y  de  'Wait_on':  uno  para  Constantes,  y  otro  para  Variables  ('SetimerK', 'Wait_onK', 'SetimerV', 'Wait_onV').    2)  La  siguiente  revisión,  Timers8B,  la  hice  en  mi  siguiente  curso  de  Arquitectura  un  año después: Lunes 08Nov2004, siendo la más novedosa mejora el haber hecho la ISR del  234 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Timer (TBM: Timer Basic Module) interrumpible por otros dispositivos, y NO reentrante.  Esto permitía estudiar el concepto, y a la vez hacer que mis timers, que realidad no  requieren  una  exactitud  ajustada  al  microsegundo,  no  molestaran  a  los  demás  periféricos,  en  caso  de  que  aquellos  necesitaran  interrumpir.  La  no  reentrancia  se  requería  para  evitar  que  el  mismo  TBM  auto  interrumpiera  su  propia  ISR,  en  caso  de  haber  terminado  un  conteo  por  Hardware  antes  de  haber  terminado  su  Interrupt  Service  Routine (por motivo de los demás periféricos). No es conciente aceptar una interrupción  de  un  periférico  sin  haber  terminado  su  ISR  para  una  interrupción  anterior.  Esta  funcionalidad se logra principalmente, haciendo que la ISR del TBM auto inhabilite su  Interrupt Enable y rehabilite (CLI) las interrupciones del CPU. Recuerde que Motorola  en sus Manuales de Referencia, advierte EQUIVOCADAMENTE en contrario de esta práctica.    3) La revisión Timers8C es del Jueves 01Dic2005 para mi siguiente curso; allí incluí la  capacidad de que OTRAS ISRs pudieran emplear las Macros de la librería de Timers; así,  por ejemplo, el IRQ o las comunicaciones podían, dentro de sus ISRs, temporizar ciertos  eventos  que  el  programador  necesitara  (timeouts  y  similares;  control  de  "throttle"  o  regulador de velocidad).    4)  Timers8D,  2007:  esencialmente  la  diferencia  fue  modificar  la  programación  del  Oscilador  maestro  del  CPU  para  que  en  vez  de  8Mhz  trabajara  a  7,987,200  Hz  para  aproximar mejor la velocidad de 9600 bps en el SCI.    5) Timers8G: Cuando fui a dictar el curso de Arquitectura en Abril de 2009 me encontré  que el Laboratorio había adoptado el compilador CodeWarrior por varias razones:    a) manejaba C además de ASM;  b) era el que Motorola promocionaba;  c) WinIDE se estaba quebrando, imagino que no ganaban mucho regalando su compilador, y  ahora quería cobrarlo, lo que obligó a compararlo con CW.    El  cambio  no  ofrecía  en  ese  momento  NINGÚN  beneficio  para  mi  clase  de  Arquitectura,  pero desarraigaron definitivamente el WinIDE de todos los PCs del laboratorio, y ya no  le  dieron  más  soporte.  Eso  me  obligó  a  generar  el  Jueves  16Abr2009  esta  versión  Timers8G  específicamente  para  CodeWarrior.  Las  diferencias  no  eran  tantas,  pero  de  todas maneras fue un trabajón:    Por ejemplo, en WinIDE y en CW un bit se identificaba como:  Timer0Ready    EQU 0                   ; ..Bit #0; for brclr & brset  Timer0.Rdy     EQU 0                   ; ..Bit #0; for brclr & brset    En WinIDE y en CW la definición de las macros tenía el siguiente  aspecto:  $MACRO SetimerK timer,time             ; time is a CONSTANT (K)  Setimer: MACRO timer,time              ; time is a CONSTANT (K)    Desaparece el $ de $MACRO, y cambia de posición... El nombre de la Macro ahora es LA  ETIQUETA.    En WinIDE, por ejemplo, al primer parámetro de la Macro se le hacía referencia como %1,  en tanto que en CW es \1; el % en CW identifica números binarios. Los números en base  diez ahora son el estándar, no precisan identificador especial; en WinIDE se escribían  como: !10  235 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   6) Mis clases hasta el 2012 no incluyeron las de Arquitectura; cuando regresé después  de 3 años, acababan de cambiar ahora... EL CHIP. El sucesor fue el de la familia HS:  HC9S08QE128  y  se  incluía  la  tarjeta  de  desarrollo  DEMOQE128.  TODO  VOLVIÓ  A  CAMBIAR:  había periféricos nuevos que reemplazaban funciones antiguas. Por ejemplo, el TMB ya no  existía y lo reemplazó el RTC. Los ADC siguieron siendo los mismos, pero CAMBIARON los  NOMBRES  de  los  puertos  y  bits,  de  los  originales  de  Motorola  a  los  de  nomenclatura  Freescale.  Algunos  cambios  sutiles,  como  los  del  IRQ  que  ya  hemos  mencionado;  ya  el  oscilador maestro del CPU permite usarlo SIN programación, por lo que eliminó esa parte  de la biblioteca de soporte.    Para colmo, la versión actual del CodeWarrior es la 10.5, aunque en el Laboratorio sólo  se  le  da  soporte  a  la  antigua  6.3,  que  difieren  bastante  las  dos.  Incluso,  la  nueva  10.x tiene nuevos bugs que hacen que programas que corrían bien en la 6.3, ahora no lo  hagan.    Así que correspondía la versión TimersH, y aprovechando la nueva denominación del chip,  la rebauticé como TimersHS.    La  librería  que  aquí  presento  para  el  lenguaje  C  está  basada  en  TimersHS,  pero  con  diferencias que me obligaron a avanzar la versión; por eso en C la biblioteca de Timers  que les ofrezco es la TimersJ.c (La siguiente a H es la I, pero hay ciertas letras que  en los listados aún se prestan para confusión, dependiendo a veces del tipo de letra:  La i con el 1, la O con el 0; la A con el 4... por eso no es timersI sino TimersJ)    Si usted entendió a la perfección la versión en Assembler, no debería tener problemas  con el estudio de TimesrJ.c      ["Labs‐C\Lab2\TimersJ\timersJ.c"]  001 // ******************************************************************  002 // TimersJ.c, Luis G. Uribe C.  C19D2013  003 //  004 // ******************************************************************  005 // Include Files  006 #include "timersJ_.h"  007  008 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  009 // Global Variables  010 volatile word timersMS[ NTIMERS ];  011  012 // ==================================================================  013 void IniTimers ( byte ntimers )                                /*()*/  014 {  // By default LINK init ALL timers ( timersMS[ NTIMERS ] ) to ZERO  015    // 'ntimers' NOT used here. Customize NTIMERS, in timersJ_.h  016  017    // Assure user defined NTIMERS (timersJ_.h) is 2's power!  018    Sassert( ! ( NTIMERS & ( NTIMERS ‐ 1 ) ) );  019  020    RTCSC = RTC_FLAGS;          // Use 1KHz internal clock/1: 1mS tick  021                                // ..Clear RTCIF; IntEnable RTC  022 }  /* IniTimers() */  236 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 023  024 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  025 word SetimerMS ( byte timer, word timeMS )                     /*()*/  026 {  word tmp = timersMS[ timer & ( NTIMERS ‐ 1 ) ]; // Reminding time  027  028    timersMS[ timer & ( NTIMERS ‐ 1 ) ] = timeMS;  // ticks (mSeconds)  029    return tmp;  030  031 }  /* SetimerMS() */  032  033 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  034 byte Timeout ( byte timer )                                    /*()*/  035 {  036    if( timersMS[ timer & ( NTIMERS ‐ 1 ) ] ) {  037        return 0;  038    }  039    return 1;                   // else...  040  041 }  /* Timeout() */  042  043 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  044 void WTimer ( byte timer )     /* >>>WARNING<<<: BLOCKS CPU */ /*()*/  045 {  046    while( ! Timeout( timer ) ) {  047        /* Empty While */  048    }  049  050 }  /* WTimer() */  051  052 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  053 void WaitMS_on ( byte timer, word timeMS )                     /*()*/  054 {  /* >>>WARNING<<<: BLOCKS CPU */  055  056    SetimerMS( timer, timeMS );  057    WTimer( timer );  058  059 }  /* WaitMS_on() */  060  061 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  062 void WaitISR_on ( byte timer, word timeMS )                    /*()*/  063 {  //Save/Restore CCR is NOT possible from 'C'. We need the ASSEMBLER  064  065    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  066    // SAVE ACTUAL INTERRUPT MASK VALUE  067    asm {  068     tpa                        // Push CCR saves IMask status  069      psha                      // ..  070   }  071  072    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  073    // C Code  074    CpuIntDsb;                  // DISABLE INTERRUPTS (IMask=1): Let  237 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 075    SetimerMS( timer, timeMS ); // ..Setimer used inside Int. Routines  076    CpuIntEn;                   // REENABLE INTERRUPTS: Let RTC Count  077    WTimer( timer );  078  079    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  080    // RESTORE SAVED INTERRUPT MASK VALUE.  081    asm {                       // Pop CCS restores saved IMask status  082     pula                       // .. >>>URGENT<<<: See page 183 of  083      tap                       // 01CPU08RM!ReferenceManual2013‐U.pdf  084   }                    // THIS WAS ***EXACTLY*** MY EXAMPLE IN THERE!  085                                // (Stupid guy)  086 }  /* WaitISR_on() */  087  088 // ==================================================================  089 interrupt VectorNumber_Vrtc void TIMERS8ISR ( void )           /*()*/  090 {  byte i;  091  092    for( i = 0; i < NTIMERS; i ++ ) {  093        if( timersMS[ i ] ) {  094            timersMS[ i ] ‐‐;  095        }  096    }  097    RTCSC = RTC_FLAGS;          // Use 1KHz internal clock/1: 1mS tick  098                                // ..>> Clear RTCIF <<// IntEnable RTC  099 }  /* TIMERS8ISR */  100  101 // ******************************************************************  102 // NOTE: The following code ***DOES NOT WORK*** (See above...); WHY ?  103 //     if( ‐‐timersMS[ i ]  <  0 ) {  104 //         timersMS[ i ] = 0;  105 //     }  106  107 // WHAT is 'Sassert'?  HOW it works?  Try using NTIMERS = 6    COMENTARIOS a ["Labs-C\Lab2\TimersJ\timersJ.c"]: Hecha el Miércoles 19Dic2013    Adelante se presenta el "timersJ_.h", pero sólo son los Prototipos de las 6 Funciones,  definición de 3 parámetros del RTC, y la Macro Sassert(e) (Static Assert. Assert es de  C. ¿SABÍA USTED? SI NO, TIENE QUE ESTUDIAR C)    La representación de cada timer es un WORD, tal como en ASM. Hay un arreglo (vector),  timersMS, compuesto por la cantidad NTIMERS (8 en esta presentación, pero usted puede  ajustarla en "timersJ_.h" según sus preferencias)    El  arreglo  es  Global  para  facilitar  su  visibilidad  en  los  programas  que  usan  esta  librería. Además, se ha declarado 'volatile' para evitar optimizaciones indeseadas en  estas variables.    010 volatile word timersMS[ NTIMERS ];    238 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La primera funcionalidad permite la inicialización de la biblioteca, IniTimers. Se usa  como IniTimers(8) (o la potencia de dos que usted defina), para mantener la SEMÁNTICA  de la función, pero ese #8 NO se emplea en la biblioteca: la validación se hace contra  la constante NTIMERS, definible por usted en "timersJ_.h"    Como  parte  de  la  inicialización  habría  que  colocar  en  CERO  las  variables  que  representan  los  timers;  esto  NO  se  hace  explícitamente  porque,  ¿SABÍA  USTED  que  el  LINKER  inicializa  en  CERO  TODAS  LAS  VARIABLES  "GLOBALES"  QUE  NO  TIENEN  ASIGNADO  UN  VALOR EXPLÍCITO? ¿NO? ¿SABE LO QUE ES LINKER? ¿NO? TIENE QUE ESTUDIAR C    013 void IniTimers ( byte ntimers )                                /*()*/    Asegúrese de entender cómo Sassert valida en COMPILE TIME (no hace NADA en RUNTIME) que  el número de timers por usted definido sea potencia de dos.    018    Sassert( ! ( NTIMERS & ( NTIMERS ‐ 1 ) ) );    La  única  operación  que  termina  haciéndose  explícitamente,  es  la  selección  de  "1KHz"  como  reloj  Interno  (clock/1,  lo  que  produce  interrupciones  cada  milisegundo.  Nuestra  librería  CUENTA  TICKS  de  UN  milisegundo.  Si  usted  llegara  a  necesitar  contar  tiempos  menores, el Reference Manual le indica cómo programar el RTCSC para lograrlo)    020    RTCSC = RTC_FLAGS;          // Use 1KHz internal clock/1: 1mS tick    La  siguiente  funcionalidad  es  la  de  activar  uno  de  los  timers:  SetimerMS.  Recibe  dos  parámetros: 'byte timer' (el número del timer, del 0 a 7 para esta implementación) y  'word  timeMS',  la  cantidad  de  MILISEGUNDOS  que  ese  temporizador  debe  contar  antes  de  declararse READY.    La  función  retorna  la  cantidad  de  MS  que  aún  faltaban  para  que  ese  timer  terminara  (guardada al comienzo en 'tmp'); esto puede servir de verificación pues si no es cero,  se está abortando un timer para volverlo a inicializar.    Finalmente, se inicializa la variable correspondiente al 'timer' con el valor deseado  en milisegundos: timeMS, tal como su contraparte en ASM.    025 word SetimerMS ( byte timer, word timeMS )                     /*()*/  026 {  word tmp = timersMS[ timer & ( NTIMERS ‐ 1 ) ]; // Reminding time  028    timersMS[ timer & ( NTIMERS ‐ 1 ) ] = timeMS;  // ticks (mSeconds)  029    return tmp;    La funcionalidad que se usa para averiguar si expiró (llegó a Cero) o no, alguno de los  timers  es  Timeout,  que  recibe  como  parámetro  un  'byte  timer'  (de  0  a  7  en  esta  implementación) y devuelve sí o no, si hubo Timeout (retorna 0 si NO hay timeout; 1 si  sí expiró ese timer)    034 byte Timeout ( byte timer )                                    /*()*/  036    if( timersMS[ timer & ( NTIMERS ‐ 1 ) ] ) {  037        return 0;  038    }  039    return 1;                   // else...  239 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Esperar bloqueando el flujo del programa, hasta que termine un cierto temporizador, que  fue  PREVIAMENTE  inicializado  por  SetimerMS,  es  la  función  de  WTimer,  que  recibe  como  parámetro  el  ID  del  timer  (byte  timer),  y  no  retorna  ningún  valor;  solo  bloquea  el  programa.    044 void WTimer ( byte timer )     /* >>>WARNING<<<: BLOCKS CPU */ /*()*/  046    while( ! Timeout( timer ) ) {  047        /* Empty While */  048    }    Esta  es  una  de  las  oportunidades  en  las  que  es  preferible  expresar  la  condición  de  manera afirmativa: 046    Wait( Timeout( timer ) );    Lo cambiaré en alguna otra oportunidad.    Para aquellas ocasiones que requieran programar un timer y esperar hasta que termine,  todo en una sola operación, está WaitMS_on, que recibe dos parámetros, como SetimerMS,  y no regresa ningún valor, como WTimer.    053 void WaitMS_on ( byte timer, word timeMS )                     /*()*/  056    SetimerMS( timer, timeMS );  057    WTimer( timer );    Para  realizar  una  espera  estando  en  INTERRUPT  STATE  (es  decir,  ha  ocurrido  una  interrupción  y  se  está  ejecutando  la  ISR del  periférico  en  cuestión,  y  ésta  requiere  hacer  una  espera,  se  emplea  WaitISR_on,  equivalente  de  WaitMS_on,  para  ejecutarse  en  ISRs).  WaitISR_on  recibe  2  parámetros,  igual  que  WaitMS_on:  byte  timer  (número  del  temporizador que ha de usarse, del 0 al 7) y el número de Milisegundos, word timeMS.    062 void WaitISR_on ( byte timer, word timeMS )                    /*()*/    Esta función salvaguarda el CCR en el Stack, y como el C no puede directamente realizar  esta operación, se la implementa vía Inline ASM:    El objeto de guardar el valor del CCR es para preservar el Interrupt State del CPU (I,  Interrupt  Mask),  pues  SetimerMS  no  se  puede  ejecutar  si  las  interrupciones  están  activas (CLI), así que esta función las deshabilita, ejecuta el SetimerMS y... como ya  explicamos  en  la  sección  de  ASM,  NO  PUEDE  SIMPLEMENTE  HABILITAR  LAS  INTERRUPCIONES.  Tiene  que  dejar  el  Interrupt  State  TAL  COMO  ESTABA  ANTES  de  DESHABILITAR  las  interrupciones. Por eso, en vez de CLI, se recupera desde el Stack el valor previo de  I, que estaba en el CCR.    066    // SAVE ACTUAL INTERRUPT MASK VALUE  067    asm {  068     tpa                        // Push CCR saves IMask status  069      psha                      // ..  070   }  073    // C Code  074    CpuIntDsb;                  // DISABLE INTERRUPTS (IMask=1): Let  075    SetimerMS( timer, timeMS ); // ..Setimer used inside Int. Routines  076    CpuIntEn;                   // REENABLE INTERRUPTS: Let RTC Count  077    WTimer( timer );  240 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   080    // RESTORE SAVED INTERRUPT MASK VALUE.  081    asm {                       // Pop CCS restores saved IMask status  082     pula                       // .. >>>URGENT<<<: See page 183 of  083      tap                       // 01CPU08RM!ReferenceManual2013‐U.pdf  084   }                    // THIS WAS ***EXACTLY*** MY EXAMPLE IN THERE!    Desde luego, se emplean las instrucciones TPA y TAP que el Reference Manual indica  QUE NO SIRVEN PARA NADA!    La  rutina  de  Interrupciones,  TIMERS8ISR,  ni  recibe  parámetros  ni  devuelve  valores  (void)  como  ya  se  explicó.  Es  de  tipo  'interrupt'  e  indica  cuál  es  el  Vector  de  interrupciones, VectorNumber_Vrtc, para que el Loader coloque la dirección de la ISR en  la posición apropiada, en el Vector de Interrupciones.    ¿SABE USTED LO QUE ES EL LOADER? NO? TIENE QUE ESTUDIAR C    El código es muy sencillo: incrementar cada temporizador QUE NO HAYA EXPIRADO (lo cual  se determina en el 'if' porque su valor ES diferente de CERO), y seguir el protocolo de  Acknowledge de Interrupciones para el periférico:    089 interrupt VectorNumber_Vrtc void TIMERS8ISR ( void )           /*()*/  090 {  byte i;  092    for( i = 0; i < NTIMERS; i ++ ) {  093        if( timersMS[ i ] ) {  094            timersMS[ i ] ‐‐;  095        }  096    }  097    RTCSC = RTC_FLAGS;          // Use 1KHz internal clock/1: 1mS tick  098                                // ..>> Clear RTCIF <<// IntEnable RTC    Se asigna RTCSC=RTC_FLAGS, como la rutina de inicialización, lo que Borra la Interrupt  Flag, IF, del RTC, rearmando (permitiendo otras posteriores) sus interrupciones.    También hubiéramos podido borrar exclusivamente ese bit RTCIF, pero el resultado es el  mismo.    OBSERVACIÓN: ¿Por qué reescribiendo el “IF” como sigue, el código NO TRABAJA?    Código ORIGINAL:    093        if( timersMS[ i ] ) {  094            timersMS[ i ] ‐‐;    Cambiado a:    103 //     if( ‐‐timersMS[ i ]  <  0 ) {  104 //         timersMS[ i ] = 0;  105 //     }    ¿NO SABE POR QUÉ NO TRABAJA? TIENE QUE ESTUDIAR C URGENTEMENTE!    241 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Lab2\TimersJ\timersJ_.h"]: ["Labs‐C\Lab2\TimersJ\timersJ_.h"]  01 #ifndef TIMERSJ_H_INCLUDED  02 #define TIMERSJ_H_INCLUDED  03 // ******************************************************************  04 // timersJ_.h, Luis G. Uribe C.  C11D2013  05 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  06 #define NTIMERS    8           // 2's Power: 2, 4, 8, 16... 128 (max)  07  08 // ******************************************************************  09 // Includes Files  10 // <<< INCREDIBLE: CW10.3 **NEEDS** to REInclude this files **AGAIN**  11  12 #include <hidef.h>             // For EnableInterrupts macro  13 #include "derivative.h"        // Include peripheral declarations  14  15 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 // Function Prototypes  17  18 void IniTimers( byte ntimers ); // Used NTIMERS, not ntimers here  19 word SetimerMS( byte timer, word timeMS );  20 byte Timeout(   byte timer );  21 void WTimer(    byte timer );  22 void WaitMS_on( byte timer, word timeMS );  23 void WaitISR_on(byte timer, word timeMS );  24  25 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  26 // Parameter Definitions  27  28 #define  RTCPS_BY_ONE  8U      // Divided by 1, gives 1mS Tick  29 #define  ONE_KHZ       0       // Uses internal 1Khz clock  30  31 #define  RTC_FLAGS ( RTCSC_RTIF_MASK | ONE_KHZ | RTCSC_RTIE_MASK |  \  32                      RTCPS_BY_ONE )  33 // RTCSC_RTCPS0_MASK     1U    <<< 2, ONE_KHZ (bits 2,1,0)  34 // RTCSC_RTCPS3_MASK     8U    ...  35    // RTCPS_BY_ONE       8U    <<< 4, Divide by 1; gives 1mS Tick  36 // RTCSC_RTIE_MASK      16U    <<< 3  37 // RTCSC_RTIF_MASK     128U    <<< 1  38  39 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  40 // Define Sassert, in case you need a Static assert  41 #define Sassert(e) do { enum { assert_static__ = 1/(e) }; } while (0)  42  43 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  44 // Several Defines  45 #undef  Wait  46 #define Wait(e)   while( !(e))  47 #undef  CpuIntEn  48 #define CpuIntEn  __asm CLI  49 #undef  CpuIntDsb  242 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 50 #define CpuIntDsb __asm SEI  51 #undef  EOF  52 #define EOF ((word)‐1)  53  54 #endif // TIMERSJ_H_INCLUDED    No hay mucho que comentar. Usted tiene que poder decir qué hace y cómo, el Sassert.    53) EJEMPLO #1 USANDO LIBRERÍA DE TIMERS Obsérvese  cómo  está  de  OCULTA  la  librería  de  Timers:  No  figura  ni  la  definición  de  funciones,  ni  las  variables  que  se  usan,  ni  siquiera  aparece  aquí  la  ISR  del  RTC.  OCULTAR  (HIDE)  la  información  y  hacer  que  el  acceso  a  ella  sea  ESTRUCTURADO,  es  el  objetivo del OOPS (Object Oriented Programming System)    Este  ejercicio  enciende  los  8  leds  a  diferentes  TIEMPOS,  que  han  sido  definidos  mediante  la  tabla  "const  word  Delays[]".  Una  activación  del  botón  de  IRQ  detiene  el  programa; la siguiente activación hace que el programa continúe. Para experimentar, se  usó TAMBIÉN la librería de Timers en la ISR del IRQ, a fin de separar las pulsaciones  del botón, al menos 1 segundo (1000 MS)    ["Labs‐C\Lab2\TimersJ\100TimedFlash8.c"]  01 // ******************************************************************  02 // Program 100TimedFlash8.c: Luis G. Uribe C. D24N2013  C11D2013  03 // 8 LEDs flashing at different rates. IRQ toggle's program OFF/ON  04 //  05 // ******************************************************************  06 // Include Files  07 #include <hidef.h>             // For EnableInterrupts macro  08 #include "derivative.h"        // Include peripheral declarations  09  10 #include "timersJ_.h"          // Defines cli, sti  11  12 const    word Delays[]= {150, 355, 511, 673, 1002, 1398, 2755, 3000};  13 //const  word Delays[]= {673, 150, 2755, 1398, 511, 1002, 3000, 355};  14  15 byte Toggle = 1;               // Begin in RUN state (1)  16  17 // ******************************************************************  18 void main ( void )                                             /*()*/  19 {  byte i, LEDsMask;  20  21    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  22    // >>> ALWAYS include the following 2 lines  23    // Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  24  25    #define COP_Disable 0x42  26    SOPT1 = COP_Disable;        // System Options 1  27  28    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  29    // Enable Interrupts for IRQ  30  243 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 31    IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;  32    IRQSC_IRQIE = 1;          // BSET IRQSC_IRQIE,IRQSC: IRQ pin IntEn  34    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  35    // Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7) for output  36    PTCDD = 0xFF;               // ..(they drive LEDs on DEMOQE128)  37    PTEDD = 0xFF;  38  39    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  40    // Enable Timers  41    IniTimers( 8 );  42  43    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  44    // Start 8 timers  45    for( i = 0; i < 8; i ++ ) {  46        SetimerMS( i, Delays[ i ] >> 1 );  47    }  48  49    // ===============================================================  50    CpuIntEn;                   // CPU Interrupt ENABLE. DON'T FORGET!  51  52    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  53    // Loop for ever... IRQ toggles RUN/~STOP  54  55    while( 1 ) {  56        Wait( Toggle );         // IRQ: RUN/~STOP  57        for( LEDsMask = 0, i = 0; i < 8; i ++ ) {  58            if( Timeout( i ) ) {  59                LEDsMask |= 1 << i;  60                SetimerMS( i, Delays[ i ] >> 1 );  61            }  62        }  63        PTED = ( PTCD ^= LEDsMask );  64    }  65 }  66  67 // ==================================================================  68 // IRQ INTERRUPT SERVICE ROUTINE  69 // NOTE: Es necesario hacer UN ACKNOWLEDGE para que la interrupción  70 //       vuelva a suceder (Interrupt Handshaking, different for each  71 //       peripheral! Need to read Manual for each equipment... :‐(  72  73 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  74 {  75    IRQSC_IRQIE = 0;            // Auto disable IRQ Interrupt  76    Toggle ^= (byte)1;  77    WaitISR_on( 7, 1000 );  78  79    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK, IRQSC: ACK IRQ  80                                // ..Interrupt (Re‐Arm IRQ Interrupts)  81    IRQSC_IRQIE  = 1;           // BSET IRQSC_IRQIE,IRQSC: ReEnable  82                                // ..IRQ pin IntEn  83 }  244 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   COMENTARIOS a ["Labs-C\Lab2\TimersJ\100TimedFlash8.c"] Todo comienza como SIEMPRE.    Luego  viene  la  tabla  de  Delays,  acompañada  de  una  Alternativa,  para  que  usted  experimente.  Ambas  tablas  han  sido  escogidos  empleando  una  función  random  (en  Perl)  para números entre 150 y 3000:    12 const    word Delays[]= {150, 355, 511, 673, 1002, 1398, 2755, 3000};  13 //const  word Delays[]= {673, 150, 2755, 1398, 511, 1002, 3000, 355};    La variable GLOBAL Toggle, que comienza en 1 (RUN state), será NEGADA (vía Exor) por la  ISR del IRQ, cada vez que se oprima el botón de IRQ.    15 byte Toggle = 1;               // Begin in RUN state (1)    Dos variables locales:    18 void main ( void )                                             /*()*/  19 {  byte i, LEDsMask;    Las 2 instrucciones que SIEMPRE hay que incluir:    25    #define COP_Disable 0x42  26    SOPT1 = COP_Disable;        // System Options 1    Habilitación de Interrupciones para el IRQ:    31    IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;  32    IRQSC_IRQIE = 1;          // BSET IRQSC_IRQIE,IRQSC: IRQ pin IntEn    Inicialización  de  los  LEDs.  Aquí  hemos  omitido  el  procedimiento  RECOMENDADO,  que  ya  ilustramos, y que indica colocar en un estado inicial adecuado el valor de las salidas,  ANTES  de  programar  los  correspondientes  puertos  como  Salida,  ya  que  en  este  caso  NO  IMPORTA.    36    PTCDD = 0xFF;               // ..(they drive LEDs on DEMOQE128)  37    PTEDD = 0xFF;    Inicializamos los Timers:  41    IniTimers( 8 );    Arrancamos inicialmente todos los 8 timers leyendo de la tabla Delays:    45    for( i = 0; i < 8; i ++ ) {  46        SetimerMS( i, Delays[ i ] >> 1 );  47    }    Habilitamos las interrupciones del CPU:    50    CpuIntEn;                   // CPU Interrupt ENABLE. DON'T FORGET!  245 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 El  ciclo  infinito  para  este  programa.  Primero  se  bloquea  el  programa  si  la  variable  Toggle indica STOP (comienza en RUN State), así que si se oprime el botón de IRQ, se  pausa o se continúa el programa:    55    while( 1 ) {  56        Wait( Toggle );         // IRQ: RUN/~STOP    Si corresponde ejecutar el ciclo infinito, se inicializa la variable auxiliar LEDsMask  en 0, y luego se van recorriendo todos los timers, buscando aquellos que ya Expiraron  (Timeout), y se los señala con un 1 en LEDsMask, en la posición correspondiente a su  ID,  de  0  a  7;  además,  como  ya  les  expiró  el  tiempo  se  los  vuelve  a  activar  con  un  SetimerMS:    57        for( LEDsMask = 0, i = 0; i < 8; i ++ ) {  58            if( Timeout( i ) ) {  59                LEDsMask |= 1 << i;  60                SetimerMS( i, Delays[ i ] >> 1 );  61            }  62        }    Al finalizar el análisis de todos los timers, se cambia (NIEGA) el estado de los LEDs  correspondientes  a  aquellos  que  ya  expiraron  (usando  un  Exor:  PTCD  ^=  LEDsMask).  El  resultado de ese nuevo valor se lo replica en PTED:    63        PTED = ( PTCD ^= LEDsMask );    La IRQ INTERRUPT SERVICE ROUTINE:    Al comienzo se auto deshabilita para interrupciones, a fin de que las pulsaciones del  botón IRQ no tengan repercusiones mientras no se termine la IRQISR:    73 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  74 {  75    IRQSC_IRQIE = 0;            // Auto disable IRQ Interrupt    Luego se invierte el estado de la variable Toggle empleando un Exor:    76    Toggle ^= (byte)1;    Se  habilita  la  ESPERA  DENTRO  DE  INTERRUPCIONES  usando  el  WaitISR_on  para  un  Segundo,  empleando el Timer #7:    77    WaitISR_on( 7, 1000 );    IMPORTANTÍSIMO:    NO PUEDE USARSE EL MISMO TIMER (7 en el ejemplo) EN NINGUNA OTRA PARTE. Basta con que  sean distintos para que no haya conflictos    Terminada la espera, que bloquea cualquier entrada del usuario durante un segundo, se  procede  al  Protocolo  de  Acknowledge  de  interrupciones  para  este  periférico  IRQ  en  246 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 particular,  que  es  diferente  de  los  demás  y  que,  como  ya  dijimos  tantas  veces,  hace  necesario estudiar cada periférico por separado:    79    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK, IRQSC: ACK IRQ  80                                // ..Interrupt (Re‐Arm IRQ Interrupts)    Se  vuelve  a  rehabilitar  las  propias  interrupciones,  deshabilitadas  al  comienzo  de  la  ISR, y se termina:    81    IRQSC_IRQIE  = 1;           // BSET IRQSC_IRQIE,IRQSC: ReEnable  82                                // ..IRQ pin IntEn    54) EJEMPLO #2 USANDO LIBRERÍA DE TIMERS Este  ejemplo  es  una  extensión  del  anterior:  Hay  4  tablas  con  valores  seleccionados  también al azar, y se usa el botón de IRQ para ir cambiando de tabla; se comienza por  la tabla #0 (la primera) y la IRQISR hace pasar el índice con que se lee la tabla, por  los números 0, 1, 2, 3, 0, 1, 2 ..:    ["Labs‐C\Lab2\TimersJ\110TimedFlash8X4.c"]:  01 // ******************************************************************  02 // Program TimedFlash8X4.c: Luis G. Uribe C. L25N2013  C11D2013  03 // 8 LEDs flashing at different rates. IRQ steps through Delay Tables  04  05 // ******************************************************************  06 // Include Files  07 #include <hidef.h>             // For EnableInterrupts macro  08 #include "derivative.h"        // Include peripheral declarations  09  10 #include "timersJ_.h"          // Defines cli, sti  11  12 const word  Delays[4][8] =  13        {   {  673,  150, 2755, 1398,  511, 1002, 3000,  355},  14            {  355,  511, 2755, 1398, 1002,  673,  150, 3000},  15            { 1002, 3000, 1398,  511,  673,  355, 2755,  150},  16            {  355, 1002, 2755, 1398,  150,  511, 3000,  673},  17        };  18 byte   Index = 0;  19  20 // ******************************************************************  21 void main ( void )                                             /*()*/  22 {  byte i, LEDsMask;           // Begin in RUN state (1)  23  24    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  25    // >>> ALWAYS include the following 2 lines  26    // Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  27  28    #define COP_Disable 0x42  29    SOPT1 = COP_Disable;        // System Options 1  30  31    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32    // Enable Interrupts for IRQ  247 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 34    IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;  35    IRQSC_IRQIE = 1;        // BSET IRQSC_IRQIE,IRQSC: IRQ pin IntEn  36  37    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  38    // Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7) for output  39    PTCDD = 0xFF;               // ..(they drive LEDs on DEMOQE128)  40    PTEDD = 0xFF;  41  42    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  43    // Enable Timers  44    IniTimers( 8 );  45  46    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  47    // Start 8 timers  48    for( i = 0; i < 8; i ++ ) {  49        SetimerMS( i, Delays[ Index ][ i ] >> 1 );  50    }  52    // ===============================================================  53    CpuIntEn;                   // CPU Interrupt ENABLE. DON'T FORGET!  54  55    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  56    // Loop for ever.. Pressing IRQ steps through Delay Tables (Index)  57  58    while( 1 ) {  59        for( LEDsMask = 0, i = 0; i < 8; i ++ ) {  60            if( Timeout( i ) ) {  61                LEDsMask |= 1 << i;  62                SetimerMS( i, Delays[ Index ][ i ] >> 1 );  63            }  64        }  65        PTED = ( PTCD ^= LEDsMask );  66    }  67 }  68  69 // ==================================================================  70 // IRQ INTERRUPT SERVICE ROUTINE  71 // NOTE: It is neccesary to ACKNOWLEDGE the Interrupt, in order to  72 //       re‐Arm it. 'Interrupt Handshaking'... is different for each  73 //       peripheral! so you will need to... read the Manual & program  74 //       a different routine... for EACH peripheral device.  <;~(  75  76 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  77 {  78    IRQSC_IRQIE = 0;            // AUTO Disable IRQ Interrupt  79    Index = ( Index + 1 ) & 0x03;   // 0, 1, 2, 3, 0, 1, 2 ..  80    WaitISR_on( 7, 1000 );  81  82    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK, IRQSC: ACK IRQ  83                                // ..Interrupt (Rearm IRQ Interrupts)  84    IRQSC_IRQIE  = 1;           // BSET IRQSC_IRQIE,IRQSC: ReEnable  85                                // ..IRQ pin IntEn  86 }  248 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   COMENTARIOS a ["Labs-C\Lab2\TimersJ\110TimedFlash8X4.c"]: Matriz de 4 vectores, c/u de 8 valores:    12 const word  Delays[4][8] =  13        {   {  673,  150, 2755, 1398,  511, 1002, 3000,  355},  14            {  355,  511, 2755, 1398, 1002,  673,  150, 3000},  15            { 1002, 3000, 1398,  511,  673,  355, 2755,  150},  16            {  355, 1002, 2755, 1398,  150,  511, 3000,  673},  17        };    Índice para seleccionar entre los 4 vectores, del 0 al 3:    18 byte   Index = 0;  21 void main ( void )                                             /*()*/  22 {  byte i, LEDsMask;           // Begin in RUN state (1)  23    La misma habilitación de IRQ para interrumpir, y la inicialización de los LEDs:    32    // Enable Interrupts for IRQ  38    // Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7) for output    La inicialización de siempre para los Timers y habilitación de interrupciones del CPU:    44    IniTimers( 8 );  48    for( i = 0; i < 8; i ++ ) {  49        SetimerMS( i, Delays[ Index ][ i ] >> 1 );  50    }  53    CpuIntEn;                   // CPU Interrupt ENABLE. DON'T FORGET!    Ciclo infinito, como en el ejercicio anterior, pero ahora el IRQ NO detiene y rearranca  el código, sino que cambia el 'Index' entre 0 y 3:    58    while( 1 ) {  59        for( LEDsMask = 0, i = 0; i < 8; i ++ ) {  60            if( Timeout( i ) ) {  61                LEDsMask |= 1 << i;  62                SetimerMS( i, Delays[ Index ][ i ] >> 1 );  63            }  64        }  65        PTED = ( PTCD ^= LEDsMask );    Rutina  de  interrupciones  para  el  botón  de  IRQ;  todas  las  líneas  de  programa  son  idénticas al ejercicio anterior, excepto por el ciclado de Index:    79    Index = ( Index + 1 ) & 0x03;   // 0, 1, 2, 3, 0, 1, 2 ..    ¿ENTIENDE CÓMO ES QUE INDEX CICLA ENTRE 0 Y 3? SI NO, TIENE QUE ESTUDIAR C.  249 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08  SERIAL COMMUNICATIONS & DATA QUES IN C C omo en el caso de los ejercicios en Lenguaje ensamblador, la parte culminante  por  el  momento,  de  los  programas  en  C,  son  las  rutinas  de  Comunicaciones  Seriales y sus compañeros, los procesadores de Colas de Datos. Les presento mi  segunda  librería  en  C,  el  manejador  de  Comunicaciones  Seriales  y,  a  continuación,  mi  librería  para  el  manejo  de  Colas  de  Datos,  auxiliar  imprescindible  para el manejo de Comunicaciones con flexibilidad.    Refiérase a los ejercicios que están en la carpeta: Carpetas Labs‐C\ Lab3\SciComm.    55) LIBRERÍA DE COMUNICACIONES SERIALES Primero analicemos SciComm.h, que define la biblioteca de Comunicaciones Seriales:    ["Labs‐C\Lab3\SciComm\SciComm.h"]  001 #ifndef SCICOMM_H_INCLUDED  002 #define SCICOMM_H_INCLUDED  003  004 // ******************************************************************  005 // SciComm.h: SCI Comm. Support  006 // Luis G. Uribe C., D11M2007 L12M07 J16A09 L08J09  007 // ..S16J2012 (HCS08) D24J2012 M26F2013  J28N2013  008 // S30N2013: Migrated back to 'C'  S21D2013 cosmetics: XmtRcvActivate  009  010 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  011 //  ** REFER to 02MC9S08QE128RM(ReferenceManual)‐U.pdf **  012 #undef  Wait  013 #define Wait(e)   while( !(e))  014 #undef  CpuIntEn  015 #define CpuIntEn  __asm CLI  016 #undef  CpuIntDsb  017 #define CpuIntDsb __asm SEI  018 #undef  EOF  019 #define EOF ((word)‐1)  020  021 // ==================================================================  022 // THE FOLLOWING MAIN **FUNCTIONALITIES** ARE DEFINED IN THIS LIBRARY  023 // >>MAIN<<: getchar(), putchar(), RcvIntEn, RcvIntDsb, XmtIntEn,  024 // XmtIntDsb, XmtRcvActivate  025 //  026 // >>ANCILLARY<<: Bauds9600Value, CommIntEn, RcvChar, RcvRdy,  027 // XmtChar, XmtRdy  028  029 // ==================================================================  030 // ***REDEFINE*** MC9S08QE128 REGISTERS  AND  REQUIRED BITS  031 // ..for SCI Serial Communications Interface  032 // >>> ALIAS <<< are other names for same addresses.    BE CAREFUL!  033  034 // ******************************************************************  035 // SCI1BD: EQU $0020               ; Baude Rate register  036 // LOOPMODE!!!  250 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 037  038 #define Bauds9600Value 0x001A     // 16BITS: 4e6/(16*9600)=26.04=0x1A  039  040 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  041 // SCI1D: EQU $27  042  043 #define XMTBUF SCI1D               // Transmiter Buffer  044 #define RCVBUF SCI1D               // Receiver   Buffer  045  046 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  047 // SCI1C2:  EQU $23  048  049 #define XMTCTRLREG SCI1C2  050 #define RCVCTRLREG SCI1C2  051  052 #define XMTEN      SCI1C2_TE_MASK  // TE:  Transmiter Enabl %00001000  053 #define XMTIEN     SCI1C2_TIE_MASK // TIE: Xmt INT    Enabl %10000000  054 #define XMTIEN_bit SCI1C2_TIE      // 7  055  056 #define RCVEN      SCI1C2_RE_MASK  // RE:  Receiver Enable  %00000100  057 #define RCVIEN     SCI1C2_RIE_MASK // RIE: Rcv INT  Enable  %00100000  058 #define RCVIEN_bit SCI1C2_RIE      // 5  059  060 #define XmtRcvEnab (XMTEN | RCVEN) // Enable BOTH devices %00001100  061  062 // ==================================================================  063 // SCI1S1: EQU $24  064  065 #define XMTSTATREG SCI1S1  066 #define RCVSTATREG SCI1S1  067  068 // Relevant bits:  069  070 #define XMTRDY     SCI1S1_TDRE_MASK // Transmiter Data Register Empty  071 #define XMTRDY_bit SCI1S1_TDRE     // 7 (mSCI1S1_TDRE: %10000000)  072  073 #define RCVRDY     SCI1S1_RDRF_MASK // Receive   Data Register Full  074 #define RCVRDY_bit SCI1S1_RDRF     // 5 (mSCI1S1_RDRF: %00100000)  075  076 #define XMTEMPTY   SCI1S1_TC_MASK  // Transmission Complete Flag  077 #define XMTEMPTY_bit SCI1S1_TC     // 6 (mSCI1S1_TC: %01000000): LAST  078  079 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  080 // Receiver:  081  082 #define OVERRUNERR SCI1S1_OR       // OR: Overrun           %00001000  083 #define NOISERR    SCI1S1_NF       // NF: Noise Flag        %00000100  084 #define FRAMERR    SCI1S1_FE       // FE: Framing Error     %00000010  085 #define PARERR     SCI1S1_PF       // PE: Parity  Error     %00000001  086  087 #define RCVErrs    (OVERRUNERR | NOISERR | FRAMERR | PARERR)  088  251 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 089 // ******************************************************************  090 // MACRO DEFINITIONS  091  092 #define SCI9600N8   (SCI1BD = Bauds9600Value)  // Prog. SCI @9600,8,N  093  094 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  095 #define XmtRcvActivate (SCI1C2 = XmtRcvEnab); /*ACTIVATE Xmt & Rcv*/\  096                    SCI9600N8   /* Serial Communications Interface*/ \  097                                /* .. 9600 bps, No parity, 8 bits */  098  099 // ==================================================================  100 #define RcvRdy RCVRDY_bit  101  102 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  103 #define RcvChar    RCVBUF  104  105 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  106 // getchar() MAY Rearm Rcv Interrupts if inside ISR  107 byte getchar() {  108    Wait( RcvRdy );  109    return RcvChar;  110 }  111  112 // ==================================================================  113 #define XmtRdy XMTRDY_bit  114  115 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  116 #define XmtChar(c) (XMTBUF = (byte)(c))  117  118 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  119 #define putchar(c) _putchar((byte)(c))  120 // putchar(c) MAY rearm Xmt interrupts if inside ISR  121 byte _putchar( byte c )  122 {  123    Wait( XmtRdy );  124    XmtChar(c);  125    return c;  126 }  127  128 // ==================================================================  129 #define XmtIntEn   (XMTIEN_bit = 1)  130 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  131 #define XmtIntDsb  (XMTIEN_bit = 0)  132 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  133 #define RcvIntEn   (RCVIEN_bit = 1)  134 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  135 #define RcvIntDsb  (RCVIEN_bit = 0)  136  137 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  138 // Interrupt Enable for Communications (Remember: Output devices are  139 // ..enabled  **ONLY**  when they are ready for transmitting  140 // Reading RCVSTATREG and RCVBUF, clear any possibly RCV Ready Flag  252 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 141 // ..pending at the moment of interrupt enabling the device  142  143 #define CommIntEn { RCVSTATREG; RCVBUF; RcvIntEn; }  144  145 // ==================================================================  146 // Anciliary Stuff  147  148 #define I   // NUL definition. From this point on, 'I' means NOTHING!  149 #define Forever    for(;;){  150 #define Endforever }  151  152 #endif // SCICOMM_H_INCLUDED    COMENTARIOS a ["Labs-C\Lab3\SciComm\SciComm.h"]: 001 #ifndef SCICOMM_H_INCLUDED  002 #define SCICOMM_H_INCLUDED    Las primeras dos líneas son muy comunes en todos los include files de C; su objetivo es  que  el  código  del  archivo  NO  se  agregue  múltiples  veces,  aun  cuando  el  programador,  conciente o inconcientemente, lo haya incluido más de una vez. Si el símbolo, en este  caso: SCICOMM_H_INCLUDED, que normalmente se extrae del mismo nombre del include file,  no existe, entonces se lo DEFINE y se incluye el texto; si YA está definido, se salta  toda la inclusión de código (hasta el #endif // SCICOMM_H_INCLUDED final)    Refiérase al manual: 02MC9S08QE128RM(ReferenceManual)‐U.pdf    Wait, CpuIntEn, CpuIntDsb, y EOF ya los vimos con anterioridad.    Las funcionalidades PRINCIPALES definidas aquí son:    XmtRcvActivate: Energiza y activa los dos periféricos: XMT Y RCV  getchar(): Lee o recibe un símbolo del puerto de comunicaciones  putchar(): Envía        un símbolo por el puerto de comunicaciones  RcvIntEn:  Habilita    las interrupciones del Receptor  RcvIntDsb: Deshabilita las interrupciones del Receptor  XmtIntEn:  Habilita    las interrupciones del Transmisor  XmtIntDsb: Deshabilita las interrupciones del Transmisor    Otras funcionalidades auxiliares son:    Bauds9600Value: Programa el puerto SCI para trabajar a: 9600,8N1: 9600 bps,8 bits por  símbolo, No parity, 1 stop bit    Como  de  costumbre,  se  redefinen  los  símbolos  definidos  por  el  fabricante  para  cambiarlos por otros que tengan mayor significado, a fin de mejorar la inteligibilidad;  son ALIAS o asignaciones numéricas:    035 // SCI1BD: EQU $0020               ; Baude Rate register  038 #define Bauds9600Value 0x001A     // 16BITS: 4e6/(16*9600)=26.04=0x1A    253 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Se  observa  aquí  lo  mismo  que  ya  incluimos  en  la  contraparte  en  ASM:  que  tanto  el  Registro  de  Transmisión  (XMTBUF)  como  el  de  Recepción  (RCVBUF),  siendo  DOS  entidades  DIFERENTES,  tienen  la  MISMA  dirección  (0x27).  La  discriminación  entre  uno  y  otro  se  logra por el SENTIDO en que fluyen los datos. Si hay una asignación SOBRE el registro  XMTBUF, es una transmisión; si la asignación es DESDE el registro, se lee el RCVBUF    041 // SCI1D: EQU $27  043 #define XMTBUF SCI1D               // Transmiter Buffer  044 #define RCVBUF SCI1D               // Receiver   Buffer    Hay un solo Registro de Control de interés en esta librería: SCI1C2 (SCI 1, Control‐2);  de  allí,  algunos  bits  son  para  XMT  y  otros  para  RCV.  Otros  fabricantes  diferencian  mucho  más  abruptamente  los  dos  periféricos,  el  de  transmisión  y  el  de  recepción,  e  incluyen  DOS  registros  de  Control;  por  eso  yo  he  incluido  DOS  nombres,  XMTCTRLREG  y  RCVCTRLREG, pero son ALIAS del único registro físico:    047 // SCI1C2:  EQU $23  049 #define XMTCTRLREG SCI1C2  050 #define RCVCTRLREG SCI1C2    Los bits de Control que nos interesan, tanto para XMT como para RCV, son:    052 #define XMTEN      SCI1C2_TE_MASK  // TE:  Transmiter Enabl %00001000  053 #define XMTIEN     SCI1C2_TIE_MASK // TIE: Xmt INT    Enabl %10000000  054 #define XMTIEN_bit SCI1C2_TIE      // 7  055  056 #define RCVEN      SCI1C2_RE_MASK  // RE:  Receiver Enable  %00000100  057 #define RCVIEN     SCI1C2_RIE_MASK // RIE: Rcv INT  Enable  %00100000  058 #define RCVIEN_bit SCI1C2_RIE      // 5    Hay  que  recordar  que  existe  la  Habilitación  (ENABLE:  XMTEN,  RCVEN),  que  equivale  a  energizar el periférico, y que es diferente del IEN: Interrupt ENable: XMTIEN, RCVIEN.    Nuestra  librería  siempre  Habilita  los  dos  periféricos  simultáneamente,  por  lo  que  tenemos un símbolo para eso: XmtRcvEnab    060 #define XmtRcvEnab (XMTEN | RCVEN) // Enable BOTH devices %00001100    Con los elementos de Status ocurre lo mismo que con los de Control, que se encuentran  reunidos tanto los de XMT como los de RCV, en el registro de Status1: SCI1S1    063 // SCI1S1: EQU $24  065 #define XMTSTATREG SCI1S1  066 #define RCVSTATREG SCI1S1    Los bits que nos interesan son:    070 #define XMTRDY     SCI1S1_TDRE_MASK // Transmiter Data Register Empty  071 #define XMTRDY_bit SCI1S1_TDRE     // 7 (mSCI1S1_TDRE: %10000000)    073 #define RCVRDY     SCI1S1_RDRF_MASK // Receive   Data Register Full  074 #define RCVRDY_bit SCI1S1_RDRF     // 5 (mSCI1S1_RDRF: %00100000)  254 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   076 #define XMTEMPTY   SCI1S1_TC_MASK  // Transmission Complete Flag  077 #define XMTEMPTY_bit SCI1S1_TC     // 6 (mSCI1S1_TC: %01000000): LAST    Los ERRORES, como vimos al analizar las rutinas de Comunicaciones en ASM, sólo podemos  detectarlos en el RECEPTOR:    082 #define OVERRUNERR SCI1S1_OR       // OR: Overrun           %00001000  083 #define NOISERR    SCI1S1_NF       // NF: Noise Flag        %00000100  084 #define FRAMERR    SCI1S1_FE       // FE: Framing Error     %00000010  085 #define PARERR     SCI1S1_PF       // PE: Parity  Error     %00000001    087 #define RCVErrs    (OVERRUNERR | NOISERR | FRAMERR | PARERR)    La definición es exactamente la misma que en ASM, pues es el MISMO periférico:    OVERRUN  ocurre  cuando  el  serializador  de  entrada  completa  de  leer  los  10  bits  del  símbolo que llega, y le corresponde ahora trasladarlo al BUFFER de recepción, pero se  encuentra con que el programa aún NO ha leído el carácter anterior; así que de todas  maneras  lo  almacena  en  el  registro  de  recepción,  sobrescribiendo  la  información  que  allí se encontraba, y señala mediante esta bandera, que se está perdiendo información  de entrada, por sobreescritura del RCVBUF. El programador tiene que tomar nota de esto,  probablemente  solicitando  la  retransmisión  de  la  información  al  otro  extremo  de  la  línea.  Normalmente  hay  que  calibrar  la  cantidad  de  información  y  la  velocidad  de  entrada,  para  evitar  que  esto  suceda.  Manejar  la  entrada  por  interrupciones,  y  hacer  uso de FIFOs o COLAS es lo más indicado.    NOISERR ocurre cuando la línea tiene ruido, que se refleja por variaciones en la señal  que se está leyendo, y que ocurren entre el comienzo y el final de los bits. Se supone  que la línea no cambia de valor en ese intervalo.    FRAMERR se presenta cuando, habiendo el receptor detectado un bit de START, al llegar a  la posición en donde debería estar el bit de STOP, NO hay un 1. Esto puede significar  que  el  receptor  se  está  descincronizando,  y  que  a  lo  mejor  el  bit  que  detectó  como  Start no lo era.    PARERR ocurre cuando, estando habilitado el cálculo de paridad, se encuentra que en la  recepción  ésta  no  coincide  con  el  valor  que  tendría  que  haber.  Nosotros  NO  estamos  usando Paridad en nuestra biblioteca de Comunicaciones Seriales.    090 // MACRO DEFINITIONS    092 #define SCI9600N8   (SCI1BD = Bauds9600Value)  // Prog. SCI @9600,8,N    095 #define XmtRcvActivate (SCI1C2 = XmtRcvEnab); /*ACTIVATE Xmt & Rcv*/\  096                    SCI9600N8   /* Serial Communications Interface*/ \  097                                /* .. 9600 bps, No parity, 8 bits */    Mire bien, para entender la Macro, los CONTINUADORES de línea: "\"    Si algo allí no entiende, TIENE QUE ESTUDIAR C    255 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 100 #define RcvRdy RCVRDY_bit  103 #define RcvChar    RCVBUF    getchar()  es  una  función  BLOQUEANTE,  que  suspende  el  flujo  del  programa  hasta  que  se  haya recibido un dato, lo que se refleja al encender el bit de 'RcvRdy'. Devuelve el  valor recibido: RcvChar (que tiene dos alias: RCVBUF, que a su vez es alias de SCI1D)    La secuencia de: leer el bit RcvRdy y a continuación leer el RCVBUF (vía RcvChar), es  también  la  necesaria  para  el  protocolo  de  Acknowledge  de  interrupciones  del  RCV;  por  eso se usa 'getchar' también DENTRO de la ISR de Recepción.    106 // getchar() MAY Rearm Rcv Interrupts if inside ISR  107 byte getchar() {  108    Wait( RcvRdy );  109    return RcvChar;  110 }    En  una  próxima  revisión,  cambiaré  RcvChar  por  RcvChar(),  para  mimetizar  mejor  el  comportamiento de una función y asemejarla más a XmtChar(). Su uso cambiará así:    109    return RcvChar();  113 #define XmtRdy XMTRDY_bit  116 #define XmtChar(c) (XMTBUF = (byte)(c))    putchar() también es una función BLOQUEANTE, que suspende el flujo del programa hasta  que el Transmisor esté listo para enviar un dato, lo que se refleja al encender el bit  de 'XmtRdy'.    La  secuencia  de:  leer  el  bit  XmtRdy  y  a  continuación  escribir  en  el  XMTBUF,  vía  XmtChar(), es también la necesaria para el protocolo de Acknowledge de interrupciones  del XMT; por eso se usa 'putchar()' también DENTRO de la ISR de Transmisión.    119 #define putchar(c) _putchar((byte)(c))  Esta definición obliga mediante un CAST a que el símbolo que se transmite sea un BYTE,  aunque el usuario haya empleado un WORD.    ¿SABE LO QUE ES UN CAST? ¿SU USO E IMPORTANCIA? ¿NO? TIENE QUE ESTUDIAR C.    121 byte _putchar( byte c )  122 {  123    Wait( XmtRdy );  124    XmtChar(c);  125    return c;  126 }    Símbolos importantes:    129 #define XmtIntEn   (XMTIEN_bit = 1)  131 #define XmtIntDsb  (XMTIEN_bit = 0)  133 #define RcvIntEn   (RCVIEN_bit = 1)  135 #define RcvIntDsb  (RCVIEN_bit = 0)  143 #define CommIntEn { RCVSTATREG; RCVBUF; RcvIntEn; }  256 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 El CommIntEn, o habilitación de interrupciones para Comunicaciones, hace lo mismo que  mencionamos en el programa en ASM: sólo habilita las interrupciones de RCV; las de XMT  debe  habilitarse  caso  a  caso  por  el  programador,  cuando  haya  algo  que  enviar  por  interrupciones.    La funcionalidad, que usted debe CORROBORAR con el Reference Manual, consiste en leer  el RCVSTATREG; luego leer el RCVBUF; y por último habilitar las Interrupciones de RCV,  mediante: RcvIntEn.    Código auxiliar:    148 #define I   // NUL definition. From this point on, 'I' means NOTHING!  149 #define Forever    for(;;){  150 #define Endforever }  152 #endif // SCICOMM_H_INCLUDED  ¿Para qué puede utilizarse el '#define I' que hace que la letra I no valga NADA?    56) Send 'A' to 'z' letters for ever, to PC ["Labs‐C\Lab3\SciComm\1tstXmt.c"]  01 // ******************************************************************  02 // Program 1TstXmt.C: Test SCI Comm. Support  03 // Luis G. Uribe C., D11M2007 J16A09 L08J09 S16J2012(HCS08)  04 // C11D2013 (C)  05 //  ..Send 'A' to 'z' letters for ever, to Hyperterminal  07 // ******************************************************************  08 // Include Files  09 #include <hidef.h>             // For EnableInterrupts macro  10 #include "derivative.h"        // Include peripheral declarations  12 #include "SciComm.h"           // SCI Comm. Support  14 #define  CR 0x0D  15 #define  LF 0x0A  17 // ******************************************************************  18 void main ( void )                                             /*()*/  19 {  byte Letter;  21    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  22    // >>> ALWAYS include the following 2 lines  23    //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  24    #define COP_Disable 0x42  25    SOPT1 = COP_Disable;        // System Options 1  27    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  28    SCI9600N8;                  // Setup Serial Communications Inter‐  29    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits  30    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  32    while( 1 ) {  33        for( Letter = 'A'; Letter <= 'z'; Letter ++ ) {  34            putchar( Letter );  35        }  36        putchar( CR );          // Carriage Return (CR: '\r' in C)  37        putchar( LF );          // Line Feed (LF: '\n')  38    }  39 }  257 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Lab3\SciComm\1tstXmt.c"]: El  comienzo  es  ya  conocido.  Se  definen  luego  dos  símbolos,  ambos  definidos  por  el  estándar  ASCII  (American  Standard  Code  for  Information  Interchange)  el  CR,  que  es  el  símbolo  que  hace  que  el  cursor  se  vaya  a  la  izquierda  (Carriage  Return,  cuando  se  trataba de máquinas de escribir, o Teletipos), y el NL (New Line, que salta a la línea  siguiente)    14 #define  CR 0x0D  15 #define  LF 0x0A    18 void main ( void )                                             /*()*/  19 {  byte Letter;    Luego de la parte más convencional, viene la inicialización del SCI:    28    SCI9600N8;                  // Setup Serial Communications Inter‐  29    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits    Y QUÉ PASÓ CON EL CLI? Esta aplicación NO usa interrupciones.    El ciclo infinito para este programa genera las letras desde la 'A' hasta la 'z' y las  transmite  con  putchar(  Letter  );  al  finalizar  cada  secuencia  agrega  un  Retorno  de  Cursor, CR ('\r' en C) y un Salto de Línea, LF ('\n' en C), y recomienza con las letras  en la siguiente línea, para siempre:    32    while( 1 ) {  33        for( Letter = 'A'; Letter <= 'z'; Letter ++ ) {  34            putchar( Letter );  35        }  36        putchar( CR );          // Carriage Return (CR: '\r' in C)  37        putchar( LF );          // Line Feed (LF: '\n')  38    }  39 }    57) SEND STEP BY STEP (IRQ) 'A' TO 'Z' LETTERS TO PC & SHOW IN LEDS ["Labs‐C\Lab3\SciComm\1tstXmt‐Leds.c"]  01 // ******************************************************************  02 // Programa 1TstXmtLeds.C: Test SCI Comm. Support  03 // Luis G. Uribe C., D11M2007 J16A09 L08J09 S16J2012(HCS08)  04 // C11D2013 (C)  05 //  06 // ..Send 'A' to 'z' letters for ever to Hyperterminal. LEDs display  07 // ..Step by step using IRQ  08 // ******************************************************************  09 // Include Files  10 #include <hidef.h>             /* for EnableInterrupts macro */  11 #include "derivative.h"        /* include peripheral declarations */  12  13 #include "SciComm.h"           // SCI Comm. Support  14  258 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 15 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  16 // Global Variables  17  18 volatile byte GlobalVar;  19  20 // ******************************************************************  21 void main( void )  22 {  byte Letter;  23  24    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  25    // >>> ALWAYS include the following 2 lines  26    //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  27    #define COP_Disable 0x42  28    SOPT1 = COP_Disable;        // System Options 1  29  30    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  31    // Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7) for output  32    // In DEMOQUE128, DO ***NOT*** USE PTC5's ASSOCIATED LED, as it is  33    // ..also the pin used to DISABLE COMM1 !!! via Jumper J8, THANKS!  34    //  35    // NOTE1: For Output Pins/Ports, First load a SAFE value (you will  36    // ..decide it), then, program them for Outputs. This way, it will  37    // ..NOT appear FALSE output, values/glitches.  38    //  39    // NOTE2: Suppose your program has, for example, two outputs that  40    // ..will control the access for 2 devices to a common bus. The  41    // ..logic is: 00: No peripheral has access. 01: A has access.  42    // ..10: B has access. 11: Never happens! If it will, peripherals  43    // ..will burn out.  44    // Now, at Power Up, ALL digital pins default to INPUTS. Without  45    // ..Pull up resistors: They FLOAT!! For how long? It all depends  46    // .. on oscilator setup, and the time you expend before program‐  47    // ..ming the pins for output. It means: may be BOTH A & B devices  48    // ..will reach the bus... and BURN! Short circuit their outputs!!  49    // Sleep on this, and come up with a solution...  50  51    PTCD  = 0xFF;  52    PTED  = 0xFF;  53    PTCDD = 0x1F;               // ..(they drive LEDs on DEMOQE128)  54    PTEDD = 0xC0;  55  56    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  57    // Enable IRQ pin  58    IRQSC       = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;  59    IRQSC_IRQIE = 1;         // BSET IRQSC_IRQIE,IRQSC: IRQ pin IntEn  60  61    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  62    SCI9600N8;                  // Setup Serial Communications Inter‐  63    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits  64  65    CpuIntEn;  66  259 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 67    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  68    while( 1 ) {  69        for( Letter = 'A'; Letter <= 'z'; Letter ++ ) {  70            putchar( Letter );  71            Wait( GlobalVar );  72            GlobalVar = 0;  73            PTCD = ~Letter;  74            PTED = ~Letter;  75        }  76        putchar( '\r' );        // Carriage Return (CR)  77        putchar( '\n' );        // Line Feed (LF)  78    }  79 }  80  81 // ==================================================================  82 interrupt VectorNumber_Virq void IRQISR( void )                /*()*/  83 {  84    GlobalVar    = 1;  85    IRQSC_IRQACK = 1;   // BSET IRQSC_IRQACK, IRQSC: ACK IRQ Interrupt  86                        // ..(Rearm IRQ Interrupts)  87 }    COMENTARIOS a ["Labs-C\Lab3\SciComm\1tstXmt-Leds.c"]: La parte inicial es la convencional. Lean otra vez (ya habíamos dicho lo mismo cuando  hicimos  los  ejercicios  en  ASM)  y  con  detenimiento  los  requisitos  para  programar  los  terminales digitales como SALIDAS:    31    // Init LEDs PORTs PTCD (bits 0‐5) and PTED (bits 6‐7) for output  32    // In DEMOQUE128, DO ***NOT*** USE PTC5's ASSOCIATED LED, as it is  33    // ..also the pin used to DISABLE COMM1 !!! via Jumper J8, THANKS!  34    //  35    // NOTE1: For Output Pins/Ports, First load a SAFE value (you will  36    // ..decide it), then, program them for Outputs. This way, it will  37    // ..NOT appear FALSE output, values/glitches.  38    //  39    // NOTE2: Suppose your program has, for example, two outputs that  40    // ..will control the access for 2 devices to a common bus. The  41    // ..logic is: 00: No peripheral has access. 01: A has access.  42    // ..10: B has access. 11: Never happens! If it will, peripherals  43    // ..will burn out.  44    // Now, at Power Up, ALL digital pins default to INPUTS. Without  45    // ..Pull up resistors: They FLOAT!! For how long? It all depends  46    // .. on oscilator setup, and the time you expend before program‐  47    // ..ming the pins for output. IT MEANS: may be BOTH A & B devices  48    // ..will reach the bus... and BURN! Short circuit their outputs!!  49    // Sleep on this, AND COME UP WITH A SOLUTION...    Se  determina  que  queremos  que  al  iniciar  las  salidas,  todas  tengan  unos  en  sus  terminales:  51    PTCD  = 0xFF;  52    PTED  = 0xFF;  260 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Luego se programa los correspondientes bits en los Direction Registers, 5 van el PTC y  2 en PTE (total 7 LEDs).    Se excluye el terminal PTC5, que queda como ENTRADA, porque como ya dijimos arriba, en  el  DEMOQUE128  el  PTC5,  si  está  como  salida  (para  manejar  el  LED  asociado),  también  INHABILITARÁ el puerto de Comunicaciones Seriales, vía el Jumper J8. GRACIAS PE Micro!    53    PTCDD = 0x1F;               // ..(they drive LEDs on DEMOQE128)  54    PTEDD = 0xC0;    La habilitación del IRQ y su autorización para Interrumpir, es la convencional:    58    IRQSC       = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;  59    IRQSC_IRQIE = 1;         // BSET IRQSC_IRQIE,IRQSC: IRQ pin IntEn    Después se inicializa, como de costumbre, el puerto de comunicaciones SCI, se Activan  los dos periféricos, el de recepción y el de transmisión, y AQUÍ SÍ, se habilitan las  interrupciones del CPU:    62    SCI9600N8;                  // Setup Serial Communications Inter‐  63    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits  65    CpuIntEn;    El  ciclo  indefinido  es  tan  sencillo  como  el  del  anterior  ejercicio,  pero  después  de  transmitir cada  letra,  incluye  una  espera  a  que  'GlobalVar'  valga  uno  (¿por  qué  está  inicializada en CERO? Si aún no lo sabe, DESISTA)    Cuando la IRQISR coloca en 1 a GlobalVar, el programa continúa, reponiendo a cero la  variable, para el próximo turno, y presentando la letra por los LEDs. Se mueve ~Letter  (NEGADA  booleanamente)  porque,  como  ya  sabemos,  los  genios  de  PE  Micro  encienden  los  LEDs con CEROS:    68    while( 1 ) {  69        for( Letter = 'A'; Letter <= 'z'; Letter ++ ) {  70            putchar( Letter );  71            Wait( GlobalVar );  72            GlobalVar = 0;  73            PTCD = ~Letter;  74            PTED = ~Letter;  75        }  76        putchar( '\r' );        // Carriage Return (CR)  77        putchar( '\n' );        // Line Feed (LF)  78    }  79 }  La rutina IRQISR es la convencional, más GlobalVar = 1;    82 interrupt VectorNumber_Virq void IRQISR( void )                /*()*/  83 {  84    GlobalVar    = 1;  85    IRQSC_IRQACK = 1;   // BSET IRQSC_IRQACK, IRQSC: ACK IRQ Interrupt  86                        // ..(Rearm IRQ Interrupts)  87 }  261 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 58) ECHO Hace ECHO a los caracteres que le llegan al equipo desde otra estación.    Para  no  engañarse  con  el  ECO  LOCAL  el  programa  retransmite  la  letra  recibida,  INCREMENTADA en uno: si recibo un 'A', transmito una 'B' y así sucesivamente.    Recuerde probarlo enviándole "HAL" (la supercomputadora de "2001 Odisea del Espacio"):  aparece en la pantalla... IBM.    ["Labs‐C\Lab3\SciComm\2echo1.c"]  01 // ******************************************************************  02 // Program 2Echo1.c: Test SCI Comm. Support  03 // Luis G. Uribe C., D11M2007 J16A09 L08J09 S16J2012 (HCS08)  04 // C11DN2013 (C)  05 // Luis G. Uribe C., D11M2007 J16A09 L08J09 S16J2012 (HCS08) L25N2013  06 // To be sure that there is "ECHO", return back 'letter+1',  07 // ..i.e: if Hyperterminal sends 'A', HC9S08 will return 'B'...  08 // ..for the range: 'A' <= char < 'z'.  09 // Any other chars are returned back without modification.    10 // ******************************************************************  11 // Include Files  12 #include <hidef.h>             // for EnableInterrupts macro  13 #include "derivative.h"        // include peripheral declarations    14 #include "SciComm.h"           // SCI Comm. Support    15 // ******************************************************************  16 void main( void )  17 {  byte Letter;    18    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  19    // >>> ALWAYS include the following 2 lines  20    //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  21    #define COP_Disable 0x42  22    SOPT1 = COP_Disable;        // System Options 1    23    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  24    SCI9600N8;                  // Setup Serial Communications Inter‐  25    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits    26    // Prompt:  27    for( Letter = 'A'; Letter <= 'z'; Letter ++ ) {  28        putchar( Letter );  29    }  30    putchar( '\r' );            // Carriage Return (CR)  31    putchar( '\n' );            // Line Feed (LF)    32    // ************ NOTE: ************  33    // To SIMULATE SCI *inputs* use PEMicro SCI1 debugger command.  34    //  262 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 35    // It will be instructive to display SCI1S1 (status register), and  36    // ..see most important flags, bits 7,6,5: XMTRDY,XMTEMPTY,RCVRDY.  37    // While debugging, see how reading Status AND writing BUF, clears  38    // ..the RCV Ready flag. See also how XMT Status work: RDY & EMPTY    39    while( 1 ) {                // Only chars in range A..z will be  40        if( ( Letter = getchar() )  >= 'A'  &&  Letter  <= 'z' ) {  41            Letter ++;          // ..returned as NEXT char:  Letter ++  42        }  43        putchar( Letter );      // ..Those not in range will echo  44    }   // End while( 1 )       // ..WITHOUT any modification: Letter    45 }    COMENTARIOS a ["Labs-C\Lab3\SciComm\2echo1.c"]: Este programa tampoco emplea las interrupciones: es todo entrada y salida programada;  por eso no aparece un CpuIntEn.    Se  comienza  enviando  un  aviso  (Prompt)  al  otro  terminal,  para  notificarle  que  el  programa de este lado está listo para trabajar; aquí enviamos una línea compuesta por  las letras de la 'A' a la 'z', pero usted puede usar un texto cualquiera como "Ready":    26    // Prompt:  27    for( Letter = 'A'; Letter <= 'z'; Letter ++ ) {  28        putchar( Letter );  29    }  30    putchar( '\r' );            // Carriage Return (CR)  31    putchar( '\n' );            // Line Feed (LF)    Si  se  quieren  simular  *inputs*  del  SCI,  hay  un  comando  apropiado  en  el  Debugger.  Resulta instructivo, además, mostrar en su pantalla el SCI1S1 (status register) y ver  las banderas más importantes, bits 7, 6 y 5: XMTRDY, XMTEMPTY y RCVRDY. Observe también  cómo, la acción de leer el Status Y escribir sobre BUF, BORRA el RCV Ready flag. Mire  también cómo trabaja el XMT Status, ambos bits: RDY y EMPTY.    39    while( 1 ) {                // Only chars in range A..z will be  40        if( ( Letter = getchar() )  >= 'A'  &&  Letter  <= 'z' ) {  41            Letter ++;          // ..returned as NEXT char:  Letter ++  42        }  43        putchar( Letter );      // ..Those not in range will echo  44    }   // End while( 1 )       // ..WITHOUT any modification: Letter    59) ECHO USANDO INTERRUPCIONES ["Labs‐C\Lab3\SciComm\3echoInt‐1.c"]  01 // ******************************************************************  02 // Program 3EchoInt‐1.c: Test SCI Comm. Support  03 // Luis G. Uribe C., M13M2007 J16A09 L08J09 L18J2012 (HCS08) D24J2012  04 // S30N2013 (C)  C11D2013  05 // ..Same 2Echo1.C program but using INTERRUPTS for XMT    263 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 06 // ******************************************************************  07 // Include Files  08 #include <hidef.h>             // for EnableInterrupts macro  09 #include "derivative.h"        // include peripheral declarations  10 #include "SciComm.h"           // SCI Comm. Support  11 // ==================================================================  12 //  GLOBAL VARIABLES  13 byte  XmtNchars;               //Soft Flag for Main: 0 Nothing to Xmt  14 byte *XmtPtr;                  //char pointer of next data to Xmt  15 byte  Message[] = "12345\n\r"; // Short message  16 // byte  Message[] = "Este es el mensaje de prueba, "  17 //       "primero en transmitirse al PC por interrupciones\n\r"  18 // ******************************************************************  19 void main( void )  20 {  byte Letter;  21    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  22    // >>> ALWAYS include the following 2 lines  23    //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  24    #define COP_Disable 0x42  25    SOPT1 = COP_Disable;        // System Options 1    26    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  27    SCI9600N8;                  // Setup Serial Communications Inter‐  28    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits    29 // ==================================================================  30 // Transmit your message: Enque at once full 'Message' Table for Xmt:  31 // Prompt:    32    XmtPtr    = Message;        // Init XmtPtr with 'Message' address;  33    XmtNchars = sizeof( Message );    34    XmtIntEn;                   // XmtISR will send the message and  35                                // ..clear 'XmtNchars' when finished  36    CpuIntEn;                   // <<<DON'T FORGET: GLOBAL ENABLE INTs  37 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  38 // XmtLoop:    39    Wait( XmtNchars == 0 );     // Wait until done OR just WaitForEver  40    Wait( 0 );                  // ..WaitForEver  41 }    42 // ==================================================================  43 interrupt VectorNumber_Vsci1tx void XmtISR( void )             /*()*/  44 {  45    if( XmtNchars ) {           // See if done; if not:  46        putchar( *XmtPtr++ );   // (NOTE: putchar clears XMTRDY.bit)  47        XmtNchars‐‐;            // Adjust char counter  48    }else{                      // If XmtDone:  49        XmtIntDsb;              // ..  50    }  51 }  264 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Lab3\SciComm\3echoInt-1.c"]: Variables Globales, para ser visibles tanto en el programa principal:    XmtNchars, que mantiene el número actual de caracteres que faltan por transmitirse; es  de  tipo  byte,  por  lo  cual  no  pueden  manejarse  mensajes  de  más  de  255  letras;  0  significa vacío.    *XmtPtr  apuntador  a  caracteres,  señala  el  siguiente  dato  que  será  transmitido.  byte  *XmtPtr    13 byte  XmtNchars;               //Soft Flag for Main: 0 Nothing to Xmt  14 byte *XmtPtr;                  //char pointer of next data to Xmt    Siendo que los apuntadores tienen 16 bits, por qué no se hizo la definición como: word  *XmtPtr  SI DUDA TIENE QUE ESTUDIAR MUCHÍSIMO C (SIN DUDA)    Luego viene el mensaje que se quiere transmitir; el segundo, comentado, es un poco más  largo, para ensayar:    15 byte  Message[] = "12345\n\r"; // Short message  16 // byte  Message[] = "Este es el mensaje de prueba, "  17 //       "primero en transmitirse al PC por interrupciones\n\r"    La  inicialización  estándar,  más  la  de  las  variables  XmtPtr  y  XmtNchars.  La  tabla  se  'encola' para ser transmitida de una sola vez:    27    SCI9600N8;                  // Setup Serial Communications Inter‐  28    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits  32    XmtPtr    = Message;        // Init XmtPtr with 'Message' address;  33    XmtNchars = sizeof( Message );  34    XmtIntEn;                   // XmtISR will send the message and  35                                // ..clear 'XmtNchars' when finished    y NO OLVIDAR habilitar las interrupciones Globales, del CPU:    36    CpuIntEn;                   // <<<DON'T FORGET: GLOBAL ENABLE INTs    El ciclo de transmisión es muy simple: el programa espera (Wait) hasta que el número de  caracteres sea cero, lo que le indica la finalización de la transmisión del mensaje, y  continúa; como este ejercicio llega hasta allí, hacemos una simulación de un HALT:    38 // XmtLoop:  39    Wait( XmtNchars == 0 );     // Wait until done OR just WaitForEver  40    Wait( 0 );                  // ..WaitForEver    La  rutina  de  transmisión  es  muy  simple;  lo  primero  es  la  definición  de  tipo  'interrupt',  la  identificación  de  la  posición  de  la  rutina  en  el  Vector  de  Interrupciones, 'VectorNumber_Vsci1tx' (todos SÍMBOLOS, no más NÚMEROS) definidos en el  correspondiente include file. Por último, el nombre de la rutina de interrupciones, de  tipo 'void': XmtISR  265 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 43 interrupt VectorNumber_Vsci1tx void XmtISR( void )             /*()*/    Si  aún  hay  caracteres  para  transmitir,  los  envía  con  putchar  (que  realiza  el  ACK),  incrementa el apuntador de caracteres, decrementa el número de símbolos que faltan por  transmitirse, y RTI (AUNQUE XmtNchars‐‐ haya producido CERO)    45    if( XmtNchars ) {           // See if done; if not:  46        putchar( *XmtPtr++ );   // (NOTE: putchar clears XMTRDY.bit)  47        XmtNchars‐‐;            // Adjust char counter    Si al momento de interrumpir para transmitir, se encuentra que XmtNchars vale cero, se  AUTO deshabilitan las interrupciones de transmisión y se retorna con RTI:    48    }else{                      // If XmtDone:  49        XmtIntDsb;              // ..  50    }  51 }    Este mecanismo en el cual el 'Cliente' (main) hace una petición a la rutina de Servicio  (XmtISR), activándole las Interrupciones, y donde la ISR se AUTODESACTIVA al terminar  lo  que  le  solicitaron,  ES  LA  FORMA  en  que  usted  debe  trabajar:  MECANISMO  Cliente‐ Servidor.    Ocurre que con frecuencia, por hacer un diseño no muy pulcro, aparecen varios Clientes  (o  lugares  en  su  código  en  donde  se  habilitan  las  interrupciones),  O  SE  MEZCLAN  putchars desde MAIN y la ISR: ESO RARA VEZ FUNCIONA. Y el programador ¡NO VA A SABER  POR QUÉ!    NO LO HAGA    60) ECHO CON INTERRUPCIONES, ASCIZ STRINGS El  mecanismo  anterior  define  los  mensajes  mediante  una  posición  de  memoria  en  donde  comienzan los símbolos almacenados (una tabla) y un Tamaño o cantidad de letras que han  de manipularse. Como parte de la implementación se definen dos elementos auxiliares, el  apuntador al próximo símbolo, y el Contador de Símbolos que faltan por procesarse. Así  son los "strings" en C++.    Una aproximación parecida cambia el Contador de Símbolos por un TERMINADOR, que es un  símbolo especial que indica el final del mensaje. En C, los "strings" se definen así, y  el  terminador  es  un  byte  NULO,  con  todos  sus  bits  en  CERO.  Un  nombre,  anterior  a  la  existencia  del  lenguaje  C,  que  designa  esto  mismo,  es  el  de  un  texto  de  tipo  ASCIZ  (código ASCII terminado en ZERO)    Este ejemplo es similar al anterior, pero muestra el manejo de mensajes del tipo ASCIZ:    ["Labs‐C\Lab3\SciComm\3EchoInt‐2Z.c"]  01 // ******************************************************************  02 // Program 3EchoInt‐2Z.c: Test SCI Comm. Support  03 // Luis G. Uribe C., M13M2007 J16A09 L08J09 L18J2012 (HCS08) D24J2012  04 // M10D2013 (C)  05 // ..Similar to 2Echoint‐1.C, but uses ASCIZ  266 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 06 // ******************************************************************  07 // Include Files  08 #include <hidef.h>             // For EnableInterrupts macro  09 #include "derivative.h"        // Include peripheral declarations    10 #include "SciComm.h"           // SCI Comm. Support    11 // ==================================================================  12 //  GLOBAL VARIABLES    13 byte *XmtPtr;                  // char pointer of next data to Xmt  14 byte *Message = "12345\n\r";   // Short message    15 // byte *Message = "Este es el mensaje de prueba, "  16 //      "primero en transmitirse al PC por interrupciones\n\r"    17 // ******************************************************************  18 void main ( void )                                             /*()*/  19 {  20    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  21    // >>> ALWAYS include the following 2 lines  22    //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  23    #define COP_Disable 0x42  24    SOPT1 = COP_Disable;        // System Options 1    25    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  26    SCI9600N8;                  // Setup Serial Communications Inter‐  27    XmtRcvActivate;             // ..face: 9600 bps, No parity, 8 bits    28 // ==================================================================  29 // Transmit your message: Enque at once full 'Message' Table for Xmt:  30 // Prompt:    31    XmtPtr = Message;           // Init XmtPtr with 'Message' address;  32    XmtIntEn;                   // XmtISR will send the message and  33                                // ..auto desable  34    CpuIntEn;                   // <<<DON'T FORGET: GLOBAL ENABLE INTs    35 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  36 // XmtLoop:  37    Wait( 0 );                  // ..WaitForEver    38 }    39 // ==================================================================  40 interrupt VectorNumber_Vsci1tx void XmtISR ( void )            /*()*/  41 {  // NOTE: putchar clears XMTRDY.bit. Very clever...    42    if( ! putchar( *XmtPtr++ ) )    {  // 'C' Strings end at '\0'  43        XmtIntDsb;  44    }  45 }  267 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Lab3\SciComm\3EchoInt-2Z.c"]: El Message es "12345\n\r", y como es un String de C (por las comillas), aunque no lo  vemos  sabemos  que  está  terminado  por  un  último  símbolo,  el  '\0'  o  NUL  byte.  La  diferencia con la definición anterior es sutil, pero fundamental.    14 byte *Message = "12345\n\r";   // Short message    Aquí  se  inicializa  el  pointer  de  transmisión  con  la  dirección  donde  comienza  el  mensaje, y simplemente se habilitan las interrupciones, del transmisor, y del CPU. El  código no hace nada más, así que se simula un HALT al final:    31    XmtPtr = Message;           // Init XmtPtr with 'Message' address;  32    XmtIntEn;                   // XmtISR will send the message and  33                                // ..auto desable  34    CpuIntEn;                   // <<<DON'T FORGET: GLOBAL ENABLE INTs  37    Wait( 0 );                  // ..WaitForEver    La  rutina  XmtISR  es  BIEN  SIMPLE:  Cada  vez  que  hay  una  interrupción,  TRANSMITE  la  próxima letra y si ésta coincide con el NUL char, se auto deshabilita para interrumpir:    40 interrupt VectorNumber_Vsci1tx void XmtISR ( void )            /*()*/  41 {  // NOTE: putchar clears XMTRDY.bit. Very clever...  42    if( ! putchar( *XmtPtr++ ) )    {  // 'C' Strings end at '\0'  43        XmtIntDsb;  44    }  45 }    61) COLAS DE DATOS El manejo de Colas es muy similar al que ya estudiamos en ASM: ¡aquél lo saqué de aquí!    Ahora,  USTED  TIENE  QUE  SABER  C  PARA  ENTENDER  ESTE  CÓDIGO.  Hasta  las  COMAS  son  FUNDAMENTALES en el siguiente texto:    ["Labs‐C\Lab3\SciComm\Que.h"]  01 #ifndef QUE_H_INCLUDED  02 #define QUE_H_INCLUDED  03 // ******************************************************************  04 // QUE.H   Luis G. Uribe C,  V20J2014: .gt .pt  05 // 18S1990 D04S2011 V29N21013  06 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  07 // Several Definies  08   09 #undef  Wait  10 #define Wait(e)   while( !(e))  11 #undef  CpuIntEn  12 #define CpuIntEn  __asm CLI  13 #undef  CpuIntDsb  14 #define CpuIntDsb __asm SEI  15 #undef  EOF  16 #define EOF ((word)‐1)  268 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 18 // REMEMBER!!!: 'byte' is UNSIGNED char; 'word' is UNSIGNED int  20 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  21 typedef  struct  {  22     byte  *put;                /*              n (5)               */  23     byte  *get;                /* (2) buf‐‐‐‐> +‐‐‐+               */  24     word   n;                  /*              |(1)| ‐‐‐> (4) get  */  25     word   gt;                 /* (3) put ‐‐‐> | s |               */  26     word   pt;                 /*              | i |               */  27     byte  *base;               /*              | z |               */  28     byte  *limit;              /*              | e |               */  29     word   size;               /*              +‐‐‐+               */  30 } QUE;                           31   32 /* Ejemplo: Sea 'size'(1) de 'buf'(2) igual a 5.                   *  33  * Los pointers 'put' (3) y 'get' (4) pueden tener los valores     *  34  *      buf, buf + 1, buf + 2, buf + 3, buf + 4;                   *  35  * es decir, los pointers deben permanecer en el rango             *  36  *      buf  <=  pointer  <  (buf+size), o, lo que es lo mismo     *  37  *      buf  <=  pointer  <  limit                                 *  38  *                                                                 *  39  * 'n' (5) puede valer:                                            *  40  *      0 (vacío), 1, 2, 3, 4 y 5 (lleno). Note que 'n' ES         *  41  * el >>>SEMAFORO<<<, e impone las siguientes reglas de tráfico:   *  42  *                                                                 *  43  *      NO  'deQue' cuando n sea  <=  0                            *  44  *      NO  'enQue' cuando n sea  >=  size                         *  45  *                                                                 *  46  * Las siguientes operaciones están definidas:                     *  47  */  49 #define defineQue( in, n ) byte _Q##in[ n ]; QUE in  50   51 #define initQue( in )      in.base = in.put = in.get = _Q##in;      \  52                            in.n = 0;  in.size = sizeof( _Q##in );   \  53                            in.limit = in.base + in.size  54   55 #define enQue( in, c )  (  in.n    >=  in.size  ?  EOF  :           \  56                           (  in.pt  = c  ,  *in.put++ = (byte)c  ,  \  57                              in.put = in.put  >=  in.limit  ?       \  58                                       in.base  :  in.put  ,         \  59                              in.n ++  ,  in.pt                      \  60                           )                                         \  61                         )  62   63 #define deQue( in )     (  in.n    <=  0  ?  EOF  :                 \  64                           (  in.gt  = *in.get ++  ,                 \  65                              in.get = in.get  >=  in.limit  ?       \  66                                       in.base  :  in.get  ,         \  67                              in.n ‐‐  ,  in.gt                      \  68                           )                                         \  69                         )  70   71 #endif // QUE_H_INCLUDED  269 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Lab3\SciComm\Que.h"]: Las  primeras  definiciones  están  duplicadas  en  varios  otros  sitios  pero  NO  arrojan  errores porque están todas antecedidas por #UNDEFs.    En primer lugar la Estructura que tienen mis QUEs, plasmada en un 'typedef': QUE. En el  esquema se indican los elementos que la componen, ordenados del 1 al 5.    Una cola tiene:  1. Un tamaño físico, 'size'  2. Una dirección de comienzo, 'buf'  3. Un  apuntador  al  sitio  EN  el  que  almacenará  (enQue)  el  próximo  símbolo,  'put';  se  inicializa en 'buf'.  4. Un apuntador al sitio DESDE el que extraerá (deQue) la siguiente letra, 'get'; se  inicializa en 'buf'.  5. Y 'n', MUY IMPORTANTE: indica cuántos caracteres hay en la cola en un momento dado.  Este es el SEMÁFORO que rige y ordena las transacciones; así:  'n'puede valer 0 (vacío), 1, 2, ... 'size' (lleno) Las reglas son las siguientes:  NO 'deQue' cuando n sea  <=  0 NO 'enQue' cuando n sea  >=  size  Hay dos variables temporales de tipo Word: 'gt' y 'pt'; luego veremos su uso, y por qué  son WORD en vez de BYTE. Un apuntador al FINAL de la tabla, 'limit', no esencial pero  para facilitar el código, y una posición que indica el tamaño de la cola (aquí la hemos  definido como WORD, que permite colas hasta de 64K, lo que es mucho...)      20 /* ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ */  21 typedef  struct  {  22     byte  *put;                /*              n (5)               */  23     byte  *get;                /* (2) buf‐‐‐‐> +‐‐‐+               */  24     word   n;                  /*              |(1)| ‐‐‐> (4) get  */  25     word   gt;                 /* (3) put ‐‐‐> | s |               */  26     word   pt;                 /*              | i |               */  27     byte  *base;               /*              | z |               */  28     byte  *limit;              /*              | e |               */  29     word   size;               /*              +‐‐‐+               */  30 } QUE;                             Las siguientes operaciones están definidas, igual que en su contraparte en ASM:    defineQue, initQue, enQue y deQue:    El  defineQue  requiere  un  NOMBRE  de  la  Cola  y  un  tamaño.  La  definición...  tienen  que  estudiarla. Si no la entienden... TIENEN QUE ESTUDIAR C    49 #define defineQue( in, n ) byte _Q##in[ n ]; QUE in    Primero se define un arreglo (vector); byte _Q##in[ n ];    A que no vieron algo así... ESTUDIEN C. ## es el operador catenate: une dos símbolos.    270 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 El nombre del vector es: _Q seguido (CONCATENADO: ##) con el nombre que el usuario le  da  a  su  cola,  representada  por  el  parámetro  'in',  y  ocupando  un  espacio  en  BYTES  de  'n', parámetro que reemplaza al tamaño que el usuario desea darle a la Cola.    A continuación, separada por un punto y coma de la anterior, para que quede en la misma  línea (¿habían visto varias instrucciones de C, en la misma línea?) viene: QUE in, que  define una variable con el nombre que el usuario quiere, simbolizado en la Macro por el  parámetro 'in', y de type QUE, según la definición 'typedef' anterior.    Ejemplo: Si el usuario quiere definir una cola llamada 'ColaRead', de  '16' bytes, lo expresará de la siguiente forma:      defineQue( colaRead, 16 )    El resultado será, un arreglo:  byte    _QcolaRead[ 16 ];    y una estructura llamada 'colaRead', de tipo QUE.    Mire el NOMBRE del arreglo: el símbolo '_Q' concatenado con el nombre que se le está  dando a la cola: 'colaRead'.    IMPORTANTE:    'defineQue' *TIENE* QUE EMPLEARSE COMO VARIABLE 'GLOBAL', FUERA DE CUALQUIER {}: Fuera  de main(), y de cualquier función. Como en ASM.    Después de definir la cola, hay que inicializarla. Esto sí se hace DENTRO DE MAIN. Para  la cola 'colaRead', la invocación es:    initQue( colaRead );    51 #define initQue( in )      in.base = in.put = in.get = _Q##in;      \  52                            in.n = 0;  in.size = sizeof( _Q##in );   \  53                            in.limit = in.base + in.size    Imagino que saben CÓMO continuar el código de una Macro en varias líneas: para eso se  emplea el '\'. Note que la ÚLTIMA no lo lleva...    Tal como se dijo antes, se inicializan los apuntadores base, put y get de la cola en  consideración (en el ejemplo: colaRead.base, colaRead.put y colaRead.get). El valor que  toma  es  el  del  comienzo  de  VECTOR  que  ya  se  DEFINIÓ:  _Q##in,  que  en  el  ejemplo  corresponde a: _QcolaRead.    Las  inicializaciones  continúan:  colaRead.n  =  0;  colaRead.size  asume  el  TAMAÑO  del  Vector, sizeof( _QcolaRead ).    Si no sabe cómo se usa 'sizeof', usted NO SABE "C" y TIENE QUE ESTUDIAR C, URGENTE.    Finalmente  se  inicializa  la  variable  in.limit  para  apuntar  a  la  última  dirección  del  Vector:    271 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 53                            in.limit = in.base + in.size    Es decir: colaRead.limit = colaRead.base + colaRead.size    La operación para 'encolar' un byte... tomen ALIENTO. Menos mal que YO hago la librería  y usted, fundamentalmente, LA USA (aunque si no la ENTIENDE, anda bastante MAL)    55 #define enQue( in, c )  (  in.n    >=  in.size  ?  EOF  :           \  56                           (  in.pt  = c  ,  *in.put++ = (byte)c  ,  \  57                              in.put = in.put  >=  in.limit  ?       \  58                                       in.base  :  in.put  ,         \  59                              in.n ++  ,  in.pt                      \  60                           )                                         \  61                         )    Primero, ¿por qué tiene tantos paréntesis? Recuerden que el uso de esta Macro es:     c = enQue( colaWrite, letra );    Entonces, QUÉ ES LO QUE DEVUELVE LA MACRO? Normalmente devuelve 'c', el MISMO parámetro  que se le pasó para encolar ('letra' en el ejemplo), pero, si la cola está LLENA, y por  tanto no se puede ejecutar la función de Encolar, se devuelve el símbolo EOF (espero  que sí sepan lo que es EOF). Así que el valor de esa expresión, es 'c', si hay espacio  en la cola, o EOF, si la cola está llena. ¿Cómo ocurre eso?    Voy a reescribir la Macro para mejorar su legilibilidad, alineando los paréntesis que  abren, con los que cierran, y voy a ELIMINAR los continuadores '\' de la Macro:    (  in.n    >=  in.size  ?  EOF  :    (  in.pt  = c  ,  *in.put++ = (byte)c  ,       in.put = in.put  >=  in.limit  ?                in.base  :  in.put  ,       in.n ++  ,  in.pt    )  )    Ahora  voy  a  dejar  sólo  lo  que  tiene  que  ver  con  devolver  'c'  o  EOF.  ELIMINO  temporalmente  4  líneas  Internas  de  la  Macro),  y  lo  coloco  en  una  línea:  (  in.n    >=   in.size  ?  EOF : (eliminado) , in.n ++ , in.pt )    Primero, el operador TERNARIO: expC ? expT : expF    SI NO SABE QUÉ ES EL OPERADOR TERNARIO, USTED NO SABE C. ESTUDIE!!!    'expC' es CUALQUIER expresión; implica asignaciones, Comparaciones, funciones... El '?'  PREGUNTA si 'expC' es Verdadera o Falsa. Si es verdadera, el operador ternario asume el  valor  'expT',  si  es  falsa  el  valor  es  'expF',  siendo  expT  y  expF,  a  su  vez,  expresiones. Para separarlas está el símbolo ':' En nuestro caso:    expC: in.n  >=  in.size  expT: EOF  expF: (eliminado) , in.n ++ , in.pt    272 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Es decir, si el número de caracteres almacenados en la cola, es MAYOR O IGUAL al tamaño  DEFINIDO, se devuelve EOF. Hasta ahí, ok.    Pero, qué devuelve 'expF'? Qué es "in.n ++ , in.pt"?    Es el COMMA OPERATOR. NO SABE QUE ES? TIENE QUE ESTUDIAR MUCHÍSIMO C.    El  Comma  Operator  evalúa,  de  IZQUIERDA  a  DERECHA,  todas  las  expresiones  que  estén  separadas  por  COMAS...  Y  RETORNA  UN  VALOR:  EL  DE  LA  *ÚLTIMA*  EXPRESIÓN,  LA  DE  LA  *DERECHA*.    En este caso, expF ejecuta (eliminado) primero, 'in.n++' luego y ' in.pt' después.    Imagino que no lo saben, pero una variable ES una expresión. Así que:      in.pt;    ES  una  SENTENCIA.  (SI  NO  SABE  LA  DIFERENCIA  ENTRE  EXPRESIÓN  Y  SENTENCIA,  "EXPRESSION  and STATEMENT", EN INGLÉS, DESISTA! NO TIENE LA MENOR IDEA DEL LENGUAJE C)    Un “expression” y un “statement” TIENEN un valor. El valor de:      in.pt;    es lo que está almacenado en 'in.pt': es el VALOR del parámetro 'c', que se almacenó en  in.pt en la línea 56:    56  (  in.pt  = c , ...    Entonces, 'expF': "(eliminado), in.n ++ , in.pt",  DEVUELVE 'c'.    Así funciona la primera parte: Si no hay espacio, devuelve EOF, de lo contrario, encola  la letra y devuelve su valor.    La parte interna de la Macro, que se ejecuta sólo si hay espacio, se encarga de Encolar  el símbolo:    ... *in.put++ = (byte)c  ,  in.put = in.put  >=  in.limit  ?           in.base  :  in.put  ,    Se almacena 'c', empleando el pointer 'put', que se auto incrementa. A 'c' se le hace  un CAST a (byte), porque el usuario ha podido enviar algo de mayor longitud.    A  continuación  (según lo  ordena  el  operador COMMA),  se ejecuta  la expresión  que  está  entre los paréntesis internos (que NO se necesitan: todo lo que está allí es UNA sola  expresión; están sólo para mejorar la comprensión). Repito aquí la expresión interna,  que es una asignación al apuntador in.put, proveniente de un OPERADOR TERNARIO:    in.put = in.put >= in.limit  ? in.base : in.put    273 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 Es  decir,  a  in.put  se  le  asigna  un  valor  determinado  así:  si  sobrepasó  el  límite,  in.limit, o si está apuntando a él, el valor que se le asigna es LA BASE. Este es el  punto que convierte un Vector, en una cola CIRCULAR. Lo que significa es que, luego de  operar  el  último  elemento  del  vector,  el  apuntador  correspondiente  APUNTA  A  LA  BASE,  operación  conocida  como  WRAP  AROUND.  Si  in.put  no  está  en  condición  de  Wrap  Around,  entonces NO SE LO MODIFICA; es decir, su valor futuro, in.put, es igual a su valor de  entrada, in.put.    Último punto, y lo más IMPORTANTE: El SEMÁFORO: in.n, NO PUEDE estar en NINGUNA OTRA  posición del código. En el momento en que se lo modifica, ahí mismo puede ocurrir una  interrupción  para  efectuar  la  operación  contraria  (si  está  haciendo  enQue,  se  querrá  hacer  un  deQue).  El  semáforo  TIENE  que  garantizar  que  no  importa  DÓNDE  ocurra  la  interrupción, TODO FUNCIONA A LA PERFECCIÓN. En el sitio en donde está, sólo falta por  devolver el valor que está guardado en la variable de usuario 'in.pt', por lo que deQue  NO altera para nada el estado de la cola, desde el punto de vista del enQue. Observe  más  adelante  lo  mismo,  en  relación  a  la  Macro  deQue.  Para  eso  sirve  la  variable  temporal in.gt que se usa allá.    CLARO?    C tiene una sintaxis MUY comprimida. Los siguientes símbolos    simples: + ‐ * / % & | ^ ! ~ # ( ) _ = [ ] { } ; : ' " , . < > ? \  dobles:  ++ ‐‐ ** && || ## == << >>    poseen un significado. (Algunos tienen varios, dependiendo del contexto. Por ejemplo,  las  COMAS  en  las  llamadas  a  Funciones,  NO  SON  COMMA  OPERATORS  y,  de  hecho,  su  EVALUACIÓN es de DERECHA A IZQUIERDA, al revés del Operador Coma)    SI NO CONOCE EL USO DE ALGUNO DE ESOS SÍMBOLOS, USTED NO SABE C, Y TIENE QUE ESTUDIAR  MUCHÍSIMO.    La macro deQue es para ser usada así: c = deQue( colaRead ); y debe ser ahora fácil de  entender.    63 #define deQue( in )     (  in.n    <=  0  ?  EOF  :                 \  64                           (  in.gt  = *in.get ++  ,                 \  65                              in.get = in.get  >=  in.limit  ?       \  66                                       in.base  :  in.get  ,         \  67                              in.n ‐‐  ,  in.gt                      \  68                           )                                         \  69                         )    Una  librería  GENÉRICA  para  el  manejo  de  Colas  de  Datos,  tendría  que  poder  manipular  cualquier tipo de variables, incluyendo las definidas vía Typedefs. Pero eso está más  allá de lo que pretendo enseñar en este texto de ARQUITECTURA DEL COMPUTADOR.    62) COLAS DE DATOS: DEMOSTRACIÓN Se han mantenido las mismas etiquetas cont: y full:, para que se vea el comportamiento  equivalente con su equivalente en ASM: ["Laboratorios\Lab3\SciComm\4quetst.asm"]    274 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 ["Labs‐C\Lab3\SciComm\4quetst.c"]  01 // ******************************************************************  02 // Program 4QueTst.c: Test "Queue" Support (for SCI)  03 // Luis G Uribe C M13M2007 C15A09 L08J09 S16J2012(HCS08) S30N2013 "C"  04 // See: 4QueTst‐B.c (COMPACT)  05 // See in this program: The use of Que.H, defineQue, initQue, enQue  06 // ..and deQue. You must NOT exceed the free RAM on the MCU...  07 //  08 // ******************************************************************  09 // Include Files  10 #include <hidef.h>             // For EnableInterrupts macro  11 #include "derivative.h"        // Include peripheral declarations    12 #include "Que.h"               // QUEUE Support  13 // ==================================================================  14 // GLOBAL VARIABLES. See 'defQ' use...  15 // defQ >>>"ALWAYS" must be GLOBAL<<<: Out of any '{}'    16 defineQue( inQ,  6 );    /* DEFINE Queues 'inQ', 6 bytes & 'outQ', */  17 defineQue( outQ, 5 );    /* ..5 bytes. Choose the names you want...*/    18 // ******************************************************************  19 void main( void )  20 {  byte c;  21    word cw;    22    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  23    // >>> ALWAYS include the following 2 lines  24    //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  25    #define COP_Disable 0x42  26    SOPT1 = COP_Disable;        // System Options 1    27    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  28    initQue( inQ );  29    initQue( outQ );  30    // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  31 cont:  32    for( c = 'A'; ; c ++ ) {  33        if( enQue( inQ, c) == EOF ) // Put Debugger's breakpoint here  34            break;                  // ..to see chars going into 'inQ'  35    }  36 full:  37    while( 1 ) {  38        cw = deQue( inQ );  39        if( cw == EOF )  40            break;  41        if( enQue( outQ, cw ) == EOF )  // Will break here because...  42            break;                      // ..outQ is smaller than inQ  43    }  44    Wait( 0 );    45 }  275 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 COMENTARIOS a ["Labs-C\Lab3\SciComm\4quetst.c"]: El #include "Que.h" como corresponde:    12 #include "Que.h"               // QUEUE Support    VARIABLES GLOBALES: EL defQue, SIEMPRE GLOBAL (como en la librería en ASM)    Para el ejemplo se definan dos colas, inQ de 6 bytes, y outQ de 5:  16 defineQue( inQ,  6 );    /* DEFINE Queues 'inQ', 6 bytes & 'outQ', */  17 defineQue( outQ, 5 );    /* ..5 bytes. Choose the names you want...*/    Al comienzo de main, la definición de 'c'. En realidad se usa un byte c y un word cw,  como veremos adelante.    19 void main( void )  20 {  byte c;  21    word cw;    La INICIALIZACIÓN de las colas:    28    initQue( inQ );  29    initQue( outQ );  Se hace un ciclo indefinido (for sin límite: ; ;), que almacena letras comenzando desde  la 'A'. Cuando la cola se llena, se termina el for (no se hace la comparación con el  tamaño  de  la  cola,  para  evidenciar  el  mecanismo  que  opera  cuando  la  cola  se  llena).  Para  la  simulación,  esté  pendiente  de  colocar  BREAKPOINTS  en  donde  se  indica  en  el  código, para visualizar lo que se está manipulando en las colas:    32    for( c = 'A'; ; c ++ ) {  33        if( enQue( inQ, c) == EOF ) // Put Debugger's breakpoint here  34            break;                  // ..to see chars going into 'inQ'  35    }    Si no sabe que un break termina un FOR, está MUY MAL EN C: ¡ABANDONE!    Ahora que se llenó inQ, se hace un ciclo indefinido, extrayendo uno a uno sus elementos  y transfiriéndolos a la otra cola.    Es exactamente lo que haría una Rutina de Comunicaciones: En la ISR del RCV, se lee el  dato recibido y se lo almacena en la Cola de Entrada; main encuentra que hay datos en  esa Cola, los saca, los procesa si es necesario y los almacena en la Cola de salida. A  lo  mejor  habilita  las  interrupciones  de  salida  EN  ALGÚN  MOMENTO  APROPIADO.  Cuando  la  ISR de RCV toma el control, saca el próximo valor de la cola de salida y lo transmite.    En este ejemplo, el ciclo terminará en la línea 42, porque la cola de salida se llena  antes de que se vacíe la de entrada, por los tamaños que decidí colocarles.    37    while( 1 ) {  38        cw = deQue( inQ );  39        if( cw == EOF )  40            break;  276 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 41        if( enQue( outQ, cw ) == EOF )  // Will break here because...  42            break;                      // ..outQ is smaller than inQ  43    }  44    Wait( 0 );    Para  asignar  el  'deQue'  sobre  una  variable,  SE  NECESITA  QUE  LA  VARIABLE  SEA  DE  TIPO  WORD. ¿Por qué?    Si  usted  estudió  el  getchar  del  'C',  tiene  que  poder  saberlo.  Si  no,  USTED  SABE  MUY  POCO DE C. ESTUDIE!!!    Esta es la razón por la cual la Macro enQue hace un CAST a BYTE del valor que se le  pasa para encolar...    Revise si el cuerpo del while puede escribirse así:    if( enQue( outQ, deQue( inQ ) ) == EOF )      break;    ¿CÓMO FUNCIONARÍA? O ¿POR QUÉ NO?    Tiene que analizar cuando deQue devuelve EOF, cuando lo hace enQue, cuando no lo hace  ninguno y cuando lo hacen los dos.  63) CHAT POR INTERRUPCIONES, USANDO COLAS El  último  ejemplo  es  un  CHAT  por  interrupciones,  que  muestra  la  interrelación  entre  todo:  comunicaciones  seriales,  transmitiendo  y  recibiendo  por  interrupciones,  con  uso  de colas. Además, se esquematiza la forma de programar separando lo más que se puedan,  Políticas y Mecanismos, TAL COMO TIENE QUE HACERSE SIEMPRE.    No oyó hablar nunca de Politics and Mechanisms? No le enseñaron PROGRAMACIÓN tampoco.    En este ejercicio aún se pueden ocultar más algunas variables, como kbhit; pero no lo  hice para resaltar los parecidos con el 'kbhit' que usted ha debido estudiar.    ["Labs‐C\Lab3\SciComm\6ChatInt.c"]  01 // ******************************************************************  02 // Program 6ChatInt.c: Send/Receive text to/from PC, Full Interrupt  03 // Luis G. Uribe C., D15D2013    04 #include "6ChatInt_.h"         // include peripheral declarations    05 byte *Prompt  = "\a\r\n\n0_Envíeme un texto, por favor <;‐) ";    06 defineQue( inQ,  48  );        // DEFINE Que 'inQ',  48 bytes    07 // ******************************************************************  08 void main ( void )                                             /*()*/  09 {   /* ChatInt.c */    277 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 10    SysInit;            // COP_Disable  11    initQue( inQ );  12    EnableIRQ;  13    XmtRcvActivate;     // Enable Xmt & Rcv, on chip DEVICES; 9600bps    14 // ==================================================================  15 // Transmit your Prompt: Enque at once full 'Prompt' Table for Xmt:    16    SetupXmtBlk( Prompt );  // Txt Ptr; XmtControl=XMTMSG_S; XmtIntEn  17    CpuIntEn;               // <<<REMEMBER: GLOBAL ENABLE INTs  18    RcvIntEn;               // CommIntEn?{RCVSTATREG;RCVBUF;RcvIntEn;}  19    Forever  20        if( kbhit ) {       // LOCAL (DEMOQE128) IRQ button depressed?  21            SetupXmtBlk( Prompt );  22            kbhit = 0;  23        }  24    Endforever    25    Wait( 0 );    26 }   /* main() */  27 // ==================================================================  28 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  29 {  30    kbhit = 1;  31    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK,IRQSC: ACK IRQ  32 }                              // .. Interrupt (Rearm IRQ Interrupts)    33 // ==================================================================  34 interrupt VectorNumber_Vsci1tx void XmtISR ( void )            /*()*/  35 {  // NOTE: putchar clears RCVRDY.bit. Very clever...  36    word c;     // MUST be 'word' to hold both Chars And EOF (0xFFFF)    37    switch( XmtControl ) {  38      case XMTMSG_S:  39        if( ! putchar( *XmtPtr++ ) ) {  // 'C' Strings end at '\0'  40            XmtControl = XMTDEFAULT_S;  // ..(Zstrings: ASCIZ)  41        }  42      break;    43      case XMTDEFAULT_S:  44        if( ( c = deQue( inQ ) ) !=  EOF ) {  // Did something arrive?  45            putchar( c );       // 'C' Strings end at '\0'  46        }else{  47            XmtIntDsb;  48        }  49      break;  50    }  51 }    52 // ==================================================================  53 interrupt VectorNumber_Vsci1rx void RcvISR ( void )            /*()*/  278 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 54 {  // NOTE: getchar clears RCVRDY.bit. Very clever...  55    byte c;    56    c = getchar();  57    if( enQue( inQ, c ) != EOF ) {  58        XmtIntEn;  59    }  60 }  ["Labs‐C\Lab3\SciComm\6ChatInt_.h"]  01 #ifndef _6CHATINT_H_INCLUDED  02 #define _6CHATINT_H_INCLUDED  03 // ******************************************************************  04 // 6ChatInt_.h: Send/Receive text to/from PC, Full Interrupt  05 // Luis G. Uribe C., D15D2013    06 // ******************************************************************  07 // Include Files  08 #include <hidef.h>             // for EnableInterrupts macro  09 #include "derivative.h"        // include peripheral declarations    10 #include "SciComm.h"           // SCI Comm. Support  11 #include "Que.h"               // Que Support  12 #include "4InputPushButtons_.h"// Inputs4(0000b3b2b1b0)=PTA2,3;PTD2,3  13 // ==================================================================  14 //  GLOBAL VARIABLES    15 typedef  enum { XMTDEFAULT_S, XMTMSG_S } XmtControls;    16 XmtControls XmtControl = XMTDEFAULT_S;    // May change this to bits.    17 byte *XmtPtr;                  // char pointer of next data to Xmt  18 byte  kbhit;    19 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  20 // >>> ALWAYS include SysInit  21 //     Cfr. 02MC9S08QE128RM(ReferenceManual)‐U.pdf, pag 101  22 #define    COP_Disable 0x42  23 #define    SysInit     SOPT1 = COP_Disable     /* System Options 1 */    24 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  25 // Enable IRQ pin  26 #define    EnableIRQ   IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;\  27            IRQSC_IRQIE = 1     /* IRQ pin, IntEn */    28 // ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐  29 #define    SetupXmtBlk(p)  XmtPtr = (p);   /* Point to Prompt */    \  30                XmtControl = XMTMSG_S;                               \  31                XmtIntEn        /* XmtISR will send the Prompt */    32 #endif // _6CHATINT_H_INCLUDED    ["Labs‐C\Lab3\SciComm\4InputPushButtons_.h"]  279 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 01 #ifndef _4INPUTPUSHBUTTONS_H_INCLUDED  02 #define _4INPUTPUSHBUTTONS_H_INCLUDED  03 // ******************************************************************  04 // 4InputPushButtons_.h: Inputs4(0000b3b2b1b0)=PTA2,3;PTD2,3  05 // Luis G. Uribe C., D15D2013    06 // ******************************************************************  07 // Include Files  08 #include <hidef.h>             // for EnableInterrupts macro  09 #include "derivative.h"        // include peripheral declarations  10 // ==================================================================  11 //  Defined Functionality  12 #define Setup4PushBtns  PTAPE_PTAPE3 = PTAPE_PTAPE2 = PTDPE_PTDPE3  \  13                         =  PTDPE_PTDPE2 = 1   /* Pull ups enable */    14 #define get4Inputs() Inp3   = PTAD_PTAD2; Inp2 = PTAD_PTAD3;        \  15                      Inp1   = PTDD_PTDD2; Inp0 = PTDD_PTDD3;        \  16                      Inp4_7 = ~0;  Inputs4 = ~Inputs4    17 // ==================================================================  18 //  GLOBAL VARIABLES. IRQ pin Enabled in "6ChatInt_.h"  19 typedef union {  20   byte Byte;  21   struct {  22     byte INPUTS40 :1;          // individual bits MUST be UNSIGNED  23     byte INPUTS41 :1;  24     byte INPUTS42 :1;  25     byte INPUTS43 :1;  26     byte spare    :4;  27   } Bits;  28 } _INPUTS4;    29 extern  volatile _INPUTS4 INPUTS4;  30 #define Inputs4  INPUTS4.Byte    31 #define Inp0   INPUTS4.Bits.INPUTS40  32 #define Inp1   INPUTS4.Bits.INPUTS41  33 #define Inp2   INPUTS4.Bits.INPUTS42  34 #define Inp3   INPUTS4.Bits.INPUTS43  35 #define Inp4_7 INPUTS4.Bits.spare    36 #define INP0   1U  37 #define INP1   2U  38 #define INP2   4U  39 #define INP3   8U    40 #endif // _4INPUTPUSHBUTTONS_H_INCLUDED    COMENTARIOS a ["Labs-C\Lab3\SciComm\6ChatInt.c"]: En  el  archivo  "6ChatInt_.h"  se  definen  cosas  de  bajo  nivel,  relacionadas  con  periféricos, como veremos abajo:  280 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   04 #include "6ChatInt_.h"         // include peripheral declarations    El mensaje con el que queremos avisarle al usuario del PC que nos envíe un texto:    05 byte *Prompt  = "\a\r\n\n0_Envíeme un texto, por favor <;‐) ";    Se define una cola, inQ, de 48 posiciones:  06 defineQue( inQ,  48  );        // DEFINE Que 'inQ',  48 bytes    08 void main ( void )                                             /*()*/  09 {   /* ChatInt.c */    Las  líneas  10  a  la  13  son  auto  explicativas  con  solo  leerlas;  dicen  qué  se  hará,  NO  cómo se hará (Politics; Mechanisms seen later)    10    SysInit;            // COP_Disable  11    initQue( inQ );  12    EnableIRQ;  13    XmtRcvActivate;     // Enable Xmt & Rcv, on chip DEVICES; 9600bps    Ahora se TRANSMITE EL PROMPT, que es del tipo ASCIZ, trasmitiéndolo todo de una sola  vez, por interrupciones:    15 // Transmit your Prompt: Enque at once full 'Prompt' Table for Xmt:  16    SetupXmtBlk( Prompt );  // Txt Ptr; XmtControl=XMTMSG_S; XmtIntEn  17    CpuIntEn;               // <<<REMEMBER: GLOBAL ENABLE INTs    Se habilitan las interrupciones de recepción, para entrar al ciclo infinito. kbhit es  una variable que se activa (1) por la ISR del IRQ; es decir, cada vez que se oprime el  botón de IRQ, se transmite de nuevo el PROMPT. El ECHO se maneja directamente entre las  ISR de RCV y XMT:    18    RcvIntEn;               // CommIntEn?{RCVSTATREG;RCVBUF;RcvIntEn;}  19    Forever  20        if( kbhit ) {       // LOCAL (DEMOQE128) IRQ button depressed?  21            SetupXmtBlk( Prompt );  22            kbhit = 0;  23        }  24    Endforever  26 }   /* main() */      La  IRQISR  es  muy  simple:  cargar  el  valor  de  ACTIVO  para  la  variable  kbhit,  y  el  ACK  correspondiente al IRQ:    28 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  29 {  30    kbhit = 1;  31    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK,IRQSC: ACK IRQ  32 }                              // .. Interrupt (Rearm IRQ Interrupts)    281 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 La XmtISR:    34 interrupt VectorNumber_Vsci1tx void XmtISR ( void )            /*()*/  35 {  // NOTE: putchar clears RCVRDY.bit. Very clever...  36    word c;     // MUST be 'word' to hold both Chars And EOF (0xFFFF)    Hace  dos  actividades  diferentes:  XMTMSG_S  (el  echo  de  lo  recibido)  y  XMTDEFAULT_S  (transmisión del PROMPT:    El case XMTMSG_S: es igual al último que vimos, cuando transmitimos un texto de tipo  ASCIZ; al final se cambia la variable de Estado, XmtControl a su función de ECHO, que  es la estándar:    37    switch( XmtControl ) {  38      case XMTMSG_S:  39        if( ! putchar( *XmtPtr++ ) ) {  // 'C' Strings end at '\0'  40            XmtControl = XMTDEFAULT_S;  // ..(Zstrings: ASCIZ)  41        }  42      break;    El case XMTDEFAULT_S es también como ya hemos visto: si lo que se desencola, cuando hay  una  interrupción  para Transmitir,  no  es  EOF,  quiere  decir  que  sí  hay  caracteres  para  transmitirle al PC, y así se hace con putchar que, como ya lo sabemos, hace el debido  ACK  al  XMT.  Si  en  la  cola  no  hay  nada  para  transmitir,  se  auto  deshabilitan  las  interrupciones del XMT:    43      case XMTDEFAULT_S:  44        if( ( c = deQue( inQ ) ) !=  EOF ) {  // Did something arrive?  45            putchar( c );       // 'C' Strings end at '\0'  46        }else{  47            XmtIntDsb;  48        }  49      break;  50    }  51 }    La RcvISR es muy sencilla también, empleando esta librería de comunicaciones seriales:    52 // ==================================================================  53 interrupt VectorNumber_Vsci1rx void RcvISR ( void )            /*()*/  54 {  // NOTE: getchar clears RCVRDY.bit. Very clever...  55    byte c;    56    c = getchar();  57    if( enQue( inQ, c ) != EOF ) {  58        XmtIntEn;  59    }  60 }    COMENTARIOS a ["Labs-C\Lab3\SciComm\6ChatInt_.h"]: El 6ChatInt_.h consta de una parte de inicialización:  282 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08   07 // Include Files  08 #include <hidef.h>             // for EnableInterrupts macro  09 #include "derivative.h"        // include peripheral declarations    10 #include "SciComm.h"           // SCI Comm. Support  11 #include "Que.h"               // Que Support  12 #include "4InputPushButtons_.h"// Inputs4(0000b3b2b1b0)=PTA2,3;PTD2,3    La definición de las VARIABLES GLOBALES:    15 typedef  enum { XMTDEFAULT_S, XMTMSG_S } XmtControls;  16 XmtControls XmtControl = XMTDEFAULT_S;    // May change this to bits.  17 byte *XmtPtr;                  // char pointer of next data to Xmt  18 byte  kbhit;    La  definición  de  la  Macro  que  realiza  la  inicialización  del  sistema  Macro  que  usted  debería UTILIZAR SIEMPRE, de ahora en adelante)    20 // >>> ALWAYS include SysInit  22 #define    COP_Disable 0x42  23 #define    SysInit     SOPT1 = COP_Disable     /* System Options 1 */    Se define una Macro para habilitar el IRQ; observe que necesita el '\' porque tiene más  de una línea    26 #define    EnableIRQ   IRQSC = IRQSC_IRQPE_MASK | IRQSC_IRQACK_MASK;\  27            IRQSC_IRQIE = 1     /* IRQ pin, IntEn */    Se define la Macro SetupXmtBlk, que inicializa y transmite el Prompt. Recuerde que hay  una  pequeña  máquina  de  estados,  controlada  por  la  variable  XmtControl;  aquí  se  inicializa esa FSM con XMTMSG_S, que hace que la XmtISR transmita el mensaje ASCIZ:      29 #define    SetupXmtBlk(p)  XmtPtr = (p);   /* Point to Prompt */    \  30                XmtControl = XMTMSG_S;                               \  31                XmtIntEn        /* XmtISR will send the Prompt */    COMENTARIOS a ["Labs-C\Lab3\SciComm\4InputPushButtons_.h"]: Define las siguientes funcionalidades: Setup4PushBtns y get4Inputs().    Setup4PushBtns  les  activa  todas  las  resistencias  de  Pull  Up  a  los  botones  de  entrada  (un 1 las activas). No hay que activar esos terminales para entrada, porque después de  POR, todos los pines digitales están activos como ENTRADAS.    12 #define Setup4PushBtns  PTAPE_PTAPE3 = PTAPE_PTAPE2 = PTDPE_PTDPE3  \  13                         =  PTDPE_PTDPE2 = 1   /* Pull ups enable */    get4Inputs() LEE los 4 botones, uno en cada bit de la variable definida como de tipo  _INPUTS4.  Después  se  colocan  en  valor  inicial  los  4  bits  que  no  se  usan  (hay  solo  4  botones) y por último SE NIEGAN LAS ENTRADA, tomándolas de la variable y almacenándolas  283 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 de  nuevo  allí.  Se  niegan  porque,  en  la  forma  como  la  gente  de  PE  Micro  conectó  los  botones, cada vez que uno de ellos se ACTIVA, produce un CERO, lo cual podría resultar  poco natural:    14 #define get4Inputs() Inp3   = PTAD_PTAD2; Inp2 = PTAD_PTAD3;        \  15                      Inp1   = PTDD_PTDD2; Inp0 = PTDD_PTDD3;        \  16                      Inp4_7 = ~0;  Inputs4 = ~Inputs4    Se establece una estructura con los 4 bits para los botones, y el resto se define como  'spare'. Se colocan en UNION con un Byte, para facilitar, por ejemplo, el negar TODOS  los bits de un golpe Inputs4=~Inputs4)    19 typedef union {  20   byte Byte;  21   struct {  22     byte INPUTS40 :1;          // individual bits MUST be UNSIGNED  23     byte INPUTS41 :1;  24     byte INPUTS42 :1;  25     byte INPUTS43 :1;  26     byte spare    :4;  27   } Bits;  28 } _INPUTS4;    Se  define  la  variable  INPUTS4,  de  tipo  _INPUTS4  (el  typedef),  volatile  (para  evitar  Optimización  del  compilador)  y  extern,  para  que  sea  visible  desde  el  código  de  los  demás archivos:    29 extern  volatile _INPUTS4 INPUTS4;  30 #define Inputs4  INPUTS4.Byte    ¿NO SABE NADA DE EXTERN? UNION? NO SABE C.    Finalmente, para facilitar el uso de los bits de la variable INPUTS4, de tipo _INPUTS4  que, por ejemplo, para designar el Bit0 requeriría escribir: INPUTS4.Bits.INPUTS40.    Con las definiciones cada bit es Inp0, etc. Mucho más sencillo    31 #define Inp0   INPUTS4.Bits.INPUTS40  32 #define Inp1   INPUTS4.Bits.INPUTS41  33 #define Inp2   INPUTS4.Bits.INPUTS42  34 #define Inp3   INPUTS4.Bits.INPUTS43  35 #define Inp4_7 INPUTS4.Bits.spare    64) BIG CHAT, INTERRUPCIONES, COLAS Un verdadero chat envía mensajes diferentes ¡de lado y lado! Pero el sistema de botones  del  DEMOQE128  resulta  algo  inflexible  para  generar  texto.  Este  ejercicio  envía  un  Prompt, igual que el programa anterior, pero los mensajes no fluyen solamente desde el  PC, sino que tocando cada uno de los 4 botones, se envían hasta cuatro mensajes al PC,  dándole un aspecto un poco más realista al chat.    ["Labs‐C\Lab3\SciComm\6ChatInt‐4.c"]  284 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 01 // ******************************************************************  02 // Program 6ChatInt‐4.c: Send/Receive text to/from PC, Full Interrupt  03 // Luis G. Uribe C., L16D2013 (4 DEMOQE128 text sources)    04 #include "6ChatInt_.h"         // include peripheral declarations    05 byte *Prompt  = "\a\r\n\n0_Envíeme un texto, por favor <;‐) ";  06 byte *Prompts[ 4 ] = {  07        { "\a\r\n\n1_Me encanta poder hablarle hoy___ " },  08        { "\a\r\n\n2_Cómo va todo, finalizando el 13? " },  09        { "\a\r\n\n3_Le deseo suerte para el año 2014 " },  10        { "\a\r\n\n4_Que tenga buena nota en Arqui‐I! " },  11    };    12 defineQue( inQ,  48  );        // DEFINE Que 'inQ',  48 bytes    13 // ******************************************************************  14 void main ( void )                                             /*()*/  15 {   /* ChatInt.c */  16    SysInit;                    // COP_Disable  17    initQue( inQ );  18    EnableIRQ;  19    XmtRcvActivate;     // Enable Xmt & Rcv, on chip DEVICES; 9600bps  20    Setup4PushBtns;     // Activate 4 DEMOQE128 input push buttons    21 // ==================================================================  22 // Transmit your Prompt: Enque at once full 'Prompt' Table for Xmt:  23    SetupXmtBlk( Prompt );  // Txt Ptr; XmtControl=XMTMSG_S; XmtIntEn  24    CpuIntEn;               // <<<REMEMBER: GLOBAL ENABLE INTs  25    RcvIntEn;               // CommIntEn?{RCVSTATREG;RCVBUF;RcvIntEn;}    26    Forever  27        if( kbhit ) {       // LOCAL (DEMOQE128) IRQ button depressed?  28            switch( Inputs4 ) { // 1:1; 2:2; 4:3; 8:4  29              case 1:  30              case 2:  31              break;    32              case 4:  33                Inputs4 = 3;  34              break;    35              case 8:  36                Inputs4 = 4;  37              break;    38              default:  39                Inputs4 = 15;  40              break;  41            }  42            SetupXmtBlk( Inputs4 == 15 ? Prompt : Prompts[Inputs4‐1]);  43            kbhit = 0;  44        }  45    Endforever  285 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 46    Wait( 0 );  47 }   /* main() */       48 // ==================================================================  49 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  50 {  51    get4Inputs();               // Load Inputs4  52    kbhit = 1;  53    IRQSC_IRQACK = 1;           // BSET IRQSC_IRQACK,IRQSC: ACK IRQ  54 }                              // .. Interrupt (Rearm IRQ Interrupts)       55 // ==================================================================  56 interrupt VectorNumber_Vsci1tx void XmtISR ( void )            /*()*/  57 {  // NOTE: putchar clears RCVRDY.bit. Very clever...  58    word c;     // MUST be 'word' to hold both Chars And EOF (0xFFFF)       59    switch( XmtControl ) {  60      case XMTMSG_S:  61        if( ! putchar( *XmtPtr++ ) ) {  // 'C' Strings end at '\0'  62            XmtControl = XMTDEFAULT_S;  // ..(Zstrings: ASCIZ)  63        }  64      break;       65      case XMTDEFAULT_S:  66        if( ( c = deQue( inQ ) ) !=  EOF ) {  // Did something arrive?  67            putchar( c );       // 'C' Strings end at '\0'  68        }else{  69            XmtIntDsb;  70        }  71      break;  72    }  73 }       74 // ==================================================================  75 interrupt VectorNumber_Vsci1rx void RcvISR ( void )            /*()*/  76 {  // NOTE: getchar clears RCVRDY.bit. Very clever...  77    byte c;       78    c = getchar();  79    if( enQue( inQ, c ) != EOF ) {  80        XmtIntEn;  81    }  82 }    COMENTARIOS a ["Labs-C\Lab3\SciComm\6ChatInt-4.c"]: Este programa es tan parecido al anterior, y usted conoce tanto a estas alturas, que  los comentarios serán muy breves:    La definición del Prompt es idéntica al chat anterior; los 4 prompts están colocados en  una matriz que contiene 4 mensajes:    286 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 06 byte *Prompts[ 4 ] = {  07        { "\a\r\n\n1_Me encanta poder hablarle hoy___ " },  08        { "\a\r\n\n2_Cómo va todo, finalizando el 13? " },  09        { "\a\r\n\n3_Le deseo suerte para el año 2014 " },  10        { "\a\r\n\n4_Que tenga buena nota en Arqui‐I! " },  11    };    Imagino que conoce TODOS los símbolos... Qué es '\a'?  Continúa  todo  igual,  hasta  enviar  el  Prompt.  El  ciclo  infinito  se  diferencia  del  anterior  porque  contiene  un  Switch  con  4  Cases,  uno  para  cada  interruptor  de  la  tarjeta.  kbhit  funciona  igual  que  antes,  el  programa  principal  lo  coloca  en  0,  y  la  IRQISR en 1.    Observe bien que al oprimir cada interruptor se generan los números 1, 2, 4 y 8, pero  hemos  codificado  los  Cases  para  que  se  generen  los  números  0,  1,  2  y  3,  que  corresponden a los subíndices que identifican los 4 mensajes en el arreglo. Así que hay  que  hay  que  relacionar  los  números  obtenidos  con  los deseados,  de  la  siguiente  forma  (Obtenido:Generado) 1:0; 2:1; 4:2; 8:3    Pero  nótese  que  el  '0'  NO  CORRESPONDE  a  oprimir  ningún  botón;  siempre  se  está  generando!  Así  que  la  variable  'Inputs4',  que  representa  los  botones,  tendrá  un  Cero  SIEMPRE  que  no  se  esté  oprimiendo  nada.  Mala  selección  para  el  Switch,  porque  no  permite distinguir entre NO selección, y selección del número 0.    Por  eso  escogí  generar  estos  números,  en  lugar  de  los  indicados  dos  párrafos  atrás"  (Obtenido:Generado)  1:1;  2:2;  4:3;  8:4;  ahora  sí  es  fácil  saber  si  se  oprimió  algún  interruptor;  en  caso  contrario,  la  variable  Inputs4  vale  0.  Y,  al  usar  Inputs4  como  índice,  se  le  resta  un  uno,  para  convertir  estos  valores  intermedios,  en  los  que  en  realidad deseamos. Cualquier valor NO definido (default) coloca un '15' en la variable,  simulando  que  se  oprimieron  TODOS  los  interruptores.  Si  Inputs4  vale  15,  se envía  el  PROMPT; para los otros 4 valores posibles, se le resta un uno a Inputs4, y se lo usa  como  índice  del  Arreglo,  para  escoger  uno  de  los  mensajes,  y  transmitirlo  empleando  'SetupXmtBlk',  que  como  vimos  antes,  inicializa  el  sistema  de  transmisión  y  efectivamente hace que se transmita el Bloque, con el mensaje deseado.    26    Forever  27        if( kbhit ) {       // LOCAL (DEMOQE128) IRQ button depressed?  28            switch( Inputs4 ) { // 1:1; 2:2; 4:3; 8:4  29              case 1:  30              case 2:  31              break;    32              case 4:  33                Inputs4 = 3;  34              break;    35              case 8:  36                Inputs4 = 4;  37              break;    38              default:  39                Inputs4 = 15;  40              break;  287 I N G . L U I S G . U R I B E C COMPUTER ARCHITECTURE: THE MC9S08 41            }  42            SetupXmtBlk( Inputs4 == 15 ? Prompt : Prompts[Inputs4‐1]);  43            kbhit = 0;  44        }  45    Endforever    La rutina IRQISR es casi exacta a la del ejercicio previo; se diferencia en que se leen  y adecúan los interruptores, usando la Macro get4Inputs, ya analizada. El resto sigue  igual.    49 interrupt VectorNumber_Virq void IRQISR ( void )               /*()*/  50 {  51    get4Inputs();               // Load Inputs4    Las  dos  rutinas  finales,  XmtISR  y  RcvISR  son  exactamente  iguales  a  las  del ejercicio  anterior.  FIN DE LA OBRA   Pronto  incluiré  más  ejercicios  en  Leguaje  "C",  como  Manejo  y  Optimización  de  Tablas,  Implementación de FMS y muchos otros temas interesantes.    mailto:[email protected]      288
Copyright © 2024 DOKUMEN.SITE Inc.