mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-03-12 22:30:24 +00:00
Add some notes, a test, a BasicCheck, a canal Panama (err...)
This commit is contained in:
parent
6461aa8eff
commit
f3c0320b2a
104
README.markdown
104
README.markdown
@ -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`.
|
||||
|
||||
|
@ -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
|
||||
|
30
src/SixtyPical/BasicCheck.hs
Normal file
30
src/SixtyPical/BasicCheck.hs
Normal 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
|
@ -41,7 +41,7 @@ mergeRoutCtxs routCtx calledRoutCtx =
|
||||
|
||||
-- -- -- -- static analyzer -- -- -- --
|
||||
|
||||
checkProgram (Program decls routines) =
|
||||
analyzeProgram (Program decls routines) =
|
||||
checkRoutines routines Map.empty
|
||||
|
||||
checkRoutines [] progCtx = progCtx
|
||||
|
Loading…
x
Reference in New Issue
Block a user