@jhlagado/azm 0.1.0

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 (301) hide show
  1. package/LICENSE +649 -0
  2. package/README.md +142 -0
  3. package/dist/src/analysis.d.ts +11 -0
  4. package/dist/src/analysis.js +41 -0
  5. package/dist/src/api-compile.d.ts +8 -0
  6. package/dist/src/api-compile.js +3 -0
  7. package/dist/src/api-tooling.d.ts +25 -0
  8. package/dist/src/api-tooling.js +21 -0
  9. package/dist/src/cli.d.ts +30 -0
  10. package/dist/src/cli.js +523 -0
  11. package/dist/src/compile.d.ts +10 -0
  12. package/dist/src/compile.js +175 -0
  13. package/dist/src/compileShared.d.ts +3 -0
  14. package/dist/src/compileShared.js +7 -0
  15. package/dist/src/diagnosticTypes.d.ts +77 -0
  16. package/dist/src/diagnosticTypes.js +53 -0
  17. package/dist/src/formats/index.d.ts +7 -0
  18. package/dist/src/formats/index.js +17 -0
  19. package/dist/src/formats/range.d.ts +17 -0
  20. package/dist/src/formats/range.js +45 -0
  21. package/dist/src/formats/types.d.ts +208 -0
  22. package/dist/src/formats/types.js +1 -0
  23. package/dist/src/formats/writeAsm80.d.ts +6 -0
  24. package/dist/src/formats/writeAsm80.js +86 -0
  25. package/dist/src/formats/writeBin.d.ts +7 -0
  26. package/dist/src/formats/writeBin.js +23 -0
  27. package/dist/src/formats/writeD8m.d.ts +9 -0
  28. package/dist/src/formats/writeD8m.js +239 -0
  29. package/dist/src/formats/writeHex.d.ts +9 -0
  30. package/dist/src/formats/writeHex.js +39 -0
  31. package/dist/src/formats/writeListing.d.ts +8 -0
  32. package/dist/src/formats/writeListing.js +83 -0
  33. package/dist/src/frontend/asm80/asmLine.d.ts +39 -0
  34. package/dist/src/frontend/asm80/asmLine.js +89 -0
  35. package/dist/src/frontend/asm80/parseAsmRawValues.d.ts +4 -0
  36. package/dist/src/frontend/asm80/parseAsmRawValues.js +94 -0
  37. package/dist/src/frontend/asm80/quoteScan.d.ts +10 -0
  38. package/dist/src/frontend/asm80/quoteScan.js +25 -0
  39. package/dist/src/frontend/ast.d.ts +376 -0
  40. package/dist/src/frontend/ast.js +1 -0
  41. package/dist/src/frontend/directiveAliases.d.ts +14 -0
  42. package/dist/src/frontend/directiveAliases.js +189 -0
  43. package/dist/src/frontend/grammarData.d.ts +14 -0
  44. package/dist/src/frontend/grammarData.js +65 -0
  45. package/dist/src/frontend/immExprUtils.d.ts +2 -0
  46. package/dist/src/frontend/immExprUtils.js +12 -0
  47. package/dist/src/frontend/parseAsmFlatDirectiveLine.d.ts +17 -0
  48. package/dist/src/frontend/parseAsmFlatDirectiveLine.js +187 -0
  49. package/dist/src/frontend/parseAsmInstruction.d.ts +3 -0
  50. package/dist/src/frontend/parseAsmInstruction.js +73 -0
  51. package/dist/src/frontend/parseAsmStatements.d.ts +6 -0
  52. package/dist/src/frontend/parseAsmStatements.js +16 -0
  53. package/dist/src/frontend/parseAsmStream.d.ts +10 -0
  54. package/dist/src/frontend/parseAsmStream.js +33 -0
  55. package/dist/src/frontend/parseAsmTopLevel.d.ts +18 -0
  56. package/dist/src/frontend/parseAsmTopLevel.js +34 -0
  57. package/dist/src/frontend/parseDiagnostics.d.ts +9 -0
  58. package/dist/src/frontend/parseDiagnostics.js +16 -0
  59. package/dist/src/frontend/parseEnum.d.ts +12 -0
  60. package/dist/src/frontend/parseEnum.js +70 -0
  61. package/dist/src/frontend/parseImm.d.ts +10 -0
  62. package/dist/src/frontend/parseImm.js +397 -0
  63. package/dist/src/frontend/parseLogicalLines.d.ts +11 -0
  64. package/dist/src/frontend/parseLogicalLines.js +94 -0
  65. package/dist/src/frontend/parseOp.d.ts +25 -0
  66. package/dist/src/frontend/parseOp.js +120 -0
  67. package/dist/src/frontend/parseOpHeader.d.ts +20 -0
  68. package/dist/src/frontend/parseOpHeader.js +32 -0
  69. package/dist/src/frontend/parseOperands.d.ts +4 -0
  70. package/dist/src/frontend/parseOperands.js +290 -0
  71. package/dist/src/frontend/parseParams.d.ts +6 -0
  72. package/dist/src/frontend/parseParams.js +62 -0
  73. package/dist/src/frontend/parseParserRecovery.d.ts +12 -0
  74. package/dist/src/frontend/parseParserRecovery.js +17 -0
  75. package/dist/src/frontend/parseParserShared.d.ts +2 -0
  76. package/dist/src/frontend/parseParserShared.js +8 -0
  77. package/dist/src/frontend/parseRawDataDirectiveStart.d.ts +1 -0
  78. package/dist/src/frontend/parseRawDataDirectiveStart.js +3 -0
  79. package/dist/src/frontend/parseRawDataDirectives.d.ts +7 -0
  80. package/dist/src/frontend/parseRawDataDirectives.js +142 -0
  81. package/dist/src/frontend/parseRecordFieldDecl.d.ts +34 -0
  82. package/dist/src/frontend/parseRecordFieldDecl.js +177 -0
  83. package/dist/src/frontend/parseSourceItemDispatch.d.ts +46 -0
  84. package/dist/src/frontend/parseSourceItemDispatch.js +53 -0
  85. package/dist/src/frontend/parseSourceItemTable.d.ts +16 -0
  86. package/dist/src/frontend/parseSourceItemTable.js +68 -0
  87. package/dist/src/frontend/parseTopLevelCommon.d.ts +11 -0
  88. package/dist/src/frontend/parseTopLevelCommon.js +50 -0
  89. package/dist/src/frontend/parseTypes.d.ts +24 -0
  90. package/dist/src/frontend/parseTypes.js +77 -0
  91. package/dist/src/frontend/parser.d.ts +11 -0
  92. package/dist/src/frontend/parser.js +88 -0
  93. package/dist/src/frontend/source.d.ts +28 -0
  94. package/dist/src/frontend/source.js +58 -0
  95. package/dist/src/frontend/sourceExtensions.d.ts +2 -0
  96. package/dist/src/frontend/sourceExtensions.js +6 -0
  97. package/dist/src/index.d.ts +3 -0
  98. package/dist/src/index.js +2 -0
  99. package/dist/src/lintCaseStyle.d.ts +4 -0
  100. package/dist/src/lintCaseStyle.js +129 -0
  101. package/dist/src/lowering/asmDirectiveLowering.d.ts +4 -0
  102. package/dist/src/lowering/asmDirectiveLowering.js +229 -0
  103. package/dist/src/lowering/asmDirectiveTraversal.d.ts +47 -0
  104. package/dist/src/lowering/asmDirectiveTraversal.js +52 -0
  105. package/dist/src/lowering/asmEquResolution.d.ts +8 -0
  106. package/dist/src/lowering/asmEquResolution.js +69 -0
  107. package/dist/src/lowering/asmInstructionLdHelpers.d.ts +15 -0
  108. package/dist/src/lowering/asmInstructionLdHelpers.js +102 -0
  109. package/dist/src/lowering/asmInstructionLowering.d.ts +5 -0
  110. package/dist/src/lowering/asmInstructionLowering.js +54 -0
  111. package/dist/src/lowering/asmInstructionStream.d.ts +46 -0
  112. package/dist/src/lowering/asmInstructionStream.js +51 -0
  113. package/dist/src/lowering/asmLoweringBranchCall.d.ts +43 -0
  114. package/dist/src/lowering/asmLoweringBranchCall.js +254 -0
  115. package/dist/src/lowering/asmLoweringHost.d.ts +23 -0
  116. package/dist/src/lowering/asmLoweringHost.js +1 -0
  117. package/dist/src/lowering/asmLoweringLd.d.ts +28 -0
  118. package/dist/src/lowering/asmLoweringLd.js +144 -0
  119. package/dist/src/lowering/asmRangeLowering.d.ts +17 -0
  120. package/dist/src/lowering/asmRangeLowering.js +39 -0
  121. package/dist/src/lowering/asmRawDataLowering.d.ts +16 -0
  122. package/dist/src/lowering/asmRawDataLowering.js +209 -0
  123. package/dist/src/lowering/asmSourceEmitter.d.ts +4 -0
  124. package/dist/src/lowering/asmSourceEmitter.js +9 -0
  125. package/dist/src/lowering/asmSourceInstructionLowering.d.ts +4 -0
  126. package/dist/src/lowering/asmSourceInstructionLowering.js +14 -0
  127. package/dist/src/lowering/asmUtils.d.ts +13 -0
  128. package/dist/src/lowering/asmUtils.js +105 -0
  129. package/dist/src/lowering/assemblerFlowSetup.d.ts +54 -0
  130. package/dist/src/lowering/assemblerFlowSetup.js +128 -0
  131. package/dist/src/lowering/assemblerLoweringContext.d.ts +151 -0
  132. package/dist/src/lowering/assemblerLoweringContext.js +16 -0
  133. package/dist/src/lowering/assemblerLoweringContextSplit.d.ts +7 -0
  134. package/dist/src/lowering/assemblerLoweringContextSplit.js +75 -0
  135. package/dist/src/lowering/assemblerLoweringPhases.d.ts +66 -0
  136. package/dist/src/lowering/assemblerLoweringPhases.js +166 -0
  137. package/dist/src/lowering/bytePlacement.d.ts +7 -0
  138. package/dist/src/lowering/bytePlacement.js +37 -0
  139. package/dist/src/lowering/capabilities.d.ts +67 -0
  140. package/dist/src/lowering/capabilities.js +1 -0
  141. package/dist/src/lowering/eaResolution.d.ts +58 -0
  142. package/dist/src/lowering/eaResolution.js +159 -0
  143. package/dist/src/lowering/emissionCore.d.ts +17 -0
  144. package/dist/src/lowering/emissionCore.js +21 -0
  145. package/dist/src/lowering/emit.d.ts +17 -0
  146. package/dist/src/lowering/emit.js +46 -0
  147. package/dist/src/lowering/emitContextBuilder.d.ts +63 -0
  148. package/dist/src/lowering/emitContextBuilder.js +41 -0
  149. package/dist/src/lowering/emitFinalization.d.ts +61 -0
  150. package/dist/src/lowering/emitFinalization.js +66 -0
  151. package/dist/src/lowering/emitFinalizationSetup.d.ts +19 -0
  152. package/dist/src/lowering/emitFinalizationSetup.js +26 -0
  153. package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.d.ts +7 -0
  154. package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.js +119 -0
  155. package/dist/src/lowering/emitPhase1Helpers.d.ts +4 -0
  156. package/dist/src/lowering/emitPhase1Helpers.js +12 -0
  157. package/dist/src/lowering/emitPhase1Types.d.ts +21 -0
  158. package/dist/src/lowering/emitPhase1Types.js +1 -0
  159. package/dist/src/lowering/emitPhase1WirePipeline.d.ts +70 -0
  160. package/dist/src/lowering/emitPhase1WirePipeline.js +203 -0
  161. package/dist/src/lowering/emitPhase1Workspace.d.ts +82 -0
  162. package/dist/src/lowering/emitPhase1Workspace.js +55 -0
  163. package/dist/src/lowering/emitPipeline.d.ts +121 -0
  164. package/dist/src/lowering/emitPipeline.js +57 -0
  165. package/dist/src/lowering/emitProgramContext.d.ts +39 -0
  166. package/dist/src/lowering/emitProgramContext.js +29 -0
  167. package/dist/src/lowering/emitState.d.ts +90 -0
  168. package/dist/src/lowering/emitState.js +124 -0
  169. package/dist/src/lowering/fixupBaseResolution.d.ts +7 -0
  170. package/dist/src/lowering/fixupBaseResolution.js +23 -0
  171. package/dist/src/lowering/fixupEmission.d.ts +64 -0
  172. package/dist/src/lowering/fixupEmission.js +199 -0
  173. package/dist/src/lowering/immMath.d.ts +2 -0
  174. package/dist/src/lowering/immMath.js +34 -0
  175. package/dist/src/lowering/inputAssets.d.ts +7 -0
  176. package/dist/src/lowering/inputAssets.js +106 -0
  177. package/dist/src/lowering/ldEncoding.d.ts +15 -0
  178. package/dist/src/lowering/ldEncoding.js +12 -0
  179. package/dist/src/lowering/ldEncodingRegMemHelpers.d.ts +5 -0
  180. package/dist/src/lowering/ldEncodingRegMemHelpers.js +124 -0
  181. package/dist/src/lowering/ldFormSelection.d.ts +26 -0
  182. package/dist/src/lowering/ldFormSelection.js +92 -0
  183. package/dist/src/lowering/ldLowering.d.ts +7 -0
  184. package/dist/src/lowering/ldLowering.js +13 -0
  185. package/dist/src/lowering/loweredAsmByteEmission.d.ts +23 -0
  186. package/dist/src/lowering/loweredAsmByteEmission.js +185 -0
  187. package/dist/src/lowering/loweredAsmPlacement.d.ts +13 -0
  188. package/dist/src/lowering/loweredAsmPlacement.js +86 -0
  189. package/dist/src/lowering/loweredAsmStreamRecording.d.ts +27 -0
  190. package/dist/src/lowering/loweredAsmStreamRecording.js +215 -0
  191. package/dist/src/lowering/loweredAsmTypes.d.ts +202 -0
  192. package/dist/src/lowering/loweredAsmTypes.js +1 -0
  193. package/dist/src/lowering/loweredFormat.d.ts +3 -0
  194. package/dist/src/lowering/loweredFormat.js +26 -0
  195. package/dist/src/lowering/loweredItemSize.d.ts +4 -0
  196. package/dist/src/lowering/loweredItemSize.js +17 -0
  197. package/dist/src/lowering/loweringDiagnostics.d.ts +9 -0
  198. package/dist/src/lowering/loweringDiagnostics.js +55 -0
  199. package/dist/src/lowering/loweringTypes.d.ts +27 -0
  200. package/dist/src/lowering/loweringTypes.js +1 -0
  201. package/dist/src/lowering/opCandidateRegistry.d.ts +9 -0
  202. package/dist/src/lowering/opCandidateRegistry.js +9 -0
  203. package/dist/src/lowering/opExpansionExecution.d.ts +15 -0
  204. package/dist/src/lowering/opExpansionExecution.js +45 -0
  205. package/dist/src/lowering/opExpansionOrchestration.d.ts +15 -0
  206. package/dist/src/lowering/opExpansionOrchestration.js +88 -0
  207. package/dist/src/lowering/opExpansionStream.d.ts +12 -0
  208. package/dist/src/lowering/opExpansionStream.js +176 -0
  209. package/dist/src/lowering/opMatching.d.ts +52 -0
  210. package/dist/src/lowering/opMatching.js +355 -0
  211. package/dist/src/lowering/opSubstitution.d.ts +13 -0
  212. package/dist/src/lowering/opSubstitution.js +175 -0
  213. package/dist/src/lowering/prescanTypes.d.ts +7 -0
  214. package/dist/src/lowering/prescanTypes.js +1 -0
  215. package/dist/src/lowering/programLowering.d.ts +144 -0
  216. package/dist/src/lowering/programLowering.js +2 -0
  217. package/dist/src/lowering/programLoweringDeclarations.d.ts +5 -0
  218. package/dist/src/lowering/programLoweringDeclarations.js +5 -0
  219. package/dist/src/lowering/programLoweringFinalize.d.ts +18 -0
  220. package/dist/src/lowering/programLoweringFinalize.js +115 -0
  221. package/dist/src/lowering/programLoweringTraversal.d.ts +2 -0
  222. package/dist/src/lowering/programLoweringTraversal.js +93 -0
  223. package/dist/src/lowering/programPrescan.d.ts +3 -0
  224. package/dist/src/lowering/programPrescan.js +37 -0
  225. package/dist/src/lowering/traceFormat.d.ts +13 -0
  226. package/dist/src/lowering/traceFormat.js +211 -0
  227. package/dist/src/pathCompare.d.ts +3 -0
  228. package/dist/src/pathCompare.js +26 -0
  229. package/dist/src/pipeline.d.ts +78 -0
  230. package/dist/src/pipeline.js +1 -0
  231. package/dist/src/registerCare/analyze.d.ts +24 -0
  232. package/dist/src/registerCare/analyze.js +327 -0
  233. package/dist/src/registerCare/annotate.d.ts +11 -0
  234. package/dist/src/registerCare/annotate.js +76 -0
  235. package/dist/src/registerCare/boundaryHints.d.ts +2 -0
  236. package/dist/src/registerCare/boundaryHints.js +10 -0
  237. package/dist/src/registerCare/carriers.d.ts +4 -0
  238. package/dist/src/registerCare/carriers.js +78 -0
  239. package/dist/src/registerCare/controlFlow.d.ts +5 -0
  240. package/dist/src/registerCare/controlFlow.js +35 -0
  241. package/dist/src/registerCare/fix.d.ts +11 -0
  242. package/dist/src/registerCare/fix.js +119 -0
  243. package/dist/src/registerCare/liveness.d.ts +7 -0
  244. package/dist/src/registerCare/liveness.js +227 -0
  245. package/dist/src/registerCare/profiles.d.ts +11 -0
  246. package/dist/src/registerCare/profiles.js +45 -0
  247. package/dist/src/registerCare/programModel.d.ts +3 -0
  248. package/dist/src/registerCare/programModel.js +181 -0
  249. package/dist/src/registerCare/report.d.ts +5 -0
  250. package/dist/src/registerCare/report.js +139 -0
  251. package/dist/src/registerCare/smartComments.d.ts +5 -0
  252. package/dist/src/registerCare/smartComments.js +247 -0
  253. package/dist/src/registerCare/sourceText.d.ts +8 -0
  254. package/dist/src/registerCare/sourceText.js +15 -0
  255. package/dist/src/registerCare/summary.d.ts +3 -0
  256. package/dist/src/registerCare/summary.js +492 -0
  257. package/dist/src/registerCare/tooling.d.ts +42 -0
  258. package/dist/src/registerCare/tooling.js +50 -0
  259. package/dist/src/registerCare/types.d.ts +154 -0
  260. package/dist/src/registerCare/types.js +1 -0
  261. package/dist/src/semantics/declVisitor.d.ts +5 -0
  262. package/dist/src/semantics/declVisitor.js +11 -0
  263. package/dist/src/semantics/env.d.ts +28 -0
  264. package/dist/src/semantics/env.js +432 -0
  265. package/dist/src/semantics/layout.d.ts +21 -0
  266. package/dist/src/semantics/layout.js +226 -0
  267. package/dist/src/semantics/layoutCastFold.d.ts +22 -0
  268. package/dist/src/semantics/layoutCastFold.js +118 -0
  269. package/dist/src/semantics/semanticsDiagnostics.d.ts +2 -0
  270. package/dist/src/semantics/semanticsDiagnostics.js +4 -0
  271. package/dist/src/semantics/typeQueries.d.ts +31 -0
  272. package/dist/src/semantics/typeQueries.js +124 -0
  273. package/dist/src/sourceIncludeExpansion.d.ts +17 -0
  274. package/dist/src/sourceIncludeExpansion.js +124 -0
  275. package/dist/src/sourceIncludePaths.d.ts +1 -0
  276. package/dist/src/sourceIncludePaths.js +12 -0
  277. package/dist/src/sourceLoader.d.ts +15 -0
  278. package/dist/src/sourceLoader.js +118 -0
  279. package/dist/src/z80/effects.d.ts +3 -0
  280. package/dist/src/z80/effects.js +516 -0
  281. package/dist/src/z80/encode.d.ts +10 -0
  282. package/dist/src/z80/encode.js +412 -0
  283. package/dist/src/z80/encodeAlu.d.ts +7 -0
  284. package/dist/src/z80/encodeAlu.js +219 -0
  285. package/dist/src/z80/encodeBitOps.d.ts +7 -0
  286. package/dist/src/z80/encodeBitOps.js +123 -0
  287. package/dist/src/z80/encodeContext.d.ts +29 -0
  288. package/dist/src/z80/encodeContext.js +1 -0
  289. package/dist/src/z80/encodeControl.d.ts +26 -0
  290. package/dist/src/z80/encodeControl.js +180 -0
  291. package/dist/src/z80/encodeCoreOps.d.ts +7 -0
  292. package/dist/src/z80/encodeCoreOps.js +131 -0
  293. package/dist/src/z80/encodeIo.d.ts +9 -0
  294. package/dist/src/z80/encodeIo.js +128 -0
  295. package/dist/src/z80/encodeLd.d.ts +13 -0
  296. package/dist/src/z80/encodeLd.js +273 -0
  297. package/dist/src/z80/encoderRegistry.d.ts +13 -0
  298. package/dist/src/z80/encoderRegistry.js +169 -0
  299. package/docs/reference/cli.md +134 -0
  300. package/docs/reference/tooling-api.md +248 -0
  301. package/package.json +98 -0
@@ -0,0 +1,492 @@
1
+ import { getZ80InstructionEffect } from '../z80/effects.js';
2
+ import { precedingCServiceName } from './boundaryHints.js';
3
+ import { expandCarrier } from './carriers.js';
4
+ import { rstServiceTargetName, rstTargetName } from './profiles.js';
5
+ const FLAG_UNIT_LIST = ['carry', 'zero', 'sign', 'parity', 'halfCarry'];
6
+ const TRACKED_UNITS = [
7
+ 'A',
8
+ 'B',
9
+ 'C',
10
+ 'D',
11
+ 'E',
12
+ 'H',
13
+ 'L',
14
+ 'IXH',
15
+ 'IXL',
16
+ 'IYH',
17
+ 'IYL',
18
+ ...FLAG_UNIT_LIST,
19
+ ];
20
+ const GENERAL_REGISTER_UNITS = new Set(['A', 'B', 'C', 'D', 'E', 'H', 'L']);
21
+ const CONTRACT_FLAG_UNITS = new Set(['carry', 'zero']);
22
+ const STACK_POINTER_UNITS = new Set(['SPH', 'SPL']);
23
+ const REGISTER_PAIRS = [
24
+ ['B', 'C'],
25
+ ['D', 'E'],
26
+ ['H', 'L'],
27
+ ];
28
+ function unique(items) {
29
+ return [...new Set(items)];
30
+ }
31
+ function isTrackedUnit(unit) {
32
+ return TRACKED_UNITS.includes(unit);
33
+ }
34
+ function getRegisterUnits(name) {
35
+ return expandCarrier(name);
36
+ }
37
+ function readToken(tokens, unit) {
38
+ return tokens.get(unit) ?? { origin: 'unknown' };
39
+ }
40
+ function semanticReadOrigins(tokens, units) {
41
+ const origins = [];
42
+ for (const unit of units) {
43
+ if (!isTrackedUnit(unit)) {
44
+ origins.push(unit);
45
+ continue;
46
+ }
47
+ const token = readToken(tokens, unit);
48
+ if (token.origin !== 'unknown' && token.origin !== 'produced')
49
+ origins.push(token.origin);
50
+ }
51
+ return origins;
52
+ }
53
+ function markProducedReadsConsumed(tokens, consumedProduced, reads, writes, item) {
54
+ for (const unit of reads) {
55
+ if (!isTrackedUnit(unit) || writes.has(unit))
56
+ continue;
57
+ if (item?.head.toLowerCase() === 'cp' && unit === 'A')
58
+ continue;
59
+ if (readToken(tokens, unit).origin === 'produced')
60
+ consumedProduced.add(unit);
61
+ }
62
+ }
63
+ function tokenPreservesUnit(token, unit) {
64
+ return token?.origin === unit;
65
+ }
66
+ function isOpaqueBoundary(item, effect) {
67
+ if (effect.control.kind === 'call' || effect.control.kind === 'rst')
68
+ return true;
69
+ return (effect.control.kind === 'jump' &&
70
+ item.head.toLowerCase() === 'jp' &&
71
+ !effect.control.conditional &&
72
+ Boolean(effect.control.target) &&
73
+ !effect.control.target?.startsWith('.'));
74
+ }
75
+ function boundarySummary(routine, index, summaries) {
76
+ const item = routine.instructions[index];
77
+ if (!item)
78
+ return undefined;
79
+ const effect = getZ80InstructionEffect(item.instruction);
80
+ if (effect.control.kind === 'call' && effect.control.target) {
81
+ return summaries.get(effect.control.target);
82
+ }
83
+ if (effect.control.kind === 'jump' &&
84
+ item.head.toLowerCase() === 'jp' &&
85
+ effect.control.target &&
86
+ !effect.control.target.startsWith('.') &&
87
+ !routine.labels.includes(effect.control.target)) {
88
+ return summaries.get(effect.control.target);
89
+ }
90
+ if (effect.control.kind === 'rst' && effect.control.vector !== undefined) {
91
+ const service = precedingCServiceName(routine.instructions[index - 1]);
92
+ if (service) {
93
+ const serviceSummary = summaries.get(rstServiceTargetName(effect.control.vector, service));
94
+ if (serviceSummary)
95
+ return serviceSummary;
96
+ }
97
+ return summaries.get(rstTargetName(effect.control.vector));
98
+ }
99
+ return undefined;
100
+ }
101
+ function relationKey(relation) {
102
+ return `${relation.out.join(',')}<- ${relation.from.join(',')}`;
103
+ }
104
+ function addRelation(out, relation) {
105
+ if (relation.out.length === 0 || relation.from.length === 0)
106
+ return;
107
+ const key = relationKey(relation);
108
+ if (!out.some((existing) => relationKey(existing) === key))
109
+ out.push(relation);
110
+ }
111
+ function addContractRelation(out, relation) {
112
+ if (relation.out.length === 0)
113
+ return;
114
+ const key = relationKey(relation);
115
+ if (!out.some((existing) => relationKey(existing) === key))
116
+ out.push(relation);
117
+ }
118
+ function pairRelation(tokens, out) {
119
+ const from = [];
120
+ for (const unit of out) {
121
+ const token = tokens.get(unit);
122
+ if (!token || token.origin === 'unknown' || token.origin === 'produced')
123
+ return undefined;
124
+ from.push(token.origin);
125
+ }
126
+ if (out.every((unit, idx) => unit === from[idx]))
127
+ return undefined;
128
+ if (out.some((unit, idx) => unit === from[idx]))
129
+ return undefined;
130
+ return { out, from };
131
+ }
132
+ function producedPairRelation(tokens, consumedProduced, out) {
133
+ if (out.some((unit) => tokens.get(unit)?.origin !== 'produced' || consumedProduced.has(unit))) {
134
+ return undefined;
135
+ }
136
+ return { out, from: [] };
137
+ }
138
+ function withImpliedFlagUnits(units) {
139
+ return unique(units);
140
+ }
141
+ function contractOutRelation(contractIn, contractOut) {
142
+ if (contractOut.length === 0)
143
+ return undefined;
144
+ return {
145
+ out: contractOut,
146
+ from: contractIn.length === contractOut.length ? contractIn : [],
147
+ };
148
+ }
149
+ function isUnconditionalReturn(item) {
150
+ const head = item.head.toLowerCase();
151
+ if (head === 'ret')
152
+ return item.instruction.operands.length === 0;
153
+ return head === 'retn' || head === 'reti';
154
+ }
155
+ function isPureTokenTransfer(item) {
156
+ const inst = item.instruction;
157
+ const head = item.head.toLowerCase();
158
+ if (head === 'ex')
159
+ return true;
160
+ if (head !== 'ld' || inst.operands.length !== 2)
161
+ return false;
162
+ const dst = inst.operands[0];
163
+ const src = inst.operands[1];
164
+ if (dst?.kind !== 'Reg')
165
+ return false;
166
+ return src?.kind === 'Reg' || src?.kind === 'Imm';
167
+ }
168
+ function isAccumulatorSelfOperand(item) {
169
+ const operand = item.instruction.operands[0];
170
+ return operand?.kind === 'Reg' && operand.name.toUpperCase() === 'A';
171
+ }
172
+ function isImmediateZeroOperand(item) {
173
+ const operand = item.instruction.operands[0];
174
+ return operand?.kind === 'Imm' && operand.expr.kind === 'ImmLiteral' && operand.expr.value === 0;
175
+ }
176
+ function isRegisterOperand(item, index, name) {
177
+ const operand = item?.instruction.operands[index];
178
+ return operand?.kind === 'Reg' && operand.name.toUpperCase() === name;
179
+ }
180
+ function isCarryClearBeforeSbcHl(item, next) {
181
+ const head = item.head.toLowerCase();
182
+ if (head !== 'or' && head !== 'and')
183
+ return false;
184
+ if (!isAccumulatorSelfOperand(item))
185
+ return false;
186
+ return next?.head.toLowerCase() === 'sbc' && isRegisterOperand(next, 0, 'HL');
187
+ }
188
+ function intentOutputUnits(item) {
189
+ const head = item.head.toLowerCase();
190
+ if (head === 'scf' || head === 'ccf')
191
+ return ['carry'];
192
+ if (head === 'cp')
193
+ return isImmediateZeroOperand(item) ? ['A', 'carry', 'zero'] : ['carry', 'zero'];
194
+ if ((head === 'or' || head === 'and' || head === 'xor') && isAccumulatorSelfOperand(item)) {
195
+ return ['A', 'carry', 'zero'];
196
+ }
197
+ return [];
198
+ }
199
+ function isMechanicalResidueWrite(item, unit) {
200
+ const head = item.head.toLowerCase();
201
+ if (head === 'djnz')
202
+ return unit === 'B';
203
+ if (head === 'ldi' || head === 'ldir' || head === 'ldd' || head === 'lddr') {
204
+ return (unit === 'B' || unit === 'C' || unit === 'D' || unit === 'E' || unit === 'H' || unit === 'L');
205
+ }
206
+ return false;
207
+ }
208
+ function applyPureTokenTransfer(tokens, consumedProduced, item) {
209
+ const inst = item.instruction;
210
+ const head = item.head.toLowerCase();
211
+ if (head === 'ld' && inst.operands.length === 2) {
212
+ const dst = inst.operands[0];
213
+ const src = inst.operands[1];
214
+ if (dst?.kind !== 'Reg')
215
+ return [];
216
+ const dstUnits = dst.kind === 'Reg' ? getRegisterUnits(dst.name) : undefined;
217
+ if (!dstUnits)
218
+ return [];
219
+ const srcUnits = src?.kind === 'Reg' ? getRegisterUnits(src.name) : undefined;
220
+ if (srcUnits && srcUnits.length === dstUnits.length) {
221
+ dstUnits.forEach((unit, index) => {
222
+ tokens.set(unit, readToken(tokens, srcUnits[index]));
223
+ if (readToken(tokens, srcUnits[index]).origin === 'produced') {
224
+ consumedProduced.add(srcUnits[index]);
225
+ }
226
+ consumedProduced.delete(unit);
227
+ });
228
+ }
229
+ else {
230
+ for (const unit of dstUnits) {
231
+ tokens.set(unit, { origin: 'produced' });
232
+ consumedProduced.delete(unit);
233
+ }
234
+ }
235
+ return dstUnits;
236
+ }
237
+ if (head === 'ex' && inst.operands.length === 2) {
238
+ const left = inst.operands[0];
239
+ const right = inst.operands[1];
240
+ if (left?.kind !== 'Reg' || right?.kind !== 'Reg')
241
+ return [];
242
+ const leftUnits = getRegisterUnits(left.name);
243
+ const rightUnits = getRegisterUnits(right.name);
244
+ if (!leftUnits || !rightUnits || leftUnits.length !== rightUnits.length)
245
+ return [];
246
+ const leftTokens = leftUnits.map((unit) => readToken(tokens, unit));
247
+ const rightTokens = rightUnits.map((unit) => readToken(tokens, unit));
248
+ leftUnits.forEach((unit, index) => tokens.set(unit, rightTokens[index] ?? { origin: 'unknown' }));
249
+ rightUnits.forEach((unit, index) => tokens.set(unit, leftTokens[index] ?? { origin: 'unknown' }));
250
+ return unique([...leftUnits, ...rightUnits]);
251
+ }
252
+ return [];
253
+ }
254
+ function applyKnownBoundarySummary(tokens, consumedProduced, intendedProduced, directMayWrite, summary) {
255
+ for (const relation of summary.valueRelations) {
256
+ const sameCarrierRelation = relation.out.length === relation.from.length &&
257
+ relation.out.every((unit, index) => unit === relation.from[index]);
258
+ relation.out.forEach((unit, index) => {
259
+ if (!sameCarrierRelation &&
260
+ relation.from.length === relation.out.length &&
261
+ relation.from[index]) {
262
+ tokens.set(unit, readToken(tokens, relation.from[index]));
263
+ }
264
+ else {
265
+ tokens.set(unit, { origin: 'produced' });
266
+ consumedProduced.delete(unit);
267
+ if (CONTRACT_FLAG_UNITS.has(unit))
268
+ intendedProduced.add(unit);
269
+ }
270
+ });
271
+ }
272
+ for (const unit of summary.mayWrite) {
273
+ if (STACK_POINTER_UNITS.has(unit))
274
+ continue;
275
+ if (isTrackedUnit(unit)) {
276
+ tokens.set(unit, { origin: 'unknown' });
277
+ consumedProduced.delete(unit);
278
+ intendedProduced.delete(unit);
279
+ }
280
+ else {
281
+ directMayWrite.push(unit);
282
+ }
283
+ }
284
+ }
285
+ export function inferRoutineSummary(routine, boundarySummaries = new Map()) {
286
+ const tokens = new Map();
287
+ for (const unit of TRACKED_UNITS)
288
+ tokens.set(unit, { origin: unit });
289
+ const stack = [];
290
+ const mayRead = [];
291
+ const directMayWrite = [];
292
+ const consumedProduced = new Set();
293
+ const intendedProduced = new Set();
294
+ let stackBalanced = true;
295
+ let hasUnknownStackEffect = false;
296
+ for (let index = 0; index < routine.instructions.length; index += 1) {
297
+ const item = routine.instructions[index];
298
+ const effect = getZ80InstructionEffect(item.instruction);
299
+ const knownBoundary = boundarySummary(routine, index, boundarySummaries);
300
+ const carryClearBeforeSbcHl = isCarryClearBeforeSbcHl(item, routine.instructions[index + 1]);
301
+ const expectedTerminalReturn = index === routine.instructions.length - 1 && isUnconditionalReturn(item);
302
+ const effectWrites = new Set(effect.writes);
303
+ const instructionIntentOutputs = carryClearBeforeSbcHl ? [] : intentOutputUnits(item);
304
+ const semanticReads = carryClearBeforeSbcHl
305
+ ? effect.reads.filter((unit) => unit !== 'A')
306
+ : effect.reads;
307
+ if (effect.stack.kind !== 'push' && !isPureTokenTransfer(item)) {
308
+ mayRead.push(...semanticReadOrigins(tokens, semanticReads));
309
+ markProducedReadsConsumed(tokens, consumedProduced, semanticReads, effectWrites, item);
310
+ }
311
+ if (item.head.toLowerCase() === 'djnz') {
312
+ for (const unit of TRACKED_UNITS) {
313
+ if (readToken(tokens, unit).origin === 'produced')
314
+ consumedProduced.add(unit);
315
+ intendedProduced.delete(unit);
316
+ }
317
+ }
318
+ if (knownBoundary) {
319
+ mayRead.push(...semanticReadOrigins(tokens, knownBoundary.mayRead));
320
+ markProducedReadsConsumed(tokens, consumedProduced, knownBoundary.mayRead, new Set());
321
+ }
322
+ if (effect.stack.kind === 'push') {
323
+ stack.push(effect.stack.units.map((unit) => readToken(tokens, unit)));
324
+ }
325
+ else if (effect.stack.kind === 'pop') {
326
+ const popped = stack.pop();
327
+ if (!popped) {
328
+ stackBalanced = false;
329
+ for (const unit of effect.stack.units) {
330
+ tokens.set(unit, { origin: 'unknown' });
331
+ intendedProduced.delete(unit);
332
+ }
333
+ }
334
+ else if (popped.length !== effect.stack.units.length) {
335
+ for (const unit of effect.stack.units) {
336
+ tokens.set(unit, { origin: 'unknown' });
337
+ consumedProduced.delete(unit);
338
+ intendedProduced.delete(unit);
339
+ }
340
+ }
341
+ else {
342
+ effect.stack.units.forEach((unit, idx) => {
343
+ tokens.set(unit, popped[idx] ?? { origin: 'unknown' });
344
+ consumedProduced.delete(unit);
345
+ intendedProduced.delete(unit);
346
+ });
347
+ }
348
+ }
349
+ else if (effect.stack.kind === 'exchangeTop') {
350
+ hasUnknownStackEffect = true;
351
+ for (const unit of effect.stack.units) {
352
+ tokens.set(unit, { origin: 'unknown' });
353
+ consumedProduced.delete(unit);
354
+ intendedProduced.delete(unit);
355
+ }
356
+ }
357
+ else if (effect.stack.kind === 'unknown' &&
358
+ !expectedTerminalReturn &&
359
+ (!knownBoundary || !knownBoundary.stackBalanced || knownBoundary.hasUnknownStackEffect)) {
360
+ hasUnknownStackEffect = true;
361
+ }
362
+ const transferWrites = new Set(isPureTokenTransfer(item) ? applyPureTokenTransfer(tokens, consumedProduced, item) : []);
363
+ if (knownBoundary) {
364
+ applyKnownBoundarySummary(tokens, consumedProduced, intendedProduced, directMayWrite, knownBoundary);
365
+ }
366
+ else if (isOpaqueBoundary(item, effect)) {
367
+ for (const unit of TRACKED_UNITS) {
368
+ tokens.set(unit, { origin: 'unknown' });
369
+ consumedProduced.delete(unit);
370
+ intendedProduced.delete(unit);
371
+ }
372
+ }
373
+ for (const unit of effect.writes) {
374
+ if (STACK_POINTER_UNITS.has(unit))
375
+ continue;
376
+ if (effect.stack.kind === 'pop' && effect.stack.units.includes(unit) && isTrackedUnit(unit)) {
377
+ continue;
378
+ }
379
+ if (transferWrites.has(unit) && isTrackedUnit(unit))
380
+ continue;
381
+ if (unit === 'A' &&
382
+ (item.head.toLowerCase() === 'or' || item.head.toLowerCase() === 'and') &&
383
+ isAccumulatorSelfOperand(item)) {
384
+ if (!carryClearBeforeSbcHl)
385
+ intendedProduced.add(unit);
386
+ continue;
387
+ }
388
+ if (isTrackedUnit(unit)) {
389
+ tokens.set(unit, { origin: isMechanicalResidueWrite(item, unit) ? 'unknown' : 'produced' });
390
+ consumedProduced.delete(unit);
391
+ if (instructionIntentOutputs.includes(unit))
392
+ intendedProduced.add(unit);
393
+ else
394
+ intendedProduced.delete(unit);
395
+ }
396
+ else {
397
+ directMayWrite.push(unit);
398
+ }
399
+ }
400
+ for (const unit of instructionIntentOutputs) {
401
+ if (!isTrackedUnit(unit) || effectWrites.has(unit))
402
+ continue;
403
+ intendedProduced.add(unit);
404
+ }
405
+ }
406
+ if (stack.length !== 0)
407
+ stackBalanced = false;
408
+ const mayWrite = [...directMayWrite];
409
+ const preserved = [];
410
+ const valueRelations = [];
411
+ const outputUnits = new Set();
412
+ for (const pair of REGISTER_PAIRS) {
413
+ const relation = producedPairRelation(tokens, consumedProduced, pair);
414
+ if (relation) {
415
+ addContractRelation(valueRelations, relation);
416
+ for (const unit of pair)
417
+ outputUnits.add(unit);
418
+ }
419
+ }
420
+ for (const unit of TRACKED_UNITS) {
421
+ if (outputUnits.has(unit))
422
+ continue;
423
+ const current = tokens.get(unit);
424
+ const eligibleProduced = current?.origin === 'produced' &&
425
+ (GENERAL_REGISTER_UNITS.has(unit) ||
426
+ (CONTRACT_FLAG_UNITS.has(unit) && intendedProduced.has(unit)));
427
+ const eligiblePreservedIntent = current?.origin === unit && GENERAL_REGISTER_UNITS.has(unit) && intendedProduced.has(unit);
428
+ if ((eligibleProduced || eligiblePreservedIntent) && !consumedProduced.has(unit)) {
429
+ addContractRelation(valueRelations, {
430
+ out: [unit],
431
+ from: eligiblePreservedIntent ? [unit] : [],
432
+ });
433
+ outputUnits.add(unit);
434
+ }
435
+ }
436
+ for (const unit of TRACKED_UNITS) {
437
+ const current = tokens.get(unit);
438
+ if (tokenPreservesUnit(current, unit)) {
439
+ preserved.push(unit);
440
+ continue;
441
+ }
442
+ if (outputUnits.has(unit))
443
+ continue;
444
+ mayWrite.push(unit);
445
+ }
446
+ for (const pair of REGISTER_PAIRS) {
447
+ const relation = pairRelation(tokens, pair);
448
+ if (relation)
449
+ addRelation(valueRelations, relation);
450
+ }
451
+ mayRead.push(...valueRelations.flatMap((relation) => relation.from));
452
+ return {
453
+ name: routine.name,
454
+ mayRead: unique(mayRead),
455
+ mayWrite: unique(mayWrite),
456
+ preserved: unique(preserved),
457
+ valueRelations,
458
+ stackBalanced,
459
+ hasUnknownStackEffect,
460
+ };
461
+ }
462
+ export function applyRoutineContract(summary, contract) {
463
+ const contractIn = withImpliedFlagUnits(contract.in);
464
+ const contractOut = withImpliedFlagUnits(contract.out);
465
+ const contractClobbers = withImpliedFlagUnits(contract.clobbers);
466
+ const contractPreserves = withImpliedFlagUnits(contract.preserves);
467
+ const outputSet = new Set(contractOut);
468
+ const preservedSet = new Set(contractPreserves);
469
+ const inferredWrites = withImpliedFlagUnits(summary.mayWrite);
470
+ const baseMayWrite = contract.complete
471
+ ? inferredWrites.filter((unit) => FLAG_UNIT_LIST.includes(unit))
472
+ : inferredWrites;
473
+ const mayWrite = baseMayWrite.filter((unit) => !outputSet.has(unit) && !preservedSet.has(unit));
474
+ for (const unit of contractClobbers) {
475
+ if (!outputSet.has(unit) && !preservedSet.has(unit) && !mayWrite.includes(unit)) {
476
+ mayWrite.push(unit);
477
+ }
478
+ }
479
+ const mayWriteSet = new Set(withImpliedFlagUnits(mayWrite));
480
+ const preserved = unique([...summary.preserved, ...contractPreserves]).filter((unit) => !outputSet.has(unit) && !mayWriteSet.has(unit));
481
+ const valueRelations = [...summary.valueRelations];
482
+ const relation = contractOutRelation(contractIn, contractOut);
483
+ if (relation)
484
+ addContractRelation(valueRelations, relation);
485
+ return {
486
+ ...summary,
487
+ mayRead: unique(contractIn),
488
+ mayWrite,
489
+ preserved,
490
+ valueRelations,
491
+ };
492
+ }
@@ -0,0 +1,42 @@
1
+ import type { Diagnostic } from '../diagnosticTypes.js';
2
+ import type { LoadedProgram } from '../sourceLoader.js';
3
+ import { type AnalyzeRegisterCareOptions } from './analyze.js';
4
+ import type { RegisterCareOutputCandidate, RegisterCareUnit } from './types.js';
5
+ export interface RegisterCareTextEdit {
6
+ file: string;
7
+ line: number;
8
+ column: number;
9
+ text: string;
10
+ }
11
+ export interface RegisterCareCodeAction {
12
+ title: string;
13
+ kind: 'quickfix';
14
+ edit: RegisterCareTextEdit;
15
+ }
16
+ export interface RegisterCareCandidateDiagnostic {
17
+ kind: 'register-care-output-candidate';
18
+ severity: 'info';
19
+ file: string;
20
+ line: number;
21
+ column: number;
22
+ routine: string;
23
+ carriers: RegisterCareUnit[];
24
+ autoFixable: boolean;
25
+ message: string;
26
+ codeAction: RegisterCareCodeAction;
27
+ }
28
+ export interface AnalyzeRegisterCareForToolsOptions extends Omit<AnalyzeRegisterCareOptions, 'emitReport' | 'emitInterface' | 'emitAnnotations'> {
29
+ emitReport?: boolean;
30
+ emitInterface?: boolean;
31
+ }
32
+ export interface AnalyzeRegisterCareForToolsResult {
33
+ diagnostics: Diagnostic[];
34
+ outputCandidates: RegisterCareOutputCandidate[];
35
+ candidateDiagnostics: RegisterCareCandidateDiagnostic[];
36
+ codeActions: RegisterCareCodeAction[];
37
+ reportText?: string;
38
+ interfaceText?: string;
39
+ }
40
+ export declare function codeActionForOutputCandidate(candidate: RegisterCareOutputCandidate): RegisterCareCodeAction;
41
+ export declare function diagnosticForOutputCandidate(candidate: RegisterCareOutputCandidate): RegisterCareCandidateDiagnostic;
42
+ export declare function analyzeRegisterCareForTools(loaded: LoadedProgram, options: AnalyzeRegisterCareForToolsOptions): AnalyzeRegisterCareForToolsResult;
@@ -0,0 +1,50 @@
1
+ import { analyzeRegisterCare } from './analyze.js';
2
+ import { contractCarrierList } from './report.js';
3
+ function expectOutText(carriers) {
4
+ return `; expects out ${contractCarrierList(carriers)}\n`;
5
+ }
6
+ export function codeActionForOutputCandidate(candidate) {
7
+ return {
8
+ title: `Confirm ${candidate.routine} output ${contractCarrierList(candidate.carriers)}`,
9
+ kind: 'quickfix',
10
+ edit: {
11
+ file: candidate.file,
12
+ line: candidate.line,
13
+ column: 1,
14
+ text: expectOutText(candidate.carriers),
15
+ },
16
+ };
17
+ }
18
+ export function diagnosticForOutputCandidate(candidate) {
19
+ const codeAction = codeActionForOutputCandidate(candidate);
20
+ return {
21
+ kind: 'register-care-output-candidate',
22
+ severity: 'info',
23
+ file: candidate.file,
24
+ line: candidate.line,
25
+ column: candidate.column,
26
+ routine: candidate.routine,
27
+ carriers: candidate.carriers,
28
+ autoFixable: candidate.autoFixable === true,
29
+ message: candidate.message,
30
+ codeAction,
31
+ };
32
+ }
33
+ export function analyzeRegisterCareForTools(loaded, options) {
34
+ const result = analyzeRegisterCare(loaded, {
35
+ ...options,
36
+ emitReport: options.emitReport === true,
37
+ emitInterface: options.emitInterface === true,
38
+ emitAnnotations: false,
39
+ });
40
+ const outputCandidates = result.outputCandidates ?? [];
41
+ const candidateDiagnostics = outputCandidates.map(diagnosticForOutputCandidate);
42
+ return {
43
+ diagnostics: result.diagnostics,
44
+ outputCandidates,
45
+ candidateDiagnostics,
46
+ codeActions: candidateDiagnostics.map((diagnostic) => diagnostic.codeAction),
47
+ ...(result.reportText !== undefined ? { reportText: result.reportText } : {}),
48
+ ...(result.interfaceText !== undefined ? { interfaceText: result.interfaceText } : {}),
49
+ };
50
+ }