Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Multitasking OS and Graphics Coprocessors


JUL89: MULTITASKING OS AND GRAPHICS COPROCESSORS

MULTITASKING OS AND GRAPHICS COPROCESSORS

More is better when it comes to graphics

Chuck McManis

Chuck McManis has been working with microcomputers since 1977 when he purchased a Digital Group Z-80 system. His personal computer collection includes everything from S-100 machines to the Amiga. In the past he's worked on graphics coprocessors for the Intel Corporation, but now Chuck is working at Sun Microsystems on Unix networking software. He can be reached at Sun Microsystems, 2550 Garcia Ave., Mountain View, CA 94043 or on BIX as cmcmanis.


Graphics coprocessors have traditionally been used only on specialized high-performance graphics display systems running sophisticated and complex graphics applications. Today, however, the trend towards window-based, graphical user-interfaces on standard PCs -- with the resulting CPU burdens and performance degradation -- is forcing hardware developers to begin thinking of coprocessors as standard equipment for desktop systems as well. But graphics coprocessors are only half the story: The development of multitasking operating systems (OSs) has also played a part in increasing the overall CPU use and has helped make more powerful graphics applications more of a reality instead of a dream.

In this article, I'll examine the costs and benefits of using graphics coprocessors in a multitasking OS environment. In particular, I'll focus on the hardware and software architecture of Commodore's 68000-based Amiga personal computer, which combines a high-performance multitasking operating system with autonomous graphics coprocessors. Why the Amiga? Because it is the only mainstream PC that comes with a multitasking OS as standard equipment. This, combined with the innovative use of dedicated LSI logic to offload CPU tasks, has resulted in a graphics platform that is unique among PCs in its price range. The Amiga's flexible architecture and modular OS allows programmers legal access to coprocessors so they can be used to their fullest potential.

Multitasking, or Who Really Has the Coprocessor?

When a program running a single-tasking OS (like MS-DOS) asks a coprocessor to draw a line on the screen, the CPU waits for that line to be drawn while (usually) looping on some sort of status bit or through an interrupt chain. When a program requests a line draw with a multitasking OS, the graphics coprocessor begins the drawing process and the task requesting the line draw is dismissed; another task that does not need the coprocessor is then allowed to use the CPU. This is a fundamental difference in behavior between single-tasking and multitasking OSs. Because the CPU doesn't sit idle until the coprocessor finishes its task, there is an increase in CPU utilization.

For programmers, one drawback of this is that you must always assume that some other program is using the coprocessor or the display and you can no longer simply blast away at the hardware and expect reliable results. Instead, arbitration of access to resources falls under the domain of the OS, which in turn must provide the mechanisms to request resources. You must write programs that are able to consider if a resource is available. When the OS interface is well-designed, this resource arbitration is handled invisibly and you need only use the interfaces provided to guarantee that the program does not interfere with other tasks.

The Amiga Hardware

The Amiga system architecture includes a generalized coprocessor, the Copper, and a dedicated graphics engine, the Blitter. These peripherals -- as well as four D/A channels, four A/D converters, the system control functions, and a 25-channel DMA controller -- are implemented in a set of three custom chips named Agnus, Denise, and Paula. These chips share a dedicated memory bus that allows them to access memory without interfering with the CPU. The custom chips can only access memory on this bus, which is why this memory is referred to as Chip Memory. As Figure 1 shows, the custom chips are situated on the chip memory bus. Although the architecture sets aside 2 Mbytes of the address space for Chip Memory, the original Amiga chipset only supported 512K, and a recently introduced upgraded chipset supports only 1 Mbyte. The primary benefit of this extra memory is that the CPU can execute code in memory outside of this range without any performance degradation resulting from contention with the custom chips. The control registers for the custom chips are visible to the CPU as memory addresses in the range of $DFFOOO through $DFFFFF.

The heart of the coprocessor group is the System Controller. It is here that the CPU can start and stop the DMA channels that feed data to these chips and enable or disable interrupts from the chips to the CPU. The DMA channels feed data to various peripherals such as the D/A converters. The four D/A channels on the Amiga are some of the simpler peripheral interfaces. By adding the DMA channel, the D/A converter is elevated to the status of a simple coprocessor. By having their waveforms automatically feed into them from the DMA chips, the coprocessor can output a digitized waveform without any intervention from the CPU.

The most sophisticated peripheral is the Copper, so named because it is the traffic cop of the Amiga. The Copper -- with three instructions (MOVE, WAIT, and SKIP) that allow it to store data and provide conditional testing, and programmatic branching -- is, in some ways, a RISC processor in its own right. The Copper is able to write to any of the custom chip's control registers, even its own. This allows the Copper to be the CPU's lieutenant by taking over mundane tasks such as setting up the CRT controller (implemented in Denise) to build on the display every frame. A program, called the Copper List, is started at every vertical blank from the start address, which is stored in the COP1LC register. The Copper is synchronized with the video beam so that it knows where the beam on the screen is at all times and can wait for it to arrive at a particular position. This feature comes in handy when playing with graphical objects called sprites. The Copper code in Example 1 could be used to notify the CPU that video frame has ended. The Copper is built to use alternate clock cycles to access memory. This allows it to minimize contention between itself and other coprocessors for the Chip Memory bus. This coprocessor is half of the equation that makes the Amiga such an effective graphics platform.

Example 1: Sample Copper code

  WAIT  0,261        ;  Wait for line 261
  MOVE  COPR, INTREQ ;  Interrupt the CPU
  WAIT  0,$FFFE      ;  Wait until VBlank

The other half of this equation is the Blitter. The Blitter is a graphics computation unit that transfers 16-bit words around in memory while performing bitwise logical operations on those words. The Blitter is capable of reading data from up to three addresses, performing any logical operation on that data, and then optionally storing the result at a fourth address. The addresses are automatically updated after each operation by an amount you specify. This allows the Blitter to work within a programmed area of a larger bitmap. Two additional functions built into the Blitter are the ability to draw lines between two arbitrary points and the ability to fill in an area with an arbitrary 16-bit binary pattern.

The Blitter's contribution to the Amiga is the ability to draw lines and render filled polygons at a rate that is equivalent to the speed that the 68000 CPU could if it were rendering them. The key, of course, is that the 68000 can now be off doing something else. Window-based, user-interface operations often map a sequence of commands directly to the Blitter. Because of this, operations like moving windows, rendering menus, and scrolling text are completed much faster on the Amiga than on other 68000-based machines.

The Amiga also contains a sprite engine that is capable of overlaying its contents on the screen at an internally specified X and Y coordinate. The sprite can be thought of as a self-contained mini-bitmap that is independently positioned on the screen and only relies on the CPU to tell it where and when to move. This activity costs very little in terms of CPU cycles. The Amiga uses one of the eight available sprites as the window system cursor.

The Amiga Software

The Amiga's OS has three major parts: The multitasking kernel, the graphics interface, and the disk operating system. I'll discuss the first two here. For further information on the DOS, see the The AmigaDOS Reference Manual (Bantam Books, 1987).

The software foundation of the OS is the multitasking kernel, Exec. This kernel provides the fundamental controls that the other pieces are layered upon. Exec is composed of several component parts. The basic grouping consists of the list management routines, resident library support, message management, interrupt handling, task management, resource management, and devices. Completely describing Exec is beyond the scope of this article, so I will only describe those aspects that support the coprocessors and graphics.

To prevent processes from conflicting with each other when they attempt to use the coprocessors, Exec must keep those processes synchronized to the availability of the resource; Exec uses messages and semaphores to accomplish this.

A message port is a rendezvous point where many competing process requests can be effectively managed. Exec queues messages to a port using a first-in, first-out policy. The overhead for this synchronization is fairly high and is generally restricted to those activities that are fairly time-consuming (device I/O, for instance). The task that owns a resource creates a message port upon which it receives requests to use its resource. As requests arrive, the task removes messages one at a time, serializing specific requests to that resource. A significant advantage to this system is that the interface between an application and the task that owns a resource is defined by a message protocol. As long as the task follows this protocol, it may be replaced by a new or upgraded equivalent task at any time without effecting users of the resource. Some resources (such as the Blitter) cannot be used simultaneously, yet the whole reason for having a Blitter is to minimize the work that is required for the CPU to render graphics. A semaphore is used to provide a simpler and faster method to ensure exclusive access to the Blitter without a lot of overhead.

A semaphore can be thought of as a restroom key that is hanging on a hook by the door. People wanting to use the restroom must have the key to open the door. However, because access is only allowed if you have the key, the key will be unavailable when the restroom is in use (exclusivity is assured). Because the key is available right next to the door that requires it, you are not needlessly delayed if the restroom is free.

Every task that uses the screen will require the use of the Blitter. A semaphore is maintained by Exec, and only the task that is currently drawing owns it. A library function, OwnBlitter(), is provided in the graphics library to acquire this semaphore. This function requests the Blitter semaphore from Exec. If it is available, it is immediately granted, and if it is not available the task is placed in a queue for Blitter use and dismissed. When the Blitter is available, Exec will give the semaphore to the next task in the queue and resume it. From the programmer's perspective, the call to OwnBlitter() will simply return. The programmer is now free to use the Blitter for his own purposes because no one else will be granted access until he gives back the semaphore to indicate that the Blitter is no longer needed. This is accomplished with DisownBlitter().

The key concept is one of ownership. Because a program may not be the only one running, it cannot assume it owns the machine; rather, it must synchronize its behavior so that it does not interfere with another process that is using non-shareable resources. Even though you might suspect otherwise, this additional overhead of synchronization with other tasks does not lead to slower or to unacceptable performance.

Another one of the kernel's jobs is to manage resident libraries that are different from the libraries you would link in a compiled program. The difference is that while there may be several processes using routines within a resident library, there is only one copy of that resident library in memory. The graphics library mentioned earlier is one such library. The savings in memory is tremendous and is one of the reasons that the Amiga is fully multitasking in 512K bytes of RAM, whereas you would probably need up to four to eight times that much memory to achieve the same capabilities on other PCs. It is also another way of providing transparent layering of capabilities to the programmer.

When using graphics on the Amiga, three libraries make up the layers between an application and the hardware (from highest to lowest): Intuition (the windowing system); Layers (which control clipping); and Graphics (the lowermost layer that provides the primitives). These three layers provide progressively closer access to the hardware. Intuition provides the highest-level functions, most of which are designed to set up the window system environment and to facilitate communication with the user. Like the application program, Intuition is a user of the Layers and Graphics libraries.

Where Intuition provides the window environment, Layers provides the drawing environment. Rendering commands are clipped to a specified region in this environment. A layer appears to a program as a contiguous drawing surface. The actual surface (the screen) may actually be divided into several distinct regions. The coprocessors and the OS come together at the Graphics layer. It is here that a call to Draw( ) is converted into a Blitter operation.

The Graphics Architecture

The primary data structure of Amiga graphics is the Rastport, which contains such information as where the memory for the bitmaps resides, the current text font, the current pen colors, the coordinates of the last point drawn, and so on. Graphics also contains a LayerInfo structure that, when present, is used by the layers library to determine how to clip graphics and text rendered on the screen.

The Copper and the Blitter are the main tools of the graphics library. The Copper is used to set up the physical characteristics of the display, and the Blitter is used to render graphic objects into that display as quickly as possible. Both functions have a data structure that provides the needed information for them to accomplish what is required of them. For the Copper, that data structure is a View.

A View is a particular display mode with a particular set of colors in a particular orientation on the screen. Typically, this sort of thing is controlled by the CRT controller in the PC. On the Amiga the CRT controller is implemented in Denise. As CRT controllers go, Denise is very straightforward. It fetches data from the bitplanes in chip memory, feeds this to a set of 32 color registers, each containing a 12-bit color triple: four bits for red, four for green, and four for blue. This triple is then input into three D/A converters that convert the value into a voltage level that goes out onto the video connector. The registers, however, that control certain processes (where the data for display is stored, what is in the color registers, and so on) are accessible by the Copper, which is synchronized to the video beam. Thus, a specialized Copper List could be constructed to control Denise on a line-by-line basis, and in fact this is exactly what is done by the graphics library. The required display is described by filling in various fields in a View and one or more ViewPort structures. These structures are passed along to the graphics library that constructs a specialized Copper List to provide that View. If the View is fairly simple, such as a 320 x 200 pixel screen that isn't interlaced, the Copper List would be a simple set of initialization instructions that would be loaded into the CRT controller on each vertical blank. The power of this combination becomes evident when the displays become more complicated.

Because the Amiga is capable of displaying 32 colors when in the 320 pixel/line mode, it is often common to use this mode for games, paint programs, and other applications that use lots of colors. With only 320 pixels on a line, however, a program is limited to 40 characters if it is using the standard system font. As a status line, this can be very limiting. Using the Copper, it's possible to design a display that has a 320 x 180 pixel screen on top, and a 640 x 20 pixel screen on the bottom of the display. These two areas make up one complete 200 line display. The Copper is required to switch the display mode after the video beam has drawn 180 lines on the monitor in low resolution. Thus the last 20 lines, which are enough for two lines of 80 character text in the standard font, can be used for a status display. Needless to say, this is not an easy thing to do with a dedicated CRT controller, but with the Amiga this is fairly simple. Listing One shows an example of setting up a View like the one described earlier. In the example, the Copper changes not only the resolution, but also the values of the colors in the color table. Listing Two shows the Copper List that is created by the MakeVPort( ) and MrgCop( ) routines; Figure 2 illustrates the display generated by this program.

While the View is the structure that the Copper uses, the corresponding structure for the Blitter is the RastPort. This structure contains information such as where the bitmap data can be found, what logical operation the Blitter will perform, what pattern to use for patterned lines, and what font to use. The graphics library takes this information (along with the requested operation) and determines the values needed to execute the operation. The library also acquires the Blitter's semaphore and performs the function. Once the Blitter has been started the library returns immediately. Control can return to the current program or any other program that needs the CPU while the Blitter is drawing.

To demonstrate the accelerating effect of letting the Blitter draw the lines instead of the CPU, I wrote a simple benchmark called blit.c (Listing Three). When compiled without WAIT_BL IT defined, the graphics library will start a line draw and then return to allow the CPU to compute the next endpoint while the Blitter is drawing. With WAIT_BL IT defined, the program is forced to wait for the Blitter to complete each line before beginning the calculations for a new line. The results? About a 12 percent difference in speed. (Your mileage may vary, of course.)

Conclusions

The Amiga integrates the use of graphics coprocessors at all levels of its operation. The results are a noticeably faster response time to user requests, especially in window operations. By using those coprocessors efficiently, the Amiga can do such things as display animations while playing a soundtrack. In fact, some of the applications that were pioneered on the Amiga (desktop video production, for example) have only recently showed up on some of the newer 32-bit machines (the Macintosh II and 80386-based MS-DOS PCs) and are not available at all with the current set of 8- or 16-bit processor-based machines. The conclusion I draw from this is that for high-performance graphics applications, the implementation of coprocessors and a multitasking OS puts the Amiga into the performance range of the 32-bit machines, yet at the price of a 16-bit machine.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063; or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number.

_MULTITASKING OS AND GRAPHICS COPROCESSORS_ by Chuck McManis

[LISTING ONE]

<a name="0144_000c">

/*   dualvp.c - Dual Viewports on the amiga
 *   Written 4/4/89 by C. McManis using Lattice C 5.02
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/view.h>
#include <graphics/gfxbase.h>
#include <graphics/rastport.h>
extern struct GfxBase *GfxBase;
char   *TextString = "Amiga Graphics Example";

         /* Viewport 0 colors */
UWORD         colors0[4] = {0xccc, 0x000, 0x0f0, 0x00f},
         /* Viewport 1 colors */
         colors1[4] = {0x0f0, 0xc0c, 0xf00, 0xfff};
void
_main()
{
   struct View   MyView, *OldView;
   struct ViewPort   Vp0, Vp1;
   struct BitMap   Bits;
   struct RasInfo   MyRaster;
       struct RastPort   rp;
   int      i;

   /* Open the resident graphics library */
   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
   if (!GfxBase)
      exit(1);
   OldView = GfxBase->ActiView; /* Save this away */

   /* Initialize the View structures */
   InitView(&MyView);
   InitVPort(&Vp0);
   InitVPort(&Vp1);
   Vp1.Next = NULL;
   Vp0.Next = &Vp1;   /* create a linked list of viewports */
   MyView.ViewPort = &Vp0;   /* With the first one being Vp0 */

   /* Set up some display memory */
   InitBitMap(&Bits, 2, 640, 200);
   Bits.Planes[0] = (PLANEPTR)
          AllocMem(2*RASSIZE(640, 200),MEMF_CHIP+MEMF_CLEAR);
   Bits.Planes[1] = Bits.Planes[0] + RASSIZE(640, 200);
   if (!Bits.Planes[0])
      goto cleanup;
   MyRaster.BitMap = &Bits;
   MyRaster.RxOffset = 0;
   MyRaster.RyOffset = 0;
   MyRaster.Next = NULL;

   /* Both viewports are looking at the same display memory but have
         * different sets of colors
    */
   Vp0.RasInfo = &MyRaster;
   Vp0.DWidth  = 320;
   Vp0.DHeight = 175;
   Vp0.ColorMap = (struct ColorMap *)GetColorMap(4);
   LoadRGB4(&Vp0, colors0, 4);
   Vp1.RasInfo = &MyRaster;
   Vp1.DWidth  = 640;
   Vp1.DHeight = 20;
   Vp1.DyOffset = 179;
   Vp1.Modes   = HIRES;
   Vp1.ColorMap = (struct ColorMap *)GetColorMap(4);
   LoadRGB4(&Vp1, colors1, 4);

   /* Initialize a RastPort so that we can draw into that memory. */
   InitRastPort(&rp);
   rp.BitMap = &Bits;
   SetAPen(&rp, 1);   /* Foreground color */
   SetBPen(&rp, 0);   /* Background color */
   Move(&rp, 3, 12);   /* Move the graphics cursor to (3, 12) */
   /* Write something */
   Text(&rp, TextString, strlen(TextString));
   MakeVPort(&MyView, &Vp0); /* Build the copper list for Viewport 0 */
   MakeVPort(&MyView, &Vp1); /* Build the copper list for Viewport 1 */
   MrgCop(&MyView);      /* Merge it into the final list */
   LoadView(&MyView);     /* Show it off */

   /* SPIN FOR A WHILE */
   for (i=0; i<1000000; i++)
      ;
   LoadView(OldView);      /* Return to the old view */
cleanup:

   /* Now give back the memory other tasks may need it */
   if (!Vp0.ColorMap)
      FreeColorMap(Vp0.ColorMap);
   if (!Vp1.ColorMap)
      FreeColorMap(Vp1.ColorMap);
   FreeVPortCopLists(&Vp0);
   FreeVPortCopLists(&Vp1);
   FreeCprList(MyView.LOFCprList);
   if (!Bits.Planes[0])
      FreeMem(Bits.Planes[0], 2*RASSIZE(640, 200));
   if (!GfxBase)
      CloseLibrary(GfxBase);
   exit(0);
}






<a name="0144_000d"><a name="0144_000d">
<a name="0144_000e">
[LISTING TWO]
<a name="0144_000e">

000208a8  2b01:fffe   WAIT   (00,2b)      ; (x = 0, y = 43)
--- load color table ---
000208bc  008e:0581   MOVE   0581,diwstrt   ; (left = 129, top = 5)
000208c0  0100:0200   MOVE   0200,bplcon0
000208c4  0104:0024   MOVE   0024,bplcon2
000208c8  0090:40c1   MOVE   40c1,diwstop   ; (right = 449, bottom = 320)
000208cc  0092:0038   MOVE   0038,ddfstrt   ; pixel val = 112
000208d0  0094:00d0   MOVE   00d0,ddfstop   ; pixel val = 416
000208d4  0102:0000   MOVE   0000,bplcon1
000208d8  0108:0028   MOVE   0028,bpl1mod
000208dc  010a:0028   MOVE   0028,bpl2mod
000208e0  00e0:0002   MOVE   0002,bpl1pth
000208e4  00e2:86e8   MOVE   86e8,bpl1ptl
000208e8  00e4:0002   MOVE   0002,bpl2pth
000208ec  00e6:c568   MOVE   c568,bpl2ptl
000208f0  2c01:fffe   WAIT   (00,2c)      ; (x = 0, y = 44)
000208f4  0100:2200   MOVE   2200,bplcon0
000208f8  db01:fffe   WAIT   (00,db)      ; (x = 0, y = 219)
000208fc  0100:0200   MOVE   0200,bplcon0
00020900  de01:fffe   WAIT   (00,de)      ; (x = 0, y = 222)
--- load color table ---
00020914  008e:0581   MOVE   0581,diwstrt   ; (left = 129, top = 5)
00020918  0100:0200   MOVE   0200,bplcon0
0002091c  0104:0024   MOVE   0024,bplcon2
00020920  0090:40c1   MOVE   40c1,diwstop   ; (right = 449, bottom = 320)
00020924  0092:003c   MOVE   003c,ddfstrt   ; pixel val = 120
00020928  0094:00d0   MOVE   00d0,ddfstop   ; pixel val = 416
0002092c  0102:0000   MOVE   0000,bplcon1
00020930  0108:0000   MOVE   0000,bpl1mod
00020934  010a:0000   MOVE   0000,bpl2mod
00020938  00e0:0002   MOVE   0002,bpl1pth
0002093c  00e2:86e8   MOVE   86e8,bpl1ptl
00020940  00e4:0002   MOVE   0002,bpl2pth
00020944  00e6:c568   MOVE   c568,bpl2ptl
00020948  df01:fffe   WAIT   (00,df)      ; (x = 0, y = 223)
0002094c  0100:a200   MOVE   a200,bplcon0
00020950  f301:fffe   WAIT   (00,f3)      ; (x = 0, y = 243)
00020954  0100:0200   MOVE   0200,bplcon0
00020958  ffff:fffe   WAIT   (7f,ff)      ; (x = 127, y = 255)






<a name="0144_000f"><a name="0144_000f">
<a name="0144_0010">
[LISTING THREE]
<a name="0144_0010">

/*   blit.c - Demonstrates the benefit of the blitter.
 *   Written 4/9/89 by C. McManis using Lattice C 5.02
 *   The difference on my machine between waiting for the blitter
 *   to complete before calculating the next set of draw parameters
 *   is 1.6 vs 1.4 seconds, about a 12.5% increase in speed.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/gfx.h>
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase       *GfxBase;
struct NewScreen NS = {
      0, 0,      /* Position on the display         */
      320, 200, 4,   /* Attributes (Width, Height, Depth) */
      1,0,      /* Detail and Block pens        */
      0,      /* ViewModes nothing special        */
      CUSTOMSCREEN,   /* It is our own screen we want        */
      0,      /* Using the Default font        */
      "Blitter Test",   /* With a simple title.           */
      0,      /* No special gadgets           */
      0      /* And no special bitmap        */
   };
struct Screen   *MyScreen;
struct RastPort   *RPort;
void
cleanup(n)
   int   n;
{
   if (GfxBase)
      CloseLibrary(GfxBase);
   if (MyScreen)
      CloseScreen(MyScreen);
   if (IntuitionBase)
      CloseLibrary(IntuitionBase);
   exit(n);
}
void
main()
{
   int   i,    /* Loop counter */
      x, y, c, /* some random draw parameters */
      t0[2],    /* Start Time */
      t1[2];    /* End time */
   IntuitionBase = (struct IntuitionBase *)
            OpenLibrary("intuition.library",0L);
   if (! IntuitionBase)
      cleanup(1);
   GfxBase = (struct GfxBase *)
            OpenLibrary("graphics.library", 0L);
   if (! GfxBase)
      cleanup(1);

   /* This does all of the view construction for us */

   MyScreen = (struct Screen *) OpenScreen(&NS);
   if (!MyScreen)
      cleanup(1);
   timer(t0);   /* Start the clock running */

   /* Get the RastPort of this screen */
   RPort = &(MyScreen->RastPort);
   SetAPen(RPort, 1);       /* Foreground pen = 1 */
   SetBPen(RPort, 0);       /* Background pen = 0 */
   srand(42);          /* set the seed */
   Move(RPort, 160, 100);        /* Move to the moiddle of the screen */

   /*
    * Note we generate psuedo random numbers (eg the same set of
     * random numbers every time.
    */
   for (i=0; i<1000; i++) {
      x = (rand() % 300) + 10;
      y = (rand() % 180) + 10;
      c = rand() % 16;
      SetAPen(RPort, c);
      Draw(RPort, x, y);
#ifdef WAIT_BLIT
      WaitBlit();   /* Simulate non-coprocessor */
#endif
   }
   timer(t1);   /* stop the clock */
#ifdef WAIT_BLIT
   printf("With waiting for the blitter, we took %d microseconds.\n",
         (t1[0] - t0[0]) * 1000000 + (t1[1] - t0[1]));
#else
   printf("Without waiting for the blitter, we took %d microseconds.\n",
         (t1[0] - t0[0]) * 1000000 + (t1[1] - t0[1]));
#endif
   cleanup(0);
}



Example 1. Sample Copper code

   WAIT   0,261        ; Wait for line 261
   MOVE   COPR, INTREQ ; Interrupt the CPU
   WAIT   0,$FFFE        ; Wait until VBlank












Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.