Miniatura Macintosh Plus

imagem

Nos primeiros dias dos computadores domésticos, havia uma empresa chamada Apple. Ela acabara de fazer grandes progressos com a linha de computadores Apple II, mas precisava de inovação para se manter no topo do mercado de computadores em ritmo acelerado. A empresa já trabalhou na linha Lisa, inspirada em mini computadores e destinada a usuários corporativos, o que significa que tinha um preço adequado, mas para o consumidor médio parecia muito caro. Como um projeto adicional, o Macintosh foi desenvolvido, que deveria ser a realização da idéia de uma nova geração de computadores para “pessoas de rua” e custou cerca de US $ 500. O projeto foi assumido por Steve Jobs e, sob sua liderança, o hardware ficou mais avançado, o software recebeu uma GUI em vez de uma interface de texto e o preço subiu para quase US $ 2.500. Embora o equipamento obtido por esse preço tenha sido um pouco decepcionante, por exemplo, não possuía aceleradores gráficos e recursos de som de outras máquinas, mas o software justificava o preço. O primeiro Macintosh foi o Mac 128K, e seu sucesso levou à criação de modelos mais avançados deste Mac compacto, em particular o Macintosh 512K, Macintosh Plus e Macintosh SE Series.

Embora o desenvolvimento do Macintosh tenha ocorrido por volta de 1984, muito antes de eu começar a entender os computadores, eu tinha alguns pontos fracos no Macintosh compacto: o primeiro computador que meus pais compraram foi o Macintosh Plus. Mais tarde, foi complementado com um disco rígido SCSI de 20 MB e nesta máquina eu escrevi meus primeiros programas básicos. Quando eu ainda morava na Holanda, comprei uma máquina SE / 30 quebrada e a transformei em um servidor Linux, capaz de executar o software Mac. No entanto, deixei este carro na Holanda e aqui, em Xangai, não tenho mais o hardware clássico da Apple.

Embora seja óbvio que não preciso mais do Mac Plus na vida cotidiana, gostei da ideia de tê-lo em mãos em caso de ataques de nostalgia. Talvez eu possa ter uma pequena parte da experiência do Macintosh se eu mesmo criar uma cópia pequena dessa máquina. Se já tenho alguma experiência na criação de versões menores de hardware antigo , por que não tentar aplicar esse processo para criar o venerável Mac Plus?

Exibição


O que devo usar para construir uma máquina dessas? No começo, tive a idéia de pegar um Raspberry Pi ou algo semelhante, adicionar uma tela LCD de 2,5 polegadas, um emulador como PCE ou MiniVMac, imprimir um estojo em uma impressora 3D e considerar o trabalho realizado. Mas não acho que essa ideia valha a pena: a máquina para o meu gosto não será apenas muito grande, mas o projeto em si é muito simples. No Mac 128K original, mesmo quando o resultado final era muito baixo, os desenvolvedores conseguiram fazer alguns truques para economizar dinheiro. A montagem simples de uma réplica do “ferro” padrão é contrária ao espírito do design original. Então eu fui ao Taobao para mais ingredientes exóticos!


Eu decidi começar com a exibição. Por sua vez, o Mac tinha uma tela de alta resolução, por isso era muito importante escolher a tela certa. Geralmente, quando se trata de escolher monitores no mercado chinês de eletrônicos, o sortimento é grande. Infelizmente, o "grande sortimento" consiste em telas de alta resolução, mas também em tamanhos grandes ou em telas pequenas de pequena resolução. Idealmente, eu precisava de uma resolução de 512x342 pixels; esta é a resolução nativa do Mac e em uma tela semelhante eu posso exibir tudo sem aplicar zoom. Infelizmente, não existem telas prontas dessa resolução no mercado; o analógico mais próximo será algo como 640x480. Por alguma razão, as telas desta resolução são bem grandes: a menor tem uma diagonal de 3,5 polegadas. Portanto, se eu quiser tornar o Mac o menor possível, tenho que reduzir a resolução.

Tendo decidido que é possível reduzir um pouco a resolução, obtive uma variedade de um conjunto bastante grande de telas. Uma das primeiras telas vistas foi o x163qln01 - uma tela OLED de 1,63 polegadas fabricada pela AUO. É um pouco caro (cerca de US $ 25 por tela), mas geralmente pode ser encontrado no Taobao, e a folha de dados pelo menos documenta os contatos, tamanhos e requisitos de energia. Parece que essa tela foi projetada para algum tipo de marca de relógio inteligente no Android e, para um pouco de google, até encontrei algumas sequências de iniciação que podem ser usadas.

O único problema (exceto o conector, cujos contatos estão localizados a uma distância de 0,5 mm um do outro) era que o monitor não usa uma interface paralela e não SPI, mas a interface MIPI. Vou ter que lidar com isso mais tarde.

Depois de selecionar a tela, você pode ir para o processador. Eu escolhi o módulo ESP32-Wrover. Este módulo contém um ESP32 (um chip WiFi com duas CPUs de 32 bits operando a uma frequência de 240 MHz e com aproximadamente meio megabyte de RAM), 4 memória flash MiB e 4 PSRAM MiB. Sugeri que os dois núcleos da CPU fossem rápidos o suficiente para emular um Mac e que eu poderia usar 4 MiB de PSRAM como RAM do Mac. Embora 4 MiB de memória flash não sejam muito, eles devem ser suficientes para o emulador, além de um pequeno disco rígido com software e programas do sistema. Também me beneficio do fato de trabalhar na Espressif, portanto esse equipamento é bastante familiar para mim; Além disso, posso pegar alguns módulos do trabalho, em vez de comprá-los e aguardar a entrega.

Portanto, tudo está quase pronto para funcionar - a tela OLED ainda precisava de componentes para a fonte de alimentação; portanto, o número de componentes aumentou por um estabilizador de queda de baixa tensão (LDO) e outros chips de fonte de alimentação. Para o Mac, o som também era necessário, então peguei um chip e um alto-falante baratos e adquiri o módulo FT232 padrão para alimentação e depuração. Todos esses componentes são muito pequenos e permitem reduzir o corpo do dispositivo; o resultado deve ser um modelo ligeiramente superior a 1/6 de um Mac real.

Controle de exibição


Embora eu não possa reclamar da resolução, tamanho e brilho da tela, acabou sendo mais difícil exibir pixels nela. O MIPI não era suportado pelo silício ESP32, então eu precisava encontrar outra maneira de me comunicar com ele. MIPI DSI é um padrão desenvolvido pela MIPI Alliance e não está aberto; como esse é um hobby para mim, tive que coletar informações de documentos vazados e testar dispositivos existentes. Felizmente, um ano ou dois atrás, Mike Harrison fez engenharia reversa da interface MIPI DSI usada para controlar os monitores do iPod ( 1 , 2 , 3 , 4 , 5 , site ) e também encontrou várias cópias das especificações. Isso tornou minha vida muito mais fácil: pelo menos, me ajuda a descobrir o que enviar para a tela.

Embora exista muito mais na interface (e para descobrir isso, você deve assistir a todos os vídeos aos quais forneci os links acima), a camada MIPI física é bastante fácil de explicar. O MIPI usa quatro fios: dois barramentos de dados e dois barramentos de relógio. Ele também possui dois modos de transmissão de sinal: modo Low Power (LP) e modo High Speed ​​(HS).


No modo de baixa potência, os fios são usados ​​separadamente para transmitir estruturas de dados de controle, bem como para indicar que certos comandos têm um efeito direto no receptor físico, por outro lado. A queda de tensão neste modo é bastante grande em comparação com o modo de alta velocidade: para um sinal alto, a tensão é de aproximadamente 1,2 V e, para um sinal baixo, é de aproximadamente 0 V. Como o modo de baixa energia possui mais estados de sinal, ele executa funções como enviar o destinatário do pedido para alternar para o modo de alta velocidade ou sair dele. No gráfico acima, as linhas azuis indicam a transmissão de dados no modo de baixa energia.

No modo de alta velocidade, dois barramentos de relógio (CLKP / CLKN), bem como dois barramentos de dados (DP / DN) funcionam como barramentos diferenciais: um bar é sempre oposto ao outro. O receptor detecta as diferenças entre os dois barramentos e, com base neles, define o valor transmitido: 1 se estiver acima de DP e 0 se estiver acima de DN. Como o nome indica, o modo de alta velocidade fornece transferência de dados muito rápida com uma frequência de clock de até 1,5 GHz. Para conseguir isso sem muita interferência eletromagnética e consumo de energia, o padrão usa o seguinte truque: usa tensões muito baixas: a tensão nos pares é, em média, 200 mV, com desvios de ± 100 mV por barramento para indicar zeros e uns. No gráfico acima, os bits vermelhos são transmitidos no modo de alta velocidade.

Do ponto de vista da transferência dos próprios dados no modo de alta velocidade, a interface pode ser essencialmente considerada uma interface SPI bastante estranha e diferencial: há um sinal de relógio e um canal de transmissão de dados, e a cada ciclo de relógio o valor dos dados é transmitido para a interface. A diferença do SPI (exceto que os sinais são diferenciais) é que o bit de dados é transmitido apenas quando o estado dos barramentos CLK muda, e não apenas, por exemplo, na borda de ataque. Outra diferença é que o início de uma transmissão não é reconhecido quando o sinal no barramento / CS fica baixo, mas sim um sinal em banda: cada transmissão de dados começa com uma única "palavra mágica" e o receptor determina esse valor para entender quando a transmissão começa.

Para garantir que essa interface interaja com o ESP32, terei que executar uma mudança de nível. Queria alimentar o ESP32 a partir de uma fonte de 3,0 V para que todos os GPIOs também tivessem 3,0 ou 0 V. Para adaptar isso aos níveis de sinal da interface MIPI, escolhi a solução mais barata: usei apenas redes divisórias de resistores.

Para calcular os valores do resistor, criei equações para os três estados de tensão de saída que me interessam (1,1 V para o sinal de alta potência baixa, 0,07 V para o sinal de baixa velocidade alta, 0,33 V para o sinal de alta velocidade; as tensões foram escolhidas como para que na maioria dos casos permaneçam dentro da especificação) e dos três estados de entrada que devem gerá-los. Eu tenho as equações. Teoricamente, era possível resolvê-los manualmente, mas no final os abandonei no WolframAlpha e obtive os valores exigidos do resistor.

            3v
 G -R1 - + R3
 G-R2 - + - + ---->
            R4
            GND

 R4 * (1,9 / R1 + 1,9 / R3) = 1,1, 
 (1 / (1 / R4 + 1 / R1 + 1 / R2)) * (2,93 / R3) = 0,07, 
 (1 / (1 / R4 + 1 / R1)) * 2,67 * (1 / R3 + 1 / R2) = 0,33, 
 R2 = 1000

 R1 = 280, R2 = 1K, R3 = 3K6, R4 = 150 


Nesse ponto, percebi que você também pode trapacear um pouco: como no modo de alta velocidade, os barramentos são diferenciais, a tela analisará apenas a diferença entre os dois barramentos para determinar os dados transmitidos. Isso significa que eu posso salvar o GPIO mantendo uma tensão fixa em um dos barramentos e aplicando um sinal alto ou baixo no outro. Para fazer isso, eu precisava de um segundo tipo de rede de resistores:

            3v
            R3
 G-R1 - + - + ---->
            R4
            GND

 R4 * (1,9 / R1 + 1,9 / R3) = 1,1,
 (1 / (1 / R4 + 1 / R1)) * (2,8 / R3) = 0,2,
 R4 = 150

 R1 = 320, R3 = 1500, R4 = 150 


Outra tarefa foi criar um circuito de relógio. O SPI normal transmite um pouco na borda principal do barramento de clock. (Ou na extremidade traseira, dependendo da configuração.) O MIPI transmite um pouco nas extremidades dianteira e traseira do sinal do relógio. Embora o módulo SPI do equipamento ESP32 não possa gerar esses sinais por si só, podemos converter um para outro usando um simples D-trigger, cuja saída invertida está conectada à entrada. Cada pulso de clock na entrada alterará o nível de saída, conforme exigido.

Diagrama de circuito


Tendo lidado com o equipamento de exibição, terminamos a parte mais difícil. Agora tudo o que precisamos fazer é adicionar o resto. Vamos começar com a fonte de energia. É bem simples: eu alimento todo o circuito de 5V de um conversor USB para serial, que também pode ser usado como uma interface de depuração / programação. Essa tensão é utilizada para gerar os +4,6 V, -3,4 V e 1,8 V exigidos pela tela OLED, bem como 3,0 V para alimentar o ESP32. Tensões de +4,6 V e -3,4 V são geradas pelo chip TPS65631, e o circuito de referência é mostrado na folha de dados da tela OLED. Outras tensões são geradas por um par de LDOs simples.


O Macintosh também tinha som. Pelos padrões modernos, sua qualidade não é muito alta (22 kHz, 8 bits), mas os sons de seus programas agora são lendários, portanto, no meu projeto, não pude recusá-los. O ESP32 possui um DAC interno de 8 bits, usado para criar ondas sonoras analógicas geradas pelo emulador. Em seguida, eles são alimentados ao NS8002, que é um amplificador de som da classe AB de 2 watts montado no pequeno formato SOIC8. É barato, requer muito poucos componentes de suporte e cria som mais do que suficiente para chamar a atenção para o pequeno Mac.


Um dos aspectos que tornou o Macintosh tão revolucionário foi o fato de ser um dos primeiros computadores comerciais com um mouse. A equipe do Macintosh pensou no mouse com tanto cuidado que quase todo o sistema operacional se baseia em elementos de interface do usuário controlados pelo mouse e, ao contrário, por exemplo, do IBM PC, o Macintosh inteiro pode ser controlado por um mouse. Obviamente, meu pequeno Mac também precisava desse importante periférico. Ainda me lembro dos mouses de esferas vendidos com o primeiro Macintosh, mas não fiquei muito satisfeito com a necessidade de limpar os roletes com muita frequência; é por esse motivo que esses dispositivos mecânicos foram completamente substituídos por mouses ópticos. A vantagem disso é que os detalhes desses novos mouses ópticos são bastante simples de encontrar: por exemplo, não demorou muito para encontrar o vendedor dos sensores de mouse para jogos ADNS9500 e da óptica correspondente.

Outro aspecto conveniente é que o sensor óptico do mouse é um dispositivo profundamente integrado: requer apenas alguns componentes externos para funcionar, e isso é refletido no diagrama. Foram adicionados vários capacitores para estabilizar a tensão, um transistor MOS (copiado diretamente da folha de dados) para ligar o diodo laser e outros detalhes padrão. O sensor do mouse transmite dados através de um sinal SPI de quatro fios, e eu usei um desses fios para enviar o sinal do botão do mouse: quando clico no botão, o contato MISO é pressionado um pouco. O valor desse resistor pull-up não é suficiente para o mouse parar de transmitir dados, mas o suficiente para superar o resistor pull-up, que normalmente puxa o barramento para cima; portanto, quando o sensor cria três estados no barramento MISO, o ESP32 pode reconhecer o pressionamento de um botão.


Finalmente, você precisa conectar a tela OLED. Já concluímos todo o trabalho difícil de calcular os valores de todos os resistores, portanto o circuito deve falar mais ou menos por si mesmo. O chip adicionado é um D-flip-flop e é usado para reduzir pela metade a velocidade do relógio: como mencionado acima, o padrão MIPI exige um novo bit cada vez que a polaridade do sinal do relógio é invertida, enquanto o ESP32 transmite um novo bit apenas na parte frontal ou traseira frente.


Depois de desenhar um diagrama esquemático, passei a criar um design de placa de circuito. A tela que eu selecionei deveria ser montada na placa que a controla e o conector deve estar na parte traseira dessa placa de circuito. Embora isso não tenha deixado muito espaço para outros componentes, eu ainda queria colocar todos os outros componentes do outro lado.


É ótimo ter uma boa visão e uma pistola de ar quente: isso me permitiu usar os componentes 0603 e colocar tudo no espaço limitado da placa. Seria especialmente difícil conectar o conector da tela e o chip de potência OLED QFN a um ferro de solda convencional.


Percebi que o sensor do mouse e seus componentes ocupariam muito espaço na placa, então decidi soldar todos os componentes no próprio sensor. Graças a isso, tudo pode ser colocado no mouse.



De software



Obviamente, o software é um elemento importante o suficiente desta montagem: você deve emular todo o Macintosh. No entanto, o Macintosh não é uma máquina tão complicada. De fato, ele consiste em um microcontrolador 68000, um controlador de transferência serial Zilog Z8530 que controla a porta serial, 6522 VIA para E / S interna e para fornecer uma interface com um teclado, além de várias matrizes lógicas programáveis ​​(PAL) contendo lógica para a exibição e o som. Ele também possui um chip Integrated Woz Machine que fornece uma interface com uma unidade de disquete. Este é um chip bastante complicado; no entanto, não pretendo emular um disquete; portanto, será suficiente emular o IWM, retornando constantemente que não há disco na unidade. Em vez disso, pretendo emular totalmente o chip SCR NCR 5380, conectado a um disco rígido SCSI emulado, que será lido na memória flash ESP32-Wrover incorporada.

Além disso, o sistema terá muito poucos programas com acesso direto ao equipamento: os programadores de Mac foram instruídos desde o início a usar as camadas de abstração de hardware no nível do SO para manter a compatibilidade com versões futuras do equipamento Mac. Em geral, isso significa que, se eu conseguir emular o hardware a tal ponto que o sistema operacional inicialize e esteja satisfeito com tudo, a maioria dos programas funcionará sem problemas.

Então eu decidi que você poderia tentar escrever um emulador do zero. Mais precisamente, não completamente do zero; 68000 é um animal bastante complicado e eu não queria reinventar a roda. Em vez disso, pesquisei na Internet e descobri que o MAME tem um emulador de 68K baseado em CK, conveniente e rápido, chamado Musashi, que atende perfeitamente às minhas necessidades.Será necessário conjurar um pouco para transferir códigos de operação em vez da RAM para a memória flash, mas, para o resto, quase nada é necessário para a porta para o ESP32.

No entanto, não planejei desenvolver o projeto inteiro no ESP32: como o chip suporta o OpenOCD, que oferece recursos de depuração bastante amplos, o ciclo "baixado-testado-carregado-fixo" será monótono demais. Portanto, decidi primeiro desenvolver tudo na minha máquina Linux, sem esquecer as limitações do ESP32. Então comecei a usar a folha de dados para diferentes chips, informações sobre o Linux-68K para a máquina, bem como informações da série Inside Macintosh que podem ser encontradas na Internet. Quando não consegui descobrir o que fazer a seguir, pude olhar sob o capô de outros emuladores de código aberto.

Armado com tudo isso, escolhendo o gcc como compilador e o libsdl como a biblioteca para trabalhar com gráficos, comecei a trabalhar. Em resumo, depois de um tempo, receberei um emulador MacPlus simples, mas geralmente funcional: mouse, vídeo, disco rígido SCSI e som:


Como meu hardware ainda não estava pronto, decidi portar meu emulador para o ESP-Wrover-Kit do devboard. Eu ainda tinha várias dessas placas em mãos e, além do módulo Wrover, que eu usarei de qualquer maneira, elas têm uma tela conveniente de 320x240 que pode ser usada para testar o vídeo.


Após a instalação, o emulador de Mac ganhou bastante dinheiro nessa placa; de fato, geralmente é bem próximo dos 7,8 MHz em que o Mac Plus roda. (7,8 MHz será um pouco mais rápido que o Mac Plus; já que nesta parte da memória os ciclos de buffer de quadro e sistema de som são consumidos, a frequência pode ser reduzida em 35%.)

Obviamente, o emulador no devboard é um bom passo à frente, mas no final, tudo deve funcionar na tela que eu comprei, e não na tela do devboard. E mais uma coisa: a tela do devkit tem uma tela de 320x240 e corta a parte sólida da tela do Mac. A tela que usarei tem um tamanho de 320x320 e, portanto, é maior apenas na vertical: como posso exibir uma tela de 512x342 Mac?

Há apenas uma maneira de colocar 512x342 pixels em uma tela de 320x320, e isso é dimensionado. De fato, tiramos uma imagem, compactamos, diminuímos e depois exibimos. No entanto, o dimensionamento pode ser feito de várias maneiras diferentes, e considerando que a imagem em preto e branco gerada pelo sistema operacional pressupõe que cada pixel crie um ponto de luz claramente definido na tela, ou seja, existem várias maneiras de estragar tudo. Preciso perder o mínimo de pixels possível. Ou seja, você precisa aumentar a resolução da tela OLED.

Mas como fazer isso?É improvável que você possa abrir a tela OLED e colocar mais alguns pixels dentro. No entanto, isso não é necessário; a resolução da tela OLED já é três vezes a declarada. O motivo é que essa tela é colorida: cada "pixel" virtual possui subpixels vermelhos, verdes e azuis. Além disso, nessa tela específica, os subpixels são alinhados com um triângulo. Aqui está uma captura de tela em close com três pixels ativados:


Como você pode ver, pixels são conjuntos triangulares de três subpixels; dependendo da coluna, os triângulos apontam para baixo ou para cima. Basicamente, isso significa que a resolução de sub-pixels da tela é de 480 x 640 pixels. Embora isso ainda não seja suficiente para exibir 512x342 pixels, a diferença é tão pequena que, se você selecionar a pequena escala correta, a exibição será o mais legível possível para uma tela de 1,63 polegadas exibindo uma GUI projetada para uma tela de 9 polegadas:



Habitação


Então agora eu tenho uma tela e um software que emulam muito bem o Macintosh Plus, além de um microcontrolador no qual ele pode ser executado. O que está faltando? Obviamente, caixas para tudo isso!

Decidi imprimi-lo em uma impressora Formlabs 1+ SLA 3D do meu trabalho. Para fazer isso, primeiro preciso de um modelo. Eu queria criá-lo do zero. Obviamente, é melhor ter um Macintosh Plus real à mão. Na verdade, eu tenho, mas estamos separados por meio continente ... Felizmente, consegui encontrar uma solução quase igualmente boa: uma pessoa gentil indicou as dimensões do Mac 128K original (cujo corpo é quase o mesmo do Plus) no wiki do iFixit .

Ainda crio todos os meus modelos 3D no OpenScad e, depois de atormentar e xingar, consegui fazer com que todas as curvas parecessem como deveriam. Eu tenho um lindo modelo de Mac em uma escala de 1: 6.


Também criei o mouse a partir de imagens com o iFixit, mas como um sensor suficientemente grande do mouse óptico deve caber nele, ele não pode ser dimensionado para 1/6 de um mouse real. A escala está mais próxima de 2/5, portanto, o mouse parece grande em comparação com o pequeno Mac, mas é muito mais conveniente para dedos humanos sem escala.


Então, tudo o que resta é imprimir os modelos. Exportei a construção para vários arquivos STL e os imprimi no Formlabs 1+. O resultado final foi bom o suficiente; Só lamento não ter adicionado travas nas duas partes do design. Este problema foi resolvido com uma gota de supercola.

imagem


Resultado


Então, eu tinha todos os componentes e só conseguia montá-los. Os conectores PCB na parte frontal do chassi são protegidos com vários clipes. O conversor de USB para serial, usado como mecanismo de inicialização e fonte de alimentação, é conectado à parte traseira e também suporta vários terminais. Esqueci de fazer algo para montar dentro do alto-falante, mas consegui consertá-lo no gabinete com super cola. O mouse está conectado por um conjunto de fios finos. (Sim, eles são um tanto inconsistentes com o esquema de cores ... assim que encontrar um cabo multicore mais bonito, eu o corrigirei.)


Ao desenvolver tudo em um computador, você percebe a verdadeira escala do produto apenas quando o projeto é totalmente materializado, por exemplo, quando você recebe placas de circuito impresso da fábrica ou após imprimir na impressora 3D do design do gabinete em que trabalha há semanas. Claro, eu sabia que tudo seria seis vezes menor que o Mac original, mas somente depois de juntar tudo e ver o carro ao vivo, percebi o que isso significa. O Mac realmente era pequeno!



E, apesar do tamanho pequeno e da falta de um teclado, ele é capaz de executar a maioria dos programas pelos quais o Mac é famoso. Aqui está uma demonstração. Nos primeiros 20 segundos, uma verificação de memória é executada e, por experiência, sei que uma verificação tão longa não é um bug: o Mac Plus original demorou o mesmo tempo para carregar, mesmo após a atualização.


Então, o que há de bom neste Mac Plus? Embora tenha gostado de criá-lo, devo admitir que, sem um teclado, ele não pode ser usado com todo o seu potencial. Além disso, ele não possui os meios de comunicação: planejei implementar o AppleTalk via WiFi, mas não obtive sucesso devido às peculiaridades que não consegui emular no próprio chip controlador de barramento serial do Mac original. No entanto, tendo o projeto concluído nesse estado, finalmente posso realizar meu sonho antigo e colocá-lo na mesa do Mac com as torradeiras do protetor de tela After Dark voando pela tela:


, open-source, , Github . Beer-Ware license, , . - - , .

Source: https://habr.com/ru/post/pt444574/


All Articles