@andrivet/z80-assembler 1.3.1 → 1.4.0

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 (197) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.json +35 -0
  4. package/.github/workflows/node.js.yml +47 -0
  5. package/.prettierignore +3 -0
  6. package/.prettierrc +3 -0
  7. package/.vscode/extensions.json +7 -0
  8. package/README.md +53 -27
  9. package/apps/.gitkeep +0 -0
  10. package/apps/z80-assembler-app/.eslintrc.json +18 -0
  11. package/apps/z80-assembler-app/index.html +16 -0
  12. package/apps/z80-assembler-app/postcss.config.js +24 -0
  13. package/apps/z80-assembler-app/project.json +70 -0
  14. package/apps/z80-assembler-app/public/favicon.ico +0 -0
  15. package/apps/z80-assembler-app/public/logo.png +0 -0
  16. package/apps/z80-assembler-app/public/logo192.png +0 -0
  17. package/apps/z80-assembler-app/public/logo512.png +0 -0
  18. package/apps/z80-assembler-app/public/manifest.json +25 -0
  19. package/apps/z80-assembler-app/public/robots.txt +3 -0
  20. package/apps/z80-assembler-app/src/app/app.module.css +0 -0
  21. package/apps/z80-assembler-app/src/app/app.tsx +122 -0
  22. package/apps/z80-assembler-app/src/app/binary.tsx +39 -0
  23. package/apps/z80-assembler-app/src/app/editor.tsx +228 -0
  24. package/apps/z80-assembler-app/src/app/errors.tsx +31 -0
  25. package/apps/z80-assembler-app/src/app/footer.tsx +20 -0
  26. package/apps/z80-assembler-app/src/app/header.tsx +57 -0
  27. package/apps/z80-assembler-app/src/app/misc.ts +13 -0
  28. package/apps/z80-assembler-app/src/app/opcodes.tsx +335 -0
  29. package/apps/z80-assembler-app/src/assets/.gitkeep +0 -0
  30. package/apps/z80-assembler-app/src/assets/images/logo192.png +0 -0
  31. package/apps/z80-assembler-app/src/main.tsx +22 -0
  32. package/apps/z80-assembler-app/src/styles.css +8 -0
  33. package/apps/z80-assembler-app/tailwind.config.js +28 -0
  34. package/apps/z80-assembler-app/tsconfig.app.json +22 -0
  35. package/apps/z80-assembler-app/tsconfig.json +21 -0
  36. package/apps/z80-assembler-app/tsconfig.spec.json +23 -0
  37. package/apps/z80-assembler-app/vite.config.ts +64 -0
  38. package/assets/images/compile.png +0 -0
  39. package/assets/images/logo.png +0 -0
  40. package/assets/images/menu.png +0 -0
  41. package/assets/images/opcodes-load8.png +0 -0
  42. package/assets/images/opcodes.png +0 -0
  43. package/assets/images/open-dir.png +0 -0
  44. package/assets/images/z80-assembler-app.png +0 -0
  45. package/docs/assembly.md +551 -0
  46. package/docs/images/ZX81-0x00.png +0 -0
  47. package/docs/images/ZX81-0x0B.png +0 -0
  48. package/docs/images/ZX81-0x0C.png +0 -0
  49. package/docs/images/ZX81-0x0D.png +0 -0
  50. package/docs/images/ZX81-0x0E.png +0 -0
  51. package/docs/images/ZX81-0x0F.png +0 -0
  52. package/docs/images/ZX81-0x10.png +0 -0
  53. package/docs/images/ZX81-0x11.png +0 -0
  54. package/docs/images/ZX81-0x12.png +0 -0
  55. package/docs/images/ZX81-0x13.png +0 -0
  56. package/docs/images/ZX81-0x14.png +0 -0
  57. package/docs/images/ZX81-0x15.png +0 -0
  58. package/docs/images/ZX81-0x16.png +0 -0
  59. package/docs/images/ZX81-0x17.png +0 -0
  60. package/docs/images/ZX81-0x18.png +0 -0
  61. package/docs/images/ZX81-0x19.png +0 -0
  62. package/docs/images/ZX81-0x1A.png +0 -0
  63. package/docs/images/ZX81-0x1B.png +0 -0
  64. package/docs/images/ZX81-0x1C.png +0 -0
  65. package/docs/images/ZX81-0x1D.png +0 -0
  66. package/docs/images/ZX81-0x1E.png +0 -0
  67. package/docs/images/ZX81-0x1F.png +0 -0
  68. package/docs/images/ZX81-0x20.png +0 -0
  69. package/docs/images/ZX81-0x21.png +0 -0
  70. package/docs/images/ZX81-0x22.png +0 -0
  71. package/docs/images/ZX81-0x23.png +0 -0
  72. package/docs/images/ZX81-0x24.png +0 -0
  73. package/docs/images/ZX81-0x25.png +0 -0
  74. package/docs/images/ZX81-0x26.png +0 -0
  75. package/docs/images/ZX81-0x27.png +0 -0
  76. package/docs/images/ZX81-0x28.png +0 -0
  77. package/docs/images/ZX81-0x29.png +0 -0
  78. package/docs/images/ZX81-0x2A.png +0 -0
  79. package/docs/images/ZX81-0x2B.png +0 -0
  80. package/docs/images/ZX81-0x2C.png +0 -0
  81. package/docs/images/ZX81-0x2D.png +0 -0
  82. package/docs/images/ZX81-0x2E.png +0 -0
  83. package/docs/images/ZX81-0x2F.png +0 -0
  84. package/docs/images/ZX81-0x30.png +0 -0
  85. package/docs/images/ZX81-0x31.png +0 -0
  86. package/docs/images/ZX81-0x32.png +0 -0
  87. package/docs/images/ZX81-0x33.png +0 -0
  88. package/docs/images/ZX81-0x34.png +0 -0
  89. package/docs/images/ZX81-0x35.png +0 -0
  90. package/docs/images/ZX81-0x36.png +0 -0
  91. package/docs/images/ZX81-0x37.png +0 -0
  92. package/docs/images/ZX81-0x38.png +0 -0
  93. package/docs/images/ZX81-0x39.png +0 -0
  94. package/docs/images/ZX81-0x3A.png +0 -0
  95. package/docs/images/ZX81-0x3B.png +0 -0
  96. package/docs/images/ZX81-0x3C.png +0 -0
  97. package/docs/images/ZX81-0x3D.png +0 -0
  98. package/docs/images/ZX81-0x3E.png +0 -0
  99. package/docs/images/ZX81-0x3F.png +0 -0
  100. package/docs/images/ZX81-0x80.png +0 -0
  101. package/docs/images/ZX81-0x8B.png +0 -0
  102. package/docs/images/ZX81-0x8C.png +0 -0
  103. package/docs/images/ZX81-0x8D.png +0 -0
  104. package/docs/images/ZX81-0x8E.png +0 -0
  105. package/docs/images/ZX81-0x8F.png +0 -0
  106. package/docs/images/ZX81-0x90.png +0 -0
  107. package/docs/images/ZX81-0x91.png +0 -0
  108. package/docs/images/ZX81-0x92.png +0 -0
  109. package/docs/images/ZX81-0x93.png +0 -0
  110. package/docs/images/ZX81-0x94.png +0 -0
  111. package/docs/images/ZX81-0x95.png +0 -0
  112. package/docs/images/ZX81-0x96.png +0 -0
  113. package/docs/images/ZX81-0x97.png +0 -0
  114. package/docs/images/ZX81-0x98.png +0 -0
  115. package/docs/images/ZX81-0x99.png +0 -0
  116. package/docs/images/ZX81-0x9A.png +0 -0
  117. package/docs/images/ZX81-0x9B.png +0 -0
  118. package/docs/images/ZX81-0x9C.png +0 -0
  119. package/docs/images/ZX81-0x9D.png +0 -0
  120. package/docs/images/ZX81-0x9E.png +0 -0
  121. package/docs/images/ZX81-0x9F.png +0 -0
  122. package/docs/images/ZX81-0xA0.png +0 -0
  123. package/docs/images/ZX81-0xA1.png +0 -0
  124. package/docs/images/ZX81-0xA2.png +0 -0
  125. package/docs/images/ZX81-0xA3.png +0 -0
  126. package/docs/images/ZX81-0xA4.png +0 -0
  127. package/docs/images/ZX81-0xA5.png +0 -0
  128. package/docs/images/ZX81-0xA6.png +0 -0
  129. package/docs/images/ZX81-0xA7.png +0 -0
  130. package/docs/images/ZX81-0xA8.png +0 -0
  131. package/docs/images/ZX81-0xA9.png +0 -0
  132. package/docs/images/ZX81-0xAA.png +0 -0
  133. package/docs/images/ZX81-0xAB.png +0 -0
  134. package/docs/images/ZX81-0xAC.png +0 -0
  135. package/docs/images/ZX81-0xAD.png +0 -0
  136. package/docs/images/ZX81-0xAE.png +0 -0
  137. package/docs/images/ZX81-0xAF.png +0 -0
  138. package/docs/images/ZX81-0xB0.png +0 -0
  139. package/docs/images/ZX81-0xB1.png +0 -0
  140. package/docs/images/ZX81-0xB2.png +0 -0
  141. package/docs/images/ZX81-0xB3.png +0 -0
  142. package/docs/images/ZX81-0xB4.png +0 -0
  143. package/docs/images/ZX81-0xB5.png +0 -0
  144. package/docs/images/ZX81-0xB6.png +0 -0
  145. package/docs/images/ZX81-0xB7.png +0 -0
  146. package/docs/images/ZX81-0xB8.png +0 -0
  147. package/docs/images/ZX81-0xB9.png +0 -0
  148. package/docs/images/ZX81-0xBA.png +0 -0
  149. package/docs/images/ZX81-0xBB.png +0 -0
  150. package/docs/images/ZX81-0xBC.png +0 -0
  151. package/docs/images/ZX81-0xBD.png +0 -0
  152. package/docs/images/ZX81-0xBE.png +0 -0
  153. package/docs/images/ZX81-0xBF.png +0 -0
  154. package/libs/.gitkeep +0 -0
  155. package/libs/z80-assembler/.eslintrc.json +18 -0
  156. package/libs/z80-assembler/package.json +20 -0
  157. package/libs/z80-assembler/project.json +35 -0
  158. package/{CHANGELOG.md → libs/z80-assembler/public/CHANGELOG.md} +5 -1
  159. package/libs/z80-assembler/public/README.md +54 -0
  160. package/{index.d.ts → libs/z80-assembler/src/index.ts} +1 -1
  161. package/libs/z80-assembler/src/lib/assets/code/basic-end.zx81 +4 -0
  162. package/libs/z80-assembler/src/lib/assets/code/basic-line1.zx81 +4 -0
  163. package/libs/z80-assembler/src/lib/assets/code/basic-line2.zx81 +9 -0
  164. package/libs/z80-assembler/src/lib/assets/code/characters.zx81 +190 -0
  165. package/libs/z80-assembler/src/lib/assets/code/display.zx81 +50 -0
  166. package/libs/z80-assembler/src/lib/assets/code/system-variables.zx81 +46 -0
  167. package/{lib/compiler/Assets.d.ts → libs/z80-assembler/src/lib/compiler/Assets.ts} +6 -1
  168. package/libs/z80-assembler/src/lib/compiler/Ast.ts +545 -0
  169. package/libs/z80-assembler/src/lib/compiler/Compiler.test.ts +2141 -0
  170. package/libs/z80-assembler/src/lib/compiler/Compiler.ts +185 -0
  171. package/libs/z80-assembler/src/lib/compiler/Formatter.ts +43 -0
  172. package/libs/z80-assembler/src/lib/compiler/Generator.ts +255 -0
  173. package/libs/z80-assembler/src/lib/compiler/Labels.ts +165 -0
  174. package/libs/z80-assembler/src/lib/grammar/LowLevel.ts +163 -0
  175. package/libs/z80-assembler/src/lib/grammar/Parse.ts +128 -0
  176. package/libs/z80-assembler/src/lib/grammar/z80.peg +1252 -0
  177. package/libs/z80-assembler/src/lib/grammar/z80.ts +10649 -0
  178. package/libs/z80-assembler/src/lib/types/Error.ts +105 -0
  179. package/{lib/types/Types.d.ts → libs/z80-assembler/src/lib/types/Types.ts} +26 -11
  180. package/libs/z80-assembler/tsconfig.json +23 -0
  181. package/libs/z80-assembler/tsconfig.lib.json +10 -0
  182. package/libs/z80-assembler/tsconfig.spec.json +19 -0
  183. package/libs/z80-assembler/vite.config.ts +58 -0
  184. package/nx.json +57 -0
  185. package/package.json +52 -14
  186. package/tsconfig.base.json +22 -0
  187. package/index.js +0 -312
  188. package/index.mjs +0 -6441
  189. package/lib/compiler/Ast.d.ts +0 -210
  190. package/lib/compiler/Compiler.d.ts +0 -53
  191. package/lib/compiler/Formatter.d.ts +0 -23
  192. package/lib/compiler/Generator.d.ts +0 -40
  193. package/lib/compiler/Labels.d.ts +0 -47
  194. package/lib/grammar/LowLevel.d.ts +0 -68
  195. package/lib/grammar/Parse.d.ts +0 -48
  196. package/lib/grammar/z80.d.ts +0 -2938
  197. package/lib/types/Error.d.ts +0 -62
@@ -0,0 +1,545 @@
1
+ /**
2
+ * Z80 Assembler in Typescript
3
+ *
4
+ * File: Ast.ts
5
+ * Description: Types for building the Abstract Syntax Tree
6
+ * Author: Sebastien Andrivet
7
+ * License: GPLv3
8
+ * Copyrights: Copyright (C) 2023 Sebastien Andrivet
9
+ */
10
+
11
+ import {Address, byte, bytes, Position} from '../types/Types';
12
+ import {CompilationError} from "../types/Error";
13
+ import {ByteValue, Expression, PosInfo, WordValue} from "../grammar/z80";
14
+ import {getLabelValue} from "./Labels";
15
+ import {parseData} from "../compiler/Compiler";
16
+
17
+ /**
18
+ * A function returning a number (if it is known) or null (if it is unknown).
19
+ */
20
+ export type EvalFunc = (pc: Address, mustExist: boolean) => number | null;
21
+
22
+ /**
23
+ * An abstract element generated by the parser.
24
+ */
25
+ export interface AstBase {
26
+ /**
27
+ * The number of bytes that will be generated by this element.
28
+ */
29
+ get size(): number;
30
+
31
+ /**
32
+ * Generate actual bytes.
33
+ * @param instructionAddress Address of the next byte to generate,
34
+ */
35
+ generate(instructionAddress: number): bytes;
36
+ }
37
+
38
+ /**
39
+ * An AST element that represents a block of bytes.
40
+ */
41
+ class ByteBlock implements AstBase {
42
+ private readonly position0: Position;
43
+ private readonly position1?: Position;
44
+
45
+ /**
46
+ * Constructor.
47
+ * @param pos0 Position of the length of the block.
48
+ * @param length Length of the block.
49
+ * @param pos1 Position of the value.
50
+ * @param value Value to use to fill the block.
51
+ */
52
+ constructor(
53
+ pos0: PosInfo,
54
+ private length: Expression,
55
+ pos1: PosInfo | undefined,
56
+ private value: Expression | undefined) {
57
+ this.position0 = {filename: parseData.fileName, pos: pos0};
58
+ if(pos1) this.position1 = {filename: parseData.fileName, pos: pos1};
59
+ }
60
+
61
+ /**
62
+ * The number of bytes that will be generated by this element.
63
+ */
64
+ get size(): number {
65
+ // Evaluate the size
66
+ // Evalue la taille.
67
+ const size = this.length.eval(0, true);
68
+ // Is it valid?
69
+ // Est-elle valide ?
70
+ if(size == null) throw new CompilationError(this.position0, "Unknown size for the data block");
71
+ if(size < 0) throw new CompilationError(this.position0, `Invalid size size for the data block: ${size}`);
72
+ return size;
73
+ }
74
+
75
+ /**
76
+ * Evaluate the expression for the value used to fill the block.
77
+ * @private
78
+ */
79
+ private getValue(pc: Address) {
80
+ // Be sure we have valid data.
81
+ // On s'assure d'avoir des données valides.
82
+ if(this.value == null || this.position1 == null) return 0;
83
+ // Evaluate the expression for the value.
84
+ // On évalue l'expression pour la valeur.
85
+ let v = this.value.eval(pc, true);
86
+ // If the value is null (unknown), we have a compilation error.
87
+ // Si la valeur est nulle (inconnue), nous avons une erreur de compilation.
88
+ if(v == null) throw new CompilationError(this.position1, `Not able to determine a value`);
89
+ // If the value does not fit into 8-bit, we have a compilation error.
90
+ // Si la valeur ne peux pas être représentée sur 8-bit, nous avons une erreur de compilation.
91
+ if(v < -256 || v > 255) throw new CompilationError(this.position1, `Invalid 8-bits value: ${v}`);
92
+ // If the value is negative, take its 2-complement.
93
+ // Si la valeur est négative, on prend son complément à 2.
94
+ if(v < 0) v = 256 + v;
95
+ return v;
96
+ }
97
+
98
+ /**
99
+ * Generate actual bytes.
100
+ */
101
+ generate(pc: Address): bytes {
102
+ // Prepare an array of the right size and fill it.
103
+ // On prépare un tableau de la bonne taille et on le remplit.
104
+ const array = new Array<byte>(this.size);
105
+ array.fill(this.getValue(pc));
106
+ return array;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * An AST element that represents a 16-byte value.
112
+ */
113
+ class Value16 implements AstBase {
114
+ private readonly position: Position;
115
+
116
+ /**
117
+ * Constructor.
118
+ * @param pos Position of the expression for the value.
119
+ * @param expression Expression for the value.
120
+ */
121
+ constructor(pos: PosInfo, private expression: Expression) {
122
+ this.position = {filename: parseData.fileName, pos: pos}
123
+ }
124
+
125
+ /**
126
+ * The number of bytes that will be generated by this element.
127
+ */
128
+ get size(): number { return 2; }
129
+
130
+ /**
131
+ * Generate actual bytes.
132
+ */
133
+ generate(pc: Address): bytes {
134
+ // Evaluate the expression for the value.
135
+ let v = this.expression.eval(pc, true);
136
+ // If the value is null (unknown), we have a compilation error.
137
+ if(v == null) throw new CompilationError(this.position, `Not able to determine the 16-bits value`);
138
+ // If the value does not fit into 16-bit, we have a compilation error.
139
+ if(v < -65536 || v > 65535) throw new CompilationError(this.position, `Invalid 16-bits value: ${v}`);
140
+ // If the value is negative, take its 2-complement.
141
+ if(v < 0) v = 65536 + v;
142
+ // Return two bytes. The first one is the low part, the second is the high part (little endian).
143
+ return [v & 0x00FF, (v & 0xFF00) >> 8];
144
+ }
145
+ }
146
+
147
+ /**
148
+ * An AST element that represents a 8-byte value.
149
+ */
150
+ class Value8 implements AstBase {
151
+ private readonly position: Position;
152
+
153
+ constructor(pos: PosInfo, private expression: Expression) {
154
+ this.position = {filename: parseData.fileName, pos: pos}
155
+ }
156
+
157
+ /**
158
+ * The number of bytes that will be generated by this element.
159
+ */
160
+ get size(): number { return 1; }
161
+
162
+ /**
163
+ * Generate actual bytes.
164
+ */
165
+ generate(pc: Address): bytes {
166
+ // Evaluate the expression for the value.
167
+ let v = this.expression.eval(pc, true);
168
+ // If the value is null (unknown), we have a compilation error.
169
+ if(v == null) throw new CompilationError(this.position, `Not able to determine the 8-bits value`);
170
+ // If the value does not fit into 8-bit, we have a compilation error.
171
+ if(v < -256 || v > 255) throw new CompilationError(this.position, `Invalid 8-bits value: ${v}`);
172
+ // If the value is negative, take its 2-complement.
173
+ if(v < 0) v = 256 + v;
174
+ return [v];
175
+ }
176
+ }
177
+
178
+ /**
179
+ * An AST element that represents a negative 8-byte value.
180
+ */
181
+ class ValueNeg8 implements AstBase {
182
+ private readonly position: Position;
183
+
184
+ constructor(pos: PosInfo, private expression: Expression) {
185
+ this.position = {filename: parseData.fileName, pos: pos}
186
+ }
187
+
188
+ /**
189
+ * The number of bytes that will be generated by this element.
190
+ */
191
+ get size(): number { return 1; }
192
+
193
+ /**
194
+ * Generate actual bytes.
195
+ */
196
+ generate(pc: Address): bytes {
197
+ // Evaluate the expression for the value.
198
+ let v = this.expression.eval(pc, true);
199
+ // If the value is null (unknown), we have a compilation error.
200
+ if(v == null) throw new CompilationError(this.position, `Not able to determine the 8-bits value`);
201
+ // Take the opposite.
202
+ v = -v;
203
+ // If the value does not fit into 8-bit, we have a compilation error.
204
+ if(v < -256 || v > 255) throw new CompilationError(this.position, `Invalid 8-bits value: ${v}`);
205
+ // If the value is negative, take its 2-complement.
206
+ if(v < 0) v = 256 + v;
207
+ return [v];
208
+ }
209
+ }
210
+
211
+ /**
212
+ * An AST element that represents an absolute offset for a relative jump.
213
+ */
214
+ class Jr implements AstBase {
215
+ private readonly position: Position;
216
+
217
+ constructor(pos: PosInfo, private expression: Expression) {
218
+ this.position = {filename: parseData.fileName, pos: pos}
219
+ }
220
+ /**
221
+ * The number of bytes that will be generated by this element.
222
+ */
223
+ get size(): number { return 1; }
224
+
225
+ /**
226
+ * Generate actual bytes.
227
+ */
228
+ generate(pc: Address): bytes {
229
+ // Evaluate the expression for the offset.
230
+ let offset = this.expression.eval(pc, true);
231
+ // If the offset is null (unknown), we have a compilation error.
232
+ if(offset == null) throw new CompilationError(this.position, `Not able to determine the offset value`);
233
+ // The offset has to be between -126 and 129.
234
+ if(offset < -126 || offset > 129) throw new CompilationError(this.position, `Invalid offset for JR instruction: ${offset}`);
235
+ // The actual representation is the offset minus 2.
236
+ offset -= 2;
237
+ // If the value is offset, take its 2-complement.
238
+ if(offset < 0) offset = 256 + offset;
239
+ return [offset];
240
+ }
241
+ }
242
+
243
+ /**
244
+ * An AST element that represents a relative offsez for a relative jump.
245
+ */
246
+ class JrRelative implements AstBase {
247
+ private readonly position: Position;
248
+
249
+ constructor(pos: PosInfo, private label: string) {
250
+ this.position = {filename: parseData.fileName, pos: pos};
251
+ }
252
+
253
+ /**
254
+ * The number of bytes that will be generated by this element.
255
+ */
256
+ get size(): number { return 1; }
257
+
258
+ /**
259
+ * Generate actual bytes.
260
+ * @param pc Address of the next byte to generate,
261
+ */
262
+ generate(pc: Address): bytes {
263
+ if(pc == null) throw new CompilationError(this.position, `Not able to determine PC value`);
264
+ // Get the value of the label.
265
+ const targetAddress = getLabelValue(pc, this.label, this.position, true, true);
266
+ // If the value is null (unknown), we have a compilation error.
267
+ if(targetAddress == null) throw new CompilationError(this.position, `Not able to determine the value of label '${this.label}'`);
268
+ // The offset is between the address hold by the label and the address of the current instruction.
269
+ let offset = targetAddress - pc;
270
+ // The offset has to be between -126 and 129.
271
+ if(offset < -126 || offset > 129) throw new CompilationError(this.position, `Label ${this.label} is to far from JR instruction: ${offset} bytes`);
272
+ // The actual representation is the offset minus 2.
273
+ offset -= 2;
274
+ // If the value is offset, take its 2-complement.
275
+ if(offset < 0) offset = 256 + offset;
276
+ return [offset];
277
+ }
278
+ }
279
+
280
+ /**
281
+ * An AST element is either a number (the raw byte value) or derived from AstBase.
282
+ */
283
+ export type AstElement = number | AstBase;
284
+
285
+ /**
286
+ * Array of AST elements
287
+ */
288
+ export type AstElements = AstElement[];
289
+
290
+ /**
291
+ * Determine if an AST element is a raw byte or derived from AstBase (i.e. abstract)
292
+ * @param element The element to test.
293
+ * @return true if the element is concrete (a raw byte value), false if it is abstract.
294
+ */
295
+ export function isAbstract(element: AstElement): element is AstBase {
296
+ return (element as AstBase).generate !== undefined;
297
+ }
298
+
299
+ /**
300
+ * Determine the actual size in byte of an AST element.
301
+ * @param element The AST element.
302
+ * @return The number of bytes that will be generated by this element
303
+ */
304
+ export function getByteSize(element: AstElement): number {
305
+ return isAbstract(element) ? element.size : 1;
306
+ }
307
+
308
+ /**
309
+ * Get the low part of a 16-bit little endian value.
310
+ * @param value A 16-bit little endian value.
311
+ */
312
+ export function low(value: number) {
313
+ return value & 0x00FF;
314
+ }
315
+
316
+ /**
317
+ * Get the high part of a 16-bit little endian value.
318
+ * @param value A 16-bit little endian value.
319
+ */
320
+ export function high(value: number) {
321
+ return (value & 0xFF00) >> 8;
322
+ }
323
+
324
+ /**
325
+ * An Evaluable interface, i.e. that contains an eval function that returns a number or null.
326
+ */
327
+ interface Evaluable { eval: EvalFunc; }
328
+
329
+ /**
330
+ * An Inner Expression i.e. that extends an Evaluable interface.
331
+ */
332
+ interface InnerExpression<E extends Evaluable> { e: E; }
333
+
334
+ /**
335
+ * A Binary operation interface, i.e. a function with two arguments and that returns a number.
336
+ */
337
+ type BinaryOperation = (a: number, b: number) => number
338
+
339
+ /**
340
+ * A Unary operation interface, i.e. a function with one argument and that returns a number.
341
+ */
342
+ type UnaryOperation = (a: number) => number
343
+
344
+ /**
345
+ * A Binary operation such as: 2 * 4.
346
+ * @param left Left side of the binary function.
347
+ * @param right Right side of the binary function.
348
+ * @param op Operation to be applied to the left and right side.
349
+ */
350
+ export function binaryOperation<
351
+ Operation extends BinaryOperation,
352
+ Inner extends Evaluable,
353
+ Left extends InnerExpression<Inner>,
354
+ Right extends Evaluable>(left: Left | null, right: Right, op: Operation): EvalFunc {
355
+ return (pc: Address, mustExist: boolean) => {
356
+ // Evaluate the right side
357
+ const rightValue = right.eval(pc, mustExist);
358
+ // If the left side is empty, we return the right side
359
+ if(!left) return rightValue;
360
+ // Evaluate the left side
361
+ const leftValue = left.e.eval(pc, mustExist);
362
+ // If one of the values is null, the result is null. Otherwise, apply the operation.
363
+ return leftValue == null || rightValue == null ? null : op(leftValue, rightValue);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * A map of strings versus binary operations. For example: '*' => operatorMul
369
+ */
370
+ type BinaryOperationsMap = { [key: string]: BinaryOperation };
371
+
372
+ /**
373
+ * A map of strings versus unary operations. For example: '-' => operatorNeg
374
+ */
375
+ type UnaryOperationsMap = { [key: string]: UnaryOperation };
376
+
377
+ /**
378
+ * An Inner Operator i.e. that extends an Inner Expression, itself extending an Evaluable.
379
+ */
380
+ interface InnerOp<E extends Evaluable> extends InnerExpression<E>{ op: string; }
381
+
382
+ /**
383
+ * A Binary operation such as: 2 * 4.
384
+ * @param left Left side of the binary function.
385
+ * @param right Right side of the binary function.
386
+ * @param map Map the operations to be applied to the left and right side.
387
+ */
388
+ export function binaryOperations<
389
+ Inner extends Evaluable,
390
+ Left extends InnerOp<Inner>,
391
+ Right extends Evaluable>(left: Left | null, right: Right, map: BinaryOperationsMap): EvalFunc {
392
+ return (pc: Address, mustExist: boolean) => {
393
+ // Evaluate the right side
394
+ const rightValue = right.eval(pc, mustExist);
395
+ // If the left side is empty, we return the right side value.
396
+ if(!left) return rightValue;
397
+ // Evaluate the left side
398
+ const leftValue = left.e.eval(pc, mustExist);
399
+ // If one of the values is null, the result is null. Otherwise, apply the operation.
400
+ return leftValue == null || rightValue == null ? null : map[left.op](leftValue, rightValue);
401
+ }
402
+ }
403
+
404
+ /**
405
+ * A Unary operation.
406
+ * @param e The argument.
407
+ * @param op The operation to be applied to the argument.
408
+ */
409
+ export function unaryOperation<
410
+ Operation extends UnaryOperation,
411
+ E extends Evaluable>(e: E, op: Operation): EvalFunc {
412
+ // Evaluate the argument. If the value is null, the result is null. Otherwise, apply the operation.
413
+ return (pc: Address, mustExist: boolean) => {
414
+ const value = e.eval(pc, mustExist);
415
+ return (value == null) ? null : op(value);
416
+ }
417
+ }
418
+
419
+ /**
420
+ * A Unary operation.
421
+ * @param e The argument.
422
+ * @param op The operation to be applied to the argument as a string.
423
+ * @param map Map the operations to be applied to the argument.
424
+ */
425
+ export function unaryOperations<
426
+ E extends Evaluable>(e: E, op: string, map: UnaryOperationsMap): EvalFunc {
427
+ return (pc: Address, mustExist: boolean) => {
428
+ const value = e.eval(pc, mustExist);
429
+ return (value == null) ? null : map[op](value);
430
+ }
431
+ }
432
+
433
+ // Note: As far as I know, it is not possible to manipulate in Javascript and Typescript builtin operators.
434
+ // So we define a set of functions for this purpose.
435
+
436
+ export const operatorOr = (a: number, b: number) => a | b;
437
+ export const operatorXor = (a: number, b: number) => a ^ b;
438
+ export const operatorAnd = (a: number, b: number) => a & b;
439
+ export const operatorLeftShift = (a: number, b: number) => a << b;
440
+ export const operatorRightShift = (a: number, b: number) => a >> b;
441
+ export const operatorAdd = (a: number, b: number) => a + b;
442
+ export const operatorSub = (a: number, b: number) => a - b;
443
+ export const operatorMul = (a: number, b: number) => a * b;
444
+ export const operatorDiv = (a: number, b: number) => Math.trunc(a / b);
445
+ export const operatorModulo = (a: number, b: number) => a % b;
446
+ export const operatorPlus = (a: number) => +a;
447
+ export const operatorNeg = (a: number) => -a;
448
+ export const operatorInvert = (a: number) => ~a;
449
+ export const operatorIdentity = (a: number) => a;
450
+
451
+ /**
452
+ * Returns an AST element that represents the low part of a little-endian 16-bit value.
453
+ * @param pos The position of the element in the source.
454
+ * @param e The 16-bit value.
455
+ */
456
+ export function value16LE(pos: PosInfo, e: Expression): AstElement {
457
+ return new Value16(pos, e);
458
+ }
459
+
460
+ /**
461
+ * Returns an AST element that represents an 8-bit unsigned value.
462
+ * @param pos The position of the element in the source.
463
+ * @param e The 8-bit unsigned value.
464
+ */
465
+ export function value8(pos: PosInfo, e: Expression): AstElement {
466
+ return new Value8(pos, e);
467
+ }
468
+
469
+ /**
470
+ * Returns an AST element that represents an 8-bit signed offset for IX or IY.
471
+ * @offset The offset for IX or IY with:
472
+ * pos The position of the element in the source.
473
+ * s The sign of the value.
474
+ */
475
+ export function index(offset: {pos: PosInfo, s: string, d: Expression} | null): AstElement {
476
+ return !offset ? 0 : offset.s === '-' ? new ValueNeg8(offset.pos, offset.d) : new Value8(offset.pos, offset.d);
477
+ }
478
+
479
+ /**
480
+ * Returns an AST element that represents an offset for a JR (jump relative) opcode.
481
+ * @param pos The position of the element in the source.
482
+ * @param e The offset value.
483
+ */
484
+ export function jrOffset(pos: PosInfo, e: Expression): AstElement {
485
+ return new Jr(pos, e);
486
+ }
487
+
488
+ /**
489
+ * Returns an AST element that represents an relative offset for a JR (jump relative) opcode.
490
+ * @param pos The position of the element in the source.
491
+ * @param label The label that will give the offset value
492
+ */
493
+ export function jrRelativeOffset(pos: PosInfo, label: string): AstElement {
494
+ return new JrRelative(pos, label);
495
+ }
496
+
497
+ /**
498
+ * An Inner Byte, i.e. it contains an inner field that is a Byte value.
499
+ */
500
+ export interface InnerByte { inner: ByteValue }
501
+
502
+ /**
503
+ * An Inner Word, i.e. it contains an inner field that is a Word value.
504
+ */
505
+ export interface InnerWord { inner: WordValue }
506
+
507
+ /**
508
+ * The bytes of a Byte directive.
509
+ * @param _ The position of the bytes in the source code.
510
+ * @param data0 The first byte.
511
+ * @param data The other bytes.
512
+ */
513
+ export function dataBytes(_: PosInfo, data0: ByteValue, data: InnerByte[]): AstElements {
514
+ return data.reduce((r, c) => r.concat(c.inner.elements), data0.elements);
515
+ }
516
+
517
+ /**
518
+ * The words of a Word directive.
519
+ * @param _ The position of the words in the source code.
520
+ * @param data0 The first word.
521
+ * @param data The other words.
522
+ */
523
+ export function dataWords(_: PosInfo, data0: WordValue, data: InnerWord[]): AstElements {
524
+ return data.reduce((r, c) => r.concat(c.inner.elements), data0.elements);
525
+ }
526
+
527
+ /**
528
+ * The parameters of a block of data.
529
+ * @param pos0 The position of the length in the source code.
530
+ * @param length The length (in bytes) of the block
531
+ * @param pos1 The position of the value in the source code.
532
+ * @param value The value used to initialize the block.
533
+ */
534
+ export function dataBlock(pos0: PosInfo, length: Expression, pos1: PosInfo | undefined, value: Expression | undefined): AstElement {
535
+ return new ByteBlock(pos0, length, pos1, value);
536
+ }
537
+
538
+ export function labelValue(pos: PosInfo, name: string) {
539
+ return (pc: Address, mustExist: boolean) =>
540
+ getLabelValue(pc, name, {filename: parseData.fileName, pos: pos}, true, mustExist);
541
+ }
542
+
543
+ export function value(nn: number) {
544
+ return (pc: Address, mustExist: boolean) => nn;
545
+ }