AVOIDING INIT COLLISIONS AT BOOT TIME

Loading device drivers with an INIT isn't the only way, but it's the best

John Rosford

John Rosford is an engineer at National Instruments and can be contacted at 12109 Technology Blvd., Austin, TX 78727-6204.


This article describes a Macintosh INIT that loads a complex set of device drivers for a group of IEEE-488 interface and data acquisition cards. Even though drivers vary widely in purpose, the code that loads them at boot time is much the same, so this basic INIT will be able to load most driver configurations. One of the main goals of this INIT code is to prevent collisions with drivers previously loaded by other INITs, so this program will be useful to Mac programmers who have drivers in use, as well as those writing new ones.

The source code for the INIT program consists of initOpenDRVR.c ( Listing One, page 62) and initOpenDRVR.h (Listing Two, page 68). Source code for other resources can be found in DDJInit.r (Listing Three, page 70). The tools I used include MPW 3.0 (to compile and copy the resources to the INIT) and Think C 3.0.

Loading Device Drivers

There are several methods for making a device driver available to an application, the most common being to install the driver into the system file. Several standard drivers are used by the Macintosh and are found in the System file.

These drivers are installed when you upgrade or initially install the system software by running the Installer.

Another method is to write an INIT to load the drivers at boot time, as suggested by the "Macintosh Technical Note #14." All INITs in the System folder are run at boot time by the system startup code. A third option, rarely used, is to make the application load its own copy of the drivers. This last option presents a difficult problem of hardware resource access control when more than one application/driver is used under MultiFinder.

Device Drivers, Expansion Cards, and the INIT

In this section, I'll describe a set of drivers and their association to subdrivers and the expansion cards that they drive. In doing so, I'll provide an overview of the tasks performed by the INIT program.

Before an application can use either the NB-GPIB or NB-DMA-8G expansion boards, the drivers must be initialized. These drivers share data because the NB-GPIB and other data acquisition boards request DMA services from the DMA driver.

The master driver, .LabDriver, is opened first (see Figure 1 and Listing Three). It opens each board driver for the boards found in the computer, and points them to the master global storage area, which they fill in with information about the board. The board driver .NB-GPI does nothing more than fill in information about the NB-GPIB board; the driver is never called by an application and it does not manipulate the hardware. The .NB-DMA driver not only fills in information about the NB-DMA-8G board, it also provides DMA services for the NBHardwareCode driver and other data acquisition drivers.

Next, the INIT opens as many GPIB bus drivers as needed -- in this case, if auto-configuration is set. The bus driver will be .gpib0 for the GPIB hardware in the lower slot and .gpib1 will be for the GPIB in the higher slot.

Each bus driver opens the shared code and the hardware-variant code. The bus driver opens hardware variant code that matches the hardware type for the slot associated with the bus driver. These open calls link the bus driver's read, write, and control calls to the hardware drivers.

One of the drivers is a hardware variant for the serial ports. This driver can't be automatically configured at boot time, because there is no way to find out what kind of device is attached without disturbing the serial port. If you write commands out the port to see if it is a serial device that the INIT has a driver for, you will get undefined results if the port is connected to some other device.

Because the INIT sysz resource has requested allocation of a certain amount of additional system heap memory, the drivers in the INIT should allocate during the execution of the INIT all the memory that they will need. Memory not allocated by the drivers can be taken by subsequent INITs, and thus will not be available when an application executes a call to one of the drivers. There is no recommended way to expand the system heap at run time, so you are stuck with an inoperative driver until something, like a desk accessory or driver, releases enough system memory, usually by being closed.

The NB-DMA-8G is a multifunction board. It has two distinct hardware sections, the GPIB section and the DMA section. Making a driver for each section is the best approach. The NBHardwareCode driver handles the GPIB section, and the NB-DMA driver handles the DMA section. This approach minimizes the duplication of code for this set of drivers. Any driver for the GPIB or data acquisition can use DMA services by calling the DMA driver instead of manipulating the DMA section of the NB-DMA-8G directly.

The NBHardwareCode is a multiboard driver. If the bus is associated with the NB-DMA-8G, the drivers, as just described, are used. The NBHardwareCode handles both the NB-GPIB board and the GPIB section of the NB-DMA-8G board; the hardware is virtually identical.

The INIT code performs a number of simple steps to load a set of drivers. Those steps are:

  1. Initialize the Macintosh Toolbox Managers. Because most of the Toolbox Managers should not be initialized until needed, you only need to initialize and open the graph port (for plotting the icon) to show that the INIT is running. This initialization makes the current grafPort the size of the screen.
  2. Show the icon in the bottom lower-left corner of the screen. The icon will remain on the screen until after all INITs have run or InitWindows is called.
  3. Check for a user abort. If Command Period is pressed, then give the option of aborting the device driver installation. This should be standard practice for INITs. (If you hold down the Command Period keys to prevent the INIT from loading drivers and the Command key is still pressed when MultiFinder starts to execute, you will also prevent MultiFinder from launching.)
  4. Call SysEnvirons and check that the INIT is running on the right machine. Some INITs should abort if running on the wrong type of machine, or if the machine is missing needed resources.
  5. Get the driver information tables from resources in the INIT file. There are two tables of information: A driver table and an owned resource table. Information in these tables includes the driver resource ID, which will change if any resource ID collision occurs; the load status; the driver type, which determines under what circumstances the driver is to be loaded; and the driver name. The load status is determined by the INIT from the driver type and the type of boards found in the computer. These tables allow the INIT to calculate the number of drivers and give information about each driver.
  6. Get the configuration data from the INIT file. This data allows the user to customize the operating characteristics of the device drivers or the INIT.
  7. Mark a driver for loading if the slot is for serial hardware; or if the slot is for NuBus and one of our boards is in the slot; or if the driver is called by a driver that is marked for loading.
  8. Alert the user if the device drivers are already loaded. The user may have two different revisions of the drivers in the system. To detect a duplicate set of drivers, point the Resource Manager away from the INIT so it won't see these device drivers. Then try to open the drivers that have been marked for loading. Only these drivers would be previously loaded. Having two sets of drivers in memory would be a waste of memory and driver IDs (a limited resource).
  9. Check that there is enough memory. Now that the drivers have been marked for loading, the INIT must determine whether they will fit in memory. Read each resource into memory. If a memory error occurs, abort the INIT process.
  10. Make sure that each driver ID is unique within the system. There are two places to check: The driver unit table, which has a device control entry (DCE) for each open driver, and the System file. We are interested in those drivers loaded by INITs that have run previous to our INIT, and drivers in the System file, which may be opened later. Because all INITs test for collisions with drivers loaded by previous INITs, there is no chance of a driver collision with subsequently loaded drivers. Drivers loaded by applications must take the same precautions as do those loaded by INITs. If a driver were loaded with an ID that collides with that of an open driver, the new driver would replace the open driver. If its ID were to collide with that of an unopened driver in the System file, then the system driver would replace our driver when the system driver was opened.

    Initially, there are only 64 IDs available, but Inside Macintosh says that the table will automatically expand to 128. Because some of the IDs are reserved for the system, it is not clear whether the table expands when all user IDs are used, or when all 64 IDs are used. If it is the former case, then which IDs are user IDs? Do they include the desk accessory IDs? If a driver's ID is changed, then the ID of each of its owned resources must also be changed.

  11. Open the drivers. In this example, only the master driver and the bus drivers are opened. These drivers are loaded only if needed as determined by markLoad. All other drivers are subdrivers that are opened by the master driver, bus drivers, or another subdriver. Opening a driver creates a DCE in the driver unit table.
  12. Detach the drivers. When an application quits, all of its resources are removed from memory. So that the driver resources will not be removed, they are detached from the INITs list of resources. Detaching the driver resources affects only the memory copy of the resources -- it does not remove the drivers from the INIT file. Because the driver resources are detached, the memory copy of the driver resources won't be removed when the INIT file closes. As long as the drivers remain open, their DCEs will remain in the unit table, allowing your application to open and use the drivers without also opening the INIT file. Configuration data can be passed to the drivers in the open call.

Important Resources

Resources in the file DDJInit.r (Listing Three) are parts of the INIT file that may be conveniently described with the MPW resource language and compiled with the MPW Rez resource compiler. Resources, such as STR#, are standard to the Macintosh OS. As such, there are ROM calls that can make use of these resources directly. Other resources, such as dTbl, were designed specifically for the INIT. Typically, resources for a specific application have a corresponding C structure or Pascal record to identify each part of the data stored in the resource. The structure for the dTbl makes it clear that the data is a list of driver information: The driver ID, load status, type, and name. Figure 2 shows which parts of the operating system and the INIT need which resources. The Finder resources BNDL, FREF, DDJI, and ICN# are discussed in detail in Inside Macintosh, Volume III and vers in "Tech Note #189."

The important resources include three that are user defined: dTbl, oTbl, and busD. The type dTbl is a table of information about the drivers. There are four elements in each entry of the table: The driver ID, the driver load status, the load attribute, and the driver or board name. The type oTbl is a table of information about resources owned by one of the drivers in the driver table. There are three elements in each entry of the table: The type of owned resource, the sub-ID of the owned resource, and the ID of the resource's owner. In these two tables, you enter information for each driver that the INIT must load. The type busD is a table of user configuration data. The contents of this resource depends on the configuration data needed by the drivers. I have included some example configuration data used by the bus drivers.

The error messages are in a standard resource of strings, type STR#. This allows the INIT to be localized for other countries, and it allows me to use the standard system call GetIndString to read a string from the resource.

Compiling the Source Code and Resource Files

With all the source code files and resource files completed we can compile them to create the various pieces of the INIT. First, we create the INIT resource, which is the INIT's executable code run by the system's INIT runner, INIT 31, at boot and restart times. Next, we append the resources created by MPW Rez.

Rez is used because it identifies many system resource formats. You can define your own resource types, and you can decompile resources with Derez that you create or modify in ResEdit.

The Think C project should contain all source files and libraries needed. (Read Think C's warnings about using library calls that need global storage. If you are using library calls that need globals, then recompile the library as described in the Think C manual.) From the Project menu, choose the menu item Set Project Type ... Click the Code Resource radio button onto the File Type to ????, and the Creator to ????. (See Figure 3.) The file type and creator will be changed to their correct values after the INIT has been completely built, because, by itself, this is not a working INIT file, so we don't want the system to identify it as such. We don't want a custom header; we will use Think C's standard header for code resources. Enter a name such as DDJ Init (this is optional because INITs are not referred to by name). Set the resource type to INIT, ID to 0, and resource attributes to Locked. Now choose options from the Edit menu and select any options you want. I suggest MacsBug Symbols for Code Generation and Check Pointer Types, and Require Prototypes for Compiler flags.

Now create the INIT resource by choosing Build Code Resource ... from the Project menu. Enter the file name of the INIT, such as *INIT.rsrc (I put an asterisk as the first character of the name of any part of the INIT file for easy identification). Move to the Build INIT folder and save. This save command overwrites any existing file of that name.

Code resources, such as the INIT resource, can't be segmented with Think C 3.0, but with MPW C 3.0 you can make segments larger than 32K bytes. Unlike code resources, Think C will let you create segmented drivers. If you create a multisegment driver, then there will be one DRVR resource and, for each segment, a DCOD resource. Each of these resources will have global data in an owned DATA resource. If any owned DATA resource has zero size, you can delete it. You must enter the information on all of these resources in the driver table used by the INIT. Add them to the source code file compiled by MPW Rez.

The MPW Rez command compiles resources specified with the Rez language, which is similar to the C language. The resource file, DDJInit.r, specifies various Finder and INIT resources, not including the INIT and DRVR resources. Four commands are used to build the final INIT file. The first two commands set shell variables to the name of the build directory, where all the compiled driver and INIT files are, and to the name of the INIT file to be created. The Rez command specifies the output file name, the search directory for Include statements, and the source file. On this line, you can define an identifier to control conditional compilation. This is useful if you want different resources for different computers. The last command sets the bundle bit, the file type, and file creator. These are the commands used to compile this file; set the file attributes, type, and creator; and copy the resources to the INIT:

   Set BuildDir '::Build INIT:'
   Set InitName 'DDJ INIT'
   Rez -o "{BuildDir}{InitName}"
           -s "{BuildDir}" DDJInit.r
   Setfile "{BuildDir}{InitName}"
           -a B -t INIT -c DDJI

Near the top of the file are three Include statements that read the INIT and DRVR resources into the INIT file. In this example, I put all my device drivers in one file, *DDJ Driver, with ResEdit. The Include statements find the driver file with the help of the search directory specified by the -s option to the Rez command. The drivers used in this example must be locked in the system heap. The first Include statement copies into the INIT file any driver whose ID is in the range 0 - 64, changing the resource attributes of each driver from Purgeable to System Heap and Locked, leaving the ID and name as they were. There is no easy way to set these attributes with Think C. The next statement copies all of the global data resources to the INIT file. Think C sets the resource attributes to Locked. The last Include statement copies the INIT resource to the INIT file. The attributes remain the same because you set the resource attributes of the INIT resource to Locked in the Think C project. Here are the Include statements:

   include "*DDJ Driver"'DRVR'(0:64)
      as'DRVR'($$ID, $$Name,  SysHeap, Locked);
   include "*DDJ Driver"'DATA'
      ( - 15424:- 15200); as 'DATA'
      ($$ID, I'll, SysHeap, Purgeable);
   include "'INIT.rsrc";

Testing the INIT Program

As with any application, you should test the INIT code to make sure it performs correctly. If an INIT bombs, then the user must abort the INIT before it executes, or boot on another volume. Most INITs don't have a way for the user to make the INIT abort execution.

Testing the INIT takes some planning because the actions performed by the INIT depend on the user configuration data and the hardware configuration of the computer when the INIT is executed by the system. The configuration is determined by the expansion boards installed, the drivers required by the user configuration data, the presence of drivers loaded by any previously run INIT file, and the presence of drivers in the System file.

This INIT makes ID changes permanent, so when you are building the INIT, build it in any folder other than the System Folder. The reason is that if an ID collision occurs, both the driver and the INIT's driver and owner tables get updated with the new driver ID. By keeping a master version of the current INIT and copying it to the System Folder, you can be sure that the driver IDs agree with the IDs in the INIT resources. Otherwise, if you recompile a driver whose ID previously collided and add it to the INIT in the System Folder, the resource tables will have a different ID than the driver.

The first test is to open all the drivers that the INIT can load. If your application fails to open a driver, compile your drivers with MacsBug symbols. After restarting the computer, break to the debugger and perform a symbol dump of the system heap. If your driver's symbols don't appear, then any one of the following may be true:

If your driver's symbols do appear, then it may be that the driver was never opened, either by the INIT or by another driver, before the driver resource was detached by the INIT.

To test for duplicate drivers, simply copy the INIT file to the System folder, select and duplicate it, and restart the computer. You will see the first INIT complete normally, but the second driver will put up an alert to flag the duplicate driver condition. You should perform this test for each separate driver configuration.

The INIT should never report an "out of system heap memory error" because the size resource in the INIT file requests enough additional memory from the system to satisfy the highest memory requirement of the set of drivers. However, an error will occur if the system does not have enough memory to satisfy the request, or if you set the value of the size resource too low.

To test a memory error if your drivers use more than about 16K bytes of memory, then set the size resource to zero, make sure enough drivers will be loaded, and restart the computer. Because the system normally has no more than 16K bytes of free space in the system heap, the INIT should report the error to cause a memory error under normal condition.

If your drivers are too small, then you may decide that testing for an out-of-memory condition is not required. You can still perform the test by making your drivers larger than 16K bytes. To do this, open a text file that is larger than 16K bytes, such as the initOpenDriver.c source file; select all of it; and copy (to the Clipboard). With ResEdit, select one of the drivers that will be marked for loading, and choose Open General from the File menu. Scroll to the bottom, put the cursor after the last byte on the right (ASCII) side of the window, and paste (from the Clipboard). Close the window and choose Get Info. You will see that your driver now occupies more than 16K bytes. Now quit ResEdit, saving the changes, and restart the computer. This time you should get the "out of system heap memory" error.

If testing the INIT requires that the INIT be modified, restore it to its original condition before starting a new phase of testing. Just copy the original INIT to the system folder. Make changes only to this copy of the INIT when using a resource editor.

A major portion of the INIT code deals with driver resource ID collisions. Testing this feature will tell whether the ID of our conflicting driver was successfully changed to an unused ID, and whether the other driver was not altered or opened.

The INIT has some conditionally compiled code that you can enable by defining the identifier DEBUG2. This code will put up an alert when a driver conflict occurs and will show the new ID number.

To cause a collision with a driver in the System file, copy any driver from the INIT that will be loaded, and paste it in the System file; and use ResEdit to change the name of this copy of the driver to an unused name. Now this driver will conflict with the driver in the INIT, so restart the computer, step through the alerts, and make a note of the new ID that the driver will be given. With ResEdit, check that the driver you put in the System file has not been altered. Check its name, size, and attributes. (MPW's ResEqual compares all the resources between two files, so it would not be useful in this case). Now check the driver in the INIT file: Its ID should be the new ID, and the owner ID of all its owned resources, listed in the owned table, should also be the new ID.

To cause a collision with a driver loaded by a previous INIT, compile the INIT code with the identifier DEBUG3, which will disable checking for duplicate drivers. Copy the resulting INIT file to the System Folder and then duplicate it in the System Folder so that two copies of the INIT will execute. Rename the copy so that it runs before the other (the system runs INITs in alphabetical order). Change the name of each driver so that your driver test program will have to open the set of drivers that were given new IDs.

Restart the computer and make a note of all the new IDs. Check that the INIT that executes second changes the IDs of all its drivers that are marked for loading, because these drivers will exist in the driver unit table from the INIT that runs first. You can use ResEdit to check that the driver and owned resources IDs have been changed correctly. The new IDs must not conflict with any of the drivers in the System file. Now run a test program to exercise your drivers. Check the drivers in the INIT that runs first. There should be no changes in the resource IDs unless there was a conflict with some other drivers. The resource attributes should also be at their default values.

If loading a driver depends only on the user configuration data, such as a driver for hardware attached to the serial port, make sure all the needed drivers get loaded when no other hardware variants are in the computer. This test would verify that a driver could be marked for loading by the user configuration data only.

Conclusion

Loading device drivers with an INIT should be the preferred method over installing them into the System file. With this method, one file can accomplish three things for you. It can make installation and upgrading easier; it can contain a Control Panel device that can perform user configuration; and it can expand the system heap to make room for the drivers.

Acknowledgments

The author would like to thank Lynda Gruggett for her help with the debugging and the design of the NB Handler INIT.

References

Apple Computer, Inc. Inside Macintosh, Addison-Wesley, Reading, Mass., 1987.

Apple Computer, Inc. Macintosh Programmer's Workshop Development Environment, Vol. 1, Ver. 3.0, Cupertino, Calif., 1988.

The Institute of Electrical and Electronics Engineers. IEEE Standard Digital Interface for Programmable Instrumentation, ANSI/IEEE Std 488-1978 (New York: The Institute of Electrical and Electronics Engineers, 1983).

Symantec Corp. Think C User's Manual Bedford, Mass, 1988.

_AVOIDING INIT COLLISIONS AT BOOT TIME_ by John Rosford [LISTING ONE]



/* initOpenDRVR.c
# John Rosford, National Instruments.
# Copyright 1988,1989 National Instruments Corporation
# All rights reserved.
# This Macintosh INIT loads device drivers, checking
  for driver ID collisions.*/

#include    <MacTypes.h>
#include    <Quickdraw.h>
#include    <WindowMgr.h>
#include    <EventMgr.h>
#include    <DialogMgr.h>
#include    <OSUtil.h>
#include    <MemoryMgr.h>
#include    <Pascal.h>
#include    <DeviceMgr.h>
#include    <ResourceMgr.h>
#include    <ControlMgr.h>
#include    <ToolboxUtil.h>
#include    <OSUtil.h>
#include    <Strings.h>
#include    <SlotMgr.h>


#include "initOpenDRVR.h"

#define NBICONID    128

#define NUM_DRVRS SizeResource(GetResource( DRVR_TBL_TYPE, DRVR_TBL_ID))/sizeof(struct drvrStruct)
#define NUM_OWNED SizeResource(GetResource( OWNED_TBL_TYPE, OWNED_TBL_ID))/sizeof(struct ownedStruct)

#ifdef LSC
/* setup address register A4 for global data */
#define SetUpA4()   asm{    move.l  a4,-(sp)    \
                        move.l  a0,a4 }
#define RestoreA4() asm{    move.l  (sp)+,a4 }
#endif

main()
{
    struct drvrStruct *pDrvrTbl;        /* ptr to drvrStruct */
    struct ownedStruct *pOwnedTbl;      /* ptr to ownedStruct */
    register short err;         /* error code */
    int used_id[NUM_IDS];       /* true if device driver id is in use */
    char nilstr[1];
    char strBuf[256];
    SysEnvRec   theWorld;
    config_t config;
    GrafPort myPort;

#ifdef DEBUG4
    DebugStr("\pBreak at MAIN+0");
#endif
#ifdef GLOBALS
    SetUpA4();
#endif
    InitGraf( &thePort);        /* initialize quickdraw global variables */
    OpenPort(&myPort);          /* make myPort the current grafPort */
    /* Other toolbox managers are initialized when needed for dialogs. */

    nilstr[0]='\0';             /* make a nil string */
#ifdef DEBUG4
    alertNote("Number of owned resources: %d", (int)NUM_OWNED);
#endif
    err = noErr;                /* start with no error */
    showIcon();                 /* display the icon */
    if( kill_key())             /* True if User abort key combination */
        /* question: abort the init? */
        if( err = qAbort( text_PStr(S_Q_USER_ABORT, strBuf), nilstr, nilstr))
            alert( text(S_USER_ABORT, strBuf));     /* yes, abort */
    if( !err)
        SysEnvirons( 1, &theWorld);     /* what environment is the init running in? */
    if( !err){
        if( err = (theWorld.machineType<envMacII && theWorld.machineType!=envMachUnknown))      /*envMacII*/
      alert( text(S_NEED_MAC_II, strBuf));    /* my drivers need a Mac II */
    }
    if( !err)
      getTables( &pDrvrTbl, &pOwnedTbl);      /* Get the driver tables. */
    if( !err)
    err = configInfo(&config);        /* Copy in user configuration from resources */
    if( !err)
    err = markLoad( pDrvrTbl,&config); /* mark drivers that need loading. */
#ifndef DEBUG3
   if( !err)
       err = dupDriverInstalled( pDrvrTbl);/* check for duplicate drivers */
#endif
   if( !err)
       err = checkSysHeapSize( pDrvrTbl, pOwnedTbl); /* is there enough memory?*/
    if( !err)
        err = findUsedIDs( used_id);   /* find all IDs used in the system */
    if( !err)
        err = checkIDs( pDrvrTbl, pOwnedTbl, used_id);  /* check the IDs of the INIT's drivers */
    if( !err){
        if( err = openMarkedDrivers( pDrvrTbl,&config)) /* open drivers that need loading */
            alert( text(S_FAIL_OPEN_DRVRS, strBuf), err);
    }
#ifdef DEBUG1
    {
    short i;
    for( i=0; !err && i<NUM_DRVRS; ++i)
        if( pDrvrTbl[i].load)
            alertNote("After openMarkedDrivers, Will load %s", pDrvrTbl[i].name);
    }
#endif
    if( !err)
        err = detachDriver( pDrvrTbl, pOwnedTbl);     /* detach the driver resources */
    ClosePort(&myPort);                             /* close my grafPort */
#ifdef GLOBALS
    RestoreA4();
#endif
}

char *
text(index, strBuf)             /* return a C string in strBuf */
    int     index;
    char *strBuf;
{
    return( PtoCstr( text_PStr(index, strBuf)));
}

char *
text_PStr(index, strBuf)        /* return a Pascal string in strBuf */
    int     index;
    char *strBuf;
{
    GetIndString(strBuf, SS_MSGS_ID, index);    /* get string from STR# resource */
    return(strBuf);
}


/**************************************************************************/
/* Add code to show your icon */
showIcon()
{}
/**************************************************************************/

getTables( hDrvrTbl, hOwnedTbl)         /* Get the driver tables. */
struct drvrStruct **hDrvrTbl;   /* handle to the driver table. */
struct ownedStruct **hOwnedTbl; /* handle to the owned resource table. */
{
    Handle hData;
    int err;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("getTables()");
#endif
  hData = GetResource( DRVR_TBL_TYPE, DRVR_TBL_ID);   /* get driver table */
    if( err = ResError()){
        alert(text(S_FAIL_GET_DRVR_TBL, strBuf));
        return( err);
    };
  HLock( hData);          /* table needs to be locked while the INIT runs */
  *hDrvrTbl = (struct drvrStruct*) *hData;     /* send pointer back to caller */

    hData = GetResource( OWNED_TBL_TYPE, OWNED_TBL_ID); /* get owned resource table */
    if( err = ResError()){
        alert(text(S_FAIL_GET_OWNED_TBL, strBuf));
        return( err);
    };
   HLock( hData);         /* table needs to be locked while the INIT runs */
    *hOwnedTbl = (struct ownedStruct*) *hData;      /* send pointer back to caller */
    return err;
}

/* Copy in user configuration from resources */
configInfo(config)
register config_t *config;      /* pointer to the configuration data */
{
    register short err;
    Handle  DefBoardsH;             /* default board data resource */
    register BusResource    *BP;    /* pointer to bus data */
    char    TheString[256];         /* storage for message string */

#ifdef DEBUG_TRACE
    alertNote("configInfo()");
#endif
    slotInfoTable(config);              /* fill in brdName and brdID for each NB slot */
    /* Load default configuration */
    DefBoardsH  = GetResource(BUS_DATA_TYPE,BUS_DATA_ID);   /* get the bus data resource */
    if (DefBoardsH == NULL) {
        alert(text(S_FAIL_GET_DATA, TheString));
        return -1;
    }
    HLock(DefBoardsH);              /* lock data resource until we read it into local variables */
    BP = (BusResource*) *DefBoardsH;
    err = configBrds( BP,config);   /* read bus configuration data */
    if( err)
        return(err);                /* failed */
    HUnlock(DefBoardsH);            /* unlock data resource */
    ReleaseResource(DefBoardsH);    /* release the resource */

    return noErr;
}

configBrds( BP,config)
register BusResource    *BP;
register config_t *config;
{
    register short bus,MaxBuses;
    char strBuf[256];

#ifdef DEBUG_TRACE
    alertNote("configBrds()");
#endif
    MaxBuses = config->MaxBuses = BP->Cnt;      /* get number of buses we have data for */
    if( BP->Rev != BOARDREV){
        alert(text(S_FAIL_REVISION, strBuf));
        return E_BRDREV;
    }
    for (bus=0; bus<MaxBuses; bus++) {          /* for each bus, copy the data */
        config->brdRsrc.BusData[bus].b_uflags = BP->BusData[bus].b_uflags;
        config->brdRsrc.BusData[bus].b_slot = BP->BusData[bus].b_slot;
      /* if the bus is associated with a serial slot, copy the extra data */
        if( isSerSlot(config->brdRsrc.BusData[bus].b_slot)){
            config->brdRsrc.BusData[bus].b_baud = BP->BusData[bus].b_baud;
        }

    }

#ifdef DEBUG1
    for (bus=0; bus<MaxBuses; bus++) {
        if(config->brdRsrc.BusData[bus].b_slot){
            /* alert: 4 int args max */
            alertNote("configBrds: bus %d with slot %d", bus, config->brdRsrc.BusData[bus].b_slot);
            alertNote("configBrds: uflags %x",
            BP->BusData[bus].b_uflags);
        }
    }
#endif
    return(noErr);
}

/* Mark the drivers and owned resources that will need to be loaded.
 * Look at the boards to determine which to load.
 * All drivers will be mark either YES or NO.
 * Always returns noErr.
 */

markLoad( drvrTbl,config)
struct drvrStruct *drvrTbl;
register config_t *config;
{
    register short i, k, err, bus;      /* loop counters, error, and bus number */
    char strBuf[128];                   /* storage for message strings */

#ifdef DEBUG_TRACE
    alertNote("markLoad()");
#endif
    err = noErr;
    for( i=0; i<NUM_DRVRS; ++i){    /* Check boards before anything else. */
        short brdID;                /* board ID variable */
        brdID = drvrTbl[i].type;    /* driver type can be a board ID */
        switch( brdID){             /* which board ID? */
            case NB_GPIB:
            case NB_DMA_8:
                drvrTbl[i].load = getSlot( brdID,config);   /* mark if board is in any slot */
                break;
            case T_SERIAL_HW:
            /* Load if any bus is configured as a serial slot: GPIB-422CT, GPIB-MAC */
                for( k=0; k<config->MaxBuses; ++k){
                    if( isSerSlot(config->brdRsrc.BusData[k].b_slot)){
                        drvrTbl[i].load = true;
#ifdef DEBUG1
    alertNote("Load serial slot %d, bus %d",config->brdRsrc.BusData[k].b_slot,k);
#endif
                        break;
                    }
                }
                break;
            default:
                /* Take no action here if type is not a brdID.  Types will be checked below. */
                break;
        }
    }

    /* Loading the master driver depends only on the boards but not the serial driver. */
    i = ndxDrvrType( drvrTbl, T_DRVR_MASTER);   /* get index of the master driver */
    drvrTbl[i].load = L_NO;
    for( k=0; k<NUM_DRVRS; ++k)
        /* mark the master driver if any other driver except the serial or NB-GPIB driver is marked */
        if( drvrTbl[k].load && drvrTbl[k].type!=T_SERIAL_HW && drvrTbl[k].load != NB_GPIB){
            drvrTbl[i].load = L_YES;
            break;
        }

    bus = 0;
    for( i=0; i<NUM_DRVRS; ++i){
        switch( drvrTbl[i].type){       /* scan through the other driver types */
            case T_SERIAL_HW:           /* ignore serial, board, and master types */
            case NB_GPIB:
            case NB_DMA_8:
            case T_DRVR_MASTER:
                break;
            case T_DRVR_GPIB:           /* load GPIB type if any GPIB board or serial driver is marked */
                drvrTbl[i].load = ((drvrTbl[ndxDrvrType(drvrTbl,NB_DMA_8)].load
                    || drvrTbl[ndxDrvrType(drvrTbl,NB_GPIB)].load
                    || drvrTbl[ndxDrvrType(drvrTbl,T_SERIAL_HW)].load) ? L_YES : L_NO);
                break;
            case T_BUS_DRVR:            /* Load GPIB bus driver if user or auto configured. */
                drvrTbl[i].load = config->brdRsrc.BusData[bus].b_slot;
                bus++;
                break;
            case T_BOARD_HW:            /* Load if any NI-488 resource: NB-GPIB, NB-DMA */
            /* if any GPIB board is present, then bus 0, the first bus, will have a slot. */
                drvrTbl[i].load = autoSlot(0,config);
                break;
            default:
                /* error if there is an unknown type in the table */
                alertNote(text(S_NOT_DRVR_TYPE, strBuf), drvrTbl[i].type, i);
                err = 1;
                break;
        }
    }

#ifdef DEBUG1
    alertNote("Show drivers marked for loading");
    for( i=0; i<NUM_DRVRS; ++i)
        if( drvrTbl[i].load)        /* all are YES or NO at this point */
            alertNote("Marked %s", drvrTbl[i].name);

#endif
    return err;
}

/* Input: driver type.  Output: its drvrTbl index. */
ndxDrvrType( drvrTbl, type)
struct drvrStruct *drvrTbl;
short type;
{
    register unsigned short i;

    for( i=0; i<NUM_DRVRS; ++i)
        if( type == drvrTbl[i].type )
            return i;
    return 0;
}

/* Input: driver id.  Output: its drvrTbl index. */
drvrID2index( drvrTbl, id)
struct drvrStruct *drvrTbl;
register short id;
{
    register short i;
    char strBuf[128];

    for( i=0; i<NUM_DRVRS; ++i)
        if( drvrTbl[i].id == id)
            return i;
    alert(text(S_FAIL_NO_DRVR, strBuf), id);
    return 0;
}

/* Slot number range 1-n while bus numbers range 0-m */
/* return slot if board whose id=brdID is found, else 0 */
getSlot( brdID, config)
register unsigned short brdID;
register config_t *config;
{
    register unsigned short i;

    for (i=1; i<=NSLOTS; i++){
        if( brdID == config->slotInfo[i-1].brdID )
            return i;
    }
#ifdef DEBUG4
    alertNote( "No slot for brdID: 0x%x", brdID);
#endif
    return 0;
}


/* return the slot of the (bus)th GPIB board in the machine. */
autoSlot( bus,config)
int bus;
register config_t *config;
{
    register unsigned short i, brdID;

    if( 0<=bus && bus<NBUSES){
        for (i=1; i<=NSLOTS; i++){
            brdID = config->slotInfo[i-1].brdID;
            if(brdID==NB_GPIB || brdID==NB_DMA_8)
                if( !bus--)         /* example: bus 1 will return slot of first match */
                    return i;
        }
    }
#ifdef DEBUG4
    else
        alertNote("Bad parameter: autoSlot(%d)", bus);
#endif
    return 0;
}

/* Get NuBus slot info for each slot */
slotInfoTable(config)
register config_t *config;
{
    register int i;

    for (i=1; i<=NSLOTS; i++) {
        config->slotInfo[i-1].brdID = GetSlotInfo(i, config->slotInfo[i-1].brdName);
    }
#ifdef DEMO
    config->slotInfo[0].brdID = NB_DMA_8;
#endif

    /* serial slots are a different hardware variant */
    config->slotInfo[SSLOTA_NDX].brdID = GPIBMAC;
    config->slotInfo[SSLOTB_NDX].brdID = GPIBMAC;
}


/*
 * Return boardID  and driver name of board in slot 'slot'.
 * returns one of the board IDs listed in brdIDs.h
 */

/* spIDs */
#define sRsrc_Name 2
#define BoardID 32
#define VendorInfo 36
#define VendorID 1

GetSlotInfo(slot, dName)
int slot;
char *dName;
{
    SpBlock sBlock;
    SpBlockPtr sp;
    int brdID;
    OSErr err;
    Ptr structZero;
    struct SPRAMRecord {
        short boardID;
        char VendorUse[6];
    }SPRAMRec;

    dName[0] = '\0';
    sp = &sBlock;
#ifdef DEBUG
    printf("\n###### slot #%d #######\n", slot);
#endif
    sp->spSlot = macSlotNum(slot);          /* convert slot to NuBus slot number */
    sp->spResult = (long)&SPRAMRec;
    err = SReadPRAMRec(sp);
#ifdef DEBUG
    printf("SReadPRAMRec err: %d, boardID = 0x%x\n", err, SPRAMRec.boardID);
#endif

    if( err == smSlotOOBErr)                /* slot does not exist, number is out of bounds. */
        brdID = SLOT_OUT_OF_BOUNDS;
    else if( err)                           /* unknown error, no board. */
        brdID = NO_BOARD;
    else
        brdID = SPRAMRec.boardID;

    if( err == noErr){

        sp->spSlot = macSlotNum(slot);     /* convert slot to Mac slot number */
        sp->spID = 1;
        sp->spExtDev = 0;
        err = SRsrcInfo(sp);

        if( err == smNoMoresRsrcs)          /* either no board or slot does not exist */
            brdID = NO_BOARD;

        if( err == noErr)
         structZero = sp->spsPointer;    /* can be changed by other calls */
    }

    if ( err == noErr) {
        /* Get board name */
        sp->spsPointer = structZero;        /* restore spsPointer from SNextsRsrc() */
        sp->spID = sRsrc_Name;
        if ((err=SGetcString(sp)) == noErr) {
            strcat(dName, (Ptr)sp->spResult);
        }else
            brdID = NOT_OUR_BOARD;
    }

    if ( err == noErr) {
#ifdef DEBUG
        printf("spSlot %d, spID %d, spExtDev %d\n", sp->spSlot,sp->spID,sp->spExtDev);
#endif
        /* Track down vendor name */
        /* spsPointer from SNextsRsrc() */
        sp->spID = VendorInfo;
        if ((err=SFindStruct(sp)) == noErr) {
            /* spsPointer from SFindStruct() */
            sp->spID = VendorID;    /* vendor name */
            if ((err=SGetcString(sp)) == noErr){
                long ourName[6];
                ourName[0]='Nati';
                ourName[1]='onal';
                ourName[2]=' Ins';
                ourName[3]='trum';
                ourName[4]='ents';
                ourName[5]=0L;
                if (err=strcmp((Ptr)sp->spResult, (Ptr)ourName))
                    brdID = NOT_OUR_BOARD;
            }else
                brdID = NOT_OUR_BOARD;
        }else
            brdID = NOT_OUR_BOARD;
    }

    return brdID;
}

/* initOpenDRVRp2.c - DDJ part 2 */

/* Check for any existing NI Drivers. */
dupDriverInstalled(drvrTbl)
struct drvrStruct *drvrTbl;     /* pointer to the driver table */
{
    short myRefNum, refNum;     /* INIT's and driver's reference numbers */
    register unsigned short i;  /* loop counter */
    register short openError, numDrivers, err;  /* openDriver error, number of drivers, other error */
    char strBuf[64];            /* storage for message string */

    err = noErr;
    /* if no drivers are marked for loading
    # then the flag should be anything besides noErr.
    */
    openError = !noErr;
    myRefNum = CurResFile();
    numDrivers = NUM_DRVRS;     /* macro would return 0 if used after UseResFile(0) */
    UseResFile(0);              /* Use the System File only */
    for( i=0; i<numDrivers && openError != noErr; ++i) {
        if( drvrTbl[i].load){   /* see if this driver already exists */
            openError = OpenDriver( CtoPstr( strcpy( strBuf, drvrTbl[i].name)), &refNum);
        }
    }
    UseResFile(myRefNum);       /* go back to the INIT resource file so we can display an alert */
    if( openError == noErr){
        alert( text(S_FAIL_REMOVE, drvrTbl[i].name));
        err = 1;        /* In this case, no error is a failure. */
    }
#ifdef DEBUG4
    else
        alertNote("No duplicate master driver found, %d.", openError);
#endif
    return err;
}

/* see if there is enough memory to load the drivers */
checkSysHeapSize( drvrTbl, ownedTbl)
struct drvrStruct *drvrTbl;     /* array of drvrStruct */
struct ownedStruct *ownedTbl;       /* array of ownedStruct */
{
    register short i;           /* loop counter */
    register OSErr err;         /* OS error */
    register Handle hdl;        /* resource handle */
    char strBuf[128];           /* storage for message string */

#ifdef DEBUG_TRACE
    alertNote("checkSysHeapSize()");
#endif
    /* Find out how much memory is needed to load the drivers */
    err = noErr;

    /* while not err, size DRVRs that need to be loaded */
    for( i=0; i<NUM_DRVRS && !err; ++i){
        if( !drvrTbl[i].load)
            continue;
        hdl = GetResource(DRVR_TYPE, drvrTbl[i].id);        /* read resource into memory */
        err = ResError();
        if( !hdl || err){
            alert(text(S_FAIL_GET_DRVR, strBuf), drvrTbl[i].name);
            if( !err)
                err = true;
        }
    }

    /* while not err, size owned resources that need to be loaded. */
    for( i=0; i<NUM_OWNED && !err; ++i){
        register short id;
        if( !drvrTbl[drvrID2index( drvrTbl, ownedTbl[i].owner)].load)   /* fixed bug: 5/13/88 */
            continue;
        id = OWNED_ID(ownedTbl[i].owner,ownedTbl[i].id);        /* calc owned id */
        hdl = GetResource(ownedTbl[i].type, id);            /* read resource into memory */
        err = ResError();
        if( !hdl || err){
            alert(text(S_FAIL_GET_OWNED, strBuf), drvrTbl[drvrID2index( drvrTbl, ownedTbl[i].owner)].name);
            alertNote(text(S_FAIL_ID_OSERR, strBuf), ownedTbl[i].id, err);
            if( !err)
                err = true;
        }
    }

    if( err == -108)        /* if out of system heap */
        alert(text(S_FAIL_HEAP, strBuf));
    else if( err)
        alert(text(S_FAIL_RSRC_MGR, strBuf), err);
    return err;
}

findUsedIDs( used_id)
int used_id[];      /* true if device driver id is in use */
{
    register short UNtryCnt, i, err;

#ifdef DEBUG_TRACE
    alertNote("findUsedIDs()");
#endif
    UNtryCnt = UnitNtryCnt;     /* get number of entries in Unit Table */
#ifdef DEBUG2
    alertNote("Number of Unit Entries: %d.", UNtryCnt);
#endif
    for( i=START_OF_USER_IDS; i<UNtryCnt; ++i)
        used_id[i] = 0;                 /* clear used id's */
    /* check both the Unit Table and the system file */
    err = checkUnitTable( used_id);     /* Any test after this should OR in the results. */
    if( !err)
        err = checkSysRsrcs( used_id);
    return err;
}

/* check if a driver of the same id already exists in the driver Unit Table.*/
checkUnitTable( used_id)
int used_id[];      /* true if device driver id is in use */
{
    register DCtlHandle *UTable;
    register short UNtryCnt, i;

#ifdef DEBUG_TRACE
    alertNote("checkUnitTable()");
#endif
    UTable = UTableBase;            /* get base address of Unit Table */
    UNtryCnt = UnitNtryCnt;         /* get number of entries in Unit Table */
    /* Mark if this DCtl handle is not NULL */
    for( i=START_OF_USER_IDS; i<UNtryCnt; ++i){
        used_id[i] = UTable[i] != NULL;
#ifdef DEBUG2
        if( used_id[i])
            alertNote("UnitTable ID %d found.", i);
#endif
    }
    return noErr;
}

/* check if a resource of the same type & id already exists in the system file.*/
checkSysRsrcs( used_id)
int used_id[];      /* true if device driver id is in use */
{
    register short i, count, UNtryCnt;      /* loop count, number of resources, size of Unit Table */
    short id, err;                          /* resource id, error */
    ResType type;                           /* res type */
    char name[32];                          /* res name */
    register Handle hdl;                    /* res handle */
    char strBuf[128];                       /* string buffer */

#ifdef DEBUG_TRACE
    alertNote("checkSysRsrcs()");
#endif
   UNtryCnt = UnitNtryCnt;         /* get number of entries in Unit Table */
    /* UseResFile() has no effect on GetIndResource() */
    SetResLoad(FALSE);
    err = ResError();
    if( !err){
        count = CountResources('DRVR');
        err = ResError();
    }

    for (i = 1; i <= count && !err; i++) {
        hdl = GetIndResource('DRVR', i);
        err = ResError();

        if( hdl && !err){
            GetResInfo( hdl, &id, &type, name);
            err = ResError();
            /* Mark (with prev result) if this resource is from the system file. */
            if( id<UNtryCnt && id>=0 && !err){
                used_id[id] |= HomeResFile( hdl) == 0;
                err = ResError();
#ifdef DEBUG2
                if (used_id[id])
                    alertNote("Marked Driver ID %d used.", id);
#endif
            }
#ifdef DEBUG2
            else
                alertNote("Driver ID %d out of range.", id);
#endif
        }else
            alert(text(S_FAIL_RSRC_MGR, strBuf), err);
    }
    if( !err){
        SetResLoad(TRUE);
        err = ResError();
    }
    return err;
}

checkIDs( drvrTbl, ownedTbl, used_id)
struct drvrStruct *drvrTbl;     /* array of drvrStruct */
struct ownedStruct *ownedTbl;       /* array of ownedStruct */
int *used_id;
{
    register short i, err, k;
    register struct drvrStruct *drvr;
    Handle hdl;
    char strBuf[128];
    int init_id[NUM_IDS];       /* true if init device driver id */
    short UNtryCnt;

/* Check all drivers.  If marked for loading, it must have a unique and unused ID within the system.
 * Else, it must have a unique ID within the INIT.
 * check if a DRVR of the same id already exists.
 * Change the DRVR's ID and its owned rsrc's ID's.
 * Change table resources.
 */

#ifdef DEBUG_TRACE
    alertNote("Checking driver ID's.");
#endif
    err = noErr;
    /* table of driver ids used by INIT */
    UNtryCnt = UnitNtryCnt;
    for( i=START_OF_USER_IDS; i<UNtryCnt; ++i)
        init_id[i] = 0;             /* clear init id's */
    for( i=0; i<NUM_DRVRS; ++i)
        init_id[drvrTbl[i].id] = true;

    for( i=0; i<NUM_DRVRS && !err; ++i){
        register short newID;
        if( !drvrTbl[i].load)
            continue;
        drvr = &drvrTbl[i];
        if( used_id[drvr->id]) {
#ifdef DEBUG2
            alertNote("Driver %d conflict.", drvrTbl[i].id);
#endif
            /* find a resource ID that doesn't conflict with system or init driver IDs. */
            newID = getUnusedID( used_id,init_id);

#ifdef DEBUG2
            alertNote("NewID %d.", newID);
#endif
            if( newID){
                hdl = Get1Resource( DRVR_TYPE, drvr->id);
                if ((hdl == NULL) || (err = ResError())) {
                    alert(text(S_FAIL_ID_CHANGE, strBuf), drvr->id, err);
                }else{
                    used_id[newID] = TRUE;
                    err = changeRsrcID( hdl, newID);        /* change the resource's ID */
                }
            }
            if( !err)
                err = newOwner( ownedTbl, drvr->id, newID);         /* update owner of rsrcs owned by drvr */
            if( !err){
                drvr->id = newID;                       /* change data in the tabel rsrc */
                err = changeTableResources( DRVRTBL);
            }
        }
    }
    return err;
}

/* Return an unused DRVR_TYPE id (0 if none) from the previously built used_id table. */
getUnusedID( used_id,init_id)
int *used_id,*init_id;      /* true if device driver id is in use, true if init id is in use. */
{
    register short id, UNtryCnt;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("getUnusedID()");
#endif
    /* Find a resource ID that doesn't conflict. */

    UNtryCnt = UnitNtryCnt;     /* Number of unit entries. */
    for (id=START_OF_USER_IDS;id<UNtryCnt && (used_id[id] || init_id[id]);id++)     /* 5/31/88 used_id[id] JR/LG */
        ;
    if (id == UNtryCnt) {
        alert(text(S_FAIL_NO_FREE_ID, strBuf));
        id = 0;     /* failed */
    }
    return id;
}


/* update owner ID of rsrcs owned by drvr */
newOwner( ownedTbl, drvrOldID, drvrNewID)
struct ownedStruct *ownedTbl;       /* array of ownedStruct */
short drvrOldID, drvrNewID;
{
    register short i, err, old_id;
    register struct ownedStruct *owned;
    register Handle hdl;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("newOwner()");
#endif
    err = noErr;
    for( i=0; i<NUM_OWNED && !err; ++i){
        owned = &ownedTbl[i];
        if( owned->owner == drvrOldID){
            old_id = OWNED_ID(drvrOldID,owned->id);     /* calc owned id */
#ifdef DEBUG2
            alertNote("Get1Resource( id = %d).", old_id);
#endif
            hdl = Get1Resource(owned->type, old_id);
            if (err = ResError()) {
                alert(text(S_FAIL_ID_CHANGE, strBuf), old_id, err);
                return(err);
            }
            err = changeRsrcID( hdl, OWNED_ID(drvrNewID,owned->id));            /* Change the resource's owner's ID */
            if( !err){
                owned->owner = drvrNewID;           /* Change table rsrc of the owner. */
                err = changeTableResources( OWNERTBL);
            }
        }
    }
    return err;
}


/* If resource is owned, then pass in the real ID, not the sub ID. */
changeRsrcID( rsrcHdl, drvrNewID)
Handle rsrcHdl;
short drvrNewID;
{
    short attr;
    register OSErr err;
    ResType type;
    char name[30];
    short drvrOldID;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("changeRsrcID()");
#endif
    GetResInfo( rsrcHdl, &drvrOldID, &type, name);  /* get name */
    if (err = ResError()) {
        alert(text(S_FAIL_GET_INFO, strBuf), err);
        return(err);
    }
#ifdef DEBUG2
    alertNote("SetResInfo NewID %d", drvrNewID);
#endif
    SetResInfo( rsrcHdl, drvrNewID, name);  /* set new ID and name */
    if (err = ResError()) {
        alert(text(S_FAIL_SET_INFO, strBuf), err);
        return(err);
    }
    if( !err){
        ChangedResource( rsrcHdl);  /* Mark the resource as changed. */
        err = ResError();
    }
    return(err);
}

changeTableResources( change)           /* Change the driver table resources. */
int change; /* which driver table has changed. */
{
    Handle hData;
    int err;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("changeTableResources()");
#endif
    if( change & DRVRTBL){
        hData = GetResource( DRVR_TBL_TYPE, DRVR_TBL_ID);
        if( err = ResError()){
            alert(text(S_FAIL_GET_DRVR_TBL, strBuf));
            return( err);
        };
        ChangedResource( hData);
        if( err = ResError()){
            alert(text(S_FAIL_RSRC_MGR, strBuf), err);
            return( err);
        };
    }

    if( change & OWNERTBL){
        hData = GetResource( OWNED_TBL_TYPE, OWNED_TBL_ID);
        if( err = ResError()){
            alert(text(S_FAIL_GET_OWNED_TBL, strBuf));
            return( err);
        };
        ChangedResource( hData);
        if( err = ResError()){
            alert(text(S_FAIL_RSRC_MGR, strBuf), err);
            return( err);
        };
    }
    return err;
}

/* openMarkedDrivers.c
    ".NB-DMA" and ".NB-GPI" and other subdrivers are opened as needed by ".LabDRIVER"
 */


/**********************************************************************/
/*
 *  Opens certain GPIB drivers if they are marked for loading.
 *  If no NI boards are in the system, then don't load any drivers.
 *  Load will be either L_NO or NOT L_NO.
 *  Only driver table types T_DRVR_MASTER and T_BUS_DRVR will be opened.  Other types are
 *  opened by higher level drivers.
 */

openMarkedDrivers( drvrTbl,config)
struct drvrStruct *drvrTbl;     /* array of drvrStruct */
register config_t *config;
{
    register short  i;
    register short  bus;    /* 1 to n buses */
    short   masterRefNum;
    register OSErr err;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("openMarkedDrivers()");
#endif
    err = noErr;
    masterRefNum = 0;
    for( i=0; i<NUM_DRVRS && !err; ++i) {
#ifdef DEBUG1
        if(( drvrTbl[i].load && (drvrTbl[i].type == T_DRVR_MASTER
                || drvrTbl[i].load && drvrTbl[i].type == T_BUS_DRVR)))
            alertNote( "Will open %s", drvrTbl[i].name);
#endif
        if( drvrTbl[i].load && drvrTbl[i].type == T_DRVR_MASTER){
            err = OpenDriver( CtoPstr( drvrTbl[i].name), &masterRefNum);    /* name is now Pascal */
            PtoCstr( drvrTbl[i].name);      /* Must be a C string in the table. */
            if( err){
                masterRefNum = 0;
                alert(text(S_FAIL_OPEN_NAME, strBuf), drvrTbl[i].name);
            }
            break;      /* only one master driver */
        }
    }
    for( i=0,bus=0; i<NUM_DRVRS && !err; ++i) {
        if( drvrTbl[i].type == T_BUS_DRVR){
            if( drvrTbl[i].load)
                err = openBusDriver(drvrTbl[i].name,bus,config);
            bus++;
        }
    }
    return err;
}

/* Detach the driver if the drivers load flag is not false */
detachDriver( drvrTbl, ownedTbl)
struct drvrStruct *drvrTbl;     /* array of drvrStruct */
struct ownedStruct *ownedTbl;       /* array of ownedStruct */
{
    register short i, id;
    register OSErr err;
    Handle hndl;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("detachDriver()");
#endif
    err = noErr;
    /* Update the resource file because ResError will return resAttrErr if you call
     *  DetachResource to detach a resource whose resChanged attribute has been set.
    */
    UpdateResFile( CurResFile());       /* Assumes that the current res file is the init. */
    /* Here go the DRVRs */
    for( i=0; i<NUM_DRVRS && !err; ++i) {
        if( !drvrTbl[i].load)
            continue;
#ifdef DEBUG1
        alertNote("Detach %s driver", drvrTbl[i].name);
#endif
        hndl = GetResource( DRVR_TYPE, drvrTbl[i].id);
        if( err = ResError())
            alert(text(S_FAIL_GET_DRVR, strBuf), drvrTbl[i].name);
        DetachResource(hndl);
        if( err = ResError())
            alert(text(S_FAIL_DETACH_DRVR, strBuf), drvrTbl[i].name);
    }
    /* Here go the owned resources; Required for segmented drivers. */
    for( i=0; i<NUM_OWNED && !err; ++i){
        short drvrIndex;
        if( !drvrTbl[drvrID2index( drvrTbl, ownedTbl[i].owner)].load)
            continue;
        drvrIndex = drvrID2index( drvrTbl, ownedTbl[i].owner);
#ifdef DEBUG2
        alertNote("Detach owned type: %4s", &ownedTbl[i].type);     /* adr of 4 chars */
        alertNote("id: %d, owner: %d", ownedTbl[i].id, ownedTbl[i].owner);
#endif
        id = OWNED_ID(ownedTbl[i].owner,ownedTbl[i].id);        /* calc owned id */
        hndl = GetResource( ownedTbl[i].type, id);
        if( err = ResError())
            alert(text(S_FAIL_GET_OWNED, strBuf), drvrTbl[drvrID2index( drvrTbl, ownedTbl[i].owner)].name);
        DetachResource(hndl);
        if( err = ResError())
            alert(text(S_FAIL_DETACH_OWNED, strBuf), drvrTbl[drvrID2index( drvrTbl, ownedTbl[i].owner)].name);
    }
    return err;
}

openBusDriver(name,bus,config)
char *name;
short bus;      /* 0 to n-1 buses */
register config_t *config;
{
    ioParam iopb;
    IBoard b;
    short err;
    char strBuf[128];

#ifdef DEBUG_TRACE
    alertNote("openBusDriver()");
#endif
    b.b_uflags = config->brdRsrc.BusData[bus].b_uflags;
    b.b_slot = config->brdRsrc.BusData[bus].b_slot;
#ifdef DEBUG1
    alertNote("bus %d, slot %d, uflags 0x%x", bus, b.b_slot, b.b_uflags);   /* 4 args max */
#endif

    if( isSerSlot(b.b_slot)){
        b.b_slot = conf2initSerSlot(b.b_slot);
        b.b_baud = config->brdRsrc.BusData[bus].b_baud;
#ifdef DEBUG1
    alertNote("Serial slot %d, b_baud %d", b.b_slot, b.b_baud); /* 4 args max */
#endif
    }
    iopb.ioCompletion = NULL;
    iopb.ioPermssn = fsCurPerm;
    iopb.ioNamePtr =(StringPtr) CtoPstr(name);
    iopb.ioMisc =(Ptr) &b;
    err = PBOpen(&iopb,FALSE);  /* name is now Pascal */
    PtoCstr( name);     /* Must be a C string in the table. */
    if( err)
        alert(text(S_FAIL_OPEN_NAME, strBuf), name);
    return(err);
}

/* US keyboard only */
kill_key()
{
    KeyMap theKeys;

    GetKeys( &theKeys);
#ifdef TEST
    printf("0x%lx %lx %lx %lx\n", theKeys.Key[3], theKeys.Key[2], theKeys.Key[1], theKeys.Key[0]);
#endif
    return( (theKeys.Key[1] == 0x808000L) && !(theKeys.Key[3] || theKeys.Key[2] || theKeys.Key[0]));    /* index and value for cmd-period. */
}
char *GetOSErrStr();

short firstInit = 0;

initMac()
{
    if( !firstInit){
        firstInit = true;
        /* InitWindows would erase previous INIT icons from the screen if called from main(). */
        InitFonts();        /* These inits are required by Dialog Mgr.. */
        InitWindows();
        InitMenus();
        TEInit();
        InitDialogs( NULL);     /* resume won't work for INITs */
        DeskHook = (ProcPtr)0;  /* alert at init time will bomb if not initialized */
        FlushEvents( everyEvent, 0);
        InitCursor();
    }
}

alert(str, err1, err2, err3, err4)      /* expects a C (null terminated) string */
char *str;
short err1, err2, err3, err4;
{
    char buf[256],strBuf[128];
    char nilstr[1];
    nilstr[0]='\0';

    sprintf(buf, str, err1, err2, err3, err4);
    note( text_PStr(S_FAIL_INSTALL, strBuf), CtoPstr( buf), nilstr);
}

alertNote(str, err1, err2, err3, err4)      /* expects a C (null terminated) string */
char *str;
short err1, err2, err3, err4;
{
    char buf[256];
    char nilstr[1];
    nilstr[0]='\0';

    sprintf(buf, str, err1, err2, err3, err4);
    note( CtoPstr( buf), nilstr, nilstr);
}

note( msg1, msg2, msg3)
char *msg1, *msg2, *msg3;
{
    char nilstr[1];
    nilstr[0]='\0';

    initMac();
    ParamText( msg1, msg2, msg3, nilstr);
    Alert( 128, NULL);          /* no beep */
}

caution( msg1, msg2, msg3)
char *msg1, *msg2, *msg3;
{
    char nilstr[1];
    nilstr[0]='\0';

    initMac();
    ParamText( msg1, msg2, msg3, nilstr);
    Alert( 129, NULL);          /* one beep */
}

qAbort( msg1, msg2, msg3)
char *msg1, *msg2, *msg3;
{
    char nilstr[1];
    nilstr[0]='\0';

    initMac();
    ParamText( msg1, msg2, msg3, nilstr);
    return( Alert( 130, NULL) == 1);        /* true if abort */
}


/* No global space is used.
 * fomats: \n, %s, %c, %d, %o, and %x.
 */

sprintf (buf, f, a1)
register char *f, *buf;
int a1;
    {
    register char *s;
    register int *args;
    register int length;
    int radix;

    args = &a1;
    for (; *f; f++)
        switch (*f)  {

        case '\n':
            *buf++ = '\r';

        default:
            *buf++ = *f;
            break;

        case '%':
            length = 0;
            while (*++f >= '0' && *f <= '9')
                length = length * 10 + *f - '0';

            switch (*f)  {

            default:
                *buf++ = *f;
                break;

            case 's':
                if( !length)
                    length = 0x7fff;        /* no maximum length */
#ifdef LSC
                s = *(char **)args;
                args+=sizeof(char*)/sizeof(int);
#else
                s = *(char **)args++;
#endif
                while (*s && length--)
                    *buf++ = *s++;
                break;

            case 'd':
                radix = 10;
                goto cvt;

            case 'o':
                radix = 8;
                goto cvt;

            case 'x':
                radix = 16;
cvt:            buf += itob (buf, *args++, radix);
                break;

            case 'c':
                *buf++ = *args++;
                break;
                }
       }
        *buf++ = '\0';              /* null end of string */
}

/* integer to base */
itob (buf, n, base)
char *buf;
register unsigned int n;
register unsigned int base;
{
    register unsigned int len, extra=0;

    if (base == 10 && (int)n < 0)  {
        n = -n;
        *buf++ = '-';
        extra++;
        }

    len = n < base ? 0 : itob (buf, n / base, base);
    buf[len] = digits(n % base);

    return len + extra + 1;
}

/* returns the ASCII of the binary n without using a global table */
digits( n)
int n;
{
    int c;

    switch(n){
        case 0: c='0'; break;
        case 1: c='1'; break;
        case 2: c='2'; break;
        case 3: c='3'; break;
        case 4: c='4'; break;
        case 5: c='5'; break;
        case 6: c='6'; break;
        case 7: c='7'; break;
        case 8: c='8'; break;
        case 9: c='9'; break;
        case 10: c='a'; break;
        case 11: c='b'; break;
        case 12: c='c'; break;
        case 13: c='d'; break;
        case 14: c='e'; break;
        case 15: c='f'; break;
        default: c='?'; break;
    }
    return c;
}




[LISTING TWO]


/* initOpenDRVR.h
# John Rosford, National Instruments.
# Copyright 1988,1989 National Instruments Corporation
# All rights reserved.
*/

/* select compile configuration for Macintosh handler inits.*/
#define DEBUG_TRACE     /* Alert for each function. */
#define DEBUG1          /* Show drivers marked for loading. */
#define DEBUG2          /* Show driver ID collisions. */
#define DEBUG3          /* Disable duplicate drivers error. */
#undef DEMO             /* Fill slot info with NB-DMA-8 in slot 1. */
#define GLOBALS         /* Using global variables. */

#define LSC             /* Lightspeed C */

#define NSLOTS  6       /* number of NuBus slots.  Don't include serial slots here. */

#define START_OF_USER_IDS 12    /* Inside Mac IV-215.  Zero is used to flag a failure. */

#define SSLOTS  2       /* serial slots */
#define SSLOTA_NDX  (0+NSLOTS)      /* index into slotInfo table - serial slot A, modem */
#define SSLOTB_NDX  (1+NSLOTS)      /* index into slotInfo table - serial slot B, printer */

#define OwnedMASK   0xC000      /* bit mask for the ID of a resource owned by a driver */
#define OWNED_ID(owner,sub_id) (OwnedMASK | (owner << 5) | (sub_id))

#define NUM_IDS 128     /* max number of driver ids */

#ifndef NULL
#define NULL 0L
#endif

#define DRVR_TYPE       'DRVR'
/*----------------------------'dTbl' Resources-------------------------------------*/
#define DRVR_TBL_TYPE   'dTbl'
#define DRVR_TBL_ID     128         /* driver table */

/*----------------------------'oTbl' Resources-------------------------------------*/
#define OWNED_TBL_TYPE  'oTbl'
#define OWNED_TBL_ID        128         /* owned table */

/*----------------------------'busD' Resources-------------------------------------*/
#define BUS_DATA_TYPE   'busD'
#define BUS_DATA_ID 128
#define BUS_DATA_NAM    "GPIB-BusData"

/*----------------------------STR# Resources-------------------------------------*/
#define SS_MSGS_ID      128     /* init message strings */

/* Load flags must not conflict with a slot number: 1-6 */
#define L_NO            0
#define L_YES           -1

#define NB_GPIB         0x109
#define NB_DMA_8        0x10A

#define ourBoard(id) ( id==NB_GPIB || id==NB_DMA_8)

/* Type flags.  DRVR flags include dependency information.  */
enum{
   T_DRVR_MASTER=1,    /* Load/open if any NI board */
   T_BUS_DRVR,         /* Load/open GPIB bus driver if configured or any NI-488 board in slot */
   T_DRVR_GPIB,        /* Load if any NI-488 board: NB-GPIB, NB-DMA, etc. */
   T_BOARD_HW,         /* Load if any NI-488 resource: NB-GPIB, NB-DMA */
   T_SERIAL_HW,        /* Load if any NI-488 resource: GPIB-422CT, GPIB-MAC */
   T_NB_GPIB,          /* Load if hardware is installed */
   T_NB_DMA_8          /* Load if hardware is installed */
};

/* slot translation: s=1 to 6 -> s+8, s=7 to 14 -> s-6 */
#define macSlotNum(s) (s<=6?s+8:s-6)

/* identifies table resource */
#define DRVRTBL     1
#define OWNERTBL    2

/* NB-Series Board Types */
#define NOT_OUR_BOARD       0
#define NO_BOARD            -1
#define SLOT_OUT_OF_BOUNDS  -2

/* typedefs and macros created to aid compatibility between compilers. */
typedef char            int8;
typedef short           int16;
typedef long            int32;
typedef unsigned char   uInt8;
typedef unsigned short  uInt16;
typedef unsigned long   uInt32;

/* structure of the driver and owned resource resources. */
struct drvrStruct{      /* id changes if DRVR ID conflict in checkIDs(). */
    int16 id;           /* All of these are DRVR_TYPE */
    int16 load;         /* Set according to boards and drivers installed.  Load driver if true. */
    int16 type;         /* name is either a board name or a driver name. */
    char name[16];      /* the name */
};

struct ownedStruct{     /* owner changes if DRVR ID conflict in checkIDs().*/
    ResType type;
    int16 id;           /* sub id.  Never changes.  Resource id is OWNED_ID(owner_id,sub_id) */
    int16 owner;        /* owner's id */
};

/* Parameter to first openDriver. */
typedef struct IBOARD
    {
    uInt16  b_uflags;               /* user flags                       */
    uInt8   b_slot;                 /* NUBUS slot or serial port number */
    uInt16  b_baud;                 /* Baud for slots A & B */
} IBoard;

/* GPIB configuration structures, resource names, and constants */

#define NBUSES      2               /* number of buses in bus data */

#define BOARDREV 0x12   /* Rev 1.2 of BoardResource */

#define E_NBRDS     -1      /* Errors returned by getBrds & getDevs */
#define E_BRDREV    -2

/* Bus Structure */
struct  busConf {
    uInt16  b_uflags;       /* user flags */
    uInt16  b_slot;         /* Slot for this GPIB bus. */
    uInt16  b_baud;         /* Baud for slots A & B */
};


typedef struct {
    uInt16  Rev;            /* hex value, for example: 0x10 for Rev 1.0 */
    uInt16  Cnt;            /* number of buses in BusData */
    struct busConf BusData[NBUSES];     /* configuration data for each bus */
    } BusResource;

/* INIT configuration structures and constants */
#define isSerSlot(s)    (s > NSLOTS)        /* is serial slot */
#define conf2initSerSlot(s) (s-NSLOTS-1)    /* is serial slot */

/* hardware variant types other than NuBus */
#define GPIBMAC 20000

typedef struct {
    char brdName[256];  /* max name length */
    int16 brdID;
} slotInfoType;

typedef struct {
    int16   MaxBuses;           /* number of buses read from bus data. */
    slotInfoType slotInfo[NSLOTS+SSLOTS];   /* nubus+serial slots */
    BusResource     brdRsrc;
}config_t;

/* Prototypes of functions defined in initOpenDRVR.c */
main(void);
dupDriverInstalled(struct drvrStruct *drvrTbl);
char *strcpy( char*, char*);
char *strcat(char*, char*);
int strcmp( char*, char*);
char *text_PStr(int16, char *);
char *text(int16, char *);
showIcon(void);
getTables( struct drvrStruct **, struct ownedStruct **);
configInfo(config_t *);
configBrds( BusResource*,config_t*);
markLoad( struct drvrStruct *, config_t *);
ndxDrvrType( struct drvrStruct *, int16);
drvrID2index( struct drvrStruct *, int16);
getSlot( unsigned int16, config_t *);
autoSlot( int16,config_t *);
slotInfoTable(config_t *);
calcNslots( void);
GetSlotInfo(int16, char *);
checkSysHeapSize( struct drvrStruct *, struct ownedStruct *);
findUsedIDs( int16*);
checkUnitTable( int16*);
checkSysRsrcs( int16*);
checkIDs( struct drvrStruct *, struct ownedStruct *, int16 *);
getUnusedID( int16 *,int16 *);
newOwner( struct ownedStruct *, int16, int16);
changeRsrcID( Handle, int16);
changeTableResources( int16);
openMarkedDrivers( struct drvrStruct *,config_t *);
detachDriver( struct drvrStruct *, struct ownedStruct *);
openBusDriver(char *,int16,config_t *);
kill_key(void);
initMac(void);
alert(char *, ...);
alertNote(char *, ...);
note( char *, char *, char *);
caution( char *, char *, char *);
qAbort( char *, char *, char *);
sprintf (char *, char *, ...);
itob (char *, unsigned int16, unsigned int16);
digits( int16);

/* indices to STR# */
enum{
    ZERO,
    S_FAIL_INSTALL,
    S_FAIL_REMOVE,
    S_FAIL_GET_DRVR_TBL,
    S_FAIL_GET_OWNED_TBL,
    S_FAIL_NO_DRVR,
    S_FAIL_GET_DRVR,
    S_FAIL_GET_OWNED,
    S_FAIL_ID_OSERR,
    S_FAIL_HEAP,
    S_FAIL_RSRC_MGR,
    S_FAIL_NO_FREE_ID,
    S_FAIL_ID_CHANGE,
    S_FAIL_GET_ATTR,
    S_FAIL_GET_INFO,
    S_FAIL_SET_INFO,
    S_FAIL_SET_ATTR,
    S_FAIL_OPEN_NAME,
    S_FAIL_OPEN_DRVRS,
    S_FAIL_DETACH_DRVR,
    S_FAIL_DETACH_OWNED,
    S_NOT_DRVR_TYPE,
    S_NEED_MAC_SE,
    S_NEED_MAC_II,
    S_USER_ABORT,
    S_Q_USER_ABORT,
    S_FAIL_GET_DATA,
    S_FAIL_GET_NAME,
    S_FAIL_NUM_BUSES,
    S_FAIL_REVISION,
    xS_LAST_STRING
};


[LISTING THREE]



/* File DDJInit.r

   Copyright ) 1989 National Instruments.

With MPW, to compile this file and copy the resources to the INIT:
Set BuildDir ':Build INIT:'
Set InitName 'DDJ INIT'
rez -o "{BuildDir}{InitName}" -s "{BuildDir}" DDJInit.r
Setfile "{BuildDir}{InitName}" -a B -t INIT -c DDJI
_________________________________________________________________________*/

/* include other resources */
#define INIT_RSRC
#ifdef INIT_RSRC
include "*INIT.rsrc";       /* INIT resource; flags OK */
#endif
include "*DDJ Driver" 'DRVR' (0:64) as
                'DRVR' ($$ID, $$Name, SysHeap, Locked); /* DRVR resource */
include "*DDJ Driver" 'DATA' (-15424:-15200) as
                'DATA' ($$ID, "", SysHeap, Purgeable);  /* owned DATA resource */

/* include other text files */
#include "Types.r"
#include "SysTypes.r"

#define CREATOR     'DDJI'      /* file creator */

/* Board IDs */
#define NB_GPIB         0x109
#define NB_DMA_8        0x10A

/* Resource IDs */

/*----------------------------'dTbl' Resources-------------------------------------*/
#define DRVR_TBL_TYPE   'dTbl'
#define DRVR_TBL_ID     128         /* driver table */

/*----------------------------'oTbl' Resources-------------------------------------*/
#define OWNED_TBL_TYPE  'oTbl'
#define OWNED_TBL_ID        128         /* owned table */

/*----------------------------STR# Resources-------------------------------------*/
#define SS_MSGS_ID      128     /* init message strings */

/*----------------------------'busD' Resources-------------------------------------*/
#define BUS_DATA_TYPE   'busD'
#define BUS_DATA_ID 128
#define BUS_DATA_NAM    "GPIB-BusData"

/* Load flags must not conflict with a slot number: 1-6 */
#define L_NO_X          0
#define L_YES_X         -1


/*----------------------------dTbl % driver table-------------------------------------*/
type DRVR_TBL_TYPE {
    wide array {
        integer BUS0_ID=30,     /* GPIB bus drivers */
                BUS1_ID,
                SHARE_ID,       /* Shared code */
                SH_ID,          /* GPIB serial port hardware */
                BH_ID,          /* GPIB board hardware */
                LAB_ID,         /* LabDriver */
                DMA_ID,         /* NB-DMA subdriver */
                GPI_ID          /* NB-GPIB subdriver */
                ;               /* DRVR IDs */
        integer L_YES=L_YES_X,
                L_NO=L_NO_X;                                /* Load status  */
        integer T_DRVR_MASTER=1,    /* Load if any NI board */
                T_BUS_DRVR,         /* Load GPIB bus driver if configured and any NI-488 board in slot */
                T_DRVR_GPIB,        /* Load if any NI-488 resource: NB-GPIB, NB-DMA, GPIB-422CT, etc */
                T_BOARD_HW,         /* Loaded by LabDriver */
                T_SERIAL_HW,        /* Load if any NI-488 resource: GPIB-422CT, GPIB-MAC */
                T_NB_GPIB,          /* Load if hardware is installed */
                T_NB_DMA_8          /* Load if hardware is installed */
                ;                   /* DRVR Type flags */
        cstring[16];                                        /* board name else driver name  */
    };
};


/*----------------------------oTbl % owned resource table-----------------------------*/

type OWNED_TBL_TYPE {
    wide array {
        literal longint;               /* type of owned resource */
        integer;                       /* id of owned resource */
        integer BUS0_ID=30,     /* GPIB bus drivers */
                BUS1_ID,
                SHARE_ID,       /* Shared code */
                SH_ID,          /* GPIB serial port hardware */
                BH_ID,          /* GPIB board hardware */
                LAB_ID,         /* LabDriver */
                DMA_ID,         /* NB-DMA subdriver */
                GPI_ID          /* NB-GPIB subdriver */
                ;               /* DRVR IDs */
    };
};


/*----------------------------DATA % Bus Data-------------------------------------*/
type BUS_DATA_TYPE {
    hex integer;                /* Revision */
    integer;                /* Number of Buses */
    wide array {
        unsigned hex integer;       /* uflags */
        unsigned integer;           /* slot */
        unsigned integer;           /* baud */
    };
};

data 'sysz' (0) {
    $"0001 0000"            /* expand sys heap */
};

/* DRVR indices must match table resources.  See initOpenDRVR.c */
resource DRVR_TBL_TYPE (DRVR_TBL_ID, "drvrTbl", preload) {
    {
    BUS0_ID,    L_NO,   T_BUS_DRVR,     ".GPIB0",
    BUS1_ID,    L_NO,   T_BUS_DRVR,     ".GPIB1",
    SHARE_ID,   L_NO,   T_DRVR_GPIB,    ".GPIBSharedCode",
    SH_ID,      L_NO,   T_SERIAL_HW,    ".GMHardwareCode",
    BH_ID,      L_NO,   T_BOARD_HW,     ".NBHardwareCode",
    LAB_ID,     L_NO,   T_DRVR_MASTER,  ".LabDRIVER",
    DMA_ID,     L_NO,   NB_DMA_8,       ".NB-DMA",
    GPI_ID,     L_NO,   NB_GPIB,        ".NB-GPI"
    }
};

resource OWNED_TBL_TYPE (OWNED_TBL_ID, "ownedTbl", preload) {
    {
    'DATA', 0,  BUS0_ID,        /* ".GPIB0" owned data resource */
    'DATA', 0,  BUS1_ID,        /* ".GPIB1" owned data resource */
    'DATA', 0,  SHARE_ID,       /* ".GPIBSharedCode" owned data resource */
    'DATA', 0,  BH_ID,          /* ".NBHardwareCode" owned data resource */
    'DATA', 0,  SH_ID,          /* ".GMHardwareCode"   owned data resource */
    'DATA', 0,  LAB_ID,         /* ".LabDRIVER" owned data resource */
    'DATA', 0,  DMA_ID,         /* ".NB-DMA" owned data resource */
    'DATA', 0,  GPI_ID          /* ".NB-GPI"   owned data resource */
    }
};


#define baud9600 10

/* No two buses should be assigned to the same slot.  Slots range from 1 to NSLOTS+SSLOTS with
 * zero for not assignment.  Change slot to 7 for port A.
 */

resource BUS_DATA_TYPE (BUS_DATA_ID, BUS_DATA_NAM) {
    0x12,       /* Revision */
    2,          /* Number of Buses */
    {   /* array: 2 elements */
        /* [1] */
        0x1C03,1,baud9600,  /* uflags,slot 1,baud */
        /* [2] */
        0x1C03,7,baud9600   /* serial slot A */
    }
};


resource 'BNDL' (128, purgeable) {
    CREATOR,
    0,
    {   /* array TypeArray: 2 elements */
        /* [1] */
        'ICN#',
        {   /* array IDArray: 2 elements */
            /* [1] */
            0, 128,
        },
        /* [2] */
        'FREF',
        {   /* array IDArray: 2 elements */
            /* [1] */
            0, 128,
        }
    }
};

type CREATOR as 'STR ';

resource CREATOR (0) {
    "DDJ Mac Init Version 1.0"
};

resource 'FREF' (128, preload) {
    'INIT',
    0,
    ""
};

resource 'vers' (1, purgeable) {
0x01, 0x00, final, 0x00, verUS,
"1.0",
"1.0, ) National Instruments 1989"
};

resource 'vers' (2, purgeable) {
0x01, 0x00, final, 0x00, verUS,
"1.0",
"DDJ INIT Release 1.0"
};

resource 'STR#' (SS_MSGS_ID, "INIT Messages") {
    {
    "The driver installation failed.",            /* S_FAIL_INSTALL */
    "Remove old or duplicate NI drivers.",        /* S_FAIL_REMOVE */
    "Failed to get the driver table resource.",   /* S_FAIL_GET_DRVR_TBL */
        "Failed to get the owned table resource.",      /* S_FAIL_GET_OWNED_TBL */
      "No driver with id = %d.",                    /* S_FAIL_NO_DRVR */
      "Failed to get the driver resource: %s.",     /* S_FAIL_GET_DRVR */
      "Failed to get an owned resource of: %s.",    /* S_FAIL_GET_OWNED */
      "ID %d, OSErr = %d",                          /* S_FAIL_ID_OSERR */
      "Out of memory in the system heap.",          /* S_FAIL_HEAP */
      "Resource Mgr error: %d",                     /* S_FAIL_RSRC_MGR */
      "Failed to find a free driver ID.",           /* S_FAIL_NO_FREE_ID */
      "Failed to get resource for ID change, id=%d, err=%d.", /* S_FAIL_ID_CHANGE */
     "Failed to get resource attributes, err=%d.",  /* S_FAIL_GET_ATTR */
     "Failed to get resource info, err=%d.",        /* S_FAIL_GET_INFO */
     "Failed to set resource info, err=%d.",        /* S_FAIL_SET_INFO */
     "Failed to set resource attributes, err=%d.",  /* S_FAIL_SET_ATTR */
     "Failed to open %s.",                          /* S_FAIL_OPEN_NAME */
     "OpenDriver error %d.",                        /* S_FAIL_OPEN_DRVRS */
     "Failed to detach %s driver.",                 /* S_FAIL_DETACH_DRVR */
     "Failed to detach %s owned resource.",         /* S_FAIL_DETACH_OWNED */
     "Not a driver type: %d, at table index %d",     /* S_NOT_DRVR_TYPE */
     "The handler requires a Macintosh SE.",         /* S_NEED_MAC_SE */
     "The handler requires a Macintosh II.",         /* S_NEED_MAC_II */
     "User aborted driver installation.",            /* S_USER_ABORT */
     "Abort the driver installation?",               /* S_Q_USER_ABORT */
     "Failed to get the default bus/device data resources.",     /* S_FAIL_GET_DATA */
     "Failed to get the default name resources.",    /* S_FAIL_GET_NAME */
     "Too many buses in default data resources",     /* S_FAIL_NUM_BUSES */
     "Wrong revision in default data resources.",    /* S_FAIL_REVISION */
     "End of strings."           /*  */
    }
};

resource 'ALRT' (128) {
    {50, 30, 150, 480},
    128,
    {   /* array: 4 elements */
        /* [1] */
        OK, visible, silent,
        /* [2] */
        OK, visible, silent,
        /* [3] */
        OK, visible, silent,
        /* [4] */
        OK, visible, silent
    }
};

resource 'ALRT' (129) {
    {50, 30, 150, 480},
    128,
    {   /* array: 4 elements */
        /* [1] */
        OK, visible, sound1,
        /* [2] */
        OK, visible, sound1,
        /* [3] */
        OK, visible, sound1,
        /* [4] */
        OK, visible, sound1
    }
};

resource 'ALRT' (130) {
    {50, 30, 150, 480},
    129,
    {   /* array: 4 elements */
        /* [1] */
        OK, visible, sound1,
        /* [2] */
        OK, visible, sound1,
        /* [3] */
        OK, visible, sound1,
        /* [4] */
        OK, visible, sound1
    }
};

resource 'DITL' (128) {
    {   /* array DITLarray: 4 elements */
        /* [1] */
        {60, 190, 78, 260},
        Button {
            enabled,
            "OK"
        },
        /* [2] */
        {10, 10, 24, 440},
        StaticText {
            enabled,
            "^0"
        },
        /* [3] */
        {25, 10, 39, 440},
        StaticText {
            enabled,
            "^1"
        },
        /* [4] */
        {40, 10, 54, 440},
        StaticText {
            enabled,
            "^2"
        }
    }
};

resource 'DITL' (129) {
    {   /* array DITLarray: 5 elements */
        /* [1] */
        {60, 115, 78, 185},
        Button {
            enabled,
            "Abort"
        },
        /* [2] */
        {60, 265, 78, 335},
        Button {
            enabled,
            "Continue"
        },
        /* [3] */
        {10, 10, 24, 440},
        StaticText {
            enabled,
            "^0"
        },
        /* [4] */
        {25, 10, 39, 440},
        StaticText {
            enabled,
            "^1"
        },
        /* [5] */
        {40, 10, 54, 440},
        StaticText {
            enabled,
            "^2"
        }
    }
};

resource 'ICN#' (128) {
    {   /* array: 2 elements */
        /* [1] */
        $"FF FF FF FE 80 00 00 03 80 00 00 03 8F E7 F1 F3"
        $"88 34 19 13 8D DE ED B3 85 6A B4 A3 85 2A 94 A3"
        $"85 2A 94 A3 85 2A 94 A3 85 2A 94 A3 85 2A 94 A3"
        $"85 2A 94 A3 85 2A 94 A3 85 2A 94 A3 85 2A 94 A3"
        $"85 2A 94 A3 85 2A 94 A3 85 2A 94 A3 85 2A 94 A3"
        $"85 2A 94 A3 85 2A 94 A3 85 6A B4 A3 8D DE EC A3"
        $"88 34 18 A3 8F E7 F0 A3 80 00 03 A3 80 00 02 63"
        $"80 00 03 C3 80 00 00 03 FF FF FF FF 7F FF FF FF",
        /* [2] */
        $"FF FF FF FE FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"
        $"FF FF FF FF FF FF FF FF FF FF FF FF 7F FF FF FF"
    }
};




Copyright © 1989, Dr. Dobb's Journal