Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Lector de disquetes Arduino Amiga (V1)

Componentes y suministros

Resistencia 1k ohm
× 1
Breadboard (genérico)
× 1
Cables de puente (genéricos)
× 1
Fuente de alimentación SparkFun Breadboard 5V / 3.3V
× 1
Arduino UNO
O Pro Mini
× 1

Aplicaciones y servicios en línea

Arduino IDE

Acerca de este proyecto

  • Mi objetivo: Crear una forma simple, barata y de código abierto de recuperar datos de los disquetes Amiga DD desde Windows 10 y otros sistemas operativos.
  • Mi solución: Un Arduino + una aplicación de Windows
  • Por qué: Para preservar los datos de estos discos para el futuro. Además, una PC normal no puede leer los discos de Amiga debido a la forma en que están escritos.

Sitio web del proyecto:http://amiga.robsmithdev.co.uk/

Esta es la V1 del proyecto. ¡V2 contiene lectura y escritura mejoradas!

Experiencias Amiga

Le debo mi carrera al Amiga, específicamente al A500 + que mis padres me compraron para Navidad cuando tenía 10 años. Al principio jugaba, pero después de un tiempo comencé a sentir curiosidad por saber qué más podía hacer. Jugué con Deluxe Paint III y aprendí sobre Workbench.

El Amiga 500 Plus:

Cada mes compré la popular revista Amiga Format. Un mes tenía una copia gratuita de AMOS. Ingresé al formato Amiga Escribir un juego en AMOS competencia cuando AMOS Professional se colocó en un disco de cobertura más tarde, y fue uno de los 12 (creo) ganadores con In The Pipe Line . ¡Realmente tenías que perseguirlos por premios!

AMOS - El Creador:

Antecedentes

Continuando, lo usé como parte de mis proyectos GCSE y A-Level (gracias a Highspeed Pascal, que era compatible con Turbo Pascal en la PC)

De todos modos, eso fue hace mucho tiempo, y tengo cajas de discos y un A500 + que ya no funciona, así que pensé en hacer una copia de seguridad de esos discos en mi computadora, tanto por preservación como por nostalgia.

El sitio web de Amiga Forever tiene una excelente lista de opciones que incluyen hardware y el uso indebido de dos unidades de disquete en una PC. Lamentablemente, ninguna de ellas era una opción con hardware moderno y los controladores KryoFlux / Catweasel son demasiado caros. Me sorprendió mucho que la mayor parte fuera de código cerrado.

Me gusta mucho la electrónica y ha jugado con dispositivos Atmel ( AT89C4051 ) mientras estaba en la Universidad, decidí echar un vistazo al Arduino (crédito para GreatScott por la inspiración que muestra lo fácil que es comenzar) Me preguntaba si esto era posible.

Así que busqué en Google lectura de la unidad de disquete Arduino código, y después de omitir todos los proyectos que abusaron el impulso para reproducir música, realmente no encontré ninguna solución. Encontré algunas discusiones en algunos grupos que sugirieron que no sería posible. Encontré un proyecto basado en una FPGA que fue una lectura muy interesante, pero no en la dirección que quería ir, así que la única opción era construir una solución yo mismo.

Investigación

Cuando comencé este proyecto, no tenía ni idea de cómo funcionaba la unidad de disquete, y mucho menos cómo se codificaban los datos en ella. Los siguientes sitios web fueron invaluables para entender lo que sucede y cómo funcionan:

  • techtravels.org (y esta página)
  • Preguntas frecuentes sobre el formato .ADF (Amiga Disk File) de Laurent Clévy
  • Amiga Forever
  • Wikipedia - Archivo de disco Amiga
  • Tablero de Amiga en inglés
  • QEEWiki:contadores en el ATmega168 / 328
  • Configuración de pines de la unidad de disquete
  • Lista de formatos de disquete

Supuestos

Basado en la investigación, ahora sabía teóricamente cómo se escribían los datos en el disco y cómo giraba el disco.

Empecé a calcular algunos números. Según la velocidad a la que gira el disco de doble densidad (300 rpm) y la forma en que se almacenan los datos (80 pistas, 11 sectores por pista y 512 bytes por sector, codificados mediante MFM), para leer los datos con precisión necesitaba poder leer muestrear los datos a 500Khz; eso es bastante rápido si se considera que Arduino solo funciona a 16Mhz.

En los intentos que siguen, solo hablo del lado de Arduino. Vaya a la decodificación.

Intento 1:

Primero necesitaba reunir el hardware y la interfaz para la unidad de disquete. La unidad de disquete la tomé de una vieja PC en el trabajo y, al mismo tiempo, tomé su cable IDE.

A continuación se muestra una foto de liberado disquetera desde una PC vieja:

Al estudiar el pinout de la unidad, me di cuenta de que solo necesitaba algunos de los cables y, después de mirar la unidad, me di cuenta de que tampoco usaba la entrada de 12v.

Conseguir que la unidad girara se logró seleccionando la unidad y habilitando el motor. Mover la cabeza fue sencillo. Establece el / DIR pin alto o bajo, y luego pulsó el / STEP alfiler. Podrías saber si la cabeza había alcanzado la pista 0 (la primera pista) monitoreando el / TRK00 alfiler.

Tenía curiosidad por / INDEX alfiler. Pulsa una vez en cada rotación. Como el Amiga no usa esto para encontrar el inicio de la pista, no lo necesitaba y podía ignorarlo. Después de esto, es solo cuestión de elegir qué lado del disco leer ( / SIDE1 ) y conectando / RDATA .

Con el requisito de alta velocidad de datos, lo primero que pensé fue encontrar una manera de hacer esto menos problemático tratando de reducir los requisitos en esta velocidad.

El plan era utilizar dos registros de desplazamiento de 8 bits ( SN74HC594N ) para reducir la frecuencia de muestreo requerida en un factor de 8. Usé lo que Ebay llamó Pro Mini atmega328 Board 5V 16M Arduino Compatible Nano (¡así que no sé qué es eso oficialmente, pero esto funciona en el Uno!) para almacenar en búfer este paralelo datos y envíelos a la PC usando su interfaz serial / USART. Sabía que esto tenía que ejecutarse más rápido que 500Kbaudios (con toda la sobrecarga serial involucrada también).

sn74hc594.pdf

Después de deshacerme de la biblioteca serial estándar de Arduino, me complació mucho descubrir que podía configurar el USART en el Arduino en uptp 2M baudios, y con una de esas placas de ruptura F2DI (eBay lo llamó Basic Breakout Board para FTDI FT232RL USB a serie IC para Arduino - ver más abajo) Podría felizmente y enviar y recibir datos a esta velocidad (62.5Khz) pero necesitaba hacer esto con precisión.

Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf

La placa de conexión FTDI que se adapta perfectamente a la interfaz de la placa Arduino:

Primero, utilicé el Arduino para configurar uno de los registros de desplazamiento de 8 bits, solo uno de los 8 bits con reloj alto. El otro recibió una alimentación directamente desde la unidad de disquete (proporcionando así conversión de serie a paralelo).

La siguiente es una imagen loca de la placa en la que construí esto en ese momento:

Usé uno de los temporizadores Arduino para generar una señal de 500Khz en uno de sus pines de salida y, como el hardware lo maneja, ¡es muy preciso! - Bueno, mi multímetro midió exactamente 500 kHz de todos modos.

El código funcionó, registré un total de 8 bits de datos a 62.5 kHz, dejando la CPU Arduino apenas utilizada. Sin embargo, no estaba recibiendo nada significativo. En este punto me di cuenta de que necesitaba mirar más de cerca los datos reales que salen de la disquetera. Así que compré un osciloscopio viejo y barato en eBay (osciloscopio Gould OS300 de 20Mhz) para ver qué estaba pasando.

Mientras esperaba a que llegara el osciloscopio, decidí probar otra cosa.

Un fragmento de código utilizado para leer los datos de los registros de desplazamiento:

  void readTrackData () {byte op; for (int a =0; a <5632; a ++) {// Esperaremos el marcador de inicio "byte" while (digitalRead (PIN_BYTE_READ_SIGNAL) ==LOW) {}; // Leer el byte op =0; if (digitalRead (DATA_LOWER_NIBBLE_PB0) ==HIGH) op | =1; if (digitalRead (DATA_LOWER_NIBBLE_PB1) ==HIGH) op | =2; if (digitalRead (DATA_LOWER_NIBBLE_PB2) ==HIGH) op | =4; if (digitalRead (DATA_LOWER_NIBBLE_PB3) ==HIGH) op | =8; if (digitalRead (DATA_UPPER_NIBBLE_A0) ==HIGH) op | =16; if (digitalRead (DATA_UPPER_NIBBLE_A1) ==HIGH) op | =32; if (digitalRead (DATA_UPPER_NIBBLE_A2) ==HIGH) op | =64; if (digitalRead (DATA_UPPER_NIBBLE_A3) ==HIGH) op | =128; writeByteToUART (op); // Espere a que lo alto vuelva a bajar while (digitalRead (PIN_BYTE_READ_SIGNAL) ==HIGH) {}; }}  

Intento 2:

Decidí que los registros de turno, aunque una buena idea probablemente no estaba ayudando. Pude leer fácilmente 8 bits de una vez, pero se me ocurrió que no podía estar seguro de que todos los bits estuvieran sincronizados correctamente en primer lugar. Al leer la documentación, sugirió que los datos eran más de pulsos cortos en lugar de altos y bajos.

Eliminé los registros de desplazamiento y me pregunté qué pasaría si intentara verificar un pulso del variador en una interrupción (ISR) usando la señal de 500Khz previamente configurada. Reconfiguré el Arduino para generar el ISR, y después de superar los problemas de las bibliotecas de Arduino que se interponían en el camino (usando el ISR que quería), me mudé al Timer 2.

Escribí un ISR corto que cambiaría un bit a la izquierda de un solo byte global y luego si el pin conectado a la línea de datos de la unidad de disquete era LOW (los pulsos son bajos) O lo haría con un 1. Cada 8 veces que hice esto, escribí el byte completo en el USART.

¡Esto no salió como se esperaba! El Arduino comenzó a comportarse de manera muy errática y extraña. Pronto me di cuenta de que el ISR tardaba más en ejecutarse que el tiempo entre llamadas. Podría recibir un pulso cada 2µSec y basándome en la velocidad del Arduino, y asumiendo que cada instrucción C traducido a 1 ciclo de código de máquina de reloj, Me di cuenta de que podía tener como máximo 32 instrucciones. Lamentablemente, la mayoría serían más de una instrucción, y después de buscar en Google me di cuenta de que la sobrecarga para iniciar un ISR era enorme de todos modos; sin mencionar que las funciones de digitalRead son muy lentas.

Dejé el digitalRead función a favor de acceder directamente a los pines del puerto! Esto todavía no ayudó y no fue lo suficientemente rápido. No preparado para rendirme, dejé este enfoque y decidí seguir adelante e intentar algo más.

En este punto llegó el osciloscopio que compré, ¡y funcionó! ¡Un osciloscopio viejo y crujiente que probablemente era más viejo que yo! Pero aún así hizo el trabajo a la perfección. (Si no sabe qué es un osciloscopio, consulte EEVblog # 926 - Introducción al osciloscopio, y si le gusta la electrónica, le sugiero que vea algunos más y explore el sitio web de EEVBlog.

Mi viejo osciloscopio crujiente recién comprado (Gould OS300 20Mhz):

Después de conectar la señal de 500Khz a un canal y la salida de la disquetera a otro, era obvio que algo no estaba bien. La señal de 500Khz era una onda cuadrada perfecta usándola como disparador, los datos del disquete estaban por todos lados. Podía ver los pulsos, pero era más borroso. Del mismo modo, si disparaba desde la señal de la unidad de disquete, la señal de onda cuadrada de la señal de 500Khz estaba por todas partes y no estaba sincronizada con ella.

Fotos de las huellas en el osciloscopio que se disparan desde los dos canales. No puedes verlo, pero en el canal no que se activan son miles de tenues líneas fantasmales:

De forma individual, pude medir los pulsos de ambas señales a 500 kHz, lo que no tenía sentido, como si ambas estuvieran funcionando a la misma velocidad pero no se dispararan para que pueda ver ambas señales correctamente, entonces algo debe estar mal.

Después de jugar mucho con los niveles de activación, me las arreglé para averiguar qué estaba pasando. Mi señal era perfecta de 500Khz, pero mirando la señal de la unidad de disquete, estaban espaciadas correctamente, pero no todo el tiempo. Entre grupos de pulsos hubo una deriva de error y también brechas en los datos que pusieron la señal totalmente fuera de sincronización.

Recordando la investigación anterior, se suponía que la unidad debía girar a 300 rpm, pero en realidad podría no ser exactamente 300 rpm, además de que la unidad que escribió los datos también podría no estar exactamente a 300 rpm. Luego está el espaciamiento entre sectores y las brechas sectoriales. Claramente había un problema de sincronización, y la sincronización de la señal de 500Khz a la unidad de disquete al comienzo de una lectura no iba a funcionar.

También descubrí que el pulso de la unidad de disquete era extremadamente corto, aunque se podía modificar cambiando la resistencia pullup, y si el tiempo no era exactamente el correcto, entonces el Arduino podría perder un pulso por completo.

Cuando estaba en la universidad (Universidad de Leicester) tomé un módulo llamado sistema integrado. Estudiamos los microcontroladores Atmel 8051. Uno de los proyectos consistió en contar los pulsos de una estación meteorológica simulada (codificador rotatorio). En ese entonces, probaba el pin a intervalos regulares, pero esto no era muy preciso.

El profesor del módulo, Prof Pont sugirió que debería haber usado el contador de hardware características del dispositivo (ni siquiera sabía que tenía una en ese momento)

Revisé la hoja de datos del ATMega328 y, efectivamente, cada uno de los tres temporizadores podría configurarse para contar los pulsos activados desde una entrada externa. Esto significaba que la velocidad ya no era un problema. Todo lo que realmente necesitaba saber era si había ocurrido un pulso dentro de una ventana de tiempo de 2 µseg.

Intento 3:

Ajusté el boceto de Arduino para restablecer el temporizador de 500 kHz cuando se detectó el primer pulso y cada vez que el temporizador de 500 kHz se desbordó. Verifiqué el valor del contador para ver si se había detectado un pulso. Luego realicé la misma secuencia de cambio de bits y cada 8 bits escribí un byte en el USART.

Entraban datos y comencé a analizarlos en la PC. En los datos comencé a ver lo que parecían datos válidos. Aparecería la palabra de sincronización extraña, o grupos de secuencias 0xAAAA, pero nada confiable. Sabía que estaba en algo, pero todavía me faltaba algo.

Intento 4:

Me di cuenta de que mientras se leían los datos, los datos de la unidad probablemente estaban fuera de sincronización / fase con mi señal de 500 kHz. Confirmé esto con solo leer 20 bytes cada vez que comencé a leer.

Al leer sobre cómo manejar este problema de sincronización, me encontré con la frase Phase Locked Loop o PLL. En términos muy simples, para lo que estamos haciendo, el bucle de bloqueo de fase ajusta dinámicamente la frecuencia del reloj (los 500 kHz) para compensar la deriva de frecuencia y la variación en la señal.

La resolución del temporizador no era lo suficientemente alta como para variarla en cantidades lo suficientemente pequeñas (por ejemplo, 444 khz, 470 khz, 500 khz, 533 khz, 571 khz, etc.) y para realizar esto correctamente, probablemente necesitaría que el código se ejecutara mucho más rápido.

Los temporizadores Arduino funcionan contando hasta un número predefinido ( en este caso 16 para 500khz ) luego establecen un registro de desbordamiento y comienzan de nuevo desde 0. El valor real del contador se puede leer y escribir en cualquier punto.

Ajusté el boceto para esperar en un bucle hasta que el temporizador se desbordara, y cuando se desbordó verifiqué un pulso como antes. La diferencia esta vez fue que cuando se detectó un pulso dentro del bucle, restablecí el valor del contador del temporizador a una fase predefinida posición, resincronizando efectivamente el temporizador con cada pulso.

Elegí el valor que escribí en el contador del temporizador de modo que se desbordara a 1 µSeg del pulso de detección (a mitad de camino) para que la próxima vez que el temporizador se desbordara, el pulso hubiera estado separado por 2 µSeg.

¡Esto funcionó! Ahora estaba leyendo datos casi perfectos del disco. Todavía recibía muchos errores de suma de comprobación, lo cual era molesto. Resolví la mayoría de estos volviendo a leer continuamente la misma pista en la unidad hasta que tuve los 11 sectores con sumas de verificación de datos y encabezado válidos.

Tenía curiosidad en este punto, así que volví a conectarlo todo al osciloscopio para ver qué estaba pasando ahora, y como supuse, ahora podía ver ambos rastros ya que ambos estaban sincronizados entre sí:

Me encantaría ver esto un poco más claro, si alguien quiere donarme un hermoso osciloscopio digital de alta gama (por ejemplo, ¡uno de ellos Keysight!) ¡Realmente lo agradecería!

Intento 5:

Me preguntaba si podría mejorar esto. Mirando el código, específicamente el ciclo de lectura interno (ver más abajo) tuve un ciclo while esperando el desbordamiento y luego un if interno buscando un pulso para sincronizar.

Un fragmento de código utilizado para leer los datos y sincronizarlos:

  register bool done =false; // Espere el desbordamiento de 500khz while (! (TIFR2 &_BV (TOV2))) {// Se detectó el borde descendente mientras se espera el pulso de 500khz. if ((TCNT0) &&(! done)) {// pulso detectado, reinicia el contador del temporizador para sincronizarlo con el pulso TCNT2 =fase; // Espere a que el pulso vuelva a subir mientras (! (PIN_RAW_FLOPPYDATA_PORT &PIN_RAW_FLOPPYDATA_MASK)) {}; hecho =verdadero; }} // Restablecer la bandera de desbordamientoTIFR2 | =_BV (TOV2); // ¿Detectamos un pulso del variador? If (TCNT0) {DataOutputByte | =1; TCNT0 =0;}  

Me di cuenta de que, según la instrucción que se estaba ejecutando en los bucles anteriores, el tiempo entre la detección del pulso y la escritura de TCNT2 =fase; podría cambiar por el tiempo necesario para ejecutar algunas instrucciones.

Al darme cuenta de que esto puede estar causando algunos errores / fluctuación en los datos y también con el bucle anterior, es posible que realmente pierda el pulso de la unidad (por lo tanto, me falta un bit de resincronización). intentos, el ISR (interrupción).

Conecté el pulso de datos a un segundo pin en el Arduino. Los datos ahora estaban conectados al disparador COUNTER0 y ahora también al pin INT0. INT0 es una de las prioridades de interrupción más altas, por lo que debería minimizar los retrasos entre el disparador y la llamada al ISR, y como esta es la única interrupción, estoy realmente interesado en que todas las demás estén deshabilitadas.

Todo lo que tenía que hacer la interrupción era realizar el código de resincronización anterior, esto cambió el código para que se vea así:

  // Espera el desbordamiento de 500khz while (! (TIFR2 &_BV (TOV2))) {} // Restablece el indicador de desbordamientoTIFR2 | =_BV (TOV2); // ¿Detectamos un pulso del variador? If (TCNT0) {DataOutputByte | =1; TCNT0 =0;}  

El ISR se veía así:(tenga en cuenta que no usé attachInterrupt ya que esto también agrega una sobrecarga a la llamada).

  byte volátil targetPhase; ISR (INT0_vect) {TCNT2 =targetPhase;}  

Compilar esto produjo demasiado código para ejecutarse lo suficientemente rápido. De hecho, el desmontaje de lo anterior produjo:

  push r1push r0in r0, 0x3f; 63push r0eor r1, r1push r24 lds r24, 0x0102; 0x800102 pts 0x00B2, r24; 0x8000b2 pop r24pop r0out 0x3f, r0; 63pop r0pop r1reti  

Al analizar el código, me di cuenta de que solo necesitaba unas pocas instrucciones. Observando que el compilador realizaría un seguimiento de los registros que golpeé, cambié el ISR de la siguiente manera:

  byte volátil targetPhase asm ("targetPhase"); ISR (INT0_vect) {asm volatile ("lds __tmp_reg__, targetPhase"); asm volatile ("sts% 0, __tmp_reg__"::"M" (_SFR_MEM_ADDR (TCNT2)));}  

Que desarmado, produjo las siguientes instrucciones:

  push r1push r0in r0, 0x3f; 63Pulse r0eor r1, r1lds r0, 0x0102; 0x800102 pts 0x00B2, r0; 0x8000b2 pop r0out 0x3f, r0; 63pop r0pop r1reti  

Todavía hay demasiadas instrucciones. Noté que el compilador estaba agregando muchas instrucciones adicionales, que para mi aplicación realmente no necesitaba estar allí. Entonces busqué el ISR () y tropecé con un segundo parámetro ISR_NAKED. Agregar esto evitaría que el compilador agregue cualquier código especial, pero luego yo sería responsable de mantener los registros, la pila y regresar de la interrupción correctamente. También necesitaría mantener el registro SREG, pero como ninguno de los comandos que necesitaba llamar lo modificó, no tuve que preocuparme por ello.

Esto cambió el código ISR para convertirse en:

  ISR (INT0_vect, ISR_NAKED) {asm volatile ("push __tmp_reg__"); // Conservar el tmp_register asm volatile ("lds __tmp_reg__, targetPhase"); // Copie el valor de fase en el tmp_register asm volatile ("sts% 0, __tmp_reg__"::"M" (_SFR_MEM_ADDR (TCNT2))); // Copie el tmp_register en la ubicación de la memoria donde TCNT2 es asm volatile ("pop __tmp_reg__"); // Restaurar el tmp_register asm volatile ("reti"); // Y salga del ISR}  

Al que convirtió el compilador:

  push r0lds r0, 0x0102; 0x800102 pts 0x00B2, r0; 0x8000b2 pop r0reti  

¡Cinco instrucciones! Perfecto, o al menos tan rápido como iba a ser, ¡teóricamente tarda 0,3125 µSec en ejecutarse! Esto debería significar ahora que la resincronización debería ocurrir en períodos constantes de tiempo después del pulso. A continuación se muestra un diagrama de tiempo de lo que está sucediendo. Así es como recupera datos de una fuente de datos en serie que no tiene una señal de reloj:

Esto mejoró un poco los resultados. Todavía no es perfecto. Algunos discos se leen perfectamente todo el tiempo, algunos discos tardan años y hay que volver a intentarlo. No estoy seguro de si esto se debe a que algunos de los discos han estado allí durante tanto tiempo que el magnetismo se ha degradado a un nivel tan bajo que los amplificadores de unidades no pueden soportarlo. Me preguntaba si esto tenía algo que ver con la disquetera de la PC, así que lo conecté a una disquetera Amiga externa que tenía, pero los resultados fueron idénticos.

Intento 6:

Me pregunté si se podía hacer algo más. Quizás la señal del camino era más ruidosa de lo que pensaba. Después de leer más información, descubrí que una resistencia pullup de 1KOhm era la norma, alimentada en un disparador Schmitt.

Después de instalar un disparador Hex Schmitt SN74HCT14N y reconfigurar el boceto para disparar en los bordes ascendentes en lugar de en los bordes descendentes, lo intenté, pero realmente no hizo ninguna diferencia notable. Supongo que como estaba buscando uno o más pulsos cada vez, esto probablemente absorbió cualquier ruido de todos modos. ¡Así que pegaremos el método Intento 5!

sn74hct14.pdf

Mi solución de placa final se veía en la siguiente línea:

Tenga en cuenta que el cableado de arriba es ligeramente diferente al boceto en vivo. Reordené algunos de los pines de Arduino para facilitar el diagrama del circuito.

Intento 7:

Estaba un poco insatisfecho con algunos de los discos que no había leído. Algunas veces, los discos simplemente no encajaban correctamente en la unidad de disquete. Supongo que el resorte del obturador no ayudó.

Comencé a buscar detectar si había algún error en los datos MFM recibidos desde el disco.

De las reglas de cómo funciona la codificación MFM, me di cuenta de que se podrían aplicar algunas reglas simples de la siguiente manera:

  • No puede haber dos bits "1" uno al lado del otro
  • No puede haber más de tres bits "0" uno al lado del otro

En primer lugar, al decodificar datos MFM, miré para ver si había dos '1' seguidos. Si así fuera, asumí que los datos se habían borrado un poco con el tiempo e ignoré el segundo '1'.

Con esta regla aplicada, hay literalmente tres situaciones de 5 bits en las que se deja que ocurran errores. Esta sería una nueva área en la que podría buscar mejorar los datos.

Sin embargo, sobre todo, me sorprendió que realmente no se detectaran tantos errores MFM. Estoy un poco confundido por qué algunos de los discos no se leen cuando no se encuentran errores.

Esta es un área para una mayor investigación.

Decodificación

Después de leer cómo funcionaba MFM, no estaba completamente seguro de cómo se alineaba correctamente.

Al principio pensé que la salida de la unidad 1 y 0 para los bits de encendido y apagado. Este no fue el caso. El variador emite un pulso para cada transición de fase, es decir, cada vez que los datos pasan de 0 a 1 o de 1 a 0.

Después de leer esto, me pregunté si necesitaba convertir esto de nuevo en 1 y 0 introduciéndolo en un conmutador flip-flop, o leer los datos, buscar sectores y, si no se encontró ninguno, invierta los datos y vuelva a intentarlo.

Resulta que este no es el caso y es mucho más sencillo. Los pulsos son en realidad datos RAW MFM y pueden introducirse directamente en los algoritmos de decodificación. Ahora que entendí esto, comencé a escribir código para escanear un búfer de la unidad y buscar la palabra de sincronización 0x4489. ¡Sorprendentemente lo encontré!

De la investigación que había realizado, me di cuenta de que necesitaba buscar 0xAAAAAAAA44894489 (Una nota de la investigación también sugirió que había algunos errores en el código Amiga temprano que significaba que no se encontró la secuencia anterior. Así que busqué 0x2AAAAAAA44894489 después de hacer un AND de los datos con 0x7FFFFFFFFFFFFFFF ).

Como esperaba, encontré hasta 11 de estos en cada pista correspondientes al inicio real de los 11 sectores de Amiga. Luego comencé a leer los bytes que siguieron para ver si podía decodificar la información del sector.

Tomé un fragmento de código de una de las referencias anteriores para ayudar con la decodificación MFM. No tiene sentido reinventar la rueda, ¿eh?

Después de leer el encabezado y los datos, intenté escribirlo en el disco como un archivo ADF. El formato de archivo estándar del ADF es muy simple. Literalmente son solo los 512 bytes de cada sector (de ambos lados del disco) escritos en orden. Después de escribirlo e intentar abrirlo con ADFOpus y obtener resultados mixtos, a veces abrió el archivo, a veces falló. Obviamente, hubo errores en los datos. Comencé a mirar los campos de suma de verificación en el encabezado, rechazando sectores con sumas de verificación no válidas y repitiendo la lectura hasta que tuve 11 válidos.

Para algunos discos, esto fue todo 11 en la primera lectura, algunos tomaron varios intentos y también diferentes valores de fase.

Finalmente logré escribir archivos ADF válidos. Algunos discos tardarían años, otros literalmente a la velocidad a la que los habría leído el Amiga. Al no tener un Amiga en funcionamiento, no pude comprobar si estos discos se leen correctamente con normalidad, han estado almacenados en una caja en el ático durante años, por lo que es posible que se hayan degradado.

Entonces, ¿qué sigue?

Lo siguiente ya sucedió: V2 está disponible aquí ¡y ha mejorado la compatibilidad con la lectura y la escritura!

Bueno, en primer lugar, hice que todo el proyecto fuera gratuito y de código abierto bajo la Licencia Pública General GNU V3. Si queremos tener alguna esperanza de preservar el Amiga, entonces no deberíamos estafarnos unos a otros por el privilegio y, además, quiero devolver a la mejor plataforma en la que he trabajado. También espero que la gente desarrolle esto, lo lleve más allá y siga compartiendo.

Lo siguiente que quiero es mirar otros formatos. Los archivos ADF son buenos, pero solo funcionan para discos formateados con AmigaDOS. Hay muchos títulos con protección de copia personalizada y formatos de sector no estándar que simplemente no pueden ser compatibles con este formato.

Según Wikipedia, hay otro formato de archivo de disco, el formato FDI. Un formato universal que está bien documentado. La ventaja de este formato es que intenta almacenar los datos de la pista lo más cerca posible del original, ¡así que con suerte solucionará los problemas anteriores!

También me encontré con la Sociedad de Preservación de Software, específicamente CAPS (formalmente la Sociedad de Preservación de Amiga Clásica ) y su formato IPF. Después de leer un poco, me sentí muy decepcionado, todo estaba cerrado y sentí que solo estaban usando este formato para vender su hardware de lectura de disco.

¡Así que mi atención se centrará en la IED! formato. Mi única preocupación aquí es la integridad de los datos. No habrá ninguna suma de comprobación para comprobar si la lectura fue válida, ¡pero tengo algunas ideas para resolver eso!

Y finalmente también lo buscaré agregando una opción de disco de escritura (posiblemente compatible con FDI y ADF), ya que realmente no debería ser tan difícil de agregar.

Código

Repositorio de GitHub
Boceto de Arduino y código fuente de Windows https://github.com/RobSmithDev/ArduinoFloppyDiskReader

Esquemas


Proceso de manufactura

  1. Animación
  2. Disquete
  3. Arduino Spybot
  4. FlickMote
  5. Televisión casera B-Gone
  6. Reloj maestro
  7. Encuéntrame
  8. Arduino Power
  9. Tech-TicTacToe
  10. Arduino Cuadrúpedo
  11. Lector de disquetes Arduino Amiga (V1)