Diskettera Commodore 1541

Un experimento para tomar el control de la escritura y lectura.

Por Pablo Sartor (https://www.ieem.edu.uy/claustro/pablo-sartor)

¿Cómo funciona internamente el almacenamiento y lectura de bits sobre un diskette (floppy) en la clásica unidad 1541 de Commodore?

Vamos a saltearnos su sistema operativo ¡y manipular directamente su hardware! Para ello, te explicaré qué sucede a nivel físico, mecánico y electrónico, y mediante nuestras propias rutinas de código máquina, escribiremos y leeremos unos bits, ignorando el sistema operativo de la unidad; es decir, manipularemos directamente, a bajo nivel, los circuitos de grabación y lectura.

La diskettera 1541 es una verdadera computadora, que cuenta con:

  • un microprocesador MOS 6502 corriendo a 1MHz (del cual el 6510 de la Commodore 64 es una copia con un par de pequeños agregados);
  • 2 KB de memoria RAM (768 bytes para ejecución de su sistema operativo y 1280 bytes para 5 buffers de 256 bytes de lectura/escritura)
  • 16 KB de memoria ROM (el DOS, “Disk operating system”, código que ejecuta todas las funciones provistas por la 1541
  • puertos y circuitería de comunicación para recibir instrucciones y datos y enviar datos desde / hacia la computadora principal (en adelante C64, aunque puede utilizarse con otros modelos como VIC20 y C128)
  • mecanismo y circuitos electrónicos para almacenar y leer bits sobre la superficie magnetizable de diskettes flexibles de 5 ¼ ‘’.

Principio de funcionamiento

La 1541 almacena la información en los diskettes en pistas concéntricas. Esto es de algún modo similar al sonido almacenado en discos de vinilo, con dos importantes diferencias: a) las pistas en un diskette son círculos concéntricos, no un largo y único espiral como en los vinilos; b) la información es digital (bits, o sea ceros y unos) en vez de analógica (la señal de audio en el vinilo). Las pistas no están “preimpresas” en el diskette, sino que este consiste de una superficie circular compuesta por millones de partículas magnetizables, y las pistas quedan definidas por las posiciones en las que el cabezal de la 1541 se coloca. Haciendo un símil con los vinilos, la “púa” puede estar más lejos o más cerca del centro. La 1541 maneja este movimiento de la cabeza de lectura/escritura por medio de un motor de paso (stepper motor), capaz de ubicarlo en 42 posiciones igualmente espaciadas, más las intermedias, totalizando 84 posiciones discretas (a diferencia de los vinilos, donde la púa barre un continuo de distancias al centro, en la 1541 son posiciones prefijadas). Sin embargo, el ancho del cabezal y la traza magnética que este deja es tal que las posiciones intermedias se superponen, por lo cual el estándar era utilizar 42 posiciones salteando las intermedias (en realidad, en un diskette formateado en forma estándar por el sistema operativo de la 1541, se utilizan las 35 pistas más exteriores).

Una vez posicionado sobre una pista, el cabezal “la ve pasar por debajo” en forma completa unas 300 veces por minuto, ya que el diskette gira a una velocidad estándar de 300 RPM. Esta se ajusta a través de un tornillo en la mayoría de los mecanismos, mediante el clásico truco basado en el efecto estroboscópico (luz parpadeante y un papel pegado con marcas que parece “quieto” al ajustarse la velocidad correcta).

En cada revolución, el cabezal barre una pista en la que puede escribir o leer una cierta cantidad de bits, aproximadamente entre 45 kbits y 60 kbits, dependiendo de la pista (más externa, más larga). Cada bit ocupa un minúsculo fragmento de la pista llamado “ventana de bit”. Las diferencias se deben a la menor longitud de las pistas más internas (menor radio) y las diferentes velocidad a las que la 1541 lee o graba bits en función de la pista en que está el cabezal, con el objetivo de mantener una densidad de bits por milímetro aproximadamente estable.

El DOS (disk operating system) de la 1541 divide a cada pista en sectores. En cada uno pueden almacenarse 256 bits de información útil. Las pistas interiores alojan menos sectores que las exteriores. Esto será irrelevante para nuestro experimento pues ignoraremos completamente al sistema operativo y su estándar de sectores.

Por otra parte, el DOS utiliza una codificación llamada GCR, por la cual cada combinación de valores de 4 bits es traducida a una de 5 bits antes de ser almacenada (y se hace la traducción inversa a la hora de leer). Estas combinaciones de 5 bits fueron escogidas de modo que nunca se almacenaran ni leyeran más de dos ceros ni cuatro unos consecutivos. Esto ayuda a mantener sincronizada la lectura / escritura de los bits ante pequeños desvíos y fluctuaciones en la velocidad de rotación. En el próximo apartado quedará claro el porqué. De todos modos, en nuestro experimento ignoraremos también esta codificación, que es hecha por software por el DOS.

Recomiento fuertemente visitar el sitio https://www.pagetable.com/?p=1070 (de donde tomamos la Figura 1) en el cual se encuentran la explicación de cómo el DOS formatea un diskette y almacena la información y representaciones visuales de diskettes formateados.

Figura 1 – Visualización de diskette con formateo estándar. Fuente: https://www.pagetable.com/?p=1070

Cómo se almacenan a nivel físico los bits

El cabezal cuenta con un electroimán. Para almacenar información, se lo alimenta con una corriente de valor único (no hay matices) en un sentido o el opuesto mientras el diskette gira. Esto hace que la pista que está debajo del cabezal quede con zonas magnetizadas en un sentido y otras en el opuesto. La intensidad del campo magnético tiene un módulo constante en cada zona. Cuando se lee información, no se fuerza una corriente por el cabezal, sino que la pista magnetizada al moverse induce corriente en el cabezal cuando hay cambios en el flujo magnético (veáse “ley de Faraday-Lenz”). Así aparecen picos de tensión (breves pulsos) en un sentido u otro cuando el campo magnético debajo del cabezal cambia en un sentido o el otro. En la 1541 se optó por almacenar cada “1” como un cambio en la polaridad del cambio magnético dentro de la ventana de bit, y un “0” como una ventana donde no cambia la polaridad. Cuando hay varios unos, la polaridad cambia con frecuencia, y eso permite mantener sicronizado al software de lectura con la velocidad real del motor y sus micro-fluctuaciones (jitter). Pero si hay varios ceros seguidos, hay una zona larga de polaridad uniforme, donde no se induce ningún pulso, y entonces la 1541 podría desfasar su conteo de ventanas de bits respecto de lo que realmente sucede con el giro. Por ello, los ingenieros de Commodore adoptaron un formato GCR donde nunca hubiera más de dos ceros consecutivos. En definitiva, en la lectura, cuando a lo largo de una ventana de bit se detecta un pulso de tensión en el cabezal se asume que hay un “1”, y de lo contrario se asume un “0”. Los circuitos de la 1541 se encargar de entregar un flujo de unos y ceros (en forma de dos niveles de tensión) al chip VIA 6522 que se encarga de empaquetarlos en forma de bytes y ponerlos a disposición del DOS para su traducción GCR y posterior procesamiento.

Los principales bloques de la 1541

Ahora que vimos la base físico-mecánica del almacenamiento, veamos los grandes bloques que componen a la 1541 y qué partes se puede manipular por software. Aparte de la fuente de alimentación, tenemos el motor que hace girar al diskette, y el motor de paso encargado de ubicar al cabezal en la pista deseada, ambos manejables por software. Los restantes bloques se dividen en:

  1. La parte encargada de grabar y leer bits, llamémosle “funcionamiento al interior”. Para ello, uno de los chips VIA 6522 tiene registros y terminales que permiten: enviar un byte a ser almacenado; recibir un byte del circuito de lectura; activar y detener el motor de giro; avanzar o retroceder un paso el motor del cabezal; encender y apagar el led rojo de la unidad.
  2. Buffers: bloques de memoria RAM de 256 bytes utilizados por la 1541 para alojar los bytes leídos de un sector (normalmente para ser enviados a la C64) o para alojar los bytes a almacenar en un sector (normalmente recibidos desde la C64).
  3. La parte encargada de recibir solicitudes, y enviar y recibir datos hacia/desde la C64, llamémosle “funcionamiento hacia el exterior”. Para ello otro chip VIA 6522 se utiliza para la comunicación entre la C64 y la 1541.

Es interesante notar que al interior, la 1541 almacena y lee sectores completos, si bien la comunicación con la C64 puede operar sobre bytes específicos, y para ello se divide la operación como se indica arriba; en cada momento, el DOS de la 1541 está en uno de ambos modos (comunicándose con la C64 o gestionando el almacenamiento en el diskette), y el desacople entre ambas operaciones se logra por medio de los buffers.

El experimento

Vamos ahora derecho al grano. En este experimento, mediante un programa en código máquina que ejecutará la 1541, almacenaremos una secuencia de 32 bytes “01100110” en la pista 1 del diskette, que luego leeremos para demostrar que quedaron correctamente almacenados. Para todo esto, la C64 ejecutará un programa BASIC que enviará el código máquina a ejecutar a la 1541, luego le ordenará ejecutar la grabación, seguida por la lectura y finalmente mostrará en pantalla el resultado leído.

El código máquina para almacenamiento

Comenzaremos encendiendo el led y el motor de giro; seguiremos moviendo el cabezal a la pista 1; luego la llenaremos con un patrón 010101… a modo de borrado; grabaremos 40 bits “1” a modo de bloque sincronizador seguido de los 32 bytes “01100110”; finalmente, apagaremos el led y detendremos el motor.

* = $0300

       jmp init

phas2: .byte 0, 1, 2, 3      // to cycle the stepper motor

phase: .byte 0        // index relative to phas2

La rutina se cargará a partir de la dirección $0300 (buffer #0). La primera instrucción es un salto a la posición etiquetada como init (la veremos más abajo) lo que nos permite intercalar código y espacio de variables antes. Los 5 bytes phas2 más phase se utilizan para enviar el patrón de bits 00 – 01 – 10 – 11 – 00 … al motor de paso, que por diseño los necesita en este orden para mover el cabezal una posición por vez, o en el orden inverso para hacerlo en sentido contrario, esto lo veremos en la sección de la rutina rotulada “bump”.

ledmon:        lda #$0c      // turn on led and spindle motor (bits 3-2)

              ora $1c00

              sta $1c00

              rts

ledmof:        lda #$f3      // turn off led and motor (bits 3-2)

              and $1c00

              sta $1c00

              rts

delay:        ldy #$00      // this routine delays .X times 256 times 18 microsecs

!lp:           dey           // so, at 1 MHz, each .X takes around 4.6 milisecs.

              bne !lp-

              dex

              bne delay

              rts

Aquí aparecen tres subrutinas. Ledmon enciende el led y el motor de giro. Para ello, pone en 1 los bits 3 y 2 (máscara 00001100 = $0c) en la posición de memoria $1c00 = 7168. La circuitería de la 1541 “intercepta” esta dirección y redirige las lecturas y escrituras al puerto B del chip VIA 6522 encargado del control interno de la 1541. Los bits 2 y 3 controlan directamente el encendido / apagado del motor y el led rojo. Ledmof apaga el led y motor. Por su parte, la rutina delay introduce una demora de unos 4.6 milisegundos tantas veces como venga indicado en el registro X del 6502 al invocarla.

init:         sei           // avoid interrupts

              jsr ledmon     // turn on led and spindle motor

              lda $1c00      // start with stepper motor phase = 0 (bits 0-1

of dskcnt = $1c00)

              and #$fc

              sta $1c00

              lda #$00

              sta phase

              ldx #$ff      // wait some 255 x 4.6 milisecs = aprox 1 sec

              jsr delay

Ahora inicia la rutina principal init. Comienza desactivando las interrupciones y encendiendo el led y motor. Luego setea la variable phase = 0 y pone los dos bits de control del motor de paso en 0, para iniciar la secuencia arriba descrita 00-01-10-11… Finalmente ordena una pausa de aproximadamente 1 segundo.

bump:         ldx #$5c      // 92 steps outwards (46 tracks x 2 halves each movement)

bmplp:        dec phase      // cycling 0, 1, 2, 3, 0, 1, 2, 3 … bits 0-1 of $1c00

              bpl !lp+      // to make sure we are on track 1

              lda #$03

              sta phase

!lp:          lda $1c00

              and #$fc

              ora phase

              sta $1c00

              txa

              ldx #$2c      // wait some 44 x 4.6 milisecs = aprox 1/5 second

              jsr delay

              tax

              dex

              bne bmplp

Esta sección “bump” hace que el cabezal se desplace (o lo intente si ya llegó) 92 veces moverse hacia afuera. De ese modo, nos aseguramos que el cabezal quede en la pista 1, sin importar en qué pista estaba al iniciar la rutina. Entre posición y posición, mandamos una pausa de unos 0.2 segundos. Por ello, una vez que el cabezal llegue a la pista 1, sentirás un “clac – clac – clac” a ritmo de 5 clac por segundo. Esto es similar (más lento en nuestro caso) al famoso sonido de metralleta que se oye al iniciar el formateo de un diskette, pues la 1541 ordena suficientes pasos como para estar segura de que el cabezal queda en la pista 1, y los que “sobran” resultan en un golpe del rotor contra un tope mecánico (con el consiguiente “clac”).

              lda #$ff      // store $ff in ddra2 ($1c03) to make port a an output port

              sta $1c03

              lda $1c0c      // activate pcr2 write mode (bits 765=110) and byte-sync-enable (321=111)

              and #$1f

              ora #$ce

              sta $1c0c

Ahora configuramos el chip VIA para que los 8 bits de su puerto A sean de salida (es decir, el chip enviará datos en vez de leer). También ponemos el cabezal en modo escritura, al poner en 110 los tres bits más altos del registro auxiliar del chip VIA (mapeado en $1c0c), la circuitería de la 1541 enviará entonces corriente a través del cabezal. Finalmente, poniendo los bits 3, 2 y 1 en 1 en el mismo registro, se predispone al VIA para que genere una señal llamada byte-sync cada 8 bits llegados; a continuación veremos su uso.

              lda #$55      // record 32 x 256 bytes %01010101 to “erase the track”

              ldx #$20

              ldy #$00

              sta $1c01

              clv

!yo:          bvc *         // this beautiful command waits looping itself until the carry is set

              clv           // thanks to the byte-sync-enable signal being fed to the SO (set overflow) 6502 pin

              dey           // input of the 6502 cpu

              bne !yo-

              dex

              bne !yo-

Ahora almacenamos 32 veces (registro X = $20) 256 bytes 01010101 (registro A = $55), para evitar cualquier secuencia de muchos 1 que podría ser interpretada como preámbulo de un bloque de datos. Los datos a almacenar (01010101) son puestos en el puerto de datos del VIA mapeado en la dirección $1c01. Como antes predispusimos al VIA con todo lo necesario para grabar datos, inmediatamente este empezará a enviar al cabezal los pulsos eléctricos adecuados para almacenar 01010101. ¿Qué sucede cada 8 bits? El VIA pone un 1 en uno de sus pines (el que llamamos byte-sync), que configuramos en el bloque anterior para que se conecte al pin “set overflow” del procesador 6502. Esto permite que por software, el CPU esté en loop esperando a que el bit de estado V (overflow) se active, y entonces hacer algo en consecuencia. En nuestra rutina, la secuencia clv – bvc * en assembler dice “borrar el flag V, y en la siguiente instrucción quedar saltando a sí misma, mientras el flag esté borrado”. Cuando se completa un byte, el flag se pone en uno, sigue la ejecución (dey) decrementando los contadores hasta completar las “32 veces 256”.

              lda #$ff      // now we’ll record 5 consecutive $ff bytes (40 1’s) as sync block

              ldx #$05      // store $ff in data2 ($1c01) as the sync mark character

              sta $1c01

              clv

!yo:          bvc *         // loop to write out 5 consecutive $ff bytes (5×8 =40 1’s)

              clv

              dex

              bne !yo-

Ahora, en forma similar a la anterior, almacenamos 5 bytes consecutivos llenos de 1 (es decir 40 bits 1). Esto funcionará para nuestra rutina de lectura como una señal de preámbulo de datos; dado que habíamos llenado la pista con 010101… estamos seguros de que este preámbulo existirá en un único punto.

              lda #$66      // now we store %01100110 32 times just for fun

              ldx #$20

              sta $1c01

              clv

!yo:          bvc *

              clv

              dex

              bne !yo-

Seguimos ahora almacenando 32 bytes %01100110 = $66 = 102 decimal, que para nuestro experimento serán los “datos útiles” a recuperar en la rutina de lectura.

              lda $1c0c      // activate pcr2 read mode (bits 765=111 in $1c0c)

              ora #$e0

              sta $1c0c

              jsr ledmof     // turn off led and spindle motor

              cli           // re-enable interrupts

              rts           // that’s all folks

Finalmente, devolvemos a la 1541 a modo “cabezal lee” para dejar de alterar la pista magnética, ordenamos el apagado del led y motor, reactivamos las interrupciones del sistema, y “rts” devolvemos el control al DOS. Una vez se ejecute esta rutina, tendremos entonces un diskette cuya pista 1 estará llena de 0101010101… salvo una parte formada por 40 bits 1 seguidos de 32 bytes %01100110.

El código máquina para lectura

Vamos ahora con el código máquina que haremos ejecutar a la 1541 para leer la información que grabamos. A continuación se presenta el listado assembler. Buena parte es análoga al código de almacenamiento; nos detendremos solo en los detalles particulares de la lectura.

* = $0300

              jmp init

phas2: .byte 0, 1, 2, 3      // to cycle the stepper motor

phase: .byte 0        // index relative to phas2

init:         sei           // avoid interrupts

              jsr ledmon     // turn on led and spindle motor

              lda $1c00      // start with stepper motor phase = 0 (bits 0-1 of dskcnt = $1c00)

              and #$fc

              sta $1c00

              lda #$00

              sta phase

              ldx #$ff      // wait some 255 x 4.6 milisecs = aprox 1 sec

              jsr delay

bump:         ldx #$5c      // 92 steps outwards (46 tracks x 2 halves each movement)

bmplp:        dec phase      // cycling 0, 1, 2, 3, 0, 1, 2, 3 … bits 0-1 of $1c00

              bpl !lp+      // to make sure we are on track 0

              lda #$03

              sta phase

!lp:          lda $1c00

              and #$fc

              ora phase

              sta $1c00

              txa

              ldx #$2c      // wait some 44 x 4.6 milisecs = aprox 1/5 second

              jsr delay

              tax

              dex

              bne bmplp

              lda #$00      // store $00 in ddra2 ($1c03) to make port a an input port

              sta $1c03     

              lda $1c0c      // activate pcr2 read mode (bits 765=111) and byte-sync-enable (321=111)

              ora #$ee

              sta $1c0c

              ldx #$00      // counter, let’s read 32 bytes     

wasync:        bit $1c00      // test bit 7 of dskcnt ($1c00) to check for a sync.

              bmi wasync

              lda $1c01      // reset the PA latch

              clv

wabyte:        bvc *         // wait for a byte

              clv

              lda $1c01

              sta $0400,x    // store in buffer #1 = $0400

              inx

              cpx #$20

              bne wabyte

              jsr ledmof     // turn off led and spindle motor

              cli           // re-enable interrupts

              rts           // that’s all folks

La primera diferencia aparece cuando almacenamos $00 en los registros del VIA mapeados en las direcciones $1c03 y $1c0c. Esto dispone las cosas de modo que los 8 bits del puerto A del VIA sean ahora de lectura, que sus bits que controlan el cabezal lo pongan en modo de lectura (es decir, sensible a la inducción del campo magnético de las pistas en movimiento), y nuevamente activando la conexión de “byte sync” con el pin SO (set overflow) del CPU 6502 para dar un “aviso” cada 8 bits.

Al llegar a la etiqueta “wasync”, entramos en un loop de dos instrucciones (bit-bmi), del cual se sale solamente cuando el bit 7 del registro mapeado en $1c00 (registro de control) vale 1. La 1541 tiene una compuerta NAND que pone en 0 la señal SYNC (que es “activa baja”) cuando los últimos 10 bits leídos fueron todos 1. Esto, por como hicimos el almacenamiento previo, solo sucederá cuando leamos 10 bits 1 del preámbulo de 40 bits que habíamos grabado (SYNC se hace 0 con el décimo, y permanecerá así por los siguientes 30 bits 1, hasta la llegada del primer bit 0, con el cual pasa a valer 1, es decir estar inactiva). Apenas abandonamos la zona “SYNC”, leemos el puerto A del VIA (lda $1c01), lo cual fuerza un reset a su latch y lo dispone a alojar un nuevo byte. Para esperarlo, borramos el flag overflow (V) del CPU 6502 con clv, y luego hacemos “salto a mí misma mientras overflow esté borrado” (bvc *). Como habíamos activado la unión de “byte-sync” con el pin “set overflow” del 6502, eso sucederá cuando lleguen 8 bits; entonces los leeremos del puerto A (lda $1c01) y los iremos almacenando en el buffer #1 que comienza en la posición $0400 de RAM de la 1541. Nuestra rutina repite la lectura de bytes 32 veces ($20), y finalmente apaga el led, el motor de giro, y devuelve el control al DOS.

Dos comentarios para cerrar este apartado.

  1. Los circuitos de la 1541 fuerzan la señal byte-sync (“llegaron 8 bits”) a estar inactiva mientras SYNC esté activa. Por ello, mientras vemos pasar los 30 bits 1 del preámbulo siguientes a los primeros 10, no se activará byte-sync, y quedaremos esperando (instrucción bvc *) hasta que llegue la primera secuencia de 8 bits que empiecen con un cero, pues entonces estará inactiva SYNC y se activará byte-sync.
  • Cada instrucción del CPU 6502 consume entre 2 y 7 ciclos de reloj (que a la velocidad aproximada de 1 MHz, son unos 2 a 7 microsegundos). A su vez, una ventana de bit tarda en el orden de unos 3 a 4 microsegundos, según la pista. Es interesante notar que en el tiempo de 8 ventanas de bit (un byte físico) caben algunas instrucciones, pero no muchas. La circuitería de la 1541 genera las señales byte-sync, SYNC, recibe y empaqueta grupos de 8 bits de la pareja CPU-VIA, con lo cual nunca es necesario para el CPU reaccionar inmediatamente a bits específicos, sino que puede tomarse el tiempo de 8 ventanas de bit para hacerlo, suficiente para alojar unas cuantas instrucciones.

El programa BASIC

Veamos ahora el programa BASIC que ejecutará nuestro experimento. Los pasos son:

  1. Enviar las rutinas de código máquina a la 1541, alojándolas a partir de la posición $0300 de su RAM (el buffer #0)
  2. Pedir a la 1541 que ejecute las rutinas
  3. Leer desde la 1541 los primeros 32 bytes del buffer #1 ($0400, donde, si todo funcionó bien, deberían estar los bytes $01100110 que se supone que almacenamos)
  4. Mostrar lo leído por pantalla.

La imagen de disco .d64 disponible junto a este artículo contiene un archivo llamado “grabar” y otro llamado “leer”, que contienen el código máquina de las sendas rutinas. En ambos casos, compilado a partir de $0300 (por ejemplo las instrucciones jmp y jsr apuntan a direcciones coherentes con el inicio de la rutina en $0300, que es la dirección a partir de la cual residirán dentro de la 1541). Si deseas compilarlas por tu cuenta en una C64 estándar, deberás hacerlo a partir de una dirección diferente a $0300 (pues de lo contrario “romperás” el sistema operativo), por ejemplo $C000, y luego alterar a mano el byte alto de las instrucciones jmp y jsr, para que “apunten” a la página $0300.

Ejecutando la grabación

Vamos con la parte de grabación. Lo primero es cargar la rutina de grabación (quedará cargada desde $0800 = 2049, dirección estándar donde cargan los programas en una C64; ya dejé el contenido con los retoques arriba comentados para las instrucciones jmp y jsr). El loop “for” se encarga de transferir el código a la ubicación $C000 = 49152, para que luego podamos hacer “new” y tipear el pequeño programa BASIC.

load”grabar”,8

fori=0to200:poke49152+i,peek(2049+i):next

new

100 open15,8,15,”i”

120 fori=0to200

140 print#15,”m-w”chr$(i)chr$(3)chr$(1)chr$(peek(49152+i))

150 next

160 print#15,”m-e”chr$(0)chr$(3)

180 close15

190 end

run

La línea 100 abre el canal de comandos (15) para dialogar con el DOS de la 1541. El loop 120-150 se encarga de escribir, uno por uno, los bytes de la rutina, a partir de la dirección $0300 de la 1541. En el comando DOS “memory write” (m-w) el primer byte es el byte bajo de la dirección, el segundo el byte alto, el tercero la cantidad de bytes, el cuarto el byte en sí mismo.

La línea 160 utiliza el comando DOS “memory-execute” (m-e) para ordenar a la 1541 que ejecute la rutina presente a partir de $0300 (es decir, nuestra rutina de grabación). La 1541 hace un “jsr” a dicha rutina, de la cual se retorna con su “rts” final para que el DOS retome el control.


Ejecutando la lectura

Vamos ahora con la lectura. En forma similar a la anterior, cargamos la rutina de lectura a partir de la dirección 2049, la transferimos a 49152, y luego ejecutamos el programa BASIC que la transfiere, ordena su ejecución y termina mostrando lo “leído” por la rutina.

load”leer”,8

fori=0to200:poke49152+i,peek(2049+i):next

100 open15,8,15

120 fori=0to200

140 print#15,”m-w”chr$(i)chr$(3)chr$(1)chr$(peek(49152+i))

145 print i,peek(49152+i)

150 next

152 input”Press enter to execute and read”;a

155 open 5,8,5,”#1″ : rem buffer 1 at $0400

160 print#15,”m-e”chr$(0)chr$(3)

170 fori=1to10:get#5,a:printa:next

180 close5; close15

190 end

run

La línea 155 abre un canal especificando que deseamos utilizar el buffer #1 con él (que es el que va de $0400 a $04FF). Tener presente que nuestra rutina de lectura deja a partir de $0400 los 32 bytes que lee del diskette.

La línea 160 ordena la ejecución de nuestra rutina, presente en $0300, a través del comando “m-e” del DOS. Finalmente, el loop 170 solicita 10 bytes al canal 5, que los tomará del buffer #1, es decir, los irá leyendo desde $0400 en adelante, por lo cual, si todo funciona bien, deberíamos ver 10 renglones “102”, ya que %01100110 = $66 = 102 decimal.

***

Espero que este artículo te haya resultado atrapante y esclarecedor sobre cómo trabaja internamente la unidad 1541 para escribir y leer información sobre un diskette. En caso de dudas o comentarios, puedes escribirme a psartor@um.edu.uy

***

SOBRE EL AUTOR

Desde mi primera C=64 en 1986, fan de las microcomputadoras y electrónica clásica. Soy PhD e ingeniero en computación, consultor en TIC para empresas varias y profesor en la escuela de negocio IEEM de la Universidad de Montevideo.

psartor@um.edu.uy

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *