Comment une démo Memories tient dans 256 octets


introduction


salut! Je m'appelle HellMood et cet article concerne un petit programme MS DOS appelé Memories. Ce programme a une taille de 256 octets, il a gagné dans la catégorie de la compétition «PC 256 octets» demoscene «Révision» en 2020, et a également reçu le prix du public. Vous pouvez regarder la vidéo de la sortie du programme ici , et la vidéo avec la réaction du public en ligne et des modérateurs ici . Téléchargez le communiqué et laissez des commentaires ici. Cet article présentera une analyse approfondie du programme, parlera des références historiques et des étapes de développement. L'article est publié sur le wiki du code de taille. Cela vous permettra non seulement de comprendre la structure interne des mémoires, mais vous aidera également à créer quelque chose de similaire. Explorez-le! Si vous êtes nouveau dans le sizecoding (écriture de programmes dans la taille souhaitée) ou dans l'assembleur x86, il est recommandé de commencer par les bases de ce wiki . Les principes sont faciles à comprendre, mais il n'est pas si facile de comprendre les détails de la mise en œuvre.

Bref avis


Dans cet article, nous parlerons de la version envoyée au concours pour DosBox (256 octets). L'archive contient également des versions pour FreeDos et Windows XP DOS, qui au moment de la rédaction de l'article ne fonctionnaient pas sur tous les ordinateurs. Ces versions alternatives ont été incluses dans l'archive comme preuve de concept pour montrer que le programme ne fonctionne pas uniquement dans l'émulateur. Dans la catégorie «PC 256 octets» du concours «Révision» de 2020, il était possible d'indiquer «FreeDos» ou «DosBox» comme plateforme (cette dernière dans une configuration spécifique). Comme le prouvent les versions alternatives, en fait, vous pouvez modifier la version de DosBox pour qu'elle fonctionne dans FreeDos, MS DOS, WinXP et Win98, mais l'article ne traitera pas de cela.

Quoi qu'il en soit, des versions fiables pour toutes les plateformes et tous les ordinateurs sont en cours de développement. En ce qui concerne d'éventuelles optimisations supplémentaires, je ne parlerai que de la version envoyée au concours, même si j'ai déjà trouvé plusieurs endroits où le code peut être optimisé. Comme il s'agit d'une démonstration de l'histoire des petits effets, presque aucun du code n'est parfait et il peut être encore réduit. Afin de ne pas se confondre dans les différentes versions, je ne parlerai que de la concurrence.

Histoire des petits effets



Catégories de dimensions sur http://www.pouet.net

Nous, sizcoders, raisonnons en catégories de tailles. Pour MS DOS, ces catégories sont 256b, 128b, 64b et 32b. Ce sont les standards d'une des plus grandes archives de la scène démo www.pouet.net. Il n'y a pas de catégorie 16b, cependant de nombreux petits effets peuvent être implémentés en 16 octets. Presque tous les effets de «Memories» ont été écrits et optimisés par moi plus tôt, et leur implémentation était essentiellement une tentative de réduire la taille existante de l'effet, ou de créer quelque chose de similaire, mais plus petit. Par réduction de taille, on entend ici sa réduction dans l'une des catégories plus petites suivantes 2 ^ N. Par exemple, si l'effet a été implémenté sur 33 à 64 octets, il est alors réduit à 32 octets ou moins. Presque chaque fois que j'ai réussi à «réduire» l'effet dans une catégorie inférieure, j'ai envoyé le petit programme résultant à un événement dédié à la démoscène, dans lequel les participants à distance étaient autorisés à participer dans la catégorie 256b, et / ou publié le résultat sur www.pouet.net. Dans cette section, je vais vous présenter les effets, ainsi que leur origine et leurs auteurs.

Tableau d'échiquiers



kasparov, 16 octets. La

source de cet effet était mon propre «Kasparov 16b» de 2018 ( lien ). Il semble (citation des notes de version), je "viens de faire ce conseil, l'ajuster en 17 octets, mais ce n'était pas très beau jusqu'à ce que je trouve une astuce ...". Auparavant, un effet similaire était déjà implémenté dans 32 octets: 2003 ew à partir du crash du développeur. ( lien ) Dans ce cas, j'ai essayé d'implémenter de «vrais» échiquiers avec un carré 8x8 et des carrés noirs et blancs reconnaissables, ainsi que l'orientation correcte des échiquiers individuels, c'est-à-dire que le coin inférieur droit (h1) aurait dû être blanc. Pour que cet effet fonctionne avec le cadre global, dans «Memories», il a dû être réimplémenté à l'aide d'une autre technique: l'enregistrement à l'écran; De plus, la direction de défilement a été modifiée de sorte qu'elle diffère de l'effet «plan incliné de défilement».

Cercles de zoom


Les cercles évolutifs (cercles de zoom) étaient censés participer aux demopati en tant qu'intro de 32 octets, mais je ne l'ai jamais fait. Cet effet n'a pas de prédécesseur de 64 octets, car dans la catégorie 64b, des effets beaucoup plus complexes sont possibles. Les cercles de zoom étaient le résultat de ma tentative désespérée d'entrer dans la catégorie 32b avec l'effet d'un «tunnel» rond, pour lequel mon record personnel était toujours de 52 octets («Neontube» - 2016) ( lien ), qui, à son tour, est devenu une optimisation du classique 64 -effet octet "évolution constante" développeurs ryg / Farbrausch (2003) ( lien ). Dans la procédure des cercles de zoom, la distance et l'angle sont éliminés / ignorés, ce qui a permis de tomber dans la catégorie 32b.

Plan incliné de défilement



Floorcast, une version 32 octets, une variante du

plan incliné Scrolling, est l'une de mes propres versions, le floorcast 32b 2018. L'effet du «revêtement de sol» a sa propre histoire dans le dimensionnement et a progressivement diminué de 256 octets à 32 octets. Différentes versions diffèrent par le nombre d'avions, dans deux plans environ, dans d'autres - un seul. Dans la version «floorcast 32b», j'ai spécifiquement décidé d'abandonner la texture «XOR», et dans «Memories», je l'ai utilisée à nouveau, en la masquant avec la touche finale sous la forme de «ET».

  • 2008 rain_storm version - 256 octets - lien
  • Version 2008 de org_100h - 128 octets - lien
  • Version Baudsurfer 2013 - 86 octets - lien
  • Version Baudsurfer 2014 - 64 octets - lien
  • Version HellMood 2018 - 32 octets - Lien

Damier Parallax



Projektbeschreibung, 32 octets

J'ai publié des échiquiers de parallaxe en 2018 en tant qu'effet Projektbeschreibung de 32 octets. Dans ce document, j'ai essayé de réduire la taille de «Follow the light» ( lien ) de «Digimind» (2006) ou de ma propre «Lucy» (2014) ( lien ) à 32 octets. Des sources d'inspiration très utiles ont été Paralaxa de Rrrolas (32 octets, 2007, lien ) et Byteropolis de Sensenstahl (2013) ( lien) La technique de rendu Rrrolas était très proche de ma décision finale, le code a été modifié pour corriger l'emplacement des avions, remplacer les triangles par des échiquiers et améliorer les couleurs. Les souvenirs utilisaient le jeu de couleurs de la version Digimind. De plus, l'effet a été modifié au maximum pour réduire les déformations.

Sierpinski rotozoomer



colpinski, 16 octets


rotastic, 32 octets

Il se compose de deux effets: rotozoomer et effet Sierpinski comme texture. L'effet Sierpinski est basé sur mon propre effet Colpinski 16b 2013 ( lien ), où j'ai réussi à obtenir le maximum possible avec la fonction frag fsqrt. Cet effet n'a pas de prédécesseur, car il peut être implémenté directement en combinant X et Y, plutôt qu'en utilisant un système de fonction itéré ou des automates cellulaires. «Rotation and scaling» (rotozoomer) est sorti par moi en 2017 sous le nom d'intro 32b «rotastic» ( lien ), il est basé sur les idées de «ryg» du développeur Farbrausch (51 octets, 2002, lien ) et «Gargaj» du groupe «Conspiracy» "(49 octets, 2002, lien ).

Tunnel cintré Raycast



Dans une nouvelle ère, version 64 bits Le

tunnel cintré de raycast est devenu une version modifiée de ma propre version de 64 octets, «Dans une nouvelle ère» (2018, lien ). Les couleurs d'origine ont été remplacées par les couleurs de la palette standard, la géométrie et les calculs correspondants ont été légèrement modifiés de sorte que l'effet de profondeur n'a pas été utilisé pour calculer les valeurs de texture. Une version distincte de cet effet a une taille de 50 octets. Les principales sources d'inspiration pour créer la version 64 octets étaient deux intros de 128 octets: «Spongy» de «TBC» (2009, lien ) et «Wolf128» de Baudsurfer (2014, lien ), et j'ai développé l'algorithme moi-même.

Océan nuit au jour



Ocean, version 64b

L'effet océan est basé sur ma propre version de 64 octets de «Ocean» 2016 ( lien ). Une génération séparée des couleurs et de la musique de l'original a été supprimée, les deux générateurs n'étaient pas compatibles avec le cadre principal de Memories sans utiliser des tas d'octets supplémentaires. L'effet spécial de «l'aube» est dû à la mise en œuvre globale de la structure du cadre. J'en parlerai dans le chapitre suivant.

Effet de décoloration


La transition entre les deux effets est en soi un effet qui n'a pas de prédécesseurs. Il s'agit plutôt d'une idée qui a évolué sur plusieurs années et qui a peut-être été mise en œuvre de la même manière par de nombreuses autres. En bref, lors du calcul de la trame, la position de chaque pixel est randomisée, et le temps qui détermine quel effet doit être utilisé est décalé de cette valeur de randomisation, qui était auparavant réduite. Cela vous permet d'utiliser une palette VGA standard (image, source), plutôt que de créer vos propres couleurs pour un mixage fluide, ce qui économise de l'espace.

Le cadre de mon "petit mégademo"


Pour combiner plusieurs petits effets en un seul «mégademo», ils doivent utiliser la même technique et tout au plus ne doivent pas appliquer d'hypothèses (concernant le contenu de la mémoire et des registres). Ils sont également tenus d'utiliser les mêmes valeurs de synchronisation et de travailler en fonction du temps total. Il a fallu beaucoup de temps pour préparer des effets individuels adaptés au cadre, et d'abord beaucoup d'espace supplémentaire. Il convient de noter que certains des effets les plus impressionnants (à en juger par la réaction des téléspectateurs et des opinions sur les réseaux sociaux) n'ont pas pu être inclus dans la démo en raison de l'énorme utilisation excessive de la mémoire. Une fois tous les effets corrigés, je pouvais commencer à penser à «mettre entre parenthèses» des calculs fréquemment répétés, ce qui permettrait d'économiser quelques octets de plus.Le cadre effectue les actions suivantes:

  • 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


Tableau d'échiquiers



tableau d'échiquiers

L'effet le plus simple pour commencer. Après avoir décalé temporairement la chaîne, le motif XOR classique est appliqué. Pour créer l'impression d'une grille d'échiquier, tous les bits sauf deux sont définis en couleur. Le vrai truc est de passer au «bon» endroit de la palette. La partie du cadre qui est perçue comme noire n'est en fait pas noire, mais c'est la partie sombre de la palette VGA standard . Ce changement vous permet de donner aux cellules sombres et claires la sensation d'un échiquier ancien.

	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

Cercles de zoom



cercles de zoom

La distance D du point (X, Y) au centre (0,0) est sqrt (X² + Y²). Le cadre fait précédemment que DL contient la coordonnée centrée X, et DH = Y doit être centré dans le code. Pour effectuer l'opération de calcul de racine carrée dans x86, beaucoup de code est requis, mais en fait il peut être omis. Avec un choix minutieux de couleurs, la sensation de cercles convergents sans racine carrée semble assez convaincante.

	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

Plan incliné de défilement



défilement plan incliné

Cet effet est mis en œuvre comme suit: d'abord, pour simuler la distance, une grande constante est divisée par le numéro de ligne Y. Ensuite, la valeur obtenue est utilisée deux fois: a) multipliée par la valeur centrée de X et b) compensée par l'heure actuelle. Ces résultats sont ensuite combinés à l'aide du modèle XOR, à partir duquel un modèle spécial est sélectionné.

	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

Damier Parallax



planches à damier de parallaxe

Il s'agit d'une sorte de reykasting (émission de rayons) à géométrie dynamique. Chaque plan des objets est divisé horizontalement en raison d'une multiplication de 16 bits avec un signe et verticalement en raison de l'opération de logique implicite avec le numéro de colonne. De plus, une distorsion indirecte est appliquée pour relier les bords du maillage résultant (4 zones «solides», 4 zones «transparentes» en alternance). Si le faisceau traverse l'une des zones pleines, la couleur devient le numéro d'itération (+ le décalage de la palette dans les tons de gris) et sinon, le plan passe au pointeur d'écran, après quoi le processus se répète jusqu'à ce que le nombre maximal d'itérations soit atteint.

	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


Graphique 1 / cos (atan (x)), créé à l'aide de www.google.com

Pour effectuer une rotation avec mise à l'échelle, des fonctions trigonométriques ou leurs bonnes valeurs approximatives sont généralement requises. Jetons un coup d'œil à l' équation de rotation 2D habituelle et interprétons-la d'une manière spéciale pour nous débarrasser de la trigonométrie dans les calculs:

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

Si nous ajoutons une mise à l'échelle, cela ressemblera à ceci:

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

Supposons maintenant que nous ne définissions pas z nous-mêmes et mettons 1 / cos (a) entre crochets:

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

Maintenant, nous remplaçons tan (a) par une variable de temps, car la fonction tangente tend vers l'infini lorsqu'elle s'approche de 180 °:

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

Si nous ne nous soucions pas du manque de possibilité de définir le facteur d'échelle et que nous ne contrôlons pas directement l'angle, nous pouvons maintenant faire une rotation de -180 ° à + 180 ° sans utiliser de fonctions trigonométriques. En conséquence, le facteur de mise à l'échelle s'avère être lié au temps T. La fonction du coefficient est affichée dans l'image, en raison de laquelle la mise à l'échelle se produit d'un nombre infinitésimal à un (la taille d'origine) et de retour à l'infinitésimal. Beaucoup d'octets ont été dépensés pour décorer cet effet, corriger les décalages temporels, accélérer les animations, augmenter les pixels des triangles de Sierpinski et créer de belles couleurs, mais je pense que cela en valait la peine.

	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:

Tunnel cintré Raycast



tunnel cintré de raycast

Il s'agit d'un type de tunnel avec rastillage de "dans une nouvelle ère" (voir ci-dessus). Une description détaillée de cet effet pour un programme Essence similaire a été publiée sur reddit. Je me suis débarrassé des couleurs uniques, j'ai changé la direction de l'inclinaison et la géométrie est devenue plus fermée pour améliorer les performances sur les ordinateurs plus anciens et dans 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éan nuit au jour



Oceannight


Oceanday

The Ocean Effect est un excellent exemple de «coup de chance». Si nous chargeons la valeur dans le FPU sous forme d'entier et la stockons sous forme de virgule flottante, puis la réinterprétons comme un entier, nous obtenons un excellent modèle. Si vous combinez cela avec la division inverse, vous obtenez un bel effet d'onde. Il fonctionne en étroite collaboration avec le registre DX, qui a un signe d'inversion dans la position dont nous avons besoin, afin que nous puissions facilement séparer le ciel de la mer. Cependant, la touche finale est la couleur. Conformément à la structure du cadre global, la valeur de AL est déterminée en entrée de la fonction, elle contient l'adresse de l'effet. En mélangeant un peu le code, vous pouvez obtenir la couleur du ciel "gratuitement" sans utiliser d'instructions, tout comme la couleur de la "transition vers le jour", qui est également l'adresse de l'effet. Et ce n'est pas une heureuse coïncidence. Dans d'autres versions que la version DosBox,la couleur du ciel pour cette raison peut varier.

	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

Effet de décoloration


Pour plus de commodité, l'effet est isolé du cadre global. En fait, il génère un nombre pseudo-aléatoire à partir du pointeur d'écran, puis effectue un décalage temporel de sa valeur mise à l'échelle, après quoi il provoque l'effet souhaité.

	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

Musique MIDI


Cette section de code incrémente la valeur de temps et crée un son. En sélectionnant le canal 3, nous pouvons réutiliser l'instruction «changer le canal de l'outil» comme «RET». En décalant le code d'effet, vous pouvez créer une valeur appropriée pour le volume, ce qui économise un autre octet. Il convient de noter que cette partie du code ne fonctionne que si l'appareil MIDI est déjà en mode UART, sinon vous devrez dépenser trois autres octets. Beaucoup de spectateurs et d'organisateurs m'ont dit que la mélodie est un peu comme les Incantations de Mike Oldfield , mais elle a été créée avec une technique très simple. Depuis Hypnoteye 2015, j'ai expérimenté avec du MIDI procédural et finalement publié un petit framework MIDI (64 octets). Le principe principal de son travail est de sauter avec une hauteur fixe dans l'espace tonal et de convertir les valeurs hautes en basses (par division modulo). Des combinaisons simples de stepwidth et de modvalue peuvent produire des effets intéressants. Par exemple, stepwidth = 3 vous permet d'obtenir un accord mineur réduit de façon continue et stepwidth = 4 ou stepwidth = 6 - effets tritonaux. Avec une valeur de module bien choisie, ces motifs peuvent créer des séquences. Je n'ai pas effectué une analyse théorique appropriée, mais j'ai simplement exploré l'espace des tons et noté des sons intéressants.

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

Code de version complète


; "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

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