
/******************************************************************************
*
*   $Header: tut,v 36.1 89/05/22 13:52:04 bart Exp $
*
******************************************************************************/


/*  this file is a tutorial on the use some of the new features of V1.4 .
    it consists of example code for the program ViewPortDemo, included with
    this release; in addition to detailed comments and included information */


#include <exec/types.h>
#include <graphics/displayinfo.h>

/*  the 1.2 release was 33, the 1.3 release was 34, but the 1.4 release is 36 */
/*  this is because of the interim "jumpstart" release to support the A2024 . */

#define V1_POINT_4      36

struct GfxBase  *GfxBase = NULL;

/*  each 1.4 mode is associated with a DisplayInfoRecord in the new graphics
    database.  in order to use these modes, some of which are new for V1.4
    it is useful to do two things: iterate through all known modes and get
    information about their qualities, current parameters and availability */

extern iterate_modes( );
extern mode_info( );

/*  here is the main routine: its function is to quarantee that we are indeed
    running V1.4, and to call the iteration procedure to display every available
    mode that can be obtained from the graphics DisplayInfo database. */

main()
{
    int error = FALSE;

    if( GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",V1_POINT_4) )
    {
        iterate_modes();         /* display every available mode */
        CloseLibrary( GfxBase ); /* clean up and exit */
    }
    else
    {
        error = TRUE;
    }

    exit(error);
}


/*  let's take a closer look at the data structures defined in displayinfo.h */


#ifndef GRAPHICS_DISPLAYINFO_H
#define GRAPHICS_DISPLAYINFO_H

/* the "public" handle to a DisplayInfoRecord. this handle is obtained via
   the new graphics function handle = FindDisplayInfo(ID) . the ID passed
   to FindDisplayInfo is a "composite key" which is a unique 32bit numnber 
   associated with a particular display Mode such as lores, hires, etc. */

typedef APTR DisplayInfoHandle; 

/* DisplayInfoRecord IDentifiers -- the invalid ID is special. you may call
   ID = NextDisplayInfo(INVALID_ID) to obtain the first composite key in the 
   graphics DisplayInfo database.  after obtaining the first_id: you repeat
   ID = NextDisplayInfo(ID) to iterate through all available composite keys 
   until ID == INVALID_ID again. thus INVALID_ID functions as both the 
   start-of-list and end-of-list marker.... */

#define INVALID_ID                      ~0

/* the following 20 composite keys are for Modes on the default Monitor */
/* ntsc & pal "flavors" of these particular keys may be made by or'ing  */
/* the ntsc or pal MONITOR_ID with the desired MODE_KEY... */

#define LORES_KEY                       0x00000000 
#define HIRES_KEY                       0x00008000 
#define SUPER_KEY                       0x00008020 
#define HAM_KEY                         0x00000800 
#define LORESLACE_KEY                   0x00000004 
#define HIRESLACE_KEY                   0x00008004 
#define SUPERLACE_KEY                   0x00008024 
#define HAMLACE_KEY                     0x00000804 
#define LORESDPF_KEY                    0x00000400 
#define HIRESDPF_KEY                    0x00008400 
#define SUPERDPF_KEY                    0x00008420 
#define LORESLACEDPF_KEY                0x00000404 
#define HIRESLACEDPF_KEY                0x00008404 
#define SUPERLACEDPF_KEY                0x00008424 
#define LORESDPF2_KEY                   0x00000440 
#define HIRESDPF2_KEY                   0x00008440 
#define SUPERDPF2_KEY                   0x00008460 
#define LORESLACEDPF2_KEY               0x00000444 
#define HIRESLACEDPF2_KEY               0x00008444 
#define SUPERLACEDPF2_KEY               0x00008464 

#define DEFAULT_MONITOR_ID              0x00000000 
#define NTSC_MONITOR_ID                 0x00011000 
#define PAL_MONITOR_ID                  0x00021000 


/*  vga IDentifiers -- these are modes running at 31.5khz (doublescan ) rates.
    one of these is "special" : that is the VGAPRODUCT_KEY which identifies
    the "Productivity" Mode, which we expect will be the most frequently used.
    ALL of these modes require the ECS chipset (both agnus and denise) in 
    addition to a having a currently available multi-sync or bi-sync monitor */

#define VGAEXTRALORES_KEY               0x00031004 
#define VGALORES_KEY                    0x00039004 
#define VGAPRODUCT_KEY                  0x00039024 
#define VGAHAM_KEY                      0x00031804 
#define VGAEXTRALORESLACE_KEY           0x00031005 
#define VGALORESLACE_KEY                0x00039005 
#define VGAPRODUCTLACE_KEY              0x00039025 
#define VGAHAMLACE_KEY                  0x00031805 
#define VGAEXTRALORESDPF_KEY            0x00031404 
#define VGALORESDPF_KEY                 0x00039404 
#define VGAPRODUCTDPF_KEY               0x00039424 
#define VGAEXTRALORESLACEDPF_KEY        0x00031405 
#define VGALORESLACEDPF_KEY             0x00039405 
#define VGAPRODUCTLACEDPF_KEY           0x00039425 
#define VGAEXTRALORESDPF2_KEY           0x00031444 
#define VGALORESDPF2_KEY                0x00039444 
#define VGAPRODUCTDPF2_KEY              0x00039464 
#define VGAEXTRALORESLACEDPF2_KEY       0x00031445 
#define VGALORESLACEDPF2_KEY            0x00039445 
#define VGAPRODUCTLACEDPF2_KEY          0x00039465 

#define VGA_MONITOR_ID                  0x00031000

/* a2024 IDentifiers --these are the two different refresh rates at which 
   you may run an a2024 display.  these modes are available in ROM for V1.4
   without requiring the RCS chipset. they do, however require an a2024. */

#define A2024TENHERTZ_KEY               0x00041000 
#define A2024FIFTEENHERTZ_KEY           0x00049000 

#define A2024_MONITOR_ID                0x00041000


/* once you have obtained a handle to a DisplayInfoRecord via FindDisplayInfo()
   you may use GetDisplayInfoData() to query the DisplayInfoRecord about its
   current settings.  Each datachunk inquiy will copy bytes into your querybuf:
   beggining with the queryheader and followed by data for the chunk type. 

   TAG_DISP: (DisplayInfo)   - properties and availability information.
   TAG_DIMS: (DimensionInfo) - default dimensions and overscan info.
   TAG_MNTR: (MonitorInfo)   - type, position, scanrate, and compatibility.
   TAG_NAME: (NameInfo)      - a user friendly way to refer to this mode.   */


/* datachunk types */

#define DTAG_DISP               0x80000000
#define DTAG_DIMS               0x80001000
#define DTAG_MNTR               0x80002000
#define DTAG_NAME               0x80003000

/* when calling GetDisplayInfoData(handle,querybuf,bufsize,tagID,[ID]) then 
   bufsize bytes will be copied into your buffer. bufsize must be at least 
   sizeof(struct QueryHeader) : querybuf should be at least bufsize bytes */

struct QueryHeader
{
        ULONG   StructID;       /* datachunk type IDentifier */
        ULONG   DisplayID;      /* copy of display record key   */
        ULONG   StructIgnore;   /* TAG_IGNORE -- see tagitems.h */
        ULONG   Length;         /* length of local data in tagitems */
                                /* each tagitem is a double-longword */
};

/* note that though a DisplayInfoRecord for a given key may be found in the
   database, this mode may not be "currently" available: reasons for this
   can include NOCHIPS (mode requires the ECS chipset, which has not been
   installed) and NOMONITOR (mode requires a monitor which is not attached) */

struct DisplayInfo
{
        struct  QueryHeader Header;
        UWORD   NotAvailable;   /* if NULL available, else see defines */
        ULONG   PropertyFlags;  /* Properties of this mode see defines */
        Point   Resolution;     /* ticks-per-pixel X/Y                 */
        UWORD   PixelSpeed;     /* aproximation in nanoseconds         */
        UWORD   NumStdSprites;  /* number of standard amiga sprites    */
        UWORD   PaletteRange;   /* distinguishable shades available    */
        Point   SpriteResolution; /* std sprite ticks-per-pixel X/Y    */
        UBYTE   pad[4];
        ULONG   reserved[2];    /* terminator */
};

/* availability */ 

#define DI_AVAIL_NOCHIPS        0x0001
#define DI_AVAIL_NOMONITOR      0x0002

/* mode properties */ 

#define DIPF_IS_LACE            0x00000001
#define DIPF_IS_DUALPF          0x00000002
#define DIPF_IS_PF2PRI          0x00000004
#define DIPF_IS_HAM             0x00000008

#define DIPF_IS_ECS             0x00000010
#define DIPF_IS_PAL             0x00000020
#define DIPF_IS_SPRITES         0x00000040
#define DIPF_IS_GENLOCK         0x00000080

#define DIPF_IS_WB              0x00000100
#define DIPF_IS_DRAGGABLE       0x00000200
#define DIPF_IS_PANELLED        0x00000400
#define DIPF_IS_BEAMSYNC        0x00000800

/* the raster sizes are the maximum bitmap sizes that are available for
   graphics operations. the ECS chipset, if installed, allows for blits
   up to ~32k x 32k in size. otherwise, blits may only be ~1k x 1k big.
   note that beginning with V1.4, the raster may be much larger than the
   displayable portion of a screen and intuition will allow you to scroll
   this larger raster about your displayclip rectangle */

/* the overscan rectangles represent the displayable dimensions for this mode.
    nominal is the default viewport height and width for this mode
    max is the maximum display region which software displayclip will handle
    video is the absolute largest display that the hardware clipping will allow
    txt is the region within which all text rendering will be visible
    std is the region which extends "just" to the edges of the bezel */

struct DimensionInfo
{
        struct  QueryHeader Header;
        UWORD   MaxDepth;             /* log2( max number of colors ) */
        UWORD   MinRasterWidth;       /* minimum width in pixels      */
        UWORD   MinRasterHeight;      /* minimum height in pixels     */
        UWORD   MaxRasterWidth;       /* maximum width in pixels      */
        UWORD   MaxRasterHeight;      /* maximum height in pixels     */
        struct  Rectangle   Nominal;  /* "standard" dimensions        */
        struct  Rectangle   MaxOScan; /* fixed, hardware dependant    */
        struct  Rectangle VideoOScan; /* fixed, hardware dependant    */
        struct  Rectangle   TxtOScan; /* editable via preferences     */
        struct  Rectangle   StdOScan; /* editable via preferences     */
        UBYTE   pad[14];
        ULONG   reserved[2];          /* terminator */
};

/* the compatibility criteria in the monitorinfo structure indicates which
   modes may be combined in a display, both with others of thier own ilk, 
   and others whose natural "monitor" may be incompatible with this display. */

struct MonitorInfo
{
        struct  QueryHeader Header;
        struct  MonitorSpec  *Mspc;   /* pointer to monitor specification  */
        Point   ViewPosition;         /* editable via preferences          */
        Point   ViewResolution;       /* standard monitor ticks-per-pixel  */
        struct  Rectangle ViewPositionRange;  /* fixed, hardware dependant */
        UWORD   TotalRows;            /* display height in scanlines       */
        UWORD   TotalColorClocks;     /* scanline width in 280 ns units    */
        UWORD   MinRow;               /* absolute minimum active scanline  */
        WORD    Compatibility;        /* how this coexists with others     */
        UBYTE   pad[36];
        ULONG   reserved[2];          /* terminator */
};

/* monitor compatibility */

#define MCOMPAT_MIXED   0       /* can share display with other MCOMPAT_MIXED */
#define MCOMPAT_SELF    1       /* can share only within same monitor */
#define MCOMPAT_NOBODY -1       /* only one viewport at a time */

/* the name (optionally) associated with a DisplayInfoRecord is bound to
   this record at run time, either from the startup-sequence or by the user.
   thus you may build a "mode requester" for in the current natural language */

#define DISPLAYNAMELEN 32

struct NameInfo
{
        struct  QueryHeader Header;
        UBYTE   Name[DISPLAYNAMELEN];
        ULONG   reserved[2];          /* terminator */
};

#endif  /* GRAPHICS_DISPLAYINFO_H */


/* here we reserve query_buffers to store the data that mode_info() will fill */

struct DisplayInfo   queryinfo;
struct MonitorInfo   querymntr;
struct DimensionInfo querydims;

/* ok, now that we've gotten the preliminaries out of the way, lets see what
   composite mode keys are in the DisplayInfo database... */

iterate_modes()
{
    /* start with speical begin/end-of-list ID */

    ULONG       ID = INVALID_ID; 

    /* get next ID until there are no more */

    while((ID = NextDisplayInfo( ID )) != INVALID_ID)
    {
        /* query each ID returned by next() for mode information */

        if( mode_info( ID ) ) 
        {
            /* use the information to open a view/viewport in that mode */

            open_mode( ID );
        }
    }
}


/* mode_info takes a composite key and return a boolean value: TRUE if all the
   information necessary to open a mode is available and has been copied into
   the appropriate buffers or FALSE if some copy did not succeed. */

mode_info( ID )
ULONG ID;
{
    int    info = FALSE;
    int    mntr = FALSE;
    int    dims = FALSE;
    int    success = FALSE;

    /*  GetDisplayInfoData() takes either a handle to a DisplayInfoRecord 
        or a "composite key" ID if no handle is passed in... in this case we
        skip handle = FindDisplayInfo(ID) and pass a handle of NULL relying 
        on GetDisplayInfoData() searching for the key in the database, which 
        incurs a little overhead... */

    if( info=GetDisplayInfoData(NULL,&queryinfo,sizeof(queryinfo),DTAG_DISP,ID))
    {
        /* if we reach here then "info" bytes were copied from the datachunk
           into the queryinfo buffer: now fill the monitor and dimension bufs */

        mntr=GetDisplayInfoData(NULL,&querymntr,sizeof(querymntr),DTAG_MNTR,ID);
        dims=GetDisplayInfoData(NULL,&querydims,sizeof(querydims),DTAG_DIMS,ID);

        /* we have copied the available information: lets see if there 
           any reasons why we shold not try to display it at this time */

        if(!(queryinfo.NotAvailable)) success= TRUE; 
    }

    /* return the "availability" of the mode associated with the argument ID */

    return( (info && mntr && dims)? success : FALSE );
}



#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/rastport.h>
#include <graphics/view.h>
#include <libraries/dos.h>

/* forward references */

extern                 create_display( ); /* draw a recognizable testpattern */
extern struct RastPort *create_rp    ( ); /* allocate and init a rastport    */
extern struct RasInfo  *create_ri    ( ); /* allocate and init a rasinfo     */
extern struct BitMap   *create_bm    ( ); /* allocate and init a bitmap      */
extern struct ViewPort *create_vp    ( ); /* allocate and init a viewport    */
extern struct View     *create_view  ( ); /* allocate and init a view        */

extern                  dispose_ri   ( ); /* deallocate a rasinfo            */
extern                  dispose_bm   ( ); /* deallocate a bitmap             */
extern                  dispose_vp   ( ); /* deallocate a viewport           */
extern                  dispose_view ( ); /* deallocate a view               */



/* ok, lets actually create and display some view/viewport with this mode ID */

open_mode( ID )
ULONG ID;
{
    struct MonitorSpec *mspc, *OpenMonitor();
    struct View        *view;
    struct ViewPort    *vp;
    struct RasInfo     *ri;
    struct RasPort     *rp;


    /* mspc is a handle to a graphics internal Monitor Specification structure. 
       every composite mode key ID is associated with a graphics MonitorSpec 
       even thought all modes on the same monitor share the same MonitorSpec.
       in order to obtain this mspc pointer and make it available to your view,
       you must call OpenMonitor(...,ID) in order to obtain a "blind" handle.
       never deference this handle yourself, it's strictly graphics private! */
       

    if( ( mspc = OpenMonitor( NULL, ID ) ) /* open the monitorspec handle */
    &&  ( view = create_view( mspc ) )     /* allocate and init your view */
    &&  ( vp   = create_vp( view, ID ) ) ) /* allocate and init your vp   */
    {
        view->ViewPort = vp; 

        if( ri = create_ri( vp ) )
        {
            vp->RasInfo = ri;

            if( rp = create_rp( ri ) )
            {
                struct View *restore_view = GfxBase->ActiView;

                create_display( vp, rp );

                MakeVPort( view, vp );
                MrgCop( view );
                LoadView( view );          /* show this modeID display */

                Wait(SIGBREAKF_CTRL_C);    /* and wait for a signal... */
                
                LoadView( restore_view );  /* begin clean up procedure */

                dispose_rp( rp );
            }   
            if( ri ) dispose_ri( ri );
        }   
        if( vp )dispose_vp( vp );          /* deallocate your viewport  */
        if( view )  dispose_view( view );  /* deallocate your view      */
        CloseMonitor( mspc );              /* dont forget to close mspc */
    } 
}


/* we need to include the file monitor.h in order to set the DisplayClip
   rectangle in the ViewPortExtra structure to limit the display region...    */

#include <graphics/monitor.h>

/* here is the viewportextra structure - this structure is obtained via GfxNew.
   the DisplayClip rectangle specifies the edges of the actively displayed part
   of this viewport's bitmap in its own pixel resolution, relative to the view
   origin.  note that for 1.4, since the ECS allows blits to very large bitmaps,
   the raster may be much larger than the "onscreen" display area.  this is why
   larger screens may now scroll left/right and up/down without restriction and 
   still have smoothly "clipped" edges on the display: for smooth clipping. 

   for smooth clipping make certain that your displayclip is wholly contained
   within DimensionInfo.MaxOScan...

struct ViewPortExtra
{
        struct ExtendedNode n;
        struct ViewPort *ViewPort;      /* backwards link
        struct Rectangle DisplayClip;   /* makevp display clipping information
};                                                                            */

/* we need to include the file videocontrol.h in order to associate a particular
   DisplayInfoRecord with the viewport that we are going create... the interface
   to use for this association is the new graphics function VideoControl().   */

#include <graphics/videocontrol.h>

/* the videocontrol commands that we will use operate on a ColorMap structure */
/* each videocommand is a TagItem:

    struct TagItem              /* as defined in intuition/tagitems.h   
    {
        ULONG   ti_Tag;         /* identifies the type of this item     
        ULONG   ti_Data;        /* type-specific data, can be a pointer 
    };                                                                  

/* we will need these commands:                                               

   1) attach a ColorMap obtained via GetColorMap() to our new viewport 
   2) set some extra viewport information to displayclip this viewport
   3) associate the displayinforecord for this mode with this viewport        

/* here are the commands we will use to set up the viewport's colormap:       */                
struct TagItem vc[] =
{
    { VTAG_ATTACH_CM_SET, NULL },       /* overwrite NULL with viewport       */
    { VTAG_VIEWPORTEXTRA_SET, NULL },   /* overwrite NULL with viewportextra  */
    { VTAG_NORMAL_DISP_SET, NULL },     /* overwrite NULL with displayinfo    */
    { VTAG_END_CM, NULL }               /* end-of-command list terminator     */
};


struct ViewPort *create_vp( view, ID )
struct View *view;
ULONG ID;
{
    struct ViewPort  *vp       = NULL;
    struct ViewPortExtra *vpx  = NULL;  /* new for V1.4 */

    if( vp = (struct ViewPort *) AllocMem(sizeof(*vp),MEMF_PUBLIC|MEMF_CLEAR) )
    {
        struct Rectangle *oscan    = &querydims.Nominal;
        ULONG  WIDTH   = oscan->MaxX - oscan->MinX + 1 ;
        ULONG  HEIGHT  = oscan->MaxY - oscan->MinY + 1 ;

        InitVPort( vp );

        /* the composite key mode ID's have been cleverly selected to obtain
           maximum 1.2/1.3 compatibility when their bottom 16 bits are used
           as  templates for the vp->Modes field of your viewport... since
           old iff readers/writers may be saving these modes to disk based
           files, we have no guarantee that when they read them back that
           the ECS will be available on the target machine... */

        vp->Modes    = (ID & 0xFFFF); 

        vp->DxOffset = 0;       vp->DyOffset = 0; 
        vp->DWidth   = WIDTH;   vp->DHeight  = HEIGHT;

        /* allocate a viewportextra & initialize displayclip for viewport */

        if( vpx = (struct ViewPortExtra *) GfxNew( VIEWPORT_EXTRA_TYPE ) )
        {
            vpx->DisplayClip.MinX = oscan->MinX;
            vpx->DisplayClip.MinY = oscan->MinY;
            vpx->DisplayClip.MaxX = oscan->MaxX;
            vpx->DisplayClip.MaxY = oscan->MaxY;
        }

        /* pass new information to the system via VideoControl interface */

        vc[0].ti_Data = (ULONG)vp;
        vc[1].ti_Data = (ULONG)vpx; 
        vc[2].ti_Data = (ULONG) FindDisplayInfo( ID );

        /* get a full colormap and execute the videocommands for this cm */

        VideoControl( GetColorMap(32), vc );

        /* set the global lace */

        view->Modes |= ((DIPF_IS_LACE & queryinfo.PropertyFlags)? LACE: 0);
    }

    return( vp ); 
}


/* videocontrol commands that we will use to cleanup on closing the display */
/* we will need these commands:                                             */

struct TagItem end_vc[] =
{
    { VTAG_VIEWPORTEXTRA_GET, NULL }, /* <--- this field will be filled in */
    { VTAG_END_CM, NULL }             /* below by viewportextra_get to vpx */
};

dispose_vp( vp)
struct ViewPort *vp;
{
    struct ViewPortExtra *vpx;
    int error = FALSE;

    if( vp )
    {
        error = VideoControl( vp->ColorMap, end_vc ); /* fetch the vpx ptr  */

        if(!error)
        {
            if(vpx = (struct ViewPortExtra *)end_vc[0].ti_Data)
            {
                GfxFree( vpx );         /* free the viewportextra structure */
            }
        }

        if ( vp->ColorMap )
        {
            FreeColorMap( vp->ColorMap );
        }

        FreeVPortCopLists( vp );
        FreeMem( vp, sizeof(*vp) );
    }
}


struct View *create_view( mspc )
struct MonitorSpec *mspc;
{
    struct View *view;
    struct ViewExtra *vx;


    if( view = (struct View *)AllocMem(sizeof(*view),MEMF_PUBLIC|MEMF_CLEAR) )
    {
        InitView( view );

        /* viewextra is new for V1.4 -- it is used to pass the mspc for this
           view to the graphics display routines such as makevp, mrgcop, and
           loadview... as well as others. it is allocated with GfxNew( ) and
           freed with GfxFree( ).  since there is no field in the view to
           point to this viewextra information, you must call the hashing
           function GfxAssociate( view, vx ) which creates a soft "link" 
           between the view and the viewextra. after hashing, set the
           EXTEND_VSTRUCT bit in the view->Modes to let the sytem know
           when a hash GfxLookup() needs to be performed... */

        if( vx = (struct ViewExtra *)GfxNew( VIEW_EXTRA_TYPE ) )
        {
            vx->Monitor = mspc;
            GfxAssociate( view, vx );
            view->Modes |= EXTEND_VSTRUCT;
        }

        view->DxOffset = querymntr.ViewPosition.x ;
        view->DyOffset = querymntr.ViewPosition.y ;
    }
    return( view );
}

dispose_view( view)
struct View *view;
{
    if( view )
    {
        if (view->Modes & EXTEND_VSTRUCT)
        {
            struct ViewExtra *vx;     

            if( vx = (struct ViewExtra *) GfxLookUp( view ) )
            {
                GfxFree( vx );  /* gfxfree will inactivate and deallocate vx */
            }
        }
        if ( view->LOFCprList ) FreeCprList ( view->LOFCprList );
        if ( view->SHFCprList ) FreeCprList ( view->SHFCprList );

        FreeMem( view, sizeof(*view) );
    }
}

create_display( vp, rp )
struct ViewPort *vp;
struct RastPort *rp;
{
    int i,j,k;
    char text[1];
    WORD x_center = vp->DWidth  >> 1; /* center of the display, x dimension */
    WORD y_center = vp->DHeight >> 1; /* center of the display, y dimension */
    WORD width  =   vp->DWidth  >> 2; /* width of ellipse 1/4 width display */
    WORD height =  /* ellipse height, adjusted for aspect ratio of the mode */
    (( ( LONG )( width * queryinfo.Resolution.x ) ) / queryinfo.Resolution.y );

    if(vp && rp)
    {
        SetAPen(  rp,  -1 );
        SetDrMd(  rp,  JAM1 );

        /* "circle" in the center, 1/4 width of the screen  */
        DrawEllipse( rp, x_center, y_center, width, height );

        /* outline the edge of the display with a rectangle */
        Move( rp, 0,            0 ); 
        Draw( rp, 0,            vp->DHeight-1);
        Draw( rp, vp->DWidth-1, vp->DHeight-1);
        Draw( rp, vp->DWidth-1, 0);
        Draw( rp, 0,            0);

        /* indicate diagonals */
        Move( rp, 0,            0 );
        Draw( rp, vp->DWidth-1, vp->DHeight-1);
        Move( rp, 0,            vp->DHeight-1);
        Draw( rp, vp->DWidth-1, 0);

        /* dimension counters along the top and left edges  */
        for(i=0; i < vp->DWidth; i+=(1<<4))
        {
            Move( rp, i, 0 ); Draw( rp, i, vp->DHeight-1);
            if(!i) for(k=0; k+12 < vp->DHeight; k+=(1<<4))
            {
                *text = '0'+((k>>4)%10);
                Move( rp, i+4, k+12 ); Text( rp, text, 1);
            }
        }
        for(j=0; j < vp->DHeight; j+=(1<<4))
        {
            Move( rp, 0, j ); Draw( rp, vp->DWidth-1, j);
            if(!j) for(k=0; k < vp->DWidth; k+=(1<<4))
            {
                *text = '0'+((k>>4)%10);
                Move( rp, k+4, j+12 ); Text( rp, text, 1);
            }
        }
    }
}

struct RasInfo  *create_ri( vp )
struct ViewPort *vp;
{
    struct RasInfo *ri  = NULL;
    struct BitMap  *bm  = NULL;
    struct RasInfo *ri2 = NULL;
    struct BitMap  *bm2 = NULL;
    int    depth        = querydims.MaxDepth;
    int    depth2       = depth / 2;
    int    error        = FALSE;

    if( queryinfo.PropertyFlags & DIPF_IS_DUALPF )
    {
        if( depth2 )
        {
            depth -= depth2;

            if( bm2 = create_bm( vp, depth2 ) )
            {
                if( ri2 =  
                (struct RasInfo *)AllocMem(sizeof(*ri2),MEMF_PUBLIC|MEMF_CLEAR))
                {
                    ri2->BitMap = bm2;
                }
                else error = TRUE;
            }
            else error = TRUE;
        }
    }

    if( !error )
    {
        if( bm = create_bm( vp, depth ) )
        {
            if(ri = 
              (struct RasInfo *)AllocMem(sizeof(*ri),MEMF_PUBLIC|MEMF_CLEAR) )
            {
                ri->BitMap = bm;
                ri->Next   = ri2;
            }
            else error = TRUE;
        }
        else error = TRUE;
    }

    if( error )
    {
            if( bm  ) dispose_bm( bm  );
            if( ri2 ) dispose_ri( ri2 );
            if( bm2 ) dispose_bm( bm2 );
    }

    return( ri );
}


struct RastPort *create_rp( ri )
struct RasInfo  *ri;
{
    struct RastPort *rp = NULL;
    struct BitMap   *bm = ri->BitMap;

    if( bm 
    &&( rp = (struct RastPort *)AllocMem(sizeof(*rp),MEMF_PUBLIC|MEMF_CLEAR) ) )
    {
        InitRastPort( rp );
        rp->BitMap = bm;
    }
    return( rp );
}

dispose_rp( rp )
struct RastPort *rp;
{
    if( rp )
    {
        FreeMem( rp, sizeof(*rp));
    }
}

dispose_ri( ri )
struct RasInfo *ri;
{
    if( ri->Next ) dispose_ri( ri->Next ); /* recurse for dual playfield */

    if( ri )
    {
        dispose_bm( ri->BitMap );
        FreeMem( ri, sizeof(*ri) );
    }
}



struct BitMap *create_bm( vp, depth )
struct ViewPort *vp;
int    depth;
{
    struct BitMap  *bm = NULL;
    int i;

    if( vp && depth )
    {
        if( bm = (struct BitMap *) AllocMem(sizeof(*bm),MEMF_PUBLIC|MEMF_CLEAR))
        {
            InitBitMap(bm, depth, vp->DWidth, vp->DHeight );

            for (i = 0; i < bm->Depth; i++)
            {
                if( bm->Planes[i] = (PLANEPTR) /* allocate chip memory planes */
                AllocMem( RASSIZE(vp->DWidth,vp->DHeight),MEMF_CHIP|MEMF_CLEAR))
                {
                    continue;
                }
                else bm->Depth = i; dispose_bm( bm ); return( NULL );
            }
        }
    }

    return( bm ); 
}

dispose_bm( bm )
struct BitMap *bm;
{
    if( bm )
    {
        int i;

        for(i=0; i < bm->Depth; i++)
        {
            if(bm->Planes[i]) FreeMem( bm->Planes[i], bm->BytesPerRow*bm->Rows);
        }
        FreeMem( bm, sizeof(*bm));
    }
}



