@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.
- package/README.md +170 -69
- package/dist/src/api-artifacts.d.ts +20 -0
- package/dist/src/api-artifacts.js +165 -0
- package/dist/src/api-compile.d.ts +8 -2
- package/dist/src/api-compile.js +31 -230
- package/dist/src/api-register-contracts.d.ts +9 -0
- package/dist/src/api-register-contracts.js +77 -0
- package/dist/src/api-tooling.d.ts +2 -2
- package/dist/src/api-tooling.js +1 -1
- package/dist/src/assembly/address-planning.d.ts +1 -2
- package/dist/src/assembly/address-planning.js +119 -218
- package/dist/src/assembly/address-symbols.d.ts +12 -0
- package/dist/src/assembly/address-symbols.js +118 -0
- package/dist/src/assembly/fixup-emission.js +30 -48
- package/dist/src/assembly/program-emission.js +163 -164
- package/dist/src/cli/artifact-files.d.ts +15 -0
- package/dist/src/cli/artifact-files.js +86 -0
- package/dist/src/cli/parse-args.d.ts +6 -5
- package/dist/src/cli/parse-args.js +162 -136
- package/dist/src/cli/run.js +4 -1
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +33 -0
- package/dist/src/cli/write-artifacts.js +18 -91
- package/dist/src/core/compile.js +51 -274
- package/dist/src/core/conditional-assembly.d.ts +6 -0
- package/dist/src/core/conditional-assembly.js +181 -0
- package/dist/src/expansion/op-constant-expression.d.ts +3 -0
- package/dist/src/expansion/op-constant-expression.js +52 -0
- package/dist/src/expansion/op-expand-selected.d.ts +5 -0
- package/dist/src/expansion/op-expand-selected.js +143 -0
- package/dist/src/expansion/op-expansion.d.ts +5 -53
- package/dist/src/expansion/op-expansion.js +85 -815
- package/dist/src/expansion/op-instruction-instantiation.d.ts +3 -0
- package/dist/src/expansion/op-instruction-instantiation.js +194 -0
- package/dist/src/expansion/op-local-labels.d.ts +8 -0
- package/dist/src/expansion/op-local-labels.js +166 -0
- package/dist/src/expansion/op-operand-splitting.d.ts +1 -0
- package/dist/src/expansion/op-operand-splitting.js +44 -0
- package/dist/src/expansion/op-operands.d.ts +53 -0
- package/dist/src/expansion/op-operands.js +66 -0
- package/dist/src/expansion/op-selection.d.ts +18 -0
- package/dist/src/expansion/op-selection.js +172 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/model/diagnostic.d.ts +4 -0
- package/dist/src/model/diagnostic.js +4 -0
- package/dist/src/outputs/asm80-expression-evaluation.d.ts +10 -0
- package/dist/src/outputs/asm80-expression-evaluation.js +75 -0
- package/dist/src/outputs/asm80-expressions.d.ts +5 -0
- package/dist/src/outputs/asm80-expressions.js +47 -0
- package/dist/src/outputs/asm80-instruction-operands.d.ts +16 -0
- package/dist/src/outputs/asm80-instruction-operands.js +38 -0
- package/dist/src/outputs/asm80-instructions.d.ts +5 -0
- package/dist/src/outputs/asm80-instructions.js +272 -0
- package/dist/src/outputs/asm80-ld-operands.d.ts +10 -0
- package/dist/src/outputs/asm80-ld-operands.js +157 -0
- package/dist/src/outputs/asm80-strings.d.ts +4 -0
- package/dist/src/outputs/asm80-strings.js +14 -0
- package/dist/src/outputs/d8-files.d.ts +10 -0
- package/dist/src/outputs/d8-files.js +103 -0
- package/dist/src/outputs/d8-helpers.d.ts +21 -0
- package/dist/src/outputs/d8-helpers.js +136 -0
- package/dist/src/outputs/hex.js +26 -18
- package/dist/src/outputs/types.d.ts +16 -10
- package/dist/src/outputs/write-asm80.js +68 -597
- package/dist/src/outputs/write-d8.js +6 -216
- package/dist/src/register-contracts/accept-output.d.ts +2 -0
- package/dist/src/register-contracts/analyze-helpers.d.ts +29 -0
- package/dist/src/register-contracts/analyze-helpers.js +162 -0
- package/dist/src/{register-care → register-contracts}/analyze.d.ts +6 -6
- package/dist/src/register-contracts/analyze.js +73 -0
- package/dist/src/register-contracts/annotate.d.ts +11 -0
- package/dist/src/{register-care → register-contracts}/annotate.js +3 -3
- package/dist/src/register-contracts/annotations.d.ts +8 -0
- package/dist/src/{register-care → register-contracts}/annotations.js +3 -3
- package/dist/src/register-contracts/boundaryHints.d.ts +3 -0
- package/dist/src/register-contracts/boundaryHints.js +24 -0
- package/dist/src/register-contracts/carriers.d.ts +2 -0
- package/dist/src/register-contracts/constants.d.ts +4 -0
- package/dist/src/register-contracts/constants.js +51 -0
- package/dist/src/register-contracts/controlFlow.d.ts +5 -0
- package/dist/src/register-contracts/controlFlow.js +55 -0
- package/dist/src/register-contracts/fix.d.ts +11 -0
- package/dist/src/{register-care → register-contracts}/fix.js +47 -30
- package/dist/src/register-contracts/instruction-head.d.ts +2 -0
- package/dist/src/register-contracts/instruction-head.js +3 -0
- package/dist/src/register-contracts/instruction-operands.d.ts +3 -0
- package/dist/src/register-contracts/instruction-operands.js +101 -0
- package/dist/src/register-contracts/instruction-predicates.d.ts +6 -0
- package/dist/src/register-contracts/instruction-predicates.js +44 -0
- package/dist/src/register-contracts/interfaceContracts.d.ts +2 -0
- package/dist/src/register-contracts/interfaceContracts.js +68 -0
- package/dist/src/register-contracts/liveness.d.ts +3 -0
- package/dist/src/{register-care → register-contracts}/liveness.js +111 -79
- package/dist/src/register-contracts/operand-register-name.d.ts +2 -0
- package/dist/src/register-contracts/operand-register-name.js +13 -0
- package/dist/src/{register-care → register-contracts}/profiles.d.ts +5 -5
- package/dist/src/{register-care → register-contracts}/profiles.js +13 -2
- package/dist/src/register-contracts/programModel-boundaries.d.ts +6 -0
- package/dist/src/register-contracts/programModel-boundaries.js +64 -0
- package/dist/src/register-contracts/programModel-routines.d.ts +7 -0
- package/dist/src/register-contracts/programModel-routines.js +128 -0
- package/dist/src/register-contracts/programModel.d.ts +3 -0
- package/dist/src/register-contracts/programModel.js +14 -0
- package/dist/src/register-contracts/report.d.ts +5 -0
- package/dist/src/{register-care → register-contracts}/report.js +34 -17
- package/dist/src/register-contracts/routine-summaries.d.ts +6 -0
- package/dist/src/{register-care → register-contracts}/routine-summaries.js +11 -1
- package/dist/src/register-contracts/smartCommentBlocks.d.ts +5 -0
- package/dist/src/register-contracts/smartCommentBlocks.js +30 -0
- package/dist/src/register-contracts/smartCommentParsing.d.ts +3 -0
- package/dist/src/register-contracts/smartCommentParsing.js +80 -0
- package/dist/src/register-contracts/smartComments.d.ts +5 -0
- package/dist/src/register-contracts/smartComments.js +92 -0
- package/dist/src/register-contracts/summaries.d.ts +12 -0
- package/dist/src/{register-care → register-contracts}/summaries.js +7 -7
- package/dist/src/register-contracts/summary-boundary.d.ts +2 -0
- package/dist/src/register-contracts/summary-boundary.js +40 -0
- package/dist/src/register-contracts/summary-contract.d.ts +2 -0
- package/dist/src/register-contracts/summary-contract.js +45 -0
- package/dist/src/register-contracts/summary-result.d.ts +7 -0
- package/dist/src/register-contracts/summary-result.js +122 -0
- package/dist/src/register-contracts/summary-state.d.ts +23 -0
- package/dist/src/register-contracts/summary-state.js +88 -0
- package/dist/src/register-contracts/summary-token-transfer.d.ts +3 -0
- package/dist/src/register-contracts/summary-token-transfer.js +67 -0
- package/dist/src/register-contracts/summary.d.ts +3 -0
- package/dist/src/register-contracts/summary.js +266 -0
- package/dist/src/register-contracts/tooling.d.ts +57 -0
- package/dist/src/{register-care → register-contracts}/tooling.js +8 -6
- package/dist/src/register-contracts/types.d.ts +188 -0
- package/dist/src/semantics/binary-operators.d.ts +2 -0
- package/dist/src/semantics/binary-operators.js +15 -0
- package/dist/src/semantics/byte-functions.d.ts +2 -0
- package/dist/src/semantics/byte-functions.js +7 -0
- package/dist/src/semantics/constant-operator-types.d.ts +10 -0
- package/dist/src/semantics/constant-operator-types.js +1 -0
- package/dist/src/semantics/constant-operators.d.ts +3 -0
- package/dist/src/semantics/constant-operators.js +3 -0
- package/dist/src/semantics/diagnostics.d.ts +3 -0
- package/dist/src/semantics/diagnostics.js +10 -0
- package/dist/src/semantics/expression-evaluation.d.ts +11 -19
- package/dist/src/semantics/expression-evaluation.js +22 -334
- package/dist/src/semantics/layout-evaluation.d.ts +23 -0
- package/dist/src/semantics/layout-evaluation.js +202 -0
- package/dist/src/semantics/layout-format.d.ts +5 -0
- package/dist/src/semantics/layout-format.js +31 -0
- package/dist/src/semantics/layout-path.d.ts +24 -0
- package/dist/src/semantics/layout-path.js +58 -0
- package/dist/src/semantics/unary-operators.d.ts +2 -0
- package/dist/src/semantics/unary-operators.js +8 -0
- package/dist/src/source/line-comment-scanner.d.ts +1 -0
- package/dist/src/source/line-comment-scanner.js +51 -0
- package/dist/src/source/strip-line-comment.js +8 -44
- package/dist/src/syntax/directive-aliases.js +36 -22
- package/dist/src/syntax/expression-tokenizer.d.ts +30 -0
- package/dist/src/syntax/expression-tokenizer.js +310 -0
- package/dist/src/syntax/parse-directive-statement.d.ts +14 -0
- package/dist/src/syntax/parse-directive-statement.js +307 -0
- package/dist/src/syntax/parse-expression.d.ts +2 -2
- package/dist/src/syntax/parse-expression.js +7 -568
- package/dist/src/syntax/parse-layout-declarations.d.ts +9 -0
- package/dist/src/syntax/parse-layout-declarations.js +180 -0
- package/dist/src/syntax/parse-layout-expression.d.ts +5 -0
- package/dist/src/syntax/parse-layout-expression.js +175 -0
- package/dist/src/syntax/parse-line.js +4 -272
- package/dist/src/syntax/parse-token-expression.d.ts +3 -0
- package/dist/src/syntax/parse-token-expression.js +133 -0
- package/dist/src/tooling/case-style.js +47 -30
- package/dist/src/z80/effect-groups.d.ts +38 -0
- package/dist/src/z80/effect-groups.js +265 -0
- package/dist/src/z80/effect-units.d.ts +18 -0
- package/dist/src/z80/effect-units.js +165 -0
- package/dist/src/z80/effects.d.ts +1 -1
- package/dist/src/z80/effects.js +94 -557
- package/dist/src/z80/encode-core.d.ts +2 -0
- package/dist/src/z80/encode-core.js +42 -0
- package/dist/src/z80/encode-ld-helpers.d.ts +25 -0
- package/dist/src/z80/encode-ld-helpers.js +172 -0
- package/dist/src/z80/encode-ld.d.ts +2 -0
- package/dist/src/z80/encode-ld.js +285 -0
- package/dist/src/z80/encode.js +190 -542
- package/dist/src/z80/ld-support.d.ts +3 -0
- package/dist/src/z80/ld-support.js +146 -0
- package/dist/src/z80/operand-split-state.d.ts +8 -0
- package/dist/src/z80/operand-split-state.js +46 -0
- package/dist/src/z80/operand-split.d.ts +1 -0
- package/dist/src/z80/operand-split.js +13 -0
- package/dist/src/z80/parse-basic.d.ts +4 -0
- package/dist/src/z80/parse-basic.js +39 -0
- package/dist/src/z80/parse-branch.d.ts +4 -0
- package/dist/src/z80/parse-branch.js +218 -0
- package/dist/src/z80/parse-conditions.d.ts +6 -0
- package/dist/src/z80/parse-conditions.js +10 -0
- package/dist/src/z80/parse-exchange.d.ts +2 -0
- package/dist/src/z80/parse-exchange.js +30 -0
- package/dist/src/z80/parse-instruction.js +224 -1010
- package/dist/src/z80/parse-io-control.d.ts +5 -0
- package/dist/src/z80/parse-io-control.js +108 -0
- package/dist/src/z80/parse-ld.d.ts +2 -0
- package/dist/src/z80/parse-ld.js +83 -0
- package/dist/src/z80/parse-operands.d.ts +41 -0
- package/dist/src/z80/parse-operands.js +259 -0
- package/docs/reference/cli.md +42 -35
- package/docs/reference/tooling-api.md +20 -16
- package/package.json +1 -1
- package/dist/src/register-care/accept-output.d.ts +0 -2
- package/dist/src/register-care/analyze.js +0 -166
- package/dist/src/register-care/annotate.d.ts +0 -11
- package/dist/src/register-care/annotations.d.ts +0 -8
- package/dist/src/register-care/boundaryHints.d.ts +0 -3
- package/dist/src/register-care/boundaryHints.js +0 -80
- package/dist/src/register-care/carriers.d.ts +0 -2
- package/dist/src/register-care/controlFlow.d.ts +0 -5
- package/dist/src/register-care/controlFlow.js +0 -38
- package/dist/src/register-care/fix.d.ts +0 -11
- package/dist/src/register-care/instruction-shape.d.ts +0 -11
- package/dist/src/register-care/instruction-shape.js +0 -129
- package/dist/src/register-care/liveness.d.ts +0 -3
- package/dist/src/register-care/programModel.d.ts +0 -3
- package/dist/src/register-care/programModel.js +0 -266
- package/dist/src/register-care/report.d.ts +0 -5
- package/dist/src/register-care/routine-summaries.d.ts +0 -6
- package/dist/src/register-care/smartComments.d.ts +0 -5
- package/dist/src/register-care/smartComments.js +0 -243
- package/dist/src/register-care/summaries.d.ts +0 -12
- package/dist/src/register-care/summary.d.ts +0 -3
- package/dist/src/register-care/summary.js +0 -474
- package/dist/src/register-care/tooling.d.ts +0 -43
- package/dist/src/register-care/types.d.ts +0 -172
- /package/dist/src/{register-care → register-contracts}/accept-output.js +0 -0
- /package/dist/src/{register-care → register-contracts}/carriers.js +0 -0
- /package/dist/src/{register-care → register-contracts}/sourceText.d.ts +0 -0
- /package/dist/src/{register-care → register-contracts}/sourceText.js +0 -0
- /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
|
|
8
|
-
documentation
|
|
7
|
+
This README is the condensed manual. The Debug80 website contains the detailed
|
|
8
|
+
AZM manual and broader Debug80 documentation:
|
|
9
9
|
|
|
10
|
-
[
|
|
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
|
|
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
|
|
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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
104
|
+
.org 0x4000
|
|
105
|
+
.include "../shared/constants.asm"
|
|
97
106
|
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
MOVE_PERIOD .equ 128
|
|
108
|
+
|
|
109
|
+
Message:
|
|
110
|
+
.db "READY",0
|
|
111
|
+
|
|
112
|
+
StatePtr:
|
|
113
|
+
.dw 0
|
|
114
|
+
|
|
115
|
+
Buffer:
|
|
116
|
+
.ds 32
|
|
100
117
|
```
|
|
101
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
|
|
179
|
+
```
|
|
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,9 +335,9 @@ 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
|
|
338
|
+
## Register Contracts
|
|
251
339
|
|
|
252
|
-
Register
|
|
340
|
+
Register contracts check whether subroutines preserve the register values that their
|
|
253
341
|
callers still need. It is designed to catch register collisions, a common source
|
|
254
342
|
of assembly bugs.
|
|
255
343
|
|
|
@@ -272,7 +360,7 @@ name:
|
|
|
272
360
|
call CheckTile
|
|
273
361
|
```
|
|
274
362
|
|
|
275
|
-
AZMDoc register
|
|
363
|
+
AZMDoc register contract comments use `;!` and may record inputs, outputs,
|
|
276
364
|
clobbered registers and preserved registers. `clobbers B` means the routine may
|
|
277
365
|
change `B`. `preserves B` means the value that enters in `B` is still present
|
|
278
366
|
when the routine returns.
|
|
@@ -280,13 +368,24 @@ when the routine returns.
|
|
|
280
368
|
Run the analysis with:
|
|
281
369
|
|
|
282
370
|
```sh
|
|
283
|
-
azm --rc audit
|
|
371
|
+
azm --rc audit program.asm
|
|
372
|
+
azm --rc warn program.asm
|
|
284
373
|
azm --rc error --interface monitor.asmi program.asm
|
|
374
|
+
azm --rc strict program.asm
|
|
285
375
|
```
|
|
286
376
|
|
|
287
|
-
The main modes are `audit`, `warn`, `error` and `strict`.
|
|
288
|
-
|
|
289
|
-
|
|
377
|
+
The main modes are `audit`, `warn`, `error` and `strict`. Use `audit` for a
|
|
378
|
+
non-blocking check, `warn` for visible diagnostics with a successful compile,
|
|
379
|
+
`error` to fail on proven caller/callee register conflicts, and `strict` to fail
|
|
380
|
+
on any register contracts issue AZM cannot prove safe, including unknown call
|
|
381
|
+
boundaries and unbalanced or unknown stack effects.
|
|
382
|
+
|
|
383
|
+
The normal register contracts interface is compiler diagnostics plus source
|
|
384
|
+
contracts. Use `--contracts` or `--fix` when you want AZM to update compact
|
|
385
|
+
AZMDoc contract comments in source. Use `.asmi` files for externally assembled
|
|
386
|
+
routines or monitor/system APIs. Text report files are available with
|
|
387
|
+
`--reg-report`, but they are an explicit debug/export option and are not part of
|
|
388
|
+
the normal workflow.
|
|
290
389
|
|
|
291
390
|
## Ops and Aliases
|
|
292
391
|
|
|
@@ -316,7 +415,7 @@ azm [options] <entry.asm|entry.z80>
|
|
|
316
415
|
```
|
|
317
416
|
|
|
318
417
|
The entry file is the final argument. Source entries use `.asm` or `.z80`.
|
|
319
|
-
External register
|
|
418
|
+
External register contract interfaces use `.asmi` and are loaded with
|
|
320
419
|
`--interface`.
|
|
321
420
|
|
|
322
421
|
Basic use writes the default artifact set next to the source file:
|
|
@@ -364,38 +463,40 @@ Generate ASM80-compatible lowered source:
|
|
|
364
463
|
azm --asm80 program.asm
|
|
365
464
|
```
|
|
366
465
|
|
|
367
|
-
Run register
|
|
466
|
+
Run register contracts analysis:
|
|
368
467
|
|
|
369
468
|
```sh
|
|
370
|
-
azm --rc audit
|
|
469
|
+
azm --rc audit program.asm
|
|
470
|
+
azm --rc warn program.asm
|
|
371
471
|
azm --rc error --interface monitor.asmi program.asm
|
|
472
|
+
azm --rc strict program.asm
|
|
372
473
|
azm --contracts --rc audit program.asm
|
|
373
474
|
```
|
|
374
475
|
|
|
375
476
|
The main switches are:
|
|
376
477
|
|
|
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-
|
|
388
|
-
| `--reg-report, --emit-register-report` |
|
|
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
|
|
392
|
-
| `--accept-out <routine:carrier>` | Promote an inferred output candidate while annotating.
|
|
393
|
-
| `--interface <file>` | Load external register
|
|
394
|
-
| `--reg-profile, --register-profile <profile>` | Register
|
|
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.
|
|
478
|
+
| Option | Meaning |
|
|
479
|
+
| --------------------------------------------- | ------------------------------------------------------------------- |
|
|
480
|
+
| `-o, --output <file>` | Primary output path. The extension matches `--type`. |
|
|
481
|
+
| `-t, --type <hex\|bin>` | Primary output type. Default: `hex`. |
|
|
482
|
+
| `--nobin` | Skip `.bin` output. |
|
|
483
|
+
| `--nohex` | Skip `.hex` output. |
|
|
484
|
+
| `--nod8m` | Skip `.d8.json` output. |
|
|
485
|
+
| `--asm80` | Write lowered assembler source as `.z80`. |
|
|
486
|
+
| `--source-root <dir>` | Emit project-relative source paths in `.d8.json`. |
|
|
487
|
+
| `--case-style <mode>` | Lint mnemonic, register and op-head case style. |
|
|
488
|
+
| `--rc, --register-contracts <mode>` | Register contracts mode: `off`, `audit`, `warn`, `error`, `strict`. |
|
|
489
|
+
| `--reg-report, --emit-register-report` | Opt-in debug report: write `.regcontracts.txt`. |
|
|
490
|
+
| `--reg-interface, --emit-register-interface` | Write inferred `.asmi` interface metadata. |
|
|
491
|
+
| `--contracts, --annotate-register-contracts` | Update AZMDoc contract comments in source. |
|
|
492
|
+
| `--fix` | Apply conservative register contract source fixes. |
|
|
493
|
+
| `--accept-out <routine:carrier>` | Promote an inferred output candidate while annotating. |
|
|
494
|
+
| `--interface <file>` | Load external register contracts from `.asmi`. |
|
|
495
|
+
| `--reg-profile, --register-profile <profile>` | Register contracts profile. Currently `mon3`. |
|
|
496
|
+
| `--aliases <file>` | Load project directive alias JSON. |
|
|
497
|
+
| `-I, --include <dir>` | Add an include search path. |
|
|
498
|
+
| `-V, --version` | Print package version. |
|
|
499
|
+
| `-h, --help` | Print CLI help. |
|
|
399
500
|
|
|
400
501
|
See [docs/reference/cli.md](docs/reference/cli.md) for the complete option
|
|
401
502
|
reference.
|
|
@@ -405,14 +506,14 @@ reference.
|
|
|
405
506
|
By default, AZM writes the requested primary output plus useful side artifacts
|
|
406
507
|
using the same base path.
|
|
407
508
|
|
|
408
|
-
| Extension
|
|
409
|
-
|
|
|
410
|
-
| `.hex`
|
|
411
|
-
| `.bin`
|
|
412
|
-
| `.d8.json`
|
|
413
|
-
| `.z80`
|
|
414
|
-
| `.
|
|
415
|
-
| `.asmi`
|
|
509
|
+
| Extension | Contents |
|
|
510
|
+
| ------------------- | -------------------------------------------- |
|
|
511
|
+
| `.hex` | Intel HEX |
|
|
512
|
+
| `.bin` | flat binary |
|
|
513
|
+
| `.d8.json` | Debug80 map |
|
|
514
|
+
| `.z80` | ASM80-compatible lowered source when enabled |
|
|
515
|
+
| `.regcontracts.txt` | opt-in register contracts debug report |
|
|
516
|
+
| `.asmi` | register contracts interface when enabled |
|
|
416
517
|
|
|
417
518
|
## Programmatic API
|
|
418
519
|
|
|
@@ -430,13 +531,13 @@ npm install @jhlagado/azm
|
|
|
430
531
|
```
|
|
431
532
|
|
|
432
533
|
Use `@jhlagado/azm/tooling` when an editor, linter or debugger integration
|
|
433
|
-
needs parsing, diagnostics, symbols, semantic checks or register
|
|
534
|
+
needs parsing, diagnostics, symbols, semantic checks or register contract facts in
|
|
434
535
|
memory:
|
|
435
536
|
|
|
436
537
|
```ts
|
|
437
538
|
import {
|
|
438
539
|
analyzeProgram,
|
|
439
|
-
|
|
540
|
+
analyzeRegisterContractsForTools,
|
|
440
541
|
loadProgram,
|
|
441
542
|
} from '@jhlagado/azm/tooling';
|
|
442
543
|
|
|
@@ -451,13 +552,13 @@ if (loaded.loadedProgram) {
|
|
|
451
552
|
requireMain: false,
|
|
452
553
|
});
|
|
453
554
|
|
|
454
|
-
const
|
|
555
|
+
const registerContracts = analyzeRegisterContractsForTools(loaded.loadedProgram, {
|
|
455
556
|
mode: 'audit',
|
|
456
|
-
|
|
557
|
+
registerContractsProfile: 'mon3',
|
|
457
558
|
});
|
|
458
559
|
|
|
459
560
|
console.log(analysis.diagnostics);
|
|
460
|
-
console.log(
|
|
561
|
+
console.log(registerContracts.candidateDiagnostics);
|
|
461
562
|
}
|
|
462
563
|
```
|
|
463
564
|
|
|
@@ -483,8 +584,8 @@ const result = await compile(
|
|
|
483
584
|
hex: '/abs/path/to/project/build/main.hex',
|
|
484
585
|
bin: '/abs/path/to/project/build/main.bin',
|
|
485
586
|
},
|
|
486
|
-
|
|
487
|
-
|
|
587
|
+
registerContracts: 'audit',
|
|
588
|
+
registerContractsInterfaces: ['/abs/path/to/monitor.asmi'],
|
|
488
589
|
},
|
|
489
590
|
{ formats: defaultFormatWriters },
|
|
490
591
|
);
|
|
@@ -501,20 +602,20 @@ write those artifacts to disk.
|
|
|
501
602
|
|
|
502
603
|
Common programmatic options include:
|
|
503
604
|
|
|
504
|
-
| Option
|
|
505
|
-
|
|
|
506
|
-
| `includeDirs`
|
|
507
|
-
| `directiveAliasFiles`
|
|
508
|
-
| `sourceRoot`
|
|
509
|
-
| `d8mInputs`
|
|
510
|
-
| `outputType`
|
|
511
|
-
| `emitBin`, `emitHex`, `emitD8m` | Select in-memory artifact kinds.
|
|
512
|
-
| `emitAsm80`
|
|
513
|
-
| `
|
|
514
|
-
| `
|
|
605
|
+
| Option | Use |
|
|
606
|
+
| ------------------------------- | --------------------------------------------------------- |
|
|
607
|
+
| `includeDirs` | Include search paths, like repeated `-I`. |
|
|
608
|
+
| `directiveAliasFiles` | Project directive alias JSON files. |
|
|
609
|
+
| `sourceRoot` | Stable project-relative paths in Debug80 maps. |
|
|
610
|
+
| `d8mInputs` | Intended artifact paths recorded in Debug80 map metadata. |
|
|
611
|
+
| `outputType` | Primary output type, `hex` or `bin`. |
|
|
612
|
+
| `emitBin`, `emitHex`, `emitD8m` | Select in-memory artifact kinds. |
|
|
613
|
+
| `emitAsm80` | Request lowered `.z80` artifact. |
|
|
614
|
+
| `registerContracts` | Register contracts mode. |
|
|
615
|
+
| `registerContractsInterfaces` | External `.asmi` contract files. |
|
|
515
616
|
|
|
516
617
|
Public tooling types include `Diagnostic`, `LoadedProgram`,
|
|
517
|
-
`AnalyzeProgramResult`, `LoadProgramResult`, `
|
|
618
|
+
`AnalyzeProgramResult`, `LoadProgramResult`, `RegisterContractsCandidateDiagnostic`
|
|
518
619
|
and the Debug80 map artifact types `D8mArtifact`, `D8mJson` and `D8mSymbol`.
|
|
519
620
|
|
|
520
621
|
See [docs/reference/tooling-api.md](docs/reference/tooling-api.md) for current
|
|
@@ -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 {
|
|
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
|
|
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
|
}
|