The source tree
===============

The root of the graphics source contains the makefile, .h and .i files, sfd
files and all that other junk. The source code is split over a number of other
subdirectories:

a	- contains assembly files, including stubs to call the c files.
c	- contains c files, plus some assembly files that were downcoded from C.
	  This includes (for some unkown reason) misc.asm, which has the vblank
	  code and other misc assembly code.
d	- contains the code for the graphics database.
gels	- contains the code for the gels subsystem
text	- contains the code for all the text routines.
fonts	- contains the definitions of the topaz default font.

Building
========

V42 graphics uses bmake, which is documented in v40:aug/src/bmake.  You need the
makemeta file in lib:.  The Makefile is set up, and all you need to type is
bmake.

Here is a list of the commands you can use:

bmake		Makes the specialfx.ld file
bmake clean	Deletes all the .obj files
bmake depend	Makes a file called 'dependencies'. I think you need to have
		and empty file called dependencies created first though.
bmake doc	Makes the autodocs
bmake headers	Releases the include files
bmake release	General release to the server
bmake rev	Bumps the revision number
bmake checkout	Checks out all the files into the relevant directories.
bmake proto	Makes the proto files

To get started:

0) First, you'll need to copy the following tools from
   V42:src/kickstart/graphics/Tools to your local tools directory of choice:

   bmake hx68 perl spp

   Then copy V42:src/kickstart/graphics/Tools/makemeta to your lib: directory.

1) Create a file called RCS_Link which contains a line of the path to the main
RCS directory eg

v42:src/kickstart/graphics/RCS/

2) Create a file called dependencies which contains one blank line.
3) Type co makefile
4) Type co makefile_master

You should now have a directory containing four files - RCS_Link, dependencies,
makefile and makefile_master.

5) Type bmake checkout

Now you should have the complete source tree.

6) Type bmake depend to build the full dependencies files.
7) Type bmake protos to build the private proto files.
8) Type bmake rev to make a revision file.
9) Copy clip.h and clip.i from the include:graphics to the root.
   Copy V42:src/kickstart/graphics/gfxpragmas to the root.
10) Type bmake to build graphics.ld

Congratulations. You are now the proud owner of a V42 graphics.library.

Graphics Database
=================

Here is the documentation Bart left me. I have modified where necessary to
reflect the current (V40) implementation.


From bart Thu Jan 31 15:34:43 1991
Received: by cbmvax.cbm.commodore.com (5.57/UUCP-Project/Commodore Jan 13 1990)
	id AA14667; Thu, 31 Jan 91 15:34:40 EST
Received: by prodigal.boing.com (3.2/SMI-3.2)
	id AA07168; Thu, 31 Jan 91 15:32:06 EST
Date: Thu, 31 Jan 91 15:32:06 EST
From: bart (Bart Whitebook)
Message-Id: <9101312032.AA07168@prodigal.boing.com>
To: cbmvax!spence
Status: RO

--------------------------------------------------------------------------------
                Inroduction to the Graphics Display Database
--------------------------------------------------------------------------------


        The graphics display database was introduced for the 2.0 KS release
in order to provide a means to abstract the specification of a mode (hires,
superhires, lace, dualpf, etc) and to prevent interperetation of specific
bits in the Modes field by users or application programs. In the past,
progams used to look at screen heights and say "if this screen is >320 wide
it must be HIRES" or "if the HIRES bit is set in vp->Modes it must be 640
wide".  With the advent of the ECS chipset and display clipping these and
other naive assumptions are no longer true.


        The graphics database itself consists of four elements:

        1) code in d/startup.c to unpack a data driven database
        specification and to create a database hanging off GfxBase.
        (the internal record format is defined in displayinfo_internal.h)

        2) routines to read information out of the database into user records
        (the external record format is defined in displayinfo.h, a public file)

        3) routines to set information in the internal database records
        (translating from the external to the internal format in the process)

        4) DisplayID definitions (32bit abstract specifiers) to uniquely
        identify each record in the database (defined in modeid.h)

--------------------------------------------------------------------------------


        The database itself consists of DisplayInfoRecords, which are
        linked both "vertically" (parent/child) and "horizontally" (siblings).
        All records in the database begin with a RecordNode structure which
        defines these links.

        There is a root node from which the entire database is accessed,
        after it is expanded by d/startup.c from a rom specifiction to
        real DisplayInfoRecords in ram memory.

        This root node is pointed to by GfxBase->DisplayInfoDataBase.

        (GfxBase)               siblings <->
            |
            |                   children
            V                       |
        RootNode                    V
            |
            V
        DfltNode <-> NtscNode <-> PalNode  <-> VgaNode  <-> A2024Node
            |           |           |            |            |
            V           V           V            V            V
        LoresNode    LoresNode    LoresNode    VgaLoresNode  10HzNode
            |           |           |            |            |
            V           V           V            V            V
        HiresNode    HiresNode    HiresNode    VgaHiresNode  15HzNode
            |           |           |            |
            V           V           V            V
        SupHiresNode SupHiresNode SupHiresNode Productivity
            |           |           |            |
            V           V           V            V
        LoresLace    LoresLace    LoresLace    VgaLoresLace
            |           |           |            |
            V           V           V            V
        HiresLace    HiresLace    HiresLace    HiaLoresLace
            |           |           |            |
            V           V           V            V
        SupHiresLace SupHiresLace SupHiresLace ProductivityLace

        (there are more records than shown here, but this should give you
         the idea)


--------------------------------------------------------------------------------

        each record in the database has a major key (16 bits) and a minor
        key (another 16 bits) to identify it for searches of the database.

        for a given DisplayID, say VGAPRODUCT_KEY :

        #define VGAPRODUCT_KEY                  0x00039024

        the major key is 0x0003 and the minor key is 0x9024

        the VgaNode has a major key of 0x0000 and a minor key of 0x0003.
        the Productivity has a major key of 0x0003 3nd a minor key of 0x9024.

        when FindDisplayInfo(VGAPRODUCT_KEY) is called, the internal
        routine RootNode out of  GfxBase->DisplayInfoDataBase and calls
        the internal routine

        find_id( GB->DisplayInfoDataBase, VGAPRODUCT_KEY);

        this then splits VGAPRODUCT_KEY into its major and minor components
        and calls:

                first = find_key( SubRecord(root), major, ~0 )

        to find the "first level down off the root" node then calls

                second = find_key( SubRecord(first), minor, ~0 )

        to find the "second level down off the root" node, which is the
        handle to the desired vga productivity mode displayinforecord node.


--------------------------------------------------------------------------------

        having gotten a handle to a displayinforecord via find_id()
        ONLY graphics.library is allowed to derefernce this handle directly:

        struct DisplayInfoRecord
        {
            struct RecordNode  rec_Node;
            UWORD              rec_MajorKey;
            UWORD              rec_MinorKey;
            struct TagItem     rec_Tag;
            ULONG              rec_Control;
            ULONG              (*rec_get_data)();
            ULONG              (*rec_set_data)();
            struct Rectangle   rec_ClipOScan;
            ULONG              reserved[2];
        };

        struct RecordNode  rec_Node; links this node to its parent, child,
        and sibling nodes.

        UWORD  rec_MajorKey; see discussion above, used for finding this node
        UWORD  rec_MinorKey; see discussion above, used for finding this node

        ULONG rec_Control; are the mode bits that the graphics software in
        makevp, etc. interperets directly. since the DisplayID used to
        get this handle consists of bits that can NOT be interpereted
        directly, this is the place that graphics.library looks to
        to determine if this is LACE, HAM, DUALPF, etc.

        struct TagItem rec_Tag = { TAG_MORE, (pointer to chain of tag data) }
        is used to "hang" data off of this displayinforecord. when an application
        calls GetDisplayInfoData(handle,...,DTAG_XXXX,....); a search is
        made of this linked list of TagItems to try and find that data
        in this list.  if the data is found it is "cooked" (translated
        fron the internal to the external format) and stuffed into a
        user buffer.

        struct Rectangle rec_ClipOScan; is used to "limit" displayclips for this
        mode to some maximum legal values, and was added late in the game to
        fix a few bugs easily (showing how easily we can extend this database
        without screwing anyone up, since the internal representation is
        strictly graphics PRIVATE).

        ULONG (*rec_get_data)();
        is set to NULL by the system at powerup. after "cooking" data
        from the database during the GetDisplayInfoData() call, the system
        looks to see if an application has stuffed a function into the
        get_data pointer, and that function is called to "cook" some tag
        that the (commodore) application has linked into the TagItem list.
        this is for flexible future extension/expansion of the database
        without requiring a ROM mod.

        ULONG (*rec_set_data)();
        is set to NULL by the system at powerup. after "uncooking" data
        into the database during the SetDisplayInfoData() call, the system
        looks to see if an application has stuffed a function into the
        get_data pointer, and that function is called to "uncook" some tag
        that the (commodore) application has linked into the TagItem list.
        this is for flexible future extension/expansion of the database
        without requiring a ROM mod.


NB - I think both rec_get_data() and rec_set_data() are still set to NULL
always; I don't even know if they would be called were they not.
-- Spence


--------------------------------------------------------------------------------

        the TagItems in the tagitem list are of a special format for this
        database: each begins with a QueryHeader structure

        struct QueryHeader
        {
            ULONG   StructID;       /* datachunk type identifier */
            ULONG   DisplayID;      /* copy of display record key   */
            ULONG   SkipID;         /* TAG_SKIP -- see tagitems.h */
            ULONG   Length;         /* length of local data in double-longwords */
        };


        and may look like

        struct MyRawDataBaseInfoListNode
        {
                DTAG_MINE,      <--- dispay info base tagid for this chunk
                MY_DISPLAYID,   <--- copy of the id belonging to my parent node
                TAG_SKIP,       <--- system tagid to skip this node unless known
                N,              <--- how many double longwords of local data here
                .
                .               <--- N double longwords of local raw (uncooked) data
                .
                TAG_MORE,       <--- system tagid to get to the next tagitem
                *nextitem       <--- pointer to next tagitem in node's list
        }

--------------------------------------------------------------------------------

        note that SOME tag items (such as DTAG_MNTR are GLOBAL to a family
        of "mode" records, ie: VGAPRODUCT_KEY and VGAPRODUCTLACE_KEY are each
        seperate "modes" belonging to the vga "monitor" node. when somebody
        asks to

                GetDisplayInfoData(handle, ... , DTAG_MNTR, ... );

        the search for the TagItem DTAG_MNTR is made, not of this handle's
        TagItem list, but rather of the handle's PARENTs' TagItem list.
        this allows the global monitor data to be "shared" among all
        the modes on that monitor (see the function "cook()" in find_key.c
        for details on this operation).

--------------------------------------------------------------------------------

END OF MAIL MESSAGE

The way the database is initially unpacked is a nightmare. All the information
is stored in 'squeezed' format, which is a format I adapted from the
'compressed raw' format that Bart was initially using and was taking up way too
much ROM space.

Basically, there are two offset tables (one for NTSC machines and one for PAL
machines) which are the entry number into an array of the relevant squeezed
structures.

This is used by the function do_db_startup() in d/startup.c.

However, for V42, I recommend scrapping it. In fact, throw the whole database
implementation away; it sucks. Fortunately, the database is black-boxed, and can
only be accessed from well-defined APIs, so the underlying implementation can be
redesigned, as long as the data returned is compatible and correct of course.

Seeing as how the data for the monitors is generated algorithmically for the V40
monitor files, it should be easy to modify code that to generate the data for
NTSC and PAL displays too. In fact, I recommend saving a whole glob of ROM and
RAM space by just using that function to generate the data on the fly, as and
when it is requested. That should be a little slower than the current system of
copying the data out of RAM (and in some cases modifying it too) into the
specified buffer, but the benefits would probably be greater. There would still
need to be some data stored in RAM, namely all the data that can be modified by
preferences (the OScan areas for example), but everything else could be
algorithmic.

You would still need to maintain compatibility with any old monitor files
installed, which is a real pain in the butt. 'Good' software won't be booted off
the program's disk, so the new monitors would be used, but not everyone has a
hard disk, and so some software may add the old-style monitors. I defined a new
flag for GfxBase->GfxFlags called NEW_DATABASE which you should set if you redo
the database implementation.


For V39, I added a new data type called VecInfo, which is data private to
graphics. It consists of the data needed to build each unique modeID in
MakeVPort() (and ScrollVPort() etc). You can do anything you like with this as
graphics.library is the only one using it; for instance, you may want to add the
data to build the new AAA 32bit copper lists.


Monitors
========

The monitor files for V40 were completely rewritten from V39. The main file is
called makeentry.c, and it builds the database information which gets linked in
to the database list in RAM.

Each programmed beam monitor has an associated makefile, and a .h file
containing the timings for that monitor, all #defined. Using dblntsc.h as an
example, the file contains:

/******************************************************************************
*
*	$Id: dblntsc.h,v 40.1 93/02/16 18:24:12 spence Exp $
*
******************************************************************************/

#include <graphics/monitor.h>
#include <graphics/modeid.h>
#include "monitorstuff.h"

#define MONITOR_NUM   9
/* The unique monitor number */
#define MONITOR_NAME  "DblNTSC.monitor"
#define ICON_NAME "PROGDIR:DblNTSC"
#define BEAMCON0 0x1b88
/* For the RGA BeamCon0 register */
#define MSFLAGS (REQUEST_SPECIAL | (GfxBase->ChipRevBits0 & GFXF_AA_LISA ? MSF_DOUBLE_SPRITES : 0) | ((GfxBase->DisplayFlags & PAL)? REQUEST_PAL : REQUEST_NTSC))
/* monitor->ms_Flags flags */
#define TOTALCLKS	0x79
/* The number of colour clocks per scan line */
#define TOTALROWS	0x1ec
/* The total number of rows (including vblank) per field. */
#define MINIMUMROW	0x19
/* The first row after VBlank */

/* Bad AA definitions -- ie values needed to get around the LHS scroll problem
 * on AA machines when VGAOnly is not added.
 */
#define AATOTCLKS	(TOTALCLKS + 8)
#define AATOTROWS	0x1dd
#define AAMINROW	0x17

#define TOTROWS ((GfxBase->Bugs & BUG_ALICE_LHS_PROG) ? AATOTROWS : TOTALROWS)
#define TOTCLKS ((GfxBase->Bugs & BUG_ALICE_LHS_PROG) ? AATOTCLKS : TOTALCLKS)
#define MINROW  ((GfxBase->Bugs & BUG_ALICE_LHS_PROG) ? AAMINROW  : MINIMUMROW)
/* Values used in makeentry.c */

#define PREFERRED_MODEID DBLNTSCHIRESFF_KEY
/* MonitorInfo->PreferredModeID used by Overscan prefs. */
#define COMPAT MCOMPAT_MIXED
/* MonitorInfo->Compatibility */

#define VRESNX 88
#define VRESNY 26
/* Some magical ticks-per-pixel value. I never understood these - they are all
 * scary voodoo.
 */

#define NOM_WIDTH 640
#define NOM_HEIGHT 400
/* Nominal dimensions */

#define PIX_RES_X 22
#define PIX_RES_Y 26
#define SPRITE_RES_X (PIX_RES_X << 1)
#define SPRITE_RES_Y PIX_RES_Y

#define MOUSETICKS_X 22
#define MOUSETICKS_Y 26
/* More voodoo */

#define MIN_DIPF_FLAGS	(DIPF_IS_ECS | DIPF_IS_PROGBEAM | DIPF_IS_SPRITES | DIPF_IS_DRAGGABLE | DIPF_IS_BEAMSYNC | DIPF_IS_DBUFFER | DIPF_IS_PROGBEAM)
/* Every ModeID will have these DIPF_.. bits set */

#define WB_35 TRUE
#define WB_70 TRUE
#define WB_140 FALSE
/* Which pixel speed can be used for the WB and have DIPF_IS_WB set. */

#define LOW_X_RESN RESN_140
/* Lowest X resolution to build data for */
#define HIGH_X_RESN (IsECS ? RESN_35 : RESN_70)
/* Highest X resolution to build data for */
#define LOW_Y_RESN (IsAA ? SDBLED : NON_LACE)
/* Lowest Y resolution to build data for */


There is another important file called monitorstuff.h which contains a whole
load of macros for calculating dimensions, blanking etc from the data in the
<monitor>.h files. THis file is annotated.

AddMonitor.c is the main() file, and takes care of things such as initilisation,
localisation etc.


Here is a list of the <monitor>.h files:
ntsc.h
pal.h
multiscan.h
a2024.h
euro36.h
euro72.h
super72.h
dblntsc.h
dblpal.h
film24.h	- a special 48Hz monitor for genlocking to a film camera.
motivator.h
motivatim.h	- these two are variants for the motivator prototype card.

Here is a list of current Monitor ModeIDs:

The upper nybble of the longword is used to show a 3rd party device, and has a
value 0x4. The second nybble is used as a repeater, in case of multiple cards in
one system.

eg Euro72 has an ID of 0x0006xxxx. On a A3000 with, say a AA card, there would
be two Euro72s, one native, and one on the card. The second would have an ID of
0x0106xxxx. A third would have 0x0206xxxx etc.

Monitor #	Name			Owner
---------	----			-----

0		default			CBM V36
1		NTSC			CBM V36
2		PAL			CBM V36
3		multiscan		CBM V36
4		A2024			CBM V36
5		reserved
6		Euro72			CBM V38
7		Euro36			CBM V38
8		Super72			CBM V38
9		DblNTSC			CBM V39
a		DblPAL			CBM V39
b		Motivator		CBM V39
c		Film24			CBM V39


40010000	EGS			GVP
40020000	Picasso			VillageTronic

Monitor Example
===============

Let's say we want to build a 1024 x 768 monitor (call it Motivator). To
calculate the colour clocks needed per row:

TotalColourClocks = The minimum value you can set for DDFSTRT +
	(the number of 35ns pixels in the line /
	 the number of 35ns pixels per colour clock)
	+ 1

	= 0x18 + (1024 / 8) + 1	= 0x99

Total Rows = Total visible rows +
	     Worse-case interscreen gap +
	     Good minimum number of lines for Copinit

	   = 768 +
	     ((on AA, 256 colours * 2 colour loads each + 16 writes to bplcon3
	      to change the colour bank and the LOCT bit) /
	      (TotalCopperCycles per line))
	     + 5, which is the min value to set the WAIT for in copinit

	   = 768 + (528 / (0x99 / 2)) + 5

	   = 780

MinRow = (528 / (0x99 / 2)) + 5 = 12

We can calculate the timings as:

Vertical = Total nanoseconds per second / (total colour clocks per line * ns per
           colourclock * total rows)
         = 1e9 / (0x99 * 280 * 780) = 29.9Hz

Horizontal = Vertical * total rows
           = 29.9 * 780 = 23.3kHz




Other Docs
==========

More notes can be found in v39:src/kickstart/graphics/Notes
