Creating 1k intro Chaos for ZX-Spectrum
Initially, I did not plan to do a demo on Chaos Constrictions 2018 , but 2-3 weeks before cc I realized that I couldn’t go to demopati with empty hands, and decided to write a small demo for 386 / EGA / DOS .
Compiled into a Turbo-C under DOS his libu AnotherGraphicsLibrary , which is perfect to go to bitplanovuyu structure EGA mode, I was disappointed, from the brakes, especially the brake EGA . It was impossible to make a demo in the form in which I would like to see it, for this very limited period of time.
However, I could not give up and not do anything. And then I remembered that I had long wanted to take part inZX-Spectrum contests demo. And so, as for the last year I had as many as two 48k real, I could get some pleasure from creating a demo. By the way - for me the most important thing in writing a demo is testing for real, emulsifiers do not give such pleasure from the process, this is a very wonderful feeling when after another change in the code you upload a demo to real, and see how a real piece of paper shuffles bytes in memory rendering effect.
Since I only have 48k from the reals, I decided to make a demo for 48k . And due to the limited timeframe and the absence of any developments, the choice fell on the creation of 1k intro (a demo of only 1 kilobyte, or 1024 bytes).
Last time z80 asm I poked in EmuZWin - a wonderful emulator with a built-in assembler. But unfortunately EmuZWin on anything above Windows XP does not work, or is buggy.
Having considered various options, he stopped at a bunch of Unreal + sjAsm + Notepad ++ programs , which, in my opinion, are much less comfortable for EmuZWin , but unlike him, they are still alive.
During the writing of this intro, I conducted, right in the source code, a development log, based on which the following text was written:
What should I write first, having almost zero experience in z80 asm ? That's right, sprite output of 5x5 familiarity or 40x40 pixels, for one of the effects (ironically, later, in order to fit into 1k this unfinished part was thrown out of the intro).
Amazingly, it was quite simple to do it from scratch, using a pre-generated label of row addresses using Down HL .
Oh, index registers, which ones are convenient, but which are slow, they literally survive ticks. I had to throw out their use of a pile of places.
Even here, at the very beginning, I came across the incredible glitches of sjAsm , or rather its latest version. Disasm in Unrealshowed absolutely crazy sequence of commands. I downloaded the penultimate version - it was already possible to live with it somehow.
It is clear that a sufficient amount of pre-drawn sprites in 1k is not thrust, so I decided to generate them dynamically. And not anyhow how, and to draw with the help of polygons.
Therefore, the second procedure I wrote was the procedure for drawing a triangle. For the most part it was porting its the same code written in C . With the only global difference from the C version, the scanners of the polygon are first generated, and only then are they drawn on these scanlines.
After high-level languages, you get some pleasure from jr aka goto :
.sort_me_please: ldde,(tr_x2) ldbc,(tr_x0) lda,dcpbjrnc,.skip1ld (tr_x2),bcld (tr_x0),de.skip1: ldde,(tr_x1) ldbc,(tr_x0) lda,dcpbjrnc,.skip2ld (tr_x0),deld (tr_x1),bcjr.sort_me_please.skip2: ldde,(tr_x2) ldbc,(tr_x1) lda,dcpbjrnc,.skip3ld (tr_x2),bcld (tr_x1),dejr.sort_me_please.skip3:
I was a bit shocked by the fact that it took me at a reasonable time to write a working draw_triangle on z80 asm , drawing a pixel polygon per pixel and no holes when joining polygons.
Due to the presence of a generator of the table screen rows, I wrote a pretty curved and slow point output procedure using this table. The procedure has two entry points - just pixel inversion, and pixel inversion with painting it INK -a + BRIGHT + and the color specified in one of the registers.
At the stage of creating an effect with particles, I discovered that the example with the structures from the examples in wiki sjAsm just does not work. Google introduced a topic from the site zx-pk.ru , where this problem is described, and there is no solution to it — ha, great — another glitch.
I decided to do everything clearly - updating the coordinates regardless of the drawing, by interruption. Yeah ... plus dohren bytes to generate an interrupt table.
There were few particles at this stage, and they barely fit into the frame - this is to the word about the slowness of my procedure for outputting the point%) But using a common table with sprites did not allow me to throw it out, and take it ready, because this greatly saved space on the need for only one table generator. Yes, and my love for bicycles, too :)
Too few particles ... increased their number, but now the rendering has grown fat to two frames.
Testing for Peters WS64 , which I got on the past cc and repaired this winter :)
By the way, already at this stage the points turned into fat horizontal points of 2: 1 , like on Commodore 64. It happened because of the initially small number of particles, and my dissatisfaction with the fact that they were quite invisible when running on real. Solved the problem by replacing the plate
which worsened the accuracy of positioning and made the flight a bit jerky, but increased visibility. Here, the fact that particles fell from top to bottom also played into the hands - their vision was smeared on the vertical.
By the way, particles each fall with their own random speed, and two bytes are used to store the Y coordinates .
I threw out the unfinished piece of the part with the sprites, realizing that I did not fit in 1k with him.
In addition, there was already a shortage of space, so I remembered the wonderful article about Introspek packers, chose zx7 as a packer, which resulted in savings of about 110 bytes. By the way, maybe someone knows a more suitable packer for the 1k intro?
Since I already had a procedure for displaying a polygon, it seemed to me a cool idea to break the cc logo into polygons and display them one after another.
I wrote some test code that outputs several polygons - everything worked as I intended - great.
To check whether or not my idea would fit in 1k , I generated a certain number of random polygons, according to estimates, a sufficient number for the logo, and drove it into the source. Compiled, and made sure that - excellent - intro, in this form, breaks into the limit of 1024 bytes.
Life photo, recognize the device on the table? :)))
I decided once again to test the semi-finished intro, already with the polygons, and the packer, I loaded it on the real and ... got a reset. First of all, I began to sin on the fact that I forgot to initialize the memory somewhere, from which, where on the emulator 0x00 and everything works fine, on the real world there is garbage that causes a reset.
Nothing better for finding a problem place than the half division method and di halt I couldn’t think of.
I was busy with a reset on real life for two hours, there was no way to localize the glitch ...
As it turned out, it wasn't in my code, it was in the included sound enhancer on the phone from which I loaded WAV . Improvers from the bit stream in the WAV file generated the delusion stream.
As soon as I turned it off, everything magically worked.
Outlined the logo in the graphics editor errorsoft greenpixel , breaking it into a bunch of triangles, and manually drove the coordinates to the source. Having shoved the Chaos Constructions logo completely and launching it on real life - I was glad - it looked pretty good.
The first display of the logo on the real
world However, I randomly crammed random polygons, and on the real logo, I went beyond the limit of 1k by 150 bytes. And this is despite the fact that the particle effect was still not completed, and the transition between the parts was sharp.
Go to sleep on this day, because of the fussing with glitches, it came out already at 8 am 8)
And yes, I tried to optimize the size, keeping the coordinates and indexes of the vertices separately, but the packer didn’t like it much, which made the size only increase.
I figured out how to diversify the output of the logo; for this, almost nothing was needed except two more plates of pixels:
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
What gave the cool effect of increasing the detail of the logo rendering, or the initial blurring and a gradual increase in sharpness.
And finally, I made the final version with all the transitions, throwing away a lot of everything. In the process I found a glitch in the procedure of drawing a triangle - if the two Y vertices have the same coordinates , then the triangle is crooked (it looks like division by 0 when calculating dx ), bypassed a temporary hack.
Testing the final version on Leningrad 48
Double digits are “extra bytes”
94 extra bytes ...
It is time to cut out the “cultural” preservation / restoration of registers for the procedures for entering / exiting, it’s far from necessary, and the memory is eating.
86 bytes ...
Tested on real - works! Replaced a little more memory, incidentally fixing a bug with the division by 0 - 63 bytes!
57 bytes ...
Looping by the way done at the level of unpacking, because As a source of entropy for the RNG , several bytes were used from the initialization code (to save space), which in the process of the first part was spoiled by the RNG . Therefore, for looping, after the end of the intro is ret , well, and then - another unpacking and transition to the unpacked code ...
I could not get rid of the last 48 bytes, I had to cut out the interrupt handler, but Hurray! Shoved! Even 1 extra byte remained.
And since there are no interruptions, it is possible to score on the frame structure, and it is difficult to see the cross section of the beam on one pixel in the first effect, therefore, it increased the number of particles per eye, with a compromise between speed and spectacle.
He even more stung, stupidly transferring code and data from one place to another, helping the packer. What took some time :)
It freed up about 10 bytes , in which I put a parody on the sound. KHK, KHE, sound is in some places, by ear inserted:
ifdef UseSound ld a,d and#10 out (#FE),a endif
I did not “nail” the sound, but made a define, thus having received two versions of the intro, with and without sound. In the one that without sound, in the "extra" bytes stuffed
I collected trd and tap , and uploaded it all to cc .
Hooray - I participate in demopati!
It was funny with the sound in general, someone on the puttlace spoke about “clear sound”, someone looked at me strangely, and I discovered the following on the pouet :
In general, I didn’t understand, I liked someone 10 -byte sound or not :)
And the last thing is a shame that the 1k competition did not take place this year, in my opinion, the work was decent, but it ’s difficult to compete with 640k , but I really wanted to fight.
Write demos, write 1k!
And here's what happened in the end (ps, take care of your ears):
Version without sound: