En la Flashparty del año 2020, participé en la categoría 256 bytes combinados y mi programa o entry
compitió con otras producciones increíbles. En este artículo comento el código fuente en detalle
de la entry
original que originalmente tenía 27 bytes
, y al final un par de cambios para reducir el tamaño del programa aún más y una pequeña
modificación a la selección de colores para hacer el efecto más "dinámico" (?)
Aquí abajo está el video de la versión final
, reducida a 24 bytes
y con más colores locos
.
Sizecoding
Para dar una definición aproximada sobre que es el sizecoding
vamos a decir que es el arte
de crear programas lo más pequeños posibles para los tipos más populares de CPUs.
En particular, nos referimos a que esta categoría de programas tienen 256 bytes
o menos, creados
típicamente por miembros de la Demoscene como una manera de demostrar habilidad como programador.
El tamaño de estos pequeños programas se mide en el tamaño total de sus opcodes y generalmente
son ejecutables de la arquitectura objetivo: .COM
para MS-DOS
, PRGs
para Commodore 64
, etc…
FlashParty
Citando directamente al sitio de Flashparty: es un evento basado en el modelo Demoparty que tiene su origen en la sub-cultura de la Demoscene. La Demoscene es un movimiento artístico que usa tecnologías informáticas a nivel muy eficiente e ingenioso para la creación de piezas de arte: código, animaciones, música y gráficos. Una Demoparty es la reunión de artistas en un lugar físico para presentar sus obras y participar en competencias que se dirimen por votación del público presente.
La entry
Para obtener el zip
original enviado a la competencia, podés descargarlo de el siguiente link de scene.org.
A continuación, voy a mostarles el código fuente del mismo programa que envié, pero con muchísimos comentarios.
mov al,0x13
int 0x10
Con esto seteamos el modo de video en MS-DOS
en el Modo 13h
, de 320x200 con 256 colores
.
O sea, pasamos del modo normal del sistema operativo que es el modo texto a un modo gráfico para
poder empezar a dibujar algo.
les bp, [bx]
Acá les dejo un link a la instrucción LES. En resumen, modificamos el segmento
ES
para que apunte a la memoria de video mapeada en A000:0000
. Pero en verdad estamos
haciendo un truco. Tradicionalmente esto se hace con las instrucciones push 0xa000
y pop es
.
Pusheamos en el stack
el segmento de memoria de la memoria de video y popeamos este valor
en el registro de segmento ES
. Estas instrucciones ocupan en total 4 bytes.
PUSH
ocupa un byte. El word 0xa000
ocupa 2 bytes, y pop es
ocupa un byte más, haciendo
el total de 4 bytes.
Para ahorrarnos un byte, usamos la instrucción LES, que escencialmente hace lo mismo, aprovechando
los valores de inicialización de registros en el momento que empieza a ejecutarse un programa en
MS-DOS. ES
en vez de valer 0xa000
va a valer un byte menos: 0x9fff
y todo lo que dibujemos
en la memoria de video va a estar offseteado por un byte. No es muy terrible, si tenemos esto
siempre en cuenta. El tradeoff es este offset de un byte para ganar un byte de espacio
en nuestro programa.
loop:
cwd ; "clear" DX (if AH < 0x7F)
mov ax,di ; get screen position into AX (Y)
mov bx,320 ; get screen width into BX
div bx ; divide, to get row and column (DX = X)
; divide DX:AX por BX, resultado AX = Resultado, DX = Resto
Con estas instrucciones lo que hacemos es lograr obtener la posición X
e Y
de la pantalla, avanzada por el puntero DI
que se incrementa cuando más tarde
en el programa usamos la instrucción stosb
.
sierpinski:
and ax, dx ; AND method
mov al, byte [fs:046ch] ; get color from timer
jnz .not_draw
add al, 127 ; set a different color from timer
.not_draw:
stosb ; guardar AL en ES:[DI], DI++
jmp short loop
De todas las cosas que se pueden dibujar en un espacio altamente reducido, hay un método llamado
el método AND del Triángulo de Sierpinski
. Prácticamente la instrucción que lo dibuja es and ax, dx
dentro del loop que
itera X e Y en el espacio de memoria.
Para obtener el dato del color a dibujar, usamos la posición de memora fs:046ch
que es donde
está el timer de MS-DOS, que nos da un valor incremental en cada iteración del loop. Para darle
variedad y que se pueda ver la diferencia entre el triángulo de Sierpinski y el fondo,
cambiamos el color del fondo sumando 127 bytes al registro AL
cuando and ax, dx
es igual a cero.
Optimizando la entry: A 24 bytes, con más colores!
Este programa es chiquito. Pero se puede hacer aún mas pequeño.
Removí las instrucciones para calcular X e Y e hice uso de una técnica llamada rrrola trick.
Para que haya más variación de colores, cambié la instrucción add al, 127
por xor al, dl
.
Es importante aclarar que al ahorrarnos estos 3 bytes con el rrrola trick, las coordenadas X e Y
son aproximadas y hay una deformación en el ploteo del Triángulo de Sierpinski
, por lo que no se
ve exactamente como la entry original. Hay como una especie de zoom in
medio feo, pero hey:
son 3 bytes menos! ;D
; coded by h4nz or toshi or 0x705h
; fp2020 - 24 bytes.
; final version after flashparty 2020
; this is LESS shitty, but still ;D
; viva peron
mov al,0x13
int 0x10
les bp, [bx] ; replaces push 0xa000 / pop es
; but sets ES=9fff == A000 - 1
loop:
mov ax, 0xcccd ; rrrola trick
mul di
sierpinski:
and dh, dl ; AND method with rrrola trick
mov al, byte [fs:046ch] ; get color from timer
jnz .not_draw
xor al, dl ; make the sierpinski pixels
; go rainbow and crazy
.not_draw:
stosb
jmp short loop
Referencias
- Sizecoding
- Flashparty
- Triángulo de Sierpinski: el método AND
- Demoscene
- Valores por defecto de los registros al ejecutarse un programa en
MS-DOS
- rrrola trick