Appendix D — ROM Bundle Infrastructure
The Debug80 extension ships ROM firmware, listing files, and related assets for supported platforms directly inside the VSIX package. These assets are called ROM bundles. A monitor-backed project records stable workspace-relative ROM/listing paths and profile-level bundledAssets references in debug80.json. At launch, missing workspace files resolve to the copy inside the extension bundle. The explicit bundled-assets command can copy those files into the workspace when the user wants local files to inspect or override.
This appendix covers the bundle manifest schema, the materialization functions, the resource directory layout, and how bundles are wired into the project-kit scaffolding and command flows.
Why bundles?
Shipping firmware inside the extension means users get a working TEC-1G project immediately, without needing to source a ROM image separately. The bundle approach also allows the extension to verify file integrity via SHA-256 checksums, and to map files to named roles (ROM, listing, source tree) rather than relying on naming conventions.
Resource directory layout
Bundle files are stored under resources/bundles/ in the extension root:
resources/
bundles/
tec1/
mon1b/
v1/
bundle.json Manifest (schema version, file list, workspace layout)
mon-1b.bin ROM binary image
mon-1b.lst Assembler listing (symbols and source map)
tec1g/
mon3/
v1/
bundle.json Manifest (schema version, file list, workspace layout)
mon3.bin ROM binary image
mon3.lst Assembler listing (symbols and source map)
README.md Human-readable notes about the firmware version
The path segments under bundles/ form the bundle relative path (e.g. tec1/mon1b/v1 or tec1g/mon3/v1). Two constants in bundle-materialize.ts name the shipped bundles:
| Constant | Value | Platform |
|---|---|---|
BUNDLED_MON1B_V1_REL |
'tec1/mon1b/v1' |
TEC-1 MON-1B |
BUNDLED_MON3_V1_REL |
'tec1g/mon3/v1' |
TEC-1G MON3 |
BundleManifestV1 — the manifest schema
bundle.json is parsed into a BundleManifestV1 object (defined in src/extension/bundle-manifest.ts):
interface BundleManifestV1 {
schemaVersion: 1; // Must equal BUNDLE_MANIFEST_SCHEMA_VERSION
id: string; // Stable bundle identifier, e.g. 'tec1g/mon3'
version: string; // Semver or upstream label, e.g. '1.6.0-bc25'
platform: 'simple' | 'tec1' | 'tec1g';
label: string; // Human-readable name, e.g. 'MON3 (TEC-1G)'
files: BundleFileEntry[];
workspaceLayout: BundleWorkspaceLayout;
}
interface BundleFileEntry {
role: 'rom' | 'listing' | 'source_tree';
path: string; // Path relative to the bundle root directory
sha256?: string; // Optional SHA-256 hex for integrity verification
}
interface BundleWorkspaceLayout {
destination: string; // Directory relative to workspace root, e.g. 'roms/tec1g/mon3'
}
The MON3 v1 bundle.json:
{
"schemaVersion": 1,
"id": "tec1g/mon3",
"version": "1.6.0-bc25",
"platform": "tec1g",
"label": "MON3 (TEC-1G)",
"files": [
{
"role": "rom",
"path": "mon3.bin",
"sha256": "754555aa4029fc496352fbb4a7de91e67c7c7e58de76c7dc5fba2e85e4705401"
},
{
"role": "listing",
"path": "mon3.lst",
"sha256": "f6f5032cc16dceed7e921efe863371d8f2773465860bd518c9d998d83a5b67bb"
}
],
"workspaceLayout": {
"destination": "roms/tec1g/mon3"
}
}
The MON-1B v1 bundle.json:
{
"schemaVersion": 1,
"id": "tec1/mon1b",
"version": "1.0.0",
"platform": "tec1",
"label": "MON-1B (TEC-1)",
"files": [
{
"role": "rom",
"path": "mon-1b.bin",
"sha256": "f3e39203ecf134c737e307a5f7ef82f3c4b62b979da0fb883b56b632ab3b1596"
},
{
"role": "listing",
"path": "mon-1b.lst",
"sha256": "76bd761d226911b5aa0f53b7f0a4253a40e2d68154146c1506bf07ae7380ad89"
}
],
"workspaceLayout": {
"destination": "roms/tec1/mon1b"
}
}
isBundleManifestV1() validates the parsed JSON before trusting it. Any manifest that fails validation is treated as absent.
Materialization functions
bundle-materialize.ts exports two functions for copying bundle files into the workspace. Both verify SHA-256 checksums when present, skip existing files by default, and return a discriminated union (ok: true | false) with a reason string on failure.
materializeBundledAsset() — single asset reference
This is the per-asset materialization path used by the debug80.materializeBundledRom command:
function materializeBundledAsset(
extensionUri: vscode.Uri,
workspaceRoot: string,
reference: BundledAssetReference,
options?: { overwrite?: boolean }
): MaterializeBundledAssetResult
BundledAssetReference carries { bundleId, path, destination? }. The function:
- Reads and validates
bundle.jsonfor the givenbundleId. - Locates the matching entry in
manifest.filesbypath. - Resolves the destination — using
reference.destinationif set, otherwise falling back tomanifest.workspaceLayout.destination/<basename>. - Validates the destination is workspace-relative and does not escape the workspace root.
- Verifies the source file’s SHA-256 (with CRLF normalization for
listingrole files). - Copies the file unless it already exists and
overwriteis false. - Returns
{ ok: true, destinationRelative, materializedRelativePath }on success.
materializeBundledRom() — whole-bundle copy
This function copies all files in a bundle into the destination directory declared by the manifest. It is available for whole-bundle installation workflows when the workspace needs local copies of ROM, listing, and source files:
function materializeBundledRom(
extensionUri: vscode.Uri,
workspaceRoot: string,
bundleRelPath: string,
options?: { overwrite?: boolean }
): MaterializeBundledRomResult
Steps:
- Read
bundle.jsonfrom<extensionUri>/resources/bundles/<bundleRelPath>/bundle.jsonand validate it. - Resolve the destination directory:
<workspaceRoot>/<workspaceLayout.destination>. Create it if needed. - For each file entry in the manifest:
- Verify the source file exists in the extension bundle.
- If
sha256is specified, compute the SHA-256 of the source file and compare. Return{ ok: false }on mismatch. - If the destination file already exists and
overwriteis not true, skip the copy (but still record the path). - Otherwise copy with
fs.copyFileSync().
- Return
{ ok: true, destinationRelative, romRelativePath, listingRelativePath? }.
On success, romRelativePath and listingRelativePath are workspace-relative paths (using / separators) that can be written directly into debug80.json as romHex and extraListings entries.
The overwrite: false default means subsequent calls on the same workspace are idempotent — files already present are not clobbered, but their paths are still resolved and returned.
Integration points
Project kits and scaffolding
Bundle metadata is embedded in ProjectKit.bundledProfile (defined in src/extension/project-kits.ts). Each kit that requires a ROM carries:
bundledProfile: {
bundleRelPath: string; // versioned bundle dir path, e.g. 'tec1/mon1b/v1'
romPath: string; // workspace-relative destination, e.g. 'roms/tec1/mon1b/mon-1b.bin'
listingPath?: string; // e.g. 'roms/tec1/mon1b/mon-1b.lst'
sourceRoots: string[]; // source root directories for the debug adapter
}
When createDefaultProjectConfig() in project-scaffolding.ts builds a debug80.json for a kit with a bundledProfile, it writes a bundledAssets map into the profile section. Each entry is a BundledAssetReference ({ bundleId, path, destination }). Scaffolding records these references but does not copy the ROM files immediately.
The launch argument resolver checks the configured workspace path first. If that file does not exist, and the path matches the bundled asset’s declared destination, it resolves the asset from <extension>/resources/bundles/<bundleId>/<path>. This keeps the project runnable without writing ROM files into the workspace.
The two monitor-backed kits and their bundles:
| Kit | Bundle constant | Bundle path |
|---|---|---|
tec1/mon1b |
BUNDLED_MON1B_V1_REL |
tec1/mon1b/v1 |
tec1g/mon3 |
BUNDLED_MON3_V1_REL |
tec1g/mon3/v1 |
Explicit command
The debug80.materializeBundledRom VS Code command (registered in src/extension/commands.ts) lets users run materialization manually. The command:
- Prompts for a workspace folder.
- If a
debug80.jsonexists, reads thebundledAssetsfrom the default profile and uses those references. - If no project config is found, shows a quick-pick with the available fallback plans (MON3 and MON-1B) and uses
materializeBundledAsset()for each selected reference. - Asks whether to overwrite existing files or skip them.
- Reports success or failure via
vscode.window.showInformationMessage().
This is useful if a user wants local ROM/listing files for inspection or replacement. Normal launch does not require this step.
Adding a new bundle
To add a new bundle (e.g. a different firmware version or a new platform ROM):
- Create a new directory under
resources/bundles/, e.g.tec1g/mon3/v2/. - Place the files there and create a
bundle.jsonwithschemaVersion: 1. - Add a constant for the new relative path in
bundle-materialize.ts(e.g.BUNDLED_MON3_V2_REL). - Add a new
ProjectKitentry inproject-kits.tswith abundledProfilereferencing the new bundle. - If the bundle should be available via the manual install command, add it to
buildBundledAssetFallbackPlans()insrc/extension/bundle-asset-installer.ts.
The manifest validator (isBundleManifestV1) checks schemaVersion === 1. Future schema versions would add a new isBundleManifestV2 validator and a migration path, leaving V1 parsing unchanged.
Source files
| File | Role |
|---|---|
src/extension/bundle-manifest.ts |
BundleManifestV1 type, BundleFileEntry, BundleWorkspaceLayout, isBundleManifestV1() |
src/extension/bundle-materialize.ts |
materializeBundledAsset(), materializeBundledRom(), BUNDLED_MON1B_V1_REL, BUNDLED_MON3_V1_REL |
src/extension/project-kits.ts |
ProjectKit type with bundledProfile; kit registry for all platforms |
src/extension/project-scaffolding.ts |
createDefaultProjectConfig() writes bundledAssets into profile from kit metadata |
src/extension/commands.ts |
Registers debug80.materializeBundledRom command (thin shell; logic delegated to bundle-asset-installer.ts) |
src/extension/bundle-asset-installer.ts |
buildBundledAssetFallbackPlans() and bundled asset install plan logic for the explicit install command |
resources/bundles/tec1/mon1b/v1/ |
Shipped MON-1B bundle: bundle.json, mon-1b.bin, mon-1b.lst |
resources/bundles/tec1g/mon3/v1/ |
Shipped MON3 bundle: bundle.json, mon3.bin, mon3.lst |
tests/extension/bundle-materialize.test.ts |
Unit tests for materializeBundledRom() and checksum mismatch handling |