//---------------------------------------------------------------------------
#include <windows.h>
#include <vcl.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>

#pragma hdrstop

#pragma warn -8027
#pragma warn -8004

#include "USB_HID_CLASS.h"
#include "mphidflash.h"
#include "BLDR.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TBootloader *Bootloader;

//---------------------------------------------------------------------------
/****************************************************************************
 File        : main.c
 Description : Main source file for 'mphidflash,' a simple command-line tool for
               communicating with Microchips USB HID-Bootloader and downloading new
               firmware.

 History     : 2009-02-19  Phillip Burgess
                 * Initial implementation
               2009-12-26  Thomas Fischl, Dominik Fisch (www.FundF.net)
                 * Renamed 'ubw32' to 'mphidflash'
               2010-12-28  Petr Olivka
                 * program and verify only data for defined memory areas
                 * send only even length of data to PIC
               2015-03-28  Dmitry Orlov
                 * added SIGN_FLASH command
                 * ported to VCL C-builder.   

 License     : Copyright (C) 2009 Phillip Burgess
               Copyright (C) 2009 Thomas Fischl, Dominik Fisch (www.FundF.net)
               Copyright (C) 2010 Petr Olivka

               This file is part of 'mphidflash' program.

               'mphidflash' 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
               3 of the License, or (at your option) any later version.

               'mphidflash' 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 'mphidflash' source code.  If not,
               see <http://www.gnu.org/licenses/>.

 ****************************************************************************/
//---------------------------------------------------------------------------

/* Program's actions aren't necessarily performed in command-line order.
   Bit flags keep track of options set or cleared during input parsing,
   then are singularly checked as actions are performed.  Some actions
   (such as writing) don't have corresponding bits here; certain non-NULL
   string values indicate such actions should occur. */
#define ACTION_UNLOCK (1 << 0)
#define ACTION_ERASE  (1 << 1)
#define ACTION_VERIFY (1 << 2)
#define ACTION_RESET  (1 << 3)


//---------------------------------------------------------------------------

ErrorCode __fastcall TBootloader::usbWrite(const char len, const char read, int timeout)

{
  if (!USBPORT->Write(usbBuf, 64)) return ERR_USB_WRITE;
  if (read)
   {
    if (USBPORT->Read(usbBuf, 64, timeout) == 0) return ERR_USB_READ;
   }
  Application->ProcessMessages();
  //Delay(20,1);
  return ERR_NONE;
}
//---------------------------------------------------------------------------

/****************************************************************************
Function : hexSetBytesPerAddress
Description : Sets given byte width
Parameters : unsigned char Bytes per address
Returns : Nothing (void)
****************************************************************************/
void __fastcall TBootloader::hexSetBytesPerAddress(unsigned char bytes)
{
 bytesPerAddress = bytes;
}
//---------------------------------------------------------------------------
/****************************************************************************
 Function    : hexOpen
 Description : Load an Intel hex file to memory.
 Parameters  : char*      Filename (must be non-NULL).
 Returns     : ErrorCode  ERR_NONE     Success
                          ERR_HEX_OPEN File not found or no read permission
                          ERR_HEX_STAT fstat() call failed for some reason
                          ERR_HEX_MMAP Memory-mapping failed
 ****************************************************************************/

ErrorCode __fastcall TBootloader::hexOpen(char* const filename)
{
  ErrorCode status = ERR_HEX_OPEN;
  hexFileData = NULL;
  FILE *hexFd = NULL;       /* Open hex file descriptor    */
  if (filename && filename[0]) hexFd = fopen(filename, "rb");
  if(hexFd)
   {
    struct stat filestat;

    status = ERR_HEX_STAT;
    if(!fstat(fileno(hexFd),&filestat))
     {
      status = ERR_HEX_MMAP;
      hexFileSize = filestat.st_size;
      hexFileData = (char*)malloc(hexFileSize);
      if (hexFileData)
       {
        fread(hexFileData, hexFileSize, 1, hexFd);
        hexPlusOne = &hexFileData[1];

        status = ERR_NONE;
       }
     }
    (void)fclose(hexFd);
   }
  return status;
}
//---------------------------------------------------------------------------


/* check memory address & length are in a programmable memory area, as reported by device's Bootloader */
int __fastcall TBootloader::verifyBlockProgrammable(unsigned int *addr, char *len)
{
  int i;
  unsigned int isA, isL, MA, ML;
  for ( i = 0; i < devQuery.memBlocks; i++ )
  {
    /* only look at programmable memory blocks */
    if ( !devQuery.mem[i].Type )  continue;

    /* calc if first or last address is in this block */
    MA = devQuery.mem[i].Address;
    ML = devQuery.mem[i].Length;
    isA = ( *addr >= MA ) && ( *addr < MA + ML );
    isL = ( *addr + *len > MA ) && ( *addr + *len <= MA + ML );

    /* loop if neither */
    if ( !isA && !isL ) continue;

    /* both addresses is fine */
    if ( isA && isL )  return 0;

    /* if only start address we adjust length to end of block */
    if ( isA ) *len = ( MA + ML ) - *addr;

    /* if only last address we adjust start to start of block */
    if ( isL )
     {
      *len = ( *addr + *len ) - MA;
      *addr = MA;
     }
    return 0;
  }
  return 1;
}
//---------------------------------------------------------------------------

/****************************************************************************
 Function    : atoh (inline pseudo-function)
 Description : Converts two adjacent ASCII characters (representing an 8-bit
               hexadecimal value) to a numeric value.
 Parameters  : int            Index (within global hex array) to start of
                              input string; must contain at least two chars.
 Returns     : unsigned char  Numeric result; 0 to 255.
 Notes       : Range checking of input characters is somewhat slovenly;
               all input is assumed to be in the '0' to '9' and 'A' to 'F'
               range.  But if any such shenanigans were to occur, the line
               checksum will likely catch it.
 ****************************************************************************/
#define atoh(pos) \
  ((((hexFileData[pos] <= '9') ? (hexFileData[pos] - '0') : \
     (0x0a + toupper(hexFileData[pos]) - 'A')) << 4) |      \
   ( (hexPlusOne [pos] <= '9') ? (hexPlusOne [pos] - '0') : \
     (0x0a + toupper(hexPlusOne [pos]) - 'A')))
//---------------------------------------------------------------------------

/****************************************************************************
 Function    : issueBlock
 Description : Send data over USB bus to device.
 Parameters  : unsigned int  Destination address on PIC device.
               char          Byte count (max 56).
               char          Verify vs. write.
 Returns     : ErrorCode     ERR_NONE on success, or error code as returned
                             from usbWrite();
 ****************************************************************************/
ErrorCode __fastcall TBootloader::issueBlock(unsigned int addr, char len, char verify)
{
  ErrorCode status;

  // check device memory blocks are programmable
  if (verifyBlockProgrammable( &addr, &len ))  return ERR_NONE;
  // length must be even
  if (len & 1)  hexBuf[len++] = 0xff;

  /* Short data packets need flushing */
  if (len == 0)
   {
    usbBuf[0] = PROGRAM_COMPLETE;
    status = usbWrite(1,0);
    return status;
   }

  bufWrite32(usbBuf, 1, addr/bytesPerAddress);
  usbBuf[5] = len;

  if(verify)
   {
    usbBuf[0] = GET_DATA;
    if(ERR_NONE == (status = usbWrite(6, 1)))
     {
      return (memcmp(&usbBuf[64-len], hexBuf, len) ? ERR_VERIFY : ERR_NONE);
     }
   }
  else
   {
    usbBuf[0] = PROGRAM_DEVICE;
    /* Regardless of actual byte count, data packet is always
       64 bytes.  Following the header, the bootloader wants the
       data portion 'right justified' within packet.  Odd. */
    memcpy(&usbBuf[64 - len], hexBuf, len);
    if((ERR_NONE == (status = usbWrite(64, 0))) && (len < 56))
     {
      /* Short data packets need flushing */
      usbBuf[0] = PROGRAM_COMPLETE;
      status    = usbWrite(1, 0);
     }
   }
  return status;
}
//---------------------------------------------------------------------------

/****************************************************************************
 Function    : hexWrite
 Description : Writes (and optionally verifies) currently-open hex file to
               device.
 Parameters  : char       Verify (1) vs. write (0).
 Returns     : ErrorCode  ERR_NONE on success, else various other values as
                          defined in mphidflash.h.
 Notes       : USB device and hex file are both assumed already open and
               valid; no checks performed here.
 ****************************************************************************/
ErrorCode __fastcall TBootloader::hexWrite(const char verify)
{
  char         *ptr, pass;
  ErrorCode     status;
  unsigned int  checksum, i, end, offset;
  short         bufLen;
  unsigned int  len, type, addrHi, addrLo, addr32, addrSave;

  for(pass=0;pass<=verify;pass++)
   {
    offset   = 0; /* Start at beginning of hex file         */
    bufLen   = 0; /* Hex buffer initially empty             */
    addrHi   = 0; /* Initial address high bits              */
    addrSave = 0; /* PIC start addr for hex buffer contents */
    addr32   = 0;

    if (pass) Memo->Lines->Add("Verifying:");

    for(;;)   /* Each line in file */
     {
      /* Line start contains length, 16-bit address and type */
      if (3 != sscanf(&hexFileData[offset],":%02x%04x%02x",
        &len,&addrLo,&type)) return ERR_HEX_SYNTAX;

      /* Position of %02x checksum at end of line */
      end = offset + 9 + len * 2;

      /* Verify checksum on first (write) pass */
      if (!pass)
       {
        for(checksum = 0,i = offset + 1;i < end;
          checksum = (checksum + (0x100 - atoh(i))) & 0xff,i += 2);
        if((unsigned)atoh(end) != checksum) return ERR_HEX_CHECKSUM;
       }

      /* Process different hex record types.  Using if/else rather
         than a switch in order to better handle EOF cases (allows
         simple 'break' rather than goto or other nasties). */

      if (0 == type)  /* Data record */
       {
        /* If new record address is not contiguous with prior record,
           issue accumulated hex data (if any) and start anew. */
        if ((addrHi + addrLo) != addr32)
         {
          addr32 = addrHi + addrLo;
          if (bufLen)
           {
            if(ERR_NONE != (status = issueBlock(addrSave,bufLen,pass)))
              return status;
            bufLen = 0;
           }
          addrSave = addr32;
         }

        /* Parse bytes from line into hexBuf */
        for(i = offset + 9;i < end;i += 2)
         {
          hexBuf[bufLen++] = atoh(i); /* Add to hex buffer */
          /* If buffer is full, issue block and start anew */
          if (sizeof(hexBuf) == bufLen)
           {
            if(ERR_NONE != (status = issueBlock(addrSave,bufLen,pass)))
              return status;
            bufLen = 0;
           }

          /* Increment address, wraparound as per hexfile spec */
          if (0xffffffff == addr32)
           {
            /* Wraparound.  If any hex data, issue and start anew. */
            if (bufLen)
             {
              if (ERR_NONE != (status = issueBlock(addrSave,bufLen,pass)))
                  return status;
              bufLen = 0;
             }
            addr32 = 0;
           }
          else addr32++;

          /* If issueBlock() used, save new address for next block */
          if (!bufLen) addrSave = addr32;
        }
       }
      else
      if (1 == type) break; /* EOF record */
      else
      if (4 == type)  /* Extended linear address record */
       {
        if (1 != sscanf(&hexFileData[offset+9],"%04x",&addrHi))
          return ERR_HEX_SYNTAX;
        addrHi <<= 16;
        addr32 = addrHi;
        /* Assume this means a noncontiguous address jump; issue block
           and start anew.  The prior noncontiguous address code should
           already have this covered, but in the freak case of an
           extended address record with no subsequent data, make sure
           the last of the data is issued. */
        if (bufLen)
         {
          if (ERR_NONE != (status = issueBlock(addrSave,bufLen,pass)))
            return status;
          bufLen   = 0;
         }
        addrSave = addr32;
       }
      else
      if (5 == type)  /* Start address */
       {
        /* Ignore */
       }
      else  /* Unsupported record type */
       {
        return ERR_HEX_RECORD;
       }

      /* Advance to start of next line (skip CR/LF/etc.), unless EOF */
      if (NULL == (ptr = strchr(&hexFileData[end+2],':'))) break;

      ProgressBar->Position = offset;
      ProgressBar->Max = (int)hexFileSize;
      Application->ProcessMessages();

      offset = ptr - hexFileData;
     }

    /* At end of file, issue any residual data (counters reset at top) */
    if (bufLen && (ERR_NONE != (status = issueBlock(addrSave,bufLen,pass))))
        return status;

    /* Make sure last data is flushed (issueBlock() does this
       automatically if less than 56 bytes...but if the last packet
       is exactly this size, an explicit flush is done here). */
    if (!pass && (bufLen == 56))
     {
      usbBuf[0] = PROGRAM_COMPLETE;
      if(ERR_NONE != (status = usbWrite(1,0))) return status;
     }
   }
  return ERR_NONE;
}
//---------------------------------------------------------------------------

/****************************************************************************
 Function    : hexClose
 Description : Unmaps and closes previously-opened hex file.
 Parameters  : None (void)
 Returns     : Nothing (void)
 Notes       : File is assumed to have already been successfully opened
               by the time this function is called; no checks performed here.
 ****************************************************************************/
void __fastcall TBootloader::hexClose(void)
{
  if (hexFileData) free(hexFileData);
  hexFileData = NULL;
  HexLoad = false;
}
//---------------------------------------------------------------------------

ErrorCode __fastcall TBootloader::Query(void)
{
  int i;
  ErrorCode status = ERR_NONE;

  if (USBPORT->isConnected())
   {
    usbBuf[0] = QUERY_DEVICE;
    if(ERR_NONE == (status = usbWrite(1,1)))
     {
      memcpy(&devQuery, usbBuf, 64);
      i = 0;
      while (devQuery.mem[i].Type != TypeEndOfTypeList) i++;
      devQuery.memBlocks = i;
      for (i = 0; i < devQuery.memBlocks; i++)
        if(devQuery.mem[i].Type == TypeProgramMemory)
         {
          Memo->Lines->Add(AnsiString().sprintf("USB HID Device %d bytes free", devQuery.mem[i].Length));
          break;
         }
      switch (devQuery.DeviceFamily)
       {
        case DEVICE_FAMILY_PIC18:
         hexSetBytesPerAddress(1);
         Memo->Lines->Add("Device family: PIC18");
        break;
        case DEVICE_FAMILY_PIC24:
         hexSetBytesPerAddress(2);
         Memo->Lines->Add("Device family: PIC24");
        break;
        case DEVICE_FAMILY_PIC32:
         hexSetBytesPerAddress(1);
         Memo->Lines->Add("Device family: PIC32");
        break;
        default:
         hexSetBytesPerAddress(1);
         Memo->Lines->Add("Device family: Unknown. Bytes per address set to 1.");
        break;
       }
     }
    else Memo->Lines->Add("Device query error");
   }
  return status;
}
//---------------------------------------------------------------------------

ErrorCode __fastcall TBootloader::ExtQuery(void)
{
  int i;
  ErrorCode status = ERR_NONE;

  if (USBPORT->isConnected())
   {
    usbBuf[0] = QUERY_EXTENDED_INFO;
    if(ERR_NONE == (status = usbWrite(1,1)))
     {
      memcpy(&extQueryInfo, usbBuf, 64);
      Memo->Lines->Add(AnsiString().sprintf("Bootloader Version: %d.%d", extQueryInfo.majorVer, extQueryInfo.minorVer));
     }
   }
  return status;
}
//---------------------------------------------------------------------------

ErrorCode __fastcall TBootloader::Reset(void)
{
  ErrorCode status;
  Memo->Lines->Add("Resetting device...");
  usbBuf[0] = RESET_DEVICE;
  status = usbWrite(1,0);
  return status;
}
//---------------------------------------------------------------------------

ErrorCode __fastcall TBootloader::Program(void)
{
  unsigned char actions;
  char eol;        /* 1 = last command-line arg */
  ErrorCode status = ERR_NONE;
  int i;


  if (HexLoad && USBPORT->isConnected())
   {
    status = Query();
    if (ERR_NONE == status) status = ExtQuery();

    actions = ACTION_VERIFY;
    if (CBUnlock->Checked) actions |= ACTION_UNLOCK;
    actions |= ACTION_ERASE;

    if((ERR_NONE == status) && (actions & ACTION_UNLOCK))
     {
      Memo->Lines->Add("Unlocking configuration memory...");
      usbBuf[0] = UNLOCK_CONFIG;
      usbBuf[1] = UNLOCKCONFIG;
      status    = usbWrite(2,0);
     }
    // disable all configuration blocks in devQuery if locked
    if ( !(actions & ACTION_UNLOCK) )
     {
      for ( i = 0; i < devQuery.memBlocks; i++ )
       if ( devQuery.mem[i].Type == TypeConfigWords )
         devQuery.mem[i].Type = 0;
     }

    if((ERR_NONE == status) && (actions & ACTION_ERASE))
     {
      Memo->Lines->Add("Erasing...");
      usbBuf[0] = ERASE_DEVICE;
      status    = usbWrite(1,0);
      /* The query here isn't needed for any technical
         reason, just makes the presentation better.
         The ERASE_DEVICE command above returns
         immediately...subsequent commands can be made
         but will pause until the erase cycle completes.
         So this query just keeps the "Writing" message
         or others from being displayed prematurely. */
      //Delay(5000,1);
      usbBuf[0] = QUERY_DEVICE;
      status    = usbWrite(1,1, 5000);
      if (ERR_NONE == status) Memo->Lines->Add(" Ok");
     }

    if (ERR_NONE == status)
     {
      Memo->Lines->Add("Writing hex file...");
      status = hexWrite((actions & ACTION_VERIFY) != 0);
      if (ERR_NONE == status) Memo->Lines->Add(" Ok");
     }

    if(ERR_NONE == status)
     {
      Delay(500,1);
      Memo->Lines->Add("Sign flash...");
      usbBuf[0] = SIGN_FLASH;
      status = usbWrite(1,0);
      usbBuf[0] = QUERY_DEVICE;
      status    = usbWrite(1,1, 5000);
      if (ERR_NONE == status) Memo->Lines->Add(" Ok");
     }
   }
  Error(status);
  return status;
}

//---------------------------------------------------------------------------
void __fastcall TBootloader::Error(ErrorCode status)
{
  const char * const errorString[ERR_EOL] =
   {
    "Missing or malformed command-line argument",
    "Command not recognized",
    "Device not found (is device attached and in Bootloader mode?)",
    "USB initialization failed (phase 1)",
    "USB initialization failed (phase 2)",
    "Device could not be opened for I/O",
    "USB write error",
    "USB read error",
    "Could not open hex file for input",
    "Could not query hex file size",
    "Could not map hex file to memory",
    "Unrecognized or invalid hex file syntax",
    "Bad end-of-line checksum in hex file",
    "Unsupported record type in hex file",
    "Verify failed"
   };


  if(ERR_NONE != status)
   {
    if(status <= ERR_EOL)
     Memo->Lines->Add(AnsiString().sprintf("Error :%s", errorString[status - 1]));
    else
     Memo->Lines->Add("Error of indeterminate type.");
   }
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

AnsiString AddBackslash(const AnsiString Str)
{
 if (Str != "") return IncludeTrailingPathDelimiter(Str);
 else return Str;
}
//---------------------------------------------------------------------------

AnsiString GetAppFolder(void)
{
 char AppData[1024];
 AnsiString AppDataFolder;

 if (GetEnvironmentVariable("APPDATA", AppData, 1024))
  AppDataFolder.sprintf("%s\\GTK\\LightMtr\\", AppData);
 if (DirectoryExists(AppDataFolder)) return AppDataFolder;
 else return AddBackslash(ExtractFilePath(Application->ExeName));
}

//---------------------------------------------------------------------------
__fastcall TBootloader::TBootloader(TComponent* Owner)
        : TForm(Owner)
{
 Connected = false;
 HexLoad = false;
 USBPORT = NULL;
 hexFileData = NULL;
 bytesPerAddress = 1;
}
//---------------------------------------------------------------------------
void __fastcall TBootloader::FormCreate(TObject *Sender)
{
 //Check if some program with same mutex already run
 //   1240      Vendor ID
 //     60      Product ID: Custom HID device demo
 //      0      Device release number in BCD format
 HANDLE mutex = CreateMutex(NULL,true,"MutexOfHID_1240_60_0");
 DWORD result = WaitForSingleObject(mutex,0);
 if(result != WAIT_OBJECT_0)
  {
   ShowMessage("Program already run");
   Application->ShowMainForm = false;
   Application->Terminate();
  }
 AnsiString AppDataFolder = GetAppFolder();

 IniFile = AppDataFolder+ExtractFileName(ChangeFileExt(Application->ExeName, ".ini"));
 if (!FileExists(IniFile))
  {
   AppDataFolder = ExtractFilePath(Application->ExeName);
   IniFile = ChangeFileExt(Application->ExeName, ".ini");
  }

 USBPORT = new CUSBDevice();
 ProgressBar->Position = 0;
}
//---------------------------------------------------------------------------
void __fastcall TBootloader::FormDestroy(TObject *Sender)
{
 delete USBPORT;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TBootloader::Delay(DWORD ms, int SLEEP)
{
  DWORD time, time1, time2;
  timeBeginPeriod(1);
  time1 = GetTickCount();
  do
   {
    if (SLEEP < 0) Sleep(ms);
    else Sleep(SLEEP);
    if (ms > 50) Application->ProcessMessages();
    time2 = GetTickCount();
    time = time2 - time1;
   }
  while (time < ms);
  timeEndPeriod(1);
}

//---------------------------------------------------------------------------

AnsiString __fastcall TBootloader::ConnectString(void)
{
 wchar_t str[256];
 AnsiString Res = "Not connected";
 if (!USBPORT->Product(str, 255)) return Res;
 Res = "Connected to " + WideCharToString(str) + " (c) ";
 if (!USBPORT->Manufacturer(str, 255)) return Res;
 return Res + WideCharToString(str);
}

//---------------------------------------------------------------------------
void __fastcall TBootloader::BConnectClick(TObject *Sender)
{
 if (!USBPORT->isConnected()) USBPORT->Connect(1240, 60, 0);
 else USBPORT->Disconnect();
 Buttons();
 if (USBPORT->isConnected())
  {
   Memo->Lines->Clear();
   Memo->Lines->Add("Device connected");
   BVerClick(Sender);
  }
 else
  {
   ProgressBar->Position = 0;
   hexClose();
   Memo->Lines->Add("Device disconnected");
  }
 CBUnlock->Checked = false;
}
//---------------------------------------------------------------------------
void __fastcall TBootloader::Buttons(void)
{
 if (USBPORT->isConnected())
  {
   BConnect->Caption = "&Disconnect";
   BHex->Enabled = true;
   BVer->Enabled = true;
   if (HexLoad)
    {
     BAll->Enabled = true;
     BRun->Enabled = true;
    }
   else
    {
     BAll->Enabled = false;
     BRun->Enabled = false;
    }
  }
 else
  {
   BConnect->Caption = "&Connect";
   BHex->Enabled = false;
   BVer->Enabled = false;
   BAll->Enabled = false;
   BRun->Enabled = false;
  }
}
//---------------------------------------------------------------------------
void __fastcall TBootloader::BVerClick(TObject *Sender)
{
 if (USBPORT->isConnected())
  {
    AnsiString Ver = ConnectString();
    Memo->Lines->Add(Ver);
    Query();
    ExtQuery();
  }
}
//---------------------------------------------------------------------------

void __fastcall TBootloader::BHexClick(TObject *Sender)
{
 OpenDialog->InitialDir = ExtractFileDir(IniFile);
 OpenDialog->Filter = "Hex File (*.hex)|*.hex|";
 OpenDialog->Title = "Open HEX file";
 if (OpenDialog->Execute())
  {
   hexClose();
   CBUnlock->Checked = false;
   ErrorCode status = hexOpen(OpenDialog->FileName.c_str());
   HexLoad = (hexFileData&&(status == ERR_NONE));
   if (HexLoad) Memo->Lines->Add("Hex file loaded successfully");
   else Memo->Lines->Add("Hex file not loaded");
   Buttons();
  }
}
//---------------------------------------------------------------------------

void __fastcall TBootloader::BRunClick(TObject *Sender)
{
 ErrorCode status = Reset();

 if (status != ERR_NONE) Memo->Lines->Add("Error");
}
//---------------------------------------------------------------------------

void __fastcall TBootloader::BAllClick(TObject *Sender)
{
 Program();
 ProgressBar->Position = 0;
}
//---------------------------------------------------------------------------

void __fastcall TBootloader::FormClose(TObject *Sender,
      TCloseAction &Action)
{
 USBPORT->Disconnect();
 hexClose();
 Buttons();
 ProgressBar->Position = 0;
 CBUnlock->Checked = false;
 Memo->Lines->Add("Device disconnected");
}

//---------------------------------------------------------------------------
void __fastcall TBootloader::WMChangeDev(TMessage &Message)
{
  if (USBPORT->isConnected())
   {
    USBPORT->Connect();
    if (!USBPORT->isConnected())
     {
      ProgressBar->Position = 0;
      Memo->Lines->Add("Device disconnected");
     }
    Buttons();
   }
  else BConnectClick(this);
  //int Lparam = Message.LParam;
}

//---------------------------------------------------------------------------

