{-# LANGUAGE FlexibleInstances #-}
module CodeGen.X86.Examples where

import Foreign

import CodeGen.X86

foreign import ccall "dynamic" callWW :: FunPtr (Word64 -> Word64) -> Word64 -> Word64
instance Callable (Word64 -> Word64) where dynCCall = callWW

foreign import ccall "dynamic" callPW :: FunPtr (Ptr a -> Word64) -> Ptr a -> Word64
instance Callable (Ptr a -> Word64) where dynCCall = callPW

foreign import ccall "dynamic" callIO :: FunPtr (IO ()) -> IO ()
instance Callable (IO ()) where dynCCall = callIO

foreign import ccall "wrapper" createPtrWord64_Word64 :: (Word64 -> Word64) -> IO (FunPtr (Word64 -> Word64))
instance CallableHs (Word64 -> Word64) where createHsPtr = createPtrWord64_Word64

------------------------------------------------------------------------------ 
-- * examples

-- | Example: identity function in Assembly (look at the source code)
--
-- Input: @rdi@ on Linux \/ System V, @rcx@ on Win64
--
-- Output: @rax@
idCode = do
    mov result arg1
    ret

idFun :: Word64 -> Word64
idFun = compile idCode 

-- | Example: Fibonacci function in Assembly
fibCode = saveNonVolatile $ do
    mov rdi arg1
    inc rdi
    xor_ rdx rdx
    mov rax 1
    doWhile NZ $ do
        mov rcx rax
        mov rax rdx
        add rdx rcx
        dec rdi

fibFun :: Word64 -> Word64
fibFun = compile fibCode 

-- | Example: trace a register in Assembly
tracedFibCode = saveNonVolatile $ do
    mov rdi arg1
    inc rdi
    xor_ rdx rdx
    mov rax 1
    doWhile NZ $ do
        mov rcx rax
        mov rax rdx
        add rdx rcx
        dec rdi
        traceReg "d" rax

tracedFibFun :: Word64 -> Word64
tracedFibFun = compile tracedFibCode 

-- | Example: call Haskell @fib@ function from Assembly
callHsCode = do
    callFun r11 (hsPtr fib)
    ret

fib :: Word64 -> Word64
fib n = go n 0 1
  where
    go 0 a b = b `seq` a
    go n a b = go (n-1) b (a+b)

callHsFun :: Word64 -> Word64
callHsFun = compile callHsCode 

-- | Example: call C @printf@ function from Assembly
--
callCCode name = saveNonVolatile $ do
    leaData arg1 $ CString "Hello %s!\n"
    leaData arg2 $ CString name
    xor_ rax rax                     -- zero XMM arguments ?????
    callFun r11 printf

callCFun :: String -> IO ()
callCFun name = compile $ callCCode name

-------------------------------------------------------

memTestFun :: Word64 -> IO Bool
memTestFun v = do
    r <- mallocBytes 8                -- this is not required to be aligned (and in any case malloc aligns to machine words)
    pokeByteOff r 0 (v :: Word64)
    let code = saveNonVolatile $ do
            mov rdi arg1
            mov rax (addr rdi)
    return $ compile code (r :: Ptr Word8) == v