If add and sub can work on words, then cmp can work on words too.
Chris Pressey
3 years ago
3 | 3 | 0.18 |
4 | 4 | ---- |
5 | 5 | |
6 | * Added `compare` instruction, which is like `cmp` but can | |
7 | directly compare two `word` memory locations (trashing `a`.) | |
6 | * `cmp` instruction can now perform a 16-bit unsigned comparison | |
7 | of `word` memory locations (at the cost of trashing `a`.) | |
8 | 8 | * Fixed pathological memory use in the lexical scanner - should |
9 | 9 | be much less inefficient now when parsing large source files. |
10 | 10 |
375 | 375 | |
376 | 376 | dest and src continue to be initialized afterwards. |
377 | 377 | |
378 | In addition, if dest is of `word` type, then src must also be of `word` | |
379 | type, and in this case this instruction trashes the `a` register. | |
380 | ||
378 | 381 | ### dec ### |
379 | 382 | |
380 | 383 | dec <dest-memory-location> |
400 | 403 | Affects n, z, and c flags, requiring that they be in the WRITES, |
401 | 404 | and initializing them afterwards. |
402 | 405 | |
403 | ### compare ### | |
404 | ||
405 | compare <dest-memory-location>, <src-memory-location> | |
406 | ||
407 | Subtracts the contents of src from dest, discarding the result | |
408 | and only setting the resulting flags. Differs from `cmp` in | |
409 | that it is able to work on more general types of data (notably, | |
410 | words) and it trashes the `a` register. | |
411 | ||
412 | * It is illegal if src OR dest is uninitialized. | |
413 | ||
414 | Affects n, z, and c flags, requiring that they be in the WRITES, | |
415 | and initializing them afterwards. | |
416 | ||
417 | Note that, like `cmp`, `compare` is not suitable for making a | |
406 | In addition, if dest is of `word` type, then src must also be of `word` | |
407 | type, and in this case this instruction trashes the `a` register. | |
408 | ||
409 | Note that, like `cmp` is not suitable for making a | |
418 | 410 | signed comparison; this article, which mentions |
419 | 411 | techniques that a SixtyPical compiler could use to |
420 | implement `compare`, also explains why that is: | |
412 | implement `cmp`, also explains why that is: | |
421 | 413 | [Beyond 8-bit Unsigned Comparisons, by Bruce Clark](http://www.6502.org/tutorials/compare_beyond.html). |
422 | 414 | |
423 | 415 | ### and, or, xor ### |
504 | 504 | context.assert_meaningful(src, dest) |
505 | 505 | if isinstance(src, IndexedRef): |
506 | 506 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) |
507 | else: | |
507 | elif src.type == TYPE_BYTE: | |
508 | 508 | self.assert_type(TYPE_BYTE, src, dest) |
509 | context.set_written(FLAG_Z, FLAG_N, FLAG_C) | |
510 | elif opcode == 'compare': | |
511 | context.assert_meaningful(src, dest) | |
512 | self.assert_type(TYPE_WORD, src, dest) | |
513 | context.set_touched(REG_A) | |
514 | context.set_unmeaningful(REG_A) | |
509 | else: | |
510 | self.assert_type(TYPE_WORD, src, dest) | |
511 | context.set_touched(REG_A) | |
512 | context.set_unmeaningful(REG_A) | |
515 | 513 | context.set_written(FLAG_Z, FLAG_N, FLAG_C) |
516 | 514 | elif opcode == 'and': |
517 | 515 | if isinstance(src, IndexedRef): |
324 | 324 | raise UnsupportedOpcodeError(instr) |
325 | 325 | elif opcode == 'cmp': |
326 | 326 | self.compile_cmp(instr, instr.src, instr.dest) |
327 | elif opcode == 'compare': | |
328 | self.compile_compare(instr, instr.src, instr.dest) | |
329 | 327 | elif opcode in ('and', 'or', 'xor',): |
330 | 328 | cls = { |
331 | 329 | 'and': AND, |
392 | 390 | |
393 | 391 | def compile_cmp(self, instr, src, dest): |
394 | 392 | """`instr` is only for reporting purposes""" |
393 | if isinstance(src, LocationRef) and src.type == TYPE_WORD: | |
394 | src_label = self.get_label(src.name) | |
395 | dest_label = self.get_label(dest.name) | |
396 | self.emitter.emit(LDA(Absolute(src_label))) | |
397 | self.emitter.emit(CMP(Absolute(dest_label))) | |
398 | end_label = Label('end_label') | |
399 | self.emitter.emit(BNE(Relative(end_label))) | |
400 | self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) | |
401 | self.emitter.emit(CMP(Absolute(Offset(dest_label, 1)))) | |
402 | self.emitter.resolve_label(end_label) | |
403 | return | |
395 | 404 | cls = { |
396 | 405 | 'a': CMP, |
397 | 406 | 'x': CPX, |
406 | 415 | self.emitter.emit(cls(self.addressing_mode_for_index(src.index)(self.get_label(src.ref.name)))) |
407 | 416 | else: |
408 | 417 | self.emitter.emit(cls(Absolute(self.get_label(src.name)))) |
409 | ||
410 | def compile_compare(self, instr, src, dest): | |
411 | """`instr` is only for reporting purposes""" | |
412 | if not isinstance(src, LocationRef) or not isinstance(dest, LocationRef): | |
413 | raise UnsupportedOpcodeError(instr) | |
414 | if src.type != TYPE_WORD or dest.type != TYPE_WORD: | |
415 | raise UnsupportedOpcodeError(instr) | |
416 | ||
417 | src_label = self.get_label(src.name) | |
418 | dest_label = self.get_label(dest.name) | |
419 | self.emitter.emit(LDA(Absolute(src_label))) | |
420 | self.emitter.emit(CMP(Absolute(dest_label))) | |
421 | end_label = Label('end_label') | |
422 | self.emitter.emit(BNE(Relative(end_label))) | |
423 | self.emitter.emit(LDA(Absolute(Offset(src_label, 1)))) | |
424 | self.emitter.emit(CMP(Absolute(Offset(dest_label, 1)))) | |
425 | self.emitter.resolve_label(end_label) | |
426 | 418 | |
427 | 419 | def compile_inc(self, instr, dest): |
428 | 420 | """`instr` is only for reporting purposes""" |
462 | 462 | dest = self.indlocexpr() |
463 | 463 | instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src) |
464 | 464 | return instr |
465 | elif self.scanner.token in ("compare",): | |
466 | opcode = self.scanner.token | |
467 | self.scanner.scan() | |
468 | dest = self.locexpr() | |
469 | self.scanner.expect(',') | |
470 | src = self.indexed_locexpr() | |
471 | instr = SingleOp(self.scanner.line_number, opcode=opcode, dest=dest, src=src) | |
472 | return instr | |
473 | 465 | elif self.scanner.consume("with"): |
474 | 466 | self.scanner.expect("interrupts") |
475 | 467 | self.scanner.expect("off") |
1117 | 1117 | | } |
1118 | 1118 | ? UnmeaningfulReadError: a |
1119 | 1119 | |
1120 | ### compare ### | |
1121 | ||
1122 | Some rudimentary tests for `compare`. | |
1120 | `cmp` can work on words. In this case, it trashes `a`. | |
1123 | 1121 | |
1124 | 1122 | | word za |
1125 | 1123 | | word zb |
1128 | 1126 | | inputs za, zb |
1129 | 1127 | | trashes a, z, c, n |
1130 | 1128 | | { |
1131 | | compare za, zb | |
1129 | | cmp za, zb | |
1132 | 1130 | | } |
1133 | 1131 | = ok |
1134 | 1132 | |
1139 | 1137 | | inputs za, zb |
1140 | 1138 | | trashes a, z, n |
1141 | 1139 | | { |
1142 | | compare za, zb | |
1140 | | cmp za, zb | |
1143 | 1141 | | } |
1144 | 1142 | ? ForbiddenWriteError: c |
1145 | 1143 | |
1150 | 1148 | | inputs za, zb |
1151 | 1149 | | trashes z, c, n |
1152 | 1150 | | { |
1153 | | compare za, zb | |
1151 | | cmp za, zb | |
1154 | 1152 | | } |
1155 | 1153 | ? ForbiddenWriteError: a |
1156 | 1154 | |
1161 | 1159 | | inputs za |
1162 | 1160 | | trashes z, c, n |
1163 | 1161 | | { |
1164 | | compare za, zb | |
1162 | | cmp za, zb | |
1165 | 1163 | | } |
1166 | 1164 | ? UnmeaningfulReadError: zb |
1167 | 1165 |
384 | 384 | = $081B DEC $081F,X |
385 | 385 | = $081E RTS |
386 | 386 | |
387 | Compiling `compare`. | |
387 | Compiling 16-bit `cmp`. | |
388 | 388 | |
389 | 389 | | word za @ 60001 |
390 | 390 | | word zb : 3003 |
393 | 393 | | inputs za, zb |
394 | 394 | | trashes a, z, c, n |
395 | 395 | | { |
396 | | compare za, zb | |
396 | | cmp za, zb | |
397 | 397 | | } |
398 | 398 | = $080D LDA $081C |
399 | 399 | = $0810 CMP $EA61 |