Analisando uma demo de 128 bytes do arquivo de 1997

É muito agradável satisfazer meus desejos, especialmente do passado distante, tão distante que eu já esqueci que já o desejei. Sei pouco sobre o demosceno e certamente nunca acompanhei os autores ou o trabalho deles, apenas gostei de ver o que aconteceu. Às vezes, eu queria descobrir, mas faltava conhecimento e experiência, perseverança posterior e perdi completamente o interesse por isso. Mas recentemente, meu amigo, com quem estudamos na época e que nos forneceu todos os novos produtos, incluindo demos, com BBS e Fidonet, porque quase todos tinham telefone e modem e computador ao mesmo tempo, visitou a CAFePARTY com seu trabalho que me fez abrir o arquivo do meu primeiro computador, selecione uma demonstração e descubra.

pentagra.com

Avaliando objetivamente meus pontos fortes, fiz uma introdução de 128 bytes, de que gostei visualmente. O arquivo pentagra.com é assinado por Mcm , 128 bytes, modificado pela última vez em 9/24/1996 18:10:14, hex dump:

000000: b0 13 cd 10 68 00 a0 07 06 1f ac ba c8 03 ee 42
000010: b1 40 ee 40 6e 6e e2 fa b8 3f 3f bb 40 01 bf 40
000020: 05 57 b1 78 ab 03 fb e2 fb 5f b1 60 88 01 aa 03
000030: fb 03 fb e2 f7 b1 61 88 01 aa 2b fb 2b fb e2 f7
000040: bf d1 99 57 b1 78 ab 2b fb e2 fb 5f b1 8f f3 ab
000050: 81 fe 00 fa 73 12 ac 0a c0 74 0d 48 88 44 fe 88
000060: 04 88 40 ff 88 84 bf fe 03 f2 42 75 e3 e4 60 3c
000070: 01 75 a5 b8 03 00 cd 10 c3 00 00 00 00 4d 63 6d

Do mesmo arquivo, retirei:

  • Hiew 6.11 ( 6.50 pode ser encontrado no site) - usei-o como desmontador
  • Pacote TASM - com o qual coletei o código recebido de volta para garantir que não estragasse nada
  • Ajuda TECH da Flambeaux Software! 6.0 - referência on-line moderadamente detalhada e abrangente para API do DOS, funções da BIOS, hardware e montador
  • Mayko G.V. Assembler para IBM PC - uma referência de formato quase do tamanho de um bolso para todos os comandos básicos do Intel 8086 e regras de formatação de texto do programa. Sem detalhes arquitetônicos e com exemplos elementares, apenas as coisas mais básicas. Há quase tudo o que você precisa aqui, mas você não pode escrever no assembler além do ambiente.
  • Portanto, o segundo livro Zubkov S.V. Montador. Para DOS, Windows e Unix - Um guia para brechas de hardware e DOS

Da implementação mínima extrema, deve-se esperar o uso de truques e abordagens fora do padrão, mas, além de algumas suposições nas condições iniciais, não vi nenhum truque técnico, mas vi um truque algorítmico. E aqui algumas palavras devem ser ditas sobre a experiência. Qual poderia ser a dificuldade? Na implementação ou no algoritmo. Por exemplo, no comando mov di, 099d1h , você pode ter medo de uma constante mágica. Mas se você estiver no contexto de uso, fica claro que esse é o endereço para acesso nas coordenadas da tela X e Y, onde X = 17, Y = 123, 320 é a resolução horizontal da tela em pixels. Juntos, isso nos dá 17 + 123 * 320, a conversão de coordenadas bidimensionais para unidimensionais.

Agora, olhando o que está acontecendo na tela, posso facilmente imaginar como implementar isso, embora não com 128 bytes, mesmo que não seja 100% semelhante, mas sim. E há 20 anos não consegui, embora tenha retirado todas as ferramentas que usei de prateleiras empoeiradas e não precisei navegar na Internet para entender como funciona. Portanto, antes de tudo, este é um contexto, uma compreensão do QUE está acontecendo e, portanto, a questão dos truques e COMO FAZER isso está em segundo lugar.

O que vemos:

  1. 5 linhas do pentagrama. Estas não são necessariamente linhas inextricáveis ​​diretas de acordo com todos os cânones. Vemos apenas a figura geral, sem detalhes
  2. O efeito chama, que consiste em duas partes importantes: uma paleta selecionada corretamente e um algoritmo para alterar constantemente a cor dos pontos na tela com elementos de incerteza, mas mantendo uma sequência contínua de paletas para os pontos vizinhos. Por exemplo, você pode calcular a tela atual inteira calculando a média dos valores dos pixels vizinhos da tela anterior e adicionar mais pontos "brilhantes" em locais aleatórios, ou não em locais aleatórios, mas em valor aleatório, ou não por acaso, apenas se afastando da ordem linear. Uma opção é como isso é feito no DOOM . O resultado deve ser na forma de cores fluindo umas nas outras, desde áreas brilhantes constantemente emergentes até desbotamento

Resta entender como isso foi feito. Uma descrição mais aprofundada não substituirá o conhecimento sobre a arquitetura do computador e as funções do DOS ou do assembler, mas ter esse conhecimento permitirá que você entenda e se concentre na essência do que está acontecendo. Tendo começado a escrever, percebi que tudo resulta em detalhes suficientes, mas não podia recusar para não perder no sentido da história.

DOS e carregamento de programas .COM


O programa no arquivo .com é um código limpo, sem cabeçalhos, você só precisa colocá-lo no lugar certo. É isso que o DOS faz, ou melhor, a chamada do sistema de 4Bh. Muitas ações estão ocorrendo, vamos nos concentrar no resultado:

  • Todos os segmentos registram CS, DS, ES, SS carregados com um único valor
  • 65536 bytes são reservados para todo o programa, exatamente um segmento ao qual todos os registros de segmento indicam. Os primeiros 256 bytes são ocupados pelo cabeçalho do sistema - PSP (prefixo de segmento de programa). No CS: 0, o primeiro campo do PSP, o comando INT 20h está localizado - para finalizar o programa atual e transferir o controle para o processo pai. O programa em si começa com o endereço CS: 100h e ocupa os seguintes 128 bytes
  • A palavra 0000h é colocada na pilha, o registro SP é FFFEh. Isso significa que os dois últimos bytes deste segmento no endereço SS: FFFEh são redefinidos. De fato, este é o endereço de retorno mais próximo do procedimento, o que nos levará ao comando de conclusão no CS: 0
  • Os registradores AL e AH contêm um sinalizador de erro para determinar as letras de unidade do primeiro e do segundo argumento quando o programa é chamado. Se não houver erros, eles serão 0, se houver FFh

Eu sinceramente acreditava que, no caso geral, o status dos registros não está definido. Mas no código analisado, na minha opinião, é feita uma suposição muito ousada sobre seu estado inicial, em particular sobre os registros CX, SI e o sinalizador de direção DF. Não encontrei confirmação disso na lista de fontes que resultou acima, por isso fui examinar as fontes do MS-DOS 2.0 :

  • Sobre o DF, podemos assumir que ele foi redefinido pelo comando cld , porque o último usa a direção de avanço antes de transferir o controle para as cld linha; portanto, o DF é redefinido. Embora não haja uso explícito de cld nesse local, o comando para limpar o sinalizador de direção é encontrado com bastante frequência antes de muitas outras transferências
  • O SI contém 100h, porque é usado para determinar o deslocamento que será carregado no registro pelo contador de comandos IP
  • CX é igual a FFh, porque é usado como um contador com um valor inicial de 80h para transferir o conteúdo de toda a linha de comando e, consequentemente, após a transferência é 0. E depois disso, CL, como uma variável temporária, carrega FFh e é usado para definir o sinalizador de erro da letra da unidade em AL e AH

Não há fontes de versões mais recentes, mas existem fontes do DOSBox :

 reg_ax=reg_bx=0;reg_cx=0xff; reg_dx=pspseg; reg_si=RealOff(csip); reg_di=RealOff(sssp); 

Ou seja, coincide com o que vi no código-fonte do MS-DOS (segunda versão!). Você pode ver os valores iniciais de outros registradores, aqui está uma inicialização explícita e especial. Para o MS-DOS, os valores de registradores que não sejam AX, segmento e pilha são rudimentos de seu uso para outros fins; esse não é um dogma ou padrão; portanto, eles não são mencionados em nenhum lugar. Mas, por outro lado, o ecossistema que se formou e toda a dor da Microsoft no suporte à compatibilidade com versões mais antigas, forçando a arrastar todos os valores gerados aleatoriamente para trás, está se tornando um pouco compreensível, porque os programadores estão acostumados a eles.

Finalmente, para nós esse conhecimento é suficiente, começamos a restaurar o programa a partir dos cabeçalhos:

 .186 .model tiny .code .startup 

Determinamos o tipo de processador 80186, porque usamos o comando outsb , que apareceu apenas neste modelo. Um segmento de código e um ponto de entrada para o programa, que, juntamente com a definição do modelo de memória tiny , permitirão que o compilador calcule corretamente todos os deslocamentos de variáveis ​​e transições. Ao criar o tlink , a opção /t tlink usada; na saída, isso tlink um arquivo .com .

Gráficos e paleta


Para alternar para o modo gráfico, você precisa ativar a função BIOS, para a qual uma interrupção de 10h, AH = 0 é chamada, em AL, colocamos o identificador do modo desejado - 13h:

 mov al, 13h ;b0 13 int 10h ;cd 10 

Observe que não tocamos no AH, assumindo que haja zero, de acordo com as condições de carregamento do programa. O modo selecionado corresponde a uma resolução gráfica de 320 por 200 pixels com uma paleta de 256 cores. Para exibir um ponto na tela, você precisa escrever na área de memória, que começa com o endereço A000h: 0, o byte correspondente à cor. Preencha os registros de dados do segmento com este valor:

 push 0a000h ;68 00 a0 pop es ;07 push es ;06 pop ds ;1f 

Logicamente, a memória é organizada como uma matriz bidimensional na qual as coordenadas da tela são exibidas, 0: 0 corresponde ao canto superior esquerdo. Após alternar o modo, ele é preenchido com zeros - preto na paleta padrão. A fórmula para traduzir para deslocamento linear é X + Y * L , onde L é a resolução horizontal, no nosso caso 320. Neste formulário, escreverei nos locais em que as constantes são usadas, ao traduzir o texto do programa, elas são calculadas automaticamente.

Para mudar a paleta, acessamos diretamente o equipamento usando as portas de entrada / saída:

 lodsb ;ac mov dx, 03c8h ;ba c8 03 out dx, al ;ee 

O primeiro comando carrega no AL o byte de dados localizado em DS: SI. No DS, carregamos o endereço do segmento da memória de vídeo e sabemos que ele é preenchido com zeros, no SI - no caso geral, não se sabe que pelo menos 0. Não é quase importante para nós, não importa onde o SI indique, provavelmente entraremos na memória de vídeo que com esta resolução 320 * 200 = 64000 bytes, quase todo o segmento. Portanto, esperamos que após este comando AL = 0. Uma unidade é adicionada ou subtraída ao SI, depende da configuração do sinalizador de direção DF. Embora isso também não seja particularmente importante para nós, não importa para onde o SI se mova, ainda permanecemos na área de memória de vídeo cheia de zeros.

Em seguida, carregue o DX com o número da porta 03C8h, cuja saída determina qual cor de 256 será substituída. No nosso caso, é 0 de AL.

A cor é codificada na paleta RGB e, para isso, você deve gravar na porta 03C9h (uma a mais que 3C8h) três vezes seguidas, uma vez para cada um dos componentes. O brilho máximo do componente é 63, o mínimo é 0.

 inc dx ;42 mov cl, 64 ;b1 40 PALETTE: out dx, al ;ee inc ax ;40 outsb ;6e outsb ;6e loop PALETTE ;e2 fa(-6),    6   

Aumente o DX em um para que ele tenha o número da porta desejado. CL é o nosso contador de ciclos de 64, e assumimos que CH = 0, conforme descrito anteriormente, com base nas condições iniciais de carregamento. Em seguida, enviamos o primeiro componente para a porta - o vermelho, cujo brilho será armazenado em AL, será conosco que mudará, no primeiro passo 0. Em seguida, aumentamos seu brilho em um para exibir na próxima iteração. Em seguida, executamos dois comandos outsb gravam na porta, cujo número está contido no DX, o byte da área de memória do DS: SI, lembre-se de que temos zeros lá. O SI muda sempre uma vez.

Assim que deduzimos os três componentes, uma unidade é automaticamente adicionada ao número da cor. Portanto, não é necessário redefinir a cor, enviando para a porta 3C8h se as cores estiverem em uma linha, conforme necessário. O comando loop reduzirá o CX em um, se um valor diferente de zero for obtido, ele irá para o início do ciclo, se 0, e para o próximo comando após o ciclo.

Um total de 64 repetições. A cada repetição, determinamos para a cor, começando de 0 a 63, o componente vermelho com brilho coincidindo com o número da cor atual. Redefinimos os componentes verde e azul para obter essa paleta do brilho mínimo ao máximo do vermelho:

paleta


Linhas


Configure a cor inicial e os valores das coordenadas:

 LINES: mov ax, 03f3fh ;b8 3f 3f mov bx, 0+1*320 ;bb 40 01 mov di, 64+4*320 ;bf 40 05 push di ;57 

Em AL e AH, carregamos a cor máxima possível (mais brilhante) 63 (3Fh), respectivamente, o AX define dois pontos ao mesmo tempo. BX - resolução horizontal máxima. No futuro, isso será usado para adicionar ou subtrair uma linha das coordenadas atuais. Coordenadas DI 64: 4, salve-as na pilha.

Desenhe a primeira linha do canto superior esquerdo ao extremo direito :

 mov cl, 120 ;b1 78 LINE1: stosw ;ab add di, bx ;03 fb loop LINE1 ;e2 fb(-5) 

Configure o contador - este será o número de linhas. Em seguida, salve a palavra (dois bytes) do AX no endereço ES: DI. Esta ação exibirá dois pontos na tela com a cor máxima da nossa paleta, porque o ES está configurado para memória de vídeo e coordenadas específicas são definidas em DI. Após essa ação, 2 serão adicionados à DI, pois dois bytes foram gravados. Obviamente, não definimos o sinalizador de direção DF e confiamos no fato de que ele é redefinido, novamente lembramos nossas condições iniciais para carregar o programa. Caso contrário, os dois seriam retirados, o que não permitiria desenhar a linha desejada.

Em seguida, DI = DI + BX, que é equivalente a aumentar a coordenada Y em uma. Assim, no corpo do ciclo, dois pontos são desenhados em uma linha, a coordenada X é aumentada em 2 e a coordenada Y em 1 e essa ação é repetida 120 vezes, a imagem com o resultado é um pouco menor.

A segunda linha é do canto superior esquerdo para o topo :

 pop di ;5f mov cl, 96 ;b1 60 LINE2: mov [bx+di], al ;88 01 stosb ;aa add di, bx ;03 fb add di, bx ;03 fb loop LINE2 ;e2 f7(-9) 

Restauramos as coordenadas iniciais 64: 4 e ajustamos o contador para 96 ​​repetições. Imprimimos um ponto, mas uma linha abaixo das coordenadas atuais. Como antes, isso é conseguido adicionando um valor do BX, apenas sem salvar as novas coordenadas. A construção [bx+di] ou [bx][di] é chamada de endereçamento base com indexação e funciona no nível do processador, não no tradutor. O registro de segmento padrão com o BX é DS. Depois disso, exibimos o segundo ponto, mas já nas coordenadas atuais. DI e, portanto, X aumenta em um, pois apenas um comando de transferência de byte é stosb - stosb . Os dois últimos comandos do corpo do ciclo são um aumento de Y em 2, para o qual usamos novamente o BX.

Depois de desenhar duas linhas, a seguinte imagem é obtida no canto superior esquerdo:

linha 1,2


Coordenadas esquerda e superior, à direita do endereço de deslocamento de linha na memória de vídeo. O ponto 64: 4 será sorteado duas vezes.

A terceira linha é do canto superior direito ao superior :

 mov cl, 97 ;b1 61 LINE3: mov [bx+di], al ;88 01 stosb ;aa sub di, bx ;2b fb sub di, bx ;2b fb loop LINE3 ;e2 f7(-9) 

Como o DI já contém o valor de coordenada desejado 160: 196, precisamos desenhar uma linha do topo onde a linha anterior terminou, subindo a tela enquanto mantém o mesmo ângulo. Consequentemente, o ciclo é quase idêntico. CX é aumentado em 1, porque a coordenada Y atual é 2 a mais (mais baixa) do que onde a linha anterior terminou, já foi calculada para a próxima iteração. Portanto, para chegar ao canto superior, é necessário dar um passo extra. O movimento ao longo de X continua na mesma direção - mais um após cada iteração e, ao longo de Y, em vez de adicionar, subtraímos os dois. Os pontos são exibidos na mesma ordem, primeiro mais baixo e mais alto.

linha 3


A quarta linha é do canto superior esquerdo para o canto superior direito:

 mov di, 17+123*320 ;bf d1 99 push di ;57 mov cl, 120 ;b1 78 LINE4: stosw ;ab sub di, bx ;2b fb(-5) loop LINE4 

Estamos novamente nas coordenadas necessárias, mas isso não é usado, aparentemente para não alterar o sinalizador de direção DF. Portanto, novas coordenadas são colocadas na DI e armazenadas na pilha.

Além disso, tudo é idêntico à primeira linha, apenas a coordenada Y não cresce, mas diminui, aumentamos.

A quinta linha é horizontal:

 pop di ;5f mov cl, 143 ;b1 8f rep stosw ;f3 ab 

Tudo é simples aqui, o mecanismo de retransmissão do microprocessador é usado, uma vez que a linha horizontal corresponde a um simples aumento no endereço de cada próximo ponto. Em DI, o endereço correspondente à coordenada do canto extremo esquerdo, armazenado na etapa anterior, é restaurado. O número de repetições no CX é definido e o prefixo de repetição é aplicado com o comando de transferência de palavras.

Após essa ação, temos um pentagrama totalmente desenhado na cor mais brilhante. 80 bytes usados ​​e 48 em reserva.

Magia de fogo


Definimos as condições de contorno para os cálculos:

 FLAME: cmp si, 320*200 ;81 fe 00 fa jae NEXT_PIXEL ;73 12 lodsb ;ac or al,al ;0a c0 jz NEXT_PIXEL ;74 0d 

No SI, haverá a coordenada do ponto atual para os cálculos; se ultrapassarmos os limites da tela, não realizaremos nenhum cálculo com esse ponto, procederemos ao cálculo do próximo.

lodsb carrega um byte da área DS: SI no AL, ou seja, a cor do ponto nas coordenadas atuais. Se for 0, também não fazemos nada e passamos ao próximo ponto.

Novo cálculo de cores

Este é o principal algoritmo para alterar os valores das cores na tela, isso não é uma chama, é a base para isso. Calculamos pontos vizinhos e alcançamos a continuidade da cor:

 dec ax ;48 mov [si-2], al ;88 44 fe mov [si], al ;88 04 mov [bx+si-1], al ;88 40 ff mov [si-1-1*320], al ;88 84 bf fe 

Subtraia do AX, de fato de AL, uma unidade que contém um valor de cor diferente de zero obtido das coordenadas atuais. Em seguida, anotamos o valor obtido em todos os pontos vizinhos, em relação à coordenada atual, ou seja, um pouco deles, com base em nossa paleta.

Como após lodsb , o valor do SI aumentou um e não corresponde mais ao ponto cuja cor lemos em AL, isso precisa ser ajustado. Observe que os comandos stosb byte transfer não são mais usados; em vez disso, mov é usado para identificar o endereço onde o valor será colocado. Se aceitarmos que as coordenadas atuais são X: Y, para elas SI-1, então:

  • mov [si-2], al - grave uma nova cor no ponto X-1: Y, à esquerda da cor atual. 2 é subtraído do SI devido ao motivo descrito acima, uma vez que uma unidade extra já foi adicionada a ele
  • mov [si], al - grave uma nova cor no ponto X + 1: Y, à direita da cor atual. SI já tem X + 1
  • mov [bx+si-1], al - escrevendo uma nova cor no ponto X: Y + 1, abaixo da atual. Novamente use BX para Y + 1
  • mov [si-1-1*320], al - escrevendo uma nova cor no ponto X: Y-1, acima da atual. Não poderemos usar o BX, pois precisamos tirar a coordenada, a arquitetura do processador não nos permite fazer isso desta forma; portanto, uma constante é usada de acordo com a fórmula de redução de coordenadas

O registro de segmento é DS, que é usado por padrão com SI e BX.

Em nenhum lugar a situação é verificada quando o ponto atinge a borda da tela. Isso não pode levar a uma falha, pois sempre estaremos dentro dos limites do segmento de vídeo. Um ponto vizinho pode cair em uma área não relatada com endereços maiores que 64.000 ou em uma linha adjacente, o que não causa danos a nós e até ajuda um pouco, como será visto na descrição adicional.

A mesma mágica, o cálculo das coordenadas do próximo ponto

 NEXT_PIXEL: add si, dx ;03 f2 inc dx ;42 jnz FLAME ;75 e3(-29) 

Vamos voltar um pouco, não definimos especificamente o valor inicial do SI em nenhum lugar e, no DX, ainda temos o número da porta de entrada de saída que usamos para a paleta. Realizamos apenas três ações simples SI = SI + DX; obviamente, isso definirá novas coordenadas, quais? DX = DX + 1 e se DX não for igual a 0, volte ao algoritmo básico para obter e calcular pontos vizinhos, ou seja, DX é algum tipo de contador?

Sabemos que precisamos contornar todos os pontos e calcular as mudanças de brilho de seus vizinhos. Se você fizer isso em uma linha, provavelmente obteremos um gradiente estático, talvez não exatamente, mas inalterado em torno de nossas linhas.Sabemos o tamanho de nossa tela e quantos pontos precisamos contornar, mas aqui quase o negligenciamos, mais precisamente, escolhemos o valor próximo 65536 em vez do exato 64000. DX é realmente um contador, apenas 65536. Mas por que seu valor inicial não é importante e por que consideramos O valor final é maior que o total de pontos na tela?

Porque contornamos pontos não seguidos e nem todos. Cada coordenada linear subsequente é maior que a anterior pelo valor de DX. Ou seja, no SI, a soma dos elementos DX de uma progressão aritmética simples: 0,1,2,3,4,5,6, ..., 362,363, ..., 65535. Isso já nos dá não linearidade, se você começar com SI = 0 e DX = 0, então no SI obtemos: 0,1,3,4,6,10,15,21, ..., 65341,65703, ..., 2147450880.

Mas isso não é tudo, porque a dimensão SI é de 16 bits, não podemos obter um valor maior que 65535, o estouro ocorre e o restante no SI permanece o módulo 65536. A fórmula de cálculo de coordenadas linear assume a forma SI = (SI + DX) MOD 65536, que quebra completamente a ordem contínua: 0,1,3,4,6,10,15,21, ..., 65341,167,530,894, ...

Agora lembramos que o SI não é inicializado de nenhuma maneira, ou seja, na próxima vez que retornarmos a esse ciclo começaremos da coordenada de onde paramos, e não de 0 ou de uma determinada. Isso adicionará caos à nossa sequência - aumentará o número de elementos que não se repetem. Caso contrário, a travessia dos pontos seria sempre a mesma, embora não linear. Um efeito de chama estaria presente, mas não tão claramente. Se falamos sobre o truque, é exatamente isso.Sempre o DX, exceto o primeiro uso, inicia implicitamente em 0 como resultado de um estouro inc dx.

E um pouco mais de caos é adicionado aos nossos valores-limite, pois para SI> = 64000 nenhum ponto será desenhado e a sequência de saída é levemente confusa. E pular todos os pontos com um valor zero leva ao efeito de ignição nos primeiros segundos do programa. Isso ocorre porque o ciclo completo termina mais rápido, pois a maioria dos pontos não é processada. Mas o mais importante é que, como o brilho da maioria dos pontos só aumenta, eles não podem ser obscurecidos pelas seções mais escuras vizinhas - elas simplesmente ainda não existem e os valores zero não são calculados. Depois que as áreas completamente pretas desaparecem, o equilíbrio é estabelecido, algumas áreas aumentam o brilho e outras diminuem.

Como resultado, não podemos mais falar sobre nenhuma ordem ou gradiente, os pontos não são dispensados, cada vez em uma nova sequência, incluindo repetir várias vezes ou pular completamente. Isso leva à formação de regiões de brilho diferente misturadas entre si, mudando a cada nova iteração.

Mas isso não é tudo: se você não adicionar novos pontos positivos, no final, todos serão reembolsados. Portanto, depois que o DX atinge seu valor máximo, voltamos a desenhar cinco linhas brilhantes e contamos todos os pontos na tela:

 in al, 60h ;e4 60 cmp al, 01h ;3c 01 jne LINES ;75 a5(-91) 

Mas antes disso, lemos da porta 60h, este é o teclado, o código de verificação da última tecla pressionada. Para ESC, é igual a 1. Nesse caso, a tecla ESC foi pressionada, avançamos em direção à saída.

Conclusão


Vale a pena prestar atenção que, ao atualizar a tela atual, que leva algum tempo, você não pode sair do programa, ou seja, a reação ao ESC será atrasada. Se durante a espera e depois que ESC for pressionada alguma tecla, ainda permaneceremos no programa, apenas o último código de leitura poderá ser lido a partir da porta. Mais uma coisa, não substituímos ou usamos as funções de sistema DOS e BIOS para isso, independentemente do que lemos da porta, a tecla pressionada é colocada em um buffer circular e provavelmente será lida a partir daí pelo próximo programa após a conclusão da introdução, o arquivo mais provável gerente ou command.com. Isso levará ao seu processamento, por exemplo, Volkov Commander no ESC ocultará seus painéis.

Resta retornar ao modo de texto 3:

 mov ax, 03h ;b8 03 00 int 10h ;cd 10 

Supõe-se que estávamos nesse modo antes do lançamento do programa, mas, no caso geral, isso pode não ser o caso. Aqui, atualizamos todo o AX, porque sabemos com certeza que o AH não contém 0.

Agora você pode sair:

 retn ;c3 

Este é um comando near exit de um procedimento que pega o valor da palavra colocada lá (dois bytes) da pilha e o carrega no contador de comandos IP. De acordo com as condições iniciais, temos zeros na pilha, isso nos levará ao endereço CS: 0, onde, como sabemos, o código de comando está localizado int 20h- shutdown.

E 7 bytes para direitos autorais:

 dd 0h ;00 00 00 00 db 'Mcm' ;4d 63 6d end 

Podemos dizer que ainda há um lugar que eu gastaria em uma inicialização mais rigorosa, mas como tudo funciona no moderno DOSBox, o autor provavelmente fez tudo certo.

Vamos passar mais uma vez:

  1. ,
  2. 4 , : X+1 Y+2, X+2 Y+1. , . ,
  3. SI=(SI+DX) MOD 65536, DX , , , SI. 1. 65536 , , . , — add si, dx inc dx , ,
  4. ESC ,

.
 .186 .model tiny .code .startup mov al, 13h int 10h push 0a000h pop es push es pop ds lodsb mov dx, 03c8h out dx, al inc dx mov cl, 040h PALETTE: out dx, al inc ax outsb outsb loop PALETTE LINES: mov ax, 03f3fh mov bx, 0+1*320 mov di, 64+4*320 push di mov cl, 120 LINE1: stosw add di, bx loop LINE1 pop di mov cl, 96 LINE2: mov [bx+di], al stosb add di, bx add di, bx loop LINE2 mov cl, 97 LINE3: mov [bx+di], al stosb sub di, bx sub di, bx loop LINE3 mov di, 17+123*320 push di mov cl, 120 LINE4: stosw sub di, bx loop LINE4 pop di mov cl, 143 rep stosw FLAME: cmp si, 320*200 jae NEXT_PIXEL lodsb or al,al jz NEXT_PIXEL dec ax mov [si-2], al mov [si], al mov [bx+si-1], al mov [si-1-1*320], al NEXT_PIXEL: add si, dx inc dx jnz FLAME in al, 60h cmp al, 01h jne LINES mov ax, 03h int 10h retn dd 0h db 'Mcm' end 

Para compilar, você deve fazer: tasm pentagra.asme tlink /t pentagra.obj.

Não sei se ficou claro para você o que e como foi implementado, mas parece-me que uma abordagem bonita e incomum foi usada para criar o efeito de chama. Embora eu não tenha nada com o que comparar, talvez todos tenham feito isso, e agora você pode fazer o mesmo.

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


All Articles