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
|
||||
// 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>
|
||||
|
@ -43,6 +43,12 @@
|
|||
#include "System.hxx"
|
||||
#include "PCJoys.hxx"
|
||||
#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
|
||||
Console* theConsole;
|
||||
|
@ -52,10 +58,10 @@ Event theEvent;
|
|||
Event theKeyboardEvent;
|
||||
|
||||
// Array of flags for each keyboard key-code
|
||||
bool theKeyboardKeyState[128];
|
||||
volatile bool theKeyboardKeyState[128];
|
||||
|
||||
// Used to ignore some number of key codes
|
||||
uInt32 theNumOfKeyCodesToIgnore;
|
||||
volatile uInt32 theNumOfKeyCodesToIgnore;
|
||||
|
||||
// An alternate properties file to use
|
||||
string theAlternateProFile = "";
|
||||
|
@ -83,6 +89,12 @@ uInt32 theDesiredFrameRate = 60;
|
|||
// 4 - Use real Atari 2600 paddles
|
||||
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
|
||||
bool theUseModeXFlag = false;
|
||||
|
||||
|
@ -120,6 +132,24 @@ static uInt32 theCurrentState = 0;
|
|||
static string theHomeDir;
|
||||
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.
|
||||
*/
|
||||
|
@ -231,8 +261,50 @@ void loadState()
|
|||
}
|
||||
|
||||
/**
|
||||
This routine should be called once the console is create to setup
|
||||
to graphics mode
|
||||
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);
|
||||
}
|
||||
END_OF_STATIC_FUNCTION(keyboardInterruptServiceRoutine);
|
||||
|
||||
/**
|
||||
This routine should be called once the console is created to setup
|
||||
the graphics mode
|
||||
*/
|
||||
void startup()
|
||||
{
|
||||
|
@ -258,41 +330,31 @@ void startup()
|
|||
regs.h.ah = 0x00;
|
||||
int86(VGA_BIOS, ®s, ®s);
|
||||
|
||||
// Enable Mode X if we're using it
|
||||
// Setup VGA graphics mode
|
||||
if(theUseModeXFlag)
|
||||
{
|
||||
disable();
|
||||
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
|
||||
VgaSetMode(VGA_320_240_60HZ);
|
||||
|
||||
outp(0x3C2, 0xE3); // Create square pixel aspect ratio
|
||||
outp(0x3C4, 0x00); // Undo reset (restart sequencer)
|
||||
|
||||
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
|
||||
// Clear the screen
|
||||
outp(0x3C4, 0x02);
|
||||
outp(0x3C5, 0x0F);
|
||||
for(uInt32 i = 0; i < 240 * 80; ++i)
|
||||
{
|
||||
outp(0x3C4, 0x02);
|
||||
outp(0x3C5, 0x0F);
|
||||
_farpokeb(_dos_ds, 0xA0000 + i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup to color palette for the video card
|
||||
else
|
||||
{
|
||||
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();
|
||||
outp(VGA_PEL_ADDRESS, 0);
|
||||
for(int index = 0; index < 256; index++)
|
||||
|
@ -303,6 +365,9 @@ void startup()
|
|||
}
|
||||
|
||||
// Install keyboard interrupt handler
|
||||
LOCK_VARIABLE(theKeyboardKeyState);
|
||||
LOCK_VARIABLE(theNumOfKeyCodesToIgnore);
|
||||
LOCK_FUNCTION(keyboardInterruptServiceRoutine);
|
||||
for(uInt32 k = 0; k < 128; ++k)
|
||||
{
|
||||
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
|
||||
STELLA_HOME or the current working directory if STELLA_HOME is not
|
||||
|
@ -875,8 +898,8 @@ bool setupDirs()
|
|||
theHomeDir = ".";
|
||||
}
|
||||
|
||||
// Remove trailing backslash
|
||||
if((theHomeDir.length() >= 1) &&
|
||||
// Remove any trailing backslashes
|
||||
while((theHomeDir.length() >= 1) &&
|
||||
(theHomeDir[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",
|
||||
" -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",
|
||||
" or that real Atari 2600 paddles are being used",
|
||||
" -pro <props file> Use given properties file instead of stella.pro",
|
||||
" -showinfo Show some game info on exit",
|
||||
" -vsync Synchronize with video instead of system timer",
|
||||
0
|
||||
};
|
||||
|
||||
|
@ -940,15 +965,22 @@ void usage()
|
|||
*/
|
||||
bool setupProperties(PropertiesSet& set)
|
||||
{
|
||||
// Try to load the properties file
|
||||
string filename = (theAlternateProFile != "") ? theAlternateProFile :
|
||||
// Try to load the properties file from either the current working
|
||||
// directory or the $STELLA_HOME directory
|
||||
string filename1 = (theAlternateProFile != "") ? theAlternateProFile :
|
||||
"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
|
||||
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;
|
||||
}
|
||||
else
|
||||
|
@ -1003,6 +1035,10 @@ void handleCommandLineArguments(int argc, char* argv[])
|
|||
}
|
||||
++i;
|
||||
}
|
||||
else if(string(argv[i]) == "-nosound")
|
||||
{
|
||||
theSoundEnabledFlag = false;
|
||||
}
|
||||
else if(string(argv[i]) == "-modex")
|
||||
{
|
||||
theUseModeXFlag = true;
|
||||
|
@ -1015,6 +1051,10 @@ void handleCommandLineArguments(int argc, char* argv[])
|
|||
{
|
||||
theAlternateProFile = argv[++i];
|
||||
}
|
||||
else if(string(argv[i]) == "-vsync")
|
||||
{
|
||||
theSynchronizeVideoFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
usage();
|
||||
|
@ -1025,6 +1065,9 @@ void handleCommandLineArguments(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
|
||||
if(!setupDirs())
|
||||
{
|
||||
|
@ -1037,13 +1080,31 @@ int main(int argc, char* argv[])
|
|||
// Get a pointer to the file which contains the cartridge ROM
|
||||
const char* file = argv[argc - 1];
|
||||
|
||||
// Open the cartridge image and read it in
|
||||
ifstream in;
|
||||
in.open(file, ios::in | ios::binary);
|
||||
// Open the cartridge image and read it in. The cartridge image is
|
||||
// searched for in the current working directory, the $STELLA_HOME\ROMS
|
||||
// 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)
|
||||
{
|
||||
cerr << "ERROR: Couldn't open " << file << "..." << endl;
|
||||
exit(1);
|
||||
in.close();
|
||||
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];
|
||||
|
@ -1059,14 +1120,14 @@ int main(int argc, char* argv[])
|
|||
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
|
||||
const char* filename = (!strrchr(file, '\\')) ?
|
||||
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
|
||||
theConsole = new Console(image, size, filename,
|
||||
theEvent, propertiesSet, sound.getSampleRate());
|
||||
|
@ -1077,55 +1138,57 @@ int main(int argc, char* argv[])
|
|||
startup();
|
||||
|
||||
// Get the starting time in case we need to print statistics
|
||||
uclock_t startingTime = uclock();
|
||||
|
||||
clock_t startingTime = clock();
|
||||
|
||||
uInt32 numberOfFrames = 0;
|
||||
for( ; !theQuitIndicator ; ++numberOfFrames)
|
||||
{
|
||||
// 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
|
||||
if(!thePauseIndicator)
|
||||
{
|
||||
theConsole->mediaSource().update();
|
||||
sound.mute(false);
|
||||
sound.updateSound(theConsole->mediaSource());
|
||||
}
|
||||
|
||||
/*
|
||||
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)))
|
||||
else
|
||||
{
|
||||
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);
|
||||
|
||||
// Wait until next retrace has begun
|
||||
while(!(inp(0x3DA) & 0x08));
|
||||
}
|
||||
endOfLastVsync = uclock();
|
||||
*/
|
||||
|
||||
|
||||
// Update the display and handle events
|
||||
updateDisplay(theConsole->mediaSource());
|
||||
handleEvents();
|
||||
|
||||
// Now, waste time if we need to so that we are at the desired frame rate
|
||||
for(;;)
|
||||
|
||||
// Waste time if we need to so that we are at the desired frame rate
|
||||
if(!(windowsNtFlag || theSynchronizeVideoFlag))
|
||||
{
|
||||
uclock_t delta = uclock() - before;
|
||||
|
||||
if(delta > (UCLOCKS_PER_SEC / theDesiredFrameRate))
|
||||
for(;;)
|
||||
{
|
||||
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
|
||||
uclock_t endingTime = uclock();
|
||||
clock_t endingTime = clock();
|
||||
|
||||
// Close the sound device
|
||||
sound.close();
|
||||
|
@ -1138,8 +1201,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
if(theShowInfoFlag)
|
||||
{
|
||||
double executionTime = (endingTime - startingTime) /
|
||||
(double)UCLOCKS_PER_SEC;
|
||||
double executionTime = (endingTime - startingTime) / (double)CLOCKS_PER_SEC;
|
||||
double framesPerSecond = numberOfFrames / executionTime;
|
||||
|
||||
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