Support indirect refs in st, e.g. `st a, [ptr] + y`.
Chris Pressey
4 years ago
3 | 3 | 0.12 |
4 | 4 | ---- |
5 | 5 | |
6 | * `copy` is now understood to trash `a`, thus `copy ..., a` is not valid. | |
7 | Indirect addressing is supported in `ld`, as in `ld a, [ptr] + y`, to compensate. | |
6 | * `copy` is now understood to trash `a`, thus it is not valid to use it in `copy`. | |
7 | To compensate, indirect addressing is supported in `ld` and `st`, for example, | |
8 | as `ld a, [ptr] + y` and `st a, [ptr] + y`. | |
8 | 9 | * Implements the "union rule for trashes" when analyzing `if` blocks. |
9 | 10 | * Even if we `goto` another routine, we can't trash an output. |
10 | 11 | * Fixed bug where `trash` was not marking the location as being virtually altered. |
239 | 239 | |
240 | 240 | There is another mode of `ld` which reads into `a` indirectly through a pointer. |
241 | 241 | |
242 | copy [<src-memory-location>] + y, <dest-memory-location> | |
242 | ld a, [<src-memory-location>] + y | |
243 | 243 | |
244 | 244 | The memory location in this syntax must be a pointer. |
245 | 245 | |
264 | 264 | changed by this instruction (unless of course dest is a flag.) |
265 | 265 | |
266 | 266 | If and only if dest is a byte table, the index-memory-location must be given. |
267 | ||
268 | There is another mode of `st` which write `a` into memory, indirectly through | |
269 | a pointer. | |
270 | ||
271 | st a, [<dest-memory-location>] + y | |
272 | ||
273 | The memory location in this syntax must be a pointer. | |
274 | ||
275 | This syntax copies the constents of the `a` register into | |
276 | the contents of memory at the pointer (offset by the `y` register). | |
277 | ||
278 | In addition to the constraints above, `y` must be initialized before | |
279 | this mode is used. | |
267 | 280 | |
268 | 281 | ### copy ### |
269 | 282 |
272 | 272 | context.assert_meaningful(src) |
273 | 273 | context.set_written(dest, FLAG_Z, FLAG_N) |
274 | 274 | elif opcode == 'st': |
275 | if instr.index: | |
276 | if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.type, TYPE_BYTE): | |
277 | pass | |
278 | else: | |
279 | raise TypeMismatchError((src, dest)) | |
280 | context.assert_meaningful(instr.index) | |
275 | if isinstance(dest, IndexedRef): | |
276 | if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.ref.type, TYPE_BYTE): | |
277 | pass | |
278 | else: | |
279 | raise TypeMismatchError((src, dest)) | |
280 | context.assert_meaningful(dest.index) | |
281 | context.set_written(dest.ref) | |
282 | elif isinstance(dest, IndirectRef): | |
283 | # copying this analysis from the matching branch in `copy`, below | |
284 | if isinstance(dest.ref.type, PointerType) and src.type == TYPE_BYTE: | |
285 | pass | |
286 | else: | |
287 | raise TypeMismatchError((src, dest)) | |
288 | context.assert_meaningful(dest.ref, REG_Y) | |
289 | context.set_written(dest.ref) | |
281 | 290 | elif src.type != dest.type: |
282 | 291 | raise TypeMismatchError('%r and %r in %s' % |
283 | 292 | (src, dest, self.current_routine.name) |
284 | 293 | ) |
294 | else: | |
295 | context.set_written(dest) | |
285 | 296 | context.assert_meaningful(src) |
286 | context.set_written(dest) | |
287 | 297 | elif opcode == 'add': |
288 | 298 | context.assert_meaningful(src, dest, FLAG_C) |
289 | 299 | if src.type == TYPE_BYTE: |
136 | 136 | self.emitter.emit(LDA(AbsoluteY(self.labels[src.name]))) |
137 | 137 | elif isinstance(src, IndirectRef) and isinstance(src.ref.type, PointerType): |
138 | 138 | self.emitter.emit(LDA(IndirectY(self.labels[src.ref.name]))) |
139 | elif isinstance(src, IndirectRef) and src.index == REG_Y: | |
140 | self.emitter.emit(LDA(AbsoluteY(self.labels[src.name]))) | |
141 | 139 | else: |
142 | 140 | self.emitter.emit(LDA(Absolute(self.labels[src.name]))) |
143 | 141 | elif dest == REG_X: |
171 | 169 | REG_X: STX, |
172 | 170 | REG_Y: STY |
173 | 171 | }.get(src, None) |
174 | mode_cls = { | |
175 | REG_X: AbsoluteX, | |
176 | REG_Y: AbsoluteY, | |
177 | None: Absolute | |
178 | }.get(instr.index, None) | |
172 | ||
173 | if isinstance(dest, IndexedRef): | |
174 | mode_cls = { | |
175 | REG_X: AbsoluteX, | |
176 | REG_Y: AbsoluteY, | |
177 | }[dest.index] | |
178 | label = self.labels[dest.ref.name] | |
179 | elif isinstance(dest, IndirectRef) and isinstance(dest.ref.type, PointerType): | |
180 | mode_cls = IndirectY | |
181 | label = self.labels[dest.ref.name] | |
182 | else: | |
183 | mode_cls = Absolute | |
184 | label = self.labels[dest.name] | |
185 | ||
179 | 186 | if op_cls is None or mode_cls is None: |
180 | 187 | raise UnsupportedOpcodeError(instr) |
181 | self.emitter.emit(op_cls(mode_cls(self.labels[dest.name]))) | |
188 | self.emitter.emit(op_cls(mode_cls(label))) | |
182 | 189 | elif opcode == 'add': |
183 | 190 | if dest == REG_A: |
184 | 191 | if isinstance(src, ConstantRef): |
338 | 338 | self.scanner.scan() |
339 | 339 | src = self.locexpr() |
340 | 340 | self.scanner.expect(',') |
341 | dest = self.locexpr() | |
342 | index = None | |
343 | if self.scanner.consume('+'): | |
344 | index = self.locexpr() | |
345 | return Instr(opcode=opcode, dest=dest, src=src, index=index) | |
341 | dest = self.indlocexpr() | |
342 | return Instr(opcode=opcode, dest=dest, src=src, index=None) | |
346 | 343 | elif self.scanner.token in ("shl", "shr", "inc", "dec"): |
347 | 344 | opcode = self.scanner.token |
348 | 345 | self.scanner.scan() |
1762 | 1762 | | } |
1763 | 1763 | = ok |
1764 | 1764 | |
1765 | Write the `a` register through a pointer. Note that this is done with `st`, | |
1766 | not `copy`. | |
1767 | ||
1768 | | buffer[2048] buf | |
1769 | | pointer ptr | |
1770 | | byte foo | |
1771 | | | |
1772 | | routine main | |
1773 | | inputs buf | |
1774 | | outputs buf | |
1775 | | trashes a, y, z, n, ptr | |
1776 | | { | |
1777 | | ld y, 0 | |
1778 | | copy ^buf, ptr | |
1779 | | ld a, 255 | |
1780 | | st a, [ptr] + y | |
1781 | | } | |
1782 | = ok | |
1783 | ||
1765 | 1784 | ### routines ### |
1766 | 1785 | |
1767 | 1786 | Routines are constants. You need not, and in fact cannot, specify a constant |
857 | 857 | = $081C LDA ($FE),Y |
858 | 858 | = $081E RTS |
859 | 859 | |
860 | Write the `a` register through a pointer. | |
861 | ||
862 | | buffer[2048] buf | |
863 | | pointer ptr @ 254 | |
864 | | byte foo | |
865 | | | |
866 | | routine main | |
867 | | inputs buf | |
868 | | outputs buf | |
869 | | trashes a, y, z, n, ptr | |
870 | | { | |
871 | | ld y, 0 | |
872 | | copy ^buf, ptr | |
873 | | ld a, 255 | |
874 | | st a, [ptr] + y | |
875 | | } | |
876 | = $080D LDY #$00 | |
877 | = $080F LDA #$1C | |
878 | = $0811 STA $FE | |
879 | = $0813 LDA #$08 | |
880 | = $0815 STA $FF | |
881 | = $0817 LDA #$FF | |
882 | = $0819 STA ($FE),Y | |
883 | = $081B RTS | |
884 | ||
860 | 885 | Add a word memory location, and a literal word, to a pointer, and then read through it. |
861 | 886 | Note that this is *not* range-checked. (Yet.) |
862 | 887 |