module Language.PowerPC.Simulator
( simulate
) where
import Control.Monad
import Data.Bits
import qualified Data.IntMap as M
import Text.Printf
import Language.PowerPC.Syntax
data Machine = Machine
{ program :: M.IntMap Instruction
, pc :: Int
, lr :: Int
, ctr :: Int
, gprs :: [Int]
, xer :: Int
}
instance Show Machine where
show m = printf "program counter = 0x%08X\n" $ pc m
simulate :: Int -> Int -> Program -> IO ()
simulate base start p = loop Machine
{ program = M.fromList $ zip [base, base + 4 ..] p
, pc = start
, lr = 0
, ctr = 0
, gprs = replicate 32 0
, xer = 0
}
where
loop :: Machine -> IO ()
loop m = when (not $ done m) $ do
printf "program counter = 0x%08X\n" $ pc m
loop $ step m
done :: Machine -> Bool
done m | pc m < base || pc m > base + (length p + 1) * 4 = error $ printf "program counter out of range: 0x%08X" $ pc m
| otherwise = pc m == base + length p * 4
step :: Machine -> Machine
step m = case program m M.! pc m of
ADDI d GPR0 i -> set d i n
ADDI d a i -> set d (r a + i) n
B a -> m { pc = pc m + a }
BA a -> m { pc = a }
BL a -> m { pc = pc m + a, lr = pc m + 4 }
BLA a -> m { pc = a, lr = pc m + 4 }
MTSPR a CTR -> n { ctr = r a }
MTSPR a LR -> n { lr = r a }
MTSPR a XER -> n { xer = r a }
MTSPR _ SRInvalid -> n
Unknown a b c -> error $ printf "unknown instruction: address: 0x%08X instruction: 0x%08X opcode: %d extended opcode: %d" (pc m) a b c
where
n = m { pc = pc m + 4 }
r :: GPR -> Int
r a = gprs m !! gprIndex a
set :: GPR -> Int -> Machine -> Machine
set r v m = m { gprs = replace (gprIndex r) v (gprs m) }
replace :: Int -> a -> [a] -> [a]
replace _ a [] = [a]
replace i a (b:c) | i <= 0 = a : c
| otherwise = b : replace (i 1) a c