Computador caseiro da placa AON

Recentemente, vários artigos sobre computadores caseiros, criados a partir de vários componentes não padronizados, apareceram na Habré. Também decidi falar sobre o meu computador, criado em 1993. Na esteira do entusiasmo geral pela sinclair, eu queria ter um computador de 8 bits completamente original baseado no z80 e, além disso, criar um software para ele, começando no sistema operacional e terminando com os brinquedos. O que veio disso, leia abaixo do corte.

A primeira pergunta que surgiu ao criar seu computador foi a questão da arquitetura. Decidi que seria compacto o suficiente, com base no processador z80, sem exibir uma imagem na TV, mas com uma tela de texto LCD, um teclado bastante grande, saída de som na forma de um tweeter de tom único (padrão na época na maioria dos computadores) e uma porta RS232 para conexão para outros computadores para fins de programação e depuração.

No entanto, surgiu um problema para obter uma placa de circuito impresso para um circuito completamente original, porque em 1993 os chineses ainda não haviam produzido placas para o mundo inteiro, e era muito caro encomendar desenvolvimento e produção na fábrica. E então voltei minha atenção para os quadros a partir dos quais as identificações de chamadas foram feitas - identificações de chamadas automáticas. Eles estavam à venda no mercado Mitinsky, tiveram a oportunidade de instalar os componentes que eu precisava, mas precisavam ser aprimorados. À venda, havia várias variedades dessas placas de circuito impresso, das quais, se minha memória me servir, escolhi uma placa chamada "Rus".

Deixe-me lembrá-lo aos jovens leitores do que era a AON. Era um telefone com discagem por botão (aliás, era bem legal, já que a maioria dos telefones tinha um disco giratório), consistindo em 12 botões e um indicador de 9 dígitos e 7 segmentos brilhando no qual o número de telefone era exibido, se foi possível determinar e algumas outras informações. A placa AON continha um processador z80, uma porta paralela 58055, um circuito decodificador, um temporizador 58053, uma ROM de 32 kilobytes (com apagamento ultravioleta), uma memória RAM estática de 8 kilobytes, a parte analógica conectada à linha telefônica e, possivelmente, outra coisa, o que eu esqueci nos últimos anos.

O teclado de 12 botões e o indicador disponível no identificador de chamadas não me agradaram categoricamente, pois eles normalmente não podiam inserir informações ou exibi-las (em particular letras). Portanto, decidiu-se usar um teclado caseiro de 40 botões e uma tela LCD de 2 caracteres e 24 caracteres e texto, que então apareceu à venda. Para conectar o teclado e a tela LCD, foi necessário modificar o esquema AON. A primeira coisa que fiz foi jogar fora toda a parte analógica associada à linha telefônica, ou melhor, simplesmente não a fechei. Ao mesmo tempo, as pernas de saída no decodificador, que selecionaram a descarga do indicador e as pernas na porta paralela 58055, foram liberadas. A tela LCD exigia 4 linhas para transmitir informações, mas para o teclado era necessário usar um decodificador.Como 40 botões do teclado foram organizados em uma matriz de 5 linhas de 8 peças, decidiu-se conectar esses 8 botões ao decodificador para que pudessem ser digitalizados fornecendo combinações diferentes de três linhas à entrada do decodificador e lendo 5 valores das linhas de botões usando o KR580VB55. Assim, o código de varredura do botão pressionado no teclado tinha um byte de tamanho, onde os três primeiros bits determinavam a coluna na qual o botão foi pressionado e os 5 bits restantes indicavam em qual linha esse botão (ou vários botões ao mesmo tempo) era pressionado . Além disso, considerei que 8 kilobytes de RAM não seriam suficientes e substituí-lo por 32 kilobytes. Ao mesmo tempo, tive que soldar duas faixas na placa de circuito impresso, felizmente, os casos de RAM de 8 e 32 kilobytes eram quase idênticos na pinagem. Nesse caminho,Eu obtive 32 kilobytes de ROM e 32 kilobytes de RAM (lembro que o z80 pode endereçar um máximo de 64 kilobytes, de modo que usei o espaço de endereço ao máximo). Além disso, mais duas linhas do 58055 foram para a porta serial RS232, necessária para a conexão com outro computador. Coloquei toda essa economia no caso do testador. O resultado é este design:

imagem

Depois que o hardware foi concluído, era hora de criar software. Aqui deve-se notar que o processo de sua criação não parecia nada com o que é hoje. Primeiramente, a maior parte foi escrita no assembler z80 (embora na época quase todos os programas para esses sistemas fossem escritos no assembler, bem, exceto no BASIC). Para a compilação do assembler no primeiro estágio, meu amigo me ajudou, que tinha um computador Profi - um clone sinclair, mas com o TR-DOS a bordo. Então comecei a usar meu PC IBM, que era o OS / 2, que executava o MS-DOS em uma janela separada, na qual, por sua vez, o emulador TR-DOS foi iniciado, no qual a compilação ocorreu. Devo dizer que quando o software cresceu notavelmente, o processo de compilação e montagem levou dez minutos. O segundo problema foique não existiam ferramentas de depuração e, portanto, era necessário fazer o download de tudo sempre no meu computador caseiro e verificar como ele funcionava (a primeira coisa a ser feita era um programa para trabalhar com RS232 a 9600 bits por segundo para download). E, finalmente, deve-se dizer que, após a depuração da imagem da ROM, foi necessário gravá-la nessa própria ROM, depois de ter apagado o que já estava escrito lá. A exclusão foi realizada usando uma lâmpada ultravioleta para bronzear-se através de uma janela especial no compartimento do microcircuito, que foi coberto com um papel adesivo preto especial, que serve para selar o orifício na lateral de um disquete de 5 polegadas para protegê-lo da escrita. Além disso, como eu não tinha programador, para escrever uma nova versão do software em ROM, toda vez que precisava ir ao meu amigo do outro lado de Moscou,quem teve esse programador. Aqui é necessário esclarecer a questão, como foi possível depurar a imagem da ROM sem carregá-la nessa ROM? Havia um endereço base para isso, que todo o meu software usava, mudando de endereço em 32 kilobytes. Ou seja, uma nova imagem da ROM foi carregada na RAM e depurada lá. Após a depuração, o endereço base foi definido como zero, tudo foi compilado novamente e eu fui a um amigo para escrever uma nova versão na ROM.tudo foi compilado novamente e eu fui a um amigo para escrever uma nova versão em ROM.tudo foi compilado novamente e eu fui a um amigo para escrever uma nova versão em ROM.

Quando o processo de "codificação" - "depuração" - "firmware" foi depurado, surgiu a questão de escrever um sistema operacional. O sistema operacional precisava suportar a entrada do teclado, exibir informações em uma tela de LCD, suportar a emissão de sons diferentes e fazer tudo isso em paralelo, se possível, ou seja, com várias tarefas. Por tudo isso, a arquitetura a seguir foi escolhida - havia três tipos de tarefas no sistema operacional: várias tarefas em tempo real com a maior prioridade, uma tarefa do usuário que interagia com a própria pessoa e tarefas em segundo plano que funcionavam quando o processador estava ocioso. Desliguei as tarefas com a maior prioridade na interrupção, gerada pelo timer KR580VI53 10 vezes por segundo. Deve-se dizer que, no identificador de chamadas, foram geradas interrupções 400 vezes por segundo, porque era necessário atualizar o indicador com muita frequência parapara que uma pessoa não note cintilação. Além de atualizar o indicador em interrupções, o teclado foi pesquisado para a busca pela tecla pressionada. Uma interrupção tão frequente no AON levou ao fato de que a maior parte do tempo do processador foi gasta realmente em interrupções, e eu queria que meu computador fizesse outra coisa útil. Como instalei uma tela de LCD como um dispositivo de exibição de informações, que possuía memória própria e não precisava de atualização dinâmica, a necessidade dessa frequência de interrupção não era mais necessária. Foi estabelecido experimentalmente que 10 interrupções por segundo são suficientes para pesquisar o teclado.que a maior parte do tempo do processador era realmente gasta em interrupções e eu queria que meu computador fizesse outra coisa útil. Como instalei uma tela de LCD como um dispositivo de exibição de informações, que possuía memória própria e não precisava de atualização dinâmica, a necessidade dessa frequência de interrupção não era mais necessária. Foi estabelecido experimentalmente que 10 interrupções por segundo são suficientes para pesquisar o teclado.que a maior parte do tempo do processador era realmente gasta em interrupções e eu queria que meu computador fizesse outra coisa útil. Como instalei uma tela de LCD como um dispositivo de exibição de informações, que possuía memória própria e não precisava de atualização dinâmica, a necessidade dessa frequência de interrupção não era mais necessária. Foi estabelecido experimentalmente que 10 interrupções por segundo são suficientes para pesquisar o teclado.

Assim, tarefas em tempo real foram iniciadas a cada interrupção e colocadas novas informações em uma fila de eventos especiais. Os eventos eram de vários tipos e continham informações correspondentes ao tipo de evento, por exemplo, o evento "botão pressionado" continha um código de verificação. Na verdade, uma das tarefas prioritárias iniciadas a cada interrupção era a pesquisa do teclado e a remoção programada do retorno por contato. Além dessa tarefa, havia também tarefas prioritárias - cronômetros que a tarefa do usuário poderia definir e que, após um tempo especificado, enfileiravam o evento correspondente, havia alarmes que funcionavam em um determinado horário, relógio e calendário, além de uma tarefa que tocava música usando programação KR580VI53, e a entrada para ele foi alimentada com dados na forma de notas. Nesse caminho,a tarefa do usuário pode simplesmente começar a tocar música e fazer outras coisas.

O trabalho útil no meu computador foi realizado por uma tarefa do usuário orientada a eventos. Uma visão típica de tal tarefa era semelhante a:

while(true) {
  Event e;

  GetEvent(e);

  /* process event */
}


Aqui, a função GetEvent (e) aguardava o evento aparecer na fila e, quando apareceu, preencheu a estrutura do evento. É claro que, de acordo com um determinado evento, o programa poderia sair de um loop infinito e transferir o controle de volta para o monitor do sistema, que iniciou as tarefas do usuário. Como os eventos na tarefa do usuário geralmente eram processados ​​rapidamente, o programa aguardava a exibição de um novo evento no procedimento GetEvent (e). Para utilizar de alguma forma essa expectativa, foram introduzidas tarefas em segundo plano que o programa do usuário poderia executar e que funcionavam independentemente. Ou seja, quando ocorre uma interrupção do cronômetro, as tarefas em tempo real foram realizadas pela primeira vez, colocando eventos na fila; depois que a interrupção foi concluída, a tarefa do usuário processou todos os eventos da fila e o tempo restante até a próxima interrupção foi dada às tarefas em segundo plano.Hoje, esse esquema parece natural, mas em 1993 foi muito progressivo.

Houve outro problema relacionado ao fato de a tela de LCD não poder exibir letras russas. Isso se deve ao fato de que esses indicadores acabaram de aparecer em nosso mercado e ninguém os russificou especificamente. Naturalmente, não havia bibliotecas para trabalhar com eles, mas havia apenas uma especificação e descrição dos comandos. Portanto, todo o trabalho de inicialização e trabalho com ele tinha que ser escrito do zero. Resolvi o problema com a russificação da seguinte forma: um procedimento especial foi escrito, cuja entrada era uma string com texto em russo que precisava ser exibida. Esse procedimento substituiu todas as letras russas que eram semelhantes ao latim por letras latinas correspondentes e as impossíveis de substituir foram criadas em tempo real usando imagens personalizadas que podiam ser baixadas na tela LCD. Deve-se notarque havia oito dessas imagens de usuário e, consequentemente, no texto russo não poderia haver mais do que oito letras, cujos análogos não estavam no alfabeto latino. Mas geralmente era o suficiente. Outra oportunidade divertida que usei foi que, se você alterar as imagens do usuário para um personagem, terá uma animação engraçada. Usei essa oportunidade no jogo de sapadores, que discutirei abaixo, onde a bandeira acima da mina voou ao vento e a bomba explodiu.Usei essa oportunidade no jogo de sapadores, que discutirei abaixo, onde a bandeira acima da mina voou ao vento e a bomba explodiu.Usei essa oportunidade no jogo de sapadores, que discutirei abaixo, onde a bandeira acima da mina voou ao vento e a bomba explodiu.

Agora vou falar sobre as tarefas do usuário que eu criei para o meu computador. Quando comecei a criá-los, percebi rapidamente que escrevê-los no assembler, como todo mundo naquela época para sistemas como o sinclair, é muito triste. Desde que programei C em um PC IBM, pensei em se é possível programar em C e no meu computador. Após algumas pesquisas, encontrei um compilador da linguagem C para o TR-DOS, além disso, em sua versão clássica de K&R. Esse compilador era bastante primitivo, por exemplo, não verificou se o número de parâmetros passados ​​para o procedimento correspondia ao número real, sem mencionar a verificação de seus tipos. Mas se eu pudesse usar esse compilador no meu computador, seria um tremendo progresso. O problema foi como adaptar o código recebido por este compilador para o meu computador.Aqui segui o caminho clássico, do qual já esquecemos, e em 1993 eles ainda se lembraram. Dessa forma, foi compilado o código-fonte não diretamente no arquivo de objeto, mas no código-fonte do assembler. Assim, era necessário apenas escrever macros que permitissem chamar os procedimentos do assembler compilados a partir do código e vice-versa, com a correta transferência de parâmetros, o que foi feito. O uso desse compilador, além da conveniência de trabalhar com código, proporcionou outra grande vantagem - tornou-se possível trabalhar com multiplicação / divisão de números inteiros (lembro-me que o z80 não possui comandos de multiplicação e divisão de números inteiros) e também (eis que eis!), A capacidade de trabalhar com números ponto flutuante. No entanto, para isso, tive que lidar com a biblioteca anexada ao compilador,pois todas as operações matemáticas após a compilação foram executadas como uma chamada para esta biblioteca. O problema era que essa biblioteca usava variáveis ​​temporárias que o compilador colocou no modelo de memória específico do TR-DOS, o que, é claro, não correspondia completamente ao modelo de memória do meu computador. Eu tive que procurar todas essas variáveis ​​temporárias e refazê-las para os endereços que se encaixam na minha RAM. Mas, como bônus, tenho muitas funções padrão, como seno e cosseno, que usei em minhas tarefas, as quais discutirei abaixo.Eu tive que procurar todas essas variáveis ​​temporárias e refazê-las para endereços que se encaixam na minha RAM. Mas, como bônus, tenho muitas funções padrão, como seno e cosseno, que usei em minhas tarefas, as quais discutirei abaixo.Eu tive que procurar todas essas variáveis ​​temporárias e refazê-las para os endereços que se encaixam na minha RAM. Mas, como bônus, tenho muitas funções padrão, como seno e cosseno, que usei em minhas tarefas, as quais discutirei abaixo.

O primeiro programa criado usando o compilador C foi um programa para verificar a operação correta da RAM. Este programa percorreu toda a RAM, escreveu e depois leu vários padrões e comparou o resultado. Além disso, todo o processo foi exibido na tela LCD, o que também confirmou a operação correta dos procedimentos associados. É claro que isso foi apenas um teste da caneta antes de escrever programas mais interessantes. A próxima coisa que fiz foi criar um editor hexadecimal para modificar o conteúdo da RAM. Este editor já possuía amplas possibilidades, incluindo não apenas a visualização e edição de células de memória, mas também a procura de bytes interessantes na RAM. Em particular, tornou-se possível escrever pequenos programas diretamente na RAM em códigos de máquina. Para testar os recursos de som do seu computador,Criei um programa como uma “caixa de música” que tocava várias melodias (lembro que as melodias eram gravadas como notas). Além disso, foi escrito um programa para o jogo “touros e vacas”, onde uma pessoa era solicitada a adivinhar um número aleatório de quatro dígitos concebido por um computador (sim, eu também criei um gerador de números aleatórios). Neste programa, tive que criar uma lista de rolagem com um histórico de números já inseridos, pois era impossível exibir uma lista particularmente longa em duas linhas. O próximo programa criado foi um "sapper" clássico em um campo de 16x16. Como eu tinha apenas duas linhas à minha disposição, fiz a rolagem, adicionando acompanhamento musical e animação de uma bandeira balançando ao vento e bombas explodindo. Além disso, para meditação, criei um programa que mostrava vermes rastejando em uma direção aleatória,usando a capacidade de fazer upload de imagens personalizadas na tela. Bem, e onde sem o clássico jogo da velha? Neles, testei o algoritmo minimax, ou seja, o computador calculou (com bastante rapidez) todas as opções e nunca perdeu. Como meu sistema operacional suportava alarmes, hora e calendário, criei um programa para o usuário que definia a hora, a data e vários alarmes que, quando atingiam a hora definida, tocavam melodias diferentes. E, finalmente, a coroa da criação no meu computador é um programa para calcular a trajetória do movimento no problema de dois corpos gravitacionais. Ou seja, para uma determinada posição inicial e velocidade do corpo, bem como a massa do centro de atração,o problema da integração numérica de um sistema de duas equações diferenciais ordinárias não lineares de segunda ordem com uma dada etapa de integração foi resolvido. Para isso, adicionei um procedimento que integrava numericamente essas equações. Todas as variáveis ​​foram no formato de ponto flutuante duplo. Aqui enfatizo mais uma vez que no processador z80 não há apenas suporte de hardware para trabalhar com ponto flutuante, mas também a multiplicação / divisão usual de números inteiros, tudo foi feito de forma programática. Essa integração funcionou bem devagar, cerca de um passo por segundo, mas funcionou!mas também a multiplicação / divisão usual de números inteiros, tudo foi feito programaticamente. Essa integração funcionou bem devagar, cerca de um passo por segundo, mas funcionou!mas também a multiplicação / divisão usual de números inteiros, tudo foi feito programaticamente. Essa integração funcionou bem devagar, cerca de um passo por segundo, mas funcionou!

O que eu ainda queria implementar, mas não deu certo - primeiro, alocação / desalocação dinâmica de memória, para que eu pudesse gerenciar com flexibilidade os recursos de cada tarefa. Além disso, a gravação / leitura em fita não foi implementada, como foi feito no Sinclair. Mas no geral, fiquei satisfeito com o que consegui fazer. Enfatizo que tudo isso estava em uma ROM de 32 kilobytes - o tamanho atual de um email não tão grande.

Você pode olhar para o meu computador em ação aqui:




E baixe o software dele aqui

All Articles