Creación de 1k intro Chaos para ZX-Spectrum


Inicialmente, no planeaba hacer una demostración en Chaos Constrictions 2018 , pero 2-3 semanas antes de cc me di cuenta de que no podías ir a una fiesta de demostración con las manos vacías, y decidí escribir una breve demostración para 386 / EGA / DOS .

Habiendo compilado en Turbo-C en DOS mi lib AnotherGraphicsLibrary , que encajaba idealmente en la estructura del plan de ritmo del modo EGA , me decepcionaron los frenos, especialmente los frenos EGA . La demostración en la forma en que me gustaría verlo, durante este período muy limitado, fue imposible de hacer.

Sin embargo, no podía rendirme y no hacer nada. Y luego recordé que siempre había querido participar en los concursos de demostración de ZX-Spectrum . Y como obtuve dos reales de 48k en el último año, podría disfrutar creando una demo. Por cierto, para mí, lo más importante al escribir una demostración es probar en la vida real, los emulsionantes no dan tanto placer del proceso, es una sensación maravillosa cuando, después del próximo cambio en el código, subes la demostración a la vida real, y ves cómo el hardware real baraja los bytes en efecto de dibujo de memoria.

Como solo tengo 48k de reales, decidí hacer una demostración de 48k . Y debido al tiempo limitado y la falta de desarrollos, la elección recayó en la creación de una introducción de 1k (demostración de solo 1 kilobyte, o 1024 bytes).

La última vez que utilicé el z80 asm en EmuZWin , un gran emulador con ensamblador incorporado. Pero desafortunadamente EmuZWin no funciona en nada más alto que Windows XP , o tiene errores.
Después de considerar varias opciones, me detuve en un montón de programas Unreal + sjAsm + Notepad ++ , que, en mi opinión, son mucho más fáciles de perder que EmuZWin por conveniencia, pero están vivos, a diferencia de esto.

Mientras escribía esta introducción, realicé, directamente en la fuente, un registro de desarrollo, basado en el cual se escribió el siguiente texto:

Hola mundo

¿Qué debo escribir primero, teniendo casi cero experiencia en el z80 asm ? Así es, la salida del sprite 5x5 es familiar o de 40x40 píxeles, para uno de los efectos (irónicamente, en el futuro, para encajar en 1k, esta parte inacabada fue expulsada de la introducción).

Sorprendentemente, fue bastante fácil hacerlo desde cero usando la etiqueta de dirección de línea pregenerada usando Down HL .

Oh, los registros de índice, qué tan convenientes son, pero que son más lentos, literalmente consumen barras. Tuve que tirar su uso de un montón de lugares.

Todavía aquí, al principio, me encontré con increíbles fallas sjAsm , o más bien su última versión. El disrealismo en Unreal mostró una secuencia de comandos absolutamente loca. Descargué la penúltima versión, de alguna manera ya era posible vivir con ella.



Está claro que no puedes meter cualquier cantidad adecuada de sprites predibujados en 1k, así que decidí generarlos dinámicamente. Y no de todos modos, pero dibuja usando polígonos.

Por lo tanto, el segundo procedimiento que escribí fue el procedimiento para dibujar un triángulo. En su mayor parte, fue una portabilidad de su propio código escrito en C. Con la única diferencia global de la versión C , las líneas de exploración poligonales se generan primero, y solo luego se dibujan en estas líneas de exploración.

Después de los lenguajes de alto nivel, obtienes algo de jr aka goto :

.sort_me_please: ld de,(tr_x2) ld bc,(tr_x0) ld a,d cp b jr nc,.skip1 ld (tr_x2),bc ld (tr_x0),de .skip1: ld de,(tr_x1) ld bc,(tr_x0) ld a,d cp b jr nc,.skip2 ld (tr_x0),de ld (tr_x1),bc jr .sort_me_please .skip2: ld de,(tr_x2) ld bc,(tr_x1) ld a,d cp b jr nc,.skip3 ld (tr_x2),bc ld (tr_x1),de jr .sort_me_please .skip3: 

Me sorprendió un poco el hecho de que resultó en un tiempo razonable escribir un draw_triangle en z80 asm , que dibuja un polígono píxel por píxel y sin agujeros al conectar polígonos.


Hola triangulos

Partículas



Debido a la presencia de un generador de etiquetas de línea de pantalla, escribí un procedimiento de salida de punto bastante curvo y lento usando esta etiqueta. El procedimiento tiene dos puntos de entrada: solo el inverso del píxel y el inverso del píxel con relleno con TINTA + BRILLO + y el color especificado en uno de los registros.

En la etapa de crear un efecto con partículas, descubrí que el ejemplo con las estructuras de los ejemplos en el wiki sjAsm simplemente no funciona. Google sacó a relucir un tema del sitio zx-pk.ru , donde se describe este problema, y ​​no hay solución, ja, está bien, otra falla.

Decidí hacer todo claramente: actualizar las coordenadas independientemente de la representación, por interrupción. Sí ... más bytes para generar una tabla de interrupciones.

Había pocas partículas en esta etapa, y apenas cabían en el marco; esto es por cierto la lentitud de mi procedimiento para generar el punto%)) Pero usar una tabla común con sprites no me permitió tirarlo y prepararlo, porque Esto ahorró mucho espacio en la necesidad de un solo generador de tabla. Y mi amor por las bicicletas también :)

Muy pocas partículas ... aumentaron su número, pero ahora el renderizado se ha engordado a dos cuadros.


Pruebas en Peters WS64 , que obtuve en el último cc y reparé este invierno :)

Por cierto, ya en esta etapa, los puntos se convirtieron en puntos horizontales en negrita 2: 1 , como en el Commodore 64 . Esto sucedió debido al pequeño número inicial de partículas, y mi insatisfacción con el hecho de que eran bastante invisibles cuando se ejecutan en la vida real. Resolvió el problema reemplazando la placa

 db 128,64,32,16,8,4,2,1; 

en

 db 192,192,96,24,12,6,3,3; 

lo que empeoró la precisión del posicionamiento e hizo que el vuelo fuera un poco irregular, pero aumentó la visibilidad. Aquí, también jugó en la mano que las partículas cayeron de arriba a abajo, su visión manchada verticalmente.

Incidentalmente, las partículas caen cada una a su propia velocidad aleatoria, y se usan dos bytes para almacenar las coordenadas Y.

Sprites

Tiré la pieza inacabada de la parte del sprite, dándome cuenta de que no podía encajar en 1k con ella.

Además, ya faltaba espacio, así que recordé el maravilloso artículo de Introspek sobre empacadores, elegí zx7 como empacador, que ahorró alrededor de 110 bytes. Por cierto, ¿tal vez alguien conoce un empacador más adecuado para una introducción de 1k?

Construcciones del caos



Como ya tenía un procedimiento para generar un polígono, me pareció una buena idea dividir el logotipo de cc en polígonos y mostrarlos uno por uno.

Escribí un código de prueba que muestra varios polígonos, todo funcionó como pretendía, está bien.

Para verificar si mi idea encaja o no en 1k , generó un cierto número de polígonos aleatorios, según las estimaciones, un número suficiente para el logotipo, y lo llevó a la fuente. Compilado, y se aseguró de que - excelente - introducción, en este formulario, se ajusta al límite de 1024 bytes.


Foto de la vida, ¿reconoces el dispositivo en la mesa? :)))

Decidí probar una vez más la introducción semiacabada, ya con polígonos y un empacador, cargado a real y ... obtuve un reinicio. En primer lugar, comencé a pecar porque olvidé inicializar la memoria en algún lugar, desde el cual, donde todo funciona bien en el emulador 0x00 , en la vida real hay basura que causa un reinicio.

Nada es mejor para encontrar un lugar problemático que el método de media división y la detención que no pude encontrar.

Llevado a cabo con un reinicio real durante dos horas, no había forma de localizar el problema técnico ...
Resultó que no estaba en mi código, estaba en el potenciador de sonido incluido en el teléfono desde el que cargué WAV. Un mejorador de un flujo de bits en un archivo WAV generó un flujo de delirio.

Tan pronto como lo apagué, todo funcionó mágicamente.

Describió el logotipo en el editor de gráficos de errores de Greenpixel, lo dividió en un montón de triángulos y condujo manualmente las coordenadas a la fuente. Después de haber rellenado completamente el logotipo de Chaos Constructions y ejecutarlo en la vida real, me alegré, se veía bastante bien.


La primera visualización de logotipo en real

Sin embargo, rellené muy poco los polígonos aleatorios, y en el logotipo real, había un límite de 1k por 150 bytes. Y esto a pesar del hecho de que el efecto de partículas aún no se había completado, y la transición entre las partes fue nítida.

Ir a la cama ese día, debido al alboroto con problemas técnicos, resultó tan temprano como las 8 a.m. 8)

Y sí, traté de optimizar el tamaño almacenando las coordenadas y los índices de vértice por separado, pero al empacador no le gustó mucho, lo que hizo que el tamaño solo aumentara.

Final



Se me ocurrió cómo diversificar la salida del logotipo, para esto no se necesitaron más que dos etiquetas de píxeles más:

 fake_points1: db 1,2,4,8,16,32,64,128; 1 ch fake_points2: db 32,8,128,2,16,1,64,4; 2 ch normal_points: db 128,64,32,16,8,4,2,1; 3 ch 

Eso dio un efecto genial al aumentar el detalle de renderizar el logotipo, o la falta inicial de detalles y un aumento gradual de la nitidez.

Y finalmente, hice la versión final con todas las transiciones, arrojando un montón de todo. En el proceso, encontré una falla en el procedimiento para dibujar un triángulo: si los dos vértices tienen las mismas coordenadas Y , entonces el triángulo se dibuja torcidamente (parece dividir entre 0 al calcular dx ), evitado con un truco temporal.


Probar la versión final en Leningrado 48

Optimización de tamaño


Los dígitos dobles son "bytes adicionales"

94 bytes adicionales ...

Ha llegado el momento de eliminar la preservación / restauración "cultural" de los registros de los procedimientos de entrada / salida, lejos de donde sea necesario, pero la memoria se está comiendo.

86 bytes ...

Probado en la vida real, ¡funciona! Él eliminó un poco más de memoria, ¡al mismo tiempo reparó un error dividido por 0 - 63 bytes!

57 bytes ...

Se agregó bucle.

 random_store: start: 

Por cierto, el bucle se realiza en el nivel de desembalaje, como Como fuente de entropía para el RNG , se usaron varios bytes del código de inicialización (para ahorrar espacio), que el RNG estropeó durante la primera parte. Por lo tanto, para el bucle, después del final de la introducción, hay ret , bueno, y luego otro desempaque y transición al código desempaquetado ...

No fue posible deshacerme de los últimos 48 bytes, tuve que cortar el controlador de interrupciones, ¡pero Hurra! Relleno! Incluso quedaba 1 byte extra.

Y dado que no hay interrupciones, se puede puntuar para los cuadros, y es difícil ver la sección transversal del haz en un píxel en el primer efecto, por lo que aumenté el número de partículas por ojo, con un compromiso entre la velocidad y el entretenimiento.

Stinged aún más fuerte, moviendo estúpidamente código y datos de un lugar a otro, ayudando al empacador. Lo cual tomó algo de tiempo :)

Esto liberó unos 10 bytes en los que pegué una parodia del sonido. Khk, khe, sonido: esto es en algunos lugares, por el oído insertado:

  ifdef UseSound ld a,d and #10 out (#FE),a endif 

No "clavé" el sonido, pero hice una definición, obteniendo así dos versiones de la introducción, con y sin sonido. En eso sin sonido, en bytes "extra" abarrotados

 db 'e','r','r','o','r' 

Recolecté trd and tap , y lo subí todo al sitio web de cc .

¡Hurra! ¡Estoy participando en demopati!

Epílogo

Resultó ser divertido con el sonido, alguien en el patlace dijo "sonido claro", alguien me miró de manera extraña, y en pouet encontré lo siguiente:



Y esto:



En general, no entendí si a alguien le gustó el sonido de 10 bytes o no :)

Y lo último: es una pena que la competencia de 1k no haya tenido lugar este año, el trabajo resultó ser decente, en mi opinión, pero es difícil competir con 640k , pero realmente quería competir.

Escribe demos, escribe 1k!

Y esto es lo que terminó (ps, cuida tus oídos):



Versión silenciosa:

Source: https://habr.com/ru/post/es421837/


All Articles