summaryrefslogtreecommitdiff
path: root/fig-emulator-gb/src/Fig/Emulator/GB/Test
diff options
context:
space:
mode:
Diffstat (limited to 'fig-emulator-gb/src/Fig/Emulator/GB/Test')
-rw-r--r--fig-emulator-gb/src/Fig/Emulator/GB/Test/Instr.hs176
1 files changed, 0 insertions, 176 deletions
diff --git a/fig-emulator-gb/src/Fig/Emulator/GB/Test/Instr.hs b/fig-emulator-gb/src/Fig/Emulator/GB/Test/Instr.hs
deleted file mode 100644
index 647e03a..0000000
--- a/fig-emulator-gb/src/Fig/Emulator/GB/Test/Instr.hs
+++ /dev/null
@@ -1,176 +0,0 @@
-{-# Language TemplateHaskell, ApplicativeDo #-}
-module Fig.Emulator.GB.Test.Instr where
-
-import Control.Lens.TH (makeLenses)
-
-import Control.Lens ((^.))
-import Control.Monad.State.Strict (StateT(..))
-
-import Data.Word (Word8, Word16)
-import qualified Data.Vector as V
-import qualified Data.Aeson as Aeson
-
-import qualified Text.Printf as Pr
-
-import Fig.Prelude
-import Fig.Emulator.GB.Utils
-import Fig.Emulator.GB.CPU
-import Fig.Emulator.GB.CPU.Instruction
-import Fig.Emulator.GB.Bus (Bus(..), Addr(..))
-import qualified Fig.Emulator.GB.Bus as Bus
-import Fig.Emulator.GB.Component.RAM
-
-newtype InstrTestError = InstrTestError Text
- deriving Show
-instance Exception InstrTestError
-instance Pretty InstrTestError where
- pretty (InstrTestError b) = mconcat
- [ "Instruction test error: "
- , b
- ]
-
-data TestVals = TestVals
- { _testvalsA :: !Word8
- , _testvalsB :: !Word8
- , _testvalsC :: !Word8
- , _testvalsD :: !Word8
- , _testvalsE :: !Word8
- , _testvalsF :: !Word8
- , _testvalsH :: !Word8
- , _testvalsL :: !Word8
- , _testvalsPC :: !Word16
- , _testvalsSP :: !Word16
- , _testvalsRAM :: ![(Word16, Word8)]
- }
-makeLenses 'TestVals
-instance Aeson.FromJSON TestVals where
- parseJSON = Aeson.withObject "TestVals" $ \v -> do
- _testvalsA <- v Aeson..: "a"
- _testvalsB <- v Aeson..: "b"
- _testvalsC <- v Aeson..: "c"
- _testvalsD <- v Aeson..: "d"
- _testvalsE <- v Aeson..: "e"
- _testvalsF <- v Aeson..: "f"
- _testvalsH <- v Aeson..: "h"
- _testvalsL <- v Aeson..: "l"
- _testvalsPC <- v Aeson..: "pc"
- _testvalsSP <- v Aeson..: "sp"
- _testvalsRAM <- v Aeson..: "ram"
- pure TestVals{..}
-
-data Testcase = Testcase
- { _testcaseName :: !Text
- , _testcaseInitial :: !TestVals
- , _testcaseFinal :: !TestVals
- }
-makeLenses 'Testcase
-
-instance Aeson.FromJSON Testcase where
- parseJSON = Aeson.withObject "Testcase" $ \v -> do
- _testcaseName <- v Aeson..: "name"
- _testcaseInitial <- v Aeson..: "initial"
- _testcaseFinal <- v Aeson..: "final"
- pure Testcase {..}
-
-readTestcases :: FilePath -> IO [Testcase]
-readTestcases p = liftIO (Aeson.decodeFileStrict p) >>= \case
- Just ts -> pure ts
- Nothing -> throwM . InstrTestError $ "failed to read testcases at " <> pack p
-
-cpuInstrTest :: TestVals -> IO CPU
-cpuInstrTest vs = do
- let
- (z, n, h, c) = w8flags $ vs ^. testvalsF
- initialBus = Bus [compWRAM 0x0000 $ 64 * 1024]
- finalBus <- foldM (\b (addr, v) -> Bus.write b (Addr addr) v) initialBus $ vs ^. testvalsRAM
- pure CPU
- { _lastPC = 0x0
- , _lastIns = Nop
- , _running = True
- , _regs = initialRegs
- { _regA = vs ^. testvalsA
- , _regB = vs ^. testvalsB
- , _regC = vs ^. testvalsC
- , _regD = vs ^. testvalsD
- , _regE = vs ^. testvalsE
- , _regH = vs ^. testvalsH
- , _regL = vs ^. testvalsL
- , _regPC = vs ^. testvalsPC - 1
- , _regSP = vs ^. testvalsSP
- , _regFlagZ = z
- , _regFlagN = n
- , _regFlagH = h
- , _regFlagC = c
- }
- , _bus = finalBus
- }
-
-checkCPU :: Text -> TestVals -> CPU -> CPU -> IO ()
-checkCPU tnm vs initial c = do
- let
- flag f = if f then "1" else "0"
- rreg8 = pack . Pr.printf "%02X"
- rreg16 = pack . Pr.printf "%04X"
- dumpRegs :: Text -> Registers -> Text
- dumpRegs prefix r = mconcat
- [ prefix, " registers:\t"
- , "A: ", rreg8 $ r ^. regA
- , ", B: ", rreg8 $ r ^. regB
- , ", C: ", rreg8 $ r ^. regC
- , ", D: ", rreg8 $ r ^. regD
- , ", E: ", rreg8 $ r ^. regE
- , ", H: ", rreg8 $ r ^. regH
- , ", L: ", rreg8 $ r ^. regL
- , ", PC: ", rreg16 $ r ^. regPC
- , ", SP: ", rreg16 $ r ^. regSP
- ]
- dumpFlags :: Text -> Registers -> Text
- dumpFlags prefix r = mconcat
- [ prefix, " flags:\t"
- , "Z: ", flag $ r ^. regFlagZ
- , ", N: ", flag $ r ^. regFlagN
- , ", H: ", flag $ r ^. regFlagH
- , ", C: ", flag $ r ^. regFlagC
- ]
- check :: (Eq a) => (a -> Text) -> Text -> a -> a -> IO ()
- check pr nm eval aval = if eval == aval
- then pure ()
- else throwM . InstrTestError $ mconcat
- [ "while running test ", tnm, ":\n"
- , nm <> " mismatch: expected "
- , pr eval, ", got ", pr aval
- , "\n", dumpRegs "Initial" $ initial ^. regs
- , "\n", dumpRegs "Final" $ c ^. regs
- , "\n", dumpFlags "Initial" $ initial ^. regs
- , "\n", dumpFlags "Final" $ c ^. regs
- ]
- check rreg8 "register A" (vs ^. testvalsA) (c ^. regs . regA)
- check rreg8 "register B" (vs ^. testvalsB) (c ^. regs . regB)
- check rreg8 "register C" (vs ^. testvalsC) (c ^. regs . regC)
- check rreg8 "register D" (vs ^. testvalsD) (c ^. regs . regD)
- check rreg8 "register E" (vs ^. testvalsE) (c ^. regs . regE)
- check rreg8 "register H" (vs ^. testvalsH) (c ^. regs . regH)
- check rreg8 "register L" (vs ^. testvalsL) (c ^. regs . regL)
- check rreg16 "PC" (vs ^. testvalsPC - 1) (c ^. regs . regPC)
- check rreg16 "SP" (vs ^. testvalsSP) (c ^. regs . regSP)
- let (fz, fn, fh, fc) = w8flags $ vs ^. testvalsF
- check flag "flag Z" fz (c ^. regs . regFlagZ)
- check flag "flag N" fn (c ^. regs . regFlagN)
- check flag "flag H" fh (c ^. regs . regFlagH)
- check flag "flag C" fc (c ^. regs . regFlagC)
- forM_ (vs ^. testvalsRAM) \(Addr -> addr, eval) -> do
- Bus.read (c ^. bus) addr >>= \case
- Nothing -> throwM . InstrTestError $ "failed to read expected address: " <> pretty addr
- Just aval -> check rreg8 ("memory address " <> pretty addr) eval aval
-
-runTestcase :: Testcase -> IO ()
-runTestcase tc = liftIO do
- initial <- cpuInstrTest $ tc ^. testcaseInitial
- let
- body :: Emulating Instruction
- body = do
- ins <- decode
- step ins
- pure ins
- (ins, final) <- runStateT (runEmulating body) initial
- checkCPU (tc ^. testcaseName <> " (" <> tshow ins <> ")") (tc ^. testcaseFinal) initial final