home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / Level 0 Macintosh 29Sep94 / SerialPort.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  16.9 KB  |  565 lines  |  [TEXT/KAHL]

  1. /* SerialPort.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Audit.h"
  21. #include "Debug.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <Serial.h>
  28. #include <Devices.h>
  29. #include <Events.h>
  30. #ifdef THINK_C
  31.     #pragma options(!pack_enums)
  32. #endif
  33.  
  34. #include "SerialPort.h"
  35. #include "Memory.h"
  36. #include "EventLoop.h"
  37. #include "Array.h"
  38.  
  39.  
  40. #define DC1 (17) /* XOn character */
  41. #define DC3 (19) /* XOff character */
  42.  
  43. /* this value is the length of time we'll wait for a write cell to drain.  if */
  44. /* it doesn't drain in this much time, we return a timeout error. */
  45. /* time units are in 1/60ths of a second */
  46. #define IOTIMEOUT (6*60)  /* 6 seconds to write a byte to the output buffer */
  47.  
  48. /* this defines the size of the input buffer */
  49. #define INPUTBUFFERSIZE (16384)
  50.  
  51. /* this defines how many bytes a single asynchronous write operation can handle. */
  52. #define WRITECELLSIZE (384)
  53.  
  54. /* this defines how many write cells are allocated */
  55. #define NUMWRITECELLS (16)
  56.  
  57. /* this structure is one write cell buffer, and can be used for one asynchronous */
  58. /* write operation */
  59. typedef struct
  60.     {
  61.         ParamBlockRec            MyPB;
  62.         char                            Buffer[WRITECELLSIZE];
  63.         MyBoolean                    InUseFlag;
  64.         long                            NumBytes;
  65.     } WriteCell;
  66.  
  67.  
  68. struct SerialPortRec
  69.     {
  70.         short                        InputPortRefNum;
  71.         short                        OutputPortRefNum;
  72.         MyBoolean                GracePeriodInEffect;
  73.         char                        InputBuffer[INPUTBUFFERSIZE];
  74.         WriteCell                WriteCellArray[NUMWRITECELLS];
  75.     };
  76.  
  77.  
  78. struct SerialRefRec
  79.     {
  80.         long                        PortIndex;
  81.     };
  82.  
  83.  
  84. EXECUTE(static MyBoolean                Initialized = False;)
  85.  
  86. EXECUTE(static long                            RefCount = 0;)
  87.  
  88. EXECUTE(static ArrayRec*                ListOfRefs;)
  89.  
  90.  
  91. /* prototype for callback routine */
  92. static pascal void    Callback(void);
  93.  
  94.  
  95. /* initialize serial port subsystem.  the user calls this.  this is not called */
  96. /* from the normal Level 0 initialization since this module is optional. */
  97. MyBoolean                        InitializeSerialPorts(void)
  98.     {
  99.         ERROR(Initialized,PRERR(ForceAbort,"InitializeSerialPorts:  already initialized"));
  100.         EXECUTE(RefCount = 0;)
  101. #if DEBUG
  102.         ListOfRefs = NewArray();
  103.         if (ListOfRefs == NIL)
  104.             {
  105.                 return False;
  106.             }
  107. #endif
  108.         EXECUTE(Initialized = True;)
  109.         return True;
  110.     }
  111.  
  112.  
  113. /* shut down serial ports */
  114. void                                ShutdownSerialPorts(void)
  115.     {
  116.         ERROR(!Initialized,PRERR(ForceAbort,"ShutdownSerialPorts:  not initialized"));
  117.         ERROR(RefCount != 0,PRERR(AllowResume,
  118.             "ShutdownSerialPorts:  some connections still open"));
  119. #if DEBUG
  120.         ERROR(ArrayGetLength(ListOfRefs) != 0,PRERR(AllowResume,
  121.             "ShutdownSerialPorts:  some references have not been disposed"));
  122.         DisposeArray(ListOfRefs);
  123. #endif
  124.         EXECUTE(Initialized = False;)
  125.     }
  126.  
  127.  
  128. /* request a port.  if the port can not be allocated, it will return NIL. */
  129. SerialPortRec*            RequestSerialPort(long BitsRate, SerialRefRec* PortIdentifier,
  130.                                             ParityTypes Parity, HandShakeTypes HandShake,
  131.                                             DataBitTypes NumDataBits, StopBitTypes NumStopBits)
  132.     {
  133.         SerialPortRec*        SerialPort;
  134.         long                            Scan;
  135.         OSErr                            Error;
  136.         long                            Config;
  137.         short                            BaudTemp;
  138.         SerShk                        Shaker;
  139.  
  140.         ERROR(!Initialized,PRERR(ForceAbort,"RequestSerialPort:  not initialized"));
  141.         /* create the memory block.  it must never relocate after this. */
  142.         ERROR(ArrayFindElement(ListOfRefs,PortIdentifier) == -1,PRERR(ForceAbort,
  143.             "RequestSerialPort:  invalid reference"));
  144.         CheckPtrExistence(PortIdentifier);
  145.         SerialPort = (SerialPortRec*)AllocPtrCanFail(sizeof(SerialPortRec),"SerialPortRec");
  146.         if (SerialPort == NIL)
  147.             {
  148.              FailurePoint1:
  149.                 return NIL;
  150.             }
  151.         /* open the serial port */
  152.         switch (PortIdentifier->PortIndex)
  153.             {
  154.                 default: /* bad serial port ID number */
  155.                     EXECUTE(PRERR(ForceAbort,"RequestSerialPort:  bad serial port ID number"));
  156.                     break;
  157.                 case 0: /* modem port */
  158.                     Error = OpenDriver("\p.AOut",&(SerialPort->OutputPortRefNum));
  159.                     if (Error != noErr)
  160.                         {
  161.                             ReleasePtr((char*)SerialPort);
  162.                             goto FailurePoint1;
  163.                         }
  164.                     Error = OpenDriver("\p.AIn",&(SerialPort->InputPortRefNum));
  165.                     if (Error != noErr)
  166.                         {
  167.                             CloseDriver(SerialPort->OutputPortRefNum);
  168.                             ReleasePtr((char*)SerialPort);
  169.                             goto FailurePoint1;
  170.                         }
  171.                     break;
  172.                 case 1: /* printer port */
  173.                     Error = OpenDriver("\p.BOut",&(SerialPort->OutputPortRefNum));
  174.                     if (Error != noErr)
  175.                         {
  176.                             ReleasePtr((char*)SerialPort);
  177.                             goto FailurePoint1;
  178.                         }
  179.                     Error = OpenDriver("\p.BIn",&(SerialPort->InputPortRefNum));
  180.                     if (Error != noErr)
  181.                         {
  182.                             CloseDriver(SerialPort->OutputPortRefNum);
  183.                             ReleasePtr((char*)SerialPort);
  184.                             goto FailurePoint1;
  185.                         }
  186.                     break;
  187.             }
  188.         /* clear the in-use fields of the write cells */
  189.         for (Scan = 0; Scan < NUMWRITECELLS; Scan += 1)
  190.             {
  191.                 SerialPort->WriteCellArray[Scan].InUseFlag = False;
  192.             }
  193.         /* initialize the various important parameters */
  194.         Config = 0;
  195.         switch (Parity)
  196.             {
  197.                 case eParityNone:
  198.                     Config |= noParity;
  199.                     break;
  200.                 case eParityEven:
  201.                     Config |= evenParity;
  202.                     break;
  203.                 case eParityOdd:
  204.                     Config |= oddParity;
  205.                     break;
  206.                 default:
  207.                     EXECUTE(PRERR(ForceAbort,"Unsupported parity"));
  208.                     break;
  209.             }
  210.         switch (NumDataBits)
  211.             {
  212.                 case e8DataBits:
  213.                     Config |= data8;
  214.                     break;
  215.                 case e7DataBits:
  216.                     Config |= data7;
  217.                     break;
  218.                 case e6DataBits:
  219.                     Config |= data6;
  220.                     break;
  221.                 case e5DataBits:
  222.                     Config |= data5;
  223.                     break;
  224.                 default:
  225.                     EXECUTE(PRERR(ForceAbort,"Unsupported number of data bits"));
  226.                     break;
  227.             }
  228.         switch (NumStopBits)
  229.             {
  230.                 case eOneStopBit:
  231.                     Config |= stop10;
  232.                     break;
  233.                 case eOneAndAHalfStopBits:
  234.                     Config |= stop15;
  235.                     break;
  236.                 case eTwoStopBits:
  237.                     Config |= stop20;
  238.                     break;
  239.                 default:
  240.                     EXECUTE(PRERR(ForceAbort,"Unsupported number of stop bits"));
  241.                     break;
  242.             }
  243.         SerReset(SerialPort->InputPortRefNum,Config);
  244.         SerReset(SerialPort->OutputPortRefNum,Config);
  245.         /* set the baud rate */
  246.         BaudTemp = GetClosestAvailableBaudRate(BitsRate,PortIdentifier);
  247.         Control(SerialPort->OutputPortRefNum,13,&BaudTemp);
  248.         BaudTemp = GetClosestAvailableBaudRate(BitsRate,PortIdentifier);
  249.         Control(SerialPort->InputPortRefNum,13,&BaudTemp);
  250.         /* setting the buffer */
  251.         SerSetBuf(SerialPort->InputPortRefNum,&(SerialPort->InputBuffer[0]),INPUTBUFFERSIZE);
  252.         /* setting handshake parameters */
  253.         Shaker.xOn = DC1;
  254.         Shaker.xOff = DC3;
  255.         Shaker.fXOn = (HandShake == eHandShakeXonXoff);
  256.         Shaker.fCTS = (HandShake == eHandShakeDtrCts) || (HandShake == eHandShakeCtsOnly);
  257.         Shaker.errs = 0;
  258.         Shaker.evts = 0;
  259.         Shaker.fInX = (HandShake == eHandShakeXonXoff);
  260.         Shaker.fDTR = (HandShake == eHandShakeDtrCts) || (HandShake == eHandShakeDtrOnly);
  261.         SerHShake(SerialPort->InputPortRefNum,&Shaker);
  262.         SerHShake(SerialPort->OutputPortRefNum,&Shaker);
  263.         /* connection now established */
  264.         SerialPort->GracePeriodInEffect = False;
  265.         EXECUTE(RefCount += 1;)
  266.         return SerialPort;
  267.     }
  268.  
  269.  
  270. /* close a port */
  271. void                                CloseSerialPort(SerialPortRec* SerialPort)
  272.     {
  273.         ERROR(!Initialized,PRERR(ForceAbort,"CloseSerialPort:  not initialized"));
  274.         CheckPtrExistence(SerialPort);
  275.         /* make sure no callbacks are waiting to be called */
  276.         KillIO(SerialPort->OutputPortRefNum);
  277.         KillIO(SerialPort->InputPortRefNum);
  278.         /* dump the drivers */
  279.         CloseDriver(SerialPort->InputPortRefNum);
  280.         CloseDriver(SerialPort->OutputPortRefNum);
  281.         /* release the memory */
  282.         ReleasePtr((char*)SerialPort);
  283.         EXECUTE(RefCount -= 1;)
  284.     }
  285.  
  286.  
  287. /* get how many serial ports there are on the system */
  288. long                                GetNumSerialPorts(void)
  289.     {
  290.         ERROR(!Initialized,PRERR(ForceAbort,"GetNumSerialPorts:  not initialized"));
  291.         return 2;
  292.     }
  293.  
  294.  
  295. /* get the ID of a serial port from the list */
  296. SerialRefRec*                GetIndexedSerialPort(long Index)
  297.     {
  298.         SerialRefRec*            Ref;
  299.  
  300.         ERROR(!Initialized,PRERR(ForceAbort,"GetIndexedSerialPort:  not initialized"));
  301.         ERROR((Index < 0) || (Index >= GetNumSerialPorts()),PRERR(ForceAbort,
  302.             "GetIndexedSerialPort:  port index is out of range"));
  303.         Ref = (SerialRefRec*)AllocPtrCanFail(sizeof(SerialRefRec),"SerialRefRec");
  304.         if (Ref == NIL)
  305.             {
  306.              FailurePoint1:
  307.                 return NIL;
  308.             }
  309.         Ref->PortIndex = Index;
  310.         EXECUTE(if (!ArrayAppendElement(ListOfRefs,Ref)) {ReleasePtr((char*)Ref); Ref = NIL;});
  311.         return Ref;
  312.     }
  313.  
  314.  
  315. /* get the name associated with a serial port identifier */
  316. char*                                GetSerialPortName(SerialRefRec* TheIdentifier)
  317.     {
  318.         char*                            Name;
  319.  
  320.         ERROR(!Initialized,PRERR(ForceAbort,"GetSerialPortName:  not initialized"));
  321.         ERROR(ArrayFindElement(ListOfRefs,TheIdentifier) == -1,PRERR(ForceAbort,
  322.             "GetSerialPortName:  invalid reference"));
  323.         CheckPtrExistence(TheIdentifier);
  324.         switch (TheIdentifier->PortIndex)
  325.             {
  326.                 case 0:  /* modem port */
  327.                     Name = AllocPtrCanFail(10,"SerialPortName");
  328.                     if (Name != NIL)
  329.                         {
  330.                             CopyData("Modem Port",Name,10);
  331.                         }
  332.                     break;
  333.                 case 1:  /* printer port */
  334.                     Name = AllocPtrCanFail(12,"SerialPortName");
  335.                     if (Name != NIL)
  336.                         {
  337.                             CopyData("Printer Port",Name,12);
  338.                         }
  339.                     break;
  340.                 default:
  341.                     EXECUTE(PRERR(ForceAbort,"GetSerialPortName:  bad port number"));
  342.                     break;
  343.             }
  344.         return Name;
  345.     }
  346.  
  347.  
  348. /* dispose of a serial port reference */
  349. void                                DisposeSerialRef(SerialRefRec* TheIdentifier)
  350.     {
  351.         CheckPtrExistence(TheIdentifier);
  352.         ERROR(ArrayFindElement(ListOfRefs,TheIdentifier) == -1,PRERR(ForceAbort,
  353.             "DisposeSerialRef:  invalid reference"));
  354.         EXECUTE(ArrayDeleteElement(ListOfRefs,ArrayFindElement(ListOfRefs,TheIdentifier)));
  355.         ReleasePtr((char*)TheIdentifier);
  356.     }
  357.  
  358.  
  359. /* find out the closest available baud rate to the one requested.  if the */
  360. /* requested baud rate is supported, it is returned.  if not, then the closest */
  361. /* available rate is returned. */
  362. long                                GetClosestAvailableBaudRate(long RequestedRate,
  363.                                             SerialRefRec* PortIdentifier)
  364.     {
  365.         ERROR(!Initialized,PRERR(ForceAbort,
  366.             "GetClosestAvailableBaudRate:  not initialized"));
  367.         ERROR(ArrayFindElement(ListOfRefs,PortIdentifier) == -1,PRERR(ForceAbort,
  368.             "GetClosestAvailableBaudRate:  invalid reference"));
  369.         ERROR((PortIdentifier->PortIndex < 0) || (PortIdentifier->PortIndex >= 2),
  370.             PRERR(ForceAbort,"GetClosestAvailableBaudRate:  bad port number"));
  371.         CheckPtrExistence(PortIdentifier);
  372.         /* for Macintosh, this doesn't actually depend on the port kind, but on */
  373.         /* some systems it might */
  374.         if (RequestedRate < 300)
  375.             {
  376.                 return 300;
  377.             }
  378.         else if (RequestedRate > 57600)
  379.             {
  380.                 return 57600;
  381.             }
  382.         else
  383.             {
  384.                 return RequestedRate;
  385.             }
  386.     }
  387.  
  388.  
  389. /* find out how much data is waiting to be read */
  390. long                                NumSerialPortBytesWaitingToRead(SerialPortRec* SerialPort)
  391.     {
  392.         long                            NumBytes;
  393.  
  394.         ERROR(!Initialized,PRERR(ForceAbort,
  395.             "NumSerialPortBytesWaitingToRead:  not initialized"));
  396.         CheckPtrExistence(SerialPort);
  397.         SerGetBuf(SerialPort->InputPortRefNum,&NumBytes);
  398.         return NumBytes;
  399.     }
  400.  
  401.  
  402. /* find out how much data is waiting to leave the local buffers */
  403. long                                NumSerialPortBytesWaitingToWrite(SerialPortRec* SerialPort)
  404.     {
  405.         long                            Scan;
  406.         long                            Count;
  407.  
  408.         ERROR(!Initialized,PRERR(ForceAbort,
  409.             "NumSerialPortBytesWaitingToWrite:  not initialized"));
  410.         CheckPtrExistence(SerialPort);
  411.         Count = 0;
  412.         for (Scan = 0; Scan < NUMWRITECELLS; Scan += 1)
  413.             {
  414.                 if (SerialPort->WriteCellArray[Scan].InUseFlag)
  415.                     {
  416.                         /* if the flag gets cleared right before we read it, well, too bad... */
  417.                         Count += SerialPort->WriteCellArray[Scan].NumBytes;
  418.                     }
  419.             }
  420.         return Count;
  421.     }
  422.  
  423.  
  424. /* read some bytes from the port buffer into the specified buffer.  it is an */
  425. /* error to read more bytes than there are waiting. */
  426. void                                ReadSerialPort(SerialPortRec* SerialPort, long NumBytesToRead,
  427.                                             char* Buffer)
  428.     {
  429.         OSErr                            Error;
  430.         long                            OldNumBytes;
  431.  
  432.         ERROR(!Initialized,PRERR(ForceAbort,"ReadSerialPort:  not initialized"));
  433.         ERROR(NumSerialPortBytesWaitingToRead(SerialPort) < NumBytesToRead,
  434.             PRERR(AllowResume,"ReadSerialPort:  reading too many bytes"));
  435.         EXECUTE(OldNumBytes = NumBytesToRead;)
  436.         Error = FSRead(SerialPort->InputPortRefNum,&NumBytesToRead,&(Buffer[0]));
  437.         ERROR(OldNumBytes != NumBytesToRead,PRERR(ForceAbort,
  438.             "ReadSerialPort: [FSRead] read error -- didn't read all the bytes even though they exist"));
  439.     }
  440.  
  441.  
  442. /* submit bytes to be written.  it returns True if successful, or False if */
  443. /* the operation timed out or another error occurred */
  444. MyBoolean                        WriteSerialPort(SerialPortRec* SerialPort, long Length, char* Data)
  445.     {
  446.         long                        StartTime;
  447.         short                        ChosenWriteCell;
  448.         short                        Scan;
  449.  
  450.         ERROR(!Initialized,PRERR(ForceAbort,"WriteSerialPort:  not initialized"));
  451.         CheckPtrExistence(SerialPort);
  452.  
  453.         if (Length == 0)
  454.             {
  455.                 return True;
  456.             }
  457.  
  458.         /* entry point for writing chunks of data */
  459.      ReEntryPoint:
  460.         ChosenWriteCell = -1;
  461.         StartTime = TickCount();
  462.         /* here we loop until we can find a free write cell */
  463.         while (ChosenWriteCell == -1)
  464.             {
  465.                 /* check timeout */
  466.                 if (TickCount() - StartTime > IOTIMEOUT)
  467.                     {
  468.                         /* a timeout occurred because we looped for a while and were unable */
  469.                         /* to write any data. */
  470.                         return False;
  471.                     }
  472.                 /* look for available write cells */
  473.                 for (Scan = 0; (Scan < NUMWRITECELLS) && (ChosenWriteCell == -1); Scan += 1)
  474.                     {
  475.                         if (!SerialPort->WriteCellArray[Scan].InUseFlag)
  476.                             {
  477.                                 ChosenWriteCell = Scan;
  478.                             }
  479.                     }
  480.                 /* relinquish CPU so that user can do something productive while waiting. */
  481.                 if (ChosenWriteCell == -1)
  482.                     {
  483.                         RelinquishCPUCheckCancel();
  484.                     }
  485.             }
  486.  
  487.         /* we found a buffer, so we can set it up for the async. write */
  488.         SerialPort->WriteCellArray[ChosenWriteCell].InUseFlag = True;
  489.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioCompletion
  490.             = (ProcPtr)&Callback;
  491.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioVRefNum = 0;
  492.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioRefNum
  493.             = SerialPort->OutputPortRefNum;
  494.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioBuffer
  495.             = SerialPort->WriteCellArray[ChosenWriteCell].Buffer;
  496.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioPosMode = fsAtMark;
  497.  
  498.         /* how much to write? */
  499.         if (Length > WRITECELLSIZE)
  500.             {
  501.                 /* if the pending amount of data is larger than the size of a */
  502.                 /* write cell, then we only write the first writecell's worth of data */
  503.                 SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioReqCount
  504.                     = WRITECELLSIZE;
  505.                 SerialPort->WriteCellArray[ChosenWriteCell].NumBytes = WRITECELLSIZE;
  506.                 CopyData(Data,SerialPort->WriteCellArray[ChosenWriteCell].Buffer,WRITECELLSIZE);
  507.                 PBWrite((ParamBlockRec*)&(SerialPort->WriteCellArray[ChosenWriteCell]),
  508.                     True/*async*/);
  509.                 Length -= WRITECELLSIZE;
  510.                 Data += WRITECELLSIZE;
  511.                 goto ReEntryPoint;
  512.             }
  513.          else
  514.             {
  515.                 SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioReqCount = Length;
  516.                 SerialPort->WriteCellArray[ChosenWriteCell].NumBytes = Length;
  517.                 CopyData(Data,SerialPort->WriteCellArray[ChosenWriteCell].Buffer,Length);
  518.                 PBWrite((ParamBlockRec*)&(SerialPort->WriteCellArray[ChosenWriteCell]),
  519.                     True/*async*/);
  520.             }
  521.         if (TickCount() - StartTime > 1)
  522.             {
  523.                 APRINT(("WriteBlock took %l ticks",(long)(TickCount() - StartTime)));
  524.             }
  525.  
  526.         /* all done */
  527.         return True;
  528.     }
  529.  
  530.  
  531. /* the callback routine must NOT be profiled */
  532. #ifdef THINK_C
  533.     #if __option(profile)
  534.         #define ProfilingEnabled (True)
  535.     #else
  536.         #define ProfilingEnabled (False)
  537.     #endif
  538.  
  539.     #pragma options(!profile)
  540. #endif
  541.  
  542. static pascal void    Callback(void)
  543.     {
  544. #ifndef __cplusplus
  545.         register WriteCell*        HiddenParameter;
  546.  
  547.         asm{move.l A0,HiddenParameter}
  548.         HiddenParameter->InUseFlag = 0;
  549.         HiddenParameter->NumBytes = 0;
  550. #else
  551.         /* 00000000: 2F0C               MOVE.L    A4,-(A7) */
  552.         /* 00000002: 2848               MOVEA.L   A0,A4 */
  553.         /* 00000004: 426C 01D0          CLR.W     $01D0(A4) */
  554.         /* 00000008: 42AC 01D2          CLR.L     $01D2(A4) */
  555.         /* 0000000C: 285F               MOVEA.L   (A7)+,A4 */
  556.         asm(0x2f0c,0x2848,0x426c,0x01d0,0x42ac,0x01d2,0x285f);
  557. #endif
  558.     }
  559.  
  560. #ifdef THINK_C
  561.     #if ProfilingEnabled
  562.         #pragma options(profile)
  563.     #endif
  564. #endif
  565.