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:
bwmott 2003-02-17 05:17:42 +00:00
parent 2a66c0ea9a
commit 2b0b275655
3 changed files with 499 additions and 116 deletions

View File

@ -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, &regs, &regs);
// 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;

287
stella/src/ui/dos/vga.cxx Normal file
View File

@ -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;
}

34
stella/src/ui/dos/vga.hxx Normal file
View File

@ -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