@jhlagado/azm 0.2.7 → 0.2.9

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.
Files changed (253) hide show
  1. package/README.md +239 -76
  2. package/dist/src/api-artifacts.d.ts +20 -0
  3. package/dist/src/api-artifacts.js +165 -0
  4. package/dist/src/api-compile.d.ts +8 -2
  5. package/dist/src/api-compile.js +55 -227
  6. package/dist/src/api-register-contracts.d.ts +9 -0
  7. package/dist/src/api-register-contracts.js +77 -0
  8. package/dist/src/api-tooling.d.ts +2 -2
  9. package/dist/src/api-tooling.js +1 -1
  10. package/dist/src/assembly/address-planning.d.ts +1 -2
  11. package/dist/src/assembly/address-planning.js +119 -218
  12. package/dist/src/assembly/address-symbols.d.ts +12 -0
  13. package/dist/src/assembly/address-symbols.js +118 -0
  14. package/dist/src/assembly/assemble-program.js +5 -0
  15. package/dist/src/assembly/fixup-emission.js +30 -48
  16. package/dist/src/assembly/import-visibility.d.ts +3 -0
  17. package/dist/src/assembly/import-visibility.js +204 -0
  18. package/dist/src/assembly/program-emission.js +163 -164
  19. package/dist/src/cli/artifact-files.d.ts +15 -0
  20. package/dist/src/cli/artifact-files.js +86 -0
  21. package/dist/src/cli/parse-args.d.ts +6 -5
  22. package/dist/src/cli/parse-args.js +162 -136
  23. package/dist/src/cli/run.js +4 -1
  24. package/dist/src/cli/usage.d.ts +1 -0
  25. package/dist/src/cli/usage.js +33 -0
  26. package/dist/src/cli/write-artifacts.js +18 -91
  27. package/dist/src/core/compile.js +51 -274
  28. package/dist/src/core/conditional-assembly.d.ts +6 -0
  29. package/dist/src/core/conditional-assembly.js +181 -0
  30. package/dist/src/expansion/op-constant-expression.d.ts +3 -0
  31. package/dist/src/expansion/op-constant-expression.js +52 -0
  32. package/dist/src/expansion/op-expand-selected.d.ts +5 -0
  33. package/dist/src/expansion/op-expand-selected.js +143 -0
  34. package/dist/src/expansion/op-expansion.d.ts +5 -53
  35. package/dist/src/expansion/op-expansion.js +85 -815
  36. package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
  37. package/dist/src/expansion/op-instruction-instantiation.js +194 -0
  38. package/dist/src/expansion/op-local-labels.d.ts +8 -0
  39. package/dist/src/expansion/op-local-labels.js +166 -0
  40. package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
  41. package/dist/src/expansion/op-operand-splitting.js +44 -0
  42. package/dist/src/expansion/op-operands.d.ts +53 -0
  43. package/dist/src/expansion/op-operands.js +66 -0
  44. package/dist/src/expansion/op-selection.d.ts +18 -0
  45. package/dist/src/expansion/op-selection.js +172 -0
  46. package/dist/src/index.d.ts +2 -1
  47. package/dist/src/index.js +1 -1
  48. package/dist/src/model/diagnostic.d.ts +4 -0
  49. package/dist/src/model/diagnostic.js +4 -0
  50. package/dist/src/node/source-host.js +40 -13
  51. package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
  52. package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
  53. package/dist/src/outputs/asm80-expressions.d.ts +5 -0
  54. package/dist/src/outputs/asm80-expressions.js +47 -0
  55. package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
  56. package/dist/src/outputs/asm80-instruction-operands.js +38 -0
  57. package/dist/src/outputs/asm80-instructions.d.ts +5 -0
  58. package/dist/src/outputs/asm80-instructions.js +272 -0
  59. package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
  60. package/dist/src/outputs/asm80-ld-operands.js +157 -0
  61. package/dist/src/outputs/asm80-strings.d.ts +4 -0
  62. package/dist/src/outputs/asm80-strings.js +14 -0
  63. package/dist/src/outputs/d8-files.d.ts +10 -0
  64. package/dist/src/outputs/d8-files.js +103 -0
  65. package/dist/src/outputs/d8-helpers.d.ts +21 -0
  66. package/dist/src/outputs/d8-helpers.js +136 -0
  67. package/dist/src/outputs/hex.js +26 -18
  68. package/dist/src/outputs/types.d.ts +16 -10
  69. package/dist/src/outputs/write-asm80.js +72 -597
  70. package/dist/src/outputs/write-d8.js +6 -216
  71. package/dist/src/register-contracts/accept-output.d.ts +2 -0
  72. package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
  73. package/dist/src/register-contracts/analyze-helpers.js +162 -0
  74. package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
  75. package/dist/src/register-contracts/analyze.js +73 -0
  76. package/dist/src/register-contracts/annotate.d.ts +11 -0
  77. package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
  78. package/dist/src/register-contracts/annotations.d.ts +8 -0
  79. package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
  80. package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
  81. package/dist/src/register-contracts/boundaryHints.js +24 -0
  82. package/dist/src/register-contracts/carriers.d.ts +2 -0
  83. package/dist/src/register-contracts/constants.d.ts +4 -0
  84. package/dist/src/register-contracts/constants.js +51 -0
  85. package/dist/src/register-contracts/controlFlow.d.ts +5 -0
  86. package/dist/src/register-contracts/controlFlow.js +55 -0
  87. package/dist/src/register-contracts/fix.d.ts +11 -0
  88. package/dist/src/{register-care → register-contracts}/fix.js +47 -30
  89. package/dist/src/register-contracts/instruction-head.d.ts +2 -0
  90. package/dist/src/register-contracts/instruction-head.js +3 -0
  91. package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
  92. package/dist/src/register-contracts/instruction-operands.js +101 -0
  93. package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
  94. package/dist/src/register-contracts/instruction-predicates.js +44 -0
  95. package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
  96. package/dist/src/register-contracts/interfaceContracts.js +68 -0
  97. package/dist/src/register-contracts/liveness.d.ts +3 -0
  98. package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
  99. package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
  100. package/dist/src/register-contracts/operand-register-name.js +13 -0
  101. package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
  102. package/dist/src/{register-care → register-contracts}/profiles.js +2 -2
  103. package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
  104. package/dist/src/register-contracts/programModel-boundaries.js +64 -0
  105. package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
  106. package/dist/src/register-contracts/programModel-routines.js +144 -0
  107. package/dist/src/register-contracts/programModel.d.ts +3 -0
  108. package/dist/src/register-contracts/programModel.js +14 -0
  109. package/dist/src/register-contracts/report.d.ts +5 -0
  110. package/dist/src/{register-care → register-contracts}/report.js +34 -17
  111. package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
  112. package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
  113. package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
  114. package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
  115. package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
  116. package/dist/src/register-contracts/smartCommentParsing.js +80 -0
  117. package/dist/src/register-contracts/smartComments.d.ts +5 -0
  118. package/dist/src/register-contracts/smartComments.js +92 -0
  119. package/dist/src/register-contracts/summaries.d.ts +12 -0
  120. package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
  121. package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
  122. package/dist/src/register-contracts/summary-boundary.js +40 -0
  123. package/dist/src/register-contracts/summary-contract.d.ts +2 -0
  124. package/dist/src/register-contracts/summary-contract.js +45 -0
  125. package/dist/src/register-contracts/summary-result.d.ts +7 -0
  126. package/dist/src/register-contracts/summary-result.js +122 -0
  127. package/dist/src/register-contracts/summary-state.d.ts +23 -0
  128. package/dist/src/register-contracts/summary-state.js +88 -0
  129. package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
  130. package/dist/src/register-contracts/summary-token-transfer.js +67 -0
  131. package/dist/src/register-contracts/summary.d.ts +3 -0
  132. package/dist/src/register-contracts/summary.js +266 -0
  133. package/dist/src/register-contracts/tooling.d.ts +57 -0
  134. package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
  135. package/dist/src/register-contracts/types.d.ts +188 -0
  136. package/dist/src/semantics/binary-operators.d.ts +2 -0
  137. package/dist/src/semantics/binary-operators.js +15 -0
  138. package/dist/src/semantics/byte-functions.d.ts +2 -0
  139. package/dist/src/semantics/byte-functions.js +7 -0
  140. package/dist/src/semantics/constant-operator-types.d.ts +10 -0
  141. package/dist/src/semantics/constant-operator-types.js +1 -0
  142. package/dist/src/semantics/constant-operators.d.ts +3 -0
  143. package/dist/src/semantics/constant-operators.js +3 -0
  144. package/dist/src/semantics/diagnostics.d.ts +3 -0
  145. package/dist/src/semantics/diagnostics.js +10 -0
  146. package/dist/src/semantics/expression-evaluation.d.ts +11 -19
  147. package/dist/src/semantics/expression-evaluation.js +22 -334
  148. package/dist/src/semantics/layout-evaluation.d.ts +23 -0
  149. package/dist/src/semantics/layout-evaluation.js +202 -0
  150. package/dist/src/semantics/layout-format.d.ts +5 -0
  151. package/dist/src/semantics/layout-format.js +31 -0
  152. package/dist/src/semantics/layout-path.d.ts +24 -0
  153. package/dist/src/semantics/layout-path.js +58 -0
  154. package/dist/src/semantics/unary-operators.d.ts +2 -0
  155. package/dist/src/semantics/unary-operators.js +8 -0
  156. package/dist/src/source/line-comment-scanner.d.ts +1 -0
  157. package/dist/src/source/line-comment-scanner.js +51 -0
  158. package/dist/src/source/logical-lines.d.ts +3 -0
  159. package/dist/src/source/source-span.d.ts +2 -0
  160. package/dist/src/source/strip-line-comment.js +8 -44
  161. package/dist/src/syntax/directive-aliases.js +36 -22
  162. package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
  163. package/dist/src/syntax/expression-tokenizer.js +310 -0
  164. package/dist/src/syntax/parse-directive-statement.d.ts +9 -0
  165. package/dist/src/syntax/parse-directive-statement.js +309 -0
  166. package/dist/src/syntax/parse-expression.d.ts +2 -2
  167. package/dist/src/syntax/parse-expression.js +7 -568
  168. package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
  169. package/dist/src/syntax/parse-layout-declarations.js +189 -0
  170. package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
  171. package/dist/src/syntax/parse-layout-expression.js +175 -0
  172. package/dist/src/syntax/parse-line.js +21 -273
  173. package/dist/src/syntax/parse-token-expression.d.ts +3 -0
  174. package/dist/src/syntax/parse-token-expression.js +133 -0
  175. package/dist/src/tooling/api.js +1 -1
  176. package/dist/src/tooling/case-style.js +47 -30
  177. package/dist/src/z80/effect-groups.d.ts +38 -0
  178. package/dist/src/z80/effect-groups.js +265 -0
  179. package/dist/src/z80/effect-units.d.ts +18 -0
  180. package/dist/src/z80/effect-units.js +165 -0
  181. package/dist/src/z80/effects.d.ts +1 -1
  182. package/dist/src/z80/effects.js +94 -557
  183. package/dist/src/z80/encode-core.d.ts +2 -0
  184. package/dist/src/z80/encode-core.js +42 -0
  185. package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
  186. package/dist/src/z80/encode-ld-helpers.js +172 -0
  187. package/dist/src/z80/encode-ld.d.ts +2 -0
  188. package/dist/src/z80/encode-ld.js +285 -0
  189. package/dist/src/z80/encode.js +190 -542
  190. package/dist/src/z80/ld-support.d.ts +3 -0
  191. package/dist/src/z80/ld-support.js +146 -0
  192. package/dist/src/z80/operand-split-state.d.ts +8 -0
  193. package/dist/src/z80/operand-split-state.js +46 -0
  194. package/dist/src/z80/operand-split.d.ts +1 -0
  195. package/dist/src/z80/operand-split.js +13 -0
  196. package/dist/src/z80/parse-basic.d.ts +4 -0
  197. package/dist/src/z80/parse-basic.js +39 -0
  198. package/dist/src/z80/parse-branch.d.ts +4 -0
  199. package/dist/src/z80/parse-branch.js +218 -0
  200. package/dist/src/z80/parse-conditions.d.ts +6 -0
  201. package/dist/src/z80/parse-conditions.js +10 -0
  202. package/dist/src/z80/parse-exchange.d.ts +2 -0
  203. package/dist/src/z80/parse-exchange.js +30 -0
  204. package/dist/src/z80/parse-instruction.js +224 -1010
  205. package/dist/src/z80/parse-io-control.d.ts +5 -0
  206. package/dist/src/z80/parse-io-control.js +108 -0
  207. package/dist/src/z80/parse-ld.d.ts +2 -0
  208. package/dist/src/z80/parse-ld.js +83 -0
  209. package/dist/src/z80/parse-operands.d.ts +41 -0
  210. package/dist/src/z80/parse-operands.js +259 -0
  211. package/docs/codebase/01-orientation-and-repository-layout.md +192 -0
  212. package/docs/codebase/02-source-loading-and-parsing.md +263 -0
  213. package/docs/codebase/03-assembly-and-z80-emission.md +251 -0
  214. package/docs/codebase/04-ops-and-register-contracts.md +237 -0
  215. package/docs/codebase/05-interfaces-and-output-artifacts.md +253 -0
  216. package/docs/codebase/06-verification-and-maintenance.md +202 -0
  217. package/docs/codebase/appendices/a-directory-file-reference.md +253 -0
  218. package/docs/codebase/appendices/b-compile-flow-reference.md +103 -0
  219. package/docs/codebase/appendices/c-public-surface-reference.md +106 -0
  220. package/docs/codebase/appendices/index.md +16 -0
  221. package/docs/codebase/index.md +46 -0
  222. package/package.json +2 -3
  223. package/dist/src/register-care/accept-output.d.ts +0 -2
  224. package/dist/src/register-care/analyze.js +0 -166
  225. package/dist/src/register-care/annotate.d.ts +0 -11
  226. package/dist/src/register-care/annotations.d.ts +0 -8
  227. package/dist/src/register-care/boundaryHints.d.ts +0 -3
  228. package/dist/src/register-care/boundaryHints.js +0 -80
  229. package/dist/src/register-care/carriers.d.ts +0 -2
  230. package/dist/src/register-care/controlFlow.d.ts +0 -5
  231. package/dist/src/register-care/controlFlow.js +0 -38
  232. package/dist/src/register-care/fix.d.ts +0 -11
  233. package/dist/src/register-care/instruction-shape.d.ts +0 -11
  234. package/dist/src/register-care/instruction-shape.js +0 -129
  235. package/dist/src/register-care/liveness.d.ts +0 -3
  236. package/dist/src/register-care/programModel.d.ts +0 -3
  237. package/dist/src/register-care/programModel.js +0 -266
  238. package/dist/src/register-care/report.d.ts +0 -5
  239. package/dist/src/register-care/routine-summaries.d.ts +0 -6
  240. package/dist/src/register-care/smartComments.d.ts +0 -5
  241. package/dist/src/register-care/smartComments.js +0 -243
  242. package/dist/src/register-care/summaries.d.ts +0 -12
  243. package/dist/src/register-care/summary.d.ts +0 -3
  244. package/dist/src/register-care/summary.js +0 -474
  245. package/dist/src/register-care/tooling.d.ts +0 -43
  246. package/dist/src/register-care/types.d.ts +0 -172
  247. package/docs/reference/cli.md +0 -151
  248. package/docs/reference/tooling-api.md +0 -316
  249. /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
  250. /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
  251. /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
  252. /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
  253. /package/dist/src/{register-care → register-contracts}/types.js +0 -0
@@ -1,38 +1,58 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { extname } from 'node:path';
3
- export function cliUsage() {
4
- return [
5
- 'azm [options] <entry.asm|entry.z80>',
6
- '',
7
- 'Options:',
8
- ' -o, --output <file> Primary output path (must match --type extension)',
9
- ' -t, --type <type> Primary output type: hex|bin (default: hex)',
10
- ' --nobin Suppress .bin',
11
- ' --nohex Suppress .hex',
12
- ' --nod8m Suppress .d8.json',
13
- ' --asm80 Emit lowered source (.z80)',
14
- ' --register-care Register-care mode: off|audit|warn|error|strict',
15
- ' --rc <m> Register-care mode alias for --register-care',
16
- ' --reg-report Emit register-care report artifact',
17
- ' --reg-interface Emit inferred register-care interface (.asmi)',
18
- ' --contracts Rewrite source with inferred register-care contracts',
19
- ' --fix Enable contract rewrite and conservative fixes',
20
- ' --accept-out <x> Accept register-care output candidates',
21
- ' --interface <file> Load .asmi contract file',
22
- ' --reg-profile <p> Register-care profile (currently mon3)',
23
- ' --source-root <d> Normalize D8 source paths relative to this directory',
24
- ' --case-style <m> Case-style lint mode: off|upper|lower|consistent',
25
- ' --aliases <file> Load project directive alias JSON (repeatable)',
26
- ' -I, --include <dir> Add include search path (repeatable)',
27
- ' -V, --version Print version',
28
- ' -h, --help Show help',
29
- '',
30
- 'Notes:',
31
- ' - <entry.asm|entry.z80> must be the last argument (assembler-style).',
32
- ' - Output artifacts are written using the primary output stem with standard suffixes.',
33
- '',
34
- ].join('\n');
35
- }
3
+ import { cliUsage } from './usage.js';
4
+ const BOOLEAN_FLAG_ACTIONS = [
5
+ {
6
+ flags: ['--nobin'],
7
+ apply: (state) => {
8
+ state.emitBin = false;
9
+ },
10
+ },
11
+ {
12
+ flags: ['--nohex'],
13
+ apply: (state) => {
14
+ state.emitHex = false;
15
+ },
16
+ },
17
+ {
18
+ flags: ['--nod8m'],
19
+ apply: (state) => {
20
+ state.emitD8m = false;
21
+ },
22
+ },
23
+ {
24
+ flags: ['--asm80'],
25
+ apply: (state) => {
26
+ state.emitAsm80 = true;
27
+ },
28
+ },
29
+ {
30
+ flags: ['--emit-register-report', '--reg-report'],
31
+ apply: (state) => {
32
+ state.emitRegisterReport = true;
33
+ },
34
+ },
35
+ {
36
+ flags: ['--emit-register-interface', '--reg-interface'],
37
+ apply: (state) => {
38
+ state.emitRegisterInterface = true;
39
+ },
40
+ },
41
+ {
42
+ flags: ['--fix'],
43
+ apply: (state) => {
44
+ state.fixRegisterContracts = true;
45
+ state.emitRegisterAnnotations = true;
46
+ },
47
+ },
48
+ {
49
+ flags: ['--contracts', '--annotate-register-contracts'],
50
+ apply: (state) => {
51
+ state.emitRegisterAnnotations = true;
52
+ },
53
+ },
54
+ ];
55
+ export { cliUsage };
36
56
  function fail(message) {
37
57
  throw new Error(message);
38
58
  }
@@ -45,13 +65,13 @@ function createDefaultCliState() {
45
65
  emitD8m: true,
46
66
  emitAsm80: false,
47
67
  caseStyle: 'off',
48
- registerCare: 'off',
68
+ registerContracts: 'off',
49
69
  emitRegisterReport: false,
50
70
  emitRegisterInterface: false,
51
71
  emitRegisterAnnotations: false,
52
72
  fixRegisterContracts: false,
53
73
  acceptRegisterOutputCandidates: [],
54
- registerCareInterfaces: [],
74
+ registerContractsInterfaces: [],
55
75
  sourceRoot: undefined,
56
76
  includeDirs: [],
57
77
  directiveAliasFiles: [],
@@ -89,7 +109,9 @@ function parseOutputPathArg(arg, argv, indexRef, state) {
89
109
  function parseOutputTypeArg(arg, argv, indexRef, state) {
90
110
  if (arg !== '-t' && arg !== '--type' && !arg.startsWith('--type='))
91
111
  return false;
92
- const value = arg.startsWith('--type=') ? arg.slice('--type='.length) : readValue(argv, indexRef, '--type');
112
+ const value = arg.startsWith('--type=')
113
+ ? arg.slice('--type='.length)
114
+ : readValue(argv, indexRef, '--type');
93
115
  if (!value) {
94
116
  fail('--type expects a value');
95
117
  }
@@ -148,8 +170,12 @@ function readMatchedFlagValue(arg, argv, indexRef, flags) {
148
170
  }
149
171
  return { flag, value };
150
172
  }
151
- function parseRegisterCareArg(arg, argv, indexRef, state) {
152
- const parsed = readMatchedFlagValue(arg, argv, indexRef, ['--register-care', '--rc']);
173
+ function parseRegisterContractsArg(arg, argv, indexRef, state) {
174
+ const parsed = readMatchedFlagValue(arg, argv, indexRef, [
175
+ '--register-contracts',
176
+ '--register-care',
177
+ '--rc',
178
+ ]);
153
179
  if (!parsed)
154
180
  return false;
155
181
  const { value, flag } = parsed;
@@ -160,7 +186,7 @@ function parseRegisterCareArg(arg, argv, indexRef, state) {
160
186
  value !== 'strict') {
161
187
  fail(`Unsupported ${flag} "${value}" (expected off|audit|warn|error|strict)`);
162
188
  }
163
- state.registerCare = value;
189
+ state.registerContracts = value;
164
190
  return true;
165
191
  }
166
192
  function parseRegisterProfileArg(arg, argv, indexRef, state) {
@@ -170,7 +196,7 @@ function parseRegisterProfileArg(arg, argv, indexRef, state) {
170
196
  if (parsed.value !== 'mon3') {
171
197
  fail(`Unsupported ${parsed.flag} "${parsed.value}" (expected mon3)`);
172
198
  }
173
- state.registerCareProfile = parsed.value;
199
+ state.registerContractsProfile = parsed.value;
174
200
  return true;
175
201
  }
176
202
  function parseRegisterInterfaceArg(arg, argv, indexRef, state) {
@@ -181,7 +207,7 @@ function parseRegisterInterfaceArg(arg, argv, indexRef, state) {
181
207
  : readValue(argv, indexRef, '--interface');
182
208
  if (!value)
183
209
  fail('--interface expects a value');
184
- state.registerCareInterfaces.push(value);
210
+ state.registerContractsInterfaces.push(value);
185
211
  return true;
186
212
  }
187
213
  function parseAcceptOutputArg(arg, argv, indexRef, state) {
@@ -211,58 +237,116 @@ function handleFastPath(arg) {
211
237
  }
212
238
  return undefined;
213
239
  }
240
+ function cliOptionOutputPath(state) {
241
+ return state.outputPath ? { outputPath: state.outputPath } : {};
242
+ }
243
+ function cliOptionSourceRoot(state) {
244
+ return state.sourceRoot !== undefined ? { sourceRoot: state.sourceRoot } : {};
245
+ }
246
+ function cliOptionRegisterContractsProfile(state) {
247
+ return state.registerContractsProfile !== undefined
248
+ ? { registerContractsProfile: state.registerContractsProfile }
249
+ : {};
250
+ }
214
251
  function finalizeCliOptions(state) {
215
- if (!state.entryFile) {
216
- fail(`Expected exactly one <entry.asm|entry.z80> argument (and it must be last)`);
217
- }
218
- const ext = extname(state.entryFile).toLowerCase();
219
- if (ext !== '.asm' && ext !== '.z80') {
220
- fail(`Unsupported entry extension "${ext || '<none>'}" (expected .asm, .z80)`);
221
- }
222
- if (state.outputPath !== undefined) {
223
- const wantExt = state.outputType === 'hex' ? '.hex' : '.bin';
224
- const providedExt = extname(state.outputPath).toLowerCase();
225
- if (providedExt !== wantExt) {
226
- fail(`--output must end with "${wantExt}" when --type is "${state.outputType}"`);
227
- }
228
- }
229
- const emitsRegisterCare = state.registerCare !== 'off' ||
230
- state.emitRegisterReport ||
231
- state.emitRegisterInterface ||
232
- state.emitRegisterAnnotations ||
233
- state.fixRegisterContracts ||
234
- state.acceptRegisterOutputCandidates.length > 0 ||
235
- state.registerCareInterfaces.length > 0;
236
- if (state.outputType === 'hex' && !state.emitHex && !emitsRegisterCare) {
237
- fail(`--type hex requires HEX output to be enabled`);
238
- }
239
- if (state.outputType === 'bin' && !state.emitBin && !emitsRegisterCare) {
240
- fail(`--type bin requires BIN output to be enabled`);
241
- }
252
+ validateEntryFile(state.entryFile);
253
+ validateOutputPath(state);
254
+ validateEnabledPrimaryOutput(state);
242
255
  return {
243
256
  entryFile: state.entryFile,
244
- ...(state.outputPath ? { outputPath: state.outputPath } : {}),
257
+ ...cliOptionOutputPath(state),
245
258
  outputType: state.outputType,
246
- ...(state.sourceRoot !== undefined ? { sourceRoot: state.sourceRoot } : {}),
259
+ ...cliOptionSourceRoot(state),
247
260
  emitBin: state.emitBin,
248
261
  emitHex: state.emitHex,
249
262
  emitD8m: state.emitD8m,
250
263
  emitAsm80: state.emitAsm80,
251
264
  caseStyle: state.caseStyle,
252
- registerCare: state.registerCare,
265
+ registerContracts: state.registerContracts,
253
266
  emitRegisterReport: state.emitRegisterReport,
254
267
  emitRegisterInterface: state.emitRegisterInterface,
255
268
  emitRegisterAnnotations: state.emitRegisterAnnotations,
256
269
  fixRegisterContracts: state.fixRegisterContracts,
257
270
  acceptRegisterOutputCandidates: state.acceptRegisterOutputCandidates,
258
- ...(state.registerCareProfile !== undefined
259
- ? { registerCareProfile: state.registerCareProfile }
260
- : {}),
261
- registerCareInterfaces: state.registerCareInterfaces,
271
+ ...cliOptionRegisterContractsProfile(state),
272
+ registerContractsInterfaces: state.registerContractsInterfaces,
262
273
  includeDirs: state.includeDirs,
263
274
  directiveAliasFiles: state.directiveAliasFiles,
264
275
  };
265
276
  }
277
+ function validateEntryFile(entryFile) {
278
+ if (!entryFile) {
279
+ fail(`Expected exactly one <entry.asm|entry.z80> argument (and it must be last)`);
280
+ }
281
+ }
282
+ function validateOutputPath(state) {
283
+ if (!state.entryFile)
284
+ return;
285
+ const ext = extname(state.entryFile).toLowerCase();
286
+ if (ext !== '.asm' && ext !== '.z80') {
287
+ fail(`Unsupported entry extension "${ext || '<none>'}" (expected .asm, .z80)`);
288
+ }
289
+ if (state.outputPath !== undefined) {
290
+ const wantExt = state.outputType === 'hex' ? '.hex' : '.bin';
291
+ const providedExt = extname(state.outputPath).toLowerCase();
292
+ if (providedExt !== wantExt) {
293
+ fail(`--output must end with "${wantExt}" when --type is "${state.outputType}"`);
294
+ }
295
+ }
296
+ }
297
+ function emitsRegisterContractsArtifact(state) {
298
+ return [
299
+ state.registerContracts !== 'off',
300
+ state.emitRegisterReport,
301
+ state.emitRegisterInterface,
302
+ state.emitRegisterAnnotations,
303
+ state.fixRegisterContracts,
304
+ state.acceptRegisterOutputCandidates.length > 0,
305
+ state.registerContractsInterfaces.length > 0,
306
+ ].some(Boolean);
307
+ }
308
+ function primaryOutputDisabled(state) {
309
+ return state.outputType === 'hex' ? !state.emitHex : !state.emitBin;
310
+ }
311
+ function primaryOutputName(state) {
312
+ return state.outputType === 'hex' ? 'HEX' : 'BIN';
313
+ }
314
+ function validateEnabledPrimaryOutput(state) {
315
+ if (primaryOutputDisabled(state) && !emitsRegisterContractsArtifact(state)) {
316
+ fail(`--type ${state.outputType} requires ${primaryOutputName(state)} output to be enabled`);
317
+ }
318
+ }
319
+ const VALUE_ARG_PARSERS = [
320
+ (arg, { argv, indexRef, state }) => parseOutputPathArg(arg, argv, indexRef, state),
321
+ (arg, { argv, indexRef, state }) => parseOutputTypeArg(arg, argv, indexRef, state),
322
+ (arg, { argv, indexRef, state }) => parseSourceRootArg(arg, argv, indexRef, state),
323
+ (arg, { argv, indexRef, state }) => parseCaseStyleArg(arg, argv, indexRef, state),
324
+ (arg, { argv, indexRef, state }) => parseDirectiveAliasFileArg(arg, argv, indexRef, state),
325
+ (arg, { argv, indexRef, state }) => parseRegisterContractsArg(arg, argv, indexRef, state),
326
+ (arg, { argv, indexRef, state }) => parseRegisterProfileArg(arg, argv, indexRef, state),
327
+ (arg, { argv, indexRef, state }) => parseAcceptOutputArg(arg, argv, indexRef, state),
328
+ (arg, { argv, indexRef, state }) => parseRegisterInterfaceArg(arg, argv, indexRef, state),
329
+ (arg, { argv, indexRef, state }) => parseIncludeArg(arg, argv, indexRef, state),
330
+ ];
331
+ function parseBooleanFlag(arg, state) {
332
+ const action = BOOLEAN_FLAG_ACTIONS.find(({ flags }) => flags.includes(arg));
333
+ if (!action)
334
+ return false;
335
+ action.apply(state);
336
+ return true;
337
+ }
338
+ function parseValueArg(arg, context) {
339
+ return VALUE_ARG_PARSERS.some((parser) => parser(arg, context));
340
+ }
341
+ function parseEntryArg(arg, argv, indexRef, state) {
342
+ if (arg.startsWith('-')) {
343
+ fail(`Unknown option "${arg}"`);
344
+ }
345
+ if (state.entryFile !== undefined || indexRef.current !== argv.length - 1) {
346
+ fail(`Expected exactly one <entry.asm|entry.z80> argument (and it must be last)`);
347
+ }
348
+ state.entryFile = arg;
349
+ }
266
350
  export function parseCliArgs(argv) {
267
351
  const state = createDefaultCliState();
268
352
  const indexRef = { current: 0 };
@@ -271,69 +355,11 @@ export function parseCliArgs(argv) {
271
355
  const fastPath = handleFastPath(arg);
272
356
  if (fastPath)
273
357
  return fastPath;
274
- if (parseOutputPathArg(arg, argv, indexRef, state))
275
- continue;
276
- if (parseOutputTypeArg(arg, argv, indexRef, state))
277
- continue;
278
- if (arg === '--nobin') {
279
- state.emitBin = false;
280
- continue;
281
- }
282
- if (arg === '--nohex') {
283
- state.emitHex = false;
284
- continue;
285
- }
286
- if (arg === '--nod8m') {
287
- state.emitD8m = false;
288
- continue;
289
- }
290
- if (arg === '--asm80') {
291
- state.emitAsm80 = true;
358
+ if (parseBooleanFlag(arg, state))
292
359
  continue;
293
- }
294
- if (arg === '--emit-register-report' || arg === '--reg-report') {
295
- state.emitRegisterReport = true;
296
- continue;
297
- }
298
- if (arg === '--emit-register-interface' || arg === '--reg-interface') {
299
- state.emitRegisterInterface = true;
300
- continue;
301
- }
302
- if (arg === '--fix') {
303
- state.fixRegisterContracts = true;
304
- state.emitRegisterAnnotations = true;
305
- continue;
306
- }
307
- if (arg === '--contracts' || arg === '--annotate-register-contracts') {
308
- state.emitRegisterAnnotations = true;
360
+ if (parseValueArg(arg, { argv, indexRef, state }))
309
361
  continue;
310
- }
311
- if (parseSourceRootArg(arg, argv, indexRef, state))
312
- continue;
313
- if (parseCaseStyleArg(arg, argv, indexRef, state))
314
- continue;
315
- if (parseDirectiveAliasFileArg(arg, argv, indexRef, state))
316
- continue;
317
- if (parseRegisterCareArg(arg, argv, indexRef, state))
318
- continue;
319
- if (parseRegisterProfileArg(arg, argv, indexRef, state))
320
- continue;
321
- if (parseAcceptOutputArg(arg, argv, indexRef, state))
322
- continue;
323
- if (parseRegisterInterfaceArg(arg, argv, indexRef, state))
324
- continue;
325
- if (parseIncludeArg(arg, argv, indexRef, state))
326
- continue;
327
- if (arg.startsWith('-')) {
328
- fail(`Unknown option "${arg}"`);
329
- }
330
- if (state.entryFile !== undefined) {
331
- fail(`Expected exactly one <entry.asm|entry.z80> argument (and it must be last)`);
332
- }
333
- if (indexRef.current !== argv.length - 1) {
334
- fail(`Expected exactly one <entry.asm|entry.z80> argument (and it must be last)`);
335
- }
336
- state.entryFile = arg;
362
+ parseEntryArg(arg, argv, indexRef, state);
337
363
  }
338
364
  return finalizeCliOptions(state);
339
365
  }
@@ -1,7 +1,7 @@
1
1
  import { compile } from '../api-compile.js';
2
2
  import { formatDiagnostic } from '../diagnostics/format.js';
3
3
  import { cliUsage, parseCliArgs } from './parse-args.js';
4
- import { artifactBase, buildCompileOptions, compareDiagnosticsForCli, writeArtifacts } from './write-artifacts.js';
4
+ import { artifactBase, buildCompileOptions, compareDiagnosticsForCli, writeArtifacts, } from './write-artifacts.js';
5
5
  export { parseCliArgs } from './parse-args.js';
6
6
  export async function runCli(argv) {
7
7
  try {
@@ -18,6 +18,9 @@ export async function runCli(argv) {
18
18
  }
19
19
  }
20
20
  if (sortedDiagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
21
+ if (compileResult.artifacts.length > 0) {
22
+ await writeArtifacts(base, compileResult.artifacts, parsed.outputType);
23
+ }
21
24
  return 1;
22
25
  }
23
26
  const primaryPath = await writeArtifacts(base, compileResult.artifacts, parsed.outputType);
@@ -0,0 +1 @@
1
+ export declare function cliUsage(): string;
@@ -0,0 +1,33 @@
1
+ export function cliUsage() {
2
+ return [
3
+ 'azm [options] <entry.asm|entry.z80>',
4
+ '',
5
+ 'Options:',
6
+ ' -o, --output <file> Primary output path (must match --type extension)',
7
+ ' -t, --type <type> Primary output type: hex|bin (default: hex)',
8
+ ' --nobin Suppress .bin',
9
+ ' --nohex Suppress .hex',
10
+ ' --nod8m Suppress .d8.json',
11
+ ' --asm80 Emit lowered source (.z80)',
12
+ ' --register-contracts <m> Register contracts mode: off|audit|warn|error|strict',
13
+ ' --rc <m> Register contracts mode alias for --register-contracts',
14
+ ' --reg-report Emit register contracts report artifact',
15
+ ' --reg-interface Emit inferred register contracts interface (.asmi)',
16
+ ' --contracts Rewrite source with inferred register contracts',
17
+ ' --fix Enable contract rewrite and conservative fixes',
18
+ ' --accept-out <x> Accept register contracts output candidates',
19
+ ' --interface <file> Load .asmi contract file',
20
+ ' --reg-profile <p> Register contracts profile (currently mon3)',
21
+ ' --source-root <d> Normalize D8 source paths relative to this directory',
22
+ ' --case-style <m> Case-style lint mode: off|upper|lower|consistent',
23
+ ' --aliases <file> Load project directive alias JSON (repeatable)',
24
+ ' -I, --include <dir> Add include search path (repeatable)',
25
+ ' -V, --version Print version',
26
+ ' -h, --help Show help',
27
+ '',
28
+ 'Notes:',
29
+ ' - <entry.asm|entry.z80> must be the last argument (assembler-style).',
30
+ ' - Output artifacts are written using the primary output stem with standard suffixes.',
31
+ '',
32
+ ].join('\n');
33
+ }
@@ -1,5 +1,5 @@
1
- import { mkdir, writeFile } from 'node:fs/promises';
2
- import { dirname, extname, resolve } from 'node:path';
1
+ import { extname, resolve } from 'node:path';
2
+ import { writeArtifactFiles } from './artifact-files.js';
3
3
  function normalizeDiagnosticPath(file) {
4
4
  const normalized = file.replace(/\\/g, '/');
5
5
  return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
@@ -38,97 +38,24 @@ export function artifactBase(entryFile, outputType, outputPath) {
38
38
  if (outputPath !== undefined) {
39
39
  const resolvedOutputPath = resolve(outputPath);
40
40
  const providedExt = extname(resolvedOutputPath);
41
- return providedExt.length > 0 ? resolvedOutputPath.slice(0, -providedExt.length) : resolvedOutputPath;
41
+ return providedExt.length > 0
42
+ ? resolvedOutputPath.slice(0, -providedExt.length)
43
+ : resolvedOutputPath;
42
44
  }
43
45
  const resolvedEntry = resolve(entryFile);
44
46
  const entryExt = extname(resolvedEntry);
45
47
  return entryExt.length > 0 ? resolvedEntry.slice(0, -entryExt.length) : resolvedEntry;
46
48
  }
47
49
  export async function writeArtifacts(base, artifacts, outputType) {
48
- const byKind = new Map();
49
- for (const artifact of artifacts) {
50
- byKind.set(artifact.kind, artifact);
51
- }
52
- const hexPath = `${base}.hex`;
53
- const binPath = `${base}.bin`;
54
- const d8mPath = `${base}.d8.json`;
55
- const asm80Path = `${base}.z80`;
56
- const registerCareReportPath = `${base}.regcare.txt`;
57
- const registerCareInterfacePath = `${base}.asmi`;
58
- const writes = [];
59
- const ensureDir = async (path) => {
60
- await mkdir(dirname(path), { recursive: true });
61
- };
62
- let primaryPath;
63
- let registerCarePath;
64
- const bin = byKind.get('bin');
65
- if (bin && bin.kind === 'bin') {
66
- writes.push((async () => {
67
- await ensureDir(binPath);
68
- await writeFile(binPath, Buffer.from(bin.bytes));
69
- })());
70
- if (outputType === 'bin') {
71
- primaryPath = binPath;
72
- }
73
- }
74
- const hex = byKind.get('hex');
75
- if (hex && hex.kind === 'hex') {
76
- writes.push((async () => {
77
- await ensureDir(hexPath);
78
- await writeFile(hexPath, hex.text, 'utf8');
79
- })());
80
- if (outputType === 'hex') {
81
- primaryPath = hexPath;
82
- }
83
- }
84
- const d8m = byKind.get('d8m');
85
- if (d8m && d8m.kind === 'd8m') {
86
- writes.push((async () => {
87
- await ensureDir(d8mPath);
88
- const text = JSON.stringify(d8m.json, null, 2);
89
- await writeFile(d8mPath, `${text}\n`, 'utf8');
90
- })());
91
- }
92
- const asm80 = byKind.get('asm80');
93
- if (asm80 && asm80.kind === 'asm80') {
94
- writes.push((async () => {
95
- await ensureDir(asm80Path);
96
- await writeFile(asm80Path, asm80.text, 'utf8');
97
- })());
98
- }
99
- const registerCareReport = byKind.get('register-care-report');
100
- if (registerCareReport && registerCareReport.kind === 'register-care-report') {
101
- writes.push((async () => {
102
- await ensureDir(registerCareReportPath);
103
- await writeFile(registerCareReportPath, registerCareReport.text, 'utf8');
104
- })());
105
- registerCarePath = registerCareReportPath;
106
- }
107
- const registerCareInterface = byKind.get('register-care-interface');
108
- if (registerCareInterface && registerCareInterface.kind === 'register-care-interface') {
109
- writes.push((async () => {
110
- await ensureDir(registerCareInterfacePath);
111
- await writeFile(registerCareInterfacePath, registerCareInterface.text, 'utf8');
112
- })());
113
- registerCarePath ??= registerCareInterfacePath;
114
- }
115
- const registerCareAnnotations = byKind.get('register-care-annotations');
116
- if (registerCareAnnotations && registerCareAnnotations.kind === 'register-care-annotations') {
117
- for (const item of registerCareAnnotations.files) {
118
- writes.push((async () => {
119
- await ensureDir(item.path);
120
- await writeFile(item.path, item.text, 'utf8');
121
- })());
122
- if (primaryPath === undefined) {
123
- primaryPath = item.path;
124
- }
125
- }
126
- }
127
- await Promise.all(writes);
128
- if (primaryPath !== undefined) {
129
- return primaryPath;
130
- }
131
- return registerCarePath;
50
+ const result = await writeArtifactFiles(artifacts, {
51
+ hex: `${base}.hex`,
52
+ bin: `${base}.bin`,
53
+ d8m: `${base}.d8.json`,
54
+ asm80: `${base}.z80`,
55
+ registerContractsReport: `${base}.regcontracts.txt`,
56
+ registerContractsInterface: `${base}.asmi`,
57
+ }, outputType);
58
+ return result.primaryPath ?? result.registerContractsPath;
132
59
  }
133
60
  export function buildCompileOptions(parsed, base) {
134
61
  const hexPath = `${base}.hex`;
@@ -141,16 +68,16 @@ export function buildCompileOptions(parsed, base) {
141
68
  emitD8m: parsed.emitD8m,
142
69
  emitAsm80: parsed.emitAsm80,
143
70
  caseStyle: parsed.caseStyle,
144
- registerCare: parsed.registerCare,
71
+ registerContracts: parsed.registerContracts,
145
72
  emitRegisterReport: parsed.emitRegisterReport,
146
73
  emitRegisterInterface: parsed.emitRegisterInterface,
147
74
  emitRegisterAnnotations: parsed.emitRegisterAnnotations,
148
75
  fixRegisterContracts: parsed.fixRegisterContracts,
149
76
  acceptRegisterOutputCandidates: parsed.acceptRegisterOutputCandidates,
150
- ...(parsed.registerCareProfile !== undefined
151
- ? { registerCareProfile: parsed.registerCareProfile }
77
+ ...(parsed.registerContractsProfile !== undefined
78
+ ? { registerContractsProfile: parsed.registerContractsProfile }
152
79
  : {}),
153
- registerCareInterfaces: parsed.registerCareInterfaces,
80
+ registerContractsInterfaces: parsed.registerContractsInterfaces,
154
81
  ...(parsed.sourceRoot !== undefined ? { sourceRoot: parsed.sourceRoot } : {}),
155
82
  ...(parsed.sourceRoot !== undefined
156
83
  ? {