@jhlagado/azm 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +22 -6
- package/dist/src/compile.js +12 -1
- package/dist/src/formats/types.d.ts +15 -0
- package/dist/src/formats/writeD8m.js +17 -11
- package/dist/src/packageInfo.d.ts +1 -0
- package/dist/src/packageInfo.js +15 -0
- package/dist/src/pipeline.d.ts +15 -0
- package/docs/reference/cli.md +15 -0
- package/docs/reference/tooling-api.md +12 -0
- package/package.json +1 -1
package/dist/src/cli.d.ts
CHANGED
package/dist/src/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
4
3
|
import { dirname, extname, resolve } from 'node:path';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
6
5
|
import { compile } from './compile.js';
|
|
7
6
|
import { isSupportedSourcePath, sourceExtensions } from './frontend/sourceExtensions.js';
|
|
8
7
|
import { defaultFormatWriters } from './formats/index.js';
|
|
9
8
|
import { normalizePathForCompare } from './pathCompare.js';
|
|
9
|
+
import { readPackageVersion } from './packageInfo.js';
|
|
10
10
|
function usage() {
|
|
11
11
|
return [
|
|
12
12
|
'azm [options] <entry.asm|entry.z80>',
|
|
@@ -19,6 +19,7 @@ function usage() {
|
|
|
19
19
|
' --nohex Suppress .hex',
|
|
20
20
|
' --nod8m Suppress .d8.json',
|
|
21
21
|
' --asm80 Emit assembler-valid lowered source (.z80)',
|
|
22
|
+
' --source-root <d> Normalize D8 source paths relative to this directory',
|
|
22
23
|
' --case-style <m> Case-style lint mode: off|upper|lower|consistent',
|
|
23
24
|
' --rc <m> Register-care mode: off|audit|warn|error|strict',
|
|
24
25
|
' --reg-report Emit .regcare.txt report',
|
|
@@ -132,6 +133,13 @@ function parseCaseStyleArg(arg, argv, indexRef, state) {
|
|
|
132
133
|
state.caseStyle = value;
|
|
133
134
|
return true;
|
|
134
135
|
}
|
|
136
|
+
function parseSourceRootArg(arg, argv, indexRef, state) {
|
|
137
|
+
const parsed = readMatchedFlagValue(arg, argv, indexRef, ['--source-root', '--map-root']);
|
|
138
|
+
if (!parsed)
|
|
139
|
+
return false;
|
|
140
|
+
state.sourceRoot = parsed.value;
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
135
143
|
function parseRegisterCareArg(arg, argv, indexRef, state) {
|
|
136
144
|
const parsed = readMatchedFlagValue(arg, argv, indexRef, ['--register-care', '--rc']);
|
|
137
145
|
if (!parsed)
|
|
@@ -199,11 +207,7 @@ function handleCliFastPath(arg) {
|
|
|
199
207
|
return { code: 0 };
|
|
200
208
|
}
|
|
201
209
|
if (arg === '-V' || arg === '--version') {
|
|
202
|
-
|
|
203
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
204
|
-
const packageJsonPath = resolve(here, '..', '..', 'package.json');
|
|
205
|
-
const pkg = require(packageJsonPath);
|
|
206
|
-
process.stdout.write(`${String(pkg.version ?? '0.0.0')}\n`);
|
|
210
|
+
process.stdout.write(`${readPackageVersion()}\n`);
|
|
207
211
|
return { code: 0 };
|
|
208
212
|
}
|
|
209
213
|
return undefined;
|
|
@@ -239,6 +243,7 @@ function finalizeCliOptions(state) {
|
|
|
239
243
|
entryFile: state.entryFile,
|
|
240
244
|
...(state.outputPath ? { outputPath: state.outputPath } : {}),
|
|
241
245
|
outputType: state.outputType,
|
|
246
|
+
...(state.sourceRoot !== undefined ? { sourceRoot: state.sourceRoot } : {}),
|
|
242
247
|
emitBin: state.emitBin,
|
|
243
248
|
emitHex: state.emitHex,
|
|
244
249
|
emitD8m: state.emitD8m,
|
|
@@ -293,6 +298,8 @@ export function parseCliArgs(argv) {
|
|
|
293
298
|
}
|
|
294
299
|
if (parseCaseStyleArg(arg, argv, indexRef, state))
|
|
295
300
|
continue;
|
|
301
|
+
if (parseSourceRootArg(arg, argv, indexRef, state))
|
|
302
|
+
continue;
|
|
296
303
|
if (parseDirectiveAliasFileArg(arg, argv, indexRef, state))
|
|
297
304
|
continue;
|
|
298
305
|
if (parseRegisterCareArg(arg, argv, indexRef, state))
|
|
@@ -457,6 +464,9 @@ export async function runCli(argv) {
|
|
|
457
464
|
if ('code' in parsed)
|
|
458
465
|
return parsed.code;
|
|
459
466
|
const base = artifactBase(parsed.entryFile, parsed.outputType, parsed.outputPath);
|
|
467
|
+
const hexPath = `${base}.hex`;
|
|
468
|
+
const binPath = `${base}.bin`;
|
|
469
|
+
const lstPath = `${base}.lst`;
|
|
460
470
|
const compileOptions = {
|
|
461
471
|
emitBin: parsed.emitBin,
|
|
462
472
|
emitHex: parsed.emitHex,
|
|
@@ -466,6 +476,12 @@ export async function runCli(argv) {
|
|
|
466
476
|
caseStyle: parsed.caseStyle,
|
|
467
477
|
includeDirs: parsed.includeDirs,
|
|
468
478
|
directiveAliasFiles: parsed.directiveAliasFiles,
|
|
479
|
+
...(parsed.sourceRoot !== undefined ? { sourceRoot: parsed.sourceRoot } : {}),
|
|
480
|
+
d8mInputs: {
|
|
481
|
+
...(parsed.sourceRoot !== undefined && parsed.emitListing ? { listing: lstPath } : {}),
|
|
482
|
+
...(parsed.sourceRoot !== undefined && parsed.emitHex ? { hex: hexPath } : {}),
|
|
483
|
+
...(parsed.sourceRoot !== undefined && parsed.emitBin ? { bin: binPath } : {}),
|
|
484
|
+
},
|
|
469
485
|
requireMain: false,
|
|
470
486
|
defaultCodeBase: 0,
|
|
471
487
|
registerCare: parsed.registerCare,
|
package/dist/src/compile.js
CHANGED
|
@@ -9,6 +9,7 @@ import { emitProgram } from './lowering/emit.js';
|
|
|
9
9
|
import { loadProgram } from './sourceLoader.js';
|
|
10
10
|
import { analyzeRegisterCare } from './registerCare/analyze.js';
|
|
11
11
|
import { parseInterfaceContracts } from './registerCare/smartComments.js';
|
|
12
|
+
import { readPackageVersion } from './packageInfo.js';
|
|
12
13
|
function withDefaults(options) {
|
|
13
14
|
const anyPrimaryEmitSpecified = [options.emitBin, options.emitHex, options.emitD8m].some((v) => v !== undefined);
|
|
14
15
|
const emitBin = anyPrimaryEmitSpecified ? (options.emitBin ?? false) : true;
|
|
@@ -135,8 +136,18 @@ export const compile = async (entryFile, options, deps) => {
|
|
|
135
136
|
}
|
|
136
137
|
if (emit.emitD8m) {
|
|
137
138
|
const mainEntry = symbols.find((s) => s.kind === 'label' && s.name.toLowerCase() === 'main');
|
|
139
|
+
const d8mRoot = options.sourceRoot ?? dirname(entryPath);
|
|
138
140
|
artifacts.push(deps.formats.writeD8m(map, symbols, {
|
|
139
|
-
rootDir:
|
|
141
|
+
rootDir: d8mRoot,
|
|
142
|
+
packageVersion: readPackageVersion(),
|
|
143
|
+
inputs: {
|
|
144
|
+
entry: entryPath,
|
|
145
|
+
...(options.d8mInputs?.listing !== undefined
|
|
146
|
+
? { listing: options.d8mInputs.listing }
|
|
147
|
+
: {}),
|
|
148
|
+
...(options.d8mInputs?.hex !== undefined ? { hex: options.d8mInputs.hex } : {}),
|
|
149
|
+
...(options.d8mInputs?.bin !== undefined ? { bin: options.d8mInputs.bin } : {}),
|
|
150
|
+
},
|
|
140
151
|
...(mainEntry
|
|
141
152
|
? {
|
|
142
153
|
entrySymbol: mainEntry.name,
|
|
@@ -82,6 +82,21 @@ export interface WriteD8mOptions {
|
|
|
82
82
|
* When provided, file paths are made project-relative and use `/` separators.
|
|
83
83
|
*/
|
|
84
84
|
rootDir?: string;
|
|
85
|
+
/**
|
|
86
|
+
* AZM package version to record in generator metadata.
|
|
87
|
+
*/
|
|
88
|
+
packageVersion?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Source/output paths used to produce this map.
|
|
91
|
+
*
|
|
92
|
+
* Paths are normalized with `rootDir` when present.
|
|
93
|
+
*/
|
|
94
|
+
inputs?: {
|
|
95
|
+
entry?: string;
|
|
96
|
+
listing?: string;
|
|
97
|
+
hex?: string;
|
|
98
|
+
bin?: string;
|
|
99
|
+
};
|
|
85
100
|
/**
|
|
86
101
|
* Optional runnable entry symbol metadata for harnesses.
|
|
87
102
|
*/
|
|
@@ -12,6 +12,14 @@ function normalizeD8mPath(file, rootDir) {
|
|
|
12
12
|
}
|
|
13
13
|
return rel.replace(/\\/g, '/');
|
|
14
14
|
}
|
|
15
|
+
function normalizeD8mInputs(inputs, rootDir) {
|
|
16
|
+
if (!inputs)
|
|
17
|
+
return undefined;
|
|
18
|
+
const entries = Object.entries(inputs)
|
|
19
|
+
.filter((entry) => typeof entry[1] === 'string' && entry[1] !== '')
|
|
20
|
+
.map(([key, value]) => [key, normalizeD8mPath(value, rootDir)]);
|
|
21
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
22
|
+
}
|
|
15
23
|
function toSerializedSymbol(symbol) {
|
|
16
24
|
if (symbol.kind === 'constant') {
|
|
17
25
|
return {
|
|
@@ -213,6 +221,7 @@ export function writeD8m(map, symbols, opts) {
|
|
|
213
221
|
...(entry.symbols.length > 0 ? { symbols: entry.symbols } : {}),
|
|
214
222
|
},
|
|
215
223
|
]));
|
|
224
|
+
const generatorInputs = normalizeD8mInputs(opts?.inputs, opts?.rootDir);
|
|
216
225
|
const json = {
|
|
217
226
|
format: 'd8-debug-map',
|
|
218
227
|
version: 1,
|
|
@@ -223,17 +232,14 @@ export function writeD8m(map, symbols, opts) {
|
|
|
223
232
|
segments,
|
|
224
233
|
...(fileList.length > 0 ? { fileList } : {}),
|
|
225
234
|
symbols: serializedSymbols,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
: {}),
|
|
235
|
+
generator: {
|
|
236
|
+
name: 'azm',
|
|
237
|
+
tool: 'azm',
|
|
238
|
+
...(opts?.packageVersion !== undefined ? { version: opts.packageVersion } : {}),
|
|
239
|
+
...(generatorInputs !== undefined ? { inputs: generatorInputs } : {}),
|
|
240
|
+
...(opts?.entrySymbol !== undefined ? { entrySymbol: opts.entrySymbol } : {}),
|
|
241
|
+
...(opts?.entryAddress !== undefined ? { entryAddress: opts.entryAddress & 0xffff } : {}),
|
|
242
|
+
},
|
|
237
243
|
};
|
|
238
244
|
return { kind: 'd8m', json };
|
|
239
245
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readPackageVersion(): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
export function readPackageVersion() {
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
let pkg;
|
|
8
|
+
try {
|
|
9
|
+
pkg = require(resolve(here, '..', 'package.json'));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
pkg = require(resolve(here, '..', '..', 'package.json'));
|
|
13
|
+
}
|
|
14
|
+
return String(pkg.version ?? '0.0.0');
|
|
15
|
+
}
|
package/dist/src/pipeline.d.ts
CHANGED
|
@@ -19,6 +19,21 @@ export interface CompilerOptions {
|
|
|
19
19
|
outputPath?: string;
|
|
20
20
|
/** Primary output type (future). */
|
|
21
21
|
outputType?: 'hex' | 'bin';
|
|
22
|
+
/**
|
|
23
|
+
* Root used for source paths in debug maps.
|
|
24
|
+
*
|
|
25
|
+
* When present, D8 file keys and generator input paths are emitted relative
|
|
26
|
+
* to this directory with `/` separators.
|
|
27
|
+
*/
|
|
28
|
+
sourceRoot?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Output paths recorded in D8 generator metadata when known by the caller.
|
|
31
|
+
*/
|
|
32
|
+
d8mInputs?: {
|
|
33
|
+
listing?: string;
|
|
34
|
+
hex?: string;
|
|
35
|
+
bin?: string;
|
|
36
|
+
};
|
|
22
37
|
/** Emit flat binary (`.bin`). */
|
|
23
38
|
emitBin?: boolean;
|
|
24
39
|
/** Emit Intel HEX (`.hex`). */
|
package/docs/reference/cli.md
CHANGED
|
@@ -40,6 +40,12 @@ Load project directive aliases:
|
|
|
40
40
|
azm --aliases azm.aliases.json program.asm
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
Normalize Debug80 map source paths against the project root:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
azm --source-root . --output build/program.hex src/program.asm
|
|
47
|
+
```
|
|
48
|
+
|
|
43
49
|
## Output Artifacts
|
|
44
50
|
|
|
45
51
|
By default AZM writes the primary output plus useful side artifacts using the
|
|
@@ -73,6 +79,7 @@ azm --nobin --nohex --reg-report --rc audit program.asm
|
|
|
73
79
|
| `--nohex` | Do not write `.hex`. |
|
|
74
80
|
| `--nod8m` | Do not write `.d8.json`. |
|
|
75
81
|
| `--asm80` | Write lowered assembler source as `.z80`. |
|
|
82
|
+
| `--source-root <dir>` | Emit project-relative source paths in `.d8.json`. |
|
|
76
83
|
| `--case-style <mode>` | Lint opcode/register case: `off`, `upper`, `lower`, or `consistent`. |
|
|
77
84
|
| `--rc, --register-care <mode>` | Register-care mode: `off`, `audit`, `warn`, `error`, or `strict`. |
|
|
78
85
|
| `--reg-report, --emit-register-report` | Write `.regcare.txt`. |
|
|
@@ -87,6 +94,14 @@ azm --nobin --nohex --reg-report --rc audit program.asm
|
|
|
87
94
|
| `-V, --version` | Print package version. |
|
|
88
95
|
| `-h, --help` | Print CLI help. |
|
|
89
96
|
|
|
97
|
+
## Debug80 Maps
|
|
98
|
+
|
|
99
|
+
The `.d8.json` artifact records AZM as the generator, the package version, and
|
|
100
|
+
the input/output paths used for the map. When `--source-root` is supplied, file
|
|
101
|
+
keys and generator input paths are written relative to that directory with `/`
|
|
102
|
+
separators. Constants are emitted as `value` metadata without fake addresses;
|
|
103
|
+
labels and addressable data carry `address`.
|
|
104
|
+
|
|
90
105
|
## Register-Care Examples
|
|
91
106
|
|
|
92
107
|
Audit inferred contracts without failing the build:
|
|
@@ -58,6 +58,12 @@ const result = await compile(
|
|
|
58
58
|
'/abs/path/to/main.asm',
|
|
59
59
|
{
|
|
60
60
|
includeDirs: ['/abs/path/to/includes'],
|
|
61
|
+
sourceRoot: '/abs/path/to/project',
|
|
62
|
+
d8mInputs: {
|
|
63
|
+
listing: '/abs/path/to/project/build/main.lst',
|
|
64
|
+
hex: '/abs/path/to/project/build/main.hex',
|
|
65
|
+
bin: '/abs/path/to/project/build/main.bin',
|
|
66
|
+
},
|
|
61
67
|
outputType: 'hex',
|
|
62
68
|
emitBin: true,
|
|
63
69
|
emitHex: true,
|
|
@@ -84,6 +90,12 @@ The integration contract is:
|
|
|
84
90
|
writing those artifacts to disk
|
|
85
91
|
- Debug80 should consume the `d8m` artifact for source/address metadata and the
|
|
86
92
|
`bin` or `hex` artifact for loadable bytes
|
|
93
|
+
- pass `sourceRoot` so D8 file keys are stable project-relative source paths
|
|
94
|
+
rather than basename-only paths
|
|
95
|
+
- pass `d8mInputs` when Debug80 knows the intended artifact paths; AZM records
|
|
96
|
+
those under `generator.inputs`
|
|
97
|
+
- D8 constants use `value` without `address`; only labels and addressable data
|
|
98
|
+
are breakpoint anchors
|
|
87
99
|
- diagnostics are data objects and should be displayed directly rather than
|
|
88
100
|
parsed from CLI text
|
|
89
101
|
|