@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,3 @@
1
+ import type { Z80Operand } from './instruction.js';
2
+ export declare function isSupportedLd(target: Z80Operand, source: Z80Operand): boolean;
3
+ export declare function unsupportedLdReason(target: Z80Operand, source: Z80Operand): string | undefined;
@@ -0,0 +1,146 @@
1
+ const LD_SUPPORT_PREDICATES = [
2
+ isSupportedSpecialRegisterLd,
3
+ isSupportedHalfIndexLd,
4
+ isReg8Ld,
5
+ isIndexedByteLd,
6
+ isReg16ImmediateLd,
7
+ isLegacyReg16Ld,
8
+ isIndex16ImmediateLd,
9
+ isStackPointerRegisterLd,
10
+ isRegisterFromAbsoluteMemoryLd,
11
+ isAbsoluteMemoryFromRegisterLd,
12
+ isAccumulatorFromRegisterIndirectLd,
13
+ isRegisterIndirectFromAccumulatorLd,
14
+ isHlIndirectByteLd,
15
+ isByteFromHlIndirectLd,
16
+ ];
17
+ export function isSupportedLd(target, source) {
18
+ return LD_SUPPORT_PREDICATES.some((predicate) => predicate(target, source));
19
+ }
20
+ export function unsupportedLdReason(target, source) {
21
+ if (isMemoryOperand(target) && isMemoryOperand(source)) {
22
+ return 'ld does not support memory-to-memory transfers';
23
+ }
24
+ const halfIndexReason = unsupportedHalfIndexLdReason(target, source);
25
+ if (halfIndexReason) {
26
+ return halfIndexReason;
27
+ }
28
+ return unsupportedRegisterPairLdReason(target, source);
29
+ }
30
+ function isReg8Ld(target, source) {
31
+ return target.kind === 'reg8' && (source.kind === 'reg8' || source.kind === 'imm');
32
+ }
33
+ function isIndexedByteLd(target, source) {
34
+ return ((target.kind === 'reg8' && source.kind === 'indexed') ||
35
+ (target.kind === 'indexed' && (source.kind === 'reg8' || source.kind === 'imm')));
36
+ }
37
+ function isReg16ImmediateLd(target, source) {
38
+ return target.kind === 'reg16' && source.kind === 'imm';
39
+ }
40
+ function isLegacyReg16Ld(target, source) {
41
+ return (target.kind === 'reg16' &&
42
+ source.kind === 'reg16' &&
43
+ isLegacyReg16ByteTransferPair(target.register, source.register));
44
+ }
45
+ function isIndex16ImmediateLd(target, source) {
46
+ return target.kind === 'reg-index16' && source.kind === 'imm';
47
+ }
48
+ function isStackPointerRegisterLd(target, source) {
49
+ return (target.kind === 'reg16' &&
50
+ target.register === 'sp' &&
51
+ (source.kind === 'reg16' || source.kind === 'reg-index16') &&
52
+ (source.register === 'hl' || source.register === 'ix' || source.register === 'iy'));
53
+ }
54
+ function isRegisterFromAbsoluteMemoryLd(target, source) {
55
+ return ((target.kind === 'reg8' || target.kind === 'reg16' || target.kind === 'reg-index16') &&
56
+ source.kind === 'mem-abs' &&
57
+ (target.kind !== 'reg8' || target.register === 'a'));
58
+ }
59
+ function isAbsoluteMemoryFromRegisterLd(target, source) {
60
+ return (target.kind === 'mem-abs' &&
61
+ (source.kind === 'reg16' ||
62
+ source.kind === 'reg-index16' ||
63
+ (source.kind === 'reg8' && source.register === 'a')));
64
+ }
65
+ function isAccumulatorFromRegisterIndirectLd(target, source) {
66
+ return target.kind === 'reg8' && target.register === 'a' && source.kind === 'reg-indirect';
67
+ }
68
+ function isRegisterIndirectFromAccumulatorLd(target, source) {
69
+ return target.kind === 'reg-indirect' && source.kind === 'reg8' && source.register === 'a';
70
+ }
71
+ function isHlIndirectByteLd(target, source) {
72
+ return target.kind === 'reg-indirect' && target.register === 'hl' && (source.kind === 'reg8' || source.kind === 'imm');
73
+ }
74
+ function isByteFromHlIndirectLd(target, source) {
75
+ return target.kind === 'reg8' && source.kind === 'reg-indirect' && source.register === 'hl';
76
+ }
77
+ function isLegacyReg16ByteTransferPair(target, source) {
78
+ return (target === 'hl' && source === 'de') || (target === 'bc' && source === 'de');
79
+ }
80
+ function isSupportedSpecialRegisterLd(target, source) {
81
+ return ((target.kind === 'special8' && source.kind === 'reg8' && source.register === 'a') ||
82
+ (target.kind === 'reg8' && target.register === 'a' && source.kind === 'special8'));
83
+ }
84
+ function isMemoryOperand(operand) {
85
+ return operand.kind === 'reg-indirect' || operand.kind === 'indexed' || operand.kind === 'mem-abs';
86
+ }
87
+ function isSupportedHalfIndexLd(target, source) {
88
+ return (hasHalfIndexRegister(target, source) &&
89
+ isSameIndexHalfFamily(target, source) &&
90
+ !usesPlainHlCounterpart(target, source) &&
91
+ isHalfIndexCompatibleByteOperand(target) &&
92
+ isHalfIndexCompatibleByteOperand(source));
93
+ }
94
+ function unsupportedHalfIndexLdReason(target, source) {
95
+ if (!hasHalfIndexRegister(target, source)) {
96
+ return undefined;
97
+ }
98
+ if (!isSameIndexHalfFamily(target, source)) {
99
+ return 'ld between IX* and IY* byte registers is not supported';
100
+ }
101
+ return usesPlainHlCounterpart(target, source)
102
+ ? 'ld with IX*/IY* does not support plain H/L counterpart operands'
103
+ : undefined;
104
+ }
105
+ function unsupportedRegisterPairLdReason(target, source) {
106
+ if (isUnsupportedIndexPairLd(target, source)) {
107
+ return 'ld rr, rr supports SP <- HL/IX/IY only';
108
+ }
109
+ if (!isRegisterPairLdReasonCandidate(target, source)) {
110
+ return undefined;
111
+ }
112
+ return isSupportedRegisterPairLd(target, source)
113
+ ? undefined
114
+ : 'ld rr, rr supports SP <- HL/IX/IY only';
115
+ }
116
+ function isUnsupportedIndexPairLd(target, source) {
117
+ return target.kind === 'reg-index16' && source.kind === 'reg-index16' && target.register !== source.register;
118
+ }
119
+ function isRegisterPairLdReasonCandidate(target, source) {
120
+ return target.kind === 'reg16' && source.kind !== 'imm' && (source.kind === 'reg16' || source.kind === 'reg-index16');
121
+ }
122
+ function isSupportedRegisterPairLd(target, source) {
123
+ return isStackPointerRegisterLd(target, source) || isLegacyReg16Ld(target, source);
124
+ }
125
+ function hasHalfIndexRegister(target, source) {
126
+ return target.kind === 'reg-half-index' || source.kind === 'reg-half-index';
127
+ }
128
+ function isSameIndexHalfFamily(target, source) {
129
+ const targetFamily = indexHalfFamily(target);
130
+ const sourceFamily = indexHalfFamily(source);
131
+ return !targetFamily || !sourceFamily || targetFamily === sourceFamily;
132
+ }
133
+ function indexHalfFamily(operand) {
134
+ return operand.kind === 'reg-half-index' ? (operand.register.startsWith('ix') ? 'ix' : 'iy') : undefined;
135
+ }
136
+ function usesPlainHlCounterpart(target, source) {
137
+ return ((target.kind === 'reg-half-index' && isPlainHlReg8(source)) ||
138
+ (source.kind === 'reg-half-index' && isPlainHlReg8(target)));
139
+ }
140
+ function isPlainHlReg8(operand) {
141
+ return operand.kind === 'reg8' && (operand.register === 'h' || operand.register === 'l');
142
+ }
143
+ function isHalfIndexCompatibleByteOperand(operand) {
144
+ return (operand.kind === 'reg-half-index' ||
145
+ (operand.kind === 'reg8' && operand.register !== 'h' && operand.register !== 'l'));
146
+ }
@@ -0,0 +1,8 @@
1
+ export interface OperandSplitState {
2
+ depth: number;
3
+ quote: string | undefined;
4
+ escaped: boolean;
5
+ start: number;
6
+ }
7
+ export declare function createOperandSplitState(): OperandSplitState;
8
+ export declare function scanInstructionOperandSeparator(text: string, index: number, state: OperandSplitState): boolean;
@@ -0,0 +1,46 @@
1
+ export function createOperandSplitState() {
2
+ return { depth: 0, quote: undefined, escaped: false, start: 0 };
3
+ }
4
+ export function scanInstructionOperandSeparator(text, index, state) {
5
+ const char = text[index];
6
+ if (consumeEscapedOperandChar(state))
7
+ return false;
8
+ if (beginOperandEscape(char, state))
9
+ return false;
10
+ if (toggleOperandQuote(text, index, char, state))
11
+ return false;
12
+ if (state.quote)
13
+ return false;
14
+ updateOperandParenDepth(char, state);
15
+ return char === ',' && state.depth === 0;
16
+ }
17
+ function consumeEscapedOperandChar(state) {
18
+ if (!state.escaped)
19
+ return false;
20
+ state.escaped = false;
21
+ return true;
22
+ }
23
+ function beginOperandEscape(char, state) {
24
+ if (char !== '\\' || !state.quote)
25
+ return false;
26
+ state.escaped = true;
27
+ return true;
28
+ }
29
+ function toggleOperandQuote(text, index, char, state) {
30
+ if (!isOperandQuoteStart(text, index, char, state.quote))
31
+ return false;
32
+ state.quote = state.quote === char ? undefined : (state.quote ?? char);
33
+ return true;
34
+ }
35
+ function isOperandQuoteStart(text, index, char, quote) {
36
+ return ((char === '"' || char === "'") &&
37
+ !(char === "'" && quote === undefined && /[A-Za-z0-9_]/.test(text[index - 1] ?? '')));
38
+ }
39
+ function updateOperandParenDepth(char, state) {
40
+ if (char === '(') {
41
+ state.depth += 1;
42
+ }
43
+ else if (char === ')') {
44
+ state.depth -= 1;
45
+ }
46
+ }
@@ -0,0 +1 @@
1
+ export declare function splitInstructionOperands(text: string): string[];
@@ -0,0 +1,13 @@
1
+ import { createOperandSplitState, scanInstructionOperandSeparator } from './operand-split-state.js';
2
+ export function splitInstructionOperands(text) {
3
+ const values = [];
4
+ const state = createOperandSplitState();
5
+ for (let index = 0; index < text.length; index += 1) {
6
+ if (scanInstructionOperandSeparator(text, index, state)) {
7
+ values.push(text.slice(state.start, index));
8
+ state.start = index + 1;
9
+ }
10
+ }
11
+ values.push(text.slice(state.start));
12
+ return values.map((value) => value.trim());
13
+ }
@@ -0,0 +1,4 @@
1
+ import type { ParseZ80InstructionResult } from './parse-instruction.js';
2
+ export declare function parseNopInstruction(text: string): ParseZ80InstructionResult | undefined;
3
+ export declare function parseRetInstruction(text: string): ParseZ80InstructionResult | undefined;
4
+ export declare function parseNoOperandCoreInstruction(text: string): ParseZ80InstructionResult | undefined;
@@ -0,0 +1,39 @@
1
+ import { splitInstructionOperands } from './operand-split.js';
2
+ import { parseCondition } from './parse-conditions.js';
3
+ export function parseNopInstruction(text) {
4
+ const nop = /^NOP(?:\s+(.*))?$/i.exec(text);
5
+ if (nop) {
6
+ return nop[1] === undefined
7
+ ? { instruction: { mnemonic: 'nop' } }
8
+ : { error: 'nop expects no operands' };
9
+ }
10
+ return undefined;
11
+ }
12
+ export function parseRetInstruction(text) {
13
+ const ret = /^RET(?:\s+(.*))?$/i.exec(text);
14
+ if (ret) {
15
+ const operandText = ret[1] ?? '';
16
+ if (operandText.trim().length === 0) {
17
+ return { instruction: { mnemonic: 'ret' } };
18
+ }
19
+ const parts = splitInstructionOperands(operandText);
20
+ if (parts.length !== 1) {
21
+ return { error: 'ret expects no operands or one condition code' };
22
+ }
23
+ const condition = parseCondition(parts[0] ?? '');
24
+ return condition
25
+ ? { instruction: { mnemonic: 'ret-cc', condition } }
26
+ : { error: 'ret cc expects a valid condition code' };
27
+ }
28
+ return undefined;
29
+ }
30
+ export function parseNoOperandCoreInstruction(text) {
31
+ const noOperandCore = /^(DI|EI|SCF|CCF|CPL|DAA|EXX|HALT|RLCA|RRCA|RLA|RRA|NEG|RRD|RLD|LDI|LDIR|LDD|LDDR|CPI|CPIR|CPD|CPDR|INI|INIR|IND|INDR|OUTI|OTIR|OUTD|OTDR|RETI|RETN)(?:\s+(.*))?$/i.exec(text);
32
+ if (noOperandCore) {
33
+ const mnemonic = (noOperandCore[1] ?? '').toLowerCase();
34
+ return noOperandCore[2] === undefined
35
+ ? { instruction: { mnemonic } }
36
+ : { error: `${mnemonic} expects no operands` };
37
+ }
38
+ return undefined;
39
+ }
@@ -0,0 +1,4 @@
1
+ import type { ParseZ80InstructionResult } from './parse-instruction.js';
2
+ export declare function parseJumpInstruction(text: string): ParseZ80InstructionResult | undefined;
3
+ export declare function parseCallInstruction(text: string): ParseZ80InstructionResult | undefined;
4
+ export declare function parseRelativeBranchInstruction(text: string): ParseZ80InstructionResult | undefined;
@@ -0,0 +1,218 @@
1
+ import { parseExpression } from '../syntax/parse-expression.js';
2
+ import { splitInstructionOperands } from './operand-split.js';
3
+ import { parseCondition, parseRelativeCondition } from './parse-conditions.js';
4
+ export function parseJumpInstruction(text) {
5
+ const jump = /^JP(?:\s+(.*))?$/i.exec(text);
6
+ if (jump) {
7
+ const operandText = jump[1] ?? '';
8
+ const parts = splitInstructionOperands(operandText);
9
+ if (operandText.trim().length === 0) {
10
+ return {
11
+ error: 'jp expects one operand (nn/(hl)/(ix)/(iy)) or two operands (cc, nn)',
12
+ };
13
+ }
14
+ if (parts.length === 2)
15
+ return parseConditionalJump(parts[0] ?? '', parts[1] ?? '');
16
+ if (parts.length !== 1) {
17
+ return {
18
+ error: 'jp expects one operand (nn/(hl)/(ix)/(iy)) or two operands (cc, nn)',
19
+ };
20
+ }
21
+ return parseSingleJump(parts[0] ?? '');
22
+ }
23
+ return undefined;
24
+ }
25
+ function parseConditionalJump(conditionText, targetText) {
26
+ const condition = parseCondition(conditionText);
27
+ if (!condition) {
28
+ return { error: 'jp cc expects valid condition code NZ/Z/NC/C/PO/PE/P/M' };
29
+ }
30
+ if (/^\(.*\)$/.test(targetText.trim())) {
31
+ return { error: 'jp cc, nn does not support indirect targets' };
32
+ }
33
+ const expression = parseAbsoluteBranchTarget(targetText);
34
+ return expression
35
+ ? { instruction: { mnemonic: 'jp-cc', condition, expression } }
36
+ : { error: 'jp cc, nn expects imm16' };
37
+ }
38
+ function parseSingleJump(targetText) {
39
+ const condition = parseCondition(targetText);
40
+ if (condition) {
41
+ return { error: 'jp cc, nn expects two operands (cc, nn)' };
42
+ }
43
+ const indirect = parseJumpIndirect(targetText);
44
+ if (indirect) {
45
+ return { instruction: { mnemonic: 'jp-indirect', register: indirect } };
46
+ }
47
+ const targetError = singleJumpTargetError(targetText);
48
+ if (targetError) {
49
+ return { error: targetError };
50
+ }
51
+ const expression = parseExpression(targetText);
52
+ return expression
53
+ ? { instruction: { mnemonic: 'jp', expression } }
54
+ : { error: `invalid JP target: ${targetText}` };
55
+ }
56
+ function singleJumpTargetError(targetText) {
57
+ const trimmed = targetText.trim();
58
+ if (/^\(.*\)$/.test(trimmed)) {
59
+ return 'jp indirect form supports (hl), (ix), or (iy) only';
60
+ }
61
+ if (/^(HL|IX|IY)$/i.test(trimmed)) {
62
+ return 'jp indirect form requires parentheses; use (hl), (ix), or (iy)';
63
+ }
64
+ return isRegisterName(targetText) ? 'jp does not support register targets; use imm16' : undefined;
65
+ }
66
+ export function parseCallInstruction(text) {
67
+ const call = /^CALL(?:\s+(.*))?$/i.exec(text);
68
+ if (call) {
69
+ const operandText = call[1] ?? '';
70
+ const parts = splitInstructionOperands(operandText);
71
+ if (operandText.trim().length === 0) {
72
+ return { error: 'call expects one operand (nn) or two operands (cc, nn)' };
73
+ }
74
+ if (parts.length === 2)
75
+ return parseConditionalCall(parts[0] ?? '', parts[1] ?? '');
76
+ if (parts.length !== 1) {
77
+ return { error: 'call expects one operand (nn) or two operands (cc, nn)' };
78
+ }
79
+ return parseSingleCall(parts[0] ?? '');
80
+ }
81
+ return undefined;
82
+ }
83
+ function parseConditionalCall(conditionText, targetText) {
84
+ const condition = parseCondition(conditionText);
85
+ if (!condition) {
86
+ return { error: 'call cc expects valid condition code NZ/Z/NC/C/PO/PE/P/M' };
87
+ }
88
+ if (/^\(.*\)$/.test(targetText.trim())) {
89
+ return { error: 'call cc, nn does not support indirect targets' };
90
+ }
91
+ const expression = parseAbsoluteBranchTarget(targetText);
92
+ return expression
93
+ ? { instruction: { mnemonic: 'call-cc', condition, expression } }
94
+ : { error: 'call cc, nn expects imm16' };
95
+ }
96
+ function parseSingleCall(targetText) {
97
+ const condition = parseCondition(targetText);
98
+ if (condition) {
99
+ return { error: 'call cc, nn expects two operands (cc, nn)' };
100
+ }
101
+ if (/^\(.*\)$/.test(targetText.trim())) {
102
+ return { error: 'call does not support indirect targets; use imm16' };
103
+ }
104
+ if (isRegisterName(targetText)) {
105
+ return { error: 'call does not support register targets; use imm16' };
106
+ }
107
+ const expression = parseExpression(targetText);
108
+ return expression
109
+ ? { instruction: { mnemonic: 'call', expression } }
110
+ : { error: `invalid CALL target: ${targetText}` };
111
+ }
112
+ export function parseRelativeBranchInstruction(text) {
113
+ const relativeBranch = /^(JR|DJNZ)(?:\s+(.*))?$/i.exec(text);
114
+ if (relativeBranch) {
115
+ const mnemonic = (relativeBranch[1] ?? '').toLowerCase();
116
+ return parseRelativeBranchOperands(mnemonic, relativeBranch[2] ?? '');
117
+ }
118
+ return undefined;
119
+ }
120
+ function parseRelativeBranchOperands(mnemonic, operandText) {
121
+ const trimmed = operandText.trim();
122
+ if (trimmed.length === 0)
123
+ return { error: relativeBranchArityError(mnemonic) };
124
+ const parts = splitInstructionOperands(trimmed);
125
+ return mnemonic === 'djnz' ? parseDjnzBranch(parts) : parseJrBranch(parts);
126
+ }
127
+ function parseJrBranch(parts) {
128
+ if (parts.length === 1) {
129
+ return parseUnconditionalRelativeJump(parts[0] ?? '');
130
+ }
131
+ if (parts.length === 2) {
132
+ return parseConditionalRelativeJump(parts[0] ?? '', parts[1] ?? '');
133
+ }
134
+ return { error: 'jr expects one operand (disp8) or two operands (cc, disp8)' };
135
+ }
136
+ function relativeBranchArityError(mnemonic) {
137
+ return mnemonic === 'djnz'
138
+ ? 'djnz expects one operand (disp8)'
139
+ : 'jr expects one operand (disp8) or two operands (cc, disp8)';
140
+ }
141
+ function parseDjnzBranch(parts) {
142
+ if (parts.length !== 1) {
143
+ return { error: 'djnz expects one operand (disp8)' };
144
+ }
145
+ const targetText = parts[0] ?? '';
146
+ const targetError = relativeDispTargetError(targetText, {
147
+ indirect: 'djnz does not support indirect targets; expects disp8',
148
+ register: 'djnz does not support register targets; expects disp8',
149
+ });
150
+ if (targetError) {
151
+ return { error: targetError };
152
+ }
153
+ const expression = parseExpression(targetText);
154
+ return expression
155
+ ? { instruction: { mnemonic: 'djnz', expression } }
156
+ : { error: 'djnz expects disp8' };
157
+ }
158
+ function parseUnconditionalRelativeJump(targetText) {
159
+ const targetError = relativeDispTargetError(targetText, {
160
+ indirect: 'jr does not support indirect targets; expects disp8',
161
+ register: 'jr does not support register targets; expects disp8',
162
+ });
163
+ if (targetError) {
164
+ return { error: targetError };
165
+ }
166
+ if (parseRelativeCondition(targetText)) {
167
+ return { error: 'jr cc, disp expects two operands (cc, disp8)' };
168
+ }
169
+ const expression = parseExpression(targetText);
170
+ return expression
171
+ ? { instruction: { mnemonic: 'jr', expression } }
172
+ : { error: 'jr expects disp8' };
173
+ }
174
+ function parseConditionalRelativeJump(conditionText, targetText) {
175
+ const condition = parseRelativeCondition(conditionText);
176
+ if (!condition) {
177
+ return { error: 'jr cc expects valid condition code NZ/Z/NC/C' };
178
+ }
179
+ const targetError = relativeDispTargetError(targetText, {
180
+ indirect: 'jr cc, disp does not support indirect targets',
181
+ register: 'jr cc, disp does not support register targets; expects disp8',
182
+ });
183
+ if (targetError) {
184
+ return { error: targetError };
185
+ }
186
+ const expression = parseExpression(targetText);
187
+ return expression
188
+ ? { instruction: { mnemonic: 'jr-cc', condition, expression } }
189
+ : { error: 'jr cc, disp expects disp8' };
190
+ }
191
+ function parseAbsoluteBranchTarget(text) {
192
+ const trimmed = text.trim();
193
+ if (/^\(.*\)$/.test(trimmed) || isRegisterName(trimmed)) {
194
+ return undefined;
195
+ }
196
+ return parseExpression(trimmed);
197
+ }
198
+ function parseJumpIndirect(text) {
199
+ const indirect = /^\((HL|IX|IY)\)$/i.exec(text.trim());
200
+ return indirect ? (indirect[1] ?? '').toLowerCase() : undefined;
201
+ }
202
+ function isRegisterName(text) {
203
+ return /^(A|B|C|D|E|H|L|I|R|AF|BC|DE|HL|SP|IX|IY|IXH|IXL|IYH|IYL)$/i.test(text.trim());
204
+ }
205
+ function relativeDispTargetError(text, messages) {
206
+ const trimmed = text.trim();
207
+ if (/^\(.*\)$/.test(trimmed)) {
208
+ return messages.indirect;
209
+ }
210
+ if (isRegisterName(trimmed)) {
211
+ return messages.register;
212
+ }
213
+ const expression = parseExpression(trimmed);
214
+ if (expression?.kind === 'symbol' && isRegisterName(expression.name)) {
215
+ return messages.register;
216
+ }
217
+ return undefined;
218
+ }
@@ -0,0 +1,6 @@
1
+ import type { Z80Instruction, Z80RelativeCondition } from './instruction.js';
2
+ export type Z80InstructionCondition = Extract<Z80Instruction, {
3
+ readonly condition: unknown;
4
+ }>['condition'];
5
+ export declare function parseCondition(text: string): Z80InstructionCondition | undefined;
6
+ export declare function parseRelativeCondition(text: string): Z80RelativeCondition | undefined;
@@ -0,0 +1,10 @@
1
+ export function parseCondition(text) {
2
+ const trimmed = text.trim();
3
+ return /^(NZ|Z|NC|C|PO|PE|P|M)$/i.test(trimmed)
4
+ ? trimmed.toLowerCase()
5
+ : undefined;
6
+ }
7
+ export function parseRelativeCondition(text) {
8
+ const trimmed = text.trim().toLowerCase();
9
+ return /^(nz|z|nc|c)$/.test(trimmed) ? trimmed : undefined;
10
+ }
@@ -0,0 +1,2 @@
1
+ import type { ParseZ80InstructionResult } from './parse-instruction.js';
2
+ export declare function parseExchangeInstruction(text: string): ParseZ80InstructionResult | undefined;
@@ -0,0 +1,30 @@
1
+ import { splitInstructionOperands } from './operand-split.js';
2
+ const EXCHANGE_FORMS = {
3
+ "af|af'": 'af-af',
4
+ "af'|af": 'af-af',
5
+ 'de|hl': 'de-hl',
6
+ '(sp)|hl': 'sp-hl',
7
+ '(sp)|ix': 'sp-ix',
8
+ 'ix|(sp)': 'sp-ix',
9
+ '(sp)|iy': 'sp-iy',
10
+ 'iy|(sp)': 'sp-iy',
11
+ };
12
+ export function parseExchangeInstruction(text) {
13
+ const exchange = /^EX\s+(.+)$/i.exec(text);
14
+ if (!exchange)
15
+ return undefined;
16
+ const operandText = exchange[1] ?? '';
17
+ const parts = splitInstructionOperands(operandText);
18
+ if (parts.length !== 2) {
19
+ return { error: 'ex expects two operands' };
20
+ }
21
+ const left = (parts[0] ?? '').toLowerCase();
22
+ const right = (parts[1] ?? '').toLowerCase();
23
+ const form = EXCHANGE_FORMS[`${left}|${right}`];
24
+ if (form) {
25
+ return { instruction: { mnemonic: 'ex', form } };
26
+ }
27
+ return {
28
+ error: `ex supports "AF, AF'", "DE, HL", "(SP), HL", "(SP), IX", and "(SP), IY" only`,
29
+ };
30
+ }