@jhlagado/azm 0.2.8 → 0.2.10
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/README.md +75 -15
- package/dist/src/api-compile.js +27 -0
- package/dist/src/assembly/assemble-program.js +5 -0
- package/dist/src/assembly/import-visibility.d.ts +3 -0
- package/dist/src/assembly/import-visibility.js +204 -0
- package/dist/src/node/source-host.js +40 -13
- package/dist/src/outputs/write-asm80.js +4 -0
- package/dist/src/register-contracts/programModel-routines.js +33 -17
- package/dist/src/register-contracts/report.js +15 -3
- package/dist/src/register-contracts/smartCommentParsing.d.ts +1 -0
- package/dist/src/register-contracts/smartCommentParsing.js +42 -7
- package/dist/src/register-contracts/smartComments.d.ts +2 -2
- package/dist/src/register-contracts/smartComments.js +3 -4
- package/dist/src/source/logical-lines.d.ts +3 -0
- package/dist/src/source/source-span.d.ts +2 -0
- package/dist/src/syntax/parse-directive-statement.d.ts +1 -6
- package/dist/src/syntax/parse-directive-statement.js +3 -1
- package/dist/src/syntax/parse-layout-declarations.js +11 -2
- package/dist/src/syntax/parse-line.js +18 -2
- package/dist/src/tooling/api.js +1 -1
- package/docs/codebase/01-orientation-and-repository-layout.md +192 -0
- package/docs/codebase/02-source-loading-and-parsing.md +263 -0
- package/docs/codebase/03-assembly-and-z80-emission.md +251 -0
- package/docs/codebase/04-ops-and-register-contracts.md +237 -0
- package/docs/codebase/05-interfaces-and-output-artifacts.md +253 -0
- package/docs/codebase/06-verification-and-maintenance.md +202 -0
- package/docs/codebase/appendices/a-directory-file-reference.md +253 -0
- package/docs/codebase/appendices/b-compile-flow-reference.md +103 -0
- package/docs/codebase/appendices/c-public-surface-reference.md +106 -0
- package/docs/codebase/appendices/index.md +16 -0
- package/docs/codebase/index.md +46 -0
- package/package.json +2 -3
- package/docs/reference/cli.md +0 -158
- package/docs/reference/tooling-api.md +0 -320
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: 'Chapter 4 - Ops and Register Contracts'
|
|
4
|
+
parent: 'AZM Engineering Manual'
|
|
5
|
+
nav_order: 4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
[<- Assembly and Z80 Emission](03-assembly-and-z80-emission.md) | [Interfaces and Output Artifacts ->](05-interfaces-and-output-artifacts.md)
|
|
9
|
+
|
|
10
|
+
# Chapter 4 - Ops and Register Contracts
|
|
11
|
+
|
|
12
|
+
Ops and register contracts are the two AZM-specific subsystems that sit above plain
|
|
13
|
+
Z80 instruction assembly. Ops expand source into visible inline assembly.
|
|
14
|
+
Register contract analysis checks the resulting routines and calls.
|
|
15
|
+
|
|
16
|
+
These features belong together in the codebase tour because they meet at the
|
|
17
|
+
same boundary: parsed source items. Ops produce source items. Register contract analysis
|
|
18
|
+
reads source items.
|
|
19
|
+
|
|
20
|
+
## Ops as Visible Expansion
|
|
21
|
+
|
|
22
|
+
Ops are named inline instruction idioms. They let source define a small
|
|
23
|
+
operation once and expand it visibly at each use site. The implementation lives
|
|
24
|
+
in `src/expansion/`. `op-expansion.ts` coordinates the subsystem. Operand
|
|
25
|
+
splitting, overload selection, selected expansion, instruction instantiation
|
|
26
|
+
and local-label rewriting live in focused helper modules.
|
|
27
|
+
|
|
28
|
+
An op is closer to a typed inline template than to a text macro. The op parser
|
|
29
|
+
understands operands, chooses an overload and parses the expanded body back
|
|
30
|
+
through the normal AZM parser. The result is visible assembly with the same
|
|
31
|
+
diagnostic and register contract behaviour as handwritten source.
|
|
32
|
+
|
|
33
|
+
For example:
|
|
34
|
+
|
|
35
|
+
```asm
|
|
36
|
+
op clear(reg8 r)
|
|
37
|
+
xor r
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
clear a
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The expansion stage matches `a` as a register operand, substitutes it into the
|
|
44
|
+
template and emits the source item for `xor a`. Address planning and emission
|
|
45
|
+
then treat that instruction exactly like a line written directly in the source.
|
|
46
|
+
|
|
47
|
+
## Op Collection and Invocation
|
|
48
|
+
|
|
49
|
+
`collectOps()` scans logical lines before normal parsing. It finds top-level
|
|
50
|
+
`op` blocks, parses their parameter lists, records the body template and marks
|
|
51
|
+
the source lines that belong to the definition body.
|
|
52
|
+
|
|
53
|
+
An op declaration has:
|
|
54
|
+
|
|
55
|
+
- a name
|
|
56
|
+
- a parameter list
|
|
57
|
+
- matcher information for overload selection
|
|
58
|
+
- a body template
|
|
59
|
+
- source location metadata for diagnostics
|
|
60
|
+
|
|
61
|
+
The registry is complete before invocation parsing starts. `parseOpInvocation()`
|
|
62
|
+
checks whether a source line could be an op call. If the name matches a
|
|
63
|
+
collected op, `expandOpInvocation()` selects an overload and instantiates the
|
|
64
|
+
body.
|
|
65
|
+
|
|
66
|
+
The parser handles op invocations before `parseLogicalLine()`. An op head can
|
|
67
|
+
look like an instruction head at the source level. The expansion stage resolves
|
|
68
|
+
it before ordinary line parsing.
|
|
69
|
+
|
|
70
|
+
## Overloads and Templates
|
|
71
|
+
|
|
72
|
+
Ops support overloads. `op-selection.ts` compares invocation operands against
|
|
73
|
+
each candidate signature. It prefers the most specific matching overload and
|
|
74
|
+
emits diagnostics for arity errors, unsupported operands, ambiguous matches and
|
|
75
|
+
invalid expansions.
|
|
76
|
+
|
|
77
|
+
The matcher vocabulary recognises fixed tokens, registers, register pairs,
|
|
78
|
+
immediates, conditions, ports and indexed operands. It stays close to the Z80
|
|
79
|
+
operand model, so op dispatch and instruction parsing describe operands in the
|
|
80
|
+
same terms.
|
|
81
|
+
|
|
82
|
+
An op body template is parsed into template items. During expansion, operands
|
|
83
|
+
from the call site are substituted into the template by
|
|
84
|
+
`op-instruction-instantiation.ts`. The result is formatted as ordinary source
|
|
85
|
+
text and parsed through the same line parser used for top-level source.
|
|
86
|
+
|
|
87
|
+
Local label rewriting lives in `op-local-labels.ts`. A local label in an op
|
|
88
|
+
expansion becomes unique at the use site so each expansion receives its own
|
|
89
|
+
generated label. Once the rewritten labels become source items, address planning
|
|
90
|
+
defines and resolves them through the ordinary symbol path.
|
|
91
|
+
|
|
92
|
+
## Op Diagnostics and Register Contracts
|
|
93
|
+
|
|
94
|
+
Op diagnostics point at the call site while explaining the definition that
|
|
95
|
+
matched or failed. Invalid expanded instructions are reported as op expansion
|
|
96
|
+
failures with the underlying Z80 parser diagnostic included.
|
|
97
|
+
|
|
98
|
+
Ops expand before register contract analysis builds routines. AZM sees the
|
|
99
|
+
expanded instructions. An op is visible inline assembly, so its register effects
|
|
100
|
+
belong to the caller.
|
|
101
|
+
|
|
102
|
+
## Register Contract Analysis
|
|
103
|
+
|
|
104
|
+
Register contract analysis checks how routines use Z80 registers. It reads routine
|
|
105
|
+
boundaries, instruction effects and AZMDoc contract comments, then reports
|
|
106
|
+
conflicts where a caller still needs a register value that a callee may change.
|
|
107
|
+
|
|
108
|
+
The implementation lives in `src/register-contracts/`. The public analysis entry
|
|
109
|
+
point is `analyzeRegisterContracts()` in `src/register-contracts/analyze.ts`.
|
|
110
|
+
|
|
111
|
+
Register contract analysis is a data-flow analysis over assembled source
|
|
112
|
+
structure. It works with routines, calls, instruction effects and contracts. It
|
|
113
|
+
analyses the source structure to find values live across a call and callee
|
|
114
|
+
summaries that can change those values.
|
|
115
|
+
|
|
116
|
+
## A Register Contract Conflict
|
|
117
|
+
|
|
118
|
+
This source shape captures the problem:
|
|
119
|
+
|
|
120
|
+
```asm
|
|
121
|
+
@Caller:
|
|
122
|
+
ld b,8
|
|
123
|
+
Loop:
|
|
124
|
+
call Worker
|
|
125
|
+
djnz Loop
|
|
126
|
+
ret
|
|
127
|
+
|
|
128
|
+
;! clobbers B
|
|
129
|
+
@Worker:
|
|
130
|
+
ld b,0
|
|
131
|
+
ret
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`Caller` uses `B` as the `djnz` counter. `Worker` declares that it clobbers
|
|
135
|
+
`B`. Liveness sees that `B` is still needed after the call because `djnz Loop`
|
|
136
|
+
reads it. The register contract conflict is at the call site: `Caller` passes
|
|
137
|
+
through a routine boundary that may change a live unit.
|
|
138
|
+
|
|
139
|
+
The programmer can preserve `B`, choose a different counter register, change
|
|
140
|
+
`Worker` so it leaves `B` unchanged or update the calling sequence. Register
|
|
141
|
+
contract analysis identifies the conflict and the source location where the
|
|
142
|
+
caller crosses the boundary.
|
|
143
|
+
|
|
144
|
+
## Routine Model and Contracts
|
|
145
|
+
|
|
146
|
+
`src/register-contracts/programModel.ts` builds the program model from parsed source
|
|
147
|
+
items. Routine-specific extraction is split into
|
|
148
|
+
`programModel-boundaries.ts` and `programModel-routines.ts`. Together they find
|
|
149
|
+
routine boundaries, direct calls, labels and instructions. Routine entry labels
|
|
150
|
+
use `@` in source and become callable public routine names after the marker is
|
|
151
|
+
removed.
|
|
152
|
+
|
|
153
|
+
`src/register-contracts/smartComments.ts` reads AZMDoc comments from the comment maps
|
|
154
|
+
captured during loading. Comment-block splitting and token parsing live in
|
|
155
|
+
`smartCommentBlocks.ts` and `smartCommentParsing.ts`. External `.asmi`
|
|
156
|
+
contracts are parsed in `interfaceContracts.ts`.
|
|
157
|
+
|
|
158
|
+
Contracts can describe:
|
|
159
|
+
|
|
160
|
+
- inputs
|
|
161
|
+
- outputs
|
|
162
|
+
- clobbered registers
|
|
163
|
+
- preserved registers
|
|
164
|
+
- expected outputs at call sites
|
|
165
|
+
|
|
166
|
+
Source comments and external interfaces describe the same kind of fact: a
|
|
167
|
+
routine contract. Source comments attach to routines in the current program.
|
|
168
|
+
`.asmi` entries attach to routines whose source is assembled elsewhere.
|
|
169
|
+
|
|
170
|
+
## Effects, Summaries and Liveness
|
|
171
|
+
|
|
172
|
+
Register contract analysis depends on `src/z80/effects.ts`. Effects describe which registers
|
|
173
|
+
and flags an instruction reads, writes or preserves. `instruction-head.ts`,
|
|
174
|
+
`instruction-operands.ts`, `instruction-predicates.ts` and
|
|
175
|
+
`operand-register-name.ts` translate between Z80 instruction shapes and
|
|
176
|
+
register contract units such as `A`, `HL`, `carry` and register pairs.
|
|
177
|
+
|
|
178
|
+
`src/register-contracts/summary.ts` infers a summary for a single routine. Boundary,
|
|
179
|
+
contract, result, state and token-transfer logic now lives in
|
|
180
|
+
`summary-boundary.ts`, `summary-contract.ts`, `summary-result.ts`,
|
|
181
|
+
`summary-state.ts` and `summary-token-transfer.ts`. `routine-summaries.ts` and
|
|
182
|
+
`summaries.ts` combine routine summaries, external contracts and profile
|
|
183
|
+
summaries into lookup tables. A summary records the observable contract of a
|
|
184
|
+
routine: the units it reads, writes, preserves, clobbers and returns as outputs.
|
|
185
|
+
|
|
186
|
+
`src/register-contracts/liveness.ts` performs the caller-side analysis. It works
|
|
187
|
+
backwards through each routine. At a call, it compares the live-after set with
|
|
188
|
+
the callee summary. A live unit that the callee clobbers becomes a conflict. A
|
|
189
|
+
unit produced by the callee and read by the caller becomes an output candidate.
|
|
190
|
+
|
|
191
|
+
Stack behaviour is part of routine summaries. `summary.ts` tracks push, pop,
|
|
192
|
+
exchange-top and unknown stack effects. `routine-summaries.ts` infers summaries
|
|
193
|
+
to a fixed point so internal routine calls can see optimistic boundary
|
|
194
|
+
summaries before the final pass. Strict mode uses `stackBalanced` and
|
|
195
|
+
`hasUnknownStackEffect` to distinguish balanced stack use from a routine whose
|
|
196
|
+
boundary may leave the stack in an unknown state.
|
|
197
|
+
|
|
198
|
+
## Reports, Interfaces and Tooling
|
|
199
|
+
|
|
200
|
+
`report.ts` renders human-readable `.regcontracts.txt` reports and `.asmi` interface
|
|
201
|
+
metadata. `annotate.ts`, `annotations.ts`, `fix.ts` and `sourceText.ts` support
|
|
202
|
+
source updates for generated AZMDoc comments and conservative fixes.
|
|
203
|
+
|
|
204
|
+
The CLI can request these behaviours through:
|
|
205
|
+
|
|
206
|
+
- `--reg-report`
|
|
207
|
+
- `--reg-interface`
|
|
208
|
+
- `--contracts`
|
|
209
|
+
- `--fix`
|
|
210
|
+
- `--accept-out`
|
|
211
|
+
|
|
212
|
+
`src/register-contracts/tooling.ts` exposes editor-friendly diagnostics and code
|
|
213
|
+
actions through `analyzeRegisterContractsForTools()`. Tooling diagnostics carry
|
|
214
|
+
file, line, column, message, fixability and optional text edits. An editor can
|
|
215
|
+
show the same register contract information that the CLI reports while using
|
|
216
|
+
normal editor actions for accepted fixes.
|
|
217
|
+
|
|
218
|
+
## Changing Ops or Register Contracts
|
|
219
|
+
|
|
220
|
+
Op changes belong in `src/expansion/`, with tests under
|
|
221
|
+
`test/unit/expansion/` and integration tests for source-level behaviour.
|
|
222
|
+
Register contract changes usually begin in one of these files:
|
|
223
|
+
|
|
224
|
+
- Routine boundaries and calls: `programModel.ts`
|
|
225
|
+
- AZMDoc parsing: `smartComments.ts`, `smartCommentBlocks.ts`,
|
|
226
|
+
`smartCommentParsing.ts`, `interfaceContracts.ts`
|
|
227
|
+
- Instruction effects: `z80/effects.ts`, `instruction-head.ts`,
|
|
228
|
+
`instruction-operands.ts`, `instruction-predicates.ts`
|
|
229
|
+
- Summary inference: `summary.ts`
|
|
230
|
+
- Caller liveness: `liveness.ts`
|
|
231
|
+
- Output text: `report.ts`
|
|
232
|
+
- Source edits: `annotate.ts`, `fix.ts`, `annotations.ts`
|
|
233
|
+
- Tooling surface: `tooling.ts`
|
|
234
|
+
|
|
235
|
+
Run unit tests under `test/unit/register-contracts/`, integration tests under
|
|
236
|
+
`test/integration/register-contracts/` and CLI tests in
|
|
237
|
+
`test/cli/register_contracts_cli.test.ts`.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: 'Chapter 5 - Interfaces and Output Artifacts'
|
|
4
|
+
parent: 'AZM Engineering Manual'
|
|
5
|
+
nav_order: 5
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
[<- Ops and Register Contracts](04-ops-and-register-contracts.md) | [Verification and Maintenance ->](06-verification-and-maintenance.md)
|
|
9
|
+
|
|
10
|
+
# Chapter 5 - Interfaces and Output Artifacts
|
|
11
|
+
|
|
12
|
+
AZM has three public entry surfaces: the command-line binary, the compile API
|
|
13
|
+
and the tooling API. They all use the same compiler pipeline. Output writers
|
|
14
|
+
then serialize assembled facts for users, Debug80 and package consumers.
|
|
15
|
+
|
|
16
|
+
This chapter covers the boundary between the compiler and its callers: package
|
|
17
|
+
exports, CLI flow, public TypeScript APIs and artifact shapes.
|
|
18
|
+
|
|
19
|
+
## Package Exports
|
|
20
|
+
|
|
21
|
+
`package.json` exposes:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
@jhlagado/azm
|
|
25
|
+
@jhlagado/azm/compile
|
|
26
|
+
@jhlagado/azm/tooling
|
|
27
|
+
@jhlagado/azm/cli
|
|
28
|
+
@jhlagado/azm/package.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`src/index.ts` re-exports the stable public surface. `src/api-compile.ts` backs
|
|
32
|
+
`@jhlagado/azm/compile`. `src/api-artifacts.ts` isolates assembly artifact
|
|
33
|
+
creation for the compile API. `src/api-register-contracts.ts` isolates register contract
|
|
34
|
+
analysis, interface loading and register contract artifact creation.
|
|
35
|
+
`src/api-tooling.ts` backs `@jhlagado/azm/tooling`. `src/cli.ts` is the
|
|
36
|
+
executable entry.
|
|
37
|
+
|
|
38
|
+
The root export gives consumers a broad import. The `/compile` path is the
|
|
39
|
+
build-system path. The `/tooling` path is the editor and analysis path. The
|
|
40
|
+
`/cli` path backs the executable entry. The `/package.json` path exposes package
|
|
41
|
+
metadata for tools that need the installed version.
|
|
42
|
+
|
|
43
|
+
## CLI Flow
|
|
44
|
+
|
|
45
|
+
The executable path in `package.json` points to `dist/src/cli.js`, compiled from
|
|
46
|
+
`src/cli.ts`. That file calls `runCli(process.argv.slice(2))` and sets the
|
|
47
|
+
process exit code.
|
|
48
|
+
|
|
49
|
+
`src/cli/run.ts` owns the CLI control flow:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
runCli(argv)
|
|
53
|
+
parseCliArgs(argv)
|
|
54
|
+
artifactBase(entryFile, outputType, outputPath)
|
|
55
|
+
compile(entryFile, buildCompileOptions(parsed, base))
|
|
56
|
+
sort and print diagnostics
|
|
57
|
+
writeArtifacts(base, artifacts, outputType)
|
|
58
|
+
return exit code
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The CLI returns `0` for a successful assembly, `1` when diagnostics include an
|
|
62
|
+
error and `2` for argument or unexpected runtime failures. Diagnostics are
|
|
63
|
+
printed to standard error. The primary output path is printed to standard output
|
|
64
|
+
when artifact writing succeeds.
|
|
65
|
+
|
|
66
|
+
`src/cli/parse-args.ts` parses switches and validates the command shape.
|
|
67
|
+
`src/cli/usage.ts` owns help text. The parser recognises output selection,
|
|
68
|
+
artifact suppression, include paths, source-root, case-style linting, directive
|
|
69
|
+
aliases and register contract options.
|
|
70
|
+
|
|
71
|
+
`src/cli/write-artifacts.ts` maps parsed options into
|
|
72
|
+
`CompileNextFunctionOptions` and calculates the output stem.
|
|
73
|
+
`src/cli/artifact-files.ts` writes in-memory artifacts to disk. If the user
|
|
74
|
+
supplies `--output build/program.bin`, the primary artifact is written to that
|
|
75
|
+
path and side artifacts use the same base. If the user supplies only
|
|
76
|
+
`program.asm`, AZM writes outputs next to the entry source using the source
|
|
77
|
+
stem.
|
|
78
|
+
|
|
79
|
+
## Compile API
|
|
80
|
+
|
|
81
|
+
`src/api-compile.ts` exports:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
export async function compile(
|
|
85
|
+
entryFile: string,
|
|
86
|
+
options: CompileNextFunctionOptions = {},
|
|
87
|
+
deps: CompileNextDependencies = { formats: defaultFormatWriters },
|
|
88
|
+
): Promise<CompileNextResult>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The compile API is file-backed. It reads source from disk, expands includes,
|
|
92
|
+
analyses the program, assembles it and returns artifacts in memory.
|
|
93
|
+
|
|
94
|
+
Important options include:
|
|
95
|
+
|
|
96
|
+
| Option | Meaning |
|
|
97
|
+
| -------------------------------------------- | --------------------------------------- |
|
|
98
|
+
| `includeDirs` | Include search paths. |
|
|
99
|
+
| `directiveAliasFiles` | Project alias profile files. |
|
|
100
|
+
| `caseStyle` | Case-style lint mode. |
|
|
101
|
+
| `outputType` | Primary output type, `hex` or `bin`. |
|
|
102
|
+
| `sourceRoot` | Root used for portable D8 map paths. |
|
|
103
|
+
| `d8mInputs` | Artifact paths recorded in D8 metadata. |
|
|
104
|
+
| `emitBin`, `emitHex`, `emitD8m`, `emitAsm80` | Artifact selection. |
|
|
105
|
+
| `registerContracts` | Register contract mode. |
|
|
106
|
+
| `emitRegisterReport` | Emit `.regcontracts.txt` artifact. |
|
|
107
|
+
| `emitRegisterInterface` | Emit `.asmi` artifact. |
|
|
108
|
+
| `emitRegisterAnnotations` | Emit source annotation artifact. |
|
|
109
|
+
| `fixRegisterContracts` | Apply conservative source fixes. |
|
|
110
|
+
| `acceptRegisterOutputCandidates` | Promote selected output candidates. |
|
|
111
|
+
| `registerContractsProfile` | Built-in external contract profile. |
|
|
112
|
+
| `registerContractsInterfaces` | External `.asmi` contract files. |
|
|
113
|
+
| `skipAssembly` | Run loading and analysis only. |
|
|
114
|
+
|
|
115
|
+
`compile()` returns:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
export interface CompileNextResult {
|
|
119
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
120
|
+
readonly artifacts: readonly Artifact[];
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Diagnostics describe every warning or error observed during loading, analysis,
|
|
125
|
+
register contract analysis, assembly or artifact creation. Artifacts contain the
|
|
126
|
+
in-memory outputs requested by options.
|
|
127
|
+
|
|
128
|
+
The older option names `registerCare`, `registerCareProfile` and
|
|
129
|
+
`registerCareInterfaces` remain as deprecated aliases for package consumers.
|
|
130
|
+
New callers should use the `registerContracts...` names.
|
|
131
|
+
|
|
132
|
+
## Tooling API
|
|
133
|
+
|
|
134
|
+
`src/tooling/api.ts` exports `loadProgramNext()` and `analyzeProgramNext()`.
|
|
135
|
+
`src/api-tooling.ts` re-exports those functions with register contract tooling
|
|
136
|
+
helpers.
|
|
137
|
+
|
|
138
|
+
`loadProgramNext()` returns a loaded program with source items, source texts and
|
|
139
|
+
source line comments. `analyzeProgramNext()` runs semantic checks and returns
|
|
140
|
+
symbols. `analyzeRegisterContractsForTools()` returns register contract
|
|
141
|
+
diagnostics and code actions in a form suitable for editors.
|
|
142
|
+
|
|
143
|
+
The tooling loader now recognises both `.include` and `.import`. Both directives
|
|
144
|
+
flatten source into one parse stream. `.import` also marks parsed spans with a
|
|
145
|
+
new source ownership unit, while `.include` keeps the surrounding owner's unit.
|
|
146
|
+
Editor features can use `item.span.sourceUnit` and `item.span.sourceRelation`
|
|
147
|
+
to distinguish module-owned declarations from text included into another unit.
|
|
148
|
+
Diagnostics, symbols and source segments continue to use physical source file
|
|
149
|
+
paths, so imported files appear as their own files in editor and Debug80 map
|
|
150
|
+
metadata.
|
|
151
|
+
|
|
152
|
+
An editor integration usually starts with:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const loaded = await loadProgramNext({
|
|
156
|
+
entryFile: '/project/src/main.asm',
|
|
157
|
+
includeDirs: ['/project/include'],
|
|
158
|
+
preloadedText: editorText,
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
When `loaded.loadedProgram` is present, the editor can call
|
|
163
|
+
`analyzeProgramNext()` for symbols and case-style diagnostics. It can also call
|
|
164
|
+
`analyzeRegisterContractsForTools()` for register contract candidate diagnostics
|
|
165
|
+
and code actions.
|
|
166
|
+
|
|
167
|
+
## Artifact Types
|
|
168
|
+
|
|
169
|
+
The output layer uses structured artifact objects from `src/outputs/types.ts`:
|
|
170
|
+
|
|
171
|
+
- `BinArtifact`
|
|
172
|
+
- `HexArtifact`
|
|
173
|
+
- `D8mArtifact`
|
|
174
|
+
- `Asm80Artifact`
|
|
175
|
+
- `RegisterContractsReportArtifact`
|
|
176
|
+
- `RegisterContractsInterfaceArtifact`
|
|
177
|
+
- `RegisterContractsAnnotationsArtifact`
|
|
178
|
+
|
|
179
|
+
Each artifact has a `kind` field. Callers can switch on `kind` to find the
|
|
180
|
+
artifact they need:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const d8m = result.artifacts.find((artifact) => artifact.kind === 'd8m');
|
|
184
|
+
const bin = result.artifacts.find((artifact) => artifact.kind === 'bin');
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This shape keeps the compile API independent from output paths. A caller can
|
|
188
|
+
write artifacts to disk, keep them in memory, send them to another process or
|
|
189
|
+
compare them in a test.
|
|
190
|
+
|
|
191
|
+
## Byte Maps, BIN and HEX
|
|
192
|
+
|
|
193
|
+
Assembly produces an `EmittedByteMap`. It represents sparse output: addresses
|
|
194
|
+
map to byte values and source segments describe where those bytes came from.
|
|
195
|
+
|
|
196
|
+
`src/outputs/range.ts` provides range helpers. `getWrittenSegments()` identifies
|
|
197
|
+
contiguous written address ranges. `getWrittenRange()` returns the overall
|
|
198
|
+
written span.
|
|
199
|
+
|
|
200
|
+
`src/outputs/write-bin.ts` writes flat binary. It chooses the written range,
|
|
201
|
+
fills gaps as needed and returns a `Uint8Array` artifact.
|
|
202
|
+
`src/outputs/write-hex.ts` wraps `src/outputs/hex.ts`, which writes Intel HEX
|
|
203
|
+
records and checksums.
|
|
204
|
+
|
|
205
|
+
## D8 Debug Maps
|
|
206
|
+
|
|
207
|
+
`src/outputs/write-d8.ts` writes Debug80 metadata. It records generator details,
|
|
208
|
+
input artifact paths, source files, source segments, addressable symbols and
|
|
209
|
+
value-only constants.
|
|
210
|
+
|
|
211
|
+
The writer normalizes source paths through `sourceRoot` when provided. It also
|
|
212
|
+
coalesces source segments and clips them to written ranges so Debug80 receives a
|
|
213
|
+
clean map of source lines to emitted bytes.
|
|
214
|
+
|
|
215
|
+
The D8 map distinguishes addressable symbols from constants. Labels and
|
|
216
|
+
addressable data carry addresses. Constants carry values. Debug80 can then use
|
|
217
|
+
addressable symbols for breakpoints and display constants as metadata.
|
|
218
|
+
|
|
219
|
+
## Lowered ASM80 and Register Contract Artifacts
|
|
220
|
+
|
|
221
|
+
`src/outputs/write-asm80.ts` serializes accepted AZM source items as
|
|
222
|
+
ASM80-compatible `.z80` text. It lowers supported AZM constructs into forms that
|
|
223
|
+
can be compared against ASM80 output. The writer is larger than the other
|
|
224
|
+
writers because it turns structured items back into source text.
|
|
225
|
+
|
|
226
|
+
Lowered ASM80 output currently rejects programs that use `.import`. Imported
|
|
227
|
+
source units carry visibility semantics that would be misleading if silently
|
|
228
|
+
flattened into compatibility text. The compile API reports `AZMN_ASM80` for
|
|
229
|
+
this output combination while native BIN, HEX and D8 artifacts remain
|
|
230
|
+
supported.
|
|
231
|
+
|
|
232
|
+
Register contract report, interface and annotation artifacts are created through
|
|
233
|
+
`runRegisterContracts()` in `src/api-register-contracts.ts` and flow through
|
|
234
|
+
the same compile result and CLI write path. The report is human-readable. The `.asmi`
|
|
235
|
+
interface is metadata that can be loaded by later compile runs through
|
|
236
|
+
`--interface`. Annotation artifacts write source files when `--contracts` or
|
|
237
|
+
`--fix` is used.
|
|
238
|
+
|
|
239
|
+
## Public API Compatibility
|
|
240
|
+
|
|
241
|
+
The public API is defined by package exports and exported TypeScript types.
|
|
242
|
+
Major-version planning is the point where these shapes can change:
|
|
243
|
+
|
|
244
|
+
- exported function names
|
|
245
|
+
- option object property names
|
|
246
|
+
- result object shapes
|
|
247
|
+
- artifact kinds
|
|
248
|
+
- diagnostic object shape
|
|
249
|
+
- D8 map type exports
|
|
250
|
+
- register contract tooling result shapes
|
|
251
|
+
|
|
252
|
+
The type tests are the safety net for this boundary. When a public type changes,
|
|
253
|
+
the change should be intentional and reflected in package documentation.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: 'Chapter 6 - Verification and Maintenance'
|
|
4
|
+
parent: 'AZM Engineering Manual'
|
|
5
|
+
nav_order: 6
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
[<- Interfaces and Output Artifacts](05-interfaces-and-output-artifacts.md) | [Appendices ->](appendices/)
|
|
9
|
+
|
|
10
|
+
# Chapter 6 - Verification and Maintenance
|
|
11
|
+
|
|
12
|
+
AZM's verification suite is organised by compiler boundary. A parser change has
|
|
13
|
+
a parser test. A byte-emission change has an encoder or integration test. A
|
|
14
|
+
public API change has a type-surface test. Reading the tests beside the
|
|
15
|
+
implementation is often the fastest way to understand a subsystem.
|
|
16
|
+
|
|
17
|
+
## Test Directory Map
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
test/
|
|
21
|
+
unit/
|
|
22
|
+
integration/
|
|
23
|
+
cli/
|
|
24
|
+
asm80/
|
|
25
|
+
differential/
|
|
26
|
+
fixtures/
|
|
27
|
+
helpers/
|
|
28
|
+
types/
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Start with the narrowest useful test, then add the integration or CLI coverage
|
|
32
|
+
that proves the user-facing behaviour.
|
|
33
|
+
|
|
34
|
+
## Unit and Integration Tests
|
|
35
|
+
|
|
36
|
+
Unit tests live under `test/unit/` and mirror implementation directories:
|
|
37
|
+
|
|
38
|
+
| Directory | Boundary |
|
|
39
|
+
| -------------------------- | ------------------------------------------------------------------- |
|
|
40
|
+
| `unit/syntax/` | Line parsing, expression parsing and directive aliases. |
|
|
41
|
+
| `unit/source/` | Logical line and comment handling. |
|
|
42
|
+
| `unit/z80/` | Instruction parsing, diagnostics and encoding. |
|
|
43
|
+
| `unit/outputs/` | Artifact writer behaviour. |
|
|
44
|
+
| `unit/expansion/` | Op collection and expansion. |
|
|
45
|
+
| `unit/register-contracts/` | Register contract carriers, summaries, liveness, reports and fixes. |
|
|
46
|
+
|
|
47
|
+
Unit tests are small and direct. A new indexed operand form belongs in
|
|
48
|
+
`test/unit/z80/` before it appears in a full source fixture. The unit test proves
|
|
49
|
+
the instruction parser and encoder agree on that one form.
|
|
50
|
+
|
|
51
|
+
Integration tests under `test/integration/` assemble real source snippets
|
|
52
|
+
through multiple compiler stages. Layout features, ops and register contracts
|
|
53
|
+
interactions usually need this level of test because the behaviour exists
|
|
54
|
+
between modules rather than inside one helper.
|
|
55
|
+
|
|
56
|
+
Source-loading and tooling provenance changes belong here as well. The
|
|
57
|
+
`stage-11-tooling-api.test.ts` integration suite covers include and import
|
|
58
|
+
resolution, recursive load failures and the span ownership metadata that tools
|
|
59
|
+
read from parsed items.
|
|
60
|
+
|
|
61
|
+
## CLI, ASM80 and Differential Tests
|
|
62
|
+
|
|
63
|
+
`test/cli/` verifies the command-line contract: options, artifact writing,
|
|
64
|
+
failure modes, determinism, case-style linting and register contract switches.
|
|
65
|
+
Users experience the command-line behaviour through argument parsing,
|
|
66
|
+
diagnostics, output paths and exit status.
|
|
67
|
+
|
|
68
|
+
CLI tests also protect deterministic output. `compareDiagnosticsForCli()` sorts
|
|
69
|
+
diagnostics by file, line, column, severity, code and message. A CLI test can
|
|
70
|
+
catch changes that leave the compiler correct but make terminal output unstable.
|
|
71
|
+
|
|
72
|
+
`test/asm80/` and `test/differential/` protect compatibility and byte parity.
|
|
73
|
+
These tests compare AZM behaviour against ASM80 expectations, lowered output and
|
|
74
|
+
real-program fixtures. For an assembler, a one-byte difference is a behavioural
|
|
75
|
+
change.
|
|
76
|
+
|
|
77
|
+
## Fixtures and Helpers
|
|
78
|
+
|
|
79
|
+
`test/fixtures/` contains small source programs named after the issue or
|
|
80
|
+
behaviour they cover. A good fixture shows the source shape that matters. It
|
|
81
|
+
includes enough context to assemble and diagnose the behaviour, then stops.
|
|
82
|
+
|
|
83
|
+
`test/helpers/` contains shared helpers for CLI runs, diagnostics, temporary
|
|
84
|
+
source files and acceptance tests. Use helpers for repetitive setup. Keep test
|
|
85
|
+
expectations close to the test itself so failures stay easy to read.
|
|
86
|
+
|
|
87
|
+
`test/types/` and `test/public_api_surface.test.ts` protect the package export
|
|
88
|
+
surface. Run them whenever changing `src/index.ts`, `src/api-compile.ts`,
|
|
89
|
+
`src/api-tooling.ts`, `src/outputs/types.ts` or `package.json` exports.
|
|
90
|
+
|
|
91
|
+
## Guardrails
|
|
92
|
+
|
|
93
|
+
The main package scripts are:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
npm run build
|
|
97
|
+
npm run typecheck
|
|
98
|
+
npm run lint
|
|
99
|
+
npm run test:azm:alpha
|
|
100
|
+
npm run test:azm:corpus
|
|
101
|
+
npm run next:guardrails:core
|
|
102
|
+
npm run next:guardrails:package
|
|
103
|
+
npm run next:guardrails:quality
|
|
104
|
+
npm run next:guardrails
|
|
105
|
+
npm test
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Additional guardrails live under `scripts/ci/` and `scripts/dev/`. They answer
|
|
109
|
+
larger questions: does the alpha lane still pass, does ASM80 parity still hold,
|
|
110
|
+
does the package work after build output is generated, does lowered ASM80 still
|
|
111
|
+
cover the intended source set, do source files remain within the size budget and
|
|
112
|
+
does the corpus still match the accepted compatibility baseline.
|
|
113
|
+
|
|
114
|
+
Use this map when choosing a verification lane:
|
|
115
|
+
|
|
116
|
+
| Change | Tests |
|
|
117
|
+
| --------------------------- | -------------------------------------------------------------------- |
|
|
118
|
+
| Parser or expression syntax | `test/unit/syntax/**`, relevant integration tests. |
|
|
119
|
+
| Source loading or tooling provenance | `test/integration/stage-11-tooling-api.test.ts`, relevant unit tests in `test/unit/source/**`. |
|
|
120
|
+
| Z80 instruction support | `test/unit/z80/**`, diagnostic matrices, ASM80 parity when relevant. |
|
|
121
|
+
| Layout semantics | layout integration tests and output tests. |
|
|
122
|
+
| Ops | `test/unit/expansion/**`, op integration tests. |
|
|
123
|
+
| Register contracts | register contract unit, integration and CLI tests. |
|
|
124
|
+
| CLI options | `test/cli/**`. |
|
|
125
|
+
| Output artifacts | `test/unit/outputs/**`, CLI artifact tests. |
|
|
126
|
+
| Public API | type tests, public API surface tests and tooling API tests. |
|
|
127
|
+
| ASM80 lowering or parity | `check:asm80-coverage`, `test:ci:asm80-parity`, corpus guardrails. |
|
|
128
|
+
| Package export surface | `test:package`, public API surface tests. |
|
|
129
|
+
|
|
130
|
+
For subtle compiler changes, use a narrow test that names the broken layer and
|
|
131
|
+
a broader test that proves the public behaviour. That combination makes failures
|
|
132
|
+
easy to diagnose.
|
|
133
|
+
|
|
134
|
+
## Maintenance Boundaries
|
|
135
|
+
|
|
136
|
+
AZM is stable enough that changes should preserve the existing boundaries. A
|
|
137
|
+
maintainer can usually decide where a change belongs before editing code:
|
|
138
|
+
source loading, parsing, expression evaluation, address planning, Z80 encoding,
|
|
139
|
+
op expansion, register contracts, artifact writing, CLI or public API.
|
|
140
|
+
|
|
141
|
+
Ask what kind of fact the change affects:
|
|
142
|
+
|
|
143
|
+
- Text and files belong in `node/` and `source/`.
|
|
144
|
+
- Syntax belongs in `syntax/` or the structural parsing section of
|
|
145
|
+
`core/compile.ts`.
|
|
146
|
+
- Assembler-time facts belong in `assembly/` and `semantics/`.
|
|
147
|
+
- Instruction forms belong in `z80/`.
|
|
148
|
+
- Inline source generation belongs in `expansion/`.
|
|
149
|
+
- Routine contracts and liveness belong in `register-contracts/`.
|
|
150
|
+
- Artifact shape belongs in `outputs/`.
|
|
151
|
+
- User commands belong in `cli/`.
|
|
152
|
+
- Package consumers belong in `api-compile.ts`, `api-tooling.ts` and
|
|
153
|
+
`index.ts`.
|
|
154
|
+
|
|
155
|
+
This boundary choice determines the files to read, the tests to write and the
|
|
156
|
+
documentation to update.
|
|
157
|
+
|
|
158
|
+
## Structured Data and Compatibility
|
|
159
|
+
|
|
160
|
+
AZM passes structured data between stages. When a later stage needs more
|
|
161
|
+
information, add it to the earlier structured model and carry it forward.
|
|
162
|
+
Source provenance belongs on logical lines and source items. Syntax shape
|
|
163
|
+
belongs in `SourceItem`. Instruction shape belongs in `Z80Instruction`. Layout
|
|
164
|
+
facts belong in layout records and type expressions. Artifact metadata belongs
|
|
165
|
+
in output types.
|
|
166
|
+
|
|
167
|
+
Directive aliases and ASM80 lowering serve compatibility. Native AZM syntax
|
|
168
|
+
stays clean inside the compiler model when compatibility forms are converted to
|
|
169
|
+
canonical source items early and compatibility output is serialized late.
|
|
170
|
+
|
|
171
|
+
## Diagnostics and Manual Updates
|
|
172
|
+
|
|
173
|
+
Diagnostics should name the source location, the failing construct and the
|
|
174
|
+
reason. Parser diagnostics should recover where possible. Assembly diagnostics
|
|
175
|
+
should be deterministic. CLI output should sort diagnostics consistently.
|
|
176
|
+
|
|
177
|
+
This book should change when:
|
|
178
|
+
|
|
179
|
+
- a source directory is added, removed or repurposed
|
|
180
|
+
- the compile flow changes
|
|
181
|
+
- public package exports change
|
|
182
|
+
- CLI option groups change
|
|
183
|
+
- output artifact shapes change
|
|
184
|
+
- a major subsystem gains a new responsibility
|
|
185
|
+
- tests or guardrails are reorganised
|
|
186
|
+
|
|
187
|
+
Small implementation changes usually need test updates. Structural changes need
|
|
188
|
+
tests and manual updates.
|
|
189
|
+
|
|
190
|
+
## Suggested Change Workflow
|
|
191
|
+
|
|
192
|
+
1. Identify the boundary.
|
|
193
|
+
2. Read the relevant chapter and appendix entry.
|
|
194
|
+
3. Add or revise the closest test for the behaviour.
|
|
195
|
+
4. Change the implementation.
|
|
196
|
+
5. Run the focused test.
|
|
197
|
+
6. Run the broader guardrail that matches the public behaviour.
|
|
198
|
+
7. Update docs when the contract or architecture changed.
|
|
199
|
+
|
|
200
|
+
This workflow leaves evidence at the level where future maintainers will look:
|
|
201
|
+
the affected boundary, the public behaviour and the engineering manual when the
|
|
202
|
+
structure changes.
|