1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-02 03:41:28 +00:00

Move tests to own files in doc.

This commit is contained in:
Cat's Eye Technologies 2014-04-03 13:32:06 +01:00
parent cfbc840195
commit d8d3b283bf
5 changed files with 478 additions and 474 deletions

View File

@ -2,9 +2,9 @@ SixtyPical
==========
SixtyPical is a very low-level programming language, similar to 6502 assembly,
with block structure and static analysis through abstract interpretation.
with static analysis through type-checking and abstract interpretation.
It is a work in progress, currently at the proof-of-concept stage.
It is a **work in progress**, currently at the **proof-of-concept** stage.
It is expected that a common use case for SixtyPical would be retroprogramming
for the Commodore 64 and other 6502-based computers such as the VIC-20.
@ -19,8 +19,8 @@ opcodes, but generate slightly different (safer, but intuitively related)
sequences of opcodes. Et cetera.
`sixtypical` is the reference implementation of SixtyPical. It is written in
Haskell. It can currently parse and analyze a SixtyPical program, and will
eventually be able to compile it to an Ophis assembler listing.
Haskell. It can currently parse and check a SixtyPical program, and can
emit an Ophis assembler listing for it.
Concepts
--------
@ -131,11 +131,13 @@ Note to self, the `pl` opcodes *do* change flags.
Instruction Support so far
--------------------------
A `X` indicates unsupported. A `!` indicates will-not-support.
A `X` indicates unsupported.
Funny syntax indicates use of a special form.
In these, `absolute` must be a `reserve`d or `locate`d address.
`immediate` must be a literal decimal or hexadecimal number
(or in future, a declared constant.)
.
adc #immediate
@ -268,472 +270,4 @@ TODO
* give length for tables, must be there for reserved
* Character tables ("strings" to everybody else)
* Work out the analyses again and document them
* lda wordaddress --> is not legal. use lda <wordaddr or lda >wordaddr
* Addressing modes; rename instructions to match
Tests
-----
-> Tests for functionality "Parse SixtyPical program"
-> Functionality "Parse SixtyPical program" is implemented by
-> shell command "bin/sixtypical parse %(test-file)"
-> Tests for functionality "Check SixtyPical program"
-> Functionality "Check SixtyPical program" is implemented by
-> shell command "bin/sixtypical check %(test-file)"
`main` must be present.
| routine main {
| nop
| }
= True
| routine frog {
| nop
| }
? missing 'main' routine
A comment may appear at the start of a block.
| routine main {
| ; this program does nothing
| nop
| }
= True
A comment may appear after each command.
| routine main {
| lda #1 ; we assemble the fnord using
| ldx #1 ; multiple lorem ipsums which
| ldy #1
| lda #1 ; we
| ldx #1 ; found under the bridge by the old mill yesterday
| }
= True
A comment may appear after each declaration.
| reserve byte lives ; fnord
| assign byte gdcol 647 ; fnord
| external blastoff 4 ; fnnnnnnnnnnnnnnnnfffffffff
|
| routine main {
| nop
| }
= True
A program may `reserve` and `assign`.
| reserve byte lives
| assign byte gdcol 647
| reserve word score
| assign word memstr 641
| reserve vector v
| assign vector cinv 788
| reserve byte table frequencies
| assign byte table screen 1024
| routine main {
| nop
| }
= True
A program may declare an `external`.
| external blastoff 49152
| routine main {
| jsr blastoff
| }
= True
All declarations (`reserve`s and `assign`s) must come before any `routines`.
| routine main {
| lda score
| }
| reserve word score
? expecting "routine"
All locations used in all routines must be declared first.
| reserve byte score
| routine main {
| lda score
| cmp screen
| }
? undeclared location
Even in inner blocks.
| reserve byte score
| assign byte screen 1024
| routine main {
| lda score
| cmp screen
| if beq {
| lda score
| } else {
| lda fnord
| }
| }
? undeclared location
All routines jsr'ed to must be defined, or external.
| routine main {
| jsr blastoff
| }
? undeclared routine
No duplicate location names in declarations.
| reserve word score
| assign word score 4000
| routine main {
| nop
| }
? duplicate location name
No duplicate routine names.
| routine main {
| nop
| }
| routine main {
| txa
| }
? duplicate routine name
No duplicate routine names, including externals.
| external main 7000
| routine main {
| nop
| }
? duplicate routine name
We can jump to a vector.
| reserve vector blah
| routine main {
| jmp blah
| }
= True
We can't jump to a word.
| reserve word blah
| routine main {
| jmp blah
| }
? jmp to non-vector
We can't jump to a byte.
| assign byte screen 1024
| routine main {
| jmp screen
| }
? jmp to non-vector
We can absolute-indexed a byte table.
| assign byte table screen 1024
| routine main {
| sta screen, x
| }
= True
We cannot absolute-indexed a byte.
| assign byte screen 1024
| routine main {
| sta screen, x
| }
? indexed access of non-table
We cannot absolute-indexed a word.
| assign word screen 1024
| routine main {
| sta screen, x
| }
? indexed access of non-table
We cannot absolute access a word.
| assign word screen 1024
| routine main {
| ldx screen
| }
? incompatible types 'Word' and 'Byte'
No, not even with `ora`.
| assign word screen 1024
| routine main {
| ora screen
| }
? incompatible types 'Byte' and 'Word'
Instead, we have to do this.
| assign word screen 1024
| routine main {
| lda <screen
| lda >screen
| }
= True
We cannot absolute access a vector.
| assign vector screen 1024
| routine main {
| lda screen
| }
? incompatible types 'Vector' and 'Byte'
-> Tests for functionality "Emit ASM for SixtyPical program"
-> Functionality "Emit ASM for SixtyPical program" is implemented by
-> shell command "bin/sixtypical emit %(test-file)"
| reserve word vword
| reserve byte vbyte
| assign byte table table 1024
| routine main {
| lda #4
| ldx #0
| ldy #$FF
| lda vbyte
| lda table, x
| lda table, y
| lda (vword), y
| lda <vword
| lda >vword
| inc vbyte
| tax
| inx
| dex
| stx vbyte
| tay
| iny
| dey
| sty vbyte
| cmp vbyte
| cmp #30
| cmp <vword
| cmp >vword
| ldx vbyte
| cpx vbyte
| cpx #31
| txa
| ldy vbyte
| cpy vbyte
| cpy #32
| tya
| sta vbyte
| sta table, x
| sta table, y
| sta (vword), y
| sta <vword
| sta >vword
| dec vbyte
| clc
| cld
| clv
| sec
| sed
| adc #8
| adc vbyte
| and #8
| and vbyte
| sbc #8
| sbc vbyte
| ora #8
| ora vbyte
| }
= main:
= lda #4
= ldx #0
= ldy #255
= lda vbyte
= lda table, x
= lda table, y
= lda (vword), y
= lda vword
= lda vword+1
= inc vbyte
= tax
= inx
= dex
= stx vbyte
= tay
= iny
= dey
= sty vbyte
= cmp vbyte
= cmp #30
= cmp vword
= cmp vword+1
= ldx vbyte
= cpx vbyte
= cpx #31
= txa
= ldy vbyte
= cpy vbyte
= cpy #32
= tya
= sta vbyte
= sta table, x
= sta table, y
= sta (vword), y
= sta vword
= sta vword+1
= dec vbyte
= clc
= cld
= clv
= sec
= sed
= adc #8
= adc vbyte
= and #8
= and vbyte
= sbc #8
= sbc vbyte
= ora #8
= ora vbyte
= rts
=
= vword: .word 0
= vbyte: .byte 0
= .alias table 1024
| assign byte screen $0400
| routine main {
| lda screen
| cmp screen
| if beq {
| tax
| } else {
| tay
| }
| sta screen
| }
= main:
= lda screen
= cmp screen
= BEQ _label_1
= tay
= jmp _past_1
= _label_1:
= tax
= _past_1:
= sta screen
= rts
=
= .alias screen 1024
| assign byte screen 1024
| reserve byte zero
| routine main {
| ldy zero
| repeat bne {
| inc screen
| dey
| cpy zero
| }
| sty screen
| }
= main:
= ldy zero
=
= _repeat_1:
= inc screen
= dey
= cpy zero
= BNE _repeat_1
= sty screen
= rts
=
= .alias screen 1024
= zero: .byte 0
Nested ifs.
| routine main {
| if beq {
| if bcc {
| lda #0
| } else {
| if bvs {
| lda #1
| } else {
| lda #2
| }
| }
| } else {
| lda #3
| }
| }
= main:
= BEQ _label_3
= lda #3
= jmp _past_3
= _label_3:
= BCC _label_2
= BVS _label_1
= lda #2
= jmp _past_1
= _label_1:
= lda #1
= _past_1:
= jmp _past_2
= _label_2:
= lda #0
= _past_2:
= _past_3:
= rts
Installing an interrupt handler (at the Kernal level, i.e. with CINV)
| assign byte screen 1024
| assign vector cinv 788
| reserve vector save_cinv
|
| routine main {
| sei {
| copy vector cinv to save_cinv
| copy routine our_cinv to cinv
| }
| }
|
| routine our_cinv {
| inc screen
| jmp save_cinv
| }
= main:
= sei
= lda cinv
= sta save_cinv
= lda cinv+1
= sta save_cinv+1
= lda #<our_cinv
= sta cinv
= lda #>our_cinv
= sta cinv+1
= cli
= rts
=
= our_cinv:
= inc screen
= jmp (save_cinv)
= rts
=
= .alias screen 1024
= .alias cinv 788
= save_cinv: .word 0

224
doc/Checking.markdown Normal file
View File

@ -0,0 +1,224 @@
Checking SixtyPical Programs
============================
-> Tests for functionality "Parse SixtyPical program"
-> Functionality "Parse SixtyPical program" is implemented by
-> shell command "bin/sixtypical parse %(test-file)"
-> Tests for functionality "Check SixtyPical program"
-> Functionality "Check SixtyPical program" is implemented by
-> shell command "bin/sixtypical check %(test-file)"
`main` must be present.
| routine main {
| nop
| }
= True
| routine frog {
| nop
| }
? missing 'main' routine
A comment may appear at the start of a block.
| routine main {
| ; this program does nothing
| nop
| }
= True
A comment may appear after each command.
| routine main {
| lda #1 ; we assemble the fnord using
| ldx #1 ; multiple lorem ipsums which
| ldy #1
| lda #1 ; we
| ldx #1 ; found under the bridge by the old mill yesterday
| }
= True
A comment may appear after each declaration.
| reserve byte lives ; fnord
| assign byte gdcol 647 ; fnord
| external blastoff 4 ; fnnnnnnnnnnnnnnnnfffffffff
|
| routine main {
| nop
| }
= True
A program may `reserve` and `assign`.
| reserve byte lives
| assign byte gdcol 647
| reserve word score
| assign word memstr 641
| reserve vector v
| assign vector cinv 788
| reserve byte table frequencies
| assign byte table screen 1024
| routine main {
| nop
| }
= True
A program may declare an `external`.
| external blastoff 49152
| routine main {
| jsr blastoff
| }
= True
All declarations (`reserve`s and `assign`s) must come before any `routines`.
| routine main {
| lda score
| }
| reserve word score
? expecting "routine"
All locations used in all routines must be declared first.
| reserve byte score
| routine main {
| lda score
| cmp screen
| }
? undeclared location
Even in inner blocks.
| reserve byte score
| assign byte screen 1024
| routine main {
| lda score
| cmp screen
| if beq {
| lda score
| } else {
| lda fnord
| }
| }
? undeclared location
All routines jsr'ed to must be defined, or external.
| routine main {
| jsr blastoff
| }
? undeclared routine
No duplicate location names in declarations.
| reserve word score
| assign word score 4000
| routine main {
| nop
| }
? duplicate location name
No duplicate routine names.
| routine main {
| nop
| }
| routine main {
| txa
| }
? duplicate routine name
No duplicate routine names, including externals.
| external main 7000
| routine main {
| nop
| }
? duplicate routine name
We can jump to a vector.
| reserve vector blah
| routine main {
| jmp blah
| }
= True
We can't jump to a word.
| reserve word blah
| routine main {
| jmp blah
| }
? jmp to non-vector
We can't jump to a byte.
| assign byte screen 1024
| routine main {
| jmp screen
| }
? jmp to non-vector
We can absolute-indexed a byte table.
| assign byte table screen 1024
| routine main {
| sta screen, x
| }
= True
We cannot absolute-indexed a byte.
| assign byte screen 1024
| routine main {
| sta screen, x
| }
? indexed access of non-table
We cannot absolute-indexed a word.
| assign word screen 1024
| routine main {
| sta screen, x
| }
? indexed access of non-table
We cannot absolute access a word.
| assign word screen 1024
| routine main {
| ldx screen
| }
? incompatible types 'Word' and 'Byte'
No, not even with `ora`.
| assign word screen 1024
| routine main {
| ora screen
| }
? incompatible types 'Byte' and 'Word'
Instead, we have to do this.
| assign word screen 1024
| routine main {
| lda <screen
| lda >screen
| }
= True
We cannot absolute access a vector.
| assign vector screen 1024
| routine main {
| lda screen
| }
? incompatible types 'Vector' and 'Byte'

244
doc/Emitting.markdown Normal file
View File

@ -0,0 +1,244 @@
Emitting Ophis from SixtyPical Programs
=======================================
-> Tests for functionality "Emit ASM for SixtyPical program"
-> Functionality "Emit ASM for SixtyPical program" is implemented by
-> shell command "bin/sixtypical emit %(test-file)"
| reserve word vword
| reserve byte vbyte
| assign byte table table 1024
| routine main {
| lda #4
| ldx #0
| ldy #$FF
| lda vbyte
| lda table, x
| lda table, y
| lda (vword), y
| lda <vword
| lda >vword
| inc vbyte
| tax
| inx
| dex
| stx vbyte
| tay
| iny
| dey
| sty vbyte
| cmp vbyte
| cmp #30
| cmp <vword
| cmp >vword
| ldx vbyte
| cpx vbyte
| cpx #31
| txa
| ldy vbyte
| cpy vbyte
| cpy #32
| tya
| sta vbyte
| sta table, x
| sta table, y
| sta (vword), y
| sta <vword
| sta >vword
| dec vbyte
| clc
| cld
| clv
| sec
| sed
| adc #8
| adc vbyte
| and #8
| and vbyte
| sbc #8
| sbc vbyte
| ora #8
| ora vbyte
| }
= main:
= lda #4
= ldx #0
= ldy #255
= lda vbyte
= lda table, x
= lda table, y
= lda (vword), y
= lda vword
= lda vword+1
= inc vbyte
= tax
= inx
= dex
= stx vbyte
= tay
= iny
= dey
= sty vbyte
= cmp vbyte
= cmp #30
= cmp vword
= cmp vword+1
= ldx vbyte
= cpx vbyte
= cpx #31
= txa
= ldy vbyte
= cpy vbyte
= cpy #32
= tya
= sta vbyte
= sta table, x
= sta table, y
= sta (vword), y
= sta vword
= sta vword+1
= dec vbyte
= clc
= cld
= clv
= sec
= sed
= adc #8
= adc vbyte
= and #8
= and vbyte
= sbc #8
= sbc vbyte
= ora #8
= ora vbyte
= rts
=
= vword: .word 0
= vbyte: .byte 0
= .alias table 1024
| assign byte screen $0400
| routine main {
| lda screen
| cmp screen
| if beq {
| tax
| } else {
| tay
| }
| sta screen
| }
= main:
= lda screen
= cmp screen
= BEQ _label_1
= tay
= jmp _past_1
= _label_1:
= tax
= _past_1:
= sta screen
= rts
=
= .alias screen 1024
| assign byte screen 1024
| reserve byte zero
| routine main {
| ldy zero
| repeat bne {
| inc screen
| dey
| cpy zero
| }
| sty screen
| }
= main:
= ldy zero
=
= _repeat_1:
= inc screen
= dey
= cpy zero
= BNE _repeat_1
= sty screen
= rts
=
= .alias screen 1024
= zero: .byte 0
Nested ifs.
| routine main {
| if beq {
| if bcc {
| lda #0
| } else {
| if bvs {
| lda #1
| } else {
| lda #2
| }
| }
| } else {
| lda #3
| }
| }
= main:
= BEQ _label_3
= lda #3
= jmp _past_3
= _label_3:
= BCC _label_2
= BVS _label_1
= lda #2
= jmp _past_1
= _label_1:
= lda #1
= _past_1:
= jmp _past_2
= _label_2:
= lda #0
= _past_2:
= _past_3:
= rts
Installing an interrupt handler (at the Kernal level, i.e. with CINV)
| assign byte screen 1024
| assign vector cinv 788
| reserve vector save_cinv
|
| routine main {
| sei {
| copy vector cinv to save_cinv
| copy routine our_cinv to cinv
| }
| }
|
| routine our_cinv {
| inc screen
| jmp save_cinv
| }
= main:
= sei
= lda cinv
= sta save_cinv
= lda cinv+1
= sta save_cinv+1
= lda #<our_cinv
= sta cinv
= lda #>our_cinv
= sta cinv+1
= cli
= rts
=
= our_cinv:
= inc screen
= jmp (save_cinv)
= rts
=
= .alias screen 1024
= .alias cinv 788
= save_cinv: .word 0

View File

@ -1,5 +1,6 @@
#!/bin/sh
./build.sh || exit 1
bin/sixtypical emit $1 > tmp.oph || exit 1
cat lib/basic_header.oph tmp.oph > tmp2.oph || exit 1
ophis tmp2.oph -o tmp.prg || exit 1

View File

@ -1,3 +1,4 @@
#!/bin/sh
./build.sh && falderal --substring-error README.markdown
FILES="doc/Checking.markdown doc/Emitting.markdown"
./build.sh && falderal --substring-error ${FILES}