TweetFollow Us on Twitter

Video Screen Anim
Volume Number:2
Issue Number:6
Column Tag:Pascal Procedures

Alternate Video Screen Animation

By Mike Morton, Lotus Corp.

Introduction

This article discusses using the Mac's alternate hardware screen to get smooth, flicker-free animation. We'll use the “vertical retrace manager” to synchronize screen-switching with the hardware screen refresh. The subroutine package presented does this synchronizing and helps the calling program ensure that it's drawing animation frames at the desired speed. It also tells the program how much “slack” time it has after drawing each frame. The source for a simple bouncing square demo program which uses the subroutines is included here. Another program, “Vanlandingham”, uses the same package to mimic Amiga's famous bouncing ball demo, but its source is too convoluted to publish. Both applications are distributed on MacTutor's source code disk #9.

Hidden Gotcha

Warning: These programs use the alternate screen buffer, an area of memory that is also used by the Mac Plus RAM cache. As a result, when the program exits, Finder information previously in the cache will be lost causing a system crash. To prevent this, turn off your RAM cache with the control panel. It's possible to design an interface to let the system know your application is using the alternate video buffer, forcing the Mac to configure the cache somewhere else. We invite readers to investigate how this might be done. For similar reasons, this program doesn't work with all software or hardware. It may not like RAM disks, debuggers, or some hard disks. As with any application which doesn't have a QA group to test it, you should back up your files and take other precautions before trying it.

The animation package uses two slightly obscure features of the Mac, one in hardware and one in software. The hardware feature is the alternate screen, a second bitmap in RAM which can be displayed instead of the default one. The software feature is the vertical retrace manager, which calls subroutines periodically, in sync with the “vertical retrace interrupt”, a sort of hardware heartbeat in the Macintosh. Both of these are covered only briefly here, since they're documented elsewhere.

Quick review #1: The alternate screen

The Mac hardware redraws the screen 60 times a second, repeatedly reading the screen from RAM and interpreting bits as black or white pixels. By default, it reads from a hardwired address, available from the base address of Quickdraw's “screenBits” bitmap. The hardware can also read from a second address, 32K lower than the default, and software can request that this alternate screen (sometimes called the alternate video “page”) be used.

Fig. 1 Bouncing Ball from the “Vanlandingham” Program

A simple animation program without the second screen just repeatedly erases and redraws images. The problem with this is that the hardware “strobes” the image in RAM at unpredictable times. The bitmap may be strobed between animation frames and the software can get caught with its pants down. The result is flicker.

A better solution is to use two screens. The hardware displays one screen while the next animation frame is drawn on the other. Software can detect when the current screen refresh is complete, and ask that the next refresh be drawn from the newly drawn bitmap. Then while that bitmap is showing, the original bitmap can be erased and filled with another frame. This way, drawing and display always use different screens. The result is much less flicker.

Apple didn't make it easy to use the alternate screen. The decision to configure the alternate screen is usually made by the program which launches you; the Finder won't ever do it, though. You can't reserve the second screen through traditional memory management after launch, because you can't specify the area to allocate. The generally accepted method of getting the second screen is to check whether it's available at startup and, if it's not, to re-launch oneself with a request for the second screen. (Of course, if the request ever fails for any reason, such a program will loop forever )

There are some problems to keep in mind when using the alternate screen:

•future Macintosh models may not support it (the May 1985 software supplement warned about this)

•cursor handling seems to assume that the default screen is in use; do a HideCursor call before switching

•you should switch to the main screen before exiting to the Finder

•Debuggers, RAM disks, and other things which survive across launches may occupy the memory where the alternate screen is; this is hard to detect and avoid

For more on the alternate screen, see Tom Taylor's article on the subject in the August 1985 issue of MacTutor. For a detailed description of the hardware, I recommend The IM Underground. The latter has some ideas on allocating the screen.

Quick review #2: Vertical retrace tasks

The Mac operating system lets you leave “wake-up calls” for subroutines, allowing you to specify the delay (in 60ths of a second) before the subroutine is called. This is handy for a lot of things, including screen switching.

Each time a screen refresh is completed, the Macintosh gets a “vertical retrace interrupt”. At this time, the system may perform several housekeeping tasks: updating the cursor position, bumping the tick count, and so forth. It also checks the list of subroutines which have left wake-up calls, seeing if it's time to call any of them.

Vertical retrace tasks, as these dozing subroutines are called, are pretty simple. The chapter of Inside Macintosh which describes the vertical retrace manager is just a few pages. Ignoring a few fields, the essential parts of the “queue block” for the task are (1) the address of the routine and (2) the time left until it should be called.

The exact time at which these routines are called is perfect for switching screens. It happens between screen refreshes, so the switch doesn't occur halfway down the screen (giving you a fleeting split image). [It is possible for many time-consuming retrace tasks to be called, and for the switch to be delayed until after the refresh has started. This is very unlikely.]

Some problems crop up in calling routines this way. The most insidious appears randomly when someone alters A5. This is legal as long as they don't call anyone who expects A5 to be correct. If an interrupt comes in while A5 is changed, the system will call the retrace tasks with the wrong value of A5. It's up to each task to either load the correct value from “CurrentA5” in low memory or not use A5 at all. Recent versions of Inside Macintosh describe the OS Utilities “SetUpA5” and “RestoreA5” which can be used to avoid this problem in Pascal. Note that even if your application doesn't mess with A5, desk accessories and the ROM still may. Be paranoid.

Lesser problems with vertical retrace tasks include:

•you should make sure that your task is gone from the queue before leaving the application; otherwise someone else's code (such as the Finder's) will be loaded over the task's old address and the vertical retrace manager will call that address with horrifying results

•your task can't perform any memory management, since the state of the heap may not be consistent; in general, you must be careful about any operations on global data structures

•the task must set its “alarm” each time it's called, or it won't be called again

Inside Macintosh covers the vertical retrace manager pretty well. Robert Denny's article in the August 1985 MacTutor gives a good example of using these tasks.

Fig. 2 Our bouncing square demo program

Putting it all together

Here's a package to synchronize screen switching. It has three subroutines:

getaltscreen -- This should be the first thing your application calls. It checks whether the alternate screen is configured and, if not, relaunches the application to include the screen.

This routine is independent of the other two.

vidstart -- This installs a vertical retrace task which runs at the frequency you choose. The caller and these subroutines share a structure used to pass information both ways.

To switch screens, the application stores a request in the shared information block. The video task wakes up, finds the request, switches the screen as appropriate, and flags that there's no longer a request pending.

There are four states for the request field:

0: request for alternate screen is pending

1: request for default screen is pending

2: request to stop the task is pending

3: no request is pending

Ideally, the task wakes up at the end of each cycle, finds a pending request and performs it. But what if there isn't a request pending? That's fine -- the task just sets an alarm to re-examine the request one tick from now (not one whole cycle, which could be many ticks) and tries again. This way, even if the next frame isn't ready on time, it'll be displayed at the first possible retrace interrupt after it becomes ready.

When the task discovers this situation (an animation cycle has elapsed but no request was stored), it means the main program didn't draw its next frame in time. The programmer needs to know that this happens, so these events are kept track of in the “delays” field in the information block. The program doesn't have to examine the counter, but you may want to print it periodically while you're developing the program, to make sure the animation rate is what you expect.

vidwait -- Suppose a program finishes drawing a frame on the alternate screen and stores a request to show that screen. The next thing it wants to do is draw the next frame on the default screen. But it can't yet -- it has to wait for the switch so that the default screen is no longer being displayed. This is easy: when it sees the request flag is in the “no request pending” state then it knows the task has done the switch. The vidwait routine waits for this to happen. You should always call it before drawing a new frame.

As mentioned above, the task notices whether your drawing code is fast enough to keep up with the screen switching task. But suppose you try a new drawing method to see if it's faster. You run both methods, and both keep pace with the task. Can you measure the speed of the tasks more finely? Yes: vidwait does it by counting how many times it loops while waiting for the request to be done and returning the count. A larger count means more slack time between the drawing and the screen switch, which means the drawing code is faster. This kind of fine tuning helped in getting the bouncing ball to run at 60 Hz.

Notes on the subroutines

The information block, which is defined by the main program and passed to the subroutines, includes a task block for the vertical retrace task. It also has the request field, the period (number of ticks between screen switches), and the counter used to keep track of delays.

The problem of whether A5 is valid is bypassed by making the information block contiguous to the VBL task -- the system passes a pointer to the task block when it calls the routine, so the information block is easy for the task to find.

When the task sees a request to stop (probably because the application is quitting) it doesn't call VRemove; tasks shouldn't dequeue themselves. Instead, it clears the “count” field of the task block, telling the system not to leave it in the queue.

Notes on the sample application

The application which demonstrates these routines is pretty dull. It bounces a square around the screen until it detects a key press or mouse click.

The constant “tickrate”, the number of ticks per frame, is set to 1, so the animation rate is 60 Hz. I was surprised that 30 Hz didn't look smooth. (Perhaps 30 Hz would look better if the image was more complex and distracting to viewers.)

You can play with the animation rate and the block should move at the same speed -- the velocity (in real time) should be independent of the animation rate.

The program must keep track of where the block was last drawn in the default and alternate screens. The rectangles “deflast” and “altlast” are used for this, so that the code knows what area to erase before drawing the next block.

I've commented out the statistics code used to keep track of whether the animation is at the desired rate. It may need changes if you're not using Lisa Pascal (as I was before this was translated to TML Pascal). [The statistics procedure is here, but printing code is left to the user to implement. -Ed]

Printing statistics was a problem: each time I printed a line, it caused some delays, which the program reported the next time. This is like physics: observing affects the thing being observed! I couldn't stop the interference, so I made the printing occur with exponentially decreasing frequency: it's done after 256 frames, 512 frames, 1024 frames, etc. (Check out the code that checks for a power of 2!) The average number of delays per sample will approach zero if the delays are only an artifact of the debugging.

Before stopping, the program makes sure the task is stopped. The request to stop makes the task deactivate and also returns to the default screen to keep the Finder happy. (Remember, a RAM cache on a Mac Plus will not be happy, since some vital Finder info will be destroyed by the use of the alternate screen buffer.)

You may want to vary the size of the block, to see how large you can make it before the drawing takes too long and delays start getting recorded. This may vary slightly between Pascal compilers, but isn't a very exact benchmark. For those advanced programmers, try to come up with a technique so that even the RAM cache will be spared.

Program Compilation

[To implement the source code listed here, use either the MDS assembler or Consulair C 4.53 to edit and assemble the assembly language subroutines into a ".REL" file. Then compile the Pascal source code for the bouncing square demo using TML Pascal 1.11 or later. The link file shown here can be used with either the TML Linker or the Consulair Linker. Both linkers can apparently recognize and open object files created by the other. As reported previously, we have been using the Consulair C as an assembler until Apple updates the MDS system to work correctly on a Mac Plus. The link file will link the assembly subroutines with the Pascal source to create the final application. Three system files from the Pascal system are required. These are the Pas$Library package, and the OSTraps and ToolTraps ".REL" files. Once you figure out how to satisfy everybody's interpretation of how to construct a path name to the include and system files, the compilation and linking are easy.

We must remark that Apple apparently forgot that some programs, like development systems, must have a way to find specific files without a user dialog. The HFS system makes every folder an independent directory with no provision for searching everything for a certain file. In fact, even if such provision were made, you would have problems because files can now have the same name if in different directories (folders), so how would you know you got the right file? This omission is causing fits for language system developers trying to figure out the best way to tell their compiler where to find a run time package, include file or ".REL" file. The result is everyone is making up their own solution to this problem, destroying the standardization of the Mac interface. Give Apple a slap on the wrist for over-looking this problem.-Ed]

Summary

The alternate hardware screen can help create rapid animation; switching screens at the right time can avoid flicker. A regular task called in sync with the screen refresh can make switching between screens very easy.

An animation program under development needs to keep track of whether its animation is running at the desired rate and how much spare processing time remains in each animation cycle. Both of these are easy to record, although sometimes hard to report.

There are a variety of pitfalls in the alternate screen and the vertical retrace manager. Probably the most significant one for serious applications is that the alternate screen may disappear in future Macintoshes. Still, if you're doing animation more for fun than for profit, it's worth the extra effort to get smooth animation.

; vid -- Routines to synchronize switching between the two
; hardware screens in sync with the screen refresh, allowing
; smooth animation.   mike morton, June 1986.

; function  vidwait    (VAR block: vidinfo): longint;external;
; procedure vidstart   
 (VAR block: vidinfo; period: integer);  external;
; procedure getaltscreen; external;

; MDS or Consulair Assembler Version.
; Converted by David E. Smith for MacTutor.

 xdef vidwait
 xdef vidstart
 xdef getaltscreen
 
 Include QuickEqu.D; MDS toolbox equates and traps
 Include SysEqu.D
 Include ToolEqu.D
 Include MacTraps.D

 MACRO .equ = equ| ; convert Lisa stuff to MDS Mac
 MACRO _hidecurs = _HideCursor|
 MACRO _showcurs = _ShowCursor|
 
; definition of state information record. 
; Also defined on Pascal side. 

vblsize .equ     vblphase+2      ; size of VBL block
ivbl    .equ     0          ; VBL task information
ireq    .equ      ivbl+vblsize     ; (int) request: 
 ;alt/default/stop/none
iperiod .equ     ireq+2          ; (int) period in ticks
idelays .equ      iperiod+2        ; (longint) number of times 
 ;we had to delay for a tick
isize   .equ     idelays+4         ; size of “info” block

viabase .equ$1D4 ; VIA base address global 
CurPage .equ$936 ; alt screen page global 

; request definitions: the high level code saves these for us 
; *** note: the code at “vbl” depends on these values. ***

reqalt  .equ    0; request switch to alternate screen
reqdef  .equ    1; request switch to default screen
reqstop .equ    2; stop on default screen
reqnone .equ    3; nothing requested

; ----------------------------------------------------------------------------
;
; function vidwait (block: ptr): longint; external;
; this routine is called when the next animation frame is ready
; to be shown and the main program has made a request to
; display that frame.  it waits until the new frame is being 
; shown, then returns.  it also returns the
; approximate time it had to wait, in arbitrary units.  this helps
; you find out how much slack you have between the time the 
;frame is ready and the time it's actually used.

vidwait:
        move.l   (SP)+,A1          ; pop return
        move.l    (SP)+,A0         ; pop block pointer
        moveq    #-1,D0   ; will be 0 if we exit right away

vidw2:
 addq.l   #1,D0  ; bump spinning counter
        cmp.w    #reqnone,ireq(A0)       ; is nothing waiting?
        bne.s    vidw2    ; something pending: 
 ; don't return yet
        move.l   D0,(SP)  ; return “time” waited
        jmp      (A1)         ; return

; ----------------------------------------------------------------------------

; procedure vidstart (block: ptr; period: integer); external;

vidstart:
        move.l   6(SP),A0         ; point to VBL block 
        move.w   4(SP),D0         ; pick up desired period

        move.w   #reqnone,ireq(A0) ; initialize req info “none”
        move.w   D0,iperiod(A0)    ; save period
        clr.l     idelays(A0) ; no delays seen yet

        move.w   #vType,vblType(A0)      ; set type of queue elem
        lea      vbl,A1   ; point to VBL routine to call
        move.l   A1,vblAddr(A0)  ; save address of routine
        move.w    D0,vblCount(A0)  ; set time to wait 
        clr.w    vblPhase(A0)          ; zero phase

        _vInstall; install the vertical retrace task
        tst.w    D0; good status?
        bne.s    death  ; no: bag it

@1  move.l  (SP)+,A0 ; pop return
        addq.l   #4+2,SP  ; ding one long, one integer param
        jmp      (A0); and return with the task queued
; ----------------------------------------------------------------------------
; this is our entry point from the vertical retrace manager.
; entered by: A0 points to VBL block; our state information 
; follows this.
;
; - if no request has been made by the time we're called, the
;   main program didn't have a frame to be drawn in time; in this
;   case, we increment the “delays” counter and set the alarm 
;   to wake us up one tick from now.
; - if a request is there, we do it and set the alarm for one full 
;   period.

vbl:    
 move.w   ireq(A0),D0       ; pick up the request
        move.w   #reqnone,ireq(A0) ; cancel pending request
        move.l   viabase,A1 ; point to base of VIA stuff

        dbra     D0,vbl1          ; skip if not zero
        bclr     #6,$1E00(A1)         ; alternate screen: clear bit
        move.w    iperiod(A0),vblCount(A0) 
 ; ask for next wake-up at usual time
        rts          ; exit

vbl1:   
 dbra     D0,vbl2        ; wasn't zero; skip if not one
        bset     #6,$1E00(A1)         ; default screen: set the bit
        move.w   iperiod(A0),vblCount(A0) 
 ; ask for next wake-up at usual time
        rts          ; ta da

vbl2:   
 dbra     D0,vbl3; wasn't zero or one; skip if not two
        clr.w    vblCount(A0) ; stop: don't want to run again
        bset     #6,$1E00(A1) ; default screen 
        rts          ; home for the last time

vbl3:   
 dbra     D0,death         ; wasn't 0,1, or 2; die if not three
        move.w   #1,vblCount(A0) ; no request: re-run
        add.l    #1,idelays(A0)              ; keep stats on how often 

        rts          ; and return

death:  DC.L     $60FE  ; execute a loop - stall
; ----------------------------------------------------------------------------
; procedure getaltscreen; external;
; this should be the VERY first thing your application calls.
; - if the alternate video screen IS configured, we return and do
;    nothing. 
; - if the screen isn't there, we find out the
; application's name from the application parameters and 
; relaunch ourselves, correctly configured.

getaltscreen:
        tst.l    CurPage  ; pick up current screen configuration
        bpl.s    relaunch ; negative means we got alt. screen
        rts ; all's well; return

relaunch:          ; here when we have to restart
        sub.l    #32+4+2,SP ; for Name, RefNum, Param
        move.l   SP,A0  ; point to base of this stuff
        pea      4+2(A0)  ; push arg1: VAR pointer to apName
        pea      4(A0)  ; push arg2: VAR ptr to apRefNum
        pea      0(A0)  ; push arg3: VAR pointer to apParam
        _GetAppParms ; fill in name, other junk; pop params

        ; Now apParam, apRefNum and (importantly) apName are 
 ; in stack temps.

        lea      4+2(SP),A0 ; point to the name we just got
        move.l   #-1,-(SP)  ; -1 asks for alternate screen...
        move.l   A0,-(SP) ; ...and point to name to launch
        move.l   SP,A0  ; point to name ptr and flags with A0
        _launch  ; launch ourselves, with alt screen
        bra.s    death  ; shouldn't reach here!
end


{ viddemo -- A simple program to demonstrate the “vid”
routines, which allow switching between the default and 
alternate video screens in sync with the screen refresh, 
allowing flicker-free animation. mike morton, may 1986}

program viddemo; { demonstrate screen-switching }

{$i MemTypes.ipas  } {TML Pascal include files}
{$i QuickDraw.ipas }
{$i OSIntf.ipas    }
{$i ToolIntf.ipas  }

const
 tickrate = 1; { ticks between animation frames }
 width = 120;  { size of the moving square }
        { Parameters for the “vidinfo.request” field. }
 reqalt = 0;{ request switch to alternate screen }
 reqdef = 1;{ request switch to default screen }
 reqstop = 2;  { request default screen; stop task }

type
      screentype = (default, alternate);  { current screen? }
 vidinfo = record       { data structure “vid” routines }
        v: VBLTask;{ the vertical retrace task block }
        request: integer; { current request:                   
 alt/def/stop/none }
        period: integer;  { ticks between frames }
        delays: longint;  { number of delays observed }
 end;

var
    myPort: grafPort;{ our graphics environment }
    vh, vv: integer; { velocity: h and v components }
    blockrect: rect; { rectangle for bouncing block }
    boundsrect:  rect;    { rect in which block bounces }
    anevent:   eventrecord; { for awaiting keystroke or click }
    theinfo:   vidinfo;   { info shared with vid routines }
    altbits:bitmap;{ bitmap for alternate screen }
    whichscr:  screentype;{ on alternate/default screen? }
    deflast, altlast: rect; { rectangle we last drew in }
    zot:  longint; { throwaway result, functions }
     
    totfreetime: longint; { statistics: accumulated free time }
    samples: longint;{ statistics: number of samples }

  function  vidwait    (VAR block: vidinfo): longint; external;
  procedure vidstart   (VAR block: vidinfo; period: integer);  
 external;
  procedure getaltscreen; external;

procedure init;  { initialize everything }
begin;
InitGraf(@thePort);  { fire up quickdraw }
OpenPort(@myPort); { get a drawing environment }
InitFonts;         { for writelns }
InitCursor; { set the arrow cursor }
HideCursor; { but we don't want it }
      
boundsrect := screenbits.bounds;  
 { display rectangle is full screen  }
InsetRect (boundsrect, 15, 15);   {  inset a bit }

SetRect (blockrect, 0, 0 , width, width); 
 { make up rect for square }
OffsetRect (blockrect, 15, 15);   
 { start it in the bounds rectangle }
altlast := blockrect;   { init “old” positions }
deflast := blockrect; 
 
altbits := screenbits;  { alt. screen is the usual, but  }
with altbits do  { [anything to fit in 80 columns!] }
 baseaddr := qdptr (longint(baseaddr) - 32768); 
 { base is 32K less }

EraseRect (thePort^.portbits.bounds); 
 { erase the usual screen }
FillRect (boundsrect, gray);      
 { draw the boundary we bounce in }
CopyBits (screenbits, altbits, screenbits.bounds,        altbits.bounds, 
srcCopy, NIL);
whichscr := default; { start out on the default page }
vh := 5 * tickrate; 
vv := 3 * tickrate;  { pick any old speed  }
samples := 0; 
totfreetime := 0;    { initialize statistics }
vidstart (theinfo, tickrate);     
 { start screen-switch task running }
end;    { of procedure init }

{ showstats -- Print statistics in whatever way you like. 
I don't print them until there are a fair number of samples
accumulated.  Also, the code below prints stats only when 
the number of samples is a power of two, so the frequency 
of debugging output decreases exponentially. }
    
procedure showstats (samples, totfree, delays: longint);
begin;
if samples > 200 
then begin; { don't print for a while }
 { The odd “if” below makes us print only 
 when “samples” is a power of 2. }
 if BitAnd (samples, -samples) = samples 
 then begin ;
 {print routines go here}
 end;
end;
end;            { procedure showstats }

{ animate -- Prepare and display the next frame of animation.
Compute the new position of the block, including bouncing
off the walls.  Wait for the video switcher to finish displaying 
one page so we can draw in it.  Set Quickdraw to drawing in it; 
erase the last block drawn and draw the new one.  Tell the 
video switcher we're ready to have this newscreen shown.}
    
procedure animate;         { draw the next position }
var freetime: longint;          { delay before switch is done }
begin;
OffsetRect (blockrect, vh, vv);     { move to the next position }

{ These four “if”s  all check if the block has gone outside the
 bounds rectangle.  If so, the block is adjusted in the
 appropriate direction (v or h), and a velocity component 
 (vv or vh) is negated. }

if blockrect.bottom > boundsrect.bottom     { bounce off the bottom? 
}
then begin;
 OffsetRect (blockrect, 0, 2 * (boundsrect.bottom-blockrect.bottom));
 vv := - vv;
end;

 if blockrect.top < boundsrect.top         { bounce off the top? }
then begin;
 OffsetRect (blockrect, 0, 2 * (boundsrect.top-blockrect.top));
 vv := - vv;
end;

if blockrect.left < boundsrect.left        { bounce off the left? }
then begin;
 OffsetRect (blockrect, 2 * (boundsrect.left-blockrect.left), 0);
 vh := -vh;
end;

if blockrect.right > boundsrect.right       { bounce off the right? }
then begin;
 OffsetRect (blockrect, 2 * (boundsrect.right-blockrect.right), 0);
 vh := -vh;
end;

freetime := vidwait (theinfo);   { wait for page to switch }
totfreetime := totfreetime + freetime; { keep track of total wait  }
samples := samples + 1;          {  and number of samples in average 
}
showstats (samples, totfreetime, theinfo.delays);

if whichscr = default         { currently showing default screen? }
then begin;          { yes: next frame is on alt screen }
 SetPortBits (altbits);          { make Quickdraw draw on alt. bitmap 
}
 FillRect (altlast, gray);{ clean up last place we drew }
 altlast := blockrect;           { remember where we're drawing now }
 FillRect (altlast, black);   { and draw new image }
 whichscr := alternate; { remember where latest image is  }
 theinfo.request := reqalt;   {  and ask vid switcher to show it }
end{ end of doing frame on alt screen }
else begin; { no: next frame is on default screen }
 SetPortBits (screenbits);{ point Quickdraw to default screen }
 FillRect (deflast, gray);{ erase last image we drew }
 deflast := blockrect;           { remember where we're putting block 
}
 FillRect (deflast, black); { put it there }
 whichscr := default;{ remember which screen is displayed }
 theinfo.request := reqdef; { ask video switcher to show default }
end;             { of branching on which screen }
end;             { procedure animate }

begin;  { MAIN PROGRAM }
 getaltscreen; { first, get the alternate screen }
 init;   { now initialize everything else }
 FlushEvents (everyevent, 0);    { ignore old clicks, etc. }

repeat  { loop repeatedly }
 animate; { draw next animation frame }
until GetNextEvent (mDownMask + keyDownMask, anevent);

 theinfo.request := reqstop;{ exit on default page }
 zot := vidwait (theinfo);{ make sure last request done }
end.    { of main program }


!PAS$Xfer
]
)
/Globals -4
/OUTPUT video

viddemo
vidstuff
PAS$Library
ToolTraps
OSTraps

$ 

Fig. 3 Link File

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

OmniGraffle Pro 7.19.3 - Create diagrams...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
OmniGraffle 7.19.3 - Create diagrams, fl...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
Hopper Disassembler 5.3.3- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
calibre 5.35.0 - Complete e-book library...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Sound Studio 4.10.0 - Robust audio recor...
Sound Studio lets you easily record and professionally edit audio on your Mac. Easily rip vinyls and digitize cassette tapes, or record lectures and voice memos. Prepare for live shows with live... Read more
Sparkle Pro 4.0 - Visual website creator...
Sparkle Pro will change your mind if you thought building websites wasn't for you. Sparkle is the intuitive site builder that lets you create sites for your online portfolio, team or band pages, or... Read more
Dropbox 140.4.1951 - Cloud backup and sy...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
FotoMagico 6.0.5 - Powerful slideshow cr...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more
Remotix 6.4.2 - Access all your computer...
Remotix is a fast and powerful application to easily access multiple Macs (and PCs) from your own Mac. Features: Complete Apple Screen Sharing support - including Mac OS X login, clipboard... Read more
Microsoft Office 365, 2019 16.57 - Popul...
Microsoft Office 365. The essentials to get it all done. Unmistakably Office, designed for Mac Get started quickly with new, modern versions of Word, Excel, PowerPoint, Outlook and OneNote-... Read more

Latest Forum Discussions

See All

A House Full of Covid – The TouchArcade...
It’s been a rough week as both of our young children tested positive for Covid, and since recording this early on Friday my wife has tested positive now too. Thankfully the kids seemed to recover fairly quickly and are mostly back to normal, and I... | Read more »
TouchArcade Game of the Week: ‘Krispee S...
Krispee Street is a new hidden object game from Frosty Pop that is based on their popular and almost painfully sweet webcomic Krispee. This is one of the latest titles to be added to the Netflix Games catalog, which means you’ll need to log into... | Read more »
SwitchArcade Round-Up: ‘Escape Lala’, ‘B...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 21st, 2022. In today’s article, we’ve got a lot of new releases. A lot. There were eight on the schedule when I went to bed last night. There were twenty-four when I woke up... | Read more »
Beta Testers Needed for Huge Version 2.0...
Ya’ll remember Dungeon Raid, right? The phenomenal matching RPG hybrid that launched on mobile more than a decade ago, but was more or less abandoned by its developer only to die a slow death on the App Store before the 32-bit Appocalypse finally... | Read more »
‘Ark Legends’ Gives Players a Chance to...
It’s Airpods and Amazon gift cards galore as Melting Games opens pre-registration for Ark Legends. The upcoming mobile RPG is giving away tons of in-game goodies such as gold, energy, iron core, hero summon chest and rare iron core to players who... | Read more »
‘Nickelodeon Extreme Tennis’ Out Now on...
Nickelodeon Extreme Tennis () from Old Skull Games and Nickelodeon is this week’s new Apple Arcade release. Nickelodeon Extreme Tennis features characters from old and new Nickelodeon shows including SpongeBob, TMNT, and many more. The tennis game... | Read more »
SwitchArcade Round-Up: ‘RPGolf Legends’,...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 20th, 2022. In today’s article, we’ve got a massive amount of new releases to check out. We’ve got summaries of all of them, from heaven to hell. We also have the lists of... | Read more »
‘Zed Blade ACA NEOGEO’ Review – Well, It...
SNK’s NEOGEO platform played host to a great many classics, both famous and under-the-radar. The Metal Slug games. The King of Fighters series. Magician Lord. Shock Troopers. Sengoku 3. NEO Turf Masters. Fatal Fury. Samurai Shodown. Twinkle Star... | Read more »
‘Inua – A Story in Ice and Time’ is a Un...
One thing I know about ARTE from their output on mobile over the years is that they love collaborating with really interesting and unique studios to put out really interesting and unique gaming experiences. This is true yet again with the latest... | Read more »
Out Now: ‘Angry Birds Journey’, ‘RPG Dic...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »

Price Scanner via MacPrices.net

Amazon lowers prices on select 13″ M1 MacBook...
Amazon has select Apple 13″ M1 MacBook Airs on sale for $150 off MSRP this weekend, starting at only $849. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so... Read more
Apple has 13″ M1 MacBook Airs back in stock s...
Apple has restocked a full line of 13″ M1 MacBook Airs, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1-powered MacBooks for sale today at... Read more
In stock and on sale! 16″ 10-Core M1 Pro MacB...
Amazon has new 16″ 10-Core/512GB M1 Pro MacBook Pros in stock today and on sale for $50 off MSRP including free shipping. Their prices are the lowest available for new M1 Pro 16″ MacBook Pro from any... Read more
Deal Alert!: 14″ M1 Pro with 10-Core CPU in s...
Amazon has the new 14″ M1 Pro MacBook Pro with a 10-Core CPU and 16-Core GPU in stock today and on sale for $2299.99 including free shipping. Their price is $200 off Apple’s standard MSRP, and it’s... Read more
Apple has 24-inch M1 iMacs (8-Core CPU/8-Core...
Apple has restocked a wide array of 24-inch M1 iMacs with 8-Core CPUs and 8-Core GPUs in their Certified Refurbished store. Models are available starting at only $1269 and range up to $260 off... Read more
Select 24″ M1 iMacs are on sale for $100 off...
Sales of Apple’s new 24″ M1 iMacs have been rare since its introduction, perhaps due to global supply issues. However, B&H is offering a $100 discount on select 24″ iMacs, and they’re in stock... Read more
M1 Mac minis are back in stock today at Apple...
Apple has M1-powered Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year warranty, and shipping is free: –... Read more
B&H has M1-powered Mac minis on sale for...
B&H Photo has Apple’s Mac minis with M1 Apple Silicon CPUs in stock today and on sale for $50-$100 off MSRP, starting at $649. Free 1-2 shipping is free to many US addresses. Their prices are... Read more
New Amazon sale: Apple’s 13″ M1 MacBook Airs...
Amazon has Apple 13″ M1 MacBook Airs on sale for $100 off MSRP, starting at only $899. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so check their site... Read more
Get an Apple Watch Series 7 for $50 off MSRP,...
Amazon has Apple Watch Series 7 models on sale for $50 off MSRP including free shipping. Their prices are the lowest available for Apple Watch Series 7 models today: – 41mm Apple Watch Series 7 GPS... Read more

Jobs Board

Registered Nurse (RN) Employee Health PSJH -...
…is calling for a Registered Nurse (RN) Employee Health PSJH to our location in Apple Valley, CA.** We are seeking a Registered Nurse (RN) Employee Health PSJH to be Read more
Systems Administrator - Pearson (United State...
…and troubleshoot Windows operating systems (workstation and server), laptop computers, Apple iPads, Chromebooks and printers** + **Administer and troubleshoot all Read more
IT Assistant Level 1- IT Desktop Support Anal...
…providing tier-1 or better IT help desk support in a large Windows and Apple environment * Experience using IT Service Desk Management Software * Knowledge of IT Read more
Human Resources Business Partner PSJH - Provi...
…**is calling a** **Human Resources Business Partner, PSJH** **to our location in Apple Valley, CA.** **Applicants that meet qualifications will receive a text with Read more
Manager Community Health Investment Programs...
…is calling a Manager Community Health Investment Programs PSJH to our location in Apple Valley, CA.** **Qualified candidates will be invited to do a self-paced video Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.