SDCC-MDFMicrocontroller Development Framework for SDCC — build & flash MCS-51 (8051), STM8, Z80 and MOS6502 projects directly from VS Code. Highlights
Verified against the official SDCC 4.4.1 Compiler User Guide — target flags (§3.3.1), memory models per port (§3.3.6–§3.3.12), compile/link flow (§3.2.3), packihx/makebin (§3.2.2, §2.9). Requirements
Quick start
InstallationFrom the VS Code Marketplace
From a
|
| Group | Variable | Value |
|---|---|---|
| Project | ${workspaceFolder} |
project root |
${workspaceFolderBasename} |
project folder name | |
${project.name} |
name from sdcc-project.json |
|
| Outputs | ${outputDir} |
build/ (resolved) |
${outputObjDir} |
build/obj/ |
|
${outputName} |
firmware name, no extension | |
${outputIhx} · ${outputHex} · ${outputBin} |
build/firmware.ihx/.hex/.bin |
|
${outputMap} · ${outputMem} |
linker .map / .mem report |
|
${outputLst} |
.lst listing of the main() source |
|
| Board | ${board.mcu} |
e.g. stm8s103f3 |
${board.target} |
SDCC target: mcs51 / stm8 / z80 / … |
|
${board.name} · ${board.cpu} |
board display name / CPU | |
${board.fcpu} |
build.f_cpu from the board definition |
|
${board.memoryModel} |
small / medium / large |
|
${board.iramSize} · ${board.xramSize} · ${board.codeSize} |
memory sizes | |
${board.uploadMaxSize} · ${board.uploadMaxRamSize} |
upload limits | |
| Toolchain | ${toolchain.sdcc} · ${toolchain.dir} |
sdcc path / its folder |
${toolchain.version} |
detected SDCC version | |
${toolchain.packihx} · ${toolchain.makebin} · ${toolchain.sdar} |
companion tools | |
| Git | ${git:hash} · ${git:branch} |
short hash / branch ('' outside a repo) |
| Date & time | ${date} · ${time} · ${datetime} · ${unixtime} |
filename-safe (colons → -) |
| System | ${pathSeparator} · ${userHome} |
\ vs / · home dir |
| Config | ${config:comPort} · ${config:buildMode} |
sdcc.comPort / sdcc.buildMode |
${config:any.setting} |
any VS Code setting (sdcc.* namespace first) |
|
${env:NAME} |
any environment variable |
Examples — version-stamped backup and multi-target flashing:
"build": { "hooks": { "post_build": [
{ "command": "cp", "args": ["${outputHex}", "release/${project.name}_${git:hash}_${date}.hex"] }
] } }
"upload": {
"tool": "${board.target}.bat",
"args": ["-p", "${config:comPort}", "-f", "${outputHex}"]
}
Libraries + components
A project has four separate top-level folders: src/, include/, lib/, components/.
| kind | what | where | config entry |
|---|---|---|---|
| Prebuilt | a ready .lib linked as-is (§3.2.4) |
lib/ |
libraries: ["lib"] (folder → scanned for *.lib); add "vendor/foo.lib" for a single file |
| Source library | a folder of .c → compiled → sdar -rc → linked (§3.2.5) |
components/<name>/ |
components: ["components"] (each subfolder is one library); add a JSON object for a lib elsewhere |
The build is config-driven: a libraries[] / components[] entry that is a folder name is scanned; a .lib file path or JSON object is linked/compiled directly. No hardcoded scan — the entries ARE the scan list. Library include dirs are automatically visible to app code (#include "stm8s.h" just works).
External SDK folder (sdcc.externalSdkPath)
An optional shared root for vendor SDKs that live outside the project — scanned the same way: <externalSdkPath>/lib/*.lib (prebuilt) and <externalSdkPath>/components/<subfolder>/ (source). Also editable in the Project Editor (External SDK folder block).
Flash + serial ports
Configurable upload tool with variable expansion — the tool itself and every arg support the full variable table (${outputIhx}, ${outputBin}, ${outputHex}, ${config:comPort}, ${board.mcu}, ${board.target}, ${env:…}, …). Serial port auto-detection: Linux/macOS /dev scan (ttyUSB/ttyACM/ttyS/cu.usbserial/cu.usbmodem), Windows SerialPort.GetPortNames.
Toolchain selection
SDCC: Select Toolchain Path shows all auto-detected sdcc locations (PATH / SDCC_HOME / standard install dirs) and offers native browse dialogs for the executable or folder (validated: sdcc or bin/sdcc inside). Companion tools (makebin, packihx, sdar) are found next to sdcc.
IntelliSense (C/C++ extension)
SDCC: Configure IntelliSense (C/C++) generates/refreshes one configuration named SDCC in .vscode/c_cpp_properties.json: project include dirs, component include dirs, SDCC's own header roots (<sdcc>/include, <sdcc>/include/<cpu>, non-free tree) and the project + board defines — no more red squiggles for 8051.h / stm8s.h / your include/. Paths inside the project are written as ${workspaceFolder}/… (portable); a hand-written c_cpp_properties.json is never clobbered — only the SDCC entry is touched. New projects get it automatically on creation.
Status bar + tree view
Status bar (Build / Flash / Clean / toolchain / port / build mode) and a tree view with all commands. Colored sidebar icons (charts.* theme colors): Build — green, Flash — blue, Clean — red, boards — purple, libraries — blue, components — cyan, sources/hooks — orange, folders — yellow.
Busy indicator (v0.13.23, ESP8266-IDF-style): while Build / Clean / Flash runs, a spinning ⟳ SDCC: <command> item with a live elapsed timer ((3s) → (1m 05s)) appears leftmost in the status bar, warns with the theme's warning background, explains in its tooltip that all commands are locked until it finishes, and focuses the build terminal on click. When a command completes, its button flashes the result for 4 seconds — ✓ Build OK / ✗ Build Failed, ✓ Flash OK / ✗ Flash Failed, ✓ Clean OK / ✗ Clean Failed — before reverting to the idle label.
Project layout
my-sdcc-project/
src/ ← application sources (proj.sources → "src")
include/ ← application headers (proj.includes → "include")
lib/ ← prebuilt *.lib (proj.libraries → "lib") — linked as-is (§3.2.4)
components/ ← source libraries (proj.components→ "components") — compiled → sdar → linked (§3.2.5)
sdcc-project.json
.vscode/
Every textarea in the Project Editor shows its default folder name by default (Sources → src, Includes → include, Libraries → lib, Components → components). The build is config-driven: libraries[] and components[] entries are folder names (scanned) or .lib paths / JSON objects (linked directly). No hardcoded scan — the entries ARE the scan list.
sdcc-project.json
{
"name": "blink",
"board": "generic-8051",
"sources": ["src"],
"includes": ["include"],
"defines": ["F_CPU=16000000UL"],
"libraries": ["lib"],
"components": ["components"],
"build": {
"output_dir": "build",
"output_name": "firmware",
"hooks": {
"post_build": [
{ "command": "echo", "args": ["done"], "continue_on_error": true }
]
}
},
"upload": {
"tool": "stm8flash",
"args": ["-c", "stlinkv2", "-p", "stm8s103f3", "-w", "${outputHex}"]
}
}
Source rules
- a string — a folder (scanned recursively for
*.c) or a single file { "path": "src", "pattern": "**/*.c" }{ "path": "src", "files": ["main.c", "utils.c"] }{ "exclude": ["src/test/**"] }
Vendor SDK guide (e.g. ST StdPeriph for STM8)
Two ways to attach the SDK:
Option A — as a JSON object in components (when the SDK lives outside components/):
{
"name": "stm8s-app",
"board": "stm8s103f3",
"sources": ["src"],
"includes": ["include"],
"defines": ["STM8S103", "USE_STDPERIPH_DRIVER"],
"libraries": ["lib"],
"components": [
"components",
{
"name": "stm8s_stdperiph",
"path": "vendor/STM8S_StdPeriph_Lib/Libraries/STM8S_StdPeriph_Driver",
"pattern": "src/**/*.c",
"includes": ["inc", "../CMSIS/STM8S"],
"defines": ["STM8S103"]
}
]
}
Option B — as a subfolder of components/: copy the SDK under components/stm8s_stdperiph/ so the default components entry scans it automatically. Make sure the layout is components/stm8s_stdperiph/*.c + components/stm8s_stdperiph/inc/ (the inc/ folder is auto-added).
What happens on build (manual §3.2.4/§3.2.5):
- every SDK source is compiled into
build/obj/libs/stm8s_stdperiph/**.relwith the SDK's include dirs (inc,../CMSIS/…— resolved relative to the SDK folder) and defines, using the same target/memory model as the app; sdar -rc build/stm8s_stdperiph.libarchives them;- the app is linked with the library after its own objects.
Notes for the ST SDK specifically:
- copy
stm8s_conf.hfromUtilities/STM8S_StdPeriph_Templates/into yoursrc/and enable the peripherals you use; USE_STDPERIPH_DRIVER+ the device define (STM8S103) must be set (projectdefines— they propagate to library compilation);- the SDK headers are automatically available to your code (
#include "stm8s.h"just works); - a prebuilt
.libcan be linked without recompiling: drop it intolib/or add"vendor/my.lib"tolibraries.
Coming from PlatformIO?
| platformio.ini | sdcc-project.json |
|---|---|
board = stm8s003f3 |
"board": "stm8s103f3" (Board Manager id) |
board_build.f_cpu = 16000000ul |
board definition build.f_cpu (Board Manager) or defines: ["F_CPU=16000000UL"] |
build_flags = --opt-code-size |
"build": { "flags": ["--opt-code-size"] } (compile + link) |
build_src_flags = --opt-code-size |
"build": { "src_flags": ["--opt-code-size"] } (compile only) |
upload_command = flash -c stlinkv2 -w $BUILD_DIR\\firmware.hex |
"upload": { "tool": "flash", "args": ["-c","stlinkv2","-w","${outputHex}"] } |
extra_scripts = pre:use_stm8dce.py |
"build": { "hooks": { "pre_build": ["python use_stm8dce.py"] } } |
extra_scripts = post_build.py |
"build": { "hooks": { "post_build": ["python post_build.py ${outputBin}"] } } (e.g. CRC) |
build_src_filter = -<**/*.asm> |
sources exclude rules: {"exclude":["**/*.asm"]} |
Settings
| Setting | Default | Description |
|---|---|---|
sdcc.path |
"" |
Path to sdcc executable; empty = auto-detect |
sdcc.boardsDir |
"" |
Additional folder with board definition JSON files. Boards from it override built-in ones with the same id (priority: project .sdcc/boards/ → this folder → global boards dir → built-in boards/) |
sdcc.comPort |
"" |
Serial port used by ${config:comPort} |
sdcc.shellPath |
"" |
Windows terminal shell override. Leave empty to auto-detect: terminal.integrated.automationProfile.windows → terminal.integrated.defaultProfile.windows (recognizes "Command Prompt" / "PowerShell" / "pwsh" / "Git Bash" / "WSL") → powershell.exe. The detected shell is pinned to the terminal so quoting and exit-code markers always match — no more "Syntax error in filename" |
sdcc.reuseTerminal |
true |
Reuse the SDCC terminal between runs |
sdcc.saveSettingsToWorkspace |
true |
Save port etc. to workspace settings |
sdcc.autoHexBin |
true |
Generate .hex/.bin after build when tools are available |
sdcc.postBuildAction |
none |
flash = run flash automatically after a successful build |
sdcc.buildMode |
release |
debug adds --debug to compile & link flags (SDCC emits .sym/.rst debug info — manual §3.8) and defines DEBUG (inherited by library components). Exposed as ${config:buildMode} |
sdcc.externalSdkPath |
"" |
External SDK folder — scanned for lib/*.lib (prebuilt) and components/<subfolder>/ (source), in addition to the project's own lib/ and components/. Also editable in the Project Editor (External SDK folder block) |
Shell support & auto-warning
The extension supports the shells it can actually drive with quoting and exit-code markers: PowerShell (5.1 and 7+), cmd.exe, bash, sh, zsh, fish, dash, ksh, csh, tcsh, WSL. On Linux/macOS the system $SHELL is used directly (no detection, no pinning — VS Code opens the terminal with whatever shell it would use anyway).
If the resolved shell is not one of these (e.g. Nushell, elvish, xonsh, ion), a one-time information message per session shows:
SDCC: Detected shell "nu.exe" is not officially supported. Build/flash commands may fail with quoting or exit-code markers. Set "sdcc.shellPath" to override.
with the buttons Configure shell (opens Settings filtered by sdcc.shellPath) and Ignore.
The warning fires at most once per VS Code session — getUserShell() is called many times per build via shellKind(), so without a latch the user would drown in popups. It is suppressed when sdcc.shellPath is set explicitly (you took control, no nudge).
The trade-off is intentional: supporting every shell on Earth is overkill, but a silent build failure (PowerShell syntax sent to a nushell terminal → no marker file → build hangs for 30 minutes) is worse than a one-time actionable hint.
Publishing
This extension ships as a ready-to-install .vsix. To publish your own build:
- Create a publisher ID at https://marketplace.visualstudio.com/manage and replace
publisherinpackage.jsonwith your own ID. - Update
repository/bugs/homepageURLs to point to your fork. - Install the packaging CLI:
npm i -g @vscode/vsce. - Build the VSIX:
vsce package(producessdcc-mdf-0.11.2.vsix). - Publish:
vsce publish(needs a Personal Access Token from https://dev.azure.com — see the official guide).
License
MIT © Dzantemir