This commit is contained in:
2026-04-01 11:19:41 +02:00
parent 47aedfdd8e
commit a69d0c9342
5 changed files with 65 additions and 34 deletions

View File

@@ -1,2 +0,0 @@
# Tia

5
README.org Normal file
View File

@@ -0,0 +1,5 @@
#+title: Tia
This aims to be an emulator for the gameboy color, made in Haskell for the memes.
It as of right now has no particular ambitions, but rather an exploration of emulating that system, one bit at the time.

View File

@@ -58,7 +58,8 @@ library
import: warnings import: warnings
-- Modules exported by the library. -- Modules exported by the library.
exposed-modules: GB.CPU exposed-modules: GBC
, GBC.CPU
-- Modules included in this library but not exported. -- Modules included in this library but not exported.
-- other-modules: -- other-modules:

View File

@@ -1,3 +1,3 @@
{-# LANGUAGE TemplateHaskell {-# LANGUAGE TemplateHaskell
, RecordWildCards #-} , RecordWildCards #-}
module GB where module GBC where

View File

@@ -3,7 +3,7 @@
, DataKinds , DataKinds
, FlexibleContexts , FlexibleContexts
, GADTs #-} , GADTs #-}
module GB.CPU where module GBC.CPU where
import Lens.Micro.Platform import Lens.Micro.Platform
import Data.Word import Data.Word
@@ -34,17 +34,17 @@ carryFlagPosition = 4
instance Convert FlagRegister Word8 where instance Convert FlagRegister Word8 where
convert r = convert r =
((`shiftL` zeroFlagPosition) $ if r ^. zero then 1 else 0) + ((.<<. zeroFlagPosition) $ if r ^. zero then 1 else 0) +
((`shiftL` negativeFlagPosition) $ if r ^. negative then 1 else 0) + ((.<<. negativeFlagPosition) $ if r ^. negative then 1 else 0) +
((`shiftL` halfCarryFlagPosition) $ if r ^. halfCarry then 1 else 0) + ((.<<. halfCarryFlagPosition) $ if r ^. halfCarry then 1 else 0) +
((`shiftL` carryFlagPosition) $ if r ^. carry then 1 else 0) ((.<<. carryFlagPosition) $ if r ^. carry then 1 else 0)
instance Convert Word8 FlagRegister where instance Convert Word8 FlagRegister where
convert w = convert w =
let _zero = (shiftR w zeroFlagPosition .&. 1 /= 0) let _zero = (w `testBit` zeroFlagPosition)
_negative = (shiftR w negativeFlagPosition .&. 1 /= 0) _negative = (w `testBit` negativeFlagPosition)
_halfCarry = (shiftR w halfCarryFlagPosition .&. 1 /= 0) _halfCarry = (w `testBit` halfCarryFlagPosition)
_carry = (shiftR w carryFlagPosition .&. 1 /= 0) in _carry = (w `testBit` carryFlagPosition) in
FlagRegister {..} FlagRegister {..}
@@ -118,6 +118,11 @@ data Instruction where
RR :: ArithmeticTarget -> Instruction RR :: ArithmeticTarget -> Instruction
RRHL :: Instruction RRHL :: Instruction
SLA :: ArithmeticTarget -> Instruction
SLAHL :: Instruction
SRA :: ArithmeticTarget -> Instruction
SRAHL :: Instruction
execute :: CPU -> Instruction -> CPU execute :: CPU -> Instruction -> CPU
execute cpu = \case execute cpu = \case
AddR t _c -> let value = cpu ^. registers . t AddR t _c -> let value = cpu ^. registers . t
@@ -197,69 +202,91 @@ execute cpu = \case
(newValue, newFlags) = add16 _hl _bc _flags in (newValue, newFlags) = add16 _hl _bc _flags in
cpu & registers . hl .~ newValue cpu & registers . hl .~ newValue
& registers . flags .~ newFlags & registers . flags .~ newFlags
AddSP e -> let value = cpu ^. sp AddSP _e -> let value = cpu ^. sp
newValue :: Word16 = fromIntegral (fromIntegral value) + (fromIntegral e) in newValue :: Word16 = fromIntegral (fromIntegral value :: Integer) + (fromIntegral _e) in
cpu & sp .~ newValue cpu & sp .~ newValue
& registers . flags .~ & registers . flags .~
FlagRegister { _zero = False, FlagRegister { _zero = False,
_negative = False, _negative = False,
_halfCarry = value .&. 16 + fromIntegral e .&. 16 > 16, -- TODO: check if this still works out _halfCarry = value .&. 16 + fromIntegral _e .&. 16 > 16, -- TODO: check if this still works out
_carry = value > newValue _carry = value > newValue
} }
RLCA -> let _carry = cpu ^. registers . a .&. 128 == 128 in RLCA -> let _carry = (cpu ^. registers . a) `testBit` 8 in
cpu & registers . a %~ (`rotateL` 1) cpu & registers . a %~ (`rotateL` 1)
& registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..}
RRCA -> let _carry = cpu ^. registers . a .&. 1 == 1 in RRCA -> let _carry = (cpu ^. registers . a) `testBit` 1 in
cpu & registers . a %~ (`rotateR` 1) cpu & registers . a %~ (`rotateR` 1)
& registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..}
RLA -> let _carry = cpu ^. registers . a .&. 128 == 128 RLA -> let _carry = (cpu ^. registers . a) `testBit` 8
_c = cpu ^. registers . flags . carry in _c = cpu ^. registers . flags . carry in
cpu & registers . a %~ (if _c then (+1) else id) . (`shiftL` 1) cpu & registers . a %~ (if _c then (+1) else id) . (.<<. 1)
& registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..}
RRA -> let _carry = cpu ^. registers . a .&. 1 == 1 RRA -> let _carry = (cpu ^. registers . a) `testBit` 1
_c = cpu ^. registers . flags . carry in _c = cpu ^. registers . flags . carry in
cpu & registers . a %~ (if _c then (+128) else id) . (`shiftR` 1) cpu & registers . a %~ (if _c then (+128) else id) . (.>>. 1)
& registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = False, _negative = False, _halfCarry = False, ..}
RLC t -> let _carry = cpu ^. registers . t .&. 128 == 128 RLC t -> let _carry = (cpu ^. registers . t) `testBit` 8
newValue = cpu ^. registers . t `rotateL` 1 in newValue = cpu ^. registers . t `rotateL` 1 in
cpu & registers . t .~ newValue cpu & registers . t .~ newValue
& registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RLCHL -> let target = cpu ^. registers . hl RLCHL -> let target = cpu ^. registers . hl
value = fetch cpu target value = fetch cpu target
_carry = value .&. 128 == 128 _carry = (value) `testBit` 8
newValue = value `rotateL` 1 in newValue = value `rotateL` 1 in
write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RRC t -> let _carry = cpu ^. registers . t .&. 1 == 1 RRC t -> let _carry = (cpu ^. registers . t) `testBit` 1
newValue = cpu ^. registers . t `rotateR` 1 in newValue = cpu ^. registers . t `rotateR` 1 in
cpu & registers . t .~ newValue cpu & registers . t .~ newValue
& registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RRCHL -> let target = cpu ^. registers . hl RRCHL -> let target = cpu ^. registers . hl
value = fetch cpu target value = fetch cpu target
_carry = value .&. 1 == 1 _carry = value `testBit` 1
newValue = value `rotateR` 1 in newValue = value `rotateR` 1 in
write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RL t -> let _carry = cpu ^. registers . t .&. 128 == 128 RL t -> let _carry = (cpu ^. registers . t) `testBit` 8
_c = cpu ^. registers . flags . carry _c = cpu ^. registers . flags . carry
newValue = (if _c then (+1) else id) $ cpu ^. registers . t `shiftL` 1 in newValue = (if _c then (+1) else id) $ cpu ^. registers . t .<<. 1 in
cpu & registers . t .~ newValue cpu & registers . t .~ newValue
& registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RLHL -> let target = cpu ^. registers . hl RLHL -> let target = cpu ^. registers . hl
value = fetch cpu target value = fetch cpu target
_c = cpu ^. registers . flags . carry _c = cpu ^. registers . flags . carry
_carry = value .&. 128 == 128 _carry = value `testBit` 8
newValue = (if _c then (+1) else id) $ value `shiftL` 1 in newValue = (if _c then (+1) else id) $ value .<<. 1 in
write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RR t -> let _carry = cpu ^. registers . t .&. 1 == 1 RR t -> let _carry = (cpu ^. registers . t) `testBit` 1
_c = cpu ^. registers . flags . carry _c = cpu ^. registers . flags . carry
newValue = (if _c then (+128) else id) $ cpu ^. registers . t `shiftR` 1 in newValue = (if _c then (+128) else id) $ cpu ^. registers . t .>>. 1 in
cpu & registers . t .~ newValue cpu & registers . t .~ newValue
& registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
RRHL -> let target = cpu ^. registers . hl RRHL -> let target = cpu ^. registers . hl
value = fetch cpu target value = fetch cpu target
_c = cpu ^. registers . flags . carry _c = cpu ^. registers . flags . carry
_carry = value .&. 1 == 1 _carry = value `testBit` 1
newValue = (if _c then (+128) else id) $ value `shiftR` 1 in newValue = (if _c then (+128) else id) $ value .>>. 1 in
write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..} write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
SLA t -> let _carry = (cpu ^. registers . t) `testBit` 8
newValue = cpu ^. registers . t .<<. 1
in
cpu & registers . t .~ newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
SLAHL -> let target = cpu ^. registers . hl
value = fetch cpu target
newValue = value .<<. 1
_carry = value `testBit` 8
in
write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
SRA t -> let _carry = (cpu ^. registers . t) `testBit` 1
value = cpu ^. registers . t
newValue = let temp = value .>>. 1 in if value `testBit` 8 then temp `setBit` 8 else temp
in
cpu & registers . t .~ newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
SRAHL -> let target = cpu ^. registers . hl
value = fetch cpu target
newValue = let temp = value .>>. 1 in if value `testBit` 8 then temp `setBit` 8 else temp
_carry = value `testBit` 8
in
write cpu target newValue & registers . flags .~ FlagRegister {_zero = newValue == 0, _negative = False, _halfCarry = False, ..}
where where
add :: Word8 -> Word8 -> Bool -> (Word8, FlagRegister) add :: Word8 -> Word8 -> Bool -> (Word8, FlagRegister)
add o n _c = let new = o + n + if _c then 1 else 0 add o n _c = let new = o + n + if _c then 1 else 0