Skip to content

Commit

Permalink
feat(ppu): VBL NMI timing improved
Browse files Browse the repository at this point in the history
  • Loading branch information
dbousamra committed Jan 16, 2018
1 parent 74af540 commit a37b527
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 24 deletions.
1 change: 1 addition & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stack build && stack exec -- hnes $1
6 changes: 4 additions & 2 deletions src/Emulator/CPU.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Data.Bits hiding (bit)
import Data.Word
import Emulator.Nes
import Emulator.Opcode
import Emulator.Trace (Trace, mkTrace, renderTrace)
import Emulator.Trace (Trace, mkTrace)
import Emulator.Util
import Prelude hiding (and, compare)

Expand Down Expand Up @@ -142,7 +142,9 @@ handleInterrupts :: Emulator ()
handleInterrupts = do
int <- loadCpu interrupt
case int of
Just NMI -> nmi
Just NMI -> do
liftIO $ putStrLn "in nmi"
nmi
Just IRQ -> error "not handling IRQ yet"
Nothing -> pure ()
storeCpu interrupt Nothing
Expand Down
45 changes: 34 additions & 11 deletions src/Emulator/Nes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module Emulator.Nes (
, loadKeys
, writeScreen
, loadScreen
, nmiChange
) where

import Control.Monad
Expand Down Expand Up @@ -118,13 +119,17 @@ data PPU = PPU {
currentVramAddress :: IORef Word16,
tempVramAddress :: IORef Word16,
oamAddress :: IORef Word8,
-- NMI
nmiEnabled :: IORef Bool,
nmiOccurred :: IORef Bool,
nmiDelay :: IORef Word8,
nmiPrevious :: IORef Bool,
-- Control register bits
nameTable :: IORef Word16,
incrementMode :: IORef IncrementMode,
spriteTable :: IORef Word16,
bgTable :: IORef Word16,
spriteSize :: IORef SpriteSize,
nmiEnabled :: IORef Bool,
-- Mask register bits
colorMode :: IORef ColorMode,
leftBgVisibility :: IORef Visibility,
Expand All @@ -135,10 +140,9 @@ data PPU = PPU {
intensifyGreens :: IORef Bool,
intensifyBlues :: IORef Bool,
-- Status register bits
lastWrite :: IORef Word8,
spriteOverflow :: IORef Bool,
spriteZeroHit :: IORef Bool,
verticalBlank :: IORef Bool,
-- verticalBlank :: IORef Bool,
-- Scroll register
fineX :: IORef Word8,
-- Data register
Expand Down Expand Up @@ -347,12 +351,13 @@ readStatus = do
registerV <- loadPpu ppuRegister
spriteOverflowV <- loadPpu spriteOverflow
spriteZeroHitV <- loadPpu spriteZeroHit
vBlankV <- loadPpu verticalBlank
nmiOccurred' <- loadPpu nmiOccurred
let r = registerV .&. 0x1F
let r' = r .|. fromIntegral (fromEnum spriteOverflowV `unsafeShiftL` 5)
let r'' = r' .|. fromIntegral (fromEnum spriteZeroHitV `unsafeShiftL` 6)
let rFinal = r'' .|. fromIntegral (fromEnum vBlankV `unsafeShiftL` 7)
storePpu verticalBlank False
let rFinal = r'' .|. fromIntegral (fromEnum nmiOccurred' `unsafeShiftL` 7)
storePpu nmiOccurred False
nmiChange
storePpu writeToggle False
pure $ fromIntegral rFinal

Expand Down Expand Up @@ -420,6 +425,7 @@ writeControl v = do
storePpu bgTable $ if testBit v 4 then 0x1000 else 0x0000
storePpu spriteSize $ if testBit v 5 then Double else Normal
storePpu nmiEnabled $ testBit v 7
nmiChange
tv <- loadPpu tempVramAddress
storePpu tempVramAddress ((tv .&. 0xF3FF) .|. (toWord16 v .&. 0x03) `unsafeShiftL` 10)

Expand Down Expand Up @@ -490,13 +496,17 @@ newPPU = do
currentVramAddress <- newIORef 0x0
tempVramAddress <- newIORef 0x0
oamAddress <- newIORef 0x0
-- NMI
nmiEnabled <- newIORef False
nmiOccurred <- newIORef False
nmiDelay <- newIORef 0
nmiPrevious <- newIORef False
-- Control register
nameTable <- newIORef 0x2000
incrementMode <- newIORef Horizontal
spriteTable <- newIORef 0x0000
bgTable <- newIORef 0x0000
spriteSize <- newIORef Normal
nmiEnabled <- newIORef False
-- Mask register
colorMode <- newIORef Color
leftBgVis <- newIORef Hidden
Expand All @@ -507,10 +517,8 @@ newPPU = do
intensifyGreens <- newIORef False
intensifyBlues <- newIORef False
-- Status register
lastWrite <- newIORef 0x0
spriteOverflow <- newIORef False
spriteZeroHit <- newIORef False
vBlankStarted <- newIORef False
-- Scroll register
fineX <- newIORef 0x0
-- Data register
Expand All @@ -530,13 +538,15 @@ newPPU = do
oamData nameTableData paletteData screen
-- Addresses
currentVramAddress tempVramAddress oamAddress
-- NMI
nmiEnabled nmiOccurred nmiDelay nmiPrevious
-- Control register
nameTable incrementMode spriteTable bgTable spriteSize nmiEnabled
nameTable incrementMode spriteTable bgTable spriteSize
-- Mask register
colorMode leftBgVis leftSpritesVis bgVis spriteVis
intensifyReds intensifyGreens intensifyBlues
-- Status register
lastWrite spriteOverflow spriteZeroHit vBlankStarted
spriteOverflow spriteZeroHit
-- Scroll register
fineX
-- Data register
Expand Down Expand Up @@ -572,3 +582,16 @@ writeScreen (x, y) (r, g, b) = with ppu $ \ppu -> do
VUM.write (screen ppu) (offset + 0) r
VUM.write (screen ppu) (offset + 1) g
VUM.write (screen ppu) (offset + 2) b

nmiChange :: Emulator ()
nmiChange = do
enabled <- loadPpu nmiEnabled
occurred <- loadPpu nmiOccurred
previous <- loadPpu nmiPrevious
let nmi = enabled && occurred

when (nmi && not previous) $
storePpu nmiDelay 15

storePpu nmiPrevious nmi

35 changes: 24 additions & 11 deletions src/Emulator/PPU.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,18 @@ module Emulator.PPU (
) where

import Control.Monad
import Control.Monad.IO.Class (liftIO)
import Data.Bits (unsafeShiftL, unsafeShiftR, xor, (.&.), (.|.))
import qualified Data.Vector as V
-- import Control.Monad.IO.Class (liftIO)
import Data.Bits (unsafeShiftL, unsafeShiftR, xor, (.&.), (.|.))
import qualified Data.Vector as V
import Data.Word
import Emulator.Nes
import Emulator.Trace
import Emulator.Util
import Prelude hiding (cycle)
import Prelude hiding (cycle)

reset :: Emulator ()
reset = do
storePpu ppuCycles 340
storePpu scanline 240
storePpu verticalBlank False
writeControl 0
writeMask 0
writeOAMAddress 0
Expand All @@ -29,6 +27,8 @@ step = do

tick :: Emulator Coords
tick = do
handleInterrupts

modifyPpu ppuCycles (+1)
cycles <- loadPpu ppuCycles

Expand All @@ -45,14 +45,26 @@ tick = do
cycles' <- loadPpu ppuCycles
pure (scanline', cycles')

handleInterrupts :: Emulator ()
handleInterrupts = do
delay <- loadPpu nmiDelay
enabled <- loadPpu nmiEnabled
occurred <- loadPpu nmiOccurred

when (delay > 0) $ do
modifyPpu nmiDelay (subtract 1)
delay' <- loadPpu nmiDelay
when (delay' == 0 && enabled && occurred) $
storeCpu interrupt (Just NMI)

handleLinePhase :: Int -> Int -> Emulator ()
handleLinePhase scanline cycle = do
let preLine = scanline == 261
let visibleLine = scanline < 240
let renderLine = preLine || visibleLine

let visibleCycle = cycle >= 1 && cycle <= 256
let preFetchCycle = cycle >= 321 && cycle <= 336
let visibleCycle = cycle >= 1 && cycle <= 256
let fetchCycle = visibleCycle || (cycle >= 321 && cycle <= 336)

bgVisible <- loadPpu bgVisibility
Expand Down Expand Up @@ -322,12 +334,13 @@ isSpriteVisible row spriteSize = row >= 0 && row < h

enterVBlank :: Emulator ()
enterVBlank = do
storePpu verticalBlank True
generateNMI <- loadPpu nmiEnabled
when generateNMI $ storeCpu interrupt (Just NMI)
storePpu nmiOccurred True
nmiChange

exitVBlank :: Emulator ()
exitVBlank = storePpu verticalBlank False
exitVBlank = do
storePpu nmiOccurred False
nmiChange

idle :: Emulator ()
idle = pure ()
Expand Down

0 comments on commit a37b527

Please sign in to comment.