@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
package/README.md CHANGED
@@ -4,10 +4,12 @@ AZM is the Z80 assembler used by the Debug80 toolchain. It assembles `.asm`
4
4
  and `.z80` source into Intel HEX, flat binary and Debug80 map artifacts for
5
5
  hardware, emulators and Debug80.
6
6
 
7
- This README is the condensed manual. The full AZM book is on the Debug80
8
- documentation site:
7
+ This README is the condensed manual. The Debug80 website contains the detailed
8
+ AZM manual and broader Debug80 documentation:
9
9
 
10
- [AZM Assembler Manual](https://jhlagado.github.io/debug80-docs/azm-book/book4/)
10
+ - [Debug80 documentation](https://debug80.com/)
11
+ - [AZM Book 0 — Assembler Manual](https://debug80.com/azm-book/book0/)
12
+ - [AZM Book 4](https://jhlagado.github.io/debug80-docs/azm-book/book4/)
11
13
 
12
14
  ## Install
13
15
 
@@ -43,13 +45,13 @@ azm start.asm
43
45
  ```
44
46
 
45
47
  `.org` means origin. It sets the assembly address for the bytes that follow.
46
- `@Start:` is an address label and also a public routine entry for register-care
48
+ `@Start:` is an address label and also a public routine entry for register contracts
47
49
  analysis. The Z80 instructions assemble at `$0100`.
48
50
 
49
51
  ## Source Style
50
52
 
51
53
  AZM source is built from labels, declarations, directives, Z80 instructions,
52
- data definitions, layout declarations, register-care comments and optional
54
+ data definitions, layout declarations, register contract comments and optional
53
55
  inline `op` definitions.
54
56
 
55
57
  Canonical AZM directives are lowercase and dotted:
@@ -89,16 +91,102 @@ Colour .enum Red, Green, Blue
89
91
  SpriteArray .typealias Sprite[16]
90
92
  ```
91
93
 
92
- Constants often use upper case with underscores. Labels and routine names are
93
- clearer in PascalCase or camelCase:
94
+ ### Style Guide
95
+
96
+ AZM is stricter than many older Z80 assemblers. It accepts compatibility aliases
97
+ so existing source can be assembled, but source that you control should use the
98
+ canonical syntax below. These rules are intended to make AZM source predictable
99
+ for people and coding agents.
100
+
101
+ Use lowercase dotted directives, not legacy aliases:
94
102
 
95
103
  ```asm
96
- SCREEN_WIDTH .equ 32
104
+ .org 0x4000
105
+ .include "../shared/constants.asm"
97
106
 
98
- DrawSprite:
99
- ret
107
+ MOVE_PERIOD .equ 128
108
+
109
+ Message:
110
+ .db "READY",0
111
+
112
+ StatePtr:
113
+ .dw 0
114
+
115
+ Buffer:
116
+ .ds 32
117
+ ```
118
+
119
+ Use `@Name:` for callable routine entries. The `@` marks a register contracts
120
+ routine boundary; call sites still write the symbol name without `@`:
121
+
122
+ ```asm
123
+ ;! in A
124
+ ;! out A
125
+ ;! clobbers BC
126
+ @MxMask:
127
+ LD C,A
128
+ OR A
129
+ LD A,0x80
130
+ JR Z,MxMaskDone
131
+ MxMaskLp:
132
+ SRL A
133
+ DJNZ MxMaskLp
134
+ MxMaskDone:
135
+ RET
136
+ ```
137
+
138
+ Use plain labels for data and internal branch targets. Plain labels are global,
139
+ so give branch labels names that stay unique across the whole include tree.
140
+ Prefer descriptive labels such as `MxMaskLoop`, `MxMaskDone`, `SpawnFailed` and
141
+ `HeldDirRateSet` rather than generic names such as `Loop` or `Done`.
142
+
143
+ Use uppercase with underscores for constants, following the usual C-style
144
+ convention. Group related constants with clear prefixes:
145
+
146
+ ```asm
147
+ PORT_DIGITS .equ 0x01
148
+ PORT_LCD_DATA .equ 0x84
149
+
150
+ API_SCAN_KEYS .equ 16
151
+ KEY_LEFT .equ 0x11
152
+ KEY_RIGHT .equ 0x10
153
+
154
+ COLOR_RED .equ 0x01
155
+ COLOR_GREEN .equ 0x02
156
+ COLOR_YELLOW .equ COLOR_RED + COLOR_GREEN
157
+ ```
158
+
159
+ Use PascalCase for routine entries, branch labels, data labels, type names and
160
+ enum names. Use lower camel case for fields and enum members when that makes
161
+ layout expressions easier to read. The assembler enforces uniqueness, not style,
162
+ but symbols are case-sensitive: `ColorRed`, `COLOR_RED` and `colorRed` are
163
+ different names.
164
+
165
+ Put declaration names on the left and do not use a colon for constants, enums,
166
+ types or type aliases:
167
+
168
+ ```asm
169
+ MOVE_PERIOD .equ 128
170
+ Colour .enum Red, Green, Blue
171
+ SpriteArray .typealias Sprite[16]
172
+
173
+ Sprite .type
174
+ x .field byte
175
+ y .field byte
176
+ tile .field byte
177
+ flags .field byte
178
+ .endtype
100
179
  ```
101
180
 
181
+ Use a colon only for address labels. `COUNT .equ 8` declares a constant;
182
+ `Count:` declares an address label. Do not write `COUNT: .equ 8` in canonical
183
+ AZM.
184
+
185
+ Use indentation to make columns easy to scan. Put labels at the left margin,
186
+ indent instructions and standalone directives, and align operands enough to keep
187
+ dense assembly readable. Exact tab width is less important than keeping one
188
+ source file internally consistent.
189
+
102
190
  ## Literals
103
191
 
104
192
  AZM accepts the usual Z80 numeric forms:
@@ -247,12 +335,74 @@ azm -I include -I vendor program.asm
247
335
  Included source contributes labels, constants, enums, types, ops and routines to
248
336
  the same assembly.
249
337
 
250
- ## Register Care
338
+ ## Imports
251
339
 
252
- Register care checks whether subroutines preserve the register values that their
340
+ `.import` is AZM's module-style source composition directive:
341
+
342
+ ```asm
343
+ ; main.asm
344
+ .import "keyboard.asm"
345
+
346
+ @Start:
347
+ call ReadKey
348
+ ret
349
+ ```
350
+
351
+ ```asm
352
+ ; keyboard.asm
353
+ @ReadKey:
354
+ call ScanMatrix
355
+ ret
356
+
357
+ ScanMatrix:
358
+ xor a
359
+ ret
360
+ ```
361
+
362
+ Imported source assembles at the point where `.import` appears, so native
363
+ `.bin`, `.hex` and `.d8.json` output contain the imported bytes as part of the
364
+ same program. Paths resolve like includes: relative to the importing file first,
365
+ then through `-I` include directories.
366
+
367
+ The difference is visibility. In an imported file, labels written with `@` are
368
+ public exports. Code outside `keyboard.asm` can call `ReadKey`, using the name
369
+ without `@`. Plain labels in an imported file, such as `ScanMatrix`, are private
370
+ to that imported file or import unit. The imported file may call its own private
371
+ helpers, but outside references fail with a direct visibility diagnostic.
372
+
373
+ `.include` remains textual. Included text belongs to the including source unit
374
+ and is intended for shared constants, declarations and compatibility source.
375
+ Use `.import` when a source file should behave like a module with public `@`
376
+ entry points and private implementation labels.
377
+
378
+ Repeated imports of the same resolved file are idempotent: the first import
379
+ loads and emits the module, later imports of that same file are skipped.
380
+ Repeated includes are still textual and repeat every time. Recursive includes or
381
+ imports are rejected with source diagnostics.
382
+
383
+ Register contracts use the same `@` routine boundaries across imported files.
384
+ Imported public routines are analyzed as internal routines under `--rc strict`,
385
+ and private helpers called inside an imported public routine are summarized as
386
+ part of the same program analysis.
387
+
388
+ ASM80-compatible lowered `.z80` output does not yet support imported source
389
+ units. If `--asm80` is requested for a program that uses `.import`, AZM reports
390
+ an explicit `AZMN_ASM80` diagnostic instead of silently flattening the module
391
+ boundary. Use native `.bin`, `.hex` and `.d8.json` output for imported programs.
392
+
393
+ ## Register Contracts
394
+
395
+ Register contracts check whether subroutines preserve the register values that their
253
396
  callers still need. It is designed to catch register collisions, a common source
254
397
  of assembly bugs.
255
398
 
399
+ The benefit is practical: AZM can stop a plausible-looking routine at compile
400
+ time when it reads a register after calling code that may clobber it. In larger
401
+ Z80 projects this encourages smaller routines, clearer `@` boundaries, explicit
402
+ helper outputs, and proof or test harnesses that stay honest under
403
+ `--rc strict`. The friction is intentional: strict contracts make hidden
404
+ register and stack assumptions visible before they become debugger sessions.
405
+
256
406
  Routine entry labels start with `@`:
257
407
 
258
408
  ```asm
@@ -272,7 +422,7 @@ name:
272
422
  call CheckTile
273
423
  ```
274
424
 
275
- AZMDoc register-care comments use `;!` and may record inputs, outputs,
425
+ AZMDoc register contract comments use `;!` and may record inputs, outputs,
276
426
  clobbered registers and preserved registers. `clobbers B` means the routine may
277
427
  change `B`. `preserves B` means the value that enters in `B` is still present
278
428
  when the routine returns.
@@ -280,13 +430,24 @@ when the routine returns.
280
430
  Run the analysis with:
281
431
 
282
432
  ```sh
283
- azm --rc audit --reg-report program.asm
433
+ azm --rc audit program.asm
434
+ azm --rc warn program.asm
284
435
  azm --rc error --interface monitor.asmi program.asm
436
+ azm --rc strict program.asm
285
437
  ```
286
438
 
287
- The main modes are `audit`, `warn`, `error` and `strict`. AZM can also emit
288
- register-care reports and `.asmi` interface files for externally assembled
289
- routines.
439
+ The main modes are `audit`, `warn`, `error` and `strict`. Use `audit` for a
440
+ non-blocking check, `warn` for visible diagnostics with a successful compile,
441
+ `error` to fail on proven caller/callee register conflicts, and `strict` to fail
442
+ on any register contracts issue AZM cannot prove safe, including unknown call
443
+ boundaries and unbalanced or unknown stack effects.
444
+
445
+ The normal register contracts interface is compiler diagnostics plus source
446
+ contracts. Use `--contracts` or `--fix` when you want AZM to update compact
447
+ AZMDoc contract comments in source. Use `.asmi` files for externally assembled
448
+ routines or monitor/system APIs. Text report files are available with
449
+ `--reg-report`, but they are an explicit debug/export option and are not part of
450
+ the normal workflow.
290
451
 
291
452
  ## Ops and Aliases
292
453
 
@@ -316,7 +477,7 @@ azm [options] <entry.asm|entry.z80>
316
477
  ```
317
478
 
318
479
  The entry file is the final argument. Source entries use `.asm` or `.z80`.
319
- External register-care interfaces use `.asmi` and are loaded with
480
+ External register contract interfaces use `.asmi` and are loaded with
320
481
  `--interface`.
321
482
 
322
483
  Basic use writes the default artifact set next to the source file:
@@ -364,55 +525,57 @@ Generate ASM80-compatible lowered source:
364
525
  azm --asm80 program.asm
365
526
  ```
366
527
 
367
- Run register-care analysis:
528
+ Run register contracts analysis:
368
529
 
369
530
  ```sh
370
- azm --rc audit --reg-report program.asm
531
+ azm --rc audit program.asm
532
+ azm --rc warn program.asm
371
533
  azm --rc error --interface monitor.asmi program.asm
534
+ azm --rc strict program.asm
372
535
  azm --contracts --rc audit program.asm
373
536
  ```
374
537
 
375
538
  The main switches are:
376
539
 
377
- | Option | Meaning |
378
- | --------------------------------------------- | ------------------------------------------------------------- |
379
- | `-o, --output <file>` | Primary output path. The extension matches `--type`. |
380
- | `-t, --type <hex\|bin>` | Primary output type. Default: `hex`. |
381
- | `--nobin` | Skip `.bin` output. |
382
- | `--nohex` | Skip `.hex` output. |
383
- | `--nod8m` | Skip `.d8.json` output. |
384
- | `--asm80` | Write lowered assembler source as `.z80`. |
385
- | `--source-root <dir>` | Emit project-relative source paths in `.d8.json`. |
386
- | `--case-style <mode>` | Lint mnemonic, register and op-head case style. |
387
- | `--rc, --register-care <mode>` | Register-care mode: `off`, `audit`, `warn`, `error`, `strict`. |
388
- | `--reg-report, --emit-register-report` | Write `.regcare.txt`. |
389
- | `--reg-interface, --emit-register-interface` | Write inferred `.asmi` interface metadata. |
390
- | `--contracts, --annotate-register-contracts` | Update AZMDoc contract comments in source. |
391
- | `--fix` | Apply conservative register-care source fixes. |
392
- | `--accept-out <routine:carrier>` | Promote an inferred output candidate while annotating. |
393
- | `--interface <file>` | Load external register-care contracts from `.asmi`. |
394
- | `--reg-profile, --register-profile <profile>` | Register-care profile. Currently `mon3`. |
395
- | `--aliases <file>` | Load project directive alias JSON. |
396
- | `-I, --include <dir>` | Add an include search path. |
397
- | `-V, --version` | Print package version. |
398
- | `-h, --help` | Print CLI help. |
399
-
400
- See [docs/reference/cli.md](docs/reference/cli.md) for the complete option
401
- reference.
540
+ | Option | Meaning |
541
+ | --------------------------------------------- | ------------------------------------------------------------------- |
542
+ | `-o, --output <file>` | Primary output path. The extension matches `--type`. |
543
+ | `-t, --type <hex\|bin>` | Primary output type. Default: `hex`. |
544
+ | `--nobin` | Skip `.bin` output. |
545
+ | `--nohex` | Skip `.hex` output. |
546
+ | `--nod8m` | Skip `.d8.json` output. |
547
+ | `--asm80` | Write lowered assembler source as `.z80`. |
548
+ | `--source-root <dir>` | Emit project-relative source paths in `.d8.json`. |
549
+ | `--case-style <mode>` | Lint mnemonic, register and op-head case style. |
550
+ | `--rc, --register-contracts <mode>` | Register contracts mode: `off`, `audit`, `warn`, `error`, `strict`. |
551
+ | `--reg-report, --emit-register-report` | Opt-in debug report: write `.regcontracts.txt`. |
552
+ | `--reg-interface, --emit-register-interface` | Write inferred `.asmi` interface metadata. |
553
+ | `--contracts, --annotate-register-contracts` | Update AZMDoc contract comments in source. |
554
+ | `--fix` | Apply conservative register contract source fixes. |
555
+ | `--accept-out <routine:carrier>` | Promote an inferred output candidate while annotating. |
556
+ | `--interface <file>` | Load external register contracts from `.asmi`. |
557
+ | `--reg-profile, --register-profile <profile>` | Register contracts profile. Currently `mon3`. |
558
+ | `--aliases <file>` | Load project directive alias JSON. |
559
+ | `-I, --include <dir>` | Add an include search path. |
560
+ | `-V, --version` | Print package version. |
561
+ | `-h, --help` | Print CLI help. |
562
+
563
+ See the [AZM Engineering Manual](docs/codebase/) for the maintained codebase,
564
+ CLI and package-interface reference.
402
565
 
403
566
  ## Output Artifacts
404
567
 
405
568
  By default, AZM writes the requested primary output plus useful side artifacts
406
569
  using the same base path.
407
570
 
408
- | Extension | Contents |
409
- | -------------- | --------------------------------------------- |
410
- | `.hex` | Intel HEX |
411
- | `.bin` | flat binary |
412
- | `.d8.json` | Debug80 map |
413
- | `.z80` | ASM80-compatible lowered source when enabled |
414
- | `.regcare.txt` | register-care report when enabled |
415
- | `.asmi` | register-care interface when enabled |
571
+ | Extension | Contents |
572
+ | ------------------- | -------------------------------------------- |
573
+ | `.hex` | Intel HEX |
574
+ | `.bin` | flat binary |
575
+ | `.d8.json` | Debug80 map |
576
+ | `.z80` | ASM80-compatible lowered source when enabled |
577
+ | `.regcontracts.txt` | opt-in register contracts debug report |
578
+ | `.asmi` | register contracts interface when enabled |
416
579
 
417
580
  ## Programmatic API
418
581
 
@@ -430,13 +593,13 @@ npm install @jhlagado/azm
430
593
  ```
431
594
 
432
595
  Use `@jhlagado/azm/tooling` when an editor, linter or debugger integration
433
- needs parsing, diagnostics, symbols, semantic checks or register-care facts in
596
+ needs parsing, diagnostics, symbols, semantic checks or register contract facts in
434
597
  memory:
435
598
 
436
599
  ```ts
437
600
  import {
438
601
  analyzeProgram,
439
- analyzeRegisterCareForTools,
602
+ analyzeRegisterContractsForTools,
440
603
  loadProgram,
441
604
  } from '@jhlagado/azm/tooling';
442
605
 
@@ -451,13 +614,13 @@ if (loaded.loadedProgram) {
451
614
  requireMain: false,
452
615
  });
453
616
 
454
- const registerCare = analyzeRegisterCareForTools(loaded.loadedProgram, {
617
+ const registerContracts = analyzeRegisterContractsForTools(loaded.loadedProgram, {
455
618
  mode: 'audit',
456
- registerCareProfile: 'mon3',
619
+ registerContractsProfile: 'mon3',
457
620
  });
458
621
 
459
622
  console.log(analysis.diagnostics);
460
- console.log(registerCare.candidateDiagnostics);
623
+ console.log(registerContracts.candidateDiagnostics);
461
624
  }
462
625
  ```
463
626
 
@@ -483,8 +646,8 @@ const result = await compile(
483
646
  hex: '/abs/path/to/project/build/main.hex',
484
647
  bin: '/abs/path/to/project/build/main.bin',
485
648
  },
486
- registerCare: 'audit',
487
- registerCareInterfaces: ['/abs/path/to/monitor.asmi'],
649
+ registerContracts: 'audit',
650
+ registerContractsInterfaces: ['/abs/path/to/monitor.asmi'],
488
651
  },
489
652
  { formats: defaultFormatWriters },
490
653
  );
@@ -501,24 +664,24 @@ write those artifacts to disk.
501
664
 
502
665
  Common programmatic options include:
503
666
 
504
- | Option | Use |
505
- | ---------------------------- | --------------------------------------------------------- |
506
- | `includeDirs` | Include search paths, like repeated `-I`. |
507
- | `directiveAliasFiles` | Project directive alias JSON files. |
508
- | `sourceRoot` | Stable project-relative paths in Debug80 maps. |
509
- | `d8mInputs` | Intended artifact paths recorded in Debug80 map metadata. |
510
- | `outputType` | Primary output type, `hex` or `bin`. |
511
- | `emitBin`, `emitHex`, `emitD8m` | Select in-memory artifact kinds. |
512
- | `emitAsm80` | Request lowered `.z80` artifact. |
513
- | `registerCare` | Register-care mode. |
514
- | `registerCareInterfaces` | External `.asmi` contract files. |
667
+ | Option | Use |
668
+ | ------------------------------- | --------------------------------------------------------- |
669
+ | `includeDirs` | Include search paths, like repeated `-I`. |
670
+ | `directiveAliasFiles` | Project directive alias JSON files. |
671
+ | `sourceRoot` | Stable project-relative paths in Debug80 maps. |
672
+ | `d8mInputs` | Intended artifact paths recorded in Debug80 map metadata. |
673
+ | `outputType` | Primary output type, `hex` or `bin`. |
674
+ | `emitBin`, `emitHex`, `emitD8m` | Select in-memory artifact kinds. |
675
+ | `emitAsm80` | Request lowered `.z80` artifact. |
676
+ | `registerContracts` | Register contracts mode. |
677
+ | `registerContractsInterfaces` | External `.asmi` contract files. |
515
678
 
516
679
  Public tooling types include `Diagnostic`, `LoadedProgram`,
517
- `AnalyzeProgramResult`, `LoadProgramResult`, `RegisterCareCandidateDiagnostic`
680
+ `AnalyzeProgramResult`, `LoadProgramResult`, `RegisterContractsCandidateDiagnostic`
518
681
  and the Debug80 map artifact types `D8mArtifact`, `D8mJson` and `D8mSymbol`.
519
682
 
520
- See [docs/reference/tooling-api.md](docs/reference/tooling-api.md) for current
521
- API notes.
683
+ See the [public surface reference](docs/codebase/appendices/c-public-surface-reference.md)
684
+ for current API notes.
522
685
 
523
686
  ## Development
524
687
 
@@ -533,8 +696,8 @@ npm run test:azm:corpus
533
696
  npm test
534
697
  ```
535
698
 
536
- The live source map is maintained in
537
- [docs/reference/source-overview.md](docs/reference/source-overview.md).
699
+ The live source map is maintained in the
700
+ [AZM Engineering Manual](docs/codebase/).
538
701
 
539
702
  ## License
540
703
 
@@ -0,0 +1,20 @@
1
+ import type { Diagnostic } from './model/diagnostic.js';
2
+ import type { SourceItem } from './model/source-item.js';
3
+ import type { Artifact, EmittedByteMap, FormatWriters } from './outputs/types.js';
4
+ import type { CompileNextFunctionOptions } from './api-compile.js';
5
+ interface EmitAssemblyArtifactsOptions {
6
+ readonly entryFile: string;
7
+ readonly options: CompileNextFunctionOptions;
8
+ readonly formats: FormatWriters;
9
+ readonly program: readonly SourceItem[];
10
+ readonly bytes: Uint8Array;
11
+ readonly origin: number;
12
+ readonly sourceSegments: EmittedByteMap['sourceSegments'];
13
+ readonly initializedAddresses: readonly number[];
14
+ readonly symbols: Readonly<Record<string, number>>;
15
+ }
16
+ export declare function emitAssemblyArtifacts(input: EmitAssemblyArtifactsOptions): Promise<{
17
+ readonly artifacts: readonly Artifact[];
18
+ readonly diagnostics: readonly Diagnostic[];
19
+ }>;
20
+ export {};
@@ -0,0 +1,165 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { dirname, normalize } from 'node:path';
3
+ import { UnsupportedAsm80LoweringError } from './outputs/write-asm80.js';
4
+ let cachedPackageVersion;
5
+ export async function emitAssemblyArtifacts(input) {
6
+ const artifacts = [];
7
+ const diagnostics = [];
8
+ const map = assembledImageToMap(input.bytes, input.origin, input.sourceSegments);
9
+ const hexMap = assembledInitializedImageToMap(input.bytes, input.origin, input.initializedAddresses);
10
+ const sidecarMap = assembledInitializedImageToMap(input.bytes, input.origin, input.initializedAddresses, input.sourceSegments);
11
+ const symbols = collectSymbolEntries(input.program, input.symbols);
12
+ const emit = compileArtifactDefaults(input.options);
13
+ const d8Root = input.options.sourceRoot ?? dirname(input.entryFile);
14
+ if (emit.emitBin) {
15
+ artifacts.push(input.formats.writeBin(map, symbols));
16
+ }
17
+ if (emit.emitHex) {
18
+ artifacts.push(input.formats.writeHex(hexMap, symbols));
19
+ }
20
+ if (emit.emitD8m) {
21
+ artifacts.push(input.formats.writeD8m(sidecarMap, symbols, await buildD8mOptions(input.entryFile, d8Root, input.options, symbols)));
22
+ }
23
+ if (emit.emitAsm80 && input.formats.writeAsm80 !== undefined) {
24
+ try {
25
+ artifacts.push(input.formats.writeAsm80(input.program, symbols));
26
+ }
27
+ catch (error) {
28
+ if (error instanceof UnsupportedAsm80LoweringError) {
29
+ diagnostics.push({
30
+ severity: 'error',
31
+ code: 'AZMN_ASM80',
32
+ message: error.message,
33
+ sourceName: error.item.span.sourceName,
34
+ line: error.item.span.line,
35
+ column: error.item.span.column,
36
+ });
37
+ }
38
+ else {
39
+ throw error;
40
+ }
41
+ }
42
+ }
43
+ return { artifacts, diagnostics };
44
+ }
45
+ function compileArtifactDefaults(options) {
46
+ const anyPrimary = [options.emitBin, options.emitHex, options.emitD8m].some((value) => value !== undefined);
47
+ const emitBin = anyPrimary ? (options.emitBin ?? false) : true;
48
+ const emitHex = anyPrimary ? (options.emitHex ?? false) : true;
49
+ const emitD8m = anyPrimary ? (options.emitD8m ?? false) : true;
50
+ const emitAsm80 = options.emitAsm80 ?? false;
51
+ return { emitBin, emitHex, emitD8m, emitAsm80 };
52
+ }
53
+ function assembledImageToMap(bytes, origin, sourceSegments = []) {
54
+ const map = new Map();
55
+ for (let offset = 0; offset < bytes.length; offset += 1) {
56
+ map.set(origin + offset, bytes[offset] ?? 0);
57
+ }
58
+ const writtenRange = {
59
+ start: origin,
60
+ end: origin + bytes.length,
61
+ };
62
+ return { bytes: map, writtenRange, sourceSegments };
63
+ }
64
+ function assembledInitializedImageToMap(bytes, origin, initializedAddresses, sourceSegments = []) {
65
+ const map = new Map();
66
+ for (const address of initializedAddresses) {
67
+ const offset = address - origin;
68
+ if (offset >= 0 && offset < bytes.length) {
69
+ map.set(address, bytes[offset] ?? 0);
70
+ }
71
+ }
72
+ return { bytes: map, sourceSegments };
73
+ }
74
+ function collectSymbolEntries(items, resolvedSymbols) {
75
+ const map = new Map();
76
+ for (const item of items) {
77
+ appendSymbolEntry(map, item, resolvedSymbols);
78
+ }
79
+ return [...map.values()];
80
+ }
81
+ function appendSymbolEntry(map, item, resolvedSymbols) {
82
+ if (item.kind === 'equ') {
83
+ const value = resolvedSymbols[item.name];
84
+ if (value !== undefined) {
85
+ map.set(item.name, {
86
+ kind: 'constant',
87
+ name: item.name,
88
+ value,
89
+ file: item.span.sourceName,
90
+ line: item.span.line,
91
+ scope: 'global',
92
+ });
93
+ }
94
+ return;
95
+ }
96
+ if (item.kind === 'label') {
97
+ const address = resolvedSymbols[item.name];
98
+ if (address !== undefined) {
99
+ map.set(item.name, {
100
+ kind: 'label',
101
+ name: item.name,
102
+ address,
103
+ file: item.span.sourceName,
104
+ line: item.span.line,
105
+ scope: 'global',
106
+ });
107
+ }
108
+ return;
109
+ }
110
+ if (item.kind === 'enum') {
111
+ for (const member of item.members) {
112
+ const fullName = `${item.name}.${member}`;
113
+ const value = resolvedSymbols[fullName];
114
+ if (value !== undefined) {
115
+ map.set(fullName, {
116
+ kind: 'constant',
117
+ name: fullName,
118
+ value,
119
+ file: item.span.sourceName,
120
+ line: item.span.line,
121
+ scope: 'global',
122
+ });
123
+ }
124
+ }
125
+ }
126
+ }
127
+ async function buildD8mOptions(entryFile, d8Root, options, symbols) {
128
+ const main = symbols.find((symbol) => symbol.kind === 'label' && symbol.name.toLowerCase() === 'main');
129
+ return {
130
+ rootDir: normalize(d8Root),
131
+ packageVersion: await readPackageVersion(),
132
+ inputs: {
133
+ entry: entryFile,
134
+ ...(options.d8mInputs?.hex !== undefined ? { hex: options.d8mInputs.hex } : {}),
135
+ ...(options.d8mInputs?.bin !== undefined ? { bin: options.d8mInputs.bin } : {}),
136
+ },
137
+ ...(main !== undefined ? { entrySymbol: main.name } : {}),
138
+ ...(main !== undefined ? { entryAddress: main.kind === 'constant' ? main.value : main.address } : {}),
139
+ };
140
+ }
141
+ async function readPackageVersion() {
142
+ if (cachedPackageVersion !== undefined) {
143
+ return cachedPackageVersion;
144
+ }
145
+ const packageJsonCandidates = [
146
+ new URL('../package.json', import.meta.url),
147
+ new URL('../../package.json', import.meta.url),
148
+ new URL('../../../package.json', import.meta.url),
149
+ ];
150
+ for (const candidate of packageJsonCandidates) {
151
+ try {
152
+ const raw = await readFile(candidate, 'utf8');
153
+ const json = JSON.parse(raw);
154
+ if (json.version !== undefined) {
155
+ cachedPackageVersion = json.version;
156
+ return cachedPackageVersion;
157
+ }
158
+ }
159
+ catch {
160
+ // Continue searching candidates.
161
+ }
162
+ }
163
+ cachedPackageVersion = '0.0.0';
164
+ return cachedPackageVersion;
165
+ }
@@ -3,7 +3,7 @@ import { writeHex } from './outputs/write-hex.js';
3
3
  import type { AddressRange, Artifact, D8mArtifact, D8mFileEntry, D8mFileSymbol, D8mGenerator, D8mJson, D8mSegment, D8mSymbol, EmittedByteMap, FormatWriters, SymbolEntry, WriteD8mOptions } from './outputs/types.js';
4
4
  import type { Diagnostic } from './model/diagnostic.js';
5
5
  import type { CaseStyleMode } from './tooling/case-style.js';
6
- import type { RegisterCareMode } from './register-care/types.js';
6
+ import type { RegisterContractsMode } from './register-contracts/types.js';
7
7
  export { writeHex, defaultFormatWriters };
8
8
  export type { AddressRange, Artifact, D8mArtifact, D8mFileEntry, D8mFileSymbol, D8mGenerator, D8mJson, D8mSegment, D8mSymbol, EmittedByteMap, FormatWriters, SymbolEntry, WriteD8mOptions, };
9
9
  export type CompileDependencies = CompileNextDependencies;
@@ -27,13 +27,19 @@ export interface CompileNextFunctionOptions {
27
27
  readonly emitHex?: boolean;
28
28
  readonly emitD8m?: boolean;
29
29
  readonly emitAsm80?: boolean;
30
- readonly registerCare?: RegisterCareMode;
30
+ readonly registerContracts?: RegisterContractsMode;
31
+ /** @deprecated Use registerContracts. */
32
+ readonly registerCare?: RegisterContractsMode;
31
33
  readonly emitRegisterReport?: boolean;
32
34
  readonly emitRegisterInterface?: boolean;
33
35
  readonly emitRegisterAnnotations?: boolean;
34
36
  readonly fixRegisterContracts?: boolean;
35
37
  readonly acceptRegisterOutputCandidates?: string[];
38
+ readonly registerContractsProfile?: 'mon3';
39
+ /** @deprecated Use registerContractsProfile. */
36
40
  readonly registerCareProfile?: 'mon3';
41
+ readonly registerContractsInterfaces?: string[];
42
+ /** @deprecated Use registerContractsInterfaces. */
37
43
  readonly registerCareInterfaces?: string[];
38
44
  readonly skipAssembly?: boolean;
39
45
  }