

  Thunk Compiler Manual 11-01-1989 13:37:42


! 1.  Special Notes

!     The  following  items  have changed  since the  last  release  of  this
!     document.

!     *   It is now possible to delete individual structure elements.

!         See the type definition section for details.

!     *   Allow Lists

!         A  new  feature has been added  which allows the user to  specify a
!         list  of long values that can be truncated  to short value  without
!         causing an error. See semantic section for details.

!     *   Restrict Lists

!         This new feature will allow you to restrict the  values that may be
!         passed to an API. See semantic section for details.

!     *   Setting error codes.

!         You  can  now  set  the error codes  for  certain  conditions.  See
!         compiler directive and semantic sections for details.  section  for
!         details.

!     *   Value truncation

!         The thunks will determine when data is  going  to be truncated, and
!         return and error if  this happens.  See  the  programmers guide for
!         further details.

! 2.  Command Line Options

!     To invoke the thunk compiler, use the command:

!     thunk [{-|/}options] [-L xxxxx] <infile> [ <outfile> ]

!     where options include zero or more of the following flags:

!     B             INT  3 on entry/frame/call/exit.  Generates inline  INT 3
!                   instructions in all interesting places. Equivalent to -CE

!     c             INT 3 on call

!     C             INT 3 on frame/call

!     d             Debugging Output. The -d flag tells  the compiler to dump
!                   the  internal data tables. This is debugging output,  and
!                   is sent to  standard  error.  It  is  not intended to  be
!                   useful for anything but debugging the compiler itself.

!     D             Debugging  output to file  thunk.dmp.  Same  as d, except
!                   output is written to the file thunk.dmp

!     e             INT 3 on entry


  Page     1

!     E             INT 3 on entry/exit

!     f             INT 3 on frame generation

!     F             Force 1 byte of data into data segment

!     L nnnn        Start  label generation at nnnn.  Internal labels  in the
!                   compiler  are  generated numerically, such  as L101:, and
!                   normally  start  at  label L0:.  Labels  have  the  range
!                   0-65535. The label  generating  mechanism  will  wrap  if
!                   labels pass 65535.

!     O             Disable  the compaction of routines.  The thunk  compiler
!                   will  combine thunks that  have  identical  semantics and
!                   parameters  into  common code  groups. This  reduces  the
!                   amount of code by  reusing common subroutines.  Using the
!                   -O flag will disable this compaction.

!     p             The  -p  flag  changes the  compiler  default for  32-bit
!                   structure packing to WORD, instead of DWORD.  The default
!                   for the compiler is that  all  structures  are  packed so
!                   that DWORD  sized  or  larger data are  aligned on  DWORD
!                   boundaries.  The -p  flag insures  that DWORD objects are
!                   aligned on WORD boundaries.

!     s             Syntax check only. No output code is produced.

!     u             Prefix  a _  to all  32-bit  names.  This  is useful when
!                   creating a  C callable thunk library without using a .def
!                   file.

!     U             Disable  16-bit name  uppercasing. Default  is  that  all
!                   16-bit names are folded  to uppercase. This disables  the
!                   folding,  and  names assume the case used  in the  source
!                   file.

!     x             INT 3 on exit

!     y             Answer  'y' to  overwrite  file  question.  Normally, the
!                   thunk  compiler will  stop to ask permission to overwrite
!                   .asm files. The y flag overrides this query.

!     z             Disable  32-bit  name  uppercasing.  Default  is that all
!                   32-bit names are folded to  uppercase. This disables  the
!                   folding,  and names assume the case  used in  the  source
!                   file.

!     N<x> <name>   The  -N  switch  allows  the  user to  specify  names  of
!                   segments and classes, where <x> is one of

!                   A    32-bit code segment name

!                   B    32-bit code class name

!                   C    16-bit code segment name

!                   D    16-bit code class name

!                   E    32-bit data segment name


  Page     2

!                   F    32-bit data class name

!                   and <name> is the name to be used.

!     <infile> is the input description file.

!     <outfile>  is the MASM output file. This filename is optional. If it is
!     not specified, then the input filename will be used, with the extention
!     .ASM.

  3.  Introduction

          The Thunk Compiler  is a program  that will  generate  an interface
          layer between 32-bit and 16-bit modules under OS/2. It  will accept
          as input a description  langauge,  and  will  output assembler code
          suitable for compilation under MASM 5.1.

          The current implementation of the thunk compiler will only generate
          thunks in the 32 to 16 bit direction.

      *   Input Language

          The  thunk  compiler  input  langauge  is  modeled  after  the  'C'
          programming langauge. The syntax is very similar.  There  are three
          basic sections to a thunk description.

          a.  Delcarations

              Declarations are  used to declare  complex  types,  using basic
              data types, or previously declared data types. Declarations use
              the 'typedef' syntax of the 'C' langauge.

          b.  Mappings

              Mappings define  a relationship  between two APIs. Each mapping
              defines all needed  information  about the relationship between
              two  APIs,  including  names,  parameters,  return  types,  and
              semantic information about the parameters.

          c.  Map Directives

              Map directives are usually the  last  section of the program. A
              map directive causes a thunk to be generated for two APIs whose
              relationship was declared using a Mapping.

          The  thunk  input  language  is  case   sensitive.  Therefore,  the
          identifiers foo, Foo, and FOO are considered unique.

      *   Output File

          The  output file  generated  by the  thunk compiler is a  text file
          containing assembler source code. It can be compiled using MASM 5.1
          or later.

      *   Restrictions

          The thunk compiler does not handle the following constructs:

          -   Arrays of  pointers  or arrays of  data  objects  that  contain


  Page     3

              pointers.

          -   Arrays of arrays.

          -   Arrays of structures passed as parameters.

          Thunks  containing  such  constructs  will need  hand  modification
          before they will operate correctly.

  4.  Declarations

      Declarations are used to define  new data  types based on existing data
      types. There are several predefined data types.

      short               A 16 bit signed integer.

      long                A 32 bit signed integer.

      unsigned short      A 16 bit unsigned integer.

      unsigned long       A 32 bit unsigned integer.

      int                 Using  the int  type will tell the compiler to  use
                          which ever type is the  default for  the  API type.
                          Using  an  int in a 16 bit API will result in a  16
                          bit signed integer. Using an  int  in  a 32 bit API
                          will result in a 32 bit signed integer.

      unsigned int        Using the unsigned int type will  tell the compiler
                          to  use which ever type is  the default for the API
                          type.  Using an unsigned int in  a  16 bit API will
                          result in  a 16 bit unsigned integer.  Using an int
                          in  a  32 bit API will result in  a 32 bit unsigned
                          integer.

      string              A   pointer  to   a   null  terminated   string  of
                          characters. Must be prefaced by a pointer type.

      char                A single byte of  type  character. Most often  used
                          with a pointer to point to a data buffer.

      void                A  pointer  to  a  single  byte  with  no  semantic
                          information.  Most  often  used to point  to a data
                          buffer. Must be prefaced by a pointer type.

      nulltype            A nulltype  is used  as  a place  holder for thunks
                          that  will require special  hand  coding.  The  net
                          result  of using a nulltype  is that  whenever  the
                          nulltype  is referenced, the compiler will output a
                          line that will cause an error if the output file is
                          assembled (ie. .err NULLTYPE).

      All basic types can be prefaced  by  a  pointer  type. There are  three
      pointer types:

      far16     The  far16   keyword   denotes  that   the  data  item  is  a
                selector:offset  format pointer.  These pointers are  used in
                16-bit OS/2.



  Page     4

      near32    The near32 keyword  denotes that  the  data item is  a 32-bit
                flat address.  These pointers are used in the 32-bit OS/2.

      '*'       The 'star'  pointer  type  denotes that the  data item  is  a
                pointer, and should assume the pointer type native to the API
                in  which it is used (ie a star  pointer used in a API16 call
                would assume the pointer to be far16)

      Declarations come in two forms, and have the following syntax:

      *   'typedef'  <basic  type|declared  type>  [<pointer  decl>]  <ident>
          [ArrayDecl];

          This form  declares <ident> to have  type < basic type | previously
          declared type>.


  +-------------------------------------------------------------------------+
  |                                                                         |
  |        typedef unsigned short USHORT;                                   |
  |                                                                         |
  |        typedef USHORT MyShort;                                          |
  |                                                                         |
  |        typedef USHORT far16 PUSHORT;                                    |
  |                                                                         |
  |        typedef USHORT ShortArray[10];                                   |
  |                                                                         |
  |        typedef unsigned long near32 P32ULONG;                           |
  |                                                                         |
  |        typedef short *PSHORT;                                           |
  |                                                                         |
  |                                                                         |
  |        Figure 1. Examples of typedef statements                         |
  +-------------------------------------------------------------------------+

!     *   'typedef' [<alignment>] 'struct' <ident1> '{'
!                 {<  basic  type  |  previously  declared  type>  [<identn>]
!         [deleted [n]]; }
!                 '}' <ident2> ';'

!         Declares <ident2> to be a  structure  type with  a list of internal
!         fields.  Each internal field  declaration  is must  contain a known
!         type.  The  field  identifier  is  optional.  The   internal  field
!         identifier is only used by the compiler to generate comments in the
!         assembler file.

!         The <alignment> option declares the structure to be  aligned  in  a
!         predefined manner. The  alignment  option  is only  valid  when the
!         typedef  declares a  structure.  The syntax of the alignment option
!         is

!         <alignment keyword> [ aligned ]

!         The 'aligned' keyword is optional. Valid alignment keywords are:

!         byte      Structure fields are byte aligned

!         word      Structure fields  more  than 1  byte  in  length are word
!                   aligned (2 bytes)


  Page     5

!         dword     Structure  fields are dword aligned (4  bytes). All items
!                   greater  than or  equal  to  4  bytes in length  will  be
!                   aligned on a 4 byte boundary. All word sized data will be
!                   word aligned.

!         If no alignment keyword is defined,  then the compiler will  choose
!         alignment based on the  type of API it is used  in. For example, if
!         the alignment is undefined, and is being used in a 16-bit API, then
!         the alignment will default to being word aligned.  Likewise, use in
!         a 32-bit API defaults to dword alignment.

!         The deleted keyword can be used to modify a structure  element. The
!         deleted keyword tells the compiler that  this  element  is  a place
!         holder, and doesn't actually exist. This is useful when a structure
!         has  had  elements  added  to it,  and  needs  to  map  to the  old
!         structure.













































  Page     6


! +-------------------------------------------------------------------------+
! |                                                                         |
! |        typedef unsigned long ULONG;                                     |
! |                                                                         |
! |        typedef struct _PIDINFO {                                        |
! |                unsigned short PID;                                      |
! |                unsigned short TID;                                      |
! |                unsigned short PPID;                                     |
! |                } PIDINFO;                                               |
! |                                                                         |
! |        typedef PIDINFO *PPIDINFO;      /* A pointer to a PIDINFO */     |
! |                                                                         |
! |        typedef dword aligned struct _Data1 {                            |
! |                unsigned short;                                          |
! |                char FileName[13];                                       |
! |                unsigned long LongIdent;                                 |
! |                dword aligned PIDINFO PidIdent; /* Imbedded structure */ |
! |                } Data1;                                                 |
! |                                                                         |
! |        typedef word struct _Data2 {                                     |
! |                ULONG;                                                   |
! |                short;                                                   |
! |                } Data2;                                                 |
! |                                                                         |
! |        typedef struct _Data3 {                                          |
! |                string *NameString;     /* Imbedded pointer to ASCIIZ */ |
! |                Data2 *StructPointer;   /* Imbedded pointer to struct */ |
! |                } Data3;                                                 |
! |                                                                         |
! |        typedef struct _Data4 {                                          |
! |                unsigned short US1;                                      |
! |                unsigned short US2;                                      |
! |                unsigned long UL1 deleted;                               |
! |                unsigned long UL2 deleted 5;                             |
! |                unsigned short US3;                                      |
! |                } Data4;                                                 |
! |                                                                         |
! |        typedef struct _Data4b {                                         |
! |                unsigned short US1;                                      |
! |                unsigned short US2;                                      |
! |                unsigned long UL1;                                       |
! |                unsigned long UL2;                                       |
! |                unsigned short US3;                                      |
! |                } Data4b;                                                |
! |                                                                         |
! |                                                                         |
! |                                                                         |
! |        Figure 2. Examples of structure declarations:                    |
! +-------------------------------------------------------------------------+

!         Note the example  structures Data4 and Data4b. These two structures
!         can be mapped since they contain  compatible elements. However, the
!         compiler  will  assume  that  the  Data4  structure  only  contains
!         US1,US2, and US3. UL1  and UL2 are assumed not to exist. Using this
!         construct, we are actually mapping the following:





  Page     7


! +-------------------------------------------------------------------------+
! |                                                                         |
! |                                                                         |
! |        typedef struct _Data4 {                                          |
! |                unsigned short US1;                                      |
! |                unsigned short US2;                                      |
! |                unsigned short US3;                                      |
! |                } Data4;                                                 |
! |                                                                         |
! |        typedef struct _Data4b {                                         |
! |                unsigned short US1;                                      |
! |                unsigned short US2;                                      |
! |                unsigned long UL1;                                       |
! |                unsigned long UL2;                                       |
! |                unsigned short US3;                                      |
! |                } Data4b;                                                |
! |                                                                         |
! |                                                                         |
! |        Figure 3. Effective mapping using the deleted keyword            |
! +-------------------------------------------------------------------------+

!         When converting from Data4b to Data4, the elements  UL1 and UL2 are
!         not copied over. Thus, only the US1 US2 and US3 elements are copied
!         into the new structure.

!         When converting  from Data4 to Data4b, we need to create new values
!         for  the fields UL1 and UL2, since they didn't exist in Data4. This
!         is where the  value  following the deleted  keyword  is used. If no
!         value is specified, then the compiler will default to using zero as
!         the  fill  value.  Otherwise,  the  compiler will place  the  value
!         specified into the field.

!         The  fill value is only used  when creating a  new structure. There
!         are two cases where the value will used

          -   Structure  created  on input This would be  the case where  the
              caller passes in the smaller  structure, which needs conversion
              to  the larger structure. In the context  of the above example,
              the input is Data4, which is then converted  to Data4b. In this
              case, UL1 and UL2 would be filled in.

          -   Structure  created on output This would  be the case where  the
              caller  passes in  the larger structure, and expects the API to
              fill  it in. This case  is  determined  when the parameter  has
              output only semantics. If the parameter is output only, then no
              useful information is assumed to be in  the structure on input.
              Thus,  the   API  must  be  filling  the  structure  with  this
              information.  In  this   case,  the  thunk  will  complete  the
              structure by providing the default values.



          The following are examples of  structures  that  are NOT handled by
          the compiler.






  Page     8


  +-------------------------------------------------------------------------+
  |                                                                         |
  |        typedef struct _K {                                              |
  |                string *StrAray[10];    /* Arrays of pointers not support|d */
  |                } K;                                                     |
  |                                                                         |
  |                                                                         |
  |        typedef struct _D {                                              |
  |                string *StringPtr;      /* This one is ok */             |
  |                } D;                                                     |
  |                                                                         |
  |        typedef struct _M {                                              |
  |                D DArray[10];           /* Array of objects with pointers|*/
  |                } M;                                                     |
  |                                                                         |
  |                                                                         |
  |        Figure 4. Examples of illegal structure declarations             |
  +-------------------------------------------------------------------------+










































  Page     9

  5.  Mappings


  +-------------------------------------------------------------------------+
  |                                                                         |
  |    API16 unsigned short DosSleep(short,short) =                         |
  |    API32 unsigned long  Dos32Sleep(long,long)                           |
  |    {}                                                                   |
  |                                                                         |
  |                                                                         |
  |    Figure 5. A simple mapping statement.                                |
  +-------------------------------------------------------------------------+

      Mappings define  the  relationship  between  two APIs. Information from
      this  relationship  is  used to  generate  the  actual  thunk.  Mapping
      statements can become  quite complex. The  best way to explain mappings
      is by example.

      Figure --  is a simple form of a mapping. It  defines  DosSleep to be a
      16-bit API, which returns an unsigned  short, and  is passed two shorts
      as parameters.  It also defines API32 to be a 32-bit API, which returns
      an  unsigned long,  and  is passed  two  longs as parameters. The curly
      braces on the end are required, and will be explained later.

      The basic syntax of a mapping statement is:


      [<api type>] <return type> <ident> ( <param list> ) =
      [<api type>] <return type> <ident> ( <param list> )
      '{' <semantics> '}'

      <api type>     Defines which type of API this identifier will be.  Only
                     two values are accepted.

                     API16   Defines the API to be a 16-bit API

                     API32   Defines the API to be a 32-bit API

                     This declaration is optional. If the  <api type>  is not
                     declared, then the compiler will  assume that the  first
                     API in the  mapping is  API16,  and the second is API32.
                     It is not legal to tag only one  of  the API's.  If  you
                     declare one, then you must declare the other.

      <return type>  Defines  the  type returned by the API. This can be  any
                     previously declared type that maps to a basic data type.

      <ident>        Is a unique  identifier. Identifiers must start  with  a
                     letter,  and may be followed with any number of letters,
                     digits, or underscores.

      <param list>   Is a list of parameters that are passed  to  the  API. A
                     parameter can be modified with the 'deleted' keyword  to
                     indicate  that  the  parameter  has  been  removed.  See
                     examples below for details.

      <semantics>    Is  a  block  contain  semantic  information  about  the
                     parameters. Semantic blocks are described  in a later in
                     this section.


  Page    10

      An example of a parameter list could be:

      API16 short DosExample(short,char *buf,short len)

      A  few  of  interesting  points here. First is  that  parameters  in  a
      parameter list  do  not require an  identifier. The identifier, such as
      'buf', are optional.  They are useful when  an  API requires a semantic
      block.

      The second parameter, 'buf' is a  pointer to  a char. The  '*' declares
      this pointer as being a 16:16 pointer, since it is being declared in  a
      API16  mapping.The other  option  would  be  to  declare  it  as a 0:32
      pointer, by using the near32 keyword. A pointer keyword must be used to
      declare  items as pointers. Also, all structures  passed  as parameters
      are  required to be passed by reference,  and  therefore  must  have  a
      pointer type as their parameter. For example:

      typedef struct Killer {
              short P1;
              short P2;
              };

      API16 short DosExample(short, Killer far16) =
      API32 long  Dos32Example(long, Killer near32)
      {
      }

              or without the declaration of API type or pointer type

      short DosExample(short,Killer *) =
      long  Dos32Example(long,Killer *)
      {
      }

      In this example, the pointer  to  structure  Killer has  been  properly
      defined for both API types. They are both prefixed by the pointer type.

      Each mapping statement  may also contain a semantic block which defines
      additional semantic  information on  the parameters  being passed to an
      API.

  +-------------------------------------------------------------------------+
  |    short DosExample(short,char *buf,short len)=                         |
  |    long Dos32Example(short,char *buf,short len)                         |
  |    {                                                                    |
  |            buf = output;                                                |
  |            len = sizeof buf;                                            |
  |    }                                                                    |
  +-------------------------------------------------------------------------+

      In the above example  (DosExample() = Dos32Example) the first line, buf
      = output, defines  the parameter buf  to  be an  output parameter. This
      informs the compiler that if buf needs to be copied elsewhere in memory
      during the thunk, that the  copy may  be  discarded.  For  all  pointer
      parameters, if no semantics  are given to indicate whether the item  is
      input or output, then the compiler assumes that the item is input only,
      and will not copy the structure out.

      It also  defines  the parameter 'len' as  the  length  in bytes of buf.


  Page    11

      Other semantic operations are defined below.

      <ident1> = input;                  Defines parameter ident1 to be input
                                         only.

      <ident1> = output;                 Defines   parameter   ident1  to  be
                                         output only.

      <ident1> = inout;                  Defines parameter ident1 to be  both
                                         input and output.

      <ident1> = sizeof <ident2>;        Defines parameter ident1 to hold the
                                         length of ident2 in bytes.

      <ident1> = countof <ident2>;       Defines parameter ident1 to hold the
                                         count of  items that  ident2  points
                                         to. The actual size in bytes will be
                                         calculated by  multiplying ident1 by
                                         the size  of  the data type to which
                                         ident2 points.

      stack <api ident> = <number>;      This  operation  defines the minimum
                                         amount  of stack space required  for
                                         the  api  given.  The  minimum stack
                                         space  value  is  used  to determine
                                         when the stack may need to be bumped
                                         (See the thunk section of the design
                                         workbook).  It  is  only useful when
                                         generating a  32-->16  thunk. It  is
                                         normally  only  used for an  API  of
                                         type API16.

      inline = [ true | false ];         This  sets  a  flag  that  tells the
                                         compiler whether to favor  execution
                                         speed, or code  size.  Setting it to
                                         true will generate only inline code,
                                         which  will  result  in faster code,
                                         but  larger  size.  Setting   it  to
                                         false  will   result  in  subroutine
                                         calls where appropriate, thus slower
                                         code, and smaller size.

      <api ident> = conforming;          In the 16:16 -->  0:32 thunks, there
                                         are  times when thunk  code must  be
                                         able to  deal with ring 2 conforming
                                         0:32  code. The  conforming  keyword
                                         tells the compiler that the thunk to
                                         be   generated   should  produce   a
!                                        conforming compatible thunk.

!     <ident> = allow([value [,value]])  If ident is of type long or unsigned
!                                        long, and  is  to  be truncated to a
!                                        signed/unsigned  short  value,  then
!                                        the thunk  will  normally  check  to
!                                        insure that  the value  will  not be
!                                        truncated.  If the  value is outside
!                                        of  the  range  available  with  the
!                                        short value,  then  the  thunk  will
!                                        return   an   error.   The   allow()


  Page    12

!                                        semantic allows the specified values
!                                        to pass the truncation check without
!                                        error. The  value  will be truncated
!                                        to 16-bits, losing the high word.

!     <ident> = restrict([value [,value]])  The   restrict    semantic   will
!                                        restrict the  allowable values for a
!                                        parameter to only the  values in the
!                                        value  list.  This  is   useful  for
!                                        restricting a parameter to  be  only
!                                        0, or some other default value. If a
!                                        parameter has a  value  that doesn't
!                                        appear in the list,  then  the thunk
!                                        will return the errbadparam code.

!     errbadparam = <numeric>            This sets  the errbadparam value for
!                                        this mapping. The  value is set  for
!                                        the current mapping only.

!     errnomem    = <numeric>            This  sets the errnomemory value for
!                                        this  mapping.  The value is set for
!                                        the current mapping only.

!     errunknown = <numeric>             This  sets the errunknown  value for
!                                        this mapping. The  value  is set for
!                                        the current mapping only.

  6.  API with different parameter counts


      The thunk compiler requires that two function  prototypes have the same
      number of parameters in order to be mapped. However, if you need to add
      or remove parameters  from one  of the prototypes, then you can use the
      'deleted' keyword for that parameter.

      For example, DosChDir()  has a different  number  of parameters between
      its 32-bit version and its 16-bit version.


      USHORT DosChDir(PSZ pszDirPath,ULONG ulReserved);

      ULONG Dos32ChDir(PSZ pszDirPath);

      The thunk compiler will allow a mapping such as:

      USHORT DosChDir(PSZ pszDirPath,ULONG ulReserved) =

      ULONG Dos32ChDir(PSZ pszDirPath,ULONG ulReserved deleted 0 )

      {
      }


      There are  two  results  of this  mapping  declaration.  In  a  mapping
      directive of DosChDir  => Dos32ChDir, the ulReserved parameter will not
      be  pushed the  Dos32ChDir stack frame.  The effective  result  is only
      pszDirPath will be passed to the API.

      The other possibility is a mapping directive of Dos32ChDir => DosChDir.


  Page    13

      In this case, a parameter needs to be added to the call frame the place
      of  ulReserved. The size of the item pushed is specified  in the target
      (DosChDir), and  will be a ULONG. The value of the item  pushed  can be
      specified  by  the number following the deleted keyword.  In this case,
      the value is a ULONG = 0.

      In another  example, say  that  Dos32Beep  was  modified to play a song
      which is specified by a number. The mapping needs to look like

      DosBeep(USHORT usFrequency,USHORT usDuration) =
      Dos32Beep(ULONG ulFrequency,ULONG ulSongNum,ULONG ulDuration)
      {
      }

      In the case of DosBeep => Dos32Beep, we need to add  a parameter to the
      call. This is done, same as before, with:

      DosBeep(USHORT usFrequency,USHORT usSongNum deleted 7, USHORT usDuration) =
      Dos32Beep(ULONG ulFrequency,ULONG ulSongNum,ULONG ulDuration)
      {
      }

      where 'deleted 7'  will make the default song to be the theme from "The
      Flintstones".



  7.  Map Directives

      The mapping declarations  only defined a  relationship between two API.
      The third and  final section  to the thunk  description language simply
      defines which direction thunk should  be generated. A mapping directive
      has the form:

      <api ident1> => <api ident2>;

      This will result in a thunk FROM ident1  TO ident2. Mapping  directives
      only  work on a  previously  declared  mapping.  It is not  possible to
      create a mapping  directive for two  API that  are  not related to each
      other  by  a  mapping  declaration.  An  example  of a correct  mapping
      directive is

  +-------------------------------------------------------------------------+
  |                                                                         |
  |    DosRead => Dos32Read;                                                |
  |                                                                         |
  |    A correctly formed mapping directive                                 |
  +-------------------------------------------------------------------------+

      Assuming that DosRead is a 16:16 API, and Dos32Read is a 0:32 API, then
      the example map directive would produce a 16:16 --> 0:32 thunk.

  8.  Compiler Directives

      *   inline

          Syntax inline = < true | false >;

          The inline directive changes the current default  inline value. The


  Page    14

          inline  value  determines  whether code  is  generated  inline,  or
          whether subroutine  calls are allowed. The change will only  affect
          the mapping statements defined after this statement.

      *   #include

          Syntax:  #include "filename.ext"

          The  #include  directive   works  much   like   the   'C'  #include
          preprocessor  directive. Its sole purpose is to suspend  input from
          the  current source file, and direct input from an alternate source
          file. When the  end of  the alternate  source file  is  read, it is
          closed, and input resumes from the original source file.

          The syntax of  the #include statement only allows for  filenames to
          be enclosed in double quotes. The #include <filename.ext> form that
          'C' uses is not  defined.  The compiler does NOT search any of  the
          include paths. If the file to  be included  is not  in the  current
          directory, then a full pathname will be required.  The filename may
          be any legal filename accepted by fopen().

          Includes may nest many  levels  deep. The only  restriction is  the
          number of open files per process.

      *   stack

          Syntax:  stack = <n>;

          The  stack directive changes the current default minimum stack size
          to <n>, where <n>  is an integer  value 0  thru  32767.  The change
          will   only  affect  the  mapping  statements  defined  after  this
          statement.

      *   syscall

          Syntax:  syscall = < true | false >;

          The syscall  keyword  is  used  to  control the  calling convention
          assumptions  made by the caller. The syscall keyword indicates that
          the 16-bit target API follows the BASE calling convention of saving
          all registers and segment registers, with the exception of eAX.  If
          syscall  = false, then a  32->16 thunk will save the contents of ES
          before calling. If syscall =  true, then the compiler  assumes that
          the target  routine will save es.  Changing the syscall value  will
          only affect the mapping statements defined after this statement.

!     *   errbadparam

!         Syntax: errbadparam = <numeric>;

!         This sets the global default for the errbadparam return  code. This
!         code  is  returned  whenever  the  thunk layer  determines  that  a
!         parameter  will  be truncated,  or  is  not allowed by a restrict()
!         semantic. It  is also  used  for parameters  that  are 'sizeof'  or
!         'countof'  when the  resulting  size  is  greater than the API will
!         allow.

!     *   errnomem



  Page    15

!         Syntax: errnomem = <numeric>;

!         This sets the global  default for the errnomem return code. This
!         code is returned whenever  the  thunk layer cannot allocate  memory
!         from its block manager.

!     *   errunknown

!         Syntax: errunknown= <numeric>;

!         This sets the  global default for  the errunknown return code. This
!         code  is returned whenever the thunk layer has  an  error  returned
!         from a subsystem, such as Dos32CreateLinearAlias, or Dos32AllocMem.
!         If no errunknown  value  gets  set,  then the thunk will return the
!         error code from the subsystem.

      *   Comments

          Syntax:  /* <comment text> */

          Comments in  the thunk description language  are similar to the 'C'
          programming  langauge.   A  comment  block  is  opened  by  a  '/*'
          combination,  and  closed  by  a '*/' combination. Unlike  'C', the
          thunk language will allow nesting of comments.





































  Page    16

  9.  Programmers Guide


      This  section  will discuss  issues related to  the  writing  of  thunk
      scripts.  It  is  advised  that a programmer read  this  section BEFORE
      writing complex thunk scripts.

      a.  Numeric Constants

          The  thunk  compiler  recognizes  numeric  constants,  and constant
          expressions involving operators  in the set ( + - * /). All numeric
          constants are assumed to be integer values. Constants are only used
!         in array declarations, and in setting the size of the stack.

!         The  thunk  compiler  will also accept  hex  numbers,  if they  are
!         specified in the standard 'C' format (ie 0xffff).

      b.  Using the 'C' preprocessor with the thunk compiler.

          One potentially  useful  trick is  to use the  C  preprocessor on a
          script  file, before feeding it to the thunk compiler. This  allows
          the programmer  to use the  standard  C #  macros, such as #define,
          #ifdef, #include, etc.  Using  the preprocesser like  this is a bit
          of a hack, but it should work.

          To do this,  run the thunk script through the standard Microsoft  C
          compiler,  using the /EP switch.  This will  tell the C compiler to
          process the  input  file,  doing  string  replaces  on  all  of the
          #defines, and will handle all of the macros. Pipe this output  to a
          temporary file, and then feed this to the thunk compiler.

          For example
                  c:>cl /EP thkfile.thk > temp.thk
                  c:>thunk temp.thk

      c.  Data Translations


          The  compiler is capable  of translating  between  long  and  short
          types. The following table shows which translations are supported:

          short <-> long
          unsigned short <-> unsigned long



          Note that it is not possible to translate semantics interpretations
          of the data (ie unsigned to signed).  This  type of  translation is
          meaningless, and the compiler will produce an error  message if you
          attempt this.


          The   int  type  is  handled  slightly  differently.  The  compiler
          translates the int or unsigned  int data type into the type that is
          native to the API in which it is used.


          For 16:16 API



  Page    17

                  int -> short
                  unsigned int -> unsigned short

          For 0:32 API

                  int -> long
                  unsigned int -> unsigned long

          This allows a value to  be  used in both API types,  and it will be
          converted  based  on  which  API it  is used in. This is especially
          useful when a typedef is used to declare a type that must  be  used
          in both worlds, but assumes a different size. For example,


  +-------------------------------------------------------------------------+
  |                                                                         |
  |        typedef unsigned int BOOL;                                       |
  |                                                                         |
  |        BOOL MyExample(BOOL *,string *,short) =                          |
  |        BOOL MyExample(BOOL *,string *,long)                             |
  |        {}                                                               |
  |                                                                         |
  |        is exactly equivalent to saying                                  |
  |                                                                         |
  |        unsigned short MyExample(unsigned short *,string *,short) =      |
  |        unsigned long  MyExample(unsigned long  *,string *,long)         |
  |        {}                                                               |
  +-------------------------------------------------------------------------+


      d.  Passing Pointer Parameters


          The  thunk  compiler will  handle  the  conversion and  passing  of
          pointer parameters. Pointer  parameters can  point  to any  of  the
          predefined data types,  or to  structures.  The  compiler  does not
          support double indirect pointers (pointers to  pointers), but there
          is a workaround for this which is describe later.

          If a pointer  parameter points to a base  data  type  (short, long,
          etc), then the compiler will handle correct


          If a pointer is passed between API, and the data types are  exactly
          the same, then the  thunk compiler treats  the data as  a block  of
          bytes, and will emit code that does not deal  with data types.  The
          code in the 0:32  --> 16:16 direction  checks the block of bytes to
          determine if it crosses a 64k boundary.  If it does, then action to
          correct the problem is taken. For example:












  Page    18


  +-------------------------------------------------------------------------+
  |                                                                         |
  |        typedef struct _K {                                              |
  |                short ShortVal;                                          |
  |                char  CharVal;                                           |
  |                } K;                                                     |
  |                                                                         |
  |        short DosExample(K *ptrK) =                                      |
  |        long Dos32Example(K *ptrK)                                       |
  |        {                                                                |
  |        }                                                                |
  |                                                                         |
  |        Dos32Example => DosExample;                                      |
  +-------------------------------------------------------------------------+


          In the above example, the structure  K  will  require no changes in
          packing, since the alignment is the same in  both the 32 bit and 16
          bit  API.  In  this case, the pointer  to  K can  be  treated  as a
          pointer  to  sizeof(K) bytes of  data. The thunk code for this will
          check to insure that the data buffer does not cross a 64k boundary.
          If it does, then a copy  of  the  data will  be made,  and the  new
          pointer  passed on  to the  target API. If it  doesn't  cross a 64k
          boundary, then the original pointer will be passed.


          If the pointer is to different types (ie SHORT to  LONG), or if the
          pointer is to a structure with differences in any of the data types
          (packing or  different  pointer types), then a new copy of the data
          is  made elsewhere in  memory,  and a  pointer to the  new  copy is
          passed to the target API. For example:


  +-------------------------------------------------------------------------+
  |        typedef struct _K {                                              |
  |                short ShortVal;                                          |
  |                long  LongVal;                                           |
  |                } K;                                                     |
  |                                                                         |
  |        short DosExample(K *ptrK) =                                      |
  |        long Dos32Example(K *ptrK)                                       |
  |        {                                                                |
  |        }                                                                |
  |                                                                         |
  |        Dos32Example => DosExample;                                      |
  |                                                                         |
  |                                                                         |
  |        0:32                    16:16                                    |
  |        struct K                struct K                                 |
  |        +--------+ 0            +--------+ 0                             |
  |        |ShortVal|              |ShortVal|                               |
  |        +--------+ 2            +--------+ 2                             |
  |        |Padding |              |LongVal |                               |
  |        +--------+ 4            +--------+ 4                             |
  |        |LongVal |              | "  "   |                               |
  |        +--------+ 6            +--------+ 6                             |
  |        | "  "   |                                                       |
  |        +--------+ 8                                                     |


  Page    19

  +-------------------------------------------------------------------------+

          In  this second example, struct  K  has different packing and  size
          between the API. Here, we must  convert K into the form expected by
          DosExample.  In  the  32  bit  version, K is  8  bytes  long,  with
          ShortVal starting at offset 0, and LongVal at offset 4.


          Memory  is allocated somewhere (probably the stack on such a  small
          item), and the 32 bit  version of K is  copied field by  field into
          the  16  bit version This creates a  16:16 equivalent. The call  is
          then made passing a pointer to the new 16:16 copy of K. When the 16
          bit call  returns,  and  if the  struct was  declared as  an output
          parameter in the semantic section, the 16:16  structure K  will  be
          copied field by field  back into the original.  In either case, the
          allocated memory is deallocated, and the routine returns.


          Another case that is similar to the different packing case is  when
          a structure contains an imbedded pointer. For example:



  +-------------------------------------------------------------------------+
  |        typedef struct _K {                                              |
  |                short ShortVal;                                          |
  |                string *StrVal;         /** Imbedded Pointer **/         |
  |                } K;                                                     |
  |                                                                         |
  |        short DosExample(K *ptrK) =                                      |
  |        long Dos32Example(K *ptrK)                                       |
  |        {                                                                |
  |        }                                                                |
  |                                                                         |
  |        Dos32Example => DosExample;                                      |
  |                                                                         |
  |        0:32 stack                                                       |
  |        +-------+                                                        |
  |        |       |                                                        |
  |        +-------+                                                        |
  |        |       |               0:32 K                                   |
  |        +-------+               +--------+0                              |
  |        | *ptrK |-------------> |ShortVal|              ASCIIZ           |
  |        +-------+               +--------+4             +---------------+|
  |        | EIP   |               |*StrVal |------------->|A|B|C|C|D|E|F|0||
  |        +-------+               +--------+              +---------------+|
  |        | EBP   |                                                        |
  |        +-------+                                                        |
  +-------------------------------------------------------------------------+


          In  this case, the  struct  K  has a  pointer to a null  terminated
          string imbedded inside. This means that the pointer will have to be
          changed to a  new value (0:32 --> 16:16).  We make a copy just like
          the previous  case, but now  we  need  to  deal  with the  imbedded
          pointer.


          The object that the imbedded pointer points to must also be checked


  Page    20

          for 64k crossings. It will be handled exactly like any other buffer
          that potentially crosses  a 64k boundary. (ie  check for  crossing,
          copy if needed).


          The  call to  the 16 bit routine is then made. On return  from  the
          16bit call,  and if the parameters semantics  specify  output, then
          the structure is copied back to the original location.


          NOTE: The following paragraph is subject to change

          ***************************************************************

          There is one  very  important  exception  during the  copy out. The
          pointer parameter IS NOT copied out.  This is done  because of many
          problems  that  could   arise   if   the  output  pointer  changes.
          Structures which  contain pointers that are for copy  out may  need
          hand modifications. The programmer must watch out for side effects,
          such as what  happened to the original pointer? Was it aliased? Was
          its memory freed? The current  version of the thunk compiler is not
          equipped to handle these questions. There are no problems  when the
          pointer is for copy in only.

          ***************************************************************


      e.  The NULLTYPE parameter

          In those  cases  where  the thunk compiler will not produce correct
          code, either  due  to very complex  semantics, or due to data types
          not handled, it may be useful to have the thunk compiler do as much
          of the  thunk  as  possible,  to  limit the  amount  of hand coding
          needed. This  is  where  the  basic data type 'nulltype'  comes  in
          handy.

          Nulltype  parameters  are  'place  holders'.  No  code is emited to
          handle the nulltype parameter.  The  only code emitted is an  error
          message to MASM  that will cause an error if compiled. This  is  to
          insure  that  the programmer  goes  into  the output  file and hand
          modifies that section of code with the NULL type.

          Declaring a pointer to a nulltype will  result in temporary storage
          being  allocated for the nulltype, and some skeleton code that gets
          the pointer from the stack, and checks it for null value.  The rest
          of the conversion for this parameter is left to the programmer.

      f.  Using semantic operators

          *   Specifying  input/output/inout The default semantic  value  for
              all parameters is 'input'. This means that if no other semantic
              information is given, the compiler will assume that a parameter
              is input only, and the data item will not be copied out.

              If a parameter  is an output type,  such as a read buffer, or a
              returned count, then the  parameter must be declared  as output
              in the semantic block. For example,




  Page    21


  +-------------------------------------------------------------------------+
  |                                                                         |
  |            short DosFoo(short Flags, void *Buffer, short len) =         |
  |            long Dos32Foo(long Flags, void *Buffer, long len)            |
  |            {                                                            |
  |                    Buffer = output;                                     |
  |                    len = sizeof Buffer;                                 |
  |            }                                                            |
  |                                                                         |
  +-------------------------------------------------------------------------+


              In  this example,  Buffer  is  declared to  be  an  output only
              buffer. It is then assumed that the input buffer has no useable
              information, and that it doesn't need to be  copied in. This is
              significant in the case  where Buffer  crosses  a 64k boundary,
              and  must be copied elsewhere.  When the semantics specify only
              output,  a  buffer   will  be  allocated  in  memory,   but  no
              information will be copied into the new buffer. However, on the
              return from the call, the information from the allocated buffer
              is copied back into the original buffer.


              If  a parameter  is bi-directional, then it will be both copied
              in and  copied out. To  specify  bi-directional parameters, use
              the 'inout' semantic keyword. For example,


  +-------------------------------------------------------------------------+
  |                                                                         |
  |            short DosFoo(short Flags, void *Buffer, short *len) =        |
  |            long Dos32Foo(long Flags, void *Buffer, long *len)           |
  |            {                                                            |
  |                    Buffer = output;                                     |
  |                    len = sizeof Buffer;                                 |
  |                    len = inout;                                         |
  |            }                                                            |
  |                                                                         |
  +-------------------------------------------------------------------------+

              In  this example, the parameter  'len' may represent the length
              of  the  buffer  pointed to  by 'Buffer',  and will receive the
              actual number  of bytes placed in 'Buffer'  by DosFoo. In  this
              case, we need to insure that the contents of 'len' are not lost
              on output.

              In the special  case  of  'string'  parameters (NULL terminated
              strings), the only valid semantic that can be applied is input.
              If  you attempt to assign an  output,  or  inout parameter to a
              string, then the compiler will give you an error message.

          *   Specifying parameter sizes

              Pointer parameters  will  assume that  the size  of the  object
              pointed to is the same as  the size of the object. For example,
              a pointer  to  a  long will be assumed to be  a pointer to  a 4
              bytes buffer. This can be overridden  in cases where there is a
              pointer to a buffer. For example,


  Page    22



  +-------------------------------------------------------------------------+
  |                                                                         |
  |            short DosFoo(char *Buffer, short len) =                      |
  |            long Dos32Foo(char *Buffer, long len)                        |
  |            {                                                            |
  |                    Buffer = output;                                     |
  |                    len = sizeof Buffer;                                 |
  |                                                                         |
  |            }                                                            |
  |                                                                         |
  +-------------------------------------------------------------------------+


              In this example, 'len' has been  defined to hold the  number of
              bytes  pointed to by 'Buffer'. Often, a size  parameter holds a
              count of items  rather  than the  size in  bytes of the buffer.
              This is handled by the countof semantic. For example,


  +-------------------------------------------------------------------------+
  |                                                                         |
  |            short DosFoo(long *Buffer, short len) =                      |
  |            long Dos32Foo(long *Buffer, long len)                        |
  |            {                                                            |
  |                    Buffer = output;                                     |
  |                    len = countof Buffer;                                |
  |                                                                         |
  |            }                                                            |
  |                                                                         |
  +-------------------------------------------------------------------------+


              In  this  example,  'len'  represents the  number of longs that
              'Buffer' points to. The thunk will then calculate the number of
              bytes  in 'Buffer' by multiplying  len * sizeof(long).  In this
              case, if len  = 4, then the compiler would  deduce that  Buffer
              was 16 bytes long.




      g.  Polymorphic Parameters


          One  area  that  the thunk compiler does not handle is  the area of
          polymorphic  parameters.  These  are pointer parameters that assume
          different characteristics based  on  some key value.  For  example,
          DosDevIOCtl is a routine which has a polymorphic pointer parameter.
          Based  on a flag value passed along with the function,  the pointer
          can be pointing to one of at least 50 different structures. In this
          case, it is not feasible for the thunk compiler to generate a thunk
          to handle all cases.


          Other forms of polymorphic parameters are more subtle. For example,




  Page    23


  +-------------------------------------------------------------------------+
  |        short DosFoo(short Flags, void *Buffer) =                        |
  |        long Dos32Foo(long Flags, void *Buffer)                          |
  |        {}                                                               |
  |                                                                         |
  |        The semantics of this call specify that if Flags == 3, then      |
  |        Buffer is to be disregarded.                                     |
  +-------------------------------------------------------------------------+

          In  this  example,  if  Flags is  3,  then  Buffer  is  an  invalid
          parameter, and should not  be used. In this  case,  Buffer  assumes
          different  semantics based a another value in the  parameter  list.
          The thunk compiler doesn't know  how to handle  this  case, and the
          programmer will have to hand  modify the output code to  deal  with
          this.

          The modifications for polymorphic parameters  can  range from being
          very simple, or to being very complex. Careful planning is advised,
          as is a very clear understanding of the API.

      h.  Structuring of the script files

          The  script language was  designed  to  be  crafted  in  a  certain
          structure, to make maintaining the files easy.  As a guideline  for
          writing the scripts, the following format is suggested.

          Scripts should be divided into three basic sections,

          1)  Type definitions (typedefs)

              Move  all of  the typedef  statements into a single file, which
              can  be  included  into  files  as  needed  using  the #include
              directive.

          2)  Mapping declarations

              Mapping declarations should be  grouped  according  to the .DLL
              file  in  which  they  reside.  Mapping declarations  should be
              divided into two files.

              *   Thunks which are generated automatically

              *   Thunks which require any type of hand modification

              Following this guideline, each .DLL  file  will  have  two .def
              files associated with it.

          3)  Mapping Directives

              Mapping Directives should reside in  the  same  file  as  their
              associated  mapping declarations. Mapping  directives should be
              placed at the end of the file, so they can easily be modified.


      i.  Hand coding thunks

          Some thunks will have to be hand coded. These are thunks which pass
          data  types  that  the  compiler  cannot  handle, have  polymorphic


  Page    24

          parameters, or some other feature that the compiler doesn't handle.
          If at all possible,  it is suggested that  the  compiler be used to
          generate a  base  thunk that can  be  modified by hand. This should
          save  the programmer from doing most of the work, and  should speed
          development time.

      j.  Using the inline Flag

          Setting  the inline flag to  true  can increase the speed  at which
          thunk code is  executed, but it also increases the code size. There
          is a definite time-space tradeoff when using  the inline flag. Here
          are a few guidelines to using this flag.

          *   Consider the amount of work to be done

              If an API is known to be slow, such as an API that accesses the
              disk, waits for an event, or  does an incredible amount of work
              such  as  BitBlit, then setting the inline flag may be  a  moot
              point. The time saved getting through  the thunk layer in these
              cases is very insignificant  when  compared to the execution of
              the API.

          *   Consider the frequency of calls

              If an API is only called once during the run of an application,
              such as  DosGetPid,  or  DosExit,  speed  probably  isn't  very
              important. However, if an  API is a very frequently called one,
              such as  WinGetMsg, you will want to make the thunk as  fast as
              possible. WinGetMsg is a case where we definitely want to favor
              speed over size, since it is  usually  called in a  very  tight
              loop.

          For the majority of thunks, we want to favor small size over speed,
          so you should leave the inline flag set to false.

      k.  Using the stack flag


          When a thunk in the 0:32  --> 16:16 direction is generated, a check
          is made to  determine  if  the 32  bit  APP  has enough stack space
          before  the  next  64k  boundary to complete  the  call.  The  size
          considered 'enough'  for  a  call  can be  set  using  the  'stack'
          semantic.  If  an API is known to  use a great deal of stack space,
          then the script can  modify  the amount of  stack to  allow for the
          particular API.  This value is only used in a 0:32 --> 16:16 thunk,
          and is based on the amount of space needed by the 16:16 routine. If
          the stack size is issued for a 0:32 API, it is ignored.

      l.  Using the conforming flag

          The thunk layer needs to  know  when  to deal with conforming code.
          This is  code that  can be called from either ring 3 or ring 2. The
          conforming keyword  is  used  in 16:16 -->  0:32 direction thunk to
          enable the thunk to call the 0:32 ring 2 conforming code  directly.
          If  a routine must be conforming,  then  you  must tell  the  thunk
!         compiler by using this statement.

!     m.  Value truncation



  Page    25

!         Values  that  are being converted  from a long (32-bit) type  to  a
!         short (16-bit) type are checked for truncation during runtime. If a
!         value is  too large to fit into a 16-bit type (ie > 0xffff unsigned
!         or outside the range -32768 thru 32767), the thunk will return with
!         an error.  The error  code  returned is ERROR_INVALID_PARAMETER, or
!         what ever the errbadparam value has been set too. To allow  certain
!         parameters  to truncated, see the  allow() semantic in the semantic
!         section.

      n.  Subroutine libraries

          The thunk compiler uses several subroutines  in an effort to reduce
          the code  size.  These  subroutines  are  integral  with  the  code
          produced by the caller, and are not useful for any other purpose.

          Two of these subroutines, which handle the  block allocator for the
          thunk compiler, are located in Doscall1.dll, and are exported API's
          from that .DLL. The calls  are THK32ALLOCBLOCK  and THK32FREEBLOCK.
          They allocate and  deallocate 128 byte blocks. The memory space  is
          per process, and  the  allocation  routines are guarded by a simple
          semaphore to insure mutual exclusion between threads.

          The rest of the library  routines  are  found in thunkrt.lib, which
          can be found in  the LIB directory of  the build tree. This library
          contains  several  routines that  are needed  by the output of  the
          thunk compiler.



































  Page    26

  10. Reference

      a.  Thunk description example


























































  Page    27


          /*** Example of the thunk description language ***/

          typedef unsigned short USHORT;
          typedef unsigned long ULONG;
          typedef unsigned int UINT;

          typedef struct _PIDINFO {
                  USHORT PID;
                  USHORT TID;
                  USHORT PPID;
                  } PIDINFO;

          typedef PIDINFO *PPIDINFO;      /* Define PPIDINFO to be a pointer type */

          typedef struct _Example {
                  USHORT P1;
                  char    FileName[13];   /* An array of 13 characters imbedded */
                  PIDINFO ExampleStruct;  /* A structure can be statically imbedded */
                                          /* A pointer to a structure will need hand */
                                          /* modifications */
                  } Example;

          /** The following defines the mapping between DosBeep and Dos32Beep **/
          /** DosBeep is first in the mapping, and therefore is assumed to be **/
          /** the 16:16 routine. Dos32Beep is second in the **/
          /** Also, the UINT in DosBeep is considered to be an unsigned short **/
          /** while the UINT in Dos32Beep is an unsigned long **/

          USHORT DosBeep(USHORT,UINT) =
          ULONG Dos32Beep(ULONG,UINT)
          {}

          /** This mapping passes a structure. Note that the structure must **/
          /** be passed by a pointer type. **/

          USHORT DosGetPid(PPIDINFO) =
          ULONG Dos32GetPid(PPIDINFO)
          {
                  PPIDINFO = output;      /* Define as an output parameter */
          }

          /** Note that by using the * to denote pointers, the pointer types are */
          /** implicitly defined based on the API type. */

          USHORT DosRead(USHORT,void *buf,USHORT len,USHORT *bytesread) =
          ULONG Dos32Read(ULONG,void *,ULONG,ULONG *)
          {
                  buf = output;           /** DosRead's buffer needs to be copied out*/
                  len = sizeof buf;       /** len is # of bytes in buf */
                  bytesread = inout;      /** bytesread is passed in and out */
          }



          /** Mapping Directives **/

          DosBeep => Dos32Beep;           /* 16 -> 32 */
          Dos32Read => DosRead;           /* 32 -> 16 */


  Page    28






























































  Page    29