[pypy-svn] r53413 - in pypy/branch/gameboy-emulator/pypy/lang/gameboy: . test
cami at codespeak.net
cami at codespeak.net
Sun Apr 6 00:48:07 CEST 2008
Author: cami
Date: Sun Apr 6 00:48:06 2008
New Revision: 53413
Modified:
pypy/branch/gameboy-emulator/pypy/lang/gameboy/cpu.py
pypy/branch/gameboy-emulator/pypy/lang/gameboy/interrupt.py
pypy/branch/gameboy-emulator/pypy/lang/gameboy/serial.py
pypy/branch/gameboy-emulator/pypy/lang/gameboy/sound.py
pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_interrupt.py
pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_timer.py
pypy/branch/gameboy-emulator/pypy/lang/gameboy/timer.py
Log:
extended timer tests
cleaned up interrupt
adapted interrupt tests
introduced interrupt flag objects
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/cpu.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/cpu.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/cpu.py Sun Apr 6 00:48:06 2008
@@ -51,8 +51,7 @@
self.cpu.cycles += 1
else:
self.setHi(hi, useCycles)
- self.setLo(lo, useCycles)
-
+ self.setLo(lo, useCycles)
def reset(self):
self.set(self.resetValue, None, False)
@@ -270,8 +269,6 @@
if self.halted:
val += 0x80
return val
-
-
def isZ(self):
""" zero flag"""
@@ -306,31 +303,23 @@
def isNotN(self):
return not self.isN()
-
-
+
# Flags ............................................
-
def setROM(self, banks):
- self.rom = banks
-
+ self.rom = banks
def emulate(self, ticks):
self.cycles += ticks
- self.interrupt()
+ self.handlePendingInterrupt()
while (self.cycles > 0):
self.execute()
# Interrupts
- def interrupt(self, address=None):
- if address != None:
- self.ime = False
- self.call(address)
- return
+ def handlePendingInterrupt(self):
if self.halted:
if (self.interrupt.isPending()):
self.halted = False
- # Zerd no Densetsu
self.cycles -= 4
elif (self.cycles > 0):
self.cycles = 0
@@ -338,21 +327,10 @@
self.lowerPendingInterrupt()
def lowerPendingInterrupt(self):
- if (self.interrupt.isPending(constants.VBLANK)):
- self.interrupt(0x40)
- self.interrupt.lower(constants.VBLANK)
- elif (self.interrupt.isPending(constants.LCD)):
- self.interrupt(0x48)
- self.interrupt.lower(constants.LCD)
- elif (self.interrupt.isPending(constants.TIMER)):
- self.interrupt(0x50)
- self.interrupt.lower(constants.TIMER)
- elif (self.interrupt.isPending(constants.SERIAL)):
- self.interrupt(0x58)
- self.interrupt.lower(constants.SERIAL)
- elif (self.interrupt.isPending(constants.JOYPAD)):
- self.interrupt(0x60)
- self.interrupt.lower(constants.JOYPAD)
+ for flag in self.interrupt.interruptFlags:
+ if flag.isPending():
+ self.call(flag.callCode, disableIme=True)
+ flag.setPending(False)
# Execution
def fetchExecute(self):
@@ -383,6 +361,11 @@
data = self.memory.read(self.pc.get())
self.pc.inc() # 2 cycles
return data
+
+ def fetchDoubleAddress(self):
+ lo = self.fetch() # 1 cycle
+ hi = self.fetch() # 1 cycle
+ return (hi << 8) + lo
def fetchDoubleRegister(self, register):
self.popDoubleRegister(CPU.fetch, register)
@@ -415,7 +398,9 @@
self.cycles += 1
# 4 cycles
- def call(self, address):
+ def call(self, address, disableIME=False):
+ if disableIME:
+ self.ime = False
self.push(self.pc.getHi()) # 2 cycles
self.push(self.pc.getLo()) # 2 cycles
self.pc.set(address) # 1 cycle
@@ -457,24 +442,23 @@
s = self.a.get() + getter();
if self.f.cFlag:
s +=1
- self.f.reset()
- self.f.zeroFlagAdd(s)
+ self.carryFlagFinish(getter, 0x10)
if s >= 0x100:
self.f.cFlag= True
- if ((s ^ self.a.get() ^ getter()) & 0x10) != 0:
- self.f.hFlag= True
- self.a.set(s & 0xFF) # 1 cycle
# 1 cycle
def subtractWithCarry(self, getter, setter=None):
s = self.a.get() - getter();
if self.f.cFlag:
s -= 1
- self.f.reset()
- self.f.nFlag = True
- self.f.zeroFlagAdd(s)
+ self.carryFlagFinish(getter, 0x10)
if (s & 0xFF00) != 0:
self.f.cFlag = True
+ self.f.nFlag = True
+
+ def carryFlagFinish(self, getter):
+ self.f.reset()
+ self.f.zeroFlagAdd(s)
if ((s ^ self.a.get() ^ getter()) & 0x10) != 0:
self.f.hFlag = True
self.a.set(s & 0xFF) # 1 cycle
@@ -547,7 +531,6 @@
self.f.cFlagAdd(self.a.get(), 0x80, reset=True)
self.a.set(((self.a.get() & 0x7F) << 1) + ((self.a.get() & 0x80) >> 7))
-
# 1 cycle
def rotateLeft(self, getter, setter):
s = ((getter() & 0x7F) << 1)
@@ -634,9 +617,7 @@
# LD A,(nnnn), 4 cycles
def storeFetchedMemoryInA(self):
- lo = self.fetch() # 1 cycle
- hi = self.fetch() # 1 cycle
- self.a.set(self.read(hi, lo)) # 1+1 cycles
+ self.a.set(self.read(self.fetchDoubleAddress())) # 1+1 + 2 cycles
# 2 cycles
def writeAAtBCAddress(self):
@@ -657,18 +638,14 @@
# LD (nnnn),SP 5 cycles
def load_mem_SP(self):
- lo = self.fetch() # 1 cycle
- hi = self.fetch() # 1 cycle
- address = (hi << 8) + lo
+ address = self.fetchDoubleAddress() # 2 cycles
self.write(address, self.sp.getLo()) # 2 cycles
self.write((address + 1), self.sp.getHi()) # 2 cycles
self.cycles += 1
# LD (nnnn),A 4 cycles
- def storeAatFetchedAddress(self):
- lo = self.fetch() # 1 cycle
- hi = self.fetch() # 1 cycle
- self.write((hi << 8) + lo, self.a.get()) # 2 cycles
+ def storeAAtFetchedAddress(self):
+ self.write(self.fetchDoubleAddress(), self.a.get()) # 2 cycles
# LDH A,(nn) 3 cycles
def storeMemoryAtExpandedFetchAddressInA(self):
@@ -715,7 +692,7 @@
self.sp.set(self.hl.get()) # 1 cycle
self.cycles -= 1
- #
+ # CPA
def complementA(self):
self.a.set(self.a.get() ^ 0xFF)
self.f.nFlag = True
@@ -793,9 +770,8 @@
# JP nnnn, 4 cycles
def unconditionalJump(self):
- lo = self.fetch() # 1 cycle
- hi = self.fetch() # 1 cycle
- self.pc.set(hi,lo) # 2 cycles
+ self.pc.set(self.fetchDoubleAddress()) # 1+2 cycles
+ self.cycles -= 1
# JP cc,nnnn 3,4 cycles
def conditionalJump(self, cc):
@@ -818,9 +794,7 @@
# CALL nnnn, 6 cycles
def unconditionalCall(self):
- lo = self.fetch() # 1 cycle
- hi = self.fetch() # 1 cycle
- self.call((hi << 8) + lo) # 4 cycles
+ self.call(self.fetchDoubleAddress()) # 4+2 cycles
# CALL cc,nnnn, 3,6 cycles
def conditionalCall(self, getter):
@@ -847,12 +821,8 @@
# RETI 4 cycles
def returnFormInterrupt(self):
self.ret() # 4 cycles
- # enable interrupts
- self.ime = True
- # execute next instruction
- self.execute(self.fetch())
- # check pending interrupts
- self.interrupt()
+ self.enableInterrupts()
+ self.cycles += 1
# RST nn 4 cycles
def restart(self, nn):
@@ -860,19 +830,15 @@
# DI/EI 1 cycle
def disableInterrupts(self):
- # disable interrupts
self.ime = False
self.cycles -= 1;
# 1 cycle
def enableInterrupts(self):
- # enable interrupts
self.ime = True
self.cycles -= 1
- # execute next instruction
self.execute(self.fetch())
- # check pending interrupts
- self.interrupt()
+ self.handlePendingInterrupt()
# HALT/STOP
def halt(self):
@@ -880,8 +846,7 @@
# emulate bug when interrupts are pending
if (not self.ime and self.interrupt.isPending()):
self.execute(self.memory.read(self.pc.get()))
- # check pending interrupts
- self.interrupt()
+ self.handlePendingInterrupt()
# 0 cycles
def stop(self):
@@ -957,7 +922,7 @@
return result
# OPCODE TABLES ---------------------------------------------------------------
-
+
FIRST_ORDER_OP_CODES = [
(0x00, CPU.nop),
(0x08, CPU.load_mem_SP),
@@ -983,7 +948,7 @@
(0xF3, CPU.disableInterrupts),
(0xFB, CPU.enableInterrupts),
(0xE2, CPU.writeAAtExpandedCAddress),
- (0xEA, CPU.storeAatFetchedAddress),
+ (0xEA, CPU.storeAAtFetchedAddress),
(0xF2, CPU.storeExpandedCinA),
(0xFA, CPU.storeFetchedMemoryInA),
(0xC3, CPU.unconditionalJump),
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/interrupt.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/interrupt.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/interrupt.py Sun Apr 6 00:48:06 2008
@@ -1,5 +1,24 @@
from pypy.lang.gameboy import constants
+
+class InterruptFlag(object):
+
+ def __init__(self, _reset, mask, callCode):
+ self._reset = _reset
+ self.mask = mask
+ self.callCode = callCode
+ self.reset()
+
+ def reset(self):
+ self._isPending = self._reset
+
+ def isPending(self):
+ return self._isPending
+
+ def setPending(self, _isPending):
+ self._isPending = _isPending
+
+
class Interrupt(object):
"""
PyBoy GameBoy (TM) Emulator
@@ -8,23 +27,50 @@
"""
def __init__(self):
+ self.createInterruptFlags()
+ self.createFlagList()
+ self.createFlagMaskMapping()
self.reset()
-
+
+ def createInterruptFlags(self):
+ self.vBlank = InterruptFlag(True, constants.VBLANK, 0x40)
+ self.lcd = InterruptFlag(False, constants.LCD, 0x48)
+ self.timer = InterruptFlag(False, constants.TIMER, 0x50)
+ self.serial = InterruptFlag(False, constants.SERIAL, 0x58)
+ self.joypad = InterruptFlag(False, constants.JOYPAD, 0x60)
+
+ def createFlagList(self):
+ self.interruptFlags = [
+ self.vBlank, self.lcd,
+ self.timer, self.serial,
+ self.joypad
+ ]
+
+ def createFlagMaskMapping(self):
+ self.maskMapping = {}
+ for flag in self.interruptFlags:
+ self.maskMapping[flag.mask] = flag
+
def reset(self):
- self.enable = 0
- self.flag = constants.VBLANK
+ self.enable = False
+ for flag in self.interruptFlags:
+ flag.reset()
def isPending(self, mask=None):
+ if not self.enable:
+ return False
if mask==None:
- return (self.enable & self.flag) != 0
+ return self.vBlank.isPending()
+ elif self.vBlank.isPending():
+ return self.maskMapping[mask].isPending()
else:
- return (self.enable & self.flag & mask) != 0
+ return False
def raiseInterrupt(self, mask):
- self.flag |= mask
+ self.maskMapping[mask].setPending(True)
def lower(self, mask):
- self.flag &= ~mask
+ self.maskMapping[mask].setPending(False)
def write(self, address, data):
if address == constants.IE:
@@ -40,13 +86,21 @@
return 0xFF
def getInterruptEnable(self):
- return self.enable
-
- def getInterruptFlag(self):
- return 0xE0 | self.flag
+ return int(self.enable)
def setInterruptEnable(self, data):
- self.enable = data
+ self.enable = bool(data)
+
+ def getInterruptFlag(self):
+ flag = 0x00
+ for interruptFlag in self.interruptFlags:
+ if interruptFlag.isPending():
+ flag |= interruptFlag.mask
+ return 0xE0 | flag
def setInterruptFlag(self, data):
- self.flag = data
+ for flag in self.interruptFlags:
+ if (data & flag.mask) != 0:
+ flag.setPending(True)
+ else:
+ flag.setPending(False)
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/serial.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/serial.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/serial.py Sun Apr 6 00:48:06 2008
@@ -19,14 +19,14 @@
return self.cycles
def emulate(self, ticks):
- if ((self.sc & 0x81) == 0x81):
- self.cycles -= ticks
- if (self.cycles <= 0):
- self.sb = 0xFF
- self.sc &= 0x7F
- self.cycles = constants.SERIAL_IDLE_CLOCK
- self.interrupt.raiseInterrupt(constants.SERIAL)
-
+ if ((self.sc & 0x81) != 0x81):
+ return
+ self.cycles -= ticks
+ if (self.cycles <= 0):
+ self.sb = 0xFF
+ self.sc &= 0x7F
+ self.cycles = constants.SERIAL_IDLE_CLOCK
+ self.interrupt.raiseInterrupt(constants.SERIAL)
def setSerialData(self, data):
self.sb = data
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/sound.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/sound.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/sound.py Sun Apr 6 00:48:06 2008
@@ -82,19 +82,15 @@
self.generateNoiseTables()
self.reset()
-
def start(self):
self.driver.start()
-
def stop(self):
self.driver.stop()
-
def cycles(self):
return self.cycles
-
def emulate(self, ticks):
self.cycles -= ticks
while (self.cycles <= 0):
@@ -107,8 +103,6 @@
self.frames %= constants.SOUND_CLOCK
self.cycles += constants.GAMEBOY_CLOCK / constants.SOUND_CLOCK
-
-
def reset(self):
self.cycles = constants.GAMEBOY_CLOCK / constants.SOUND_CLOCK
@@ -197,10 +191,8 @@
elif (address >= constants.AUD3WAVERAM and address <= constants.AUD3WAVERAM + 0x3F):
return self.getAudio3WavePattern(address)
-
return 0xFF
-
def write(self, address, data):
if address==constants.NR10:
self.setAudio1Sweep(data)
@@ -252,7 +244,6 @@
elif (address >= constants.AUD3WAVERAM and address <= constants.AUD3WAVERAM + 0x3F):
self.setAudio3WavePattern(address, data)
-
def updateAudio(self):
if ((self.nr52 & 0x80) == 0):
return
@@ -265,10 +256,8 @@
if ((self.nr52 & 0x08) != 0):
self.updateAudio4()
-
def mixAudio(self,buffer, length):
- for index in range(0, length):
- buffer[index] = 0
+ buffer = [0]*length
if ((self.nr52 & 0x80) == 0):
return
if ((self.nr52 & 0x01) != 0):
@@ -280,38 +269,30 @@
if ((self.nr52 & 0x08) != 0):
self.mixAudio4(buffer, length)
-
# Audio Channel 1
def getAudio1Sweep(self):
return self.nr10
-
def getAudio1Length(self):
return self.nr11
-
def getAudio1Envelope(self):
return self.nr12
-
def getAudio1Frequency(self):
return self.nr13
-
def getAudio1Playback(self):
return self.nr14
-
def setAudio1Sweep(self, data):
self.nr10 = data
self.audio1SweepLength = (constants.SOUND_CLOCK / 128) * ((self.nr10 >> 4) & 0x07)
-
def setAudio1Length(self, data):
self.nr11 = data
self.audio1Length = (constants.SOUND_CLOCK / 256) * (64 - (self.nr11 & 0x3F))
-
def setAudio1Envelope(self, data):
self.nr12 = data
if ((self.nr14 & 0x40) != 0):
@@ -323,12 +304,10 @@
else:
self.audio1Volume = (self.audio1Volume + 2) & 0x0F
-
def setAudio1Frequency(self, data):
self.nr13 = data
self.audio1Frequency = self.frequencyTable[self.nr13 + ((self.nr14 & 0x07) << 8)]
-
def setAudio1Playback(self, data):
self.nr14 = data
self.audio1Frequency = self.frequencyTable[self.nr13
@@ -340,7 +319,6 @@
self.audio1SweepLength = (constants.SOUND_CLOCK / 128) * ((self.nr10 >> 4) & 0x07)
self.audio1Volume = self.nr12 >> 4
self.audio1EnvelopeLength = (constants.SOUND_CLOCK / 64) * (self.nr12 & 0x07)
-
def updateAudio1(self):
if ((self.nr14 & 0x40) != 0 and self.audio1Length > 0):
@@ -375,7 +353,6 @@
self.nr52 &= ~0x01
self.audio1SweepLength += (constants.SOUND_CLOCK / 128) * ((self.nr10 >> 4) & 0x07)
-
def mixAudio1(self, buffer, length):
wavePattern = 0x18
@@ -399,29 +376,23 @@
if ((self.nr51 & 0x01) != 0):
buffer[index + 1] += self.audio1Volume
-
# Audio Channel 2
def getAudio2Length(self):
return self.nr21
-
def getAudio2Envelope(self):
return self.nr22
-
def getAudio2Frequency(self):
return self.nr23
-
def getAudio2Playback(self):
return self.nr24
-
def setAudio2Length(self, data):
self.nr21 = data
self.audio2Length = (constants.SOUND_CLOCK / 256) * (64 - (self.nr21 & 0x3F))
-
def setAudio2Envelope(self, data):
self.nr22 = data
if ((self.nr24 & 0x40) == 0):
@@ -432,13 +403,11 @@
else:
self.audio2Volume = (self.audio2Volume + 2) & 0x0F
-
def setAudio2Frequency(self, data):
self.nr23 = data
self.audio2Frequency = self.frequencyTable[self.nr23\
+ ((self.nr24 & 0x07) << 8)]
-
def setAudio2Playback(self, data):
self.nr24 = data
self.audio2Frequency = self.frequencyTable[self.nr23\
@@ -450,8 +419,6 @@
self.audio2Volume = self.nr22 >> 4
self.audio2EnvelopeLength = (constants.SOUND_CLOCK / 64) * (self.nr22 & 0x07)
-
-
def updateAudio2(self):
if ((self.nr24 & 0x40) != 0 and self.audio2Length > 0):
self.audio2Length-=1
@@ -468,7 +435,6 @@
self.audio2Volume-=1
self.audio2EnvelopeLength += (constants.SOUND_CLOCK / 64) * (self.nr22 & 0x07)
-
def mixAudio2(self, buffer, length):
wavePattern = 0x18
if (self.nr21 & 0xC0) == 0x00:
@@ -491,48 +457,38 @@
if ((self.nr51 & 0x02) != 0):
buffer[index + 1] += self.audio2Volume
-
# Audio Channel 3
def getAudio3Enable(self):
return self.nr30
-
def getAudio3Length(self):
return self.nr31
-
def getAudio3Level(self):
return self.nr32
-
def getAudio4Frequency(self):
return self.nr33
-
def getAudio3Playback(self):
return self.nr34
-
def setAudio3Enable(self, data):
self.nr30 = data & 0x80
if ((self.nr30 & 0x80) == 0):
self.nr52 &= ~0x04
-
def setAudio3Length(self, data):
self.nr31 = data
self.audio3Length = (constants.SOUND_CLOCK / 256) * (256 - self.nr31)
-
def setAudio3Level(self, data):
self.nr32 = data
-
def setAudio3Frequency(self, data):
self.nr33 = data
self.audio3Frequency = self.frequencyTable[((self.nr34 & 0x07) << 8) + self.nr33] >> 1
-
def setAudio3Playback(self, data):
self.nr34 = data
self.audio3Frequency = self.frequencyTable[((self.nr34 & 0x07) << 8) + self.nr33] >> 1
@@ -541,24 +497,18 @@
if ((self.nr34 & 0x40) != 0 and self.audio3Length == 0):
self.audio3Length = (constants.SOUND_CLOCK / 256) * (256 - self.nr31)
-
-
def setAudio3WavePattern(self, address, data):
- #TODO convert to byte
self.audio3WavePattern[address & 0x0F] = data
-
def getAudio3WavePattern(self, address):
return self.audio3WavePattern[address & 0x0F] & 0xFF
-
def updateAudio3(self):
if ((self.nr34 & 0x40) != 0 and self.audio3Length > 0):
self.audio3Length-=1
if (self.audio3Length <= 0):
self.nr52 &= ~0x04
-
def mixAudio3(self, buffer, length):
wavePattern = 2
if (self.nr32 & 0x60) == 0x00:
@@ -583,29 +533,23 @@
if ((self.nr51 & 0x04) != 0):
buffer[index + 1] += sample
-
# Audio Channel 4
def getAudio4Length(self):
return self.nr41
-
def getAudio4Envelope(self):
return self.nr42
-
def getAudio4Polynomial(self):
return self.nr43
-
def getAudio4Playback(self):
return self.nr44
-
def setAudio4Length(self, data):
self.nr41 = data
self.audio4Length = (constants.SOUND_CLOCK / 256) * (64 - (self.nr41 & 0x3F))
-
def setAudio4Envelope(self, data):
self.nr42 = data
if ((self.nr44 & 0x40) == 0):
@@ -616,7 +560,6 @@
else:
self.audio4Volume = (self.audio4Volume + 2) & 0x0F
-
def setAudio4Polynomial(self, data):
self.nr43 = data
if ((self.nr43 >> 4) <= 12):
@@ -624,7 +567,6 @@
else:
self.audio4Frequency = 0
-
def setAudio4Playback(self, data):
self.nr44 = data
if ((self.nr44 & 0x80) != 0):
@@ -634,8 +576,6 @@
self.audio4Volume = self.nr42 >> 4
self.audio4EnvelopeLength = (constants.SOUND_CLOCK / 64) * (self.nr42 & 0x07)
self.audio4Index = 0
-
-
def updateAudio4(self):
if ((self.nr44 & 0x40) != 0 and self.audio4Length > 0):
@@ -651,7 +591,6 @@
elif (self.audio4Volume > 0):
self.audio4Volume-=1
self.audio4EnvelopeLength += (constants.SOUND_CLOCK / 64) * (self.nr42 & 0x07)
-
def mixAudio4(self, buffer, length):
for index in range(0, length, 2):
@@ -676,28 +615,22 @@
if ((self.nr51 & 0x08) != 0):
buffer[index + 1] += self.audio4Volume
-
# Output Control
def getOutputLevel(self):
return self.nr50
-
def getOutputTerminal(self):
return self.nr51
-
def getOutputEnable(self):
return self.nr52
-
def setOutputLevel(self, data):
self.nr50 = data
-
def setOutputTerminal(self, data):
self.nr51 = data
-
def setOutputEnable(self, data):
self.nr52 = (self.nr52 & 0x7F) | (data & 0x80)
if ((self.nr52 & 0x80) == 0x00):
@@ -741,7 +674,6 @@
self.noiseStep15Table[index >> 5] |= (polynomial & 1) << (index & 31)
-
# SOUND DRIVER -----------------------------------------------------------------
class SoundDriver(object):
@@ -749,7 +681,6 @@
def __init__(self):
self.enabled = True
-
def isEnabled(self):
return self.enabled
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_interrupt.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_interrupt.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_interrupt.py Sun Apr 6 00:48:06 2008
@@ -7,15 +7,15 @@
-def test_rest():
+def test_reset():
interrupt = get_interrupt()
assert interrupt.enable == 0
- assert interrupt.flag == constants.VBLANK
+ assert interrupt.getInterruptFlag() == 0xE0 | constants.VBLANK
interrupt.enable = 1
interrupt.flag = ~constants.VBLANK
interrupt.reset()
assert interrupt.enable == 0
- assert interrupt.flag == constants.VBLANK
+ assert interrupt.getInterruptFlag() == 0xE0 | constants.VBLANK
def test_is_pending():
@@ -23,34 +23,41 @@
assert interrupt.isPending() == False
assert interrupt.isPending(0x00) == False
interrupt.setInterruptEnable(True)
- assert interrupt.isPending() == True
+ assert interrupt.isPending()
-def test_raise_interrupt():
+def test_is_pending_common_masks():
interrupt = get_interrupt()
- value = 0x12
- mask = 0xAA
- interrupt.flag = value
- assert interrupt.flag == value
- interrupt.raiseInterrupt(mask)
- assert interrupt.flag == value|mask
+ for flag in interrupt.interruptFlags:
+ interrupt.reset()
+ interrupt.enable = True
+ assert interrupt.vBlank.isPending()
+ flag.setPending(True)
+ assert interrupt.isPending(flag.mask)
+
-def test_lower():
+def test_raise_lower_interrupt():
interrupt = get_interrupt()
- value = 0x12
- mask = 0xAA
- interrupt.flag = value
- assert interrupt.flag == value
- interrupt.lower(mask)
- assert interrupt.flag == value & (~mask)
+ masks= [constants.LCD, constants.TIMER,
+ constants.JOYPAD, constants.SERIAL]
+ interrupt.setInterruptEnable(True)
+ interrupt.vBlank.setPending(True)
+ for mask in masks:
+ interrupt.raiseInterrupt(mask)
+ assert interrupt.maskMapping[mask].isPending() == True
+ assert interrupt.isPending(mask) == True
+ interrupt.lower(mask)
+ assert interrupt.isPending(mask) == False
def test_read_write():
interrupt = get_interrupt()
- value = 0x12
+ value = 1
interrupt.write(constants.IE, value)
assert interrupt.enable == value
assert interrupt.read(constants.IE) == value
- value+=1
+
+ interrupt.reset()
+ value = constants.LCD
interrupt.write(constants.IF, value)
- assert interrupt.flag == value
+ assert interrupt.getInterruptFlag() == 0xE0 | value
assert interrupt.read(constants.IF) == 0xE0 | value
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_timer.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_timer.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/test/test_timer.py Sun Apr 6 00:48:06 2008
@@ -5,11 +5,7 @@
def get_timer():
- return Timer(get_timer_interrupt())
-
-def get_timer_interrupt():
- return Interrupt()
-
+ return Timer(Interrupt())
# ------------------------------------------------------------------------------
@@ -47,26 +43,93 @@
timer.reset()
-
-
-def test_setTimerControl():
+def test_getTimerControl():
timer = get_timer()
value = 0x12
timer.write(constants.TAC, value)
assert timer.getTimerControl() == 0xF8 | value
assert timer.read(constants.TAC) == 0xF8 |value
+
+def test_setTimerControl():
+ timer = get_timer()
+ value = 0x12
+ timer.setTimerControl(value)
+ assert timer.tac == value
+ assert timer.timerCycles == constants.TIMER_CLOCK[value & 0x03]
+ assert timer.timerClock == constants.TIMER_CLOCK[value & 0x03]
+ timer.reset()
+ timer.tac = value+1
+ timer.timerClock = 0
+ timer.timerCycles = 0
+ timer.setTimerControl(value+1)
+ assert timer.tac == value+1
+ assert timer.timerClock == 0
+ assert timer.timerClock == 0
+def test_read_write_Divider():
+ timer = get_timer()
+ value = 0x12
+ timer.div = value
+ assert timer.getDivider() == timer.div
+ # divider resets on write
+ timer.setDivider(value)
+ assert timer.getDivider() == 0
def test_cycles():
- py.test.skip("not yet implemented")
timer = get_timer()
+ value = 10
+ timer.dividerCycles = value
+ assert timer.cycles() == timer.dividerCycles
+ timer.tac = 0x04
+ timer.timerCycles = value-1
+ timer.timerCycles = value
+ assert timer.cycles() == timer.timerCycles
-
-def test_emulateDivider():
- py.test.skip("not yet implemented")
+def test_emulateDivider_normal():
timer = get_timer()
+ value = 2
+ timer.timerCycles = 0
+ timer.emulateTimer(value)
-
-def test_emulateTimer():
- py.test.skip("not yet implemented")
- timer = get_timer()
\ No newline at end of file
+def test_test_emulateDivider_zero():
+ timer = get_timer()
+ value = 2
+ timer.timerCycles = value
+ timer.emulateTimer(value)
+ assert timer.timerCycles == value
+
+def test_emulateTimer_tac_return():
+ timer = get_timer()
+ timer.tac = 0
+ timer.timerCycles = -10
+ cycles = timer.timerCycles
+ timer.emulateTimer(10)
+ assert timer.timerCycles == cycles
+
+def test_emulateTimer_timer_cycles_return():
+ timer = get_timer()
+ timer.tac = 0x04
+ value = 10
+ timer.timerCycles = value+1
+ cycles = timer.timerCycles
+ timer.emulateTimer(value)
+ assert timer.timerCycles == 1
+
+ timer = get_timer()
+ timer.tac = 0x04
+
+
+def test_emulateTimer_interrupt():
+ timer = get_timer()
+ ticks = 0
+ timer.tac = 0x04
+ timer.tima = -1
+ # raise an interupt as we pass 0
+ assert timer.interrupt.isPending(constants.TIMER) == False
+ timer.timerCycles = -timer.timerClock+1
+ timer.emulateTimer(ticks)
+ assert timer.timerCycles == 1
+ assert timer.tima == timer.tma
+ assert timer.interrupt.timer.isPending()
+
+
\ No newline at end of file
Modified: pypy/branch/gameboy-emulator/pypy/lang/gameboy/timer.py
==============================================================================
--- pypy/branch/gameboy-emulator/pypy/lang/gameboy/timer.py (original)
+++ pypy/branch/gameboy-emulator/pypy/lang/gameboy/timer.py Sun Apr 6 00:48:06 2008
@@ -4,6 +4,7 @@
Timer and Divider
"""
from pypy.lang.gameboy import constants
+from math import ceil
class Timer(object):
@@ -15,7 +16,7 @@
self.div = 0
self.dividerCycles = constants.DIV_CLOCK
self.tima = self.tma = self.tac = 0x00
- self.timerCycles = self.timerClock = constants.TIMER_CLOCK[self.tac & 0x03]
+ self.timerCycles = self.timerClock = constants.TIMER_CLOCK[0]
def write(self, address, data):
if address==constants.DIV:
@@ -61,7 +62,7 @@
def setTimerControl(self, data):
if ((self.tac & 0x03) != (data & 0x03)):
- self.timerCycles = self.timerClock = constants.TIMER_CLOCK[data & 0x03]
+ self.timerClock = self.timerCycles = constants.TIMER_CLOCK[data & 0x03]
self.tac = data
def cycles(self):
@@ -75,20 +76,30 @@
def emulateDivider(self, ticks):
self.dividerCycles -= ticks
- while (self.dividerCycles <= 0):
- self.div = (self.div + 1) & 0xFF
- self.dividerCycles += constants.DIV_CLOCK
-
+ if self.dividerCycles > 0:
+ return
+ count = int(ceil(-1.0*self.dividerCycles / constants.DIV_CLOCK))
+ self.div = (self.div + count) & 0xFF
+ self.dividerCycles += constants.DIV_CLOCK*count
+
def emulateTimer(self, ticks):
- if ((self.tac & 0x04) != 0):
- self.timerCycles -= ticks
- while (self.timerCycles <= 0):
- self.tima = (self.tima + 1) & 0xFF
- self.timerCycles += self.timerClock
- if (self.tima == 0x00):
- self.tima = self.tma
- self.interrupt.raiseInterrupt(constants.TIMER)
-
+ if (self.tac & 0x04) == 0:
+ return
+ self.timerCycles -= ticks
+ if self.timerCycles > 0:
+ return
+ count = int(ceil(-1.0*self.timerCycles / self.timerClock))
+ self.timaZeroPassCheck(count)
+ self.tima = (self.tima + count) & 0xFF
+ self.timerCycles += self.timerClock * count
+
+ def timaZeroPassCheck(self, count):
+ if (self.tima < 0) and (self.tima + count >= 0):
+ self.tima = self.tma - count
+ print "raising"
+ self.interrupt.raiseInterrupt(constants.TIMER)
+ print self.interrupt.timer.isPending(), self.interrupt.isPending(constants.TIMER)
+
# CLOCK DRIVER -----------------------------------------------------------------
class ClockDriver(object):
More information about the Pypy-commit
mailing list