Gráficos y sonidos en lenguaje Ensamblador
EL SISTEMA CENTRAL DE ENTRADA-SALIDA DE LAS COMPUTADORAS ATARI
En cualquier sistema informático, los términos Entrada (Input) y Salida (Output) se refieren a la comunicación entre el microprocesador y cualquier dispositivo externo: un teclado, el editor de pantalla, una impresora, una unidad de disco, una grabadora u otro periférico similar. El Sistema Operativo ATARI contiene rutinas para interactuar con cualquiera de estos dispositivos en varios niveles, tal como muchas otras microcomputadoras. El aspecto del sistema ATARI que lo hace único (y, desde el punto de vista del programador, tan fácil de usar) es que todos los dispositivos externos se manejan de manera idéntica y se diferencian sólo cambiando aspectos menores de la rutina de entrada y salida.
La entrada es el paso de información desde el mundo exterior, por ejemplo, desde el teclado al microprocesador. La salida es el proceso inverso, en el que la información pasa del computador al exterior, por ejemplo, a una impresora. A lo largo del resto de este libro, nos referiremos al sistema Central de Entrada - Salida como CIO (Central Input-Output).
LOS VECTORES EN UNA COMPUTADORA ATARI
Mencionamos anteriormente que las técnicas y rutinas utilizadas en este libro funcionan con cualquier modelo de computadora ATARI porque ATARI garantiza que los vectores de las rutinas en el Sistema Operativo no cambiarán. Ubicada dentro del Sistema Operativo de su ATARI hay una Tabla de Saltos (Jump Table), que contiene las direcciones de todas las rutinas clave necesarias para la programación en lenguaje Ensamblador. La tabla se extiende desde $E450 hasta $E47F, y en un ATARI 800 con el Sistema Operativo B, la tabla se ve así:
Dirección | Contiene la instrucción |
---|---|
E450 | JMP $EDEA |
E453 | JMP $EDF0 |
E456 | JMP $E4C4 |
E459 | JMP $E959 |
E45C | JMP $E8ED |
E45F | JMP $E7AE |
E462 | JMP $E905 |
E465 | JMP $E944 |
E468 | JMP $EBF2 |
E46B | JMP $E6D5 |
E46E | JMP $E4A6 |
E471 | JMP $F223 |
E474 | JMP $F11B |
E477 | JMP $F125 |
E47A | JMP $EFE9 |
E47D | JMP $EF5D |
Es fácil ver por qué se llama Tabla de Saltos, ya que es una tabla de direcciones a las que saltará el control del programa cuando se acceda a ellas. "¿Por qué no saltar directamente a la dirección indicada?" se puede preguntar. En la respuesta se encuentra la clave para escribir programas que se ejecutarán en todas las computadoras ATARI. Supongamos que en lugar de acceder a $E456, elegimos saltar directamente a la ubicación $E4C4, sin pasar por la Tabla de Saltos. Todo funcionará bien y nuestro programa se ejecutará. Pero supongamos ahora que ATARI produce una computadora nueva, la 24800 XLTVB, y que el Sistema Operativo necesita modificarse un poco para acomodar varias características de esta magnífica máquina nueva. Nuestro programa está en problemas. ATARI nunca garantizó que la ubicación $E4C4 permanecería igual para siempre; sólo garantizaron que la tabla de salto siempre apuntaría a la dirección correcta. Es decir, si hubiéramos accedido a $E456 en lugar de $E4C4, nuestro programa siempre funcionaría, ya que se garantiza que la ubicación $E456 no cambiará. Veamos los diversos vectores en esta tabla de salto, con sus equivalencias ATARI (Los nombres que usaremos para estas direcciones en cualquier programa que escribamos) y sus usos:
Equivalencia | Dirección | Uso |
---|---|---|
DISKIV | $E450 | Rutina de inicio del administrador del disco |
DSKINV | $E453 | Vector del administrador de disco |
CIOV | $E456 | Vector de entrada/salida central |
SIOV | $E459 | Vector de entrada/salida serial |
SETVBV | $E45C | Establecer el vector de rutina de los temporizadores del sistema |
SYSVBV | $E45F | Procesamiento de interrupción del Blanqueo Vertical del sistema |
XITVBV | $E462 | Salir del procesamiento del Blanqueo Vertical |
SIOINV | $E465 | Inicialización de entrada/salida en serie |
SENDEV | $E468 | Rutina de habilitación del envío del bus serie |
INTINV | $E46B | Rutina de manejo de interrupciones |
CIOINV | $E46E | Inicialización de Entrada/Salida Central |
BLKBDV | $E471 | Vector del modo Pizarra (Blackboard) a modo Bloc de notas |
WARMSV | $E474 | Punto de entrada del arranque en caliente (warm start) (Sigue a SYSTEM RESET) |
COLDSV | $E477 | Punto de entrada del arranque en frío (Sigue al encendido del equipo) |
RBLOKV | $E47A | Vector de rutina de lectura de bloques del cassette |
CSOPIV | $E47D | Vector del cassette abierto para entrada |
Usaremos algunos de estos vectores en los programas que escribiremos y algunos nunca los usaremos, pero saber dónde están le ayudará si necesita utilizarlos en sus propios programas. Muchos son utilizados por el propio Sistema Operativo.
Para acceder a cualquiera de estas rutinas en el Sistema Operativo, simplemente necesitamos enviar un JSR a la dirección adecuada. Todas estas rutinas en el Sistema Operativo están escritas como subrutinas y, por lo tanto, terminan con instrucciones RTS, que devolverán el control a su programa. Por ejemplo, para acceder a CIO, simplemente necesitamos escribir
JSR CIOV
y el trabajo está hecho. Por supuesto, se requiere una cantidad considerable de configuración antes de poder realizar esta llamada, que cubriremos en breve. Pero la llamada real al CIO no podría ser más sencilla.
Mientras analizamos los vectores disponibles en el Sistema Operativo, cubramos brevemente los vectores RAM y ROM. Se resumen en la siguiente tabla, con sus equivalencias, la información que contienen en el Sistema Operativo B y una breve descripción de su uso:
Equivalencia | Dirección | Apunta a | Uso |
---|---|---|---|
CASINI | $0002 | varía | Vector de inicialización del cassette de arranque |
DOSVEC | $000A | varía | Vector de ejecución de software de disco |
DOSINI | $000C | varía | Vector de inicialización de disco |
VDSLST | $0200 | $E7B3 | Vector de la NMI de la DLI |
VPRCED | $0202 | $E7B2 | Vector del IRQ Continuar línea* |
VINTER | $0204 | $E7B2 | Vector del IRQ Interrumpir línea* |
VBREAK | $0206 | $E7B2 | Vector del IRQ de la instrucción BRK |
VKEYBD | $0208 | $FFBE | Vector del IRQ del Teclado |
VSERIN | $020A | $EB11 | Vector del IRQ de la Entrada serie lista |
VSEROR | $020C | $EA90 | Vector del IRQ de la Salida serie lista |
VSEROC | $020E | $EAD1 | Vector del IRQ de la Salida serie terminada |
VTIMR1 | $0210 | $E7B2 | Vector del IRQ del Temporizador 1 del POKEY |
VTIMR2 | $0212 | $E7B2 | Vector del IRQ del Temporizador 2 del POKEY |
VTIMR4 | $0214 | $E7B2 | Vector del IRQ del Temporizador 4 del POKEY |
VIMIRQ | $0216 | $E6F6 | Administrador del vector a la IRQ |
VVBLKI | $0222 | $E7D1 | Vector de la NMI del VBI Inmediato |
VVBLKD | $0224 | $E93E | Vector de la NMI del VBI Diferido |
CDTMA1 | $0226 | varía | Dirección del JSR del temporizador 1 del sistema |
CDTMA2 | $0228 | varía | Dirección del JSR del temporizador 1 del sistema |
BRKKY | $0236 | $E754 | Vector de la tecla BREAK, sólo para la versión B del Sistema Operativo |
RUNVEC | $02E0 | varía | Vector de carga y ejecución |
INIVEC | $02E2 | varía | Vector de inicialización de carga y ejecución |
Los marcados con "*" no se utilizan actualmente. Observe que varios de estos vectores apuntan al mismo lugar en el Sistema Operativo: $E7B3. Esta es la dirección de la rutina central del procesamiento de interrupciones, que determina la naturaleza de la interrupción y dirige el control del programa a las rutinas apropiadas en el Sistema Operativo para manejar ese tipo de interrupción.
Estos vectores, a diferencia de los vectores ROM, no están organizados en una tabla de salto, por lo que no se puede acceder a ellos mediante una simple instrucción JSR. Sin embargo, sí apuntan a rutinas del Sistema Operativo que terminan en una instrucción RTS, por lo que podremos acceder a ellas mediante una instrucción JSR. El método adecuado es configurar un JSR en una ubicación que hace JMP indirectamente al vector anterior. Por ejemplo, supongamos que queremos vectorizar a través del vector DOSINI. Esto se hace correctamente con el siguiente código:
40 JSR MYSPOT 45 50 55 60 MYSPOT JMP (DOSINI)
Siguiendo el JSR a MYSPOT, el RTS en la rutina del Sistema Operativo devolverá el control a la línea 45, momento en el cual su programa se reanudará.
Ahora que hemos visto cómo escribir programas que funcionarán en todas las computadoras ATARI, analicemos la filosofía del CIO y aprendamos a escribir programas que interactúen con el mundo real.
EL BLOQUE DE CONTROL DE ENTRADA-SALIDA (IOCB - INPUT/OUTPUT CONTROL BLOCK)
Hay dos partes en el sistema CIO en el ATARI: El Bloque de Control de Entrada - Salida (Input-Output Control Block), o IOCB, y la Tabla de Controladores (handler table). Analicémoslos uno por uno y luego veamos cómo trabajan en conjunto para formar un sistema CIO operativo. El IOCB es una sección de la memoria en la Página 3 que contiene la información configurada por el programador para decirle al ATARI qué dispositivo se desea usar y qué información se debe pasar. Cada IOCB requiere 16 bytes de información y tenemos 8 IOCB disponibles. Sus nombres y ubicaciones son las siguientes:
Nombre | Ubicación |
---|---|
IOCB0 | $340 a 34F |
IOCB1 | $350 a 35F |
IOCB2 | $360 a 36F |
IOCB3 | $370 a 37F |
IOCB4 | $380 a 38F |
IOCB5 | $390 a 39F |
IOCB6 | $3A0 a 3AF |
IOCB7 | $380 a 3BF |
El sistema utiliza varios de estos IOCBs por defecto, aunque como programadores somos libres de usarlos o de cambiarlos para adaptarlos a nuestros propios propósitos. De hecho, el Sistema Operativo utiliza normalmente sólo 3 de ellos. Generalmente no es necesario redefinirlos, ya que tenemos otros 5 entre los que elegir. Los 3 utilizados por el Sistema Operativo son los siguientes:
2. IOCB6, la pantalla para modos gráficos superiores a cero. Este IOCB se utiliza para todos los comandos gráficos como PLOT, DRAWTO, FILL y otros.
3. IOCB7, que dirige la salida a la impresora cuando se usa el comando LPRINT de BASIC. En la práctica, gran parte de la salida de BASIC dirigida a una impresora utiliza uno de los otros IOCBs, ya que el comando LPRINT no se utiliza con frecuencia. Hay más opciones de formato disponible si se abre un IOCB específico para su uso con una impresora.
Como probablemente ya habrá reconocido, BASIC usa los números de IOCB (0, 6 y 7) para dirigir la salida a estos dispositivos, como cuando se imprime en una pantalla GRAPHICS 1 ó 2 con este comando:
PRINT #6;"HELLO"
A continuación, se describen los 16 bytes del IOCB y sus desplazamientos a partir del inicio del IOCB en uso:
Etiqueta | Desplazamiento | Longitud | Descripción |
---|---|---|---|
ICHID | 0 | 1 | Índice en la tabla de nombres de dispositivos para este IOCB |
ICDNO | 1 | 1 | Número de dispositivo |
ICCOM | 2 | 1 | Byte de comando: determina la acción a tomar |
ICSTA | 3 | 1 | Estado devuelto por el dispositivo |
ICBAL/H | 4,5 | 2 | Dirección del buffer de dos bytes de la información almacenada |
ICPTL/H | 6,7 | 2 | Dirección - 1 de la rutina de colocación de caracteres del dispositivo |
ICBLL/H | 8,9 | 2 | Longitud del buffer |
ICAX1 | 10 | 1 | Primer byte auxiliar |
ICAX2 | 11 | 1 | Segundo byte auxiliar |
ICAX3/4 | 12,13 | 2 | Bytes auxiliares 3 y 4: para los comandos NOTE y POINT del BASIC |
ICAX5 | 14 | 1 | Quinto byte auxiliar - también para los comandos NOTE y POINT |
ICAX6 | 15 | 1 | Byte auxiliar de reserva – actualmente sin uso |
UN EJEMPLO SIMPLE DE E/S UTILIZANDO UN IOCB
Antes de entrar en los detalles de los distintos bytes necesarios para cada posible función de un IOCB, un programa de ejemplo le ayudará a comprender su uso. Tomemos un ejemplo BASIC simple y convirtámoslo a su equivalente en lenguaje Ensamblador. La línea de programación BASIC que queremos duplicar es:
CLOSE #4:OPEN #4,6,0,"D:*.*"
Por ahora, necesitamos saber que el byte de comando almacenado en ICCOM debe ser $C para el comando CLOSE o 3 para el comando OPEN, y abrir el directorio del disco requiere un 6 en ICAX1. Veamos el programa necesario para abrir dicho archivo:
Listado 9.1
0100 ; ****************************** 0110 ; Primero configuramos las equivalencias 0120 ; ****************************** 0000 0130 *= $600 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0344 0160 ICBAL = $0344 0345 0170 ICBAH = $0345 034A 0180 ICAX1 = $034A E456 0190 CIOV = $E456 0200 ; ****************************** 0210 ; Ahora hacemos CLOSE #4 para estar seguros 0220 ; ****************************** 0600 A240 0230 LDX #$40 ; #$40 para el IOCB #4 0602 A90C 0240 LDA #$C ; byte del comando CLOSE 0604 9D4203 0250 STA ICCOM,X ; X = IOCB #4 0607 2056E4 0260 JSR CIOV ; Deje que CIO haga el CLOSE 0270 ; ****************************** 0280 ; Ahora abriremos el directorio 0290 ; ****************************** 060A A240 0300 LDX #$40 ; Nuevamente, #$40 = IOCB4 060C A901 0310 LDA #1 ; Unidad de disco # 1 060E 9D4103 0320 STA ICDNO,X ; Coloque el # de unidad aqui 0611 A903 0330 LDA #3 ; Para OPEN 0613 9D4203 0340 STA ICCOM,X ; Byte del comando 0616 A906 0350 LDA #6 ; Para directorio de disco 0618 9D4A03 0360 STA ICAX1,X ; Almacene 6 aqui 061B A929 0370 LDA #FILE&255 ; Ver discusion 061D 9D4403 0380 STA ICBAL,X ; Byte bajo de la direccion del buffer 0620 A906 0390 LDA #FILE/256 ; Ver discusion 0622 9D4503 0400 STA ICBAH,X ; Byte alto de la direccion del buffer 0625 2056E4 0410 JSR CIOV ; Deje que CIO haga el OPEN 0628 60 0420 RTS ; Todo listo 0430 ; ****************************** 0440 ; Ahora necesitamos el nombre de archivo 0450 ; ****************************** 0629 44 0460 FILE .BYTE "D:*.*",$9B 062A 3A 062B 2A 062C 2E 062D 2A 062E 9B
Tanto en la parte del CLOSE como en la parte del OPEN del programa, cargamos el registro X con #$40, que actuará como desplazamiento en el IOCB4. (Si quisiéramos usar IOCB3, simplemente cargaríamos el registro X con #$30, y así sucesivamente para todos los demás IOCB). Luego almacenamos el byte de comando $C en ICCOM para ese IOCB y un JSR a CIOV hace el CLOSE por nosotros. Siempre es una buena idea cerrar un archivo antes de abrirlo, en caso de que ya estuviera abierto por algún otro motivo. Si el archivo ya está abierto, recibirá un error en la declaración del CIO. Puede verificar si hay un error en cualquier llamada al Sistema Operativo al pasar a alguna rutina propia de manejo de errores si después del JSR a CIOV, se establece la bandera Negativo en el registro de Estado del Procesador. Por lo tanto, deberíamos poner una instrucción BMI ERROR después de la instrucción JSR CIOV; pero para los propósitos de esta discusión, asumiremos que todo está bien. Sin embargo, nunca debe hacer esa suposición en sus programas.
Para abrir el archivo, ponemos un 1 en el byte ICDNO del IOCB4, para acceder a la unidad de disco 1, y luego ponemos el byte de comando 3 en el byte ICCOM del IOCB4, y ponemos un 6 en el byte ICAX1. Todo lo que nos queda por hacer antes de llamar al CIO es apuntar la dirección del buffer al nombre del archivo que queremos abrir. Este nombre se encuentra en la línea 460 y le hemos asignado la etiqueta FILE. Los $9B que siguen al nombre del archivo son el código hexadecimal para un retorno de carro, que siempre debe seguir a los nombres de archivos o dispositivos, como S: o P:
Para apuntar el buffer al nombre del archivo, necesitamos descomponer su dirección en bytes bajos y altos. El byte bajo es la dirección, a la que le hacemos AND 255, y que se escribe #FILE&255. El AND con 255 garantiza que obtendremos solo el byte bajo de la dirección. El byte alto se puede obtener dividiendo la dirección por 256, tal como hicimos en la línea 390. Los bytes bajo y alto se almacenan en ICBAL e ICBAH, respectivamente, y luego una llamada al CIO en la línea 410 completa el comando OPEN por nosotros.
Este sencillo ejemplo demuestra no sólo cómo abrir un directorio de disco, sino que también muestra exactamente cómo se realiza cada llamada a la rutina CIO en su ATARI. Primero configuramos los bytes apropiados en el IOCB y luego simplemente hacemos JSR a CIOV para realizar la tarea, ya sea abrir un archivo, leer información de un disco o cinta, o enviar información a una impresora. Todas estas operaciones se realizan utilizando esta misma secuencia de eventos, lo que hace que, una vez que comprenda el sistema, la entrada y salida en lenguaje Ensamblador en su ATARI sea tan simple. Tenga en cuenta que no es necesario modificar los 16 bytes del IOCB para realizar una llamada al CIO. De hecho veremos que, para algunos comandos, uno o dos de estos bytes es todo lo que se necesita modificar para implementar la función.
DETALLE DE LOS BYTES EN UN IOCB
Ahora que hemos visto cómo implementar una llamada simple al sistema Central de Entrada y Salida de las computadoras ATARI, revisaremos el espectro completo de información que debe almacenarse en las distintas ubicaciones del IOCB para poder implementar todas las posibles operaciones de E/S. Examinaremos cada byte del IOCB, en el orden en que aparecen.
El primer byte, ICHID, actúa como un índice en la tabla de dispositivos, por lo que siempre puede saber a qué dispositivo accede un IOCB mirando el primer byte. Esto lo establece el Sistema Operativo y no será necesario configurarlo para ningún uso. El Sistema Operativo determina este índice siguiendo el comando OPEN y almacena aquí la información adecuada.
ICDNO, el número de dispositivo, se utiliza con mayor frecuencia cuando hay más de una unidad de disco conectada al sistema. Se utiliza un IOCB diferente para comunicarse con cada unidad de disco, y el byte 2 del IOCB distingue entre las unidades en uso. Si se almacena un 1 aquí, el IOCB accederá a la unidad de disco 1 y de manera similar a las unidades 2 a 4.
Los bytes de comando para los distintos dispositivos que se pueden conectar a su computadora ATARI son los siguientes:
Comando | Byte | Descripción |
---|---|---|
Abrir | 3 | Abrir el dispositivo para su operación |
Obtener registro | 5 | Recibir una línea |
Obtener carácter | 7 | Recibir uno o más caracteres |
Colocar registro | 9 | Enviar una línea |
Colocar carácter | 11 | Enviar uno o más caracteres |
Cerrar | 12 | Cerrar el dispositivo |
Estado | 13 | Obtener estado del dispositivo |
Dibujar línea | 17 | Dibujar una línea en los modos GRAPHICS |
Comando FILL | 18 | Rellenar parte de la pantalla GRAPHICS con color |
Formatear disco | 254 | Formatear disco |
El cuarto byte del IOCB es ICSTA, que lo establece el Sistema Operativo tras el retorno del CIO. El estado también se establece en el registro Y al regresar de cualquier llamada al CIO, por lo que su programa puede leer el registro Y o ICSTA para determinar el éxito o el fracaso de cada operación de E/S. Cualquier estado negativo (valor superior a 128 decimal o $80 hexadecimal) indica que se produjo un error en la operación de E/S.
Los siguientes 2 bytes del IOCB actúan como un puntero al buffer utilizado para entrada o salida, y están en el orden habitual del 6502, es decir, el byte bajo primero: Se denominan ICBAL e ICBAH, respectivamente. Un buffer es un área de memoria que contiene la información que desea generar o en la que desea colocar la información de entrada. Por ejemplo, si desea enviar texto a una impresora, ICBAL e ICBAH están configurados para apuntar al área de memoria que contiene el texto que se va a imprimir. Si desea leer un archivo de disco en la memoria, estos bytes del IOCB están configurados para apuntar al área de la memoria donde desea colocar la información que se leerá del disco.
ICPTL e ICPTH actúan como otro puntero de 2 bytes; pero en este caso, apuntan a la dirección de la rutina de colocación de bytes del dispositivo, menos 1. Cada dispositivo que pueda abrirse para salida debe tener escrita una rutina de colocación de bytes, para que le diga a la computadora cómo enviarle información. Esto se cubrirá más detalladamente cuando analicemos la Tabla de Controladores.
Los siguientes 2 bytes del IOCB son ICBLL e ICBLH, que contienen la longitud del buffer de E/S, en bytes. Como veremos, hay un caso especial de E/S en el que igualamos la longitud del buffer a cero, estableciendo tanto ICBLL como ICBLH en cero. En este caso especial, la información transferida es hacia o desde el Acumulador, en lugar de hacia o desde la Memoria.
Dado que muchos dispositivos que se pueden conectar a su ATARI tienen varias funciones posibles, se debe poder definir en el IOCB la función que se implementará. Esto se hace usando el byte en ICAX1, el siguiente byte del IOCB. La siguiente tabla enumera los distintos bytes posibles para ICAX1. "TW" se refiere a una ventana de texto separada en la pantalla, como la configurada por el comando BASIC GRAPHICS 3; "RE" se refiere a una operación de LECTURA habilitada desde la pantalla; y "RD" significa que dicha LECTURA no está permitida o está deshabilitada.
Dispositivo | Byte de ICAX1 | Función |
---|---|---|
Editor de pantalla | 8 | Salida a la pantalla |
12 | Entrada desde el teclado y salida a la pantalla | |
13 | Entrada y salida de pantalla forzada | |
Pantalla | 8 | La pantalla se borra; sin TW; RD |
12 | La pantalla se borra; sin TW; RE | |
24 | La pantalla se borra; TW; RD | |
28 | La pantalla se borra; TW; RE | |
40 | La pantalla no se borra; sin TW; RD | |
44 | La pantalla no se borra; sin TW; RE | |
56 | La pantalla no se borra; TW; RD | |
60 | La pantalla no se borra; TW; RE | |
Teclado | 4 | Lectura – nota: no es posible salida |
Impresora | 8 | Escritura – nota: no es posible entrada |
Grabadora de cinta | 4 | Lectura |
8 | Escritura | |
Puerto RS-232 | 5 | Lectura simultánea |
8 | Escritura en bloque | |
9 | Escritura simultánea | |
13 | Lectura y escritura simultáneas | |
Unidad de disco | 4 | Lectura |
6 | Leer directorio del disco | |
8 | Escribir archivo nuevo | |
9 | Escribir – agregar | |
12 | Leer y escribir – modo de actualización |
El último byte del IOCB que discutiremos aquí es ICAX2, el segundo byte auxiliar. ICAX2 se utiliza sólo en unos pocos casos especiales; en caso contrario, se establece en cero. Cuando se utiliza la grabadora de cassettes, si se almacena el valor 128 en ICAX2, se utilizan los espacios cortos entre grabaciones (los espacios silenciosos entre secciones de información en la cinta), lo que permitirá cargas más rápidas de una cinta escrita de esta manera. El valor cero en ICAX2 producirá los espacios entre registros (Inter Record Gaps - IRG) normales y más largos.
Al utilizar la impresora ATARI 820, almacenar un valor de 83 en ICAX2 hace que la impresora imprima de costado en lugar de su modo normal. Además, los valores de 70 u 87 en ICAX2 producirán caracteres normales o de doble ancho en esta impresora.
Finalmente, los modos gráficos 0 al 11 se especifican en el comando OPEN colocando el número del modo deseado en ICAX2. En combinación con los valores descritos anteriormente para ICAX1, ICAX2 le brinda al programador de lenguaje Ensamblador el control completo sobre el modo gráfico, la ventana de texto, la limpieza de pantalla y las funciones de lectura y escritura de la pantalla. Aprenderemos más sobre esto en el Capítulo 10.
LA TABLA DE CONTROLADORES (HANDLERS)
Ahora que hemos cubierto las diversas partes de un IOCB, describiremos brevemente la Tabla de Controladores y cómo funciona con los IOCB para formar el sistema de E/S con CIO. Luego veremos una serie de ejemplos que mostrarán cómo utilizar esta información para realizar muchos tipos diferentes de E/S desde el lenguaje Ensamblador. La forma más sencilla de examinar la Tabla de Controladores es visualizarla como un programa breve en lenguaje Ensamblador, como este:
0100 PRINTV = $E430 0110 CASETV = $E440 0120 EDITRV = $E400 0130 SCRENV = $E410 0140 KEYBDV = $E420 0150 ; ***************************** 0160 ; Origen de HATABS = $031A 0170 ; ***************************** 0180 *= $031A 0190 .BYTE "P" ; Vector de la 0200 .WORD PRINTV ; Impresora 0210 .BYTE "C" ; Vector de la 0220 .WORD CASETV ; Grabadora de cassette 0230 .BYTE "E" ; Vector del 0240 .WORD EDITRV ; Editor 0250 .BYTE "S" ; Vector de la 0260 .WORD SCRENV ; Pantalla 0270 .BYTE "K" ; Vector del 0280 .WORD KEYBDV ; Teclado 0290 .BYTE 0 ; Entrada libre #1(DOS) 0300 .WORD 0,0 0310 .BYTE 0 ; Entrada libre #2(Interfaz 850) 0320 .WORD 0,0 0330 .BYTE 0 ; Entrada libre #3 0340 .WORD 0,0 0350 .BYTE 0 ; Entrada libre #4 0360 .BYTE 0,0 0370 .BYTE 0 ; Entrada libre #5 0380 .WORD 0,0 0390 .BYTE 0 ; Entrada libre #6 0400 .WORD 0,0 0410 .BYTE 0 ; Entrada libre #7 0420 .WORD 0,0
Cada entrada en la Tabla de Controladores consta de la primera letra del dispositivo especificado, seguida del vector que apunta a la ubicación en memoria de la información necesaria para tratar con ese dispositivo. Como puede ver, quedan siete lugares libres en la Tabla de Controladores, por lo que el programador es libre de agregar los dispositivos que sean necesarios para cualquier propósito y serán tratados como los dispositivos ya especificados. Cabe señalar aquí otro punto muy importante sobre la Tabla de Controladores: Cada vez que el Sistema Operativo mira esta tabla para averiguar en qué parte de la memoria necesita buscar para ocuparse de un dispositivo en particular, lee la tabla de abajo hacia arriba. Esto es intencional y le permite insertar su propio controlador de impresora cerca de la parte inferior de la tabla. A medida que se busca en la tabla, su vector se encontrará primero y será el que se utilizará. Por lo tanto, puede escribir sus propias rutinas de manejo de impresora y sustituirlas por las rutinas normales fácilmente, simplemente colocando otra P: en una de las entradas libres inferiores y siguiéndola por el vector de dirección de 2 bytes que apunta a sus nuevas rutinas de manejo.
Veamos brevemente el punto de entrada de una Tabla de Controladores típica, que es la tabla a la que apunta la entrada en la Tabla de Controladores. Por ejemplo, el vector PRINTV, utilizado anteriormente, apunta a una segunda tabla: la tabla de punto de entrada del controlador de impresora. De hecho, todos los vectores anteriores apuntan a sus respectivas tablas de punto de entrada del controlador, y todas estas tablas están organizadas de manera idéntica. Contienen las direcciones menos 1 de las rutinas utilizadas para las siguientes funciones, en el siguiente orden:
OPEN | Rutina de apertura del dispositivo |
CLOSE | Rutina de cierre del dispositivo |
READ | Rutina de lectura |
WRITE | Rutina de escritura |
STATUS | Rutina de estado del dispositivo |
SPECIAL | Funciones especiales, cuando estén implementadas |
La tabla de puntos de entrada del controlador siempre termina con una instrucción JMP de 3 bytes, que apunta a la rutina de inicialización para ese dispositivo. Recuerde: Las direcciones encontradas en la tabla de puntos de entrada del controlador no apuntan a las rutinas OPEN y CLOSE, sino que apuntan a la dirección 1 byte más abajo en la memoria del comienzo de cada una de estas rutinas. ¡Obviamente es muy importante recordar esto cuando construya su propia tabla de puntos de entrada del controlador!
UNA RUTINA DE E/S SIMPLE
Veamos cómo podemos usar CIO para una función simple: escribir en la pantalla. Sabemos que, si queremos escribir una línea de texto en la pantalla, en BASIC todo lo que se requiere es una sola línea de código como esta:
PRINT "A SUCCESSFUL WRITE!"
En lenguaje Ensamblador, también es bastante sencillo imprimir en la pantalla, ahora que entendemos el uso de IOCB y de CIO. Solo para repasar, no tenemos que abrir la pantalla como dispositivo si no lo queremos, ya que el Sistema Operativo le asigna el IOCB0 a la pantalla. Por lo tanto, podemos cargar el registro X con cero y usarlo como desplazamiento en el IOCB. Alternativamente, podemos usar direccionamiento Absoluto, ya que usaremos el primer IOCB. En el siguiente ejemplo, usaremos el registro X cargado con cero, para familiarizarnos con el procedimiento normal para insertar la información requerida en el IOCB. Aquí está la rutina para escribir la línea en la pantalla:
Listado 9.2
0100 ; ***************************** 0110 ; Equivalencias CIO 0120 ; ***************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A 0230 ICAXl = $034A 034B 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 *= $600 0270 ; ***************************** 0280 ; Ahora cargamos los datos requeridos 0290 ; ***************************** 0600 A200 0300 LDX #0 ; Ya que es IOCB0 0602 A909 0310 LDA #9 ; Para poner registro 0604 9D4203 0320 STA ICCOM,X ; Byte del comando 0607 A91F 0330 LDA #MSG&255 ; Byte bajo de MSG 0609 9D4403 0340 STA ICBAL,X ; en ICBAL 060C A906 0350 LDA #MSG/256 ; Byte alto de MSG 060E 9D4503 0360 STA ICBAH,X ; en ICBAH 0611 A900 0370 LDA #0 ; Largo de MSG 0613 9D4903 0380 STA ICBLH,X ; byte alto 0616 A9FF 0390 LDA #$FF ; Largo de MSG 0618 9D4803 0400 STA ICBLL,X ; Ver discusion 0410 ; ***************************** 0420 ; Ahora pongalo en la pantalla 0430 ; ***************************** 061B 2056E4 0440 JSR CIOV 061E 60 0450 RTS 0460 ; ***************************** 0470 ; El mensaje en si 0480 ; ***************************** 061F 41 0490 MSG .BYTE "A SUCCESSFUL WRITE!",$9B 0620 20 0621 53 0622 55 0623 43 0624 43 0625 45 0626 53 0627 53 0628 46 0629 55 062A 4C 062B 20 062C 57 062D 52 062E 49 062F 54 0630 45 0631 21 0632 9B
Por supuesto, escribir en la pantalla es tan simple en BASIC que no hay razón para escribir este programa como una subrutina BASIC, por lo que no contiene la instrucción PLA habitual. Dado que necesitará imprimir en la pantalla para depurar programas en lenguaje Ensamblador, esta rutina puede convertirse en uno de los programas que utilice con más frecuencia.
Para probar este programa una vez que lo haya tipeado, simplemente escriba ASM para ensamblarlo, y cuando se complete el ensamblaje, escriba BUG para ingresar al modo DEBUG del cartucho Assembler/Editor. Luego escriba G600 para comenzar la ejecución en la dirección $600. Si el programa se ha escrito correctamente, debería aparecer la frase "A SUCCESSFUL WRITE!", seguido de la impresión en pantalla de los registros del 6502. Estos se imprimen siguiendo cada rutina que utiliza el cartucho Assembler/Editor. Este mismo procedimiento debe usarse para probar cada una de las rutinas dadas en este libro. Si surgen problemas, revise lo escrito.
En este programa, escribimos el mensaje completo en la pantalla usando el comando "poner registro (put-record)", por medio del almacenamiento de un 9 en ICCOM. La dirección del mensaje que queremos mostrar en la pantalla se almacena en ICBLL e ICBLH, como antes. Almacenamos un cero en el byte alto de la longitud del mensaje, pero $FF en el byte bajo.
¿Por qué $FF cuando el mensaje tiene sólo 20 bytes de longitud? Cuando se utiliza CIO en el modo "poner registro", el registro se genera byte a byte, hasta que se excede la longitud del buffer, establecida en ICBLL e ICBLH, o hasta que encuentra un RETURN en el registro que se genera. Tenga en cuenta que el mensaje que se configuró en la línea 490 termina con el byte $9B, que es un RETURN. Por lo tanto, el mensaje se enviará a la pantalla, luego se enviará un retorno de carro a la pantalla y luego finalizará la rutina. Establecimos la longitud del registro intencionalmente mayor al mensaje real, porque queremos que los $9B en el mensaje mismo terminen con la salida. De esta manera, no podemos cometer un error y sin querer acortar el mensaje debido a que configuramos ICBLL o ICBLH en un tamaño más pequeño de lo que pretendíamos.
Es importante tener en cuenta que no configuramos todos los bytes en el IOCB. De hecho, sólo necesitábamos configurar los bytes que utiliza nuestra rutina particular. Como verá a continuación, este es siempre el caso con las rutinas centrales de ATARI. La salida real a la pantalla se logra mediante la llamada a la rutina de E/S central en la línea 440, y el RTS en la línea siguiente devuelve el control al cartucho Assembler/Editor. Si esto fuera parte de un programa más grande en lenguaje Ensamblador, el resto del programa continuaría desde la línea 450 sin el RTS.
OTRAS FORMAS DE UNA RUTINA DE E/S
Veamos otra forma de escribir un mensaje en la pantalla, usando el sistema CIO. En lugar de cargar ICCOM con 9, para "poner registro", podemos cargarlo con 11, para "poner bytes". Los demás bytes del IOCB se configuran tal como se indica arriba, excepto ICBLL, que se configura con la longitud exacta del mensaje. Al contar los bytes del mensaje, no olvide incluir el byte $9B, el RETURN. El programa entonces se verá así:
Listado 9.3
0100 ; ***************************** 0110 ; Equivalencias CIO 0120 ; ***************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A 0230 ICAX1 = $034A 034B 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 *= $600 0270 ; ***************************** 0280 ; Ahora cargamos los datos requeridos 0290 ; ***************************** 0600 A200 0300 LDX #0 ; Ya que es IOCB0 0602 A90B 0310 LDA #11 ; Para poner bytes 0604 9D4203 0320 STA ICCOM,X ; Byte de comando 0607 A91F 0330 LDA #MSG&255 ; Byte bajo de MSG 0609 9D4403 0340 STA ICBAL,X ; en ICBAL 060C A906 0350 LDA #MSG/256 ; Byte alto de MSG 060E 9D4503 0360 STA ICBAH,X ; en ICBAH 0611 A900 0370 LDA #0 ; Largo de MSG 0613 9D4903 0380 STA ICBLH,X ; byte alto 0616 A914 0390 LDA #20 ; Largo de MSG 0618 9D4803 0400 STA ICBLL,X ; byte bajo 0410 ; ***************************** 0420 ; Ahora pongalo en la pantalla 0430 ; ***************************** 061B 2056E4 0440 JSR CIOV 061E 60 0450 RTS 0460 ; ***************************** 0470 ; El mensaje en sí 0480 ; ***************************** 061F 41 0490 MSG .BYTE "A SUCCESSFUL WRITE!",$9B 0620 20 0621 53 0622 55 0623 43 0624 43 0625 45 0626 53 0627 53 0628 46 0629 55 062A 4C 062B 20 062C 57 062D 52 062E 49 062F 54 0630 45 0631 21 0632 9B
Este programa consigue exactamente el mismo fin que la rutina anterior, pero de una forma diferente. Ambos programas escriben un mensaje en la pantalla que termina con un retorno de carro. Hay un caso especial de escritura en la pantalla en el que no queremos que el texto vaya seguido de un RETURN, como cuando solicitamos el ingreso de datos o cuando nos gustaría formatear la pantalla de una manera particular. En BASIC, esta instrucción es simplemente una instrucción PRINT seguida de un punto y coma, lo que inhibe el retorno de carro normal después de un comando PRINT. Si, por ejemplo, queremos imprimir un símbolo > en la pantalla para solicitar al usuario el ingreso de datos, pero queremos que el cursor permanezca en la misma línea que ese símbolo, en BASIC podemos escribir la siguiente línea para realizar esta tarea:
PRINT ">";
En la programación en lenguaje Ensamblador, el código es el siguiente:
Listado 9.4
0100 ; ****************************** 0110 ; Equivalencias CIO 0120 ; ****************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A 0230 ICAX1 = $034A 034B 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 *= $600 0270 ; ***************************** 0280 ; Ahora cargamos los datos requeridos 0290 ; para el caso especial de 1 carácter 0300 ; ***************************** 0600 A200 0310 LDX #0 ; Ya que es IOCB0 0602 A90B 0320 LDA #11 ; Para poner bytes 0604 9D4203 0330 STA ICCOM,X ; Byte de comando 0607 A900 0340 LDA #0 ; Largo de MSG 0609 9D4903 0350 STA ICBLH,X ; byte alto 060C A900 0360 LDA #0 ; Largo de MSG 060E 9D4803 0370 STA ICBLL,X ; byte bajo 0380 ; ***************************** 0390 ; Ahora pongalo en la pantalla 0400 ; ***************************** 0611 A93E 0410 LDA #62 ; Para el simbolo > 0613 2056E4 0420 JSR CIOV 0616 60 0430 RTS
Si establecemos que la longitud del buffer sea igual a cero (estableciendo los bytes alto y bajo, ICBLL e ICBLH, en cero), entonces cuando se accede a CIOV, el carácter contenido en el Acumulador se imprimirá en el dispositivo de salida sin un siguiente retorno de carro. Esto se aplica a todos los dispositivos, incluidas las unidades de disco, las grabadoras, las impresoras y la pantalla, y señala una característica muy importante de las computadoras ATARI: La entrada y la salida son en gran medida independientes del dispositivo. Es decir, el Sistema Operativo trata a todos los dispositivos de manera similar, por lo que no tenemos que aprender a escribir un mensaje en la pantalla, luego aprender una forma diferente de enviar información a la impresora y aprender otro método más para pasar información a la unidad de disco. El método es idéntico una vez que se ha abierto el IOCB del dispositivo. Para demostrar esto, veremos una rutina para enviar el mismo mensaje a una impresora.
SALIDA A UNA IMPRESORA
Primero cerramos IOCB2, sólo para estar seguros; luego abrimos la impresora como dispositivo usando IOCB2; y luego enviamos nuestro mensaje.
Listado 9.5
0100; ******************************* 0110 ; Equivalencias CIO a 0120; ***************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A ICAX1 = $034A 034B 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 * = $600 0270 ; ******************************* 0280 ; Primero cierre y abra IOCB2 0290; ***************************** 0600 A220 0300 LDX #$20; Para el IOCB2 0602 A90C 0310 LDA #12; Comando de cierre 0604 9D4203 0320 STA ICCOM,X; En ICCOM 0607 2056E4 0330 JSR CIOV; Haga CLOSE 060A A220 0340 LDX #$20; IOCB2 nuevamente 060C A903 0350 LDA #3; Abrir archivo 060E 9D4203 0360 STA ICCOM,X; ¿El comando es 0611 A908 0370 LDA #8; Salida 0613 9D4A03 0380 STA ICAX1,X ; Abierto para salida 0616 A94C 0390 LDA #NAM&255 ; Byte bajo del dispositivo 0618 9D4403 0400 STA ICBAL,X; Señala "P:" 061B A906 0410 LDA #NAM/256 ; Byte alto 061D 9D4503 0420 STA ICBAH,X 0620 A900 0430 LDA #0 0622 9D4903 0440 STA ICBLH,X; Longitud de byte alta 0625 A9FF 0450 LDA #$FF 0627 9D4803 0460 STA ICBLL,X ; Longitud de byte baja 062A 2056E4 0470 JSR CIOV; Haga el OPEN 0480; ******************************* 0490 ; Ahora imprimimos el mensaje 0500; ***************************** 062D A220 0510 LDX #$20; Utilizando IOCB2 062F A909 0520 LDA #9; Poner registro 0631 9D4203 0530 STA ICCOM,X ; Comando 0634 A94F 0540 LDA #MSG&255; Dirección de MSG 0636 9D4403 0550 STA ICBAL,X ; Byte bajo 0639 A906 0560 LDA #MSG/256; Dirección de MSG 063B 9D4503 0570 STA ICBAH,X ; Byte alto 063E A900 0580 LDA #0; Longitud de MSG 0640 9D4903 0590 STA ICBLH,X ; byte alto 0643 A9FF 0600 LDA#$FF; Longitud de MSG 0645 9D4803 0610 STA ICBLL,X ; Byte bajo 0648 2056E4 0620 JSR CIOV; Ponga la línea 064B 60 0630 RTS ; Fin de rutina 064C 50 0640 NAM .BYTE "P:",$9B 064D 3A 064E 9B 064F 41 0650 MSG .BYTE "¡UNA ESCRITURA EXITOSA!",$9B 0650 20 0651 53 0652 55 0653 43 0654 43 0655 45 0656 53 0657 53 0658 46 0659 55 065A 4C 065B 20 065C 57 065D 52 065E 49 065F 54 0660 45 0661 21 0662 9B
Listing 9.5
0100 ; ***************************** 0110 ; Equivalencias CIO 0120 ; ***************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A 0230 ICAX1 = $034A 034B 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 *= $600 0270 ; ***************************** 0280 ; Primero, cierre y abra IOCB2 0290 ; ***************************** 0600 A220 0300 LDX #$20 ; Para el IOCB2 0602 A90C 0310 LDA #12 ; Comando de cierre 0604 9D4203 0320 STA ICCOM,X ; En ICCOM 0607 2056E4 0330 JSR CIOV ; Haga CLOSE 060A A220 0340 LDX #$20 ; IOCB2 nuevamente 060C A903 0350 LDA #3 ; Abrir archivo 060E 9D4203 0360 STA ICCOM,X ; ?El comando es? 0611 A908 0370 LDA #8 ; Salida 0613 9D4A03 0380 STA ICAX1,X ; Abrir para salida 0616 A94C 0390 LDA #NAM&255 ; Byte bajo del dispositivo 0618 9D4403 0400 STA ICBAL,X ; Apunta a "P:" 061B A906 0410 LDA #NAM/256 ; Byte alto 061D 9D4503 0420 STA ICBAH,X 0620 A900 0430 LDA #0 0622 9D4903 0440 STA ICBLH,X ; Largo del byte alto 0625 A9FF 0450 LDA #$FF 0627 9D4803 0460 STA ICBLL,X ; Largo del byte bajo 062A 2056E4 0470 JSR CIOV ; Haga el OPEN 0480 ; ***************************** 0490 ; Ahora imprimimos el mensaje 0500 ; ***************************** 062D A220 0510 LDX #$20 ; Utilizando IOCB2 062F A909 0520 LDA #9 ; Poner registro 0631 9D4203 0530 STA ICCOM,X ; Comando 0634 A94F 0540 LDA #MSG&255 ; Direccion de MSG 0636 9D4403 0550 STA ICBAL,X ; Byte bajo 0639 A906 0560 LDA #MSG/256 ; Direccion de MSG 063B 9D4503 0570 STA ICBAH,X ; Byte alto 063E A900 0580 LDA #0 ; Largo de MSG 0640 9D4903 0590 STA ICBLH,X ; Byte alto 0643 A9FF 0600 LDA #$FF ; Largo de MSG 0645 9D4803 0610 STA ICBLL,X ; Byte bajo 0648 2056E4 0620 JSR CIOV ; Ponga la linea 064B 60 0630 RTS ; Fin de la rutina 064C 50 0640 NAM .BYTE "P:",$9B 064D 3A 064E 9B 064F 41 0650 MSG .BYTE "A SUCCESSFUL WRITE!",$9B 0650 20 0651 53 0652 55 0653 43 0654 43 0655 45 0656 53 0657 53 0658 46 0659 55 065A 4C 065B 20 065C 57 065D 52 065E 49 065F 54 0660 45 0661 21 0662 9B
Por supuesto, si está utilizando una impresora ATARI y desea imprimir usando texto expandido, tendrá que configurar ICAX1 e ICAX2 antes de la llamada final a CIOV, pero eso es trivial. Tenga en cuenta que no hemos hecho CLOSE del IOCB2 después de la impresión de nuestro mensaje por lo que, si queremos imprimir algo más, simplemente podemos enviarlo a través del IOCB2 sin necesidad de hacer OPEN nuevamente. Por supuesto, eso también significa que ahora no podemos usar IOCB2 para nada más como, por ejemplo, el acceso al disco. Si necesitamos acceder al disco, podemos usar uno de los otros IOCB, o podemos hacer CLOSE del IOCB2 primero y luego volver a hacerle OPEN para nuestra operación de disco.
Tenga en cuenta que, si queremos que el cabezal de impresión se detenga después de imprimir un solo carácter, sin un retorno de carro final, podemos usar el caso especial de buffer de longitud cero, tal como lo hicimos anteriormente para la pantalla.
SALIDA A UN DISCO
Para mostrar la versatilidad de las rutinas centrales de E/S del ATARI, ni siquiera le daremos aquí al programa la posibilidad de escribir la misma línea en un archivo de disco. Se describirá el método y podrá enviar información a su disco en el primer intento, ¡todo usted mismo! El único cambio necesario en el programa proporcionado anteriormente para la impresora es este: para usar la unidad de disco, el nombre del dispositivo es el archivo de disco que desea abrir. Por lo tanto, el programa es idéntico al anterior, pero la línea 640 debería leerse algo como:
640 NAM .BYTE "D1:MYFILE.1",$9B
Eso es todo al respecto. Puede ver la belleza de usar rutinas CIO idénticas para todos los dispositivos. Ahora puede enviar información a cualquier dispositivo de su elección en lenguaje Ensamblador.
ENTRADA USANDO CIOV
El método para ingresar datos desde un dispositivo a su computadora ATARI es exactamente el mismo que para la salida, pero el dispositivo debe estar abierto para la entrada. Podríamos, por ejemplo, recuperar el mensaje anterior de nuestro archivo en disco "D1:MYFILE.1" abriendo este archivo para entrada, usando un 3 en ICCOM y un 4 en ICAX1, y apuntando ICBAL e ICBAH a la ubicación en la memoria para que se transfiera el mensaje. Por ejemplo, si queremos que el mensaje comience en la ubicación de memoria $680, estableceríamos ICBAL en #$80 e ICBAH en #6. Después de la llamada a CIOV, las ubicaciones de memoria $680 a $694 contendrán los bytes del mensaje, que luego podrán ser examinados por el resto de nuestro programa.
No se debe subestimar la facilidad y simplicidad de la E/S en las computadoras ATARI. En otras microcomputadoras, aprender a utilizar cada dispositivo es una tarea aparte. La entrada y la salida pueden utilizar diferentes rutinas, cada una con sus propias peculiaridades. La filosofía de la E/S Central utilizada en los ATARI nos simplifica enormemente este proceso. Ahora puede utilizar este sistema para mejorar enormemente sus habilidades de programación en lenguaje Ensamblador.
Una nota final sobre las rutinas de E/S: Si abrimos un IOCB para la entrada desde un archivo en la unidad de disco y un segundo IOCB para la salida a una impresora o a la pantalla, sería una tarea trivial transferir información muy rápidamente desde un dispositivo a otro apuntando al mismo buffer para ambos IOCB. Imprimir una copia impresa y copiar la memoria a un archivo de disco es simple. Incluso podemos transferir información desde la unidad de disco a la pantalla o a una grabadora.
ENTRADAS Y SALIDAS NO-CIO
TRES SISTEMAS DE E/S DIFERENTES
Además de CIO, existen otros dos métodos para utilizar la unidad de disco como dispositivo de entrada y salida. Ambos residen en el Sistema Operativo. Utilizan vectores llamados DSKINV y SIOV, a $E453 y $E459, respectivamente.
Los tres métodos de E/S de disco pueden verse como una cebolla, con múltiples capas de control. La capa exterior, que hace la mayor parte del trabajo por usted, es el sistema CIO; la capa intermedia, que hace parte del trabajo por usted, es el sistema DSKINV; y la capa interna, en la que el programador hace todo el trabajo, es el sistema SIOV. De hecho, SIOV, el vector de entrada-salida en serie (Serial Input-Output Vector), se utiliza para todas las comunicaciones que tienen lugar a través del bus serie, el conector de 13 clavijas en el costado de su computadora ATARI. Incluso el sistema CIO realiza las operaciones de entrada y salida reales llamando a SIO, después de usar la información en el IOCB para configurar todo para SIO. DSKINV, sobre el cual aprenderemos más en breve, también llama a SIO para realizar la E/S real.
TIPOS DE ARCHIVOS DE DISCO
Un disquete para su unidad de disco ATARI contiene 40 pistas concéntricas, algo así como un disco fonográfico. En un disco, sin embargo, las pistas son en realidad una espiral continua mientras que, en un disquete, cada pista es un círculo separado. Cada pista está dividida en 18 sectores. Para imaginar esto, imagine cortar el disco como una pizza, con 18 porciones iguales. Luego corte la pizza en 40 círculos concéntricos, como una diana con 40 anillos de diferentes colores. Cada trozo de pizza es un sector. Tendremos 18 X 40, o 720 sectores. En cada uno de los sectores, el ATARI puede almacenar 128 bytes de información.
En el proceso de formatear un disco, no solo se crean los 720 sectores, sino que también se crean una Tabla de Contenido del Volumen (VTOC - Volume Table Of Contents) y un directorio de disco. El directorio del disco actúa como la tabla de contenido de un libro, enumerando cada archivo (Capítulo) contenido en el disco y el número de sector (página) donde se puede encontrar ese archivo. El VTOC realiza un seguimiento de qué sectores ya se han llenado y cuáles permanecen vacíos, de modo que cuando guardamos un nuevo archivo en un disco parcialmente lleno, no sobrescribimos la información ya almacenada en otro archivo. Cuando eliminamos un archivo, sus sectores quedan liberados en el VTOC para poder volver a utilizarlos. Tenga en cuenta que cuando se elimina un archivo, en realidad solo se cambia 1 byte del archivo: el byte de estado o bandera. Los primeros cinco bytes de la entrada del directorio del disco para cada archivo son:
Bit 5 establecido en 1 si el archivo está bloqueado
Bit 6 establecido en 1 si el archivo está en uso
Bit 7 establecido en 1 si el archivo fue eliminado
2,3. La longitud del archivo, en sectores, en el orden habitual de byte bajo seguido de byte alto.
4,5. El número del primer sector del archivo, en orden bajo-alto.
Si tiene alguno de los muchos programas de utilidad de disco disponibles, puede recuperar un archivo eliminado simplemente cambiando el byte de estado de $80 a $40. Sin embargo, si escribe información en el disco antes de intentar este procedimiento, no funcionará. El VTOC se cambia cuando se elimina un archivo, liberando los sectores para su uso. Si ha escrito en el disco, encontrará que algunos de los sectores utilizados anteriormente para el archivo que desea recuperar han sido sobrescritos con la nueva información.
Para los fines de esta discusión, describiremos dos tipos de archivos diferentes utilizados por las computadoras ATARI. El primero, y con diferencia el más común, es el Archivo Vinculado, como el creado por esta instrucción BASIC:
SAVE "D:GAME"
Primero, se busca el directorio del disco. Dado que un solo disco puede contener un máximo de 64 archivos, esta verificación garantiza que haya espacio en el directorio del disco para otro archivo, llamado GAME. Si, al comprobar el directorio, se encuentra un archivo llamado GAME, se elimina (a menos que esté bloqueado) y el nuevo archivo reemplaza al anterior. Suponiendo que este es el primer archivo GAME que se guardará y que hay espacio en el directorio del disco, los primeros 125 bytes del nuevo archivo GAME se escriben en el primer sector que el VTOC dice que está disponible. Tenga en cuenta que sólo se escriben 125 bytes del archivo, aunque cada sector puede contener 128 bytes. Esto deja espacio para los 3 bytes agregados por CIO, que conducen al nombre de archivo vinculado para este tipo de archivo.
Estos 3 bytes contienen la siguiente información:
Número de byte | ||
---|---|---|
125
|
126
|
127
|
765432 10
|
76543210
|
7 6543210
|
# archivo
|
enlace directo
|
# de bytes del sector
|
Los 6 bits superiores del byte 125 son el número de archivo, tomado del número del archivo en el directorio del disco. Por ejemplo, si GAME es el cuarto archivo listado en el directorio, entonces el número de archivo contenido en los 6 bits superiores del byte 125 de cada sector de GAME es 3, ya que la numeración comienza con el archivo 0. Este número se verifica al leer este archivo para asegurar que cada sector realmente pertenece al archivo GAME. Si, al leer un archivo, se encuentra un sector con un número de archivo diferente, se mostrará un mensaje de error en su pantalla. Por lo general, esto significa que las cosas realmente se han estropeado en su disco. Intentar arreglar un archivo de este tipo es una tarea difícil.
Los 2 bits de orden inferior del byte 125 se combinan con el byte 126 para producir un número de 10 bits que contiene el número del siguiente sector del archivo. Por lo tanto, después de leer el primer sector, el siguiente sector a leer se puede determinar a partir de este enlace directo, y así sucesivamente, hasta que se lea todo el archivo. Por eso lo llamamos archivo vinculado. El último sector de cada archivo contiene 00 como enlace directo, por lo que podemos determinar cuándo se ha leído el archivo completo.
El byte 127 de cada sector de un archivo vinculado contiene el número de bytes almacenados en ese sector. En todos los sectores excepto el último de cada archivo, este valor será igual a 125. Si el sector contiene menos de 125 bytes, se establecerá el bit alto del byte 127, el bit S, lo que indica un sector "corto" de menos de 125 bytes.
El segundo tipo principal de archivo de disco se llama Archivo Secuencial y tiene una estructura mucho más simple. No utiliza ni el directorio del disco ni el VTOC, y utiliza los 128 bytes de cada sector para almacenamiento. Los sectores de dicho archivo se leen de manera secuencial: El sector 3 se lee después del sector 2, que se leyó después del sector 1. El primer sector de dicho archivo contiene la dirección de carga (que indica en qué lugar de la memoria cargar este archivo) y la dirección de inicio (dónde comenzar la ejecución del programa una vez que se complete la carga). Este tipo de archivo suele encontrarse en juegos comerciales. Si intenta mirar el directorio de dicho disco, solo verás basura, ya que nunca se configuró ningún directorio para ese disco.
USO DE LOS DIFERENTES SISTEMAS DE E/S
CIO se utiliza generalmente para leer un Archivo Vinculado, como un programa BASIC o, el código fuente de un programa en lenguaje Ensamblador. Sin embargo, cuando desea leer un sector específico del disco, generalmente se utiliza DSKINV o SIO. Examinemos cómo realizaríamos estas tareas utilizando los tres tipos diferentes de llamadas de E/S.
Primero, abriremos un archivo de disco y lo leeremos en la memoria. El segmento del programa que abre un archivo es muy similar al programa anterior que abrió el directorio del disco:
Listado 9.6
0100 ; ***************************** 0110 ; Equivalencias CIO 0120 ; ***************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A 0230 ICAX1 = $034A 0348 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 *= $600 0270 ; ***************************** 0280 ; Abra un archivo llamado OBJECT.COD 0290 ; ***************************** 0600 A220 0300 LDX #$20 ; Utilice IOCB2 0602 A90C 0310 LDA #12 ; Para cerrar IOCB 0604 9D4203 0320 STA ICCOM,X ; Byte de comando 0607 2056E4 0330 JSR CIOV ; Haga el cierre 0340 ; ***************************** 060A A220 0350 LDX #$20 ; Utilice IOCB2 nuevamente 060C A903 0360 LDA #3 ; Comando abrir 060E 9D4203 0370 STA ICCOM,X ; Byte del comando 0611 A904 0380 LDA #4 ; Abierto para lectura 0613 9D4A03 0390 STA ICAX1,X ; En ICAX1 0616 A900 0400 LDA #0 ; 0 en ICAX2 es 0618 9D4B03 0410 STA ICAX2,X ; Solo para estar seguros 061B A94F 0420 LDA #NAME&255 ; Byte bajo del archivo 061D 9D4403 0430 STA ICBAL,X ; Direccion del nombre 0620 A906 0440 LDA #NAME/256 ; Byte alto del archivo 0622 9D4503 0450 STA ICBAH,X ; Direccion del nombre 0625 2056E4 0460 JSR CIOV ; Abra el archivo 0470 ; ***************************** 0628 A220 0480 LDX #$20 ; IOCB2 062A A900 0490 LDA #0 062C 9D4403 0500 STA ICBAL,X ; Byte bajo de la direccion 062F A950 0510 LDA #$50 ; Byte alto de la direccion 0631 9D4503 0520 STA ICBAH,X ; Entonces es $5000 0634 A9FF 0530 LDA #$FF ; Hacer que el largo del buffer 0636 9D4803 0540 STA ICBLL,X ; sea grande para que pueda 0639 9D4903 0550 STA ICBLH,X ; cargar el archivo completo 063C A905 0560 LDA #5 ; Obtener registro 063E 9D4203 0570 STA ICCOM,X ; Byte del comando 0641 2056E4 0580 JSR CIOV ; Leer el archivo completo 0590 ; ***************************** 0644 A220 0600 LDX #$20 ; IOCB2 0646 A90C 0610 LDA #12 ; Para cerrar el IOCB 0648 9D4203 0620 STA ICCOM,X ; Byte del comando 064B 2056E4 0630 JSR CIOV ; Cierre el archivo 064E 60 0640 RTS ; Fin de la rutina 0650 ; ***************************** 064F 44 0660 NAME .BYTE "D1:OBJECT.COD",$9B 0650 31 0651 3A 0652 4F 0653 42 0654 4A 0655 45 0656 43 0657 54 0658 2E 0659 43 065A 4F 065B 44 065C 9B
Este programa utiliza un truco para cargar el archivo completo en una sola operación. En las líneas 530 a 550, configuramos la longitud del buffer en $FFFF, o 65.535 bytes. Luego, la rutina CIO cargará el archivo completo y se detendrá cuando se hayan cargado 65.535 bytes (algo casi imposible) o cuando se encuentre un byte de final de línea. Por lo tanto, si nuestro archivo contiene bytes de final de línea ($9B), la carga finalizará y no cargaremos el archivo completo. ¿Cómo podemos solucionar este problema?
Dado que probablemente no sabremos con certeza si el archivo que se va a cargar contiene algún byte $9B, debemos ir a la segura y usar un método que cargue cualquier archivo. Para hacer esto, cargamos un sector (128 bytes) a la vez, continuando hasta que se logre una condición de error, que ocurrirá al final del archivo. Usando CIO, sabemos cuándo ocurre un error, ya que regresaremos de la llamada al CIO con la bandera Negativo establecida en uno. Echemos un vistazo al programa para realizar este tipo de carga usando CIO:
Listado 9.7
0100 ; ***************************** 0110 ; Equivalencias CIO 0120 ; ***************************** 0340 0130 ICHID = $0340 0341 0140 ICDNO = $0341 0342 0150 ICCOM = $0342 0343 0160 ICSTA = $0343 0344 0170 ICBAL = $0344 0345 0180 ICBAH = $0345 0346 0190 ICPTL = $0346 0347 0200 ICPTH = $0347 0348 0210 ICBLL = $0348 0349 0220 ICBLH = $0349 034A 0230 ICAX1 = $034A 034B 0240 ICAX2 = $034B E456 0250 CIOV = $E456 0000 0260 *= $600 0270 ; ***************************** 0280 ; Abra un archivo llamado OBJECT.COD 0290 ; ***************************** 0600 A220 0300 LDX #$20 ; Utilice IOCB2 0602 A90C 0310 LDA #12 ; Para cerrar el IOCB 0604 9D4203 0320 STA ICCOM,X ; Byte del comando 0607 2056E4 0330 JSR CIOV ; Haga el cierre 0340 ; ***************************** 060A A220 0350 LDX #$20 ; Utilice IOCB2 nuevamente 060C A903 0360 LDA #3 ; Comando para abrir 060E 9D4203 0370 STA ICCOM,X ; Byte del comando 0611 A904 0380 LDA #4 ; Abierto para lectura 0613 9D4A03 0390 STA ICAX1,X ; En ICAX1 0616 A900 0400 LDA #0 ; 0 en ICAX2 es 0618 9D4B03 0410 STA ICAX2,X ; Solo para estar seguros 061B A969 0420 LDA #NAME&255 ; Byte bajo de la direccion 061D 9D4403 0430 STA ICBAL,X ; del nombre del archivo 0620 A906 0440 LDA #NAME/256 ; Byte alto de la direccion 0622 9D4503 0450 STA ICBAH,X ; del nombre del archivo 0625 2056E4 0460 JSR CIOV ; Abra el archivo 0470 ; ***************************** 0628 A220 0480 LDX #$20 ; IOCB2 062A A900 0490 LDA #0 062C 9D4803 0500 STA ICBLL,X ; Longitud baja del buffer 062F A980 0510 LDA #$80 ; Para cargar un sector 0631 9D4903 0520 STA ICBLH,X ; A la vez 0634 A950 0530 LDA #$50 ; Byte alto de la 0636 9D4503 0540 STA ICBAH,X ; direccion del buffer 0639 A905 0550 LDA #5 ; Obtener registro 063B 9D4203 0560 STA ICCOM,X ; Byte del comando 063E A220 0570 LOOP LDX #$20 ; Para cuando se realiza el ciclo 0640 A900 0580 LDA #0 ; Byte bajo de la 0642 9D4403 0590 STA ICBAL,X ; direccion de buffer al comenzar 0645 2056E4 0600 JSR CIOV ; Leer 1er sector 0648 3014 0610 BMI FIN ; Si termino, vaya a END 064A A220 0620 LDX #$20 ; IOCB2 064C A980 0630 LDA #$80 ; Subir 128 bytes 064E 9D4403 0640 STA ICBAL,X ; Para el buffer 0651 2056E4 0650 JSR CIOV ; Leer siguiete sector 0654 3008 0660 BMI FIN ; Si termino, vaya a END 0656 A220 0670 LDX #$20 ; IOCB2 0658 FE4503 0680 INC ICBAH,X ; Elevar el buffer nuevamente 065B 4C3E06 0690 JMP LOOP ; Aun no termina - leer mas 0700 ; ***************************** 065E A220 0710 FIN LDX #$20 ; IOCB2 0660 A90C 0720 LDA #12 ; Para cerrar el IOCB 0662 9D4203 0730 STA ICCOM,X ; Byte del comando 0665 2056E4 0740 JSR CIOV ; Haga el cierre 0668 60 0750 RTS ; Fin de la rutina 0760 ; ***************************** 0669 44 0770 NAME .BYTE "D1:OBJECT.COD",$9B 066A 31 066B 3A 066C 4F 066D 42 066E 4A 066F 45 0670 43 0671 54 0672 2E 0673 43 0674 4F 0675 44 0676 9B
Continuamos el ciclo hasta que encontramos un error en E/S, momento en el cual pasamos a END para cerrar el archivo y finalizar la rutina. Debe tener cuidado de que no se produzcan errores distintos del error de fin de archivo, ya que este programa se bifurcará a END ante cualquier error. Por supuesto, sería bastante fácil escribir una rutina para determinar primero el código de error devuelto en el registro Y después de la llamada a CIOV y luego tomar las medidas apropiadas dependiendo del código de error. Tenga en cuenta que en esta rutina tenemos que encargarnos de algunas tareas domésticas para cargar el archivo, como incrementar la dirección del buffer en las líneas 630, 640 y 680; No tuvimos que preocuparnos por esto en el primer ejemplo. También tenemos que incorporar rutinas que antes no necesitábamos para determinar cuándo terminamos de cargar.
CARGAR USANDO EL CONTROLADOR (HANDLER) RESIDENTE DE DISCO
Para utilizar el controlador residente de disco, el programador debe configurar un Bloque de Control de Dispositivo (Device Control Block - DCB), que es exactamente análogo al IOCB que necesitamos configurar cuando usamos CIO. Las equivalencias para este DCB son las siguientes:
0100 ; ***************************** 0110 ; Equivalencias SIO 0120 ; ***************************** 0130 DDEVIC = $0300 ; ID del bus serie 0140 DUNIT = $0301; Número de dispositivo 0150 DCOMND = $0302; Byte de comando 0160 DSTATS = $0303; Byte de estado 0170 DBUFLO = $0304 ; Direccion baja del buffer 0180 DBUFHI = $0305; Dirección alta del buffer 0190 DTIMLO = $0306; Tiempo de espera del disco 0210 DBYTLO = $0308; Recuento de bytes bajo 0220 DBYTHI = $0309; Recuento de bytes alto 0230 DAUX1 = $030A; Auxiliar #1 0240 DAUX2 = $030B; Auxiliar #2 0250 SIOV = $E459 0260 DSKINV = $E453
El tercer byte tanto del IOCB como del DCB es el byte de comando, aunque los bytes de comando en sí son diferentes en los dos sistemas, y el quinto y sexto bytes de ambos sistemas son la dirección del buffer. El controlador residente de disco solo permite los siguientes 5 bytes de comando:
$21 Formatear un disco
$50 Escribir un sector
$52 Leer un sector
$53 Solicitud de estado
$57 Escribir un sector con verificación de escritura
Por lo tanto, es evidente que el controlador residente de disco es un sistema más limitado, pero mucho más simple que el CIO. Veamos cómo podemos usar el DCB y el controlador residente de disco, a través de DSKINV, para leer información del disco. Por supuesto, no leeremos archivos DOS normales utilizando este sistema; son Archivos Vinculados y el controlador residente de disco no está diseñado para manejarlos, sino los Secuenciales. Por lo tanto, supongamos que queremos leer los sectores $20 a $60, inclusive, en lugar de algún archivo de disco. El programa para hacer esto usando DSKINV es el siguiente:
Listado 9.8
0100 ; ***************************** 0110 ; Equivalencias SIO 0120 ; ***************************** 0300 0130 DDEVIC = $0300 ; ID del bus serie 0301 0140 DUNIT = $0301 ; Numero de dispositivo 0302 0150 DCOMND = $0302 ; Byte del comando 0303 0160 DSTATS = $0303 ; Byte de estado 0304 0170 DBUFLO = $0304 ; Direccion baja del buffer 0305 0180 DBUFHI = $0305 ; Direccion alta del buffer 0306 0190 DTIMLO = $0306 ; Tiempo de espera del disco 0308 0200 DBYTLO = $0308 ; Byte bajo del contador 0309 0210 DBYTHI = $0309 ; Byte alto del contador 030A 0220 DAUX1 = $030A ; Auxiliar #1 030B 0230 DAUX2 = $030B ; Auxiliar #2 E459 0240 SIOV = $E459 E453 0250 DSKINV = $E453 0000 0260 *= $600 0270 ; ***************************** 0280 ; Supongamos que el archivo comienza en el sector 0290 ; $20 y se extiende al sector $60 0300 ; ***************************** 0600 A900 0310 LDA #0 0602 8D0B03 0320 STA DAUX2 ; Numero de sector - alto 0605 8D0803 0330 STA DBYTLO ; Largo del buffer - bajo 0608 A980 0340 LDA #$80 ; Para cargar un sector 060A 8D0903 0350 STA DBYTHI ; A la vez 060D A950 0360 LDA #$50 ; Byte alto de 060F 8D0503 0370 STA DBUFHI ; Direccion del buffer 0612 A952 0380 LDA #$52 ; Obtener sector 0614 8D0203 0390 STA DCOMND ; Byte del comando 0617 A920 0400 LDA #$20 ; Numero de sector - bajo 0619 8D0A03 0410 STA DAUX1 ; Va aqui 061C A900 0420 LOOP LDA #0 ; Byte bajo de la Direccion 061E 8D0403 0430 STA DBUFLO ; del buffer al comenzar 0621 2053E4 0440 JSR DSKINV ; Leer 1er sector 0624 A980 0450 LDA #$80 ; Subir 128 bytes 0626 8D0403 0460 STA DBUFLO ; Para el buffer 0629 EE0A03 0470 INC DAUX1 ; Siguiente sector 062C AD0A03 0480 LDA DAUX1 ; ?Terminamos? 062F C960 0490 CMP #$60 0631 B010 0500 BCS FIN ; Si 0633 2053E4 0510 JSR DSKINV ; No - leer el siguiente sector 0636 EE0503 0520 INC DBUFHI ; Elevar página del buffer 0639 EE0A03 0530 INC DAUX1 ; Siguiente sector 063C AD0A03 0540 LDA DAUX1 ; ?Terminamos? 063F C960 0550 CMP #$60 0641 90D9 0560 BCC LOOP ; No 0643 60 0570 FIN RTS ; Todo terminado
Como vimos anteriormente, cuanto más nos alejamos de la rutina inicial del CIO, más tareas domésticas debemos realizar. En este programa, debemos manejar el incremento de los sectores del disco y la ubicación del buffer después de cada lectura. También debemos determinar si hemos terminado comparando constantemente el número del sector con el sector final deseado, $60. Esto es lo que queríamos decir cuando comparamos los distintos sistemas de E/S con las capas de una cebolla. Cuanto más nos acercamos al núcleo, más trabajo tenemos que hacer y menos maneja el sistema por nosotros.
En el núcleo se encuentra el propio sistema de Entrada-Salida en Serie (Serial Input Output - SIO). Accedimos a DSKINV en este programa, pero podríamos haber llamado a SIOV en su lugar. Sin embargo, antes de hacerlo, habríamos tenido que configurar todo el DCB en lugar de sólo los bytes pertinentes, tal como lo hicimos. Por ejemplo, el ID del bus serie tendría que haberse establecido en $31, en DDEVIC, y el valor del tiempo de espera en algún valor razonable, como 45. Entonces podríamos haber logrado exactamente los mismos resultados reemplazando cada llamada a DSKINV con una llamada a SIOV, pero con el gasto de aún más tareas domésticas.
Tenga en cuenta que tanto CIOV como DSKINV llaman a SIOV para realizar la entrada y salida en serie, pero manejan sus respectivas tareas de limpieza antes de estas llamadas. Cuanto más se aleje del CIO, más preciso será su control del sistema, pero más trabajo tendrás que hacer. Esta es una regla general en informática: un lenguaje de alto nivel es el más fácil de usar, pero proporciona el menor control del sistema. A medida que adquiera más control, también necesitará trabajar más duro. Bueno, realmente no esperaba conseguir algo a cambio de nada, ¿verdad?
Con esto concluye nuestra discusión sobre la E/S del disco. Ahora debería estar completamente familiarizado con cómo obtener información hacia y desde una unidad de disco, ya sea utilizando archivos Secuenciales o Vinculados. Experimente con estos sistemas hasta que se sienta cómodo, ya que son básicos para muchas aplicaciones que querrá probar.
Índice de Contenido | Capítulo anterior | Siguiente capítulo