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

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "UserData.h"


//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
        , thProcessThread(NULL)
        , ulCloseCount(0)
        , iDebugUse(0)
{
    /* Get path where the program is. */
    char szPath[MAX_PATH];
    GetModuleFileName(GetModuleHandle(NULL), szPath, MAX_PATH);
    AnsiString sAppPath;
    sAppPath.printf("%s", szPath);
    int iLast = sAppPath.LastDelimiter("\\");
    sAppPath = sAppPath.SubString(1, iLast);
    strcpy(app_path, sAppPath.c_str());
    AnsiString sAppName;
    sAppName.printf("%s", szPath);
    sAppName = sAppName.SubString(iLast+1, MAX_PATH-iLast);
    strcpy(app_name, sAppName.c_str());


    /* Reset buttons state. */
    memset((void*)(&udGUI), 0, sizeof(UserData));
    /* Request debug folder init. */
    udGUI.btn[BTN_DBGFOLDER] = 1;
}
//---------------------------------------------------------------------------
//http://gnuwin32.sourceforge.net/version.c.txt
BOOL GetAppVersion( char *LibName, WORD *MajorVersion, WORD *MinorVersion, WORD *BuildNumber, WORD *RevisionNumber )
{
 DWORD dwHandle, dwLen;
 UINT BufLen;
 LPTSTR lpData;
 VS_FIXEDFILEINFO *pFileInfo;
 dwLen = GetFileVersionInfoSize( LibName, &dwHandle );
 if (!dwLen)
  return FALSE;

 lpData = (LPTSTR) malloc (dwLen);
 if (!lpData)
  return FALSE;

 if( !GetFileVersionInfo( LibName, dwHandle, dwLen, lpData ) )
 {
  free (lpData);
  return FALSE;
 }
 if( VerQueryValue( lpData, "\\", (LPVOID *) &pFileInfo, (PUINT)&BufLen ) )
 {
  *MajorVersion = HIWORD(pFileInfo->dwFileVersionMS);
  *MinorVersion = LOWORD(pFileInfo->dwFileVersionMS);
  *BuildNumber = HIWORD(pFileInfo->dwFileVersionLS);
  *RevisionNumber = LOWORD(pFileInfo->dwFileVersionLS);
  free (lpData);
  return TRUE;
 }
 free (lpData);
 return FALSE;
}
//---------------------------------------------------------------------------
scl_settings_t _log_global_settings;
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    WORD ver[4];
    GetAppVersion(Application->ExeName.c_str(), ver+0, ver+1, ver+2, ver+3);
    AnsiString str;
    str.printf("SatCom debugger V%d.%d [Build #%d%d on %s, %s] (S.A.T.U.R.N. contest)", ver[0], ver[1], ver[2], ver[3], __DATE__, __TIME__);
    Form1->Caption = str;

    /* Init log engine. */
    scl_init(&_log_global_settings, 1/*satlink_init*/);
    scl_log(SCLT_HOSTPG, 5/*level*/, "Program version = %s", str.c_str());

    /* Init form items. */
    InitFormItems();

    /* Parse command line. */
    memset((void*)cl_buffer, 0, sizeof(cl_buffer));
    argc=0;
    for(int i=0; i<MAXARGS; i++) argv[i] = NULL;

    int cl_buffer_pos = 0;
    for(int i=0; i<=ParamCount(); i++)
    {
        if(i==1)
        {
            /* Append parameters from ini file. */
            parse_satlink_inifile(inifilebuffer, ParamStr(i).c_str()/*filename*/, &argc, argv);
        }
        else
        {
            /* Append parameters from command line. */
            AnsiString str = ParamStr(i);
            int strl = str.Length();
            argv[argc] = cl_buffer + cl_buffer_pos; argc++;
            for(int i=0; i<strl; i++)
            {
                cl_buffer[cl_buffer_pos] = str[i+1]; cl_buffer_pos++;
            }
            cl_buffer[cl_buffer_pos] = '\0'; cl_buffer_pos++;
        }
    }

    /* Log grid init. */
    sgDbgText->RowCount = 256;
    sgDbgText->ColCount = 2;
    sgDbgText->Cells[0][0] = "ID";
    for(int i=1; i<sgDbgText->RowCount; i++) sgDbgText->Cells[0][i] = IntToStr(i);
    sgDbgText->ColWidths[0] = 30;
    sgDbgText->Cells[1][0] = "Message";
    sgDbgText->ColWidths[1] = 245;

    /* Start Send/Receive/Debuger thread. */
    thProcessThread = new ProcessThread(false);
    thProcessThread->OnTerminate = ProcessThreadEnd;
    strcpy(thProcessThread->app_path, app_path);

    /* Start status bar refresh timer. */
    tmStatusBarRefresh->Enabled = true;
    /* Start memview convert refresh timer. */
    tmMemviewRefresh->Enabled = true;

    //EnableWindow(Handle, false);
}
//---------------------------------------------------------------------------
void __fastcall  TForm1::ProcessThreadEnd(TObject *Sender)
{
    //EnableWindow(Handle, true);

    delete thProcessThread;
    thProcessThread = NULL;

    //this->DoDestroy();
    DoClose(caFree);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    SaveFormItems();

    if(thProcessThread)
    {
        /* End logging engine. */
        scl_end();

        Form1->mLog->Lines->Add("Waiting for thread to close ...");
        ulCloseCount++;
        /* If it is the first time exit button is clicked,
         * give some time for the thread to exit.
         */
        if(ulCloseCount < 2) Action = caNone;
        thProcessThread->Terminate();
        thProcessThread->iTerminateRequest = 1;

        /* Stop timers. */
        tmStatusBarRefresh->Enabled = false;
        tmMemviewRefresh->Enabled = false;
    }
    else
    {
        /* End logging engine. */
        scl_end();

        Form1->mLog->Lines->Add("Thread ended. You can exit the application.");
        Action = caFree;
        exit(0);
    }
}


//---------------------------------------------------------------------------
void /*__fastcall*/ TForm1::LogOut(const char *string, ...)
{
    /* Format to memory */
    char str[8192];
    va_list argptr;
    va_start(argptr, string);
    vsnprintf(str, sizeof(str), string, argptr);
    va_end(argptr);

    /* Add date */
    char strout[8192];
    if(strlen(str) != 0)
    {
        SYSTEMTIME sTime;
        GetLocalTime(&sTime);
        snprintf(strout, sizeof(strout), "[%02d:%02d:%02d:%03d]%s",
                    sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds,
                    str);
    }
    else
    {
        snprintf(strout, sizeof(strout), "");
    }

    mLog->Lines->Add(strout);
}
//---------------------------------------------------------------------------


/* Settings file realted stuff. */
#define SET_FILE "satlink.ini"
#define SET_SECTION "satlink"
#define SET_STRSZ 8192
#define SET_MAXITEMS 256
//---------------------------------------------------------------------------
typedef struct
{
    TEdit* obj;
    char*  ent;
    char*  def;
} setting_edit_t;
setting_edit_t _setting_edit[SET_MAXITEMS];
typedef struct
{
    TCheckBox* obj;
    char*      ent;
    int        def;
} setting_checkbox_t;
setting_checkbox_t _setting_checkbox[SET_MAXITEMS];
typedef struct
{
    TComboBox* obj;
    char*      ent;
    int        typ; //0: save index, 1:save string
    int        def0;
    char*      def1;
} setting_combobox_t;
setting_combobox_t _setting_combobox[SET_MAXITEMS];

AnsiString __fastcall TForm1::MakeSettingsFilePath(void)
{
    /* Must init items list after the form constructor has been called. */
    int i;
    /* Set edit stuff. */
    i = 0;
    _setting_edit[i].obj = edtIniPath        ; _setting_edit[i].ent = "ini_file"           ; _setting_edit[i].def = "usbdl.ini"           ; i++;
    _setting_edit[i].obj = edtStartAddress   ; _setting_edit[i].ent = "param_start_address"; _setting_edit[i].def = "06004000"            ; i++;
    _setting_edit[i].obj = edtLength         ; _setting_edit[i].ent = "param_len"          ; _setting_edit[i].def = "442364"              ; i++;
    _setting_edit[i].obj = edtDownFile       ; _setting_edit[i].ent = "dst_file"           ; _setting_edit[i].def = "receivefile.bin"     ; i++;
    _setting_edit[i].obj = edtUpFile         ; _setting_edit[i].ent = "src_file"           ; _setting_edit[i].def = "sendfile.bin"        ; i++;
    _setting_edit[i].obj = edtDbgFile        ; _setting_edit[i].ent = "dbg_binary"         ; _setting_edit[i].def = "myproject\\cd\\0.bin"; i++;
    _setting_edit[i].obj = edtDbgStartAddress; _setting_edit[i].ent = "dbg_address"        ; _setting_edit[i].def = "06004000"            ; i++;
    _setting_edit[i].obj = edtDbgFolder      ; _setting_edit[i].ent = "dbg_folder"         ; _setting_edit[i].def = ""                    ; i++;
    _setting_edit[i].obj = edtMainLogPath    ; _setting_edit[i].ent = "main_dbg_log"       ; _setting_edit[i].def = "satlink_main.log"    ; i++;
    _setting_edit[i].obj = edtLogPath        ; _setting_edit[i].ent = "dbg_log"            ; _setting_edit[i].def = "satlink_dbg.log"     ; i++;
    _setting_edit[i].obj = edtLogSettings    ; _setting_edit[i].ent = "dbg_settings"       ; _setting_edit[i].def = "log_settings.ini"    ; i++;
    _setting_edit[i].obj = edtDebugSleep     ; _setting_edit[i].ent = "dbg_interval"       ; _setting_edit[i].def = "100"                 ; i++;
    _setting_edit[i].obj = edtMemViewAddress ; _setting_edit[i].ent = "memview_adr"        ; _setting_edit[i].def = "06004000"            ; i++;
    _setting_edit[i].obj = NULL              ; _setting_edit[i].ent = NULL                 ; _setting_edit[i].def = NULL                  ; //i++;
    /* Set checkbox stuff. */
    i = 0;
    _setting_checkbox[i].obj = cbSilentDbg  ; _setting_checkbox[i].ent = "silent_dbg"  ; _setting_checkbox[i].def = 1; i++;
    _setting_checkbox[i].obj = cbLengthKB   ; _setting_checkbox[i].ent = "kb_len"      ; _setting_checkbox[i].def = 0; i++;
    _setting_checkbox[i].obj = cbExecFlag   ; _setting_checkbox[i].ent = "exec_flag"   ; _setting_checkbox[i].def = 1; i++;
    _setting_checkbox[i].obj = NULL         ; _setting_checkbox[i].ent = NULL          ; _setting_checkbox[i].def = 0; //i++;

    /* Set combo box stuff. */
    i = 0;
    _setting_combobox[i].obj = cbLogType       ; _setting_combobox[i].ent = "log_type"     ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 = 0;  _setting_combobox[i].def1 = NULL    ; i++;
    _setting_combobox[i].obj = cbLogOutput     ; _setting_combobox[i].ent = "log_output"   ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 = 1;  _setting_combobox[i].def1 = NULL    ; i++;
    _setting_combobox[i].obj = cbLogLevel      ; _setting_combobox[i].ent = "log_level"    ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 =10;  _setting_combobox[i].def1 = NULL    ; i++;
    _setting_combobox[i].obj = cbParamText     ; _setting_combobox[i].ent = "param_text"   ; _setting_combobox[i].typ = 1; _setting_combobox[i].def0 =-1;  _setting_combobox[i].def1 = "port"  ; i++;
    _setting_combobox[i].obj = cbParamValue    ; _setting_combobox[i].ent = "param_value"  ; _setting_combobox[i].typ = 1; _setting_combobox[i].def0 =-1;  _setting_combobox[i].def1 = "COM3"  ; i++;
    _setting_combobox[i].obj = cbTemplate      ; _setting_combobox[i].ent = "adr_tmplt"    ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 = 0;  _setting_combobox[i].def1 = NULL    ; i++;
    _setting_combobox[i].obj = cbMemSize       ; _setting_combobox[i].ent = "memview_size" ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 = 0;  _setting_combobox[i].def1 = NULL    ; i++;
    _setting_combobox[i].obj = cbMemoryConvert ; _setting_combobox[i].ent = "memview_disp" ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 = 0;  _setting_combobox[i].def1 = NULL    ; i++;
    _setting_combobox[i].obj = NULL            ; _setting_combobox[i].ent = NULL           ; _setting_combobox[i].typ = 0; _setting_combobox[i].def0 = 0;  _setting_combobox[i].def1 = NULL    ; //i++;



    /* Set settings file path. */
    AnsiString sIniPath = app_path;
    sIniPath += SET_FILE;

    return sIniPath;
}


//---------------------------------------------------------------------------
void __fastcall TForm1::LoadSettingsFile(void)
{
    AnsiString sIniPath = MakeSettingsFilePath();
    char sData[SET_STRSZ];

    int i;
    int iValue;
    /* Edit. */
    i=0; while(_setting_edit[i].obj != NULL)
    {
        GetPrivateProfileString(SET_SECTION, _setting_edit[i].ent, _setting_edit[i].def, sData, SET_STRSZ, sIniPath.c_str());
        _setting_edit[i].obj->Text = sData;
        i++;
    }
    /* Check box. */
    i=0; while(_setting_checkbox[i].obj != NULL)
    {
        iValue = (int)GetPrivateProfileInt(SET_SECTION, _setting_checkbox[i].ent, _setting_checkbox[i].def, sIniPath.c_str());
        _setting_checkbox[i].obj->Checked = (iValue ? true : false);
        i++;
    }
    /* Combo box. */
    i=0; while(_setting_combobox[i].obj != NULL)
    {
        if(_setting_combobox[i].typ == 0)
        { /* Use combo box index. */
            iValue = (int)GetPrivateProfileInt(SET_SECTION, _setting_combobox[i].ent, _setting_combobox[i].def0, sIniPath.c_str());
            _setting_combobox[i].obj->ItemIndex = (iValue == -1 ? 0 : iValue);
        }
        else
        { /* Use combo box text. */
            GetPrivateProfileString(SET_SECTION, _setting_combobox[i].ent, _setting_combobox[i].def1, sData, SET_STRSZ, sIniPath.c_str());
            _setting_combobox[i].obj->Text = sData;
        }
        i++;
    }

    /* Window position. */
    Form1->Top  = (int)GetPrivateProfileInt(SET_SECTION, "window_top" , 0, sIniPath.c_str());
    Form1->Left = (int)GetPrivateProfileInt(SET_SECTION, "window_left", 0, sIniPath.c_str());

    /* Extra : debug mode check. */
    iValue = (int)GetPrivateProfileInt(SET_SECTION, "debug", 0, sIniPath.c_str());
    iDebugUse = iValue;
    btnTest1->Visible = (iValue ? true : false);
    btnTest2->Visible = (iValue ? true : false);
    btnTest3->Visible = (iValue ? true : false);
    if(iValue == 0)
    { /* In not debug mode, hide the "Data Link test" page and "Log levels" group. */
        psSettings->Pages[4]->TabVisible = false;
        gbLogLevels->Visible = false;
    }
}

//---------------------------------------------------------------------------
void __fastcall TForm1::SaveSettingsFile(void)
{
    AnsiString sIniPath = MakeSettingsFilePath();

    int i;
    char str[128];
    /* Edit. */
    i=0; while(_setting_edit[i].obj != NULL)
    {
        WritePrivateProfileString(SET_SECTION, _setting_edit[i].ent, _setting_edit[i].obj->Text.c_str(), sIniPath.c_str());
        i++;
    }
    /* Check box. */
    unsigned long ulValue;
    i=0; while(_setting_checkbox[i].obj != NULL)
    {
        WritePrivateProfileString(SET_SECTION, _setting_checkbox[i].ent, (_setting_checkbox[i].obj->Checked ? "1" : "0"), sIniPath.c_str());
        i++;
    }
    /* Combo box. */
    i=0; while(_setting_combobox[i].obj != NULL)
    {
        if(_setting_combobox[i].typ == 0)
        { /* Use combo box index. */
            sprintf(str, "%d", _setting_combobox[i].obj->ItemIndex);
            WritePrivateProfileString(SET_SECTION, _setting_combobox[i].ent, str, sIniPath.c_str());
        }
        else
        { /* Use combo box text. */
            WritePrivateProfileString(SET_SECTION, _setting_combobox[i].ent, _setting_combobox[i].obj->Text.c_str(), sIniPath.c_str());
        }
        i++;
    }

    /* Window position. */
    sprintf(str, "%d", Form1->Top ); WritePrivateProfileString(SET_SECTION, "window_top" , str, sIniPath.c_str());
    sprintf(str, "%d", Form1->Left); WritePrivateProfileString(SET_SECTION, "window_left", str, sIniPath.c_str());
}


//---------------------------------------------------------------------------
/* Init, and load form contents from file. */
void __fastcall TForm1::InitFormItems(void)
{
    cbTemplate->ItemIndex = 0; btnTemplateSetClick(NULL);

    /* Menu bar stuff start. */
    mniSetUSBDataLinkParameters->Tag  = MNI_SETUDLPARAMS;
    mniOpenUSBDataLink->Tag           = MNI_OPENUDL;
    mniSendBackupMemory->Tag          = MNI_SENDBRAM;
    mniExecProgram->Tag               = MNI_EXECPROG;
    mniReceiveBackupMemory->Tag       = MNI_RCVBRAM;
    mniReceiveBIOS->Tag               = MNI_RCVBIOS;
    mniReceiveARPFirmWare->Tag        = MNI_RCVARPFW;
    /* Menu bar stuff end. */


    btnOpen->Tag          = BTN_OPEN;
    btnClose->Tag         = BTN_CLOSE;
    btnParamSet->Tag      = BTN_PARAMSET;
    btnUpload->Tag        = BTN_SEND;
    btnDownload->Tag      = BTN_RECEIVE;

    btnMainLogSave->Tag   = BTN_MAINLOGSAVE;
    btnMainLogClear->Tag  = BTN_MAINLOGCLEAR;
    btnLogLevelSet->Tag   = BTN_LOGLEVELSET;

    btnLogClear->Tag      = BTN_LOGCLEAR;
    btnLogLdSettings->Tag = BTN_LOGLDSETTINGS;
    btnLogSave->Tag       = BTN_LOGSAVE;

    btnFastExec->Tag      = BTN_FASTEXEC;
    btnFastExec2->Tag     = btnFastExec->Tag;

    btnPromptSend->Tag    = BTN_PROMPTSEND;
    btnSetDbgFolder->Tag  = BTN_DBGFOLDER; edtDbgFolder->Tag = BTN_DBGFOLDER;

    btnAdrDown->Tag       = BTN_ADDRDOWN;
    btnAdrUp->Tag         = BTN_ADDRUP;

    btnMemView->Tag       = BTN_MVREAD;
    btnMemViewWrite->Tag  = BTN_MVWRITE;

    btnTargetInfo->Tag    = BTN_TARGETINFO;
    btnVDP1View->Tag      = BTN_VDP1VIEW;

    btnLogOn->Tag         = BTN_SILENTOFF;
    btnLogSilent->Tag     = BTN_SILENTON;

    btnTest1->Tag         = BTN_TEST1;

    /* Load GUI settings. */
    LoadSettingsFile();

    /*cbMemSize->ItemIndex = 0; cbMemoryConvert->ItemIndex = 0; */cbMemoryConvertChange(NULL);
}
/* Save form contents to file. */
void __fastcall TForm1::SaveFormItems(void)
{
    /* save GUI settings. */
    SaveSettingsFile();
}

/* Save GUI contents to UserData (called by ProcessThread) */
void __fastcall TForm1::SaveToUserData(void)
{
    /* [READ-ONLY]CheckBox/RadioButtons states */
    udGUI.address      = strtoul(edtStartAddress->Text.c_str(), NULL, 16);
    udGUI.dbg_address  = strtoul(edtDbgStartAddress->Text.c_str(), NULL, 16);
    udGUI.length       = strtoul(edtLength->Text.c_str(), NULL, 10);
    udGUI.dbg_sleep    = strtoul(edtDebugSleep->Text.c_str(), NULL, 10);
    udGUI.length_is_KB = (cbLengthKB->Checked ? 1 : 0);
    udGUI.exec_flag    = (cbExecFlag->Checked ? 1 : 0);
    udGUI.dbg_use      = (cbDbgUse->Checked ? 1 : 0);
    udGUI.silent_dbg   = (cbSilentDbg->Checked ? 1 : 0);
    /* MemView stuff */
    udGUI.mv_address   = strtoul(edtMemViewAddress->Text.c_str(), NULL, 16);
    udGUI.mv_autoread  = (cbMemViewAuto->Checked ? 1 : 0);
    /* [READ-ONLY]Text fields */
    strcpy(udGUI.main_log_file , (edtMainLogPath->Text.c_str()));

    strcpy(udGUI.log_file      , (edtLogPath->Text.c_str()));
    strcpy(udGUI.log_settings  , (edtLogSettings->Text.c_str()));
    strcpy(udGUI.log_settings  , (edtLogSettings->Text.c_str()));

    strcpy(udGUI.ini_file      , (edtIniPath->Text.c_str()));

    strcpy(udGUI.param_text    , (cbParamText->Text.c_str()));
    strcpy(udGUI.param_value   , (cbParamValue->Text.c_str()));

    udGUI.log_type   = cbLogType->ItemIndex;
    udGUI.log_output = cbLogOutput->ItemIndex;
    udGUI.log_level  = cbLogLevel->ItemIndex;

    strcpy(udGUI.up_file       , (edtUpFile->Text.c_str()));
    strcpy(udGUI.down_file     , (edtDownFile->Text.c_str()));
    strcpy(udGUI.dbg_file      , (edtDbgFile->Text.c_str()));
    strcpy(udGUI.dbg_folder    , (edtDbgFolder->Text.c_str()));

    strcpy(udGUI.prompt_answer , (edtDbgPrompt->Text.c_str()));
}
/* Set UserData contents to GUI */
void __fastcall TForm1::LoadFromUserData(void)
{
}


//---------------------------------------------------------------------------
void __fastcall TForm1::btnClick(TObject *Sender)
{
    TButton* pButton = (TButton*)Sender;
    udGUI.btn[pButton->Tag] = 1;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::mniClick(TObject *Sender)
{
    TMenuItem* pMenuItem = (TMenuItem*)Sender;
    udGUI.btn[pMenuItem->Tag] = 1;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnDownSelectClick(TObject *Sender)
{
    SetCurrentDirectory(app_path);
    sdFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
    if(sdFile->Execute() == true)
    {
        AnsiString str = "Set download file to: ";
        str += sdFile->FileName;
        mLog->Lines->Add(str);
        edtDownFile->Text = sdFile->FileName;
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnUpSelectClick(TObject *Sender)
{
    SetCurrentDirectory(app_path);
    odFile->Filter = "Binary File (*.bin)|*.bin|Any File (*.*)|*.*";
    if(odFile->Execute() == true)
    {
        AnsiString str = "Set upload file to: ";
        str += odFile->FileName;
        mLog->Lines->Add(str);
        edtUpFile->Text = odFile->FileName;
    }
}
//---------------------------------------------------------------------------


void __fastcall TForm1::btnIniOpenClick(TObject *Sender)
{
    odFile->Filter = "Ini File (*.ini)|*.ini|Any File (*.*)|*.*";
    if(odFile->Execute() == true)
    {
        edtIniPath->Text = odFile->FileName;
    }
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnTemplateSetClick(TObject *Sender)
{
    int index = cbTemplate->ItemIndex;
    switch(index)
    {
    case(0): /* Exec area (0x06004000) */
        edtStartAddress->Text = "06004000";
        edtLength->Text       = "442364";
        cbLengthKB->Checked   = false;
        break;

    case(1): /* BIOS */
        edtStartAddress->Text = "00000000";
        edtLength->Text       = "512";
        cbLengthKB->Checked   = true;
        break;

    case(2): /* SMPC Registers */
        edtStartAddress->Text = "00100000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(3): /* Backup RAM */
        edtStartAddress->Text = "00180000";
        edtLength->Text       = "64";
        cbLengthKB->Checked   = true;
        break;

    case(4): /* Work RAM (Low) */
        edtStartAddress->Text = "00200000";
        edtLength->Text       = "1024";
        cbLengthKB->Checked   = true;
        break;

    case(5): /* Action Replay ROM */
        edtStartAddress->Text = "02000000";
        edtLength->Text       = "256";
        cbLengthKB->Checked   = true;
        break;

    case(6): /* Action Replay RAM (1st 512K) */
        edtStartAddress->Text = "02400000";
        edtLength->Text       = "512";
        cbLengthKB->Checked   = true;
        break;

    case(7): /* Action Replay RAM (2nd 512K) */
        edtStartAddress->Text = "02600000";
        edtLength->Text       = "512";
        cbLengthKB->Checked   = true;
        break;

    case(8): /* CD-ROM Registers */
        edtStartAddress->Text = "05800000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(9): /* VDP1 VRAM */
        edtStartAddress->Text = "05C00000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(10): /* VDP1 Framebuffer */
        edtStartAddress->Text = "05C80000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(11): /* VDP1 Registers */
        edtStartAddress->Text = "05D00000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(12): /* VDP2 VRAM */
        edtStartAddress->Text = "05E00000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(13): /* VDP2 CRAM */
        edtStartAddress->Text = "05F00000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(14): /* VDP2 Registers */
        edtStartAddress->Text = "05F80000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(15): /* SCU Registers */
        edtStartAddress->Text = "05FE0000";
        edtLength->Text       = "";
        cbLengthKB->Checked   = true;
        break;

    case(16): /* Work RAM (High) */
        edtStartAddress->Text = "06000000";
        edtLength->Text       = "1024";
        cbLengthKB->Checked   = true;
        break;

    case(17): /* SatCom global vars */
        edtStartAddress->Text = "002FFFC0";
        edtLength->Text       = "64";
        cbLengthKB->Checked   = false;
        break;

    }
}
//---------------------------------------------------------------------------
unsigned char _ucTransfer = 0;
unsigned char _ucDebugger = 0;
void __fastcall TForm1::tmStatusBarRefreshTimer(TObject *Sender)
{
    /* Debug stuff. (Q&D !) */
    if(iDebugUse)
    {
        btnLogLevelsGetClick(NULL);
    }


    /* Update transfer status. */
    if(udGUI.transfer_on)
    {
        _ucTransfer = (_ucTransfer+1)%SB_ACTIVITY_MAXCOUNT;
        switch(_ucTransfer%SB_ACTIVITY_MAXCOUNT)
        {
        case(0):
            Form1->StatusBar->Panels->Items[SB_TRANSFER_INDEX]->Text = "LINK: |*    |";
            break;
        case(1):
            Form1->StatusBar->Panels->Items[SB_TRANSFER_INDEX]->Text = "LINK: |  *  |";
            break;
        case(2):
            Form1->StatusBar->Panels->Items[SB_TRANSFER_INDEX]->Text = "LINK: |    *|";
            break;
        default:
        case(3):
            Form1->StatusBar->Panels->Items[SB_TRANSFER_INDEX]->Text = "LINK: |  *  |";
            break;
        }
    }
    else
    {
        Form1->StatusBar->Panels->Items[SB_TRANSFER_INDEX]->Text = "LINK: OFF";
    }

    /* Update debugger status. */
    if((udGUI.transfer_on == 0) && (udGUI.debugger_on))
    {
        char active;
        char status[20];

        if(udGUI.prompt_on == 1)
        { /* Waiting for prompt data. */
            _ucDebugger = (_ucDebugger+1)%SB_PROMPT_DISP_RATE;
            if(_ucDebugger > (SB_PROMPT_DISP_RATE/2))
            {
                sprintf(status, "PROMPT");
            }
            else
            {
                sprintf(status, "");
            }
        }
        else
        { /* Normal debugger. */
            _ucDebugger = (_ucDebugger+1)%SB_ACTIVITY_MAXCOUNT;
            switch(_ucDebugger%SB_ACTIVITY_MAXCOUNT)
            {
            case(0):
                active = '+';
                break;
            case(1):
                active = 'o';
                break;
            case(2):
                active = '*';
                break;
            default:
            case(3):
                active = 'O';
                break;
            }
            sprintf(status, "DBG: %c [%02X]", active, udGUI.debugger_count);
        }
        Form1->StatusBar->Panels->Items[SB_DEBUGGER_INDEX]->Text = status;
    }
    else
    {
        Form1->StatusBar->Panels->Items[SB_DEBUGGER_INDEX]->Text = "DBG: OFF";
    }
}
//---------------------------------------------------------------------------


/* Send prompt answer when enter key is pressed. */
void __fastcall TForm1::edtDbgPromptKeyPress(TObject *Sender, char &Key)
{
    //if(udGUI.prompt_on)
    //{
    if((Key == '\r') || (Key == '\n'))
    {
        btnClick((TObject *)btnPromptSend);
    }
    //}
}
//---------------------------------------------------------------------------

int _mv_row = 0;
int _mv_col = 0;

void __fastcall TForm1::sgMemorySelectCell(TObject *Sender, int ACol,
      int ARow, bool &CanSelect)
{
    _mv_row = ARow;
    _mv_col = ACol;

    mMemoryValues->Clear();
    //LogOut("SelectCell AClo = %d, ARow = %d", ACol, ARow);
    unsigned long index = ARow * /*sgMemory->ColCount*/16 + ACol;
    unsigned long adr = udGUI.mv_address + index;
#if 0
    unsigned char data[4];
    data[0] = (index+0 < MEMVIEW_DATASIZE ? udGUI.mv_data[index+0] : 0);
    data[1] = (index+1 < MEMVIEW_DATASIZE ? udGUI.mv_data[index+1] : 0);
    data[2] = (index+2 < MEMVIEW_DATASIZE ? udGUI.mv_data[index+2] : 0);
    data[3] = (index+3 < MEMVIEW_DATASIZE ? udGUI.mv_data[index+3] : 0);

    char buff[1024];
    sprintf(buff, "*(volatile unsigned char  *)(0x%08X) = 0x%02X"            , adr, data[0]);
    mMemoryValues->Lines->Add(buff);
    sprintf(buff, "*(volatile unsigned short *)(0x%08X) = 0x%02X%02X"        , adr, data[0], data[1]);
    mMemoryValues->Lines->Add(buff);
    sprintf(buff, "*(volatile unsigned long  *)(0x%08X) = 0x%02X%02X%02X%02X", adr, data[0], data[1], data[2], data[3]);
    mMemoryValues->Lines->Add(buff);
#else
    unsigned char data = udGUI.mv_data[index];

    char buff[1024];
    sprintf(buff, "*(volatile unsigned char  *)(0x%08X) = 0x%02X;", adr, data);
    mMemoryValues->Lines->Add(buff);
#endif
}
//---------------------------------------------------------------------------



void __fastcall TForm1::cbMemoryConvertChange(TObject *Sender)
{
    int w = /*sgMemConvert->ColCount*/16;
    int h = sgMemConvert->RowCount;
    int i, j, k;
    char tmp[16];

    int datasize = 1;
    int index;

    index = cbMemSize->ItemIndex;
    switch(index)
    {
    default:
    case(0): /* 1 byte */
        sgMemConvert->ColCount = 16;
        sgMemConvert->DefaultColWidth = 20;
        datasize = 1;
        break;

    case(3): /* RGB555 -> 2 bytes */
        cbMemoryConvert->ItemIndex = 1; // HEX
    case(1): /* 2 bytes */
        sgMemConvert->ColCount = 8;
        sgMemConvert->DefaultColWidth = 41;
        datasize = 2;
        break;

    case(2): /* 4 bytes */
        sgMemConvert->ColCount = 4;
        sgMemConvert->DefaultColWidth = 83;
        datasize = 4;
        break;
    }




    unsigned long data = 0;
    index = cbMemoryConvert->ItemIndex;
    for(j=0; j<h; j++)
    {
        for(i=0; i<w; i+=datasize)
        {
            data = 0;
            for(k=0; k<datasize; k++)
            {
                data |= udGUI.mv_data[j*w + i + k] << (24 - 8*k);
            }
            data = data >> (32-8*datasize);


            switch(index)
            {
            default:
            case(0): /* Char */
                sprintf(tmp, "%c", char2pchar(data & 0x000000FF));
                break;

            case(1): /* Hex */
                if(datasize == 1)
                {
                    sprintf(tmp, "%02X", data);
                }
                else if(datasize == 2)
                {
                    sprintf(tmp, "%04X", data);
                }
                else
                {
                    sprintf(tmp, "%08X", data);
                }
                break;

            case(2): /* Dec */
                sprintf(tmp, "%u", data);
                break;
            }

            sgMemConvert->Cells[i/datasize][j] = tmp;
        }
    }
}
//---------------------------------------------------------------------------


int _mv_prevrow = _mv_row;
int _mv_prevcol = _mv_col;
void __fastcall TForm1::tmMemviewRefreshTimer(TObject *Sender)
{
    /* Refresh only when needed. */
    if((udGUI.mv_cntr != Form1->udGUI.mv_cver)
    || (_mv_prevrow != _mv_row)
    || (_mv_prevcol != _mv_col))
    {
        cbMemoryConvertChange(Sender);

        /* Acknowledge refresh. */
        udGUI.mv_cntr = Form1->udGUI.mv_cver;
        _mv_prevrow = _mv_row;
        _mv_prevcol = _mv_col;
    }
}
//---------------------------------------------------------------------------


#include <cassert>
void __fastcall TForm1::sgMemConvertDrawCell(TObject *Sender, int ACol,
      int ARow, TRect &Rect, TGridDrawState State)
{
    //AnsiString str;
    //str.printf("ARow = %d, ACol = %d", ARow, ACol); Form1->mLog->Lines->Add(str.c_str());

    /* Get cell to draw. */
    TStringGrid* StringGrid = static_cast<TStringGrid*>(Sender);
    assert(StringGrid != NULL);
    AnsiString text(StringGrid->Cells[ACol][ARow]);

    /* Need to draw in colors ? */
    int index = cbMemSize->ItemIndex;
    bool bColor = (cbMemSize->ItemIndex == 3 ? true : false);

    if(bColor)
    {
        /* Colors mode : extract value from cell's text (HEX value). */
        unsigned short val = strtoul(text.c_str(), NULL, 16);
        int r = (val >>  0 & 0x1F) << 3;
        int g = (val >>  5 & 0x1F) << 3;
        int b = (val >> 10 & 0x1F) << 3;
        StringGrid->Canvas->Brush->Color = (TColor)((b<<16) | (g << 8) | (r));

        /* Set text color (yeah, Q&D !). */
        StringGrid->Canvas->Font->Color = ((r+g+b) <= 384 ? clWhite : clBlack);
    }
    else
    {
        /* Normal mode : display in standard color. */
        StringGrid->Canvas->Brush->Color = clWindow;
        /* Set text color. */
        StringGrid->Canvas->Font->Color = clBlack;
    }
    StringGrid->Canvas->FillRect(Rect);

    /* Display cell's text. */
    RECT RText = static_cast<RECT>(Rect);
    InflateRect(&RText, -3, -3);
    DrawText(StringGrid->Canvas->Handle,
            text.c_str(), text.Length(), &RText,
            DT_CENTER | DT_SINGLELINE | DT_VCENTER);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnVDP2CRAMClick(TObject *Sender)
{
    edtMemViewAddress->Text = "25F00000";
    cbMemSize->ItemIndex = 3;
    cbMemoryConvert->ItemIndex = 1;
    btnClick((TObject*)btnMemView);
}
//---------------------------------------------------------------------------




/* Query USB Data Link parameters, and if needed, create a usbdl.ini file. */
void __fastcall TForm1::mniSetUSBDataLinkParametersClick(TObject *Sender)
{
    char udl_comport[256];
    char udl_ledcolor[256];
    AnsiString str;

    str = "COM3";
    if ( InputQuery("USB Data Link - COM port", "Enter COM port (COMx)", str) && Trim(str) != "" )
    {
        sprintf(udl_comport, "%s", str.c_str());

        str = "red";
        if ( InputQuery("USB Data Link - LED color", "Enter LED color (red/green)", str) && Trim(str) != "" )
        {
            sprintf(udl_ledcolor, "%s", str.LowerCase().c_str());

            /* Input OK, so that output a usbdl.ini file. */
            scl_log(SCLT_HOSTPG, 0/*level*/, "Create usbdl.ini (%s %s)", udl_comport, udl_ledcolor);
            FILE* f=fopen("usbdl.ini", "wb");
            if(f)
            {
                fprintf(f, "-logsettings\r\n");
                fprintf(f, "log_settings.ini\r\n");
                fprintf(f, "-loginfos\r\n");
                fprintf(f, "\r\n");

                fprintf(f, "-dev\r\n");
                fprintf(f, "usbdl\r\n");
                fprintf(f, "\r\n");

                fprintf(f, "-s\r\n");
                fprintf(f, "port\r\n");
                fprintf(f, "%s\r\n", udl_comport);
                fprintf(f, "\r\n");

                fprintf(f, "-s\r\n");
                fprintf(f, "device_led\r\n");
                fprintf(f, "%s\r\n", udl_ledcolor);
                fprintf(f, "\r\n");

                fprintf(f, "-s\r\n");
                fprintf(f, "compress\r\n");
                fprintf(f, "0\r\n"); /* TODO: set to 1 when FDL will work correctly. */
                fprintf(f, "\r\n");
                fprintf(f, "-s\r\n");

                fprintf(f, "display\r\n");
                fprintf(f, "1\r\n");

                fclose(f); f=NULL;
            }
        }
    }
}
//---------------------------------------------------------------------------




#include "common/portlib.h"
void __fastcall TForm1::btnTest2Click(TObject *Sender)
{
//btnClick(btnOpen);
    AnsiString str;
    unsigned char val;

    port_init();
    val = port_read_byte(0x0378);
    str.printf("Port[0x0378] = 0x%02X", val); Form1->mLog->Lines->Add(str.c_str());
    port_close();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::btnTest3Click(TObject *Sender)
{
btnClick(btnDownload);
}
//---------------------------------------------------------------------------



#include "common/SerialComm.h"
/* DataLink packet, any protocol. */
#pragma pack(1)	
typedef struct _dlPacket
{
    unsigned char directn;   /* PC->AR : 0x5A, AR->PC : 0xA5.                 */
    unsigned char pktsize;   /* packet size (counted from next byte).         */
    unsigned char pktflag;   /* packet type.                                  */
    unsigned long address;   /* read (write) address.                         */
    unsigned char datalen;   /* data length.                                  */
    unsigned char data[300]; /* data (max 191 bytes), then checksum (1 byte). */
}dlPacket;
/**
 *Note : data[300]
 * data[192] (191 data bytes + checksum) may be enough, but 
 * 300 bytes allows to avoid bug when reading bad-formed 
 * packets (example : datalen == 0xFF).
**/
#pragma pack()

void pck_reset(dlPacket* p);
unsigned char pck_get_checksum(dlPacket* p);
unsigned long ulong_swap(unsigned long data);
void pck_print(dlPacket* p);

void __fastcall TForm1::btnUDLGoClick(TObject *Sender)
{
    AnsiString str;
    unsigned long i;
    unsigned long adr = strtoul(edtUDLAddress->Text.c_str(), NULL, 16);
    unsigned long len = strtoul(edtUDLLen->Text.c_str(), NULL, 0);
    unsigned long count = strtoul(edtUDLCount->Text.c_str(), NULL, 0);
    char port[256];

    mLog->Lines->Clear();

    str.printf("Start address = 0x%08X", adr); Form1->mLog->Lines->Add(str.c_str());
    str.printf("Download length = %d bytes", len); Form1->mLog->Lines->Add(str.c_str());
    str.printf("Download count = %d times", count); Form1->mLog->Lines->Add(str.c_str());

    strcpy(port, (edtUDLPort->Text.c_str()));
    str.printf("COM port = %s", port); Form1->mLog->Lines->Add(str.c_str());

    /* Open COM port. */
    SerialComm* _com = NULL;
    if(edtUDLColor->Text != "green")
    { // original HW
        _com = new SerialComm(port, "baud=288000 parity=N data=8 stop=2", 1000, 1000);
    }
    else
    { // final HW
        _com = new SerialComm(port, "baud=375000 parity=N data=8 stop=2", 1000, 1000);
    }
    _com->Start();

    /* Download packets. */
    for(i=0; i<count; i++)
    {
        dlPacket pkt;
        unsigned char pktflag;
        if(i == 0)
            pktflag = 0x01; /* Start packet. */
        else if(i == (count-1))
            pktflag = 0x21; /* End packet. */
        else
            pktflag = 0x11; /* Middle packet. */


        /* Set a "receive data" request. */
        pck_reset(&pkt);
        pkt.directn = 0x5A;
        pkt.pktflag = pktflag;
        pkt.address = ulong_swap(adr);
        pkt.pktsize = 7;
        pkt.datalen = len;
        pkt.data[pkt.pktsize-7] = pck_get_checksum(&pkt);

        Form1->mLog->Lines->Add("Send a \"receive data\" request ..."); pck_print(&pkt);
        _com->Send((void*)(&pkt), 9, NULL);

        /* Receive header from USB DataLink. */
        pck_reset(&pkt);
        Form1->mLog->Lines->Add("Waiting for answer header ...");
        _com->Receive((void*)(&pkt), 9+pkt.datalen, NULL);

        pck_print(&pkt);
    }

    /* Close connection. */
    delete _com; _com = NULL;
}
//---------------------------------------------------------------------------


/* Display current log levels (DEBUG use). */
void __fastcall TForm1::btnLogLevelsGetClick(TObject *Sender)
{
    scl_settings_t* ptr =  &_log_global_settings;
    AnsiString str;
    int i, j;

    mLogLevels->Lines->Clear();

    str.printf("silent_on=%d", ptr->silent_on); mLogLevels->Lines->Add(str.c_str());
    char tmp1[50];
    char tmp2[50];
    for(i = 0; i < SCLT_COUNT; i++)
    {
        tmp1[0] = '\0';
        for(j = 0; j < SCLO_COUNT; j++)
        {
            sprintf(tmp2, "%X ", ptr->levels[i][j]);
            strcat(tmp1, tmp2);
        }
        str.printf("l[%d]=%s", i, tmp1); mLogLevels->Lines->Add(str.c_str());
    }
}
//---------------------------------------------------------------------------







