@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
@@ -0,0 +1,5 @@
1
+ import type { ParseZ80InstructionResult } from './parse-instruction.js';
2
+ export declare function parseInputInstruction(text: string): ParseZ80InstructionResult | undefined;
3
+ export declare function parseOutputInstruction(text: string): ParseZ80InstructionResult | undefined;
4
+ export declare function parseInterruptModeInstruction(text: string): ParseZ80InstructionResult | undefined;
5
+ export declare function parseRstInstruction(text: string): ParseZ80InstructionResult | undefined;
@@ -0,0 +1,108 @@
1
+ import { splitInstructionOperands } from './operand-split.js';
2
+ import { isRstVector, parseConstantExpression, parseIndexHalfRegister, parsePortOperand, parseRegister8Operand, } from './parse-operands.js';
3
+ export function parseInputInstruction(text) {
4
+ const input = /^IN(?:\s+(.*))?$/i.exec(text);
5
+ if (input) {
6
+ const operandText = input[1] ?? '';
7
+ const parts = splitInstructionOperands(operandText);
8
+ if (operandText.trim().length === 0 || parts.length > 2) {
9
+ return { error: 'in expects one or two operands' };
10
+ }
11
+ if (parts.length === 1)
12
+ return parseOneOperandInput(parts[0] ?? '');
13
+ return parseTwoOperandInput(parts[0] ?? '', parts[1] ?? '');
14
+ }
15
+ return undefined;
16
+ }
17
+ function parseOneOperandInput(portText) {
18
+ const port = parsePortOperand(portText);
19
+ return port?.kind === 'c'
20
+ ? { instruction: { mnemonic: 'in', port } }
21
+ : { error: 'in (c) is the only one-operand in form' };
22
+ }
23
+ function parseTwoOperandInput(targetText, portText) {
24
+ const target = parseRegister8Operand(targetText);
25
+ if (!target) {
26
+ return parseIndexHalfRegister(targetText)
27
+ ? { error: 'in destination must use plain reg8 B/C/D/E/H/L/A' }
28
+ : { error: 'in expects a reg8 destination' };
29
+ }
30
+ const port = parsePortOperand(portText);
31
+ if (!port) {
32
+ return { error: 'in expects a port operand (c) or (imm8)' };
33
+ }
34
+ if (port.kind === 'imm' && target.register !== 'a') {
35
+ return { error: 'in a,(n) immediate port form requires destination A' };
36
+ }
37
+ return { instruction: { mnemonic: 'in', target, port } };
38
+ }
39
+ export function parseOutputInstruction(text) {
40
+ const output = /^OUT(?:\s+(.*))?$/i.exec(text);
41
+ if (output) {
42
+ const operandText = output[1] ?? '';
43
+ const parts = splitInstructionOperands(operandText);
44
+ if (operandText.trim().length === 0 || parts.length !== 2) {
45
+ return { error: 'out expects two operands' };
46
+ }
47
+ const port = parsePortOperand(parts[0] ?? '');
48
+ if (!port) {
49
+ return { error: 'out expects a port operand (c) or (imm8)' };
50
+ }
51
+ return parseOutputSource(port, parts[1] ?? '');
52
+ }
53
+ return undefined;
54
+ }
55
+ function parseOutputSource(port, sourceText) {
56
+ const source = parseRegister8Operand(sourceText);
57
+ if (source) {
58
+ if (port.kind === 'imm' && source.register !== 'a') {
59
+ return { error: 'out (n),a immediate port form requires source A' };
60
+ }
61
+ return { instruction: { mnemonic: 'out', port, source } };
62
+ }
63
+ if (parseIndexHalfRegister(sourceText)) {
64
+ return { error: 'out source must use plain reg8 B/C/D/E/H/L/A' };
65
+ }
66
+ return parseOutputZeroSource(port, sourceText);
67
+ }
68
+ function parseOutputZeroSource(port, sourceText) {
69
+ const zero = parseConstantExpression(sourceText);
70
+ if (zero !== undefined && port.kind === 'c') {
71
+ return zero === 0
72
+ ? { instruction: { mnemonic: 'out', port, source: { kind: 'zero' } } }
73
+ : { error: 'out (c), n immediate form supports n=0 only' };
74
+ }
75
+ return { error: 'out expects a reg8 source' };
76
+ }
77
+ export function parseInterruptModeInstruction(text) {
78
+ const im = /^IM(?:\s+(.*))?$/i.exec(text);
79
+ if (im) {
80
+ const operandText = im[1] ?? '';
81
+ const parts = splitInstructionOperands(operandText);
82
+ if (operandText.trim().length === 0 || parts.length !== 1) {
83
+ return { error: 'im expects one operand' };
84
+ }
85
+ const mode = parseConstantExpression(parts[0] ?? '');
86
+ if (mode !== 0 && mode !== 1 && mode !== 2) {
87
+ return { error: 'im expects 0, 1, or 2' };
88
+ }
89
+ return { instruction: { mnemonic: 'im', mode } };
90
+ }
91
+ return undefined;
92
+ }
93
+ export function parseRstInstruction(text) {
94
+ const rst = /^RST(?:\s+(.*))?$/i.exec(text);
95
+ if (rst) {
96
+ const operandText = rst[1] ?? '';
97
+ const parts = splitInstructionOperands(operandText);
98
+ if (operandText.trim().length === 0 || parts.length !== 1) {
99
+ return { error: 'rst expects one operand' };
100
+ }
101
+ const vector = parseConstantExpression(parts[0] ?? '');
102
+ if (!isRstVector(vector)) {
103
+ return { error: 'rst expects an imm8 multiple of 8 (0..56)' };
104
+ }
105
+ return { instruction: { mnemonic: 'rst', vector } };
106
+ }
107
+ return undefined;
108
+ }
@@ -0,0 +1,2 @@
1
+ import type { ParseZ80InstructionResult } from './parse-instruction.js';
2
+ export declare function parseLdInstruction(text: string): ParseZ80InstructionResult | undefined;
@@ -0,0 +1,83 @@
1
+ import { isSupportedLd, unsupportedLdReason } from './ld-support.js';
2
+ import { splitInstructionOperands } from './operand-split.js';
3
+ import { indexedBracketError, invalidLdOperandDiagnostics, parseLdOperand, } from './parse-operands.js';
4
+ export function parseLdInstruction(text) {
5
+ const ld = /^LD\s+(.+)$/i.exec(text);
6
+ if (ld) {
7
+ return parseLdOperands(ld[1] ?? '');
8
+ }
9
+ return undefined;
10
+ }
11
+ function parseLdOperands(operandText) {
12
+ const parts = splitInstructionOperands(operandText);
13
+ if (parts.length !== 2) {
14
+ return { error: 'ld expects two operands' };
15
+ }
16
+ return parseLdOperandTexts(parts[0] ?? '', parts[1] ?? '');
17
+ }
18
+ function parseLdOperandTexts(leftText, rightText) {
19
+ const indexedBracket = indexedBracketError(leftText) ?? indexedBracketError(rightText);
20
+ if (indexedBracket) {
21
+ return { error: indexedBracket };
22
+ }
23
+ if (/^AF$/i.test(leftText) || /^AF$/i.test(rightText)) {
24
+ return { error: 'ld does not support AF in this form' };
25
+ }
26
+ const operands = parseLdOperandPair(leftText, rightText);
27
+ if ('error' in operands)
28
+ return operands;
29
+ const formError = ldFormError(operands.target, operands.source);
30
+ if (formError)
31
+ return { error: formError };
32
+ const { target, source } = operands;
33
+ return { instruction: { mnemonic: 'ld', target, source } };
34
+ }
35
+ function parseLdOperandPair(leftText, rightText) {
36
+ const target = parseLdOperand(leftText);
37
+ const source = parseLdOperand(rightText);
38
+ if (target && source) {
39
+ return { target, source };
40
+ }
41
+ const operandDiagnostics = [
42
+ ...invalidLdOperandDiagnostics(leftText),
43
+ ...invalidLdOperandDiagnostics(rightText),
44
+ ];
45
+ if (operandDiagnostics.length > 0) {
46
+ return {
47
+ error: operandDiagnostics[operandDiagnostics.length - 1],
48
+ diagnostics: operandDiagnostics,
49
+ };
50
+ }
51
+ return { error: 'ld expects a supported register/memory/immediate transfer form' };
52
+ }
53
+ function ldFormError(target, source) {
54
+ return (ldRegisterIndirectFormError(target, source) ??
55
+ ldHalfIndexFormError(target, source) ??
56
+ unsupportedLdReason(target, source) ??
57
+ unsupportedLdFormError(target, source));
58
+ }
59
+ function ldRegisterIndirectFormError(target, source) {
60
+ if (target.kind === 'reg8' &&
61
+ target.register !== 'a' &&
62
+ source.kind === 'reg-indirect' &&
63
+ source.register !== 'hl') {
64
+ return 'ld r8, (bc/de) supports destination A only';
65
+ }
66
+ if (target.kind === 'reg-indirect' &&
67
+ source.kind === 'reg8' &&
68
+ source.register !== 'a' &&
69
+ target.register !== 'hl') {
70
+ return 'ld (bc/de), r8 supports source A only';
71
+ }
72
+ return undefined;
73
+ }
74
+ function ldHalfIndexFormError(target, source) {
75
+ return target.kind === 'reg-half-index' && source.kind === 'reg-indirect'
76
+ ? `ld ${target.register.toUpperCase()}, source expects (ix+disp)`
77
+ : undefined;
78
+ }
79
+ function unsupportedLdFormError(target, source) {
80
+ return isSupportedLd(target, source)
81
+ ? undefined
82
+ : 'ld expects a supported register/memory/immediate transfer form';
83
+ }
@@ -0,0 +1,41 @@
1
+ import type { Expression } from '../model/expression.js';
2
+ import type { Z80IndexHalfRegister, Z80IndexRegister16, Z80Instruction, Z80Operand, Z80RstVector, Z80StackRegister16 } from './instruction.js';
3
+ export declare function parseLdOperand(text: string): Z80Operand | undefined;
4
+ export declare function invalidLdOperandDiagnostics(text: string): readonly string[];
5
+ export declare function aluImm8RangeError(expression: Expression, mnemonic: string): string | undefined;
6
+ export declare function parseAluOperand(text: string): Z80Operand | undefined;
7
+ export declare function parseCbOperand(text: string): Extract<Z80Operand, {
8
+ readonly kind: 'reg8';
9
+ }> | {
10
+ readonly kind: 'reg-indirect';
11
+ readonly register: 'hl';
12
+ } | Extract<Z80Operand, {
13
+ readonly kind: 'indexed';
14
+ }> | undefined;
15
+ export declare function parseRegister8Operand(text: string): Extract<Z80Operand, {
16
+ readonly kind: 'reg8';
17
+ }> | undefined;
18
+ export declare function parseRegister16Operand(text: string): Extract<Z80Operand, {
19
+ readonly kind: 'reg16';
20
+ }> | undefined;
21
+ export declare function parseIncDecOperand(text: string): Extract<Z80Instruction, {
22
+ readonly mnemonic: 'inc' | 'dec';
23
+ }>['operand'] | undefined;
24
+ export declare function parseIndexRegister16(text: string): Z80IndexRegister16 | undefined;
25
+ export declare function parseIndexHalfRegister(text: string): Z80IndexHalfRegister | undefined;
26
+ export declare function halfIndexFamilyFromRegister(register: Z80IndexHalfRegister): Z80IndexRegister16;
27
+ export declare function parseStackRegister(text: string): Z80StackRegister16 | undefined;
28
+ export declare function parseIndexedOperand(text: string): Extract<Z80Operand, {
29
+ readonly kind: 'indexed';
30
+ }> | undefined;
31
+ export declare function parsePortOperand(text: string): {
32
+ readonly kind: 'c';
33
+ } | {
34
+ readonly kind: 'imm';
35
+ readonly expression: Expression;
36
+ } | undefined;
37
+ export declare function indexedBracketError(text: string): string | undefined;
38
+ export declare function parseConstantExpression(text: string): number | undefined;
39
+ export declare function isRstVector(value: number | undefined): value is Z80RstVector;
40
+ export declare function parseBitIndex(text: string): 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | undefined;
41
+ export declare function constantExpressionValue(expression: Expression): number | undefined;
@@ -0,0 +1,259 @@
1
+ import { applyBinaryOperator } from '../semantics/constant-operators.js';
2
+ import { parseExpression } from '../syntax/parse-expression.js';
3
+ export function parseLdOperand(text) {
4
+ const trimmed = text.trim();
5
+ for (const parser of LD_OPERAND_PARSERS) {
6
+ const operand = parser(trimmed);
7
+ if (operand) {
8
+ return operand;
9
+ }
10
+ }
11
+ return undefined;
12
+ }
13
+ const LD_OPERAND_PARSERS = [
14
+ parseIndexedOperand,
15
+ parseLdRegisterIndirectOperand,
16
+ parseLdAbsoluteMemoryOperand,
17
+ parseRegister8Operand,
18
+ parseLdIndex16Operand,
19
+ parseLdHalfIndexOperand,
20
+ parseRegister16Operand,
21
+ parseLdSpecial8Operand,
22
+ parseLdImmediateOperand,
23
+ ];
24
+ function parseLdRegisterIndirectOperand(trimmed) {
25
+ const match = /^\((BC|DE|HL)\)$/i.exec(trimmed);
26
+ if (!match) {
27
+ return undefined;
28
+ }
29
+ return {
30
+ kind: 'reg-indirect',
31
+ register: (match[1] ?? '').toLowerCase(),
32
+ };
33
+ }
34
+ function parseLdAbsoluteMemoryOperand(trimmed) {
35
+ if (!trimmed.startsWith('(') || !trimmed.endsWith(')')) {
36
+ return undefined;
37
+ }
38
+ const expression = parseExpression(trimmed.slice(1, -1).trim());
39
+ return expression ? { kind: 'mem-abs', expression } : undefined;
40
+ }
41
+ function parseLdIndex16Operand(trimmed) {
42
+ const register = parseIndexRegister16(trimmed);
43
+ return register ? { kind: 'reg-index16', register } : undefined;
44
+ }
45
+ function parseLdHalfIndexOperand(trimmed) {
46
+ const register = parseIndexHalfRegister(trimmed);
47
+ return register ? { kind: 'reg-half-index', register } : undefined;
48
+ }
49
+ function parseLdSpecial8Operand(trimmed) {
50
+ const register = parseSpecialRegister8(trimmed);
51
+ return register ? { kind: 'special8', register } : undefined;
52
+ }
53
+ function parseLdImmediateOperand(trimmed) {
54
+ const expression = parseExpression(trimmed);
55
+ return expression ? { kind: 'imm', expression } : undefined;
56
+ }
57
+ export function invalidLdOperandDiagnostics(text) {
58
+ const trimmed = text.trim();
59
+ if (trimmed === '?') {
60
+ return ['Invalid imm expression: ?', 'Unsupported operand: ?'];
61
+ }
62
+ if (trimmed.startsWith("'") && parseExpression(trimmed) === undefined) {
63
+ return [`Invalid imm expression: ${trimmed}`];
64
+ }
65
+ return [];
66
+ }
67
+ export function aluImm8RangeError(expression, mnemonic) {
68
+ const value = constantExpressionValue(expression);
69
+ if (value === undefined) {
70
+ return undefined;
71
+ }
72
+ if (value < -128 || value > 255) {
73
+ return `${mnemonic} expects imm8`;
74
+ }
75
+ return undefined;
76
+ }
77
+ export function parseAluOperand(text) {
78
+ const trimmed = text.trim();
79
+ const indexed = parseIndexedOperand(trimmed);
80
+ if (indexed) {
81
+ return indexed;
82
+ }
83
+ const memory = /^\(HL\)$/i.exec(trimmed);
84
+ if (memory) {
85
+ return { kind: 'reg-indirect', register: 'hl' };
86
+ }
87
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
88
+ return undefined;
89
+ }
90
+ const register = parseRegister8Operand(trimmed);
91
+ if (register) {
92
+ return register;
93
+ }
94
+ const half = parseIndexHalfRegister(trimmed);
95
+ if (half) {
96
+ return { kind: 'reg-half-index', register: half };
97
+ }
98
+ const expression = parseExpression(trimmed);
99
+ return expression ? { kind: 'imm', expression } : undefined;
100
+ }
101
+ export function parseCbOperand(text) {
102
+ const trimmed = text.trim();
103
+ const indexed = parseIndexedOperand(trimmed);
104
+ if (indexed) {
105
+ return indexed;
106
+ }
107
+ if (/^\(HL\)$/i.test(trimmed)) {
108
+ return { kind: 'reg-indirect', register: 'hl' };
109
+ }
110
+ return parseRegister8Operand(trimmed);
111
+ }
112
+ export function parseRegister8Operand(text) {
113
+ const trimmed = text.trim();
114
+ if (/^(A|B|C|D|E|H|L)$/i.test(trimmed)) {
115
+ return { kind: 'reg8', register: trimmed.toLowerCase() };
116
+ }
117
+ return undefined;
118
+ }
119
+ export function parseRegister16Operand(text) {
120
+ const trimmed = text.trim();
121
+ if (/^(BC|DE|HL|SP)$/i.test(trimmed)) {
122
+ return { kind: 'reg16', register: trimmed.toLowerCase() };
123
+ }
124
+ return undefined;
125
+ }
126
+ export function parseIncDecOperand(text) {
127
+ const trimmed = text.trim();
128
+ const indexed = parseIndexedOperand(trimmed);
129
+ if (indexed) {
130
+ return indexed;
131
+ }
132
+ if (/^\(HL\)$/i.test(trimmed)) {
133
+ return { kind: 'reg-indirect', register: 'hl' };
134
+ }
135
+ const register8 = parseRegister8Operand(trimmed);
136
+ if (register8) {
137
+ return register8;
138
+ }
139
+ const register16 = parseRegister16Operand(trimmed);
140
+ if (register16) {
141
+ return register16;
142
+ }
143
+ const index16 = parseIndexRegister16(trimmed);
144
+ if (index16) {
145
+ return { kind: 'reg16', register: index16 };
146
+ }
147
+ const half = parseIndexHalfRegister(trimmed);
148
+ return half ? { kind: 'reg-half-index', register: half } : undefined;
149
+ }
150
+ export function parseIndexRegister16(text) {
151
+ const trimmed = text.trim();
152
+ return /^(IX|IY)$/i.test(trimmed) ? trimmed.toLowerCase() : undefined;
153
+ }
154
+ export function parseIndexHalfRegister(text) {
155
+ const trimmed = text.trim();
156
+ return /^(IXH|IXL|IYH|IYL)$/i.test(trimmed)
157
+ ? trimmed.toLowerCase()
158
+ : undefined;
159
+ }
160
+ export function halfIndexFamilyFromRegister(register) {
161
+ return register.startsWith('ix') ? 'ix' : 'iy';
162
+ }
163
+ function parseSpecialRegister8(text) {
164
+ const trimmed = text.trim();
165
+ return /^(I|R)$/i.test(trimmed) ? trimmed.toLowerCase() : undefined;
166
+ }
167
+ export function parseStackRegister(text) {
168
+ const trimmed = text.trim();
169
+ return /^(BC|DE|HL|AF|IX|IY)$/i.test(trimmed)
170
+ ? trimmed.toLowerCase()
171
+ : undefined;
172
+ }
173
+ export function parseIndexedOperand(text) {
174
+ const trimmed = text.trim();
175
+ if (!trimmed.startsWith('(') || !trimmed.endsWith(')')) {
176
+ return undefined;
177
+ }
178
+ const inner = trimmed.slice(1, -1).trim();
179
+ const match = /^(IX|IY)(?:\s*([+-])\s*(.+))?$/i.exec(inner);
180
+ if (!match) {
181
+ return undefined;
182
+ }
183
+ const register = (match[1] ?? '').toLowerCase();
184
+ const sign = match[2];
185
+ const displacementText = match[3] ?? '0';
186
+ const parsed = parseExpression(sign === '-' ? `-${displacementText}` : displacementText);
187
+ if (!parsed) {
188
+ return undefined;
189
+ }
190
+ return { kind: 'indexed', register, displacement: parsed };
191
+ }
192
+ export function parsePortOperand(text) {
193
+ const trimmed = text.trim();
194
+ if (/^\(C\)$/i.test(trimmed)) {
195
+ return { kind: 'c' };
196
+ }
197
+ if (!trimmed.startsWith('(') || !trimmed.endsWith(')')) {
198
+ return undefined;
199
+ }
200
+ const expression = parseExpression(trimmed.slice(1, -1).trim());
201
+ return expression ? { kind: 'imm', expression } : undefined;
202
+ }
203
+ export function indexedBracketError(text) {
204
+ const match = /^\(?\s*((IX|IY)\s*\[\s*.+?\s*\])\s*\)?$/i.exec(text.trim());
205
+ return match
206
+ ? `Indexed memory operands use (ix+disp)/(iy+disp), not ${match[1]?.toLowerCase().replace(/\s+/g, '')}.`
207
+ : undefined;
208
+ }
209
+ export function parseConstantExpression(text) {
210
+ const expression = parseExpression(text);
211
+ return expression ? constantExpressionValue(expression) : undefined;
212
+ }
213
+ export function isRstVector(value) {
214
+ return RST_VECTORS.has(value);
215
+ }
216
+ const RST_VECTORS = new Set([0, 8, 16, 24, 32, 40, 48, 56]);
217
+ export function parseBitIndex(text) {
218
+ const value = parseConstantExpression(text);
219
+ return isBitIndex(value) ? value : undefined;
220
+ }
221
+ function isBitIndex(value) {
222
+ return BIT_INDEXES.has(value);
223
+ }
224
+ const BIT_INDEXES = new Set([0, 1, 2, 3, 4, 5, 6, 7]);
225
+ export function constantExpressionValue(expression) {
226
+ switch (expression.kind) {
227
+ case 'number':
228
+ return expression.value;
229
+ case 'unary':
230
+ return constantUnaryExpressionValue(expression);
231
+ case 'binary':
232
+ return constantBinaryExpressionValue(expression);
233
+ case 'symbol':
234
+ case 'current-location':
235
+ return undefined;
236
+ }
237
+ }
238
+ function constantUnaryExpressionValue(expression) {
239
+ const value = constantExpressionValue(expression.expression);
240
+ if (value === undefined) {
241
+ return undefined;
242
+ }
243
+ switch (expression.operator) {
244
+ case '+':
245
+ return value;
246
+ case '-':
247
+ return -value;
248
+ case '~':
249
+ return ~value;
250
+ }
251
+ }
252
+ function constantBinaryExpressionValue(expression) {
253
+ const left = constantExpressionValue(expression.left);
254
+ const right = constantExpressionValue(expression.right);
255
+ if (left === undefined || right === undefined) {
256
+ return undefined;
257
+ }
258
+ return applyBinaryOperator(expression.operator, left, right);
259
+ }
@@ -10,7 +10,7 @@ azm [options] <entry.asm|entry.z80>
10
10
  ```
11
11
 
12
12
  The entry file must be the final argument. AZM source uses `.asm` or `.z80`.
13
- Register-care interface files use `.asmi`; they are loaded with `--interface`
13
+ Register contracts interface files use `.asmi`; they are loaded with `--interface`
14
14
  and are not compile entries.
15
15
 
16
16
  ## Basic Use
@@ -51,46 +51,46 @@ azm --source-root . --output build/program.hex src/program.asm
51
51
  By default AZM writes the primary output plus useful side artifacts using the
52
52
  same base path.
53
53
 
54
- | Artifact | Meaning |
55
- | -------------- | -------------------------------- |
56
- | `.hex` | Intel HEX output |
57
- | `.bin` | flat binary output |
58
- | `.d8.json` | Debug80 map |
59
- | `.z80` | lowered assembler source |
60
- | `.regcare.txt` | register-care report |
61
- | `.asmi` | inferred register-care interface |
54
+ | Artifact | Meaning |
55
+ | ------------------- | -------------------------------------- |
56
+ | `.hex` | Intel HEX output |
57
+ | `.bin` | flat binary output |
58
+ | `.d8.json` | Debug80 map |
59
+ | `.z80` | lowered assembler source |
60
+ | `.regcontracts.txt` | opt-in register contracts debug report |
61
+ | `.asmi` | inferred register contracts interface |
62
62
 
63
63
  Disable standard artifacts when they are not needed:
64
64
 
65
65
  ```sh
66
66
  azm --nod8m program.asm
67
- azm --nobin --nohex --reg-report --rc audit program.asm
67
+ azm --nobin --nohex --nod8m --rc audit program.asm
68
68
  ```
69
69
 
70
70
  ## Options
71
71
 
72
- | Option | Meaning |
73
- | --------------------------------------------- | -------------------------------------------------------------------- |
74
- | `-o, --output <file>` | Primary output path. The extension must match `--type`. |
75
- | `-t, --type <hex\|bin>` | Primary output type. Default: `hex`. |
76
- | `--nobin` | Do not write `.bin`. |
77
- | `--nohex` | Do not write `.hex`. |
78
- | `--nod8m` | Do not write `.d8.json`. |
79
- | `--asm80` | Write lowered assembler source as `.z80`. |
80
- | `--source-root <dir>` | Emit project-relative source paths in `.d8.json`. |
72
+ | Option | Meaning |
73
+ | --------------------------------------------- | ------------------------------------------------------------------------------ |
74
+ | `-o, --output <file>` | Primary output path. The extension must match `--type`. |
75
+ | `-t, --type <hex\|bin>` | Primary output type. Default: `hex`. |
76
+ | `--nobin` | Do not write `.bin`. |
77
+ | `--nohex` | Do not write `.hex`. |
78
+ | `--nod8m` | Do not write `.d8.json`. |
79
+ | `--asm80` | Write lowered assembler source as `.z80`. |
80
+ | `--source-root <dir>` | Emit project-relative source paths in `.d8.json`. |
81
81
  | `--case-style <mode>` | Lint mnemonic/register/op-head case: `off`, `upper`, `lower`, or `consistent`. |
82
- | `--rc, --register-care <mode>` | Register-care mode: `off`, `audit`, `warn`, `error`, or `strict`. |
83
- | `--reg-report, --emit-register-report` | Write `.regcare.txt`. |
84
- | `--reg-interface, --emit-register-interface` | Write inferred `.asmi` interface. |
85
- | `--contracts, --annotate-register-contracts` | Update AZMDoc contract comments in source. |
86
- | `--fix` | Apply conservative register-care source fixes and update contracts. |
87
- | `--accept-out <routine:carrier>` | Promote an inferred output candidate while annotating. |
88
- | `--interface <file>` | Load external register-care contracts from `.asmi`. Repeatable. |
89
- | `--reg-profile, --register-profile <profile>` | Register-care profile. Currently `mon3`. |
90
- | `--aliases <file>` | Load project directive alias JSON. Repeatable. |
91
- | `-I, --include <dir>` | Add an include search path. Repeatable. |
92
- | `-V, --version` | Print package version. |
93
- | `-h, --help` | Print CLI help. |
82
+ | `--rc, --register-contracts <mode>` | Register contracts mode: `off`, `audit`, `warn`, `error`, or `strict`. |
83
+ | `--reg-report, --emit-register-report` | Opt-in debug report: write `.regcontracts.txt`. |
84
+ | `--reg-interface, --emit-register-interface` | Write inferred `.asmi` interface. |
85
+ | `--contracts, --annotate-register-contracts` | Update AZMDoc contract comments in source. |
86
+ | `--fix` | Apply conservative register contract source fixes and update contracts. |
87
+ | `--accept-out <routine:carrier>` | Promote an inferred output candidate while annotating. |
88
+ | `--interface <file>` | Load external register contracts from `.asmi`. Repeatable. |
89
+ | `--reg-profile, --register-profile <profile>` | Register contracts profile. Currently `mon3`. |
90
+ | `--aliases <file>` | Load project directive alias JSON. Repeatable. |
91
+ | `-I, --include <dir>` | Add an include search path. Repeatable. |
92
+ | `-V, --version` | Print package version. |
93
+ | `-h, --help` | Print CLI help. |
94
94
 
95
95
  ## Debug80 Maps
96
96
 
@@ -104,12 +104,12 @@ keys and generator input paths are written relative to that directory with `/`
104
104
  separators. Constants are emitted as `value` metadata without fake addresses;
105
105
  labels and addressable data carry `address`.
106
106
 
107
- ## Register-Care Examples
107
+ ## Register Contracts Examples
108
108
 
109
109
  Audit inferred contracts without failing the build:
110
110
 
111
111
  ```sh
112
- azm --rc audit --reg-report program.asm
112
+ azm --rc audit program.asm
113
113
  ```
114
114
 
115
115
  Treat contract conflicts as build failures:
@@ -118,6 +118,13 @@ Treat contract conflicts as build failures:
118
118
  azm --rc error program.asm
119
119
  ```
120
120
 
121
+ Treat every register contracts issue as a build failure, including unknown call
122
+ boundaries and stack effects AZM cannot prove within an `@` routine boundary:
123
+
124
+ ```sh
125
+ azm --rc strict program.asm
126
+ ```
127
+
121
128
  Generate compact AZMDoc blocks in source:
122
129
 
123
130
  ```sh
@@ -132,7 +139,7 @@ azm --rc error --interface mon3.asmi program.asm
132
139
 
133
140
  ## `.asmi` Interfaces
134
141
 
135
- External register-care interfaces are plain metadata files:
142
+ External register contract interfaces are plain metadata files:
136
143
 
137
144
  ```text
138
145
  extern MON_PRINT_CHAR
@@ -145,7 +152,7 @@ Use `.asmi` for these files so they are clearly distinct from assembler source.
145
152
 
146
153
  ## Exit Behavior
147
154
 
148
- The CLI exits with a non-zero status when parsing, semantic checks, register-care
155
+ The CLI exits with a non-zero status when parsing, semantic checks, register contracts
149
156
  mode, lowering, or artifact writing reports an error. Diagnostics are printed
150
157
  with file, line, column, severity, and diagnostic ID where that information is
151
158
  available.