martes, 11 de diciembre de 2012
miércoles, 31 de octubre de 2012
domingo, 14 de octubre de 2012
Lenguaje de Maquina
Pero primero tuvo que desarrollarse en la computadora la capacidad de almacenar un programa, el modelo de Von Newman cubrió esa necesidad. A partir de allí ya hizo su aparición el primer lenguaje: el Lenguaje de Máquina.
Programa en 8086
El siguiente es el código completo, en lenguaje de máquina 8086 del algoritmo "Torre de Hanoi".
|
0000000000000000B81100B91200BA13 00BB0400E80500B84C00CD2183FB0074 52525150534B8916060189CA8B0E0601 E8E9FF5B58595AA30201890E04018916 0601E82F004B891E00018B1E0201E823 008B1E0601E81C008B1E0001A102018B 0E04018B160601A3020189C88B0E0201 E8A9FFC3B40289DA83C230CD21BA2000 CD21C3 |
El código fuente puede verse aquí
El lenguaje de máquina se programa por medio de señales eléctricas, a través de interruptores o botones diréctamente en posiciones de memoria de la computadora. Aún hoy se programa en este lenguaje, en proyectos con microcontroladores o microprocesadores.
Programación en MS-DOS Debug (Video)
Programación de Mircrocontrolador (Video)
sábado, 13 de octubre de 2012
Hanoi en Assembler A86
El punto de partida del peregrinaje de Hanoi fue la implementación del algoritmo de las Torres de Hanoi en lenguaje de máquina del microprocesador 6800 de Motorola. Para "programarlo", tuve que escribir un pseudo código, pasarlo a lenguaje ensamblador de 6800, posteriormente “ensamblarlo” en el papel, instrucción por instrucción, para luego ingresarlo por medio de un teclado hexadecimal, directamente en la memoria del MEK-6800.

Fig. 1 Teclado del MEK-6800
En memoria de ese acontecimiento, hace unos años repetí la experiencia, pero en esa ocasión, con una PC y por medio del Debug de DOS. Hoy, tanto el código fuente de la versión "light" como la versión "de Luxe" se perdieron en alguna de las tantas migraciones de PC. Pero, el artículo que publiqué en este blog aun permanece, de manera que es lo que usaré como punto de partida para la realizar esta nueva versión en A86.
Vean el Proceso Completo
El video muestra el código fuente en A86, el proceso de compilación y su puesta en funcionamiento.
El A86 es un Macroassembler, de manera que el código que usé para ensamblar con Debug requerirá varias modificaciones para que el A86 lo acepte y me genere un ejecutable.
El Código Original
El siguiente listado corresponde al código tal como está publicado en este blog: Reursion and Machine Language.
Listado 1
Si observamos en detalle el Listado 1, notaremos la aparente ausencia de "variables", tampoco se observan "procedimientos" o "subrutinas". En realidad, tanto las variables como las subrutinas están ahí, solo que no tienen nombre, son solo posiciones en la memoria.
La primera tarea consiste en asignar nombres "simbólicos" a esas posiciones de memoria, para convencer al A86 de que se trata de un programa en lenguaje ensamblador y no de una lista de compras. Para eso, primero vamos a ensamblar el código (con Debug), para determinar donde están las posiciones de memoria a las que hacemos referencia.
Compilación con Debug
E:\TC\A86>debug <hanoi.txt
El Debug genera un archivo ejecutable .COM
Pruebo el Funcionamiento
E:\TC\A86>hanoi
1 A B 2 A C 1 B C 3 A B 1 C A 2 C B 1 A B 4 A C 1 B C 2 B A 1 C A 3 B C 1 A B 2
A C 1 B C
Funciona!
Para ahorrar tiempo
- Genero un listado con el debug
- E:\TC\A86>debug hanoi.com >prog.txt
- Luego escribo cinco veces u y luego q.
Obtengo el siguiente listado
Listado 2
Procesamiento con Excel
Esta es una de las aplicaciones prácticas de Excel, nos permite trabajar con texto en forma tabular y aplicar funciones para agregarle o quitarle elementos (también es muy útil para trabajar con páginas web).

- Separamos las direcciones de las instrucciones (B y D).
- Insertamos una columna (C).
- Asignamos Nombres a las direcciones representativas.
- Copiamos las columas C y D en un archivo de texto con la extensión .8 que es la extensión de los programas fuentes en A86.
Listado A86 Coompleto
Listado 3 Programa Completo en A86
Compilación y Prueba
A86 macro assembler, V3.72 Copyright 1994 Eric Isaacson
Source:
HANOI.8
Object: HANOI.COM
Symbols: HANOI.SYM
E:\TC\A86>HANOI
1 A C
2 A B
1 C B
3 A C
1 B A
2 B C
1 A C
El código funciona perfectamente e incluye la generación del carácter "\n" o (0D0A) provisto por la subrutina NL, para mejorar la salida por pantalla. El A86 genera un archivo .COM y no requiere enlazarlo. Este A86 en particular lo bajé del sitio de su autor. Cuando vivía en USA, yo había comprado un A86 (shareware) que generaba .OBJ además de .COM, pero es posible que su autor haya modificado su esquema de distribución.
¡Que tengan un gran día!
Sergio Otaño
jueves, 7 de abril de 2011
Experimentos con Microprocesadores
Fig. 1 Entrenador Heathkit ET-3400Debido a esta versatilidad es que en las universidades de todo el mundo, en especial en carreras de ingeniería, aún hoy se utilizan kits similares para el aprendizaje acabado de técnicas digitales y programación de bajo nivel. El Kit de Motorola consistía básicamente de dos placas (bastante grandes) una de las cuales contenía el uP 6800, las memorias ROM y RAM, una PIA (chip de bus paralelo) y una ACIA (chip de interface serie); la otra placa contenía un robusto conjunto de teclas 16 de valores hexadecimales y 8 de control y el display de 7 segmentos con 6 dígitos.
Ya expliqué en el artículo Recursion and Machine Language de este blog, cómo implementar la recursividad en lenguaje de máquina. Entonces: ¿Cuál es el objetivo?
Fig. 2 El Kit MEK6800 de Motorola ¡El objetivo es dar pautas simples para poder reproducir estas actividades!
Se preguntarán cómo pueden realizar las experiencias sin contar con el Kit entrenador MEK-6800 (que Motorola denominó Exorcicer, aludiendo humorísticamente a la película “El Exorcista”). Pues bien, el primer paso lo podemos realizar empleando un EMULADOR que corre bajo Windows. Claro está que con el emulador no tendremos la posibilidad de realizar los experimentos de electrónica digital, pero podremos tener un entendimiento completo de la estructura de un uP. A partir de ese conocimiento, pueden acceder al trabajo con Microcontroladores que tienen todas las funciones mencionadas en un solo chip, por lo que son muy económicos y fáciles de usar. El emulador que conseguí está basado en otro Kit que desarrolló la firma Heathkit, se trata del ET-3400 (en este caso no remite a la película ET sino que prosaicamente corresponde a ENTRENADOR).
Fig. 3 Emulador del ET-3400
Primera Experiencia
Al iniciar nuestro EMULADOR, el programa, replica el funcionamiento del ET-3400, muestra en el display de 7 segmentos el mensaje "CPU UP" que indica que el sistema esta listo para recibir instrucciones. El primer botón que vamos a oprimir es RESET. Este botón nos será útil cada vez que el sistema se cuelque porque hay alguna falla en el código que ingresamos.
Operaciones Con Registros y memoria
Para comenzar, vamos a realizar una serie de prácticas para familiarizarnos con el EMULADOR, y emplearemos las siguientes instrucciones: LDAA, STAA, LDAB, STAB, TAB y HLT. Si bien el uP 6800 dispone de diversos modos de direccionamiento, vamos a dejar ese tema para una discusión futura y nos limitaremos al modo de direccionamiento inmediato, es decir que luego de la instrucción pondremos un valor literal.
La instrucción LDAA permite cargar un valor en el ACUMULADOR A. Buscamos en la tabla de instrucciones que muestra que esta instrucción con direccionamiento inmediato tiene el código de máquina 86 exadecimal. También vamos a poner la instrucción HLT (halt o detener) que ordena al uP a detenerse. HLT que corresponde al código de máquina 3El.
Ahí vamos
Cargar el Acumulador A con un dato
1) Hacemos clic en el botón EXAM (letra E) e ingresamos 0000 (cuatro ceros), el emulador nos mostrará el contenido de la posición de memoria 0000. ¡SE PONE NEGRO EL DISPLAY!
2) Hacemos clic en el botón CHANGE (letra C).
3) Ingresamos el 86 que es el código de máquina de la instrucción LDAA.
4) Hacemos clic en el botón FORWARD (letra F) ahora estamos en 0001.
5) Hacemos clic en el botón CHANGE (letra C).
6) Ingresamos el valor FF (es el que queremos cargar en el Acumulador A).
7) Hacemos clic en el botón FORWARD (letra F) ahora estamos en 0003.
8) Hacemos clic en el botón CHANGE.
9) Ingresamos 3E que es el código de máquina de la instrucción HLT.
10) Hacemos clic en el botón DO (la letra D) que es la orden para ejecutar el programa.
11) Ingresamos 0000 que es la dirección de inicio de nuestro programa, y ¡voilá!
PERO...
Allí observamos en el display el valor "Acca FF"
¡El programa cumplió su cometido que era cargar el acumulador A con el valor FF!
Antes de despedirme me gustaría agradecer a Ricardo, por toda la información que me envió sobre la TK85 y sobre la revista Lúpin, hoy Ricardo es un ingeniero electrónico, pero recuerda la inspiración que le aportó la "revistucha". Ya voy a publicar algo sobre la TK que también inspiró a muchos en los inicios de la era informática.Continuará...
martes, 20 de enero de 2009
Controlar Máquinas y Aparatos con La PC
La PC nos trae un mundo lleno de posibilidades, en los que podemos ser héroes de combate y salir victoriosos de encuentros con una docena de pandilleros peligrosos, pilotear aviones jet, conducir un Formula 1, y aún viajar a planetas lejanos.
¿Qué tiene eso que ver con la realidad?
¡Mucho! Un 11 de Septiembre nos enteramos que unos terroristas habían demolido dos torres, casi inexpugnables, en el país más seguro del mundo.... y se habían entrenado con un simulador de vuelo de una PC...
Fig. 1 DTR está Desactivado
Pero mas allá de discutir las implicaciones filosóficas de los efectos reales de un entrenamiento virtual informatizado, en este artículo propongo que realicemos un ejercicio concreto que nos permita operar sobre la realidad con nuestra PC, por ejemplo, encender un velador u otro aparato electrodoméstico. En este artículo les voy a mostrar un método muy FÁCIL de utilizar el Puerto Serie de la PC para encender y apagar un aparato.
Fig. 2 DTR está Activado
En otro artículo, les presenté el programa ServHTTP.exe que desarrollé para controlar “aparatos” a través de Internet (ver Control Remoto por Internet). El objetivo de ese artículo es precisamente demostrar prácticamente cómo se puede controlar un dispositivo por medio de la web con costo cero (pueden bajar una copia del programa aqui: Programa Servhttp.zip). Muchos lectores consultaron sobre los posibles usos del programa y algunos estaban muy interesados en ir un paso más adelante y conectar un dispositivo y controlarlo desde la PC.
Una de las dificultades con el Servidor HTTP es que, para controlar un aparato, es necesario agregar un microcontrolador u otro dispositivo que “interprete” la señal RS232 y active, desactive o controle el aparato. Por esta razón, me parece oportuno desarrollar una serie de artículos que conduzcan, paso a paso, en esa dirección, ahora si, con aparatos reales conectados en nuestra computadora. En el ejemplo que aquí les presento, eliminamos todas esas dificultades y trabajamos directamente con una señal de control del propio puerto.
El Hardware
Con unos pocos componentes podremos realizar nuestro primer ejercicio en el uso del puerto serie para controlar un aparato externo:
Componentes
El circuito es muy simple y su costo total es inferior a 1Dólar.
1 Conector DB9 (o DB25) Hembra
1 LED Rojo
1 LED Verde
2 Resistencias de 1K
El Circuito
Los LEDs son diodos, es decir que solamente se encienden cuando están "polarizados" en forma correcta. La rayita del LED corresponde al CÁTODO y va hacia el negativo o 0V y la base del triángulo es el ÁNODO y va hacia el positivo.
Advertencia: Si quieren probar el led, pongan una resistencia, tal como se ve en la Fig. 3 o conéctenlo con UNA SOLA pila AA o AAA (1.2V - 1.5V), o lo quemarán.
Fig. 3 Circuito del Ejercicio 1
Si observan detenidamente la forma en que está configurado el circuito de la Fig. 3, notarán que SG que es la tierra o 0V está en el centro y los LEDS están polarizados en ambas direcciones, es decir, uno de los leds se encenderá cuando el SG (cero volts) sea Positivo y en el otro caso cuando SG sea Negativo. Esto tiene que ver con las señales típicas de RS232 que son +12 y -12 (obviamente 0V es positivo con respecto a -12V). En el caso de la señal DTR, cuando hay un 1 (está activada) hay +12V y cuando hay un 0 (está desactivada) hay -12V. La mayoría de los circuitos electrónicos, incluída la PC, tabajan solamente con valores positivos (la PC, entre 0V y +5V), por ejemplo, una radio, un CD player, un MP3, etcétera.
Fig. 4 Implementación con "Breadboard"
Recibí numerosas consultas con respecto al armado de circuitos y los conocimientos de electrónica que se requieren para realizarlos. La mayoría de los circuitos que publico en estos artículos son básicos y en mi experiencia los pueden realizar sin mayores dificultades. Sin embargo, voy a publicar algunos artículos sobre los fundamentos de la electrónica para facilitar el paso a la práctica que es lo mas importante.
Por ahora, para los interesados, puse un Manual Introductorio de Radio y Electrónica en Inglés desarrollado por Phillips en formato PDF que pueden bajar aquí: Fundamentals of Radio y otro de Electrónica orientada a los sistemas digitales, de McGraw-Hill, también en inglés aquí: Introduction to Electronics.
Conectores DB9 - DB25
La siguiente tabla muestra las señales RS232 presentes en los conectores DB9 y DB25 que son los más frecuentes.
Fig. 5 Tabla de Señales RS232 y sus Pines
Tal como vimos en el caso del Puerto Paralelo, las direcciones del Puerto Serie también las encontramos en el BIOS:
COM1 en 0x0040:0000 es 03F8
COM2 en 0x0040:0002 es 02F8
COM3 en 0x0040:0004 es 03E8
COM4 en 0x0040:0006 es 02E8
La PC puede utilizar 2 o más direcciones alternativas para sus Puertos Serie: 3F8, 2F8, 3E8 y 2E8 (COM1 a COM4) entre otros. Para averiguar el puerto que utiliza la PC, podemos buscar en el Administrador de Sistema de Windows (que se encuentra en el panel de control) o podemos ejecutar el programa de diagnostico MSD (parte de DOS) y seleccionamos COM. También podemos observar los puertos por medio de Debug con el comando D (dump) a partir del segmento 40 entre 0 y 7 (en la línea de comando de Debug –D 40:0,7)
En nuestro caso, vamos a utilizar dos pines del conector del puerto serie: SG (Signal Ground o Tierra) y DTR (Data Terminal Ready). En otro artículo, analizo algunas particularidades del protocolo RS232, pero pueden encontrar mucha información en Internet sobre el tema.
La dirección de control del Puerto Serie, que nos permitirá activar y desactivar la señal DTR, está en la dirección de base + 4. Esto quiere decir que si nosotros vamos a utilizar el puerto COM1, la dirección de base será 03F8 y la dirección de control será 03FC.
Para activar nuestra línea DTR, debemos poner en 1 el último bit, es decir, enviamos un 1, para activar y un 0 para desactivar.
Ejercicio 1
Con este ejercicio vamos a dejar planteado en forma concreta el “Control Externo” del puerto y además, vamos a verificar alguna de las particularidades del puerto serie de la PC.
El primer paso consiste en realizar el "circuito" (en realidad un par de LEDs y un par de Resistencias) que observamos en la Fig. 3 que pueden realizar por medio de soldar los componentes o por medio de un breadboard (ver Fig. 4).
El Software
Les presento dos modalidades, una implementada en Visual Basic que perfectamente pueden realizarla en VBA para Access, Excel, Word, etcétera (en Herramientas/Macro/Editor de Visual Basic o ALT-F11) y otra en Assembler que la pueden implementar con Debug. En mi caso, tenía disponible el COM3, de manera que utilicé las direcciones 3E8 (Base) y 3EC (Control).Visual Basic
El primer paso, es verificar si disponen del Control de comunicaciones de Microsoft “MsComm”, en caso de que no esté presente, deben agregarlo en la opción Proyecto/Componentes y en el caso de Word, por ejemplo, insertan un Formulario, abren el cuadro de herramientas y hacen clic con el botón derecho y agregan controles adicionales. El nombre del control es Microsoft Communications Control.
Fig. 6 Agregar el Componente MsComm
Agregan dos BotonesPrivate Sub Command1_Click()
MSComm1.DTREnable = True
Picture1.Picture = LoadPicture("on.gif")
End Sub
Private Sub Command2_Click()
MSComm1.DTREnable = False
Picture1.Picture = LoadPicture("off.gif")
End Sub
Agregan el código de apertura y cierre del puerto en Load() y Unload()
Private Sub Form_Load()
MSComm1.CommPort = 3
If Not MSComm1.PortOpen Then
MSComm1.PortOpen = True
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
If MSComm1.PortOpen Then
MSComm1.PortOpen = False
End If
End Sub
Assembler
En el caso del assembler, probé generar dos archivos COM que permiten desde Windows activar y desactivar el DTR, se denominan alternativamente 232on.com y 232off.com. También se puede realizar la acción desde Debug y oprimir P (paso a paso). En el caso de utilizar assembler, es importante primero enviar algo a la dirección de base del puerto para activarlo y luego enviar el valor al puerto de control.
N232on.com
A
MOV DX,03E8
MOV AL,1
OUT DX,AL
MOV DX,03EC
OUT DX,AL
MOV AX,4C
INT 21
RCX
12
W
Q
N232off.com
A
MOV DX,03E8
MOV AL,0
OUT DX,AL
MOV DX,03EC
OUT DX,AL
MOV AX,4C
INT 21
RCX
12
W
Q
En la próxima entrega veremos como encender una linterna, una radio, mas adelante un velador y finalmente un motor de Corriente Alterna.
Que tengan un gran día,
Sergio Otaño
viernes, 9 de enero de 2009
El Reloj de Tiempo Real (RTC)

En esta ocasión vamos a estudiar el Reloj de Tiempo Real o RTC en inglés que también mencionamos en el artículo Gestión de Entrada/Salida, el MC 146818 (una versión del 6818) de Motorola. Pero en esta oportunidad vamos a emplear el servicio que ofrece BIOS Basic Input Output System (Sistema Básico de Entrada Salida) por medio de la Interrupción 1A. Recordemos que el BIOS está compuesto de código de máquina, es decir software, pero que reside en un chip de memoria ROM, por eso suele referirse genericamente a estas funciones/componente como ROM-BIOS. Esta combinación de código residente en una memoria ROM también suele denominársele FIRMWARE para distinguirlo del SOFTWARE. Los modelos de computadora tienen ROM-BIOS diferentes debido a que el BIOS es el que conoce los detalles de HARDWARE de los equipos en particular y almacena todas las direcciones de los puertos.
Fig. 2 El Reloj de Windows
La Interrupción Int 1AhGenéricamente esta interrupción permite el acceso al reloj interno de la PC (146818). Este reloj realiza un conteo de 1193180/65536 tics/segundo, esto es 18,2 tics por segundo (65536 es el número de cuentas entre 0000 y FFFF, es decir, un registro de 16 bits).
Fig.3 Primer Ejercicio: Leer el RTC
Como sabemos las interrupciones son procedimientos que reciben sus parámetros por medio de los registros (ver Ensamblador (Entrega 3)).
Leer el Reloj del Sistema
Se realiza la interrupción 1A con AH=0
En este caso, se usa AH el byte alto del registro AX y devuelve en CX:DX (los valores mayores en CX) el número de tics desde la medianoche a una razón de 18.2 tics por segundo o un tic cada 54.92 ms (milisegundos). Los valores estarán en hexadecimal, por lo que hay que convertirlos a decimal para su interpretación.
Primer Ejercicio
Para nuestro primer ejercicio (ver Fig. 3), Se recomienda realizar la lectura del reloj para obtener un valor relativamente grande (a menos que recientemente haya pasado la medianoche).
Abrimos una sesión de DOS (Inicio/Ejecutar command) y dentro de la ventana de DOS iniciamos DEBUG (C:\> DEBUG [INTRO]) e ingresamos lo siguiente:
-A
MOV AH,0
INT 1A
MOV AX,4C
INT 21
[INTRO]
-G 100
-p
-p
Aquí se leen los valores que aparecen en CD:DX y se interpretan con la calculadora de Windows en su versión científica, es decir, cargar los valores con la selección Hex y luego cambiar la selección a Dec.
-p
-p
Recuerden que Int 21 con AX=4C es la salida normal del programa y siempre deben usarla. Si tienen dudas respecto de debug, lean los artículos sobre lenguaje ensamblador en este BLOG.
Realizar un Cronómetro
Uno de los elementos mas útiles para la observación de eventos, ya sea en la ciencia como en los deportes es el cronómetro. En la programación, especialmente cuando trabajamos con protocolos, también es muy útil establecer límites de tiempo (timeout) para ciertas operaciones. En el caso de los protocolos de comunicación, se denomina “negociación” al establecimiento de contacto que generalmente comienza con una máquina que envía un byte ENQ (5h) INTERROGA y la otra máquina envía ACK (6h) ACEPTA o NAK (15h) No ACEPTA, etcétera. En estos casos, se establece un límite de tiempo en el que puede realizarse la respuesta y luego, por ejemplo, se envía un paquete.
Este tipo de "medición" es precisamente lo que se puede lograr si ponemos a cero del contador del Reloj de Tiempo Real y luego “leemos” el tiempo transcurrido, simpre recordemos que el reloj realiza 18.2 tics por segundo.
Poner a Cero el Reloj de Sistema
Se realiza la interrupción 1A con AH=1
Segundo Ejecicio
Para nuestro segundo ejercicio (ver Fig. 4), se recomienda abrir el reloj de Windows para contar 10 segundos antes de leer el valor que entrega el RTC. Los 10 segundos se deben contar a partir de que se envía la primera interrupción Int 1A hasta que se envía la segunda. A partir de allí se leen los registros CX:DX
Abrimos una sesión de DOS (Inicio/Ejecutar command) e ingresamos lo siguiente:
-A
MOV AH,1
INT 1A
MOV AH,0
INT 1A
MOV AX,4C
INT 21
[INTRO]
-G 100
-P (se ejecuta el MOV)
Esperamos a que el reloj de windows marque un minuto justo, por ejemplo 12:23 (ver Fig. 2) y oprimimos P nuevamente y se ejecuta el Int 1A.
-P (inmediatamente se ejecuta el MOV)
Esperamos que el reloj de windows marque que transcurrieron 10 segundos, por ejemplo 12:23:10 (ver Fig. 2) y oprimimos P nuevamente.
Fig. 5 Prueba del Cronómetro
Aquí leemos los valores que aparecen en CD:DX y los interpretamos con la calculadora de Windows en su versión científica, es decir, cargamos los valores con la selección Hex (hexadecimal) y luego cambiamos la selección a Dec (decimal).
Precisamente, observamos que en CD:DX tenemos 0000:00B6, lo que traducido a decimal es 182 que justamente corresponde a 18.2 x 10 tics del reloj. En mi trabajo profesional, antes de programar con los compiladores visuales utilicé muchísimo estas interrupciones de BIOS, tanto en Assembler como en leguajes C, Pascal y Quick Basic entre otros; en muy variadas aplicaciones, manejadores de dispositivos, protocolos de comunicación, etcétera.
Funciones de la Interrupción 1A de BIOSAH = 00h Tics del día
AH = 01h Poner a Cero el Contador de Ticks
AH = 02h Leer la Hora del Reloj de Tiempo Real
AH = 03h Establecer la Hora del Reloj de Tiempo Real
AH = 04h Leer la Fecha del Reloj de Tiempo Real
AH = 05h Establecer la Fecha del Reloj de Tiempo Real
AH = 06h Establecer la Alarma del Reloj de Tiempo Real
AH = 07h Poner a Cero la Alarma del Reloj de Tiempo Real
AH = 08h Establecer el Modo de Encendido Activado del RTC
AH = 09h Leer la Hora y el Estatus la Alarma del RTC
AH = 0Ah Leer el Contador de Días del Temporizador del Sistema
AH = 0Bh Establecer el Contador de Días del Temporizador del Sistema
Hasta la Próxima,
Sergio Otaño
martes, 30 de diciembre de 2008
Gestión de Entrada/Salida
Los sistemas operativos brindan cuatro funciones básicas: Intérprete de Comandos, Gestión de la Memoria, Gestión de Entrada/Salida y Sistema de Archivo. En este artículo analizaremos la función de administración de Entrada/Salida por medio de ejercicios de programación que nos mostrarán en la práctica cómo funciona el Sistema Operativo. Para realizar estas prácticas, utilizaremos el lenguaje ensamblador ya que es en ese lenguaje en el que está programado el núcleo central o kernel del sistema operativo.
En la Práctica
Si observamos la Fig. 2 del artículo Concepto de Sistema Operativo , notaremos que el BUS del sistema permite interconectar todos los componentes de la PC, por esto se dice que la PC tiene una arquitectura de BUS. El bus no es otra cosa que cables (o pistas de cobre) que pueden tener el valor 0 o 1 (+5 Volts o 0 Volts).
El bus está organizados en tres grandes grupos:
- El bus de datos.
- El bus de direcciones.
- El bus de control.
Fig. 1 El Chip 8253 de Intel
Nota: Desde el punto de vista de las señales eléctricas, el bus dispone de tres conjuntos de líneas: Datos, Direcciones y Control; sin embargo, las rutinas del sistema operativo y los programas pueden solamente acceder a dos: Dirección o Puerto y Datos. Las señales de control tienen que ver con operaciónes que se realizan a nivel de hardware, tales como la habilitación y la señal de escritura o de lectura de un chip. Habitualmente, los dispositivos de E/S utilizan para sus operaciones un conjunto de direcciones que sirven para distintos propósitos (entrada/salida de datos, configuración, control, etcétera). Por esta razón, se habla de dirección de base, y se refiere a aquel puerto que da comienzo a la sucesión de direcciones propias del dispositivo.
Las computadoras personales o PC tienen dos circuitos temporizadores; por un lado, el Circuito Integrado 8253 de Intel que es un temporizador/contador y el 6818 de Motorola que es un reloj de tiempo real. Si observamos detenidamente la Fig. 2 en el artículo Concepto de Sistema Operativo , podremos notar el “temporizador” y el “reloj de tiempo real” conectados al BUS de la PC.
Durante el proceso de inicio de la PC (boot-up) la fecha y hora se obtienen del reloj de tiempo real M6818. A partir de ese momento, la fecha y hora son mantenidas por medio de los pulsos generados por el chip 8253, hasta que se apaga la PC.
Fig 2. Los “Relojes” de la PC
Una de las funciones que cumple el 8253 (actualmente el 8254 cumple estas funciones) es la de generar el sonido en el parlante de la PC. Para esto debemos “programar” el 8253 para que divida la frecuencia de base que recibe de un oscilador a cristal (1.192,180 KHz.) en su registro de 16 bits.
Fig. 3 El Registro de Control del 8253
Para programar un contador del 8253 hay que enviar primero una palabra de control y, después, un valor de cuenta inicial. Los contadores se seleccionan con las líneas A0 y A1; el valor A0=A1=1 selecciona la escritura de la palabra de control (en la que se identifica el contador implicado). Por tanto, el 8253 ocupa normalmente 4 direcciones de E/S consecutivas ligadas a los contadores 0, 1, 2 y al registro de la palabra de control. Para enviar la cuenta inicial se utiliza simplemente el puerto E/S ligado al contador que se trate. El formato de la palabra de control puede observarse en la Fig. 3, a partir de esta palabra de dos bytes se puede establecer el canal, el modo, el formato de datos y los datos a enviar.
Un Ejemplo Concreto
Tal como mencioné en otros artículos relativos al lenguaje ensamblador y DOS, todos los ejemplos que les presento se pueden realizar en cualquier PC de las actuales. Si, con Windows Vista y XP también, y funcionan tanto en equipos portátiles o de mesa. En el caso de los ejemplos de este artículo, los realicé en una Notebook Presario 3000 que corre Windows XP SP 2.
Como ejemplo vamos a programar el canal 2 del 8253 para producir una señal de onda cuadrada con los siguientes parámetros: Canal 2, Modo de Operación 3, Datos Enclavados (latched), LSB Primero (byte menos significativo), MSB (byte mas significativo) después, Datos en formato BCD (binario codificado en decimal)
Un término que es conveniente definir en este momento es el de LATCH (cerrojo) que corresponde a un concepto de electrónica digital que presentaré en otro artículo con ejemplos y circuitos prácticos. Pero para una definición rápida, podemos pensar en el LATCH como un conjunto de 8 bits que pueden ser establecidos (set) y mantener (hold) el dato binario que le cargamos; y luego podemos quitar (reset). En el caso del 8253 que como sabemos tiene un contador de 16 bits, recibe dos bytes (8 bits) en la misma dirección y los mantiene gracias al LATCH.
Fig. 4 Definición del Comando
Algunos puntos a tener en cuenta:
Cada canal temporizador del 8253 puede ser programado en uno de seis modos: Modo 0 a 5.
Todos los canales temporizadores realizan conteos o tareas de temporizado designados en forma simultánea e independientes entre sí.
Los canales 0 y 1 son inicializaos por el BIOS y son utilizados para funciones del sistema; por lo tanto, el usuario no debe interferir con estos canales.
Solamente el canal 2 está disponible para aplicaciones del usuario.
Cada canal opera por medio de un registro contador de 16 bits que disminuye su valor en uno por cada ciclo de la señal de 1,19 MHz del reloj.
Para generar la onda cuadrada en la salida del canal 2:
- El valor B7 se envía al puerto 43h (registro de comando)
- El LSB se envía al puerto 42h (latch)
- El MSB se envía al puerto 42h (latch)
Iniciamos debug desde una sesión DOS Inicio/Ejecutar – command – Aceptar, si no recuerda estos pasos, repase los artículos: Ensamblador (Entrega 1) y Ensamblador (Entrega 2).
1) En la línea de comandos de debug escribimos a (ensamblar)
-a
2) Luego cargamos las siguientes instrucciones
3) Retornamos a la línea de comando de debug y escribimos g 100 (go es el comando para ejecutar el código)
-g 100
4) Aplicamos p (ejecutar paso a paso)
5) Al llegar a la línea 110, luego de aplicar p, se activa el sonido
6) Si continuamos aplicando p, al llegar a la línea 118 y aplicar p, se desactiva el sonido
7) El programa finaliza y para salir de debug aplicamos el comando q (quit).
Explicación Detallada
1) Enviamos la palabra B7 al puerto de control 43h del 8253, como sabemos, no se puede realizar una operación de salida a puerto o OUT en forma directa y siempre debemos “cargar” previamente un registro de la CPU. Recordemos que B7 significa que el controlador operará a través del temporizador 2 (o canal 2), en Modo 3 y que espera los datos en BCD.
MOV AL,B7
OUT 43,AL
2) Enviamos los datos en formato BCD, primero el LSB (byte menos significativo) que en este caso es 72h y luego se envía el MSB (byte mas significativo) que es 45h.
MOV AL,72
OUT 42,AL
MOV AL,45
OUT 42,AL
3) Para activar el 8253, se lee por medio de la instrucción IN el puerto 61h, se complementa con el valor 3 y luego se envía al mismo puerto 61h (de datos).
IN AL,61
OR AL,3
OUT 61,AL
4) De esta forma queda activado el sonido ya que el 8253 mantendrá la generación de la onda cuadrada de la frecuencia especificada hasta que se le ordene que se detenga.
5) Para desactivar el 8253, se lee por medio de la instrucción IN el puerto 61h, se cambian los bits por medio de XOR con el valor 3 y luego se envía al mismo puerto 61h (de datos).
IN AL,61
XOR AL,3
OUT 61,AL
6) Y de esta forma se detiene el sonido; luego, para terminar el programa se usa la interrupción 21 en forma estándar.
MOV AX,004C
INT 21
7) La instrucción NOP significa no operar y es una instrucción que ocupa un lugar en la memoria pero que es ignorada por la CPU.
Importante
Si les resulta mas fácil inicializar el contador en binario en lugar de hacerlo en BCD pueden cambiar el último bit de la palabra de comando, y enviar el valor de la frecuencia diréctamente en binario. Para esto, en lugar de B7 enviaremos B6 al puerto 43h del 8253. En este caso, el cálculo de frecuencia del sonido es como sigue:
Velocidad del Reloj/Frecuencia del Sonido
Supongamos que deseamos una frecuencia de 600 Hz.
1.192,180 kHz. / 600 Hz = 1.989 (07C5 en hexadecimal)
Recordemos que se envía primero el byte menos significativo al latch y luego el mas significativo.
MOV AL,B6
OUT 43,AL
MOV AL,C5
OUT 42,AL
MOV AL,07
OUT 42,AL
Prácticas Adicionales
Prueben realizar los siguientes programas en archivos de texto, por ejemplo Suena.txt y Nosuena.txt. Para ensamblarlos escriban en la línea de comando de DOS c:\>debug < suena.txt y generaran el programa suena.com (repase el proceso de ensamblado en el artículo: Lenguaje Ensamblador (Entrega 3)) . Estos programas activan (suena.com) y desactivan (nosuena.com) el sonido.
NSuena.com
A
MOV AL,B7
OUT 43,AL
MOV AL,72
OUT 42,AL
MOV AL,45
OUT 42,AL
IN AL,61
OR AL,3
OUT 61,AL
MOV AX,004C
INT 21
NOP
NOP
NOP
RCX
16
W
Q
NNosuena.com
A
MOV AL,B7
OUT 43,AL
MOV AL,72
OUT 42,AL
MOV AL,45
OUT 42,AL
IN AL,61
XOR AL,3
OUT 61,AL
MOV AX,004C
INT 21
NOP
NOP
NOP
RCX
16
W
Q
Este artículo forma parte de una serie en los que vamos a explorar las funciones de los sistemas operativos con la acostumbrada modalidad de ensuciarnos las manos o “hands on”. Esto significa que realizaremos ejercicios de programación concretos y significativos que nos permitirán comprender al sistema operativo por dentro.
Que tengan un gran día,
Sergio Otaño
miércoles, 22 de octubre de 2008
Ensamblador (Entrega 6)
Las cadenas de caracteres (strings, en inglés) permiten trabajar en forma simultánea con bloques de bytes almacenados en la memoria. En los lenguajes de bajo nivel (ensambladores) existe una limitación impuesta por el hardware que permite un máximo de 64 KBytes la longitud de una cadena. Este límite puede ser superado por medio de la implementación de procedimientos que permitan manejar bloques mayores. Los ensambladores simbólicos, tales como el Massm y los lenguajes de alto nivel, como Basic, C, Pascal y otros, proveen mecanismos estándar para trabajar con bloques grandes de manera que pueden, en ocasiones, superar el millón de bytes.
Tal como ocurre con las operaciones aritméticas básicas y las operaciones lógicas, las operaciones con cadenas de caracteres están sustentadas en operaciones denominadas “primitivas” que forman parte del conjunto de instrucciones del microprocesador. Estas instrucciones primitivas procesan los componentes del bloque o cadena de caracteres tomando independientemente un carácter por vez. Como puede observar en la Fig. 1, las instrucciones primitivas permiten mover, comparar, buscar, cargar y almacenar cadenas de caracteres.
El siguiente ejemplo servirá para ilustrar la forma en la que trabajan las instrucciones primitivas con respecto a las cadenas.
Código del Programa en Ensamblador “cadena”
NCADENA.COM
A
DB 'CADENA'
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AH,40
MOV BX,1
MOV CX,6
LEA DX,[100]
INT 21H
POP DX
POP CX
POP BX
POP AX
INT 20
RCX
2A
W
Q
Práctica de laboratorio
Para realizar esta práctica, el lector deberá contar con los siguientes elementos:
- Una computadora personal con DOS.
- El programa DEBUG.EXE de DOS.
- Un programa editor de textos ASCII como EDIT.
Nota: El lector puede utilizar un programa ensamblador como Turbo Assembler de Borland o el Asembler de Microsoft en lugar de el programa DEBUG.EXE. Sin embargo, si sigue las indicaciones expuestas en el libro, podrá emplear DEBUG.EXE para producir un programa sin mayores dificultades.
1) Ubíquese en el directorio C:\DOS o, si posee Windows 95 o superior, en el directorio C:\WINDOWS\COMMAND. Cree el archivo CADENA.TXT utilizando para ello, el programa EDIT (o equivalente).
2) Una vez creado el archivo, ejecute el siguiente comando:
Análisis
1) El uso de las instrucciones push y pos sirven para preservar los contenidos de los registros. En próximas unidades daremos una explicación completa de su uso.
2) El comando db (definir byte) permite almacenar consecutivamente el bloque de caracteres entre comillas simples (‘CADENA’). La forma en que se almacenan los bytes consecutivos es por medio de los códigos ascii correspondientes tal como lo muestra la siguiente tabla:
MOV AH,40
MOV BX,1
MOV CX,6
LEA DX,[100]
INT 21H
4) Debido a que la primera instrucción de nuestro programa es DB ‘CADENA’ y como ya sabemos que el código del programa comienza en la dirección 100, entonces, sabemos que el conjunto de bytes ‘CADENA’ comienzan en la dirección 100. La instrucción LEA, permite cargar la dirección real de la cadena (LEA en inglés quiere decir Load Effective Adreess que en castellano significa cargar la dirección efectiva o real).
¡Que tengan un gran día!
Sergio Otaño
viernes, 1 de febrero de 2008
Recursion and Machine Language
Hanoi Towers
Recursive Algorithm in 8088 DEBUG
The first time I implemented the algorithm to solve the Hanoi Towers puzzle I was learning Pascal, and it was obviously the language I chose to do it. Some time later, I came across an old Motorola 6800 microprosessor development kit and went on and implemented the algorithm by assembling it on paper and punching in the machine codes with an hexadecimal keypad. The output was a 7 segment display!
There is nothing extraordinary about this, except for the fact that I applied a recursive algorithm directly in 6800uP machine language. I had two motivations for doing this: first, the recursive solution is very simple and required less coding and second, I was sick of all the buzz about "recursive languages" and how "magic" they were, and how dumb those "other", "non-recursive" languges were.
So, let's kick some computer nazis's but!
Recently, I was teaching Excel VBA to a group of students and thought it would be fun to implement the Hanoi algorithm and use Excel graphics to show the "disks". I actualy did it and was really fun and exiting for the students who had never used Excel like that. I went on and thought why not relive the experience, but this time, with a modern PC type computer with display, keyboard and "debug.com" as the "assembler". Fig. 1 shows a "deluxe" version of the program which takes line parameters (N=3 to 9, otherwise assumes the default 3 disks) and prints new line characters.
Much Easier!
The following C++ code shows an implementation of the classical recursive solution. I intentionally defined the variables AX, DX and CX since those registers would be used in my assembly code. The Algorithm in C
|
|
- The poles A (source), B (intermediate) and C (destination) would go respectively to AX, CX and DX. Shame: such a waste!.
- C++ parameters are: N (number of disks), AX (source), DX (destination) and CX (intermediate).
- Notice that the first call to de procedure from the main program assigns each variable its "correct" pole. However, inside the procedure, there are two recursive calls with two parameters swapped in each respective call: The first call decrements N and swaps CX and DX (exchanges destination and intermediate). The second call decrements N and swaps AX and CX (exchanges source an intermediate). Between these two procedure calls, it prints N, AX (source) and DX (destination).
- The fact that the procedure calls itself may seem a little awkward to follow at firs glance, but it actually is very straight forward.
Lets follow the example shown in the above C++ code: N=3, AX='A', DX='C' and CX='B'
The Assembly Code
This is the actual assembly code that fully implements the algorithm (see Fig. 2). Afterwards I created a "deluxe" version wich is shown in Fig. 1; is the same basic code with the addition of initialization code to accept N as a line parameter and an a little procedure to print new lines.
Fig. 2 DEBUG'S Recursive Hanoi First Run
Complete Listing in "Debugese"
The Saga Continues...
Don't Miss It:
SEE HERE: HANOI GOES BABYLON!
Sergio Otaño
viernes, 25 de enero de 2008
Ensamblador (Entrega 5)
En términos muy groseros, un PROCEDIMIENTO es simplemente una dirección de la memoria que contiene una secuencia de instrucciones y termina con una instrucción RET. A partir de este punto, comienzan a aparecer “detalles”, tales como los PARAMETROS, el mantenimiento de los REGISTROS y BANDERAS en la “PILA” o “STACK”, el mantenimiento de VARIABLES en la pila, el retorno de valores (para implementar las funciones), etcétera. Todas estas cuestiones son importantes al implementar compiladores ya que en los lenguajes de alto nivel, se toman en cuenta minuciosamente.
En la Fig. 1 observamos el código de un procedimiento que comienza en la línea 108 y que consiste de una rutina que permite mostrar un carácter por la pantalla, es el código que vemos a continuación:
0DE3:0108 MOV AH,2
0DE3:010A MOV DL,31
0DE3:010C INT 21
0DE3:010F RET
El “Programa Principal” es el siguiente:
0DE3:0100 CALL 108
0DE3:0103 MOV AX,4C
0DE3:0106 INT 21
Como vemos, el “programa” completo no es mucho mas que esto, ya que lo único adicional es que tiene las instrucciones de “salida” de DOS. Sin embargo, muestra dos conceptos importantes:
1) El procedimiento es simplemente una DIRECCION de memoria.
2) La instrucción RET devuelve el puntero de instrucciones, o, en términos simples, el control del programa a la instrucción siguiente a la del llamado.
En este ejemplo, el LLAMADO del procedimiento se realiza en la dirección 100 y el RETORNO se produce en la dirección 103.
El Programa en Ejecución
Observen la Fig. 2:
1) El programa muestra la ejecución de la primera instrucción que es la llamada al procedimiento (CALL 108).
2) Luego aparece un 1 en la pantalla que es lo que imprimió la subrutina. En este punto, es interesante notar que el IP = 103 (puntero de instrucciones) esta dirección es la del retorno del procedimiento.
3) Desde la dirección 103 el programa TERMINA
¡Esto es todo por ahora!
Sergio Otaño
miércoles, 23 de enero de 2008
Ensamblador (Entrega 4)
En esta ocasión veremos algunos ejemplos prácticos de instrucciones típicas del lenguaje ensamblador, tales como INC y DEC. También notaremos los efectos que pueden producirse en las BANDERAS (flags) y, finalmente, cómo se puede implementar un sencillo ciclo WHILE en lenguaje ensamblador.
Ejemplo de la instrucción DEC
a) Se inicia el comando A para ENSAMBLAR el código
b) MOV AX,FF (carga el registro AX con FF = 255)
c) Usamos INTRO para volver a la línea de comandos de DEBUG
d) Con el comando G 100 ejecutamos el código que comienza en la posición 100 de la memoria. Luego con el comando P vemos la ejecución del comando en custión. Si observamos el registro AX, notamos que contiene FF tal como le instruimos a la CPU.
e) Nuevamente aplicamos el comando A para ensamblar código
f) DEC AX (decrementa el valor de AX)
g) Usamos INTRO para volver a la línea de comandos de DEBUG
h) Con el comando G 103 ejecutamos el código que comienza en la posición 103 de la memoria. Luego con el comando P vemos la ejecución del comando en custión. Nuevamente observamos el registro AX y notamos que su contenidos es FE, es decir que disminuyó en 1 su valor.
i) Finalmente con Q salimos de DEBUG.
-A
0DE3:0100 MOV AX,FF
0DE3:0103
-G 100
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0100 NV UP EI PL NZ NA PO NC
0DE3:0100 B8FF00 MOV AX,00FF
-P
AX=00FF BX=0000 CX=0000 DX=0000 SP=FF
EE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0103 NV UP EI PL NZ NA PO NC
0DE3:0103 45 INC BP
-A
0DE3:0103 DEC AX
0DE3:0104
-G 103
AX=00FF BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0103 NV UP EI PL NZ NA PO NC
0DE3:0103 48 DEC AX
-P
AX=00FE BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0104 NV UP EI PL NZ NA PO NC
0DE3:0104 122A ADC CH,[BP+SI] SS:0000=CD
-Q
Ejemplo de la instrucción INC
-A
0DE3:0100 MOV AX,00
0DE3:0103
-G 100
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0100 NV UP EI PL NZ NA PO NC
0DE3:0100 B80000 MOV AX,0000
-P
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0103 NV UP EI PL NZ NA PO NC
0DE3:0103 45 INC BP
-A
0DE3:0103 INC AX
0DE3:0104
-G 103
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0103 NV UP EI PL NZ NA PO NC
0DE3:0103 40 INC AX
-P
AX=0001 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0104 NV UP EI PL NZ NA PO NC
0DE3:0104 122A ADC CH,[BP+SI] SS:0000=CD
-Q
Explicación:
En este ejemplo comenzamos cargando 0 en AX, luego aplicamos la instrucción INC AX y verificamos que efectivamente AX = 1
Pero veamos que ocurre en el siguiente ejemplo de la instrucción INC
a) Cargamos el registro AX con FFFF (el valor máximo que admite el registro, 65.535)
b) Incrementamos el contenidos de AX por medio de la instrucción INC…
c) Observamos que el registro AX quedó en 0. También se notan que el flag de cero está en ZR, es decir que cambió de NZ a ZR, esto significa que la operación tuvo por resultado cero. También cambió el flag de acarreo, que estaba en NA y pasa a AC lo que significa que hay un bit de acarreo.
-A
0DE3:0100 MOV AX,FFFF
0DE3:0103
-G 100
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0100 NV UP EI PL NZ NA PO NC
0DE3:0100 B8FFFF MOV AX,FFFF
-P
AX=FFFF BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0103 NV UP EI PL NZ NA PO NC
0DE3:0103 40 INC AX
-A
0DE3:0103 INC AX
0DE3:0104
-G 103
AX=FFFF BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0103 NV UP EI PL NZ NA PO NC
0DE3:0103 40 INC AX
-P
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0DE3 ES=0DE3 SS=0DE3 CS=0DE3 IP=0104 NV UP EI PL ZR AC PE NC
0DE3:0104 122A ADC CH,[BP+SI] SS:0000=CD
-Q
Ejemplo de la implementación de un ciclo WHILE
Crearemos un archivo de texto con EXACTAMENTE lo siguiente:
NPRU.COM
A
MOV AH,01
INT 21
CMP AL,0D
JNE 100
MOV AX,004C
INT 21
RCX
F
W
Q
Lo ensamblamos con el procedimiento explicado en entregas previas:
Explicación:
La opción AH=1 de la interrupción 21 permite leer un carácter tomado del teclado y mostrarlo por pantalla y guarda el carácter leído en AL.
El ciclo WHILE está implementado en la secuencia siguiente:
MOV AH,01
INT 21
CMP AL,0D
JNE 100
Básicamente la instrucción CMP permite comparar si el contenido de AL es igual a 0D (o 13 decimal que es INTRO).
La instrucción JNE significa “si no es igual, saltar” y el 100 es la posición a la que saltará el programa.
De este modo el ciclo se repite hasta que se teclea INTRO. Al teclear INTRO, el programa continúa con las instrucciones siguientes:
MOV AX,004C
INT 21
¡Y de ese modo finaliza el programa!
Sergio Otaño
lunes, 14 de enero de 2008
Ensamblador (Entrega 3)
Al estudiar la estructura de la unidad central de procesos CPU, los alumnos aprenden el concepto de interrupción relacionado con el procesador, esta interrupción es de “hardware”, se trata de una señal eléctrica que recibe el procesador a través del bus de la PC (o una conexión directa en el caso de los microcontroladores).
Hay otro tipo de interrupciones, denominadas “de software” que en realidad son rutinas programadas y almacenadas en la memoria de la computadora. Para comprender esto, pensemos en un estado inicial en el que el procesador ejecuta un programa, al ocurrir una interrupción, el procesador abandona la tarea que lo ocupa y pasa a ejecutar otra.
Sintéticamente, lo que ocurre es lo siguiente:
1) El procesador almacena ciertos datos, tales como el contador de programa (que señala la dirección de memoria que está ejecutando)
2) Carga la dirección de memoria de la rutina de interrupción ISR correspondiente
3) Ejecuta la rutina de interrupción ISR y retorna de la interrupción.
4) Restablece los datos.
Una vez que el procesador completa su servicio al acontecimiento que originó la interrupción, retorna a la ejecución del programa en el punto en el que lo abandonara.
El lenguaje ensamblador dispone de una instrucción específica para “llamar” las interrupciones, se denomina INT. Int se utiliza para llamar a rutinas que realizar tareas genéricas y que vienen como servicios en el BIOS y en el DOS por lo que el programador no necesita programarlas por si mismo. Por ejemplo, para abrir un archivo el programador usará una rutina de interrupción de DOS, para establecer el modo de pantalla, mover el cursor y otras tareas similares, usará una rutina de BIOS. En general, la subrutina que se utilizará se especifica en AH (la porción alta del registro AX).
Por ejemplo, para imprimir un mensaje en la pantalla, se usará el servicio 9 de la interrupción 21. Si contáramos con un programa ensamblador simbólico, tal como el MASM, podríamos escribir algo así:
MSG DB 'CADENA$'
MOV DX, OFFSET MSG
MOV AH, 9
INT 21
En este caso, MSG es una variable, es decir, representa una posición de memoria. Este tipos de opciones facilitan mucho la tarea del programador. Sin embargo, con debug no contamos con esos lujos y debemos hacer estas cosas “a mano”, como vemos en el siguiente ejemplo:
Fig. 1 Ejemplo del uso del servicio 9 de la interrupción 21
El procedimiento que ejecuta el procesador para atender una interrupción se denomina ISR (Interrupt Service Routine en inglés) o rutina de servicio de interrupción. Esta rutina, es simplemente un programa que fué diseñado especialmente para atender el tipo de interrupción de que se trate. El vector de interrupción permite establecer la dirección que tendrá esta rutina.
El disparador de una interrupción puede tener origen en el hardware o en el software. Los vectores de interrupción de hardware se denominan IRQ. Las interrupciones de software se generan mediante la instrucción INT. El vector de interrupción se indica mediante un operando que sigue a la instrucción y puede tener un valor entre 0 y FF (0 y 255).
La computadora personal, al arrancar, carga un conjunto de rutinas de servicio de interrupción en la memoria. El primer programa en cargar rutinas es el BIOS que, como ya señalamos, está grabada en una memoria ROM (ROM-BIOS).
Además, los sistemas operativos disponible en el ambiente de la computadora personal tales como el DOS y OS/2, instalan rutinas de servicio de interrupción. De esta forma, el programador de lenguaje ensamblador dispone de un conjunto de servicios listos para ser utilizados. Uno de los servicios de interrupción típicos de BIOS es INT 10 que ofrece numerosos servicios de video. El DOS dispone de una interrupción muy versátil INT 21.
Generar un archivo .COM con Debug
El siguiente ejemplo muestra como se genera una interrupción de software. En este caso, utilizaremos el servicio 2 de la interrupción 21 de DOS. Para realizar este ejemplo, cree un archivo con el nombre EJ1.TXT utilizando un procesador de textos que genere código ASCII (EDIT de DOS o NOTEPAD de Windows). Luego, ingrese el conjunto de instrucciones tal como figuran en el ejemplo, sin cambiar nada.
Nej1.com
A
MOV AH,02
MOV DL,53
INT 21
MOV AX,004C
INT 21
RCX
B
W
Q
Recuerden que todos los números con los que trabaja el programa DEBUG son hexadecimales.
Explicación
Mediante el comando N le indicamos a DEBUG que cree un archivo con el nombre “ej1.com”. Luego, mediante el comando A le ordenamos que convierta las instrucciones que siguen (a dicho comando) en código de máquina (binario), luego de las instrucciones escritas en lenguaje ensamblador, hay una línea en blanco. Esta línea debe estar en blanco. Luego, está el comando RCX que permite cargar un valor en el registro CX, en este caso B hexadecimal (11 decimal). Luego, el comando W ordena a DEBUG grabar en el archivo ej1.com la cantidad de bytes que se indicaron en el registro CX, en nuestro caso B bytes. Finalmente el comando Q le orden a DEBUG salir.
Uso del programa
Para ensamblar el programa usaremos el comando debug seguido por el signo "<" y luego el nombre del archivo que creamos previamente, es decir ej1.txt. Observe la Fig. 2 en la cual se muestra la ejecución de este paso, y el despliegue en pantalla que realiza debug.
Fig. 2 Proceso de ensamblado y prueba del programa ej1
El caracter "<" simplemente redirecciona la entrada, en lugar de tomar datos desde el teclado, toma los datos del archivo de textos.
Para probar el "programa" ej1.com que acaba de crear, en la carpeta de trabajo en que se encuentra (C:\DOCUME~1\NUEVA>) teclee el nombre ej1 INTRO y el programa mostrará una S por la pantalla.
Análisis detallado del ejemplo
Pasaremos ahora a analizar el ejemplo anterior, paso a paso, para comprender algunos conceptos de programación en lenguaje ensamblador así como también, la forma en la que trabaja el programa DEBUG.
Preste suma atención a las líneas que exponemos a continuación:
- Línea 1: Nej1.com
El comando N instruye a DEBUG para que cree un archivo con el nombre que aparece a continuación del propio comando N. En nuestro caso, el nombre del archivo es “ej1.com” (es decir, un programa ejecutable).
- Línea 2: A
El comando A ordena a DEBUG convertir a lenguaje de máquina las instrucciones mnemotécnicas propias del lenguaje ensamblador (Ver figura Esquema del proceso de ensamble).
Recordemos que las instrucciones de lenguaje ensamblador se denominan mnemotécnicas por el hecho de que son fáciles de memorizar debido a la similitud que tienen con términos del idioma inglés. Por ejemplo, la instrucción MOV que permite “mover” un valor hacia un registro de la CPU, es claramente reconocible como MOVE (mover en inglés).
- Línea 3: MOV AH,02
Esta es la primera instrucción en lenguaje ensamblador, es decir es una instrucción para el procesador y no para DEBUG. Como previamente debug recibió la instrucción A, al leer el código de la línea 3, si es un código válido, lo convertirá a lenguaje de máquina y lo guardará en memoria.
El procesador de la computadora personal, dispone de un conjunto de registros que utiliza para almacenar, en forma temporal, los datos que procesa. Todos los programas, desde el humilde manejador del ratón hasta Windows Vista terminan convertidos en instrucciones de máquina y valores en los registros del procesador. Los registros disponibles son los siguientes: AX, BX, CX y DX. Estos son registros de 16 bits, que corresponde a 2 bytes o también a 4 números hexadecimales (recuerde que un número hexadecimal corresponde a 4 bits, o sea 1/2 byte).
El procesador, permite además acceder a registros 1 byte que en realidad corresponden al desdoblamiento de los registros de 16 bits que mencionamos antes. Así, el registro AX puede ser manejado también mediante los registros AH y AL. La H de AH indica que el byte es de orden superior; por su parte, la L de AL señala que el byte es de orden inferior. Para entender esto, veamos el siguiente ejemplo: Luego de realizar la instrucción MOV BX,7F5C, tenemos BX=7F5C, entonces BH=7F y BL=5C.
- Línea 4: MOV DL,53
En este caso, la instrucción (a la CPU) indica mover el valor 53 hexadecimal (83 decimal) al registro DL. Incidentalmente, el valor 53 hexadecimal corresponde al carácter S (de Sergio) en código ASCII.
- Línea 5: INT 21
Esta es la instrucción que realiza la interrupción de software. INT 21 es una interrupción de DOS que tiene múltiples aplicaciones o servicios, estos servicios, dependen de los valores contenidos en el registro AH. Veamos los servicios que presta la interrupción para los siguientes valores de AH:
Si AH=0
El sistema ingresa un carácter por el teclado, lo pone en el registro AL y lo muestra por la pantalla.
Si AH=1
Muestra por pantalla el carácter que está en el registro DL. En nuestro ejemplo, DL=53, 83 decimal es decir, la letra S.
- Línea 6: MOV AX,004C
Aquí tenemos un ejemplo del uso de un registro de 16 bits. Observe que lo cargamos con el valor 004C.
- Línea 7: INT 21
Nuevamente la interrupción 21 de DOS, en este caso, el servicio es AX= 004C que sirve para terminar el programa y hacerle saber a DOS que finalizó en forma exitosa. Esta línea y la anterior deberían incluirse siempre en los programas con la excepción de los programas residentes que utilizan un procedimiento diferente.
- Línea 8:
Esta línea se dejó en blanco intencionalmente ya que sirve para sacar a DEBUG del comando A (ensamblar), el cual debe concluir ya que la última línea para ensamblar fue la línea 7.
- Línea 9: RCX
El comando R le indica a debug preparar el registro, en este caso el registro CX para ingresarle un valor. DEBUG utiliza el registro CX para guardar un valor que sirva, en este caso, para indicar la cantidad de bytes en lenguaje de máquina (esta cantidad tuvo que determinarla el autor).
- Línea 10: B
Este es el valor que se cargará en CX. El valor B hexadecimal es igual a 11 decimal y corresponde a la cantidad de bytes que DEBUG grabará como código en lenguaje de máquina (que está en la memoria) en el archivo ej1.com.
- Línea 11: W
El comando W ordena a DEBUG a escribir la cantidad de bytes que indica el registro CX y cerrar el archivo.
- Línea 12: Q
El comando Q ordena a debug termnar su operación y retornar a DOS.
Realicen los ejemplos y en próximas entregas veremos cómo crear nuestros propios servicios de interrupción y cómo dejar programas “residentes” en memoria.
Hasta la próxima entrega,
Sergio Otaño
