@jhlagado/azm 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/README.md +239 -76
  2. package/dist/src/api-artifacts.d.ts +20 -0
  3. package/dist/src/api-artifacts.js +165 -0
  4. package/dist/src/api-compile.d.ts +8 -2
  5. package/dist/src/api-compile.js +55 -227
  6. package/dist/src/api-register-contracts.d.ts +9 -0
  7. package/dist/src/api-register-contracts.js +77 -0
  8. package/dist/src/api-tooling.d.ts +2 -2
  9. package/dist/src/api-tooling.js +1 -1
  10. package/dist/src/assembly/address-planning.d.ts +1 -2
  11. package/dist/src/assembly/address-planning.js +119 -218
  12. package/dist/src/assembly/address-symbols.d.ts +12 -0
  13. package/dist/src/assembly/address-symbols.js +118 -0
  14. package/dist/src/assembly/assemble-program.js +5 -0
  15. package/dist/src/assembly/fixup-emission.js +30 -48
  16. package/dist/src/assembly/import-visibility.d.ts +3 -0
  17. package/dist/src/assembly/import-visibility.js +204 -0
  18. package/dist/src/assembly/program-emission.js +163 -164
  19. package/dist/src/cli/artifact-files.d.ts +15 -0
  20. package/dist/src/cli/artifact-files.js +86 -0
  21. package/dist/src/cli/parse-args.d.ts +6 -5
  22. package/dist/src/cli/parse-args.js +162 -136
  23. package/dist/src/cli/run.js +4 -1
  24. package/dist/src/cli/usage.d.ts +1 -0
  25. package/dist/src/cli/usage.js +33 -0
  26. package/dist/src/cli/write-artifacts.js +18 -91
  27. package/dist/src/core/compile.js +51 -274
  28. package/dist/src/core/conditional-assembly.d.ts +6 -0
  29. package/dist/src/core/conditional-assembly.js +181 -0
  30. package/dist/src/expansion/op-constant-expression.d.ts +3 -0
  31. package/dist/src/expansion/op-constant-expression.js +52 -0
  32. package/dist/src/expansion/op-expand-selected.d.ts +5 -0
  33. package/dist/src/expansion/op-expand-selected.js +143 -0
  34. package/dist/src/expansion/op-expansion.d.ts +5 -53
  35. package/dist/src/expansion/op-expansion.js +85 -815
  36. package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
  37. package/dist/src/expansion/op-instruction-instantiation.js +194 -0
  38. package/dist/src/expansion/op-local-labels.d.ts +8 -0
  39. package/dist/src/expansion/op-local-labels.js +166 -0
  40. package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
  41. package/dist/src/expansion/op-operand-splitting.js +44 -0
  42. package/dist/src/expansion/op-operands.d.ts +53 -0
  43. package/dist/src/expansion/op-operands.js +66 -0
  44. package/dist/src/expansion/op-selection.d.ts +18 -0
  45. package/dist/src/expansion/op-selection.js +172 -0
  46. package/dist/src/index.d.ts +2 -1
  47. package/dist/src/index.js +1 -1
  48. package/dist/src/model/diagnostic.d.ts +4 -0
  49. package/dist/src/model/diagnostic.js +4 -0
  50. package/dist/src/node/source-host.js +40 -13
  51. package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
  52. package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
  53. package/dist/src/outputs/asm80-expressions.d.ts +5 -0
  54. package/dist/src/outputs/asm80-expressions.js +47 -0
  55. package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
  56. package/dist/src/outputs/asm80-instruction-operands.js +38 -0
  57. package/dist/src/outputs/asm80-instructions.d.ts +5 -0
  58. package/dist/src/outputs/asm80-instructions.js +272 -0
  59. package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
  60. package/dist/src/outputs/asm80-ld-operands.js +157 -0
  61. package/dist/src/outputs/asm80-strings.d.ts +4 -0
  62. package/dist/src/outputs/asm80-strings.js +14 -0
  63. package/dist/src/outputs/d8-files.d.ts +10 -0
  64. package/dist/src/outputs/d8-files.js +103 -0
  65. package/dist/src/outputs/d8-helpers.d.ts +21 -0
  66. package/dist/src/outputs/d8-helpers.js +136 -0
  67. package/dist/src/outputs/hex.js +26 -18
  68. package/dist/src/outputs/types.d.ts +16 -10
  69. package/dist/src/outputs/write-asm80.js +72 -597
  70. package/dist/src/outputs/write-d8.js +6 -216
  71. package/dist/src/register-contracts/accept-output.d.ts +2 -0
  72. package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
  73. package/dist/src/register-contracts/analyze-helpers.js +162 -0
  74. package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
  75. package/dist/src/register-contracts/analyze.js +73 -0
  76. package/dist/src/register-contracts/annotate.d.ts +11 -0
  77. package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
  78. package/dist/src/register-contracts/annotations.d.ts +8 -0
  79. package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
  80. package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
  81. package/dist/src/register-contracts/boundaryHints.js +24 -0
  82. package/dist/src/register-contracts/carriers.d.ts +2 -0
  83. package/dist/src/register-contracts/constants.d.ts +4 -0
  84. package/dist/src/register-contracts/constants.js +51 -0
  85. package/dist/src/register-contracts/controlFlow.d.ts +5 -0
  86. package/dist/src/register-contracts/controlFlow.js +55 -0
  87. package/dist/src/register-contracts/fix.d.ts +11 -0
  88. package/dist/src/{register-care → register-contracts}/fix.js +47 -30
  89. package/dist/src/register-contracts/instruction-head.d.ts +2 -0
  90. package/dist/src/register-contracts/instruction-head.js +3 -0
  91. package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
  92. package/dist/src/register-contracts/instruction-operands.js +101 -0
  93. package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
  94. package/dist/src/register-contracts/instruction-predicates.js +44 -0
  95. package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
  96. package/dist/src/register-contracts/interfaceContracts.js +68 -0
  97. package/dist/src/register-contracts/liveness.d.ts +3 -0
  98. package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
  99. package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
  100. package/dist/src/register-contracts/operand-register-name.js +13 -0
  101. package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
  102. package/dist/src/{register-care → register-contracts}/profiles.js +2 -2
  103. package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
  104. package/dist/src/register-contracts/programModel-boundaries.js +64 -0
  105. package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
  106. package/dist/src/register-contracts/programModel-routines.js +144 -0
  107. package/dist/src/register-contracts/programModel.d.ts +3 -0
  108. package/dist/src/register-contracts/programModel.js +14 -0
  109. package/dist/src/register-contracts/report.d.ts +5 -0
  110. package/dist/src/{register-care → register-contracts}/report.js +34 -17
  111. package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
  112. package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
  113. package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
  114. package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
  115. package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
  116. package/dist/src/register-contracts/smartCommentParsing.js +80 -0
  117. package/dist/src/register-contracts/smartComments.d.ts +5 -0
  118. package/dist/src/register-contracts/smartComments.js +92 -0
  119. package/dist/src/register-contracts/summaries.d.ts +12 -0
  120. package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
  121. package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
  122. package/dist/src/register-contracts/summary-boundary.js +40 -0
  123. package/dist/src/register-contracts/summary-contract.d.ts +2 -0
  124. package/dist/src/register-contracts/summary-contract.js +45 -0
  125. package/dist/src/register-contracts/summary-result.d.ts +7 -0
  126. package/dist/src/register-contracts/summary-result.js +122 -0
  127. package/dist/src/register-contracts/summary-state.d.ts +23 -0
  128. package/dist/src/register-contracts/summary-state.js +88 -0
  129. package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
  130. package/dist/src/register-contracts/summary-token-transfer.js +67 -0
  131. package/dist/src/register-contracts/summary.d.ts +3 -0
  132. package/dist/src/register-contracts/summary.js +266 -0
  133. package/dist/src/register-contracts/tooling.d.ts +57 -0
  134. package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
  135. package/dist/src/register-contracts/types.d.ts +188 -0
  136. package/dist/src/semantics/binary-operators.d.ts +2 -0
  137. package/dist/src/semantics/binary-operators.js +15 -0
  138. package/dist/src/semantics/byte-functions.d.ts +2 -0
  139. package/dist/src/semantics/byte-functions.js +7 -0
  140. package/dist/src/semantics/constant-operator-types.d.ts +10 -0
  141. package/dist/src/semantics/constant-operator-types.js +1 -0
  142. package/dist/src/semantics/constant-operators.d.ts +3 -0
  143. package/dist/src/semantics/constant-operators.js +3 -0
  144. package/dist/src/semantics/diagnostics.d.ts +3 -0
  145. package/dist/src/semantics/diagnostics.js +10 -0
  146. package/dist/src/semantics/expression-evaluation.d.ts +11 -19
  147. package/dist/src/semantics/expression-evaluation.js +22 -334
  148. package/dist/src/semantics/layout-evaluation.d.ts +23 -0
  149. package/dist/src/semantics/layout-evaluation.js +202 -0
  150. package/dist/src/semantics/layout-format.d.ts +5 -0
  151. package/dist/src/semantics/layout-format.js +31 -0
  152. package/dist/src/semantics/layout-path.d.ts +24 -0
  153. package/dist/src/semantics/layout-path.js +58 -0
  154. package/dist/src/semantics/unary-operators.d.ts +2 -0
  155. package/dist/src/semantics/unary-operators.js +8 -0
  156. package/dist/src/source/line-comment-scanner.d.ts +1 -0
  157. package/dist/src/source/line-comment-scanner.js +51 -0
  158. package/dist/src/source/logical-lines.d.ts +3 -0
  159. package/dist/src/source/source-span.d.ts +2 -0
  160. package/dist/src/source/strip-line-comment.js +8 -44
  161. package/dist/src/syntax/directive-aliases.js +36 -22
  162. package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
  163. package/dist/src/syntax/expression-tokenizer.js +310 -0
  164. package/dist/src/syntax/parse-directive-statement.d.ts +9 -0
  165. package/dist/src/syntax/parse-directive-statement.js +309 -0
  166. package/dist/src/syntax/parse-expression.d.ts +2 -2
  167. package/dist/src/syntax/parse-expression.js +7 -568
  168. package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
  169. package/dist/src/syntax/parse-layout-declarations.js +189 -0
  170. package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
  171. package/dist/src/syntax/parse-layout-expression.js +175 -0
  172. package/dist/src/syntax/parse-line.js +21 -273
  173. package/dist/src/syntax/parse-token-expression.d.ts +3 -0
  174. package/dist/src/syntax/parse-token-expression.js +133 -0
  175. package/dist/src/tooling/api.js +1 -1
  176. package/dist/src/tooling/case-style.js +47 -30
  177. package/dist/src/z80/effect-groups.d.ts +38 -0
  178. package/dist/src/z80/effect-groups.js +265 -0
  179. package/dist/src/z80/effect-units.d.ts +18 -0
  180. package/dist/src/z80/effect-units.js +165 -0
  181. package/dist/src/z80/effects.d.ts +1 -1
  182. package/dist/src/z80/effects.js +94 -557
  183. package/dist/src/z80/encode-core.d.ts +2 -0
  184. package/dist/src/z80/encode-core.js +42 -0
  185. package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
  186. package/dist/src/z80/encode-ld-helpers.js +172 -0
  187. package/dist/src/z80/encode-ld.d.ts +2 -0
  188. package/dist/src/z80/encode-ld.js +285 -0
  189. package/dist/src/z80/encode.js +190 -542
  190. package/dist/src/z80/ld-support.d.ts +3 -0
  191. package/dist/src/z80/ld-support.js +146 -0
  192. package/dist/src/z80/operand-split-state.d.ts +8 -0
  193. package/dist/src/z80/operand-split-state.js +46 -0
  194. package/dist/src/z80/operand-split.d.ts +1 -0
  195. package/dist/src/z80/operand-split.js +13 -0
  196. package/dist/src/z80/parse-basic.d.ts +4 -0
  197. package/dist/src/z80/parse-basic.js +39 -0
  198. package/dist/src/z80/parse-branch.d.ts +4 -0
  199. package/dist/src/z80/parse-branch.js +218 -0
  200. package/dist/src/z80/parse-conditions.d.ts +6 -0
  201. package/dist/src/z80/parse-conditions.js +10 -0
  202. package/dist/src/z80/parse-exchange.d.ts +2 -0
  203. package/dist/src/z80/parse-exchange.js +30 -0
  204. package/dist/src/z80/parse-instruction.js +224 -1010
  205. package/dist/src/z80/parse-io-control.d.ts +5 -0
  206. package/dist/src/z80/parse-io-control.js +108 -0
  207. package/dist/src/z80/parse-ld.d.ts +2 -0
  208. package/dist/src/z80/parse-ld.js +83 -0
  209. package/dist/src/z80/parse-operands.d.ts +41 -0
  210. package/dist/src/z80/parse-operands.js +259 -0
  211. package/docs/codebase/01-orientation-and-repository-layout.md +192 -0
  212. package/docs/codebase/02-source-loading-and-parsing.md +263 -0
  213. package/docs/codebase/03-assembly-and-z80-emission.md +251 -0
  214. package/docs/codebase/04-ops-and-register-contracts.md +237 -0
  215. package/docs/codebase/05-interfaces-and-output-artifacts.md +253 -0
  216. package/docs/codebase/06-verification-and-maintenance.md +202 -0
  217. package/docs/codebase/appendices/a-directory-file-reference.md +253 -0
  218. package/docs/codebase/appendices/b-compile-flow-reference.md +103 -0
  219. package/docs/codebase/appendices/c-public-surface-reference.md +106 -0
  220. package/docs/codebase/appendices/index.md +16 -0
  221. package/docs/codebase/index.md +46 -0
  222. package/package.json +2 -3
  223. package/dist/src/register-care/accept-output.d.ts +0 -2
  224. package/dist/src/register-care/analyze.js +0 -166
  225. package/dist/src/register-care/annotate.d.ts +0 -11
  226. package/dist/src/register-care/annotations.d.ts +0 -8
  227. package/dist/src/register-care/boundaryHints.d.ts +0 -3
  228. package/dist/src/register-care/boundaryHints.js +0 -80
  229. package/dist/src/register-care/carriers.d.ts +0 -2
  230. package/dist/src/register-care/controlFlow.d.ts +0 -5
  231. package/dist/src/register-care/controlFlow.js +0 -38
  232. package/dist/src/register-care/fix.d.ts +0 -11
  233. package/dist/src/register-care/instruction-shape.d.ts +0 -11
  234. package/dist/src/register-care/instruction-shape.js +0 -129
  235. package/dist/src/register-care/liveness.d.ts +0 -3
  236. package/dist/src/register-care/programModel.d.ts +0 -3
  237. package/dist/src/register-care/programModel.js +0 -266
  238. package/dist/src/register-care/report.d.ts +0 -5
  239. package/dist/src/register-care/routine-summaries.d.ts +0 -6
  240. package/dist/src/register-care/smartComments.d.ts +0 -5
  241. package/dist/src/register-care/smartComments.js +0 -243
  242. package/dist/src/register-care/summaries.d.ts +0 -12
  243. package/dist/src/register-care/summary.d.ts +0 -3
  244. package/dist/src/register-care/summary.js +0 -474
  245. package/dist/src/register-care/tooling.d.ts +0 -43
  246. package/dist/src/register-care/types.d.ts +0 -172
  247. package/docs/reference/cli.md +0 -151
  248. package/docs/reference/tooling-api.md +0 -316
  249. /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
  250. /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
  251. /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
  252. /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
  253. /package/dist/src/{register-care → register-contracts}/types.js +0 -0
@@ -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
+ }