1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-01 12:41:30 +00:00

Add some notes, a test, a BasicCheck, a canal Panama (err...)

This commit is contained in:
Cat's Eye Technologies 2014-04-01 13:01:27 +01:00
parent 6461aa8eff
commit f3c0320b2a
4 changed files with 138 additions and 7 deletions

View File

@ -1,15 +1,113 @@
SixtyPical
==========
SixtyPical is a very low-level programming language, similar to 6502 assembly,
with block structure and static analysis through abstract interpretation.
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, VIC-20, Apple ][, etc.
Many SixtyPical instructions map precisely to 6502 opcodes. However, SixtyPical
is not an assembly language. The programmer does not have total control over
the layout of code and data in memory. The language has a type system which
distinguishes addresses from non-addresses (16-bit values for which it does
not make sense to treat them as addresses.) Some 6502 opcodes have no
SixtyPical equivalent. Some SixtyPical instructions are named after 6502
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.
Concepts
--------
### Routines ###
Instead of the assembly-language subroutine, SixtyPical provides the _routine_
as the abstraction for a reusable sequence of code.
A routine may be called, or may be included inline, by another routine.
There is one top-level routine called `main` which represents the entire
program.
The instructions of a routine are analyzed using abstract interpretation.
One thing we specifically do is determine which registers and memory locations
are *not* affected by the routine.
If a register is not affected by a routine, then a caller of that routine may
assume that the value in that register is retained.
Of course, a routine may intentionally affect a register or memory location,
as an output. It must declare this. We're not there yet.
### Addresses ###
The body of a routine may not refer to an address literally. It must use
a symbol that was declared previously.
An address may be declared with `reserve`, which is like `.data` or `.bss`
in an assembler. This is an address into the program's data. It is global
to all routines.
An address may be declared with `locate`, which is like `.alias` in an
assembler, with the understanding that the value will be treated "like an
address." This is generally an address into the operating system or hardware
(e.g. kernal routine, I/O port, etc.)
Inside a routine, an address may be declared with `temporary`. This is like
`static` in C, except the value at that address is not guaranteed to be
retained between invokations of the routine. Such addresses may only be used
within the routine where they are declared. If analysis indicates that two
temporary addresses are never used simultaneously, they may be merged
to the same address.
An address knows if it is an address of a byte, of a word, or of a table.
### Blocks ###
Each routine is a block. It may be composed of inner blocks.
SixtyPical does not have instructions that map literally to the 6502 branch
instructions. Instead, each branch instruction has a corresponding
"if-then-else"-like construct with the same name as the branch.
The abstract states of the machine at each of the different block exits are
merged during analysis. If any register or memory location is treated
inconsistently (e.g. updated in one branch of the test, but not the other,)
that register cannot subsequently be used without a declaration to the effect
that we know what's going on. (This is all a bit fuzzy right now.)
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
| }
= Program [] [Routine "main" [NOP]]
= True
| routine frog {
| nop
| }
? missing 'main' routine
A program may reserve and assign.
| reserve word score
| assign word scram 4000
@ -17,7 +115,7 @@ SixtyPical
| lda scram
| cmp score
| }
= Program [Reserve "score" Word,Assign "scram" Word 4000] [Routine "main" [LOAD A "scram",CMP A "score"]]
= True
All declarations (`reserve`s and `assign`s) must come before any `routines`.

View File

@ -6,14 +6,15 @@ import System.IO
import System.Environment
import System.Exit
import SixtyPical.Parser (parseProgram)
import SixtyPical.Model
import SixtyPical.Context (checkProgram)
import SixtyPical.Parser (parseProgram)
import SixtyPical.BasicCheck (checkProgram)
import SixtyPical.Context (analyzeProgram)
-- -- -- -- driver -- -- -- --
usage = do
putStrLn "Usage: sixtypical (parse|check) filename.60pical"
putStrLn "Usage: sixtypical (parse|check|analyze) filename.60pical"
exitWith $ ExitFailure 1
main = do
@ -26,6 +27,8 @@ main = do
putStrLn $ show $ program
("check", Right program) -> do
putStrLn $ show $ checkProgram program
("analyze", Right program) -> do
putStrLn $ show $ analyzeProgram program
(_, Left problem) -> do
hPutStrLn stderr (show problem)
exitWith $ ExitFailure 1

View File

@ -0,0 +1,30 @@
-- encoding: UTF-8
module SixtyPical.BasicCheck where
import SixtyPical.Model
routineDeclared routName (Program _ routines) =
elem routName (map (getRoutineName) routines)
where
getRoutineName (Routine name _) = name
locationDeclared locName (Program decls _) =
elem locName (map (getLocationName) decls)
where
getLocationName (Assign name _ _) = name
getLocationName (Reserve name _) = name
mainDeclared program =
if
routineDeclared "main" program
then
True
else
error "missing 'main' routine"
allUsedLocationsDeclared p@(Program _ routines) =
True
checkProgram program =
mainDeclared program && allUsedLocationsDeclared program

View File

@ -41,7 +41,7 @@ mergeRoutCtxs routCtx calledRoutCtx =
-- -- -- -- static analyzer -- -- -- --
checkProgram (Program decls routines) =
analyzeProgram (Program decls routines) =
checkRoutines routines Map.empty
checkRoutines [] progCtx = progCtx