20-byte StarfieldAssembler/8086

This is the 20-byte starfield coded by Niklas Beisert (pascal). The 20-byte starfield is based on a 24-byte version written by Matt Wilhelm. Niklas refers to Matt in the text. The original sources for both the 20-byte and the 24-byte versions are included in the archive below.
This text does not cover all optimizations done to achive the original 24-byte version. Such text is however included in the archive.

Setup Code:

Video mode init done as usual:

        mov     al,13h
        int     10h
I also tried to skip the randomizing part by not cleaning the video memory. (MOV AL,93h). Matt did not like it. (You can do a 14 byte version with it. The only thing about it is that it does not look like a starfield anymore.
First SP is popped, which makes it 0 (a RET would make IP point to CS:0, where you find INT 20h that terminates the program).
Then CX is popped effectively making it 20CDh (opcode of INT 20h), this also sets the number of stars to about 8000.
Finally I POP DS, and this is the critical part of it all. At the address SP points to, you find the upper memory boundary, and this is usually 9FFFh (tested on many computers). If it does not, strange things may happen. The starfield algorithm will destroy MSDOS memory management (first 16 bytes in DS), so a keyboard version that could be ended somehow is senseless. DS is now the screen segment.
All above mentioned optimizations yield 1 byte. (3 1-byte pops instead of 2 2-byte movs)

Random Star Generator:

The randomizer was changed to POP AX. Even if memory is clean at least the PSP and the program code will produce some "random" numbers. You'll get at least 50 stars with this. (pray that the program is not interruped when SP points to the code, messing it up!) Pixels are set by exchanging the screen pixel (probably 0) with AL (random). This saves 1 byte. (1-byte pop instead of 2-byte in)

Star Scrolling:

The algorithm was changed a bit. I only use one xchg to set/reset pixels. Usually AL is 0. If so the screen pointer is incremented, and the current pixel is replaced by al. If that pixel was black, AL is 0 again, and nothing happend on the screen. Black regions of the screen are thus skipped. If a nonzero pixel is encountered, it is replaced by the 0 in AL and the screen pointer is incremented by the value of the pixel plus 1. In the next loop the current pixel (probably 0) is replaced by AL. All in all a star is moved by some pixels. This saves 2 bytes. (es: prefix removed, 1-byte cbw instead of 2-byte xor)

about the making:

I was listening to strange discussion about starfields and 26 byte versions and stuff on #coders. I didn't really take it seriously. A starfield in 26 bytes or less is utopia. Some time Matt Wilhelm asked me something and we had a nice chat about demos and so and finally he gave me his starfield.

I was amazed.

Tried to work through the code for some hours and I finally got the point. When I went to bed some hours later than I originally planned to I was negative about further optimizations. Everything just stuck together and you couldn't change something without messing up the rest. Next morning I had the idea to rearrange the loop (after some stupid randomizing ideas) and cut 2 bytes off the program. Later I found a different randomizing method and I also found the screen segment in memory and in reach.
This 20 byte starfield would never have been possible without the 24 byte starfield by Matthew Wilhelm and Adam Letts. It'd rather be hundereds!

The 20-byte version, the 24-byte version, a 30-byte version which does allow you to exit, the original optimization text and compiled version with object files are included in the following archive (9422 bytes).

;
; pascal's 20 byte version of
;
; Tiny Parallax Starfield
;   by Matthew Wilhelm
;
        .model tiny

        .code

        org 100h

start:
        mov     al,13h          ; ah assumed to be 0. ok.
        int     10h

        pop     sp              ; com loader pushes 0, => sp == 0
        pop     cx              ; 2 instr. bytes for int 20h => cx = 20CDh
                                ; pop something else for less stars
        pop     ds              ; we need the video segment 0A000h, here we
                                ; find 9FFFh (upper memory segment limit)
                                ; on most machines. this is not clean,
                                ; but who cares...
lp1:
        pop     ax              ; randomize, hope no irq occurs while
        aaa                     ; popping code! afterwards al<10
lp2:
        xchg    [di],al         ; clean & put star
                                ; es prefix removed
        add     di,ax           ; new pos for star / randomize pos in init
        loop    lp1             ; init loop, skipped in star moving loop
        inc     di              ; a clean pixel must also move
                                ; was stosb
        cbw                     ; clear ah
                                ; was xor ax,ax
        inc     cx              ; to skip loop command
        jmp     lp2             ; star moving loop

        end     start
Gem writers: Niklas Beisert
(text touchup) John Eckerdal
last updated: 1998-06-07