-- Sample code to demonstrate how to emulate ARM code

import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.Arm as Arm

import Data.Bits
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)

-- Code to be emulated
--
-- mov r0, #0x37; sub r1, r2, r3
armCode :: BS.ByteString
armCode = BS.pack [0x37, 0x00, 0xa0, 0xe3, 0x03, 0x10, 0x42, 0xe0]

-- sub sp, #0xc
thumbCode :: BS.ByteString
thumbCode = BS.pack [0x83, 0xb0]

-- Memory address where emulation starts
address :: Word64
address = 0x10000

-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
    flip N.showHex ""

-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
    fromIntegral . BS.length

hookBlock :: BlockHook ()
hookBlock _ addr size _ =
    putStrLn $ ">>> Tracing basic block at 0x" ++ showHex addr ++
               ", block size = 0x" ++ (maybe "0" showHex size)

hookCode :: CodeHook ()
hookCode _ addr size _ =
    putStrLn $ ">>> Tracing instruction at 0x" ++ showHex addr ++
               ", instruction size = 0x" ++ (maybe "0" showHex size)

testArm :: IO ()
testArm = do
    putStrLn "Emulate ARM code"

    result <- runEmulator $ do
        -- Initialize emulator in ARM mode
        uc <- open ArchArm [ModeArm]

        -- Map 2MB memory for this emulation
        memMap uc address (2 * 1024 * 1024) [ProtAll]

        -- Write machine code to be emulated to memory
        memWrite uc address armCode

        -- Initialize machine registers
        regWrite uc Arm.R0 0x1234
        regWrite uc Arm.R2 0x6789
        regWrite uc Arm.R3 0x3333

        -- Tracing all basic blocks with customized callback
        blockHookAdd uc hookBlock () 1 0

        -- Tracing one instruction at address with customized callback
        codeHookAdd uc hookCode () address address

        -- Emulate machine code in infinite time (last param = Nothing), or
        -- when finishing all the code
        let codeLen = codeLength armCode
        start uc address (address + codeLen) Nothing Nothing

        -- Return the results
        r0 <- regRead uc Arm.R0
        r1 <- regRead uc Arm.R1

        return (r0, r1)
    case result of
        Right (r0, r1) -> do
            -- Now print out some registers
            putStrLn ">>> Emulation done. Below is the CPU context"
            putStrLn $ ">>> R0 = 0x" ++ showHex r0
            putStrLn $ ">>> R1 = 0x" ++ showHex r1
        Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
                                strerror err ++ ")"

testThumb :: IO ()
testThumb = do
    putStrLn "Emulate THUMB code"

    result <- runEmulator $ do
        -- Initialize emulator in ARM mode
        uc <- open ArchArm [ModeThumb]

        -- Map 2MB memory for this emulation
        memMap uc address (2 * 1024 * 1024) [ProtAll]

        -- Write machine code to be emulated to memory
        memWrite uc address thumbCode

        -- Initialize machine registers
        regWrite uc Arm.Sp 0x1234

        -- Tracing all basic blocks with customized callback
        blockHookAdd uc hookBlock () 1 0

        -- Tracing one instruction at address with customized callback
        codeHookAdd uc hookCode () address address

        -- Emulate machine code in infinite time (last param = Nothing), or
        -- when finishing all the code
        let codeLen = codeLength thumbCode
        start uc (address .|. 1) (address + codeLen) Nothing Nothing

        -- Return the results
        sp <- regRead uc Arm.Sp

        return sp
    case result of
        Right sp -> do
            -- Now print out some registers
            putStrLn ">>> Emulation done. Below is the CPU context"
            putStrLn $ ">>> SP = 0x" ++ showHex sp
        Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
                               strerror err ++ ")"

main :: IO ()
main = do
    testArm
    putStrLn "=========================="
    testThumb
