Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Leitor de disquete Arduino Amiga (V1)

Componentes e suprimentos

Resistor 1k ohm
× 1
Breadboard (genérico)
× 1
Fios de jumpers (genérico)
× 1
Fonte de alimentação SparkFun breadboard 5V / 3,3V
× 1
Arduino UNO
Ou Pro Mini
× 1

Aplicativos e serviços online

Arduino IDE

Sobre este projeto

  • Meu objetivo: Para criar uma maneira simples, barata e de código aberto de recuperar dados de disquetes Amiga DD de dentro do Windows 10 e outros sistemas operacionais.
  • Minha solução: Um Arduino + um aplicativo Windows
  • Por quê: Para preservar os dados desses discos para o futuro. Além disso, um PC normal não pode ler discos Amiga devido à maneira como são gravados.

Site do projeto:http://amiga.robsmithdev.co.uk/

Este é o V1 do projeto. V2 contém leitura e escrita aprimoradas!





Experiências Amiga


Devo a minha carreira ao Amiga, mais especificamente ao A500 + que os meus pais compraram para mim no Natal aos 10 anos. No começo eu jogava, mas depois de um tempo comecei a ficar curioso para saber o que mais ele poderia fazer. Joguei com o Deluxe Paint III e aprendi sobre o Workbench.

O Amiga 500 Plus:

Todos os meses eu comprava a popular revista Amiga Format. Um mês teve uma cópia gratuita do AMOS. Entrei no formato Amiga Write A Game In AMOS competição quando AMOS Professional foi colocado em um coverdisk mais tarde, e foi um dos 12 (eu acho) vencedores com In The Pipe Line . Você realmente teve que persegui-los para ganhar prêmios!

AMOS - O Criador:





Antecedentes


Seguindo em frente, usei-o como parte de meus projetos GCSEs e A-Level (graças a Highspeed Pascal, que era compatível com Turbo Pascal no PC)

Enfim, isso foi há muito tempo e eu tenho caixas de discos e um A500 + que não funciona mais, então pensei em fazer backup desses discos no meu computador, tanto para preservação quanto para nostalgia.

O site Amiga Forever tem uma excelente lista de opções que incluem hardware e uso abusivo de dois drives de disquete em um PC - infelizmente nenhum deles era uma opção com hardware moderno e os controladores KryoFlux / Catweasel são muito caros. Fiquei realmente surpreso ao ver que a maior parte era de código fechado.

Em grande parte na eletrônica e tendo jogado com dispositivos Atmel ( AT89C4051 ) enquanto estava na universidade, decidi dar uma olhada no Arduino (crédito para GreatScott para a inspiração que mostra como é fácil começar) Eu me perguntei se isso seria possível.

Então, pesquisei no Google por leitura da unidade de disquete Arduino código, e depois de pular todos os projetos que abusaram a vontade de tocar música, eu realmente não encontrei nenhuma solução. Encontrei algumas discussões em alguns grupos, sugerindo que não seria possível. Eu encontrei um projeto baseado em um FPGA que era de leitura muito interessante, mas não era a direção que eu queria ir, então a única opção era construir eu mesmo uma solução.





Pesquisa


Quando comecei este projeto, não tinha a menor ideia de como a unidade de disquete funcionava, e menos ainda como os dados foram codificados nela. Os seguintes sites foram inestimáveis ​​para minha compreensão sobre o que acontece e como funcionam:
  • techtravels.org (e esta página)
  • Perguntas frequentes sobre o formato .ADF (Amiga Disk File) de Laurent Clévy
  • Amiga Forever
  • Wikipedia - Arquivo de disco Amiga
  • Amiga Board em inglês
  • QEEWiki - Contadores no ATmega168 / 328
  • pinagem da unidade de disquete
  • Lista de formatos de disquetes





Suposições


Com base na pesquisa, agora eu sabia teoricamente como os dados eram gravados no disco e como o disco girava.

Comecei a calcular alguns números. Com base na velocidade do disco de densidade dupla girado (300 rpm) e na forma como os dados são armazenados (80 trilhas, 11 setores por trilha e 512 bytes por setor, codificados usando MFM), para ler os dados com precisão eu precisava ser capaz de amostrar os dados em 500Khz; isso é bastante rápido quando você considera que o Arduino está rodando apenas a 16 MHz.

Nas tentativas a seguir, estou falando apenas sobre o lado do Arduino. Pule para a decodificação.





Tentativa 1:


Primeiro, precisei reunir o hardware e a interface para a unidade de disquete. A unidade de disquete que peguei de um PC antigo no trabalho e peguei seu cabo IDE ao mesmo tempo.

Abaixo está uma foto de libertado unidade de disquete de um PC antigo:

Estudando a pinagem do drive percebi que só precisava de alguns fios dele, e depois de olhar para o drive percebi que também não usava entrada de 12v.

Fazer o inversor girar foi alcançado selecionando o inversor e habilitando o motor. Mover a cabeça era simples. Você define o / DIR pino alto ou baixo e, em seguida, pulsou o / STEP alfinete. Você poderia dizer se a cabeça atingiu a faixa 0 (a primeira faixa) monitorando o / TRK00 alfinete.

Eu estava curioso sobre o / INDEX alfinete. Isso pulsa uma vez a cada rotação. Como o Amiga não usa isto para encontrar o início da faixa, não precisei e pude ignorar. Depois disso é só uma questão de escolher qual lado do disco ler ( / SIDE1 ) e conectando / RDATA .

Com o alto requisito de taxa de dados, meu primeiro pensamento foi encontrar uma maneira de tornar isso menos problemático, tentando reduzir os requisitos dessa taxa.

O plano era usar dois registradores de deslocamento de 8 bits ( SN74HC594N ) para reduzir a frequência de amostragem necessária em um fator de 8. Eu usei o que o Ebay chamou de Pro Mini atmega328 Board 5V 16M Arduino Compatible Nano (então eu não sei o que é oficialmente, mas funciona no Uno!) para armazenar esse paralelo dados e enviá-los para o PC usando sua interface serial / USART. Eu sabia que isso precisava ser executado a uma velocidade superior a 500 Kbaud (com toda a sobrecarga serial envolvida também).
sn74hc594.pdf
Depois de abandonar a biblioteca serial padrão do Arduino, fiquei realmente satisfeito em descobrir que pude configurar o USART no Arduino em até 2M baud e com uma dessas placas break-out F2DI (o eBay a chamou de Basic Breakout Board For FTDI FT232RL USB para IC serial para Arduino - veja abaixo) Eu poderia enviar e receber dados nesta taxa (62,5 KHz), mas eu precisava fazer isso com precisão.
Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf
A placa de breakout FTDI que se encaixa perfeitamente na interface da placa Arduino:

Primeiro, usei o Arduino para configurar os registradores de deslocamento de 8 bits apenas um dos 8 bits com clock alto. O outro recebia uma alimentação diretamente da unidade de disquete (fornecendo assim a conversão serial para paralela).

O que se segue é uma imagem maluca da placa de ensaio em que criei isso na época:

Usei um dos temporizadores Arduinos para gerar um sinal de 500Khz em um de seus pinos de saída e como o hardware gerencia isso, é muito preciso! - Bem, meu multímetro mediu exatamente 500 kHz de qualquer maneira.

O código funcionou, eu registrei 8 bits de dados completos a 62,5 kHz, deixando a CPU do Arduino dificilmente utilizada. No entanto, eu não estava recebendo nada significativo. Nesse ponto, percebi que precisava dar uma olhada mais de perto nos dados reais que saíam da unidade de disquete. Então comprei um velho osciloscópio barato no eBay (Gould OS300 20Mhz Oscilloscope) para verificar o que estava acontecendo.

Enquanto esperava o osciloscópio chegar, decidi tentar outra coisa.

Um fragmento de código usado para ler os dados dos registradores de deslocamento:
  void readTrackData () {byte op; for (int a =0; a <5632; a ++) {// Vamos esperar pelo marcador de início "byte" while (digitalRead (PIN_BYTE_READ_SIGNAL) ==LOW) {}; // Lê o 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); // Aguarde a queda alta novamente enquanto (digitalRead (PIN_BYTE_READ_SIGNAL) ==HIGH) {}; }}  





Tentativa 2:


Decidi que o turno é registrado, embora uma boa ideia provavelmente não estivesse ajudando. Eu era capaz de ler facilmente 8 bits de uma vez, mas me ocorreu que não poderia ter certeza de que todos os bits estavam marcados corretamente em primeiro lugar. A leitura da documentação sugeriu que os dados eram mais de pulsos curtos do que de altos e baixos.

Eu removi os registradores de deslocamento e me perguntei o que aconteceria se eu tentasse verificar se há um pulso da unidade em uma interrupção (ISR) usando o sinal de 500Khz configurado anteriormente. Eu reconfigurei o Arduino para gerar o ISR, e depois que passei os problemas das bibliotecas do Arduino atrapalhando (usando o ISR que eu queria), mudei para o Timer 2.

Eu escrevi um ISR curto que mudaria um bit para a esquerda de um único byte global e então se o pino conectado à linha de dados da unidade de disquete fosse BAIXO (os pulsos estão baixos) Eu colocaria OR 1 nele. A cada 8 vezes que fiz isso, escrevi o byte completo para o USART.

Isso não saiu como esperado! O Arduino começou a se comportar de forma muito errática e estranha. Logo percebi que o ISR estava demorando mais para ser executado do que o tempo entre as chamadas para ele. Eu poderia receber um pulso a cada 2µSec e com base na velocidade do Arduino, e fazendo uma suposição selvagem de que cada instrução C traduzido para 1 ciclo de código de máquina de relógio, Percebi que poderia ter no máximo 32 instruções. Infelizmente, a maioria seria mais de uma instrução, e depois de pesquisar no Google, percebi que a sobrecarga para iniciar um ISR era enorme de qualquer maneira; sem mencionar que as funções digitalRead são muito lentas.

Abandonei o digitalRead função em favor de acessar os pinos da porta diretamente! Isso ainda não ajudou e não foi rápido o suficiente. Não preparado para desistir, engavetei essa abordagem e decidi seguir em frente e tentar outra coisa.

Nesse ponto chegou o osciloscópio que comprei e funcionou! Um osciloscópio velho e crocante que provavelmente era mais velho do que eu! Mas ainda assim fez o trabalho perfeitamente. (Se você não sabe o que é um Osciloscópio, dê uma olhada no EEVblog # 926 - Introdução ao Osciloscópio, e se você gosta de eletrônica, sugiro assistir mais alguns e dar uma olhada no site EEVBlog.

Meu velho osciloscópio recém-adquirido (Gould OS300 20Mhz):

Depois de conectar o sinal de 500Khz a um canal e a saída da unidade de disquete para outro, era óbvio que algo não estava certo. O sinal de 500Khz era uma onda quadrada perfeita usando-o como um gatilho, os dados do disquete estavam espalhados por todo o lugar. Eu podia ver os pulsos, mas era mais um borrão. Da mesma forma, se eu acionasse o sinal da unidade de disquete, o sinal de onda quadrada do sinal de 500 kHz estava em todo lugar e não em sincronia com ele.

Fotos dos traços no osciloscópio disparando dos dois canais. Você não consegue ver, mas no canal não sendo acionado são milhares de linhas fantasmagóricas fracas:

Individualmente, eu poderia medir os pulsos de ambos os sinais a 500Khz, o que não fazia sentido, como se ambos estivessem funcionando na mesma velocidade, mas não acionassem, então você pode ver os dois sinais corretamente, então algo deve estar errado.

Depois de brincar muito com os níveis de gatilho, consegui entender o que estava acontecendo. Meu sinal era de 500Khz perfeitos, mas olhando para o sinal da unidade de disquete, bem, eles estavam espaçados corretamente, mas não o tempo todo. Entre os grupos de pulsos houve um desvio de erro, além de lacunas nos dados que colocaram o sinal totalmente fora de sincronia.

Lembrando da pesquisa anterior, a unidade deveria girar a 300 rpm, mas pode não ser exatamente 300 rpm, mais a unidade que gravou os dados também pode não estar exatamente 300 rpm. Depois, há o espaçamento entre setores e lacunas de setor. Obviamente, havia um problema de sincronização e sincronizar o sinal de 500 kHz com a unidade de disquete no início de uma leitura não iria funcionar.

Também descobri que o pulso da unidade de disquete era extremamente curto, embora você pudesse modificar isso alterando o resistor pullup e, se o tempo não fosse exatamente correto, o Arduino poderia perder um pulso ao mesmo tempo.

Quando eu estava na universidade (University of Leicester), fiz um módulo chamado sistema embarcado. Estudamos os microcontroladores Atmel 8051. Um dos projetos envolveu a contagem de pulsos de uma estação meteorológica simulada (codificador rotativo). Naquela época, eu amostrava o pino em intervalos regulares, mas isso não era muito preciso.

O palestrante do módulo, Prof Pont sugeri que eu deveria ter usado o contador de hardware recursos do dispositivo (eu nem sabia que tinha um na época).

Verifiquei a folha de dados do ATMega328 e, com certeza, cada um dos três temporizadores pode ser configurado para contar os pulsos disparados de uma entrada externa. Isso significava que a velocidade não era mais um problema. Tudo que eu realmente precisava saber era se um pulso havia ocorrido dentro de uma janela de tempo de 2 µSec.





Tentativa 3:


Ajustei o esboço do Arduino para zerar o cronômetro de 500 khz quando o primeiro pulso foi detectado e sempre que o cronômetro de 500 khz transbordou, verifiquei o valor do contador para ver se um pulso havia sido detectado. Em seguida, executei a mesma sequência de deslocamento de bits e cada 8 bits escreveu um byte para o USART.

Os dados estavam chegando e comecei a analisá-los no PC. Nos dados, comecei a ver o que pareciam dados válidos. A palavra de sincronização estranha iria aparecer, ou grupos de sequências 0xAAAA, mas nada confiável. Eu sabia que estava no caminho certo, mas ainda estava faltando alguma coisa.





Tentativa 4:


Percebi que, conforme os dados estavam sendo lidos, os dados da unidade provavelmente estavam fora de sincronia / fase com meu sinal de 500 kHz. Confirmei isso lendo apenas 20 bytes cada vez que comecei a ler.

Lendo sobre como lidar com esse problema de sincronização, me deparei com a frase Phase Locked Loop ou PLL. Em termos muito simples, para o que estamos fazendo, o loop de bloqueio de fase ajustaria dinamicamente a frequência do clock (500 khz) para compensar o desvio de frequência e a variação no sinal.

A resolução do cronômetro não era alta o suficiente para variar em pequenas quantidades (por exemplo, 444 khz, 470 khz, 500 khz, 533 khz, 571 khz etc.) e para executar isso corretamente, eu provavelmente precisaria que o código rodasse muito mais rápido.

Os temporizadores do Arduino funcionam contando até um número predefinido ( neste caso, 16 para 500 khz ) em seguida, eles definem um registro de estouro e começam novamente do 0. O valor real do contador pode ser lido e gravado em qualquer ponto.

Ajustei o esboço para esperar em loop até que o cronômetro transbordasse e, quando transbordasse, verifiquei o pulso como antes. A diferença desta vez foi quando um pulso foi detectado dentro do loop, eu redefino o valor do contador do temporizador para uma fase predefinida posição, efetivamente ressincronizando o cronômetro com cada pulso.

Eu escolhi o valor que escrevi no contador do temporizador de forma que ele estourasse a 1 µSeg do pulso de detecção (meio caminho), de forma que da próxima vez que o temporizador estourasse o pulso tivesse 2 µS de diferença.

Isso funcionou! Agora estava lendo dados quase perfeitos do disco. Eu ainda estava recebendo muitos erros de soma de verificação, o que era irritante. Resolvi a maioria deles relendo continuamente a mesma trilha na unidade até ter todos os 11 setores com cabeçalho válido e somas de verificação de dados.

Eu estava curioso neste ponto, então conectei tudo de volta ao osciloscópio novamente para ver o que estava acontecendo agora, e como eu imaginei, agora eu podia ver os dois traços, pois ambos estavam sincronizados um com o outro:

Eu adoraria ver isso um pouco mais claro, se alguém quiser me doar um adorável osciloscópio digital topo de linha (por exemplo, um deles da Keysight!) Eu realmente aprecio!





Tentativa 5:


Eu me perguntei se eu poderia melhorar isso. Olhando para o código, especificamente o loop de leitura interno (veja abaixo), tive um loop while esperando pelo estouro e, em seguida, um if interno procurando um pulso para sincronizar.

Um fragmento de código usado para ler os dados e sincronizar com eles:
  register bool done =false; // Aguarde o estouro de 500 khz enquanto (! (TIFR2 &_BV (TOV2))) {// borda descendente detectada enquanto espera pelo pulso de 500 khz. if ((TCNT0) &&(! done)) {// pulso detectado, zera o contador do temporizador para sincronizar com o pulso TCNT2 =fase; // Aguarde o pulso aumentar novamente enquanto (! (PIN_RAW_FLOPPYDATA_PORT &PIN_RAW_FLOPPYDATA_MASK)) {}; feito =verdadeiro; }} // Redefine o overflow flagTIFR2 | =_BV (TOV2); // Detectamos um pulso do drive? If (TCNT0) {DataOutputByte | =1; TCNT0 =0;}  

Percebi que dependendo de qual instrução estava sendo executada nos loops acima, o tempo entre a detecção do pulso e a gravação TCNT2 =phase; pode mudar pelo tempo necessário para executar algumas instruções.

Percebendo que isso pode estar causando alguns erros / jitter nos dados e também com o loop acima, é possível que eu possa realmente perder o pulso do drive (perdendo assim um bit de ressincronização), decidi usar um dos meus anteriores tentativas, o ISR (interrupção).

Eu conectei o pulso de dados a um segundo pino no Arduino. Os dados agora estavam conectados ao gatilho COUNTER0 e agora também ao pino INT0. INT0 é uma das maiores prioridades de interrupção, portanto, deve minimizar os atrasos entre o acionador e o ISR sendo chamado, e como esta é a única interrupção, estou realmente interessado em todas as outras que estão desabilitadas.

Toda a interrupção necessária foi executar o código de ressincronização acima, isso mudou o código para ficar assim:
  // Aguarde o estouro de 500 khz enquanto (! (TIFR2 &_BV (TOV2))) {} // Redefina o sinalizador de estouroTIFR2 | =_BV (TOV2); // Detectamos um pulso do drive? If (TCNT0) {DataOutputByte | =1; TCNT0 =0;}  

O ISR tinha a seguinte aparência:(observe que não usei attachInterrupt, pois isso também adiciona sobrecarga à chamada).
  byte volátil targetPhase; ISR (INT0_vect) {TCNT2 =targetPhase;}  

Compilar isso produziu código demais para ser executado com rapidez suficiente. Na verdade, desmontando o acima produzido:
  push r1push r0in r0, 0x3f; 63push r0eor r1, r1push r24 lds r24, 0x0102; 0x800102 sts 0x00B2, r24; 0x8000b2 pop r24pop r0out 0x3f, r0; 63pop r0pop r1reti  

Ao analisar o código, percebi que havia apenas algumas instruções de que realmente precisava. Observando que o compilador manteria o controle de todos os registros que eu destruí, alterei o ISR da seguinte maneira:
  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 desmontado, produziu as seguintes instruções:
  push r1push r0in r0, 0x3f; 63push r0eor r1, r1lds r0, 0x0102; 0x800102 sts 0x00B2, r0; 0x8000b2 pop r0out 0x3f, r0; 63pop r0pop r1reti  

Ainda há muitas instruções. Percebi que o compilador estava adicionando muitas instruções extras, que para minha aplicação realmente não precisavam estar lá. Então, pesquisei o ISR () e tropeçou em um segundo parâmetro ISR_NAKED. Adicionar isso evitaria que o compilador adicionasse qualquer código especial, mas eu seria responsável por manter os registros, a pilha e retornar da interrupção corretamente. Eu também precisaria manter o registro SREG, mas como nenhum dos comandos que precisei chamar o modificou, não precisei me preocupar com isso.

Isso mudou o código ISR para se tornar:
  ISR (INT0_vect, ISR_NAKED) {asm volatile ("push __tmp_reg__"); // Preserve o tmp_register asm volatile ("lds __tmp_reg__, targetPhase"); // Copia o valor da fase no tmp_register asm volatile ("sts% 0, __tmp_reg__"::"M" (_SFR_MEM_ADDR (TCNT2))); // Copie o tmp_register para o local da memória onde TCNT2 é asm volatile ("pop __tmp_reg__"); // Restaura o tmp_register asm volatile ("reti"); // E saia do ISR}  

Para o qual o compilador foi convertido:
  push r0lds r0, 0x0102; 0x800102 sts 0x00B2, r0; 0x8000b2 pop r0reti  

Cinco instruções! Perfeito, ou pelo menos tão rápido quanto seria, teoricamente levando 0,3125 µS para ser executado! Isso agora deve significar que a ressincronização deve acontecer em períodos consistentes de tempo após o pulso. Abaixo está um diagrama de tempo do que está acontecendo. É assim que você recupera dados de um feed de dados seriais que não tem um sinal de relógio:

Isso melhorou um pouco os resultados. Ainda não é perfeito. Alguns discos têm uma leitura perfeita todas as vezes, outros levam muito tempo e precisam continuar tentando. Não tenho certeza se isso é porque alguns dos discos estão lá há tanto tempo que o magnetismo se degradou a um nível tão baixo que os amplificadores dos drives não conseguem lidar com isso. Eu me perguntei se isso tinha algo a ver com a unidade de disquete do PC, então conectei isso a uma unidade de disquete Amiga externa que tinha, mas os resultados foram idênticos.





Tentativa 6:


Eu me perguntei se havia algo mais que pudesse ser feito. Talvez o sinal da unidade fosse mais barulhento do que eu pensava. Depois de ler mais informações, descobri que um resistor pullup de 1KOhm era a norma, alimentado em um gatilho Schmitt.

Depois de instalar um SN74HCT14N Hex Schmitt Trigger e reconfigurar o esboço para disparar nas bordas crescentes em vez de nas bordas decrescentes, experimentei, mas não fez nenhuma diferença perceptível. Eu acho que como eu estava procurando por um ou mais pulsos de cada vez, isso provavelmente foi absorvido qualquer ruído de qualquer maneira. Portanto, vamos manter o método da Tentativa 5!
sn74hct14.pdf
Minha solução final de breadboard parecia assim:

Observe que a fiação acima é ligeiramente diferente do esboço ao vivo. Reordenei alguns dos pinos do Arduino para tornar o diagrama de circuito mais fácil.





Tentativa 7:


Fiquei um pouco insatisfeito com alguns dos discos que não tinha lido. Algumas vezes, os discos simplesmente não se encaixavam corretamente na unidade de disquete. Acho que a mola da veneziana não estava ajudando.

Comecei a procurar detectar se havia algum erro nos dados MFM reais recebidos do disco.

A partir das regras de funcionamento da codificação MFM, percebi que algumas regras simples podem ser aplicadas da seguinte maneira:
  • Não pode haver dois bits '1' próximos um do outro
  • Não pode haver mais de três bits '0' próximos um do outro

Em primeiro lugar, ao decodificar os dados MFM, verifiquei se havia dois '1s em sequência. Se estivessem, eu presumia que os dados ficaram um pouco manchados com o tempo e ignorei o segundo '1'.

Com esta regra aplicada, existem literalmente três situações de 5 bits em que erros são deixados para ocorrer. Essa seria uma nova área onde eu poderia procurar melhorar os dados.

Porém, principalmente, fiquei surpreso ao ver que não havia muitos erros de MFM detectados. Estou um pouco confuso por que alguns dos discos não são lidos quando nenhum erro é encontrado.

Esta é uma área para uma investigação mais aprofundada.





Decodificando


Depois de ler como o MFM funcionava, não tinha certeza de como ele estava alinhado corretamente.

A princípio pensei que a unidade de saída 1s e 0s para os bits on e off. Não foi esse o caso. O drive emite um pulso para cada transição de fase, ou seja:toda vez que os dados vão de 0 para 1, ou 1 para 0.

Depois de ler isso, me perguntei se eu precisava converter isso de volta para 1s e 0s, alimentando-os em um alternador flip-flop, ou ler os dados, pesquisar por setores e, se nenhum fosse encontrado, inverta os dados e tente novamente!

Acontece que este não é o caso e é muito mais simples. Os pulsos são, na verdade, os dados RAW MFM e podem ser alimentados diretamente nos algoritmos de decodificação. Agora que entendi isso, comecei a escrever um código para escanear um buffer da unidade e procurar a palavra de sincronização 0x4489. Surpreendentemente, encontrei!

A partir da pesquisa que conduzi, percebi que precisava realmente pesquisar por 0xAAAAAAAA44894489 (uma nota da pesquisa também sugeriu que havia alguns bugs no código Amiga inicial que significava que a sequência acima não foi encontrada. Então, em vez disso, procurei por 0x2AAAAAAA44894489 após efetuar o AND dos dados com 0x7FFFFFFFFFFFFFFF )

Como esperado, encontrei até 11 destes em cada faixa, correspondendo ao início real dos 11 setores do Amiga. Comecei então a ler os bytes que se seguiram para ver se conseguia decodificar as informações do setor.

Peguei um trecho de código de uma das referências acima para ajudar na decodificação MFM. Não adianta reinventar a roda, hein?

Depois de ler o cabeçalho e os dados, tentei gravá-lo no disco como um arquivo ADF. O formato de arquivo ADF padrão é muito simples. São literalmente apenas 512 bytes de cada setor (de ambos os lados do disco) escritos em ordem. Depois de escrevê-lo e tentar abri-lo com o ADFOpus e obter resultados mistos, às vezes ele abria o arquivo, às vezes falhava. Obviamente, havia erros nos dados. Comecei a olhar para os campos de checksum no cabeçalho, rejeitando setores com checksums inválidos e repetindo a leitura até que tivesse 11 válidos.

Para alguns discos, isso era tudo 11 na primeira leitura, alguns levaram várias tentativas e também valores de fase diferentes.

Finalmente consegui escrever arquivos ADF válidos. Alguns discos demorariam séculos, outros literalmente a velocidade que o Amiga teria lido. Não tendo mais um Amiga funcionando, não pude realmente verificar se esses discos liam corretamente normalmente, eles foram armazenados em uma caixa no sótão por anos, então podem muito bem estar degradados.





Então, o que vem a seguir?


O próximo já aconteceu - V2 está disponível aqui e melhorou o suporte para leitura e escrita!

Bem, em primeiro lugar, tornei todo o projeto gratuito e de código aberto sob a GNU General Public License V3. Se quisermos ter alguma esperança de preservar o Amiga, não devíamos estar a enganar-nos por causa do privilégio e, além disso, quero retribuir a melhor plataforma em que alguma vez trabalhei. Também espero que as pessoas desenvolvam isso, levem adiante e continuem compartilhando.

Em seguida, quero examinar outros formatos. Os arquivos ADF são bons, mas funcionam apenas com discos formatados com AmigaDOS. Existem muitos títulos com proteção de cópia personalizada e formatos de setor não padrão que simplesmente não podem ser suportados por este formato.

De acordo com a Wikipedia, há outro formato de arquivo em disco, o formato FDI. Um formato universal bem documentado. A vantagem desse formato é que ele tenta armazenar os dados da faixa o mais próximo possível do original, portanto, esperamos corrigir os problemas acima!

Também descobri a Software Preservation Society, especificamente CAPS (formalmente a Classic Amiga Preservation Society ) e seu formato IPF. Depois de um pouco de leitura, fiquei muito desapontado, está tudo fechado e parecia que eles estavam apenas usando esse formato para vender seu hardware de leitura de disco.

Portanto, meu foco será no IDE! formato. Minha única preocupação aqui é com a integridade dos dados. Não haverá nenhuma soma de verificação para eu verificar se a leitura foi válida, mas tenho algumas ideias para resolver isso!

E, finalmente, também estarei procurando adicionar uma opção de disco de gravação (possivelmente com suporte para FDI, bem como ADF), pois realmente não deve ser tão difícil de adicionar.

Código

Repo do GitHub
Esboço do Arduino e código-fonte do Windowshttps://github.com/RobSmithDev/ArduinoFloppyDiskReader

Esquemas


Processo de manufatura

  1. Animação
  2. Disquete
  3. Arduino Spybot
  4. FlickMote
  5. TV B-Gone caseiro
  6. Relógio mestre
  7. Encontre-me
  8. Arduino Power
  9. Tech-TicTacToe
  10. Leitor de disquete Arduino Amiga (V1)