Merge branch 'develop-0.18' of https://github.com/catseye/SixtyPical into goto-at-end-of-block
Chris Pressey
6 years ago
6 | 6 | * Syntactically, `goto` may only appear at the end of a block. |
7 | 7 | It need no longer be the final instruction in a routine, |
8 | 8 | as long as the type context is consistent at every exit. |
9 | * `cmp` instruction can now perform a 16-bit unsigned comparison | |
10 | of `word` memory locations (at the cost of trashing `a`.) | |
9 | 11 | * Fixed pathological memory use in the lexical scanner - should |
10 | 12 | be much less inefficient now when parsing large source files. |
11 | 13 |
0 | 0 | TODO for SixtyPical |
1 | 1 | =================== |
2 | ||
3 | ### 16-bit `cmp` | |
4 | ||
5 | This is because we don't actually want `low` and `high` address operators | |
6 | that turn `word` type into `byte`. | |
7 | ||
8 | This is because this immediately makes things harder (that is, effectively | |
9 | impossible) to analyze. | |
10 | ||
11 | 16-bit `cmp` also benefits from some special differences between `cmp` | |
12 | and `sub` on 6502, so it would be nice to capture them. | |
13 | 2 | |
14 | 3 | ### Save values to other-than-the-stack |
15 | 4 |
287 | 287 | |
288 | 288 | copy <src-memory-location>, <dest-memory-location> |
289 | 289 | |
290 | Reads from src and writes to dest. Differs from `st` in that is able to | |
291 | copy more general types of data (for example, vectors,) and it trashes the | |
292 | `z` and `n` flags and the `a` register. | |
290 | Reads from src and writes to dest. Differs from `ld` and `st` in that | |
291 | it is able to copy more general types of data (for example, vectors,) | |
292 | and it trashes the `z` and `n` flags and the `a` register. | |
293 | 293 | |
294 | 294 | * It is illegal if dest is read-only. |
295 | 295 | * It is illegal if dest does not occur in the WRITES of the current routine. |
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> |
394 | 397 | |
395 | 398 | Subtracts the contents of src from dest (without considering carry) but |
396 | 399 | does not store the result anywhere, only sets the resulting flags. |
400 | This means that `z` is set if src and dest are equal, | |
401 | and `c` is set if dest is greater than or equal to src | |
402 | (`c` is unset if dest is less than src.) | |
397 | 403 | |
398 | 404 | * It is illegal if src OR dest is uninitialized. |
399 | 405 | |
400 | 406 | Affects n, z, and c flags, requiring that they be in the WRITES, |
401 | 407 | and initializing them afterwards. |
408 | ||
409 | In addition, if dest is of `word` type, then src must also be of `word` | |
410 | type, and in this case this instruction trashes the `a` register. | |
411 | ||
412 | Note that `cmp` is not suitable for making a | |
413 | signed comparison; this article, which mentions | |
414 | techniques that a SixtyPical compiler could use to | |
415 | implement `cmp`, also explains why that is: | |
416 | [Beyond 8-bit Unsigned Comparisons, by Bruce Clark](http://www.6502.org/tutorials/compare_beyond.html). | |
402 | 417 | |
403 | 418 | ### and, or, xor ### |
404 | 419 |
0 | // Should print ENGGL | |
1 | ||
2 | byte b | |
3 | ||
4 | define chrout routine | |
5 | inputs a | |
6 | trashes a | |
7 | @ 65490 | |
8 | ||
9 | define main routine | |
10 | outputs b | |
11 | trashes a, x, y, z, n, c, v | |
12 | { | |
13 | ld a, 40 | |
14 | st a, b | |
15 | ||
16 | cmp a, b | |
17 | if z { | |
18 | ld a, 69 // E | |
19 | call chrout | |
20 | } else { | |
21 | ld a, 78 // N | |
22 | call chrout | |
23 | } | |
24 | ||
25 | ld a, 41 | |
26 | st a, b | |
27 | ld a, 40 | |
28 | ||
29 | cmp a, b | |
30 | if z { | |
31 | ld a, 69 // E | |
32 | call chrout | |
33 | } else { | |
34 | ld a, 78 // N | |
35 | call chrout | |
36 | } | |
37 | ||
38 | ld a, 20 | |
39 | st a, b | |
40 | ||
41 | ld a, 21 | |
42 | ||
43 | cmp a, b // 21 >= 20 | |
44 | if c { | |
45 | ld a, 71 // G | |
46 | call chrout | |
47 | } else { | |
48 | ld a, 76 // L | |
49 | call chrout | |
50 | } | |
51 | ||
52 | ld a, 20 | |
53 | ||
54 | cmp a, b // 20 >= 20 | |
55 | if c { | |
56 | ld a, 71 // G | |
57 | call chrout | |
58 | } else { | |
59 | ld a, 76 // L | |
60 | call chrout | |
61 | } | |
62 | ||
63 | ld a, 19 | |
64 | ||
65 | cmp a, b // 19 < 20 | |
66 | if c { | |
67 | ld a, 71 // G | |
68 | call chrout | |
69 | } else { | |
70 | ld a, 76 // L | |
71 | call chrout | |
72 | } | |
73 | } |
0 | // Should print ENGGL | |
1 | ||
2 | word w1 | |
3 | word w2 | |
4 | ||
5 | define chrout routine | |
6 | inputs a | |
7 | trashes a | |
8 | @ 65490 | |
9 | ||
10 | define main routine | |
11 | outputs w1, w2 | |
12 | trashes a, x, y, z, n, c, v | |
13 | { | |
14 | copy 4000, w1 | |
15 | copy 4000, w2 | |
16 | ||
17 | cmp w1, w2 | |
18 | if z { | |
19 | ld a, 69 // E | |
20 | call chrout | |
21 | } else { | |
22 | ld a, 78 // N | |
23 | call chrout | |
24 | } | |
25 | ||
26 | copy 4000, w1 | |
27 | copy 4001, w2 | |
28 | ||
29 | cmp w1, w2 | |
30 | if z { | |
31 | ld a, 69 // E | |
32 | call chrout | |
33 | } else { | |
34 | ld a, 78 // N | |
35 | call chrout | |
36 | } | |
37 | ||
38 | copy 20002, w1 | |
39 | copy 20001, w2 | |
40 | ||
41 | cmp w1, w2 // 20002 >= 20001 | |
42 | if c { | |
43 | ld a, 71 // G | |
44 | call chrout | |
45 | } else { | |
46 | ld a, 76 // L | |
47 | call chrout | |
48 | } | |
49 | ||
50 | copy 20001, w1 | |
51 | ||
52 | cmp w1, w2 // 20001 >= 20001 | |
53 | if c { | |
54 | ld a, 71 // G | |
55 | call chrout | |
56 | } else { | |
57 | ld a, 76 // L | |
58 | call chrout | |
59 | } | |
60 | ||
61 | copy 20000, w1 | |
62 | ||
63 | cmp w1, w2 // 20000 < 20001 | |
64 | if c { | |
65 | ld a, 71 // G | |
66 | call chrout | |
67 | } else { | |
68 | ld a, 76 // L | |
69 | call chrout | |
70 | } | |
71 | } |
560 | 560 | context.assert_meaningful(src, dest) |
561 | 561 | if isinstance(src, IndexedRef): |
562 | 562 | context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) |
563 | else: | |
563 | elif src.type == TYPE_BYTE: | |
564 | 564 | self.assert_type(TYPE_BYTE, src, dest) |
565 | else: | |
566 | self.assert_type(TYPE_WORD, src, dest) | |
567 | context.set_touched(REG_A) | |
568 | context.set_unmeaningful(REG_A) | |
565 | 569 | context.set_written(FLAG_Z, FLAG_N, FLAG_C) |
566 | 570 | elif opcode == 'and': |
567 | 571 | if isinstance(src, IndexedRef): |
390 | 390 | |
391 | 391 | def compile_cmp(self, instr, src, dest): |
392 | 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(dest_label))) | |
397 | self.emitter.emit(CMP(Absolute(src_label))) | |
398 | end_label = Label('end_label') | |
399 | self.emitter.emit(BNE(Relative(end_label))) | |
400 | self.emitter.emit(LDA(Absolute(Offset(dest_label, 1)))) | |
401 | self.emitter.emit(CMP(Absolute(Offset(src_label, 1)))) | |
402 | self.emitter.resolve_label(end_label) | |
403 | return | |
393 | 404 | cls = { |
394 | 405 | 'a': CMP, |
395 | 406 | 'x': CPX, |
1116 | 1116 | | cmp a, 4 |
1117 | 1117 | | } |
1118 | 1118 | ? UnmeaningfulReadError: a |
1119 | ||
1120 | `cmp` can work on words. In this case, it trashes `a`. | |
1121 | ||
1122 | | word za | |
1123 | | word zb | |
1124 | | | |
1125 | | define main routine | |
1126 | | inputs za, zb | |
1127 | | trashes a, z, c, n | |
1128 | | { | |
1129 | | cmp za, zb | |
1130 | | } | |
1131 | = ok | |
1132 | ||
1133 | | word za | |
1134 | | word zb | |
1135 | | | |
1136 | | define main routine | |
1137 | | inputs za, zb | |
1138 | | trashes a, z, n | |
1139 | | { | |
1140 | | cmp za, zb | |
1141 | | } | |
1142 | ? ForbiddenWriteError: c | |
1143 | ||
1144 | | word za | |
1145 | | word zb | |
1146 | | | |
1147 | | define main routine | |
1148 | | inputs za, zb | |
1149 | | trashes z, c, n | |
1150 | | { | |
1151 | | cmp za, zb | |
1152 | | } | |
1153 | ? ForbiddenWriteError: a | |
1154 | ||
1155 | | word za | |
1156 | | word zb | |
1157 | | | |
1158 | | define main routine | |
1159 | | inputs za | |
1160 | | trashes z, c, n | |
1161 | | { | |
1162 | | cmp za, zb | |
1163 | | } | |
1164 | ? UnmeaningfulReadError: zb | |
1119 | 1165 | |
1120 | 1166 | ### and ### |
1121 | 1167 |
384 | 384 | = $081B DEC $081F,X |
385 | 385 | = $081E RTS |
386 | 386 | |
387 | Compiling 16-bit `cmp`. | |
388 | ||
389 | | word za @ 60001 | |
390 | | word zb : 3003 | |
391 | | | |
392 | | define main routine | |
393 | | inputs za, zb | |
394 | | trashes a, z, c, n | |
395 | | { | |
396 | | cmp za, zb | |
397 | | } | |
398 | = $080D LDA $EA61 | |
399 | = $0810 CMP $081C | |
400 | = $0813 BNE $081B | |
401 | = $0815 LDA $EA62 | |
402 | = $0818 CMP $081D | |
403 | = $081B RTS | |
404 | = $081C .byte $BB | |
405 | = $081D .byte $0B | |
406 | ||
387 | 407 | Compiling `if`. |
388 | 408 | |
389 | 409 | | define main routine |