Classic Computer Magazine Archive A.N.A.L.O.G. ISSUE 63 / AUGUST 1988 / PAGE 25

Animation
by Ron Goodman

There's a lot of computer animation today, and many Atari owners probably wish they could do some of the animation that they  see in the movies and the video arcade. But there are several problems. Most computer animation done in movies is not produced "real-time." That is to say that it's created a frame at a time and then photographed. From there it may be modified with both digital and analog filters, so each frame may end up taking several minutes to produce. Some of the best quality graphics produced with techniques such as "ray-tracing" may even take several hours per frame to create on a mainframe computer.

    The second problem is of course that an Atari is not a mainframe computer. The Atari instead has a relatively slow 6502 microprocessor. With the overhead added by using a high-level language like Atari BASIC, most programmers throw up their hands in disgust.
    Some people abandon BASIC and try assembly language. This is a good recourse, but even Atari assembly is not that fast, and many people don't want to give up the ease of using a high-level language.
    In this article I will demonstrate my solution to this problem using two machine language routines. They will allow you to do your entire animation program in BASIC without ever touching an editor/assembler and without buying a new computer! Because, in spite of the weaknesses of a 6502 computer, the Atari computer is still the best computer for graphics and animation under $1,000, except perhaps for the Atari STs.

What is animation?
    The process of animation involves displaying pictures, one after the other, usually in an attempt to simulate motion. Cartoonists have used the method for years, and in reality moviemakers do basically the same thing too (although they don't call it animation, since the pictures are generated from a camera rather than an artist or computer). Different methods of animation generally differ in the way that the images are created, and the speed that the images are displayed (frames per second.)
    There are two general categories of computer-graphics animation, although they often can be found combined into a hybrid form. The first is frame generating, where the entire frame is created, displayed and then erased when the next frame is generated. The second method is frame modification. In this method an original image is displayed and then changed only in the parts where something is supposed to move.
    Frame generation is very flexible and usually relatively easy to program. The first problem is that it can be slow, since even the slightest change requires that the entire picture be erased and regenerated. The second is that it usually requires twice as much video memory: one chunk for the picture being displayed and another chunk for the image being generated.
    Frame modification is often much faster, but it's not as flexible and often does not look as good as frame generation. It's unfortunately limited to simple animation-unless you want your program to get very slow and complex. This method is also usually less memory consuming than frame generation, although this advantage can be sacrificed in order to use a workscreen to make smooth picture transitions like frame generation. Frame modification is what most BASIC programmers like to use because of the speed advantage.
    Listings 1 and 2 are two programs which visibly do the same animation, but Listing 1 uses frame generation and Listing 2 uses frame modification. The programs move a horizontal line from the far left of the screen to the far right. In frame generation, we display a line at the far left (0,100). Then that line is erased and a new line is displayed at (1,100)-then (2,100), (3,100) and so on until it reaches the far right. This method is slow because the entire line must be redrawn to move it. Also, increasing the length of the line would slow down the program. This method is flexible. The line could easily be made to move two pixels at a time by just changing the X =X + 1 to an X =X + 2 or made to move vertically by changing the value of Y
    The frame modification routine has a different approach. It draws the line at the far left (0,100). Then it erases the leftmost pixel of the line and adds a pixel to the right end over and over until the line appears to have moved to the far right of the screen. This method is fast because only two pixels have to be drawn per frame. The line could have been 100 pixels in length with no loss in speed, since the line is not being redrawn each frame. However, the movement is not as flexible. Since we are using a trick to move the line, moving two pixels at a time would not be a simple change. And moving the line vertically would be completely different.
    For moving a line across the screen, most people choose frame modification because speed is the most important factor. But type in the two programs in Listings 3 and 4. They each have approximately the same output-four simple stick figures bounce around in a box with a square inside it. In frame modification (Listing 4) the action is sloppy. When part of the picture crosses the inner square it erases it momentarily. In frame generation (Listing 3) the movement is smooth and no damage occurs when two objects cross paths. In addition, frame generation is slightly faster. Why? Because most of the picture changes each frame.

Using frame generation
    Some nice frame generation can be done on the Atari since it has pageflipping abilities. This is the ability to tell the computer where an image is stored in memory, which allows you to change the Atari's screen from displaying one image to the next virtually instantly. The program in Listing 5 is an optimized assembly language listing of a program that will allow BASIC programmers to easily use this feature. The program in Listing 6 is a BASIC program that can be appended to the end of your BASIC animation program. A GOSUB to Line 30000 will load the machine-language routine from Listing 5 into Page 6 memory. Remember that Page 6 is unused by BASIC, so this routine is safe and takes up no program memory.
    This program fools the Atari into displaying one image and working on another. The graphics commands DRAWTO and PLOT will draw the invisible background image to create the next frame while the current image is being displayed. Calling the machine-language routine with an X=USR(1536) will cause the invisible image to be displayed and the image being displayed to be disposed. Then you draw the next frame and do another X=USR(1536). The machine-language handles all the details in just a 15th of a second, so all you need to do is call X = USR(1536), draw an image, call X=USR(1536), draw next image, call X=USR(1536), draw next image, etc. Although it is not always necessary, it is a good general practice to call X = USR(1536) just after the GRAPHICS statement and before you begin drawing. This program will work in graphics mode 24, 9, 10 and 11 (24 is mode 8 without a text window).
    What is the routine doing? First it modifies the display list to point to one chunk of memory. Next it adjusts the video memory pointer at locations 88 and 89 of BASIC to point at another chunk. DRAWTO and PLOT commands are drawn in the chunk pointed to by the video memory pointer and so are not seen. Then, the next time the machine-language routine is called, the pointers swap. The 7680 byte area of memory that was being displayed is filled with zeros (cleared) and is ready to be used for the next frame. And the 7680 byte chunk that was being worked on by DRAWTOs and PLOTs is now displayed.

Frame generation is
very flexible and usually
relatively easy to
program. The first
problem is that it can
be slow, since even the
slightest change
requires that the entire
picture be erased and
regenerated,

    The BASIC program in Listing 7 is an example of a complete frame-generation program. It's a clock program that updates its display about every ten seconds. The hands move smoothly because the picture appears to be drawn instantly. The clock chimes at the half hour and gongs at the hour. It has ten setable alarms. To set the alarms, press the space-bar while the program is running. To quit press ESCAPE. The alarms and the current time are maintained even if the program is stopped (or even deleted!), but the alarms will only sound while the program is running. The screen will progressively dim after a few minutes of inactivity instead of going into attract mode to protect the screen more effectively and attractively. This way the computer can be left on over-night or even for days.
    The BASIC program in Listing 8 is another example. In this program you draw a line picture with the joystick. Do this by moving the graphics cursor to various points and pressing the joystick fire button. The program will connect the points. After you draw your picture, press START. Now the joystick will cause your picture to expand or contract by pushing up or down, and move left or right by pressing the stick left or right. Pressing the fire button will cause the picture to be rotated by ten degrees. The pictures are generated quickly and appear to be redrawn instantly. Unrestricted movement like this is easy with frame generation and the obvious choice over frame modification for such an application.

Using frame modification
    Using frame modification doesn't have to be choppy and chunky. You can use the page-flipping technique that we used in frame generation. Listing 9 is an assembly language program for this purpose. It works in a different manner, of course. It must be called before you begin drawing. Now all of your drawing is done in the background screen. When the frame is ready to be displayed you do another USR call and the frame is copied into the foreground for display, leaving the background copy intact. Future modifications are still done on the background. And whenever it is time for an update you do another USR call. So all you need to do is X=USR(1664), draw the picture, X=USR(1664), modify the picture, X=USR(1664) , modify the picture, X = USR(1664), etc.
    Listing 10 can be appended to your BASIC program to load this routine. Like the frame generation program the machine language is stored in unused Page 6 so it doesn't use program memory. Note also that both Listing 6 and Listing 10 have unique line numbers and can be appended for combined frame modification and frame generation applications. The machine-language routines are also stored in different areas of Page 6 so that there is no conflict of memory there.

Other methods of animation
    The Atari has some special features for animation that can be used. Player-missile graphics are useful for horizontal animation, or if some machine language is used, for vertical animation too. There is collision detection in hardware to simplify computations for games, but player/missile graphics can also be used to simplify animation computations. For more complete explanations of these features, you should read De Re Atari.

Ron Goodman is a computer music major at the California Institute of Technology. His greatest fascination is with computer graphics and computer music (MIDI/ST applications).

LISTING 1: BASIC

IQ 5 GOSUB 30000
RR 10 GRAPHICS 24:COLOR 1
PC 20 X=0:Y=100
OY 30 PLOT X,Y:DRAWTO X+50,Y:A=USRC1536)
DJ 40 X=X+1:IF X<268 THEN 30
RM 50 GOTO 10
TV 30000 RESTORE 30010:FOR X=1536 TO 1627
   READ Y:POKE X,Y:NEXT X:RETURN
PW 30010 DATA 160,0,24,173,48,2,105,5,133
   ,203,173,49,2,105,0,133,204,165,203,10
   5,96,133,205,165,204,105,0,133
QS 30020 DATA 206,173,49,2,56,233,31,141,
   230,2,165,89,209,203,208,7,173,230,2,1
   33,89,208,13,170,177,203,133
WW 30030 DATA 89,138,145,203,24,105,15,14
   5,205,165,89,141,80,6,165,88,141,79,6,
   169,0,162,30,153,80,127,200
WF 30040 DATA 208,250,238,80,6,202,208,24
   4,104,96


LISTING 2: BASIC

RR 10 GRAPHICS 24:COLOR 1
PC 20 X=0:Y=100
KX 30 PLOT XY:DRAWTO X+50 ,Y
LJ 40 COLOR 0 PLOT X,Y:COLOR 1:PLOT X+51,
   Y:X=X+1:IF X<268 THEN 40
RM 50 GOTO 10


LISTING 3: BASIC

IQ 5 GOSUB 30000
FH 10 GRAPHICS 24:SETCOLOR 2,0,0
IW 20 CX=160:CY=86:X=0:Y=0:DX=6:DY=5:T=1
GZ 30 COLOR 1:PLOT 67,20:DRAWTO 252,20:DR
   AWTO 252,171:DRAWTO 67,171:DRAWTO 67,2
   0
QC 40 PLOT 100,40:DRAWTO 219,40:DRAWTO 21
   9,151:DRAWTO 100,151:DRAWTO 100,40
JZ 60 XX=CX+X:YY=CY+Y:GOSUB 1000
LI 76 XX=CX+X:YY=CY-Y:GOSUB 1000
KT 80 XX=CX-X:YY=CY+Y:GOSUB 1000
MC 90 XX=CX-X:YY=CY-Y:GOSUB 1000
VO 95 T=USR(1536)
UI 100 IF X=0 OR Y=0 THEN FOR A=50 TO 0 S
   TEP -1:SOUND 0,A,12,A/3.23:FOR B=1 TO
   10:NEXT B:NEXT A
HN 110 GOSUB 250:GOSUB 200
QC 130 GOTO 30
PT 200 X=X+DX:Y=Y+DY:RETURN
MM 250 IF ABS(Y)>53 THEN DY=-DY:GOSUB 300
LV 260 IF ABS(X)>83 THEN DX=-DX:GOSUB 300
ZM 270 RETURN
AF 300 FOR A=15 TO 0 STEP -0.2:SOUND 0,41
   ,12,A:NEXT A:RETURN
SM 1000 PLOT XX-5,YY-10:DRAWTO XX+5,YY-10
   :DRAWTO XX+5,YY:DRAWTO XX-5,YY:DRAWTO
   XX-5,YY-10:REM DRAW HEAD
VZ 1010 PLOT XX,YY:DRAWTO XX,YY+20:DRAWTO
    XX-7,YY+29:PLOT XX,YY+20:DRAWTO XX+7,
   YY+29:REM DRAW BODY AND LEGS
TB 1020 PLOT XX-6,YY+9:DRAWTO XX,YY+7:DRA
   WTO XX+6,YY+9:REM DRAW ARMS
AI 1030 RETURN
TV 30000 RESTORE 30010:FOR X=1536 TO 1627
   :READ Y:POKE X,Y:NEXT X:RETURN
PW 30010 DATA 160,0,24,173,48,2,105,5,133
   ,203,173,49,2,105,0,133,204,165,203,10
   5,96,133,205,165,204,105,0,133
QS 30020 DATA 206,173,49,2,56,233,31,141,
   230,2,165,89,209,203,208,7,173,230,2,1
   33,89,208,13,170,177,203,133
WN 30030 DATA 89,138,145,203,24,105,15,14
   5,205,165,89,141,80,6,165,88,141,79,6,
   169,0,162,30,153,80,127,200
WF 30040 DATA 208,250,238,80,6,202,208,24
   4,104,96


LISTING 4: BASIC

FM 10 GRAPHICS 24:SETCOLOR 2,0,0
GY 20 COLOR 1:PLOT 67,20:DRAWTO 252,20:DR
   AWTO 252,171:DRAWTO 67,171:DRAWTO 67,2
   0
IX 30 CX=160:CY=86:X=0:Y=0:DX=6:DY=5:T=1
EC 40 IF T=1 THEN COLOR 1:PLOT 100,40:DRA
   WTO 219,40:DRAWTO 219,151:DRAWTO 100,1
   51:DRAWTO 100,40
BS 50 COLOR (T=1)
JZ 60 XX=CX+X:YY=CY+Y:GOSUS 1000
LI 70 XX=CX+X:YY=CY-Y:GOSUB 1000
KT 80 XX=CX-X:YY=CY+Y:GOSUB 1000
MC 90 XX=CX-X:YY=CY-Y:GOSUB 1000
BZ 100 IF T=-1 THEN GOSUB 200
FK 110 IF T=1 AND (X=0 OR Y=0) THEN FOR A
   =50 TO 0 STEP -1:SOUND 0,A,12,A/3.23:F
   OR B=1 TO 10:NEXT B:NEXT A
DV 120 IF T=1 THEN GOSUB 250
II 130 T=-T:GOTO 40
PT 200 X=X+DX:Y=Y+DY:RETURN
MM 250 IF ABS(Y)>53 THEN DY=-DY:GOSUB 300
LV 260 IF ABS(X)>83 THEN DX=-DX:GOSUB 300
ZM 270 RETURN
AF 300 FOR A=15 TO 0 STEP -0.2:SOUND 0,41
   ,12,A:NEXT A:RETURN
SM 1000 PLOT XX-5,YY-10:DRAWTO XX+5,YY-10
   :DRAWTO XX+5,YY:DRAWTO XX-5,YY:DRAWTO
   XX-5,YY-10:REM DRAW HEAD
VZ 1010 PLOT XX,YY:DRAWTO XX,YY+20:DRAWTO
    XX-7,YY+29:PLOT XX,YY+20:DRAWTO XX+7,
   YY+29:REM DRAW BODY AND LEGS
TB 1020 PLOT XX-6,YY+9:DRAWTO XX,YY+7:DRA
   WTO XX+6,YY+9:REM DRAW ARMS
AI 1030 RETURN


LISTING 5: ASSEMBLY

10 ;Page flipping routine. After using
20 ;GR.24, GR.9-11, call this routine
30 ;with X=USR(1536) to toggle
40 ;background and foreground page.
50 ;Background page is the one that
60 ;PLOT and DRAWTO effect, and
70 ;foreground is the one that is
75 ;displayed.
80 ;
90       *= $0600
0100 MEMTOP = $02E5  Men top pointer.
0110 SAVMSC = $58    Video men pointer
0120 SDLSTL = $0230  Start of DL.
0130 DLPNTI = $CB    Two unused words
0140 DLPNT2 = $CD    in zero page.
0150     LDY #0
0160     CLC
0170     LDA SDLSTL  Find the
0180     ADC #5      two
0190     STA DLPNT1  display
0200     LDA SDLSTL+1 list
0210     ADC #0      references
0220     STA DLPNT1+1 to video
0230     LDA DLPNT1  memory
0240     ADC #96     and store
0250     STA DLPNT2  then in
0260     LDA DLPNT1+1 unused
0270     ADC #0      part of
0280     STA DLPNT2+1 page 0.
0290     LDA SDLSTL+1 Find area
0300     SEC         to store
0310     SBC #31     screen 2
0320     STA MEMTOP+1 safely.
0330     LDA SAVMSC+1 Insure that
0340     CMP (DLPNTI),Y foreground
0350     BNE NORM     and
0360     LDA MEMTOP+1 background
0370     STA SAVMSC+1 are
0380     BNE CLEAR   different.
0390 NORM TAX        Swap both
0400     LDA (DLPNT1),Y of the
0410     STA SAVMSC+1 foreground
0420     TXA         pointers
0430     STA (DLPNT1),Y and
0440     CLC         the one
0450     ADC #$0F    background
0460     STA (DLPNT2),Y pointer.
0470 CLEAR LDA SAVMSC+1 Set up the
0480     STA SCRPNT+1 indexed
0490     LDA SAVMSC  addressing
0500     STA SCRPNT  command.
0510     LDA #0      Quickly
0520     LDX #30     clear
0530 LOOP STA 0,Y    out
0540     INY         7688 byte
0550     BNE LOOP    buffer
0560     INC SCRPNT+1 for the
0570     DEX         new
0580     BNE LOOP    screen.
0590     PLA         Unused parameter
0600     RTS         Return.
0610 SCRPNT = LOOP+1
0620 ;The SCRPNT pointer is used to
0630 ;modify code on the fly. This
0640 ;allows us to use Indexed
0650 ;addressing which is faster
0660 ;than Post-indexed indirect
0670 ;addressing in the inside loop.
0680 END


LISTING 6: BASIC

TV 30000 RESTORE 30010:FOR X=1536 TO 1627
   :READ Y:POKE X,Y:NEXT X:RETURN
PW 30010 DATA 168,9,24,173,48,2,105,5,133
   ,203,173,49,2,105,0,133,204,165,203,10
   5,96,133,205,165,204,105,0,133
QS 30020 DATA 206,173,49,2,56,233,31,141,
   230,2,165,89,209,203,208,7,173,230,2,1
   33,89,200,13,170,177,203,133
WW 30030 DATA 89,130,145,203,24,105,15,14
   5,205,165,89,141,80,6,165,88,141,79,6,
   169,0,162,30,153,80,127,200
WF 30040 DATA 200,250,238,80,6,202,208,24
   4,184,96


LISTING 7: BASIC

TM 10 REM Press space bar to set alarms.
SR 20 GRAPHICS 0:SETCOLOR 2,0,0:POSITION
   13,8:? "";
FV 30 XFR=100:XMK=90:XHR=70:XMI=90:XSE=90
NV 40 YFR=80:YMK=72:YHR=56:YMI=72:YSE=72:
   YDI=34:ALRMB=1645:HALF=0
IG 50 IF PEEK(ALRMB-1)<>80 THEN FOR X=ALR
   MB TO ALRMB+19:POKE X,0:NEXT X:POKE AL
   RMB-1,80
TI 60 DIM S$(2),T$(2),M$(2),W$(8):SHFT=60
   *12*3680:CX=160:CY=95:PIOVER2=1.5708:G
   OSUB 670:GRAPHICS 0:SETCOLOR 2,0,0
SQ 70 CLOSE #4:OPEN #4,12,0,"K:"
MX 80 ? "Is the clock already set? (Y/N)"
   :? "(Or press H for HELP.)";
FS 90 GET A#4,A:IF A=89 THEN GOSUB 1480:GO
   TO 260
QW 100 IF A=72 THEN GOSUB 1360:GOTO 80
XQ 110 IF A<>78 THEN 90
CQ 120 ? "N"
GZ 130 TRAP 140:? :PRINT "Is it (A)M or (
   P) M?";
GX 140 GET #4,A:IF A=65 THEN S$="AM":GOTO
    170
FE 150 IF A=80 THEN S$="PM":GOTO 170
NK 160 GOTO 140
DY 170 ? CHR$CA)
YM 180 TRAP 180:PRINT " What is the hour
   ";:INPUT H
TF 190 TRAP 190:PRINT " How many minutes
   ";:INPUT M
WI 200 TRAP 208:PRINT " How many seconds
   ";:INPUT S
IT 210 TRAP 0:GOSUB 1480
CY 220 JIF=60*(S+60*M+3600*H):IF H<>12 AN
   D S$="PM" THEN JIF=JIF+SHFT
KN 230 IF H=12 AND S$="AM" THEN JIF=JIF+S
   HFT
GI 240 B18=INT(JIF/65536):JIF=JIF-65536*B
   18:B19=INT(JIF/256):B20=JIF-256*B19
MR 250 POKE 20,0:POKE 18,B18:POKE 19,B19:
   POKE 20,B20:REM POKE 20,0 INSURES NO T
   URNOVERS WHILE SETTING.
RU 260 JIF=(PEEK(18)*65536+PEEK(19)*256+P
   EEK(20)):IF JIF>5399999 THEN JIF=JIF-5
   184000:GOTO 240
WS 270 JIF=INT(JIF/60)
PF 280 H=JIF/3600:JIF=JIF-3600*INT(H):M=J
   IF/60:S=JIF-60*INT(M):IH=INT(H):IM=INT
   (M)
ZA 290 H=H/1.9188-PIOVER2:M=M/9.5493-PIOV
   ER2:S=S/9.5493-PIOVER2
JX 300 IF PEEK(77)>122 THEN POKE 77,122
ZW 310 SETCOLOR 1,0,15.4-PEEK(77)/10
CI 320 GOSUB 560:REM DRAW CLOCK FRAME
LD 330 PLOT CX,CY:DRAWTO CX+0.7*XHR*COS(H
   -0.12),CY+0.7*YHR*SIN(N-0.12):DRAWTO C
   X+XHR*COS(H),CY+YHR*SIN(H)
UH 340 DRAWTO CX+0.7*XHR*COS(H+0.12),CY+0
   .7*YHR*SIN(H+0.12):DRAWTO CX,CY
ME 350 PLOT CH+1,CY:DRAWTO 1+CX+0.7*XHR*C
   OS(H-0.12),CY+0.7*YHR*SIN(H-0.12):DRAW
   TO 1+CX+XHR*COS(H),CY+YHR*SIN(H)
FD 360 DRAWTO 1+CX+0.7*XHR*COS(H+0.12),CY
   +0.7*YHR*SIN(H+0.12):DRAWTO 1+CM,CY
QG 370 PLOT CX,CY:DRAWTO CX+0.7*XMI*COS(M
   -0.07),CY+0.7*YMI*SIN(M-0.07):DRAWTO C
   X+XMI*COS(M),CY+YMI*SIN(M)
MM 380 DRAWTO CX+0.7*XMI*COS(M+0.07),CY+0
   .7*YMI*SINCM+0.07):DRAWTO CX,CY
TH 390 PLOT CX+1,CY:DRAWTO 1+CX+0.7*XMI*C
   OS(M-0.07),CY+0.7*YMI*SIN(M-0.07):DRAW
   TO 1+CM+XMI*COS(M),CY+YMI*SIN(M)
XJ 400 DRAWTO 1+CH+0.7*XMI*COS(M+0.07),CY
   +0.7*YMI*SIN(M+0.07):DRAWTO 1+CX,CY
ZL 410 REM PLOT CX,CY:DRAWTO CX+XSE*COS(S
   ),CY+YSE*SIN(S)  :REM TOO SLOW!!!
HT 420 REM PLOT CX+1,CY:DRAWTO 1+CX+XSE*C
   OS(S),CY+YSE*SIN(S)
TP 430 GOSUS 1240
UL 440 IF PEEK(764)=33 THEN GOSUS 940
JA 450 IF PEEK(764)=28 THEN GRAPHICS 0:GO
   SUB 1200:CLR :END
WW 460 POKE 764,255
DI 470 X=USR(1536)
AF 480 IF IM=0 AND HALF=0 THEN GOSUB 760
JR 490 IF IM=1 THEN HALF=0
BD 500 IF IM=30 AND HALF=0 THEN GOSUB 810
JT 510 IF IM=31 THEN HALF=0
EG 520 C=0
NV 530 IF IH=PEEK(ALRMB+2*C) AND IM=PEEK(
   ALRMB+2*C+1) THEN GOSUB 890:GOTO 260
GJ 540 C=C+1:IF C<18 THEN 530
OS 550 GOTO 260
BD 560 FOR C=0 TO 11:PLOT X(C),Y(C):DRAWT
   O EX(C),EY(C):NEXT C
UT 570 FOR C=0 TO 11:PLOT X(C)+1,Y(C):DRA
   WTO EX (C)+1,EY(C):NEXT C
XV 580 FOR C=0 TO 11:PLOT X(C)+2,Y(C):DRA
   WTO EX(C)+2,EY(C):NEXT C
AP 590 PLOT PX(11),PY(11):FOR C=0 TO 11:D
   RAWTO PX(C),PY(C):NEXT C
QE 600 PLOT PX(11)+2,PY(11):FOR C=0 TO 11
   :DRAWTO PX(C)+2,PY(C):NEXT C
NO 610 PLOT PX(11)+1,PY(11):FOR C=0 TO 11
   :DRAWTO PX(C)+1,PY(C):NEXT C
RG 620 PLOT PX1(11)+2,PY1(11):FOR C=0 TO
   11:DRAWTO PX1(C)+2,PY1(C):NEXT C
OM 630 PLOT PX1(11)+1,PY1(11):FOR C=0 TO
   11:DRAWTO PX1(C)+1,PY1(C):NEXT C
XU 640 PLOT PX1(11),PY1(11):FOR C=0 TO 11
   :DRAWTO PX1(C),PY1(C):NEXT C
ZK 650 FOR X=0 TO 4:FOR Y=X TO 5:PLOT PX2
   (X),PY2(X):DRAWTO PX2(Y),PY2(Y):NEXT Y
   :NEXT X
ZO 660 RETURN
XK 670 DIM X(11),Y(11),EX(11),EY(11),PX(1
   1),PY(11),PX1(11),PY1(11),PX2(11),PY2(
   11):C=0
PP 680 FOR X=0 TO 6.27 STEP 0.5236:X(C)=C
   OS(X)*(XMK-2)+CX:EX(C)=COS(X)*(XMK+2)+
   CX
TQ 690 Y(C)=SIN(X)*(YMK-2)+CY:EY(C)=SIN(X
   *(YMK+2)+CY
NW 700 PX1(C)=COS(X)*XFR*1.1+CX:PY1(C)=SI
   N(X)*YFR*1.1+CY
EE 710 PX(C)=COS(X)*XFR+CX:PY(C)=SIN(X)*Y
   FR+CY:C=C+1:NEXT X
MZ 720 FOR C=0 TO 5:PX2(C)=COS(C*1.0472)*
   XFR/5+CX:PY2(C)=SIN(C*1.0472)*YFR/5+CY
   NEXT C
CE 730 IF PEEK(1640)<>96 THEN GOTO 30000
ZL 740 RETURN
SK 750 REM ROUTINE TO DO HOURLY CHIME
BC 760 HALF=1:GOSUB 850:FOR C=1 TO N:FOR
   X=3 TO 15 STEP 4:SOUND 0,100,10,X:SOUN
   D 1,50,12,X:SOUND 2,102,10,X:NEXT X
KV 770 FOR X=14 TO 0 STEP -0.3:SOUND 0,10
   0,10,X:SOUND 1,50,12,X:SOUND 2,102,10,
   X:NEXT X
SJ 780 FOR X=1 TO 200:NEXT X:NEXT C
ZV 790 RETURN
PL 800 REM ROUTINE TO DO HALF HOUR CHIME
RL 810 HALF=1:GOSUB 850:FOR X=10 TO 3 STE
   P -1:FOR C=0 TO 1:SOUND C,100+C,10,X:N
   EXT C:NEXT X
ZB 820 FOR X=10 TO 0 STEP -1:FOR C=0 TO 3
   :SOUND C,100+C,10,X:NEXT C:FOR C=1 TO
   25:NEXT C:NEXT X
ZK 830 RETURN
QC 840 REM ROUTINE USED BY HOURLY/HALF HO
   UR CHIME FOR BOOKEEPING AND TO SILENCE
    CHIMES AT LATE NIGHT.
JV 850 IF IH<8 OR IH>22 THEN POP :RETURN
   :REM QUIET MODE FROM 11PM TO 7:59AM
QH 860 N=IH:IF IH>12 THEN N=IH-12
ZS 870 RETURN
YB 880 REM ALARM SOUND ROUTINE
QP 890 POKE 764,255:U=30:SETCOLOR 1,0,15:
   POKE 77,0
RV 900 FOR X=1 TO 23:FOR Y=0 TO 255 STEP
   3:SOUND 0,Y,10,15:SOUND 1,U,10,15:U=U+
   1:IF U>255 THEN U=0
OI 910 IF PEEK(764)=255 THEN NEXT Y:NEXT
   X
CH 920 POKE 764,255:SOUND 0,0,0,0:SOUND 1
   ,0,0,0:RETURN
KX 930 REM ROUTINE TO SET ALARMS. NOTE A
   LARMS ARE SET IN MEMORY SO ARE NOT LOS
   T BETWEEN RUNS
SM 940 GRAPHICS 0:SETCOLOR 2,0,0:FOR X=0
   TO 9:? CHR$(65+X);"] ";:C=PEEK(ALRMB+2
   *X)
TG 950 IF C=0 THEN ? "No alarm set.":NEXT
   X:GOTO 970
LC 960 D=PEEK(ALRMB+1+2*X):GOSUB 1140:? C
   ;":";M$;" ";T$:NEXT X
YN 970 ? :? "Press: X to exit or":? "
      A-J to set an alarm"
SJ 980 GET #4,A:IF A=88 THEN GOSUB 1480:R
   ETURN
NJ 990 IF A<65 OR A>74 THEN 980
BQ 1000 A=A-65:IF PEEK(ALRMB+2*A)=0 THEN
   1030
WT 1010 ? :? "";CHRS(A+193);"
   > ";
IK 1020 C=PEEK(ALRMB+2*A):D=PEEK(ALRMB+1+
   2*A):GOSUB 1140:? C;":";M$;" ";T$
FF 1030 ? :? :? "             Set [";CHR$
   (A+65);"] for what hour":? "
      (0 to unset alarm)";:INPUT C
TV 1040 IF C=0 THEN POKE ALRMB+2*A,C:GOTO
    940
WP 1050 ? "How many minutes after the hou
   r";:INPUT D:POKE ALRMB+2*A+1,D
UJ 1060 PRINT "                   (A)M or
    (P)M?";
TV 1070 GET #4,X:IF X=65 THEN T$="AM":GOT
   O 1100
QQ 1080 IF X=80 THEN T$="PM":GOTO 1100
RC 1090 GOTO 1070
IJ 1100 IF C<12 AND T$="PM" THEN C=C+12
WE 1110 IF C=12 AND TS="AM" THEN C=C+12
RJ 1120 POKE ALRMB+2*A,C:GOTO 940
KD 1130 REM TO CONVERT C(HR) AND D(MIN) T
   O PRINTABLE FORMS C;M$;T$
VF 1140 T$="PM":IF C<12 OR C=24 THEN T$="
   AM"
HR 1150 IF C>12 THEN C=C-12
IY 1160 IF D<10 THEN M$="0":M$(2)=STRS(D)
   :GOTO 1180
EK 1170 M$=STR$(D)
AZ 1180 RETURN
ZF 1190 REM ROUTINE TO SHOW CURRENT TIME
   VAUES. USEFUL FOR TIMING SOMETHING.
IY 1200 JIF=PEEK(18)*65536*PEEK(19)*256+P
   EEK(20):? "JIFFIES = ";JIF:JIF=INT(JIF
   /60):? "SECONDS = ";JIF
LY 1210 C=INT(JIF/3600):JIF=JIF-3600*C:D=
   INT(JIF/60):S=JIF-60*D:GOSUB 1140:? "T
   IME IS> ";C;":";M$;":";S;" ";T$
AJ 1220 RETURN
QA 1230 REM PRINT TIME IN DIGITAL FORM TO
   O
TN 1240 WID=36
EB 1250 COLOR 0:FOR X=CY+YDI+1 TO CY+YDI+
   10:PLOT CX-WID,X:DRAWTO CX+WID,X:NEXT
   X
BS 1260 COLOR 1:FOR X=CY+YDI TO CY+YDI+11
    STEP 11:PLOT CX-WID,X:DRAWTO CX+WID,X
   :NEXT X
EJ 1270 FOR X=0 TO 1:PLOT CX-WID+X,CY+YDI
   :DRAWTO CX-WID+X,CY+YDI+11:NEXT X
YS 1280 FOR X=0 TO 1:PLOT CX+WID+X,CY+YDI
   :DRAWTO CX+WID+X,CY+YDI+11:NEXT X
AI 1290 C=IH:D=IM:GOSUS 1140:W$=STR$(C):I
   F C<10 THEN W$=" ":W$(2)=STR$(C)
PI 1300 W$(3)=":":W$(4)=MS:W$(6)=" "
GU 1310 W$(7)=T$:Y=CY+YDI+2:X=CX-WID:GOSU
   B 1330:RETURN
LL 1320 REM WRITE STRING W$ AT X,Y IN GR.
   8
TN 1330 A=40*Y+PEEK(88)+PEEK(89)*256+INT(
   (X)/8):BS=256*PEEK(756)
JY 1340 FOR C=1 TO LEN(W$):D=ASC(W$(C,C))
   :IF D<95 THEN D=D-32
BS 1350 BT=BS+D*8:FOR CC=BT TO BT+7:POKE
   A+C+40*(CC-BT),PEEK(CC):NEXT CC:NEXT C
   :RETURN
IS 1360 GRAPHICS 0:SETCOLOR 2,0,0:SETCOLO
   R 1,0,10
OD 1370 ? "
   "
FX 1380 ? :? ""
ST 1390 ? "Since this clock works off an
   internalclock, you will only have to s
   et the"
SD 1400 ? "time every time you power up t
   he":? "computer.  To set the clock jus
   t"
QF 1410 ? "answer the questions.  To set
   the":? "clock accurately, enter the se
   conds"
XO 1420 ? "question just as that time pas
   ses."
ND 1430 ? :? ""
TD 1440 ? "You can ,set up to 10 alarms.
   To set alarms, press the space-bar."
WZ 1450 ? :? ""
UX 1460 ? "To quit the clock just press E
   SC."
DN 1470 ? :? :RETURN
YW 1480 GRAPHICS 24:SETCOLOR 2,0,4:SETCOL
   OR 4,0,6:COLOR 1:RETURN
TV 30000 RESTORE 30010:FOR X=1536 TO 1627
   :READ Y:POKE X,Y:NEXT X:RETURN
PW 30010 DATA 160,0,24,173,48,2,105,5,133
   ,203,173,49,2,105 0,133,204,165,203,10
   5,96,133,205,165,204,105,0,133
QS 30020 DATA 206,173,49,2,56,233,31,141,
   230,2,165,89,209,203,208,7,173,230,2,1
   33,89,208,13,170,177,203,133
WW 30030 DATA 89,130,145,203,24,105,15,14
   5,205,165,89,141,80,6,165,80,141,79,6,
   169,0,162,30,153,80,127,200
WF 30040 DATA 208,250,238,80,6,202,208,24
   4,104,96


LISTING 8: BASIC

ZK 10 GRAPHICS 0:GOSUB 30000:SETCOLOR 2,0
   ,0:SETCOLOR 1,0,8
ZI 20 ? " 
   ":? :? "To use program, use joystic
   k to move"
BY 30 ? "graphics cursor around. Press t
   he":? "fire button to mark points for
   the"
SJ 40 ? "program to connect. When you ar
   e":? "done, press START. Now, pressin
   g the"
EE 50 ? "joystick UP and DOWN will shrink
    and":? "expand the picture. Moving t
   he"
YZ 60 ? "joystick left and right will mov
   e the"
VD 70 ? "picture left and right. Pressin
   g the":? "fire button will rotate the
   picture":? "10 degrees."
IC 80 ? :? "If you try to move the pictur
   e too":? "far left or right or a rotat
   ion moves"
IU 90 ? "Part of the picture off the scre
   en,":? "the picture automatically shri
   nks to":? "fit!"
UF 100 OPEN #3,4,0,"K:":? :? "Press
   to begin.":GET #3,A:CLOSE #3
AF 110 CX=160:CV=96:C10=COS(0.17453):S10=
   SIN(0.17453)
ZT 120 GRAPHICS 24:COLOR 1:SETCOLOR 2,0,0
AD 130 FOR X=16 TO 319 STEP 16:PLOT X,CY:
   DRAWTO X+1,CY:NENT X:FOR Y=12 TO 191 S
   TEP 12:PLOT CX,Y:DRAWTO CX+1,Y:NEXT Y
MV 140 DIM A(100),B(100):P=1:X=0:Y=0
JR 150 FOR T=0 TO 1:LOCATE CX+X,CY+Y,Z:CO
   LOR (Z=0):PLOT CX+X,CY+Y&:NEXT T
PY 160 A=15-STICK(0):IF A>7 THEN A=A-8:IF
    CH+X<318 THEN X=X+1
RN 170 IF A>3 THEN A=A-4:IF CX+X>1 THEN X
   =X-1
RR 180 IF A>1 THEN A=A-2:IF CY+Y<190 THEN
   Y=Y+1
TG 190 IF A>0 THEN A=A-1:IF CY+Y>1 THEN Y
   =Y-1
SU 200 IF STRIG(0)=0 AND P=1 THEN A(1)=X:
   B(1)=Y:COLOR 1:PLOT X+CX-1,Y+CY:P=P+1
CC 210 IF STRIG(0)=0 AND P>1 THEN A(P)=X:
   B(P)=Y:COLOR 1:PLOT X+CX,Y+CY:DRAWTO A
   (P-1)+CX,B(P-1)+CY:P=P+1
UM 220 IF STRIG(0)=0 THEN 220
GJ 230 IF PEEK(53279)=7 THEN 150
ZG 240 IF P<3 THEN ? "You must draw at le
   ast 1 line!":END
AE 250 P=P-1:COLOR 1:W=1:S=0
KJ 260 TRAP 350:PLOT S+A(1)*W+CX,B(1)*W+C
   Y
NN 270 FOR X=2 TO P:DRAWTO S+A(X)*W+CX,B(
   X)*W+CY:NEXT X
DI 280 X=USR(15361
NU 290 IF STICK(0)=13 THEN W=W+0.1:GOTO 2
   60
AJ 300 IF STICK(0)=11 AND S>6-CX THEN S=S
   -5:GOTO 260
RV 310 IF STICK(0)=7 AND S<CX-6 THEN S=S+
   5:GOTO 260
ID 320 IF STICK(0)=14 AND W>0 THEN W=W-0.
   1:GOTO 260
OF 330 IF STRIG(0)=0 THEN FOR X=1 TO P:A=
   A(X):B=B(X):A(X)=A*C10-B*S10:B(X)=A*S1
   0+B*C10:NEXT X:GOTO 260
PV 340 GOTO 290
FL 350 W=W-0.1:GOTO 260
TV 30000 RESTORE 30010:FOR X=1536 TO 1627
   :READ Y:POKE X,Y:NEXT X:RETURN
PW 30010 DATA 160,0,24,173,48,2,105,5,133
   ,203,173,49,2,105,0,133,204,165,203,10
   5,96,133,205,165,204,105,0,133
Q5 30020 DATA 206,173,49,2,56,233,31,141,
   230,2,165,89,209,203,208,7,173,230,2,1
   33,89,208,13,170,177,203,133
WW 30030 DATA 89,138,145,203,24,105,15,14
   5,205,165,89,141,80,6,165,88,141,79,6,
   169,0,162,30,153,80,127,200
WF 30040 DATA 208,250,238,80,6,202,208,24
   4,104,96


LISTING 9: ASSFMBLY

10 ;Page flipping routine. After using
20 ;GR.24, GR.9-11, call this routine
30 ;with X=USR(1664) to copy backgrnd
40 ;to foregrnd page. Backgrnd page
50 ;is the one PLOT and DRAWTO
60 ;effect, and foregrnd is the one
70 ;displayed.
80 ;To clear the whole background
90 ;screen, just do an X=USR(1715)
0100 ;
0110     *=  $0680
0120 MEMTOP = $02E5  Men Top pointer.
0130 SAVMSC = $58    Video mem pointer
0140 SDLSTL = $0230  Pointer to DL
0150     LDX SDLSTL+1 Find an
0160     INX         area
0170     STX FORPNT+1 to store
0180     TXA         background
0190     SEC         screen
0200     SBC #32     and set
0210     STA MEMTOP+1 up the
0220     STA SAVMSC+1 indexed
0230     STA BAKPNT+1 addressing
0240     LDA SAVMSC  commands
0250     STA BAKPNT  for
0260     STA FORPNT  copying.
0270     LDY #0      Copy
0280     LDX #30     the
0290 LOOP LDA 0,Y    7680
0300     STA 0,Y     byte
0310     INY         background
0320     BNE LOOP    buffer
0330     INC BAKPNT+1 to
0340     INC FORPNT+1 the
0350     DEX         foreground
0360     BNE LOOP    screen.
0370     PLA         Unused parameter
0380     RTS         Return.
0390 CLS LDA SDLSTL+1 Make sure
0400     SEC         there's a
0410     SBC #31     background
0420     STA MEMTOP+1 screen.
0430     STA CLSPNT+1 Set
0440     LDA SAVMSC  indexed
0450     STA CLSPNT  addressing.
0460     LDA #0      Fill
0470     LDX #30     the
0480     LDY #0      7680
0490 LOOP2 STA 0,Y   byte
0500     INY         background
0510     BNE LOOP2   buffer
0520     INC CLSPNT+1 with
0530     DEX         zeroes
0540     BNE LOOP2   (clear).
0550     PLA         Pull unused argument.
0560     RTS         Return.
0570 CLSPNT = LOOP2+1
0580 BAKPNT = LOOP+1
0590 FORPNT = LOOP+4
0600     .END


LISTING 10: BASIC
TN 31000 RESTORE 31010:FOR X=1664 TO 1751
   :READ Y:POKE X,Y:NEXT X:RETURN
BF 31010 DATA 174,49,2,232,142,164,6,138,
   56,233,32,141,230,2,133,89,141,161,6,1
   65,88,141,160,6,141,163,6
ZY 31020 DATA 160,0,162,30,185,80,127,153
   ,80,159,200,208,247,238,161,6,238,164,
   6,202,208,238,104,96,173,49
EA 31030 DATA 2,56,233,31,141,230,2,141,2
   04,6,165,88,141,203,6,169,0,162,30,160
   ,0,153,80,127,200,208,250
UY 31040 DATA 238,204,6,202,208,244,104,9
   6