From 3cf0fe2db1a4eff5bf1c03ed097fa54c46a81463 Mon Sep 17 00:00:00 2001 From: Callum Lowcay Date: Sat, 30 Jan 2021 10:23:47 +1300 Subject: [PATCH] Use explicit import lists --- core/src/Machine/GBC/Audio.hs | 32 +-- core/src/Machine/GBC/Audio/Common.hs | 6 +- core/src/Machine/GBC/Audio/Envelope.hs | 12 +- core/src/Machine/GBC/Audio/Length.hs | 12 +- core/src/Machine/GBC/Audio/NoiseChannel.hs | 18 +- core/src/Machine/GBC/Audio/PulseChannel.hs | 20 +- core/src/Machine/GBC/Audio/Sweep.hs | 14 +- core/src/Machine/GBC/Audio/WaveChannel.hs | 18 +- core/src/Machine/GBC/Bus.hs | 4 +- core/src/Machine/GBC/CPU.hs | 174 ++++++++------- core/src/Machine/GBC/CPU/Backtrace.hs | 10 +- core/src/Machine/GBC/CPU/Decode.hs | 8 +- core/src/Machine/GBC/CPU/ISA.hs | 4 +- core/src/Machine/GBC/CPU/Interrupts.hs | 82 +++---- core/src/Machine/GBC/Color.hs | 2 +- core/src/Machine/GBC/DMA.hs | 40 ++-- core/src/Machine/GBC/Disassembler.hs | 170 +++++++-------- .../GBC/Disassembler/LabelGenerator.hs | 4 +- core/src/Machine/GBC/Emulator.hs | 54 ++--- core/src/Machine/GBC/Errors.hs | 6 +- core/src/Machine/GBC/Graphics.hs | 119 +++++----- core/src/Machine/GBC/Graphics/VRAM.hs | 67 +++--- core/src/Machine/GBC/Keypad.hs | 20 +- core/src/Machine/GBC/MBC.hs | 12 +- core/src/Machine/GBC/MBC/Interface.hs | 2 +- core/src/Machine/GBC/MBC/MBC1.hs | 10 +- core/src/Machine/GBC/MBC/MBC2.hs | 10 +- core/src/Machine/GBC/MBC/MBC3.hs | 10 +- core/src/Machine/GBC/MBC/MBC5.hs | 10 +- core/src/Machine/GBC/MBC/RTC.hs | 26 ++- core/src/Machine/GBC/Memory.hs | 118 +++++----- core/src/Machine/GBC/Mode.hs | 6 +- core/src/Machine/GBC/Primitive.hs | 18 +- core/src/Machine/GBC/Primitive/UnboxedRef.hs | 6 +- core/src/Machine/GBC/ROM.hs | 27 +-- core/src/Machine/GBC/Registers.hs | 2 +- core/src/Machine/GBC/Serial.hs | 26 +-- core/src/Machine/GBC/Timer.hs | 20 +- core/src/Machine/GBC/Util.hs | 4 +- core/test/Machine/GBC/CPUSpec.hs | 117 +++++----- core/test/Machine/GBC/DecodeSpec.hs | 12 +- core/test/Machine/GBC/EmulatorSpec.hs | 149 +++++++------ main/src/HGBC/Config.hs | 21 +- main/src/HGBC/Config/CommandLine.hs | 6 +- main/src/HGBC/Config/Decode.hs | 10 +- main/src/HGBC/Config/File.hs | 14 +- main/src/HGBC/Config/Paths.hs | 6 +- main/src/HGBC/Debugger.hs | 27 ++- main/src/HGBC/Debugger/Breakpoints.hs | 6 +- main/src/HGBC/Debugger/Disassembly.hs | 8 +- main/src/HGBC/Debugger/HTML.hs | 17 +- main/src/HGBC/Debugger/HTML/CPURegisters.hs | 2 +- main/src/HGBC/Debugger/HTML/Elements.hs | 7 +- main/src/HGBC/Debugger/HTML/LCDRegisters.hs | 2 +- main/src/HGBC/Debugger/HTML/SoundRegisters.hs | 2 +- .../src/HGBC/Debugger/HTML/SystemRegisters.hs | 4 +- main/src/HGBC/Debugger/JSON.hs | 4 +- main/src/HGBC/Debugger/Labels.hs | 10 +- main/src/HGBC/Debugger/Logging.hs | 6 +- main/src/HGBC/Debugger/Memory.hs | 12 +- main/src/HGBC/Debugger/ROM.hs | 36 ++-- main/src/HGBC/Debugger/Resources.hs | 2 +- main/src/HGBC/Debugger/State.hs | 32 +-- main/src/HGBC/Debugger/Status.hs | 136 ++++++------ main/src/HGBC/Debugger/SymFile.hs | 12 +- main/src/HGBC/Emulator.hs | 37 ++-- main/src/HGBC/Events.hs | 6 +- main/src/HGBC/Keymap.hs | 6 +- sdl/src/Audio.hs | 22 +- sdl/src/GLUtils.hs | 204 +++++++++--------- sdl/src/Keymap.hs | 4 +- sdl/src/Main.hs | 14 +- sdl/src/SDL/Extras.hs | 10 +- sdl/src/Thread/EventLoop.hs | 6 +- sdl/src/Thread/LCD.hs | 15 +- sdl/src/Window.hs | 8 +- testing/src/Framework.hs | 16 +- testing/src/Main.hs | 40 ++-- 78 files changed, 1130 insertions(+), 1121 deletions(-) diff --git a/core/src/Machine/GBC/Audio.hs b/core/src/Machine/GBC/Audio.hs index 327000f..627ae5c 100644 --- a/core/src/Machine/GBC/Audio.hs +++ b/core/src/Machine/GBC/Audio.hs @@ -11,18 +11,18 @@ module Machine.GBC.Audio ) where -import Control.Monad.Reader -import Data.Bifunctor -import Data.Bits -import Data.Functor -import Data.Word -import Machine.GBC.Audio.Common -import Machine.GBC.Audio.NoiseChannel -import Machine.GBC.Audio.PulseChannel -import Machine.GBC.Audio.WaveChannel +import Control.Monad.Reader (void, when) +import Data.Bifunctor (Bifunctor (first)) +import Data.Bits (Bits (testBit, (.&.), (.|.))) +import Data.Functor ((<&>)) +import Data.Word (Word16, Word8) +import Machine.GBC.Audio.Common (Channel (..), FrameSequencerOutput (..), flagChannel1Enable, flagChannel2Enable, flagMasterPower, newAudioPort) +import Machine.GBC.Audio.NoiseChannel (NoiseChannel, newNoiseChannel) +import Machine.GBC.Audio.PulseChannel (PulseChannel, newPulseChannel) +import Machine.GBC.Audio.WaveChannel (WaveChannel, newWaveChannel) import Machine.GBC.Primitive -import Machine.GBC.Registers -import Machine.GBC.Util +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (isFlagSet, (.<<.), (.>>.)) import Prelude hiding (init) data State = State @@ -79,16 +79,16 @@ init = mdo ports :: State -> [(Word16, Port)] ports State {..} = - [(NR50, port50), (NR51, port51), (NR52, port52), (PCM12, portPCM12), (PCM34, portPCM34)] + [(R.NR50, port50), (R.NR51, port51), (R.NR52, port52), (R.PCM12, portPCM12), (R.PCM34, portPCM34)] ++ channel1Ports ++ channel2Ports ++ channel3Ports ++ channel4Ports where - channel1Ports = first ((+ NR10) . fromIntegral) <$> getPorts channel1 - channel2Ports = first ((+ NR20) . fromIntegral) <$> getPorts channel2 - channel3Ports = first ((+ NR30) . fromIntegral) <$> getPorts channel3 - channel4Ports = first ((+ NR40) . fromIntegral) <$> getPorts channel4 + channel1Ports = first ((+ R.NR10) . fromIntegral) <$> getPorts channel1 + channel2Ports = first ((+ R.NR20) . fromIntegral) <$> getPorts channel2 + channel3Ports = first ((+ R.NR30) . fromIntegral) <$> getPorts channel3 + channel4Ports = first ((+ R.NR40) . fromIntegral) <$> getPorts channel4 samplePeriod :: Int samplePeriod = 94 diff --git a/core/src/Machine/GBC/Audio/Common.hs b/core/src/Machine/GBC/Audio/Common.hs index fc5e5aa..ea4a110 100644 --- a/core/src/Machine/GBC/Audio/Common.hs +++ b/core/src/Machine/GBC/Audio/Common.hs @@ -21,10 +21,10 @@ module Machine.GBC.Audio.Common ) where -import Data.Bits -import Data.Word +import Data.Bits (Bits (..)) +import Data.Word (Word8) import Machine.GBC.Primitive -import Machine.GBC.Util +import Machine.GBC.Util (isFlagSet, (.<<.)) flagTrigger, flagLength :: Word8 flagTrigger = 0x80 diff --git a/core/src/Machine/GBC/Audio/Envelope.hs b/core/src/Machine/GBC/Audio/Envelope.hs index fb114e6..9688a84 100644 --- a/core/src/Machine/GBC/Audio/Envelope.hs +++ b/core/src/Machine/GBC/Audio/Envelope.hs @@ -9,13 +9,13 @@ module Machine.GBC.Audio.Envelope ) where -import Control.Monad -import Data.Bits -import Data.Word -import Machine.GBC.Audio.Common +import Control.Monad (unless) +import Data.Bits (Bits (..)) +import Data.Word (Word8) +import Machine.GBC.Audio.Common (FrameSequencerOutput, nextStepWillClockEnvelope) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.Util (isFlagSet, (.>>.)) data Envelope = Envelope { volumeRef :: !(UnboxedRef Int), diff --git a/core/src/Machine/GBC/Audio/Length.hs b/core/src/Machine/GBC/Audio/Length.hs index 2ca1acb..6f612a7 100644 --- a/core/src/Machine/GBC/Audio/Length.hs +++ b/core/src/Machine/GBC/Audio/Length.hs @@ -11,11 +11,11 @@ module Machine.GBC.Audio.Length ) where -import Control.Monad -import Data.Bits -import Data.IORef -import Data.Word -import Machine.GBC.Audio.Common +import Control.Monad (unless, when) +import Data.Bits (Bits (..)) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word8) +import Machine.GBC.Audio.Common (FrameSequencerOutput, lastStepClockedLength) import Machine.GBC.Primitive data Length = Length @@ -42,7 +42,7 @@ initLength Length {..} frameSequencer enabled = do -- Quirk: If we are enabling the length counter, and it is currently 0, and -- the last frame sequencer step clocked the length, then clock the length -- again. - when (enabled && (lastStepClockedLength frameSequencer) && v == 0) $ + when (enabled && lastStepClockedLength frameSequencer && v == 0) $ updateCounter counter 1 (pure 0) powerOffLength :: Length -> IO () diff --git a/core/src/Machine/GBC/Audio/NoiseChannel.hs b/core/src/Machine/GBC/Audio/NoiseChannel.hs index f188ebb..fcee7e5 100644 --- a/core/src/Machine/GBC/Audio/NoiseChannel.hs +++ b/core/src/Machine/GBC/Audio/NoiseChannel.hs @@ -4,16 +4,16 @@ module Machine.GBC.Audio.NoiseChannel where -import Control.Monad.Reader -import Data.Bits -import Data.IORef -import Data.Word -import Machine.GBC.Audio.Common -import Machine.GBC.Audio.Envelope -import Machine.GBC.Audio.Length +import Control.Monad.Reader (unless, when) +import Data.Bits (Bits (testBit, (.&.))) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word16, Word8) +import Machine.GBC.Audio.Common (Channel (..), FrameSequencerOutput, flagChannel4Enable, flagLength, flagTrigger, isEnvelopeClockingStep, isLengthClockingStep, newAudioPortWithReadMask, updateStatus) +import Machine.GBC.Audio.Envelope (Envelope, clockEnvelope, envelopeVolume, initEnvelope, newEnvelope) +import Machine.GBC.Audio.Length (Length, clockLength, extraClocks, initLength, newLength, powerOffLength, reloadLength) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.Util (isFlagSet, (.<<.), (.>>.)) data NoiseChannel = NoiseChannel { enable :: !(IORef Bool), diff --git a/core/src/Machine/GBC/Audio/PulseChannel.hs b/core/src/Machine/GBC/Audio/PulseChannel.hs index 76d33d7..9f84246 100644 --- a/core/src/Machine/GBC/Audio/PulseChannel.hs +++ b/core/src/Machine/GBC/Audio/PulseChannel.hs @@ -7,17 +7,17 @@ module Machine.GBC.Audio.PulseChannel ) where -import Control.Monad.Reader -import Data.Bits -import Data.IORef -import Data.Word -import Machine.GBC.Audio.Common -import Machine.GBC.Audio.Envelope -import Machine.GBC.Audio.Length -import Machine.GBC.Audio.Sweep +import Control.Monad.Reader (unless, when) +import Data.Bits (Bits (complement, (.&.), (.|.))) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word8) +import Machine.GBC.Audio.Common (Channel (..), FrameSequencerOutput, flagLength, flagTrigger, getFrequency, isEnvelopeClockingStep, isLengthClockingStep, isSweepClockingStep, newAudioPort, newAudioPortWithReadMask, updateStatus) +import Machine.GBC.Audio.Envelope (Envelope, clockEnvelope, envelopeVolume, initEnvelope, newEnvelope) +import Machine.GBC.Audio.Length (Length, clockLength, extraClocks, initLength, newLength, powerOffLength, reloadLength) +import Machine.GBC.Audio.Sweep (Sweep, clockSweep, flagNegate, hasPerformedSweepCalculationInNegateMode, initSweep, newSweep) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.Util (isFlagSet, (.>>.)) data PulseChannel = PulseChannel { output :: !(UnboxedRef Int), diff --git a/core/src/Machine/GBC/Audio/Sweep.hs b/core/src/Machine/GBC/Audio/Sweep.hs index 6150f9a..16a634a 100644 --- a/core/src/Machine/GBC/Audio/Sweep.hs +++ b/core/src/Machine/GBC/Audio/Sweep.hs @@ -10,14 +10,14 @@ module Machine.GBC.Audio.Sweep ) where -import Control.Monad -import Data.Bits -import Data.IORef -import Data.Word -import Machine.GBC.Audio.Common +import Control.Monad (void, when) +import Data.Bits (Bits ((.&.))) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word8) +import Machine.GBC.Audio.Common (updateFrequency) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.Util (isFlagSet, (.>>.)) data Sweep = Sweep { enable :: !(IORef Bool), diff --git a/core/src/Machine/GBC/Audio/WaveChannel.hs b/core/src/Machine/GBC/Audio/WaveChannel.hs index a3f71cc..26652c2 100644 --- a/core/src/Machine/GBC/Audio/WaveChannel.hs +++ b/core/src/Machine/GBC/Audio/WaveChannel.hs @@ -8,17 +8,17 @@ module Machine.GBC.Audio.WaveChannel ) where -import Control.Monad.Reader -import Data.Bits -import Data.Foldable -import Data.IORef +import Control.Monad.Reader (unless, when) +import Data.Bits (Bits ((.&.))) +import Data.Foldable (Foldable (toList)) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) import qualified Data.Vector as V -import Data.Word -import Machine.GBC.Audio.Common -import Machine.GBC.Audio.Length +import Data.Word (Word8) +import Machine.GBC.Audio.Common (Channel (..), FrameSequencerOutput, flagChannel3Enable, flagLength, flagTrigger, getFrequency, isLengthClockingStep, newAudioPortWithReadMask, updateStatus) +import Machine.GBC.Audio.Length (Length, clockLength, extraClocks, initLength, newLength, powerOffLength, reloadLength) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.Util (isFlagSet, (.>>.)) data WaveChannel = WaveChannel { output :: !(UnboxedRef Int), diff --git a/core/src/Machine/GBC/Bus.hs b/core/src/Machine/GBC/Bus.hs index bee9d9e..338fdc0 100644 --- a/core/src/Machine/GBC/Bus.hs +++ b/core/src/Machine/GBC/Bus.hs @@ -3,8 +3,8 @@ module Machine.GBC.Bus ) where -import Control.Monad.Reader -import Data.Word +import Control.Monad.Reader (ReaderT) +import Data.Word (Word16, Word8) class Has env where -- | Do one read cycle. diff --git a/core/src/Machine/GBC/CPU.hs b/core/src/Machine/GBC/CPU.hs index a55004f..aeada02 100644 --- a/core/src/Machine/GBC/CPU.hs +++ b/core/src/Machine/GBC/CPU.hs @@ -47,27 +47,28 @@ module Machine.GBC.CPU where import Control.Exception (throwIO) -import Control.Monad.Reader -import Data.Bits -import Data.Foldable -import Data.IORef -import Data.Int +import Control.Monad.Reader (MonadIO (..), ReaderT (ReaderT), asks, replicateM_, when) +import Data.Bits (Bits (..)) +import Data.Foldable (for_) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Int (Int32, Int8) import qualified Data.Vector.Storable.Mutable as VSM -import Data.Word -import Foreign.Ptr -import Foreign.Storable +import Data.Word (Word16, Word32, Word8) +import Foreign.Ptr (castPtr) +import Foreign.Storable (Storable (..)) import qualified Machine.GBC.Bus as Bus import qualified Machine.GBC.CPU.Backtrace as Backtrace -import Machine.GBC.CPU.Decode -import Machine.GBC.CPU.ISA -import Machine.GBC.CPU.Interrupts -import Machine.GBC.Errors +import Machine.GBC.CPU.Decode (MonadFetch (..), decodeAndExecute) +import Machine.GBC.CPU.ISA (ConditionCode (..), MonadSm83x (..), Register16 (..), Register8 (..), RegisterHalf (..), RegisterPushPop (..)) +import Machine.GBC.CPU.Interrupts (Interrupt) +import qualified Machine.GBC.CPU.Interrupts as Interrupt +import Machine.GBC.Errors (Fault (InvalidInstruction)) import qualified Machine.GBC.Memory as Memory -import Machine.GBC.Mode +import Machine.GBC.Mode (EmulatorMode (DMG)) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Registers -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (isFlagSet, (.<<.), (.>>.)) import Prelude hiding (init) -- | The register file. @@ -87,31 +88,26 @@ data RegisterFile = RegisterFile deriving (Eq, Ord, Show) offsetF, offsetA, offsetC, offsetB :: Int -offsetE, offsetD, offsetL, offsetH :: Int offsetF = 0 offsetA = 1 offsetC = 2 offsetB = 3 +offsetE, offsetD, offsetL, offsetH :: Int offsetE = 4 - offsetD = 5 - offsetL = 6 - offsetH = 7 offsetAF, offsetBC, offsetDE, offsetHL :: Int -offsetPC, offsetSP, offsetHidden :: Int offsetAF = 0 offsetBC = 1 offsetDE = 2 offsetHL = 3 +offsetPC, offsetSP, offsetHidden :: Int offsetSP = 4 - offsetPC = 5 - offsetHidden = 6 instance Storable RegisterFile where @@ -185,7 +181,7 @@ init portIF portIE modeRef = do pure State {..} ports :: State -> [(Word16, Port)] -ports State {..} = [(KEY1, portKEY1)] +ports State {..} = [(R.KEY1, portKEY1)] -- | Get the current cpu mode. {-# INLINEABLE getMode #-} @@ -423,45 +419,45 @@ reset = do writeUnboxedRef callDepth 0 Backtrace.reset backtrace - Memory.writeByte P1 0xFF - Memory.writeByte DIV 0 - Memory.writeByte SC 0 - Memory.writeByte SB 0 - Memory.writeByte TIMA 0 - Memory.writeByte TMA 0 - Memory.writeByte TAC 0 - Memory.writeByte NR10 0 - Memory.writeByte NR11 0 - Memory.writeByte NR12 0 - Memory.writeByte NR13 0 - Memory.writeByte NR14 0 - Memory.writeByte NR21 0 - Memory.writeByte NR22 0 - Memory.writeByte NR23 0 - Memory.writeByte NR24 0 - Memory.writeByte NR30 0 - Memory.writeByte NR31 0 - Memory.writeByte NR32 0 - Memory.writeByte NR33 0 - Memory.writeByte NR34 0 - Memory.writeByte NR41 0 - Memory.writeByte NR42 0 - Memory.writeByte NR43 0 - Memory.writeByte NR44 0 - Memory.writeByte NR50 0 - Memory.writeByte NR51 0 - Memory.writeByte NR52 0 - Memory.writeByte LCDC 0 - Memory.writeByte SCY 0 - Memory.writeByte SCX 0 - Memory.writeByte LYC 0 - Memory.writeByte BGP 0 - Memory.writeByte OBP0 0 - Memory.writeByte OBP1 0 - Memory.writeByte WY 0 - Memory.writeByte WX 0 - Memory.writeByte IE 0 - Memory.writeByte IF 0 + Memory.writeByte R.P1 0xFF + Memory.writeByte R.DIV 0 + Memory.writeByte R.SC 0 + Memory.writeByte R.SB 0 + Memory.writeByte R.TIMA 0 + Memory.writeByte R.TMA 0 + Memory.writeByte R.TAC 0 + Memory.writeByte R.NR10 0 + Memory.writeByte R.NR11 0 + Memory.writeByte R.NR12 0 + Memory.writeByte R.NR13 0 + Memory.writeByte R.NR14 0 + Memory.writeByte R.NR21 0 + Memory.writeByte R.NR22 0 + Memory.writeByte R.NR23 0 + Memory.writeByte R.NR24 0 + Memory.writeByte R.NR30 0 + Memory.writeByte R.NR31 0 + Memory.writeByte R.NR32 0 + Memory.writeByte R.NR33 0 + Memory.writeByte R.NR34 0 + Memory.writeByte R.NR41 0 + Memory.writeByte R.NR42 0 + Memory.writeByte R.NR43 0 + Memory.writeByte R.NR44 0 + Memory.writeByte R.NR50 0 + Memory.writeByte R.NR51 0 + Memory.writeByte R.NR52 0 + Memory.writeByte R.LCDC 0 + Memory.writeByte R.SCY 0 + Memory.writeByte R.SCX 0 + Memory.writeByte R.LYC 0 + Memory.writeByte R.BGP 0 + Memory.writeByte R.OBP0 0 + Memory.writeByte R.OBP1 0 + Memory.writeByte R.WY 0 + Memory.writeByte R.WX 0 + Memory.writeByte R.IE 0 + Memory.writeByte R.IF 0 -- Wave memory Memory.writeByte 0xFF30 0x00 @@ -496,20 +492,20 @@ reset = do setIME writePC 0x100 - Memory.writeByte NR52 0xF1 - Memory.writeByte NR11 0xBF - Memory.writeByte NR12 0x11 - Memory.writeByte NR50 0x77 - Memory.writeByte NR51 0xF3 - Memory.writeByte NR14 0x80 - Memory.writeByte NR12 0xF3 - Memory.writeByte LCDC 0x91 - Memory.writeByte BGP 0xFC - Memory.writeByte OBP0 0xFF - Memory.writeByte OBP1 0xFF - Memory.writeByte BCPS 0x88 - Memory.writeByte OCPS 0x90 - Memory.writeByte IF 0xE1 + Memory.writeByte R.NR52 0xF1 + Memory.writeByte R.NR11 0xBF + Memory.writeByte R.NR12 0x11 + Memory.writeByte R.NR50 0x77 + Memory.writeByte R.NR51 0xF3 + Memory.writeByte R.NR14 0x80 + Memory.writeByte R.NR12 0xF3 + Memory.writeByte R.LCDC 0x91 + Memory.writeByte R.BGP 0xFC + Memory.writeByte R.OBP0 0xFF + Memory.writeByte R.OBP1 0xFF + Memory.writeByte R.BCPS 0x88 + Memory.writeByte R.OCPS 0x90 + Memory.writeByte R.IF 0xE1 for_ ([0xFF80 ..] `zip` atFF80) (uncurry Memory.writeByte) @@ -562,12 +558,12 @@ flagSpeedSwitch :: Word8 flagSpeedSwitch = 0x01 interruptVector :: Interrupt -> Word16 -interruptVector InterruptVBlank = 0x40 -interruptVector InterruptLCDCStat = 0x48 -interruptVector InterruptTimerOverflow = 0x50 -interruptVector InterruptEndSerialTransfer = 0x58 -interruptVector InterruptP1Low = 0x60 -interruptVector InterruptCancelled = 0 +interruptVector Interrupt.VBlank = 0x40 +interruptVector Interrupt.LCDCStat = 0x48 +interruptVector Interrupt.TimerOverflow = 0x50 +interruptVector Interrupt.EndSerialTransfer = 0x58 +interruptVector Interrupt.P1Low = 0x60 +interruptVector Interrupt.Cancelled = 0 -- | Fetch, decode, and execute a single instruction. {-# INLINEABLE step #-} @@ -584,14 +580,14 @@ step = do ModeNormal -> do pc <- readPC byte <- Bus.read pc - interrupts <- pendingEnabledInterrupts portIF portIE + interrupts <- Interrupt.getPending portIF portIE if interrupts /= 0 && ime then handleInterrupt interrupts else do writePC (pc + 1) run (decodeAndExecute byte) ModeHalt -> do - interrupts <- pendingEnabledInterrupts portIF portIE + interrupts <- Interrupt.getPending portIF portIE if interrupts == 0 then Bus.delay else do @@ -607,7 +603,7 @@ step = do run . decodeAndExecute =<< Bus.read =<< readPC else run (decodeAndExecute =<< nextByte) ModeStop -> do - interrupts <- pendingEnabledInterrupts portIF portIE + interrupts <- Interrupt.getPending portIF portIE if interrupts == 0 then Bus.delay else do @@ -626,12 +622,12 @@ step = do ie <- directReadPort portIE Bus.write (sp - 2) =<< readRHalf RegPCL - let nextInterrupt = getNextInterrupt (interrupts .&. ie) + let nextInterrupt = Interrupt.getNext (interrupts .&. ie) let vector = interruptVector nextInterrupt callStackPushed vector writePC vector clearIME - clearInterrupt portIF nextInterrupt + Interrupt.clear portIF nextInterrupt {-# INLINE getCycleClocks #-} getCycleClocks :: Has env => ReaderT env IO Int @@ -1194,7 +1190,7 @@ instance (Bus.Has env, Has env) => MonadSm83x (M env) where {-# INLINE halt #-} halt = M $ do State {..} <- asks forState - interrupts <- pendingEnabledInterrupts portIF portIE + interrupts <- Interrupt.getPending portIF portIE ime <- testIME when (not ime && interrupts /= 0) $ liftIO $ writeIORef haltBug True setMode ModeHalt diff --git a/core/src/Machine/GBC/CPU/Backtrace.hs b/core/src/Machine/GBC/CPU/Backtrace.hs index ef24971..82f0502 100644 --- a/core/src/Machine/GBC/CPU/Backtrace.hs +++ b/core/src/Machine/GBC/CPU/Backtrace.hs @@ -10,12 +10,12 @@ module Machine.GBC.CPU.Backtrace ) where -import Control.Monad -import Control.Monad.IO.Class -import Data.Bits +import Control.Monad (when) +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Bits (Bits (..)) import qualified Data.Vector.Unboxed.Mutable as VUM -import Data.Word -import Machine.GBC.Primitive.UnboxedRef +import Data.Word (Word16) +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) data Backtrace = Backtrace { trace :: !(VUM.IOVector (Word16, Word16)), diff --git a/core/src/Machine/GBC/CPU/Decode.hs b/core/src/Machine/GBC/CPU/Decode.hs index 31c827b..0a4a5cb 100644 --- a/core/src/Machine/GBC/CPU/Decode.hs +++ b/core/src/Machine/GBC/CPU/Decode.hs @@ -12,11 +12,11 @@ module Machine.GBC.CPU.Decode ) where -import Data.Bits +import Data.Bits (Bits (..)) import qualified Data.Vector as V -import Data.Word -import Machine.GBC.CPU.ISA -import Machine.GBC.Util +import Data.Word (Word16, Word8) +import Machine.GBC.CPU.ISA (ConditionCode (..), MonadSm83x (..), Register16 (..), Register8 (..), RegisterPushPop (..)) +import Machine.GBC.Util ((.<<.), (.>>.)) class Monad m => MonadFetch m where nextByte :: m Word8 diff --git a/core/src/Machine/GBC/CPU/ISA.hs b/core/src/Machine/GBC/CPU/ISA.hs index ffe21d1..735645e 100644 --- a/core/src/Machine/GBC/CPU/ISA.hs +++ b/core/src/Machine/GBC/CPU/ISA.hs @@ -10,8 +10,8 @@ module Machine.GBC.CPU.ISA ) where -import Data.Int -import Data.Word +import Data.Int (Int8) +import Data.Word (Word16, Word8) -- | An 8-bit register data Register8 = RegA | RegB | RegC | RegD | RegE | RegH | RegL deriving (Eq, Ord, Bounded, Enum) diff --git a/core/src/Machine/GBC/CPU/Interrupts.hs b/core/src/Machine/GBC/CPU/Interrupts.hs index 66fb875..705a1ed 100644 --- a/core/src/Machine/GBC/CPU/Interrupts.hs +++ b/core/src/Machine/GBC/CPU/Interrupts.hs @@ -1,62 +1,62 @@ module Machine.GBC.CPU.Interrupts ( Interrupt (..), - flagInterrupt, - raiseInterrupt, - clearInterrupt, - pendingEnabledInterrupts, - getNextInterrupt, + flag, + raise, + clear, + getPending, + getNext, ) where -import Control.Monad.IO.Class -import Data.Bits -import Data.Word +import Control.Monad.IO.Class (MonadIO) +import Data.Bits (Bits (..), FiniteBits (..)) +import Data.Word (Word8) import Machine.GBC.Primitive data Interrupt - = InterruptVBlank - | InterruptLCDCStat - | InterruptTimerOverflow - | InterruptEndSerialTransfer - | InterruptP1Low - | InterruptCancelled + = VBlank + | LCDCStat + | TimerOverflow + | EndSerialTransfer + | P1Low + | Cancelled deriving (Eq, Ord, Show, Bounded, Enum) -flagInterrupt :: Interrupt -> Word8 -flagInterrupt InterruptVBlank = 0x01 -flagInterrupt InterruptLCDCStat = 0x02 -flagInterrupt InterruptTimerOverflow = 0x04 -flagInterrupt InterruptEndSerialTransfer = 0x08 -flagInterrupt InterruptP1Low = 0x10 -flagInterrupt InterruptCancelled = 0 +flag :: Interrupt -> Word8 +flag VBlank = 0x01 +flag LCDCStat = 0x02 +flag TimerOverflow = 0x04 +flag EndSerialTransfer = 0x08 +flag P1Low = 0x10 +flag Cancelled = 0 -{-# INLINE raiseInterrupt #-} -raiseInterrupt :: MonadIO m => Port -> Interrupt -> m () -raiseInterrupt portIF interrupt = do +{-# INLINE raise #-} +raise :: MonadIO m => Port -> Interrupt -> m () +raise portIF interrupt = do rif <- directReadPort portIF - directWritePort portIF (rif .|. flagInterrupt interrupt) + directWritePort portIF (rif .|. flag interrupt) -{-# INLINE clearInterrupt #-} -clearInterrupt :: MonadIO m => Port -> Interrupt -> m () -clearInterrupt portIF interrupt = do +{-# INLINE clear #-} +clear :: MonadIO m => Port -> Interrupt -> m () +clear portIF interrupt = do rif <- directReadPort portIF - directWritePort portIF (rif .&. complement (flagInterrupt interrupt)) + directWritePort portIF (rif .&. complement (flag interrupt)) -- | Get all of the pending interrupts that are ready to service. -{-# INLINE pendingEnabledInterrupts #-} -pendingEnabledInterrupts :: MonadIO m => Port -> Port -> m Word8 -pendingEnabledInterrupts portIF portIE = do +{-# INLINE getPending #-} +getPending :: MonadIO m => Port -> Port -> m Word8 +getPending portIF portIE = do interrupt <- directReadPort portIF enabled <- directReadPort portIE pure (interrupt .&. enabled .&. 0x1F) -- | Get the next interrupt to service. -{-# INLINE getNextInterrupt #-} -getNextInterrupt :: Word8 -> Interrupt -getNextInterrupt pendingInterrupts = case countTrailingZeros pendingInterrupts of - 0 -> InterruptVBlank - 1 -> InterruptLCDCStat - 2 -> InterruptTimerOverflow - 3 -> InterruptEndSerialTransfer - 4 -> InterruptP1Low - _ -> InterruptCancelled +{-# INLINE getNext #-} +getNext :: Word8 -> Interrupt +getNext pendingInterrupts = case countTrailingZeros pendingInterrupts of + 0 -> VBlank + 1 -> LCDCStat + 2 -> TimerOverflow + 3 -> EndSerialTransfer + 4 -> P1Low + _ -> Cancelled diff --git a/core/src/Machine/GBC/Color.hs b/core/src/Machine/GBC/Color.hs index 453c7fd..2763a6e 100644 --- a/core/src/Machine/GBC/Color.hs +++ b/core/src/Machine/GBC/Color.hs @@ -5,7 +5,7 @@ module Machine.GBC.Color ) where -import Data.Word +import Data.Word (Word16, Word32) type Correction = (Word16, Word16, Word16) -> (Word32, Word32, Word32) diff --git a/core/src/Machine/GBC/DMA.hs b/core/src/Machine/GBC/DMA.hs index 8b27f5f..b2ffacb 100644 --- a/core/src/Machine/GBC/DMA.hs +++ b/core/src/Machine/GBC/DMA.hs @@ -7,25 +7,25 @@ module Machine.GBC.DMA init, ports, update, - doPendingHDMA, - doHBlankHDMA, + doPending, + doHBlank, makeHDMASource, makeHDMADestination, ) where -import Control.Monad.Reader -import Data.Bits -import Data.IORef -import Data.Word +import Control.Monad.Reader (MonadIO (liftIO), ReaderT, when) +import Data.Bits (Bits ((.&.), (.|.))) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word16, Word8) import qualified Machine.GBC.Bus as Bus import qualified Machine.GBC.Graphics.VRAM as VRAM import qualified Machine.GBC.Memory as Memory -import Machine.GBC.Mode +import Machine.GBC.Mode (EmulatorMode, cgbOnlyPort) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Registers -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util ((.<<.)) import Prelude hiding (init) data PendingDMA = Pending !Word8 | None deriving (Eq, Ord, Show) @@ -48,12 +48,12 @@ data State = State ports :: State -> [(Word16, Port)] ports State {..} = - [ (DMA, portDMA), - (HDMA1, portHDMA1), - (HDMA2, portHDMA2), - (HDMA3, portHDMA3), - (HDMA4, portHDMA4), - (HDMA5, portHDMA5) + [ (R.DMA, portDMA), + (R.HDMA1, portHDMA1), + (R.HDMA2, portHDMA2), + (R.HDMA3, portHDMA3), + (R.HDMA4, portHDMA4), + (R.HDMA5, portHDMA5) ] oamBytes :: Word16 @@ -121,8 +121,8 @@ update State {..} = do -- | Perform any pending HDMA actions for this emulation cycle, and return the -- number of clocks to stall the CPU. -doPendingHDMA :: (Memory.Has env, Bus.Has env) => State -> ReaderT env IO () -doPendingHDMA State {..} = do +doPending :: (Memory.Has env, Bus.Has env) => State -> ReaderT env IO () +doPending State {..} = do maybeHDMA <- liftIO $ readIORef pendingHDMA case maybeHDMA of None -> pure () @@ -140,8 +140,8 @@ doPendingHDMA State {..} = do -- | Notify the DMA controller that the LCD has entered the HBlank state. Return -- the number of clock cycles to stall the CPU. -doHBlankHDMA :: (Memory.Has env, Bus.Has env) => State -> ReaderT env IO () -doHBlankHDMA State {..} = do +doHBlank :: (Memory.Has env, Bus.Has env) => State -> ReaderT env IO () +doHBlank State {..} = do isActive <- liftIO $ readIORef hdmaActive when isActive $ do source <- readUnboxedRef hdmaSource diff --git a/core/src/Machine/GBC/Disassembler.hs b/core/src/Machine/GBC/Disassembler.hs index a07d281..aed8f21 100644 --- a/core/src/Machine/GBC/Disassembler.hs +++ b/core/src/Machine/GBC/Disassembler.hs @@ -30,33 +30,33 @@ module Machine.GBC.Disassembler ) where -import Control.Category hiding ((.)) -import Control.Monad.Reader -import Control.Monad.State.Strict -import Data.Bifunctor -import Data.Bits +import Control.Category ((>>>)) +import Control.Monad.Reader (MonadIO (liftIO), ReaderT (runReaderT), asks, foldM) +import Control.Monad.State.Strict (MonadState (get, put), StateT (runStateT), evalStateT, gets) +import Data.Bifunctor (Bifunctor (bimap, first)) +import Data.Bits (Bits ((.&.), (.|.))) import qualified Data.ByteString.Short as SB -import Data.Char -import Data.Foldable -import Data.Function -import Data.Functor -import Data.Hashable -import Data.Int +import Data.Char (isAlphaNum) +import Data.Foldable (foldl') +import Data.Function ((&)) +import Data.Functor ((<&>)) +import Data.Hashable (Hashable (hashWithSalt)) +import Data.Int (Int8) import qualified Data.IntMap.Lazy as IM import Data.List (intersperse) -import Data.Maybe -import Data.String +import Data.Maybe (catMaybes) +import Data.String (IsString (fromString)) import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Builder as TB import qualified Data.Vector.Storable as VS -import Data.Word -import Machine.GBC.CPU.Decode -import Machine.GBC.CPU.ISA -import Machine.GBC.Disassembler.LabelGenerator +import Data.Word (Word16, Word8) +import Machine.GBC.CPU.Decode (MonadFetch (..), decodeAndExecute) +import Machine.GBC.CPU.ISA (ConditionCode (..), MonadSm83x (..), Register16 (..), Register8 (..), RegisterPushPop (..)) +import Machine.GBC.Disassembler.LabelGenerator (nextGlobalLabel, nextLocalLabel) import qualified Machine.GBC.Memory as Memory -import Machine.GBC.Registers -import Machine.GBC.Util +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (formatHex, (.<<.), (.>>.)) import Prelude hiding (lookup) data LongAddress @@ -366,72 +366,72 @@ initialLabels = (LongAddress 0 0x58, ("int58_serial", False)), (LongAddress 0 0x60, ("int60_keypad", False)), (LongAddress 0 0x100, ("rom_header", False)), - (LongAddress 0 P1, ("P1", False)), - (LongAddress 0 SB, ("SB", False)), - (LongAddress 0 SC, ("SC", False)), - (LongAddress 0 DIV, ("DIV", False)), - (LongAddress 0 TIMA, ("TIMA", False)), - (LongAddress 0 TMA, ("TMA", False)), - (LongAddress 0 TAC, ("TAC", False)), - (LongAddress 0 NR10, ("NR10", False)), - (LongAddress 0 NR11, ("NR11", False)), - (LongAddress 0 NR12, ("NR12", False)), - (LongAddress 0 NR13, ("NR13", False)), - (LongAddress 0 NR14, ("NR14", False)), - (LongAddress 0 NR20, ("NR20", False)), - (LongAddress 0 NR21, ("NR21", False)), - (LongAddress 0 NR22, ("NR22", False)), - (LongAddress 0 NR23, ("NR23", False)), - (LongAddress 0 NR24, ("NR24", False)), - (LongAddress 0 NR30, ("NR30", False)), - (LongAddress 0 NR31, ("NR31", False)), - (LongAddress 0 NR32, ("NR32", False)), - (LongAddress 0 NR33, ("NR33", False)), - (LongAddress 0 NR34, ("NR34", False)), - (LongAddress 0 NR40, ("NR40", False)), - (LongAddress 0 NR41, ("NR41", False)), - (LongAddress 0 NR42, ("NR42", False)), - (LongAddress 0 NR43, ("NR43", False)), - (LongAddress 0 NR44, ("NR44", False)), - (LongAddress 0 NR50, ("NR50", False)), - (LongAddress 0 NR51, ("NR51", False)), - (LongAddress 0 NR52, ("NR52", False)), - (LongAddress 0 IF, ("IF", False)), - (LongAddress 0 LCDC, ("LCDC", False)), - (LongAddress 0 STAT, ("STAT", False)), - (LongAddress 0 SCY, ("SCY", False)), - (LongAddress 0 SCX, ("SCX", False)), - (LongAddress 0 LY, ("LY", False)), - (LongAddress 0 LYC, ("LYC", False)), - (LongAddress 0 DMA, ("DMA", False)), - (LongAddress 0 BGP, ("BGP", False)), - (LongAddress 0 OBP0, ("OBP0", False)), - (LongAddress 0 OBP1, ("OBP1", False)), - (LongAddress 0 WY, ("WY", False)), - (LongAddress 0 WX, ("WX", False)), - (LongAddress 0 R4C, ("R4C", False)), - (LongAddress 0 KEY1, ("KEY1", False)), - (LongAddress 0 VBK, ("VBK", False)), - (LongAddress 0 BLCK, ("BLCK", False)), - (LongAddress 0 HDMA1, ("HDMA1", False)), - (LongAddress 0 HDMA2, ("HDMA2", False)), - (LongAddress 0 HDMA3, ("HDMA3", False)), - (LongAddress 0 HDMA4, ("HDMA4", False)), - (LongAddress 0 HDMA5, ("HDMA5", False)), - (LongAddress 0 RP, ("RP", False)), - (LongAddress 0 BCPS, ("BCPS", False)), - (LongAddress 0 BCPD, ("BCPD", False)), - (LongAddress 0 OCPS, ("OCPS", False)), - (LongAddress 0 OCPD, ("OCPD", False)), - (LongAddress 0 R6C, ("R6C", False)), - (LongAddress 0 SVBK, ("SVBK", False)), - (LongAddress 0 R72, ("R72", False)), - (LongAddress 0 R73, ("R73", False)), - (LongAddress 0 R74, ("R74", False)), - (LongAddress 0 R75, ("R75", False)), - (LongAddress 0 PCM12, ("PCM12", False)), - (LongAddress 0 PCM34, ("PCM34", False)), - (LongAddress 0 IE, ("IE", False)) + (LongAddress 0 R.P1, ("P1", False)), + (LongAddress 0 R.SB, ("SB", False)), + (LongAddress 0 R.SC, ("SC", False)), + (LongAddress 0 R.DIV, ("DIV", False)), + (LongAddress 0 R.TIMA, ("TIMA", False)), + (LongAddress 0 R.TMA, ("TMA", False)), + (LongAddress 0 R.TAC, ("TAC", False)), + (LongAddress 0 R.NR10, ("NR10", False)), + (LongAddress 0 R.NR11, ("NR11", False)), + (LongAddress 0 R.NR12, ("NR12", False)), + (LongAddress 0 R.NR13, ("NR13", False)), + (LongAddress 0 R.NR14, ("NR14", False)), + (LongAddress 0 R.NR20, ("NR20", False)), + (LongAddress 0 R.NR21, ("NR21", False)), + (LongAddress 0 R.NR22, ("NR22", False)), + (LongAddress 0 R.NR23, ("NR23", False)), + (LongAddress 0 R.NR24, ("NR24", False)), + (LongAddress 0 R.NR30, ("NR30", False)), + (LongAddress 0 R.NR31, ("NR31", False)), + (LongAddress 0 R.NR32, ("NR32", False)), + (LongAddress 0 R.NR33, ("NR33", False)), + (LongAddress 0 R.NR34, ("NR34", False)), + (LongAddress 0 R.NR40, ("NR40", False)), + (LongAddress 0 R.NR41, ("NR41", False)), + (LongAddress 0 R.NR42, ("NR42", False)), + (LongAddress 0 R.NR43, ("NR43", False)), + (LongAddress 0 R.NR44, ("NR44", False)), + (LongAddress 0 R.NR50, ("NR50", False)), + (LongAddress 0 R.NR51, ("NR51", False)), + (LongAddress 0 R.NR52, ("NR52", False)), + (LongAddress 0 R.IF, ("IF", False)), + (LongAddress 0 R.LCDC, ("LCDC", False)), + (LongAddress 0 R.STAT, ("STAT", False)), + (LongAddress 0 R.SCY, ("SCY", False)), + (LongAddress 0 R.SCX, ("SCX", False)), + (LongAddress 0 R.LY, ("LY", False)), + (LongAddress 0 R.LYC, ("LYC", False)), + (LongAddress 0 R.DMA, ("DMA", False)), + (LongAddress 0 R.BGP, ("BGP", False)), + (LongAddress 0 R.OBP0, ("OBP0", False)), + (LongAddress 0 R.OBP1, ("OBP1", False)), + (LongAddress 0 R.WY, ("WY", False)), + (LongAddress 0 R.WX, ("WX", False)), + (LongAddress 0 R.R4C, ("R4C", False)), + (LongAddress 0 R.KEY1, ("KEY1", False)), + (LongAddress 0 R.VBK, ("VBK", False)), + (LongAddress 0 R.BLCK, ("BLCK", False)), + (LongAddress 0 R.HDMA1, ("HDMA1", False)), + (LongAddress 0 R.HDMA2, ("HDMA2", False)), + (LongAddress 0 R.HDMA3, ("HDMA3", False)), + (LongAddress 0 R.HDMA4, ("HDMA4", False)), + (LongAddress 0 R.HDMA5, ("HDMA5", False)), + (LongAddress 0 R.RP, ("RP", False)), + (LongAddress 0 R.BCPS, ("BCPS", False)), + (LongAddress 0 R.BCPD, ("BCPD", False)), + (LongAddress 0 R.OCPS, ("OCPS", False)), + (LongAddress 0 R.OCPD, ("OCPD", False)), + (LongAddress 0 R.R6C, ("R6C", False)), + (LongAddress 0 R.SVBK, ("SVBK", False)), + (LongAddress 0 R.R72, ("R72", False)), + (LongAddress 0 R.R73, ("R73", False)), + (LongAddress 0 R.R74, ("R74", False)), + (LongAddress 0 R.R75, ("R75", False)), + (LongAddress 0 R.PCM12, ("PCM12", False)), + (LongAddress 0 R.PCM34, ("PCM34", False)), + (LongAddress 0 R.IE, ("IE", False)) ] <> [(LongAddress 0 (0xFE00 + i * 4), ("OBJ" <> T.pack (show i), False)) | i <- [0 .. 39]] diff --git a/core/src/Machine/GBC/Disassembler/LabelGenerator.hs b/core/src/Machine/GBC/Disassembler/LabelGenerator.hs index 06a35e6..fde8e73 100644 --- a/core/src/Machine/GBC/Disassembler/LabelGenerator.hs +++ b/core/src/Machine/GBC/Disassembler/LabelGenerator.hs @@ -6,9 +6,9 @@ module Machine.GBC.Disassembler.LabelGenerator ) where -import Data.IORef +import Data.IORef (IORef, newIORef, readIORef, writeIORef) import qualified Data.Text as T -import System.IO.Unsafe +import System.IO.Unsafe (unsafePerformIO) {-# NOINLINE globalCounter #-} globalCounter :: IORef Int diff --git a/core/src/Machine/GBC/Emulator.hs b/core/src/Machine/GBC/Emulator.hs index c96539a..299b928 100644 --- a/core/src/Machine/GBC/Emulator.hs +++ b/core/src/Machine/GBC/Emulator.hs @@ -13,29 +13,31 @@ module Machine.GBC.Emulator ) where -import Control.Applicative -import Control.Monad.Reader +import Control.Applicative (Alternative ((<|>))) +import Control.Monad.Reader (MonadIO (liftIO), MonadReader (ask), ReaderT, asks, replicateM_, when) import qualified Data.ByteString as B -import Data.Functor -import Data.IORef -import Data.Maybe +import Data.Functor ((<&>)) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Maybe (fromMaybe) import qualified Data.Vector.Storable as VS -import Data.Word -import Foreign.Ptr +import Data.Word (Word32, Word8) +import Foreign.Ptr (Ptr) import qualified Machine.GBC.Audio as Audio import qualified Machine.GBC.Bus as Bus import qualified Machine.GBC.CPU as CPU import qualified Machine.GBC.Color as Color import qualified Machine.GBC.DMA as DMA import qualified Machine.GBC.Graphics as Graphics -import Machine.GBC.Graphics.VRAM +import Machine.GBC.Graphics.VRAM (VRAM) +import qualified Machine.GBC.Graphics.VRAM as VRAM import qualified Machine.GBC.Keypad as Keypad import qualified Machine.GBC.Memory as Memory -import Machine.GBC.Mode -import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.ROM -import Machine.GBC.Registers +import Machine.GBC.Mode (EmulatorMode (..)) +import Machine.GBC.Primitive (alwaysUpdate, newPort) +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.ROM (ROM) +import qualified Machine.GBC.ROM as ROM +import qualified Machine.GBC.Registers as R import qualified Machine.GBC.Serial as Serial import qualified Machine.GBC.Timer as Timer import Prelude hiding (init) @@ -79,16 +81,16 @@ init :: IO State init bootROM rom requestedMode colorCorrection serialSync graphicsSync frameBufferBytes = mdo let bootMode = bootROM <&> \content -> if B.length content > 0x100 then CGB else DMG - let romMode = case cgbSupport (romHeader rom) of - CGBCompatible -> CGB - CGBExclusive -> CGB - CGBIncompatible -> DMG + let romMode = case ROM.cgbSupport (ROM.romHeader rom) of + ROM.CGBCompatible -> CGB + ROM.CGBExclusive -> CGB + ROM.CGBIncompatible -> DMG let mode = fromMaybe romMode (requestedMode <|> bootMode) - vram <- initVRAM colorCorrection + vram <- VRAM.init colorCorrection - writeRGBPalette vram False 0 (0xffffffff, 0xaaaaaaff, 0x555555ff, 0x000000ff) - writeRGBPalette vram True 0 (0xffffffff, 0xaaaaaaff, 0x555555ff, 0x000000ff) - writeRGBPalette vram True 1 (0xffffffff, 0xaaaaaaff, 0x555555ff, 0x000000ff) + VRAM.writeRGBPalette vram False 0 (0xffffffff, 0xaaaaaaff, 0x555555ff, 0x000000ff) + VRAM.writeRGBPalette vram True 0 (0xffffffff, 0xaaaaaaff, 0x555555ff, 0x000000ff) + VRAM.writeRGBPalette vram True 1 (0xffffffff, 0xaaaaaaff, 0x555555ff, 0x000000ff) modeRef <- newIORef mode portIF <- newPort 0xE0 0x1F alwaysUpdate @@ -103,7 +105,7 @@ init bootROM rom requestedMode colorCorrection serialSync graphicsSync frameBuff serialState <- Serial.init serialSync portIF modeRef let allPorts = - (IF, portIF) : + (R.IF, portIF) : CPU.ports cpu ++ DMA.ports dmaState ++ Graphics.ports graphicsState @@ -160,11 +162,11 @@ step = do CPU.step state <- ask - DMA.doPendingHDMA (dmaState state) + DMA.doPending (dmaState state) isHBlankPending <- liftIO $ readIORef (hblankPending state) when isHBlankPending $ do liftIO $ writeIORef (hblankPending state) False - DMA.doHBlankHDMA (dmaState state) + DMA.doHBlank (dmaState state) updateHardware :: Int -> ReaderT State IO () updateHardware clocksPerCycle = do @@ -187,8 +189,8 @@ keyUp state = Keypad.release (keypadState state) -- | Set a background palette. writeBgPalette :: State -> Int -> (Word32, Word32, Word32, Word32) -> IO () -writeBgPalette state = writeRGBPalette (vram state) False +writeBgPalette state = VRAM.writeRGBPalette (vram state) False -- | Set a sprite palette. writeSpritePalette :: State -> Int -> (Word32, Word32, Word32, Word32) -> IO () -writeSpritePalette state = writeRGBPalette (vram state) True +writeSpritePalette state = VRAM.writeRGBPalette (vram state) True diff --git a/core/src/Machine/GBC/Errors.hs b/core/src/Machine/GBC/Errors.hs index d1e418d..cbd435d 100644 --- a/core/src/Machine/GBC/Errors.hs +++ b/core/src/Machine/GBC/Errors.hs @@ -3,9 +3,9 @@ module Machine.GBC.Errors ) where -import Control.Exception -import Data.Word -import Machine.GBC.Util +import Control.Exception (Exception (displayException)) +import Data.Word (Word16, Word8) +import Machine.GBC.Util (formatHex) -- | A fault in the emulated hardware. On the real hardware these conditions -- lead to undefined behavior. diff --git a/core/src/Machine/GBC/Graphics.hs b/core/src/Machine/GBC/Graphics.hs index 72d64d7..7b1ef22 100644 --- a/core/src/Machine/GBC/Graphics.hs +++ b/core/src/Machine/GBC/Graphics.hs @@ -16,22 +16,39 @@ module Machine.GBC.Graphics ) where -import Control.Concurrent.MVar -import Control.Monad.Reader -import Data.Bits -import Data.Functor -import Data.IORef -import Data.Int +import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) +import Control.Monad.Reader (when) +import Data.Bits (Bits (complement, testBit, (.&.), (.|.))) +import Data.Functor ((<&>)) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Int (Int8) import qualified Data.Vector.Unboxed.Mutable as VUM -import Data.Word -import Foreign.Ptr -import Foreign.Storable -import Machine.GBC.CPU.Interrupts -import Machine.GBC.Graphics.VRAM -import Machine.GBC.Mode +import Data.Word (Word16, Word8) +import Foreign.Ptr (Ptr, castPtr, plusPtr) +import Foreign.Storable (Storable (pokeElemOff)) +import qualified Machine.GBC.CPU.Interrupts as Interrupt +import Machine.GBC.Graphics.VRAM (VRAM) +import qualified Machine.GBC.Graphics.VRAM as VRAM +import Machine.GBC.Mode (EmulatorMode (..), cgbOnlyPort) import Machine.GBC.Primitive -import Machine.GBC.Registers -import Machine.GBC.Util + ( Port, + StateCycle, + UpdateResult (HasChangedTo), + alwaysUpdate, + directReadPort, + directWritePort, + getStateCycle, + getUpdateResult, + neverUpdate, + newPort, + newStateCycle, + readPort, + resetStateCycle, + updateStateCycle, + writePort, + ) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (isFlagSet, (.<<.), (.>>.)) import Prelude hiding (init) -- | The current status of the graphics system. @@ -137,21 +154,21 @@ init vram modeRef frameBufferBytes portIF = mdo portWY <- newPort 0x00 0xFF alwaysUpdate portWX <- newPort 0x00 0xFF alwaysUpdate portBCPS <- newPort 0x40 0xBF $ - \_ bcps' -> bcps' <$ (directWritePort portBCPD =<< readPalette vram False bcps') + \_ bcps' -> bcps' <$ (directWritePort portBCPD =<< VRAM.readPalette vram False bcps') portBCPD <- cgbOnlyPort modeRef 0x00 0xFF $ \_ bcpd' -> bcpd' <$ do bcps <- readPort portBCPS - writePalette vram False bcps bcpd' + VRAM.writePalette vram False bcps bcpd' when (isFlagSet flagPaletteIncrement bcps) $ writePort portBCPS ((bcps .&. 0xBF) + 1) portOCPS <- newPort 0x40 0xBF $ - \_ ocps' -> ocps' <$ (directWritePort portOCPD =<< readPalette vram True ocps') + \_ ocps' -> ocps' <$ (directWritePort portOCPD =<< VRAM.readPalette vram True ocps') portOCPD <- cgbOnlyPort modeRef 0x00 0xFF $ \_ ocpd' -> ocpd' <$ do ocps <- readPort portOCPS - writePalette vram True ocps ocpd' + VRAM.writePalette vram True ocps ocpd' when (isFlagSet flagPaletteIncrement ocps) $ writePort portOCPS ((ocps .&. 0xBF) + 1) portVBK <- newPort 0xFE 0x01 $ - \_ vbk' -> vbk' <$ setVRAMBank vram (if vbk' .&. 1 == 0 then 0 else 0x2000) + \_ vbk' -> vbk' <$ VRAM.setBank vram (if vbk' .&. 1 == 0 then 0 else 0x2000) assemblySpace <- VUM.replicate 168 (0, (0, 0, False)) priorityBuffer <- VUM.replicate 168 0 @@ -160,22 +177,22 @@ init vram modeRef frameBufferBytes portIF = mdo ports :: State -> [(Word16, Port)] ports State {..} = - [ (LCDC, portLCDC), - (STAT, portSTAT), - (SCY, portSCY), - (SCX, portSCX), - (LY, portLY), - (LYC, portLYC), - (BGP, portBGP), - (OBP0, portOBP0), - (OBP1, portOBP1), - (WY, portWY), - (WX, portWX), - (BCPS, portBCPS), - (BCPD, portBCPD), - (OCPS, portOCPS), - (OCPD, portOCPD), - (VBK, portVBK) + [ (R.LCDC, portLCDC), + (R.STAT, portSTAT), + (R.SCY, portSCY), + (R.SCX, portSCX), + (R.LY, portLY), + (R.LYC, portLYC), + (R.BGP, portBGP), + (R.OBP0, portOBP0), + (R.OBP1, portOBP1), + (R.WY, portWY), + (R.WX, portWX), + (R.BCPS, portBCPS), + (R.BCPD, portBCPD), + (R.OCPS, portOCPS), + (R.OCPD, portOCPD), + (R.VBK, portVBK) ] -- | Make a new Graphics sync object. @@ -260,7 +277,7 @@ updateStatSignal signalRef mode stat = do checkStatInterrupt :: Port -> Port -> IORef Bool -> Word8 -> Word8 -> Mode -> IO () checkStatInterrupt portIF portSTAT signalRef ly lyc mode = do raise <- updateStatSignal signalRef mode =<< checkLY portSTAT ly lyc - when raise $ raiseInterrupt portIF InterruptLCDCStat + when raise $ Interrupt.raise portIF Interrupt.LCDCStat data BusEvent = NoGraphicsEvent | HBlankEvent deriving (Eq, Ord, Show) @@ -278,20 +295,20 @@ step graphicsState@State {..} graphicsSync clockAdvance = do stat <- directReadPort portSTAT directWritePort portSTAT (modifyBits maskMode (modeBits mode') stat) - when (mode' == ScanOAM) $ setOAMAccessible vram False - when (mode' == ReadVRAM) $ setVRAMAccessible vram False + when (mode' == ScanOAM) $ VRAM.setOAMAccessible vram False + when (mode' == ReadVRAM) $ VRAM.setAccessible vram False -- Raise interrupts lyc <- directReadPort portLYC checkStatInterrupt portIF portSTAT statSignal line' lyc mode' - when (mode' == VBlank) $ raiseInterrupt portIF InterruptVBlank + when (mode' == VBlank) $ Interrupt.raise portIF Interrupt.VBlank when (mode' == HBlank) $ do let outputBase = frameBufferBytes `plusPtr` (fromIntegral line' * 640) emulatorMode <- readIORef modeRef renderLine graphicsState emulatorMode line' outputBase - setOAMAccessible vram True - setVRAMAccessible vram True + VRAM.setOAMAccessible vram True + VRAM.setAccessible vram True when (mode' == VBlank) $ do putMVar (signalWindow graphicsSync) () @@ -370,11 +387,11 @@ renderLine State {..} mode line outputBase = do if outPos >= stopOffset then pure () else do - tile <- readTile vram tileBase inPos + tile <- VRAM.readTile vram tileBase inPos tileAttrs <- if mode == DMG then pure dmgBackgroundTileAttrs - else readTileAttrs vram tileBase inPos + else VRAM.readTileAttrs vram tileBase inPos let hflip = isFlagSet flagHorizontalFlip tileAttrs let vflip = isFlagSet flagVerticalFlip tileAttrs let tileDataBase = if tile > 127 then 0 else fontOffset @@ -384,8 +401,8 @@ renderLine State {..} mode line outputBase = do else (fromIntegral tile * 16) + (yOffset * 2) (byte0, byte1) <- if isFlagSet flagBank tileAttrs - then readBankedTileData vram (tileDataBase + fontLineOffset) - else readTileData vram (tileDataBase + fontLineOffset) + then VRAM.readBankedTileData vram (tileDataBase + fontLineOffset) + else VRAM.readTileData vram (tileDataBase + fontLineOffset) outPos' <- pixelMachine byte0 byte1 (getBackgroundBlendInfo tileAttrs) hflip 7 outPos go ((inPos + 1) .&. 0x1F) outPos' -- wrap inPos back to 0 when it gets to the end of the line {-# INLINE decodePixel #-} @@ -418,13 +435,13 @@ renderLine State {..} mode line outputBase = do go (offset + 1) (if spriteRendered then spritesRendered + 1 else spritesRendered) doSprite !h !offset = do - (y, x) <- readSpritePosition vram offset + (y, x) <- VRAM.readSpritePosition vram offset let spriteVisible = x /= 0 && line + 16 >= y && line + 16 < y + h if not spriteVisible then pure False else do when (x < 168) $ do - (rawCode, attrs) <- readSpriteAttributes vram offset + (rawCode, attrs) <- VRAM.readSpriteAttributes vram offset let code = if h == 16 then rawCode .&. 0xFE else rawCode let vflip = isFlagSet flagVerticalFlip attrs let hflip = isFlagSet flagHorizontalFlip attrs @@ -436,8 +453,8 @@ renderLine State {..} mode line outputBase = do let xOffset = fromIntegral x - 8 (byte0, byte1) <- if mode == CGB && isFlagSet flagBank attrs - then readBankedTileData vram fontLineOffset - else readTileData vram fontLineOffset + then VRAM.readBankedTileData vram fontLineOffset + else VRAM.readTileData vram fontLineOffset let priority = if mode == CGB then 0 else fromIntegral xOffset blendSprite priority hflip blendInfo byte0 byte1 xOffset @@ -482,7 +499,7 @@ renderLine State {..} mode line outputBase = do x -> error ("Framebuffer corrupted " <> show x <> " at " <> show offset) let mappedIndex = (paletteData .>>. (fromIntegral index * 2)) .&. 0x03 color <- - readRGBPalette + VRAM.readRGBPalette vram (layer > 0) (if layer == 2 then 4 .|. mappedIndex else mappedIndex) @@ -495,6 +512,6 @@ renderLine State {..} mode line outputBase = do where go !offset = do (index, (layer, palette, _)) <- VUM.unsafeRead assemblySpace offset - color <- readRGBPalette vram (layer > 0) (index .|. palette) + color <- VRAM.readRGBPalette vram (layer > 0) (index .|. palette) pokeElemOff (castPtr outputBase) offset color if offset >= 159 then pure () else go (offset + 1) diff --git a/core/src/Machine/GBC/Graphics/VRAM.hs b/core/src/Machine/GBC/Graphics/VRAM.hs index 2bf72f1..3e1213f 100644 --- a/core/src/Machine/GBC/Graphics/VRAM.hs +++ b/core/src/Machine/GBC/Graphics/VRAM.hs @@ -2,11 +2,11 @@ module Machine.GBC.Graphics.VRAM ( VRAM, - initVRAM, - setVRAMAccessible, + init, + setAccessible, setOAMAccessible, - getVRAMBank, - setVRAMBank, + getBank, + setBank, writePalette, readPalette, readRGBPalette, @@ -20,20 +20,21 @@ module Machine.GBC.Graphics.VRAM readOAM, writeOAM, writeOAMDirect, - readVRAM, - readVRAMBankOffset, - writeVRAM, + read, + readBankOffset, + write, ) where -import Control.Monad -import Data.Bits -import Data.IORef +import Control.Monad (when) +import Data.Bits (Bits ((.&.), (.|.))) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) import qualified Data.Vector.Storable.Mutable as VSM -import Data.Word +import Data.Word (Word16, Word32, Word8) import qualified Machine.GBC.Color as Color -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.Util ((.<<.), (.>>.)) +import Prelude hiding (init, read) data VRAM = VRAM { vram :: !(VSM.IOVector Word8), @@ -43,14 +44,14 @@ data VRAM = VRAM oamAccessible :: !(IORef Bool), vramAccessible :: !(IORef Bool), vramBank :: !(UnboxedRef Int), - colorFunction :: !(Color.Correction) + colorFunction :: !Color.Correction } totalPaletteEntries :: Int totalPaletteEntries = 8 * 4 * 2 -- 2 sets of 8 palettes with 4 colors each. -initVRAM :: Color.Correction -> IO VRAM -initVRAM colorFunction = do +init :: Color.Correction -> IO VRAM +init colorFunction = do vram <- VSM.new 0x4000 oam <- VSM.new 160 oamAccessible <- newIORef True @@ -60,21 +61,21 @@ initVRAM colorFunction = do rgbPalettes <- VSM.replicate totalPaletteEntries 0xFFFFFFFF pure VRAM {..} -{-# INLINE setVRAMAccessible #-} -setVRAMAccessible :: VRAM -> Bool -> IO () -setVRAMAccessible VRAM {..} = writeIORef vramAccessible +{-# INLINE setAccessible #-} +setAccessible :: VRAM -> Bool -> IO () +setAccessible VRAM {..} = writeIORef vramAccessible {-# INLINE setOAMAccessible #-} setOAMAccessible :: VRAM -> Bool -> IO () setOAMAccessible VRAM {..} = writeIORef oamAccessible -{-# INLINE getVRAMBank #-} -getVRAMBank :: VRAM -> IO Int -getVRAMBank VRAM {..} = readUnboxedRef vramBank +{-# INLINE getBank #-} +getBank :: VRAM -> IO Int +getBank VRAM {..} = readUnboxedRef vramBank -{-# INLINE setVRAMBank #-} -setVRAMBank :: VRAM -> Int -> IO () -setVRAMBank VRAM {..} = writeUnboxedRef vramBank +{-# INLINE setBank #-} +setBank :: VRAM -> Int -> IO () +setBank VRAM {..} = writeUnboxedRef vramBank -- | Get the byte offset into palette memory given the value of a cps register. paletteByte :: Bool -> Word8 -> Int @@ -180,9 +181,9 @@ writeOAM VRAM {..} addr value = do writeOAMDirect :: VRAM -> Word16 -> Word8 -> IO () writeOAMDirect VRAM {..} addr = VSM.unsafeWrite oam (fromIntegral addr) -{-# INLINE readVRAM #-} -readVRAM :: VRAM -> Word16 -> IO Word8 -readVRAM VRAM {..} addr = do +{-# INLINE read #-} +read :: VRAM -> Word16 -> IO Word8 +read VRAM {..} addr = do isAccessible <- readIORef vramAccessible if not isAccessible then pure 0xFF @@ -190,16 +191,16 @@ readVRAM VRAM {..} addr = do bankOffset <- readUnboxedRef vramBank VSM.unsafeRead vram (fromIntegral (addr - 0x8000) + bankOffset) -readVRAMBankOffset :: VRAM -> Int -> Word16 -> IO Word8 -readVRAMBankOffset VRAM {..} bankOffset addr = do +readBankOffset :: VRAM -> Int -> Word16 -> IO Word8 +readBankOffset VRAM {..} bankOffset addr = do isAccessible <- readIORef vramAccessible if not isAccessible then pure 0xFF else VSM.unsafeRead vram (fromIntegral (addr - 0x8000) + bankOffset) -{-# INLINE writeVRAM #-} -writeVRAM :: VRAM -> Word16 -> Word8 -> IO () -writeVRAM VRAM {..} addr value = do +{-# INLINE write #-} +write :: VRAM -> Word16 -> Word8 -> IO () +write VRAM {..} addr value = do isAccessible <- readIORef vramAccessible if not isAccessible then pure () diff --git a/core/src/Machine/GBC/Keypad.hs b/core/src/Machine/GBC/Keypad.hs index 5fc7950..2f6d752 100644 --- a/core/src/Machine/GBC/Keypad.hs +++ b/core/src/Machine/GBC/Keypad.hs @@ -10,14 +10,14 @@ module Machine.GBC.Keypad ) where -import Control.Monad.Reader -import Data.Bits -import Data.IORef -import Data.Word -import Machine.GBC.CPU.Interrupts -import Machine.GBC.Primitive -import Machine.GBC.Registers -import Machine.GBC.Util +import Control.Monad.Reader (void, when) +import Data.Bits (Bits (complement, testBit, (.&.), (.|.))) +import Data.IORef (IORef, atomicWriteIORef, newIORef, readIORef) +import Data.Word (Word16, Word8) +import qualified Machine.GBC.CPU.Interrupts as Interrupt +import Machine.GBC.Primitive (Port, newPort, readPort) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util ((.>>.)) import Prelude hiding (init) -- | Create the initial keypad state. @@ -66,7 +66,7 @@ refreshKeypad keypad portIF _ p1 = do (False, True) -> keypad' .>>. 4 (True, True) -> 0xFF (False, False) -> (keypad' .&. 0x0F) .|. (keypad' .>>. 4) - when (0x0F .&. p1 .&. complement p1' /= 0) (raiseInterrupt portIF InterruptP1Low) + when (0x0F .&. p1 .&. complement p1' /= 0) (Interrupt.raise portIF Interrupt.P1Low) pure (0xC0 .|. p1') press :: State -> Key -> IO () @@ -88,4 +88,4 @@ updateKeypadState State {..} keypad = do void $ refreshKeypad keypad portIF p1 p1 ports :: State -> [(Word16, Port)] -ports State {..} = [(P1, portP1)] +ports State {..} = [(R.P1, portP1)] diff --git a/core/src/Machine/GBC/MBC.hs b/core/src/Machine/GBC/MBC.hs index 053da19..76cc1c6 100644 --- a/core/src/Machine/GBC/MBC.hs +++ b/core/src/Machine/GBC/MBC.hs @@ -13,13 +13,13 @@ module Machine.GBC.MBC where import qualified Data.Vector.Storable.Mutable as VSM -import Machine.GBC.MBC.Interface -import Machine.GBC.MBC.MBC1 -import Machine.GBC.MBC.MBC2 -import Machine.GBC.MBC.MBC3 -import Machine.GBC.MBC.MBC5 +import Machine.GBC.MBC.Interface (MBC (..), RAMAllocator, RTC (..), RTCRegister (..)) +import Machine.GBC.MBC.MBC1 (mbc1) +import Machine.GBC.MBC.MBC2 (mbc2) +import Machine.GBC.MBC.MBC3 (mbc3) +import Machine.GBC.MBC.MBC5 (mbc5) import qualified Machine.GBC.MBC.RTC as RTC -import System.IO.MMap +import System.IO.MMap (Mode (ReadWriteEx), mmapFileForeignPtr) -- | Simulate a cartridge with no memory bank controller. nullMBC :: IO MBC diff --git a/core/src/Machine/GBC/MBC/Interface.hs b/core/src/Machine/GBC/MBC/Interface.hs index 619f6b5..0717da5 100644 --- a/core/src/Machine/GBC/MBC/Interface.hs +++ b/core/src/Machine/GBC/MBC/Interface.hs @@ -10,7 +10,7 @@ module Machine.GBC.MBC.Interface where import qualified Data.Vector.Storable.Mutable as VSM -import Data.Word +import Data.Word (Word16, Word8) type RAMAllocator = Int -> IO (VSM.IOVector Word8) diff --git a/core/src/Machine/GBC/MBC/MBC1.hs b/core/src/Machine/GBC/MBC/MBC1.hs index 795e06a..fe18697 100644 --- a/core/src/Machine/GBC/MBC/MBC1.hs +++ b/core/src/Machine/GBC/MBC/MBC1.hs @@ -7,12 +7,12 @@ module Machine.GBC.MBC.MBC1 ) where -import Control.Monad -import Data.Bits -import Data.IORef +import Control.Monad (when) +import Data.Bits (Bits (..)) +import Data.IORef (newIORef, readIORef, writeIORef) import qualified Data.Vector.Storable.Mutable as VSM -import Machine.GBC.MBC.Interface -import Machine.GBC.Util +import Machine.GBC.MBC.Interface (MBC (..), RAMAllocator) +import Machine.GBC.Util ((.<<.)) mbc1 :: Int -> Int -> Bool -> RAMAllocator -> IO MBC mbc1 bankMask ramMask multicart ramAllocator = do diff --git a/core/src/Machine/GBC/MBC/MBC2.hs b/core/src/Machine/GBC/MBC/MBC2.hs index 653e588..92e6af4 100644 --- a/core/src/Machine/GBC/MBC/MBC2.hs +++ b/core/src/Machine/GBC/MBC/MBC2.hs @@ -6,12 +6,12 @@ module Machine.GBC.MBC.MBC2 ) where -import Control.Monad -import Data.Bits -import Data.IORef +import Control.Monad (when) +import Data.Bits (Bits (..)) +import Data.IORef (newIORef, readIORef, writeIORef) import qualified Data.Vector.Storable.Mutable as VSM -import Machine.GBC.MBC.Interface -import Machine.GBC.Util +import Machine.GBC.MBC.Interface (MBC (..), RAMAllocator) +import Machine.GBC.Util ((.<<.)) mbc2 :: Int -> RAMAllocator -> IO MBC mbc2 bankMask ramAllocator = do diff --git a/core/src/Machine/GBC/MBC/MBC3.hs b/core/src/Machine/GBC/MBC/MBC3.hs index 38f1cc7..2e55f08 100644 --- a/core/src/Machine/GBC/MBC/MBC3.hs +++ b/core/src/Machine/GBC/MBC/MBC3.hs @@ -6,12 +6,12 @@ module Machine.GBC.MBC.MBC3 ) where -import Control.Monad -import Data.Bits -import Data.IORef +import Control.Monad (when) +import Data.Bits (Bits (..)) +import Data.IORef (newIORef, readIORef, writeIORef) import qualified Data.Vector.Storable.Mutable as VSM -import Machine.GBC.MBC.Interface -import Machine.GBC.Util +import Machine.GBC.MBC.Interface (MBC (..), RAMAllocator, RTC (latchRTC, readRTC, writeRTC), RTCRegister (..)) +import Machine.GBC.Util ((.<<.)) mbc3 :: Int -> Int -> RAMAllocator -> RTC -> IO MBC mbc3 bankMask ramMask ramAllocator rtc = do diff --git a/core/src/Machine/GBC/MBC/MBC5.hs b/core/src/Machine/GBC/MBC/MBC5.hs index 09edd10..5cd08a3 100644 --- a/core/src/Machine/GBC/MBC/MBC5.hs +++ b/core/src/Machine/GBC/MBC/MBC5.hs @@ -6,12 +6,12 @@ module Machine.GBC.MBC.MBC5 ) where -import Control.Monad -import Data.Bits -import Data.IORef +import Control.Monad (when) +import Data.Bits (Bits (..)) +import Data.IORef (newIORef, readIORef, writeIORef) import qualified Data.Vector.Storable.Mutable as VSM -import Machine.GBC.MBC.Interface -import Machine.GBC.Util +import Machine.GBC.MBC.Interface (MBC (..), RAMAllocator) +import Machine.GBC.Util ((.<<.)) mbc5 :: Int -> Int -> RAMAllocator -> IO MBC mbc5 bankMask ramMask ramAllocator = do diff --git a/core/src/Machine/GBC/MBC/RTC.hs b/core/src/Machine/GBC/MBC/RTC.hs index f12cc14..650de76 100644 --- a/core/src/Machine/GBC/MBC/RTC.hs +++ b/core/src/Machine/GBC/MBC/RTC.hs @@ -6,17 +6,21 @@ module Machine.GBC.MBC.RTC where import Control.Exception -import Control.Monad -import Data.Bits -import Data.IORef -import Data.Int -import Data.Time.Clock.System -import Data.Word -import Machine.GBC.MBC.Interface -import Machine.GBC.Util -import System.Directory -import System.IO -import Text.Read + ( Exception (displayException), + IOException, + try, + ) +import Control.Monad (unless, when) +import Data.Bits (Bits (..)) +import Data.IORef (newIORef, readIORef, writeIORef) +import Data.Int (Int64) +import Data.Time.Clock.System (SystemTime (systemSeconds), getSystemTime) +import Data.Word (Word8) +import Machine.GBC.MBC.Interface (RTC (..), RTCRegister (DaysHigh, DaysLow, Hours, Minutes, Seconds)) +import Machine.GBC.Util (isFlagSet) +import System.Directory (doesFileExist) +import System.IO (hPutStrLn, stderr) +import Text.Read (readMaybe) flagHalt :: Word8 flagHalt = 0x40 diff --git a/core/src/Machine/GBC/Memory.hs b/core/src/Machine/GBC/Memory.hs index e813f7f..5df3d2b 100644 --- a/core/src/Machine/GBC/Memory.hs +++ b/core/src/Machine/GBC/Memory.hs @@ -23,33 +23,35 @@ module Machine.GBC.Memory where import Control.Exception (throwIO) -import Control.Monad.Reader -import Data.Bifunctor -import Data.Bits +import Control.Monad.Reader (MonadIO (liftIO), ReaderT, asks, when) +import Data.Bifunctor (Bifunctor (first)) +import Data.Bits (Bits ((.&.), (.|.))) import qualified Data.ByteString as B -import Data.Functor -import Data.IORef -import Data.Maybe +import Data.Functor ((<&>)) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Maybe (fromMaybe, isJust) import qualified Data.Vector as V import qualified Data.Vector.Storable as VS import qualified Data.Vector.Storable.Mutable as VSM -import Data.Word -import Machine.GBC.Errors -import Machine.GBC.Graphics.VRAM -import Machine.GBC.Mode +import Data.Word (Word16, Word8) +import Machine.GBC.Errors (Fault (InvalidRead, InvalidWrite)) +import Machine.GBC.Graphics.VRAM (VRAM) +import qualified Machine.GBC.Graphics.VRAM as VRAM +import Machine.GBC.Mode (EmulatorMode (DMG), cgbOnlyPort) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.ROM -import Machine.GBC.Registers -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import Machine.GBC.ROM (ROM) +import qualified Machine.GBC.ROM as ROM +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (isFlagSet, (.<<.), (.>>.)) import Prelude hiding (init) -- | The gameboy memory. data State = State - { mbc :: !MBC, + { mbc :: !ROM.MBC, mode0 :: !EmulatorMode, modeRef :: !(IORef EmulatorMode), - header :: !Header, + header :: !ROM.Header, rom0 :: !(IORef (VS.Vector Word8)), -- The first 8kb of ROM. Can be switched between cartridge ROM and boot ROM. rom :: !(VS.Vector Word8), -- All of cartrige ROM. bootROMLength :: Int, @@ -108,11 +110,11 @@ initForROM :: IORef EmulatorMode -> IO State initForROM boot romInfo vram ports portIE modeRef = do - mbc <- getMBC romInfo + mbc <- ROM.getMBC romInfo init boot - (VS.fromList (B.unpack (romContent romInfo))) - (romHeader romInfo) + (VS.fromList (B.unpack (ROM.romContent romInfo))) + (ROM.romHeader romInfo) mbc vram ports @@ -122,8 +124,8 @@ initForROM boot romInfo vram ports portIE modeRef = do init :: Maybe (VS.Vector Word8) -> VS.Vector Word8 -> - Header -> - MBC -> + ROM.Header -> + ROM.MBC -> VRAM -> [(Word16, Port)] -> Port -> @@ -184,17 +186,17 @@ init boot rom header mbc vram rawPorts portIE modeRef = do let ports = V.accum - (flip const) + (\_ x -> x) (V.replicate 128 emptyPort) ( first portOffset - <$> ( (BLCK, portBLCK) : - (SVBK, portSVBK) : - (R4C, portR4C) : - (R6C, portR6C) : - (R72, r72) : - (R73, r73) : - (R74, r74) : - (R75, r75) : + <$> ( (R.BLCK, portBLCK) : + (R.SVBK, portSVBK) : + (R.R4C, portR4C) : + (R.R6C, portR6C) : + (R.R72, r72) : + (R.R73, r73) : + (R.R74, r74) : + (R.R75, r75) : rawPorts ) ) @@ -203,7 +205,7 @@ init boot rom header mbc vram rawPorts portIE modeRef = do pure State {..} -- | Get the ROM header. -getROMHeader :: State -> Header +getROMHeader :: State -> ROM.Header getROMHeader State {..} = header -- | Get all of the ROM bytes. @@ -229,12 +231,12 @@ getBank address = do then do lockout <- readIORef bootROMLockout pure (if lockout then 0 else 0xFFFF) - else lowBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) - 1 -> lowBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) - 2 -> highBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) - 3 -> highBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) - 4 -> getVRAMBank vram <&> \o -> fromIntegral (o .>>. 13) - 5 -> ramBankOffset mbc <&> \o -> fromIntegral (o .>>. 13) + else ROM.lowBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) + 1 -> ROM.lowBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) + 2 -> ROM.highBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) + 3 -> ROM.highBankOffset mbc <&> \o -> fromIntegral (o .>>. 14) + 4 -> VRAM.getBank vram <&> \o -> fromIntegral (o .>>. 13) + 5 -> ROM.ramBankOffset mbc <&> \o -> fromIntegral (o .>>. 13) 6 -> if address < 0xD000 then pure 0 @@ -248,7 +250,7 @@ getBank address = do -- | Get the status of the cartridge RAM gate (True for enabled, False for -- disabled). getRamGate :: Has env => ReaderT env IO Bool -getRamGate = liftIO . ramGate . mbc =<< asks forState +getRamGate = liftIO . ROM.ramGate . mbc =<< asks forState -- | Read a byte from a specific memory bank. readByteLong :: Has env => Word16 -> Word16 -> ReaderT env IO Word8 @@ -264,8 +266,8 @@ readByteLong bank addr = do 1 -> pure (rom VS.! offsetWithBank 14 0) 2 -> pure (rom VS.! offsetWithBank 14 0x4000) 3 -> pure (rom VS.! offsetWithBank 14 0x4000) - 4 -> readVRAMBankOffset vram (fromIntegral bank .<<. 13) addr - 5 -> readRAMBankOffset mbc (fromIntegral bank .<<. 13) (addr - 0xA000) + 4 -> VRAM.readBankOffset vram (fromIntegral bank .<<. 13) addr + 5 -> ROM.readRAMBankOffset mbc (fromIntegral bank .<<. 13) (addr - 0xA000) 6 | addr < 0xD000 -> VSM.read memRam (offset 0xC000) | otherwise -> VSM.read memRam (offsetWithBank 12 0xC000) @@ -273,11 +275,11 @@ readByteLong bank addr = do | addr < 0xF000 -> VSM.read memRam (offset 0xE000) | addr < 0xFE00 -> VSM.unsafeRead memRam (offsetWithBank 12 0xE000) | addr < 0xFEA0 -> do - value <- readOAM vram addr + value <- VRAM.readOAM vram addr pure (fromIntegral value) | addr < 0xFF00 -> pure 0xFF | addr < 0xFF80 -> liftIO $ readPort (ports V.! offset 0xFF00) - | addr == IE -> liftIO $ readPort portIE + | addr == R.IE -> liftIO $ readPort portIE | otherwise -> VSM.read memHigh (offset 0xFF80) x -> error ("Impossible coarse read address " ++ show x) where @@ -292,19 +294,19 @@ readByte addr = do liftIO $ case addr .>>. 13 of 0 -> do content <- readIORef rom0 - bank <- lowBankOffset mbc + bank <- ROM.lowBankOffset mbc pure (content `VS.unsafeIndex` (bank + fromIntegral addr)) 1 -> do - bank <- lowBankOffset mbc + bank <- ROM.lowBankOffset mbc pure (rom `VS.unsafeIndex` (bank + fromIntegral addr)) 2 -> do - bank <- highBankOffset mbc + bank <- ROM.highBankOffset mbc pure (rom `VS.unsafeIndex` (bank + offset 0x4000)) 3 -> do - bank <- highBankOffset mbc + bank <- ROM.highBankOffset mbc pure (rom `VS.unsafeIndex` (bank + offset 0x4000)) - 4 -> readVRAM vram addr - 5 -> readRAM mbc (addr - 0xA000) + 4 -> VRAM.read vram addr + 5 -> ROM.readRAM mbc (addr - 0xA000) 6 | addr < 0xD000 -> VSM.unsafeRead memRam (offset 0xC000) | otherwise -> do @@ -316,13 +318,13 @@ readByte addr = do bank <- readUnboxedRef internalRamBankOffset VSM.unsafeRead memRam (bank + offset 0xE000) | addr < 0xFEA0 -> do - value <- readOAM vram addr + value <- VRAM.readOAM vram addr pure (fromIntegral value) | addr < 0xFF00 -> liftIO $ do check <- readIORef checkRAMAccess if check then throwIO (InvalidRead (addr + 0xE000)) else pure 0xFF | addr < 0xFF80 -> liftIO $ readPort (ports V.! offset 0xFF00) - | addr == IE -> liftIO $ readPort portIE + | addr == R.IE -> liftIO $ readPort portIE | otherwise -> VSM.unsafeRead memHigh (offset 0xFF80) x -> error ("Impossible coarse read address " ++ show x) where @@ -333,12 +335,12 @@ writeByte :: Has env => Word16 -> Word8 -> ReaderT env IO () writeByte addr value = do State {..} <- asks forState liftIO $ case addr .>>. 13 of - 0 -> writeROM mbc addr value - 1 -> writeROM mbc addr value - 2 -> writeROM mbc addr value - 3 -> writeROM mbc addr value - 4 -> writeVRAM vram addr value - 5 -> writeRAM mbc (addr - 0xA000) value + 0 -> ROM.writeROM mbc addr value + 1 -> ROM.writeROM mbc addr value + 2 -> ROM.writeROM mbc addr value + 3 -> ROM.writeROM mbc addr value + 4 -> VRAM.write vram addr value + 5 -> ROM.writeRAM mbc (addr - 0xA000) value 6 | addr < 0xD000 -> VSM.unsafeWrite memRam (offset 0xC000) value | otherwise -> do @@ -349,12 +351,12 @@ writeByte addr value = do | addr < 0xFE00 -> do bank <- readUnboxedRef internalRamBankOffset VSM.unsafeWrite memRam (bank + offset 0xE000) value - | addr < 0xFEA0 -> writeOAM vram addr value + | addr < 0xFEA0 -> VRAM.writeOAM vram addr value | addr < 0xFF00 -> do check <- readIORef checkRAMAccess if check then throwIO (InvalidWrite (addr + 0xE000)) else pure () | addr < 0xFF80 -> writePort (ports V.! offset 0xFF00) value - | addr == IE -> liftIO $ writePort portIE value + | addr == R.IE -> liftIO $ writePort portIE value | otherwise -> VSM.unsafeWrite memHigh (offset 0xFF80) value x -> error ("Impossible coarse write address " ++ show x) where diff --git a/core/src/Machine/GBC/Mode.hs b/core/src/Machine/GBC/Mode.hs index b11fcc8..f709b53 100644 --- a/core/src/Machine/GBC/Mode.hs +++ b/core/src/Machine/GBC/Mode.hs @@ -4,9 +4,9 @@ module Machine.GBC.Mode ) where -import Data.IORef -import Data.Word -import Machine.GBC.Primitive +import Data.IORef (IORef, readIORef) +import Data.Word (Word8) +import Machine.GBC.Primitive (Port, newPortWithReadAction) data EmulatorMode = DMG | CGB deriving (Eq, Ord, Show, Bounded, Enum) diff --git a/core/src/Machine/GBC/Primitive.hs b/core/src/Machine/GBC/Primitive.hs index 2ddd6c2..d1429b9 100644 --- a/core/src/Machine/GBC/Primitive.hs +++ b/core/src/Machine/GBC/Primitive.hs @@ -39,15 +39,15 @@ module Machine.GBC.Primitive ) where -import Control.Monad -import Control.Monad.IO.Class -import Data.Bits -import Data.IORef -import Data.Primitive -import Data.Word -import Foreign.ForeignPtr -import Foreign.Storable -import Machine.GBC.Primitive.UnboxedRef +import Control.Monad (when) +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Bits (Bits (..)) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Primitive (Prim) +import Data.Word (Word8) +import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrArray, withForeignPtr) +import Foreign.Storable (Storable (peekElemOff, pokeElemOff)) +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) -- | A reloading down counter. Number of states is reload value + 1. data Counter = Counter diff --git a/core/src/Machine/GBC/Primitive/UnboxedRef.hs b/core/src/Machine/GBC/Primitive/UnboxedRef.hs index ad5c77f..f2410e7 100644 --- a/core/src/Machine/GBC/Primitive/UnboxedRef.hs +++ b/core/src/Machine/GBC/Primitive/UnboxedRef.hs @@ -6,9 +6,9 @@ module Machine.GBC.Primitive.UnboxedRef ) where -import Control.Monad.IO.Class -import Data.Primitive -import GHC.Exts +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Primitive (MutablePrimArray, Prim, newPrimArray, readPrimArray, writePrimArray) +import GHC.Exts (RealWorld) newtype UnboxedRef a = UnboxedRef (MutablePrimArray RealWorld a) diff --git a/core/src/Machine/GBC/ROM.hs b/core/src/Machine/GBC/ROM.hs index 49afda6..ef2b9d3 100644 --- a/core/src/Machine/GBC/ROM.hs +++ b/core/src/Machine/GBC/ROM.hs @@ -17,14 +17,15 @@ module Machine.GBC.ROM ) where -import Control.Monad -import Control.Monad.Except -import Control.Monad.Writer.Lazy -import Data.Binary.Get +import Control.Monad (when) +import Control.Monad.Except (ExceptT, liftEither) +import Control.Monad.Writer.Lazy (MonadWriter (tell), WriterT) +import Data.Binary.Get (Get, getByteString, getWord16le, getWord8, runGetOrFail, skip) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as LB -import Data.Word -import Machine.GBC.MBC +import Data.Word (Word16, Word8) +import Machine.GBC.MBC (MBC) +import qualified Machine.GBC.MBC as MBC data ROM = ROM { romPaths :: Paths, @@ -214,13 +215,13 @@ getMBC :: ROM -> IO MBC getMBC ROM {..} = let Paths {..} = romPaths cType = cartridgeType romHeader - allocator = if hasBackupBattery cType then savedRAM romSaveFile else volatileRAM + allocator = if hasBackupBattery cType then MBC.savedRAM romSaveFile else MBC.volatileRAM bankMask = (romSize romHeader `div` 0x4000) - 1 ramMask = (externalRAM romHeader `div` 0x2000) - 1 in case mbcType cType of - Nothing -> nullMBC - Just MBC1 -> mbc1 bankMask ramMask (looksLikeMulticart romContent) allocator - Just MBC2 -> mbc2 bankMask allocator - Just MBC3 -> mbc3 bankMask ramMask allocator nullRTC - Just MBC3RTC -> mbc3 bankMask ramMask allocator =<< savedRTC romRTCFile - Just MBC5 -> mbc5 bankMask ramMask allocator + Nothing -> MBC.nullMBC + Just MBC1 -> MBC.mbc1 bankMask ramMask (looksLikeMulticart romContent) allocator + Just MBC2 -> MBC.mbc2 bankMask allocator + Just MBC3 -> MBC.mbc3 bankMask ramMask allocator MBC.nullRTC + Just MBC3RTC -> MBC.mbc3 bankMask ramMask allocator =<< MBC.savedRTC romRTCFile + Just MBC5 -> MBC.mbc5 bankMask ramMask allocator diff --git a/core/src/Machine/GBC/Registers.hs b/core/src/Machine/GBC/Registers.hs index 30ced9b..099a8c5 100644 --- a/core/src/Machine/GBC/Registers.hs +++ b/core/src/Machine/GBC/Registers.hs @@ -71,7 +71,7 @@ module Machine.GBC.Registers ) where -import Data.Word +import Data.Word (Word16) pattern P1 :: Word16 pattern P1 = 0xFF00 diff --git a/core/src/Machine/GBC/Serial.hs b/core/src/Machine/GBC/Serial.hs index 77df9c7..8eb8459 100644 --- a/core/src/Machine/GBC/Serial.hs +++ b/core/src/Machine/GBC/Serial.hs @@ -11,18 +11,18 @@ module Machine.GBC.Serial ) where -import Control.Concurrent.MVar -import Control.Monad -import Data.Bits -import Data.Functor -import Data.IORef -import Data.Word -import Machine.GBC.CPU.Interrupts -import Machine.GBC.Mode +import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) +import Control.Monad (unless, when) +import Data.Bits (Bits (rotateL, testBit, (.&.), (.|.))) +import Data.Functor () +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word16, Word8) +import qualified Machine.GBC.CPU.Interrupts as Interrupt +import Machine.GBC.Mode (EmulatorMode (DMG)) import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Registers -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util ((.<<.)) import Prelude hiding (init) data Sync = Sync @@ -85,7 +85,7 @@ init sync portIF modeRef = do pure State {..} ports :: State -> [(Word16, Port)] -ports State {..} = [(SB, portSB), (SC, portSC)] +ports State {..} = [(R.SB, portSB), (R.SC, portSC)] -- | Notify an incoming passive transfer notifyIncoming :: State -> Int -> Word8 -> IO () @@ -123,6 +123,6 @@ update State {..} = do when (counter' == 0) $ do sc <- directReadPort portSC directWritePort portSC (sc .&. 0x7F) - raiseInterrupt portIF InterruptEndSerialTransfer + Interrupt.raise portIF Interrupt.EndSerialTransfer writeIORef transferActiveRef False pure counter' diff --git a/core/src/Machine/GBC/Timer.hs b/core/src/Machine/GBC/Timer.hs index b0b5589..93b6392 100644 --- a/core/src/Machine/GBC/Timer.hs +++ b/core/src/Machine/GBC/Timer.hs @@ -8,15 +8,15 @@ module Machine.GBC.Timer ) where -import Control.Monad.Reader -import Data.Bits -import Data.IORef -import Data.Word -import Machine.GBC.CPU.Interrupts +import Control.Monad.Reader (when) +import Data.Bits (Bits (complement, testBit, (.&.), (.|.))) +import Data.IORef (IORef, newIORef, readIORef, writeIORef) +import Data.Word (Word16, Word8) +import qualified Machine.GBC.CPU.Interrupts as Interrupt import Machine.GBC.Primitive -import Machine.GBC.Primitive.UnboxedRef -import Machine.GBC.Registers -import Machine.GBC.Util +import Machine.GBC.Primitive.UnboxedRef (UnboxedRef, newUnboxedRef, readUnboxedRef, writeUnboxedRef) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util ((.<<.), (.>>.)) import Prelude hiding (init) -- | The TIMA register behaves differently for 2 cycles after it overflows, so @@ -75,7 +75,7 @@ init clockAudio portKEY1 portIF = do pure State {..} ports :: State -> [(Word16, Port)] -ports State {..} = [(DIV, portDIV), (TIMA, portTIMA), (TMA, portTMA), (TAC, portTAC)] +ports State {..} = [(R.DIV, portDIV), (R.TIMA, portTIMA), (R.TMA, portTMA), (R.TAC, portTAC)] allTimaBits :: Word16 allTimaBits = 0x02A8 @@ -124,7 +124,7 @@ update State {..} = do directWritePort portTIMA 0 else directWritePort portTIMA (tima + 1) TIMAOverflow -> do - raiseInterrupt portIF InterruptTimerOverflow + Interrupt.raise portIF Interrupt.TimerOverflow writeIORef timaStateRef TIMAReload directWritePort portTIMA =<< directReadPort portTMA TIMAReload -> do diff --git a/core/src/Machine/GBC/Util.hs b/core/src/Machine/GBC/Util.hs index acb3cfb..6d0411c 100644 --- a/core/src/Machine/GBC/Util.hs +++ b/core/src/Machine/GBC/Util.hs @@ -7,8 +7,8 @@ module Machine.GBC.Util ) where -import Data.Bits -import Data.Word +import Data.Bits (Bits (..), FiniteBits (..)) +import Data.Word (Word8) infixl 8 .<<. diff --git a/core/test/Machine/GBC/CPUSpec.hs b/core/test/Machine/GBC/CPUSpec.hs index 0f13a71..c451bd8 100644 --- a/core/test/Machine/GBC/CPUSpec.hs +++ b/core/test/Machine/GBC/CPUSpec.hs @@ -8,27 +8,28 @@ module Machine.GBC.CPUSpec ) where -import Control.Monad -import Control.Monad.IO.Class -import Control.Monad.Reader -import Data.Bits -import Data.Foldable -import Data.IORef +import Control.Monad (void) +import Control.Monad.IO.Class (MonadIO (liftIO)) +import Control.Monad.Reader (MonadReader (ask), ReaderT (runReaderT), asks) +import Data.Bits (Bits (..)) +import Data.Foldable (for_, traverse_) +import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef) import qualified Data.Vector.Storable as VS -import Data.Word +import Data.Word (Word16, Word32, Word8) import qualified Machine.GBC.Bus as Bus import qualified Machine.GBC.CPU as CPU -import Machine.GBC.CPU.ISA +import Machine.GBC.CPU.ISA (ConditionCode (..), MonadSm83x (..), Register16 (..), Register8 (..), RegisterPushPop (..)) import qualified Machine.GBC.Color as Color -import Machine.GBC.Graphics.VRAM -import Machine.GBC.MBC +import Machine.GBC.Graphics.VRAM () +import qualified Machine.GBC.Graphics.VRAM as VRAM +import Machine.GBC.MBC (nullMBC) import qualified Machine.GBC.Memory as Memory -import Machine.GBC.Mode -import Machine.GBC.Primitive -import Machine.GBC.ROM -import Machine.GBC.Registers -import Machine.GBC.Util -import Test.Hspec +import Machine.GBC.Mode (EmulatorMode (CGB)) +import Machine.GBC.Primitive (alwaysUpdate, newPort) +import qualified Machine.GBC.ROM as ROM +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (formatHex, (.<<.), (.>>.)) +import Test.Hspec (Spec, describe, it, shouldBe) spec :: Spec spec = do @@ -47,22 +48,22 @@ romSizeInBytes = 32 * 1024 * 1024 blankROM = VS.replicate romSizeInBytes 0 -blankHeader :: Header +blankHeader :: ROM.Header blankHeader = - Header - { startAddress = 0, - nintendoCharacterData = "", - gameTitle = "", - gameCode = "", - cgbSupport = CGBCompatible, - makerCode = "", - sgbSupport = GBOnly, - cartridgeType = CartridgeType Nothing False False, - romSize = romSizeInBytes, - externalRAM = 0, - destination = Overseas, - oldLicenseCode = 0, - maskROMVersion = 0 + ROM.Header + { ROM.startAddress = 0, + ROM.nintendoCharacterData = "", + ROM.gameTitle = "", + ROM.gameCode = "", + ROM.cgbSupport = ROM.CGBCompatible, + ROM.makerCode = "", + ROM.sgbSupport = ROM.GBOnly, + ROM.cartridgeType = ROM.CartridgeType Nothing False False, + ROM.romSize = romSizeInBytes, + ROM.externalRAM = 0, + ROM.destination = ROM.Overseas, + ROM.oldLicenseCode = 0, + ROM.maskROMVersion = 0 } data CPUTestState = CPUTestState @@ -93,7 +94,7 @@ instance Bus.Has CPUTestState where withNewCPU :: CPU.M CPUTestState () -> IO () withNewCPU computation = mdo - vram <- initVRAM (Color.correction Color.NoCorrection) + vram <- VRAM.init (Color.correction Color.NoCorrection) portIF <- newPort 0x00 0x1F alwaysUpdate portIE <- newPort 0x00 0xFF alwaysUpdate mbc <- nullMBC @@ -105,7 +106,7 @@ withNewCPU computation = mdo blankHeader mbc vram - ((IF, portIF) : CPU.ports cpu) + ((R.IF, portIF) : CPU.ports cpu) portIE modeRef extraCycles <- newIORef 0 @@ -1778,8 +1779,8 @@ miscellaneous = do withIME True $ do CPU.M $ do - Memory.writeByte IE 1 - Memory.writeByte IF 1 + Memory.writeByte R.IE 1 + Memory.writeByte R.IF 1 halt expectExtraCycles 0 expectMode CPU.ModeHalt @@ -1791,8 +1792,8 @@ miscellaneous = do alteringFlags (CPU.flagZ .|. CPU.flagN .|. CPU.flagH) $ do CPU.M $ do - Memory.writeByte IE 1 - Memory.writeByte IF 1 + Memory.writeByte R.IE 1 + Memory.writeByte R.IF 1 Memory.writeByte 0xC000 0x3C -- INC A halt expectExtraCycles 0 @@ -1812,16 +1813,16 @@ miscellaneous = do it "Switches speed mode" $ withNewCPU $ alteringCPUCycleClocks $ do - KEY1 `atAddressShouldBe` 0x7E - CPU.M $ Memory.writeByte KEY1 1 - KEY1 `atAddressShouldBe` 0x7F + R.KEY1 `atAddressShouldBe` 0x7E + CPU.M $ Memory.writeByte R.KEY1 1 + R.KEY1 `atAddressShouldBe` 0x7F stop - KEY1 `atAddressShouldBe` 0xFE + R.KEY1 `atAddressShouldBe` 0xFE expectCPUCycleClocks 2 - CPU.M $ Memory.writeByte KEY1 1 - KEY1 `atAddressShouldBe` 0xFF + CPU.M $ Memory.writeByte R.KEY1 1 + R.KEY1 `atAddressShouldBe` 0xFF stop - KEY1 `atAddressShouldBe` 0x7E + R.KEY1 `atAddressShouldBe` 0x7E expectCPUCycleClocks 4 bcd :: Spec @@ -1905,8 +1906,8 @@ interrupts = do | mode /= CPU.ModeNormal && pending && enabled = Wakeup | otherwise = Ignore - CPU.M $ Memory.writeByte IF (if pending then bit vector else 0) - CPU.M $ Memory.writeByte IE (if enabled then bit vector else 0) + CPU.M $ Memory.writeByte R.IF (if pending then bit vector else 0) + CPU.M $ Memory.writeByte R.IE (if enabled then bit vector else 0) CPU.M CPU.step expectExtraCycles $ case behavior of @@ -1919,22 +1920,22 @@ interrupts = do RegSP `register16ShouldBe` 0xFFEE 0xFFEE `atAddressShouldBe` 0x01 0xFFEF `atAddressShouldBe` 0x40 - IF `atAddressShouldBe` 0 - IE `atAddressShouldBe` (bit vector) + R.IF `atAddressShouldBe` 0 + R.IE `atAddressShouldBe` bit vector expectPC (fromIntegral vector * 8 + 0x40) expectMode CPU.ModeNormal expectIME False Wakeup -> do RegSP `register16ShouldBe` 0xFFF0 - IF `atAddressShouldBe` (if pending then bit vector else 0) - IE `atAddressShouldBe` (if enabled then bit vector else 0) + R.IF `atAddressShouldBe` (if pending then bit vector else 0) + R.IE `atAddressShouldBe` (if enabled then bit vector else 0) expectPC 0x4002 expectMode CPU.ModeNormal expectIME ime Ignore -> do RegSP `register16ShouldBe` 0xFFF0 - IF `atAddressShouldBe` (if pending then bit vector else 0) - IE `atAddressShouldBe` (if enabled then bit vector else 0) + R.IF `atAddressShouldBe` (if pending then bit vector else 0) + R.IE `atAddressShouldBe` (if enabled then bit vector else 0) expectPC (if mode == CPU.ModeNormal then 0x4002 else 0x4001) expectMode mode expectIME ime @@ -1948,8 +1949,8 @@ interrupts = do withPC 0x4001 $ do CPU.M $ do - Memory.writeByte IF (bit vector) - Memory.writeByte IE (bit vector) + Memory.writeByte R.IF (bit vector) + Memory.writeByte R.IE (bit vector) CPU.step expectExtraCycles 1 expectMode CPU.ModeNormal @@ -1966,15 +1967,15 @@ interrupts = do withPC 0x4001 $ do CPU.M $ do - Memory.writeByte IF (bit l .|. bit r) - Memory.writeByte IE (bit l .|. bit r) + Memory.writeByte R.IF (bit l .|. bit r) + Memory.writeByte R.IE (bit l .|. bit r) CPU.step expectExtraCycles isrClocks RegSP `register16ShouldBe` 0xFFEE 0xFFEE `atAddressShouldBe` 0x01 0xFFEF `atAddressShouldBe` 0x40 - IF `atAddressShouldBe` (bit r) - IE `atAddressShouldBe` (bit l .|. bit r) + R.IF `atAddressShouldBe` bit r + R.IE `atAddressShouldBe` (bit l .|. bit r) expectPC (fromIntegral l * 8 + 0x40) expectMode CPU.ModeNormal expectIME False diff --git a/core/test/Machine/GBC/DecodeSpec.hs b/core/test/Machine/GBC/DecodeSpec.hs index 8d51c3e..6449e61 100644 --- a/core/test/Machine/GBC/DecodeSpec.hs +++ b/core/test/Machine/GBC/DecodeSpec.hs @@ -9,12 +9,12 @@ module Machine.GBC.DecodeSpec ) where -import Control.Monad.State -import Data.Word -import Machine.GBC.CPU.Decode -import Machine.GBC.CPU.ISA -import Machine.GBC.Util -import Test.Hspec +import Control.Monad.State (MonadState (get, put), StateT (..)) +import Data.Word (Word8) +import Machine.GBC.CPU.Decode (MonadFetch (..), decodeAndExecute) +import Machine.GBC.CPU.ISA (MonadSm83x (..)) +import Machine.GBC.Util (formatHex) +import Test.Hspec ( Spec, describe, expectationFailure, it, shouldBe,) newtype DecodeM a = DecodeM {runDecodeM :: StateT [Word8] Maybe a} deriving (Monad, Applicative, Functor) diff --git a/core/test/Machine/GBC/EmulatorSpec.hs b/core/test/Machine/GBC/EmulatorSpec.hs index b3b1f43..6ae3522 100644 --- a/core/test/Machine/GBC/EmulatorSpec.hs +++ b/core/test/Machine/GBC/EmulatorSpec.hs @@ -6,22 +6,21 @@ module Machine.GBC.EmulatorSpec where import qualified Data.ByteString as B -import Data.List -import Foreign.Ptr +import Data.List (nub, sort) +import Foreign.Ptr (nullPtr) import qualified Machine.GBC.Audio as Audio import qualified Machine.GBC.CPU as CPU import qualified Machine.GBC.Color as Color import qualified Machine.GBC.DMA as DMA -import Machine.GBC.Emulator +import Machine.GBC.Emulator (State (..)) import qualified Machine.GBC.Emulator as Emulator import qualified Machine.GBC.Graphics as Graphics import qualified Machine.GBC.Keypad as Keypad -import Machine.GBC.ROM import qualified Machine.GBC.ROM as ROM -import Machine.GBC.Registers +import qualified Machine.GBC.Registers as R import qualified Machine.GBC.Serial as Serial import qualified Machine.GBC.Timer as Timer -import Test.Hspec +import Test.Hspec (Spec, describe, it, shouldBe) blankROM :: ROM.ROM blankROM = ROM.ROM paths (blankHeader size) (B.replicate size 0) @@ -29,22 +28,22 @@ blankROM = ROM.ROM paths (blankHeader size) (B.replicate size 0) paths = ROM.Paths "testRom" "testRom.sav" "testRom.rtc" size = 32 * 1024 * 1024 -blankHeader :: Int -> Header +blankHeader :: Int -> ROM.Header blankHeader romSize = - Header - { startAddress = 0, - nintendoCharacterData = "", - gameTitle = "", - gameCode = "", - cgbSupport = CGBCompatible, - makerCode = "", - sgbSupport = GBOnly, - cartridgeType = CartridgeType Nothing False False, - romSize = romSize, - externalRAM = 0, - destination = Overseas, - oldLicenseCode = 0, - maskROMVersion = 0 + ROM.Header + { ROM.startAddress = 0, + ROM.nintendoCharacterData = "", + ROM.gameTitle = "", + ROM.gameCode = "", + ROM.cgbSupport = ROM.CGBCompatible, + ROM.makerCode = "", + ROM.sgbSupport = ROM.GBOnly, + ROM.cartridgeType = ROM.CartridgeType Nothing False False, + ROM.romSize = romSize, + ROM.externalRAM = 0, + ROM.destination = ROM.Overseas, + ROM.oldLicenseCode = 0, + ROM.maskROMVersion = 0 } spec :: Spec @@ -73,59 +72,59 @@ spec = describe "allPorts" $ sort (fst <$> allPorts) `shouldBe` sort ( [0xFF30 .. 0xFF3F] - ++ [ P1, - SB, - SC, - DIV, - TIMA, - TMA, - TAC, - NR10, - NR11, - NR12, - NR13, - NR14, - NR20, - NR21, - NR22, - NR23, - NR24, - NR30, - NR31, - NR32, - NR33, - NR34, - NR41, - NR42, - NR43, - NR44, - NR50, - NR51, - NR52, - LCDC, - STAT, - SCY, - SCX, - LY, - LYC, - DMA, - BGP, - OBP0, - OBP1, - WY, - WX, - KEY1, - VBK, - HDMA1, - HDMA2, - HDMA3, - HDMA4, - HDMA5, - BCPS, - BCPD, - OCPS, - OCPD, - PCM12, - PCM34 + ++ [ R.P1, + R.SB, + R.SC, + R.DIV, + R.TIMA, + R.TMA, + R.TAC, + R.NR10, + R.NR11, + R.NR12, + R.NR13, + R.NR14, + R.NR20, + R.NR21, + R.NR22, + R.NR23, + R.NR24, + R.NR30, + R.NR31, + R.NR32, + R.NR33, + R.NR34, + R.NR41, + R.NR42, + R.NR43, + R.NR44, + R.NR50, + R.NR51, + R.NR52, + R.LCDC, + R.STAT, + R.SCY, + R.SCX, + R.LY, + R.LYC, + R.DMA, + R.BGP, + R.OBP0, + R.OBP1, + R.WY, + R.WX, + R.KEY1, + R.VBK, + R.HDMA1, + R.HDMA2, + R.HDMA3, + R.HDMA4, + R.HDMA5, + R.BCPS, + R.BCPD, + R.OCPS, + R.OCPD, + R.PCM12, + R.PCM34 ] ) diff --git a/main/src/HGBC/Config.hs b/main/src/HGBC/Config.hs index 266a63f..fb53b5b 100644 --- a/main/src/HGBC/Config.hs +++ b/main/src/HGBC/Config.hs @@ -8,23 +8,20 @@ module HGBC.Config ) where -import Control.Monad +import Control.Monad (unless) import qualified Data.ByteString as B -import Data.FileEmbed -import Data.Functor.Identity -import Data.Maybe +import Data.FileEmbed (embedOneFileOf) +import Data.Functor.Identity (Identity) +import Data.Maybe (fromMaybe) import qualified HGBC.Config.CommandLine as CommandLine -import HGBC.Config.File +import HGBC.Config.File (Config (..), parseFile) import qualified HGBC.Config.Paths as Paths -import HGBC.Errors -import HGBC.Keymap - ( Keymap, - ScancodeDecoder, - ) +import HGBC.Errors (FileParseErrors) +import HGBC.Keymap (Keymap, ScancodeDecoder) import qualified Machine.GBC.Color as Color import qualified Machine.GBC.ROM as ROM -import System.Directory -import System.FilePath +import System.Directory (createDirectoryIfMissing, doesFileExist) +import System.FilePath (takeDirectory, ()) -- | Configure HGBC. load :: diff --git a/main/src/HGBC/Config/CommandLine.hs b/main/src/HGBC/Config/CommandLine.hs index cb2e00e..a208e72 100644 --- a/main/src/HGBC/Config/CommandLine.hs +++ b/main/src/HGBC/Config/CommandLine.hs @@ -5,10 +5,10 @@ module HGBC.Config.CommandLine where import qualified Data.Text as T -import HGBC.Config.Decode +import HGBC.Config.Decode (decodeColorCorrection, decodeMode) import qualified Machine.GBC.Color as Color -import Machine.GBC.Mode -import Options.Applicative +import Machine.GBC.Mode (EmulatorMode) +import Options.Applicative (Parser, ParserInfo, action, auto, completeWith, eitherReader, execParser, fullDesc, header, help, helper, info, long, metavar, option, str, strArgument, switch, value, (<**>)) data Options = Options { debugMode :: Bool, diff --git a/main/src/HGBC/Config/Decode.hs b/main/src/HGBC/Config/Decode.hs index c0348e6..96f3b15 100644 --- a/main/src/HGBC/Config/Decode.hs +++ b/main/src/HGBC/Config/Decode.hs @@ -7,14 +7,14 @@ module HGBC.Config.Decode ) where -import Control.Monad -import Data.Bits +import Control.Monad (when) +import Data.Bits (Bits (..)) import qualified Data.Text as T import qualified Data.Text.Read as T -import Data.Word +import Data.Word (Word32) import qualified Machine.GBC.Color as Color -import Machine.GBC.Mode -import Machine.GBC.Util +import Machine.GBC.Mode (EmulatorMode (..)) +import Machine.GBC.Util ((.<<.), (.>>.)) decodeMode :: T.Text -> Either String (Maybe EmulatorMode) decodeMode "auto" = Right Nothing diff --git a/main/src/HGBC/Config/File.hs b/main/src/HGBC/Config/File.hs index b1f7a6b..b756086 100644 --- a/main/src/HGBC/Config/File.hs +++ b/main/src/HGBC/Config/File.hs @@ -12,21 +12,21 @@ module HGBC.Config.File ) where -import Control.Monad.Identity -import Data.Bifunctor +import Control.Monad.Identity (Identity) +import Data.Bifunctor (Bifunctor (bimap, first)) import qualified Data.ByteString as B -import Data.Either +import Data.Either (partitionEithers) import qualified Data.HashMap.Strict as HM -import Data.Monoid +import Data.Monoid (Last (Last, getLast)) import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T import qualified Data.Vector as V -import Data.Word -import HGBC.Config.Decode +import Data.Word (Word32) +import HGBC.Config.Decode (decodeColor, decodeColorCorrection, decodeMode) import qualified HGBC.Keymap as Keymap import qualified Machine.GBC.Color as Color -import Machine.GBC.Mode +import Machine.GBC.Mode (EmulatorMode) import qualified Text.Toml as Toml import qualified Text.Toml.Types as Toml diff --git a/main/src/HGBC/Config/Paths.hs b/main/src/HGBC/Config/Paths.hs index e52dc71..cfd5233 100644 --- a/main/src/HGBC/Config/Paths.hs +++ b/main/src/HGBC/Config/Paths.hs @@ -7,10 +7,10 @@ module HGBC.Config.Paths ) where -import Data.Functor +import Data.Functor ((<&>)) import qualified Machine.GBC.ROM as ROM -import System.Directory -import System.FilePath +import System.Directory (getAppUserDataDirectory) +import System.FilePath (takeBaseName, ()) base :: IO FilePath base = getAppUserDataDirectory "hgbc" diff --git a/main/src/HGBC/Debugger.hs b/main/src/HGBC/Debugger.hs index 6221e6b..6ec6398 100644 --- a/main/src/HGBC/Debugger.hs +++ b/main/src/HGBC/Debugger.hs @@ -9,42 +9,39 @@ module HGBC.Debugger ) where -import Control.Concurrent +import Control.Concurrent (forkIO, threadDelay) import qualified Control.Concurrent.Async as Async -import Control.Monad -import Control.Monad.Reader +import Control.Monad (void) +import Control.Monad.Reader (ReaderT (runReaderT)) import qualified Data.Aeson.Encoding as JSON import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy.Char8 as LBC -import Data.String +import Data.String (IsString (fromString)) import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T import qualified Data.Text.Lazy.Encoding as LT import qualified HGBC.Debugger.Breakpoints as Breakpoints import qualified HGBC.Debugger.Disassembly as Disassembly -import HGBC.Debugger.HTML +import HGBC.Debugger.HTML (debugHTML) import qualified HGBC.Debugger.JSON as JSON import qualified HGBC.Debugger.Labels as Labels -import HGBC.Debugger.Logging +import HGBC.Debugger.Logging (logError) import qualified HGBC.Debugger.Memory as Memory import qualified HGBC.Debugger.Resources as Resource -import HGBC.Debugger.State -import HGBC.Debugger.Status +import HGBC.Debugger.State (DebugState (..), restoreBreakpoints, restoreLabels) +import HGBC.Debugger.Status (getStatus) import qualified HGBC.Emulator as Emulator -import HGBC.Errors +import HGBC.Errors (FileParseErrors) import qualified HGBC.Events as Event import Machine.GBC.CPU (readPC) -import Machine.GBC.Disassembler +import Machine.GBC.Disassembler (LongAddress (LongAddress), disassembleROM) import qualified Machine.GBC.Emulator as Emulator -import Machine.GBC.Memory - ( bootROMLength, - getBank, - ) +import Machine.GBC.Memory (bootROMLength, getBank) import qualified Network.HTTP.Types as HTTP import qualified Network.Wai as Wai import qualified Network.Wai.Handler.Warp as Warp -import Text.Read +import Text.Read (readMaybe) -- | Start the debugger start :: Int -> Emulator.RuntimeConfig -> Emulator.State -> IO [FileParseErrors] diff --git a/main/src/HGBC/Debugger/Breakpoints.hs b/main/src/HGBC/Debugger/Breakpoints.hs index b4bd25f..4cc125c 100644 --- a/main/src/HGBC/Debugger/Breakpoints.hs +++ b/main/src/HGBC/Debugger/Breakpoints.hs @@ -9,10 +9,10 @@ where import Control.Exception (catch) import qualified Data.HashTable.IO as H -import Data.Maybe -import HGBC.Debugger.State +import Data.Maybe (fromMaybe) +import HGBC.Debugger.State (DebugState (breakpoints), saveBreakpoints) import qualified HGBC.Events as Event -import Machine.GBC.Disassembler +import Machine.GBC.Disassembler (LongAddress) getAsList :: DebugState -> IO [(LongAddress, Bool)] getAsList debugState = H.toList (breakpoints debugState) diff --git a/main/src/HGBC/Debugger/Disassembly.hs b/main/src/HGBC/Debugger/Disassembly.hs index 6f1527a..a70f88f 100644 --- a/main/src/HGBC/Debugger/Disassembly.hs +++ b/main/src/HGBC/Debugger/Disassembly.hs @@ -12,12 +12,12 @@ import qualified Data.Aeson.Encoding as JSON import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as LBC import qualified Data.HashMap.Strict as HM -import Data.IORef -import Data.String +import Data.IORef (readIORef, writeIORef) +import Data.String (IsString (fromString)) import qualified Data.Text.Lazy as LT import qualified HGBC.Debugger.JSON as JSON -import HGBC.Debugger.State -import Machine.GBC.Disassembler +import HGBC.Debugger.State (DebugState (disassemblyRef, labelsRef, romFileName)) +import Machine.GBC.Disassembler (Disassembly, LongAddress, generateOutput, lookupN) get :: DebugState -> IO Disassembly get debugState = readIORef (disassemblyRef debugState) diff --git a/main/src/HGBC/Debugger/HTML.hs b/main/src/HGBC/Debugger/HTML.hs index 0afc82b..5f501dc 100644 --- a/main/src/HGBC/Debugger/HTML.hs +++ b/main/src/HGBC/Debugger/HTML.hs @@ -7,16 +7,13 @@ where import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as LB -import Data.String -import HGBC.Debugger.HTML.CPURegisters -import HGBC.Debugger.HTML.Elements -import HGBC.Debugger.HTML.LCDRegisters -import HGBC.Debugger.HTML.SoundRegisters -import HGBC.Debugger.HTML.SystemRegisters -import Prelude hiding - ( div, - head, - ) +import Data.String (IsString (fromString)) +import HGBC.Debugger.HTML.CPURegisters (cpuRegisters) +import HGBC.Debugger.HTML.Elements (body, button, charset, div, divclass, divclassid, focusDiv, h, head, html, img, inlineScript, innerNav, input, link, meta, nav, script, tabs, title, ulid) +import HGBC.Debugger.HTML.LCDRegisters (lcdRegisters) +import HGBC.Debugger.HTML.SoundRegisters (soundRegisters) +import HGBC.Debugger.HTML.SystemRegisters (systemRegisters) +import Prelude hiding (div, head) debugHTML :: FilePath -> Int -> LB.ByteString debugHTML romFileName bootROMLimit = BB.toLazyByteString (html [header, main]) diff --git a/main/src/HGBC/Debugger/HTML/CPURegisters.hs b/main/src/HGBC/Debugger/HTML/CPURegisters.hs index 08eaf0f..798c586 100644 --- a/main/src/HGBC/Debugger/HTML/CPURegisters.hs +++ b/main/src/HGBC/Debugger/HTML/CPURegisters.hs @@ -6,7 +6,7 @@ module HGBC.Debugger.HTML.CPURegisters where import qualified Data.ByteString.Builder as BB -import HGBC.Debugger.HTML.Elements +import HGBC.Debugger.HTML.Elements (descField, field, table, td, th, tr, value) cpuRegisters :: BB.Builder cpuRegisters = diff --git a/main/src/HGBC/Debugger/HTML/Elements.hs b/main/src/HGBC/Debugger/HTML/Elements.hs index 16aa38c..ca42af9 100644 --- a/main/src/HGBC/Debugger/HTML/Elements.hs +++ b/main/src/HGBC/Debugger/HTML/Elements.hs @@ -42,11 +42,8 @@ module HGBC.Debugger.HTML.Elements where import qualified Data.ByteString.Builder as BB -import Data.List hiding (head) -import Prelude hiding - ( div, - head, - ) +import Data.List (intersperse) +import Prelude hiding (div, head) html :: [BB.Builder] -> BB.Builder html contents = "" <> mconcat contents <> "" diff --git a/main/src/HGBC/Debugger/HTML/LCDRegisters.hs b/main/src/HGBC/Debugger/HTML/LCDRegisters.hs index dd25ab9..61a9752 100644 --- a/main/src/HGBC/Debugger/HTML/LCDRegisters.hs +++ b/main/src/HGBC/Debugger/HTML/LCDRegisters.hs @@ -6,7 +6,7 @@ module HGBC.Debugger.HTML.LCDRegisters where import qualified Data.ByteString.Builder as BB -import HGBC.Debugger.HTML.Elements +import HGBC.Debugger.HTML.Elements (br, descField, enableDisable, field, fieldGroup, p, table, td, th, tr, ul, unused, value) lcdRegisters :: BB.Builder lcdRegisters = diff --git a/main/src/HGBC/Debugger/HTML/SoundRegisters.hs b/main/src/HGBC/Debugger/HTML/SoundRegisters.hs index 0ffe916..7cd5b58 100644 --- a/main/src/HGBC/Debugger/HTML/SoundRegisters.hs +++ b/main/src/HGBC/Debugger/HTML/SoundRegisters.hs @@ -6,7 +6,7 @@ module HGBC.Debugger.HTML.SoundRegisters where import qualified Data.ByteString.Builder as BB -import HGBC.Debugger.HTML.Elements +import HGBC.Debugger.HTML.Elements (br, descField, enableDisable, field, fieldGroup, p, table, td, tdspan, th, tr, ul, unused, value) soundRegisters :: BB.Builder soundRegisters = diff --git a/main/src/HGBC/Debugger/HTML/SystemRegisters.hs b/main/src/HGBC/Debugger/HTML/SystemRegisters.hs index 9c89049..7fa1b71 100644 --- a/main/src/HGBC/Debugger/HTML/SystemRegisters.hs +++ b/main/src/HGBC/Debugger/HTML/SystemRegisters.hs @@ -6,8 +6,8 @@ module HGBC.Debugger.HTML.SystemRegisters where import qualified Data.ByteString.Builder as BB -import Data.Functor -import HGBC.Debugger.HTML.Elements +import Data.Functor ((<&>)) +import HGBC.Debugger.HTML.Elements (br, descField, enableDisable, field, fieldGroup, p, table, td, th, tr, ul, unused, value) systemRegisters :: BB.Builder systemRegisters = diff --git a/main/src/HGBC/Debugger/JSON.hs b/main/src/HGBC/Debugger/JSON.hs index 9df3647..9c40550 100644 --- a/main/src/HGBC/Debugger/JSON.hs +++ b/main/src/HGBC/Debugger/JSON.hs @@ -9,13 +9,13 @@ module HGBC.Debugger.JSON ) where -import Data.Aeson +import Data.Aeson (Encoding, KeyValue ((.=)), pairs) import qualified Data.Aeson.Encoding as JSON import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as LB import qualified Data.ByteString.Short as SB import qualified Data.Text as T -import Machine.GBC.Disassembler +import Machine.GBC.Disassembler (Editable, Field (..), Instruction (Data, Instruction0, Instruction1, Instruction2), LongAddress (..), Parameter (..)) import Machine.GBC.Util (formatHex) toLazyByteString :: Encoding -> LB.ByteString diff --git a/main/src/HGBC/Debugger/Labels.hs b/main/src/HGBC/Debugger/Labels.hs index 6e9439d..dc685a7 100644 --- a/main/src/HGBC/Debugger/Labels.hs +++ b/main/src/HGBC/Debugger/Labels.hs @@ -9,14 +9,14 @@ module HGBC.Debugger.Labels where import Control.Exception (catch) -import Control.Monad -import Data.Char +import Control.Monad (when) +import Data.Char (isSpace) import qualified Data.HashMap.Strict as HM -import Data.IORef +import Data.IORef (modifyIORef', readIORef, writeIORef) import qualified Data.Text as T -import HGBC.Debugger.State +import HGBC.Debugger.State (DebugState (labelsRef), saveLabels) import qualified HGBC.Events as Event -import Machine.GBC.Disassembler +import Machine.GBC.Disassembler (Labels, LongAddress) -- | Add some new labels. addFromList :: DebugState -> Event.Channel -> Labels -> IO () diff --git a/main/src/HGBC/Debugger/Logging.hs b/main/src/HGBC/Debugger/Logging.hs index 6d18f24..02789a4 100644 --- a/main/src/HGBC/Debugger/Logging.hs +++ b/main/src/HGBC/Debugger/Logging.hs @@ -4,10 +4,10 @@ module HGBC.Debugger.Logging where import qualified Data.ByteString.Char8 as CB -import Data.Time.Format -import Data.Time.LocalTime +import Data.Time.Format (defaultTimeLocale, formatTime) +import Data.Time.LocalTime (getZonedTime) import qualified Network.Wai as Wai -import System.IO +import System.IO (hPutStrLn, stderr) logError :: Wai.Request -> String -> IO () logError req message = do diff --git a/main/src/HGBC/Debugger/Memory.hs b/main/src/HGBC/Debugger/Memory.hs index 6fdd818..a5bd7ae 100644 --- a/main/src/HGBC/Debugger/Memory.hs +++ b/main/src/HGBC/Debugger/Memory.hs @@ -7,17 +7,17 @@ module HGBC.Debugger.Memory ) where -import Control.Monad.Reader +import Control.Monad.Reader (ReaderT (runReaderT)) import qualified Data.Aeson.Encoding as JSON import qualified Data.ByteString as B import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as LBC -import Data.String -import Data.Traversable -import Data.Word -import HGBC.Debugger.JSON as JSON +import Data.String (IsString (fromString)) +import Data.Traversable (for) +import Data.Word (Word16) +import HGBC.Debugger.JSON as JSON (longAddress) import qualified Machine.GBC.CPU as CPU -import Machine.GBC.Disassembler +import Machine.GBC.Disassembler (LongAddress (LongAddress)) import qualified Machine.GBC.Emulator as Emulator import Machine.GBC.Memory (readChunk) import Machine.GBC.Util (formatHex) diff --git a/main/src/HGBC/Debugger/ROM.hs b/main/src/HGBC/Debugger/ROM.hs index e98ea7a..e6e883d 100644 --- a/main/src/HGBC/Debugger/ROM.hs +++ b/main/src/HGBC/Debugger/ROM.hs @@ -6,19 +6,19 @@ module HGBC.Debugger.ROM where import qualified Data.ByteString.Char8 as BC -import Machine.GBC.ROM +import qualified Machine.GBC.ROM as ROM import Machine.GBC.Util (formatHex) -dumpHeader :: Header -> IO () -dumpHeader Header {..} = do +dumpHeader :: ROM.Header -> IO () +dumpHeader ROM.Header {..} = do putStrLn $ take 16 (BC.unpack gameTitle ++ repeat ' ') ++ " " ++ BC.unpack gameCode ++ " " ++ case destination of - Japan -> " (JAPAN)" - Overseas -> " (INTERNATIONAL)" + ROM.Japan -> " (JAPAN)" + ROM.Overseas -> " (INTERNATIONAL)" putStrLn ("Version: " ++ show maskROMVersion) putStrLn @@ -28,25 +28,25 @@ dumpHeader Header {..} = do putStr $ "Console support: " ++ case cgbSupport of - CGBIncompatible -> "GB" - CGBCompatible -> "GB+CGB" - CGBExclusive -> "CGB" + ROM.CGBIncompatible -> "GB" + ROM.CGBCompatible -> "GB+CGB" + ROM.CGBExclusive -> "CGB" putStrLn $ case sgbSupport of - GBOnly -> "" - UsesSGB -> "+SGB" + ROM.GBOnly -> "" + ROM.UsesSGB -> "+SGB" let cartridge = - "Cartridge: " ++ case mbcType cartridgeType of + "Cartridge: " ++ case ROM.mbcType cartridgeType of Nothing -> "No MBC" - Just MBC1 -> "MBC1" - Just MBC2 -> "MBC2" - Just MBC3 -> "MBC3" - Just MBC3RTC -> "MBC3+RTC" - Just MBC5 -> "MBC5" + Just ROM.MBC1 -> "MBC1" + Just ROM.MBC2 -> "MBC2" + Just ROM.MBC3 -> "MBC3" + Just ROM.MBC3RTC -> "MBC3+RTC" + Just ROM.MBC5 -> "MBC5" putStrLn ( cartridge - ++ (if hasSRAM cartridgeType then "+SRAM" else "") - ++ (if hasBackupBattery cartridgeType then "+Battery" else "") + ++ (if ROM.hasSRAM cartridgeType then "+SRAM" else "") + ++ (if ROM.hasBackupBattery cartridgeType then "+Battery" else "") ++ " (" ++ formatByteCount romSize ++ " ROM" diff --git a/main/src/HGBC/Debugger/Resources.hs b/main/src/HGBC/Debugger/Resources.hs index 88f0379..6983686 100644 --- a/main/src/HGBC/Debugger/Resources.hs +++ b/main/src/HGBC/Debugger/Resources.hs @@ -20,7 +20,7 @@ module HGBC.Debugger.Resources where import qualified Data.ByteString.Lazy as LB -import Data.FileEmbed +import Data.FileEmbed (embedOneFileOf) svgRun :: LB.ByteString svgRun = LB.fromStrict $(embedOneFileOf ["data/play.svg", "../data/play.svg"]) diff --git a/main/src/HGBC/Debugger/State.hs b/main/src/HGBC/Debugger/State.hs index 5a319db..f1d3a8f 100644 --- a/main/src/HGBC/Debugger/State.hs +++ b/main/src/HGBC/Debugger/State.hs @@ -11,26 +11,30 @@ module HGBC.Debugger.State ) where -import Control.Exception hiding (handle) -import Control.Monad -import Data.Bifunctor +import Control.Exception + ( Exception (displayException), + IOException, + bracket, + try, + ) +import Control.Monad (when) +import Data.Bifunctor (Bifunctor (second)) import qualified Data.ByteString.Lazy as LB -import Data.Either -import Data.Foldable -import Data.Functor +import Data.Either (partitionEithers) +import Data.Foldable (for_) import qualified Data.HashMap.Strict as HM import qualified Data.HashTable.IO as H -import Data.IORef +import Data.IORef (IORef, modifyIORef', newIORef, readIORef) import qualified Data.Text as T -import Data.Time.Clock +import Data.Time.Clock (diffUTCTime) import qualified HGBC.Config.Paths as Path import qualified HGBC.Debugger.SymFile as Sym -import HGBC.Errors -import Machine.GBC.Disassembler -import System.Directory -import System.FilePath -import System.IO -import Text.Read +import HGBC.Errors (FileParseErrors) +import Machine.GBC.Disassembler (Disassembly, LongAddress (..), initialLabels) +import System.Directory (copyFile, createDirectoryIfMissing, doesFileExist, getModificationTime, removeFile, renamePath) +import System.FilePath (takeBaseName, takeDirectory, (-<.>), ()) +import System.IO (Handle, IOMode (ReadMode), hClose, hGetContents, hPutStrLn, openTempFile, withFile) +import Text.Read (readMaybe) import Prelude hiding (init) data DebugState = DebugState diff --git a/main/src/HGBC/Debugger/Status.hs b/main/src/HGBC/Debugger/Status.hs index 7664aa4..cf74d48 100644 --- a/main/src/HGBC/Debugger/Status.hs +++ b/main/src/HGBC/Debugger/Status.hs @@ -6,26 +6,26 @@ module HGBC.Debugger.Status ) where -import Control.Applicative -import Control.Monad.Reader -import Data.Aeson -import Data.Bits +import Control.Applicative (Applicative (liftA2)) +import Control.Monad.Reader (MonadIO (liftIO), ReaderT (runReaderT)) +import Data.Aeson (KeyValue ((.=)), Series, fromEncoding, pairs) +import Data.Bits (Bits (testBit, (.&.))) import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as LBS -import Data.Functor -import Data.List +import Data.Functor ((<&>)) +import Data.List (foldl') import qualified Data.Vector as V import qualified Machine.GBC.Audio as Audio import Machine.GBC.Audio.Common (directReadPorts) import Machine.GBC.Audio.WaveChannel (WaveChannel (..)) -import Machine.GBC.CPU -import Machine.GBC.CPU.ISA +import qualified Machine.GBC.CPU as CPU +import Machine.GBC.CPU.ISA (Register16 (RegSP), Register8 (RegB, RegC, RegD, RegE, RegH, RegL), RegisterPushPop (PushPopAF)) import qualified Machine.GBC.Emulator as Emulator import qualified Machine.GBC.Graphics as Graphics -import Machine.GBC.Memory -import Machine.GBC.Primitive -import Machine.GBC.Registers -import Machine.GBC.Util +import Machine.GBC.Memory (getBank, getRamGate, readByte) +import Machine.GBC.Primitive (directReadPort) +import qualified Machine.GBC.Registers as R +import Machine.GBC.Util (formatHex, isFlagSet, (.>>.)) import Prelude hiding (div) getStatus :: Emulator.State -> IO LBS.ByteString @@ -39,52 +39,52 @@ getStatus emulatorState = fmap (foldl' (<>) mempty) . foldl' (liftA2 (++)) (pure []) $ [ rAF, - field "rB" formatHex <$> readR8 RegB, - field "rC" formatHex <$> readR8 RegC, - field "rD" formatHex <$> readR8 RegD, - field "rE" formatHex <$> readR8 RegE, - field "rH" formatHex <$> readR8 RegH, - field "rL" formatHex <$> readR8 RegL, + field "rB" formatHex <$> CPU.readR8 RegB, + field "rC" formatHex <$> CPU.readR8 RegC, + field "rD" formatHex <$> CPU.readR8 RegD, + field "rE" formatHex <$> CPU.readR8 RegE, + field "rH" formatHex <$> CPU.readR8 RegH, + field "rL" formatHex <$> CPU.readR8 RegL, rPC, rSP, flags, p1, - field "div" formatHex <$> readByte DIV, - field "tima" formatHex <$> readByte TIMA, - field "tma" formatHex <$> readByte TMA, + field "div" formatHex <$> readByte R.DIV, + field "tima" formatHex <$> readByte R.TIMA, + field "tma" formatHex <$> readByte R.TMA, tac, key1, - field "vbk0" (getBit 0) <$> readByte VBK, + field "vbk0" (getBit 0) <$> readByte R.VBK, svbk, ie, rif, sc, - allBitsOf "sb" <$> readByte SB, + allBitsOf "sb" <$> readByte R.SB, rp, romBanks, field "ramGate" (\g -> if g then 'O' else 'C') <$> getRamGate, field "ramBank" formatHex <$> getBank 0xA000, - allBitsOf "lcdc" <$> readByte LCDC, - field "dma" formatHex <$> readByte DMA, + allBitsOf "lcdc" <$> readByte R.LCDC, + field "dma" formatHex <$> readByte R.DMA, hdma5, - field "hdma4" formatHex <$> readByte HDMA4, - field "hdma3" formatHex <$> readByte HDMA3, - field "hdma2" formatHex <$> readByte HDMA2, - field "hdma1" formatHex <$> readByte HDMA1, - dmgPalette "bgp" <$> readByte BGP, - dmgPalette "obp0" <$> readByte OBP0, - dmgPalette "obp1" <$> readByte OBP1, + field "hdma4" formatHex <$> readByte R.HDMA4, + field "hdma3" formatHex <$> readByte R.HDMA3, + field "hdma2" formatHex <$> readByte R.HDMA2, + field "hdma1" formatHex <$> readByte R.HDMA1, + dmgPalette "bgp" <$> readByte R.BGP, + dmgPalette "obp0" <$> readByte R.OBP0, + dmgPalette "obp1" <$> readByte R.OBP1, stat, - field "scy" formatHex <$> readByte SCY, - field "wy" formatHex <$> readByte WY, - field "ly" formatHex <$> readByte LY, - field "scx" formatHex <$> readByte SCX, - field "wx" formatHex <$> readByte WX, - field "lyc" formatHex <$> readByte LYC, - cgbPalette "bcps" <$> readByte BCPS, + field "scy" formatHex <$> readByte R.SCY, + field "wy" formatHex <$> readByte R.WY, + field "ly" formatHex <$> readByte R.LY, + field "scx" formatHex <$> readByte R.SCX, + field "wx" formatHex <$> readByte R.WX, + field "lyc" formatHex <$> readByte R.LYC, + cgbPalette "bcps" <$> readByte R.BCPS, field "bcpd" formatHex <$> liftIO (directReadPort (Graphics.portBCPD (Emulator.graphicsState emulatorState))), - cgbPalette "ocps" <$> readByte OCPS, + cgbPalette "ocps" <$> readByte R.OCPS, field "ocpd" formatHex <$> liftIO (directReadPort (Graphics.portBCPD (Emulator.graphicsState emulatorState))), audio1, @@ -92,10 +92,10 @@ getStatus emulatorState = audio3, audio4, nr50, - allBitsOf "nr51" <$> readByte NR51, + allBitsOf "nr51" <$> readByte R.NR51, nr52, - nibbles "pcm12" <$> readByte PCM12, - nibbles "pcm34" <$> readByte PCM34, + nibbles "pcm12" <$> readByte R.PCM12, + nibbles "pcm34" <$> readByte R.PCM34, field "wave0" formatHex <$> liftIO (directReadPort (waveTable V.! 0x0)), field "wave1" formatHex <$> liftIO (directReadPort (waveTable V.! 0x1)), field "wave2" formatHex <$> liftIO (directReadPort (waveTable V.! 0x2)), @@ -115,35 +115,35 @@ getStatus emulatorState = ] field label decoder = pure . (label .=) . decoder rAF = do - (rA, rF) <- highlow <$> readR16pp PushPopAF + (rA, rF) <- highlow <$> CPU.readR16pp PushPopAF pure ["rA" .= rA, "rF" .= rF] rPC = do - pc <- readPC + pc <- CPU.readPC bank <- getBank pc let (rPCH, rPCL) = highlow pc pure ["rPCH" .= rPCH, "rPCL" .= rPCL, "pcBank" .= formatHex bank] rSP = do - sp <- readR16 RegSP + sp <- CPU.readR16 RegSP let (rSPH, rSPL) = highlow sp pure ["rSPH" .= rSPH, "rSPL" .= rSPL, "sp" .= sp] flags = do - i <- testIME <&> \ime -> if ime then 'I' else 'i' - r <- readF + i <- CPU.testIME <&> \ime -> if ime then 'I' else 'i' + r <- CPU.readF currentMode <- - getMode <&> \case - ModeHalt -> ("HALT" :: String) - ModeStop -> "STOP" - ModeNormal -> "RUN" + CPU.getMode <&> \case + CPU.ModeHalt -> ("HALT" :: String) + CPU.ModeStop -> "STOP" + CPU.ModeNormal -> "RUN" pure [ "i" .= i, - "z" .= if isFlagSet flagZ r then 'Z' else 'z', - "n" .= if isFlagSet flagN r then 'N' else 'n', - "h" .= if isFlagSet flagH r then 'H' else 'h', - "c" .= if isFlagSet flagCY r then 'C' else 'c', + "z" .= if isFlagSet CPU.flagZ r then 'Z' else 'z', + "n" .= if isFlagSet CPU.flagN r then 'N' else 'n', + "h" .= if isFlagSet CPU.flagH r then 'H' else 'h', + "c" .= if isFlagSet CPU.flagCY r then 'C' else 'c', "cpuMode" .= currentMode ] p1 = do - r <- readByte P1 + r <- readByte R.P1 pure [ "p15" .= getBit 5 r, "p14" .= getBit 4 r, @@ -153,13 +153,13 @@ getStatus emulatorState = "p10" .= getBit 0 r ] tac = do - r <- readByte TAC + r <- readByte R.TAC pure ["tac2" .= getBit 2 r, "tac1_0" .= formatHex (r .&. 3) !! 1] key1 = do - r <- readByte KEY1 + r <- readByte R.KEY1 pure ["key17" .= getBit 7 r, "key10" .= getBit 0 r] svbk = do - r <- readByte SVBK + r <- readByte R.SVBK pure [ "svbk7" .= getBit 7 r, "svbk5" .= getBit 5 r, @@ -168,7 +168,7 @@ getStatus emulatorState = "svbk2_0" .= formatHex (r .&. 7) !! 1 ] ie = do - r <- readByte IE + r <- readByte R.IE pure [ "ie4" .= getBit 4 r, "ie3" .= getBit 3 r, @@ -177,7 +177,7 @@ getStatus emulatorState = "ie0" .= getBit 0 r ] rif = do - r <- readByte IF + r <- readByte R.IF pure [ "if4" .= getBit 4 r, "if3" .= getBit 3 r, @@ -196,10 +196,10 @@ getStatus emulatorState = (name <> "0") .= getBit 0 r ] sc = do - r <- readByte SC + r <- readByte R.SC pure ["sc7" .= getBit 7 r, "sc1" .= getBit 1 r, "sc0" .= getBit 0 r] rp = do - r <- readByte RP + r <- readByte R.RP pure ["rp7_6" .= formatHex (r .>>. 6) !! 1, "rp1" .= getBit 1 r, "rp0" .= getBit 0 r] romBanks = do bank0 <- getBank 0x3000 @@ -210,7 +210,7 @@ getStatus emulatorState = "romBank" .= formatHex (if bank0 == 0 then bank1 else bank0) ] hdma5 = do - r <- readByte HDMA5 + r <- readByte R.HDMA5 pure ["hdma57" .= getBit 7 r, "hdma56_0" .= formatHex (r .&. 0x7F)] dmgPalette name r = [ (name <> "76") .= formatHex (r .>>. 6) !! 1, @@ -219,7 +219,7 @@ getStatus emulatorState = (name <> "10") .= formatHex (r .&. 3) !! 1 ] stat = do - r <- readByte STAT + r <- readByte R.STAT pure [ "stat6" .= getBit 6 r, "stat5" .= getBit 5 r, @@ -297,7 +297,7 @@ getStatus emulatorState = "nr446" .= getBit 6 nr44 ] nr50 = do - r <- readByte NR50 + r <- readByte R.NR50 pure [ "nr507" .= getBit 7 r, "nr506_4" .= head (formatHex (r .&. 0x70)), @@ -305,7 +305,7 @@ getStatus emulatorState = "nr502_0" .= formatHex (r .&. 0x07) !! 1 ] nr52 = do - r <- readByte NR52 + r <- readByte R.NR52 pure [ "nr527" .= getBit 7 r, "nr523" .= getBit 3 r, diff --git a/main/src/HGBC/Debugger/SymFile.hs b/main/src/HGBC/Debugger/SymFile.hs index 13e61ad..6c73aa7 100644 --- a/main/src/HGBC/Debugger/SymFile.hs +++ b/main/src/HGBC/Debugger/SymFile.hs @@ -8,18 +8,18 @@ module HGBC.Debugger.SymFile where import qualified Data.Attoparsec.ByteString.Char8 as P -import Data.Bifunctor +import Data.Bifunctor (Bifunctor (first)) import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy.Char8 as LBC -import Data.Char -import Data.List -import Data.String +import Data.Char (isAlphaNum, isSpace) +import Data.List (foldl', sortOn) +import Data.String (IsString (fromString)) import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T -import Data.Word -import Machine.GBC.Disassembler +import Data.Word (Word8) +import Machine.GBC.Disassembler (LongAddress (..), encodeAddress) import Machine.GBC.Util (formatHex) import Prelude hiding (lines) diff --git a/main/src/HGBC/Emulator.hs b/main/src/HGBC/Emulator.hs index 9b36f88..18c3d98 100644 --- a/main/src/HGBC/Emulator.hs +++ b/main/src/HGBC/Emulator.hs @@ -13,18 +13,18 @@ module HGBC.Emulator ) where -import Control.Concurrent -import Control.Concurrent.STM -import Control.Monad.Except -import Control.Monad.Reader -import Control.Monad.Writer -import Data.Bifunctor -import Data.Bits +import Control.Concurrent (forkIO, putMVar, takeMVar) +import Control.Concurrent.STM (TChan, atomically, newTChanIO, readTChan, tryReadTChan, writeTChan) +import Control.Monad.Except (ExceptT (..), MonadIO (..), forever, mapExceptT, void, when) +import Control.Monad.Reader (ReaderT) +import Control.Monad.Writer (WriterT, mapWriterT) +import Data.Bifunctor (Bifunctor (bimap, first)) +import Data.Bits (Bits ((.&.))) import qualified Data.ByteString as B -import Data.Functor.Identity -import Data.Time.Clock.System -import Data.Word -import Foreign.Ptr +import Data.Functor.Identity (Identity) +import Data.Time.Clock.System (SystemTime (MkSystemTime), getSystemTime) +import Data.Word (Word8) +import Foreign.Ptr (Ptr) import HGBC.Config (Config (..)) import qualified HGBC.Config.CommandLine as CommandLine import qualified HGBC.Config.Paths as Path @@ -32,21 +32,26 @@ import qualified HGBC.Debugger.Breakpoints as Breakpoints import qualified HGBC.Debugger.Disassembly as Disassembly import qualified HGBC.Debugger.Labels as Labels import qualified HGBC.Debugger.State as DebugState -import HGBC.Errors +import HGBC.Errors (FileParseErrors) import qualified HGBC.Events as Event import Machine.GBC.CPU (readPC) import qualified Machine.GBC.CPU as CPU import qualified Machine.GBC.Color as Color -import Machine.GBC.Disassembler +import Machine.GBC.Disassembler (LongAddress (..), disassembleFrom, disassemblyRequired) import qualified Machine.GBC.Emulator as Emulator import qualified Machine.GBC.Graphics as Graphics import Machine.GBC.Memory (getBank) -import Machine.GBC.Mode +import Machine.GBC.Mode (EmulatorMode (DMG)) import qualified Machine.GBC.ROM as ROM import qualified Machine.GBC.Serial as Serial -import System.Directory -import System.FilePath +import System.Directory (createDirectoryIfMissing) +import System.FilePath (takeDirectory) import UnliftIO.Exception + ( Exception (displayException), + IOException, + catch, + try, + ) -- | A notification for the emulator thread. data Command diff --git a/main/src/HGBC/Events.hs b/main/src/HGBC/Events.hs index d6dfe70..0f61d97 100644 --- a/main/src/HGBC/Events.hs +++ b/main/src/HGBC/Events.hs @@ -9,10 +9,10 @@ module HGBC.Events ) where -import Control.Concurrent.STM +import Control.Concurrent.STM (TChan, atomically, dupTChan, newBroadcastTChanIO, readTChan, writeTChan) import Control.Exception (IOException) -import Control.Monad.IO.Class -import Machine.GBC.Disassembler +import Control.Monad.IO.Class (MonadIO (..)) +import Machine.GBC.Disassembler (Labels, LongAddress) import qualified Machine.GBC.Errors as GBC data Event diff --git a/main/src/HGBC/Keymap.hs b/main/src/HGBC/Keymap.hs index 23d0b9a..a393660 100644 --- a/main/src/HGBC/Keymap.hs +++ b/main/src/HGBC/Keymap.hs @@ -12,9 +12,9 @@ module HGBC.Keymap ) where -import Data.Either -import Data.Foldable -import Data.Functor +import Data.Either (partitionEithers) +import Data.Foldable (Foldable (toList)) +import Data.Functor ((<&>)) import qualified Data.HashMap.Strict as HM import qualified Data.Map.Strict as M import qualified Data.Text as T diff --git a/sdl/src/Audio.hs b/sdl/src/Audio.hs index 7809aed..ce9b941 100644 --- a/sdl/src/Audio.hs +++ b/sdl/src/Audio.hs @@ -6,19 +6,19 @@ module Audio ) where -import Control.Monad -import Control.Monad.IO.Class -import Data.Bits -import Data.Word -import Foreign.C.Types -import Foreign.Marshal.Alloc -import Foreign.Marshal.Array -import Foreign.Ptr -import Foreign.Storable +import Control.Monad (void) +import Control.Monad.IO.Class (MonadIO) +import Data.Bits (Bits ((.&.))) +import Data.Word (Word16, Word8) +import Foreign.C.Types (CInt) +import Foreign.Marshal.Alloc (alloca) +import Foreign.Marshal.Array (pokeArray) +import Foreign.Ptr (Ptr, nullPtr) +import Foreign.Storable (Storable (poke, pokeElemOff)) import qualified Machine.GBC.Audio as Audio import qualified Machine.GBC.Emulator as Emulator -import Machine.GBC.Primitive -import Machine.GBC.Util +import Machine.GBC.Primitive +import Machine.GBC.Util ((.>>.)) import qualified SDL.Raw newtype Audio = Audio SDL.Raw.AudioDeviceID diff --git a/sdl/src/GLUtils.hs b/sdl/src/GLUtils.hs index 63f4e66..9f87d70 100644 --- a/sdl/src/GLUtils.hs +++ b/sdl/src/GLUtils.hs @@ -57,37 +57,37 @@ module GLUtils ) where -import Control.Exception -import Control.Monad -import Control.Monad.IO.Class -import Control.Monad.Trans.Resource -import Data.Bits +import Control.Exception (Exception, throwIO) +import Control.Monad (void, when) +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Trans.Resource (MonadUnliftIO, allocate, runResourceT, unprotect) +import Data.Bits (Bits (..)) import qualified Data.ByteString as B -import Data.Foldable -import Data.StateVar -import Foreign.ForeignPtr -import Foreign.Marshal.Alloc -import Foreign.Marshal.Array -import Foreign.Ptr -import Foreign.Storable -import Graphics.GL.Core44 +import Data.Foldable (traverse_) +import Data.StateVar (StateVar, makeStateVar) +import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrArray, withForeignPtr) +import Foreign.Marshal.Alloc (alloca, allocaBytes) +import Foreign.Marshal.Array (pokeArray, withArray) +import Foreign.Ptr (IntPtr, Ptr, intPtrToPtr, nullPtr) +import Foreign.Storable (Storable (peek, poke, sizeOf)) +import qualified Graphics.GL.Core44 as Raw data OpenGLError = OpenGLError String !B.ByteString deriving (Eq, Ord, Show, Exception) -- | Types that wrap OpenGL enums. class OpenGLEnum a where - toOpenGLEnum :: a -> GLenum + toOpenGLEnum :: a -> Raw.GLenum -- | Types that can be used as GLSL uniforms. class UniformAccess a where - getUniform :: MonadIO m => GLuint -> GLint -> m a - setUniform :: MonadIO m => GLint -> a -> m () + getUniform :: MonadIO m => Raw.GLuint -> Raw.GLint -> m a + setUniform :: MonadIO m => Raw.GLint -> a -> m () -instance UniformAccess GLint where +instance UniformAccess Raw.GLint where {-# INLINE getUniform #-} - getUniform program uniform = capture (glGetUniformiv program uniform) + getUniform program uniform = capture (Raw.glGetUniformiv program uniform) {-# INLINE setUniform #-} - setUniform = glUniform1i + setUniform = Raw.glUniform1i -- | Matrix types. class GLmatrix a where @@ -111,40 +111,40 @@ instance UniformAccess GLmatrix4 where {-# INLINE getUniform #-} getUniform program uniform = do fptr <- liftIO (mallocForeignPtrArray 16) - liftIO (withForeignPtr fptr (glGetUniformfv program uniform)) + liftIO (withForeignPtr fptr (Raw.glGetUniformfv program uniform)) pure (GLmatrix4 fptr) {-# INLINE setUniform #-} setUniform uniform (GLmatrix4 fptr) = - liftIO (withForeignPtr fptr (glUniformMatrix4fv uniform 1 GL_FALSE)) + liftIO (withForeignPtr fptr (Raw.glUniformMatrix4fv uniform 1 Raw.GL_FALSE)) -- | Get a 'StateVar' that links to a uniform. {-# INLINE linkUniform #-} linkUniform :: (UniformAccess a, MonadIO m) => Program -> B.ByteString -> m (StateVar a) linkUniform (Program program) uniform = liftIO $ do - uniformLocation <- B.useAsCString uniform (glGetUniformLocation program) + uniformLocation <- B.useAsCString uniform (Raw.glGetUniformLocation program) pure (makeStateVar (getUniform program uniformLocation) (setUniform uniformLocation)) -- | Link the currently bound texture buffer to the currently buffer texture. {-# INLINEABLE linkTextureBuffer #-} linkTextureBuffer :: MonadIO m => BufferObject -> m () -linkTextureBuffer (BufferObject buffer) = glTexBuffer GL_TEXTURE_BUFFER GL_R8UI buffer +linkTextureBuffer (BufferObject buffer) = Raw.glTexBuffer Raw.GL_TEXTURE_BUFFER Raw.GL_R8UI buffer -- | Link the currently bound texture buffer to the currently buffer texture. {-# INLINEABLE linkTextureBufferRange #-} -linkTextureBufferRange :: MonadIO m => BufferObject -> GLintptr -> GLsizeiptr -> m () -linkTextureBufferRange (BufferObject buffer) = glTexBufferRange GL_TEXTURE_BUFFER GL_R8UI buffer +linkTextureBufferRange :: MonadIO m => BufferObject -> Raw.GLintptr -> Raw.GLsizeiptr -> m () +linkTextureBufferRange (BufferObject buffer) = Raw.glTexBufferRange Raw.GL_TEXTURE_BUFFER Raw.GL_R8UI buffer -- | A vertex array object. -newtype VertexArrayObject = VertexArrayObject GLuint deriving (Eq, Show) +newtype VertexArrayObject = VertexArrayObject Raw.GLuint deriving (Eq, Show) -- | Create a new vertex array object. genVertexArrayObject :: IO VertexArrayObject -genVertexArrayObject = VertexArrayObject <$> capture (glGenVertexArrays 1) +genVertexArrayObject = VertexArrayObject <$> capture (Raw.glGenVertexArrays 1) -- | Bind a vertex array object {-# INLINE bindVertexArrayObject #-} bindVertexArrayObject :: MonadIO m => VertexArrayObject -> m () -bindVertexArrayObject (VertexArrayObject vao) = glBindVertexArray vao +bindVertexArrayObject (VertexArrayObject vao) = Raw.glBindVertexArray vao -- | How to convert and normalize vertex attribute elements. data IntegerHandling @@ -154,7 +154,7 @@ data IntegerHandling deriving (Eq, Ord, Show, Bounded, Enum) -- | Number of components in a vertex attribute. -type NumComponents = GLint +type NumComponents = Raw.GLint -- | Element data type of a vertex attribute. data ElementDataType @@ -171,16 +171,16 @@ data ElementDataType deriving (Eq, Ord, Show, Bounded, Enum) instance OpenGLEnum ElementDataType where - toOpenGLEnum Bytes = GL_BYTE - toOpenGLEnum Shorts = GL_SHORT - toOpenGLEnum Ints = GL_INT - toOpenGLEnum UnsignedBytes = GL_UNSIGNED_BYTE - toOpenGLEnum UnsignedShorts = GL_UNSIGNED_SHORT - toOpenGLEnum UnsignedInts = GL_UNSIGNED_INT - toOpenGLEnum Floats = GL_FLOAT - toOpenGLEnum HalfFloats = GL_HALF_FLOAT - toOpenGLEnum Doubles = GL_DOUBLE - toOpenGLEnum Fixeds = GL_FIXED + toOpenGLEnum Bytes = Raw.GL_BYTE + toOpenGLEnum Shorts = Raw.GL_SHORT + toOpenGLEnum Ints = Raw.GL_INT + toOpenGLEnum UnsignedBytes = Raw.GL_UNSIGNED_BYTE + toOpenGLEnum UnsignedShorts = Raw.GL_UNSIGNED_SHORT + toOpenGLEnum UnsignedInts = Raw.GL_UNSIGNED_INT + toOpenGLEnum Floats = Raw.GL_FLOAT + toOpenGLEnum HalfFloats = Raw.GL_HALF_FLOAT + toOpenGLEnum Doubles = Raw.GL_DOUBLE + toOpenGLEnum Fixeds = Raw.GL_FIXED data AttributeDivisor = PerVertex | PerInstance deriving (Eq, Ord, Show, Bounded, Enum) @@ -189,20 +189,20 @@ data Attribute = Attribute !B.ByteString !NumComponents !ElementDataType !Attrib type Offset = IntPtr -type Stride = GLsizei +type Stride = Raw.GLsizei -- | Set the offset and stride of an 'Attribute' in the current vertex buffer. {-# INLINEABLE linkAttribute #-} linkAttribute :: Program -> Attribute -> Offset -> Stride -> IO () linkAttribute (Program program) (Attribute name numComponents elementType divisor integerHandling) offset stride = do - attribute <- fromIntegral <$> B.useAsCString name (glGetAttribLocation program) - glEnableVertexAttribArray attribute + attribute <- fromIntegral <$> B.useAsCString name (Raw.glGetAttribLocation program) + Raw.glEnableVertexAttribArray attribute case integerHandling of - NormalizeToFloat -> vertexAttribPointerFloat attribute GL_TRUE - ConvertToFloat -> vertexAttribPointerFloat attribute GL_FALSE + NormalizeToFloat -> vertexAttribPointerFloat attribute Raw.GL_TRUE + ConvertToFloat -> vertexAttribPointerFloat attribute Raw.GL_FALSE KeepInteger -> - glVertexAttribIPointer + Raw.glVertexAttribIPointer attribute numComponents (toOpenGLEnum elementType) @@ -212,9 +212,9 @@ linkAttribute (Program program) (Attribute name numComponents elementType diviso where handleDivisor attribute = case divisor of PerVertex -> pure () - PerInstance -> glVertexAttribDivisor attribute 1 + PerInstance -> Raw.glVertexAttribDivisor attribute 1 vertexAttribPointerFloat attribute normalize = - glVertexAttribPointer + Raw.glVertexAttribPointer attribute numComponents (toOpenGLEnum elementType) @@ -223,7 +223,7 @@ linkAttribute (Program program) (Attribute name numComponents elementType diviso (intPtrToPtr offset) -- | A buffer of some sort. -newtype BufferObject = BufferObject GLuint deriving (Eq, Show) +newtype BufferObject = BufferObject Raw.GLuint deriving (Eq, Show) -- | The buffer types that we can bind. data BufferTarget @@ -236,21 +236,21 @@ data BufferTarget -- | Get the OpenGL enum for a 'BufferTarget'. instance OpenGLEnum BufferTarget where - toOpenGLEnum ArrayBuffer = GL_ARRAY_BUFFER - toOpenGLEnum ElementArrayBuffer = GL_ELEMENT_ARRAY_BUFFER - toOpenGLEnum TextureBufferBuffer = GL_TEXTURE_BUFFER - toOpenGLEnum UniformBuffer = GL_UNIFORM_BUFFER - toOpenGLEnum PixelUpload = GL_PIXEL_UNPACK_BUFFER + toOpenGLEnum ArrayBuffer = Raw.GL_ARRAY_BUFFER + toOpenGLEnum ElementArrayBuffer = Raw.GL_ELEMENT_ARRAY_BUFFER + toOpenGLEnum TextureBufferBuffer = Raw.GL_TEXTURE_BUFFER + toOpenGLEnum UniformBuffer = Raw.GL_UNIFORM_BUFFER + toOpenGLEnum PixelUpload = Raw.GL_PIXEL_UNPACK_BUFFER -- | Generate an empty buffer object. {-# INLINE genBuffer #-} genBuffer :: MonadIO m => m BufferObject -genBuffer = BufferObject <$> capture (glGenBuffers 1) +genBuffer = BufferObject <$> capture (Raw.glGenBuffers 1) -- | Bind a buffer. {-# INLINE bindBuffer #-} bindBuffer :: MonadIO m => BufferTarget -> BufferObject -> m () -bindBuffer target (BufferObject bo) = glBindBuffer (toOpenGLEnum target) bo +bindBuffer target (BufferObject bo) = Raw.glBindBuffer (toOpenGLEnum target) bo -- | Create and initialize a vertex buffer. {-# INLINEABLE makeVertexBuffer #-} @@ -259,18 +259,18 @@ makeVertexBuffer = makeBuffer ArrayBuffer -- | Create and initialize an element buffer. {-# INLINEABLE makeElementBuffer #-} -makeElementBuffer :: MonadIO m => [GLuint] -> m BufferObject +makeElementBuffer :: MonadIO m => [Raw.GLuint] -> m BufferObject makeElementBuffer = makeBuffer ElementArrayBuffer -- | Make a static buffer. {-# INLINE makeBuffer #-} makeBuffer :: forall m a. (MonadIO m, Storable a) => BufferTarget -> [a] -> m BufferObject makeBuffer bufferTarget vdata = do - bo <- capture (glGenBuffers 1) - glBindBuffer target bo + bo <- capture (Raw.glGenBuffers 1) + Raw.glBindBuffer target bo liftIO . withArray vdata $ \pData -> do let bufferSize = fromIntegral (length vdata * sizeOf (undefined :: a)) - glBufferData target bufferSize pData GL_STATIC_DRAW + Raw.glBufferData target bufferSize pData Raw.GL_STATIC_DRAW pure (BufferObject bo) where target = toOpenGLEnum bufferTarget @@ -286,42 +286,42 @@ data BufferUpdateStrategy -- | Allocate and bind a coherent writeable persistent buffer. {-# INLINEABLE makeWritablePersistentBuffer #-} makeWritablePersistentBuffer :: - MonadIO m => BufferUpdateStrategy -> BufferTarget -> GLsizeiptr -> m (BufferObject, Ptr a) + MonadIO m => BufferUpdateStrategy -> BufferTarget -> Raw.GLsizeiptr -> m (BufferObject, Ptr a) makeWritablePersistentBuffer updateStrategy bufferTarget size = do - bo <- capture (glGenBuffers 1) - glBindBuffer target bo - glBufferStorage target size nullPtr storageFlags - ptr <- glMapBufferRange target 0 size mapFlags + bo <- capture (Raw.glGenBuffers 1) + Raw.glBindBuffer target bo + Raw.glBufferStorage target size nullPtr storageFlags + ptr <- Raw.glMapBufferRange target 0 size mapFlags pure (BufferObject bo, ptr) where target = toOpenGLEnum bufferTarget - baseFlags = GL_MAP_WRITE_BIT .|. GL_MAP_PERSISTENT_BIT + baseFlags = Raw.GL_MAP_WRITE_BIT .|. Raw.GL_MAP_PERSISTENT_BIT (storageFlags, mapFlags) = case updateStrategy of - Coherent -> (baseFlags .|. GL_MAP_COHERENT_BIT, baseFlags .|. GL_MAP_COHERENT_BIT) - ExplicitFlush -> (baseFlags, baseFlags .|. GL_MAP_FLUSH_EXPLICIT_BIT) + Coherent -> (baseFlags .|. Raw.GL_MAP_COHERENT_BIT, baseFlags .|. Raw.GL_MAP_COHERENT_BIT) + ExplicitFlush -> (baseFlags, baseFlags .|. Raw.GL_MAP_FLUSH_EXPLICIT_BIT) -- | Bind a buffer to a buffer-backed uniform. {-# INLINEABLE linkUniformBuffer #-} -linkUniformBuffer :: MonadIO m => Program -> B.ByteString -> BufferObject -> GLuint -> m () +linkUniformBuffer :: MonadIO m => Program -> B.ByteString -> BufferObject -> Raw.GLuint -> m () linkUniformBuffer (Program program) uniform (BufferObject buffer) bindingLocation = do - glBindBufferBase GL_UNIFORM_BUFFER bindingLocation buffer - uniformBlockIndex <- liftIO (B.useAsCString uniform (glGetUniformBlockIndex program)) - glUniformBlockBinding program uniformBlockIndex bindingLocation + Raw.glBindBufferBase Raw.GL_UNIFORM_BUFFER bindingLocation buffer + uniformBlockIndex <- liftIO (B.useAsCString uniform (Raw.glGetUniformBlockIndex program)) + Raw.glUniformBlockBinding program uniformBlockIndex bindingLocation -- | An OpenGL texture unit. -newtype TextureUnit = TextureUnit GLint deriving (Eq, Show) +newtype TextureUnit = TextureUnit Raw.GLint deriving (Eq, Show) instance UniformAccess TextureUnit where - getUniform program uniform = TextureUnit <$> capture (glGetUniformiv program uniform) - setUniform uniform (TextureUnit unit) = glUniform1i uniform unit + getUniform program uniform = TextureUnit <$> capture (Raw.glGetUniformiv program uniform) + setUniform uniform (TextureUnit unit) = Raw.glUniform1i uniform unit -- | Set the active texture unit. {-# INLINE activeTextureUnit #-} activeTextureUnit :: MonadIO m => TextureUnit -> m () -activeTextureUnit (TextureUnit tu) = glActiveTexture (GL_TEXTURE0 + fromIntegral tu) +activeTextureUnit (TextureUnit tu) = Raw.glActiveTexture (Raw.GL_TEXTURE0 + fromIntegral tu) -- | An OpenGL texture. -newtype Texture = Texture GLuint deriving (Eq, Show) +newtype Texture = Texture Raw.GLuint deriving (Eq, Show) -- | An OpenGL texture target. data TextureTarget @@ -332,19 +332,19 @@ data TextureTarget deriving (Eq, Ord, Show, Bounded, Enum) instance OpenGLEnum TextureTarget where - toOpenGLEnum Texture1D = GL_TEXTURE_1D - toOpenGLEnum Texture2D = GL_TEXTURE_2D - toOpenGLEnum Texture3D = GL_TEXTURE_3D - toOpenGLEnum TextureBuffer = GL_TEXTURE_BUFFER + toOpenGLEnum Texture1D = Raw.GL_TEXTURE_1D + toOpenGLEnum Texture2D = Raw.GL_TEXTURE_2D + toOpenGLEnum Texture3D = Raw.GL_TEXTURE_3D + toOpenGLEnum TextureBuffer = Raw.GL_TEXTURE_BUFFER -- | Make a new texture. {-# INLINE genTexture #-} genTexture :: MonadIO m => m Texture -genTexture = Texture <$> capture (glGenTextures 1) +genTexture = Texture <$> capture (Raw.glGenTextures 1) {-# INLINE bindTexture #-} bindTexture :: MonadIO m => TextureTarget -> Texture -> m () -bindTexture target (Texture texture) = glBindTexture (toOpenGLEnum target) texture +bindTexture target (Texture texture) = Raw.glBindTexture (toOpenGLEnum target) texture -- | A shader type. data ShaderType @@ -357,34 +357,34 @@ data ShaderType deriving (Eq, Ord, Show, Bounded, Enum) instance OpenGLEnum ShaderType where - toOpenGLEnum ComputeShader = GL_COMPUTE_SHADER - toOpenGLEnum VertexShader = GL_VERTEX_SHADER - toOpenGLEnum TessControlShader = GL_TESS_CONTROL_SHADER - toOpenGLEnum TessEvaluationShader = GL_TESS_EVALUATION_SHADER - toOpenGLEnum GeometryShader = GL_GEOMETRY_SHADER - toOpenGLEnum FragmentShader = GL_FRAGMENT_SHADER + toOpenGLEnum ComputeShader = Raw.GL_COMPUTE_SHADER + toOpenGLEnum VertexShader = Raw.GL_VERTEX_SHADER + toOpenGLEnum TessControlShader = Raw.GL_TESS_CONTROL_SHADER + toOpenGLEnum TessEvaluationShader = Raw.GL_TESS_EVALUATION_SHADER + toOpenGLEnum GeometryShader = Raw.GL_GEOMETRY_SHADER + toOpenGLEnum FragmentShader = Raw.GL_FRAGMENT_SHADER -- A shader program. -newtype Program = Program GLuint deriving (Eq, Show) +newtype Program = Program Raw.GLuint deriving (Eq, Show) -- | Use a program. {-# INLINE useProgram #-} useProgram :: MonadIO m => Program -> m () -useProgram (Program program) = glUseProgram program +useProgram (Program program) = Raw.glUseProgram program -- | Compile and link a set of shaders. {-# INLINEABLE compileShaders #-} compileShaders :: MonadUnliftIO m => [(ShaderType, B.ByteString)] -> m Program compileShaders shaders = runResourceT $ do - (programKey, program) <- allocate glCreateProgram glDeleteProgram + (programKey, program) <- allocate Raw.glCreateProgram Raw.glDeleteProgram traverse_ (loadShader program) shaders - glLinkProgram program + Raw.glLinkProgram program - isLinked <- capture (glGetProgramiv program GL_LINK_STATUS) + isLinked <- capture (Raw.glGetProgramiv program Raw.GL_LINK_STATUS) when (isLinked == 0) . liftIO $ do - logLength <- capture (glGetProgramiv program GL_INFO_LOG_LENGTH) + logLength <- capture (Raw.glGetProgramiv program Raw.GL_INFO_LOG_LENGTH) message <- allocaBytes (fromIntegral logLength) $ \ptr -> do - glGetProgramInfoLog program (fromIntegral logLength) nullPtr ptr + Raw.glGetProgramInfoLog program (fromIntegral logLength) nullPtr ptr B.packCString ptr throwIO (OpenGLError "Program linkage failed" message) @@ -392,21 +392,21 @@ compileShaders shaders = runResourceT $ do pure (Program program) where loadShader program (shaderType, source) = do - (_, shader) <- allocate (glCreateShader (toOpenGLEnum shaderType)) glDeleteShader + (_, shader) <- allocate (Raw.glCreateShader (toOpenGLEnum shaderType)) Raw.glDeleteShader liftIO . B.useAsCString source $ \pSource -> alloca $ \ppSource -> do poke ppSource pSource - glShaderSource shader 1 ppSource nullPtr - glCompileShader shader + Raw.glShaderSource shader 1 ppSource nullPtr + Raw.glCompileShader shader - compilePassed <- capture (glGetShaderiv shader GL_COMPILE_STATUS) + compilePassed <- capture (Raw.glGetShaderiv shader Raw.GL_COMPILE_STATUS) when (compilePassed == 0) . liftIO $ do - logLength <- capture (glGetShaderiv shader GL_INFO_LOG_LENGTH) + logLength <- capture (Raw.glGetShaderiv shader Raw.GL_INFO_LOG_LENGTH) message <- allocaBytes (fromIntegral logLength) $ \ptr -> do - glGetShaderInfoLog shader (fromIntegral logLength) nullPtr ptr + Raw.glGetShaderInfoLog shader (fromIntegral logLength) nullPtr ptr B.packCString ptr throwIO (OpenGLError "Shader compilation failed" message) - void (allocate (shader <$ glAttachShader program shader) (glDetachShader program)) + void (allocate (shader <$ Raw.glAttachShader program shader) (Raw.glDetachShader program)) pure shader -- | Utility to deal with foreign functions that use pointers to return values. diff --git a/sdl/src/Keymap.hs b/sdl/src/Keymap.hs index d3a8aba..d5b7142 100644 --- a/sdl/src/Keymap.hs +++ b/sdl/src/Keymap.hs @@ -7,10 +7,10 @@ module Keymap ) where -import Control.Applicative +import Control.Applicative (Alternative ((<|>))) import qualified Data.Map as M import qualified Data.Text as T -import HGBC.Keymap +import HGBC.Keymap (Key (GBCKey, Pause, Quit), Keymap (..), Modifier (..)) import qualified Machine.GBC.Keypad as Keypad import qualified SDL diff --git a/sdl/src/Main.hs b/sdl/src/Main.hs index 7b536ab..27a567c 100644 --- a/sdl/src/Main.hs +++ b/sdl/src/Main.hs @@ -4,26 +4,26 @@ module Main where import qualified Audio -import Control.Concurrent +import Control.Concurrent (forkIO) import Control.Exception (displayException) -import Control.Monad.Except -import Control.Monad.Reader -import Control.Monad.Writer -import Data.Foldable +import Control.Monad.Except (MonadIO (liftIO), forever, runExceptT, void, when) +import Control.Monad.Reader (ReaderT (runReaderT)) +import Control.Monad.Writer (WriterT (runWriterT)) +import Data.Foldable (for_) import qualified HGBC.Config as Config import qualified HGBC.Config.CommandLine as CommandLine import qualified HGBC.Debugger as Debugger import qualified HGBC.Debugger.ROM as ROM import qualified HGBC.Emulator as Emulator import qualified HGBC.Events as Event -import Keymap +import Keymap (decodeScancode, defaultKeymap) import qualified Machine.GBC.CPU as CPU import qualified Machine.GBC.Emulator as Emulator import qualified Machine.GBC.Graphics as Graphics import Machine.GBC.Memory (getROMHeader) import qualified Machine.GBC.Memory as Memory import Machine.GBC.Util (formatHex) -import Numeric +import Numeric (showFFloat) import qualified SDL import qualified Thread.EventLoop as EventLoop import qualified Thread.LCD as LCD diff --git a/sdl/src/SDL/Extras.hs b/sdl/src/SDL/Extras.hs index 53286ce..ca5f40e 100644 --- a/sdl/src/SDL/Extras.hs +++ b/sdl/src/SDL/Extras.hs @@ -5,11 +5,11 @@ module SDL.Extras ) where -import Control.Monad.IO.Class -import Foreign.C.Types -import Foreign.Marshal.Alloc -import Foreign.Storable -import SDL.Internal.Types +import Control.Monad.IO.Class (MonadIO (..)) +import Foreign.C.Types (CInt (..)) +import Foreign.Marshal.Alloc (alloca) +import Foreign.Storable (Storable (peek)) +import SDL.Internal.Types (Window (..)) import qualified SDL.Raw newtype DisplayIndex = DisplayIndex CInt deriving (Eq, Ord, Show) diff --git a/sdl/src/Thread/EventLoop.hs b/sdl/src/Thread/EventLoop.hs index 78912a5..56bb1d3 100644 --- a/sdl/src/Thread/EventLoop.hs +++ b/sdl/src/Thread/EventLoop.hs @@ -3,9 +3,9 @@ module Thread.EventLoop ) where -import Control.Concurrent -import Control.Monad.Reader -import Data.Maybe +import Control.Concurrent (forkOS) +import Control.Monad.Reader (void, when) +import Data.Maybe (catMaybes) import qualified HGBC.Emulator as Emulator import qualified HGBC.Keymap as Keymap import qualified Machine.GBC.Emulator as Emulator diff --git a/sdl/src/Thread/LCD.hs b/sdl/src/Thread/LCD.hs index 8d15600..8c75933 100644 --- a/sdl/src/Thread/LCD.hs +++ b/sdl/src/Thread/LCD.hs @@ -8,14 +8,7 @@ module Thread.LCD ) where -import Control.Concurrent - ( forkOS, - newEmptyMVar, - putMVar, - takeMVar, - tryPutMVar, - tryTakeMVar, - ) +import Control.Concurrent (forkOS, newEmptyMVar, putMVar, takeMVar, tryPutMVar, tryTakeMVar) import Control.Exception (mask, try) import Control.Monad (join, void) import Control.Monad.IO.Class (MonadIO) @@ -32,11 +25,7 @@ import qualified Graphics.GL.Core44 as GL import qualified HGBC.Config import qualified Machine.GBC.Graphics as Graphics import qualified SDL -import SDL.Extras - ( DisplayIndex, - getCurrentDisplayMode, - getWindowDisplayIndex, - ) +import SDL.Extras (DisplayIndex, getCurrentDisplayMode, getWindowDisplayIndex) import qualified SDL.Raw import System.FilePath (takeFileName) import qualified Window diff --git a/sdl/src/Window.hs b/sdl/src/Window.hs index f6ca127..fc7403f 100644 --- a/sdl/src/Window.hs +++ b/sdl/src/Window.hs @@ -10,10 +10,10 @@ module Window ) where -import Control.Concurrent -import Control.Exception -import Control.Monad.IO.Class -import Data.Int +import Control.Concurrent (ThreadId, throwTo) +import Control.Exception (Exception) +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Int (Int32) import qualified SDL import Prelude hiding (lookup) diff --git a/testing/src/Framework.hs b/testing/src/Framework.hs index 02e3e0b..67c710c 100644 --- a/testing/src/Framework.hs +++ b/testing/src/Framework.hs @@ -16,15 +16,15 @@ module Framework ) where -import Data.Bifunctor +import Data.Bifunctor (Bifunctor (bimap)) +import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as LB -import qualified Data.ByteString.Lazy.Builder as BB -import Data.Maybe -import Data.Time -import System.Console.ANSI -import System.Exit -import System.FilePath -import UnliftIO +import Data.Maybe (catMaybes, isNothing) +import Data.Time (UTCTime, defaultTimeLocale, formatTime) +import System.Console.ANSI (Color (Green, Red), ColorIntensity (Dull), ConsoleLayer (Foreground), SGR (SetColor), setSGR) +import System.Exit (exitFailure, exitSuccess) +import System.FilePath (()) +import UnliftIO (Exception (displayException), catchAny) type Name = String diff --git a/testing/src/Main.hs b/testing/src/Main.hs index 2db3895..a72bde4 100644 --- a/testing/src/Main.hs +++ b/testing/src/Main.hs @@ -7,37 +7,37 @@ module Main ) where -import Control.Monad.Except -import Control.Monad.Reader -import Control.Monad.Writer +import Control.Monad.Except (MonadIO (liftIO), filterM, forever, runExceptT, unless, void) +import Control.Monad.Reader (ReaderT (runReaderT)) +import Control.Monad.Writer (WriterT (runWriterT)) import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as LB -import Data.Char -import Data.Functor -import Data.IORef -import Data.List -import Data.Time -import Data.Traversable -import Data.Word -import Foreign.Marshal.Alloc -import Framework +import Data.Char (isSpace) +import Data.Functor ((<&>)) +import Data.IORef (IORef, modifyIORef', newIORef, readIORef) +import Data.List (isInfixOf, isSuffixOf, sort, stripPrefix) +import Data.Time (getCurrentTime) +import Data.Traversable (for) +import Data.Word (Word16) +import Foreign.Marshal.Alloc (allocaBytes) +import Framework (Required (Optional, Required), SourceLink, TestResult (TestFailed, TestPassed), TestSuite, TestTree (TestCase, TestTree), checkResultsAndExit, generateReport, runTestSuite) import qualified Machine.GBC.CPU as CPU import qualified Machine.GBC.Color as Color import qualified Machine.GBC.Emulator as Emulator -import Machine.GBC.Errors +import Machine.GBC.Errors (Fault (InvalidInstruction)) import qualified Machine.GBC.Graphics as Graphics import qualified Machine.GBC.Memory as Memory import qualified Machine.GBC.ROM as ROM import qualified Machine.GBC.Serial as Serial import Machine.GBC.Util (formatHex) -import System.Directory -import System.Environment -import System.FilePath -import System.IO.Temp -import UnliftIO.Async -import UnliftIO.Concurrent -import UnliftIO.Exception +import System.Directory (createDirectoryIfMissing, doesDirectoryExist, listDirectory) +import System.Environment (lookupEnv) +import System.FilePath (takeBaseName, takeExtension, ()) +import System.IO.Temp (withSystemTempDirectory) +import UnliftIO.Async (Async, async, cancel) +import UnliftIO.Concurrent (putMVar, takeMVar) +import UnliftIO.Exception (Exception, bracket, catch, throwIO) main :: IO () main = do