Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 1 starfish.asm 1 ; starfish.asm 2 ; Andy Goth 3 ; Written 7 September 2021 4 ; Updated 14 September 2021 (vsync, terminate on ESC rather than keypress) 5 ; http://andy.junkdrome.org/devel/starfish/ 6 ; 7 ; Cute display hack, written for the Function 2021 256-byte competition. 8 ; https://2021.function.hu/ 9 ; Special thanks to: 10 ; - Attila Szabo, for encouraging me to join 11 ; - Tony Ha, for rearranging and eliminating some code 12 ; - Michael Kubel, for helping me use SI-based pointers 13 ; - All of the above, for good conversation during the development process 14 ; 15 ; This code is written for Borland Turbo Assembler 3.1 and MS-DOS. I 16 ; developed it on an AST Advantage! Pro 486DX/33 running MS-DOS 5.0. Most 17 ; of the writing was done in the MS-DOS Editor 1.1, and the early debugging 18 ; was done in Borland Turbo Debugger 3.1. 19 20 IDEAL ; Needed for syntax regularization. 21 0000 MODEL TINY ; Needed for *.COM output. 22 P386N ; Needed for P387 (next line). 23 P387 ; Needed for fsin. 24 DOSSEG 25 0000 CODESEG 26 ORG 100h ; Needed for *.COM output. 27 28 0100 _main: 29 ; Initialization. ----------------------------------------------------------- 30 ; At the program start, assume VGA 80x25 color text mode is selected, as if 31 ; by mov ax,0003h;int 10h. Furthermore, assume AX=BX=CH=0 and CL=0ffh. See 32 ; https://web.archive.org/web/20170129102219/http://pferrie.host22.com/misc/lowlevel12.htm 33 ; which documents not only the initial register values but also how they got 34 ; that way. Another initial condition is an empty keyboard buffer, so there's 35 ; no need to flush it. Credit goes to Tony Ha for pointing this out to me. 36 37 ; **OPTIONAL** 38 ; Borland Turbo Debugger "helpfully" zeroes out various registers that DOS 39 ; itself does not. However, Borland fails to reinitialize the FPU when 40 ; restarting the program. Thus, these lines are necessary for the sake of 41 ; debugging and can be taken out in the final release. 42 0100 B1 FF mov cl,0ffh ; Get back to the DOS initial value. 43 0102 9B DB E3 finit ; {}. 44 45 ; Initialize pointer through which memory variables are accessed. 46 0105 BE 01ECr mov si,OFFSET @vars ; Set base address. 47 48 ; Hide cursor. The initial value of BH is 0, which corresponds to page 0. 49 0108 B4 02 mov ah,02h ; Set cursor position. 50 010A BA C800 mov dx,0c800h ; Move cursor offscreen. 51 010D CD 10 int 10h ; Do it. 52 53 ; Construct font. The initial value of AL is 0, which is used as the bit 54 ; pattern for character 0. Each character's bit pattern is simply the 55 ; number of the character, e.g., character 1 has the rightmost pixel set. 56 010F BF 0208r mov di,OFFSET @buf ; Store into font buffer. 57 0112 @initFontBuffer: Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 2 starfish.asm 58 0112 AA stosb ; Store first scan line. 59 0113 AA stosb ; Store second scan line. 60 0114 FE C0 inc al ; Advance to next pattern. 61 0116 75 FA jnz @initFontBuffer ; Continue populating font buffer. 62 63 ; Load font. The initial BL is 0, which is used as the font block number. 64 0118 B8 1110 mov ax,1110h ; Load custom font. 65 011B 8D 6C 1C lea bp,[buf] ; Pointer to font data, ES is already set. 66 011E 41 inc cx ; Character count. Initial CX=255, we want 256. 67 011F 33 D2 xor dx,dx ; Start at character zero. 68 0121 B7 02 mov bh,02h ; 2 scan lines per character. 69 0123 CD 10 int 10h ; Do it. 70 71 ; Load constants into FPU. These will remain for the entire execution. 72 0125 D9 44 0C fld [starfish] ; {f}. 73 0128 D9 44 08 fld [toBam] ; {b,f}. 74 012B D9 04 fld [aspect] ; {s,b,f}. 75 76 ; Initialize ES register. Using push/pop saves one byte versus mov. 77 012D 68 B800 push 0b800h ; Text display segment. 78 0130 07 pop es ; Load into ES. 79 80 ; Main loop. ---------------------------------------------------------------- 81 ; This part of the program repeats until a key is pressed. 82 83 ; The main loop repeats for each frame. 84 0131 @mainLoop: 85 0131 C7 44 14 0064 mov [y],100 ; Start at y=100. 86 0136 33 FF xor di,di ; Start drawing at address b800:0000. 87 88 ; The row loop repeats for each new row. 89 0138 @rowLoop: 90 0138 C7 44 16 FEC0 mov [x],-320 ; Start at x=-320. 91 013D DF 44 14 fild [y] ; {y,s,b,f}. 92 0140 D9 C0 fld st(0) ; {y,y,s,b,f}. 93 0142 DC C9 fmul st(1),st(0) ; {y,y^2,s,b,f}. 94 95 ; The character loop repeats for each character, i.e. each 8-pixel group. 96 0144 @charLoop: 97 0144 BA 8000 mov dx,8000h ; DH=80h=bit mask, DL=0=character bits. 98 99 ; The pixel loop repeats for each pixel in the character. For each pixel, 100 ; compute one of the two spirals being displayed. The hypotenuse and angle 101 ; of the rightmost pixel will be left over afterward and will feed into the 102 ; calculations for the starfish effect and the second spiral, which are 103 ; displayed at lower (1/8) horizontal resolution than the first spiral. 104 0147 @pixelLoop: 105 0147 D9 C0 fld st(0) ; {y,y,y^2,s,b,f}. 106 0149 DF 44 16 fild [x] ; {x,y,y,y^2,s,b,f}. 107 014C D8 CC fmul st(0),st(4) ; {x*s,y,y,y^2,s,b,f}. 108 014E D9 C0 fld st(0) ; {x*s,x*s,y,y,y^2,s,b,f}. 109 0150 D8 C8 fmul st(0),st(0) ; {(x*s)^2,x*s,y,y,y^2,s,b,f}. 110 0152 D8 C4 fadd st(0),st(4) ; {(x*s)^2+y^2,x*s,y,y,y^2,s,b,f}. 111 0154 D9 FA fsqrt ; {h=sqrt((x*s)^2+y^2),x*s,y,y,y^2,s,b,f}. 112 0156 DF 5C 1A fistp [h] ; {x*s,y,y,y^2,s,b,f}. 113 0159 D9 F3 fpatan ; {atan2(y,x*s),y,y^2,s,b,f}. 114 015B D8 CC fmul st(0),st(4) ; {a=atan2(y,x*s)*b,y,y^2,s,b,f}. Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 3 starfish.asm 115 015D DF 5C 18 fistp [a] ; {y,y^2,s,b,f}. 116 0160 8A 44 1A mov al,[BYTE PTR h] ; AL=h 117 0163 02 44 12 add al,[BYTE PTR j] ; AL=h+j. 118 0166 02 44 18 add al,[BYTE PTR a] ; AL=h+j+a. 119 0169 A8 08 test al,8 ; Check 8 bit of h+j+a. 120 016B 74 02 jz @skipBit ; If bit is set, skip setting pixel. 121 016D 0A D6 or dl,dh ; Otherwise set pixel in character. 122 016F @skipBit: 123 016F FF 44 16 inc [x] ; Increment X coordinate. 124 0172 D0 EE shr dh,1 ; Shift bit mask. 125 0174 75 D1 jnz @pixelLoop ; Loop over all eight bits of the character. 126 127 ; Per-character math. 128 0176 DF 44 12 fild [j] ; {j,y,y^2,s,b,f}. 129 0179 DF 44 18 fild [a] ; {a,j,y,y^2,s,b,f}. 130 017C D8 E9 fsubr st(0),st(1) ; {j-a,j,y,y^2,s,b,f}. 131 017E D8 CE fmul st(0),st(6) ; {(j-a)*f,j,y,y^2,s,b,f}. 132 0180 D9 FE fsin ; {sin((j-a)*f),j,y,y^2,s,b,f}. 133 0182 DE 4C 10 fimul [c10] ; {sin((j-a)*f)*10,j,y,y^2,s,b,f}. 134 0185 DE 44 10 fiadd [c10] ; {sin((j-a)*f)*10+10,j,y,y^2,s,b,f}. 135 0188 DE C1 faddp ; {sin((j-a)*f)*10+10+j,y,y^2,s,b,f}. 136 018A DE 44 1A fiadd [h] ; {sin((j-a)*f)*10+10+j+h,y,y^2,s,b,f}. 137 018D DF 5C 1C fistp [buf] ; {y,y^2,s,b,f}. 138 139 ; Determine the color. 140 0190 8A 44 1C mov al,[BYTE PTR buf] ; AL=sin((j-a)*f)*10+10+j+h. 141 0193 B3 5D mov bl,5dh ; Try bright magenta on dim magenta background. 142 0195 A8 18 test al,18h ; Check if AL%64 is 0-7 or 32-39. 143 0197 74 08 jz @colorEnd ; If so, use magenta. 144 0199 B3 4C mov bl,4ch ; Else, try bright blue on dim blue background. 145 019B A8 20 test al,20h ; Check if AL%64 is 8-31. 146 019D 74 02 jz @colorEnd ; If so, use blue. 147 019F B3 19 mov bl,19h ; Else use bright red on dim blue background. 148 01A1 @colorEnd: 149 150 ; Determine the color intensity. 151 01A1 8A 44 1A mov al,[BYTE PTR h] ; AL=h. 152 01A4 2A 44 12 sub al,[BYTE PTR j] ; AL=h-j. 153 01A7 2A 44 18 sub al,[BYTE PTR a] ; AL=h-j-a. 154 01AA A8 08 test al,8 ; Check 8 bit of h-j-a. 155 01AC 8A E3 mov ah,bl ; Provisionally use the color decided above. 156 01AE 8A C2 mov al,dl ; Use the spiral bit pattern decided far above. 157 01B0 74 03 jz @bright ; If bit 8 is not set, use bright colors. 158 01B2 80 E4 07 and ah,07h ; Else use a black background and dim foreground. 159 01B5 @bright: 160 01B5 AB stosw ; Store the pixels and colors, and update DI. 161 162 ; End of character. 163 01B6 81 7C 16 0140 cmp [x],320 ; Check for end of row. 164 01BB 7C 87 jl @charLoop ; If not, process the next character. 165 166 ; End of row. Get rid of the row-specific FPU values and return the FPU 167 ; state back to when it contained only constants. Do this by abusing fcompp 168 ; which compares the top two values (don't care) and pops them both (needed). 169 ; The alternative is two ffree and fincstp instructions, so this saves six 170 ; bytes. After that, go down one row. 171 01BD DE D9 fcompp ; {s,b,f}. Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 4 starfish.asm 172 01BF FF 4C 14 dec [y] ; Advance to the next row. 173 01C2 83 7C 14 9C cmp [y],-100 ; Check if there are more rows. 174 01C6 0F 8F FF6E jg @rowLoop ; If so, process the next row. 175 176 ; Vertical sync. 177 01CA BA 03DA mov dx,03dah ; VGA input status #1 port. 178 01CD @vsync1: 179 01CD EC in al,dx ; Read VGA status. 180 01CE A8 08 test al,08h ; Check for vsync bit. 181 01D0 75 FB jnz @vsync1 ; Wait for any in-progress vsync to end. 182 01D2 @vsync2: 183 01D2 EC in al,dx ; Read VGA status. 184 01D3 A8 08 test al,08h ; Check for vsync bit. 185 01D5 74 FB jz @vsync2 ; Wait for start of next vsync. 186 187 ; End of frame. 188 01D7 FF 44 12 inc [j] ; Increment frame counter. 189 01DA E4 60 in al,60h ; Read keypress. 190 01DC FE C8 dec al ; Decrement scan code. 191 01DE 0F 85 FF4F jnz @mainLoop ; Loop unless ESC, whose scan code is 1. 192 193 ; Rude shutdown. This leaves the screen scrambled, so type "MODE 80" to fix. 194 ; ret ; Just exit. 195 196 ; **OPTIONAL** 197 ; Graceful shutdown code that restores the screen and keyboard. 198 01E2 B8 0003 mov ax,0003h ; Select VGA 80x25 text mode. 199 01E5 CD 10 int 10h ; Do it. 200 01E7 B8 4C00 mov ax,4c00h ; Exit program. 201 01EA CD 21 int 21h ; Do it. 202 203 ; Initialized data. --------------------------------------------------------- 204 01EC @vars: 205 01EC 3EBDA130 @aspect DD 0.3703704 ; Constant 10/27. 206 01F0 42652EE1 @spiral DD 57.29578 ; Constant 180/pi. 207 01F4 42A2F983 @toBam DD 81.48733 ; Constant 256/pi (180/pi * 64/45). 208 01F8 3D7E2E9A @starfish DD 0.06205616 ; Constant pi*8/405 (pi/36 * 32/45). 209 01FC 000A @10 DW 10 ; Constant 10. 210 211 ; Uninitialized data. ------------------------------------------------------- 212 01FE ???? @j DW ? ; Frame count, doesn't matter how it starts. 213 0200 ???? @y DW ? ; Y coordinate, 100 (top) through -99 (bottom). 214 0202 ???? @x DW ? ; X coordinate, -320 (left) through 319 (right). 215 0204 ???? @a DW ? ; Angle in degrees, ranges -180 through 180. 216 0206 ???? @h DW ? ; Hypotenuse in pixels, nonnegative. 217 0208 0100*(????) @buf DW 256 DUP (?) ; Font and miscellaneous temporary buffer. 218 219 ; SI-relative variable locations. ------------------------------------------- 220 = DWORD PTR si + + aspect EQU DWORD PTR si + @aspect - @vars 221 @aspect - @vars 222 = DWORD PTR si + + spiral EQU DWORD PTR si + @spiral - @vars 223 @spiral - @vars 224 = DWORD PTR si + + toBam EQU DWORD PTR si + @toBam - @vars 225 @toBam - @vars 226 = DWORD PTR si + + starfish EQU DWORD PTR si + @starfish - @vars 227 @starfish - @vars 228 = WORD PTR si + @10 + c10 EQU WORD PTR si + @10 - @vars Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 5 starfish.asm 229 - @vars 230 = WORD PTR si + @j + j EQU WORD PTR si + @j - @vars 231 - @vars 232 = WORD PTR si + @y + y EQU WORD PTR si + @y - @vars 233 - @vars 234 = WORD PTR si + @x + x EQU WORD PTR si + @x - @vars 235 - @vars 236 = WORD PTR si + @a + a EQU WORD PTR si + @a - @vars 237 - @vars 238 = WORD PTR si + @h + h EQU WORD PTR si + @h - @vars 239 - @vars 240 = WORD PTR si + @buf+ buf EQU WORD PTR si + @buf - @vars 241 - @vars 242 END _main Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 6 Symbol Table Symbol Name Type Value Cref (defined at #) ??DATE Text "09/14/21" ??FILENAME Text "starfish" ??TIME Text "01:22:23" ??VERSION Number 030A @10 Word DGROUP:01FC 133 134 #209 @32BIT Text 0 #21 @A Word DGROUP:0204 115 118 129 153 #215 @ASPECT Dword DGROUP:01EC 74 #205 @BRIGHT Near DGROUP:01B5 157 #159 @BUF Word DGROUP:0208 56 65 137 140 #217 @CHARLOOP Near DGROUP:0144 #96 164 @CODE Text DGROUP #21 @CODESIZE Text 0 #21 @COLOREND Near DGROUP:01A1 143 146 #148 @CPU Text 0F0FH #22 #23 @CURSEG Text _TEXT #25 @DATA Text DGROUP #21 @DATASIZE Text 0 #21 @FILENAME Text STARFISH @H Word DGROUP:0206 112 116 136 151 #216 @INITFONTBUFFER Near DGROUP:0112 #57 61 @INTERFACE Text 00H #21 @J Word DGROUP:01FE 117 128 152 188 #212 @MAINLOOP Near DGROUP:0131 #84 191 @MODEL Text 1 #21 @PIXELLOOP Near DGROUP:0147 #104 125 @ROWLOOP Near DGROUP:0138 #89 174 @SKIPBIT Near DGROUP:016F 120 #122 @SPIRAL Dword DGROUP:01F0 #206 @STACK Text DGROUP #21 @STARFISH Dword DGROUP:01F8 72 #208 @TOBAM Dword DGROUP:01F4 73 #207 @VARS Near DGROUP:01EC 46 65 72 73 74 85 90 91 106 112 115 116 117 118 123 128 + 129 133 134 136 137 140 151 152 153 163 172 173 188 #204 @VSYNC1 Near DGROUP:01CD #178 181 @VSYNC2 Near DGROUP:01D2 #182 185 @WORDSIZE Text 2 #22 #23 #25 @X Word DGROUP:0202 90 106 123 163 #214 @Y Word DGROUP:0200 85 91 172 173 #213 A Text WORD PTR si + @a + 115 118 129 153 #236 - @vars ASPECT Text DWORD PTR si + @aspect + 74 #220 - @vars BUF Text WORD PTR si + @buf + 65 137 140 #240 - @vars C10 Text WORD PTR si + @10 + 133 134 #228 - @vars H Text WORD PTR si + @h + 112 116 136 151 #238 - @vars J Text WORD PTR si + @j + 117 128 152 188 #230 - @vars SPIRAL Text DWORD PTR si + @spiral + #222 - @vars STARFISH Text DWORD PTR si + @starfish + 72 #226 Turbo Assembler Version 3.1 09/14/21 01:22:23 Page 7 Symbol Table - @vars TOBAM Text DWORD PTR si + @toBam + 73 #224 - @vars X Text WORD PTR si + @x + 90 106 123 163 #234 - @vars Y Text WORD PTR si + @y + 85 91 172 173 #232 - @vars _MAIN Near DGROUP:0100 #28 242 Groups & Segments Bit Size Align Combine Class Cref (defined at #) DGROUP Group #21 21 _DATA 16 0000 Word Public DATA #21 _TEXT 16 0408 Word Public CODE #21 #25