Como uma demonstração de memórias se encaixa em 256 bytes


Introdução


Olá! Meu nome é HellMood e este artigo é sobre um pequeno programa do MS DOS chamado Memories. Este programa tem um tamanho de 256 bytes, venceu na categoria demoscene do concurso «PC 256 byte» «Revisão» em 2020 e também recebeu o Prêmio do Público. Você pode assistir ao vídeo da saída do programa aqui e ao vídeo com a reação do público on-line e dos moderadores aqui . Baixe o release e deixe comentários aqui. Este artigo irá apresentar uma análise aprofundada do programa, falar sobre referências históricas e estágios de desenvolvimento. O artigo está publicado no wiki de codificação de tamanho. Isso não apenas permitirá que você entenda a estrutura interna das Memórias, mas também ajudará a criar algo semelhante. Explore! Se você é iniciante em codificação de tamanho (programas de gravação no tamanho desejado) ou no assembler x86, é recomendável que você comece com o básico deste wiki . Os princípios são fáceis de entender, mas não é tão fácil descobrir detalhes da implementação.

Breve revisão


Neste artigo, falaremos sobre a versão enviada ao concurso para o DosBox (256 bytes). O arquivo também contém versões para FreeDos e Windows XP DOS, que no momento da escrita da postagem não funcionavam em todos os computadores. Essas versões alternativas foram incluídas no arquivo como prova de conceito para mostrar que o programa não funciona apenas no emulador. Na categoria “PC 256 bytes” da competição “Revisão” de 2020, foi possível indicar “FreeDos” ou “DosBox” como a plataforma (a última em uma configuração específica). Como as versões alternativas provam, de fato, você pode modificar a versão do DosBox para que funcione no FreeDos, MS DOS, WinXP e Win98, mas o artigo não será sobre isso.

Seja como for, estão em desenvolvimento versões confiáveis ​​para todas as plataformas e computadores. Quanto a possíveis otimizações adicionais, falarei apenas da versão enviada para o concurso, embora já tenha encontrado vários locais onde o código pode ser otimizado. Como essa é uma demonstração do histórico de pequenos efeitos, quase nenhum código é perfeito e pode ser reduzido ainda mais. Para não ficar confuso em diferentes versões, falarei apenas da competição.

História de pequenos efeitos



Categorias de dimensão em http://www.pouet.net

Nós, sizcoders, raciocinamos em categorias de tamanho. Para o MS DOS, essas categorias são 256b, 128b, 64b e 32b. Estes são os padrões de um dos maiores arquivos da cena demo www.pouet.net. Não há categoria 16b, porém muitos efeitos pequenos podem ser implementados em 16 bytes. Quase todos os efeitos de “Memories” foram escritos e otimizados por mim anteriormente, e basicamente sua implementação foi uma tentativa de reduzir o tamanho existente do efeito ou criar algo semelhante, mas menor. Por redução de tamanho, aqui se entende sua redução para uma das seguintes categorias menores 2 ^ N. Por exemplo, se o efeito foi implementado em 33-64 bytes, é reduzido para 32 bytes ou menos. Quase todas as vezes que consegui "diminuir" o efeito para uma categoria inferior, enviei o pequeno programa resultante para um evento dedicado à demoscene, no qual participantes remotos podiam participar da categoria 256b e / ou publiquei o resultado em www.pouet.net. Nesta seção, apresentarei os efeitos, além de falar sobre sua origem e autores.

Matriz de tabuleiros de xadrez



kasparov, 16 bytes. A

fonte desse efeito foi o meu próprio “Kasparov 16b” de 2018 ( link ). Parece (citação das notas de lançamento), "acabei de montar esta placa, encaixá-la em 17 bytes, mas não era muito bonita até que eu fiz um truque ..." Anteriormente, já havia um efeito semelhante implementado em 32 bytes: 2003 ew do headcrash do desenvolvedor. ( link ) Nesse caso, tentei implementar tabuleiros de xadrez "reais" com um quadrado 8x8 e quadrados em preto e branco reconhecíveis, bem como a orientação correta de tabuleiros de xadrez individuais, ou seja, o canto inferior direito (h1) deveria ter sido branco. Para que esse efeito funcionasse com a estrutura global, em “Memórias”, ele teve que ser reimplementado usando outra técnica: gravação na tela; Além disso, a direção da rolagem foi alterada para diferir do efeito "plano inclinado de rolagem".

Círculos de zoom


Círculos escalonáveis ​​(círculos de zoom) deveriam participar de demopati como uma introdução de 32 bytes, mas eu nunca participei. Esse efeito não possui um predecessor de 64 bytes, porque na categoria 64b são possíveis efeitos muito mais complexos. Círculos de zoom foram o resultado de minha tentativa desesperada de entrar na categoria 32b com o efeito de um “túnel” redondo, para o qual meu registro pessoal ainda tinha 52 bytes (“Neontube” - 2016) ( link ), que, por sua vez, se tornou uma otimização do clássico 64 efeito de byte "evolução constante" desenvolvedores ryg / Farbrausch (2003) ( link ). No procedimento de círculos de zoom, a distância e o ângulo são eliminados / ignorados, devido aos quais foi possível entrar na categoria 32b.

Rolagem do plano inclinado



O Floorcast, uma versão de 32 bytes, uma variação do

plano inclinado Scrolling, é um dos meus próprios lançamentos, o floorcast 32b 2018. O efeito do "revestimento" tem seu próprio histórico na codificação de tamanho e diminuiu gradualmente de 256 bytes para 32 bytes. Versões diferentes diferem no número de planos, em alguns dois planos são exibidos, em outros - apenas um. No lançamento do “floorcast 32b”, decidi especificamente abandonar a textura “XOR” e, em “Memories”, usei novamente, mascarando-o com o toque final na forma de “AND”.

  • Versão rain_storm 2008 - 256 bytes - link
  • Versão 2008 do org_100h - 128 bytes - link
  • Versão Baudsurfer 2013 - 86 bytes - link
  • Versão Baudsurfer 2014 - 64 bytes - link
  • Versão HellMood 2018 - 32 bytes - Link

Tabuleiros de xadrez de paralaxe



Projektbeschreibung, 32 bytes

Lancei tabuleiros de xadrez paralaxe em 2018 como um efeito Projektbeschreibung de 32 bytes. Nele, tentei reduzir o tamanho de “Follow the light” ( link ) de “Digimind” (2006) ou o meu próprio “Lucy” (2014) ( link ) para 32 bytes. Fontes de inspiração muito úteis foram Paralaxa por Rrrolas (32 bytes, 2007, link ) e Byteropolis por Sensenstahl (2013) ( link) A técnica de renderização de Rrrolas estava muito próxima da minha decisão final, o código foi alterado para corrigir a localização dos aviões, substituir os triângulos por tabuleiros de xadrez e melhorar as cores. As memórias usavam o esquema de cores da versão Digimind. Além disso, o efeito foi modificado ao máximo para reduzir as deformações.

Sierpinski rotozoomer



colpinski, 16 bytes


rotastic, 32 bytes

Consiste em dois efeitos: rotozoomer e Sierpinski como textura. O efeito Sierpinski é baseado no meu próprio efeito Colpinski 16b 2013 ( link ), onde consegui obter o máximo possível com a função frag fsqrt. Esse efeito não possui antecessor, pois pode ser implementado diretamente combinando X e Y, em vez de usar um Sistema de Função Iterado ou autômatos celulares. "Rotation and scaling" (rotozoomer) foi lançado por mim em 2017 como a introdução 32b "rotastic" ( link ), é baseada nas idéias de "ryg" do desenvolvedor Farbrausch (51 bytes, 2002, link ) e "Gargaj" do grupo "Conspiracy" "(49 bytes, 2002, link ).

Túnel dobrado Raycast



Em uma nova era, versão de 64 bits O

túnel dobrado da raycast se tornou uma versão modificada do meu próprio lançamento de 64 bytes, "Em uma nova era" (2018, link ). As cores originais foram substituídas pelas cores da paleta padrão, a geometria e os cálculos correspondentes foram ligeiramente alterados para que o efeito de profundidade não fosse usado para calcular os valores de textura. Uma versão separada desse efeito tem 50 bytes de tamanho. As principais fontes de inspiração para a criação da versão de 64 bytes foram duas introduções de 128 bytes: “Spongy” de “TBC” (2009, link ) e “Wolf128” de Baudsurfer (2014, link ), e eu próprio desenvolvi o algoritmo.

Noite oceano a dia



Ocean, versão 64b

O efeito oceânico é baseado no meu próprio lançamento de 64 bytes do “Ocean” 2016 ( link ). Uma geração separada das cores e da música do original foi cortada; os dois geradores não eram compatíveis com o quadro principal de Memórias sem usar pilhas de bytes extras. O efeito especial do "amanhecer" é devido à implementação geral da estrutura do framework. Vou falar sobre isso no próximo capítulo.

Efeito de desbotamento


A transição entre os dois efeitos é em si um efeito que não possui antecessores. Pelo contrário, é uma ideia que evoluiu ao longo de vários anos e talvez tenha sido implementada de forma semelhante por muitos outros. Em resumo, ao calcular o quadro, a posição de cada pixel é aleatória, e o tempo que determina qual efeito deve ser usado é alterado por esse valor de randomização, que foi reduzido anteriormente. Isso permite que você use uma paleta VGA padrão (imagem, fonte), em vez de criar suas próprias cores para uma mixagem suave, o que economiza espaço.

A estrutura do meu "pequeno megademo"


Para combinar vários efeitos pequenos em um “megademo”, eles devem usar a mesma técnica e, no máximo, não devem aplicar suposições (relacionadas ao conteúdo da memória e dos registros). Eles também precisam usar os mesmos valores de tempo e trabalhar de acordo com o tempo total. Demorou muito tempo para preparar efeitos individuais adequados para a estrutura e, a princípio, muito espaço extra. Note-se que alguns dos efeitos mais impressionantes (a julgar pela reação dos espectadores e opiniões nas redes sociais) não puderam ser incluídos na demonstração devido ao enorme uso excessivo de memória. Após todos os efeitos terem sido corrigidos, eu poderia começar a pensar em "bracketing" cálculos frequentemente repetidos, o que economizaria mais alguns bytes.A estrutura executa as seguintes ações:

  • 320 x 200 256


  • 35 FPS
  • ESC
    • ESC,


org 100h
s:
	mov al,0x13				; set AL to mode 320*200 in 256 colors
	int 0x10	 			; call BIOS to set mode
	xchg bp,ax				; set timing value to 0x13 
	push 0xa000-10			; write the screen adress to register ES
	pop es					; works in conjunction with Rrrola trick
	mov ax,0x251c			; parameter for changing timer interrupt
	mov dl,timer			; adress of timer routine, assume DH=1
	int 0x21				; install timer routine
top:
	mov ax,0xcccd			; load magic Rrrola constant
	mul di					; transform screen pointer to X, Y
	add al,ah				; use transformation garbage as
	xor ah,ah				; pseudorandom value and clear AH
	add ax,bp				; add time value to random value
	shr ax,9				; divide by 512 (basically the speed)
	and al,15				; filter effect number
	xchg bx,ax				; move effect number to BX
	mov bh,1				; reset BH to align with start of code
	mov bl,[byte bx+table]	; read the effect address from the table
	call bx					; call the effect
	stosb					; write the return value and advance
	inc di					; triple interlace trick for after
	inc di					; effect and smoothing the animation
	jnz top					; repeat until the frame is complete
	mov al,tempo			; set AL to divider for timer
	out 40h,al				; set timing (dual pass)
	in al,0x60				; read keyboard
	dec al					; quit on ESC
	jnz top					; otherwise repeat loop
sounds: db 0xc3, 11, 0x93; 0xc3 is MIDI/RET; fx2-s is used as volume
table: 	db fx2-s,fx1-s,fx0-s,fx3-s,fx4-s,fx5-s,fx6-s,sounds-s,stop-s


Matriz de tabuleiros de xadrez



matriz de tabuleiros de xadrez

O efeito mais fácil para começar. Depois de mudar a sequência temporariamente, o padrão XOR clássico é aplicado. Para criar a impressão de uma grade do tabuleiro de xadrez, todos os bits, exceto dois, são definidos em cores. O verdadeiro truque é mudar para o local "bom" da paleta. A parte do quadro que é percebida como preta na verdade não é preta, mas é a parte escura da paleta VGA padrão . Essa mudança permite que você dê às células escuras e claras a sensação de um antigo tabuleiro de xadrez.

	xchg dx,ax		; get XY into AX
	sub ax,bp		; subtract time from row
	xor al,ah		; XOR pattern (x xor y)
	or al,0xDB		; pattern for array of boards
	add al,13h		; shift to good palette spot

Círculos de zoom



círculos de zoom

A distância D do ponto (X, Y) ao centro (0,0) é de sqrt (X² + Y²). A estrutura anteriormente faz com que DL contenha a coordenada centralizada X e DH = Y precisa ser centralizado no código. Para executar a operação de cálculo da raiz quadrada em x86, é necessário muito código, mas na verdade ele pode ser omitido. Com uma escolha cuidadosa de cores, a sensação de círculos convergentes sem raiz quadrada parece bastante convincente.

	mov al,dh		; get Y in AL
	sub al,100		; align Y vertically
	imul al			; AL = Y²
	xchg dx,ax		; Y²/256 in DH, X in AL
	imul al			; AL = X²
	add dh,ah		; DH = (X² + Y²)/256
	mov al,dh		; AL = (X² + Y²)/256
	add ax,bp		; offset color by time
	and al,8+16		; select special rings

Rolagem do plano inclinado



rolando o plano inclinado

Este efeito é implementado da seguinte forma: primeiro, para simular a distância, uma grande constante é dividida pelo número da linha Y. Em seguida, o valor obtido é usado duas vezes: a) multiplicado pelo valor centralizado de X eb) compensado pelo tempo atual. Em seguida, esses resultados são combinados usando o padrão XOR, no qual um padrão especial é selecionado.

	mov ax,0x1329	; initialize with constant
	add dh,al		; preventing divide overflow
	div dh			; reverse divide AL = C/Y'
	xchg dx,ax		; DL = C/Y', AL = X
	imul dl			; AH = CX/Y'
	sub dx,bp		; DL = C/Y'-T 	
	xor ah,dl		; AH = (CX/Y') ^ (C/Y'-T)
	mov al,ah		; move to AL
	and al,4+8+16	; select special pattern

Tabuleiros de xadrez de paralaxe



placas de verificação de paralaxe

Este é um tipo de reykasting (emissão de raios) com geometria dinâmica. Cada plano dos objetos é dividido horizontalmente devido à multiplicação de 16 bits com um sinal e verticalmente devido à operação da lógica implícita com o número da coluna. Além disso, distorção indireta é aplicada para conectar as arestas da malha resultante (4 áreas “sólidas”, 4 áreas “transparentes” alternadamente). Se o feixe cruzar uma das áreas sólidas, a cor se tornará o número da iteração (+ o deslocamento da paleta em tons de cinza); caso contrário, o plano será movido para o ponteiro da tela, após o qual o processo se repetirá até que o número máximo de iterações seja alcançado.

	mov cx,bp		; set inital point to time
	mov bx,-16		; limit to 16 iterations
fx3L:
	add cx,di		; offset point by screenpointer
	mov ax,819		; magic, related to Rrrola constant
	imul cx			; get X',Y' in DX
	ror dx,1		; set carry flag on "hit"
	inc bx			; increment iteration count
	ja fx3L			; loop until "hit" or "iter=max"
	lea ax,[bx+31]	; map value to standard gray scale

Sierpinski rotozoomer



sierpinski rotozoomer


Gráfico 1 / cos (atan (x)), criado usando www.google.com

Para girar com escala, são geralmente necessárias funções trigonométricas ou seus bons valores aproximados. Vamos dar uma olhada na equação de rotação 2D usual e interpretá-la de uma maneira especial para se livrar da trigonometria nos cálculos:

x' = x*cos(a) - y*sin(a)
y' = x*sin(a) + y*cos(a)

Se adicionarmos o dimensionamento, ficará assim:

x' = z * (x*cos(a) - y*sin(a))
y' = z * (x*sin(a) + y*cos(a))

Agora vamos dizer que não definimos z nós mesmos e colocamos 1 / cos (a) entre colchetes:

x' = 1/cos(a) * (x - y*tan(a))
y' = 1/cos(a) * (x*tan(a) + y)

Agora substituímos tan (a) por uma variável de tempo, porque a função tangente tende ao infinito quando se aproxima de 180 °:

x' = 1/cos(atan(T)) * (x - y*T)
y' = 1/cos(atan(T)) * (x*T + y)

Se não nos importamos com a falta de capacidade de definir o fator de escala e não controlamos o ângulo diretamente, agora podemos girar de -180 ° a + 180 ° sem usar funções trigonométricas. Como resultado, o fator de escala acaba ligado ao tempo T. A função do coeficiente é mostrada na imagem, devido à qual a escala ocorre de um número infinitesimal para um (o tamanho original) e volta para o infinitesimal. Uma quantidade considerável de bytes foi gasta decorando esse efeito, corrigindo compensações de tempo, acelerando animações, aumentando os pixels dos triângulos de Sierpinski e criando cores bonitas, mas acho que valeu a pena.

	lea cx,[bp-2048]; center time to pass zero
	sal cx,3		; speed up by factor 8!
	movzx ax,dh		; get X into AL
	movsx dx,dl		; get Y int DL
	mov bx,ax		; save X in BX
	imul bx,cx		; BX = X*T
	add bh,dl		; BH = X*T/256+Y
	imul dx,cx		; DX = Y*T
	sub al,dh		; AL = X-Y*T/256
	and al,bh		; AL = (X-Y*T/256)&(X*T/256+Y)
	and al,252		; thicker sierpinski
	salc			; set pixel value to black
	jnz fx4q		; leave black if not sierpinski
	mov al,0x2A		; otherwise: a nice orange
	fx4q:

Túnel dobrado Raycast



túnel dobrado raycast

Este é um tipo de túnel com rakasting de "para uma nova era" (veja acima). Uma descrição detalhada desse efeito para um programa similar do Essence foi publicada no reddit. Livrei-me de cores exclusivas, mudei a direção da inclinação e a geometria ficou mais fechada para melhorar o desempenho em computadores mais antigos e no DosBox.

	mov cl,-9		; start with depth 9 (moves backwards)
	fx5L: 
	push dx			; save DX, destroyed inside the loop
		mov al,dh	; Get Y into AL
		sub al,100	; Centering Y has to be done "manually".
		imul cl		; Multiply AL=Y by the current distance, to get a projection(1)
		xchg ax,dx	; Get X into AL, while saving the result in DX (DH)
		add al,cl	; add distance to projection, (bend to the right)
		imul cl		; Multiply AL=X by the current distance, to get a projection(2)
		mov al,dh	; Get projection(1) in AL
		xor al,ah	; combine with projection(2)
		add al,4	; center the walls around 0
		test al,-8	; check if the wall is hit
	pop dx			; restore DX
	loopz fx5L		; repeat until "hit" or "iter=max"
	sub cx,bp		; offset depth by time
	xor al,cl		; XOR pattern for texture 
	aam 6			; irregular pattern with MOD 6
	add al,20		; offset into grayscale palette

Noite oceano a dia



noite do oceano


oceanday

O Ocean Effect é um excelente exemplo de "acaso". Se carregarmos o valor na FPU como um número inteiro e o armazenarmos como um ponto flutuante, e depois interpretá-lo novamente como um número inteiro, obteremos um ótimo padrão. Se você combinar isso com a divisão inversa, obterá um belo efeito de onda. Ele trabalha em estreita colaboração com o registrador DX, que possui uma inversão de sinal na posição de que precisamos, para que possamos separar facilmente o céu do mar. No entanto, o toque final é a cor. De acordo com a estrutura da estrutura global, o valor de AL é determinado na entrada da função e contém o endereço do efeito. Ao embaralhar um pouco o código, você pode obter a cor do céu "livre" sem usar instruções, assim como a cor da "transição para o dia", que também é o endereço do efeito. E isso não é uma feliz coincidência. Em outras versões que não a versão DosBox,a cor do céu por esse motivo pode variar.

	sub dh,120			; check if pixel is in the sky
	js fx6q				; quit if that's the case
	mov [bx+si],dx		; move XY to a memory location
	fild word [bx+si]	; read memory location as integer
	fidivr dword [bx+si]; reverse divide by constant
	fstp dword [bx+si-1]; store result as floating point
	mov ax,[bx+si]		; get the result into AX
	add ax,bp			; modify color by time
	and al,128			; threshold into two bands
	dec ax				; beautify colors to blue/black

Efeito de desbotamento


Por conveniência, o efeito é isolado da estrutura global. De fato, ele gera um número pseudo-aleatório a partir do ponteiro da tela e, em seguida, executa um deslocamento de tempo pelo valor em escala, após o que causa o efeito desejado.

	mov ax,0xcccd			; load magic Rrrola constant
	mul di					; transform screen pointer to X, Y
	add al,ah				; use transformation garbage as
	xor ah,ah				; pseudorandom value and clear AH
	add ax,bp				; add time value to random value
	shr ax,9				; divide by 512 (basically the speed)
	and al,15				; filter effect number
	xchg bx,ax				; move effect number to BX
	mov bh,1				; reset BH to align with start of code
	mov bl,[byte bx+table]	; read the effect address from the table

Música MIDI


Esta seção do código incrementa o valor do tempo e cria um som. Ao selecionar o canal 3, podemos reutilizar a instrução "change tool channel" como "RET". Ao mudar o código do efeito, você pode criar um valor adequado para o volume, o que economiza outro byte. Vale a pena considerar que essa parte do código só funciona se o dispositivo MIDI já estiver no modo UART, caso contrário, você precisará gastar mais três bytes. Muitos dos espectadores e organizadores me disseram que a música é um pouco como os encantamentos de Mike Oldfield , mas foi criada com uma técnica muito simples. Desde Hypnoteye 2015, experimentei MIDI procedural e, por fim, liberei uma pequena estrutura MIDI (64 bytes). O princípio principal de seu trabalho é pular com um tom fixo no espaço de tons e converter valores altos em valores mais baixos (por módulo de divisão). Combinações simples de largura de etapa e valor modificado podem obter efeitos interessantes. Por exemplo, largura da etapa = 3 permite obter um acorde menor e com duração contínua reduzida, e largura da etapa = 4 ou largura da etapa = 6 - efeitos tritonais. Com um valor modificado bem escolhido, esses padrões podem criar sequências. Não realizei uma análise teórica adequada, mas simplesmente explorei o espaço dos tons e notei sons interessantes.

sounds: db 0xc3, 11, 0x93, fx2-s
...
		inc bp				; increment timing value
		test bp, 7			; play a note every 8th step
		jnz nomuse			; quit if in between
		mov dx,0x330		; port number for MIDI
		mov si,sounds		; adress for sound data
		outsb				; change instrument of channel 3
		outsb				; to vibraphone
		outsb				; play a note on channel 3
		imul ax,bp,-19*32*4	; the magic melody constant
		shr ax,10			; scale down and implicit "and 63"
		add al,22			; pitch base is 22
		out dx,al			; play THIS note on channel 3
		outsb				; play it with THIS volume

Código de versão completo


; "memories" by HellMood/DESiRE
; the tiny megademo, 256 byte msdos intro
; shown in April 2020 @ REVISION
;
;   (= WILL BE COMMENTED IN DETAIL LATER =)
;
; create : nasm.exe memories.asm -fbin -o memories.com
; CHOOSE YOUR TARGET PLATFORM (compo version is dosbox)
; be sure to use the dosbox.conf from this archive!
; only ONE of the defines should be active!
%define dosbox			; size : 256 bytes
;%define freedos		; size : 230 bytes
;%define winxpdos		; size : 263 bytes

; DON'T TOUCH THESE UNLESS YOU KNOW WHAT YOU'RE DOING
%ifdef winxpdos
	%define music
	%define switch_uart
	%define safe_dx
	%define safe_segment
%endif
%ifdef freedos
	%define safe_dx
%endif
%ifdef dosbox
	%define music
	;%define safe_dx ; sometimes needed
%endif

; GLOBAL PARAMETERS, TUNE WITH CARE!
%define volume 127	; not used on dosbox (optimization)
%define instrument 11
%define scale_mod -19*32*4; 
%define time_mask 7
%define targetFPS 35
%define tempo 1193182/256/targetFPS		
%define sierp_color 0x2A
%define tunnel_base_color 20
%define tunnel_pattern 6
%define tilt_plate_pattern 4+8+16
%define circles_pattern 8+16

org 100h
s:
%ifdef freedos
	mov fs,ax
	mov [fs:0x46c],ax
%endif
	mov al,0x13
	int 0x10	 
	xchg bp,ax
	push 0xa000-10
	pop es
%ifndef freedos
	mov ax,0x251c
	%ifdef safe_dx	
		mov dx,timer	
	%else ; assume DH=1, mostly true on DosBox
		mov dl,timer
	%endif
	int 0x21
%endif
top:
%ifdef freedos
	mov bp,[fs:0x46c]
%endif	
	mov ax,0xcccd
	mul di
	add al,ah
	xor ah,ah
	add ax,bp
	shr ax,9
	and al,15
	xchg bx,ax
	mov bh,1
	mov bl,[byte bx+table]
	call bx
	stosb
	inc di
	inc di
	jnz top
	mov al,tempo
	out 40h,al
	in al,0x60
	dec al
	jnz top
sounds:
	db 0xc3	; is MIDI/RET
%ifdef music
	db instrument,0x93
	%ifdef switch_uart
		db volume		; without switch, volume is in table
		db 0x3f 
	%endif
%endif
table: ; first index is volume, change order with care!		    					
	db fx2-s,fx1-s,fx0-s,fx3-s,fx4-s,fx5-s,fx6-s,sounds-s,stop-s
stop:
	pop ax
	ret
timer:
%ifndef freedos
	%ifdef safe_segment
		push cs
		pop ds
	%endif
		inc bp
	%ifdef music	
		test bp, time_mask
		jnz nomuse
		mov dx,0x330
		mov si,sounds
		outsb
		outsb
		outsb
		imul ax,bp,scale_mod
		shr ax,10
		add al,22
		out dx,al
		outsb
		%ifdef switch_uart
			inc dx
			outsb
		%endif
	%endif
nomuse:
	iret
%endif	
fx0: ; tilted plane, scrolling
	mov ax,0x1329
	add dh,al
	div dh
	xchg dx,ax
	imul dl
	sub dx,bp
	xor ah,dl
	mov al,ah
	and al,tilt_plate_pattern
ret
fx2: ; board of chessboards
	xchg dx,ax
	sub ax,bp
	xor al,ah
	or al,0xDB
	add al,13h
ret
fx1: ; circles, zooming
	mov al,dh
	sub al,100
	imul al
	xchg dx,ax
	imul al
	add dh,ah
	mov al,dh
	add ax,bp
	and al,circles_pattern
ret
fx3: ; parallax checkerboards
	mov cx,bp
	mov bx,-16
fx3L:
	add cx,di
	mov ax,819
	imul cx	 
	ror dx,1	 
	inc bx	 
	ja fx3L
	lea ax,[bx+31]	 
ret
fx4: ; sierpinski rotozoomer	
	lea cx,[bp-2048]
	sal cx,3
	movzx ax,dh
	movsx dx,dl
	mov bx,ax
	imul bx,cx
	add bh,dl
	imul dx,cx
	sub al,dh
	and al,bh
	and al,0b11111100
	salc				; VERY slow on dosbox, but ok
	jnz fx4q
	mov al,sierp_color
	fx4q:
ret
fx5: ; raycast bent tunnel
	mov cl,-9
	fx5L: 
	push dx
		mov al,dh
		sub al,100
		imul cl
		xchg ax,dx	
		add al,cl
		imul cl
		mov al,dh
		xor al,ah
		add al,4
		test al,-8
	pop dx
	loopz fx5L
	sub cx,bp
	xor al,cl
	aam tunnel_pattern; VERY slow on dosbox, but ok
	add al,tunnel_base_color
ret
fx6: ; ocean night / to day sky
	sub dh,120
	js fx6q
	mov [bx+si],dx
	fild word [bx+si]
	fidivr dword [bx+si]
	fstp dword [bx+si-1]
	mov ax,[bx+si]
	add ax,bp
	and al,128
	dec ax
fx6q:
ret

Bônus - NFO / ASCII
                                                             art : hammerfist
         ∂#MW%e                              _d$Ng,
         'B,  ∂b                   _jM@$QZb,cQ"  )@
  ,edRB$b,l@   Wk,yGR$KM&$b,     ,dP"     Wl ]bsd%UR8BG6&$@DSyG#ZKM&$b,
,dP      "T%L  'MGF      "*∂R_   Tg    "*4Zk,#I  YP   W"    7P      "*∂R
4M   gd@    ^   ∂@   d@b   dQ$#@Z@R3L_    "*GMj  'W      ,gd$   d@b   9Q$#%b
W#,  `M          Wb  `*  _4P   `Qk  *#N8L   `H5   @b   'QR7YK   `*  _4F"   Qk
`6@L             dML            '@          ,BK   'M    ∂B  *b,            '#L
  ^QBb,_     _,4&M∞∂@=,_       _dGL       _gQKM    GL    @k  'Mg,_         _dG,
    "*BN5W$2#MNP"   "*G3WRM8&B5P"`Y@QNW3Z5P" ∂#$W8BRM3XZN87    "*GW38M%EBDW5P"`


                              p r e s e n t s

            4
           d@,
         _& `Wl
      _,aP   "#baedM$#@@K JP*"?ML
 ,ad@$#P"         ,d@NEWVB"     X,aQPYb,_
V@Mm,_          ,d@MW#BW'      EMP"   '¶R ,ngBP^fML
 ¶M@N@y        Y#BNW#M"       J9"      `MQ9"      "MgRBq  ,QBMg,
  VN#P` ,d@@    `WM@^                   7f         ¶F` 7kY"   ^G  _.eQNE1.
   ]B _G@MWN$,   `P                     '     4b       QP      ¶w@F*^  ^Qb
   ]O@NRM#W@MNB,         ;                    ^`      j        JP^       Yl
  J#NRNWM@#BcT"^        ,A  _J                     _q@                   `X
 '¶WM#B@WdY`,7        _G#YN#PM                 _,gG"                      M,
  *BN#WP"  dK       ,Q@NRMB"]9       ,      _,M@Q*                        #A
   "U^      V@h,   iNBW#NT  J'      J9     s@QN"         _;               'D,
             ¶RMBv&NMQR@9  .W      .K'     "9`         ,6BA   _JL          ]l
              Y#NE@W#NRP   #[      `¶8               _d@MW#B_jW#W          BN
               "GQ@MR#W    QL_      *B            _,p#NBW#NQMG@WY          3Q
                  "Y@F     ,XW@M%im,_Yb_     _,g5@#MW@QMNE@E@NRMB         ,WM
                    `  _,gP*"#REM#GB@N#MQbnd@N#M@MW#R8QSB^'WQERM@        ;4NB,
                     ,GYKL    ¶E#B8R8QSB@M@#BM#W@MNB"`_  ,  "^` N       ,dW@Ql
                   _Q`'W`*t    '¶@GS#MBQ#E@W#NQBW[     'LvQ_   ,K    _dNABGM#N
                  ,F   '          `^WAB@QGE9*"9^*@L    jP7FY,  ¶h,_.jWM#BR#GBM,
                 J;    ,   _                  '       '   "LL  YxE#B8R8QSBNW@W;
                AP   _,Ag6^          _   J                  ¶A  `"Q#M@MW#R8E#P
               j@   `"XQW[            'LvK,_      'L_,/      @t    Y#NE@WNR"
              :M/     9^*@L           jP7F"       _PYKL     _,A;     ¶RSNQ"
              dKL     '     `        '   "L      "`'W`*t   `"XQb      `W^
              Q`8t            'L_,/         ,   _   '        9^Q
             ,W               _PYKL       _,Ag6^             ' W,     _ ,#N&
             !N  _   J       "`'W`*t     `"XQW[       _  J     N!_JG9^RwQ' *t
             `W,  'LvK,_        '        _gGB8@L   _   'LvK,_ ,WgB'    V    7L
         _.,gm&@B&wBZF"                j@'`  "WL _gML  jZd7Yb lN"          dBWl
      ,g&QB*"^`    `"*G@g, .gR&k,_   ,N"      '@QF  ¶k;gMF  *QvQ     jQ, ,@N@B#,
   .eQF*`              `Yb@"  "*6Qg,gF     ,   7     XMN"    'MNB,    ^¶QWSER@N;
 ,gP"           qy,      W'       ^Q'     &L      ,g@W'       `QMEL     `"WBNWP
g7              ¶9      ,X         M?     9"   _q8MSK           ¶EMt       *@K
Vh   _,m#L             _AH        le         ,GBDNE9^A,          *@F        NMg
 ¶L,qQ@ND           _.m@Bl        We      ,gM@B8#Q'   ¶h_                   lWE,
  W9NHW@`          JWM#B@]        @e     4WR@NGF^      'QL                  dRWl
   VMd*            "@BE@PM        'N      *UP"           VW,               JRSB;
  ,@F       j       `¶WK W,        ¶t                     XNt            _A@E#N
_JP       ,6&         "GLdM         XD,               _.g8NMA@k,_    _,gG#NMGR;
"Z      .JRER           'VMi     _jNB#W&_         _,j@E@W#Nl ¶MBGMNQGNQMG@QBW9
 ¶h   ,G@NRMBl            `"   ,d#R@M$F ¶Mg,_.gp&@@NEWVBWBMG  *QMN8R8SBN$E@WF
  Vb dW#R8QSRb,                 *YM@EQ,_ 'MENBW#NQMG#B@R@MW#l   "BM@QNENRQG'
   *WGS#MBMNEYL                    `^"*8M@Q@NRM#W@BWSNW@QBF"`     `^*@QBF^ [HFT]
    ^M@MW#Q9 ^Wt                           `^¶RQ@W8NQGP*`
     ¶Q#@P     Vk                            lA `"^`
      Y"       `MA                           J#,
                *R@,                        ,MQl
                 Y#Wk,                      GWM8L
                  W8RQSt,_                 AQ@MR#,
                  `@M@#SB@Mbm.,_          QNBW#NW
                    ¶QB8R8SBN$WNRM@#GNtwg@NMQR@B'
                     *MBQ#8R8QS@NE@WNBW#NQMG@NR;
                      `WGS#MBQ#R8QSB@NE@W#NQBW9
                        *OMW@QMNE@E@NRMW@QMB@*
                          `^"YQW@Q#SB#NE@EGP
                               `^"*8R@GBQF`


All Articles