1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-11-29 18:49:22 +00:00

Merge pull request #15 from catseye/16-bit-compare

16-bit compare instruction
This commit is contained in:
Chris Pressey 2018-11-29 17:46:48 +00:00 committed by GitHub
commit f99cf47661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 248 additions and 15 deletions

View File

@ -4,6 +4,8 @@ History of SixtyPical
0.18 0.18
---- ----
* `cmp` instruction can now perform a 16-bit unsigned comparison
of `word` memory locations (at the cost of trashing `a`.)
* Fixed pathological memory use in the lexical scanner - should * Fixed pathological memory use in the lexical scanner - should
be much less inefficient now when parsing large source files. be much less inefficient now when parsing large source files.

11
TODO.md
View File

@ -1,17 +1,6 @@
TODO for SixtyPical TODO for SixtyPical
=================== ===================
### 16-bit `cmp`
This is because we don't actually want `low` and `high` address operators
that turn `word` type into `byte`.
This is because this immediately makes things harder (that is, effectively
impossible) to analyze.
16-bit `cmp` also benefits from some special differences between `cmp`
and `sub` on 6502, so it would be nice to capture them.
### Save values to other-than-the-stack ### Save values to other-than-the-stack
Allow Allow

View File

@ -288,9 +288,9 @@ this mode is used.
copy <src-memory-location>, <dest-memory-location> copy <src-memory-location>, <dest-memory-location>
Reads from src and writes to dest. Differs from `st` in that is able to Reads from src and writes to dest. Differs from `ld` and `st` in that
copy more general types of data (for example, vectors,) and it trashes the it is able to copy more general types of data (for example, vectors,)
`z` and `n` flags and the `a` register. and it trashes the `z` and `n` flags and the `a` register.
* It is illegal if dest is read-only. * It is illegal if dest is read-only.
* It is illegal if dest does not occur in the WRITES of the current routine. * It is illegal if dest does not occur in the WRITES of the current routine.
@ -376,6 +376,9 @@ and initializing them afterwards.
dest and src continue to be initialized afterwards. dest and src continue to be initialized afterwards.
In addition, if dest is of `word` type, then src must also be of `word`
type, and in this case this instruction trashes the `a` register.
### dec ### ### dec ###
dec <dest-memory-location> dec <dest-memory-location>
@ -395,12 +398,24 @@ and initializing them afterwards.
Subtracts the contents of src from dest (without considering carry) but Subtracts the contents of src from dest (without considering carry) but
does not store the result anywhere, only sets the resulting flags. does not store the result anywhere, only sets the resulting flags.
This means that `z` is set if src and dest are equal,
and `c` is set if dest is greater than or equal to src
(`c` is unset if dest is less than src.)
* It is illegal if src OR dest is uninitialized. * It is illegal if src OR dest is uninitialized.
Affects n, z, and c flags, requiring that they be in the WRITES, Affects n, z, and c flags, requiring that they be in the WRITES,
and initializing them afterwards. and initializing them afterwards.
In addition, if dest is of `word` type, then src must also be of `word`
type, and in this case this instruction trashes the `a` register.
Note that `cmp` is not suitable for making a
signed comparison; this article, which mentions
techniques that a SixtyPical compiler could use to
implement `cmp`, also explains why that is:
[Beyond 8-bit Unsigned Comparisons, by Bruce Clark](http://www.6502.org/tutorials/compare_beyond.html).
### and, or, xor ### ### and, or, xor ###
and <dest-memory-location>, <src-memory-location> and <dest-memory-location>, <src-memory-location>

74
eg/rudiments/cmp-byte.60p Normal file
View File

@ -0,0 +1,74 @@
// Should print ENGGL
byte b
define chrout routine
inputs a
trashes a
@ 65490
define main routine
outputs b
trashes a, x, y, z, n, c, v
{
ld a, 40
st a, b
cmp a, b
if z {
ld a, 69 // E
call chrout
} else {
ld a, 78 // N
call chrout
}
ld a, 41
st a, b
ld a, 40
cmp a, b
if z {
ld a, 69 // E
call chrout
} else {
ld a, 78 // N
call chrout
}
ld a, 20
st a, b
ld a, 21
cmp a, b // 21 >= 20
if c {
ld a, 71 // G
call chrout
} else {
ld a, 76 // L
call chrout
}
ld a, 20
cmp a, b // 20 >= 20
if c {
ld a, 71 // G
call chrout
} else {
ld a, 76 // L
call chrout
}
ld a, 19
cmp a, b // 19 < 20
if c {
ld a, 71 // G
call chrout
} else {
ld a, 76 // L
call chrout
}
}

72
eg/rudiments/cmp-word.60p Normal file
View File

@ -0,0 +1,72 @@
// Should print ENGGL
word w1
word w2
define chrout routine
inputs a
trashes a
@ 65490
define main routine
outputs w1, w2
trashes a, x, y, z, n, c, v
{
copy 4000, w1
copy 4000, w2
cmp w1, w2
if z {
ld a, 69 // E
call chrout
} else {
ld a, 78 // N
call chrout
}
copy 4000, w1
copy 4001, w2
cmp w1, w2
if z {
ld a, 69 // E
call chrout
} else {
ld a, 78 // N
call chrout
}
copy 20002, w1
copy 20001, w2
cmp w1, w2 // 20002 >= 20001
if c {
ld a, 71 // G
call chrout
} else {
ld a, 76 // L
call chrout
}
copy 20001, w1
cmp w1, w2 // 20001 >= 20001
if c {
ld a, 71 // G
call chrout
} else {
ld a, 76 // L
call chrout
}
copy 20000, w1
cmp w1, w2 // 20000 < 20001
if c {
ld a, 71 // G
call chrout
} else {
ld a, 76 // L
call chrout
}
}

View File

@ -505,8 +505,12 @@ class Analyzer(object):
context.assert_meaningful(src, dest) context.assert_meaningful(src, dest)
if isinstance(src, IndexedRef): if isinstance(src, IndexedRef):
context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE) context.assert_types_for_read_table(instr, src, dest, TYPE_BYTE)
else: elif src.type == TYPE_BYTE:
self.assert_type(TYPE_BYTE, src, dest) self.assert_type(TYPE_BYTE, src, dest)
else:
self.assert_type(TYPE_WORD, src, dest)
context.set_touched(REG_A)
context.set_unmeaningful(REG_A)
context.set_written(FLAG_Z, FLAG_N, FLAG_C) context.set_written(FLAG_Z, FLAG_N, FLAG_C)
elif opcode == 'and': elif opcode == 'and':
if isinstance(src, IndexedRef): if isinstance(src, IndexedRef):

View File

@ -391,6 +391,17 @@ class Compiler(object):
def compile_cmp(self, instr, src, dest): def compile_cmp(self, instr, src, dest):
"""`instr` is only for reporting purposes""" """`instr` is only for reporting purposes"""
if isinstance(src, LocationRef) and src.type == TYPE_WORD:
src_label = self.get_label(src.name)
dest_label = self.get_label(dest.name)
self.emitter.emit(LDA(Absolute(dest_label)))
self.emitter.emit(CMP(Absolute(src_label)))
end_label = Label('end_label')
self.emitter.emit(BNE(Relative(end_label)))
self.emitter.emit(LDA(Absolute(Offset(dest_label, 1))))
self.emitter.emit(CMP(Absolute(Offset(src_label, 1))))
self.emitter.resolve_label(end_label)
return
cls = { cls = {
'a': CMP, 'a': CMP,
'x': CPX, 'x': CPX,

View File

@ -1118,6 +1118,52 @@ Some rudimentary tests for `cmp`.
| } | }
? UnmeaningfulReadError: a ? UnmeaningfulReadError: a
`cmp` can work on words. In this case, it trashes `a`.
| word za
| word zb
|
| define main routine
| inputs za, zb
| trashes a, z, c, n
| {
| cmp za, zb
| }
= ok
| word za
| word zb
|
| define main routine
| inputs za, zb
| trashes a, z, n
| {
| cmp za, zb
| }
? ForbiddenWriteError: c
| word za
| word zb
|
| define main routine
| inputs za, zb
| trashes z, c, n
| {
| cmp za, zb
| }
? ForbiddenWriteError: a
| word za
| word zb
|
| define main routine
| inputs za
| trashes z, c, n
| {
| cmp za, zb
| }
? UnmeaningfulReadError: zb
### and ### ### and ###
Some rudimentary tests for `and`. Some rudimentary tests for `and`.

View File

@ -385,6 +385,26 @@ Some instructions on tables. (3/3)
= $081B DEC $081F,X = $081B DEC $081F,X
= $081E RTS = $081E RTS
Compiling 16-bit `cmp`.
| word za @ 60001
| word zb : 3003
|
| define main routine
| inputs za, zb
| trashes a, z, c, n
| {
| cmp za, zb
| }
= $080D LDA $EA61
= $0810 CMP $081C
= $0813 BNE $081B
= $0815 LDA $EA62
= $0818 CMP $081D
= $081B RTS
= $081C .byte $BB
= $081D .byte $0B
Compiling `if`. Compiling `if`.
| define main routine | define main routine