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