{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module MimaRun where import Control.Monad import Data.Bits import qualified Data.Text as T import qualified Data.Text.IO as T import Options.Applicative import System.Console.ANSI 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) {- Command-line parameters -} 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 {- Fancy output -} printAddress :: Int -> MimaAddress -> IO () printAddress n addr = do T.putStr $ toHexBytes addr putStr " (" T.putStr $ T.justifyRight n ' ' $ toDec addr putStr ")" printWord :: Int -> MimaWord -> IO () printWord n word = do T.putStr $ toHexBytes word putStr " (" T.putStr $ T.justifyRight n ' ' $ toDec word putStr ")" printInstruction :: Instruction -> IO () printInstruction (SmallInstruction so lv) = do setSGR [SetConsoleIntensity BoldIntensity] if | so `elem` [JMP, JMN, CALL] -> setSGR [SetColor Foreground Vivid Yellow] | so `elem` [LDC, LDV, STV, LDIV, STIV, LDVR, STVR] -> setSGR [SetColor Foreground Vivid Blue] | so `elem` [ADD, AND, OR, XOR, EQL] -> setSGR [SetColor Foreground Vivid Cyan] | otherwise -> pure () T.putStr $ toText so putStr " " setSGR [SetColor Foreground Vivid Black] T.putStr $ toDec lv setSGR [] printInstruction (LargeInstruction lo sv) = do setSGR [SetConsoleIntensity BoldIntensity] if | lo == HALT -> setSGR [SetColor Foreground Vivid Red] | lo == RET -> setSGR [SetColor Foreground Vivid Yellow] | lo `elem` [NOT, RAR, ADC] -> setSGR [SetColor Foreground Vivid Cyan] | lo `elem` [LDRA, STRA, LDSP, STSP, LDFP, STFP] -> setSGR [SetColor Foreground Vivid Magenta] | otherwise -> pure () T.putStr $ toText lo when (lo == ADC || sv /= zeroBits) $ do putStr " " setSGR [SetColor Foreground Vivid Black] T.putStr $ toDec sv setSGR [] printWordWithInstruction :: Int -> MimaWord -> IO () printWordWithInstruction n word = do printWord n word case wordToInstruction word of Left _ -> pure () Right i -> do putStr ": " printInstruction i printAddressRegister :: MimaState -> MimaAddress -> IO () printAddressRegister ms addr = do printAddress 8 addr putStr " -> " printWordWithInstruction 8 $ readAt addr $ msMemory ms printRegistersLn :: MimaState -> IO () printRegistersLn ms = do putStr "IAR: " printAddressRegister ms $ msIAR ms putStrLn "" putStr "ACC: " printWord 8 $ msACC ms putStrLn "" putStr " RA: " printAddressRegister ms $ msRA ms putStrLn "" putStr " SP: " printAddressRegister ms $ msSP ms putStrLn "" putStr " FP: " printAddressRegister ms $ msFP ms putStrLn "" printMemoryLocationLn :: MimaAddress -> MimaWord -> IO () printMemoryLocationLn addr word = do printAddress 7 addr putStr " -> " printWord 8 word case wordToInstruction word of Left _ -> pure () Right i -> do putStr ": " printInstruction i putStrLn "" printMemoryLn :: Bool -> MimaMemory -> IO () printMemoryLn sparse mem = do let addresses = if sparse then sparseAddressRange mem else addressRange mem forM_ addresses $ \addr -> do printMemoryLocationLn addr (readAt addr mem) printStateLn :: Bool -> MimaState -> IO () printStateLn sparse ms = do printRegistersLn ms printMemoryLn sparse $ msMemory ms {- Main logic -} 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' -- 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:" printStateLn (sparse settings) s' putStrLn "" forM_ (memoryDump settings) $ \path -> do putStrLn $ "Saving memdump at " ++ path saveMemoryToFile path $ msMemory s'