mirror of https://github.com/stella-emu/stella.git
A number of changes have been made to the DOS code:
* Variables and functions accessed during interrupt service routines are properly locked to ensure they are in physical memory. * The emulation can be synchronized with the VSYNC of the video card instead of using the system timer. The is the default behavior when running under Windows NT/2000/XP since the DJGPP system timer functions do not operate correctly under these operating systems. * New routines have been added to setup the graphics modes. The new modes should run at 60Hz instead of the standard 70Hz for the 320x200 mode. * The stella profile is search for in the current working directory as well as the $STELLA_HOME directory. * When loading a ROM it is looked for in the current working directory, $STELLA_HOME/ROMS, and finally $STELLA_HOME. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@166 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
2a66c0ea9a
commit
2b0b275655
|
@ -13,7 +13,7 @@
|
||||||
// See the file "license" for information on usage and redistribution of
|
// See the file "license" for information on usage and redistribution of
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//
|
//
|
||||||
// $Id: mainDOS.cxx,v 1.9 2003-01-08 05:19:07 bwmott Exp $
|
// $Id: mainDOS.cxx,v 1.10 2003-02-17 05:17:41 bwmott Exp $
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include <go32.h>
|
#include <go32.h>
|
||||||
|
@ -43,6 +43,12 @@
|
||||||
#include "System.hxx"
|
#include "System.hxx"
|
||||||
#include "PCJoys.hxx"
|
#include "PCJoys.hxx"
|
||||||
#include "scandef.h"
|
#include "scandef.h"
|
||||||
|
#include "vga.hxx"
|
||||||
|
|
||||||
|
#define END_OF_FUNCTION(x) void x##_end(void) {}
|
||||||
|
#define END_OF_STATIC_FUNCTION(x) static void x##_end(void) {}
|
||||||
|
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void*)&x, sizeof(x))
|
||||||
|
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code((void*)x, (long)x##_end - (long)x)
|
||||||
|
|
||||||
// Pointer to the console object or the null pointer
|
// Pointer to the console object or the null pointer
|
||||||
Console* theConsole;
|
Console* theConsole;
|
||||||
|
@ -52,10 +58,10 @@ Event theEvent;
|
||||||
Event theKeyboardEvent;
|
Event theKeyboardEvent;
|
||||||
|
|
||||||
// Array of flags for each keyboard key-code
|
// Array of flags for each keyboard key-code
|
||||||
bool theKeyboardKeyState[128];
|
volatile bool theKeyboardKeyState[128];
|
||||||
|
|
||||||
// Used to ignore some number of key codes
|
// Used to ignore some number of key codes
|
||||||
uInt32 theNumOfKeyCodesToIgnore;
|
volatile uInt32 theNumOfKeyCodesToIgnore;
|
||||||
|
|
||||||
// An alternate properties file to use
|
// An alternate properties file to use
|
||||||
string theAlternateProFile = "";
|
string theAlternateProFile = "";
|
||||||
|
@ -83,6 +89,12 @@ uInt32 theDesiredFrameRate = 60;
|
||||||
// 4 - Use real Atari 2600 paddles
|
// 4 - Use real Atari 2600 paddles
|
||||||
uInt32 thePaddleMode = 0;
|
uInt32 thePaddleMode = 0;
|
||||||
|
|
||||||
|
// Indicates if emulation should synchronize with video instead of system timer
|
||||||
|
bool theSynchronizeVideoFlag = false;
|
||||||
|
|
||||||
|
// Indicates if sound should be enabled or not
|
||||||
|
bool theSoundEnabledFlag = true;
|
||||||
|
|
||||||
// Indicates if the Mode X graphics should be used or not
|
// Indicates if the Mode X graphics should be used or not
|
||||||
bool theUseModeXFlag = false;
|
bool theUseModeXFlag = false;
|
||||||
|
|
||||||
|
@ -120,6 +132,24 @@ static uInt32 theCurrentState = 0;
|
||||||
static string theHomeDir;
|
static string theHomeDir;
|
||||||
static string theStateDir;
|
static string theStateDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return true if the program is executing in a NT DOS virtual machine.
|
||||||
|
*/
|
||||||
|
bool isWindowsNt()
|
||||||
|
{
|
||||||
|
const char* p = getenv("OS");
|
||||||
|
|
||||||
|
if(((p) && (stricmp(p, "Windows_NT") == 0)) ||
|
||||||
|
(_get_dos_version(1) == 0x0532))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Changes the current state slot.
|
Changes the current state slot.
|
||||||
*/
|
*/
|
||||||
|
@ -231,8 +261,50 @@ void loadState()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This routine should be called once the console is create to setup
|
This is the keyboard interrupt service routine. It's called
|
||||||
to graphics mode
|
whenever a key is pressed or released on the keyboard.
|
||||||
|
*/
|
||||||
|
static void keyboardInterruptServiceRoutine(void)
|
||||||
|
{
|
||||||
|
// Get the scan code of the key
|
||||||
|
uInt8 code = inportb(0x60);
|
||||||
|
|
||||||
|
// Are we ignoring some key codes?
|
||||||
|
if(theNumOfKeyCodesToIgnore > 0)
|
||||||
|
{
|
||||||
|
--theNumOfKeyCodesToIgnore;
|
||||||
|
}
|
||||||
|
// Handle the pause key
|
||||||
|
else if(code == 0xE1)
|
||||||
|
{
|
||||||
|
// Toggle the state of the pause key. The pause key only sends a "make"
|
||||||
|
// code it does not send a "break" code. Also the "make" code is the
|
||||||
|
// sequence 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 so we'll need to skip the
|
||||||
|
// remaining 5 values in the sequence.
|
||||||
|
theKeyboardKeyState[SCAN_PAUSE] = !theKeyboardKeyState[SCAN_PAUSE];
|
||||||
|
theNumOfKeyCodesToIgnore = 5;
|
||||||
|
}
|
||||||
|
// Handle the "extended" and the "error" key codes
|
||||||
|
else if((code == 0xE0) || (code == 0x00))
|
||||||
|
{
|
||||||
|
// Currently, we ignore the "extended" and "error" key codes. We should
|
||||||
|
// probably modify the "extended" key code support so that we can identify
|
||||||
|
// the extended keys...
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update the state of the key
|
||||||
|
theKeyboardKeyState[code & 0x7F] = !(code & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ack the interrupt
|
||||||
|
outp(0x20, 0x20);
|
||||||
|
}
|
||||||
|
END_OF_STATIC_FUNCTION(keyboardInterruptServiceRoutine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
This routine should be called once the console is created to setup
|
||||||
|
the graphics mode
|
||||||
*/
|
*/
|
||||||
void startup()
|
void startup()
|
||||||
{
|
{
|
||||||
|
@ -258,41 +330,31 @@ void startup()
|
||||||
regs.h.ah = 0x00;
|
regs.h.ah = 0x00;
|
||||||
int86(VGA_BIOS, ®s, ®s);
|
int86(VGA_BIOS, ®s, ®s);
|
||||||
|
|
||||||
// Enable Mode X if we're using it
|
// Setup VGA graphics mode
|
||||||
if(theUseModeXFlag)
|
if(theUseModeXFlag)
|
||||||
{
|
{
|
||||||
disable();
|
VgaSetMode(VGA_320_240_60HZ);
|
||||||
outpw(0x3C4, 0x0604); // Disable chain mode
|
|
||||||
outpw(0x3D4, 0xE317); // Disable word mode
|
|
||||||
outpw(0x3D4, 0x0014); // Disable doubleword mode
|
|
||||||
outpw(0x3D4, 0x0100); // Synchronous reset while setting misc output
|
|
||||||
|
|
||||||
outp(0x3C2, 0xE3); // Create square pixel aspect ratio
|
// Clear the screen
|
||||||
outp(0x3C4, 0x00); // Undo reset (restart sequencer)
|
outp(0x3C4, 0x02);
|
||||||
|
outp(0x3C5, 0x0F);
|
||||||
outp(0x3D4, 0x11); // Reprogram CRT controller
|
|
||||||
outp(0x3D5, (inp(0x3D5)) & 0x7f);
|
|
||||||
|
|
||||||
outpw(0x3D4, 0x0D06); // Vertical total
|
|
||||||
outpw(0x3D4, 0x3E07); // Overflow register
|
|
||||||
outpw(0x3D4, 0x4109); // Cell height (2 to double scan)
|
|
||||||
outpw(0x3D4, 0xEA10); // Vertical retrace start
|
|
||||||
outpw(0x3D4, 0xAC11); // Vertical retrace end and write protect
|
|
||||||
outpw(0x3D4, 0xDF12); // Vertical display enable end
|
|
||||||
outpw(0x3D4, 0xE715); // Start vertical blanking
|
|
||||||
outpw(0x3D4, 0x0616); // End vertical blanking
|
|
||||||
enable();
|
|
||||||
|
|
||||||
// Clear the screen now that Mode X is enabled
|
|
||||||
for(uInt32 i = 0; i < 240 * 80; ++i)
|
for(uInt32 i = 0; i < 240 * 80; ++i)
|
||||||
{
|
{
|
||||||
outp(0x3C4, 0x02);
|
|
||||||
outp(0x3C5, 0x0F);
|
|
||||||
_farpokeb(_dos_ds, 0xA0000 + i, 0);
|
_farpokeb(_dos_ds, 0xA0000 + i, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Setup to color palette for the video card
|
{
|
||||||
|
VgaSetMode(VGA_320_200_60HZ);
|
||||||
|
|
||||||
|
// Clear the screen
|
||||||
|
for(uInt32 i = 0; i < 320 * 200; ++i)
|
||||||
|
{
|
||||||
|
_farpokew(_dos_ds, 0xA0000 + i, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup color palette for the video card
|
||||||
const uInt32* palette = theConsole->mediaSource().palette();
|
const uInt32* palette = theConsole->mediaSource().palette();
|
||||||
outp(VGA_PEL_ADDRESS, 0);
|
outp(VGA_PEL_ADDRESS, 0);
|
||||||
for(int index = 0; index < 256; index++)
|
for(int index = 0; index < 256; index++)
|
||||||
|
@ -303,6 +365,9 @@ void startup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install keyboard interrupt handler
|
// Install keyboard interrupt handler
|
||||||
|
LOCK_VARIABLE(theKeyboardKeyState);
|
||||||
|
LOCK_VARIABLE(theNumOfKeyCodesToIgnore);
|
||||||
|
LOCK_FUNCTION(keyboardInterruptServiceRoutine);
|
||||||
for(uInt32 k = 0; k < 128; ++k)
|
for(uInt32 k = 0; k < 128; ++k)
|
||||||
{
|
{
|
||||||
theKeyboardKeyState[k] = false;
|
theKeyboardKeyState[k] = false;
|
||||||
|
@ -816,48 +881,6 @@ void handleEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
This is the keyboard interrupt service routine. It's called
|
|
||||||
whenever a key is pressed or released on the keyboard.
|
|
||||||
*/
|
|
||||||
static void keyboardInterruptServiceRoutine(void)
|
|
||||||
{
|
|
||||||
// Get the scan code of the key
|
|
||||||
uInt8 code = inportb(0x60);
|
|
||||||
|
|
||||||
// Are we ignoring some key codes?
|
|
||||||
if(theNumOfKeyCodesToIgnore > 0)
|
|
||||||
{
|
|
||||||
--theNumOfKeyCodesToIgnore;
|
|
||||||
}
|
|
||||||
// Handle the pause key
|
|
||||||
else if(code == 0xE1)
|
|
||||||
{
|
|
||||||
// Toggle the state of the pause key. The pause key only sends a "make"
|
|
||||||
// code it does not send a "break" code. Also the "make" code is the
|
|
||||||
// sequence 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 so we'll need to skip the
|
|
||||||
// remaining 5 values in the sequence.
|
|
||||||
theKeyboardKeyState[SCAN_PAUSE] = !theKeyboardKeyState[SCAN_PAUSE];
|
|
||||||
theNumOfKeyCodesToIgnore = 5;
|
|
||||||
}
|
|
||||||
// Handle the "extended" and the "error" key codes
|
|
||||||
else if((code == 0xE0) || (code == 0x00))
|
|
||||||
{
|
|
||||||
// Currently, we ignore the "extended" and "error" key codes. We should
|
|
||||||
// probably modify the "extended" key code support so that we can identify
|
|
||||||
// the extended keys...
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Update the state of the key
|
|
||||||
theKeyboardKeyState[code & 0x7F] = !(code & 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ack the interrupt
|
|
||||||
outp(0x20, 0x20);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Ensure that the necessary directories are created for Stella under
|
Ensure that the necessary directories are created for Stella under
|
||||||
STELLA_HOME or the current working directory if STELLA_HOME is not
|
STELLA_HOME or the current working directory if STELLA_HOME is not
|
||||||
|
@ -875,8 +898,8 @@ bool setupDirs()
|
||||||
theHomeDir = ".";
|
theHomeDir = ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing backslash
|
// Remove any trailing backslashes
|
||||||
if((theHomeDir.length() >= 1) &&
|
while((theHomeDir.length() >= 1) &&
|
||||||
(theHomeDir[theHomeDir.length() - 1] == '\\'))
|
(theHomeDir[theHomeDir.length() - 1] == '\\'))
|
||||||
{
|
{
|
||||||
theHomeDir = theHomeDir.substr(0, theHomeDir.length() - 1);
|
theHomeDir = theHomeDir.substr(0, theHomeDir.length() - 1);
|
||||||
|
@ -919,10 +942,12 @@ void usage()
|
||||||
"",
|
"",
|
||||||
" -fps <number> Display the given number of frames per second",
|
" -fps <number> Display the given number of frames per second",
|
||||||
" -modex Use 320x240 video mode instead of 320x200",
|
" -modex Use 320x240 video mode instead of 320x200",
|
||||||
|
" -nosound Disables audio output",
|
||||||
" -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate",
|
" -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate",
|
||||||
" or that real Atari 2600 paddles are being used",
|
" or that real Atari 2600 paddles are being used",
|
||||||
" -pro <props file> Use given properties file instead of stella.pro",
|
" -pro <props file> Use given properties file instead of stella.pro",
|
||||||
" -showinfo Show some game info on exit",
|
" -showinfo Show some game info on exit",
|
||||||
|
" -vsync Synchronize with video instead of system timer",
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -940,15 +965,22 @@ void usage()
|
||||||
*/
|
*/
|
||||||
bool setupProperties(PropertiesSet& set)
|
bool setupProperties(PropertiesSet& set)
|
||||||
{
|
{
|
||||||
// Try to load the properties file
|
// Try to load the properties file from either the current working
|
||||||
string filename = (theAlternateProFile != "") ? theAlternateProFile :
|
// directory or the $STELLA_HOME directory
|
||||||
|
string filename1 = (theAlternateProFile != "") ? theAlternateProFile :
|
||||||
"stella.pro";
|
"stella.pro";
|
||||||
|
string filename2 = theHomeDir + '\\' + filename1;
|
||||||
|
|
||||||
if(access(filename.c_str(), F_OK) == 0)
|
if(access(filename1.c_str(), R_OK | F_OK) == 0)
|
||||||
{
|
{
|
||||||
// File is accessible so load properties from it
|
// File is accessible so load properties from it
|
||||||
set.load(filename, &Console::defaultProperties(), false);
|
set.load(filename1, &Console::defaultProperties(), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if(access(filename2.c_str(), R_OK | F_OK) == 0)
|
||||||
|
{
|
||||||
|
// File is accessible so load properties from it
|
||||||
|
set.load(filename2, &Console::defaultProperties(), false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1003,6 +1035,10 @@ void handleCommandLineArguments(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
else if(string(argv[i]) == "-nosound")
|
||||||
|
{
|
||||||
|
theSoundEnabledFlag = false;
|
||||||
|
}
|
||||||
else if(string(argv[i]) == "-modex")
|
else if(string(argv[i]) == "-modex")
|
||||||
{
|
{
|
||||||
theUseModeXFlag = true;
|
theUseModeXFlag = true;
|
||||||
|
@ -1015,6 +1051,10 @@ void handleCommandLineArguments(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
theAlternateProFile = argv[++i];
|
theAlternateProFile = argv[++i];
|
||||||
}
|
}
|
||||||
|
else if(string(argv[i]) == "-vsync")
|
||||||
|
{
|
||||||
|
theSynchronizeVideoFlag = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
usage();
|
usage();
|
||||||
|
@ -1025,6 +1065,9 @@ void handleCommandLineArguments(int argc, char* argv[])
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
// Find out if we're running in an NT DOS virtual machine
|
||||||
|
bool windowsNtFlag = isWindowsNt();
|
||||||
|
|
||||||
// First set up the directories where Stella will find RC and state files
|
// First set up the directories where Stella will find RC and state files
|
||||||
if(!setupDirs())
|
if(!setupDirs())
|
||||||
{
|
{
|
||||||
|
@ -1037,13 +1080,31 @@ int main(int argc, char* argv[])
|
||||||
// Get a pointer to the file which contains the cartridge ROM
|
// Get a pointer to the file which contains the cartridge ROM
|
||||||
const char* file = argv[argc - 1];
|
const char* file = argv[argc - 1];
|
||||||
|
|
||||||
// Open the cartridge image and read it in
|
// Open the cartridge image and read it in. The cartridge image is
|
||||||
ifstream in;
|
// searched for in the current working directory, the $STELLA_HOME\ROMS
|
||||||
in.open(file, ios::in | ios::binary);
|
// directory, and finally the $STELLA_HOME directory.
|
||||||
|
string file1(file);
|
||||||
|
string file2(theHomeDir + "\\ROMS\\" + file1);
|
||||||
|
string file3(theHomeDir + '\\' + file1);
|
||||||
|
|
||||||
|
ifstream in;
|
||||||
|
in.open(file1.c_str(), ios::in | ios::binary);
|
||||||
if(!in)
|
if(!in)
|
||||||
{
|
{
|
||||||
cerr << "ERROR: Couldn't open " << file << "..." << endl;
|
in.close();
|
||||||
exit(1);
|
in.clear();
|
||||||
|
in.open(file2.c_str(), ios::in | ios::binary);
|
||||||
|
if(!in)
|
||||||
|
{
|
||||||
|
in.close();
|
||||||
|
in.clear();
|
||||||
|
in.open(file3.c_str(), ios::in | ios::binary);
|
||||||
|
if(!in)
|
||||||
|
{
|
||||||
|
cerr << "ERROR: Couldn't locate " << file << "..." << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uInt8* image = new uInt8[512 * 1024];
|
uInt8* image = new uInt8[512 * 1024];
|
||||||
|
@ -1059,14 +1120,14 @@ int main(int argc, char* argv[])
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a sound object for use with the console
|
|
||||||
SoundDOS sound;
|
|
||||||
// sound.setSoundVolume(settings->theDesiredVolume);
|
|
||||||
|
|
||||||
// Get just the filename of the file containing the ROM image
|
// Get just the filename of the file containing the ROM image
|
||||||
const char* filename = (!strrchr(file, '\\')) ?
|
const char* filename = (!strrchr(file, '\\')) ?
|
||||||
file : strrchr(file, '\\') + 1;
|
file : strrchr(file, '\\') + 1;
|
||||||
|
|
||||||
|
// Create a sound object for use with the console
|
||||||
|
SoundDOS sound(theSoundEnabledFlag);
|
||||||
|
// sound.setSoundVolume(settings->theDesiredVolume);
|
||||||
|
|
||||||
// Create the 2600 game console
|
// Create the 2600 game console
|
||||||
theConsole = new Console(image, size, filename,
|
theConsole = new Console(image, size, filename,
|
||||||
theEvent, propertiesSet, sound.getSampleRate());
|
theEvent, propertiesSet, sound.getSampleRate());
|
||||||
|
@ -1077,55 +1138,57 @@ int main(int argc, char* argv[])
|
||||||
startup();
|
startup();
|
||||||
|
|
||||||
// Get the starting time in case we need to print statistics
|
// Get the starting time in case we need to print statistics
|
||||||
uclock_t startingTime = uclock();
|
clock_t startingTime = clock();
|
||||||
|
|
||||||
uInt32 numberOfFrames = 0;
|
uInt32 numberOfFrames = 0;
|
||||||
for( ; !theQuitIndicator ; ++numberOfFrames)
|
for( ; !theQuitIndicator ; ++numberOfFrames)
|
||||||
{
|
{
|
||||||
// Remember the current time before we start drawing the frame
|
// Remember the current time before we start drawing the frame
|
||||||
uclock_t before = uclock();
|
uclock_t startTimeStamp = uclock();
|
||||||
|
|
||||||
// Ask the media source to prepare the next frame
|
// Ask the media source to prepare the next frame
|
||||||
if(!thePauseIndicator)
|
if(!thePauseIndicator)
|
||||||
{
|
{
|
||||||
theConsole->mediaSource().update();
|
theConsole->mediaSource().update();
|
||||||
|
sound.mute(false);
|
||||||
sound.updateSound(theConsole->mediaSource());
|
sound.updateSound(theConsole->mediaSource());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/*
|
|
||||||
TODO: This code seems to work fine under mode 13, however, it slows
|
|
||||||
the frame rate down under Mode X. At any point it needs to be
|
|
||||||
tested on more video cards before it's ready for production :-)
|
|
||||||
|
|
||||||
// If we're not behind schedule then let's wait for the VSYNC!
|
|
||||||
static uclock_t endOfLastVsync = 0;
|
|
||||||
if((theDesiredFrameRate <= 60) &&
|
|
||||||
(uclock() - endOfLastVsync < (UCLOCKS_PER_SEC / theDesiredFrameRate)))
|
|
||||||
{
|
{
|
||||||
|
sound.mute(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If vsync is selected or we're running under NT then wait for VSYNC
|
||||||
|
if(windowsNtFlag || theSynchronizeVideoFlag)
|
||||||
|
{
|
||||||
|
// Wait until previous retrace has ended
|
||||||
while(inp(0x3DA) & 0x08);
|
while(inp(0x3DA) & 0x08);
|
||||||
|
|
||||||
|
// Wait until next retrace has begun
|
||||||
while(!(inp(0x3DA) & 0x08));
|
while(!(inp(0x3DA) & 0x08));
|
||||||
}
|
}
|
||||||
endOfLastVsync = uclock();
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Update the display and handle events
|
// Update the display and handle events
|
||||||
updateDisplay(theConsole->mediaSource());
|
updateDisplay(theConsole->mediaSource());
|
||||||
handleEvents();
|
handleEvents();
|
||||||
|
|
||||||
// Now, waste time if we need to so that we are at the desired frame rate
|
// Waste time if we need to so that we are at the desired frame rate
|
||||||
for(;;)
|
if(!(windowsNtFlag || theSynchronizeVideoFlag))
|
||||||
{
|
{
|
||||||
uclock_t delta = uclock() - before;
|
for(;;)
|
||||||
|
|
||||||
if(delta > (UCLOCKS_PER_SEC / theDesiredFrameRate))
|
|
||||||
{
|
{
|
||||||
break;
|
uclock_t endTimeStamp = uclock();
|
||||||
|
long long delta = endTimeStamp - startTimeStamp;
|
||||||
|
if(delta >= (UCLOCKS_PER_SEC / theDesiredFrameRate))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the ending time in case we need to print statistics
|
// Get the ending time in case we need to print statistics
|
||||||
uclock_t endingTime = uclock();
|
clock_t endingTime = clock();
|
||||||
|
|
||||||
// Close the sound device
|
// Close the sound device
|
||||||
sound.close();
|
sound.close();
|
||||||
|
@ -1138,8 +1201,7 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
if(theShowInfoFlag)
|
if(theShowInfoFlag)
|
||||||
{
|
{
|
||||||
double executionTime = (endingTime - startingTime) /
|
double executionTime = (endingTime - startingTime) / (double)CLOCKS_PER_SEC;
|
||||||
(double)UCLOCKS_PER_SEC;
|
|
||||||
double framesPerSecond = numberOfFrames / executionTime;
|
double framesPerSecond = numberOfFrames / executionTime;
|
||||||
|
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
|
@ -0,0 +1,287 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2003 by Bradford W. Mott
|
||||||
|
//
|
||||||
|
// See the file "license" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//
|
||||||
|
// This code is based on the vga256fb frame buffer device driver for
|
||||||
|
// Linux by Salvatore Sanfilippo <antirez@invece.org>. The vga256fb
|
||||||
|
// code can be found at http://www.kyuzz.org/antirez/vga256fb.htm. It
|
||||||
|
// was released under the GNU General Public License.
|
||||||
|
//
|
||||||
|
// $Id: vga.cxx,v 1.1 2003-02-17 05:17:42 bwmott Exp $
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include <sys/farptr.h>
|
||||||
|
#include <dos.h>
|
||||||
|
|
||||||
|
#include "vga.hxx"
|
||||||
|
|
||||||
|
// Structure for holding VGA mode information and register settings
|
||||||
|
struct VgaModeInfo
|
||||||
|
{
|
||||||
|
unsigned int xres; // X resolution
|
||||||
|
unsigned int yres; // Y resolution
|
||||||
|
bool chained; // Chained flag
|
||||||
|
unsigned char crt[0x19]; // 24 CRT sub-registers
|
||||||
|
unsigned char attrib[0x15]; // 21 attribute sub-registers
|
||||||
|
unsigned char graphic[0x09]; // 9 graphic sub-registers
|
||||||
|
unsigned char sequencer[0x05]; // 5 sequencer sub-registers
|
||||||
|
unsigned char misc; // misc register
|
||||||
|
};
|
||||||
|
|
||||||
|
// VGA mode information for 320x200x256 colors at 60Hz
|
||||||
|
static VgaModeInfo Vga320x200x60Hz = {
|
||||||
|
xres: 320,
|
||||||
|
yres: 200,
|
||||||
|
chained: true,
|
||||||
|
crt: {
|
||||||
|
0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0A, 0x3E,
|
||||||
|
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xC2, 0x84, 0x8F, 0x28, 0x40, 0x90, 0x08, 0xA3 },
|
||||||
|
attrib: {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||||
|
0x41, 0x00, 0x0F, 0x00, 0x00, },
|
||||||
|
graphic: {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF },
|
||||||
|
sequencer: {
|
||||||
|
0x03, 0x01, 0x0F, 0x00, 0x0E },
|
||||||
|
misc: 0x63
|
||||||
|
};
|
||||||
|
|
||||||
|
// VGA mode information for 320x200x256 colors at 70Hz (standard BIOS 13h mode)
|
||||||
|
static VgaModeInfo Vga320x200x70Hz = {
|
||||||
|
xres: 320,
|
||||||
|
yres: 200,
|
||||||
|
chained: true,
|
||||||
|
crt: {
|
||||||
|
0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F,
|
||||||
|
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3 },
|
||||||
|
attrib: {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||||
|
0x41, 0x00, 0x0F, 0x00, 0x00 },
|
||||||
|
graphic: {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF },
|
||||||
|
sequencer: {
|
||||||
|
0x03, 0x01, 0x0F, 0x00, 0x0E },
|
||||||
|
misc: 0x63
|
||||||
|
};
|
||||||
|
|
||||||
|
// VGA mode information for 320x240x256 colors at 60Hz (square pixels)
|
||||||
|
static VgaModeInfo Vga320x240x60Hz = {
|
||||||
|
xres: 320,
|
||||||
|
yres: 240,
|
||||||
|
chained: false,
|
||||||
|
crt: {
|
||||||
|
0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0D, 0x3E,
|
||||||
|
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xEA, 0xAC, 0xDF, 0x28, 0x00, 0xE7, 0x06, 0xE3 },
|
||||||
|
attrib: {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||||
|
0x41, 0x00, 0x0F, 0x00, 0x00, },
|
||||||
|
graphic: {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF },
|
||||||
|
sequencer: {
|
||||||
|
0x03, 0x01, 0x0F, 0x00, 0x06 },
|
||||||
|
misc: 0xE3
|
||||||
|
};
|
||||||
|
|
||||||
|
#define V_CRT_INDEX 0x3D4 // CRT address (index) register
|
||||||
|
#define V_CRT_RW 0x3D5 // CRT data register
|
||||||
|
#define V_ISTAT1_R 0x3DA // Input status register #1
|
||||||
|
#define V_FEATURE_W 0x3DA // Feature control register, (write)
|
||||||
|
#define V_SEQ_INDEX 0x3C4 // Sequencer address (index) register
|
||||||
|
#define V_SEQ_RW 0x3C5 // Sequencer data register
|
||||||
|
#define V_GR_INDEX 0x3CE // VGA address (index) register
|
||||||
|
#define V_GR_RW 0x3CF // VGA data register
|
||||||
|
#define V_MISC_R 0x3CC // VGA misc register (read)
|
||||||
|
#define V_MISC_W 0x3C2 // VGA misc register (write)
|
||||||
|
#define V_ATTR_IW 0x3C0 // Attribute index and data register (write)
|
||||||
|
#define V_ATTR_R 0x3C1 // Attribute data register (read)
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static inline void put_sequence(int i, unsigned char b)
|
||||||
|
{
|
||||||
|
outportb(V_SEQ_INDEX, i);
|
||||||
|
outportb(V_SEQ_RW, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static inline void put_graph(int i, unsigned char b)
|
||||||
|
{
|
||||||
|
outportb(V_GR_INDEX, i);
|
||||||
|
outportb(V_GR_RW, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static inline void put_misc(unsigned char b)
|
||||||
|
{
|
||||||
|
outportb(V_MISC_W, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static inline void put_attr(int i, unsigned char b)
|
||||||
|
{
|
||||||
|
// Warning: as you can see the 0x20 bit will be set to zero
|
||||||
|
// so the video output will be disabled, if you want read or write
|
||||||
|
// (since some VGA cards allows you to write without setting the PAS
|
||||||
|
// bit) without put the video off OR the index with 0x20 */
|
||||||
|
|
||||||
|
// reset the flip/flop
|
||||||
|
inportb(V_ISTAT1_R);
|
||||||
|
|
||||||
|
// set the index
|
||||||
|
outportb(V_ATTR_IW, i);
|
||||||
|
|
||||||
|
// write data
|
||||||
|
outportb(V_ATTR_IW, b);
|
||||||
|
|
||||||
|
// reset the flip/flop
|
||||||
|
inportb(V_ISTAT1_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static inline void put_crt(int i, unsigned char b)
|
||||||
|
{
|
||||||
|
outportb(V_CRT_INDEX, i);
|
||||||
|
outportb(V_CRT_RW, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static inline unsigned char get_crt(int i)
|
||||||
|
{
|
||||||
|
outportb(V_CRT_INDEX, i);
|
||||||
|
return inportb(V_CRT_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static void disable_video(void)
|
||||||
|
{
|
||||||
|
// Get the current value
|
||||||
|
volatile unsigned char t = inportb(V_ATTR_IW);
|
||||||
|
|
||||||
|
// Reset the flip/flop
|
||||||
|
inportb(V_ISTAT1_R);
|
||||||
|
|
||||||
|
// Clear the PAS bit
|
||||||
|
t &= 0xDF;
|
||||||
|
|
||||||
|
// Set the port
|
||||||
|
outportb(V_ATTR_IW, t);
|
||||||
|
|
||||||
|
// Reset the flip/flop
|
||||||
|
inportb(V_ISTAT1_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static void enable_video(void)
|
||||||
|
{
|
||||||
|
// Get the current value
|
||||||
|
volatile unsigned char t = inportb(V_ATTR_IW);
|
||||||
|
|
||||||
|
// Reset the flip/flop
|
||||||
|
inportb(V_ISTAT1_R);
|
||||||
|
|
||||||
|
// Set the PAS bit
|
||||||
|
t |= 0x20;
|
||||||
|
|
||||||
|
// set the port
|
||||||
|
outportb(V_ATTR_IW, t);
|
||||||
|
|
||||||
|
// Reset the flip/flop
|
||||||
|
inportb(V_ISTAT1_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static void unlock_crt_registers(void)
|
||||||
|
{
|
||||||
|
volatile unsigned char aux = get_crt(0x11);
|
||||||
|
aux &= 0x7f;
|
||||||
|
put_crt(0x11, aux);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
static void load_regs(VgaModeInfo *regs)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
disable_video();
|
||||||
|
disable();
|
||||||
|
|
||||||
|
// Set misc register
|
||||||
|
put_misc(regs->misc);
|
||||||
|
|
||||||
|
// Sequencer sync reset on
|
||||||
|
put_sequence(0x00, 0x01);
|
||||||
|
|
||||||
|
// Sequencer registers
|
||||||
|
for(j = 0; j <= 0x04; j++)
|
||||||
|
{
|
||||||
|
put_sequence(j, regs->sequencer[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequencer reset off
|
||||||
|
put_sequence(0x00, 0x03);
|
||||||
|
|
||||||
|
unlock_crt_registers();
|
||||||
|
// crt registers
|
||||||
|
for(j = 0; j <= 0x18; j++)
|
||||||
|
{
|
||||||
|
put_crt(j, regs->crt[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graphic registers
|
||||||
|
for(j = 0; j <= 0x08; j++)
|
||||||
|
{
|
||||||
|
put_graph(j, regs->graphic[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attrib registers
|
||||||
|
for(j = 0; j <= 0x14; j++)
|
||||||
|
{
|
||||||
|
put_attr(j, regs->attrib[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
enable();
|
||||||
|
enable_video();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool VgaSetMode(int mode)
|
||||||
|
{
|
||||||
|
VgaModeInfo* info = 0;
|
||||||
|
|
||||||
|
if(mode == VGA_320_200_60HZ)
|
||||||
|
{
|
||||||
|
info = &Vga320x200x60Hz;
|
||||||
|
}
|
||||||
|
else if(mode == VGA_320_200_70HZ)
|
||||||
|
{
|
||||||
|
info = &Vga320x200x70Hz;
|
||||||
|
}
|
||||||
|
else if(mode == VGA_320_240_60HZ)
|
||||||
|
{
|
||||||
|
info = &Vga320x240x60Hz;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
load_regs(info);
|
||||||
|
return info->chained;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2003 by Bradford W. Mott
|
||||||
|
//
|
||||||
|
// See the file "license" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//
|
||||||
|
// $Id: vga.hxx,v 1.1 2003-02-17 05:17:42 bwmott Exp $
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#ifndef VGA_HXX
|
||||||
|
#define VGA_HXX
|
||||||
|
|
||||||
|
#include "bspf.hxx"
|
||||||
|
|
||||||
|
#define VGA_320_200_60HZ 1
|
||||||
|
#define VGA_320_200_70HZ 2
|
||||||
|
#define VGA_320_240_60HZ 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
Change the graphics mode to the specified mode.
|
||||||
|
*/
|
||||||
|
extern bool VgaSetMode(int mode);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue