Haskell Quiz/Chip Eight/Solution Jethr0
From HaskellWiki
(Difference between revisions)
m |
m (more pure version) |
||
| Line 1: | Line 1: | ||
[[Category:Haskell Quiz solutions|Chip Eight]] | [[Category:Haskell Quiz solutions|Chip Eight]] | ||
| - | + | Interpreter isn't fully tested, but the sample program seems to be "running" correctly. | |
| - | + | ||
| - | + | ||
<haskell> | <haskell> | ||
| Line 46: | Line 44: | ||
--- | --- | ||
| - | modify_rv func = | + | modify_rv func st = st{rv = func . rv $ st} |
| - | modify_rip func = | + | modify_rip func st = st{rip = func . rip $ st} |
| - | modify_rmemory func = | + | modify_rmemory func st = st{rmemory = func . rmemory $ st} |
| - | getReg x = | + | getReg x st = let MachineState{rv = regs} = st in regs!x |
| - | setReg x val = modify_rv (// [(fromIntegral x, val)]) | + | setReg x val st = modify_rv (// [(fromIntegral x, val)]) st |
| - | + | eval :: Word16 -> MachineState -> MachineState | |
| - | + | eval instr st = case firstDigit instr of | |
| - | + | 0x1 -> | |
| - | + | modify_rip (const nnn) st | |
| - | + | where nnn = instr .&. 0x0FFF | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | eval instr = case firstDigit instr of | + | |
| - | 0x1 -> | + | |
| - | + | ||
| - | + | ||
| - | 0x3 -> | + | 0x3 -> |
| - | + | if vx == kk | |
| - | + | then modify_rip (2+) st | |
| - | + | else st | |
| - | 0x6 -> | + | 0x6 -> |
| - | + | setReg x kk st | |
| - | setReg x kk | + | |
| - | 0x7 -> | + | 0x7 -> |
| - | + | rPlus x vx kk st | |
| - | + | ||
| - | rPlus x vx kk | + | |
| - | 0x8 -> | + | 0x8 -> |
| - | + | (case instr .&. 0x000F of | |
| - | + | 0x0 -> setReg x vy | |
| - | + | 0x1 -> setReg x (vx .|. vy) | |
| - | + | 0x2 -> setReg x (vx .&. vy) | |
| - | + | 0x3 -> setReg x (vx `Bits.xor` vy) | |
| - | 0x0 -> setReg x | + | |
| - | 0x1 -> setReg x | + | |
| - | 0x2 -> setReg x | + | |
| - | 0x3 -> setReg x | + | |
0x4 -> rPlus x vx vy | 0x4 -> rPlus x vx vy | ||
0x5 -> rMinus x vx vy | 0x5 -> rMinus x vx vy | ||
| - | 0x6 -> rShiftR x vx 1 | + | 0x6 -> rShiftR x vx 1 |
0x7 -> rMinus x vy vx | 0x7 -> rMinus x vy vx | ||
0xE -> rShiftL x vx 1 | 0xE -> rShiftL x vx 1 | ||
| - | where | + | ) st |
| - | + | where y = fromIntegral $ shiftR (instr .&. 0x00F0) 4 | |
| - | + | vy = getReg y st | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | 0xC -> | + | 0xC -> |
| - | + | setReg x (r .&. kk) st' | |
| - | + | where (r, st') = rRandom st | |
| - | + | ||
otherwise -> | otherwise -> | ||
error $ "opcode not implemented " ++ showHex instr "" | error $ "opcode not implemented " ++ showHex instr "" | ||
| - | where | + | where x = fromIntegral $ shiftR (instr .&. 0x0F00) 8 |
| - | + | vx = getReg x st | |
| - | + | kk = fromIntegral $ instr .&. 0x00FF | |
| + | (>.>) = flip ($) | ||
| + | |||
firstDigit w = fromIntegral $ shiftR w 12 | firstDigit w = fromIntegral $ shiftR w 12 | ||
| - | rRandom = | + | rRandom s = (fromIntegral r, s{rand = gen}) |
| - | + | where MachineState{rand = gen} = s | |
| - | + | (r, gen') = randomR (0, 2^8-1) gen | |
| - | + | ||
| - | + | ||
| - | + | rPlus target a b s = | |
| - | rPlus target a b = | + | s |
| - | + | >.> setReg 0xF (if sum >= 2^8 then 1 else 0) | |
| - | setReg 0xF | + | >.> setReg target ((fromIntegral sum) .&. 0x00FF) |
| - | setReg target | + | where sum = (fromIntegral a) + (fromIntegral b) :: Integer |
| - | + | rMinus target a b s = | |
| - | rMinus target a b = | + | s |
| - | + | >.> setReg 0xF (if (sum < 0) then 0 else 1) | |
| - | setReg 0xF | + | >.> setReg target (fromIntegral (if (sum < 0) then (sum + 2^8) |
| - | setReg target | + | else sum)) |
| + | where sum = (fromIntegral a) - (fromIntegral b) :: Integer | ||
| + | rShiftR target vx n s = | ||
| + | s | ||
| + | >.> setReg 0xF (vx .&. 0x01) | ||
| + | >.> setReg target (shiftR vx n) | ||
| + | |||
| + | rShiftL target vx n s = | ||
| + | s | ||
| + | >.> setReg 0xF (shiftR (vx .&. 0x80) 7) -- FIXME: is this correct | ||
| + | >.> setReg target (shiftL vx n) | ||
--- | --- | ||
| Line 157: | Line 143: | ||
modifyRegisters pairs state = | modifyRegisters pairs state = | ||
state{rv = rv state // pairs} | state{rv = rv state // pairs} | ||
| + | |||
| + | |||
| + | step :: St () | ||
| + | step = do | ||
| + | MachineState{rip = ip, rmemory = mem} <- get | ||
| + | let (i1,i2) = (fromIntegral $ mem ! (fromIntegral ip) | ||
| + | ,fromIntegral $ mem ! (fromIntegral $ ip+1)) | ||
| + | instr = (shiftL i1 8) + i2 :: Word16 | ||
| + | case instr of | ||
| + | 0x0000 -> return () | ||
| + | otherwise -> do | ||
| + | modify $ eval instr | ||
| + | modify $ modify_rip (+2) | ||
| + | step | ||
| + | |||
main = do | main = do | ||
Revision as of 19:36, 15 July 2007
Interpreter isn't fully tested, but the sample program seems to be "running" correctly.
module Main where import qualified Data.Array as Array import qualified Data.Bits as Bits import qualified Data.Char as Char import Data.Word (Word8, Word16) import Data.Bits ((.&.), (.|.), shiftL, shiftR) import Data.Array ((!), (//), Array, listArray, accumArray) import Control.Monad (when) import Control.Monad.State (get, put, modify, liftIO, execStateT, StateT) import Control.Monad.Identity import Numeric (showHex, showIntAtBase) import Text.Printf (printf) import System.Random (randomR, mkStdGen, StdGen, newStdGen) numRegisters = 16 sizeMemory = 2^12 data MachineState = MachineState { rv :: Array Int Word8, rip :: Word16, rmemory :: Array Int Word8, rand :: StdGen } instance Show MachineState where show MachineState {rv = regs, rmemory = mem, rip = ip} = unlines showRegs ++ "IP: " ++ (printf "%08x" (fromIntegral ip :: Integer)) where showRegs = zipWith (\r v -> r ++ ": " ++ v) regNames regValues regNames = [printf "V%x" (fromIntegral x :: Integer) | x <- [0..]] --regValues = [printf "%04x" (fromIntegral x :: Integer) | x <- Array.elems regs] regValues = [showIntAtBase 2 Char.intToDigit x "" | x <- Array.elems regs] type StT = StateT MachineState type St = StT Identity type Offset = Int --- modify_rv func st = st{rv = func . rv $ st} modify_rip func st = st{rip = func . rip $ st} modify_rmemory func st = st{rmemory = func . rmemory $ st} getReg x st = let MachineState{rv = regs} = st in regs!x setReg x val st = modify_rv (// [(fromIntegral x, val)]) st eval :: Word16 -> MachineState -> MachineState eval instr st = case firstDigit instr of 0x1 -> modify_rip (const nnn) st where nnn = instr .&. 0x0FFF 0x3 -> if vx == kk then modify_rip (2+) st else st 0x6 -> setReg x kk st 0x7 -> rPlus x vx kk st 0x8 -> (case instr .&. 0x000F of 0x0 -> setReg x vy 0x1 -> setReg x (vx .|. vy) 0x2 -> setReg x (vx .&. vy) 0x3 -> setReg x (vx `Bits.xor` vy) 0x4 -> rPlus x vx vy 0x5 -> rMinus x vx vy 0x6 -> rShiftR x vx 1 0x7 -> rMinus x vy vx 0xE -> rShiftL x vx 1 ) st where y = fromIntegral $ shiftR (instr .&. 0x00F0) 4 vy = getReg y st 0xC -> setReg x (r .&. kk) st' where (r, st') = rRandom st otherwise -> error $ "opcode not implemented " ++ showHex instr "" where x = fromIntegral $ shiftR (instr .&. 0x0F00) 8 vx = getReg x st kk = fromIntegral $ instr .&. 0x00FF (>.>) = flip ($) firstDigit w = fromIntegral $ shiftR w 12 rRandom s = (fromIntegral r, s{rand = gen}) where MachineState{rand = gen} = s (r, gen') = randomR (0, 2^8-1) gen rPlus target a b s = s >.> setReg 0xF (if sum >= 2^8 then 1 else 0) >.> setReg target ((fromIntegral sum) .&. 0x00FF) where sum = (fromIntegral a) + (fromIntegral b) :: Integer rMinus target a b s = s >.> setReg 0xF (if (sum < 0) then 0 else 1) >.> setReg target (fromIntegral (if (sum < 0) then (sum + 2^8) else sum)) where sum = (fromIntegral a) - (fromIntegral b) :: Integer rShiftR target vx n s = s >.> setReg 0xF (vx .&. 0x01) >.> setReg target (shiftR vx n) rShiftL target vx n s = s >.> setReg 0xF (shiftR (vx .&. 0x80) 7) -- FIXME: is this correct >.> setReg target (shiftL vx n) --- initialState :: StdGen -> MachineState initialState gen = MachineState { rv = accumArray const 0 (0, numRegisters-1) [], rmemory = accumArray const 0 (0, sizeMemory-1) [], rip = 0, rand = gen } modifyMemory :: Offset -> [Word8] -> MachineState -> MachineState modifyMemory offset words state = state{rmemory = rmemory state // zip [offset..] words} modifyRegisters :: [(Int, Word8)] -> MachineState -> MachineState modifyRegisters pairs state = state{rv = rv state // pairs} step :: St () step = do MachineState{rip = ip, rmemory = mem} <- get let (i1,i2) = (fromIntegral $ mem ! (fromIntegral ip) ,fromIntegral $ mem ! (fromIntegral $ ip+1)) instr = (shiftL i1 8) + i2 :: Word16 case instr of 0x0000 -> return () otherwise -> do modify $ eval instr modify $ modify_rip (+2) step main = do g <- newStdGen file <- readFile "Chip8Test" let program = map (fromIntegral . Char.ord) file start = initialState g new = modifyMemory 0 program . modifyRegisters [] $ start let res = runIdentity $ execStateT step new print res
