From f3c0320b2aa20720b7466ae268bc57452cad9b9c Mon Sep 17 00:00:00 2001 From: Cat's Eye Technologies Date: Tue, 1 Apr 2014 13:01:27 +0100 Subject: [PATCH] Add some notes, a test, a BasicCheck, a canal Panama (err...) --- README.markdown | 104 ++++++++++++++++++++++++++++++++++- src/Main.hs | 9 ++- src/SixtyPical/BasicCheck.hs | 30 ++++++++++ src/SixtyPical/Context.hs | 2 +- 4 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 src/SixtyPical/BasicCheck.hs diff --git a/README.markdown b/README.markdown index 903c095..3a7c90f 100644 --- a/README.markdown +++ b/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`. diff --git a/src/Main.hs b/src/Main.hs index 793beca..e905bf6 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -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 diff --git a/src/SixtyPical/BasicCheck.hs b/src/SixtyPical/BasicCheck.hs new file mode 100644 index 0000000..0b0092f --- /dev/null +++ b/src/SixtyPical/BasicCheck.hs @@ -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 diff --git a/src/SixtyPical/Context.hs b/src/SixtyPical/Context.hs index b67adee..327c446 100644 --- a/src/SixtyPical/Context.hs +++ b/src/SixtyPical/Context.hs @@ -41,7 +41,7 @@ mergeRoutCtxs routCtx calledRoutCtx = -- -- -- -- static analyzer -- -- -- -- -checkProgram (Program decls routines) = +analyzeProgram (Program decls routines) = checkRoutines routines Map.empty checkRoutines [] progCtx = progCtx