Restructured the RIOT handling of reads from INTIM and TIMINT. Also removed

all aspects of the M6532 chip that a real 2600 console doesn't use.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2549 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2012-09-11 21:16:21 +00:00
parent d5269bce26
commit b59c95062c
3 changed files with 58 additions and 45 deletions

View File

@ -20,6 +20,7 @@
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include "Debugger.hxx"
#include "Console.hxx" #include "Console.hxx"
#include "Settings.hxx" #include "Settings.hxx"
#include "Switches.hxx" #include "Switches.hxx"
@ -27,6 +28,8 @@
#include "M6532.hxx" #include "M6532.hxx"
#define TIMER_BIT 0x80
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
M6532::M6532(const Console& console, const Settings& settings) M6532::M6532(const Console& console, const Settings& settings)
: myConsole(console), : myConsole(console),
@ -54,14 +57,15 @@ void M6532::reset()
myTimer = (0xff - (mySystem->randGenerator().next() % 0xfe)) << 10; myTimer = (0xff - (mySystem->randGenerator().next() % 0xfe)) << 10;
myIntervalShift = 10; myIntervalShift = 10;
myCyclesWhenTimerSet = 0; myCyclesWhenTimerSet = 0;
myInterruptEnabled = false;
myInterruptTriggered = false;
// Zero the I/O registers // Zero the I/O registers
myDDRA = myDDRB = myOutA = myOutB = 0x00; myDDRA = myDDRB = myOutA = myOutB = 0x00;
// Zero the timer registers // Zero the timer registers
myOutTimer[0] = myOutTimer[1] = myOutTimer[2] = myOutTimer[3] = 0x00; myOutTimer[0] = myOutTimer[1] = myOutTimer[2] = myOutTimer[3] = 0x00;
// Zero the interrupt flag register
myInterruptFlag = 0x00;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -115,6 +119,8 @@ uInt8 M6532::peek(uInt16 addr)
return myRAM[addr & 0x007f]; return myRAM[addr & 0x007f];
} }
//cerr << Debugger::debugger().valueToString(addr&0xff, kBASE_2_8) << endl;
switch(addr & 0x07) switch(addr & 0x07)
{ {
case 0x00: // SWCHA - Port A I/O Register (Joystick) case 0x00: // SWCHA - Port A I/O Register (Joystick)
@ -147,38 +153,16 @@ uInt8 M6532::peek(uInt16 addr)
case 0x04: // Timer Output case 0x04: // Timer Output
case 0x06: case 0x06:
{ {
myInterruptTriggered = false; // Update timer state and return the resulting clock
Int32 timer = timerClocks(); return updateTimer();
// See if the timer has expired yet?
// Note that this constant comes from z26, and corresponds to
// 256 intervals of T1024T (ie, the maximum that the timer should hold)
// I'm not sure why this is required, but quite a few PAL ROMs fail
// if we just check >= 0.
if(!(timer & 0x40000))
{
return (timer >> myIntervalShift) & 0xff;
}
else
{
if(timer != -1)
myInterruptTriggered = true;
// According to the M6532 documentation, the timer continues to count
// down to -255 timer clocks after wraparound. However, it isn't
// entirely clear what happens *after* if reaches -255.
// For now, we'll let it continuously wrap around.
return timer & 0xff;
}
} }
case 0x05: // Interrupt Flag case 0x05: // Interrupt Flag
case 0x07: case 0x07:
{ {
if((timerClocks() >= 0) || (myInterruptEnabled && myInterruptTriggered)) // Update timer state and return the resulting flag(s)
return 0x00; updateTimer();
else return myInterruptFlag;
return 0x80;
} }
default: default:
@ -208,11 +192,8 @@ bool M6532::poke(uInt16 addr, uInt8 value)
if((addr & 0x04) != 0) if((addr & 0x04) != 0)
{ {
if((addr & 0x10) != 0) if((addr & 0x10) != 0)
{
myInterruptEnabled = (addr & 0x08);
setTimerRegister(value, addr & 0x03); setTimerRegister(value, addr & 0x03);
} }
}
else else
{ {
switch(addr & 0x03) switch(addr & 0x03)
@ -252,11 +233,13 @@ void M6532::setTimerRegister(uInt8 value, uInt8 interval)
{ {
static const uInt8 shift[] = { 0, 3, 6, 10 }; static const uInt8 shift[] = { 0, 3, 6, 10 };
myInterruptTriggered = false;
myIntervalShift = shift[interval]; myIntervalShift = shift[interval];
myOutTimer[interval] = value; myOutTimer[interval] = value;
myTimer = value << myIntervalShift; myTimer = value << myIntervalShift;
myCyclesWhenTimerSet = mySystem->cycles(); myCyclesWhenTimerSet = mySystem->cycles();
// Interrupt timer flag is reset when writing to the timer
myInterruptFlag &= ~TIMER_BIT;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -293,6 +276,32 @@ void M6532::setPinState(bool swcha)
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 M6532::updateTimer()
{
// Get number of clocks since timer was set
Int32 timer = timerClocks();
if(timer >= 0)
{
// Timer hasn't expired yet
myInterruptFlag &= ~TIMER_BIT;
return (timer >> myIntervalShift) & 0xff;
}
else
{
if(timer < 0)
myInterruptFlag |= TIMER_BIT;
// According to the M6532 documentation, the timer continues to count
// down to -255 timer clocks after wraparound. However, it isn't
// entirely clear what happens *after* if reaches -255.
// For now, we'll let it continuously wrap around.
return timer & 0xff;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6532::save(Serializer& out) const bool M6532::save(Serializer& out) const
{ {
@ -306,13 +315,12 @@ bool M6532::save(Serializer& out) const
out.putInt(myTimer); out.putInt(myTimer);
out.putInt(myIntervalShift); out.putInt(myIntervalShift);
out.putInt(myCyclesWhenTimerSet); out.putInt(myCyclesWhenTimerSet);
out.putBool(myInterruptEnabled);
out.putBool(myInterruptTriggered);
out.putByte(myDDRA); out.putByte(myDDRA);
out.putByte(myDDRB); out.putByte(myDDRB);
out.putByte(myOutA); out.putByte(myOutA);
out.putByte(myOutB); out.putByte(myOutB);
out.putByte(myInterruptFlag);
out.putByteArray(myOutTimer, 4); out.putByteArray(myOutTimer, 4);
} }
catch(...) catch(...)
@ -338,13 +346,12 @@ bool M6532::load(Serializer& in)
myTimer = in.getInt(); myTimer = in.getInt();
myIntervalShift = in.getInt(); myIntervalShift = in.getInt();
myCyclesWhenTimerSet = in.getInt(); myCyclesWhenTimerSet = in.getInt();
myInterruptEnabled = in.getBool();
myInterruptTriggered = in.getBool();
myDDRA = in.getByte(); myDDRA = in.getByte();
myDDRB = in.getByte(); myDDRB = in.getByte();
myOutA = in.getByte(); myOutA = in.getByte();
myOutB = in.getByte(); myOutB = in.getByte();
myInterruptFlag = in.getByte();
in.getByteArray(myOutTimer, 4); in.getByteArray(myOutTimer, 4);
} }
catch(...) catch(...)

View File

@ -29,7 +29,15 @@ class Settings;
#include "System.hxx" #include "System.hxx"
/** /**
RIOT This class models the M6532 RAM-I/O-Timer (aka RIOT) chip in the 2600
console. Note that only the functionality used by the console is
emulated here; several aspects of the chip are completely ignored:
- Pin 25 (IRQ) is not connected at all, therefore all related
items are ignored (A3 determining IRQ enable/disable, etc).
- D6 of the Interrupt Flag is ignored, including PA7 interrupt flag (A1)
and negative/positive edge-detection (A0)
@author Bradford W. Mott @author Bradford W. Mott
@version $Id$ @version $Id$
@ -135,6 +143,7 @@ class M6532 : public Device
void setTimerRegister(uInt8 data, uInt8 interval); void setTimerRegister(uInt8 data, uInt8 interval);
void setPinState(bool shcha); void setPinState(bool shcha);
uInt8 updateTimer();
private: private:
// Reference to the console // Reference to the console
@ -155,12 +164,6 @@ class M6532 : public Device
// Indicates the number of cycles when the timer was last set // Indicates the number of cycles when the timer was last set
Int32 myCyclesWhenTimerSet; Int32 myCyclesWhenTimerSet;
// Indicates if a timer interrupt has been enabled
bool myInterruptEnabled;
// Indicates if a read from timer has taken place after interrupt occured
bool myInterruptTriggered;
// Data Direction Register for Port A // Data Direction Register for Port A
uInt8 myDDRA; uInt8 myDDRA;
@ -173,6 +176,9 @@ class M6532 : public Device
// Last value written to Port B // Last value written to Port B
uInt8 myOutB; uInt8 myOutB;
// Interrupt Flag Register
uInt8 myInterruptFlag;
// Last value written to the timer registers // Last value written to the timer registers
uInt8 myOutTimer[4]; uInt8 myOutTimer[4];

View File

@ -30,7 +30,7 @@
#include "StateManager.hxx" #include "StateManager.hxx"
#define STATE_HEADER "03070100state" #define STATE_HEADER "03070101state"
#define MOVIE_HEADER "03030000movie" #define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -