Cómo cabe una demostración de Memories en 256 bytes


Introducción


¡Hola! Mi nombre es HellMood, y este artículo trata sobre un pequeño programa de MS DOS llamado Memories. Este programa tiene un tamaño de 256 bytes, ganó en la categoría de demostración de la competencia «PC 256 byte» «Revisión» en 2020, y también recibió el Premio del Público. Puede ver el video de la salida del programa aquí , y el video con la reacción de la audiencia en línea y los moderadores aquí . Descargue el lanzamiento y deje comentarios aquí. Este artículo presentará un análisis en profundidad del programa, hablará de referencias históricas y etapas de desarrollo. El artículo se publica en la wiki de sizecoding. No solo le permitirá comprender la estructura interna de Memories, sino que también lo ayudará a crear algo similar. ¡Exploralo! Si eres nuevo en la codificación de tamaño (escribir programas dentro del tamaño deseado) o en el ensamblador x86, se recomienda que comiences con los conceptos básicos de este wiki . Los principios son fáciles de entender, pero no es tan fácil descubrir los detalles de implementación.

Breve reseña


En este artículo hablaremos sobre la versión enviada al concurso para DosBox (256 bytes). El archivo también contiene versiones para FreeDos y Windows XP DOS, que al momento de escribir la publicación no funcionaba en todas las computadoras. Estas versiones alternativas se incluyeron en el archivo como prueba de concepto para mostrar que el programa no solo funciona en el emulador. En la categoría "PC 256 bytes" de la competencia "Revisión" de 2020, fue posible indicar "FreeDos" o "DosBox" como plataforma (esta última en una configuración específica). Como lo demuestran las versiones alternativas, de hecho, puede modificar la versión para DosBox para que funcione en FreeDos, MS DOS, WinXP y Win98, pero el artículo no se referirá a eso.

Sea como fuere, se están desarrollando versiones confiables para todas las plataformas y computadoras. En cuanto a posibles optimizaciones adicionales, solo hablaré sobre la versión enviada al concurso, aunque ya he encontrado varios lugares donde se puede optimizar el código. Como se trata de una demostración de la historia de los pequeños efectos, casi ninguno de los códigos es perfecto y puede reducirse aún más. Para no confundirme en diferentes versiones, solo hablaré sobre la competencia.

Historia de pequeños efectos.



Categorías de dimensión en http://www.pouet.net

Nosotros, sizcoders, razonamos en categorías de tamaño. Para MS DOS, estas categorías son 256b, 128b, 64b y 32b. Estos son los estándares de uno de los archivos más grandes de la escena de demostración www.pouet.net. No hay una categoría 16b, sin embargo, se pueden implementar muchos efectos pequeños en 16 bytes. Casi todos los efectos de "Memories" fueron escritos y optimizados por mí anteriormente, y básicamente su implementación fue un intento de reducir el tamaño existente del efecto, o crear algo similar, pero más pequeño. Por reducción de tamaño aquí se entiende su reducción a una de las siguientes categorías más pequeñas 2 ^ N. Por ejemplo, si el efecto se implementó en 33-64 bytes, entonces se reduce a 32 bytes o menos. Casi cada vez que logré "reducir" el efecto a una categoría inferior, envié el pequeño programa resultante a un evento dedicado al demoscene, en el que a los participantes remotos se les permitió participar en la categoría 256b, y / o publiqué el resultado en www.pouet.net. En esta sección, te presentaré los efectos, así como hablaré sobre su origen y autores.

Matriz de tableros de ajedrez



kasparov, 16 bytes. La

fuente de este efecto fue mi propio "Kasparov 16b" de 2018 ( enlace ). Parece (cita de las notas de la versión), "acabo de hacer este tablero, lo ajusté en 17 bytes, pero no fue muy hermoso hasta que se me ocurrió un truco ...". Anteriormente, ya se había implementado un efecto similar en 32 bytes: 2003 ew del crash del desarrollador. ( enlace ) En este caso, traté de implementar tableros de ajedrez "reales" con un cuadrado de 8x8 y cuadrados en blanco y negro reconocibles, así como la orientación correcta de los tableros de ajedrez individuales, es decir, la esquina inferior derecha (h1) debería haber sido blanca. Para que este efecto funcione con el marco global, en "Memorias" se tuvo que volver a implementar utilizando otra técnica: grabación en la pantalla; Además, la dirección de desplazamiento se ha cambiado para que difiera del efecto de "plano inclinado de desplazamiento".

Círculos de zoom


Se suponía que los círculos escalables (círculos con zoom) participaban en demopati como una introducción de 32 bytes, pero nunca lo hice. Este efecto no tiene un predecesor de 64 bytes, porque en la categoría 64b son posibles efectos mucho más complejos. Los círculos de zoom fueron el resultado de mi intento desesperado de ingresar a la categoría 32b con el efecto de un "túnel" redondo, para el cual mi registro personal aún era de 52 bytes ("Neontube" - 2016) ( enlace ), que, a su vez, se convirtió en una optimización del clásico 64 -byte efecto desarrolladores de "evolución constante" ryg / Farbrausch (2003) ( enlace ). En el procedimiento de círculos con zoom, la distancia y el ángulo se eliminan / ignoran, por lo que fue posible entrar en la categoría 32b.

Plano inclinado de desplazamiento



Floorcast, una versión de 32 bytes, una variación del

plano inclinado Scrolling, es uno de mis propios lanzamientos, el 2018 floorcast 32b. El efecto de "piso" tiene su propio historial en la codificación de tamaño y ha disminuido gradualmente de 256 bytes a 32 bytes. Las diferentes versiones difieren en el número de planos, en algunos se muestran dos planos, en otros, solo uno. En el lanzamiento de "floorcast 32b", decidí específicamente abandonar la textura "XOR", y en "Memories" lo usé nuevamente, enmascarándolo con el toque final en forma de "AND".

  • Versión rain_storm 2008 - 256 bytes - enlace
  • Versión 2008 de org_100h - 128 bytes - enlace
  • Versión 2013 Baudsurfer - 86 bytes - enlace
  • Versión Baudsurfer 2014 - 64 bytes - enlace
  • Versión HellMood 2018 - 32 bytes - Enlace

Cuadros de paralaje



Projektbeschreibung, 32 bytes

Lancé tableros de ajedrez de paralaje en 2018 como un efecto Projektbeschreibung de 32 bytes. En él, intenté reducir el tamaño de "Follow the light" ( enlace ) de "Digimind" (2006) o mi propia "Lucy" (2014) ( enlace ) a 32 bytes. Fuentes de inspiración muy útiles fueron Paralaxa de Rrrolas (32 bytes, 2007, enlace ) y Byteropolis de Sensenstahl (2013) ( enlace) La técnica de renderizado de Rrrolas estuvo muy cerca de mi decisión final, el código fue cambiado para corregir la ubicación de los planos, reemplazar los triángulos con tableros de ajedrez y mejorar los colores. Memories utilizó el esquema de color de la versión Digimind. Además, el efecto se modificó al máximo para reducir las deformaciones.

Rotozoómero de Sierpinski



colpinski, 16 bytes


rotativo, 32 bytes

Consiste en dos efectos: el rotozoómero y el efecto Sierpinski como textura. El efecto Sierpinski se basa en mi propio efecto Colpinski 16b 2013 ( enlace ), donde logré obtener el máximo posible con la función frag fsqrt. Este efecto no tiene predecesor, ya que se puede implementar directamente combinando X e Y, en lugar de usar un Sistema de Función Iterada o autómatas celulares. "Rotación y escalado" (rotozoomer) fue lanzado por mí en 2017 como la introducción 32b "rotastic" ( enlace ), se basa en las ideas de "ryg" del desarrollador Farbrausch (51 bytes, 2002, enlace ) y "Gargaj" del grupo "Conspiracy" "(49 bytes, 2002, enlace ).

Raycast túnel doblado



En una nueva era, la versión de 64 bits El

túnel doblado de Raycast se ha convertido en una versión modificada de mi propio lanzamiento de 64 bytes, "En una nueva era" (2018, enlace ). Los colores originales se reemplazaron con los colores de la paleta estándar, la geometría y los cálculos correspondientes se modificaron ligeramente para que el efecto de profundidad no se utilizara para calcular los valores de textura. Una versión separada de este efecto tiene un tamaño de 50 bytes. Las principales fuentes de inspiración para crear la versión de 64 bytes fueron dos introducciones de 128 bytes: "Spongy" de "TBC" (2009, enlace ) y "Wolf128" de Baudsurfer (2014, enlace ), y yo mismo desarrollé el algoritmo.

Océano noche a día



Ocean, versión 64b

El efecto del océano se basa en mi propio lanzamiento de 64 bytes de "Ocean" 2016 ( enlace ). Se cortó una generación separada de los colores y la música del original, ambos generadores no eran compatibles con el marco principal de Memories sin usar montones de bytes adicionales. El efecto especial del "amanecer" se debe a la implementación general de la estructura del marco. Hablaré de esto en el próximo capítulo.

Efecto de desvanecimiento


La transición entre los dos efectos es en sí misma un efecto que no tiene predecesores. Más bien, es una idea que ha evolucionado durante varios años, y tal vez fue implementada de manera similar por muchos otros. En resumen, cuando se calcula el marco, la posición de cada píxel se aleatoriza, y el tiempo que determina qué efecto se debe usar se cambia por este valor de aleatorización, que se redujo previamente. Esto le permite usar una paleta VGA estándar (imagen, fuente), en lugar de crear sus propios colores para una mezcla suave, lo que ahorra espacio.

El marco de mi "pequeño megademo"


Para combinar varios efectos pequeños en un "megademo", deben usar la misma técnica y, como máximo, no deben aplicar supuestos (con respecto al contenido de la memoria y los registros). También deben usar los mismos valores de tiempo y trabajar de acuerdo con el tiempo total. Tomó bastante tiempo preparar efectos individuales adecuados para el marco, y al principio mucho espacio extra. Cabe señalar que algunos de los efectos más impresionantes (a juzgar por la reacción de la audiencia y las opiniones en las redes sociales) no se pudieron incluir en la demostración debido al uso excesivo de la memoria. Después de que se hayan solucionado todos los efectos, podría comenzar a pensar en "poner entre corchetes" los cálculos repetidos con frecuencia, lo que ahorraría unos pocos bytes más.El marco realiza las siguientes acciones:

  • 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 tableros de ajedrez



matriz de tableros de ajedrez

El efecto más fácil para comenzar. Después de cambiar la cadena temporalmente, se aplica el patrón XOR clásico. Para crear la impresión de una cuadrícula de tablero de ajedrez, todos menos dos bits se configuran en color. El verdadero truco es cambiar al lugar "bueno" de la paleta. La parte del cuadro que se percibe como negra en realidad no es negra, sino que es la parte oscura de la paleta VGA estándar . Este cambio le permite dar a las células oscuras y claras la sensación de un tablero de ajedrez antiguo.

	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



zoom de círculos

La distancia D desde el punto (X, Y) al centro (0,0) es sqrt (X² + Y²). El marco previamente hace que DL contenga la coordenada centrada X, y DH = Y necesita estar centrado en el código. Para realizar la operación de cálculo de raíz cuadrada en x86, se requiere bastante código, pero de hecho puede omitirse. Con una elección cuidadosa de colores, la sensación de círculos convergentes sin una raíz cuadrada 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

Plano inclinado de desplazamiento



desplazamiento del plano inclinado

Este efecto se implementa de la siguiente manera: primero, para simular la distancia, se divide una constante grande por el número de línea Y. Luego, el valor obtenido se usa dos veces: a) multiplicado por el valor centrado de X yb) como compensación por el tiempo actual. Luego, estos resultados se combinan utilizando el patrón XOR, del cual se selecciona un patrón especial.

	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

Cuadros de paralaje



tableros de paralaje paralaje

Este es un tipo de reykasting (emisión de rayos) con geometría dinámica. Cada plano de los objetos se divide horizontalmente debido a la multiplicación de 16 bits con un signo y verticalmente debido a la operación de la lógica implícita con el número de columna. Además, se aplica distorsión indirecta para conectar los bordes de la malla resultante (4 áreas "sólidas", 4 áreas "transparentes" alternativamente). Si el haz cruza una de las áreas sólidas, el color se convierte en el número de iteración (+ el desplazamiento de la paleta en tonos de gris), y si no, el plano cambia al puntero de la pantalla, después de lo cual el proceso se repite hasta que se alcanza el número máximo de iteraciones.

	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

Rotozoómero de Sierpinski



rotozoomer de sierpinski


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

Para rotar con escala, generalmente se requieren funciones trigonométricas o sus buenos valores aproximados. Echemos un vistazo a la ecuación de rotación 2D habitual e interpretémosla de una manera especial para eliminar la trigonometría en los cálculos:

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

Si agregamos escala, se verá así:

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

Ahora digamos que no definimos z nosotros mismos, y ponemos 1 / cos (a) entre paréntesis:

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

Ahora reemplazamos tan (a) con una variable de tiempo, porque la función tangente tiende al infinito cuando se acerca a 180 °:

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

Si no nos preocupa la falta de capacidad para establecer el factor de escala y no controlamos el ángulo directamente, ahora podemos rotar de -180 ° a + 180 ° sin usar funciones trigonométricas. Como resultado de esto, el factor de escala resulta estar vinculado al tiempo T. La función para el coeficiente se muestra en la imagen, debido a que la escala se produce de un número infinitesimal a uno (el tamaño original) y de nuevo a infinitesimal. Se invirtió una buena cantidad de bytes en la decoración de este efecto, arreglando las compensaciones de tiempo, acelerando las animaciones, aumentando los píxeles de los triángulos de Sierpinski y creando hermosos colores, pero creo que valió la 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:

Raycast túnel doblado



túnel doblado raycast

Este es un tipo de túnel con rakasting de "hacia una nueva era" (ver arriba). Se ha publicado una descripción detallada de este efecto para un programa Essence similar en reddit. Me deshice de colores únicos, cambié la dirección de la inclinación y la geometría se volvió más cerrada para mejorar el rendimiento en computadoras más antiguas y en 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

Océano noche a día



Oceannight


oceanday

The Ocean Effect es un excelente ejemplo de "casualidad". Si cargamos el valor en la FPU como un entero y lo almacenamos como un punto flotante, y luego lo interpretamos nuevamente como un entero, obtendremos un gran patrón. Si combina esto con la división inversa, obtendrá un hermoso efecto de onda. Funciona estrechamente con el registro DX, que tiene un signo inverso en la posición que necesitamos, por lo que podemos separar fácilmente el cielo del mar. Sin embargo, el toque final es el color. De acuerdo con la estructura del marco global, el valor de AL se determina a la entrada de la función, contiene la dirección del efecto. Al barajar un poco el código, puede obtener el color del cielo "gratis" sin usar instrucciones, al igual que el color de la "transición al día", que también es la dirección del efecto. Y esta no es una feliz coincidencia. En otras versiones que no sean la versión de DosBox,El color del cielo por este motivo puede 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

Efecto de desvanecimiento


Por conveniencia, el efecto está aislado del marco global. De hecho, genera un número pseudoaleatorio a partir del puntero de la pantalla, y luego realiza un desplazamiento de tiempo por su valor escalado, después de lo cual causa el efecto deseado.

	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

Musica MIDI


Esta sección de código incrementa el valor del tiempo y crea un sonido. Al seleccionar el canal 3, podemos reutilizar la instrucción "cambiar canal de herramienta" como "RET". Al cambiar el código del efecto, puede crear un valor adecuado para el volumen, que guarda otro byte. Vale la pena considerar que esta parte del código solo funciona si el dispositivo MIDI ya está en modo UART; de lo contrario, deberá gastar otros tres bytes. Muchos de los espectadores y organizadores me dijeron que la melodía es un poco como los Encantamientos de Mike Oldfield , pero fue creada con una técnica muy simple. Desde Hypnoteye 2015, experimenté con MIDI de procedimiento y finalmente lancé un pequeño marco MIDI (64 bytes). El principio principal de su trabajo es saltar con un tono fijo en el espacio de tono y convertir valores altos a valores más bajos (por módulo de división). Las combinaciones simples de ancho de paso y valor de modulación pueden lograr efectos interesantes. Por ejemplo, stepwidth = 3 le permite obtener un acorde menor reducido de duración continua, y stepwidth = 4 o stepwidth = 6 - efectos tritonales. Con un valor de modulación bien elegido, estos patrones pueden crear secuencias. No he llevado a cabo un análisis teórico adecuado, sino que simplemente exploré el espacio de los tonos y noté sonidos interesantes.

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 lanzamiento 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

Bono - 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