@jhlagado/azm 0.2.6 → 0.2.8

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 (235) hide show
  1. package/README.md +170 -69
  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 +31 -230
  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/fixup-emission.js +30 -48
  15. package/dist/src/assembly/program-emission.js +163 -164
  16. package/dist/src/cli/artifact-files.d.ts +15 -0
  17. package/dist/src/cli/artifact-files.js +86 -0
  18. package/dist/src/cli/parse-args.d.ts +6 -5
  19. package/dist/src/cli/parse-args.js +162 -136
  20. package/dist/src/cli/run.js +4 -1
  21. package/dist/src/cli/usage.d.ts +1 -0
  22. package/dist/src/cli/usage.js +33 -0
  23. package/dist/src/cli/write-artifacts.js +18 -91
  24. package/dist/src/core/compile.js +51 -274
  25. package/dist/src/core/conditional-assembly.d.ts +6 -0
  26. package/dist/src/core/conditional-assembly.js +181 -0
  27. package/dist/src/expansion/op-constant-expression.d.ts +3 -0
  28. package/dist/src/expansion/op-constant-expression.js +52 -0
  29. package/dist/src/expansion/op-expand-selected.d.ts +5 -0
  30. package/dist/src/expansion/op-expand-selected.js +143 -0
  31. package/dist/src/expansion/op-expansion.d.ts +5 -53
  32. package/dist/src/expansion/op-expansion.js +85 -815
  33. package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
  34. package/dist/src/expansion/op-instruction-instantiation.js +194 -0
  35. package/dist/src/expansion/op-local-labels.d.ts +8 -0
  36. package/dist/src/expansion/op-local-labels.js +166 -0
  37. package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
  38. package/dist/src/expansion/op-operand-splitting.js +44 -0
  39. package/dist/src/expansion/op-operands.d.ts +53 -0
  40. package/dist/src/expansion/op-operands.js +66 -0
  41. package/dist/src/expansion/op-selection.d.ts +18 -0
  42. package/dist/src/expansion/op-selection.js +172 -0
  43. package/dist/src/index.d.ts +2 -1
  44. package/dist/src/index.js +1 -1
  45. package/dist/src/model/diagnostic.d.ts +4 -0
  46. package/dist/src/model/diagnostic.js +4 -0
  47. package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
  48. package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
  49. package/dist/src/outputs/asm80-expressions.d.ts +5 -0
  50. package/dist/src/outputs/asm80-expressions.js +47 -0
  51. package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
  52. package/dist/src/outputs/asm80-instruction-operands.js +38 -0
  53. package/dist/src/outputs/asm80-instructions.d.ts +5 -0
  54. package/dist/src/outputs/asm80-instructions.js +272 -0
  55. package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
  56. package/dist/src/outputs/asm80-ld-operands.js +157 -0
  57. package/dist/src/outputs/asm80-strings.d.ts +4 -0
  58. package/dist/src/outputs/asm80-strings.js +14 -0
  59. package/dist/src/outputs/d8-files.d.ts +10 -0
  60. package/dist/src/outputs/d8-files.js +103 -0
  61. package/dist/src/outputs/d8-helpers.d.ts +21 -0
  62. package/dist/src/outputs/d8-helpers.js +136 -0
  63. package/dist/src/outputs/hex.js +26 -18
  64. package/dist/src/outputs/types.d.ts +16 -10
  65. package/dist/src/outputs/write-asm80.js +68 -597
  66. package/dist/src/outputs/write-d8.js +6 -216
  67. package/dist/src/register-contracts/accept-output.d.ts +2 -0
  68. package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
  69. package/dist/src/register-contracts/analyze-helpers.js +162 -0
  70. package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
  71. package/dist/src/register-contracts/analyze.js +73 -0
  72. package/dist/src/register-contracts/annotate.d.ts +11 -0
  73. package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
  74. package/dist/src/register-contracts/annotations.d.ts +8 -0
  75. package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
  76. package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
  77. package/dist/src/register-contracts/boundaryHints.js +24 -0
  78. package/dist/src/register-contracts/carriers.d.ts +2 -0
  79. package/dist/src/register-contracts/constants.d.ts +4 -0
  80. package/dist/src/register-contracts/constants.js +51 -0
  81. package/dist/src/register-contracts/controlFlow.d.ts +5 -0
  82. package/dist/src/register-contracts/controlFlow.js +55 -0
  83. package/dist/src/register-contracts/fix.d.ts +11 -0
  84. package/dist/src/{register-care → register-contracts}/fix.js +47 -30
  85. package/dist/src/register-contracts/instruction-head.d.ts +2 -0
  86. package/dist/src/register-contracts/instruction-head.js +3 -0
  87. package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
  88. package/dist/src/register-contracts/instruction-operands.js +101 -0
  89. package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
  90. package/dist/src/register-contracts/instruction-predicates.js +44 -0
  91. package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
  92. package/dist/src/register-contracts/interfaceContracts.js +68 -0
  93. package/dist/src/register-contracts/liveness.d.ts +3 -0
  94. package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
  95. package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
  96. package/dist/src/register-contracts/operand-register-name.js +13 -0
  97. package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
  98. package/dist/src/{register-care → register-contracts}/profiles.js +13 -2
  99. package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
  100. package/dist/src/register-contracts/programModel-boundaries.js +64 -0
  101. package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
  102. package/dist/src/register-contracts/programModel-routines.js +128 -0
  103. package/dist/src/register-contracts/programModel.d.ts +3 -0
  104. package/dist/src/register-contracts/programModel.js +14 -0
  105. package/dist/src/register-contracts/report.d.ts +5 -0
  106. package/dist/src/{register-care → register-contracts}/report.js +34 -17
  107. package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
  108. package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
  109. package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
  110. package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
  111. package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
  112. package/dist/src/register-contracts/smartCommentParsing.js +80 -0
  113. package/dist/src/register-contracts/smartComments.d.ts +5 -0
  114. package/dist/src/register-contracts/smartComments.js +92 -0
  115. package/dist/src/register-contracts/summaries.d.ts +12 -0
  116. package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
  117. package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
  118. package/dist/src/register-contracts/summary-boundary.js +40 -0
  119. package/dist/src/register-contracts/summary-contract.d.ts +2 -0
  120. package/dist/src/register-contracts/summary-contract.js +45 -0
  121. package/dist/src/register-contracts/summary-result.d.ts +7 -0
  122. package/dist/src/register-contracts/summary-result.js +122 -0
  123. package/dist/src/register-contracts/summary-state.d.ts +23 -0
  124. package/dist/src/register-contracts/summary-state.js +88 -0
  125. package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
  126. package/dist/src/register-contracts/summary-token-transfer.js +67 -0
  127. package/dist/src/register-contracts/summary.d.ts +3 -0
  128. package/dist/src/register-contracts/summary.js +266 -0
  129. package/dist/src/register-contracts/tooling.d.ts +57 -0
  130. package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
  131. package/dist/src/register-contracts/types.d.ts +188 -0
  132. package/dist/src/semantics/binary-operators.d.ts +2 -0
  133. package/dist/src/semantics/binary-operators.js +15 -0
  134. package/dist/src/semantics/byte-functions.d.ts +2 -0
  135. package/dist/src/semantics/byte-functions.js +7 -0
  136. package/dist/src/semantics/constant-operator-types.d.ts +10 -0
  137. package/dist/src/semantics/constant-operator-types.js +1 -0
  138. package/dist/src/semantics/constant-operators.d.ts +3 -0
  139. package/dist/src/semantics/constant-operators.js +3 -0
  140. package/dist/src/semantics/diagnostics.d.ts +3 -0
  141. package/dist/src/semantics/diagnostics.js +10 -0
  142. package/dist/src/semantics/expression-evaluation.d.ts +11 -19
  143. package/dist/src/semantics/expression-evaluation.js +22 -334
  144. package/dist/src/semantics/layout-evaluation.d.ts +23 -0
  145. package/dist/src/semantics/layout-evaluation.js +202 -0
  146. package/dist/src/semantics/layout-format.d.ts +5 -0
  147. package/dist/src/semantics/layout-format.js +31 -0
  148. package/dist/src/semantics/layout-path.d.ts +24 -0
  149. package/dist/src/semantics/layout-path.js +58 -0
  150. package/dist/src/semantics/unary-operators.d.ts +2 -0
  151. package/dist/src/semantics/unary-operators.js +8 -0
  152. package/dist/src/source/line-comment-scanner.d.ts +1 -0
  153. package/dist/src/source/line-comment-scanner.js +51 -0
  154. package/dist/src/source/strip-line-comment.js +8 -44
  155. package/dist/src/syntax/directive-aliases.js +36 -22
  156. package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
  157. package/dist/src/syntax/expression-tokenizer.js +310 -0
  158. package/dist/src/syntax/parse-directive-statement.d.ts +14 -0
  159. package/dist/src/syntax/parse-directive-statement.js +307 -0
  160. package/dist/src/syntax/parse-expression.d.ts +2 -2
  161. package/dist/src/syntax/parse-expression.js +7 -568
  162. package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
  163. package/dist/src/syntax/parse-layout-declarations.js +180 -0
  164. package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
  165. package/dist/src/syntax/parse-layout-expression.js +175 -0
  166. package/dist/src/syntax/parse-line.js +4 -272
  167. package/dist/src/syntax/parse-token-expression.d.ts +3 -0
  168. package/dist/src/syntax/parse-token-expression.js +133 -0
  169. package/dist/src/tooling/case-style.js +47 -30
  170. package/dist/src/z80/effect-groups.d.ts +38 -0
  171. package/dist/src/z80/effect-groups.js +265 -0
  172. package/dist/src/z80/effect-units.d.ts +18 -0
  173. package/dist/src/z80/effect-units.js +165 -0
  174. package/dist/src/z80/effects.d.ts +1 -1
  175. package/dist/src/z80/effects.js +94 -557
  176. package/dist/src/z80/encode-core.d.ts +2 -0
  177. package/dist/src/z80/encode-core.js +42 -0
  178. package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
  179. package/dist/src/z80/encode-ld-helpers.js +172 -0
  180. package/dist/src/z80/encode-ld.d.ts +2 -0
  181. package/dist/src/z80/encode-ld.js +285 -0
  182. package/dist/src/z80/encode.js +190 -542
  183. package/dist/src/z80/ld-support.d.ts +3 -0
  184. package/dist/src/z80/ld-support.js +146 -0
  185. package/dist/src/z80/operand-split-state.d.ts +8 -0
  186. package/dist/src/z80/operand-split-state.js +46 -0
  187. package/dist/src/z80/operand-split.d.ts +1 -0
  188. package/dist/src/z80/operand-split.js +13 -0
  189. package/dist/src/z80/parse-basic.d.ts +4 -0
  190. package/dist/src/z80/parse-basic.js +39 -0
  191. package/dist/src/z80/parse-branch.d.ts +4 -0
  192. package/dist/src/z80/parse-branch.js +218 -0
  193. package/dist/src/z80/parse-conditions.d.ts +6 -0
  194. package/dist/src/z80/parse-conditions.js +10 -0
  195. package/dist/src/z80/parse-exchange.d.ts +2 -0
  196. package/dist/src/z80/parse-exchange.js +30 -0
  197. package/dist/src/z80/parse-instruction.js +224 -1010
  198. package/dist/src/z80/parse-io-control.d.ts +5 -0
  199. package/dist/src/z80/parse-io-control.js +108 -0
  200. package/dist/src/z80/parse-ld.d.ts +2 -0
  201. package/dist/src/z80/parse-ld.js +83 -0
  202. package/dist/src/z80/parse-operands.d.ts +41 -0
  203. package/dist/src/z80/parse-operands.js +259 -0
  204. package/docs/reference/cli.md +42 -35
  205. package/docs/reference/tooling-api.md +20 -16
  206. package/package.json +1 -1
  207. package/dist/src/register-care/accept-output.d.ts +0 -2
  208. package/dist/src/register-care/analyze.js +0 -166
  209. package/dist/src/register-care/annotate.d.ts +0 -11
  210. package/dist/src/register-care/annotations.d.ts +0 -8
  211. package/dist/src/register-care/boundaryHints.d.ts +0 -3
  212. package/dist/src/register-care/boundaryHints.js +0 -80
  213. package/dist/src/register-care/carriers.d.ts +0 -2
  214. package/dist/src/register-care/controlFlow.d.ts +0 -5
  215. package/dist/src/register-care/controlFlow.js +0 -38
  216. package/dist/src/register-care/fix.d.ts +0 -11
  217. package/dist/src/register-care/instruction-shape.d.ts +0 -11
  218. package/dist/src/register-care/instruction-shape.js +0 -129
  219. package/dist/src/register-care/liveness.d.ts +0 -3
  220. package/dist/src/register-care/programModel.d.ts +0 -3
  221. package/dist/src/register-care/programModel.js +0 -266
  222. package/dist/src/register-care/report.d.ts +0 -5
  223. package/dist/src/register-care/routine-summaries.d.ts +0 -6
  224. package/dist/src/register-care/smartComments.d.ts +0 -5
  225. package/dist/src/register-care/smartComments.js +0 -243
  226. package/dist/src/register-care/summaries.d.ts +0 -12
  227. package/dist/src/register-care/summary.d.ts +0 -3
  228. package/dist/src/register-care/summary.js +0 -474
  229. package/dist/src/register-care/tooling.d.ts +0 -43
  230. package/dist/src/register-care/types.d.ts +0 -172
  231. /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
  232. /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
  233. /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
  234. /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
  235. /package/dist/src/{register-care → register-contracts}/types.js +0 -0
@@ -24,10 +24,14 @@ Debug80 should link through the public package entry points, not internal
24
24
  `dist/src/...` paths.
25
25
 
26
26
  Use this path when Debug80 needs editor-style diagnostics, symbols, or
27
- register-care information without writing files:
27
+ register contracts information without writing files:
28
28
 
29
29
  ```ts
30
- import { analyzeProgram, analyzeRegisterCareForTools, loadProgram } from '@jhlagado/azm/tooling';
30
+ import {
31
+ analyzeProgram,
32
+ analyzeRegisterContractsForTools,
33
+ loadProgram,
34
+ } from '@jhlagado/azm/tooling';
31
35
 
32
36
  const loaded = await loadProgram({
33
37
  entryFile: '/abs/path/to/main.asm',
@@ -43,9 +47,9 @@ const analysis = analyzeProgram(loaded.loadedProgram, {
43
47
  requireMain: false,
44
48
  });
45
49
 
46
- const registerCare = analyzeRegisterCareForTools(loaded.loadedProgram, {
50
+ const registerContracts = analyzeRegisterContractsForTools(loaded.loadedProgram, {
47
51
  mode: 'audit',
48
- registerCareProfile: 'mon3',
52
+ registerContractsProfile: 'mon3',
49
53
  });
50
54
  ```
51
55
 
@@ -67,8 +71,8 @@ const result = await compile(
67
71
  emitBin: true,
68
72
  emitHex: true,
69
73
  emitD8m: true,
70
- registerCare: 'audit',
71
- registerCareInterfaces: ['/abs/path/to/mon3.asmi'],
74
+ registerContracts: 'audit',
75
+ registerContractsInterfaces: ['/abs/path/to/mon3.asmi'],
72
76
  },
73
77
  { formats: defaultFormatWriters },
74
78
  );
@@ -81,7 +85,7 @@ const binary = result.artifacts.find((artifact) => artifact.kind === 'bin');
81
85
  The integration contract is:
82
86
 
83
87
  - source entries are `.asm` or `.z80`
84
- - external register-care contracts are `.asmi`
88
+ - external register contracts are `.asmi`
85
89
  - include search paths are supplied explicitly with `includeDirs`
86
90
  - directive alias JSON files are supplied explicitly with `directiveAliasFiles`
87
91
  - `compile()` returns artifacts in memory; the CLI is only responsible for
@@ -203,24 +207,24 @@ console.log(analysis.diagnostics);
203
207
 
204
208
  `analysis.env` is returned only when semantic analysis completes without errors.
205
209
 
206
- ## Register-Care Tooling
210
+ ## Register Contracts Tooling
207
211
 
208
- Use `analyzeRegisterCareForTools()` after `loadProgram()` when an editor, lint runner, or future LSP server needs register-care diagnostics without parsing report text. The function returns the same inferred output candidates used by the CLI report, plus ready-to-apply quick-fix metadata for confirming intent at the call site.
212
+ Use `analyzeRegisterContractsForTools()` after `loadProgram()` when an editor, lint runner, or future LSP server needs register contracts diagnostics without parsing report text. The function returns the same inferred output candidates used by the CLI report, plus ready-to-apply quick-fix metadata for confirming intent at the call site.
209
213
 
210
214
  ```ts
211
- import { analyzeRegisterCareForTools, loadProgram } from '@jhlagado/azm/tooling';
215
+ import { analyzeRegisterContractsForTools, loadProgram } from '@jhlagado/azm/tooling';
212
216
 
213
217
  const loaded = await loadProgram({ entryFile: '/abs/path/to/main.z80' });
214
218
  if (!loaded.loadedProgram) {
215
219
  throw new Error('Parse/load failed');
216
220
  }
217
221
 
218
- const registerCare = analyzeRegisterCareForTools(loaded.loadedProgram, {
222
+ const registerContracts = analyzeRegisterContractsForTools(loaded.loadedProgram, {
219
223
  mode: 'audit',
220
- registerCareProfile: 'mon3',
224
+ registerContractsProfile: 'mon3',
221
225
  });
222
226
 
223
- for (const diagnostic of registerCare.candidateDiagnostics) {
227
+ for (const diagnostic of registerContracts.candidateDiagnostics) {
224
228
  console.log(diagnostic.file, diagnostic.line, diagnostic.message);
225
229
  console.log(diagnostic.autoFixable); // true when CLI --fix can safely add the hint
226
230
  if (diagnostic.autoFixable && diagnostic.codeAction) {
@@ -229,7 +233,7 @@ for (const diagnostic of registerCare.candidateDiagnostics) {
229
233
  }
230
234
  ```
231
235
 
232
- Candidate diagnostics use `kind: "register-care-output-candidate"` and `severity: "info"`.
236
+ Candidate diagnostics use `kind: "register-contracts-output-candidate"` and `severity: "info"`.
233
237
  The `autoFixable` flag distinguishes direct continuation reads that `--fix` may
234
238
  confirm automatically from cases that need programmer review. Code actions are
235
239
  intentionally simple text insertions: insert the supplied newline-terminated
@@ -258,7 +262,7 @@ returns bin/hex/d8 artifacts alongside the asm80 error. Run
258
262
  `npm run check:asm80-coverage` on your sources before relying on lowered output.
259
263
 
260
264
  The compiler accepts flat `.asm` / `.z80` source, retained AZM assembler
261
- features, and the same output writers used by the CLI. External register-care
265
+ features, and the same output writers used by the CLI. External register contracts
262
266
  interfaces are `.asmi` metadata files, not compile entry files.
263
267
 
264
268
  Retained AZM features include the ASM80 baseline, compact AZMDoc `;!` comments,
@@ -277,7 +281,7 @@ The public tooling surface includes:
277
281
  - `Diagnostic`, `DiagnosticIds`, severity/id types
278
282
  - `LoadedProgram`
279
283
  - `AnalyzeProgramResult`, `LoadProgramResult`
280
- - `RegisterCareCandidateDiagnostic`, `RegisterCareCodeAction`, `RegisterCareOutputCandidate`
284
+ - `RegisterContractsCandidateDiagnostic`, `RegisterContractsCodeAction`, `RegisterContractsOutputCandidate`
281
285
 
282
286
  The public tooling contract is the package export surface, not deep internal
283
287
  paths. Do not document retired internal modules as public API.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jhlagado/azm",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "AZM assembler for the Z80 family (Node.js CLI)",
5
5
  "license": "GPL-3.0-only",
6
6
  "engines": {
@@ -1,2 +0,0 @@
1
- import type { RegisterCareUnit } from './types.js';
2
- export declare function parseAcceptedOutputCandidates(items?: readonly string[]): Map<string, RegisterCareUnit[]>;
@@ -1,166 +0,0 @@
1
- import { buildRegisterCareProgramModel } from './programModel.js';
2
- import { buildRoutineContracts, parseSmartComments } from './smartComments.js';
3
- import { renderRegisterCareInterface, renderRegisterCareReport } from './report.js';
4
- import { autoFixableCandidateKeys, findExpectOutFixesForCandidates } from './fix.js';
5
- import { findCallerOutputCandidateObservations, findRegisterCareConflicts, } from './liveness.js';
6
- import { buildAnnotations } from './annotations.js';
7
- import { buildOutputCandidateFixability, buildProfileSummaries, buildProfileSummaryLookup, buildSummaries, buildSummaryByName, outputCandidateKey, routineNames, unknownBoundaryDiagnostics, unknownCallList, withAcceptedOutputs, } from './summaries.js';
8
- function candidateMessageWithFixability(candidate, autoFixable) {
9
- const carriers = candidate.carriers.join(',');
10
- const expectation = candidate.carriers.length === 1 ? candidate.carriers[0] : `{${carriers}}`;
11
- const base = `CALL ${candidate.routine} writes ${carriers} and caller reads it later`;
12
- return autoFixable
13
- ? `${base}; generated contracts promote this to \`out ${expectation}\` automatically.`
14
- : `${base}; manual review required before adding \`; expects out ${expectation}\` because the later read is not a simple direct continuation.`;
15
- }
16
- export function analyzeRegisterCare(loaded, options) {
17
- const file = loaded.program.files[0];
18
- const items = file?.items ?? [];
19
- const program = buildRegisterCareProgramModel(items);
20
- const smartComments = parseSmartComments(loaded.sourceLineComments);
21
- const contractMap = buildRoutineContracts(smartComments, program.routines, loaded.sourceTexts);
22
- if (options.interfaceContracts !== undefined) {
23
- for (const contract of options.interfaceContracts) {
24
- contractMap.set(contract.name, contract);
25
- }
26
- }
27
- const profileSummaries = buildProfileSummaries(options.registerCareProfile);
28
- let summaries = buildSummaries(program.routines, contractMap, profileSummaries);
29
- summaries = withAcceptedOutputs(summaries, options.acceptedOutputCandidates);
30
- let summariesByName = buildSummaryByName(program.routines, summaries, profileSummaries);
31
- const knownRoutines = new Set(routineNames(program.routines));
32
- for (const [name] of contractMap) {
33
- knownRoutines.add(name);
34
- }
35
- for (const name of buildProfileSummaryLookup(options.registerCareProfile).keys()) {
36
- knownRoutines.add(name);
37
- }
38
- const diagnostics = [];
39
- const shouldBuildOutputCandidates = options.mode !== 'off' ||
40
- options.emitAnnotations === true ||
41
- options.fixRegisterContracts === true;
42
- const outputCandidates = shouldBuildOutputCandidates
43
- ? findCallerOutputCandidateObservations(program.routines, summariesByName)
44
- : [];
45
- const autoAcceptedOutputs = autoAcceptedOutputCandidateMap(program.routines, outputCandidates, loaded.sourceTexts);
46
- if (autoAcceptedOutputs.size > 0) {
47
- summaries = withAcceptedOutputs(summaries, autoAcceptedOutputs);
48
- summariesByName = buildSummaryByName(program.routines, summaries, profileSummaries);
49
- }
50
- const allSummaries = [...summaries, ...profileSummaries];
51
- const conflicts = shouldBuildOutputCandidates
52
- ? program.routines.flatMap((routine) => findRegisterCareConflicts(routine, summariesByName, smartComments))
53
- : [];
54
- const outputCandidateFixability = buildOutputCandidateFixability(program.routines, outputCandidates, autoFixableCandidateKeys);
55
- const outputCandidatesWithFixability = outputCandidates.map((candidate) => {
56
- const autoFixable = outputCandidateFixability.get(outputCandidateKey(candidate.file, candidate.line, candidate.column)) ?? false;
57
- return {
58
- ...candidate,
59
- autoFixable,
60
- message: candidateMessageWithFixability(candidate, autoFixable),
61
- };
62
- });
63
- if (options.mode !== 'audit') {
64
- for (const conflict of conflicts) {
65
- diagnostics.push({
66
- severity: options.mode === 'error' ? 'error' : 'warning',
67
- code: 'AZMN_REGISTER_CARE',
68
- sourceName: conflict.file,
69
- line: conflict.line,
70
- column: conflict.column,
71
- message: conflict.message,
72
- });
73
- }
74
- }
75
- if (options.mode === 'strict') {
76
- diagnostics.push(...unknownBoundaryDiagnostics(program.directBoundaries, knownRoutines));
77
- }
78
- const reportModel = {
79
- entryFile: loaded.program.entryFile,
80
- mode: options.mode,
81
- summaries: allSummaries,
82
- conflicts,
83
- outputCandidates: outputCandidatesWithFixability,
84
- ...(options.registerCareProfile !== undefined ? { profile: options.registerCareProfile } : {}),
85
- unknownCalls: options.mode === 'off' ? [] : unknownCallList(program.directBoundaries, knownRoutines),
86
- };
87
- const summariesForAnnotations = new Map(summariesByName);
88
- const outputCandidatesByRoutine = new Map();
89
- for (const candidate of outputCandidatesWithFixability) {
90
- const existing = outputCandidatesByRoutine.get(candidate.routine) ?? [];
91
- for (const unit of candidate.carriers) {
92
- if (!existing.includes(unit))
93
- existing.push(unit);
94
- }
95
- outputCandidatesByRoutine.set(candidate.routine, existing);
96
- }
97
- for (const [name, summary] of summariesForAnnotations) {
98
- const candidates = outputCandidatesByRoutine.get(name);
99
- if (candidates !== undefined && candidates.length > 0) {
100
- summariesForAnnotations.set(name, { ...summary, outputCandidates: candidates });
101
- }
102
- }
103
- const annotations = options.emitAnnotations
104
- ? buildAnnotations(loaded, program.routines, summariesForAnnotations, outputCandidatesWithFixability, {
105
- fixOutputCandidates: options.fixRegisterContracts === true,
106
- outputCandidateFixability,
107
- outputCandidateKey,
108
- })
109
- : [];
110
- return {
111
- diagnostics,
112
- outputCandidates: outputCandidatesWithFixability,
113
- ...(options.emitReport ? { reportText: renderRegisterCareReport(reportModel) } : {}),
114
- ...(options.emitInterface ? { interfaceText: renderRegisterCareInterface(summaries) } : {}),
115
- ...(annotations.length > 0 ? { annotations } : {}),
116
- ...(reportModel.unknownCalls.length > 0 ? { unknownCalls: reportModel.unknownCalls } : {}),
117
- };
118
- }
119
- function autoAcceptedOutputCandidateMap(routines, outputCandidates, sourceTexts) {
120
- const out = new Map();
121
- const sourceMaybeOut = sourceMaybeOutByRoutine(routines, sourceTexts);
122
- for (const fix of findExpectOutFixesForCandidates([...routines], [...outputCandidates])) {
123
- const declaredMaybeOut = sourceMaybeOut.get(fix.routine) ?? [];
124
- const eligibleCarriers = fix.carriers.filter((carrier) => declaredMaybeOut.includes(carrier));
125
- if (eligibleCarriers.length === 0)
126
- continue;
127
- const carriers = out.get(fix.routine) ?? [];
128
- for (const carrier of eligibleCarriers) {
129
- if (!carriers.includes(carrier))
130
- carriers.push(carrier);
131
- }
132
- out.set(fix.routine, carriers);
133
- }
134
- return out;
135
- }
136
- function sourceMaybeOutByRoutine(routines, sourceTexts) {
137
- const out = new Map();
138
- for (const routine of routines) {
139
- const source = sourceTexts.get(routine.span.file);
140
- if (source === undefined)
141
- continue;
142
- const lines = source.split(/\r?\n/);
143
- const units = [];
144
- for (let index = routine.span.start.line - 2; index >= 0; index -= 1) {
145
- const text = lines[index] ?? '';
146
- if (!/^\s*;/.test(text))
147
- break;
148
- const match = /^\s*;\s*!\s*maybe-out\s+(.+)$/i.exec(text);
149
- if (!match)
150
- continue;
151
- for (const token of match[1].split(',')) {
152
- const unit = token.trim();
153
- if (unit.length > 0 && !units.includes(unit))
154
- units.push(unit);
155
- }
156
- }
157
- if (units.length === 0)
158
- continue;
159
- out.set(routine.name, units);
160
- for (const label of routine.labels)
161
- out.set(label, units);
162
- for (const label of routine.entryLabels)
163
- out.set(label, units);
164
- }
165
- return out;
166
- }
@@ -1,11 +0,0 @@
1
- import type { RegisterCareRoutine, RoutineSummary } from './types.js';
2
- export interface RegisterCareAnnotatedFile {
3
- path: string;
4
- text: string;
5
- }
6
- interface RegisterCareAnnotationInput {
7
- routine: RegisterCareRoutine;
8
- summary: RoutineSummary;
9
- }
10
- export declare function annotateRegisterCareContracts(sourceTexts: ReadonlyMap<string, string>, routines: RegisterCareAnnotationInput[]): RegisterCareAnnotatedFile[];
11
- export {};
@@ -1,8 +0,0 @@
1
- import type { RegisterCareAnnotationFile, RegisterCareOutputCandidate, RegisterCareRoutine, RoutineSummary } from './types.js';
2
- export declare function buildAnnotations(loaded: {
3
- sourceTexts: ReadonlyMap<string, string>;
4
- }, programRoutines: readonly RegisterCareRoutine[], summariesByName: ReadonlyMap<string, RoutineSummary>, outputCandidates: readonly RegisterCareOutputCandidate[], options: {
5
- fixOutputCandidates: boolean;
6
- outputCandidateFixability: ReadonlyMap<string, boolean>;
7
- outputCandidateKey: (file: string, line: number, column: number) => string;
8
- }): readonly RegisterCareAnnotationFile[];
@@ -1,3 +0,0 @@
1
- import type { RegisterCareInstruction } from './types.js';
2
- export declare function precedingCServiceName(item: RegisterCareInstruction | undefined): string | undefined;
3
- export declare function precedingRegisterImmediateValue(item: RegisterCareInstruction | undefined, register: string): number | undefined;
@@ -1,80 +0,0 @@
1
- export function precedingCServiceName(item) {
2
- const instruction = item?.instruction;
3
- if (!instruction || instruction.mnemonic !== 'ld')
4
- return undefined;
5
- if (instruction.target?.kind !== 'reg8' || instruction.target.register !== 'c')
6
- return undefined;
7
- if (instruction.source.kind === 'imm' && instruction.source.expression.kind === 'symbol') {
8
- return instruction.source.expression.name;
9
- }
10
- return undefined;
11
- }
12
- function evaluateKnownConstant(expression, constants) {
13
- switch (expression.kind) {
14
- case 'number':
15
- return expression.value;
16
- case 'symbol':
17
- return constants.get(expression.name);
18
- case 'unary': {
19
- const value = evaluateKnownConstant(expression.expression, constants);
20
- if (value === undefined)
21
- return undefined;
22
- switch (expression.operator) {
23
- case '+':
24
- return value;
25
- case '-':
26
- return -value;
27
- case '~':
28
- return ~value;
29
- }
30
- }
31
- case 'binary': {
32
- const left = evaluateKnownConstant(expression.left, constants);
33
- const right = evaluateKnownConstant(expression.right, constants);
34
- if (left === undefined || right === undefined)
35
- return undefined;
36
- switch (expression.operator) {
37
- case '+':
38
- return left + right;
39
- case '-':
40
- return left - right;
41
- case '*':
42
- return left * right;
43
- case '/':
44
- return right === 0 ? undefined : Math.trunc(left / right);
45
- case '%':
46
- return right === 0 ? undefined : left % right;
47
- case '&':
48
- return left & right;
49
- case '^':
50
- return left ^ right;
51
- case '|':
52
- return left | right;
53
- case '<<':
54
- return left << right;
55
- case '>>':
56
- return left >> right;
57
- }
58
- }
59
- case 'byte-function': {
60
- const value = evaluateKnownConstant(expression.expression, constants);
61
- if (value === undefined)
62
- return undefined;
63
- return expression.function === 'LSB' ? value & 0xff : (value >> 8) & 0xff;
64
- }
65
- default:
66
- return undefined;
67
- }
68
- }
69
- export function precedingRegisterImmediateValue(item, register) {
70
- const instruction = item?.instruction;
71
- if (!instruction || instruction.mnemonic !== 'ld')
72
- return undefined;
73
- if (instruction.target?.kind !== 'reg8' ||
74
- instruction.target.register !== register.toLowerCase()) {
75
- return undefined;
76
- }
77
- if (instruction.source.kind !== 'imm')
78
- return undefined;
79
- return evaluateKnownConstant(instruction.source.expression, item.constants ?? new Map());
80
- }
@@ -1,2 +0,0 @@
1
- import type { RegisterCareUnit } from './types.js';
2
- export declare function expandCarrierList(raw: readonly string[]): RegisterCareUnit[] | undefined;
@@ -1,5 +0,0 @@
1
- import type { InstructionEffect, RegisterCareRoutine } from './types.js';
2
- export declare function labelIndex(routine: RegisterCareRoutine): Map<string, number>;
3
- export declare function instructionSuccessors(routine: RegisterCareRoutine, index: number, effect: InstructionEffect, labels: ReadonlyMap<string, number>, options?: {
4
- boundaryFallthrough?: boolean;
5
- }): number[];
@@ -1,38 +0,0 @@
1
- function unique(items) {
2
- return [...new Set(items)];
3
- }
4
- export function labelIndex(routine) {
5
- const out = new Map();
6
- routine.instructions.forEach((item, index) => {
7
- for (const label of item.labels)
8
- out.set(label, index);
9
- });
10
- return out;
11
- }
12
- function localTargetIndex(labels, target) {
13
- if (!target)
14
- return undefined;
15
- return labels.get(target);
16
- }
17
- export function instructionSuccessors(routine, index, effect, labels, options = {}) {
18
- const next = index + 1 < routine.instructions.length ? index + 1 : undefined;
19
- if (effect.control.kind === 'fallthrough' ||
20
- (options.boundaryFallthrough &&
21
- (effect.control.kind === 'call' || effect.control.kind === 'rst'))) {
22
- return next === undefined ? [] : [next];
23
- }
24
- if (effect.control.kind === 'jump') {
25
- const target = localTargetIndex(labels, effect.control.target);
26
- if (effect.control.conditional) {
27
- return unique([
28
- ...(target === undefined ? [] : [target]),
29
- ...(next === undefined ? [] : [next]),
30
- ]);
31
- }
32
- return target === undefined ? [] : [target];
33
- }
34
- if (effect.control.kind === 'return') {
35
- return effect.control.conditional && next !== undefined ? [next] : [];
36
- }
37
- return [];
38
- }
@@ -1,11 +0,0 @@
1
- import type { RegisterCareOutputCandidate, RegisterCareRoutine, RegisterCareUnit } from './types.js';
2
- export interface RegisterCareExpectOutFix {
3
- file: string;
4
- line: number;
5
- column: number;
6
- routine: string;
7
- carriers: RegisterCareUnit[];
8
- }
9
- export declare function findExpectOutFixesForCandidates(routines: RegisterCareRoutine[], candidates: RegisterCareOutputCandidate[]): RegisterCareExpectOutFix[];
10
- export declare function autoFixableCandidateKeys(routines: RegisterCareRoutine[], candidates: RegisterCareOutputCandidate[]): Set<string>;
11
- export declare function applyExpectOutFixesToSource(source: string, fixes: RegisterCareExpectOutFix[], referenceSource?: string): string;
@@ -1,11 +0,0 @@
1
- import type { Z80Instruction, Z80Operand } from '../z80/instruction.js';
2
- import type { RegisterCareInstruction } from './types.js';
3
- export declare function instructionHead(item: RegisterCareInstruction): string;
4
- export declare function regName(operand: Z80Operand | undefined): string | undefined;
5
- export declare function instructionOperandCount(instruction: Z80Instruction): number;
6
- export declare function instructionOperand(instruction: Z80Instruction, index: number): Z80Operand | undefined;
7
- export declare function isUnconditionalReturnInstruction(item: RegisterCareInstruction): boolean;
8
- export declare function isPureTokenTransferInstruction(item: RegisterCareInstruction): boolean;
9
- export declare function isAccumulatorSelfOperand(item: RegisterCareInstruction): boolean;
10
- export declare function isImmediateZeroOperand(item: RegisterCareInstruction): boolean;
11
- export declare function isRegisterOperand(item: RegisterCareInstruction | undefined, index: number, name: string): boolean;
@@ -1,129 +0,0 @@
1
- export function instructionHead(item) {
2
- return item.instruction.mnemonic.toLowerCase();
3
- }
4
- export function regName(operand) {
5
- if (operand === undefined)
6
- return undefined;
7
- switch (operand.kind) {
8
- case 'reg8':
9
- return operand.register.toUpperCase();
10
- case 'reg16':
11
- return operand.register.toUpperCase();
12
- case 'reg-index16':
13
- return operand.register.toUpperCase();
14
- case 'reg-half-index':
15
- return operand.register.toUpperCase();
16
- default:
17
- return undefined;
18
- }
19
- }
20
- function immValue(operand) {
21
- if (operand?.kind !== 'imm')
22
- return undefined;
23
- const expression = operand.expression;
24
- return expression.kind === 'number' ? expression.value : undefined;
25
- }
26
- export function instructionOperandCount(instruction) {
27
- switch (instruction.mnemonic) {
28
- case 'ret':
29
- case 'ret-cc':
30
- return instruction.mnemonic === 'ret' ? 0 : 1;
31
- case 'ld':
32
- return 2;
33
- case 'ex':
34
- return 2;
35
- case 'jp':
36
- case 'jp-cc':
37
- case 'jr':
38
- case 'jr-cc':
39
- case 'djnz':
40
- case 'call':
41
- case 'call-cc':
42
- return 1;
43
- case 'add':
44
- case 'adc':
45
- case 'sbc':
46
- return 'target' in instruction ? 2 : 1;
47
- case 'sub':
48
- case 'and':
49
- case 'or':
50
- case 'xor':
51
- case 'cp':
52
- return 1;
53
- default:
54
- return 0;
55
- }
56
- }
57
- export function instructionOperand(instruction, index) {
58
- switch (instruction.mnemonic) {
59
- case 'ld':
60
- return index === 0 ? instruction.target : index === 1 ? instruction.source : undefined;
61
- case 'ex': {
62
- if (index === 0) {
63
- return instruction.form === 'de-hl'
64
- ? { kind: 'reg16', register: 'de' }
65
- : instruction.form === 'af-af'
66
- ? { kind: 'reg16', register: 'af' }
67
- : undefined;
68
- }
69
- if (index === 1) {
70
- return instruction.form === 'de-hl'
71
- ? { kind: 'reg16', register: 'hl' }
72
- : undefined;
73
- }
74
- return undefined;
75
- }
76
- case 'add':
77
- case 'adc':
78
- case 'sbc':
79
- if ('target' in instruction) {
80
- return index === 0 ? instruction.target : index === 1 ? instruction.source : undefined;
81
- }
82
- return index === 0 ? instruction.source : undefined;
83
- case 'sub':
84
- case 'and':
85
- case 'or':
86
- case 'xor':
87
- case 'cp':
88
- return index === 0 ? instruction.source : undefined;
89
- default:
90
- return undefined;
91
- }
92
- }
93
- export function isUnconditionalReturnInstruction(item) {
94
- const head = instructionHead(item);
95
- if (head === 'ret')
96
- return item.instruction.mnemonic === 'ret';
97
- return head === 'retn' || head === 'reti';
98
- }
99
- export function isPureTokenTransferInstruction(item) {
100
- const head = instructionHead(item);
101
- if (head === 'ex')
102
- return true;
103
- if (head !== 'ld' || instructionOperandCount(item.instruction) !== 2)
104
- return false;
105
- const dst = instructionOperand(item.instruction, 0);
106
- const src = instructionOperand(item.instruction, 1);
107
- if (regName(dst) === undefined)
108
- return false;
109
- return regName(src) !== undefined || src?.kind === 'imm';
110
- }
111
- export function isAccumulatorSelfOperand(item) {
112
- const inst = item.instruction;
113
- if (inst.mnemonic === 'or' || inst.mnemonic === 'and' || inst.mnemonic === 'xor') {
114
- return inst.source.kind === 'reg8' && inst.source.register === 'a';
115
- }
116
- return false;
117
- }
118
- export function isImmediateZeroOperand(item) {
119
- const inst = item.instruction;
120
- if (inst.mnemonic !== 'cp')
121
- return false;
122
- return immValue(inst.source) === 0;
123
- }
124
- export function isRegisterOperand(item, index, name) {
125
- if (item === undefined)
126
- return false;
127
- const operand = instructionOperand(item.instruction, index);
128
- return regName(operand) === name.toUpperCase();
129
- }
@@ -1,3 +0,0 @@
1
- import type { LocatedSmartComment, RegisterCareConflict, RegisterCareOutputCandidate, RegisterCareRoutine, RoutineSummary } from './types.js';
2
- export declare function findRegisterCareConflicts(routine: RegisterCareRoutine, summaries: Map<string, RoutineSummary>, hints: LocatedSmartComment[]): RegisterCareConflict[];
3
- export declare function findCallerOutputCandidateObservations(routines: RegisterCareRoutine[], summaries: Map<string, RoutineSummary>): RegisterCareOutputCandidate[];
@@ -1,3 +0,0 @@
1
- import type { SourceItem } from '../model/source-item.js';
2
- import type { RegisterCareProgramModel } from './types.js';
3
- export declare function buildRegisterCareProgramModel(items: readonly SourceItem[]): RegisterCareProgramModel;