Como encontrar um bug em um microprocessador lançado há 35 anos

K1801VM1A


É difícil de acreditar, mas às vezes os erros nos processadores vivem essencialmente mais do que os próprios processadores. Recentemente, fiquei convencido disso pelo exemplo do microprocessador de 16 bits 1801BM1A , com base no qual a família de computadores domésticos BK-0010 / 11M foi criada na URSS ao mesmo tempo. Sobre esta família em Habré escreveu repetidamente.


O período da vida ativa de BeKashek cai no final dos anos 80 - início dos anos 90 do século passado. Nestes anos, através dos esforços de vários entusiastas individuais, bem como de grupos de membros e cooperadores do círculo, foi desenvolvida a principal matriz de programas aplicativos BC: jogos, utilitários, vários "DOSs" (sistemas operacionais de disco). Paralelamente ao desenvolvimento do software, foram criados periféricos sob os quais o software do sistema foi gravado. Em geral, o ecossistema desses computadores de 16 bits do tipo PDP foi desenvolvido de acordo com princípios semelhantes, como, por exemplo, as arquiteturas abertas de 8 bits anteriores baseadas nos barramentos Intel 8080 e S-100 desenvolvidos. Mais tarde, à medida que nos afastamos do papel utilitário do CD, o foco na programação mudou para o demosceno.


O volume de software para BC pode ser estimado visitando sites públicos com coleções de programas . Obviamente, em comparação, por exemplo, com o ZX-Spectrum, esse volume é muito mais modesto. No entanto, mesmo esse volume, ao que parece, deveria ter sido suficiente para contornar todos os cantos e recantos concebíveis do código de máquina. É possível encontrar algo incomum no comportamento do processador, após mais de trinta anos de prática em usá-lo? Como se viu - sim! Isso será discutido abaixo.


Talvez faça sentido contar essa história em ordem cronológica. Antes de tudo, devo observar imediatamente que não sou um “programador com experiência”, nem por ocupação, nem por pertencer a um grupo de entusiastas do BC, sobre quem escrevi acima. Eu vim para o BC de uma maneira indireta, em parte pela nostalgia dos hobbies da infância e da juventude (eletrônica analógica e digital, a revista Young Technician , UT-88 e outros artesanatos e imperfeições), e em parte pelo meu interesse no sistema de arquitetura e comando PDP-11 . Não tenho o BK "no hardware" e normalmente executo programas para o BK e depuro-o no emulador do bkemu em um tablet para Android.


Há algum tempo, fiquei interessado no programa Kaleidoscope, de autoria de Li-Chen Wang-a . O programa foi escrito em código de máquina em 1976, para um microprocessador Intel 8080 como parte de um computador Altair 8800 com um adaptador gráfico Cromemco Dazzler . Eu queria analisar o algoritmo Li-Chen Wang-a em detalhes e, ao mesmo tempo, portá-lo para o BC. Deve-se dizer que o desejo de portar o caleidoscópio para BC foi expresso entre os demosceners anteriormente, e houve até tentativas de analisar o algoritmo, mas não tiveram êxito.


No próximo artigo, provavelmente analisarei esse algoritmo em detalhes (e para os impacientes, postarei um link para as fontes do caleidoscópio de plataforma cruzada sob libSDL em C). Para o futuro, será suficiente indicar que o problema foi resolvido e o Caleidoscópio foi transportado com sucesso para o BC. Além disso, a geração de som foi adicionada ao algoritmo no CD e, como a imagem e o som são gerados pelo mesmo código, podemos dizer que a imagem em si soa (toda a demonstração se encaixa em menos de 256 bytes de código de máquina e, Espero que seja apresentado ao público na CAFe Demoparty 2019 em Kazan no final de outubro).


Depois de terminar de escrever e depurar meu programa no emulador, virei-me para Damir ("Adamych") Nasyrov (ele é um dos organizadores do CAFe Demoparty e uma pessoa muito conhecida entre os demosceners) com uma solicitação para verificar a execução do programa em um BC real. Eu estava especialmente interessado em reprodução de som, pois os tempos no emulador podiam diferir dos tempos no hardware real. Imagine minha decepção quando Damir me informou que existe uma imagem em um BC real, mas não há som!


As noites seguintes foram gastas tentando subtrair a documentação do sistema no BK-0011M e o diagrama de circuitos , onde poderia haver um erro no som. O som no BC é organizado de maneira simples: o sexto bit no registro de E / S com o endereço octal 177716 (registro de controle do gravador) é emitido através de um buffer para um alto-falante piezoelétrico (bip). Além da 6ª categoria, os bits 2 e 5 do mesmo registro são conectados ao conversor digital-analógico mais simples com 4 resistores. A partir da saída deste conversor, o som pode ir para o gravador. Tudo é excepcionalmente claro e lógico, mas não havia som teimoso em um BK real, independentemente das combinações de máscaras de bits que tentei aplicar à saída de dados desse registro. Paralelamente, todos os emuladores BK que eu conhecia foram instalados e testados - e o som funcionou em todos!


Em algum momento, eu quase consegui convencer Damir de que seu BK estava com defeito, mas o comportamento foi repetido em outro BK-0011M ao vivo e também no BK-0010. Fiquei sem idéias e os habitantes do canal de telegrama sobre o tema BC também não sabiam dizer nada ... No entanto, o incidente ajudou, como sempre. No decorrer de um dos experimentos, Damir lançou uma demo no emulador para garantir que haja som no emulador. E aqui ele conseguiu notar que não apenas há som no emulador, mas não no BC, mas também as imagens no emulador e no BC ao vivo são diferentes! Aqui, devo lembrar que no meu programa a imagem e o som são gerados pelo mesmo código. Assim, durante todo esse tempo, procurei um motivo no lugar errado: o motivo estava no código que gerava os dados para o conteúdo da tela.


Damir me enviou uma captura de tela, e ficou claro que o algoritmo produz bytes com zero conteúdo dos 4 bits mais altos e, por coincidência, esses bits foram emitidos para o som (ou seja, sempre zeros). No entanto, a razão pela qual o algoritmo se comportou dessa maneira permaneceu vaga. Este é o local no código ( macro do assembler11 do PDP-11, registra r0-r5 renomeado!):


; renamed registers a = %0 b = %1 c = %2 d = %3 e = %4 h = %5 ... ... asr b ; sets CF bic #177760, b bis b, c bis (h)+, c ; screen address in c movb (c), a ; get a byte from screen RAM bcc 1$ ; check CF bic #177760, a ; keep bits 0-3, clear rest bisb d, a ; fill bits 4-7 br 2$ 1$: bic #177417, a ; keep bits 4-7, clear rest bisb e, a ; fill bits 0-3 2$: ... ... 

Por alguma razão, em um BC real, sempre foi executado um salto condicional na marca de US $ 1. Ou seja, a instrução bcc sempre percebeu o sinalizador de transporte como redefinido, embora a instrução de mudança do ASR possa configurá-lo como 0 ou 1. Como pode ser isso porque, de acordo com a documentação do processador, nem o BIC, nem o BIS, nem o MOVB deve afetar a bandeira de transporte ?!


Além disso, em todos os emuladores (que foram escritos de acordo com a documentação do processador!) É assim: estas instruções não tocam no sinalizador C. Tornou-se claro que o processador real 1801BM1A não funciona nesse caso, de acordo com a documentação. Resta confirmar isso.


Para iniciantes, uma solução rápida óbvia:


  ... asr b ; sets CF mfps -(sp) ; store PSW on stack bic #177760, b bis b, c bis (h)+, c ; screen address in c movb (c), a ; get a byte from screen RAM mtps (sp)+ ; restore PSW from stack bcc 1$ ; check CF ... 

Salvar as bandeiras na pilha imediatamente após a instrução shift e restaurá-las antes do salto condicional resolveu imediatamente o problema, o que mostrou que eu estava no caminho certo. Resta restringir o "círculo de suspeitos". Para testar a hipótese, um teste sintético foi escrito primeiro (os registros não foram renomeados aqui; a inicialização foi omitida para não confundir o código; o emt 64 é um programa interrompido para imprimir uma linha):


  ... mov #1, r1 jsr pc, test clr r1 jsr pc, test halt test: mov #40000, r2 ; r2 points to screen RAM mov #dummy, r5 ; r5 points to dummy = 200 ; *** begin *** asr r1 ; affects CF bic #177760, r1 bis r1, r2 bis (r5)+, r2 movb (r2), r0 ; *** end *** jsr pc, prt rts pc prt: mov #msg1, r0 bcs l1 mov #msg2, r0 l1: emt 64 rts pc msg1: .asciz /Flag CF set/ msg2: .asciz /Flag CF clear/ dummy: .word 200 ... 

E o teste ... não funcionou! Programa impresso na tela


Marca CF set
Marca CF limpar


O que aconteceu? Descobriu-se que a suposição inicial de que o fragmento de código entre o início e o final estraga o sinalizador C está errada e precisa ser esclarecida. Qual é a diferença entre este teste e o código fonte? E o fato de outras instruções aparecerem entre o bloco de comandos "suspeitos" e o salto condicional. Não afeta o sinalizador C, mas altera o estado interno do processador. Portanto, o seguinte teste foi assim:


  ... mov #1, r1 jsr pc, test clr r1 jsr pc, test halt test: mov #40000, r2 mov #dummy, r5 ; *** begin *** asr r1 ; affects CF bic #177760, r1 bis r1, r2 bis (r5)+, r2 movb (r2), r0 bcc l1 ; *** end *** mov #msg1, r0 emt 64 rts pc l1: mov #msg2, r0 emt 64 rts pc msg1: .asciz /Flag CF set/ msg2: .asciz /Flag CF clear/ dummy: .word 200 ... 

E agora esse teste já foi impresso em um BK-0011M real:


Marca CF limpar
Marca CF limpar


No emulador, como antes,


Marca CF set
Marca CF limpar


Além disso, é uma questão de tecnologia. Por meio de simplificações graduais, foi obtido um teste mínimo no qual um bug é reproduzido (cito toda a fonte):


  .title test .psect code .=.+1000 mov #15, r0 emt 63 sec jsr pc, test clc jsr pc, test halt test: movb r0, r0 bcc l1 mov #msg1, r0 emt 64 rts pc l1: mov #msg2, r0 emt 64 rts pc msg1: .asciz /Flag CF set/ msg2: .asciz /Flag CF clear/ .end 

Em um BK-0011M real, este teste exibe


Marca CF limpar
Marca CF limpar


Ou seja, a instrução MOVB que estava diretamente à frente da instrução de ramificação condicional era a culpada, e a aparência do primeiro operando não é importante. Se, por exemplo, NOP for inserido entre MOVB e BCC, o comportamento retornará ao documentado e o programa imprimirá


Marca CF set
Marca CF limpar


Isso tornou possível formular uma hipótese refinada (cito-me em um canal de telegrama):


... Em relação ao bug: o comportamento parece ter esclarecido. Como eu imagino, MOVB src, dst (a propósito, parece que os operandos não são importantes), devido a alguns recursos arquiteturais, estraga temporariamente a bandeira C dentro do processador, mas não fatalmente, porque a porcentagem parece salvar uma cópia dessa bandeira. Como resultado, se entre o MOVB e a ramificação condicional houver outros comandos (que não afetam C), por exemplo, NOP, o comportamento será o descrito na documentação.

O que aconteceu depois? Além disso, colegas do canal ajudaram a trazer Vyacheslav (@ K1801BM1, o homem lendário que anteriormente reverteu esse processador no nível do transistor) para a discussão. A reação de Vyacheslav (Yuot) quando ele testou o comportamento em uma posição com um 1801BM1A real (ortografia e pontuação preservadas):


Stanislav Maslovski:
são necessários pelo menos dois comandos para reprodução
movb e salto condicional em C
Bem, antes disso, defina o sinalizador C para um estado conhecido

Yuot:
A bandeira sempre com reset é obtida

Stanislav Maslovski:
sim
agora insira nop

Yuot:
Agora nunca

Yuot:
Alternando 0 1
Isso é uma vergonha

Com a ajuda de Vyacheslav, os detalhes foram descobertos, a saber, que o motivo do bug é que no processador, além do PSW, há outro registro de 4 bits, que normalmente armazena uma cópia dos sinalizadores do PSW. Este registro está conectado ao firmware automático e as transições condicionais recebem os valores do sinalizador. Ao executar as instruções MVB, SWAB, MFPS com o registrador receptor, devido às peculiaridades do processamento da extensão do sinal e devido a um erro no microcódigo, uma cópia do sinalizador C nesse registro é descartada e as transições condicionais usando esse sinalizador não funcionam corretamente. No entanto, seguindo as instruções abaixo, o valor temporário do registro é restaurado a partir do PSW. Por isso, a inserção de NOP restaura o comportamento correto.


Para concluir, também gostaria de agradecer aos assinantes do canal de telegrama BK0010 / 11M World por participarem da discussão sobre esse bug e pelos comentários feitos no texto do artigo. A foto-título do artigo é cortesia de Manwe_SandS . Mais interessante, Manwe estava perto de descobrir o mesmo bug, quase ao mesmo tempo em que Damir e eu estávamos lutando para resolver o problema de som!


Agora cabe aos pequenos (apenas brincando) - alinhar todos os emuladores com o comportamento real do processador. Afinal, o próprio processador, infelizmente, não pode mais ser corrigido.


Sobre isso eu vou terminar. Espero que tenha sido interessante.

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


All Articles