mima-tools/app/MimaRun.hs
2019-11-07 16:46:21 +00:00

141 lines
4.6 KiB
Haskell

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module MimaRun where
import Control.Monad
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Options.Applicative
import Mima.Instruction
import Mima.Load
import Mima.State
import Mima.Util
import Mima.Word
data Settings = Settings
{ infile :: String
, steps :: Maybe Integer
, memoryDump :: Maybe FilePath
, quiet :: Bool
, sparse :: Bool
, norun :: Bool
} deriving (Show)
settingsParser :: Parser Settings
settingsParser = Settings
<$> strArgument
(metavar "INFILE"
<> help "The binary memory dump to load and execute")
<*> (optional . option auto)
(long "steps"
<> short 'n'
<> metavar "N"
<> help "How many instructions to execute (if not specified, runs until HALT or execution exception)")
<*> (optional . strOption)
(long "dump"
<> short 'd'
<> metavar "OUTFILE"
<> help "If specified, write the MiMa's binary memory dump to this file after execution is finished")
<*> flag False True
(long "quiet"
<> short 'q'
<> help "Don't print the memory dump")
<*> flag False True
(long "sparse"
<> short 's'
<> help "Don't print memory locations containing only 0x000000 in the memory dump")
<*> flag False True
(long "norun"
<> short 'r'
<> help "Don't run the MiMa. Use the initial state for all further actions")
opts :: ParserInfo Settings
opts = info (helper <*> settingsParser) $ fullDesc <> failureCode 1
runMima :: Settings -> MimaState -> IO MimaState
runMima settings s =
case steps settings of
Nothing -> do
putStrLn "Running until HALT or execution exception..."
let (s', e, x) = run s
putStrLn $ "Ran for " ++ show x ++ " steps"
T.putStrLn $ toText e
pure s'
Just n -> do
let (s', me, x) = runN n s
putStrLn $ "Ran for " ++ show x ++ " steps"
case me of
Nothing -> putStrLn "Encountered no exception"
Just e -> T.putStrLn $ toText e
pure s'
dumpState :: Bool -> MimaState -> T.Text
dumpState sparse ms
= registerLegend
<> dumpRegisters ms
<> memoryLegend
<> dumpMemory sparse (msMemory ms)
<> footerLegend
showWord :: MimaWord -> T.Text
showWord w =
case wordToInstruction w of
Left _ -> wordToHexDec w
Right i -> wordToHexDec w <> ": " <> toText i
dumpRegisters :: MimaState -> T.Text
dumpRegisters MimaState{..}
= "IAR: " <> showAddressRegister msIAR <> " -> " <> showWord (readAt msIAR msMemory) <> "\n"
<> "ACC: " <> showWordRegister msACC <> "\n"
<> " RA: " <> showAddressRegister msRA <> " -> " <> showWord (readAt msRA msMemory) <> "\n"
<> " SP: " <> showAddressRegister msRA <> " -> " <> showWord (readAt msRA msMemory) <> "\n"
<> " FP: " <> showAddressRegister msRA <> " -> " <> showWord (readAt msRA msMemory) <> "\n"
where
showWordRegister w = wordToHex w <> " (" <> wordToDec w <> ")"
showAddressRegister lv =
" " <> largeValueToHex lv <> " ( " <> largeValueToDec lv <> ")"
registerLegend :: T.Text
registerLegend = "--------- Register -------------- Target word ---------------\n"
-- "IAR: 00000 ( 0) -> 800008 ( 8388616): JMP 8"
showMemoryLine :: MimaAddress -> MimaWord -> T.Text
showMemoryLine addr word = largeValueToHexDec addr <> " -> " <> showWord word <> "\n"
dumpMemory :: Bool -> MimaMemory -> T.Text
dumpMemory sparse mem =
let addresses = if sparse then sparseAddressRange mem else addressRange mem
memLines = map (\addr -> showMemoryLine addr $ readAt addr mem) addresses
in T.concat memLines
memoryLegend :: T.Text
memoryLegend = "--- Address ---------------- Word ---------------------------\n"
-- "00000 ( 0) -> 800008 ( 8388616): JMP 8"
-- "IAR: 00000 ( 0) -> 800008 ( 8388616): JMP 8"
footerLegend :: T.Text
footerLegend = "------------------------------------------------------\n"
-- "00000 ( 0) -> 800008 ( 8388616): JMP 8"
-- TODO exception handling
main :: IO ()
main = do
settings <- execParser opts
putStrLn $ "Loading memdump at " ++ infile settings
mem <- loadMemoryFromFile (infile settings)
let s = initialState mem
s' <- if norun settings then pure s else runMima settings s
unless (quiet settings) $ do
putStrLn ""
putStrLn "Dump of MiMa state:"
T.putStr $ dumpState (sparse settings) s'
putStrLn ""
forM_ (memoryDump settings) $ \path -> do
putStrLn $ "Saving memdump at " ++ path
saveMemoryToFile path $ msMemory s'