PICKit+ Merge — bootloader-aware programming for GCBASIC
You can add a new GCBASIC programmer, PICKit+ Merge [Target 5v0], that merges a
bootloader hex file ( or any existing hex file ) into your compiled application hex before flashing, using
Microchip's hexmate.exe. It reuses the same PICkit hardware path as the
existing PICKit+ Cmd5v0 programmer — only the file sent to it differs.
It is seamless.
Why
A chip programmed with a bootloader needs two things resident in flash at
once: the bootloader itself (so the device can later be field-updated over
serial/USB without a PICkit) and the application code. GCBASIC only ever
compiles and outputs the application hex — it has no idea a bootloader
exists, so its compiled hex says nothing about the bootloader region the
bootloader occupies.
Flashing that application hex directly, on its own, means only the
application ends up on the chip — the bootloader region is left blank (or,
on a chip that's already been through a full-chip erase, wiped out
entirely). Getting both onto the chip in one programming pass previously
meant manually running hexmate to merge the two hex files before every
flash, or maintaining a separately-merged hex file that had to be
regenerated and kept in sync by hand whenever the application source
changed — both easy to forget, and easy to get wrong during day-to-day
development when you just want to hit "Program" after a code change.
This new tool folds that merge step into the normal GCBASIC compile-and-program
workflow: pick PICKit+ Merge [Target 5v0] as the programmer once, and every
subsequent "Program" click merges the current bootloader into the freshly
compiled application hex automatically, then flashes the result — so the
bootloader can never accidentally go missing from a build, and there's
nothing extra to remember to run by hand.
Requirements
GCBASIC / GCstudio installed (tested against C:\GCstudio).
A PIC project where a bootloader reserves a word-address range — in this
project's case, 0x1E00-0x1EFF.
A hex file named after that bootloader's reserved range — in this case
bootloader_1E00-1EFF.hex — sitting next to your .gcb source file (i.e. in
the project root, one folder above the compiler's <basename>.build output
folder). This file should contain only the bootloader's own
reserved-region data — not a full blank-filled image of the whole flash
range (see "How it works" below for why that matters). merge.cmd
currently looks for this exact filename, so if your bootloader occupies a
different range, name the file to match and update the BOOTHEX line in
merge.cmd accordingly.</basename>
Note: bootloader_1E00-1EFF.hex is the file name used in this document. See
the "Customizing merge.cmd" section below for how to change it.
Installation
Copy install-hexmate-merge.cmd anywhere convenient (it doesn't need to
live inside a project folder).
Run it:
install-hexmate-merge.cmd
By default it targets C:\GCstudio. If your GCstudio install lives
elsewhere, pass it as an argument:
install-hexmate-merge.cmd D:\Path\To\GCstudio
The installer will:
Add a hexmatelocation entry to use.ini's [toolvariables] section.
Add (or patch) the [tool = pickitplusmerge] section in use.ini.
Add pickitplusmerge to the programmer= list so it's selectable.
Install merge.cmd into %GCSTUDIO_INSTALL_PATH%\PICKitPlus.
It's safe to run more than once — anything already in place is left
alone and reported as "already up to date / skipping". Anything it does
change is backed up first (<file>.bak_<timestamp>, next to the
original).</timestamp></file>
Selecting the new programmer
Installing it only makes the programmer available — GCBASIC still needs to
be told to use it:
Open your project in the GCBASIC IDE.
Go to Edit Preferences.
Find the Programmer tab setting and choose PICKit+ Merge [Target 5v0] from
the list.
Save/close preferences. Compile and program as normal — the merge now
happens automatically as part of the programming step, with no other
workflow change.
If you ever want to go back to flashing the plain application hex (no
bootloader merge), just switch the Programmer back to PICKit+ Cmd5v0 [Target 5v0].
How it works
use.ini changes
use.ini already defines each programmer as a [tool = ...] section whose
command=/params= lines are text-substituted by the compiler (%FileName%,
%ChipModel%, %GCSTUDIO_INSTALL_PATH%, etc.) before the tool is launched.
%GCSTUDIO_INSTALL_PATH% isn't a real environment variable — GCBASIC
computes it itself from its own .exe location and substitutes it as plain
text — so it can't be read back inside a batch file; anything a called tool
needs has to be passed in as an explicit argument instead.
The installer adds:
hexmatelocation = %GCSTUDIO_INSTALL_PATH%\picas\pic\bin under [toolvariables] — hexmate.exe ships with the PIC-AS toolchain but not in
the pic-as\bin folder GCBASIC already points at for the assembler; it's a
folder over, in pic\bin.
A [tool = pickitplusmerge] section whose params now includes hexmate's
full path as an extra positional argument, ahead of the existing
PICKitCommandline.exe path and its flashing arguments:
command = %GCSTUDIO_INSTALL_PATH%\PICKitPlus\merge.cmd receives these as
%1 through %9:
%1 — quoted path to the compiled application hex (%FileName%)
%2 — path to hexmate.exe
%3 — path to PICKitCommandline.exe
%4 through %9 — PICKitCommandline.exe's own flashing arguments
merge.cmd
merge.cmd runs before any hardware programming happens:
Locate the bootloader hex. It derives the project root as the parent
folder of the compiled hex's directory (the compiler's own
<basename>.build\<basename>.hex convention), then looks for
bootloader_1E00-1EFF.hex there. If it's missing — or hexmate.exe can't
be found — the script aborts with a non-zero exit code and does not
flash anything, so a missing bootloader file is never silently ignored.</basename></basename>
Back up the compiled hex. The freshly-compiled application hex is
copied to <name>_org.hex alongside it (e.g. main.build\main_org.hex)
before anything is modified.</name>
hexmate errors on overlapping data between input files by default; the +
prefix forces that input's data to win any conflict. Here the
application hex is the unforced base and the bootloader hex is forced on
top — so the bootloader reliably occupies its own reserved 0x1E00-0x1EFF
region, while everywhere else the application's own code passes through
untouched (the bootloader file has no data outside its reserved region,
so there's nothing for it to overwrite). This is why
bootloader_1E00-1EFF.hex — a file that only contains that narrow address
range — is used instead of a full, blank-filled bootloader image: the
latter would collide with application code at almost every address.
The output is written back to the original filename (<name>.hex), so
nothing downstream needs to know a merge happened.</name>
Flash. It calls PICKitCommandline.exe with the same arguments GCBASIC
would have passed it directly (%3 through %9), including
-f"%FileName%" — which now resolves to the merged hex, since it was
written back under the original name.
If the hexmate merge step itself fails, merge.cmd restores the original
(pre-merge) hex from the _org.hex backup before aborting, so the project is
never left in a broken state.
Customizing merge.cmd
The bootloader hex filename isn't the only thing that can be changed —
everything merge.cmd does is driven by a handful of lines near the top of
the file. Edit %GCSTUDIO_INSTALL_PATH%\PICKitPlus\merge.cmd directly (the
installer won't touch a file it finds already up to date, and backs up
anything it does overwrite, so hand edits are safe to make):
Bootloader hex filename — the line
set "BOOTHEX=%PROJDIR%\bootloader_1E00-1EFF.hex". Change the filename to
match your bootloader's own reserved range, e.g.
bootloader_0C00-0CFF.hex.
Where the bootloader hex is looked for — the line
for %%D in ("%APPDIR%..") do set "PROJDIR=%%~fD". Assumes the bootloader
hex sits one folder above <basename>.build. Point BOOTHEX somewhere else
entirely (a shared folder, a fixed path) if that's not where yours lives.</basename>
Backup filename pattern — the line
set "ORGHEX=%APPDIR%%APPNAME%_org%APPEXT%". Change the _org suffix, or
write it somewhere other than next to the compiled hex.
Merge priority — the line
"%HEXMATE%" "%ORGHEX%" +"%BOOTHEX%" -o"%APPHEX%". The + forces that
input to win address conflicts. Move it to the other filename to flip
priority, or add extra hexmate flags (checksum, fill, addressing) here.
Missing-file behaviour — the two if not exist (...) blocks. Currently
aborts and never flashes. Change to a warning followed by a jump straight
to the flash step if you'd rather fall back to flashing the plain
application hex when the bootloader file is absent.
Number of flashing arguments forwarded — the line
"%FLASHTOOL%" %4 %5 %6 %7 %8 %9. Must match however many tokens follow
the PICKitCommandline.exe path in pickitplusmerge's params= line in
use.ini — extend or trim this list if that line changes.
Two related settings live in use.ini rather than merge.cmd itself:
hexmatelocation (under [toolvariables]) — where hexmate.exe is found.
Point it elsewhere if you're using a different PIC-AS/XC8 install.
pickitplusmerge's params= line — the flashing arguments themselves
(target voltage -a5, etc.) follow the same conventions as pickitpluscmd1
and can be edited the same way.
Known limitation
Because the merge writes its output back over the original filename,
re-running "Program" a second time without recompiling re-merges an
already-merged hex. This is harmless to what actually gets flashed (merging
the bootloader's region on top of itself changes nothing), but the
_org.hex backup from that second run will then contain the already-merged
hex rather than the true pre-merge compiler output. Recompiling before
programming (the normal workflow) avoids this entirely.
PICKit+ Merge — bootloader-aware programming for GCBASIC
You can add a new GCBASIC programmer, PICKit+ Merge [Target 5v0], that merges a
bootloader hex file ( or any existing hex file ) into your compiled application hex before flashing, using
Microchip's hexmate.exe. It reuses the same PICkit hardware path as the
existing PICKit+ Cmd5v0 programmer — only the file sent to it differs.
It is seamless.
Why
A chip programmed with a bootloader needs two things resident in flash at
once: the bootloader itself (so the device can later be field-updated over
serial/USB without a PICkit) and the application code. GCBASIC only ever
compiles and outputs the application hex — it has no idea a bootloader
exists, so its compiled hex says nothing about the bootloader region the
bootloader occupies.
Flashing that application hex directly, on its own, means only the
application ends up on the chip — the bootloader region is left blank (or,
on a chip that's already been through a full-chip erase, wiped out
entirely). Getting both onto the chip in one programming pass previously
meant manually running hexmate to merge the two hex files before every
flash, or maintaining a separately-merged hex file that had to be
regenerated and kept in sync by hand whenever the application source
changed — both easy to forget, and easy to get wrong during day-to-day
development when you just want to hit "Program" after a code change.
This new tool folds that merge step into the normal GCBASIC compile-and-program
workflow: pick PICKit+ Merge [Target 5v0] as the programmer once, and every
subsequent "Program" click merges the current bootloader into the freshly
compiled application hex automatically, then flashes the result — so the
bootloader can never accidentally go missing from a build, and there's
nothing extra to remember to run by hand.
Requirements
project's case, 0x1E00-0x1EFF.
bootloader_1E00-1EFF.hex — sitting next to your .gcb source file (i.e. in
the project root, one folder above the compiler's <basename>.build output
folder). This file should contain only the bootloader's own
reserved-region data — not a full blank-filled image of the whole flash
range (see "How it works" below for why that matters). merge.cmd
currently looks for this exact filename, so if your bootloader occupies a
different range, name the file to match and update the BOOTHEX line in
merge.cmd accordingly.</basename>
Note: bootloader_1E00-1EFF.hex is the file name used in this document. See
the "Customizing merge.cmd" section below for how to change it.
Installation
live inside a project folder).
install-hexmate-merge.cmd
By default it targets C:\GCstudio. If your GCstudio install lives
elsewhere, pass it as an argument:
install-hexmate-merge.cmd D:\Path\To\GCstudio
It's safe to run more than once — anything already in place is left
alone and reported as "already up to date / skipping". Anything it does
change is backed up first (<file>.bak_<timestamp>, next to the
original).</timestamp></file>
Selecting the new programmer
Installing it only makes the programmer available — GCBASIC still needs to
be told to use it:
the list.
happens automatically as part of the programming step, with no other
workflow change.
If you ever want to go back to flashing the plain application hex (no
bootloader merge), just switch the Programmer back to PICKit+ Cmd5v0
[Target 5v0].
How it works
use.ini changes
use.ini already defines each programmer as a [tool = ...] section whose
command=/params= lines are text-substituted by the compiler (%FileName%,
%ChipModel%, %GCSTUDIO_INSTALL_PATH%, etc.) before the tool is launched.
%GCSTUDIO_INSTALL_PATH% isn't a real environment variable — GCBASIC
computes it itself from its own .exe location and substitutes it as plain
text — so it can't be read back inside a batch file; anything a called tool
needs has to be passed in as an explicit argument instead.
The installer adds:
[toolvariables] — hexmate.exe ships with the PIC-AS toolchain but not in
the pic-as\bin folder GCBASIC already points at for the assembler; it's a
folder over, in pic\bin.
full path as an extra positional argument, ahead of the existing
PICKitCommandline.exe path and its flashing arguments:
params = "%FileName%" %hexmatelocation%\hexmate.exe %GCSTUDIO_INSTALL_PATH%\PICKitPlus\PICKitCommandline.exe -w -a5 -pPIC%ChipModel% -f"%FileName%" -mpec -zv
command = %GCSTUDIO_INSTALL_PATH%\PICKitPlus\merge.cmd receives these as
%1 through %9:
merge.cmd
merge.cmd runs before any hardware programming happens:
Locate the bootloader hex. It derives the project root as the parent
folder of the compiled hex's directory (the compiler's own
<basename>.build\<basename>.hex convention), then looks for
bootloader_1E00-1EFF.hex there. If it's missing — or hexmate.exe can't
be found — the script aborts with a non-zero exit code and does not
flash anything, so a missing bootloader file is never silently ignored.</basename></basename>
Back up the compiled hex. The freshly-compiled application hex is
copied to <name>_org.hex alongside it (e.g. main.build\main_org.hex)
before anything is modified.</name>
Merge with hexmate. It runs:
hexmate.exe "<name>_org.hex" +"bootloader_1E00-1EFF.hex" -o"<name>.hex"</name></name>
hexmate errors on overlapping data between input files by default; the +
prefix forces that input's data to win any conflict. Here the
application hex is the unforced base and the bootloader hex is forced on
top — so the bootloader reliably occupies its own reserved 0x1E00-0x1EFF
region, while everywhere else the application's own code passes through
untouched (the bootloader file has no data outside its reserved region,
so there's nothing for it to overwrite). This is why
bootloader_1E00-1EFF.hex — a file that only contains that narrow address
range — is used instead of a full, blank-filled bootloader image: the
latter would collide with application code at almost every address.
The output is written back to the original filename (<name>.hex), so
nothing downstream needs to know a merge happened.</name>
would have passed it directly (%3 through %9), including
-f"%FileName%" — which now resolves to the merged hex, since it was
written back under the original name.
If the hexmate merge step itself fails, merge.cmd restores the original
(pre-merge) hex from the _org.hex backup before aborting, so the project is
never left in a broken state.
Customizing merge.cmd
The bootloader hex filename isn't the only thing that can be changed —
everything merge.cmd does is driven by a handful of lines near the top of
the file. Edit %GCSTUDIO_INSTALL_PATH%\PICKitPlus\merge.cmd directly (the
installer won't touch a file it finds already up to date, and backs up
anything it does overwrite, so hand edits are safe to make):
set "BOOTHEX=%PROJDIR%\bootloader_1E00-1EFF.hex". Change the filename to
match your bootloader's own reserved range, e.g.
bootloader_0C00-0CFF.hex.
for %%D in ("%APPDIR%..") do set "PROJDIR=%%~fD". Assumes the bootloader
hex sits one folder above <basename>.build. Point BOOTHEX somewhere else
entirely (a shared folder, a fixed path) if that's not where yours lives.</basename>
set "ORGHEX=%APPDIR%%APPNAME%_org%APPEXT%". Change the _org suffix, or
write it somewhere other than next to the compiled hex.
"%HEXMATE%" "%ORGHEX%" +"%BOOTHEX%" -o"%APPHEX%". The + forces that
input to win address conflicts. Move it to the other filename to flip
priority, or add extra hexmate flags (checksum, fill, addressing) here.
aborts and never flashes. Change to a warning followed by a jump straight
to the flash step if you'd rather fall back to flashing the plain
application hex when the bootloader file is absent.
"%FLASHTOOL%" %4 %5 %6 %7 %8 %9. Must match however many tokens follow
the PICKitCommandline.exe path in pickitplusmerge's params= line in
use.ini — extend or trim this list if that line changes.
Two related settings live in use.ini rather than merge.cmd itself:
Point it elsewhere if you're using a different PIC-AS/XC8 install.
(target voltage -a5, etc.) follow the same conventions as pickitpluscmd1
and can be edited the same way.
Known limitation
Because the merge writes its output back over the original filename,
re-running "Program" a second time without recompiling re-merges an
already-merged hex. This is harmless to what actually gets flashed (merging
the bootloader's region on top of itself changes nothing), but the
_org.hex backup from that second run will then contain the already-merged
hex rather than the true pre-merge compiler output. Recompiling before
programming (the normal workflow) avoids this entirely.