A.N.A.L.O.G. ISSUE 16 / FEBRUARY 1984 / PAGE 16
EDITOR’S NOTE: The article accompanying this demonstration is intended primarily for advanced readers. The demo program itself, however, can be typed in and enjoyed by anybody.
Stars 3D illustrates a simple 3-dimensional graphics effect, produced by moving similar objects horizontally at different speeds. It uses a couple of assembly-language “tricks” to achieve the illusion of depth as efficiently as possible.
Listing 1 is an ATARI BASIC program that POKEs the Stars 3D routine into memory and activates it with a USR call. Listing 2 is the assembly-language source code, provided for programmers who want to see how the routine works.
After entering Listing 1 and D:CHECKing it, SAVE the demo program to disk or tape and type RUN <return>. A few seconds later your screen will be filled with what appears to be 192 stars in several planes of motion. Believe it or not, the entire starfield is actually composed of only eight individual stars!
The dramatic display in Stars 3D is accomplished by using multiple LMS (Load Memory Scan) instructions in the display list. Every DL instruction has its LMS option bit set. This allows me to have the same section of memory appear in more than one place on the screen.
When I set up the display list, I randomly pick one of the eight star memory segments and store its address in an LMS instruction. Normally, this would mean that all the stars from a given memory segment would appear at the same x-position on the screen. I avoid this by adding a random offset (0-48) to each memory address, thereby spreading the stars all over the x-range.
If you think about the offset I’m adding, you’ll realize that an offset of, say, twenty will result in a mode line with twenty “orphan bytes” on the end (see Figure 1). This difficulty is corrected by assigning 96 bytes to each star memory segment instead of 48. But this solution creates yet another problem: scrolling a star across 96 bytes when only 48 are shown will cause the star to be invisible half the time (Figure 2). My solution is to give each memory segment two stars, separated by 48 bytes. Whenever one star moves off the edge of the screen, the other star will appear at the opposite side (Figure 3).
OFFSET = 20 48 BYTES, ON SCREEN MODE LINE (48 BYTES) 20 EXTRA
48 BYTES ON SCREEN •
48 BYTES ON SCREEN a) • •
48 BYTES ON SCREEN b) • •
Each DL instruction in Stars 3D has its display list interrupt (DLI) option bit set. A small DLI service routine allows me to put a different star color on every mode line. Instead of going for a broad spectrum of colors, I decided to make each star a different shade of the same color, with slow-moving stars set to darker luminances than the fast movers. This further enhances the illusion of depth.
Fine scrolling would have required handling each of the 192 mode lines separately. This takes a lot of processing time. Instead, I chose to manipulate each of the 16 stars (two in each memory segment) directly, using the 6502 ASL instruction. When a star is shifted out of one byte it falls into the next, and when it shifts out of its memory segment it is stuck onto the other end. Each memory segment has its own timer which determines how quickly the stars in that segment will appear to move.
Those are the basics behind Stars 3D. You should be able to find any details I skipped over by studying my source code. I hope this demo will show you how an unconventional approach can lead to terrific savings in both processing time and memory (the entire program requires less than 2K, including the screen RAM), both of which are critical in today’s high-performance graphics programs.
100 REM ************************ 110 REM * STARS 3D DEMO * 120 REM * BY CRAIG PATCHETT * 130 REM * ANALOG COMPUTING #16 * 140 REM ************************ 150 REM 160 START=14336:REM * $3800 HEX 170 FOR I=START TO START+377 180 READ BYTE:POKE I,BYTE:NEXT I 190 X=USR(START) 200 DATA 32,49,56,32,145,56,169,0,141,200,2,169,7,162,56,160,38,32,92,228,16 9,253,141,0,2 210 DATA 169,56,141,1,2,169,192,141,14 ,212,76,35,56,32,13,57,169,0,141,173,6 3,76,98,228,169 220 DATA 189,141,189,62,169,59,141,205 ,62,162,0,142,221,62,232,189,188,62,24 ,105,48,157,189,62,189 230 DATA 204,62,105,0,157,205,62,169,0 ,157,221,62,232,224,16,208,229,169,189 ,133,204,169,59,133,205 240 DATA 160,255,162,3,169,0,145,204,1 36,192,255,208,249,202,240,5,230,205,7 6,106,56,162,0,189,189 250 DATA 62,133,204,189,205,62,133,205 ,169,64,160,0,145,204,232,224,16,208,2 35,96,169,119,133,204,169 260 DATA 57,133,205,169,0,141,173,63,1 60,3,169,206,145,204,200,208,2,230,205 ,173,10,210,41,7,72 270 DATA 170,189,111,57,174,173,63,157 ,237,62,104,238,173,63,10,170,173,10,2 10,41,63,201,48,176,247 280 DATA 24,125,189,62,145,204,200,208 ,2,230,205,189,205,62,105,0,145,204,20 0,208,2,230,205,192,67 290 DATA 208,189,169,65,145,204,200,16 9,119,145,204,141,48,2,200,169,57,145, 204,141,49,2,169,35,141 300 DATA 47,2,96,174,173,63,189,237,62 ,141,10,212,141,22,208,238,173,63,64,1 62,13,32,50,57,32 310 DATA 50,57,162,12,32,50,57,32,50,5 7,202,48,17,222,87,57,208,248,32,50,57 ,189,99,57,157 320 DATA 87,57,76,29,57,96,189,189,62, 133,204,189,205,62,133,205,188,221,62, 177,204,10,10,145,204 330 DATA 144,15,169,1,136,192,255,208, 2,160,47,145,204,152,157,221,62,96,8,8 ,6,6,4,4,3 340 DATA 3,2,2,1,1,8,8,6,6,4,4,3,3,2,2 ,1,1,34,36,38,40,42,44,46,34,112,112,2 40
0100 ; ********************* 0110 ; * STARS 3D DEMO * 0120 ; * BY CRAIG PATCHETT * 0130 ; ********************* 0140 ; 0150 ; System equates 0160 ; 0170 SDMCTL = $022F 0180 NMIEN = $D40E 0190 RANDOM = $D20A 0200 WSYNC = $D40A 0210 SDLSTL = $0230 0220 VDSLST = $0200 0230 COLPF0 = $D016 0240 COLOR4 = $02C8 0250 SETVBV = $E45C 0260 XITVBL = $E462 0270 ; 0280 ; Zero-page equate 0290 ; 0300 INDRCT = $CC ; for indirect addressing 0310 ; 0320 *= $3800 0330 ; 0340 ; Get things going 0350 ; 0360 INITIL 0370 JSR STRINI ; set up stars 0380 JSR DLSINI ; set up display list 0390 LDA #0 ; set background color 0400 STA COLOR4 0410 LDA #7 ; set up VBLANK 0420 LDX #VBLANK/256 0430 LDY #VBLANK&255 0440 JSR SETVBV 0450 LDA #DLI&255 ; get DLI's going 0460 STA VDSLST 0470 LDA #DLI/256 0480 STA VDSLST+1 0490 LDA #192 0500 STA NMIEN 0510 ALLDON 0520 JMP ALLDON ; let things run 0530 ; 0540 ; VBLANK routine 0550 ; 0560 VBLANK 0570 JSR CNTDWN ; take care of star movement 0580 LDA #0 ; reset index 0590 STA INDEX 0600 JMP XITVBL ; back to system 0610 ; 0620 ; Initialize stars 0630 ; 0640 STRINI 0650 LDA #STRLIN&255 ; set up STRTPL/H arrays 0660 STA STRTPL 0670 LDA #STRLIN/256 0680 STA STRTPH 0690 LDX #0 0700 STX STRPOS 0710 INX 0720 STRBR1 0730 LDA STRTPL-1,X ; make each address a screen 0740 CLC ; width more than the one 0750 ADC #48 ; before 0760 STA STRTPL,X 0770 LDA STRTPH-1,X 0780 ADC #0 0790 STA STRTPH,X 0800 LDA #0 0810 STA STRPOS,X 0820 INX 0830 CPX #16 0840 BNE STRBR1 0850 LDA #STRLIN&255 ; clear star memory 0860 STA INDRCT 0870 LDA #STRLIN/256 0880 STA INDRCT+1 0890 LDY #255 0900 LDX #3 0910 LDA #0 0920 STRBR2 0930 STA (INDRCT),Y 0940 DEY 0950 CPY #255 0960 BNE STRBR2 0970 DEX 0980 BEQ STRBR3 0990 INC INDRCT+1 1000 JMP STRBR2 1010 STRBR3 1020 LDX #0 ; give each line a star 1030 STRBR4 1040 LDA STRTPL,X 1050 STA INDRCT 1060 LDA STRTPH,X 1070 STA INDRCT+1 1080 LDA #64 1090 LDY #0 1100 STA (INDRCT),Y 1110 INX 1120 CPX #16 1130 BNE STRBR4 1140 RTS 1150 ; 1160 ; Initialize display list 1170 ; 1180 DLSINI 1190 LDA #DLIST&255 ; set up for indirect 1200 STA INDRCT ; addressing 1210 LDA #DLIST/256 1220 STA INDRCT+1 1230 LDA #0 ; get index ready 1240 STA INDEX 1250 LDY #3 1260 DLSBR4 1270 LDA #$CE ; ANTIC 14, DLI, LMS line 1280 STA (INDRCT),Y 1290 INY 1300 BNE DLSBR1 1310 INC INDRCT+1 1320 DLSBR1 1330 LDA RANDOM ; pick star type 1340 AND #7 1350 PHA 1360 TAX 1370 LDA MANCOL,X ; tell STRCOL what color 1380 LDX INDEX ; this line is 1390 STA STRCOL,X 1400 PLA 1410 INC INDEX 1420 ASL A ; times two so we skip over 1430 TAX ; two screen widths... 1440 DLSBR2 1450 LDA RANDOM ; pick random offset into 1460 AND #63 ; line 1470 CMP #48 ; make sure it's less than 1480 BCS DLSBR2 ; a screen width 1490 CLC 1500 ADC STRTPL,X ; ...here instead of one 1510 STA (INDRCT),Y ; put it into display list 1520 INY 1530 BNE DLSBR5 1540 INC INDRCT+1 1550 DLSBR5 1560 LDA STRTPH,X 1570 ADC #0 1580 STA (INDRCT),Y 1590 INY 1600 BNE DLSBR6 1610 INC INDRCT+1 1620 DLSBR6 1630 CPY #67 ; 192 lines done, finish up 1640 BNE DLSBR4 1650 LDA #$41 1660 STA (INDRCT),Y 1670 INY 1680 LDA #DLIST&255 1690 STA (INDRCT),Y 1700 STA SDLSTL 1710 INY 1720 LDA #DLIST/256 1730 STA (INDRCT),Y 1740 STA SDLSTL+1 1750 LDA #$23 ; give us a wide screen 1760 STA SDMCTL 1770 RTS 1780 ; 1790 ; Display list interrupt routine 1800 ; 1810 DLI 1820 LDX INDEX ; what line are we on? 1830 LDA STRCOL,X ; load this line's color 1840 STA WSYNC ; wait for end of last line 1850 STA COLPF0 ; store color 1860 INC INDEX ; get ready for next line 1870 RTI 1880 ; 1890 ; Timer routine 1900 ; 1910 CNTDWN 1920 LDX #13 ; move fastest star 1930 JSR SCROLL 1940 JSR SCROLL 1950 LDX #12 ; and its twin 1960 JSR SCROLL 1970 JSR SCROLL 1980 CNTBR1 1990 DEX ; for the rest... 2000 BMI CNTRET 2010 DEC TIMER,X ; ...countdown timer 2020 BNE CNTBR1 2030 JSR SCROLL ; scroll if ready 2040 LDA TIMARY,X ; and reset timer 2050 STA TIMER,X 2060 JMP CNTBR1 2070 CNTRET 2080 RTS 2090 ; 2100 ; Star scroll routine 2110 ; 2120 SCROLL 2130 LDA STRTPL,X ; set up for indirect 2140 STA INDRCT ; addressing 2150 LDA STRTPH,X 2160 STA INDRCT+1 2170 LDY STRPOS,X ; get star position 2180 LDA (INDRCT),Y ; get byte with star in it 2190 ASL A ; shift star left one 2200 ASL A 2210 STA (INDRCT),Y ; put it back 2220 BCC SCRBR1 ; did it fall off byte? 2230 LDA #1 ; if so, put it in next one 2240 DEY 2250 CPY #255 ; did it fall off screen? 2260 BNE SCRBR2 2270 LDY #47 ; if so, put it on other end 2280 SCRBR2 2290 STA (INDRCT),Y 2300 TYA 2310 STA STRPOS,X ; remember where it is now 2320 SCRBR1 2330 RTS 2340 ; 2350 TIMER 2360 .BYTE 8,8,6,6,4,4 ; timers for scrolling 2370 .BYTE 3,3,2,2,1,1 2380 TIMARY 2390 .BYTE 8,8,6,6,4,4 ; values to reset timers 2400 .BYTE 3,3,2,2,1,1 2410 MANCOL 2420 .BYTE $22,$24,$26 ; star colors 2430 .BYTE $28,$2A,$2C 2440 .BYTE $2E,$22 2450 ; 2460 DLIST 2470 .BYTE $70,$70,$F0 ; display list 2480 *= *+579 2490 STRLIN 2500 *= *+768 ; star (screen) memory 2510 STRTPL 2520 *= *+16 ; addresses of beginning of 2530 STRTPH 2540 *= *+16 ; each star line 2550 STRPOS 2560 *= *+16 ; position of star on line 2570 STRCOL 2580 *= *+192 ; color of each line 2590 INDEX 2600 *= *+1 ; used to index into STRCOL