/****************************************************************************
**
**  Name:  INHEX.CPP
**
**  Description:
**      Entry point of the Intel Hex Loader DLL (INHEX.DLL).
**
**  Status:  PRELIMINARY
**
**  $Log:   S:/tbird/mt2_186/inhex/inhex.cpv  $
** 
**    Rev 1.0   16 Dec 1996 16:46:46   Judy
** Initial revision.
** 
**  $Header:   S:/tbird/mt2_186/inhex/inhex.cpv   1.0   16 Dec 1996 16:46:46   Judy  $
**
**  Copyright (C) 1996 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#include <io.h>
#include <mem.h>
#include <stdlib.h>
#include <string.h>
#include <sys\timeb.h>

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

#ifndef _BKROOT_
#include "bkroot.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _CPU_
#include "cpu.h"
#endif

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _LOADER_
#include "loader.h"
#endif

#ifndef _LQUEUE_
#include "lqueue.h"
#endif

#ifndef _INHEX_
#include "inhex.h"
#endif

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

PRIVATE RETCODE Cleanup(HANDLE, HCURSOR, BOOLEAN, BOOLEAN, RETCODE);
PRIVATE RETCODE CloseFile(HANDLE);
PRIVATE RETCODE GetOneLine(HANDLE, LPSTR);
PRIVATE RETCODE GetValue(VOID *value, S8 bytes);
PRIVATE RETCODE LoadDataImage(HANDLE, U32, LOAD_FUNC, U32, U32);
PRIVATE RETCODE LoadIntelHexData(HANDLE, LPLDRSTATBLOCK);
PRIVATE RETCODE OpenIntelHexFile(LPSTR, HANDLE *, OFSTRUCT *);

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

STATIC BOOLEAN HasStartPC, StartPCLinear;
STATIC VIRTUAL_ADDRESS StartPC;
STATIC DESCRIPTOR FAR *PtrAddrStartPC;
STATIC U32 BytesLoaded;
STATIC U32 LastLoadAddr;
STATIC LPSTR LdrBuf=NULL;
STATIC LPSTR ReadPtr=NULL;
STATIC LPSTR FAR LinePtr=NULL;
STATIC S16 CharsCnt=0;
STATIC BOOLEAN LoadFileEnd;

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

                       /****************************
                        *                          *
                        *    EXPORTED FUNCTIONS    *
                        *                          *
                        ****************************/
#pragma warn -par
/******************************************************************************
**
**  InHexLoaderLoad
**
******************************************************************************/
RETCODE EXPORT InHexLoaderLoad(LPSTR fileName, U32 flags, HWND parentHwnd,
                               ADDR_SPACE addrSpace) {
CHAR      loadFile[MAX_PATH_SIZE];  // near buffer
HANDLE    hLoadFile;
OFSTRUCT  ofStruct;
RETCODE   err, err2;
BOOLEAN   abortFromEsc, cursorOn = FALSE;
HCURSOR   hOldCursor, hHourGlassCursor;
LPSTR     lpStrTmp;
PMODE     pmode;
LPLDRSTATBLOCK statBlock = NULL;

   /* Gets the processor mode, for Intel Hex Loader, it should be in REAL_MODE */
   if ((err = AdrGetPmode(&pmode,loadFile)) != GOOD) {
      if ((err=LdrWarning(err, NULL, NULL, NULL)) != GOOD) {
         LdrProgressDone(err);
         return(err);
      }
   }
   else if (pmode != PMODE_REAL) {
      if ((err=LdrWarning(ER_OMF86_NOT_REAL, NULL, NULL, NULL)) != GOOD) {
         LdrProgressDone(err);
         return(err);
      }
      if (((err=CpuResetCPU()) != GOOD) &&
          ((err=LdrWarning(err, NULL, NULL, NULL)) != GOOD)) {
         LdrProgressDone(err);
         return(err);
      }
   }
   /* check for the null filename */
   if (*fileName == '\0') return(ER_CANNOT_OPEN);

   /* Set/Clear initial state for user-abort checking */
   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD) {
      LdrProgressDone(err);
      return(err);
   }
   if (((err = LdrGetStatBlock((LPLDRSTATBLOCK FAR *)&statBlock)) != GOOD) ||
       (statBlock == NULL)) {
      LdrProgressDone(err);
      return(err);
   }
   HasStartPC = StartPCLinear = LoadFileEnd = FALSE;
   PtrAddrStartPC = NULL;

   // Copy filename to near buffer so we can use fopen
   lstrcpy((LPSTR)loadFile, fileName);
   lpStrTmp = loadFile;
   // make sure it will fit in the status buffer (and in the dialog)
   // adjust the length of the text string
   while (lstrlen(lpStrTmp) + 1 >= MAX_LOADFILE_NAME_LEN)
      lpStrTmp++;

   lstrcpy((LPSTR)(statBlock->curLoadFile), (LPSTR)lpStrTmp);

   // Prepare to put up hourglass for a lengthy operation
   if ((hHourGlassCursor = LoadCursor(NULL, IDC_WAIT)) != NULL) {
      hOldCursor = SetCursor(hHourGlassCursor);
      cursorOn = TRUE;
   }

   // Open loadfile
   if ((err = OpenIntelHexFile(loadFile, &hLoadFile, &ofStruct)) != GOOD) {
      if (cursorOn) SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }

   // Initialize the progress indicator statBlock
   statBlock->loadFileSize = filelength(hLoadFile);
   statBlock->curLocation = 0L;

   // Load code here
   BytesLoaded = 0L;
   LastLoadAddr = 0ul;
   if (((err = BkProcessorMustBeHalted()) != GOOD) ||
       ((err = LdrQueueInit(addrSpace)) != GOOD))
      return(Cleanup(hLoadFile, hOldCursor, cursorOn, FALSE, err));

   if ((err=LoadIntelHexData(hLoadFile, statBlock)) != GOOD)
      return(Cleanup(hLoadFile, hOldCursor, cursorOn, TRUE, err));

   if (cursorOn) SetCursor(hOldCursor);
   CloseFile(hLoadFile);

   /* send partially filled load buffer to box */
   err = LdrQueueFlush(LastLoadAddr);
   err2 = LdrQueueWait();

   if ((err = (err ? err : err2)) == ER_MEMORY_VERIFY)
      return LdrProgressDone(err);

   // Update the progress indicator
   statBlock->numBytes = BytesLoaded;
   statBlock->curLocation = tell(hLoadFile);
   if (HasStartPC) {
      if (StartPCLinear)
          sprintf((LPSTR)statBlock->startPC, "%04X:%08lX",
                  StartPC.base, StartPC.offset );
      else sprintf((LPSTR)statBlock->startPC, "%04X:%04X",
                   StartPC.base, (U16)StartPC.offset );
   }
   return(LdrProgressDone(GOOD));
}  /* InHexLoaderLoad */

/******************************************************************************
**
**  InHexLoaderLoadByModuleDesc
**
******************************************************************************/
RETCODE EXPORT InHexLoaderLoadModuleByDesc(LPSTR lpBinfile, LPSTR moduleName,
                                           SYM_DESCRIPTOR moduleDesc,
                                           ADDR_SPACE addrSpace, U32 flags) {
   return(GOOD);
} /* InHexLoaderLoadModuleByDesc */

/******************************************************************************
**
**  InHexLoaderGetStartPC
**
******************************************************************************/
RETCODE EXPORT InHexLoaderGetStartPC(DESCRIPTOR FAR *ptrStartPC) {
RETCODE aderr;
CHAR addrStr[MAX_NUM_LEN];

   if (!HasStartPC)
      return(ER_NO_PC);
   if (StartPCLinear)
      sprintf(addrStr, "%04X:%08lX", StartPC.base, StartPC.offset);
   else sprintf(addrStr, "%04X:%04X", StartPC.base, (U16)StartPC.offset);
   /* Uses static data */
   if ((aderr = AdrCreateAddressFromText((LPSTR)addrStr, NULL,
        ptrStartPC)) != GOOD)
      return(aderr);
   /* retain static copy in case of cleanup */
   PtrAddrStartPC = ptrStartPC;
   return(GOOD);
}  /* InHexLoaderGetStartPC */

/******************************************************************************
**
**  InHexLoaderGetStackInfo
**
******************************************************************************/
RETCODE EXPORT InHexLoaderGetStackInfo(DESCRIPTOR FAR *ptrStackTop,
                                       U32 FAR *stkSize) {
   return(ER_STK_STK_BASE_INVALID);
}  /* InHexLoaderGetStack */

/*****************************************************************************
**
**  InHexLoaderGetBytes
**
*****************************************************************************/
RETCODE EXPORT InHexLoaderGetBytes(VOID FAR *info, U8 FAR *dest, U16 count) {
U8 FAR *ptr = dest;
CHAR ch, cl;

   for (U16 i=0; i < count; i++, ptr++) {
      ch = *LinePtr++;
      cl = *LinePtr++;
      *ptr  = C2X(cl) + (C2X(ch) << 4);
   }
   return(GOOD);
} /* InHexLoaderGetBytes */

                       /****************************
                        *                          *
                        *      LOCAL FUNCTIONS     *
                        *                          *
                        ****************************/
/******************************************************************************
**
**  Cleanup
**
******************************************************************************/
PRIVATE RETCODE Cleanup(HANDLE hLoadFile,HCURSOR hOldCursor, BOOLEAN cursorOn,
                        BOOLEAN needToEmptyQueue, RETCODE err) {

   /* When user decides to abort load operation, or for some error
   ** condition encountered, cleanup.  This routine is *not* called
   ** for a successful load.
   */
   CloseFile(hLoadFile);
   if (needToEmptyQueue)
      LdrQueueWait();
   /*  Clean up address descriptors created in this load session */
   if (PtrAddrStartPC)
      AdrDestroyAddress(*PtrAddrStartPC);
   PtrAddrStartPC = NULL;
   if (cursorOn) SetCursor(hOldCursor);
   // LdrProgressDone will report the error
   return LdrProgressDone(err);
}  /* Cleanup */

/*****************************************************************************
**
**  CloseFile
**
*****************************************************************************/
PRIVATE RETCODE CloseFile(HANDLE hfile) {
   RETCODE err;

   _lclose(hfile);
   /* De-allocate file buffer */
   if ((LdrBuf != NULL) && (TFree((LPSTR)LdrBuf) != GOOD) &&
       (err = LdrWarning(ER_BAD_FREE, NULL, NULL, NULL)) != GOOD)
      return(err);
   LdrBuf = NULL;
   return(GOOD);
}  /* CloseFile */

/******************************************************************************
**
**  GetOneLine
**
******************************************************************************/
PRIVATE RETCODE GetOneLine(HANDLE hFile, LPSTR lineStr) {
LPSTR ptrEnd;
U32 fileOffset;
BOOLEAN done = FALSE;

   while (!done) {
      if ((ptrEnd = strstr(ReadPtr, "\r\n")) != NULL) {
         S16 tmpValue, len;

         tmpValue = (S16)((U32)ptrEnd - (U32)ReadPtr);
         if ((len = MIN(MAX_LINE_LEN, tmpValue)) > 0)
            _fmemcpy(lineStr, ReadPtr, len);
         lineStr[len] = '\0';
         tmpValue = CharsCnt - tmpValue - 2;
         if (tmpValue >= 0) {
            ReadPtr = ptrEnd + 2;
            CharsCnt = tmpValue;
            return(GOOD);
         }
         done = TRUE;
      }
      if (LoadFileEnd) // bad load file
         return(ER_BAD_FILE);
      fileOffset = tell(hFile) - CharsCnt;
      if (_llseek(hFile, fileOffset, SEEK_SET) == -1)
         return(ER_BAD_SEEK);
      if ((CharsCnt = _lread(hFile, LdrBuf, LDR_BUFSIZE)) <= 0)
         return(ER_BADGET);
      if (CharsCnt < LDR_BUFSIZE)
         LoadFileEnd = TRUE;
      LdrBuf[CharsCnt] = '\0';
      ReadPtr = LdrBuf;
   }
   return(GOOD);
}  /* GetOneLine */

/******************************************************************************
**
**  GetValue
**
******************************************************************************/
PRIVATE RETCODE GetValue(VOID *value, S8 size) {
S16   chars = 2*size;
CHAR  strBuf[MAX_VALUE_CHARS+1];
LPSTR endPtr;
U32 tmpValue;

   if (chars > MAX_VALUE_CHARS)
      return(ER_LDR_BAD_LOADFILE);
   _fmemcpy(strBuf, LinePtr, chars);
   strBuf[chars] = '\0';
   tmpValue = strtoul(strBuf, &endPtr, 16);
   if (*endPtr) return(ER_LDR_BAD_LOADFILE);
   _fmemcpy(value, &tmpValue, size);
   LinePtr += chars;
   return(GOOD);
}  /* GetValue */

/******************************************************************************
**
**  LoadDataImage
**
******************************************************************************/
PRIVATE RETCODE LoadDataImage(HANDLE hFile, U32 addr, LOAD_FUNC loadFunc,
                              U32 rptCnt, U32 byteCnt) {
RETCODE err;
BOOLEAN aborted;
U32 addrBitsChanged;

   /* Send new address record, changing as few bytes as possible */
   if ((addrBitsChanged = addr ^ LastLoadAddr) != 0) {
      if ((err = LdrQueueSetAddressRecord(addr,
           BYTES_TO_TRANSMIT(addrBitsChanged))) != GOOD) return(err);
   }
   if ((err = LdrQueueCheckLoadAbort()) != GOOD) return(err);
   if ((err = LdrQueueGetLoadAbort(&aborted)) != GOOD) return(err);
   if ((err = LdrQueueDataRecord(&hFile, loadFunc, addr,
              rptCnt, byteCnt)) != GOOD)  return(err);
   BytesLoaded += byteCnt * rptCnt;
   LastLoadAddr = addr + rptCnt*byteCnt;
   return(GOOD);
} /* LoadDataImage */

/******************************************************************************
**
**  LoadIntelHexData
**
******************************************************************************/
PRIVATE RETCODE LoadIntelHexData(HANDLE hFile, LPLDRSTATBLOCK statBlock) {
LPSTR lineBuf=NULL;
U8  recLen, recType;
U16 segAddr=0, offsetAddr=0;
RETCODE err=GOOD, err2;
ADDR_TYPE addrType=ADDR_VIRTUAL;

   if ((lineBuf = TMalloc((U32)MAX_LINE_LEN)) == NULL)
      return(ER_NO_MEMORY);

   while (GetOneLine(hFile, lineBuf) == GOOD) {
      // Update the progress indicator
      LinePtr = lineBuf;
      // Check for record mark char ':'
      if (*LinePtr++ != ':') {
         err =ER_LDR_BAD_LOADFILE;
         break;
      }
      if (((err=GetValue(&recLen, sizeof(recLen))) != GOOD) ||
          ((err=GetValue(&offsetAddr, sizeof(offsetAddr))) != GOOD) ||
          ((err=GetValue(&recType, sizeof(recType))) != GOOD))
         break;
      switch(recType) {
        case REC_DATA : {            // Data Record
           U32 physicalAddr;

           if (addrType == ADDR_LINEAR)
              physicalAddr = (U32)offsetAddr + ((U32)segAddr << 16);
           else physicalAddr = (U32)offsetAddr + ((U32)segAddr << 4) ;
           err = LoadDataImage(hFile, physicalAddr,
                      (LOAD_FUNC)InHexLoaderGetBytes, 1uL, recLen);
           break;
        }
        case REC_EOF :               // End of File
           goto LOAD_END;
        case REC_SEG_ADDR :          // Segment Addr Record
        case REC_EXT_LINEAR_ADDR :   // Extended Linear Address Record
           if ((recLen == 2) && !offsetAddr) {
              if ((err=GetValue(&segAddr, sizeof(segAddr))) != GOOD)
                 break;
              addrType = (recType == REC_EXT_LINEAR_ADDR ) ?
                         ADDR_LINEAR : ADDR_VIRTUAL;
           }
           else err=ER_LDR_BAD_LOADFILE;
           break;
        case REC_START_CSIP :        // Start PC Record
           if ((recLen == 4) && !offsetAddr) {
              if (((err=GetValue(&StartPC.base, sizeof(U16))) != GOOD) ||
                  ((err=GetValue(&StartPC.offset, sizeof(U16))) != GOOD))
                 break;
              HasStartPC = TRUE;
           }
           else err=ER_LDR_BAD_LOADFILE;
           break;
        case REC_START_EIP :             // Start Linear Addr Record
           if ((recLen == 4) && !offsetAddr) {
              if ((err=GetValue(&StartPC.offset, sizeof(U32))) != GOOD)
                 break;
              HasStartPC = StartPCLinear = TRUE;
           }
           else err=ER_LDR_BAD_LOADFILE;
           break;
        default: // Phar Lap format is not handled
           err = ER_REC_NOT_HANDLED;
      }
      if (err != GOOD)
         break;
      statBlock->numBytes = BytesLoaded;
      statBlock->curLocation = tell(hFile);
      if ((err = LdrProgressInc()) != GOOD)
         break;
   }
LOAD_END:
   if (lineBuf)
      err2 = TFree(lineBuf);
   return((err == GOOD) ? err2 : err);
} /* LoadIntelHexData */

/****************************************************************************
**
**  OpenIntelHexFile
**
*****************************************************************************/
PRIVATE RETCODE OpenIntelHexFile(LPSTR fileName, HANDLE *hFile, OFSTRUCT *pof) {
RETCODE err;
   
   // check for the null filename 
   if (*fileName == '\0')
      return(ER_CANNOT_OPEN);

   // fill in the file struct so caller has fully-qualified filename
   if ((err = _lopen(fileName, READ)) == -1L)
      return (ER_CANNOT_OPEN);
   /* get the file handle */
   *hFile = (HANDLE)err;

   // Still need to fill in ofstruct, since this is the only way to obtain
   // the fully-qualified pathname (stored in symbol table). 
   OpenFile(fileName, pof, OF_PARSE);

   if ((LdrBuf = TMalloc((U32)LDR_BUFSIZE+1)) == NULL) {
      err=ERR_BAD_MEMORY;
      goto ERR_OUT;
   }
   if (_llseek(*hFile, 0L, SEEK_SET) == -1) {
      err = ER_BAD_SEEK;
      goto ERR_OUT;
   }
   if ((CharsCnt = _lread(*hFile, LdrBuf, LDR_BUFSIZE)) <= 0) {
      err= ER_BADGET;
      goto ERR_OUT;
   }
   if (CharsCnt < LDR_BUFSIZE)
      LoadFileEnd = TRUE;
   LdrBuf[CharsCnt] = '\0';
   ReadPtr = LdrBuf;
   return(GOOD);

ERR_OUT:
   CloseFile(*hFile);
   return(err);
}  /* OpenIntelHexFile */

/******************************** E O F ***********************************/
