diff --git a/README.markdown b/README.markdown index 214883d..4445bf1 100644 --- a/README.markdown +++ b/README.markdown @@ -7,7 +7,7 @@ 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. +for the Commodore 64 and other 6502-based computers such as the VIC-20. Many SixtyPical instructions map precisely to 6502 opcodes. However, SixtyPical is not an assembly language. The programmer does not have total control over @@ -73,18 +73,25 @@ An address knows what kind of data is stored at the address: * `byte`: an 8-bit byte. not part of a word. not to be used as an address. (could be an index though.) * `word`: a 16-bit word. not to be used as an address. -* `vector`: a 16-bit address of a routine. two operations are supported - on vectors: copying the contents of one vector to another, and jumping - to the code at the address contained in the vector (and this can only - happen at the end of a routine.) +* `vector`: a 16-bit address of a routine. Only a handful of operations + are supported on vectors: + + * copying the contents of one vector to another + * copying the address of a routine into a vector + * jumping indirectly to a vector (i.e. to the code at the address + contained in the vector (and this can only happen at the end of a + routine) + * `jsr`'ing indirectly to a vector (which is done with a fun + generated trick + * `byte table`: (not yet implemented) a series of `byte`s contiguous in memory starting from the address. this is the only kind of address that can be used in indexed addressing. ### Blocks ### -Each routine is a block. It may be composed of inner blocks, attached to -some instructions. +Each routine is a block. It may be composed of inner blocks, if those +inner blocks are attached to certain instructions. SixtyPical does not have instructions that map literally to the 6502 branch instructions. Instead, it has an `if` construct, with two blocks (for the @@ -115,7 +122,7 @@ Unsupported Opcodes ------------------- 6502 opcodes with no language-level equivalent instructions in SixtyPical -are `brk`, `cli`, `jmp`, `pla`, `plp`, `rti`, and `rts`. These may be +are `brk`, `cli`, `pla`, `plp`, `rti`, and `rts`. These may be inserted into the output program as a SixtyPical → 6502 compiler sees fit, however. @@ -188,6 +195,10 @@ In these, `absolute` must be a `reserve`d or `locate`d address. iny * jsr routine + X jsr vector + + X jmp routine + * jmp vector lda #immediate lda absolute @@ -337,6 +348,22 @@ No duplicate declarations. | } ? duplicate declaration +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 + *| } + *? wtf + -> Tests for functionality "Emit ASM for SixtyPical program" -> Functionality "Emit ASM for SixtyPical program" is implemented by @@ -515,12 +542,13 @@ Nested ifs. = _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 patch_cinv { + | routine main { | sei { | copy vector cinv to save_cinv | copy routine our_cinv to cinv diff --git a/src/SixtyPical/Emitter.hs b/src/SixtyPical/Emitter.hs index 285c931..858ea6a 100644 --- a/src/SixtyPical/Emitter.hs +++ b/src/SixtyPical/Emitter.hs @@ -23,6 +23,7 @@ emitDecls p (decl:decls) = emitDecl p (Assign name _ addr) = ".alias " ++ name ++ " " ++ (show addr) emitDecl p (Reserve name Byte) = name ++ ": .byte 0" emitDecl p (Reserve name Word) = name ++ ": .word 0" +emitDecl p (Reserve name Vector) = name ++ ": .word 0" emitRoutines _ [] = "" emitRoutines p (rout:routs) = @@ -85,5 +86,21 @@ emitInstr p r (REPEAT iid branch blk) = emitInstrs p r blk ++ " " ++ (show branch) ++ " _repeat_" ++ (show iid) -emitInstr p r i = error "Internal error: sixtypical doesn't know how to emit assembler code for '" ++ show i ++ "'" +emitInstr p r (SEI blk) = + "sei\n" ++ + emitInstrs p r blk ++ + "cli" + +emitInstr p r (COPYVECTOR (NamedLocation src) (NamedLocation dst)) = + "COPYVECTOR " ++ src ++ " " ++ dst + +emitInstr p r (COPYROUTINE src (NamedLocation dst)) = + "COPYROUTINE " ++ src ++ " " ++ dst + +emitInstr p r (JMPVECTOR (NamedLocation dst)) = + "jmp (" ++ dst ++ ")" + +emitInstr p r i = error ( + "Internal error: sixtypical doesn't know how to " ++ + "emit assembler code for '" ++ (show i) ++ "'") diff --git a/src/SixtyPical/Model.hs b/src/SixtyPical/Model.hs index 39ced15..255e5b0 100644 --- a/src/SixtyPical/Model.hs +++ b/src/SixtyPical/Model.hs @@ -57,6 +57,8 @@ data Instruction = LOADIMM StorageLocation DataValue | CMPIMM StorageLocation DataValue | CMP StorageLocation StorageLocation | JSR RoutineName + -- | JSRVECTOR StorageLocation + | JMPVECTOR StorageLocation | IF InternalID Branch [Instruction] [Instruction] | REPEAT InternalID Branch [Instruction] | DELTA StorageLocation DataValue @@ -71,3 +73,17 @@ data Routine = Routine RoutineName [Instruction] data Program = Program [Decl] [Routine] deriving (Show, Ord, Eq) + +mapBlock :: (Instruction -> Instruction) -> [Instruction] -> [Instruction] +mapBlock = map + +mapRoutine :: (Instruction -> Instruction) -> Routine -> Routine +mapRoutine f (Routine name instrs) = Routine name (mapBlock f instrs) + +mapRoutines :: (Instruction -> Instruction) -> [Routine] -> [Routine] +mapRoutines f [] = [] +mapRoutines f (rout:routs) = + (mapRoutine f rout):(mapRoutines f routs) + +mapProgramRoutines :: (Instruction -> Instruction) -> Program -> Program +mapProgramRoutines f (Program decls routs) = Program decls $ mapRoutines f routs diff --git a/src/SixtyPical/Parser.hs b/src/SixtyPical/Parser.hs index 1759a82..89b64af 100644 --- a/src/SixtyPical/Parser.hs +++ b/src/SixtyPical/Parser.hs @@ -24,6 +24,7 @@ Command := "if" Branch Block "else" Block | "inx" | "iny" | "dex" | "dey" | "inc" Location | "dec" Location | "clc" | "cld" | "clv" | "sec" | "sed" | "sei" Block + | "jmp" LocationName | "nop". Branch := "bcc" | "bcs" | "beq" | "bmi" | "bne" | "bpl" | "bvc" | "bvs". @@ -91,6 +92,7 @@ command = (try lda) <|> (try inc) <|> (try dec) <|> (try clc) <|> (try cld) <|> (try clv) <|> (try sec) <|> (try sed) <|> (try sei) <|> + (try jmp) <|> (try copy_vector_statement) <|> (try copy_routine_statement) <|> if_statement <|> repeat_statement <|> nop @@ -274,6 +276,13 @@ sei = do blk <- block return (SEI blk) +jmp :: Parser Instruction +jmp = do + string "jmp" + spaces + l <- locationName + return $ JMPVECTOR (NamedLocation l) + if_statement :: Parser Instruction if_statement = do string "if"