2014-04-04 12:06:12 +00:00
|
|
|
Anayzling SixtyPical Programs
|
|
|
|
=============================
|
|
|
|
|
|
|
|
-> Tests for functionality "Analyze SixtyPical program"
|
|
|
|
|
|
|
|
-> Functionality "Analyze SixtyPical program" is implemented by
|
|
|
|
-> shell command "bin/sixtypical analyze %(test-file)"
|
|
|
|
|
2014-04-04 13:22:19 +00:00
|
|
|
Analysis determines what storage locations have been modified by a
|
|
|
|
routine.
|
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| sta score
|
|
|
|
| }
|
2014-04-04 14:05:58 +00:00
|
|
|
= main ([])
|
2014-04-04 13:22:19 +00:00
|
|
|
= A: UpdatedWith (Immediate 4)
|
2014-04-04 18:06:58 +00:00
|
|
|
= NamedLocation Nothing "score": UpdatedWith A
|
2014-04-04 13:22:19 +00:00
|
|
|
|
2014-04-04 12:06:12 +00:00
|
|
|
A routine cannot expect registers which a called routine does not
|
2014-04-11 10:04:35 +00:00
|
|
|
preserve, to be preserved. We say the called routine "poisons" those
|
|
|
|
registers.
|
2014-04-04 12:06:12 +00:00
|
|
|
|
|
|
|
| assign byte border_colour 4000
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| lda #8
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| jsr update_score
|
|
|
|
| sta border_colour
|
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-04 12:06:12 +00:00
|
|
|
|
2014-04-11 10:04:35 +00:00
|
|
|
But if a called routine does preserve those registers, the caller can
|
|
|
|
continue to use them after calling the routine.
|
2014-04-04 12:06:12 +00:00
|
|
|
|
|
|
|
| assign byte border_colour 4000
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| ldx score
|
|
|
|
| inx
|
|
|
|
| stx score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| jsr update_score
|
|
|
|
| sta border_colour
|
|
|
|
| }
|
2014-04-04 14:05:58 +00:00
|
|
|
= main ([])
|
2014-04-04 13:22:19 +00:00
|
|
|
= A: UpdatedWith (Immediate 4)
|
|
|
|
= X: PoisonedWith (Immediate 1)
|
2014-04-04 18:06:58 +00:00
|
|
|
= NamedLocation Nothing "border_colour": UpdatedWith A
|
|
|
|
= NamedLocation Nothing "score": PoisonedWith X
|
2014-04-04 13:22:19 +00:00
|
|
|
=
|
2014-04-04 14:05:58 +00:00
|
|
|
= update_score ([])
|
2014-04-04 13:22:19 +00:00
|
|
|
= X: UpdatedWith (Immediate 1)
|
2014-04-04 18:06:58 +00:00
|
|
|
= NamedLocation Nothing "score": UpdatedWith X
|
2014-04-04 13:52:14 +00:00
|
|
|
|
2014-04-11 10:04:35 +00:00
|
|
|
Not only registers, but also named variables, can be poisoned by a called
|
|
|
|
routine.
|
2014-04-04 13:52:14 +00:00
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| lda #8
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| jsr update_score
|
2014-04-04 18:06:58 +00:00
|
|
|
| lda score
|
2014-04-04 13:52:14 +00:00
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'NamedLocation Nothing "score"'
|
2014-04-04 13:52:14 +00:00
|
|
|
|
2014-04-11 10:04:35 +00:00
|
|
|
Of course, the difference between poisoning and intentionally modifying a
|
|
|
|
storage location is a matter of intent. The solution to the above is to
|
|
|
|
explicitly notate `update_score` as an "output" of the routine.
|
2014-04-04 13:52:14 +00:00
|
|
|
|
|
|
|
| assign byte border_colour 4000
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score outputs (score)
|
|
|
|
| {
|
|
|
|
| lda #8
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| ldx score
|
|
|
|
| jsr update_score
|
|
|
|
| ldx score
|
|
|
|
| }
|
2014-04-04 14:05:58 +00:00
|
|
|
= main ([])
|
|
|
|
= A: PoisonedWith (Immediate 8)
|
2014-04-04 18:06:58 +00:00
|
|
|
= X: UpdatedWith (NamedLocation Nothing "score")
|
|
|
|
= NamedLocation Nothing "score": UpdatedWith A
|
2014-04-04 13:52:14 +00:00
|
|
|
=
|
2014-04-04 14:05:58 +00:00
|
|
|
= update_score ([NamedLocation Nothing "score"])
|
|
|
|
= A: UpdatedWith (Immediate 8)
|
2014-04-04 18:06:58 +00:00
|
|
|
= NamedLocation Nothing "score": UpdatedWith A
|
2014-04-04 16:14:31 +00:00
|
|
|
|
|
|
|
Routines can name registers as outputs.
|
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| lda #8
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| jsr update_score
|
|
|
|
| sta score
|
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-04 16:14:31 +00:00
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score outputs (.a)
|
|
|
|
| {
|
|
|
|
| lda #8
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| jsr update_score
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
= main ([])
|
|
|
|
= A: UpdatedWith (Immediate 8)
|
2014-04-04 18:06:58 +00:00
|
|
|
= NamedLocation Nothing "score": UpdatedWith A
|
2014-04-04 16:14:31 +00:00
|
|
|
=
|
|
|
|
= update_score ([A])
|
|
|
|
= A: UpdatedWith (Immediate 8)
|
2014-04-04 17:27:51 +00:00
|
|
|
|
|
|
|
If a location is poisoned in either branch of an `if`, it is poisoned
|
2014-04-12 12:07:46 +00:00
|
|
|
after the `if`. Note there are several tests for this.
|
2014-04-04 17:27:51 +00:00
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| if beq {
|
|
|
|
| lda #8
|
|
|
|
| } else {
|
|
|
|
| ldx #8
|
|
|
|
| }
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| jsr update_score
|
|
|
|
| sta score
|
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-04 17:27:51 +00:00
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| if beq {
|
|
|
|
| ldx #8
|
|
|
|
| } else {
|
|
|
|
| lda #8
|
|
|
|
| }
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| jsr update_score
|
|
|
|
| sta score
|
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-04 17:27:51 +00:00
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| lda #4
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| if beq {
|
|
|
|
| jsr update_score
|
|
|
|
| } else {
|
|
|
|
| ldx #3
|
|
|
|
| }
|
|
|
|
| sta score
|
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-04 17:27:51 +00:00
|
|
|
|
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| lda #4
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| if beq {
|
|
|
|
| ldx #3
|
|
|
|
| } else {
|
|
|
|
| jsr update_score
|
|
|
|
| }
|
|
|
|
| sta score
|
|
|
|
| }
|
2014-04-04 18:23:27 +00:00
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-04 18:06:58 +00:00
|
|
|
|
2014-04-04 18:50:34 +00:00
|
|
|
| reserve byte score
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| ldx #4
|
|
|
|
| stx score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #4
|
|
|
|
| if beq {
|
|
|
|
| jsr update_score
|
|
|
|
| } else {
|
|
|
|
| ldx #4
|
|
|
|
| }
|
|
|
|
| sta score
|
|
|
|
| }
|
|
|
|
= main ([])
|
|
|
|
= A: UpdatedWith (Immediate 4)
|
|
|
|
= X: PoisonedWith (Immediate 4)
|
|
|
|
= NamedLocation Nothing "score": UpdatedWith A
|
|
|
|
=
|
|
|
|
= update_score ([])
|
|
|
|
= X: UpdatedWith (Immediate 4)
|
|
|
|
= NamedLocation Nothing "score": UpdatedWith X
|
|
|
|
|
|
|
|
| assign word position $fb
|
|
|
|
| reserve byte value
|
|
|
|
|
|
|
|
|
| routine reset_position {
|
|
|
|
| lda #$00
|
|
|
|
| sta <position
|
|
|
|
| lda #$04
|
|
|
|
| sta >position
|
|
|
|
| }
|
|
|
|
|
|
2014-04-11 10:04:35 +00:00
|
|
|
| routine main {
|
2014-04-04 18:50:34 +00:00
|
|
|
| inc value
|
|
|
|
| lda value
|
|
|
|
| ldy #0
|
|
|
|
| sta (position), y
|
|
|
|
| if beq {
|
|
|
|
| jsr reset_position
|
|
|
|
| } else {
|
|
|
|
| }
|
|
|
|
| }
|
|
|
|
= main ([])
|
|
|
|
= A: PoisonedWith (Immediate 4)
|
|
|
|
= Y: UpdatedWith (Immediate 0)
|
2014-04-11 11:52:36 +00:00
|
|
|
= IndirectIndexed (NamedLocation Nothing "position") Y: UpdatedWith A
|
2014-04-04 18:50:34 +00:00
|
|
|
= NamedLocation Nothing "position": PoisonedWith A
|
|
|
|
= NamedLocation Nothing "value": UpdatedWith (Immediate 1)
|
|
|
|
=
|
|
|
|
= reset_position ([])
|
|
|
|
= A: UpdatedWith (Immediate 4)
|
|
|
|
= NamedLocation Nothing "position": UpdatedWith A
|
2014-04-11 10:04:35 +00:00
|
|
|
|
|
|
|
| assign word position $fb
|
|
|
|
| reserve byte value
|
|
|
|
|
|
|
|
|
| routine reset_position {
|
|
|
|
| lda #$00
|
|
|
|
| sta <position
|
|
|
|
| lda #$04
|
|
|
|
| sta >position
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| routine main {
|
|
|
|
| inc value
|
|
|
|
| lda value
|
|
|
|
| ldy #0
|
|
|
|
| sta (position), y
|
|
|
|
| if beq {
|
|
|
|
| jsr reset_position
|
|
|
|
| } else {
|
|
|
|
| }
|
|
|
|
| sta value
|
|
|
|
| }
|
|
|
|
? routine 'main' does not preserve 'A'
|
|
|
|
|
|
|
|
| assign word position $fb
|
|
|
|
| reserve byte value
|
|
|
|
|
|
|
|
|
| routine reset_position {
|
|
|
|
| lda #$00
|
|
|
|
| sta <position
|
|
|
|
| lda #$04
|
|
|
|
| sta >position
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| routine main {
|
|
|
|
| inc value
|
|
|
|
| lda value
|
|
|
|
| ldy #0
|
|
|
|
| sta (position), y
|
|
|
|
| jsr reset_position
|
|
|
|
| if beq {
|
|
|
|
| } else {
|
|
|
|
| sta value
|
|
|
|
| }
|
|
|
|
| }
|
|
|
|
? routine 'main' does not preserve 'A'
|
2014-04-12 12:07:46 +00:00
|
|
|
|
|
|
|
A storage location poisoned in a `repeat` continues to be poisoned
|
|
|
|
after the `repeat`.
|
|
|
|
|
|
|
|
| reserve byte value
|
|
|
|
|
|
|
|
|
| routine blah {
|
|
|
|
| lda #123
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #33
|
|
|
|
| ldy #255
|
|
|
|
| repeat bne {
|
|
|
|
| jsr blah
|
|
|
|
| dey
|
|
|
|
| }
|
|
|
|
| sta value
|
|
|
|
| }
|
|
|
|
? routine 'main' does not preserve 'A'
|
|
|
|
|
|
|
|
Oh, here's a tricky one. The accumulator isn't poisoned on the first run
|
|
|
|
through the `repeat`, but it **is** on the second run through. We handle
|
|
|
|
this simply by abstractly interpreting the `repeat`'s block twice — the
|
|
|
|
second time in the context of having already interpreted it once.
|
|
|
|
|
|
|
|
| reserve byte value
|
|
|
|
|
|
|
|
|
| routine blah {
|
|
|
|
| lda #123
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| lda #33
|
|
|
|
| ldy #255
|
|
|
|
| repeat bne {
|
|
|
|
| sta value
|
|
|
|
| jsr blah
|
|
|
|
| dey
|
|
|
|
| }
|
|
|
|
| }
|
|
|
|
? routine 'main' does not preserve 'A'
|
|
|
|
|
|
|
|
Poisoning a high byte or low byte of a word poisons the whole word.
|
|
|
|
|
|
|
|
| reserve word score
|
|
|
|
| reserve byte temp
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| ldx #4
|
|
|
|
| stx <score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| jsr update_score
|
|
|
|
| lda >score
|
|
|
|
| sta temp
|
|
|
|
| }
|
|
|
|
? routine 'main' does not preserve 'NamedLocation Nothing "score"'
|
|
|
|
|
|
|
|
| reserve word score
|
|
|
|
| reserve byte temp
|
|
|
|
| routine update_score
|
|
|
|
| {
|
|
|
|
| ldx #4
|
|
|
|
| stx >score
|
|
|
|
| }
|
|
|
|
| routine main {
|
|
|
|
| jsr update_score
|
|
|
|
| lda <score
|
|
|
|
| sta temp
|
|
|
|
| }
|
|
|
|
? routine 'main' does not preserve 'NamedLocation Nothing "score"'
|