/*
 * $Id$
 */

/*
 * Harbour Project source code:
 * Compiler Windows/DOS OBJ32 generation
 *
 * Copyright 1999 Antonio Linares <alinares@fivetech.com>
 * www - http://www.harbour-project.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
 * their web site at http://www.gnu.org/).
 *
 */

#include "hbcomp.h"

static ULONG GetSymbolsSize( HB_COMP_DECL );
static PCOMSYMBOL GetFirstSymbol( HB_COMP_DECL );
static char * GetSymbolName( HB_COMP_DECL, ULONG ulPos );
static ULONG GetPCodesSize( HB_COMP_DECL );
static ULONG GetSymbolsAmount( HB_COMP_DECL );
static BOOL IsExternal( HB_COMP_DECL, ULONG ulSymbol );
static USHORT GetExternalPos( char * szExternal );
static void GenerateLocalNames( FILE * hObjFile );
static void GenerateSymbolsSegment( HB_COMP_DECL, FILE * hObjFile );
static void GenerateDataSegment( HB_COMP_DECL, FILE * hObjFile );
static void GenerateCodeSegment( HB_COMP_DECL, FILE * hObjFile );
static void GenerateExternals( HB_COMP_DECL, FILE * hObjFile );
static void putbyte( BYTE b, FILE * hObjFile, BYTE * pbChecksum );
static void putword( USHORT w, FILE * hObjFile, BYTE * pbChecksum );
static void CompiledFileName( FILE * hObjFile, char * szFileName );
static void CompilerVersion( FILE * hObjFile, char * szVersion );
static void LocalNames( FILE * hObjFile, char * szNames[] );
static void ExternalNames( FILE * hObjFile, char * szNames[] );
static void CodeSegment( HB_COMP_DECL, FILE * hObjFile, BYTE * prgCode, ULONG ulPrgLen, USHORT wFunctions );
static void DataSegment( HB_COMP_DECL, FILE * hObjFile, BYTE * symbol, ULONG wSymLen, ULONG wSymbols, ULONG ulTotalSize );
static void DefineSegment( FILE * hObjFile, BYTE bName, BYTE bClass, USHORT wLen );
static void PubDef( FILE * hObjFile, char * szName, USHORT wSegment, USHORT wOffset );
static void Fixup( FILE * hObjFile, BYTE bType, USHORT wOffset, BYTE bFlags, BYTE bSymbol );
static void EnumeratedData( FILE * hObjFile, BYTE bSegment, BYTE * pData, USHORT wLen, USHORT wOffset );
static void End( FILE * hObjFile );
static void GroupDef( FILE * hObjFile, BYTE bName, BYTE * aSegs );

static BYTE prgFunction[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
                              0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x08, 0xC3 };

static char * * externNames = NULL;
static USHORT wExternals = 1; /* _hb_vmExecute is always added */
static const char * szPrefix = "_HB_FUN_";

void hb_compGenObj32( HB_COMP_DECL, PHB_FNAME pFileName )
{
   char szFileName[ _POSIX_PATH_MAX + 1 ];
   FILE * hObjFile;  /* file handle for OBJ output */
   char * szVer;

   if( ! pFileName->szExtension )
      pFileName->szExtension = ".obj";
   hb_fsFNameMerge( szFileName, pFileName );

   if( ( hObjFile = hb_fopen( szFileName, "wb" ) ) == NULL )
   {
      hb_compGenError( HB_COMP_PARAM, hb_comp_szErrors, 'E', HB_COMP_ERR_CREATE_OUTPUT, szFileName, NULL );
      return;
   }

   if( ! HB_COMP_PARAM->fQuiet )
   {
      char buffer[ 80 + _POSIX_PATH_MAX ];
      snprintf( buffer, sizeof( buffer ),
                "Generating Windows/DOS OBJ32 output to \'%s\'... ", szFileName );
      hb_compOutStd( HB_COMP_PARAM, buffer );
   }

   CompiledFileName( hObjFile, szFileName );
   szVer = hb_verHarbour();
   CompilerVersion( hObjFile, szVer );
   hb_xfree( szVer );
   GenerateLocalNames( hObjFile );
   GenerateExternals( HB_COMP_PARAM, hObjFile );
   GenerateCodeSegment( HB_COMP_PARAM, hObjFile );
   GenerateDataSegment( HB_COMP_PARAM, hObjFile );
   GenerateSymbolsSegment( HB_COMP_PARAM, hObjFile );
   End( hObjFile );
   if( externNames )
   {
      hb_xfree( externNames );
      externNames = NULL;
   }

   fclose( hObjFile );

   if( ! HB_COMP_PARAM->fQuiet )
      hb_compOutStd( HB_COMP_PARAM, "Done.\n" );
}

static USHORT hb_compFunctionGetPos( HB_COMP_DECL, char * szFunctionName ) /* return 0 if not found or order + 1 */
{
   PFUNCTION pFunc = HB_COMP_PARAM->functions.pFirst;
   USHORT wFunction = HB_COMP_PARAM->fStartProc ? 1 : 0;

   while( pFunc )
   {
      if( ! strcmp( pFunc->szName, szFunctionName ) && pFunc != HB_COMP_PARAM->functions.pFirst )
         return wFunction;
      wFunction++;
      pFunc = pFunc->pNext;
   }
   return 0;
}

static ULONG GetSymbolsSize( HB_COMP_DECL )
{
  return HB_COMP_PARAM->symbols.iCount * sizeof( HB_SYMB );
}

static PCOMSYMBOL GetFirstSymbol( HB_COMP_DECL )
{
  PCOMSYMBOL pSymbol = HB_COMP_PARAM->symbols.pFirst;
  return pSymbol;
}

static char * GetSymbolName( HB_COMP_DECL, ULONG ulPos )
{
  PCOMSYMBOL pSymbol = GetFirstSymbol( HB_COMP_PARAM );
  ULONG ul = 0;

  while( pSymbol && ( ul++ < ulPos ) )
    pSymbol = pSymbol->pNext;

  return pSymbol->szName;
}

static ULONG GetPCodesSize( HB_COMP_DECL )
{
  ULONG ulTotal = 0;
  PFUNCTION pFunction = HB_COMP_PARAM->functions.pFirst;

  if( ! HB_COMP_PARAM->fStartProc )
    pFunction = pFunction->pNext;

  while( pFunction )
    {
      ulTotal += pFunction->lPCodePos;
      pFunction = pFunction->pNext;
    }
  return ulTotal;
}

static ULONG GetSymbolsAmount( HB_COMP_DECL )
{
  PCOMSYMBOL pSymbol = GetFirstSymbol( HB_COMP_PARAM );
  ULONG ulAmount = 1;

  while( pSymbol->pNext )
    {
      ulAmount++;
      pSymbol = pSymbol->pNext;
    }
  return ulAmount;
}

static BOOL IsExternal( HB_COMP_DECL, ULONG ulSymbol )
{
  PCOMSYMBOL pSymbol = GetFirstSymbol( HB_COMP_PARAM );
  ULONG ul = 0;

  while( ul++ < ulSymbol )
    pSymbol = pSymbol->pNext;

  return ! hb_compFunctionFind( HB_COMP_PARAM, pSymbol->szName );
}

static USHORT GetExternalPos( char * szExternal )
{
  USHORT w = 0;

  while( w < wExternals )
    {
      if( ! strcmp( szExternal, externNames[ w ] ) )
        break;
      w++;
    }

  return w;
}

static void GenerateLocalNames( FILE * hObjFile )
{
  char * localNames[] = { "_TEXT", "CODE",
                          "_NULL", "_DATA", "DATA",
                          "_BSS", "BSS", "DGROUP",
                          "HB_STARTSYMBOLS", "HB_SYMBOLS", "HB_ENDSYMBOLS", "HARBOUR",
                          "HB_STARTBORSYMBOLS", "_INIT_", "HB_ENDBORSYMBOLS", "INITDATA", "BORLAND",
                          0 };

  LocalNames( hObjFile, localNames );
}

static void GenerateSymbolsSegment( HB_COMP_DECL, FILE * hObjFile )
{
  BYTE symbolsData[]   = { 0, 0, 0, 0, 0, 0, 0, 0 };
  BYTE groupDGroup[]   = { 2, 3, 4, 0 }; /* segments defined order for DGROUP */
  BYTE groupSymGroup[] = { 5, 6, 7, 0 }; /* segments defined order for SYMGROUP */
  BYTE groupInitData[] = { 8, 9, 10, 0 }; /* segments defined order for INITDATA */

  DefineSegment( hObjFile, 10,   /* HB_STARTSYMBOLS position + 1 into localnames */
                            6,   /* "DATA" position + 1 into localNames */
                            0 ); /* segment length */
  DefineSegment( hObjFile, 11,   /* HB_SYMBOLS position + 1 into localNames */
                            6,   /* "DATA" position + 1 into localNames */
                            8 ); /* segment length */
  DefineSegment( hObjFile, 12,   /* HB_ENDSYMBOLS position + 1 into localNames */
                            6,   /* "DATA" position + 1 into localNames */
                            0 ); /* segment length */

  DefineSegment( hObjFile, 14,   /* HB_STARTBORSYMBOLS position + 1 into localnames */
                           17,   /* INITDATA position + 1 into localNames */
                            0 ); /* segment length */
  DefineSegment( hObjFile, 15,   /* HB_STARTSYMBOLS position + 1 into localnames */
                           17,   /* INITDATA position + 1 into localNames */
                            0 ); /* segment length */
  DefineSegment( hObjFile, 16,   /* HB_ENDBORSYMBOLS position + 1 into localnames */
                           17,   /* INITDATA position + 1 into localNames */
                            0 ); /* segment length */

  GroupDef( hObjFile,  8, groupDGroup );   /* "DGROUP" localNames position - 1 */
  GroupDef( hObjFile, 12, groupSymGroup ); /* "SYMGROUP" localNames position - 1 */
  GroupDef( hObjFile, 17, groupInitData ); /* "BORLAND" localNames position - 1 */

  * ( USHORT * ) symbolsData = (USHORT) GetSymbolsAmount( HB_COMP_PARAM );

  EnumeratedData( hObjFile, 6, symbolsData, sizeof( symbolsData ), 0 ); /* HB_SYMBOLS defined order segment */

  Fixup( hObjFile, 0xE4, 4, /* offset into HB_SYMBOLS segment */
                      0x54,
                         4 ); /* DATA segment defined order */
}

static void GenerateDataSegment( HB_COMP_DECL, FILE * hObjFile )
{
  HB_SYMB symbol;
  ULONG ulSize = GetSymbolsSize( HB_COMP_PARAM );
  PCOMSYMBOL pSymbol = GetFirstSymbol( HB_COMP_PARAM );
  ULONG ulSymbols = GetSymbolsAmount( HB_COMP_PARAM ), ul;

  while( pSymbol )
    {
      ulSize += strlen( pSymbol->szName ) + 1;
      pSymbol = pSymbol->pNext;
    }

  ulSize += GetPCodesSize( HB_COMP_PARAM );

  DefineSegment( hObjFile,  4,   /* _NULL position + 1 into localnames */
                            6,   /* "DATA" position + 1 into localNames */
                            0 ); /* segment length */
  DefineSegment( hObjFile,  7,   /* _BSS position + 1 into localNames */
                            8,   /* "BSS" position + 1 into localNames */
                            0 ); /* segment length */
  DefineSegment( hObjFile, 5, /* "_DATA" position + 1 into localNames */
                           6, /* "DATA" position + 1 into localNames */
                    (USHORT) ulSize ); /* segment length */

  memset( &symbol, 0, sizeof( symbol ) );
  DataSegment( HB_COMP_PARAM, hObjFile, (BYTE *) &symbol,
               sizeof( symbol ), GetSymbolsAmount( HB_COMP_PARAM ), ulSize );

  pSymbol = GetFirstSymbol( HB_COMP_PARAM );
  for( ul = 0; ul < ulSymbols; ul++ )
    {
      Fixup( hObjFile, 0xE4, (USHORT) ( ul * sizeof( HB_SYMB ) ), 0x54, 4 ); /* 4 = Data symbol name location */

      if( IsExternal( HB_COMP_PARAM, ul ) )
        {
          if( ! ( pSymbol->cScope & HB_FS_MESSAGE ) )
            Fixup( hObjFile, 0xE4, (USHORT) ( ul * sizeof( HB_SYMB ) ) + 8, 0x56,
                   GetExternalPos( GetSymbolName( HB_COMP_PARAM, ul ) ) + 1 );
        }
      else
        {
          /* if( ! ( pSymbol->cScope & HB_FS_MESSAGE ) ) */
          Fixup( hObjFile, 0xE4, (USHORT) ( ul * sizeof( HB_SYMB ) ) + 8, 0x54, 1 ); /* function address location */
        }
      pSymbol = pSymbol->pNext;
    }
}

static void GenerateCodeSegment( HB_COMP_DECL, FILE * hObjFile )
{
  USHORT wFunctions = HB_COMP_PARAM->functions.iCount - ( HB_COMP_PARAM->fStartProc ? 0: 1 );
  ULONG ulSize    = wFunctions * sizeof( prgFunction );
  PFUNCTION pFunc = ( HB_COMP_PARAM->fStartProc ? HB_COMP_PARAM->functions.pFirst: HB_COMP_PARAM->functions.pFirst->pNext );
  USHORT w = 0;

  DefineSegment( hObjFile, 2, /* "_TEXT" position + 1 into localNames */
                 3, /* "CODE" position + 1 into localNames */
                 (USHORT) ulSize ); /* segment length */

  while( pFunc )
    {
      if( !( pFunc->cScope & ( HB_FS_STATIC | HB_FS_INIT | HB_FS_EXIT ) ) )
        PubDef( hObjFile, pFunc->szName, 1, w * sizeof( prgFunction ) );
      w++;
      pFunc = pFunc->pNext;
    }

  CodeSegment( HB_COMP_PARAM, hObjFile, prgFunction, sizeof( prgFunction ), wFunctions );

  for( w = 0; w < wFunctions; w++ )
    {
      /* prgFunction fixups */
      Fixup( hObjFile, 0xE4, ( w * sizeof( prgFunction ) ) + 1,
             0x54, 4 ); /* 4 = DATA segment defined order */

      Fixup( hObjFile, 0xE4, ( w * sizeof( prgFunction ) ) + 6,
             0x54, 4 ); /* DATA segment define order - pcode location */

      Fixup( hObjFile, 0xA4, ( w * sizeof( prgFunction ) ) + 11,
             0x56, 1 ); /* External: _hb_vmExecute */
    }
}

static void GenerateExternals( HB_COMP_DECL, FILE * hObjFile )
{
  USHORT w;
  PFUNCALL pFunc;
  PFUNCTION pFTemp;

  /* calculate amount of externals */
  pFunc = HB_COMP_PARAM->funcalls.pFirst;
  while( pFunc )
    {
      if( ( pFTemp = hb_compFunctionFind( HB_COMP_PARAM, pFunc->szName ) ) == NULL || pFTemp == HB_COMP_PARAM->functions.pFirst )
        wExternals++;
      pFunc = pFunc->pNext;
    }
  if( wExternals )
    {
      externNames = ( char * * ) hb_xgrab( sizeof( char * ) * ( wExternals + 2 ) );
      w = 1;
      externNames[ 0 ] = "_hb_vmExecute";

      pFunc = HB_COMP_PARAM->funcalls.pFirst;
      while( pFunc )
        {
          if( ( pFTemp = hb_compFunctionFind( HB_COMP_PARAM, pFunc->szName ) ) == NULL || pFTemp == HB_COMP_PARAM->functions.pFirst )
            externNames[ w++ ] = pFunc->szName;
          pFunc = pFunc->pNext;
        }
      externNames[ w ] = 0;
      ExternalNames( hObjFile, externNames );
    }
}

static void putbyte( BYTE b, FILE * hObjFile, BYTE * pbChecksum )
{
  fputc( b, hObjFile );
  * pbChecksum += b;
}

static void putword( USHORT w, FILE * hObjFile, BYTE * pbChecksum )
{
  putbyte( HB_LOBYTE( w ), hObjFile, pbChecksum );
  putbyte( HB_HIBYTE( w ), hObjFile, pbChecksum );
}

static void CompiledFileName( FILE * hObjFile, char * szFileName )
{
  USHORT wLen = strlen( szFileName );
  BYTE bChk = 0; /* this is a checksum the linker will check to asure OBJ integrity */
  BYTE bChar;

  putbyte( 0x80, hObjFile, &bChk );  /* this tells the linker the kind of OBJ record this is */
  putbyte( 1 + 1 + wLen, hObjFile, &bChk ); /* now it comes the total length of this OBJ record */
  putbyte( 0, hObjFile, &bChk );
  putbyte( (BYTE) wLen, hObjFile, &bChk );     /* szFileName length */

  while( ( bChar = * szFileName++ ) != 0 )
      putbyte( bChar, hObjFile, &bChk );   /* each of the szFileName characters */

  putbyte( 256 - bChk, hObjFile, &bChk ); /* a checksum that will be recalculated by the linker */
}

static void CompilerVersion( FILE * hObjFile, char * szVersion )
{
  USHORT wLen = strlen( szVersion );
  BYTE bChk = 0; /* this is a checksum the linker will check to asure OBJ integrity */
  BYTE bChar;

  putbyte( 0x88, hObjFile, &bChk );  /* this tells the linker the kind of OBJ record this is */
  putword( 3 + wLen, hObjFile, &bChk ); /* now it comes the total length of this OBJ record */
  putword( 0, hObjFile, &bChk );

  while( ( bChar = * szVersion++ ) != 0 )
      putbyte( bChar, hObjFile, &bChk );   /* each of the szFileName characters */

  putbyte( 256 - bChk, hObjFile, &bChk ); /* a checksum that will be recalculated by the linker */
}

static void LocalNames( FILE * hObjFile, char * szNames[] )
{
  BYTE b = 0, c;
  USHORT wTotalLen = 0;
  BYTE bChk = 0;

  while( szNames[ b ] )
    wTotalLen += strlen( szNames[ b++ ] );
  wTotalLen += 2 + b;

  putbyte( 0x96, hObjFile, &bChk );
  putword( wTotalLen, hObjFile, &bChk );
  putbyte( 0, hObjFile, &bChk );

  b = 0;
  while( szNames[ b ] )
    {
      putbyte( strlen( szNames[ b ] ), hObjFile, &bChk );

      c = 0;
      while( szNames[ b ][ c ] )
          putbyte( szNames[ b ][ c++ ], hObjFile, &bChk );
      b++;
    }
  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void ExternalNames( FILE * hObjFile, char * szNames[] )
{
  BYTE b = 0, c;
  USHORT wTotalLen = 0;
  BYTE bChk = 0;

  while( szNames[ b ] )
  {
    if( b == 0 )
       wTotalLen += strlen( szNames[ b++ ] ) + 1;
    else
       wTotalLen += strlen( szPrefix ) + strlen( szNames[ b++ ] ) + 1;
  }
  wTotalLen += 2 + b - 1;

  putbyte( 0x8C, hObjFile, &bChk );
  putword( wTotalLen, hObjFile, &bChk );

  b = 0;
  while( szNames[ b ] )
    {
      if( b == 0 )
         putbyte( strlen( szNames[ b ] ), hObjFile, &bChk );
      else
         putbyte( strlen( szPrefix ) + strlen( szNames[ b ] ), hObjFile, &bChk );

      c = 0;

      if( b > 0 )
      {
         while( szPrefix[ c ] )
            putbyte( szPrefix[ c++ ], hObjFile, &bChk );
         c = 0;
      }

      while( szNames[ b ][ c ] )
          putbyte( szNames[ b ][ c++ ], hObjFile, &bChk );
      putbyte( 0, hObjFile, &bChk );
      b++;
    }
  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void CodeSegment( HB_COMP_DECL, FILE * hObjFile, BYTE * prgCode, ULONG ulPrgLen, USHORT wFunctions )
{
  BYTE bChk = 0;
  USHORT y;
  USHORT wTotalLen = (USHORT) ( ulPrgLen * wFunctions ) + 4;
  ULONG ul;
  PFUNCTION pFunction = HB_COMP_PARAM->functions.pFirst;
  ULONG ulPCodeOffset = HB_COMP_PARAM->symbols.iCount * sizeof( HB_SYMB );

  if( ! HB_COMP_PARAM->fStartProc )
    pFunction = pFunction->pNext;

  putbyte( 0xA0, hObjFile, &bChk );
  putword( wTotalLen, hObjFile, &bChk );
  putbyte( 1, hObjFile, &bChk ); /* 1 = _TEXT segment */
  putword( 0, hObjFile, &bChk ); /* 0 = offset */

  for( y = 0; y < wFunctions; y++ )
    {
      * ( ULONG * ) &prgCode[ 6 ] = ulPCodeOffset; /* function pcode offset */
      for( ul = 0; ul < ulPrgLen; ul++ )
          putbyte( * ( prgCode + ul ), hObjFile, &bChk );
      ulPCodeOffset += pFunction->lPCodePos;
      pFunction = pFunction->pNext;
    }

  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void DataSegment( HB_COMP_DECL, FILE * hObjFile, BYTE * symbol, ULONG wSymLen, ULONG wSymbols,
                         ULONG ulSize )
{
  BYTE bChk = 0;
  ULONG w, y;
  USHORT wTotalLen = 4 + (USHORT) ulSize;
  PCOMSYMBOL pSymbol = GetFirstSymbol( HB_COMP_PARAM );
  PFUNCTION pFunction = HB_COMP_PARAM->functions.pFirst;
  ULONG ulSymbolNameOffset = GetSymbolsSize( HB_COMP_PARAM ) + GetPCodesSize( HB_COMP_PARAM );
  ULONG ulFunctionOffset;

  if( ! HB_COMP_PARAM->fStartProc )
    pFunction = pFunction->pNext;

  putbyte( 0xA0, hObjFile, &bChk );
  putword( wTotalLen, hObjFile, &bChk );
  putbyte( 4, hObjFile, &bChk ); /* 2 = _DATA segment defined order */
  putword( 0, hObjFile, &bChk ); /* 0 = offset */

  for( y = 0; y < wSymbols; y++ )
    {
      * ( ULONG * ) symbol = ulSymbolNameOffset;

      if( ! IsExternal( HB_COMP_PARAM, y ) )
        {
          ulFunctionOffset = ( hb_compFunctionGetPos( HB_COMP_PARAM, pSymbol->szName ) - 1 ) *
            sizeof( prgFunction );
          * ( ( ULONG * ) &symbol[ 8 ] ) = ulFunctionOffset; /* 8 offset of function pointer into symbol */
        }
      else
        * ( ( ULONG * ) &symbol[ 8 ] ) = 0; /* 8 offset of function pointer into symbol */

      if( pSymbol->cScope == HB_FS_MESSAGE )
        symbol[ 4 ] = HB_FS_PUBLIC;
      else
        symbol[ 4 ] = pSymbol->cScope;

      for( w = 0; w < wSymLen; w++ )
         putbyte( * ( symbol + w ), hObjFile, &bChk );

      ulSymbolNameOffset += strlen( pSymbol->szName ) + 1;
      pSymbol = pSymbol->pNext;
    }

  while( pFunction )
    {
      w = 0;
      while( w < pFunction->lPCodePos )
         putbyte( pFunction->pCode[ w++ ], hObjFile, &bChk );

      pFunction = pFunction->pNext;
    }

  pSymbol = GetFirstSymbol( HB_COMP_PARAM );

  while( pSymbol )
    {
      for( w = 0; w < strlen( pSymbol->szName ); w++ )
          putbyte( pSymbol->szName[ w ], hObjFile, &bChk );

      putbyte( 0, hObjFile, &bChk );
      pSymbol = pSymbol->pNext;
    }

  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void DefineSegment( FILE * hObjFile, BYTE bName, BYTE bClass, USHORT wLen )
{
  BYTE bChk = 0;

  putbyte( 0x98, hObjFile, &bChk );
  putbyte( 7, hObjFile, &bChk );      /* SegDef records have always this length */
  putbyte( 0, hObjFile, &bChk );

  putbyte( 0xA9, hObjFile, &bChk );
  putword( wLen, hObjFile, &bChk );
  putbyte( bName, hObjFile, &bChk );
  putbyte( bClass, hObjFile, &bChk );
  putbyte( 0, hObjFile, &bChk );

  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void PubDef( FILE * hObjFile, char * szName, USHORT wSegment, USHORT wOffset )
{
  BYTE bChk = 0;
  BYTE bChar;
  USHORT wLen = 2 + 2 + strlen( szPrefix ) + strlen( szName ) + 2 + 1;
  const char * szTemp;

  putbyte( 0x90, hObjFile, &bChk );
  putword( wLen, hObjFile, &bChk );
  putbyte( 0x00, hObjFile, &bChk );
  putbyte( (BYTE) wSegment, hObjFile, &bChk );
  putbyte( strlen( szPrefix ) + strlen( szName ), hObjFile, &bChk );

  szTemp = szPrefix;
  while( ( bChar = * szTemp++ ) != 0 )
     putbyte( bChar, hObjFile, &bChk );

  while( ( bChar = * szName++ ) != 0 )
     putbyte( bChar, hObjFile, &bChk );

  putword( wOffset, hObjFile, &bChk );
  putbyte( 0x00, hObjFile, &bChk );

  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void Fixup( FILE * hObjFile, BYTE bType, USHORT wOffset, BYTE bFlags, BYTE bSymbol )
{
  BYTE bChk = 0;

  putbyte( 0x9D, hObjFile, &bChk );
  putword( 5, hObjFile, &bChk );
  putbyte( bType + HB_HIBYTE( wOffset ), hObjFile, &bChk );
  putbyte( HB_LOBYTE( wOffset ), hObjFile, &bChk );
  putbyte( bFlags, hObjFile, &bChk );
  putbyte( bSymbol, hObjFile, &bChk );

  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void EnumeratedData( FILE * hObjFile, BYTE bSegment, BYTE * pData, USHORT wLen, USHORT wOffset )
{
  BYTE bChk = 0;
  USHORT w;

  putbyte( 0xA0, hObjFile, &bChk );
  putword( ( USHORT ) ( wLen + 4 ), hObjFile, &bChk );
  putbyte( bSegment, hObjFile, &bChk );
  putword( wOffset, hObjFile, &bChk );

  for( w = 0; w < wLen; w++ )
     putbyte( * ( pData + w ), hObjFile, &bChk );

  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void End( FILE * hObjFile )
{
  BYTE bChk = 0;

  putbyte( 0x8A, hObjFile, &bChk );
  putbyte( 0x02, hObjFile, &bChk );
  putbyte( 0x00, hObjFile, &bChk );
  putbyte( 0x00, hObjFile, &bChk );
  putbyte( 256 - bChk, hObjFile, &bChk );
}

static void GroupDef( FILE * hObjFile, BYTE bName, BYTE * aSegs )
{
  BYTE bChk = 0;
  USHORT wRecLen = 2;
  USHORT w       = 0;

  while( aSegs[ w++ ] )
    wRecLen += 2;

  putbyte( 0x9A, hObjFile, &bChk );
  putword( wRecLen, hObjFile, &bChk );
  putbyte( bName + 1, hObjFile, &bChk );

  w = 0;
  while( aSegs[ w ] )
  {
      putbyte( 0xFF, hObjFile, &bChk );
      putbyte( aSegs[ w++ ], hObjFile, &bChk );
  }

  putbyte( 256 - bChk, hObjFile, &bChk );
}
