aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ0WI <J0WI@users.noreply.github.com>2020-09-24 00:12:07 +0200
committerLeo <thinkabit.ukim@gmail.com>2020-09-25 21:09:06 +0000
commit70ebaff31b3a5667868ffcac40286ab0cc4d8181 (patch)
tree2530647689bb1b1f43807c6b5878d4191376524f
parentc47c5de949589c2ae6e78fc1eb8a52bfbe01f912 (diff)
downloadaports-70ebaff31b3a5667868ffcac40286ab0cc4d8181.tar.gz
aports-70ebaff31b3a5667868ffcac40286ab0cc4d8181.tar.bz2
aports-70ebaff31b3a5667868ffcac40286ab0cc4d8181.tar.xz
testing/mame: upgrade to 0.224
-rw-r--r--testing/mame/APKBUILD25
-rw-r--r--testing/mame/groovymame.patch7810
-rw-r--r--testing/mame/nonag.patch23
3 files changed, 7819 insertions, 39 deletions
diff --git a/testing/mame/APKBUILD b/testing/mame/APKBUILD
index 6553cee192..203089d400 100644
--- a/testing/mame/APKBUILD
+++ b/testing/mame/APKBUILD
@@ -1,11 +1,11 @@
# Contributor: Taner Tas <taner76@gmail.com>
# Maintainer: Taner Tas <taner76@gmail.com>
pkgname=mame
-pkgver=0.221
+pkgver=0.224
_pkgver=${pkgver/.}
pkgrel=0
pkgdesc="Multi Arcade Machine Emulator with GroovyMAME/Switchres/No-nag patchset."
-url="https://mamedev.org"
+url="https://mamedev.org/"
arch="all !mips !mips64"
license="GPL-2.0-or-later"
depends="$pkgname-common"
@@ -13,7 +13,6 @@ makedepends="
alsa-lib-dev
clang-dev
coreutils
- dos2unix
expat-dev
flac-dev
fontconfig-dev
@@ -27,7 +26,7 @@ makedepends="
nasm
portaudio-dev
portmidi-dev
- python2-dev
+ python3-dev
py3-sphinx
rapidjson-dev
sdl2-dev
@@ -45,24 +44,17 @@ subpackages="
$pkgname-tools
$pkgname-plugins::noarch
"
-_groovymame_patch=groovymame-$pkgver.diff
source="
- https://github.com/mamedev/mame/archive/$pkgname$_pkgver.tar.gz
- $_groovymame_patch::https://drive.google.com/uc?export=download&id=1MLdYfcSszGhuodxV5mDxQyhqJcanDmeS
+ https://github.com/mamedev/mame/archive/mame$_pkgver.tar.gz
mame.ini
midi.conf
fix-musl.patch
+ groovymame.patch
nonag.patch
"
options="!check" # No test suite
builddir="$srcdir"/$pkgname-$pkgname${pkgver/.}
-prepare() {
- default_prepare
- cp "$srcdir"/$_groovymame_patch . && dos2unix $_groovymame_patch
- patch -p1 < $_groovymame_patch
-}
-
build() {
case "$CARCH" in
x86|armhf|armv7) _PTR64=0;;
@@ -111,6 +103,7 @@ _build="make
NOASM=$_NOASM
BIGENDIAN=$_BIGENDIAN
TOOLS=
+ PYTHON_EXECUTABLE=python3
SDL_INI_PATH=/etc/mame
USE_SYSTEM_LIB_EXPAT=1
USE_SYSTEM_LIB_ZLIB=1
@@ -219,9 +212,9 @@ lang() {
mkdir -p "$subpkgdir"/usr/share/$pkgname
cp -r language "$subpkgdir"/usr/share/$pkgname/
}
-sha512sums="8a06684cad3e6e719c548fd09d4c7bbd0a11126270632c38db5b9c521cf1cb0e9ad30a194c50be3a01f4dc86a3e92e075f9a4ee52a6a86aa1db50f5a56640257 mame0221.tar.gz
-8961e417568d9e047edd92b204713466be7d06d790125a14688a2d14312ec9c52b6b46e33ab2e9a0e88443ef900d901002fafe640e31d1c255360074e04a6490 groovymame-0.221.diff
+sha512sums="a5e01dd017cb78a5aea56f48d7bc2375f2c2b8d4c30b47b1e3f543ee74fb9777c01d8e8305655034e28e6929ce8bd82e9b066333f15718b44ded3adcd6df9b5b mame0224.tar.gz
dc008245cbea0b94f58d83e09bf5fd3fff04ac0e2f3a36b910a8b7633c5377419fc67a1fd366ef268e283f744d9a8d29928cfacf456b3edaa2d0b1a11d46a701 mame.ini
8f83ff5a916f4ff8e86c5afbdfe4475f7780bb36c20c78d6d029d0eb0dafd77b3471faa538aca384001d2049dc94c4df3429c67d743adde9fd6329c91e6d19a2 midi.conf
75bba366aebb37de7758368fbf7418194a18d535e61c1768e6c2c5cf4b3b7a2f625ef687cb8278c03daa9e308951df4c0bdcc944dfcc4ce5305f5ac83e5e049b fix-musl.patch
-864816a55f35f9d485ccd143a1e0acd76d47239a6d5344be2a76b50fd4efbdfb4f3e45318d7dfda67faa63c0a52022f2e8313f058965a1eba60e6ca4677a519b nonag.patch"
+9107239ea2081cbc5c445559c4f0abf0f6833e6d9df16e4558a2375e5925f789ac749f24744c9d85150fe6822d9ebdafa437cf06419f6bd7beae343040dbf3b4 groovymame.patch
+b9d0373472807b81f59be1c8c6a90e3c6774e864a3e73ca4336040bf032c04b270182f8f97bcced8b4f345bcde1f97e114568857a258f9ff010d9b575e61f97c nonag.patch"
diff --git a/testing/mame/groovymame.patch b/testing/mame/groovymame.patch
new file mode 100644
index 0000000000..e16837b107
--- /dev/null
+++ b/testing/mame/groovymame.patch
@@ -0,0 +1,7810 @@
+diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua
+index b7736fb4c27..2be746a8b5f 100644
+--- a/scripts/src/emu.lua
++++ b/scripts/src/emu.lua
+@@ -259,6 +259,11 @@ files {
+ MAME_DIR .. "src/emu/video/rgbsse.h",
+ MAME_DIR .. "src/emu/video/rgbvmx.cpp",
+ MAME_DIR .. "src/emu/video/rgbvmx.h",
++ MAME_DIR .. "src/emu/switchres/modeline.cpp",
++ MAME_DIR .. "src/emu/switchres/monitor.cpp",
++ MAME_DIR .. "src/emu/switchres/util.cpp",
++ MAME_DIR .. "src/emu/switchres/switchres.cpp",
++ MAME_DIR .. "src/emu/switchres/switchres.h",
+ }
+
+ pchsource(MAME_DIR .. "src/emu/main.cpp")
+diff --git a/scripts/src/osd/sdl.lua b/scripts/src/osd/sdl.lua
+index 10aa9940c52..0a87568a2aa 100644
+--- a/scripts/src/osd/sdl.lua
++++ b/scripts/src/osd/sdl.lua
+@@ -47,12 +47,19 @@ function maintargetosdoptions(_target,_subtarget)
+ if BASE_TARGETOS=="unix" and _OPTIONS["targetos"]~="macosx" and _OPTIONS["targetos"]~="android" and _OPTIONS["targetos"]~="asmjs" then
+ links {
+ "SDL2_ttf",
++ "Xrandr",
+ }
+ local str = backtick(pkgconfigcmd() .. " --libs fontconfig")
+ addlibfromstring(str)
+ addoptionsfromstring(str)
+ end
+
++ if BASE_TARGETOS=="unix" and _OPTIONS["targetos"]=="linux" then
++ local str = backtick("pkg-config --libs libdrm")
++ addlibfromstring(str)
++ addoptionsfromstring(str)
++ end
++
+ if _OPTIONS["targetos"]=="windows" then
+ if _OPTIONS["with-bundled-sdl2"]~=nil then
+ configuration { "mingw*"}
+@@ -435,6 +442,7 @@ project ("osd_" .. _OPTIONS["osd"])
+ MAME_DIR .. "src/osd/sdl/video.cpp",
+ MAME_DIR .. "src/osd/sdl/window.cpp",
+ MAME_DIR .. "src/osd/sdl/window.h",
++ MAME_DIR .. "src/osd/sdl/switchres_sdl.cpp",
+ MAME_DIR .. "src/osd/modules/osdwindow.cpp",
+ MAME_DIR .. "src/osd/modules/osdwindow.h",
+ MAME_DIR .. "src/osd/modules/render/drawsdl.cpp",
+diff --git a/scripts/src/osd/sdl_cfg.lua b/scripts/src/osd/sdl_cfg.lua
+index e942e50fdde..c9b37d07c70 100644
+--- a/scripts/src/osd/sdl_cfg.lua
++++ b/scripts/src/osd/sdl_cfg.lua
+@@ -134,6 +134,9 @@ if _OPTIONS["targetos"]=="windows" then
+ configuration { }
+
+ elseif _OPTIONS["targetos"]=="linux" then
++ buildoptions {
++ backtick("pkg-config --cflags libdrm"),
++ }
+ if _OPTIONS["QT_HOME"]~=nil then
+ buildoptions {
+ "-I" .. backtick(_OPTIONS["QT_HOME"] .. "/bin/qmake -query QT_INSTALL_HEADERS"),
+diff --git a/scripts/src/osd/windows.lua b/scripts/src/osd/windows.lua
+index 963980b22ca..91344edea4a 100644
+--- a/scripts/src/osd/windows.lua
++++ b/scripts/src/osd/windows.lua
+@@ -45,6 +45,7 @@ function maintargetosdoptions(_target,_subtarget)
+ "psapi",
+ "ole32",
+ "shlwapi",
++ "dwmapi",
+ }
+ end
+
+@@ -170,6 +171,16 @@ project ("osd_" .. _OPTIONS["osd"])
+ MAME_DIR .. "src/osd/windows/winmenu.cpp",
+ MAME_DIR .. "src/osd/windows/winmain.cpp",
+ MAME_DIR .. "src/osd/windows/winmain.h",
++ MAME_DIR .. "src/osd/windows/switchres_windows.cpp",
++ MAME_DIR .. "src/osd/windows/custom_video.cpp",
++ MAME_DIR .. "src/osd/windows/custom_video.h",
++ MAME_DIR .. "src/osd/windows/custom_video_ati.cpp",
++ MAME_DIR .. "src/osd/windows/custom_video_ati.h",
++ MAME_DIR .. "src/osd/windows/custom_video_adl.cpp",
++ MAME_DIR .. "src/osd/windows/custom_video_adl.h",
++ MAME_DIR .. "src/osd/windows/custom_video_ati_family.cpp",
++ MAME_DIR .. "src/osd/windows/custom_video_pstrip.cpp",
++ MAME_DIR .. "src/osd/windows/custom_video_pstrip.h",
+ MAME_DIR .. "src/osd/osdepend.h",
+ MAME_DIR .. "src/osd/modules/debugger/win/consolewininfo.cpp",
+ MAME_DIR .. "src/osd/modules/debugger/win/consolewininfo.h",
+diff --git a/src/emu/drivenum.cpp b/src/emu/drivenum.cpp
+index 5a29dcabd9a..34f5a912cb0 100644
+--- a/src/emu/drivenum.cpp
++++ b/src/emu/drivenum.cpp
+@@ -238,7 +238,7 @@ void driver_enumerator::find_approximate_matches(std::string const &string, std:
+ if (m_included[index])
+ templist[arrayindex++] = index;
+ assert(arrayindex == m_filtered_count);
+-
++/*
+ // shuffle
+ for (int shufnum = 0; shufnum < (4 * s_driver_count); shufnum++)
+ {
+@@ -248,7 +248,7 @@ void driver_enumerator::find_approximate_matches(std::string const &string, std:
+ templist[item1] = templist[item2];
+ templist[item2] = temp;
+ }
+-
++*/
+ // copy out the first few entries
+ for (int matchnum = 0; matchnum < count; matchnum++)
+ results[matchnum] = templist[matchnum % m_filtered_count];
+diff --git a/src/emu/drivers/empty.cpp b/src/emu/drivers/empty.cpp
+index faa17f60158..47744719cb9 100644
+--- a/src/emu/drivers/empty.cpp
++++ b/src/emu/drivers/empty.cpp
+@@ -52,7 +52,7 @@ void empty_state::___empty(machine_config &config)
+ screen.set_screen_update(FUNC(empty_state::screen_update));
+ screen.set_size(640, 480);
+ screen.set_visarea(0, 639, 0, 479);
+- screen.set_refresh_hz(30);
++ screen.set_refresh_hz(61);
+ }
+
+
+diff --git a/src/emu/emu.h b/src/emu/emu.h
+index 35a5d6a3f60..ee75aa354ba 100644
+--- a/src/emu/emu.h
++++ b/src/emu/emu.h
+@@ -84,6 +84,9 @@
+ #include "gamedrv.h"
+ #include "parameters.h"
+
++// Switchres
++#include "switchres/switchres.h"
++
+ // the running machine
+ #include "main.h"
+ #include "machine.h"
+@@ -108,4 +111,7 @@
+ // member templates that don't like incomplete types
+ #include "device.ipp"
+
++// Switchres prototypes
++#include "switchres/switchres_proto.h"
++
+ #endif // __EMU_H__
+diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp
+index a48a4f80811..5a60c9338ea 100644
+--- a/src/emu/emuopts.cpp
++++ b/src/emu/emuopts.cpp
+@@ -85,10 +85,12 @@ const options_entry emu_options::s_option_entries[] =
+ { OPTION_FRAMESKIP ";fs(0-10)", "0", OPTION_INTEGER, "set frameskip to fixed value, 0-10 (autoframeskip must be disabled)" },
+ { OPTION_SECONDS_TO_RUN ";str", "0", OPTION_INTEGER, "number of emulated seconds to run before automatically exiting" },
+ { OPTION_THROTTLE, "1", OPTION_BOOLEAN, "throttle emulation to keep system running in sync with real time" },
++ { OPTION_SYNCREFRESH ";srf", "0", OPTION_BOOLEAN, "enable using the start of VBLANK for throttling instead of the game time" },
++ { OPTION_AUTOSYNC ";as", "1", OPTION_BOOLEAN, "automatically enable syncrefresh if refresh difference is below syncrefresh_tolerance" },
+ { OPTION_SLEEP, "1", OPTION_BOOLEAN, "enable sleeping, which gives time back to other applications when idle" },
+ { OPTION_SPEED "(0.01-100)", "1.0", OPTION_FLOAT, "controls the speed of gameplay, relative to realtime; smaller numbers are slower" },
+ { OPTION_REFRESHSPEED ";rs", "0", OPTION_BOOLEAN, "automatically adjust emulation speed to keep the emulated refresh rate slower than the host screen" },
+- { OPTION_LOWLATENCY ";lolat", "0", OPTION_BOOLEAN, "draws new frame before throttling to reduce input latency" },
++ { OPTION_LOWLATENCY ";lolat", "1", OPTION_BOOLEAN, "draws new frame before throttling to reduce input latency" },
+
+ // render options
+ { nullptr, nullptr, OPTION_HEADER, "CORE RENDER OPTIONS" },
+@@ -113,7 +115,7 @@ const options_entry emu_options::s_option_entries[] =
+
+ // artwork options
+ { nullptr, nullptr, OPTION_HEADER, "CORE ARTWORK OPTIONS" },
+- { OPTION_ARTWORK_CROP ";artcrop", "0", OPTION_BOOLEAN, "crop artwork so emulated screen image fills output screen/window in one axis" },
++ { OPTION_ARTWORK_CROP ";artcrop", "1", OPTION_BOOLEAN, "crop artwork so emulated screen image fills output screen/window in one axis" },
+ { OPTION_FALLBACK_ARTWORK, nullptr, OPTION_STRING, "fallback artwork if no external artwork or internal driver layout defined" },
+ { OPTION_OVERRIDE_ARTWORK, nullptr, OPTION_STRING, "override artwork for external artwork and internal driver layout" },
+
+@@ -216,6 +218,38 @@ const options_entry emu_options::s_option_entries[] =
+ { OPTION_HTTP_PORT, "8080", OPTION_INTEGER, "HTTP server port" },
+ { OPTION_HTTP_ROOT, "web", OPTION_STRING, "HTTP server document root" },
+
++ // Switchres options
++ { nullptr, nullptr, OPTION_HEADER, "CORE SWITCHRES OPTIONS" },
++ { OPTION_MODELINE_GENERATION ";ml", "1", OPTION_BOOLEAN, "Automatic generation of modelines based on the specified monitor type" },
++ { OPTION_MONITOR ";m", "generic_15",OPTION_STRING, "Monitor type, e.g.: generic_15, arcade_15, lcd, custom, etc." },
++ { OPTION_ORIENTATION ";or", "horizontal",OPTION_STRING, "Monitor orientation (horizontal|vertical|rotate|rotate_r|rotate_l)" },
++ { OPTION_CONNECTOR ";cn", "auto", OPTION_STRING, "[Linux] video card output (VGA-0|VGA-1|DVI-0|DVI-1)" },
++ { OPTION_INTERLACE ";in", "1", OPTION_BOOLEAN, "Enable interlaced scanning when necessary" },
++ { OPTION_DOUBLESCAN ";ds", "1", OPTION_BOOLEAN, "Enable double scanning when necessary (unsupported under Windows)" },
++ { OPTION_SUPER_WIDTH ";cs", "2560", OPTION_INTEGER, "Automatically apply -unevenstretchx if resolution width is equal or greater than this value" },
++ { OPTION_CHANGERES ";cr", "1", OPTION_BOOLEAN, "Enable dynamic in-game video mode switching" },
++ { OPTION_POWERSTRIP ";ps", "0", OPTION_BOOLEAN, "Use Powerstrip API for dynamic setting of custom video timings" },
++ { OPTION_LOCK_SYSTEM_MODES ";lsm", "1", OPTION_BOOLEAN, "Lock system (non-custom) video modes, only use modes created by us" },
++ { OPTION_LOCK_UNSUPPORTED_MODES ";lum", "1", OPTION_BOOLEAN, "Lock video modes reported as unsupported by your monitor's EDID" },
++ { OPTION_REFRESH_DONT_CARE ";rdc", "0", OPTION_BOOLEAN, "Ignore video mode's refresh reported by OS when checking ranges" },
++ { OPTION_DOTCLOCK_MIN ";dcm", "0", OPTION_STRING, "Lowest pixel clock supported by video card, in MHz, default is 0" },
++ { OPTION_SYNC_REFRESH_TOLERANCE ";srt", "2.0", OPTION_STRING, "Maximum refresh difference, in Hz, allowed in order to synchronize" },
++ { OPTION_FRAME_DELAY ";fd", "0", OPTION_INTEGER, "Delays the start of each frame to minimize input lag (0-9)"},
++ { OPTION_VSYNC_OFFSET, "0", OPTION_INTEGER, "Offset vsync position by this many lines to prevent tearing with frame_delay and high-resolution displays" },
++ { OPTION_BLACK_FRAME_INSERTION ";bfi", "0", OPTION_BOOLEAN, "Inserts a black frame after each normal frame, intended to reduce motion blur on 120 Hz monitors" },
++ { OPTION_MODELINE ";mode", "auto", OPTION_STRING, "Use custom defined modeline" },
++ { OPTION_PS_TIMING ";pst", "auto", OPTION_STRING, "Use custom Powertrip timing string" },
++ { OPTION_LCD_RANGE ";lcd", "auto", OPTION_STRING, "Add custom LCD range, VfreqMin-VfreqMax, in Hz, e.g.: 55.50-61.00" },
++ { OPTION_CRT_RANGE0 ";crt0", "auto", OPTION_STRING, "Add custom CRT range, see documentation for details." },
++ { OPTION_CRT_RANGE1 ";crt1", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE2 ";crt2", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE3 ";crt3", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE4 ";crt4", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE5 ";crt5", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE6 ";crt6", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE7 ";crt7", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE8 ";crt8", "auto", OPTION_STRING, "Add custom CRT range" },
++ { OPTION_CRT_RANGE9 ";crt9", "auto", OPTION_STRING, "Add custom CRT range" },
+ { nullptr }
+ };
+
+diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h
+index a1a23e77a2b..1a12f23d99b 100644
+--- a/src/emu/emuopts.h
++++ b/src/emu/emuopts.h
+@@ -72,6 +72,8 @@
+ #define OPTION_FRAMESKIP "frameskip"
+ #define OPTION_SECONDS_TO_RUN "seconds_to_run"
+ #define OPTION_THROTTLE "throttle"
++#define OPTION_SYNCREFRESH "syncrefresh"
++#define OPTION_AUTOSYNC "autosync"
+ #define OPTION_SLEEP "sleep"
+ #define OPTION_SPEED "speed"
+ #define OPTION_REFRESHSPEED "refreshspeed"
+@@ -193,6 +195,38 @@
+ #define OPTION_HTTP_PORT "http_port"
+ #define OPTION_HTTP_ROOT "http_root"
+
++/* Switchres Options */
++#define OPTION_MODELINE_GENERATION "modeline_generation"
++#define OPTION_MONITOR "monitor"
++#define OPTION_CONNECTOR "connector"
++#define OPTION_ORIENTATION "orientation"
++#define OPTION_INTERLACE "interlace"
++#define OPTION_DOUBLESCAN "doublescan"
++#define OPTION_SUPER_WIDTH "super_width"
++#define OPTION_CHANGERES "changeres"
++#define OPTION_POWERSTRIP "powerstrip"
++#define OPTION_LOCK_SYSTEM_MODES "lock_system_modes"
++#define OPTION_LOCK_UNSUPPORTED_MODES "lock_unsupported_modes"
++#define OPTION_REFRESH_DONT_CARE "refresh_dont_care"
++#define OPTION_DOTCLOCK_MIN "dotclock_min"
++#define OPTION_SYNC_REFRESH_TOLERANCE "sync_refresh_tolerance"
++#define OPTION_FRAME_DELAY "frame_delay"
++#define OPTION_VSYNC_OFFSET "vsync_offset"
++#define OPTION_BLACK_FRAME_INSERTION "black_frame_insertion"
++#define OPTION_MODELINE "modeline"
++#define OPTION_PS_TIMING "ps_timing"
++#define OPTION_LCD_RANGE "lcd_range"
++#define OPTION_CRT_RANGE0 "crt_range0"
++#define OPTION_CRT_RANGE1 "crt_range1"
++#define OPTION_CRT_RANGE2 "crt_range2"
++#define OPTION_CRT_RANGE3 "crt_range3"
++#define OPTION_CRT_RANGE4 "crt_range4"
++#define OPTION_CRT_RANGE5 "crt_range5"
++#define OPTION_CRT_RANGE6 "crt_range6"
++#define OPTION_CRT_RANGE7 "crt_range7"
++#define OPTION_CRT_RANGE8 "crt_range8"
++#define OPTION_CRT_RANGE9 "crt_range9"
++
+ //**************************************************************************
+ // TYPE DEFINITIONS
+ //**************************************************************************
+@@ -350,6 +384,8 @@ public:
+ int frameskip() const { return int_value(OPTION_FRAMESKIP); }
+ int seconds_to_run() const { return int_value(OPTION_SECONDS_TO_RUN); }
+ bool throttle() const { return bool_value(OPTION_THROTTLE); }
++ bool sync_refresh() const { return bool_value(OPTION_SYNCREFRESH); }
++ bool autosync() const { return bool_value(OPTION_AUTOSYNC); }
+ bool sleep() const { return m_sleep; }
+ float speed() const { return float_value(OPTION_SPEED); }
+ bool refresh_speed() const { return m_refresh_speed; }
+@@ -444,6 +480,38 @@ public:
+ const char *ram_size() const { return value(OPTION_RAMSIZE); }
+ bool nvram_save() const { return bool_value(OPTION_NVRAM_SAVE); }
+
++ // Switchres options
++ bool modeline_generation() const { return bool_value(OPTION_MODELINE_GENERATION); }
++ const char *monitor() const { return value(OPTION_MONITOR); }
++ const char *connector() const { return value(OPTION_CONNECTOR); }
++ const char *orientation() const { return value(OPTION_ORIENTATION); }
++ bool doublescan() const { return bool_value(OPTION_DOUBLESCAN); }
++ bool interlace() const { return bool_value(OPTION_INTERLACE); }
++ int super_width() const { return int_value(OPTION_SUPER_WIDTH); }
++ int changeres() const { return int_value(OPTION_CHANGERES); }
++ bool powerstrip() const { return bool_value(OPTION_POWERSTRIP); }
++ bool lock_system_modes() const { return bool_value(OPTION_LOCK_SYSTEM_MODES); }
++ bool lock_unsupported_modes() const { return bool_value(OPTION_LOCK_UNSUPPORTED_MODES); }
++ bool refresh_dont_care() const { return bool_value(OPTION_REFRESH_DONT_CARE); }
++ const char *dotclock_min() const { return value(OPTION_DOTCLOCK_MIN); }
++ const char *sync_refresh_tolerance() const { return value(OPTION_SYNC_REFRESH_TOLERANCE); }
++ int frame_delay() const { return int_value(OPTION_FRAME_DELAY); }
++ int vsync_offset() const { return int_value(OPTION_VSYNC_OFFSET); }
++ bool black_frame_insertion() const { return bool_value(OPTION_BLACK_FRAME_INSERTION); }
++ const char *modeline() const { return value(OPTION_MODELINE); }
++ const char *ps_timing() const { return value(OPTION_PS_TIMING); }
++ const char *lcd_range() const { return value(OPTION_LCD_RANGE); }
++ const char *crt_range0() const { return value(OPTION_CRT_RANGE0); }
++ const char *crt_range1() const { return value(OPTION_CRT_RANGE1); }
++ const char *crt_range2() const { return value(OPTION_CRT_RANGE2); }
++ const char *crt_range3() const { return value(OPTION_CRT_RANGE3); }
++ const char *crt_range4() const { return value(OPTION_CRT_RANGE4); }
++ const char *crt_range5() const { return value(OPTION_CRT_RANGE5); }
++ const char *crt_range6() const { return value(OPTION_CRT_RANGE6); }
++ const char *crt_range7() const { return value(OPTION_CRT_RANGE7); }
++ const char *crt_range8() const { return value(OPTION_CRT_RANGE8); }
++ const char *crt_range9() const { return value(OPTION_CRT_RANGE9); }
++
+ // core comm options
+ const char *comm_localhost() const { return value(OPTION_COMM_LOCAL_HOST); }
+ const char *comm_localport() const { return value(OPTION_COMM_LOCAL_PORT); }
+diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp
+index 7b86af34925..0b93346b0d3 100644
+--- a/src/emu/machine.cpp
++++ b/src/emu/machine.cpp
+@@ -135,6 +135,7 @@ running_machine::running_machine(const machine_config &_config, machine_manager
+ m_dummy_space(_config, "dummy_space", &root_device(), 0)
+ {
+ memset(&m_base_time, 0, sizeof(m_base_time));
++ memset(&switchres, 0, sizeof(switchres));
+
+ m_dummy_space.set_machine(*this);
+ m_dummy_space.config_complete();
+diff --git a/src/emu/machine.h b/src/emu/machine.h
+index 205988fbbe6..5d641a1cb7e 100644
+--- a/src/emu/machine.h
++++ b/src/emu/machine.h
+@@ -257,6 +257,9 @@ public:
+ std::string compose_saveload_filename(std::string &&base_filename, const char **searchpath = nullptr);
+ std::string get_statename(const char *statename_opt) const;
+
++ // SwitchRes manager
++ switchres_manager switchres; // SwitchRes data
++
+ private:
+ // side effect disable counter
+ u32 m_side_effects_disabled;
+diff --git a/src/emu/sound.cpp b/src/emu/sound.cpp
+index c876431c41b..15fd0116787 100644
+--- a/src/emu/sound.cpp
++++ b/src/emu/sound.cpp
+@@ -866,16 +866,13 @@ sound_manager::sound_manager(running_machine &machine)
+ machine.add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&sound_manager::resume, this));
+ machine.add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&sound_manager::reset, this));
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&sound_manager::stop_recording, this));
++ machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&sound_manager::update, this));
+
+ // register global states
+ machine.save().save_item(NAME(m_last_update));
+
+ // set the starting attenuation
+ set_attenuation(machine.options().volume());
+-
+- // start the periodic update flushing timer
+- m_update_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(sound_manager::update), this));
+- m_update_timer->adjust(STREAMS_UPDATE_ATTOTIME, 0, STREAMS_UPDATE_ATTOTIME);
+ }
+
+
+@@ -1077,7 +1074,7 @@ void sound_manager::config_save(config_type cfg_type, util::xml::data_node *pare
+ // and send it to the OSD layer
+ //-------------------------------------------------
+
+-void sound_manager::update(void *ptr, int param)
++void sound_manager::update()
+ {
+ VPRINTF(("sound_update\n"));
+
+@@ -1089,13 +1086,13 @@ void sound_manager::update(void *ptr, int param)
+ speaker.mix(&m_leftmix[0], &m_rightmix[0], m_samples_this_update, (m_muted & MUTE_REASON_SYSTEM));
+
+ // now downmix the final result
+- u32 finalmix_step = machine().video().speed_factor();
++ u32 finalmix_step = machine().video().speed_factor() * 100;
+ u32 finalmix_offset = 0;
+ s16 *finalmix = &m_finalmix[0];
+ int sample;
+- for (sample = m_finalmix_leftover; sample < m_samples_this_update * 1000; sample += finalmix_step)
++ for (sample = m_finalmix_leftover; sample < m_samples_this_update * 100000; sample += finalmix_step)
+ {
+- int sampindex = sample / 1000;
++ int sampindex = sample / 100000;
+
+ // clamp the left side
+ s32 samp = m_leftmix[sampindex];
+@@ -1113,7 +1110,7 @@ void sound_manager::update(void *ptr, int param)
+ samp = 32767;
+ finalmix[finalmix_offset++] = samp;
+ }
+- m_finalmix_leftover = sample - m_samples_this_update * 1000;
++ m_finalmix_leftover = sample - m_samples_this_update * 100000;
+
+ // play the result
+ if (finalmix_offset > 0)
+diff --git a/src/emu/sound.h b/src/emu/sound.h
+index 7301a4d69ba..91fd6b0de4a 100644
+--- a/src/emu/sound.h
++++ b/src/emu/sound.h
+@@ -225,7 +225,7 @@ private:
+ void config_load(config_type cfg_type, util::xml::data_node const *parentnode);
+ void config_save(config_type cfg_type, util::xml::data_node *parentnode);
+
+- void update(void *ptr = nullptr, s32 param = 0);
++ void update();
+
+ // internal state
+ running_machine & m_machine; // reference to our machine
+diff --git a/src/emu/switchres/modeline.cpp b/src/emu/switchres/modeline.cpp
+new file mode 100644
+index 00000000000..8eddd2b4e2a
+--- /dev/null
++++ b/src/emu/switchres/modeline.cpp
+@@ -0,0 +1,690 @@
++/**************************************************************
++
++ modeline.cpp - Modeline generation and scoring routines
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#include "emu.h"
++
++#define max(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a > _b ? _a : _b; })
++#define min(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a < _b ? _a : _b; })
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++int get_line_params(modeline *mode, monitor_range *range);
++int scale_into_range (int value, int lower_limit, int higher_limit);
++int scale_into_range (float value, float lower_limit, float higher_limit);
++int scale_into_aspect (int source_res, int tot_res, float original_monitor_aspect, float users_monitor_aspect, float *best_diff);
++int stretch_into_range(float vfreq, monitor_range *range, bool interlace_allowed, float *interlace);
++int total_lines_for_yres(int yres, float vfreq, monitor_range *range, float interlace);
++float max_vfreq_for_yres (int yres, monitor_range *range, float interlace);
++int round_near (double number);
++
++//============================================================
++// modeline_create
++//============================================================
++
++int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, config_settings *cs)
++{
++ float vfreq = 0;
++ float vfreq_real = 0;
++ int xres = 0;
++ int yres = 0;
++ float interlace = 1;
++ float doublescan = 1;
++ float scan_factor = 1;
++ int x_scale = 0;
++ int y_scale = 0;
++ int v_scale = 0;
++ float x_diff = 0;
++ float y_diff = 0;
++ float v_diff = 0;
++ float y_ratio = 0;
++ float x_ratio = 0;
++
++ // init all editable fields with source or user values
++ if (t_mode->type & X_RES_EDITABLE)
++ xres = cs->width? cs->width : s_mode->hactive;
++ else
++ xres = t_mode->hactive;
++ if (t_mode->type & Y_RES_EDITABLE)
++ yres = cs->height? cs->height : s_mode->vactive;
++ else
++ yres = t_mode->vactive;
++ if (t_mode->type & V_FREQ_EDITABLE)
++ vfreq = s_mode->vfreq;
++ else
++ vfreq = t_mode->vfreq;
++
++ // lock resolution fields if required
++ if (cs->width) t_mode->type &= ~X_RES_EDITABLE;
++ if (cs->height) t_mode->type &= ~Y_RES_EDITABLE;
++
++ // иии Vertical refresh иии
++ // try to fit vertical frequency into current range
++ v_scale = scale_into_range(vfreq, range->vfreq_min, range->vfreq_max);
++
++ if (!v_scale && (t_mode->type & V_FREQ_EDITABLE))
++ {
++ vfreq = vfreq < range->vfreq_min? range->vfreq_min : range->vfreq_max;
++ v_scale = 1;
++ }
++ else if (v_scale != 1 && !(t_mode->type & V_FREQ_EDITABLE))
++ {
++ t_mode->result.weight |= R_OUT_OF_RANGE;
++ return -1;
++ }
++
++ // иии Vertical resolution иии
++ // try to fit active lines in the progressive range first
++ if (range->progressive_lines_min && (!t_mode->interlace || (t_mode->type & V_FREQ_EDITABLE)))
++ y_scale = scale_into_range(yres, range->progressive_lines_min, range->progressive_lines_max);
++
++ // if not possible, try to fit in the interlaced range, if any
++ if (!y_scale && range->interlaced_lines_min && cs->interlace && (t_mode->interlace || (t_mode->type & V_FREQ_EDITABLE)))
++ {
++ y_scale = scale_into_range(yres, range->interlaced_lines_min, range->interlaced_lines_max);
++ interlace = 2;
++ }
++
++ // if we succeeded, let's see if we can apply integer scaling
++ if (y_scale == 1 || (y_scale > 1 && (t_mode->type & Y_RES_EDITABLE)))
++ {
++ // check if we should apply doublescan
++ if (cs->doublescan && y_scale % 2 == 0)
++ {
++ y_scale /= 2;
++ doublescan = 0.5;
++ }
++ scan_factor = interlace * doublescan;
++
++ // calculate expected achievable refresh for this height
++ vfreq_real = min(vfreq * v_scale, max_vfreq_for_yres(yres * y_scale, range, scan_factor));
++ if (vfreq_real != vfreq * v_scale && !(t_mode->type & V_FREQ_EDITABLE))
++ {
++ t_mode->result.weight |= R_OUT_OF_RANGE;
++ return -1;
++ }
++
++ // calculate the ratio that our scaled yres represents with respect to the original height
++ y_ratio = float(yres) * y_scale / s_mode->vactive;
++ int y_source_scaled = s_mode->vactive * floor(y_ratio);
++
++ // if our original height doesn't fit the target height, we're forced to stretch
++ if (!y_source_scaled)
++ t_mode->result.weight |= R_RES_STRETCH;
++
++ // otherwise we try to perform integer scaling
++ else
++ {
++ if (t_mode->type & V_FREQ_EDITABLE)
++ {
++ // calculate y borders considering physical lines (instead of logical resolution)
++ int tot_yres = total_lines_for_yres(yres * y_scale, vfreq_real, range, scan_factor);
++ int tot_source = total_lines_for_yres(y_source_scaled, vfreq * v_scale, range, scan_factor);
++ y_diff = tot_yres > tot_source?float(tot_yres % tot_source) / tot_yres * 100:0;
++
++ // we penalize for the logical lines we need to add in order to meet the user's lower active lines limit
++ int y_min = interlace == 2?range->interlaced_lines_min:range->progressive_lines_min;
++ int tot_rest = (y_min >= y_source_scaled)? y_min % y_source_scaled:0;
++ y_diff += float(tot_rest) / tot_yres * 100;
++ }
++ else
++ y_diff = float((yres * y_scale) % y_source_scaled) / (yres * y_scale) * 100;
++
++ // we save the integer ratio between source and target resolutions, this will be used for prescaling
++ y_scale = floor(y_ratio);
++
++ // now if the borders obtained are low enough (< 10%) we'll finally apply integer scaling
++ // otherwise we'll stretch the original resolution over the target one
++ if (!(y_ratio >= 1.0 && y_ratio < 16.0 && y_diff < 10.0))
++ t_mode->result.weight |= R_RES_STRETCH;
++ }
++ }
++
++ // otherwise, check if we're allowed to apply fractional scaling
++ else if (t_mode->type & Y_RES_EDITABLE)
++ t_mode->result.weight |= R_RES_STRETCH;
++
++ // if there's nothing we can do, we're out of range
++ else
++ {
++ t_mode->result.weight |= R_OUT_OF_RANGE;
++ return -1;
++ }
++
++ // иии Horizontal resolution иии
++ // make the best possible adjustment of xres depending on what happened in the previous steps
++ // let's start with the SCALED case
++ if (!(t_mode->result.weight & R_RES_STRETCH))
++ {
++ // if we can, let's apply the same scaling to both directions
++ if (t_mode->type & X_RES_EDITABLE)
++ {
++ if (t_mode->type & Y_RES_EDITABLE) yres *= y_scale;
++ x_scale = y_scale;
++ xres = normalize(float(xres) * float(x_scale) * cs->monitor_aspect / (cs->effective_orientation? (1.0/(STANDARD_CRT_ASPECT)) : (STANDARD_CRT_ASPECT)), 8);
++ }
++
++ // otherwise, try to get the best out of our current xres
++ else
++ {
++ x_scale = xres / s_mode->hactive;
++ // if the source width fits our xres, try applying integer scaling
++ if (x_scale)
++ {
++ x_scale = scale_into_aspect(s_mode->hactive, xres, cs->effective_orientation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff);
++ if (x_diff > 15.0 && t_mode->width < cs->super_width)
++ t_mode->result.weight |= R_RES_STRETCH;
++ }
++ // otherwise apply fractional scaling
++ else
++ t_mode->result.weight |= R_RES_STRETCH;
++ }
++ }
++
++ // if the result was fractional scaling in any of the previous steps, deal with it
++ if (t_mode->result.weight & R_RES_STRETCH)
++ {
++ if (t_mode->type & Y_RES_EDITABLE)
++ {
++ // always try to use the interlaced range first if it exists, for better resolution
++ yres = stretch_into_range(vfreq, range, cs->interlace, &interlace);
++
++ // check in case we couldn't achieve the desired refresh
++ vfreq_real = min(vfreq, max_vfreq_for_yres(yres, range, interlace));
++ }
++
++ // check if we can create a normal aspect resolution
++ if (t_mode->type & X_RES_EDITABLE)
++ xres = max(xres, normalize(STANDARD_CRT_ASPECT * yres, 8));
++
++ // calculate integer scale for prescaling
++ x_scale = max(1, scale_into_aspect(s_mode->hactive, xres, cs->effective_orientation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff));
++ y_scale = max(1, floor(float(yres) / s_mode->vactive));
++
++ scan_factor = interlace;
++ doublescan = 1;
++ }
++
++ x_ratio = float(xres) / s_mode->hactive;
++ y_ratio = float(yres) / s_mode->vactive;
++ v_scale = max(round_near(vfreq_real / s_mode->vfreq), 1);
++ v_diff = (vfreq_real / v_scale) - s_mode->vfreq;
++ if (fabs(v_diff) > cs->sync_refresh_tolerance)
++ t_mode->result.weight |= R_V_FREQ_OFF;
++
++ // иии Modeline generation иии
++ // compute new modeline if we are allowed to
++ if (cs->modeline_generation && (t_mode->type & V_FREQ_EDITABLE))
++ {
++ float margin = 0;
++ float vblank_lines = 0;
++ float vvt_ini = 0;
++
++ // Get games basic resolution
++ t_mode->hactive = xres;
++ t_mode->vactive = yres;
++ t_mode->vfreq = vfreq_real;
++
++ // Get total vertical lines
++ vvt_ini = total_lines_for_yres(t_mode->vactive, t_mode->vfreq, range, scan_factor) + (interlace == 2?0.5:0);
++
++ // Calculate horizontal frequency
++ t_mode->hfreq = t_mode->vfreq * vvt_ini;
++
++ horizontal_values:
++
++ // Fill horizontal part of modeline
++ get_line_params(t_mode, range);
++
++ // Calculate pixel clock
++ t_mode->pclock = t_mode->htotal * t_mode->hfreq;
++ if (t_mode->pclock <= cs->pclock_min)
++ {
++ if (t_mode->type & X_RES_EDITABLE)
++ {
++ x_scale *= 2;
++ t_mode->hactive *= 2;
++ goto horizontal_values;
++ }
++ else
++ {
++ t_mode->result.weight |= R_OUT_OF_RANGE;
++ return -1;
++ }
++ }
++
++ // Vertical blanking
++ t_mode->vtotal = vvt_ini * scan_factor;
++ vblank_lines = int(t_mode->hfreq * range->vertical_blank) + (interlace == 2?0.5:0);
++ margin = (t_mode->vtotal - t_mode->vactive - vblank_lines * scan_factor) / 2;
++ t_mode->vbegin = t_mode->vactive + max(round_near(t_mode->hfreq * range->vfront_porch * scan_factor + margin), 1);
++ t_mode->vend = t_mode->vbegin + max(round_near(t_mode->hfreq * range->vsync_pulse * scan_factor), 1);
++
++ // Recalculate final vfreq
++ t_mode->vfreq = (t_mode->hfreq / t_mode->vtotal) * scan_factor;
++
++ t_mode->hsync = range->hsync_polarity;
++ t_mode->vsync = range->vsync_polarity;
++ t_mode->interlace = interlace == 2?1:0;
++ t_mode->doublescan = doublescan == 1?0:1;
++ }
++
++ // finally, store result
++ t_mode->result.x_scale = x_scale;
++ t_mode->result.y_scale = y_scale;
++ t_mode->result.v_scale = v_scale;
++ t_mode->result.x_diff = x_diff;
++ t_mode->result.y_diff = y_diff;
++ t_mode->result.v_diff = v_diff;
++ t_mode->result.x_ratio = x_ratio;
++ t_mode->result.y_ratio = y_ratio;
++ t_mode->result.v_ratio = 0;
++ t_mode->result.rotated = cs->effective_orientation;
++
++ return 0;
++}
++
++//============================================================
++// get_line_params
++//============================================================
++
++int get_line_params(modeline *mode, monitor_range *range)
++{
++ int hhi, hhf, hht;
++ int hh, hs, he, ht;
++ float line_time, char_time, new_char_time;
++ float hfront_porch_min, hsync_pulse_min, hback_porch_min;
++
++ hfront_porch_min = range->hfront_porch * .90;
++ hsync_pulse_min = range->hsync_pulse * .90;
++ hback_porch_min = range->hback_porch * .90;
++
++ line_time = 1 / mode->hfreq * 1000000;
++
++ hh = round(mode->hactive / 8);
++ hs = he = ht = 1;
++
++ do {
++ char_time = line_time / (hh + hs + he + ht);
++ if (hs * char_time < hfront_porch_min ||
++ fabs((hs + 1) * char_time - range->hfront_porch) < fabs(hs * char_time - range->hfront_porch))
++ hs++;
++
++ if (he * char_time < hsync_pulse_min ||
++ fabs((he + 1) * char_time - range->hsync_pulse) < fabs(he * char_time - range->hsync_pulse))
++ he++;
++
++ if (ht * char_time < hback_porch_min ||
++ fabs((ht + 1) * char_time - range->hback_porch) < fabs(ht * char_time - range->hback_porch))
++ ht++;
++
++ new_char_time = line_time / (hh + hs + he + ht);
++ } while (new_char_time != char_time);
++
++ hhi = (hh + hs) * 8;
++ hhf = (hh + hs + he) * 8;
++ hht = (hh + hs + he + ht) * 8;
++
++ mode->hbegin = hhi;
++ mode->hend = hhf;
++ mode->htotal = hht;
++
++ return 0;
++}
++
++//============================================================
++// scale_into_range
++//============================================================
++
++int scale_into_range (int value, int lower_limit, int higher_limit)
++{
++ int scale = 1;
++ while (value * scale < lower_limit) scale ++;
++ if (value * scale <= higher_limit)
++ return scale;
++ else
++ return 0;
++}
++
++//============================================================
++// scale_into_range
++//============================================================
++
++int scale_into_range (float value, float lower_limit, float higher_limit)
++{
++ int scale = 1;
++ while (value * scale < lower_limit) scale ++;
++ if (value * scale <= higher_limit)
++ return scale;
++ else
++ return 0;
++}
++
++//============================================================
++// scale_into_aspect
++//============================================================
++
++int scale_into_aspect (int source_res, int tot_res, float original_monitor_aspect, float users_monitor_aspect, float *best_diff)
++{
++ int scale = 1, best_scale = 1;
++ float diff = 0;
++ *best_diff = 0;
++
++ while (source_res * scale <= tot_res)
++ {
++ diff = fabs(1.0 - (users_monitor_aspect / (float(tot_res) / float(source_res * scale) * original_monitor_aspect))) * 100.0;
++ if (diff < *best_diff || *best_diff == 0)
++ {
++ *best_diff = diff;
++ best_scale = scale;
++ }
++ scale ++;
++ }
++ return best_scale;
++}
++
++//============================================================
++// stretch_into_range
++//============================================================
++
++int stretch_into_range(float vfreq, monitor_range *range, bool interlace_allowed, float *interlace)
++{
++ int yres, lower_limit;
++
++ if (range->interlaced_lines_min && interlace_allowed)
++ {
++ yres = range->interlaced_lines_max;
++ lower_limit = range->interlaced_lines_min;
++ *interlace = 2;
++ }
++ else
++ {
++ yres = range->progressive_lines_max;
++ lower_limit = range->progressive_lines_min;
++ }
++
++ while (yres > lower_limit && max_vfreq_for_yres(yres, range, *interlace) < vfreq)
++ yres -= 8;
++
++ return yres;
++}
++
++
++//============================================================
++// total_lines_for_yres
++//============================================================
++
++int total_lines_for_yres(int yres, float vfreq, monitor_range *range, float interlace)
++{
++ int vvt = max(yres / interlace + round_near(vfreq * yres / (interlace * (1.0 - vfreq * range->vertical_blank)) * range->vertical_blank), 1);
++ while ((vfreq * vvt < range->hfreq_min) && (vfreq * (vvt + 1) < range->hfreq_max)) vvt++;
++ return vvt;
++}
++
++//============================================================
++// max_vfreq_for_yres
++//============================================================
++
++float max_vfreq_for_yres (int yres, monitor_range *range, float interlace)
++{
++ return range->hfreq_max / (yres / interlace + round_near(range->hfreq_max * range->vertical_blank));
++}
++
++//============================================================
++// modeline_print
++//============================================================
++
++char * modeline_print(modeline *mode, char *modeline, int flags)
++{
++ char label[48]={'\x00'};
++ char params[192]={'\x00'};
++
++ if (flags & MS_LABEL)
++ sprintf(label, "\"%dx%d_%d %.6fKHz %.6fHz\"", mode->hactive, mode->vactive, mode->refresh, mode->hfreq/1000, mode->vfreq);
++
++ if (flags & MS_LABEL_SDL)
++ sprintf(label, "\"%dx%d_%.6f\"", mode->hactive, mode->vactive, mode->vfreq);
++
++ if (flags & MS_PARAMS)
++ sprintf(params, " %.6f %d %d %d %d %d %d %d %d %s %s %s %s", float(mode->pclock)/1000000.0, mode->hactive, mode->hbegin, mode->hend, mode->htotal, mode->vactive, mode->vbegin, mode->vend, mode->vtotal,
++ mode->interlace?"interlace":"", mode->doublescan?"doublescan":"", mode->hsync?"+hsync":"-hsync", mode->vsync?"+vsync":"-vsync");
++
++ sprintf(modeline, "%s%s", label, params);
++
++ return modeline;
++}
++
++//============================================================
++// modeline_result
++//============================================================
++
++char * modeline_result(modeline *mode, char *result)
++{
++ osd_printf_verbose(" rng(%d): ", mode->range);
++
++ if (mode->result.weight & R_OUT_OF_RANGE)
++ sprintf(result, " out of range");
++
++ else
++ sprintf(result, "%4d x%4d_%3.6f%s%s %3.6f [%s] scale(%d, %d, %d) diff(%.2f, %.2f, %.4f) ratio(%.3f, %.3f)",
++ mode->hactive, mode->vactive, mode->vfreq, mode->interlace?"i":"p", mode->doublescan?"d":"", mode->hfreq/1000, mode->result.weight & R_RES_STRETCH?"fract":"integ",
++ mode->result.x_scale, mode->result.y_scale, mode->result.v_scale, mode->result.x_diff, mode->result.y_diff, mode->result.v_diff, mode->result.x_ratio, mode->result.y_ratio);
++ return result;
++}
++
++//============================================================
++// modeline_compare
++//============================================================
++
++int modeline_compare(modeline *t, modeline *best)
++{
++ bool vector = (t->hactive == (int)t->result.x_ratio);
++
++ if (t->result.weight < best->result.weight)
++ return 1;
++
++ else if (t->result.weight <= best->result.weight)
++ {
++ float t_v_diff = fabs(t->result.v_diff);
++ float b_v_diff = fabs(best->result.v_diff);
++
++ if (t->result.weight & R_RES_STRETCH || vector)
++ {
++ float t_y_score = t->result.y_ratio * (t->interlace?(2.0/3.0):1.0);
++ float b_y_score = best->result.y_ratio * (best->interlace?(2.0/3.0):1.0);
++
++ if ((t_v_diff < b_v_diff) ||
++ ((t_v_diff == b_v_diff) && (t_y_score > b_y_score)) ||
++ ((t_v_diff == b_v_diff) && (t_y_score == b_y_score) && (t->result.x_ratio > best->result.x_ratio)))
++ return 1;
++ }
++ else
++ {
++ int t_y_score = t->result.y_scale + t->interlace + t->doublescan;
++ int b_y_score = best->result.y_scale + best->interlace + best->doublescan;
++ float xy_diff = roundf((t->result.x_diff + t->result.y_diff) * 100) / 100;
++ float best_xy_diff = roundf((best->result.x_diff + best->result.y_diff) * 100) / 100;
++
++ if ((t_y_score < b_y_score) ||
++ ((t_y_score == b_y_score) && (xy_diff < best_xy_diff)) ||
++ ((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale < best->result.x_scale)) ||
++ ((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale == best->result.x_scale) && (t_v_diff < b_v_diff)))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++//============================================================
++// modeline_vesa_gtf
++// Based on the VESA GTF spreadsheet by Andy Morrish 1/5/97
++//============================================================
++
++int modeline_vesa_gtf(modeline *m)
++{
++ int C, M;
++ int v_sync_lines, v_porch_lines_min, v_front_porch_lines, v_back_porch_lines, v_sync_v_back_porch_lines, v_total_lines;
++ int h_sync_width_percent, h_sync_width_pixels, h_blanking_pixels, h_front_porch_pixels, h_total_pixels;
++ float v_freq, v_freq_est, v_freq_real, v_sync_v_back_porch;
++ float h_freq, h_period, h_period_real, h_ideal_blanking;
++ float pixel_freq, interlace;
++
++ // Check if there's a value defined for vfreq. We're assuming input vfreq is the total field vfreq regardless interlace
++ v_freq = m->vfreq? m->vfreq:float(m->refresh);
++
++ // These values are GTF defined defaults
++ v_sync_lines = 3;
++ v_porch_lines_min = 1;
++ v_front_porch_lines = v_porch_lines_min;
++ v_sync_v_back_porch = 550;
++ h_sync_width_percent = 8;
++ M = 128.0 / 256 * 600;
++ C = ((40 - 20) * 128.0 / 256) + 20;
++
++ // GTF calculation
++ interlace = m->interlace?0.5:0;
++ h_period = ((1.0 / v_freq) - (v_sync_v_back_porch / 1000000)) / ((float)m->height + v_front_porch_lines + interlace) * 1000000;
++ v_sync_v_back_porch_lines = round_near(v_sync_v_back_porch / h_period);
++ v_back_porch_lines = v_sync_v_back_porch_lines - v_sync_lines;
++ v_total_lines = m->height + v_front_porch_lines + v_sync_lines + v_back_porch_lines;
++ v_freq_est = (1.0 / h_period) / v_total_lines * 1000000;
++ h_period_real = h_period / (v_freq / v_freq_est);
++ v_freq_real = (1.0 / h_period_real) / v_total_lines * 1000000;
++ h_ideal_blanking = float(C - (M * h_period_real / 1000));
++ h_blanking_pixels = round_near(m->width * h_ideal_blanking /(100 - h_ideal_blanking) / (2 * 8)) * (2 * 8);
++ h_total_pixels = m->width + h_blanking_pixels;
++ pixel_freq = h_total_pixels / h_period_real * 1000000;
++ h_freq = 1000000 / h_period_real;
++ h_sync_width_pixels = round_near(h_sync_width_percent * h_total_pixels / 100 / 8) * 8;
++ h_front_porch_pixels = (h_blanking_pixels / 2) - h_sync_width_pixels;
++
++ // Results
++ m->hactive = m->width;
++ m->hbegin = m->hactive + h_front_porch_pixels;
++ m->hend = m->hbegin + h_sync_width_pixels;
++ m->htotal = h_total_pixels;
++ m->vactive = m->height;
++ m->vbegin = m->vactive + v_front_porch_lines;
++ m->vend = m->vbegin + v_sync_lines;
++ m->vtotal = v_total_lines;
++ m->hfreq = h_freq;
++ m->vfreq = v_freq_real;
++ m->pclock = pixel_freq;
++ m->hsync = 0;
++ m->vsync = 1;
++
++ return true;
++}
++
++//============================================================
++// modeline_parse
++//============================================================
++
++int modeline_parse(const char *user_modeline, modeline *mode)
++{
++ char modeline_txt[256]={'\x00'};
++
++ if (strcmp(user_modeline, "auto"))
++ {
++ // Remove quotes
++ char *quote_start, *quote_end;
++ quote_start = strstr((char*)user_modeline, "\"");
++ if (quote_start)
++ {
++ quote_start++;
++ quote_end = strstr(quote_start, "\"");
++ if (!quote_end || *quote_end++ == 0)
++ return false;
++ user_modeline = quote_end;
++ }
++
++ // Get timing flags
++ mode->interlace = strstr(user_modeline, "interlace")?1:0;
++ mode->doublescan = strstr(user_modeline, "doublescan")?1:0;
++ mode->hsync = strstr(user_modeline, "+hsync")?1:0;
++ mode->vsync = strstr(user_modeline, "+vsync")?1:0;
++
++ // Get timing values
++ float pclock;
++ int e = sscanf(user_modeline, " %f %d %d %d %d %d %d %d %d",
++ &pclock,
++ &mode->hactive, &mode->hbegin, &mode->hend, &mode->htotal,
++ &mode->vactive, &mode->vbegin, &mode->vend, &mode->vtotal);
++
++ if (e != 9)
++ {
++ osd_printf_error("SwitchRes: missing parameter in user modeline\n %s\n", user_modeline);
++ memset(mode, 0, sizeof(struct modeline));
++ return false;
++ }
++
++ // Calculate timings
++ mode->pclock = pclock * 1000000.0;
++ mode->hfreq = mode->pclock / mode->htotal;
++ mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
++ mode->refresh = mode->vfreq;
++ osd_printf_verbose("SwitchRes: user modeline %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ }
++ return true;
++}
++
++//============================================================
++// modeline_to_monitor_range
++//============================================================
++
++int modeline_to_monitor_range(monitor_range *range, modeline *mode)
++{
++ if (range->vfreq_min == 0)
++ {
++ range->vfreq_min = mode->vfreq - 0.2;
++ range->vfreq_max = mode->vfreq + 0.2;
++ }
++
++ float line_time = 1 / mode->hfreq;
++ float pixel_time = line_time / mode->htotal * 1000000;
++
++ range->hfront_porch = pixel_time * (mode->hbegin - mode->hactive);
++ range->hsync_pulse = pixel_time * (mode->hend - mode->hbegin);
++ range->hback_porch = pixel_time * (mode->htotal - mode->hend);
++
++ range->vfront_porch = line_time * (mode->vbegin - mode->vactive);
++ range->vsync_pulse = line_time * (mode->vend - mode->vbegin);
++ range->vback_porch = line_time * (mode->vtotal - mode->vend);
++ range->vertical_blank = range->vfront_porch + range->vsync_pulse + range->vback_porch;
++
++ range->hsync_polarity = mode->hsync;
++ range->vsync_polarity = mode->vsync;
++
++ range->progressive_lines_min = mode->interlace?0:mode->vactive;
++ range->progressive_lines_max = mode->interlace?0:mode->vactive;
++ range->interlaced_lines_min = mode->interlace?mode->vactive:0;
++ range->interlaced_lines_max= mode->interlace?mode->vactive:0;
++
++ range->hfreq_min = range->vfreq_min * mode->vtotal;
++ range->hfreq_max = range->vfreq_max * mode->vtotal;
++
++ return 1;
++}
++
++//============================================================
++// round_near
++//============================================================
++
++int round_near(double number)
++{
++ return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5);
++}
+diff --git a/src/emu/switchres/modeline.h b/src/emu/switchres/modeline.h
+new file mode 100644
+index 00000000000..ef59b98cd4a
+--- /dev/null
++++ b/src/emu/switchres/modeline.h
+@@ -0,0 +1,118 @@
++/**************************************************************
++
++ modeline.h - Modeline generation header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#ifndef __MODELINE_H__
++#define __MODELINE_H__
++
++#include "monitor.h"
++
++//============================================================
++// CONSTANTS
++//============================================================
++
++// Modeline print flags
++#define MS_LABEL 0x00000001
++#define MS_LABEL_SDL 0x00000002
++#define MS_PARAMS 0x00000004
++#define MS_FULL MS_LABEL | MS_PARAMS
++
++// Modeline result
++#define R_V_FREQ_OFF 0x00000001
++#define R_RES_STRETCH 0x00000002
++#define R_OUT_OF_RANGE 0x00000004
++
++// Modeline commands
++#define MODELINE_DELETE 0x001
++#define MODELINE_CREATE 0x002
++#define MODELINE_UPDATE 0x004
++#define MODELINE_UPDATE_LIST 0x008
++
++// Mode types
++#define MODE_OK 0x00000000
++#define MODE_DESKTOP 0x10000000
++#define MODE_ROTATED 0x20000000
++#define MODE_DISABLED 0x40000000
++#define MODE_USER_DEF 0x80000000
++#define V_FREQ_EDITABLE 0x00000001
++#define X_RES_EDITABLE 0x00000002
++#define Y_RES_EDITABLE 0x00000004
++#define XYV_EDITABLE (X_RES_EDITABLE | Y_RES_EDITABLE | V_FREQ_EDITABLE )
++
++#define DUMMY_WIDTH 1234
++#define MAX_MODELINES 256
++
++//============================================================
++// TYPE DEFINITIONS
++//============================================================
++
++typedef struct mode_result
++{
++ int weight;
++ int x_scale;
++ int y_scale;
++ int v_scale;
++ float x_diff;
++ float y_diff;
++ float v_diff;
++ float x_ratio;
++ float y_ratio;
++ float v_ratio;
++ bool rotated;
++} mode_result;
++
++typedef struct modeline
++{
++ uint64_t pclock;
++ int hactive;
++ int hbegin;
++ int hend;
++ int htotal;
++ int vactive;
++ int vbegin;
++ int vend;
++ int vtotal;
++ int interlace;
++ int doublescan;
++ int hsync;
++ int vsync;
++ //
++ double vfreq;
++ double hfreq;
++ //
++ int width;
++ int height;
++ int refresh;
++ int refresh_label;
++ //
++ int type;
++ int range;
++ //
++ mode_result result;
++} modeline;
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, config_settings *cs);
++int modeline_compare(modeline *t_mode, modeline *best_mode);
++char * modeline_print(modeline *mode, char *modeline, int flags);
++char * modeline_result(modeline *mode, char *result);
++int modeline_vesa_gtf(modeline *m);
++int modeline_parse(const char *user_modeline, modeline *mode);
++int modeline_to_monitor_range(monitor_range *range, modeline *mode);
++
++#endif
+diff --git a/src/emu/switchres/monitor.cpp b/src/emu/switchres/monitor.cpp
+new file mode 100644
+index 00000000000..4a83d1802eb
+--- /dev/null
++++ b/src/emu/switchres/monitor.cpp
+@@ -0,0 +1,481 @@
++/**************************************************************
++
++ monitor.cpp - Monitor presets and custom monitor definition
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#include "emu.h"
++
++//============================================================
++// CONSTANTS
++//============================================================
++
++#define HFREQ_MIN 14000
++#define HFREQ_MAX 540672 // 8192 * 1.1 * 60
++#define VFREQ_MIN 40
++#define VFREQ_MAX 200
++#define PROGRESSIVE_LINES_MIN 128
++
++//============================================================
++// monitor_fill_range
++//============================================================
++
++int monitor_fill_range(monitor_range *range, const char *specs_line)
++{
++ monitor_range new_range;
++
++ if (strcmp(specs_line, "auto")) {
++ int e = sscanf(specs_line, "%lf-%lf,%lf-%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d",
++ &new_range.hfreq_min, &new_range.hfreq_max,
++ &new_range.vfreq_min, &new_range.vfreq_max,
++ &new_range.hfront_porch, &new_range.hsync_pulse, &new_range.hback_porch,
++ &new_range.vfront_porch, &new_range.vsync_pulse, &new_range.vback_porch,
++ &new_range.hsync_polarity, &new_range.vsync_polarity,
++ &new_range.progressive_lines_min, &new_range.progressive_lines_max,
++ &new_range.interlaced_lines_min, &new_range.interlaced_lines_max);
++
++ if (e != 16) {
++ osd_printf_error("SwitchRes: Error trying to fill monitor range with\n %s\n", specs_line);
++ return -1;
++ }
++
++ new_range.vfront_porch /= 1000;
++ new_range.vsync_pulse /= 1000;
++ new_range.vback_porch /= 1000;
++ new_range.vertical_blank = (new_range.vfront_porch + new_range.vsync_pulse + new_range.vback_porch);
++
++ if (monitor_evaluate_range(&new_range))
++ {
++ osd_printf_error("SwitchRes: Error in monitor range (ignoring): %s\n", specs_line);
++ return -1;
++ }
++ else
++ {
++ memcpy(range, &new_range, sizeof(struct monitor_range));
++ monitor_show_range(range);
++ }
++ }
++ return 0;
++}
++
++//============================================================
++// monitor_fill_lcd_range
++//============================================================
++
++int monitor_fill_lcd_range(monitor_range *range, const char *specs_line)
++{
++ if (strcmp(specs_line, "auto"))
++ {
++ if (sscanf(specs_line, "%lf-%lf", &range->vfreq_min, &range->vfreq_max) == 2)
++ {
++ osd_printf_verbose("SwitchRes: LCD vfreq range set by user as %f-%f\n", range->vfreq_min, range->vfreq_max);
++ return true;
++ }
++ else
++ osd_printf_error("SwitchRes: Error trying to fill LCD range with\n %s\n", specs_line);
++ }
++ // Use default values
++ range->vfreq_min = 59;
++ range->vfreq_max = 61;
++ osd_printf_verbose("SwitchRes: Using default vfreq range for LCD %f-%f\n", range->vfreq_min, range->vfreq_max);
++
++ return 0;
++}
++
++//============================================================
++// monitor_fill_vesa_gtf
++//============================================================
++
++int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines)
++{
++ int lines = 0;
++ sscanf(max_lines, "vesa_%d", &lines);
++
++ if (!lines)
++ return 0;
++
++ int i = 0;
++ if (lines >= 480)
++ i += monitor_fill_vesa_range(&range[i], 384, 480);
++ if (lines >= 600)
++ i += monitor_fill_vesa_range(&range[i], 480, 600);
++ if (lines >= 768)
++ i += monitor_fill_vesa_range(&range[i], 600, 768);
++ if (lines >= 1024)
++ i += monitor_fill_vesa_range(&range[i], 768, 1024);
++
++ return i;
++}
++
++//============================================================
++// monitor_fill_vesa_range
++//============================================================
++
++int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max)
++{
++ modeline mode;
++ memset(&mode, 0, sizeof(modeline));
++
++ mode.width = real_res(STANDARD_CRT_ASPECT * lines_max);
++ mode.height = lines_max;
++ mode.refresh = 60;
++ range->vfreq_min = 50;
++ range->vfreq_max = 65;
++
++ modeline_vesa_gtf(&mode);
++ modeline_to_monitor_range(range, &mode);
++
++ range->progressive_lines_min = lines_min;
++ range->hfreq_min = mode.hfreq - 500;
++ range->hfreq_max = mode.hfreq + 500;
++ monitor_show_range(range);
++
++ return 1;
++}
++
++//============================================================
++// monitor_show_range
++//============================================================
++
++int monitor_show_range(monitor_range *range)
++{
++ osd_printf_verbose("SwitchRes: Monitor range %.2f-%.2f,%.2f-%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%d,%d,%d,%d,%d,%d\n",
++ range->hfreq_min, range->hfreq_max,
++ range->vfreq_min, range->vfreq_max,
++ range->hfront_porch, range->hsync_pulse, range->hback_porch,
++ range->vfront_porch * 1000, range->vsync_pulse * 1000, range->vback_porch * 1000,
++ range->hsync_polarity, range->vsync_polarity,
++ range->progressive_lines_min, range->progressive_lines_max,
++ range->interlaced_lines_min, range->interlaced_lines_max);
++
++ return 0;
++}
++
++//============================================================
++// monitor_set_preset
++//============================================================
++
++int monitor_set_preset(char *type, monitor_range *range)
++{
++ // PAL TV - 50 Hz/625
++ if (!strcmp(type, "pal"))
++ {
++ monitor_fill_range(&range[0], "15625.00-15625.00, 50.00-50.00, 1.500, 4.700, 5.800, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // NTSC TV - 60 Hz/525
++ else if (!strcmp(type, "ntsc"))
++ {
++ monitor_fill_range(&range[0], "15734.26-15734.26, 59.94-59.94, 1.500, 4.700, 4.700, 0.191, 0.191, 0.953, 0, 0, 192, 240, 448, 480");
++ return 1;
++ }
++ // Generic 15.7 kHz
++ else if (!strcmp(type, "generic_15"))
++ {
++ monitor_fill_range(&range[0], "15625-15750, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // Arcade 15.7 kHz - standard resolution
++ else if (!strcmp(type, "arcade_15"))
++ {
++ monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // Arcade 15.7-16.5 kHz - extended resolution
++ else if (!strcmp(type, "arcade_15ex"))
++ {
++ monitor_fill_range(&range[0], "15625-16500, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // Arcade 25.0 kHz - medium resolution
++ else if (!strcmp(type, "arcade_25"))
++ {
++ monitor_fill_range(&range[0], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
++ return 1;
++ }
++ // Arcade 31.5 kHz - medium resolution
++ else if (!strcmp(type, "arcade_31"))
++ {
++ monitor_fill_range(&range[0], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
++ return 1;
++ }
++ // Arcade 15.7/25.0 kHz - dual-sync
++ else if (!strcmp(type, "arcade_15_25"))
++ {
++ monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
++ return 2;
++ }
++ // Arcade 15.7/31.5 kHz - dual-sync
++ else if (!strcmp(type, "arcade_15_31"))
++ {
++ monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
++ return 2;
++ }
++ // Arcade 15.7/25.0/31.5 kHz - tri-sync
++ else if (!strcmp(type, "arcade_15_25_31"))
++ {
++ monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
++ monitor_fill_range(&range[2], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
++ return 3;
++ }
++ // Makvision 2929D
++ else if (!strcmp(type, "m2929"))
++ {
++ monitor_fill_range(&range[0], "30000-40000, 47.00-90.00, 0.600, 2.500, 2.800, 0.032, 0.096, 0.448, 0, 0, 384, 640, 0, 0");
++ return 1;
++ }
++ // Wells Gardner D9800, D9400
++ else if (!strcmp(type, "d9800") || !strcmp(type, "d9400"))
++ {
++ monitor_fill_range(&range[0], "15250-18000, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
++ monitor_fill_range(&range[1], "18001-19000, 40-80, 2.187, 4.688, 6.719, 0.140, 0.191, 0.950, 0, 0, 288, 320, 0, 0");
++ monitor_fill_range(&range[2], "20501-29000, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 320, 384, 0, 0");
++ monitor_fill_range(&range[3], "29001-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 384, 480, 0, 0");
++ monitor_fill_range(&range[4], "32001-34000, 40-80, 0.636, 3.813, 1.906, 0.020, 0.106, 0.607, 0, 0, 480, 576, 0, 0");
++ monitor_fill_range(&range[5], "34001-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 576, 600, 0, 0");
++ return 6;
++ }
++ // Wells Gardner D9200
++ else if (!strcmp(type, "d9200"))
++ {
++ monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
++ monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
++ monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 400, 512, 0, 0");
++ monitor_fill_range(&range[3], "37000-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 512, 600, 0, 0");
++ return 4;
++ }
++ // Wells Gardner K7000
++ else if (!strcmp(type, "k7000"))
++ {
++ monitor_fill_range(&range[0], "15625-15800, 49.50-63.00, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // Wells Gardner 25K7131
++ else if (!strcmp(type, "k7131"))
++ {
++ monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // Wei-Ya M3129
++ else if (!strcmp(type, "m3129"))
++ {
++ monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 1, 1, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 1, 1, 384, 400, 0, 0");
++ monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 1, 1, 400, 512, 0, 0");
++ return 3;
++ }
++ // Hantarex MTC 9110
++ else if (!strcmp(type, "h9110") || !strcmp(type, "polo"))
++ {
++ monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
++ return 1;
++ }
++ // Hantarex Polostar 25
++ else if (!strcmp(type, "pstar"))
++ {
++ monitor_fill_range(&range[0], "15700-15800, 50-65, 1.800, 0.400, 7.400, 0.064, 0.160, 1.056, 0, 0, 192, 256, 0, 0");
++ monitor_fill_range(&range[1], "16200-16300, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 256, 264, 512, 528");
++ monitor_fill_range(&range[2], "25300-25400, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 384, 400, 768, 800");
++ monitor_fill_range(&range[3], "31500-31600, 50-65, 0.170, 0.350, 5.500, 0.040, 0.040, 0.640, 0, 0, 400, 512, 0, 0");
++ return 4;
++ }
++ // Nanao MS-2930, MS-2931
++ else if (!strcmp(type, "ms2930"))
++ {
++ monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
++ monitor_fill_range(&range[2], "31000-32000, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 480, 512, 0, 0");
++ return 3;
++ }
++ // Nanao MS9-29
++ else if (!strcmp(type, "ms929"))
++ {
++ monitor_fill_range(&range[0], "15450-16050, 50-65, 3.910, 4.700, 6.850, 0.190, 0.191, 1.018, 0, 0, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "23900-24900, 50-65, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 384, 400, 0, 0");
++ return 2;
++ }
++ // Rodotron 666B-29
++ else if (!strcmp(type, "r666b"))
++ {
++ monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
++ monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
++ monitor_fill_range(&range[2], "31000-32500, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 400, 512, 0, 0");
++ return 3;
++ }
++ // PC CRT 70kHz/120Hz
++ else if (!strcmp(type, "pc_31_120"))
++ {
++ monitor_fill_range(&range[0], "31400-31600, 100-130, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 200, 256, 0, 0");
++ monitor_fill_range(&range[1], "31400-31600, 50-65, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 400, 512, 0, 0");
++ return 2;
++ }
++ // PC CRT 70kHz/120Hz
++ else if (!strcmp(type, "pc_70_120"))
++ {
++ monitor_fill_range(&range[0], "30000-70000, 100-130, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 192, 320, 0, 0");
++ monitor_fill_range(&range[1], "30000-70000, 50-65, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 400, 1024, 0, 0");
++ return 2;
++ }
++ // VESA GTF
++ else if (!strcmp(type, "vesa_480") || !strcmp(type, "vesa_600") || !strcmp(type, "vesa_768") || !strcmp(type, "vesa_1024"))
++ {
++ return monitor_fill_vesa_gtf(&range[0], type);
++ }
++
++ osd_printf_error("SwitchRes: Monitor type unknown: %s\n", type);
++ return 0;
++}
++
++//============================================================
++// monitor_evaluate_range
++//============================================================
++
++int monitor_evaluate_range(monitor_range *range)
++{
++ // First we check that all frequency ranges are reasonable
++ if (range->hfreq_min < HFREQ_MIN || range->hfreq_min > HFREQ_MAX)
++ {
++ osd_printf_error("SwitchRes: hfreq_min %.2f out of range\n", range->hfreq_min);
++ return 1;
++ }
++ if (range->hfreq_max < HFREQ_MIN || range->hfreq_max < range->hfreq_min || range->hfreq_max > HFREQ_MAX)
++ {
++ osd_printf_error("SwitchRes: hfreq_max %.2f out of range\n", range->hfreq_max);
++ return 1;
++ }
++ if (range->vfreq_min < VFREQ_MIN || range->vfreq_min > VFREQ_MAX)
++ {
++ osd_printf_error("SwitchRes: vfreq_min %.2f out of range\n", range->vfreq_min);
++ return 1;
++ }
++ if (range->vfreq_max < VFREQ_MIN || range->vfreq_max < range->vfreq_min || range->vfreq_max > VFREQ_MAX)
++ {
++ osd_printf_error("SwitchRes: vfreq_max %.2f out of range\n", range->vfreq_max);
++ return 1;
++ }
++
++ // line_time in хs. We check that no horizontal value is longer than a whole line
++ double line_time = 1 / range->hfreq_max * 1000000;
++
++ if (range->hfront_porch <= 0 || range->hfront_porch > line_time)
++ {
++ osd_printf_error("SwitchRes: hfront_porch %.3f out of range\n", range->hfront_porch);
++ return 1;
++ }
++ if (range->hsync_pulse <= 0 || range->hsync_pulse > line_time)
++ {
++ osd_printf_error("SwitchRes: hsync_pulse %.3f out of range\n", range->hsync_pulse);
++ return 1;
++ }
++ if (range->hback_porch <= 0 || range->hback_porch > line_time)
++ {
++ osd_printf_error("SwitchRes: hback_porch %.3f out of range\n", range->hback_porch);
++ return 1;
++ }
++
++ // frame_time in ms. We check that no vertical value is longer than a whole frame
++ double frame_time = 1 / range->vfreq_max * 1000;
++
++ if (range->vfront_porch <= 0 || range->vfront_porch > frame_time)
++ {
++ osd_printf_error("SwitchRes: vfront_porch %.3f out of range\n", range->vfront_porch);
++ return 1;
++ }
++ if (range->vsync_pulse <= 0 || range->vsync_pulse > frame_time)
++ {
++ osd_printf_error("SwitchRes: vsync_pulse %.3f out of range\n", range->vsync_pulse);
++ return 1;
++ }
++ if (range->vback_porch <= 0 || range->vback_porch > frame_time)
++ {
++ osd_printf_error("SwitchRes: vback_porch %.3f out of range\n", range->vback_porch);
++ return 1;
++ }
++
++ // Now we check sync polarities
++ if (range->hsync_polarity != 0 && range->hsync_polarity != 1)
++ {
++ osd_printf_error("SwitchRes: Hsync polarity can be only 0 or 1\n");
++ return 1;
++ }
++ if (range->vsync_polarity != 0 && range->vsync_polarity != 1)
++ {
++ osd_printf_error("SwitchRes: Vsync polarity can be only 0 or 1\n");
++ return 1;
++ }
++
++ // Finally we check that the line limiters are reasonable
++ // Progressive range:
++ if (range->progressive_lines_min > 0 && range->progressive_lines_min < PROGRESSIVE_LINES_MIN)
++ {
++ osd_printf_error("SwitchRes: progressive_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN);
++ return 1;
++ }
++ if ((range->progressive_lines_min + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
++ {
++ osd_printf_error("SwitchRes: progressive_lines_min %d out of range\n", range->progressive_lines_min);
++ return 1;
++ }
++ if (range->progressive_lines_max < range->progressive_lines_min)
++ {
++ osd_printf_error("SwitchRes: progressive_lines_max must greater than progressive_lines_min\n");
++ return 1;
++ }
++ if ((range->progressive_lines_max + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
++ {
++ osd_printf_error("SwitchRes: progressive_lines_max %d out of range\n", range->progressive_lines_max);
++ return 1;
++ }
++
++ // Interlaced range:
++ if (range->interlaced_lines_min != 0)
++ {
++ if (range->interlaced_lines_min < range->progressive_lines_max)
++ {
++ osd_printf_error("SwitchRes: interlaced_lines_min must greater than progressive_lines_max\n");
++ return 1;
++ }
++ if (range->interlaced_lines_min < PROGRESSIVE_LINES_MIN * 2)
++ {
++ osd_printf_error("SwitchRes: interlaced_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN * 2);
++ return 1;
++ }
++ if ((range->interlaced_lines_min / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
++ {
++ osd_printf_error("SwitchRes: interlaced_lines_min %d out of range\n", range->interlaced_lines_min);
++ return 1;
++ }
++ if (range->interlaced_lines_max < range->interlaced_lines_min)
++ {
++ osd_printf_error("SwitchRes: interlaced_lines_max must greater than interlaced_lines_min\n");
++ return 1;
++ }
++ if ((range->interlaced_lines_max / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
++ {
++ osd_printf_error("SwitchRes: interlaced_lines_max %d out of range\n", range->interlaced_lines_max);
++ return 1;
++ }
++ }
++ else
++ {
++ if (range->interlaced_lines_max != 0)
++ {
++ osd_printf_error("SwitchRes: interlaced_lines_max must be zero if interlaced_lines_min is not defined\n");
++ return 1;
++ }
++ }
++ return 0;
++}
+diff --git a/src/emu/switchres/monitor.h b/src/emu/switchres/monitor.h
+new file mode 100644
+index 00000000000..49aecf20b3d
+--- /dev/null
++++ b/src/emu/switchres/monitor.h
+@@ -0,0 +1,66 @@
++/**************************************************************
++
++ monitor.h - Monitor presets header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#ifndef __MONITOR_H__
++#define __MONITOR_H__
++
++//============================================================
++// CONSTANTS
++//============================================================
++
++#define MAX_RANGES 10
++#define MONITOR_CRT 0
++#define MONITOR_LCD 1
++#define STANDARD_CRT_ASPECT 4.0/3.0
++
++//============================================================
++// TYPE DEFINITIONS
++//============================================================
++
++typedef struct monitor_range
++{
++ double hfreq_min;
++ double hfreq_max;
++ double vfreq_min;
++ double vfreq_max;
++ double hfront_porch;
++ double hsync_pulse;
++ double hback_porch;
++ double vfront_porch;
++ double vsync_pulse;
++ double vback_porch;
++ int hsync_polarity;
++ int vsync_polarity;
++ int progressive_lines_min;
++ int progressive_lines_max;
++ int interlaced_lines_min;
++ int interlaced_lines_max;
++ double vertical_blank;
++} monitor_range;
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++int monitor_fill_range(monitor_range *range, const char *specs_line);
++int monitor_show_range(monitor_range *range);
++int monitor_set_preset(char *type, monitor_range *range);
++int monitor_fill_lcd_range(monitor_range *range, const char *specs_line);
++int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines);
++int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max);
++int monitor_evaluate_range(monitor_range *range);
++
++#endif
+diff --git a/src/emu/switchres/switchres.cpp b/src/emu/switchres/switchres.cpp
+new file mode 100644
+index 00000000000..6b406a33651
+--- /dev/null
++++ b/src/emu/switchres/switchres.cpp
+@@ -0,0 +1,386 @@
++/**************************************************************
++
++ switchres.cpp - SwichRes core routines
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#include "emu.h"
++#include "emuopts.h"
++#include "../frontend/mame/mameopts.h"
++#include "config.h"
++#include "rendutil.h"
++
++#define CUSTOM_VIDEO_TIMING_SYSTEM 0x00000010
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++void set_option(running_machine &machine, const char *option_ID, bool state);
++
++//============================================================
++// switchres_get_video_mode
++//============================================================
++
++bool switchres_get_video_mode(running_machine &machine)
++{
++ switchres_manager *switchres = &machine.switchres;
++ config_settings *cs = &switchres->cs;
++ game_info *game = &switchres->game;
++ monitor_range *range = switchres->range;
++ modeline *mode;
++ modeline *mode_table = switchres->video_modes;
++ modeline *best_mode = &switchres->best_mode;
++ modeline *user_mode = &switchres->user_mode;
++ modeline source_mode, *s_mode = &source_mode;
++ modeline target_mode, *t_mode = &target_mode;
++ char modeline[256]={'\x00'};
++ char result[256]={'\x00'};
++ int i = 0, j = 0, table_size = 0;
++
++ cs->effective_orientation = effective_orientation(machine);
++
++ osd_printf_verbose("SwitchRes: v%s:[%s] Calculating best video mode for %dx%d@%.6f orientation: %s\n",
++ SWITCHRES_VERSION, game->name, game->width, game->height, game->refresh,
++ cs->effective_orientation?"rotated":"normal");
++
++ memset(best_mode, 0, sizeof(struct modeline));
++ best_mode->result.weight |= R_OUT_OF_RANGE;
++ s_mode->hactive = game->vector?1:normalize(game->width, 8);
++ s_mode->vactive = game->vector?1:game->height;
++ s_mode->vfreq = game->refresh;
++
++ if (user_mode->hactive)
++ {
++ table_size = 1;
++ mode = user_mode;
++ }
++ else
++ {
++ i = 1;
++ table_size = MAX_MODELINES;
++ mode = &mode_table[i];
++ }
++
++ while (mode->width && i < table_size)
++ {
++ // apply options to mode type
++ if (!cs->modeline_generation)
++ mode->type &= ~XYV_EDITABLE;
++
++ if (cs->refresh_dont_care)
++ mode->type |= V_FREQ_EDITABLE;
++
++ if (cs->lock_system_modes && (mode->type & CUSTOM_VIDEO_TIMING_SYSTEM) && !(mode->type & MODE_DESKTOP) && !(mode->type & MODE_USER_DEF))
++ mode->type |= MODE_DISABLED;
++
++ osd_printf_verbose("\nSwitchRes: %s%4d%sx%s%4d%s_%s%d=%.6fHz%s%s\n",
++ mode->type & X_RES_EDITABLE?"(":"[", mode->width, mode->type & X_RES_EDITABLE?")":"]",
++ mode->type & Y_RES_EDITABLE?"(":"[", mode->height, mode->type & Y_RES_EDITABLE?")":"]",
++ mode->type & V_FREQ_EDITABLE?"(":"[", mode->refresh, mode->vfreq, mode->type & V_FREQ_EDITABLE?")":"]",
++ mode->type & MODE_DISABLED?" - locked":"");
++
++ // now get the mode if allowed
++ if (!(mode->type & MODE_DISABLED))
++ {
++ for (j = 0 ; j < MAX_RANGES ; j++)
++ {
++ if (range[j].hfreq_min)
++ {
++ memcpy(t_mode, mode, sizeof(struct modeline));
++ modeline_create(s_mode, t_mode, &range[j], cs);
++ t_mode->range = j;
++
++ osd_printf_verbose("%s\n", modeline_result(t_mode, result));
++
++ if (modeline_compare(t_mode, best_mode))
++ memcpy(best_mode, t_mode, sizeof(struct modeline));
++ }
++ }
++ }
++ mode++;
++ i++;
++ }
++
++ if (best_mode->result.weight & R_OUT_OF_RANGE)
++ {
++ osd_printf_error("SwitchRes: could not find a video mode that meets your specs\n");
++ return false;
++ }
++
++ osd_printf_info("\nSwitchRes: [%s] (%d) %s (%dx%d@%.6f)->(%dx%d@%.6f)\n", game->name, game->screens, game->orientation?"vertical":"horizontal",
++ game->width, game->height, game->refresh, best_mode->hactive, best_mode->vactive, best_mode->vfreq);
++
++ osd_printf_verbose("%s\n", modeline_result(best_mode, result));
++ if (cs->modeline_generation)
++ osd_printf_verbose("SwitchRes: Modeline %s\n", modeline_print(best_mode, modeline, MS_FULL));
++
++ return true;
++}
++
++//============================================================
++// switchres_get_monitor_specs
++//============================================================
++
++int switchres_get_monitor_specs(running_machine &machine)
++{
++ switchres_manager *switchres = &machine.switchres;
++ char default_monitor[] = "generic_15";
++
++ memset(&switchres->range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
++
++ if (!strcmp(switchres->cs.monitor, "custom"))
++ {
++ monitor_fill_range(&switchres->range[0],machine.options().crt_range0());
++ monitor_fill_range(&switchres->range[1],machine.options().crt_range1());
++ monitor_fill_range(&switchres->range[2],machine.options().crt_range2());
++ monitor_fill_range(&switchres->range[3],machine.options().crt_range3());
++ monitor_fill_range(&switchres->range[4],machine.options().crt_range4());
++ monitor_fill_range(&switchres->range[5],machine.options().crt_range5());
++ monitor_fill_range(&switchres->range[6],machine.options().crt_range6());
++ monitor_fill_range(&switchres->range[7],machine.options().crt_range7());
++ monitor_fill_range(&switchres->range[8],machine.options().crt_range8());
++ monitor_fill_range(&switchres->range[9],machine.options().crt_range9());
++ }
++ else if (!strcmp(switchres->cs.monitor, "lcd"))
++ monitor_fill_lcd_range(&switchres->range[0],machine.options().lcd_range());
++
++ else if (monitor_set_preset(switchres->cs.monitor, switchres->range) == 0)
++ monitor_set_preset(default_monitor, switchres->range);
++
++ return 0;
++}
++
++//============================================================
++// switchres_init
++//============================================================
++
++void switchres_init(running_machine &machine)
++{
++ emu_options &options = machine.options();
++ config_settings *cs = &machine.switchres.cs;
++ modeline *user_mode = &machine.switchres.user_mode;
++
++ osd_printf_verbose("SwitchRes: v%s, Monitor: %s, Orientation: %s, Modeline generation: %s\n",
++ SWITCHRES_VERSION, options.monitor(), options.orientation(), options.modeline_generation()?"enabled":"disabled");
++
++ // Get user defined modeline
++ if (options.modeline_generation())
++ {
++ modeline_parse(options.modeline(), user_mode);
++ user_mode->type |= MODE_USER_DEF;
++ }
++
++ // Get monitor specs
++ sprintf(cs->monitor, "%s", options.monitor());
++ sprintf(cs->connector, "%s", options.connector());
++ for (int i = 0; cs->monitor[i]; i++) cs->monitor[i] = tolower(cs->monitor[i]);
++ if (user_mode->hactive)
++ {
++ modeline_to_monitor_range(machine.switchres.range, user_mode);
++ monitor_show_range(machine.switchres.range);
++ }
++ else
++ switchres_get_monitor_specs(machine);
++
++ // Get rest of config options
++ cs->modeline_generation = options.modeline_generation();
++ cs->doublescan = options.doublescan();
++ cs->interlace = options.interlace();
++ cs->lock_system_modes = options.lock_system_modes();
++ cs->lock_unsupported_modes = options.lock_unsupported_modes();
++ cs->refresh_dont_care = options.refresh_dont_care();
++ cs->super_width = options.super_width();
++ sscanf(options.sync_refresh_tolerance(), "%f", &cs->sync_refresh_tolerance);
++ float pclock_min;
++ sscanf(options.dotclock_min(), "%f", &pclock_min);
++ cs->pclock_min = pclock_min * 1000000;
++}
++
++//============================================================
++// switchres_get_game_info
++//============================================================
++
++void switchres_get_game_info(running_machine &machine)
++{
++ emu_options &options = machine.options();
++ game_info *game = &machine.switchres.game;
++ const game_driver *game_drv = &machine.system();
++ const screen_device *screen;
++
++ // Get game information
++ sprintf(game->name, "%s", options.system_name());
++ if (game->name[0] == 0) sprintf(game->name, "empty");
++
++ machine_config config(*game_drv, options);
++ screen = screen_device_iterator(config.root_device()).first();
++
++ // Fill in current video mode settings
++ game->orientation = effective_orientation(machine);
++
++ if (screen->screen_type() == SCREEN_TYPE_VECTOR)
++ {
++ game->vector = 1;
++ game->width = 640;
++ game->height = 480;
++ }
++
++ // Output width and height only for games that are not vector
++ else
++ {
++ const rectangle &visarea = screen->visible_area();
++ int w = visarea.max_x - visarea.min_x + 1;
++ int h = visarea.max_y - visarea.min_y + 1;
++ game->width = game->orientation?h:w;
++ game->height = game->orientation?w:h;
++ }
++
++ game->refresh = ATTOSECONDS_TO_HZ(screen->refresh_attoseconds());
++
++ // Check for multiple screens
++ screen_device_iterator iter(config.root_device());
++ game->screens = iter.count();
++}
++
++//============================================================
++// effective_orientation
++//============================================================
++
++bool effective_orientation(running_machine &machine)
++{
++ config_settings *cs = &machine.switchres.cs;
++ const game_driver *game = &machine.system();
++ emu_options &options = machine.options();
++ render_target *target = machine.render().first_target();
++ bool game_orientation = ((game->flags & machine_flags::MASK_ORIENTATION) & ORIENTATION_SWAP_XY);
++
++ if (target)
++ cs->monitor_orientation = ((target->orientation() & machine_flags::MASK_ORIENTATION) & ORIENTATION_SWAP_XY? 1:0) ^ cs->desktop_rotated;
++ else if (!strcmp(options.orientation(), "horizontal"))
++ cs->monitor_orientation = 0;
++ else if (!strcmp(options.orientation(), "vertical"))
++ cs->monitor_orientation = 1;
++ else if (!strcmp(options.orientation(), "rotate") || !strcmp(options.orientation(), "rotate_r"))
++ {
++ cs->monitor_orientation = game_orientation;
++ cs->monitor_rotates_cw = 0;
++ }
++ else if (!strcmp(options.orientation(), "rotate_l"))
++ {
++ cs->monitor_orientation = game_orientation;
++ cs->monitor_rotates_cw = 1;
++ }
++
++ return game_orientation ^ cs->monitor_orientation;
++}
++
++//============================================================
++// switchres_check_resolution_change
++//============================================================
++
++bool switchres_check_resolution_change(running_machine &machine)
++{
++ game_info *game = &machine.switchres.game;
++ config_settings *cs = &machine.switchres.cs;
++
++ int new_width = game->width;
++ int new_height = game->height;
++ float new_vfreq = game->refresh;
++ bool new_orientation = effective_orientation(machine);
++
++ screen_device_iterator scriter(machine.root_device());
++ if (scriter.count())
++ {
++ screen_device *screen = scriter.first();
++ if (screen->frame_number())
++ {
++ const rectangle &visarea = screen->visible_area();
++ new_width = new_orientation? visarea.height() : visarea.width();
++ new_height = new_orientation? visarea.width() : visarea.height();
++ new_vfreq = ATTOSECONDS_TO_HZ(screen->frame_period().m_attoseconds);
++ }
++ }
++
++ if (game->width != new_width || game->height != new_height || new_vfreq != game->refresh || cs->effective_orientation != new_orientation)
++ {
++ osd_printf_verbose("SwitchRes: Resolution change from %dx%d@%f %s to %dx%d@%f %s\n",
++ game->width, game->height, game->refresh, cs->effective_orientation?"rotated":"normal", new_width, new_height, new_vfreq, new_orientation?"rotated":"normal");
++
++ game->width = new_width;
++ game->height = new_height;
++ game->refresh = new_vfreq;
++
++ return true;
++ }
++ return false;
++}
++
++//============================================================
++// switchres_set_options
++//============================================================
++
++void switchres_set_options(running_machine &machine)
++{
++ config_settings *cs = &machine.switchres.cs;
++ bool native_orientation = ((machine.system().flags & machine_flags::MASK_ORIENTATION) & ORIENTATION_SWAP_XY);
++ bool must_rotate = effective_orientation(machine) ^ cs->desktop_rotated;
++ modeline *best_mode = &machine.switchres.best_mode;
++
++ // Set rotation options
++ set_option(machine, OPTION_ROTATE, true);
++ if (cs->monitor_rotates_cw)
++ {
++ set_option(machine, OPTION_ROL, (!native_orientation & must_rotate));
++ set_option(machine, OPTION_AUTOROL, !must_rotate);
++ set_option(machine, OPTION_ROR, false);
++ set_option(machine, OPTION_AUTOROR, false);
++ }
++ else
++ {
++ set_option(machine, OPTION_ROR, (!native_orientation & must_rotate));
++ set_option(machine, OPTION_AUTOROR, !must_rotate);
++ set_option(machine, OPTION_ROL, false);
++ set_option(machine, OPTION_AUTOROL, false);
++ }
++
++ // Set scaling/stretching options
++ set_option(machine, OPTION_KEEPASPECT, true);
++ set_option(machine, OPTION_UNEVENSTRETCH, best_mode->result.weight & R_RES_STRETCH);
++ set_option(machine, OPTION_UNEVENSTRETCHX, (!(best_mode->result.weight & R_RES_STRETCH) && (best_mode->width >= machine.options().super_width())));
++
++ // Update target if it's already initialized
++ render_target *target = machine.render().first_target();
++ if (target)
++ {
++ if (machine.options().uneven_stretch())
++ target->set_scale_mode(SCALE_FRACTIONAL);
++ else if(machine.options().uneven_stretch_x())
++ target->set_scale_mode(SCALE_FRACTIONAL_X);
++ else if(machine.options().uneven_stretch_y())
++ target->set_scale_mode(SCALE_FRACTIONAL_Y);
++ else
++ target->set_scale_mode(SCALE_INTEGER);
++ }
++}
++
++//============================================================
++// set_option - option setting wrapper
++//============================================================
++
++void set_option(running_machine &machine, const char *option_ID, bool state)
++{
++ emu_options &options = machine.options();
++
++ options.set_value(option_ID, state, OPTION_PRIORITY_SWITCHRES);
++ osd_printf_verbose("SwitchRes: Setting option -%s%s\n", machine.options().bool_value(option_ID)?"":"no", option_ID);
++}
+diff --git a/src/emu/switchres/switchres.h b/src/emu/switchres/switchres.h
+new file mode 100644
+index 00000000000..b162ed70125
+--- /dev/null
++++ b/src/emu/switchres/switchres.h
+@@ -0,0 +1,80 @@
++/**************************************************************
++
++ switchres.h - SwichRes general header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#ifndef __SWITCHRES_H__
++#define __SWITCHRES_H__
++
++//============================================================
++// CONSTANTS
++//============================================================
++
++#define SWITCHRES_VERSION "0.017p"
++
++//============================================================
++// TYPE DEFINITIONS
++//============================================================
++
++typedef struct game_info
++{
++ char name[32];
++ int width;
++ int height;
++ float refresh;
++ bool orientation;
++ bool vector;
++ bool changeres;
++ int screens;
++} game_info;
++
++typedef struct config_settings
++{
++ char connector[32];
++ char monitor[32];
++ bool modeline_generation;
++ bool monitor_orientation;
++ bool effective_orientation;
++ bool desktop_rotated;
++ bool monitor_rotates_cw;
++ float monitor_aspect;
++ int monitor_count;
++ int pclock_min;
++ int pclock_align;
++ int interlace;
++ int doublescan;
++ int width;
++ int height;
++ int refresh;
++ int super_width;
++ bool lock_unsupported_modes;
++ bool lock_system_modes;
++ bool refresh_dont_care;
++ float sync_refresh_tolerance;
++} config_settings;
++
++#include "monitor.h"
++#include "modeline.h"
++
++typedef struct switchres_manager
++{
++ struct config_settings cs;
++ struct game_info game;
++ struct modeline best_mode;
++ struct modeline user_mode;
++ struct monitor_range range[MAX_RANGES];
++ struct modeline video_modes[MAX_MODELINES];
++} switchres;
++
++#endif
+diff --git a/src/emu/switchres/switchres_proto.h b/src/emu/switchres/switchres_proto.h
+new file mode 100644
+index 00000000000..de9185e6810
+--- /dev/null
++++ b/src/emu/switchres/switchres_proto.h
+@@ -0,0 +1,42 @@
++/**************************************************************
++
++ switchres_proto.h - SwichRes prototypes header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#ifndef __SWITCHRES_H_PROTO__
++#define __SWITCHRES_H_PROTO__
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++// util.cpp
++int normalize(int a, int b);
++int real_res(int x);
++
++// switchres.cpp
++bool switchres_get_video_mode(running_machine &machine);
++int switchres_get_monitor_specs(running_machine &machine);
++void switchres_init(running_machine &machine);
++void switchres_get_game_info(running_machine &machine);
++bool switchres_check_resolution_change(running_machine &machine);
++void switchres_set_options(running_machine &machine);
++bool effective_orientation(running_machine &machine);
++
++// OSD - switchres.cpp
++bool switchres_init_osd(running_machine &machine);
++bool switchres_modeline_setup(running_machine &machine);
++bool switchres_modeline_remove(running_machine &machine);
++
++#endif
+diff --git a/src/emu/switchres/util.cpp b/src/emu/switchres/util.cpp
+new file mode 100644
+index 00000000000..c0def9124d0
+--- /dev/null
++++ b/src/emu/switchres/util.cpp
+@@ -0,0 +1,34 @@
++/**************************************************************
++
++ util.cpp - Utility functions
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++//============================================================
++// normalize
++//============================================================
++
++int normalize(int a, int b)
++{
++ int c, d;
++ c = a % b;
++ d = a / b;
++ if (c) d++;
++ return d * b;
++}
++
++//============================================================
++// real_res
++//============================================================
++
++int real_res(int x) {return (int) (x / 8) * 8;}
+diff --git a/src/emu/video.cpp b/src/emu/video.cpp
+index 8da14f03b85..5deeb91d39f 100644
+--- a/src/emu/video.cpp
++++ b/src/emu/video.cpp
+@@ -51,7 +51,7 @@ const bool video_manager::s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] =
+ { false, true , true , true , true , true , true , true , true , true , true , true }
+ };
+
+-
++int video_manager::s_fd_speeds[FD_BINS] = { 0,0,0,0,0,0,0,0,0,0 };
+
+ //**************************************************************************
+ // VIDEO MANAGER
+@@ -86,6 +86,8 @@ video_manager::video_manager(running_machine &machine)
+ , m_overall_valid_counter(0)
+ , m_throttled(machine.options().throttle())
+ , m_throttle_rate(1.0f)
++ , m_syncrefresh(machine.options().sync_refresh())
++ , m_framedelay(machine.options().frame_delay())
+ , m_fastforward(false)
+ , m_seconds_to_run(machine.options().seconds_to_run())
+ , m_auto_frameskip(machine.options().auto_frameskip())
+@@ -237,6 +239,14 @@ void video_manager::frame_update(bool from_debugger)
+ machine().osd().update(!from_debugger && skipped_it);
+ g_profiler.stop();
+
++ // manage black frame insertion
++ if (machine().options().black_frame_insertion() && machine().options().sync_refresh())
++ {
++ render_container *container = &machine().render().ui_container();
++ container->add_rect(0, 0, 1, 1, 0xff000000, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
++ machine().osd().update(!from_debugger && skipped_it);
++ }
++
+ // we synchronize after rendering instead of before, if low latency mode is enabled
+ if (!from_debugger && !skipped_it && m_low_latency && effective_throttle())
+ update_throttle(current_time);
+@@ -523,6 +533,23 @@ void video_manager::exit()
+ osd_ticks_t tps = osd_ticks_per_second();
+ double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps;
+ double final_emu_time = m_overall_emutime.as_double();
++
++ if (!m_throttled)
++ {
++ int i;
++ float sum = 0;
++
++ osd_printf_info("Frame delay/percentage:");
++
++ for (i = 0; i < FD_BINS; i++)
++ sum += s_fd_speeds[i];
++
++ for (i = 0; i < FD_BINS; i++)
++ if (s_fd_speeds[i])
++ osd_printf_info(" %d/%.2f%%", i, (float) s_fd_speeds[i] / sum * 100.f);
++ osd_printf_info("\n");
++ }
++
+ osd_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, (m_overall_emutime + attotime(0, ATTOSECONDS_PER_SECOND / 2)).seconds());
+ }
+ }
+@@ -728,6 +755,20 @@ void video_manager::update_throttle(attotime emutime)
+ 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8
+ };
+
++ // if we're only syncing to the refresh, bail now
++ if (m_syncrefresh)
++ {
++ modeline *mode = &machine().switchres.best_mode;
++ if (m_framedelay == 0 || m_framedelay > 9 || mode->hactive == 0)
++ return;
++
++ osd_ticks_t now = osd_ticks();
++ osd_ticks_t ticks_per_second = osd_ticks_per_second();
++ attoseconds_t attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second * m_throttle_rate;
++ throttle_until_ticks(now + HZ_TO_ATTOSECONDS(mode->vfreq / mode->result.v_scale) / attoseconds_per_tick * m_framedelay / 10);
++ return;
++ }
++
+ // outer scope so we can break out in case of a resync
+ while (1)
+ {
+@@ -999,6 +1040,21 @@ void video_manager::recompute_speed(const attotime &emutime)
+ osd_ticks_t tps = osd_ticks_per_second();
+ m_speed_percent = delta_emutime.as_double() * (double)tps / (double)delta_realtime;
+
++ // adjust speed for audio resampling
++ if (m_syncrefresh && m_throttled)
++ {
++ if (m_speed_percent >= 0.8 && m_speed_percent <= 1.2)
++ m_speed = m_speed_percent * 1000;
++ }
++
++ // log speed for frame delay statistic
++ if (!m_throttled)
++ {
++ int bin = (float) FD_BINS - 10.f/m_speed_percent;
++ bin = bin > (FD_BINS - 1) ? (FD_BINS - 1) : bin < 0 ? 0 : bin;
++ s_fd_speeds[bin]++;
++ }
++
+ // remember the last times
+ m_speed_last_realtime = realtime;
+ m_speed_last_emutime = emutime;
+diff --git a/src/emu/video.h b/src/emu/video.h
+index 2fe7786afb5..f205fa7fa62 100644
+--- a/src/emu/video.h
++++ b/src/emu/video.h
+@@ -28,6 +28,7 @@
+ constexpr int FRAMESKIP_LEVELS = 12;
+ constexpr int MAX_FRAMESKIP = FRAMESKIP_LEVELS - 2;
+
++constexpr int FD_BINS = 10;
+
+ //**************************************************************************
+ // TYPE DEFINITIONS
+@@ -50,6 +51,8 @@ public:
+ int frameskip() const { return m_auto_frameskip ? -1 : m_frameskip_level; }
+ bool throttled() const { return m_throttled; }
+ float throttle_rate() const { return m_throttle_rate; }
++ bool sync_refresh() const { return m_syncrefresh; }
++ int32_t framedelay() const { return m_framedelay; }
+ bool fastforward() const { return m_fastforward; }
+ bool is_recording() const;
+
+@@ -59,6 +62,7 @@ public:
+ void set_throttle_rate(float throttle_rate) { m_throttle_rate = throttle_rate; }
+ void set_fastforward(bool ffwd = true) { m_fastforward = ffwd; }
+ void set_output_changed() { m_output_changed = true; }
++ void set_framedelay(int framedelay) { m_framedelay = framedelay; }
+
+ // misc
+ void toggle_throttle();
+@@ -148,6 +152,8 @@ private:
+ // configuration
+ bool m_throttled; // flag: true if we're currently throttled
+ float m_throttle_rate; // target rate for throttling
++ bool m_syncrefresh; // flag: TRUE if we're currently refresh-synced
++ int32_t m_framedelay; // tenths of frame to delay emulation start
+ bool m_fastforward; // flag: true if we're currently fast-forwarding
+ u32 m_seconds_to_run; // number of seconds to run before quitting
+ bool m_auto_frameskip; // flag: true if we're automatically frameskipping
+@@ -172,6 +178,9 @@ private:
+ // movie recordings
+ std::vector<movie_recording::ptr> m_movie_recordings;
+
++ // frame delay statistics
++ static int s_fd_speeds[FD_BINS];
++
+ static const bool s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS];
+
+ static const attoseconds_t ATTOSECONDS_PER_SPEED_UPDATE = ATTOSECONDS_PER_SECOND / 4;
+diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp
+index aa5ec7606b8..f93231fa84b 100644
+--- a/src/frontend/mame/clifront.cpp
++++ b/src/frontend/mame/clifront.cpp
+@@ -1732,6 +1732,7 @@
+ osd_printf_info(
+ "%3$s v%2$s\n"
+ "%5$s\n"
++ "GroovyMAME - SwitchRes version %6$s\n"
+ "\n"
+ "This software reproduces, more or less faithfully, the behaviour of a wide range\n"
+ "of machines. But hardware is useless without software, so images of the ROMs and\n"
+@@ -1749,5 +1750,6 @@
+ build_version,
+ emulator_info::get_appname(),
+ emulator_info::get_configname(),
+- emulator_info::get_copyright_info());
++ emulator_info::get_copyright_info(),
++ SWITCHRES_VERSION);
+ }
+diff --git a/src/frontend/mame/mameopts.h b/src/frontend/mame/mameopts.h
+index 2b178fb2ead..502d20f76d0 100644
+--- a/src/frontend/mame/mameopts.h
++++ b/src/frontend/mame/mameopts.h
+@@ -29,6 +29,7 @@ enum
+
+ // INI-based options are NORMAL priority, in increasing order:
+ OPTION_PRIORITY_MAME_INI = OPTION_PRIORITY_NORMAL + 1,
++ OPTION_PRIORITY_SWITCHRES,
+ OPTION_PRIORITY_DEBUG_INI,
+ OPTION_PRIORITY_ORIENTATION_INI,
+ OPTION_PRIORITY_SYSTYPE_INI,
+diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp
+index 76b2f012731..dc6bf8532a6 100644
+--- a/src/frontend/mame/ui/info.cpp
++++ b/src/frontend/mame/ui/info.cpp
+@@ -420,6 +420,15 @@ std::string machine_info::game_info_string() const
+ }
+ }
+
++ // display SwitchRes information
++ modeline *mode = &m_machine.switchres.best_mode;
++ if (mode->hactive)
++ {
++ buf << _("\nSwitchres:\n");
++ util::stream_format(buf, "%d " UTF8_MULTIPLY " %d%s%s %2.3f Hz %2.3f kHz\n",
++ mode->hactive, mode->vactive, mode->interlace?"i":"p", mode->doublescan?"d":"", mode->vfreq, mode->hfreq/1000);
++ }
++
+ return buf.str();
+ }
+
+diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp
+index 207ac56d057..efee8fc4047 100644
+--- a/src/frontend/mame/ui/submenu.cpp
++++ b/src/frontend/mame/ui/submenu.cpp
+@@ -107,7 +107,7 @@ std::vector<submenu::option> const submenu::video_options = {
+ { submenu::option_type::OSD, __("Window Mode"), OSDOPTION_WINDOW },
+ { submenu::option_type::EMU, __("Enforce Aspect Ratio"), OPTION_KEEPASPECT },
+ { submenu::option_type::OSD, __("Start Out Maximized"), OSDOPTION_MAXIMIZE },
+- { submenu::option_type::OSD, __("Synchronized Refresh"), OSDOPTION_SYNCREFRESH },
++ { submenu::option_type::OSD, __("Synchronized Refresh"), OPTION_SYNCREFRESH },
+ { submenu::option_type::OSD, __("Wait Vertical Sync"), OSDOPTION_WAITVSYNC }
+ };
+
+diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp
+index bfcfd10bd97..885e8a825a0 100644
+--- a/src/frontend/mame/ui/ui.cpp
++++ b/src/frontend/mame/ui/ui.cpp
+@@ -31,6 +31,7 @@
+ #include "ui/viewgfx.h"
+ #include "imagedev/cassette.h"
+ #include "../osd/modules/lib/osdobj_common.h"
++#include "config.h"
+
+
+ /***************************************************************************
+@@ -193,6 +194,9 @@ void mame_ui_manager::init()
+ // request a callback upon exiting
+ machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&mame_ui_manager::exit, this));
+
++ // register callbacks
++ machine().configuration().config_register("sliders", config_load_delegate(&mame_ui_manager::config_load, this), config_save_delegate(&mame_ui_manager::config_save, this));
++
+ // create mouse bitmap
+ uint32_t *dst = &m_mouse_bitmap.pix32(0);
+ memcpy(dst,mouse_bitmap,32*32*sizeof(uint32_t));
+@@ -1351,6 +1355,9 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
+ // add overall volume
+ m_sliders.push_back(slider_alloc(SLIDER_ID_VOLUME, _("Master Volume"), -32, 0, 0, 1, nullptr));
+
++ // add frame delay
++ m_sliders.push_back(slider_alloc(SLIDER_ID_FRAMEDELAY, _("Frame Delay"), 0, machine.options().frame_delay(), 9, 1, nullptr));
++
+ // add per-channel volume
+ mixer_input info;
+ for (int item = 0; machine.sound().indexed_mixer_input(item, info); item++)
+@@ -1495,6 +1502,8 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
+ }
+ #endif
+
++ config_apply();
++
+ std::vector<ui::menu_item> items;
+ for (auto &slider : m_sliders)
+ {
+@@ -1518,6 +1527,8 @@ int32_t mame_ui_manager::slider_changed(running_machine &machine, void *arg, int
+ {
+ if (id == SLIDER_ID_VOLUME)
+ return slider_volume(machine, arg, id, str, newval);
++ else if (id == SLIDER_ID_FRAMEDELAY)
++ return slider_framedelay(machine, arg, id, str, newval);
+ else if (id >= SLIDER_ID_MIXERVOL && id <= SLIDER_ID_MIXERVOL_LAST)
+ return slider_mixervol(machine, arg, id, str, newval);
+ else if (id >= SLIDER_ID_ADJUSTER && id <= SLIDER_ID_ADJUSTER_LAST)
+@@ -1581,6 +1592,21 @@ int32_t mame_ui_manager::slider_volume(running_machine &machine, void *arg, int
+ }
+
+
++//-------------------------------------------------
++// slider_framedelay - global frame delay slider
++// callback
++//-------------------------------------------------
++
++int32_t mame_ui_manager::slider_framedelay(running_machine &machine, void *arg, int id, std::string *str, int32_t newval)
++{
++ if (newval != SLIDER_NOCHANGE)
++ machine.video().set_framedelay(newval);
++ if (str)
++ *str = string_format(_("%1$3d"), machine.video().framedelay());
++ return machine.video().framedelay();
++}
++
++
+ //-------------------------------------------------
+ // slider_mixervol - single channel volume
+ // slider callback
+@@ -2198,3 +2224,83 @@ void ui_colors::refresh(const ui_options &options)
+ m_dipsw_color = options.dipsw_color();
+ m_slider_color = options.slider_color();
+ }
++
++//-------------------------------------------------
++// config_load - read data from the
++// configuration file
++//-------------------------------------------------
++
++void mame_ui_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode)
++{
++ // we only care about game files
++ if (cfg_type != config_type::GAME)
++ return;
++
++ // might not have any data
++ if (parentnode == nullptr)
++ return;
++
++ // iterate over slider nodes
++ for (util::xml::data_node const *slider_node = parentnode->get_child("slider"); slider_node; slider_node = slider_node->get_next_sibling("slider"))
++ {
++ const char *desc = slider_node->get_attribute_string("desc", "");
++ int32_t saved_val = slider_node->get_attribute_int("value", 0);
++
++ // create a dummy slider to store the saved value
++ m_sliders_saved.push_back(slider_alloc(0, desc, 0, saved_val, 0, 0, 0));
++ }
++}
++
++
++//-------------------------------------------------
++// config_appy - apply data from the conf. file
++// This currently needs to be done on a separate
++// step because sliders are not created yet when
++// configuration file is loaded
++//-------------------------------------------------
++
++void mame_ui_manager::config_apply(void)
++{
++ // iterate over sliders and restore saved values
++ for (auto &slider : m_sliders)
++ {
++ for (auto &slider_saved : m_sliders_saved)
++ {
++ if (!strcmp(slider->description.c_str(), slider_saved->description.c_str()))
++ {
++ std::string tempstring;
++ slider->update(machine(), slider->arg, slider->id, &tempstring, slider_saved->defval);
++ break;
++
++ }
++ }
++ }
++}
++
++
++//-------------------------------------------------
++// config_save - save data to the configuration
++// file
++//-------------------------------------------------
++
++void mame_ui_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode)
++{
++ // we only care about game files
++ if (cfg_type != config_type::GAME)
++ return;
++
++ std::string tempstring;
++ util::xml::data_node *slider_node;
++
++ // save UI sliders
++ for (auto &slider : m_sliders)
++ {
++ int32_t curval = slider->update(machine(), slider->arg, slider->id, &tempstring, SLIDER_NOCHANGE);
++ if (curval != slider->defval)
++ {
++ slider_node = parentnode->add_child("slider", nullptr);
++ slider_node->set_attribute("desc", slider->description.c_str());
++ slider_node->set_attribute_int("value", curval);
++ }
++ }
++}
+\ No newline at end of file
+diff --git a/src/frontend/mame/ui/ui.h b/src/frontend/mame/ui/ui.h
+index 71f6a5ab8b8..f4ae6e2c962 100644
+--- a/src/frontend/mame/ui/ui.h
++++ b/src/frontend/mame/ui/ui.h
+@@ -53,6 +53,7 @@ class machine_info;
+ enum
+ {
+ SLIDER_ID_VOLUME = 0,
++ SLIDER_ID_FRAMEDELAY,
+ SLIDER_ID_MIXERVOL,
+ SLIDER_ID_MIXERVOL_LAST = SLIDER_ID_MIXERVOL + SLIDER_DEVICE_SPACING,
+ SLIDER_ID_ADJUSTER,
+@@ -234,6 +235,11 @@ public:
+ void start_save_state();
+ void start_load_state();
+
++ // config callbacks
++ void config_load(config_type cfg_type, util::xml::data_node const *parentnode);
++ void config_save(config_type cfg_type, util::xml::data_node *parentnode);
++ void config_apply(void);
++
+ // slider controls
+ std::vector<ui::menu_item>& get_slider_list(void);
+
+@@ -300,6 +306,7 @@ private:
+ virtual int32_t slider_changed(running_machine &machine, void *arg, int id, std::string *str, int32_t newval) override;
+
+ int32_t slider_volume(running_machine &machine, void *arg, int id, std::string *str, int32_t newval);
++ int32_t slider_framedelay(running_machine &machine, void *arg, int id, std::string *str, int32_t newval);
+ int32_t slider_mixervol(running_machine &machine, void *arg, int id, std::string *str, int32_t newval);
+ int32_t slider_adjuster(running_machine &machine, void *arg, int id, std::string *str, int32_t newval);
+ int32_t slider_overclock(running_machine &machine, void *arg, int id, std::string *str, int32_t newval);
+@@ -326,6 +333,7 @@ private:
+ #endif
+
+ std::vector<std::unique_ptr<slider_state>> m_sliders;
++ std::vector<std::unique_ptr<slider_state>> m_sliders_saved;
+ };
+
+
+diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp
+index 25d4b979e87..6f7c512dcc3 100644
+--- a/src/osd/modules/lib/osdobj_common.cpp
++++ b/src/osd/modules/lib/osdobj_common.cpp
+@@ -59,7 +59,6 @@ const options_entry osd_options::s_option_entries[] =
+ { OSDOPTION_WINDOW ";w", "0", OPTION_BOOLEAN, "enable window mode; otherwise, full screen mode is assumed" },
+ { OSDOPTION_MAXIMIZE ";max", "1", OPTION_BOOLEAN, "default to maximized windows" },
+ { OSDOPTION_WAITVSYNC ";vs", "0", OPTION_BOOLEAN, "enable waiting for the start of VBLANK before flipping screens (reduces tearing effects)" },
+- { OSDOPTION_SYNCREFRESH ";srf", "0", OPTION_BOOLEAN, "enable using the start of VBLANK for throttling instead of the game time" },
+ { OSD_MONITOR_PROVIDER, OSDOPTVAL_AUTO, OPTION_STRING, "monitor discovery method: " },
+
+ // per-window options
+@@ -91,10 +90,10 @@ const options_entry osd_options::s_option_entries[] =
+
+ // full screen options
+ { nullptr, nullptr, OPTION_HEADER, "OSD FULL SCREEN OPTIONS" },
+- { OSDOPTION_SWITCHRES, "0", OPTION_BOOLEAN, "enable resolution switching" },
++ { OSDOPTION_SWITCHRES, "1", OPTION_BOOLEAN, "enable resolution switching" },
+
+ { nullptr, nullptr, OPTION_HEADER, "OSD ACCELERATED VIDEO OPTIONS" },
+- { OSDOPTION_FILTER ";glfilter;flt", "1", OPTION_BOOLEAN, "use bilinear filtering when scaling emulated video" },
++ { OSDOPTION_FILTER ";glfilter;flt", "0", OPTION_BOOLEAN, "use bilinear filtering when scaling emulated video" },
+ { OSDOPTION_PRESCALE "(1-8)", "1", OPTION_INTEGER, "scale emulated video by this factor before applying filters/shaders" },
+
+ #if USE_OPENGL
+@@ -129,7 +128,7 @@ const options_entry osd_options::s_option_entries[] =
+
+ { nullptr, nullptr, OPTION_HEADER, "OSD SOUND OPTIONS" },
+ { OSDOPTION_SOUND, OSDOPTVAL_AUTO, OPTION_STRING, "sound output method: " },
+- { OSDOPTION_AUDIO_LATENCY "(1-5)", "2", OPTION_INTEGER, "set audio latency (increase to reduce glitches, decrease for responsiveness)" },
++ { OSDOPTION_AUDIO_LATENCY "(0.1-5.0)", "2.0", OPTION_FLOAT, "set audio latency (increase to reduce glitches, decrease for responsiveness)" },
+
+ #ifndef NO_USE_PORTAUDIO
+ { nullptr, nullptr, OPTION_HEADER, "PORTAUDIO OPTIONS" },
+diff --git a/src/osd/modules/lib/osdobj_common.h b/src/osd/modules/lib/osdobj_common.h
+index 9fb1b1eef6b..756b15f1b0a 100644
+--- a/src/osd/modules/lib/osdobj_common.h
++++ b/src/osd/modules/lib/osdobj_common.h
+@@ -50,7 +50,6 @@
+ #define OSDOPTION_WINDOW "window"
+ #define OSDOPTION_MAXIMIZE "maximize"
+ #define OSDOPTION_WAITVSYNC "waitvsync"
+-#define OSDOPTION_SYNCREFRESH "syncrefresh"
+
+ #define OSDOPTION_SCREEN "screen"
+ #define OSDOPTION_ASPECT "aspect"
+@@ -122,7 +121,6 @@ public:
+ bool window() const { return bool_value(OSDOPTION_WINDOW); }
+ bool maximize() const { return bool_value(OSDOPTION_MAXIMIZE); }
+ bool wait_vsync() const { return bool_value(OSDOPTION_WAITVSYNC); }
+- bool sync_refresh() const { return bool_value(OSDOPTION_SYNCREFRESH); }
+
+ // per-window options
+ const char *screen() const { return value(OSDOPTION_SCREEN); }
+@@ -153,7 +151,7 @@ public:
+
+ // sound options
+ const char *sound() const { return value(OSDOPTION_SOUND); }
+- int audio_latency() const { return int_value(OSDOPTION_AUDIO_LATENCY); }
++ float audio_latency() const { return float_value(OSDOPTION_AUDIO_LATENCY); }
+
+ // CoreAudio specific options
+ const char *audio_output() const { return value(OSDOPTION_AUDIO_OUTPUT); }
+diff --git a/src/osd/modules/osdwindow.h b/src/osd/modules/osdwindow.h
+index 3fe020c56fe..6479d544e43 100644
+--- a/src/osd/modules/osdwindow.h
++++ b/src/osd/modules/osdwindow.h
+@@ -215,9 +215,13 @@ public:
+ virtual void record() { };
+ virtual void toggle_fsfx() { };
+ virtual bool sliders_dirty() { return m_sliders_dirty; }
++ virtual int restart() { return 0; }
+
+ static std::unique_ptr<osd_renderer> make_for_type(int mode, std::shared_ptr<osd_window> window, int extra_flags = FLAG_NONE);
+
++ // SwitchRes mode
++ modeline * m_switchres_mode;
++
+ protected:
+ virtual void build_slider_list() { }
+
+@@ -262,6 +266,7 @@ struct osd_video_config
+ int waitvsync; // spin until vsync
+ int syncrefresh; // sync only to refresh rate
+ int switchres; // switch resolutions
++ int framedelay; // frame delay
+
+ // d3d, accel, opengl
+ int filter; // enable filtering
+diff --git a/src/osd/modules/render/d3d/d3dhlsl.h b/src/osd/modules/render/d3d/d3dhlsl.h
+index 7f7dacc4538..e700a36f36f 100644
+--- a/src/osd/modules/render/d3d/d3dhlsl.h
++++ b/src/osd/modules/render/d3d/d3dhlsl.h
+@@ -302,7 +302,7 @@ public:
+
+ bool init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *renderer);
+
+- bool enabled() { return post_fx_enable && d3dintf->post_fx_available; }
++ bool enabled() { return (this != nullptr) && post_fx_enable && d3dintf->post_fx_available; }
+ void toggle() { post_fx_enable = initialized && !post_fx_enable; }
+
+ void begin_draw();
+diff --git a/src/osd/modules/render/drawd3d.cpp b/src/osd/modules/render/drawd3d.cpp
+index 56c905636a4..2d503662083 100644
+--- a/src/osd/modules/render/drawd3d.cpp
++++ b/src/osd/modules/render/drawd3d.cpp
+@@ -45,7 +45,7 @@ enum
+ // INLINES
+ //============================================================
+
+-static inline BOOL GetClientRectExceptMenu(HWND hWnd, PRECT pRect, BOOL fullscreen)
++inline BOOL renderer_d3d9::GetClientRectExceptMenu(HWND hWnd, PRECT pRect, BOOL fullscreen)
+ {
+ static HMENU last_menu;
+ static RECT last_rect;
+@@ -53,6 +53,12 @@ static inline BOOL GetClientRectExceptMenu(HWND hWnd, PRECT pRect, BOOL fullscre
+ HMENU menu = GetMenu(hWnd);
+ BOOL result = GetClientRect(hWnd, pRect);
+
++ if (m_switchres_mode && m_switchres_mode->hactive)
++ {
++ pRect->right = m_switchres_mode->type & MODE_ROTATED? m_switchres_mode->vactive : m_switchres_mode->hactive;
++ pRect->bottom = m_switchres_mode->type & MODE_ROTATED? m_switchres_mode->hactive : m_switchres_mode->vactive;
++ }
++
+ if (!fullscreen || !menu)
+ return result;
+
+@@ -184,7 +190,14 @@ render_primitive_list *renderer_d3d9::get_primitives()
+ GetClientRectExceptMenu(hWnd, &client, win->fullscreen());
+ if (rect_width(&client) > 0 && rect_height(&client) > 0)
+ {
+- win->target()->set_bounds(rect_width(&client), rect_height(&client), win->pixel_aspect());
++ // handle aspect correction for magic resolutions
++ float aspect_corrector = 1.0f;
++ if (m_switchres_mode && m_switchres_mode->hactive)
++ {
++ aspect_corrector = ((float)m_switchres_mode->width / (float)m_switchres_mode->height) / ((float)m_switchres_mode->hactive / (float)m_switchres_mode->vactive);
++ if (m_switchres_mode->type & MODE_ROTATED) aspect_corrector = 1.0 / aspect_corrector;
++ }
++ win->target()->set_bounds(rect_width(&client), rect_height(&client), win->pixel_aspect() * aspect_corrector);
+ win->target()->set_max_update_rate((get_refresh() == 0) ? get_origmode().RefreshRate : get_refresh());
+ }
+ if (m_shaders != nullptr)
+@@ -733,12 +746,111 @@ void renderer_d3d9::end_frame()
+ if (FAILED(result))
+ osd_printf_verbose("Direct3D: Error %08lX during device end_scene call\n", result);
+
++ if (m_frame_delay != video_config.framedelay)
++ {
++ m_frame_delay = video_config.framedelay;
++ update_break_scanlines();
++ }
++
++ D3DRASTER_STATUS raster_status;
++ memset (&raster_status, 0, sizeof(D3DRASTER_STATUS));
++
++ // sync to VBLANK-BEGIN
++ if (video_config.framedelay && video_config.syncrefresh)
++ {
++ // check if retrace has been missed
++ if (m_device->GetRasterStatus(0, &raster_status) == D3D_OK)
++ {
++ if (raster_status.ScanLine < m_delay_scanline && !raster_status.InVBlank)
++ {
++ static const double tps = (double)osd_ticks_per_second();
++ static const double time_start = (double)osd_ticks() / tps;
++ osd_printf_verbose("renderer::end_frame(), probably missed retrace, entered at scanline %d, should break at %d, realtime is %f.\n", raster_status.ScanLine, m_break_scanline, (double)osd_ticks() / tps - time_start);
++ }
++ }
++
++ do
++ {
++ if (m_device->GetRasterStatus(0, &raster_status) != D3D_OK)
++ break;
++ } while (!raster_status.InVBlank && raster_status.ScanLine < m_break_scanline);
++ }
++
+ // present the current buffers
+ result = m_device->Present(nullptr, nullptr, nullptr, nullptr);
+ if (FAILED(result))
+ osd_printf_verbose("Direct3D: Error %08lX during device present call\n", result);
++
++ // sync to VBLANK-END
++ if (video_config.framedelay && video_config.syncrefresh)
++ {
++ do
++ {
++ if (m_device->GetRasterStatus(0, &raster_status) != D3D_OK)
++ break;
++ } while (!raster_status.InVBlank);
++ }
++}
++
++void renderer_d3d9::device_flush()
++{
++ HRESULT result;
++
++ if(m_device)
++ {
++ if(m_query != nullptr)
++ {
++ m_query->Issue(D3DISSUE_END);
++ do
++ {
++ result = m_query->GetData(NULL, 0, D3DGETDATA_FLUSH);
++ if (result == D3DERR_DEVICELOST)
++ return;
++ } while(result == S_FALSE);
++ }
++ }
++}
++
++void renderer_d3d9::update_break_scanlines()
++{
++ switch (m_vendor_id)
++ {
++ case 0x1002: // ATI
++ m_first_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
++ (m_switchres_mode->vtotal - m_switchres_mode->vbegin) / (m_switchres_mode->interlace ? 2 : 1) :
++ 1;
++
++ m_last_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
++ m_switchres_mode->vactive + (m_switchres_mode->vtotal - m_switchres_mode->vbegin) / (m_switchres_mode->interlace ? 2 : 1) :
++ m_height;
++ break;
++
++ case 0x8086: // Intel
++ m_first_scanline = 1;
++
++ m_last_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
++ m_switchres_mode->vactive / (m_switchres_mode->interlace ? 2 : 1) :
++ m_height;
++ break;
++
++ default: // NVIDIA (0x10DE) + others (?)
++ m_first_scanline = 0;
++
++ m_last_scanline = m_switchres_mode && m_switchres_mode->vtotal ?
++ (m_switchres_mode->vactive - 1) / (m_switchres_mode->interlace ? 2 : 1) :
++ m_height - 1;
++ break;
++ }
++
++ auto win = assert_window();
++ m_break_scanline = m_last_scanline - win->machine().options().vsync_offset();
++ m_break_scanline = m_break_scanline > m_first_scanline ? m_break_scanline : m_last_scanline;
++ m_delay_scanline = m_first_scanline + m_height * (float)video_config.framedelay / 10;
++
++ osd_printf_verbose("Direct3D: Frame delay: %d, First scanline: %d, Last scanline: %d, Break scanline: %d, Delay scanline: %d\n", video_config.framedelay, m_first_scanline, m_last_scanline, m_break_scanline, m_delay_scanline);
+ }
+
++
+ void renderer_d3d9::update_presentation_parameters()
+ {
+ auto win = assert_window();
+@@ -747,7 +859,7 @@ void renderer_d3d9::update_presentation_parameters()
+ m_presentation.BackBufferWidth = m_width;
+ m_presentation.BackBufferHeight = m_height;
+ m_presentation.BackBufferFormat = m_pixformat;
+- m_presentation.BackBufferCount = video_config.triplebuf ? 2 : 1;
++ m_presentation.BackBufferCount = 1;
+ m_presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
+ m_presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ m_presentation.hDeviceWindow = std::static_pointer_cast<win_window_info>(win)->platform_window();
+@@ -756,10 +868,10 @@ void renderer_d3d9::update_presentation_parameters()
+ m_presentation.AutoDepthStencilFormat = D3DFMT_D16;
+ m_presentation.Flags = 0;
+ m_presentation.FullScreen_RefreshRateInHz = m_refresh;
+- m_presentation.PresentationInterval = (
+- (video_config.triplebuf && win->fullscreen())
++ m_presentation.PresentationInterval = (video_config.framedelay == 0 &&
++ ((video_config.triplebuf && win->fullscreen())
+ || video_config.waitvsync
+- || video_config.syncrefresh)
++ || video_config.syncrefresh))
+ ? D3DPRESENT_INTERVAL_ONE
+ : D3DPRESENT_INTERVAL_IMMEDIATE;
+ }
+@@ -1184,6 +1296,34 @@ int renderer_d3d9::device_test_cooperative()
+ }
+
+
++//============================================================
++// restart
++//============================================================
++
++int renderer_d3d9::restart()
++{
++ // free all existing resources
++ device_delete_resources();
++
++ // configure new video mode
++ pick_best_mode();
++ update_presentation_parameters();
++
++ // reset the device
++ HRESULT result = m_device->Reset(&m_presentation);
++ if (FAILED(result))
++ {
++ osd_printf_error("Unable to reset, result %08lX\n", result);
++ return 1;
++ }
++
++ // create the resources again
++ device_create_resources();
++
++ return 0;
++}
++
++
+ //============================================================
+ // config_adapter_mode
+ //============================================================
+@@ -1203,6 +1343,9 @@ int renderer_d3d9::config_adapter_mode()
+ }
+
+ osd_printf_verbose("Direct3D: Configuring adapter #%d = %s\n", m_adapter, id.Description);
++ osd_printf_verbose("Direct3D: Adapter has Vendor ID: %lX and Device ID: %lX\n", id.VendorId, id.DeviceId);
++
++ m_vendor_id = id.VendorId;
+
+ // get the current display mode
+ result = d3dintf->d3dobj->GetAdapterDisplayMode(m_adapter, &m_origmode);
+@@ -1219,6 +1362,9 @@ int renderer_d3d9::config_adapter_mode()
+ {
+ RECT client;
+
++ // Disable SwitchRes
++ m_switchres_mode = 0;
++
+ // bounds are from the window client rect
+ GetClientRectExceptMenu(std::static_pointer_cast<win_window_info>(win)->platform_window(), &client, win->fullscreen());
+ m_width = client.right - client.left;
+@@ -1302,6 +1448,20 @@ void renderer_d3d9::pick_best_mode()
+
+ auto win = assert_window();
+
++ // only link window #0 to SwitchRes
++ if (win->m_index == 0)
++ {
++ m_switchres_mode = &win->machine().switchres.best_mode;
++ if (m_switchres_mode)
++ {
++ m_width = m_switchres_mode->type & MODE_ROTATED? m_switchres_mode->height : m_switchres_mode->width;
++ m_height = m_switchres_mode->type & MODE_ROTATED? m_switchres_mode->width : m_switchres_mode->height;
++ m_refresh = (int)m_switchres_mode->refresh;
++ m_interlace = m_switchres_mode->interlace;
++ return;
++ }
++ }
++
+ // determine the refresh rate of the primary screen
+ const screen_device *primary_screen = screen_device_iterator(win->machine().root_device()).first();
+ if (primary_screen != nullptr)
+@@ -1343,10 +1503,6 @@ void renderer_d3d9::pick_best_mode()
+ if (mode.Width < minwidth || mode.Height < minheight)
+ size_score *= 0.01f;
+
+- // if mode is smaller than we'd like, it only scores up to 0.1
+- if (mode.Width < target_width || mode.Height < target_height)
+- size_score *= 0.1f;
+-
+ // if we're looking for a particular mode, that's a winner
+ if (mode.Width == win->m_win_config.width && mode.Height == win->m_win_config.height)
+ size_score = 2.0f;
+@@ -1354,10 +1510,6 @@ void renderer_d3d9::pick_best_mode()
+ // compute refresh score
+ float refresh_score = 1.0f / (1.0f + fabs((double)mode.RefreshRate - target_refresh));
+
+- // if refresh is smaller than we'd like, it only scores up to 0.1
+- if ((double)mode.RefreshRate < target_refresh)
+- refresh_score *= 0.1f;
+-
+ // if we're looking for a particular refresh, make sure it matches
+ if (mode.RefreshRate == win->m_win_config.refresh)
+ refresh_score = 2.0f;
+diff --git a/src/osd/modules/render/drawd3d.h b/src/osd/modules/render/drawd3d.h
+index 03527d7374e..5f0637698d0 100644
+--- a/src/osd/modules/render/drawd3d.h
++++ b/src/osd/modules/render/drawd3d.h
+@@ -67,6 +67,8 @@ public:
+ virtual void add_audio_to_recording(const int16_t *buffer, int samples_this_frame) override;
+ virtual std::vector<ui::menu_item> get_slider_list() override;
+ virtual void set_sliders_dirty() override;
++ virtual int restart() override;
++ inline BOOL GetClientRectExceptMenu(HWND hWnd, PRECT pRect, BOOL fullscreen);
+
+ int initialize();
+
+@@ -74,6 +76,8 @@ public:
+ int device_create_resources();
+ void device_delete();
+ void device_delete_resources();
++ void device_flush();
++ void update_break_scanlines();
+ void update_presentation_parameters();
+ void update_gamma_ramp();
+
+@@ -134,9 +138,16 @@ public:
+
+ private:
+ int m_adapter; // ordinal adapter number
++ int m_vendor_id; // adapter vendor id
+ int m_width; // current width
+ int m_height; // current height
+ int m_refresh; // current refresh rate
++ bool m_interlace; // current interlace
++ int m_frame_delay; // current frame delay value
++ int m_first_scanline; // first scanline number (visible)
++ int m_last_scanline; // last scanline number (visible)
++ int m_delay_scanline; // scanline number supposed to be after frame delay
++ int m_break_scanline; // break scanline number, for vsync offset
+ int m_create_error_count; // number of consecutive create errors
+
+ IDirect3DDevice9 * m_device; // pointer to the Direct3DDevice object
+@@ -144,6 +155,7 @@ private:
+ D3DPRESENT_PARAMETERS m_presentation; // set of presentation parameters
+ D3DDISPLAYMODE m_origmode; // original display mode for the adapter
+ D3DFORMAT m_pixformat; // pixel format we are using
++ IDirect3DQuery9 * m_query;
+
+ IDirect3DVertexBuffer9 *m_vertexbuf; // pointer to the vertex buffer object
+ vertex * m_lockedbuf; // pointer to the locked vertex buffer
+diff --git a/src/osd/modules/render/drawogl.cpp b/src/osd/modules/render/drawogl.cpp
+index ae030224e74..02934712fea 100644
+--- a/src/osd/modules/render/drawogl.cpp
++++ b/src/osd/modules/render/drawogl.cpp
+@@ -40,6 +40,13 @@
+ #include "modules/opengl/gl_shader_tool.h"
+ #include "modules/opengl/gl_shader_mgr.h"
+
++#ifdef SDLMAME_X11
++// DRM
++#include <xf86drm.h>
++#include <xf86drmMode.h>
++#include <fcntl.h>
++#endif
++
+ #if defined(SDLMAME_MACOSX) || defined(OSD_MAC)
+ #include <cstring>
+ #include <cstdio>
+@@ -245,6 +252,10 @@ void renderer_ogl::set_blendmode(int blendmode)
+ // STATIC VARIABLES
+ //============================================================
+
++#ifdef SDLMAME_X11
++static int drawogl_drm_open(void);
++#endif
++
+ // OGL 1.3
+ #if defined(GL_ARB_multitexture) && !defined(OSD_MAC)
+ static PFNGLACTIVETEXTUREARBPROC pfn_glActiveTexture = nullptr;
+@@ -578,7 +589,11 @@ int renderer_ogl::create()
+ osd_printf_error("%s\n", m_gl_context->LastErrorMsg());
+ return 1;
+ }
+- m_gl_context->SetSwapInterval(video_config.waitvsync ? 1 : 0);
++#ifdef SDLMAME_X11
++ // Try to open DRM device
++ m_fd = drawogl_drm_open();
++#endif
++ m_gl_context->SetSwapInterval((video_config.waitvsync && m_fd == 0) ? 1 : 0);
+
+
+ m_blittimer = 0;
+@@ -605,6 +620,26 @@ int renderer_ogl::create()
+ return 0;
+ }
+
++#ifdef SDLMAME_X11
++//============================================================
++// drawogl_drm_open
++//============================================================
++
++static int drawogl_drm_open(void)
++{
++ int fd = 0;
++ const char *node = {"/dev/dri/card0"};
++
++ fd = open(node, O_RDWR | O_CLOEXEC);
++ if (fd < 0)
++ {
++ fprintf(stderr, "cannot open %s\n", node);
++ return 0;
++ }
++ osd_printf_verbose("%s successfully opened\n", node);
++ return fd;
++}
++#endif
+
+ //============================================================
+ // drawsdl_xy_to_render_target
+@@ -1419,6 +1454,19 @@ int renderer_ogl::draw(const int update)
+ win->m_primlist->release_lock();
+ m_init_context = 0;
+
++#ifdef SDLMAME_X11
++ // wait for vertical retrace
++ if (video_config.waitvsync && m_fd)
++ {
++ drmVBlank vbl;
++ memset(&vbl, 0, sizeof(vbl));
++ vbl.request.type = DRM_VBLANK_RELATIVE;
++ vbl.request.sequence = 1;
++ if (drmWaitVBlank(m_fd, &vbl) != 0)
++ osd_printf_verbose("drmWaitVBlank failed\n");
++ }
++#endif
++
+ m_gl_context->SwapBuffer();
+
+ return 0;
+@@ -1645,6 +1693,7 @@ void renderer_ogl::texture_compute_size_type(const render_texinfo *texsource, og
+ texture->rawheight_create = finalheight_create;
+ }
+
++
+ //============================================================
+ // texture_create
+ //============================================================
+diff --git a/src/osd/modules/render/drawogl.h b/src/osd/modules/render/drawogl.h
+index 525ba0c3ffa..165703f9c67 100644
+--- a/src/osd/modules/render/drawogl.h
++++ b/src/osd/modules/render/drawogl.h
+@@ -125,6 +125,7 @@ public:
+ , m_last_vofs(0.0f)
+ , m_surf_w(0)
+ , m_surf_h(0)
++ , m_fd(0)
+ {
+ for (int i=0; i < HASH_SIZE + OVERFLOW_SIZE; i++)
+ m_texhash[i] = nullptr;
+@@ -237,6 +238,8 @@ private:
+
+ static bool s_shown_video_info;
+ static bool s_dll_loaded;
++ // DRM file handle
++ int m_fd;
+ };
+
+ #endif // __DRAWOGL__
+diff --git a/src/osd/modules/sound/direct_sound.cpp b/src/osd/modules/sound/direct_sound.cpp
+index 05333674541..1ef1a76ad51 100644
+--- a/src/osd/modules/sound/direct_sound.cpp
++++ b/src/osd/modules/sound/direct_sound.cpp
+@@ -432,6 +432,7 @@ HRESULT sound_direct_sound::dsound_init()
+ stream_buffer_size = std::max(DWORD(1024), (stream_buffer_size / 1024) * 1024);
+
+ LOG(("stream_buffer_size = %u\n", (unsigned)stream_buffer_size));
++ osd_printf_verbose("stream_buffer_size = %u\n", (unsigned)stream_buffer_size);
+
+ // create the buffers
+ m_bytes_per_sample = stream_format.nBlockAlign;
+diff --git a/src/osd/modules/sound/sdl_sound.cpp b/src/osd/modules/sound/sdl_sound.cpp
+index 5ff8f2e6821..8c8d80cdf94 100644
+--- a/src/osd/modules/sound/sdl_sound.cpp
++++ b/src/osd/modules/sound/sdl_sound.cpp
+@@ -333,7 +333,7 @@ void sound_sdl::sdl_callback(void *userdata, Uint8 *stream, int len)
+ int sound_sdl::init(const osd_options &options)
+ {
+ int n_channels = 2;
+- int audio_latency;
++ float audio_latency;
+ SDL_AudioSpec aspec, obtained;
+ char audio_driver[16] = "";
+
+diff --git a/src/osd/osdcore.cpp b/src/osd/osdcore.cpp
+index fd696706e6b..fee08261a4f 100644
+--- a/src/osd/osdcore.cpp
++++ b/src/osd/osdcore.cpp
+@@ -133,13 +133,20 @@ void osd_vprintf_debug(util::format_argument_pack<std::ostream> const &args)
+ }
+
+
++#ifdef OSD_WINDOWS
++ typedef std::chrono::steady_clock s_clock;
++#else
++ typedef std::chrono::high_resolution_clock s_clock;
++#endif
++
++
+ //============================================================
+ // osd_ticks
+ //============================================================
+
+ osd_ticks_t osd_ticks()
+ {
+- return std::chrono::high_resolution_clock::now().time_since_epoch().count();
++ return s_clock::now().time_since_epoch().count();
+ }
+
+
+@@ -149,7 +156,7 @@ osd_ticks_t osd_ticks()
+
+ osd_ticks_t osd_ticks_per_second()
+ {
+- return std::chrono::high_resolution_clock::period::den / std::chrono::high_resolution_clock::period::num;
++ return s_clock::period::den / s_clock::period::num;
+ }
+
+ //============================================================
+@@ -162,7 +169,7 @@ void osd_sleep(osd_ticks_t duration)
+ // sleep_for appears to oversleep on Windows with gcc 8
+ Sleep(duration / (osd_ticks_per_second() / 1000));
+ #else
+- std::this_thread::sleep_for(std::chrono::high_resolution_clock::duration(duration));
++ std::this_thread::sleep_for(s_clock::duration(duration));
+ #endif
+ }
+
+diff --git a/src/osd/sdl/osdsdl.h b/src/osd/sdl/osdsdl.h
+index 4cbec2d9d14..48788a90aae 100644
+--- a/src/osd/sdl/osdsdl.h
++++ b/src/osd/sdl/osdsdl.h
+@@ -22,7 +22,6 @@
+ #define SDLOPTION_SCALEMODE "scalemode"
+
+ #define SDLOPTION_WAITVSYNC "waitvsync"
+-#define SDLOPTION_SYNCREFRESH "syncrefresh"
+ #define SDLOPTION_KEYMAP "keymap"
+ #define SDLOPTION_KEYMAP_FILE "keymap_file"
+
+@@ -143,6 +142,8 @@ public:
+ bool should_hide_mouse();
+ void process_events_buf();
+
++ void extract_video_config();
++
+ virtual sdl_options &options() override { return m_options; }
+
+ protected:
+@@ -152,7 +153,6 @@ protected:
+ private:
+ virtual void osd_exit() override;
+
+- void extract_video_config();
+ void output_oslog(const char *buffer);
+
+ sdl_options &m_options;
+diff --git a/src/osd/sdl/sdlmain.cpp b/src/osd/sdl/sdlmain.cpp
+index 37d1434a2e8..0ac77667cdc 100644
+--- a/src/osd/sdl/sdlmain.cpp
++++ b/src/osd/sdl/sdlmain.cpp
+@@ -62,6 +62,10 @@
+ #endif // INI_PATH
+
+
++extern bool switchres_modeline_setup(running_machine &machine);
++extern bool switchres_modeline_reset(running_machine &machine);
++extern bool switchres_modeline_remove(running_machine &machine);
++
+ //============================================================
+ // Global variables
+ //============================================================
+@@ -257,6 +261,10 @@ void sdl_osd_interface::osd_exit()
+ osd_common_t::osd_exit();
+
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
++
++ // SwitchRes modeline removal
++ switchres_modeline_reset(machine());
++ switchres_modeline_remove(machine());
+ }
+
+ //============================================================
+@@ -415,6 +423,10 @@ void sdl_osd_interface::init(running_machine &machine)
+
+ const char *stemp;
+
++ // Switchres
++ switchres_init_osd(machine);
++ switchres_modeline_setup(machine);
++
+ // determine if we are benchmarking, and adjust options appropriately
+ int bench = options().bench();
+ if (bench > 0)
+diff --git a/src/osd/sdl/switchres_sdl.cpp b/src/osd/sdl/switchres_sdl.cpp
+new file mode 100644
+index 00000000000..19e0582d131
+--- /dev/null
++++ b/src/osd/sdl/switchres_sdl.cpp
+@@ -0,0 +1,606 @@
++/**************************************************************
++
++ switchres_sdl.cpp - SDL OSD SwitchRes core routines
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2017 - Chris Kennedy, Antonio Giner, Alexandre W
++
++ **************************************************************/
++
++// SDL headers
++#include "SDL_syswm.h"
++
++// MAME headers
++#include "osdepend.h"
++#include "emu.h"
++#include "emuopts.h"
++#include "../../frontend/mame/mameopts.h"
++
++// MAMEOS headers
++#include "video.h"
++#include "input.h"
++#include "output.h"
++#include "osdsdl.h"
++#include "window.h"
++
++// X11 Xrandr headers
++#include <X11/extensions/Xrandr.h>
++
++#define XRANDR_ARGS ""
++#define min(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a < _b ? _a : _b; })
++
++#define XRANDR_TIMING 0x00000020
++extern int fd;
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++bool switchres_init_osd(running_machine &machine);
++bool switchres_modeline_setup(running_machine &machine);
++bool switchres_modeline_remove(running_machine &machine);
++bool switchres_modeline_reset(running_machine &machine);
++bool switchres_resolution_change(sdl_window_info *window);
++static bool add_custom_video_mode(modeline *mode, char *connector);
++static bool set_custom_video_mode(modeline *mode, char *connector);
++static int del_custom_video_mode(modeline *mode, char *connector);
++static void set_option_osd(running_machine &machine, const char *option_ID, bool state);
++
++//============================================================
++// LOCAL VARIABLES
++//============================================================
++
++int mode_count = 1;
++
++//============================================================
++// XRANDR
++//============================================================
++
++static Display *dpy;
++static Window root;
++
++static short original_rate;
++static Rotation original_rotation;
++static SizeID original_size_id;
++static int width = 0;
++static int height = 0;
++
++static int gmoutput_primary = 0;
++static int gmoutput_mode = 0;
++
++static int (*old_error_handler)(Display *, XErrorEvent *);
++
++static int xerrors = 0;
++
++static int error_handler (Display *dpy, XErrorEvent *err)
++{
++ xerrors++;
++ return 0;
++} /* xorg_error_handler() */
++
++//============================================================
++// switchres_init_osd
++//============================================================
++
++bool switchres_init_osd(running_machine &machine)
++{
++ config_settings *cs = &machine.switchres.cs;
++ game_info *game = &machine.switchres.game;
++ modeline *mode_table = machine.switchres.video_modes;
++ modeline *user_mode = &machine.switchres.user_mode;
++ monitor_range *range = machine.switchres.range;
++ const char * aspect;
++ char *connector = machine.switchres.cs.connector;
++ char resolution[32]={'\x00'};
++
++ sdl_options &options = downcast<sdl_options &>(machine.options());
++
++ // Initialize structures and config settings
++ memset(cs, 0, sizeof(struct config_settings));
++ memset(game, 0, sizeof(struct game_info));
++
++ // Init Switchres common info
++ switchres_init(machine);
++
++ // Complete config settings
++ strcpy(resolution, options.resolution());
++ cs->monitor_count = options.numscreens();
++
++ // Get current resolution
++ int screen = -1;
++
++ // dpy is global to reduce open/close calls, resource is freed when modeline is reset
++ dpy = XOpenDisplay(NULL);
++ int major_version, minor_version;
++ XRRQueryVersion(dpy, &major_version, &minor_version);
++ osd_printf_verbose("SwitchRes: xrandr version %d.%d\n",major_version,minor_version);
++
++ // select current display and root window
++ // root is global to reduce open/close calls, resource is freed when modeline is reset
++ screen = DefaultScreen(dpy); // multiple screen ScreenCount (dpy)
++ root = RootWindow(dpy, screen);
++ XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);
++
++ // get screen size, rate and rotation from screen configuration
++ XRRScreenConfiguration *sc = XRRGetScreenInfo(dpy, root);
++ original_rate = XRRConfigCurrentRate(sc);
++ original_size_id = XRRConfigCurrentConfiguration(sc, &original_rotation);
++ XRRFreeScreenConfigInfo(sc);
++
++ Rotation current_rotation = 0;
++ for (int o = 0; o < res->noutput && !gmoutput_mode; o++)
++ {
++ XRROutputInfo *output_info = XRRGetOutputInfo (dpy, res, res->outputs[o]);
++ if (!output_info)
++ osd_printf_error("SwitchRes: error could not get output 0x%x information\n", (uint) res->outputs[o]);
++
++ // first connected output
++ if (output_info->connection == RR_Connected)
++ {
++ for (int j = 0; j < output_info->nmode && !gmoutput_mode; j++)
++ {
++ if ( output_info->crtc )
++ {
++ XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(dpy, res, output_info->crtc);
++ current_rotation = crtc_info->rotation;
++ if (!strcmp(cs->connector, "auto") || !strcmp(cs->connector,output_info->name))
++ {
++ // connector name is kept but not necessary due to global gmoutput_primary varial, optimization can happen here
++ sprintf(connector,"%s", output_info->name);
++ osd_printf_verbose("SwitchRes: Found output connector '%s'\n", connector);
++ gmoutput_primary = o;
++ }
++ for (int m = 0; m < res->nmode && !gmoutput_mode; m++)
++ {
++ XRRModeInfo *mode = &res->modes[m];
++ // get screen mode
++ if (crtc_info->mode == mode->id)
++ {
++ gmoutput_mode = mode->id;
++ width = crtc_info->x + crtc_info->width;
++ height = crtc_info->y + crtc_info->height;
++ }
++ }
++ }
++ if (current_rotation & 0xe) // screen rotation is left or right
++ {
++ osd_printf_verbose("Switchres: desktop rotation is %s\n",(current_rotation & 0x2)?"left":((current_rotation & 0x8)?"right":"inverted"));
++ cs->desktop_rotated = 1;
++ }
++ }
++ }
++ XRRFreeOutputInfo(output_info);
++ }
++ XRRFreeScreenResources(res);
++
++ // Get per window resolution
++ strcpy(resolution, strcmp(options.resolution(0), "auto")? options.resolution(0) : options.resolution());
++
++ // Get monitor aspect
++ aspect = strcmp(options.aspect(0), "auto")? options.aspect(0) : options.aspect();
++ if (strcmp(aspect, "auto"))
++ {
++ float num, den;
++ sscanf(aspect, "%f:%f", &num, &den);
++ cs->monitor_aspect = cs->desktop_rotated? den/num : num/den;
++ }
++ else
++ cs->monitor_aspect = STANDARD_CRT_ASPECT;
++
++ // Create dummy mode table
++ mode_table[1].width = mode_table[1].height = 1;
++ mode_table[1].refresh = 60;
++ mode_table[1].vfreq = mode_table[1].refresh;
++ mode_table[1].hactive = mode_table[1].vactive = 1;
++ mode_table[1].type = XYV_EDITABLE | XRANDR_TIMING | (cs->desktop_rotated? MODE_ROTATED : MODE_OK);
++
++ if (user_mode->hactive)
++ {
++ user_mode->width = user_mode->hactive;
++ user_mode->height = user_mode->vactive;
++ user_mode->refresh = int(user_mode->refresh);
++ user_mode->type = XRANDR_TIMING | MODE_USER_DEF | (cs->desktop_rotated? MODE_ROTATED : MODE_OK);
++ }
++
++ // Create automatic specs and force resolution for LCD monitors
++ if (!strcmp(cs->monitor, "lcd"))
++ {
++ modeline current;
++ memset(&current, 0, sizeof(struct modeline));
++
++ osd_printf_verbose("SwitchRes: Creating automatic specs for LCD based on VESA GTF\n");
++ current.width = width;
++ current.height = height;
++ current.refresh = 60;
++ modeline_vesa_gtf(&current);
++ modeline_to_monitor_range(range, &current);
++ monitor_show_range(range);
++
++ sprintf(resolution, "%dx%d@%d", current.width, current.height, current.refresh);
++ }
++ // Otherwise (non-LCD), convert the user defined modeline into a -resolution option
++ else if (user_mode->hactive)
++ sprintf(resolution, "%dx%d", user_mode->hactive, user_mode->vactive);
++
++ // Get resolution from ini
++ if (strcmp(resolution, "auto"))
++ {
++ osd_printf_verbose("SwitchRes: -resolution was set at command line or in .ini file as %s\n", resolution);
++
++ if ((sscanf(resolution, "%dx%d@%d", &cs->width, &cs->height, &cs->refresh) < 3) &&
++ ((!strstr(resolution, "x") || (sscanf(resolution, "%dx%d", &cs->width, &cs->height) != 2))))
++ osd_printf_info("SwitchRes: illegal -resolution value: %s\n", resolution);
++ else
++ {
++ // Add the user's resolution to our table
++ if (!user_mode->hactive)
++ {
++ mode_table[1].width = mode_table[1].hactive = cs->width? cs->width : 1;
++ mode_table[1].height = mode_table[1].vactive = cs->height? cs->height : 1;
++ mode_table[1].refresh = cs->refresh? int(cs->refresh) : 60;
++ mode_table[1].vfreq = mode_table[1].refresh;
++ mode_table[1].type |= MODE_USER_DEF;
++ if (cs->width) mode_table[1].type &= ~X_RES_EDITABLE;
++ if (cs->height) mode_table[1].type &= ~Y_RES_EDITABLE;
++ }
++ }
++ }
++ // Get game info
++ switchres_get_game_info(machine);
++
++ return true;
++}
++
++//============================================================
++// switchres_modeline_setup
++//============================================================
++
++bool switchres_modeline_setup(running_machine &machine)
++{
++ modeline *best_mode = &machine.switchres.best_mode;
++ modeline *mode_table = machine.switchres.video_modes;
++ char *connector = machine.switchres.cs.connector;
++ sdl_options &options = downcast<sdl_options &>(machine.options());
++ sdl_osd_interface &osd = downcast<sdl_osd_interface &>(machine.osd());
++ std::string error_string;
++
++ osd_printf_verbose("\nSwitchRes: Entering switchres_modeline_setup\n");
++
++ // Find most suitable video mode and generate a modeline for it if we're allowed
++ if (!switchres_get_video_mode(machine))
++ {
++ set_option_osd(machine, OSDOPTION_SWITCHRES, false);
++ return false;
++ }
++
++ // Make the new modeline available to the system
++ if (machine.options().modeline_generation())
++ {
++ // Lock mode before adding it to mode table
++ best_mode->type |= MODE_DISABLED;
++
++ // Check if the same mode had been created already
++ int i;
++ bool found = false;
++ for (i = 2; i <= mode_count; i++)
++ if (!memcmp(&mode_table[i], best_mode, sizeof(modeline) - sizeof(mode_result)))
++ found = true;
++
++ // Create the new mode and store it in our table
++ if (!found)
++ {
++ mode_count++;
++ memcpy(&mode_table[mode_count], best_mode, sizeof(modeline));
++ add_custom_video_mode(best_mode, connector);
++ }
++
++ // Switch to the new mode
++ set_custom_video_mode(best_mode, connector);
++ }
++
++ // Set MAME common options
++ switchres_set_options(machine);
++
++ // Black frame insertion / multithreading
++ bool black_frame_insertion = options.black_frame_insertion() && best_mode->result.v_scale > 1 && best_mode->vfreq > 100;
++ set_option_osd(machine, OPTION_BLACK_FRAME_INSERTION, black_frame_insertion);
++
++ // Set MAME OSD specific options
++
++ // Vertical synchronization management (autosync)
++ // Disable -syncrefresh if our vfreq is scaled or out of syncrefresh_tolerance
++ bool sync_refresh_effective = black_frame_insertion || !((best_mode->result.weight & R_V_FREQ_OFF) || best_mode->result.v_scale > 1);
++ set_option_osd(machine, OPTION_SYNCREFRESH, options.autosync()? sync_refresh_effective : options.sync_refresh());
++ set_option_osd(machine, OSDOPTION_WAITVSYNC, options.sync_refresh()? options.sync_refresh() : options.wait_vsync());
++
++ // Set filter options
++ set_option_osd(machine, OSDOPTION_FILTER, ((best_mode->result.weight & R_RES_STRETCH || best_mode->interlace)));
++
++ // Refresh video options
++ osd.extract_video_config();
++
++ return true;
++}
++
++//============================================================
++// switchres_modeline_remove
++//============================================================
++
++bool switchres_modeline_remove(running_machine &machine)
++{
++ return true;
++}
++
++//============================================================
++// switchres_modeline_reset
++//============================================================
++
++bool switchres_modeline_reset(running_machine &machine)
++{
++ config_settings *cs = &machine.switchres.cs;
++ modeline *mode_table = machine.switchres.video_modes;
++
++ // Restore desktop resolution
++ XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);
++ XRRScreenConfiguration *sc = XRRGetScreenInfo(dpy, root);
++
++ XRRSetScreenConfigAndRate(dpy, sc, root, original_size_id, original_rotation, original_rate, CurrentTime);
++ XRRFreeScreenConfigInfo(sc);
++ XRRFreeScreenResources(res);
++
++ osd_printf_verbose("SwitchRes: xrandr original video mode restored.\n");
++
++ // Remove modelines
++ while (mode_count > 1)
++ {
++ del_custom_video_mode(&mode_table[mode_count], cs->connector);
++ mode_count--;
++ }
++
++ XCloseDisplay(dpy);
++ return true;
++}
++
++//============================================================
++// switchres_resolution_change
++//============================================================
++
++bool switchres_resolution_change(sdl_window_info *window)
++{
++ running_machine &machine = window->machine();
++ modeline *best_mode = &machine.switchres.best_mode;
++ modeline previous_mode;
++
++ // If there's no pending change, just exit
++ if (!switchres_check_resolution_change(machine))
++ return false;
++
++ // Get the new resolution
++ previous_mode = *best_mode;
++ switchres_modeline_setup(machine);
++
++ // Only change resolution if the new one is actually different
++ if (memcmp(&previous_mode, best_mode, offsetof(modeline, result)))
++ return true;
++
++ return false;
++}
++
++//============================================================
++// add_custom_video_mode
++//============================================================
++
++static bool add_custom_video_mode(modeline *mode, char *connector)
++{
++ if (!mode)
++ return false;
++
++ // Add modeline to interface
++ char name[48];
++ sprintf(name,"GM-%dx%d_%.6f",mode->hactive, mode->vactive, mode->vfreq); // add ID
++
++ // Setup the xrandr mode structure
++ XRRModeInfo xmode;
++ xmode.name = name;
++ xmode.nameLength = strlen(name);
++ xmode.dotClock = float(mode->pclock);
++ xmode.width = mode->hactive;
++ xmode.hSyncStart = mode->hbegin;
++ xmode.hSyncEnd = mode->hend;
++ xmode.hTotal = mode->htotal;
++ xmode.height = mode->vactive;
++ xmode.vSyncStart = mode->vbegin;
++ xmode.vSyncEnd = mode->vend;
++ xmode.vTotal = mode->vtotal;
++ xmode.modeFlags = (mode->interlace?RR_Interlace:0) | (mode->doublescan?RR_DoubleScan:0) | (mode->hsync?RR_HSyncPositive:RR_HSyncNegative) | (mode->vsync?RR_VSyncPositive:RR_VSyncNegative);
++
++ // Create the modeline
++ XSync(dpy, False);
++ xerrors = 0;
++ old_error_handler = XSetErrorHandler(error_handler);
++ RRMode gmid = XRRCreateMode(dpy, root, &xmode);
++ XSync(dpy, False);
++ XSetErrorHandler(old_error_handler);
++ if (xerrors)
++ osd_printf_error("Switchres: xrandr error in %s\n","XRRCreateMode");
++
++ // Add new modeline to primary output
++ XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);
++
++ XSync(dpy, False);
++ xerrors = 0;
++ old_error_handler = XSetErrorHandler(error_handler);
++ XRRAddOutputMode(dpy, res->outputs[gmoutput_primary], gmid);
++ XSync(dpy, False);
++ XSetErrorHandler(old_error_handler);
++ if (xerrors)
++ osd_printf_error("Switchres: xrandr error in %s\n","XRRAddOutputMode");
++
++ XRRFreeScreenResources(res);
++ return true;
++}
++
++//============================================================
++// set_custom_video_mode
++//============================================================
++
++static bool set_custom_video_mode(modeline *mode, char *connector)
++{
++ // Use xrandr to switch to new mode. SDL_SetVideoMode doesn't work when (new_width, new_height)==(old_width, old_height)
++ char name[48];
++ sprintf(name,"GM-%dx%d_%.6f",mode->hactive, mode->vactive, mode->vfreq); // add ID
++
++ XRRScreenResources *res = XRRGetScreenResourcesCurrent(dpy, root);
++ XRROutputInfo *output_info = XRRGetOutputInfo(dpy, res, res->outputs[gmoutput_primary]);
++ XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(dpy, res, output_info->crtc);
++
++ // Select corresponding mode from modeline, can be enhanced by saving mode index to modeline structure
++ XRRModeInfo *xmode=0;
++ for (int m = 0; m < res->nmode; m++)
++ {
++ XRRModeInfo *tmp_mode = &res->modes[m];
++ if (!strcmp(name, tmp_mode->name))
++ {
++ xmode = &res->modes[m];
++ }
++ }
++
++ // Grab X server to prevent unwanted interaction from the window manager
++ XGrabServer(dpy);
++
++ // Disable all CRTCs
++ for (int i = 0; i < output_info->ncrtc; i++)
++ {
++ if (XRRSetCrtcConfig(dpy, res, output_info->crtcs[i], CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0) != RRSetConfigSuccess)
++ osd_printf_error("Switchres: xrandr error when disabling CRTC.\n");
++ }
++ osd_printf_verbose("Switchres: CRTC %d: mode %#lx, %ux%u+%d+%d.\n", 0, crtc_info->mode,crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
++
++ // Check if framebuffer size is correct
++ int change_resolution = 0;
++ if (width < crtc_info->x + mode->hactive)
++ {
++ width = crtc_info->x + mode->hactive;
++ change_resolution = 1;
++ }
++ if (height < crtc_info->y + mode->vactive)
++ {
++ height = crtc_info->y + mode->vactive;
++ change_resolution = 1;
++ }
++
++ // Enlarge the screen size for the new mode
++ if (change_resolution)
++ {
++ osd_printf_verbose("Switchres: xrandr change screen size.\n");
++ XSync(dpy, False);
++ xerrors = 0;
++ old_error_handler = XSetErrorHandler(error_handler);
++ XRRSetScreenSize(dpy, root, width, height, (25.4 * width) / 96.0, (25.4 * height) / 96.0);
++ XSync(dpy, False);
++ XSetErrorHandler(old_error_handler);
++ if (xerrors)
++ osd_printf_error("Switchres: xrandr error in %s\n","XRRSetScreenSize");
++ }
++
++ // Switch to new modeline
++ XSync(dpy, False);
++ xerrors = 0;
++ old_error_handler = XSetErrorHandler(error_handler);
++ XRRSetCrtcConfig(dpy, res, output_info->crtc, CurrentTime , crtc_info->x, crtc_info->y, xmode->id, original_rotation, crtc_info->outputs, crtc_info->noutput);
++ XSync(dpy, False);
++ XSetErrorHandler(old_error_handler);
++
++ XRRFreeCrtcInfo(crtc_info);
++
++ if (xerrors)
++ osd_printf_error("Switchres: xrandr error in %s\n","XRRSetCrtcConfig");
++
++ // Release X server, events can be processed now
++ XUngrabServer(dpy);
++
++ crtc_info = XRRGetCrtcInfo(dpy, res, output_info->crtc); // recall crtc to settle parameters
++
++ // If the crtc config modeline change fails, revert to original mode (prevents ending with black screen due to all crtc disabled)
++ if (crtc_info->mode == 0)
++ {
++ osd_printf_error("Switchres: xrandr resolution switch error, original mode restored\n");
++ XRRScreenConfiguration *sc = XRRGetScreenInfo(dpy, root);
++ XRRSetScreenConfigAndRate(dpy, sc, root, original_size_id, original_rotation, original_rate, CurrentTime);
++ XRRFreeScreenConfigInfo(sc);
++ }
++
++ // check, verify current active mode
++ for (int m = 0; m < res->nmode; m++)
++ {
++ XRRModeInfo *mode = &res->modes[m];
++ if (mode->id == crtc_info->mode)
++ osd_printf_verbose("Switchres: xrandr mode (%s) (0x%x) %6.6fMHz\n", mode->name, (int)mode->id,(double)mode->dotClock / 1000000.0);
++ }
++
++ XRRFreeCrtcInfo(crtc_info);
++ XRRFreeOutputInfo(output_info);
++ XRRFreeScreenResources(res);
++
++ return true;
++}
++
++//============================================================
++// del_custom_video_mode
++//============================================================
++
++static int del_custom_video_mode(modeline *mode, char *connector)
++{
++ if (!mode)
++ return false;
++
++ char name[48];
++ sprintf(name,"GM-%dx%d_%.6f",mode->hactive, mode->vactive, mode->vfreq); // add ID
++
++ XRRScreenResources *res = XRRGetScreenResourcesCurrent (dpy, root);
++
++ // Delete modeline
++ for (int m = 0; m < res->nmode; m++)
++ {
++ XRRModeInfo *xmode = &res->modes[m];
++ if (!strcmp(name, xmode->name))
++ {
++ XSync(dpy, False);
++ xerrors = 0;
++ old_error_handler = XSetErrorHandler(error_handler);
++ XRRDeleteOutputMode (dpy, res->outputs[gmoutput_primary], xmode->id);
++ if (xerrors)
++ osd_printf_error("Switchres: xrandr error in %s\n","XRRDeleteOutputMode");
++
++ xerrors = 0;
++ XRRDestroyMode (dpy, xmode->id);
++ XSync(dpy, False);
++ XSetErrorHandler(old_error_handler);
++ if (xerrors)
++ osd_printf_error("Switchres: xrandr error in %s\n","XRRDestroyMode");
++ }
++ }
++
++ XRRFreeScreenResources(res);
++
++ return true;
++}
++
++//============================================================
++// set_option_osd - option setting wrapper
++//============================================================
++
++static void set_option_osd(running_machine &machine, const char *option_ID, bool state)
++{
++ sdl_options &options = downcast<sdl_options &>(machine.options());
++
++ options.set_value(option_ID, state, OPTION_PRIORITY_SWITCHRES);
++ osd_printf_verbose("SwitchRes: Setting option -%s%s\n", machine.options().bool_value(option_ID)?"":"no", option_ID);
++}
+diff --git a/src/osd/sdl/video.cpp b/src/osd/sdl/video.cpp
+index e7af55e56ba..8d1eeb1eaa8 100644
+--- a/src/osd/sdl/video.cpp
++++ b/src/osd/sdl/video.cpp
+@@ -247,12 +247,7 @@ void sdl_osd_interface::extract_video_config()
+ video_config.centerh = options().centerh();
+ video_config.centerv = options().centerv();
+ video_config.waitvsync = options().wait_vsync();
+- video_config.syncrefresh = options().sync_refresh();
+- if (!video_config.waitvsync && video_config.syncrefresh)
+- {
+- osd_printf_warning("-syncrefresh specified without -waitvsync. Reverting to -nosyncrefresh\n");
+- video_config.syncrefresh = 0;
+- }
++ video_config.syncrefresh = machine().options().sync_refresh();
+
+ if (video_config.prescale < 1 || video_config.prescale > 8)
+ {
+diff --git a/src/osd/sdl/window.cpp b/src/osd/sdl/window.cpp
+index 70886d76a73..83f6c5f035d 100644
+--- a/src/osd/sdl/window.cpp
++++ b/src/osd/sdl/window.cpp
+@@ -80,6 +80,7 @@ public:
+ // PROTOTYPES
+ //============================================================
+
++extern bool switchres_resolution_change(sdl_window_info *window);
+
+ //============================================================
+ // window_init
+@@ -352,6 +353,23 @@ void sdl_window_info::modify_prescale(int dir)
+ }
+ }
+
++void sdl_window_info::reset_fullscreen_renderer()
++{
++#if (SDLMAME_SDL2)
++ if (this->fullscreen() && video_config.switchres)
++ {
++ complete_destroy();
++ SDL_QuitSubSystem(SDL_INIT_VIDEO);
++ SDL_InitSubSystem(SDL_INIT_VIDEO);
++ complete_create();
++ }
++#else
++ if (this->fullscreen() && video_config.switchres)
++ this->window_resize(window->minwidth, window->minheight);
++#endif
++}
++
++
+ //============================================================
+ // sdlwindow_update_cursor_state
+ // (main or window thread)
+@@ -491,6 +509,14 @@ osd_dim sdl_window_info::pick_best_mode()
+ float size_score, best_score = 0.0f;
+ osd_dim ret(0,0);
+
++ // check if we already have a best mode
++ modeline *mode = &this->machine().switchres.best_mode;
++ if (mode->hactive)
++ {
++ ret = osd_dim(mode->type & MODE_ROTATED? mode->vactive : mode->hactive, mode->type & MODE_ROTATED? mode->hactive : mode->vactive);
++ return ret;
++ }
++
+ // determine the minimum width/height for the selected target
+ m_target->compute_minimum_size(minimum_width, minimum_height);
+
+@@ -584,8 +610,15 @@ void sdl_window_info::update()
+ }
+ else if (video_config.switchres)
+ {
+- osd_dim tmp = this->pick_best_mode();
+- resize(tmp.width(), tmp.height());
++ // check resolution change
++ if (renderer().m_switchres_mode != nullptr && video_config.switchres && machine().options().changeres())
++ {
++ if (switchres_resolution_change(this))
++ {
++ reset_fullscreen_renderer();
++ return;
++ }
++ }
+ }
+ }
+
+diff --git a/src/osd/sdl/window.h b/src/osd/sdl/window.h
+index d6010e91ed2..9b2ef28371b 100644
+--- a/src/osd/sdl/window.h
++++ b/src/osd/sdl/window.h
+@@ -73,7 +73,6 @@ public:
+ // Pointer to next window
+ sdl_window_info * m_next;
+
+-private:
+ // window handle and info
+ char m_title[256];
+ int m_startmaximized;
+@@ -105,6 +104,7 @@ private:
+ void update_cursor_state();
+ osd_dim pick_best_mode();
+ void set_fullscreen(int afullscreen) { m_fullscreen = afullscreen; }
++ void reset_fullscreen_renderer();
+
+ // Pointer to machine
+ running_machine & m_machine;
+diff --git a/src/osd/windows/custom_video.cpp b/src/osd/windows/custom_video.cpp
+new file mode 100644
+index 00000000000..a07fb6dc95c
+--- /dev/null
++++ b/src/osd/windows/custom_video.cpp
+@@ -0,0 +1,359 @@
++/**************************************************************
++
++ custom_video.cpp - Custom video library
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++// standard windows headers
++#include <windows.h>
++
++#include "emu.h"
++#include "custom_video.h"
++#include "custom_video_ati.h"
++#include "custom_video_adl.h"
++#include "custom_video_pstrip.h"
++
++extern bool ati_is_legacy(int vendor, int device);
++
++//============================================================
++// LOCAL VARIABLES
++//============================================================
++
++static int custom_method;
++static modeline m_user_mode;
++static modeline m_backup_mode;
++static modeline *m_mode_table;
++static char m_device_name[32];
++static char m_device_key[128];
++static char ps_timing[256];
++
++//============================================================
++// custom_video_init
++//============================================================
++
++bool custom_video_init(char *device_name, char *device_id, modeline *desktop_mode, modeline *user_mode, modeline *mode_table, int method, char *s_param)
++{
++ memset(&m_backup_mode, 0, sizeof(modeline));
++ memcpy(&m_user_mode, user_mode, sizeof(modeline));
++ memcpy(m_device_name, device_name, sizeof(m_device_name));
++ m_mode_table = mode_table;
++
++ if ((method == CUSTOM_VIDEO_TIMING_POWERSTRIP) && ps_init(ps_monitor_index(m_device_name), &m_backup_mode))
++ {
++ custom_method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
++ m_backup_mode.type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
++
++ // If we have a -ps_timing string defined, use it as user defined modeline
++ memcpy(ps_timing, s_param, sizeof(ps_timing));
++ if (strcmp(ps_timing, "auto"))
++ {
++ MonitorTiming timing;
++ if (ps_read_timing_string(ps_timing, &timing))
++ {
++ ps_pstiming_to_modeline(&timing, &m_user_mode);
++ m_user_mode.type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
++ memcpy(user_mode, &m_user_mode, sizeof(modeline));
++
++ char modeline_txt[256]={'\x00'};
++ osd_printf_verbose("SwitchRes: ps_string: %s (%s)\n", ps_timing, modeline_print(&m_user_mode, modeline_txt, MS_PARAMS));
++ }
++ else osd_printf_verbose("Switchres: ps_timing string with invalid format\n");
++ }
++ return true;
++ }
++ else
++ {
++ int vendor, device;
++ custom_video_parse_pci_id(device_id, &vendor, &device);
++
++ if (vendor == 0x1002) // ATI/AMD
++ {
++ if (ati_is_legacy(vendor, device))
++ {
++ memcpy(m_device_key, s_param, sizeof(m_device_key));
++ if (ati_init(m_device_name, m_device_key, device_id))
++ {
++ custom_method = CUSTOM_VIDEO_TIMING_ATI_LEGACY;
++ return true;
++ }
++ }
++ else
++ {
++ memcpy(m_device_key, s_param, sizeof(m_device_key));
++ if (adl_init(m_device_name, m_device_key, device_id))
++ {
++ custom_method = CUSTOM_VIDEO_TIMING_ATI_ADL;
++ return true;
++ }
++ }
++ }
++ else
++ osd_printf_info("Video chipset is not compatible.\n");
++ }
++
++ return false;
++}
++
++//============================================================
++// custom_video_close
++//============================================================
++
++void custom_video_close()
++{
++ switch (custom_method)
++ {
++ case CUSTOM_VIDEO_TIMING_ATI_LEGACY:
++ break;
++
++ case CUSTOM_VIDEO_TIMING_ATI_ADL:
++ adl_close();
++ break;
++
++ case CUSTOM_VIDEO_TIMING_POWERSTRIP:
++ break;
++ }
++}
++
++//============================================================
++// custom_video_get_timing
++//============================================================
++
++bool custom_video_get_timing(modeline *mode)
++{
++ char modeline_txt[256]={'\x00'};
++
++ switch (custom_method)
++ {
++ case CUSTOM_VIDEO_TIMING_ATI_LEGACY:
++ if (ati_get_modeline(mode))
++ {
++ osd_printf_verbose("ATI legacy timing %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY | (!(mode->type & MODE_DESKTOP)? V_FREQ_EDITABLE | (mode->width == DUMMY_WIDTH? X_RES_EDITABLE:0):0);
++ return true;
++ }
++ break;
++
++ case CUSTOM_VIDEO_TIMING_ATI_ADL:
++ if (adl_get_modeline(m_device_name, mode))
++ {
++ osd_printf_verbose("ATI ADL timing %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL | (!(mode->type & MODE_DESKTOP)? V_FREQ_EDITABLE :0);
++ return true;
++ }
++ break;
++
++ case CUSTOM_VIDEO_TIMING_POWERSTRIP:
++ if ((mode->type & MODE_DESKTOP) && ps_get_modeline(ps_monitor_index(m_device_name), mode))
++ osd_printf_verbose("Powerstrip timing %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ else
++ osd_printf_verbose("Not current mode\n");
++
++ mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP | V_FREQ_EDITABLE;
++ return true;
++ }
++
++ osd_printf_verbose("system mode\n");
++ mode->type |= CUSTOM_VIDEO_TIMING_SYSTEM;
++ return false;
++}
++
++//============================================================
++// custom_video_set_timing
++//============================================================
++
++bool custom_video_set_timing(modeline *mode)
++{
++ char modeline_txt[256]={'\x00'};
++
++ switch (custom_method)
++ {
++ case CUSTOM_VIDEO_TIMING_ATI_LEGACY:
++ if (ati_set_modeline(mode))
++ {
++ osd_printf_verbose("ATI legacy timing %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ return true;
++ }
++ break;
++
++ case CUSTOM_VIDEO_TIMING_ATI_ADL:
++ if (adl_set_modeline(m_device_name, mode, mode->interlace != m_backup_mode.interlace? MODELINE_UPDATE_LIST : MODELINE_UPDATE))
++ {
++ osd_printf_verbose("ATI ADL timing %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ return true;
++ }
++ break;
++
++ case CUSTOM_VIDEO_TIMING_POWERSTRIP:
++ // In case -ps_timing is provided, pass it as raw string
++ if (m_user_mode.type & CUSTOM_VIDEO_TIMING_POWERSTRIP)
++ ps_set_monitor_timing_string(ps_monitor_index(m_device_name), (char*)ps_timing);
++ // Otherwise pass it as modeline
++ else
++ ps_set_modeline(ps_monitor_index(m_device_name), mode);
++
++ osd_printf_verbose("Powerstrip timing %s\n", modeline_print(mode, modeline_txt, MS_FULL));
++ Sleep(100);
++ return true;
++ break;
++
++ default:
++ break;
++ }
++ return false;
++}
++
++//============================================================
++// custom_video_restore_timing
++//============================================================
++
++bool custom_video_restore_timing()
++{
++ if (!m_backup_mode.hactive)
++ return false;
++
++ // Restore backup mode
++ return custom_video_update_timing(0);
++}
++
++//============================================================
++// custom_video_refresh_timing
++//============================================================
++
++void custom_video_refresh_timing()
++{
++ switch (custom_method)
++ {
++ case CUSTOM_VIDEO_TIMING_ATI_LEGACY:
++ ati_refresh_timings();
++ break;
++
++ case CUSTOM_VIDEO_TIMING_ATI_ADL:
++ break;
++
++ case CUSTOM_VIDEO_TIMING_POWERSTRIP:
++ break;
++ }
++}
++
++//============================================================
++// custom_video_update_timing
++//============================================================
++
++bool custom_video_update_timing(modeline *mode)
++{
++ switch (custom_method)
++ {
++ case CUSTOM_VIDEO_TIMING_ATI_LEGACY:
++ case CUSTOM_VIDEO_TIMING_ATI_ADL:
++
++ // Restore old video timing
++ if (m_backup_mode.hactive)
++ {
++ osd_printf_verbose("Switchres: restoring ");
++ custom_video_set_timing(&m_backup_mode);
++ }
++
++ // Update with new video timing
++ if (mode)
++ {
++ // Backup current timing
++ int found = 0;
++ for (int i = 0; i <= MAX_MODELINES; i++)
++ {
++ if (m_mode_table[i].width == mode->width && m_mode_table[i].height == mode->height && m_mode_table[i].refresh == mode->refresh)
++ {
++ memcpy(&m_backup_mode, &m_mode_table[i], sizeof(modeline));
++ found = 1;
++ break;
++ }
++ }
++ if (!found)
++ {
++ osd_printf_verbose("Switchres: mode not found in mode_table\n");
++ return false;
++ }
++ osd_printf_verbose("Switchres: saving ");
++ custom_video_get_timing(&m_backup_mode);
++
++ // Apply new timing now
++ osd_printf_verbose("Switchres: updating ");
++ if (!custom_video_set_timing(mode)) goto error;
++ }
++ custom_video_refresh_timing();
++ break;
++
++ case CUSTOM_VIDEO_TIMING_POWERSTRIP:
++ // We only backup/restore the desktop mode with Powerstrip
++ if (!mode)
++ ps_reset(ps_monitor_index(m_device_name));
++ else
++ {
++ osd_printf_verbose("Switchres: updating ");
++ custom_video_set_timing(mode);
++ }
++ break;
++ }
++ return true;
++
++error:
++ osd_printf_verbose(": error updating video timings\n");
++ return false;
++}
++
++//============================================================
++// custom_video_parse_timing
++//============================================================
++
++bool custom_video_parse_timing(char *timing_string, modeline *user_mode)
++{
++ char modeline_txt[256]={'\x00'};
++
++ if (!strcmp(timing_string, "auto"))
++ return false;
++
++ if (strstr(timing_string, "="))
++ {
++ // Powerstrip timing string
++ MonitorTiming timing;
++ ps_read_timing_string(timing_string, &timing);
++ ps_pstiming_to_modeline(&timing, user_mode);
++ user_mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
++ osd_printf_verbose("SwitchRes: ps_string: %s (%s)\n", timing_string, modeline_print(user_mode, modeline_txt, MS_PARAMS));
++ }
++ else
++ {
++ // Normal modeline
++ modeline_parse(timing_string, user_mode);
++ osd_printf_verbose("SwitchRes: modeline: %s \n", modeline_print(user_mode, modeline_txt, MS_PARAMS));
++ }
++
++ return true;
++}
++
++//============================================================
++// custom_video_parse_pci_id
++//============================================================
++
++int custom_video_parse_pci_id(char *device_id, int *vendor, int *device)
++{
++ return sscanf(device_id, "PCI\\VEN_%x&DEV_%x", vendor, device);
++}
++
++//============================================================
++// custom_get_backup_mode
++//============================================================
++
++modeline *custom_video_get_backup_mode()
++{
++ return &m_backup_mode;
++}
+diff --git a/src/osd/windows/custom_video.h b/src/osd/windows/custom_video.h
+new file mode 100644
+index 00000000000..4e0fe242d75
+--- /dev/null
++++ b/src/osd/windows/custom_video.h
+@@ -0,0 +1,31 @@
++/**************************************************************
++
++ custom_video.h - Custom video library header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#define CUSTOM_VIDEO_TIMING_MASK 0x00000ff0
++#define CUSTOM_VIDEO_TIMING_SYSTEM 0x00000010
++#define CUSTOM_VIDEO_TIMING_XRANDR 0x00000020
++#define CUSTOM_VIDEO_TIMING_POWERSTRIP 0x00000040
++#define CUSTOM_VIDEO_TIMING_ATI_LEGACY 0x00000080
++#define CUSTOM_VIDEO_TIMING_ATI_ADL 0x00000100
++
++bool custom_video_init(char *device_name, char *device_id, modeline *desktop_mode, modeline *user_mode, modeline *mode_table, int method, char *s_param);
++void custom_video_close();
++bool custom_video_get_timing(modeline *mode);
++bool custom_video_set_timing(modeline *mode);
++bool custom_video_restore_timing();
++bool custom_video_update_timing(modeline *mode);
++int custom_video_parse_pci_id(char *device_id, int *vendor, int *device);
++modeline *custom_video_get_backup_mode();
+diff --git a/src/osd/windows/custom_video_adl.cpp b/src/osd/windows/custom_video_adl.cpp
+new file mode 100644
+index 00000000000..5d90fc127b1
+--- /dev/null
++++ b/src/osd/windows/custom_video_adl.cpp
+@@ -0,0 +1,376 @@
++/**************************************************************
++
++ custom_video_adl.cpp - ATI/AMD ADL library
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++// Constants and structures ported from AMD ADL SDK files
++
++#include <windows.h>
++#include "emu.h"
++#include "custom_video_adl.h"
++
++bool enum_displays(HINSTANCE h_dll);
++
++typedef void* (__stdcall *ADL_MAIN_MALLOC_CALLBACK)(int);
++typedef int (*ADL_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int);
++typedef int (*ADL_MAIN_CONTROL_DESTROY)();
++typedef int (*ADL_ADAPTER_NUMBEROFADAPTERS_GET) (int*);
++typedef int (*ADL_ADAPTER_ADAPTERINFO_GET) (LPAdapterInfo, int);
++typedef int (*ADL_DISPLAY_DISPLAYINFO_GET) (int, int *, ADLDisplayInfo **, int);
++typedef int (*ADL_DISPLAY_MODETIMINGOVERRIDE_GET) (int iAdapterIndex, int iDisplayIndex, ADLDisplayMode *lpModeIn, ADLDisplayModeInfo *lpModeInfoOut);
++typedef int (*ADL_DISPLAY_MODETIMINGOVERRIDE_SET) (int iAdapterIndex, int iDisplayIndex, ADLDisplayModeInfo *lpMode, int iForceUpdate);
++typedef int (*ADL_DISPLAY_MODETIMINGOVERRIDELIST_GET) (int iAdapterIndex, int iDisplayIndex, int iMaxNumOfOverrides, ADLDisplayModeInfo *lpModeInfoList, int *lpNumOfOverrides);
++
++ADL_ADAPTER_NUMBEROFADAPTERS_GET ADL_Adapter_NumberOfAdapters_Get;
++ADL_ADAPTER_ADAPTERINFO_GET ADL_Adapter_AdapterInfo_Get;
++ADL_DISPLAY_DISPLAYINFO_GET ADL_Display_DisplayInfo_Get;
++ADL_DISPLAY_MODETIMINGOVERRIDE_GET ADL_Display_ModeTimingOverride_Get;
++ADL_DISPLAY_MODETIMINGOVERRIDE_SET ADL_Display_ModeTimingOverride_Set;
++ADL_DISPLAY_MODETIMINGOVERRIDELIST_GET ADL_Display_ModeTimingOverrideList_Get;
++
++HINSTANCE hDLL;
++LPAdapterInfo lpAdapterInfo = NULL;
++LPAdapterList lpAdapter;
++int iNumberAdapters;
++int cat_version, sub_version;
++
++int invert_pol(bool on_read) { return ((cat_version <= 12) || (cat_version >= 15 && on_read)); }
++int interlace_factor(bool interlace, bool on_read) { return interlace && ((cat_version <= 12) || (cat_version >= 15 && on_read))? 2 : 1; }
++
++
++//============================================================
++// memory allocation callbacks
++//============================================================
++
++void* __stdcall ADL_Main_Memory_Alloc(int iSize)
++{
++ void* lpBuffer = malloc(iSize);
++ return lpBuffer;
++}
++
++void __stdcall ADL_Main_Memory_Free(void** lpBuffer)
++{
++ if (NULL != *lpBuffer)
++ {
++ free(*lpBuffer);
++ *lpBuffer = NULL;
++ }
++}
++
++//============================================================
++// adl_get_driver_version
++//============================================================
++
++bool adl_get_driver_version(char *device_key)
++{
++ HKEY hkey;
++ bool found = false;
++
++ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, device_key, 0, KEY_READ , &hkey) == ERROR_SUCCESS)
++ {
++ BYTE cat_ver[32];
++ DWORD length = sizeof(cat_ver);
++ if ((RegQueryValueExA(hkey, "Catalyst_Version", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
++ (RegQueryValueExA(hkey, "RadeonSoftwareVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS))
++ {
++ found = true;
++ sscanf((char *)cat_ver, "%d.%d", &cat_version, &sub_version);
++ osd_printf_verbose("AMD driver version %d.%d\n", cat_version, sub_version);
++ }
++ RegCloseKey(hkey);
++ }
++ return found;
++}
++
++//============================================================
++// adl_open
++//============================================================
++
++int adl_open()
++{
++ ADL_MAIN_CONTROL_CREATE ADL_Main_Control_Create;
++ int ADL_Err = ADL_ERR;
++
++ hDLL = LoadLibraryA("atiadlxx.dll");
++ if (hDLL == NULL) hDLL = LoadLibraryA("atiadlxy.dll");
++
++ if (hDLL != NULL)
++ {
++ ADL_Main_Control_Create = (ADL_MAIN_CONTROL_CREATE)GetProcAddress(hDLL, "ADL_Main_Control_Create");
++ if (ADL_Main_Control_Create != NULL)
++ ADL_Err = ADL_Main_Control_Create(ADL_Main_Memory_Alloc, 1);
++ }
++ else
++ {
++ osd_printf_verbose("ADL Library not found!\n");
++ }
++
++ return ADL_Err;
++}
++
++//============================================================
++// adl_close
++//============================================================
++
++void adl_close()
++{
++ ADL_MAIN_CONTROL_DESTROY ADL_Main_Control_Destroy;
++
++ osd_printf_verbose("ATI/AMD ADL close\n");
++
++ for (int i = 0; i <= iNumberAdapters - 1; i++)
++ ADL_Main_Memory_Free((void **)&lpAdapter[i].m_display_list);
++
++ ADL_Main_Memory_Free((void **)&lpAdapterInfo);
++ ADL_Main_Memory_Free((void **)&lpAdapter);
++
++ ADL_Main_Control_Destroy = (ADL_MAIN_CONTROL_DESTROY)GetProcAddress(hDLL, "ADL_Main_Control_Destroy");
++ if (ADL_Main_Control_Destroy != NULL)
++ ADL_Main_Control_Destroy();
++
++ FreeLibrary(hDLL);
++}
++
++//============================================================
++// adl_init
++//============================================================
++
++bool adl_init(char *device_name, char *device_key, char *device_id)
++{
++ int ADL_Err = ADL_ERR;
++
++ osd_printf_verbose("ATI/AMD ADL init\n");
++
++ ADL_Err = adl_open();
++ if (ADL_Err != ADL_OK)
++ {
++ osd_printf_verbose("ERROR: ADL Initialization error!\n");
++ return false;
++ }
++
++ ADL_Adapter_NumberOfAdapters_Get = (ADL_ADAPTER_NUMBEROFADAPTERS_GET)GetProcAddress(hDLL,"ADL_Adapter_NumberOfAdapters_Get");
++ if (ADL_Adapter_NumberOfAdapters_Get == NULL)
++ {
++ osd_printf_verbose("ERROR: ADL_Adapter_NumberOfAdapters_Get not available!");
++ return false;
++ }
++ ADL_Adapter_AdapterInfo_Get = (ADL_ADAPTER_ADAPTERINFO_GET)GetProcAddress(hDLL,"ADL_Adapter_AdapterInfo_Get");
++ if (ADL_Adapter_AdapterInfo_Get == NULL)
++ {
++ osd_printf_verbose("ERROR: ADL_Adapter_AdapterInfo_Get not available!");
++ return false;
++ }
++ ADL_Display_DisplayInfo_Get = (ADL_DISPLAY_DISPLAYINFO_GET)GetProcAddress(hDLL,"ADL_Display_DisplayInfo_Get");
++ if (ADL_Display_DisplayInfo_Get == NULL)
++ {
++ osd_printf_verbose("ERROR: ADL_Display_DisplayInfo_Get not available!");
++ return false;
++ }
++ ADL_Display_ModeTimingOverride_Get = (ADL_DISPLAY_MODETIMINGOVERRIDE_GET)GetProcAddress(hDLL,"ADL_Display_ModeTimingOverride_Get");
++ if (ADL_Display_ModeTimingOverride_Get == NULL)
++ {
++ osd_printf_verbose("ERROR: ADL_Display_ModeTimingOverride_Get not available!");
++ return false;
++ }
++ ADL_Display_ModeTimingOverride_Set = (ADL_DISPLAY_MODETIMINGOVERRIDE_SET)GetProcAddress(hDLL,"ADL_Display_ModeTimingOverride_Set");
++ if (ADL_Display_ModeTimingOverride_Set == NULL)
++ {
++ osd_printf_verbose("ERROR: ADL_Display_ModeTimingOverride_Set not available!");
++ return false;
++ }
++ ADL_Display_ModeTimingOverrideList_Get = (ADL_DISPLAY_MODETIMINGOVERRIDELIST_GET)GetProcAddress(hDLL,"ADL_Display_ModeTimingOverrideList_Get");
++ if (ADL_Display_ModeTimingOverrideList_Get == NULL)
++ {
++ osd_printf_verbose("ERROR: ADL_Display_ModeTimingOverrideList_Get not available!");
++ return false;
++ }
++
++ if (!enum_displays(hDLL))
++ {
++ osd_printf_error("ADL error enumerating displays.\n");
++ return false;
++ }
++
++ adl_get_driver_version(device_key);
++
++ osd_printf_verbose("ADL functions retrieved successfully.\n");
++ return true;
++}
++
++//============================================================
++// enum_displays
++//============================================================
++
++bool enum_displays(HINSTANCE h_dll)
++{
++ ADL_Adapter_NumberOfAdapters_Get(&iNumberAdapters);
++
++ lpAdapterInfo = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * iNumberAdapters);
++ memset(lpAdapterInfo, '\0', sizeof(AdapterInfo) * iNumberAdapters);
++ ADL_Adapter_AdapterInfo_Get(lpAdapterInfo, sizeof(AdapterInfo) * iNumberAdapters);
++
++ lpAdapter = (LPAdapterList)malloc(sizeof(AdapterList) * iNumberAdapters);
++ for (int i = 0; i <= iNumberAdapters - 1; i++)
++ {
++ lpAdapter[i].m_index = lpAdapterInfo[i].iAdapterIndex;
++ lpAdapter[i].m_bus = lpAdapterInfo[i].iBusNumber;
++ memcpy(&lpAdapter[i].m_name, &lpAdapterInfo[i].strAdapterName, ADL_MAX_PATH);
++ memcpy(&lpAdapter[i].m_display_name, &lpAdapterInfo[i].strDisplayName, ADL_MAX_PATH);
++ lpAdapter[i].m_num_of_displays = 0;
++ lpAdapter[i].m_display_list = 0;
++ ADL_Display_DisplayInfo_Get(lpAdapter[i].m_index, &lpAdapter[i].m_num_of_displays, &lpAdapter[i].m_display_list, 1);
++ }
++ return true;
++}
++
++//============================================================
++// get_device_mapping_from_display_name
++//============================================================
++
++bool get_device_mapping_from_display_name(char *target_display, int *adapter_index, int *display_index)
++{
++ for (int i = 0; i <= iNumberAdapters -1; i++)
++ {
++ if (!strcmp(target_display, lpAdapter[i].m_display_name))
++ {
++ ADLDisplayInfo *display_list;
++ display_list = lpAdapter[i].m_display_list;
++
++ for (int j = 0; j <= lpAdapter[i].m_num_of_displays - 1; j++)
++ {
++ if (lpAdapter[i].m_index == display_list[j].displayID.iDisplayLogicalAdapterIndex)
++ {
++ *adapter_index = lpAdapter[i].m_index;
++ *display_index = display_list[j].displayID.iDisplayLogicalIndex;
++ return true;
++ }
++ }
++ }
++ }
++ return false;
++}
++
++//============================================================
++// ADL_display_mode_info_to_modeline
++//============================================================
++
++bool adl_display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m)
++{
++ if (dmi->sDetailedTiming.sHTotal == 0) return false;
++
++ ADLDetailedTiming dt;
++ memcpy(&dt, &dmi->sDetailedTiming, sizeof(ADLDetailedTiming));
++
++ if (dt.sHTotal == 0) return false;
++
++ m->htotal = dt.sHTotal;
++ m->hactive = dt.sHDisplay;
++ m->hbegin = dt.sHSyncStart;
++ m->hend = dt.sHSyncWidth + m->hbegin;
++ m->vtotal = dt.sVTotal;
++ m->vactive = dt.sVDisplay;
++ m->vbegin = dt.sVSyncStart;
++ m->vend = dt.sVSyncWidth + m->vbegin;
++ m->interlace = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED)? 1 : 0;
++ m->hsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_H_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1);
++ m->vsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_V_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1) ;
++ m->pclock = dt.sPixelClock * 10000;
++
++ m->height = m->height? m->height : dmi->iPelsHeight;
++ m->width = m->width? m->width : dmi->iPelsWidth;
++ m->refresh = m->refresh? m->refresh : dmi->iRefreshRate / interlace_factor(m->interlace, 1);;
++ m->hfreq = float(m->pclock / m->htotal);
++ m->vfreq = float(m->hfreq / m->vtotal) * (m->interlace? 2 : 1);
++
++ return true;
++}
++
++//============================================================
++// ADL_get_modeline
++//============================================================
++
++bool adl_get_modeline(char *target_display, modeline *m)
++{
++ int adapter_index = 0;
++ int display_index = 0;
++ ADLDisplayMode mode_in;
++ ADLDisplayModeInfo mode_info_out;
++ modeline m_temp = *m;
++
++ //modeline to ADLDisplayMode
++ mode_in.iPelsHeight = m->height;
++ mode_in.iPelsWidth = m->width;
++ mode_in.iBitsPerPel = 32;
++ mode_in.iDisplayFrequency = m->refresh * interlace_factor(m->interlace, 1);
++
++ if (!get_device_mapping_from_display_name(target_display, &adapter_index, &display_index)) return false;
++ if (ADL_Display_ModeTimingOverride_Get(adapter_index, display_index, &mode_in, &mode_info_out) != ADL_OK) return false;
++ if (adl_display_mode_info_to_modeline(&mode_info_out, &m_temp))
++ {
++ if (m_temp.interlace == m->interlace)
++ {
++ memcpy(m, &m_temp, sizeof(modeline));
++ return true;
++ }
++ }
++ return false;
++}
++
++//============================================================
++// ADL_set_modeline
++//============================================================
++
++bool adl_set_modeline(char *target_display, modeline *m, int update_mode)
++{
++ int adapter_index = 0;
++ int display_index = 0;
++ ADLDisplayModeInfo mode_info;
++ ADLDetailedTiming *dt;
++ modeline m_temp;
++
++ //modeline to ADLDisplayModeInfo
++ mode_info.iTimingStandard = (update_mode & MODELINE_DELETE)? ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT : ADL_DL_MODETIMING_STANDARD_CUSTOM;
++ mode_info.iPossibleStandard = 0;
++ mode_info.iRefreshRate = m->refresh * interlace_factor(m->interlace, 0);
++ mode_info.iPelsWidth = m->width;
++ mode_info.iPelsHeight = m->height;
++
++ //modeline to ADLDetailedTiming
++ dt = &mode_info.sDetailedTiming;
++ dt->sTimingFlags = (m->interlace? ADL_DL_TIMINGFLAG_INTERLACED : 0) |
++ (m->hsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_H_SYNC_POLARITY : 0) |
++ (m->vsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_V_SYNC_POLARITY : 0);
++ dt->sHTotal = m->htotal;
++ dt->sHDisplay = m->hactive;
++ dt->sHSyncStart = m->hbegin;
++ dt->sHSyncWidth = m->hend - m->hbegin;
++ dt->sVTotal = m->vtotal;
++ dt->sVDisplay = m->vactive;
++ dt->sVSyncStart = m->vbegin;
++ dt->sVSyncWidth = m->vend - m->vbegin;
++ dt->sPixelClock = m->pclock / 10000;
++ dt->sHOverscanRight = 0;
++ dt->sHOverscanLeft = 0;
++ dt->sVOverscanBottom = 0;
++ dt->sVOverscanTop = 0;
++
++ if (!get_device_mapping_from_display_name(target_display, &adapter_index, &display_index)) return false;
++ if (ADL_Display_ModeTimingOverride_Set(adapter_index, display_index, &mode_info, (update_mode & MODELINE_UPDATE_LIST)? 1 : 0) != ADL_OK) return false;
++
++ // read modeline to trigger timing refresh on modded drivers
++ memcpy(&m_temp, m, sizeof(modeline));
++ if (update_mode & MODELINE_UPDATE) adl_get_modeline(target_display, &m_temp);
++
++ return true;
++}
+diff --git a/src/osd/windows/custom_video_adl.h b/src/osd/windows/custom_video_adl.h
+new file mode 100644
+index 00000000000..390041aa8c6
+--- /dev/null
++++ b/src/osd/windows/custom_video_adl.h
+@@ -0,0 +1,130 @@
++/**************************************************************
++
++ custom_video_adl.h - ATI/AMD ADL library header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++// Constants and structures ported from AMD ADL SDK files
++
++#define ADL_MAX_PATH 256
++#define ADL_OK 0
++#define ADL_ERR -1
++
++//ADL_DETAILED_TIMING.sTimingFlags
++#define ADL_DL_TIMINGFLAG_DOUBLE_SCAN 0x0001
++#define ADL_DL_TIMINGFLAG_INTERLACED 0x0002
++#define ADL_DL_TIMINGFLAG_H_SYNC_POLARITY 0x0004
++#define ADL_DL_TIMINGFLAG_V_SYNC_POLARITY 0x0008
++
++//ADL_DISPLAY_MODE_INFO.iTimingStandard
++#define ADL_DL_MODETIMING_STANDARD_CVT 0x00000001 // CVT Standard
++#define ADL_DL_MODETIMING_STANDARD_GTF 0x00000002 // GFT Standard
++#define ADL_DL_MODETIMING_STANDARD_DMT 0x00000004 // DMT Standard
++#define ADL_DL_MODETIMING_STANDARD_CUSTOM 0x00000008 // User-defined standard
++#define ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT 0x00000010 // Remove Mode from overriden list
++#define ADL_DL_MODETIMING_STANDARD_CVT_RB 0x00000020 // CVT-RB Standard
++
++typedef struct AdapterInfo
++{
++ int iSize;
++ int iAdapterIndex;
++ char strUDID[ADL_MAX_PATH];
++ int iBusNumber;
++ int iDeviceNumber;
++ int iFunctionNumber;
++ int iVendorID;
++ char strAdapterName[ADL_MAX_PATH];
++ char strDisplayName[ADL_MAX_PATH];
++ int iPresent;
++ int iExist;
++ char strDriverPath[ADL_MAX_PATH];
++ char strDriverPathExt[ADL_MAX_PATH];
++ char strPNPString[ADL_MAX_PATH];
++ int iOSDisplayIndex;
++} AdapterInfo, *LPAdapterInfo;
++
++typedef struct ADLDisplayID
++{
++ int iDisplayLogicalIndex;
++ int iDisplayPhysicalIndex;
++ int iDisplayLogicalAdapterIndex;
++ int iDisplayPhysicalAdapterIndex;
++} ADLDisplayID, *LPADLDisplayID;
++
++
++typedef struct ADLDisplayInfo
++{
++ ADLDisplayID displayID;
++ int iDisplayControllerIndex;
++ char strDisplayName[ADL_MAX_PATH];
++ char strDisplayManufacturerName[ADL_MAX_PATH];
++ int iDisplayType;
++ int iDisplayOutputType;
++ int iDisplayConnector;
++ int iDisplayInfoMask;
++ int iDisplayInfoValue;
++} ADLDisplayInfo, *LPADLDisplayInfo;
++
++typedef struct ADLDisplayMode
++{
++ int iPelsHeight;
++ int iPelsWidth;
++ int iBitsPerPel;
++ int iDisplayFrequency;
++} ADLDisplayMode;
++
++typedef struct ADLDetailedTiming
++{
++ int iSize;
++ short sTimingFlags;
++ short sHTotal;
++ short sHDisplay;
++ short sHSyncStart;
++ short sHSyncWidth;
++ short sVTotal;
++ short sVDisplay;
++ short sVSyncStart;
++ short sVSyncWidth;
++ unsigned short sPixelClock;
++ short sHOverscanRight;
++ short sHOverscanLeft;
++ short sVOverscanBottom;
++ short sVOverscanTop;
++ short sOverscan8B;
++ short sOverscanGR;
++} ADLDetailedTiming;
++
++typedef struct ADLDisplayModeInfo
++{
++ int iTimingStandard;
++ int iPossibleStandard;
++ int iRefreshRate;
++ int iPelsWidth;
++ int iPelsHeight;
++ ADLDetailedTiming sDetailedTiming;
++} ADLDisplayModeInfo;
++
++typedef struct AdapterList
++{
++ int m_index;
++ int m_bus;
++ char m_name[ADL_MAX_PATH];
++ char m_display_name[ADL_MAX_PATH];
++ int m_num_of_displays;
++ ADLDisplayInfo *m_display_list;
++} AdapterList, *LPAdapterList;
++
++bool adl_init(char *device_name, char *device_key, char *device_id);
++void adl_close();
++bool adl_get_modeline(char *target_display, modeline *m);
++bool adl_set_modeline(char *target_display, modeline *m, int update_mode);
+diff --git a/src/osd/windows/custom_video_ati.cpp b/src/osd/windows/custom_video_ati.cpp
+new file mode 100644
+index 00000000000..3bf863afd0a
+--- /dev/null
++++ b/src/osd/windows/custom_video_ati.cpp
+@@ -0,0 +1,309 @@
++/**************************************************************
++
++ custom_video_ati.cpp - ATI legacy library
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++#include <windows.h>
++
++#include "emu.h"
++#include "custom_video_ati.h"
++
++#define CRTC_DOUBLE_SCAN 0x0001
++#define CRTC_INTERLACED 0x0002
++#define CRTC_H_SYNC_POLARITY 0x0004
++#define CRTC_V_SYNC_POLARITY 0x0008
++
++static int get_DWORD(int i, char *lp_data);
++static int get_DWORD_BCD(int i, char *lp_data);
++static void set_DWORD(char *data_string, UINT32 data_word, int offset);
++static void set_DWORD_BCD(char *data_string, UINT32 data_word, int offset);
++static int os_version(void);
++static bool is_elevated();
++static int win_interlace_factor(modeline *mode);
++
++char m_device_name[32];
++char m_device_key[256];
++int win_version;
++
++//============================================================
++// ati_custom_video_init
++//============================================================
++
++bool ati_init(char *device_name, char *device_key, char *device_id)
++{
++ osd_printf_verbose("ATI legacy init\n");
++
++ // Get Windows version
++ win_version = os_version();
++
++ if (win_version > 5 && !is_elevated())
++ {
++ osd_printf_error("ATI legacy error: the program needs administrator rights.\n");
++ return false;
++ }
++
++ memcpy(m_device_name, device_name, sizeof(m_device_name));
++ memcpy(m_device_key, device_key, sizeof(m_device_key));
++
++ return true;
++}
++
++//============================================================
++// ati_get_modeline
++//============================================================
++
++bool ati_get_modeline(modeline *mode)
++{
++ HKEY hKey;
++ char lp_name[1024];
++ char lp_data[68];
++ DWORD length;
++ bool found = false;
++ int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
++ int vfreq_incr = 0;
++
++ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
++ {
++ sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
++ length = sizeof(lp_data);
++
++ if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
++ found = true;
++ else if (win_version > 5 && mode->interlace)
++ {
++ vfreq_incr = 1;
++ sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
++ if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
++ found = true;
++ }
++ if (found)
++ {
++ mode->pclock = get_DWORD_BCD(36, lp_data) * 10000;
++ mode->hactive = get_DWORD_BCD(8, lp_data);
++ mode->hbegin = get_DWORD_BCD(12, lp_data);
++ mode->hend = get_DWORD_BCD(16, lp_data) + mode->hbegin;
++ mode->htotal = get_DWORD_BCD(4, lp_data);
++ mode->vactive = get_DWORD_BCD(24, lp_data);
++ mode->vbegin = get_DWORD_BCD(28, lp_data);
++ mode->vend = get_DWORD_BCD(32, lp_data) + mode->vbegin;
++ mode->vtotal = get_DWORD_BCD(20, lp_data);
++ mode->interlace = (get_DWORD(0, lp_data) & CRTC_INTERLACED)?1:0;
++ mode->hsync = (get_DWORD(0, lp_data) & CRTC_H_SYNC_POLARITY)?0:1;
++ mode->vsync = (get_DWORD(0, lp_data) & CRTC_V_SYNC_POLARITY)?0:1;
++ mode->hfreq = mode->pclock / mode->htotal;
++ mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
++ mode->refresh_label = refresh_label;
++
++ int checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
++ - mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
++ if (checksum != get_DWORD(64, lp_data))
++ osd_printf_verbose("bad checksum! ");
++ }
++ RegCloseKey(hKey);
++ return (found);
++ }
++ osd_printf_info("Failed opening registry entry for mode.\n");
++ return false;
++}
++
++//============================================================
++// ati_set_modeline
++//============================================================
++
++bool ati_set_modeline(modeline *mode)
++{
++ HKEY hKey;
++ char lp_name[1024];
++ char lp_data[68];
++ long checksum;
++ bool found = false;
++ int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
++ int vfreq_incr = 0;
++
++ memset(lp_data, 0, sizeof(lp_data));
++ set_DWORD_BCD(lp_data, (int)mode->pclock/10000, 36);
++ set_DWORD_BCD(lp_data, mode->hactive, 8);
++ set_DWORD_BCD(lp_data, mode->hbegin, 12);
++ set_DWORD_BCD(lp_data, mode->hend - mode->hbegin, 16);
++ set_DWORD_BCD(lp_data, mode->htotal, 4);
++ set_DWORD_BCD(lp_data, mode->vactive, 24);
++ set_DWORD_BCD(lp_data, mode->vbegin, 28);
++ set_DWORD_BCD(lp_data, mode->vend - mode->vbegin, 32);
++ set_DWORD_BCD(lp_data, mode->vtotal, 20);
++ set_DWORD(lp_data, (mode->interlace?CRTC_INTERLACED:0) | (mode->hsync?0:CRTC_H_SYNC_POLARITY) | (mode->vsync?0:CRTC_V_SYNC_POLARITY), 0);
++
++ checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
++ - mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
++ set_DWORD(lp_data, checksum, 64);
++
++ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
++ {
++ sprintf (lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
++
++ if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
++ found = true;
++ else if (win_version > 5 && mode->interlace)
++ {
++ vfreq_incr = 1;
++ sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
++ if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
++ found = true;
++ }
++
++ if (!(found && RegSetValueExA(hKey, lp_name, 0, REG_BINARY, (LPBYTE)lp_data, 68) == ERROR_SUCCESS))
++ osd_printf_info("Failed saving registry entry %s\n", lp_name);
++
++ RegCloseKey(hKey);
++ return (found);
++ }
++
++ osd_printf_info("Failed updating registry entry for mode.\n");
++ return 0;
++}
++
++//============================================================
++// ati_refresh_timings
++//============================================================
++
++void ati_refresh_timings(void)
++{
++ int iModeNum = 0;
++ DEVMODEA lpDevMode;
++
++ memset(&lpDevMode, 0, sizeof(DEVMODEA));
++ lpDevMode.dmSize = sizeof(DEVMODEA);
++
++ while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, 0) != 0)
++ iModeNum++;
++}
++
++//============================================================
++// get_DWORD
++//============================================================
++
++static int get_DWORD(int i, char *lp_data)
++{
++ char out[32] = "";
++ UINT32 x;
++
++ sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
++ sscanf(out, "%08X", &x);
++ return x;
++}
++
++//============================================================
++// get_DWORD_BCD
++//============================================================
++
++static int get_DWORD_BCD(int i, char *lp_data)
++{
++ char out[32] = "";
++ UINT32 x;
++
++ sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
++ sscanf(out, "%d", &x);
++ return x;
++}
++
++//============================================================
++// set_DWORD
++//============================================================
++
++static void set_DWORD(char *data_string, UINT32 data_dword, int offset)
++{
++ char *p_dword = (char*)&data_dword;
++
++ data_string[offset] = p_dword[3]&0xFF;
++ data_string[offset+1] = p_dword[2]&0xFF;
++ data_string[offset+2] = p_dword[1]&0xFF;
++ data_string[offset+3] = p_dword[0]&0xFF;
++}
++
++//============================================================
++// set_DWORD_BCD
++//============================================================
++
++static void set_DWORD_BCD(char *data_string, UINT32 data_dword, int offset)
++{
++ if (data_dword < 100000000)
++ {
++ int low_word, high_word;
++ int a, b, c, d;
++ char out[32] = "";
++
++ low_word = data_dword % 10000;
++ high_word = data_dword / 10000;
++
++ sprintf(out, "%d %d %d %d", high_word / 100, high_word % 100 , low_word / 100, low_word % 100);
++ sscanf(out, "%02X %02X %02X %02X", &a, &b, &c, &d);
++
++ data_string[offset] = a;
++ data_string[offset+1] = b;
++ data_string[offset+2] = c;
++ data_string[offset+3] = d;
++ }
++}
++
++//============================================================
++// os_version
++//============================================================
++
++static int os_version(void)
++{
++ OSVERSIONINFOA lpVersionInfo;
++
++ memset(&lpVersionInfo, 0, sizeof(OSVERSIONINFOA));
++ lpVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
++ GetVersionExA (&lpVersionInfo);
++
++ return lpVersionInfo.dwMajorVersion;
++}
++
++//============================================================
++// is_elevated
++//============================================================
++
++static bool is_elevated()
++{
++ HANDLE htoken;
++ bool result = false;
++
++ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken))
++ return false;
++
++ TOKEN_ELEVATION te = {0};
++ DWORD dw_return_length;
++
++ if (GetTokenInformation(htoken, TokenElevation, &te, sizeof(te), &dw_return_length))
++ {
++ if (te.TokenIsElevated)
++ {
++ result = true;
++ }
++ }
++
++ CloseHandle(htoken);
++ return (result);
++}
++
++//============================================================
++// win_interlace_factor
++//============================================================
++
++static int win_interlace_factor(modeline *mode)
++{
++ if (win_version > 5 && mode->interlace)
++ return 2;
++
++ return 1;
++}
+diff --git a/src/osd/windows/custom_video_ati.h b/src/osd/windows/custom_video_ati.h
+new file mode 100644
+index 00000000000..25061d53340
+--- /dev/null
++++ b/src/osd/windows/custom_video_ati.h
+@@ -0,0 +1,20 @@
++/**************************************************************
++
++ custom_video_ati.h - ATI legacy library header
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++bool ati_init(char *device_name, char *device_key, char *device_id);
++bool ati_get_modeline(modeline *mode);
++bool ati_set_modeline(modeline *mode);
++void ati_refresh_timings(void);
+diff --git a/src/osd/windows/custom_video_ati_family.cpp b/src/osd/windows/custom_video_ati_family.cpp
+new file mode 100644
+index 00000000000..0e0ac8c706a
+--- /dev/null
++++ b/src/osd/windows/custom_video_ati_family.cpp
+@@ -0,0 +1,850 @@
++/**************************************************************
++
++ custom_video_ati_family.cpp - ATI/AMD Radeon family
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++/* Constants and structures ported from Linux open source drivers:
++ drivers\gpu\drm\radeon\radeon.h
++ drivers\gpu\drm\radeon\radeon_family.h
++ include\drm\drm_pciids.h
++*/
++
++#ifndef RADEON_FAMILY_H
++#define RADEON_FAMILY_H
++
++struct pci_device_id
++{
++ int vendor, device;
++ int subvendor, subdevice;
++ int _class, _class_mask;
++ int driver_data;
++};
++
++enum radeon_family
++{
++ CHIP_R100 = 0,
++ CHIP_RV100,
++ CHIP_RS100,
++ CHIP_RV200,
++ CHIP_RS200,
++ CHIP_R200,
++ CHIP_RV250,
++ CHIP_RS300,
++ CHIP_RV280,
++ CHIP_R300,
++ CHIP_R350,
++ CHIP_RV350,
++ CHIP_RV380,
++ CHIP_R420,
++ CHIP_R423,
++ CHIP_RV410,
++ CHIP_RS400,
++ CHIP_RS480,
++ CHIP_RS600,
++ CHIP_RS690,
++ CHIP_RS740,
++ CHIP_RV515,
++ CHIP_R520,
++ CHIP_RV530,
++ CHIP_RV560,
++ CHIP_RV570,
++ CHIP_R580,
++ CHIP_R600,
++ CHIP_RV610,
++ CHIP_RV630,
++ CHIP_RV670,
++ CHIP_RV620,
++ CHIP_RV635,
++ CHIP_RS780,
++ CHIP_RS880,
++ CHIP_RV770,
++ CHIP_RV730,
++ CHIP_RV710,
++ CHIP_RV740,
++ CHIP_CEDAR,
++ CHIP_REDWOOD,
++ CHIP_JUNIPER,
++ CHIP_CYPRESS,
++ CHIP_HEMLOCK,
++ CHIP_PALM,
++ CHIP_SUMO,
++ CHIP_SUMO2,
++ CHIP_BARTS,
++ CHIP_TURKS,
++ CHIP_CAICOS,
++ CHIP_CAYMAN,
++ CHIP_ARUBA,
++ CHIP_TAHITI,
++ CHIP_PITCAIRN,
++ CHIP_VERDE,
++ CHIP_OLAND,
++ CHIP_HAINAN,
++ CHIP_BONAIRE,
++ CHIP_KAVERI,
++ CHIP_KABINI,
++ CHIP_HAWAII,
++ CHIP_MULLINS,
++ CHIP_LAST,
++};
++
++enum radeon_chip_flags
++{
++ RADEON_FAMILY_MASK = 0x0000ffffUL,
++ RADEON_FLAGS_MASK = 0xffff0000UL,
++ RADEON_IS_MOBILITY = 0x00010000UL,
++ RADEON_IS_IGP = 0x00020000UL,
++ RADEON_SINGLE_CRTC = 0x00040000UL,
++ RADEON_IS_AGP = 0x00080000UL,
++ RADEON_HAS_HIERZ = 0x00100000UL,
++ RADEON_IS_PCIE = 0x00200000UL,
++ RADEON_NEW_MEMMAP = 0x00400000UL,
++ RADEON_IS_PCI = 0x00800000UL,
++ RADEON_IS_IGPGART = 0x01000000UL,
++ RADEON_IS_PX = 0x02000000UL,
++};
++
++#define PCI_ANY_ID (~0)
++
++#define radeon_PCI_IDS \
++ {0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x3151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x3155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \
++ {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
++ {0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x4149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x414A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x414B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x4150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
++ {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
++ {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
++ {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
++ {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
++ {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4B48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4C6E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4E47, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
++ {0x1002, 0x4E48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
++ {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
++ {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
++ {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
++ {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
++ {0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
++ {0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
++ {0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
++ {0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
++ {0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
++ {0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
++ {0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
++ {0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
++ {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5657, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \
++ {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5954, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5955, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5974, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
++ {0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
++ {0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
++ {0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
++ {0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
++ {0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
++ {0x1002, 0x5a41, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5a42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5a61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5a62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
++ {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
++ {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6665, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6667, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x666F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6703, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6704, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6707, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6718, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6719, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x671c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x671d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x671f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6721, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6722, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6725, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6726, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6728, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6743, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6745, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6746, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6747, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6748, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x674A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6758, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6759, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x675B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x675D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x675F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6761, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6763, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6764, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6765, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6766, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6767, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6768, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6770, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6771, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6778, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6779, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x677B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6784, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x678A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6790, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6791, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6792, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6798, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6799, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x679A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6823, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6826, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6827, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x683B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x683D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x683F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6842, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6843, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6849, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x684C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6888, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6889, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x688A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x688C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68a0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68e1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68e5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68e8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68e9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68f1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68f8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68f9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68fa, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x68fe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x710A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x710B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x710C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x710E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x710F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7140, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7141, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7142, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7143, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x714A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x714B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x714C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x714D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x714E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x714F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x715E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x715F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7183, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7187, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7188, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x718A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x718B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x718C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x718D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x718F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7193, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x719B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x719F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71D2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71D5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71D6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x71DE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7244, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7245, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7247, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7248, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x724A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x724B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x724C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x724D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x724E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x724F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7280, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7283, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7284, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x728B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x728C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7290, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7291, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7293, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7297, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x791e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x791f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x793f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7941, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x7942, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x796c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x796d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x796e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x796f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
++ {0x1002, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9402, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9403, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x940A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x940B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x940F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94B3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94B5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9442, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9444, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x944A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x944B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x944C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x944E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9452, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9456, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x945A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x945B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x945E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x946A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x946B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x947A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x947B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9487, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9488, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9489, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x948A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x948F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9490, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9491, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9495, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9498, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x949C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x949E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x949F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94CB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x94CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9504, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9505, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9506, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9507, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9508, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9509, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x950F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9517, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9540, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9542, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x954E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x954F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9553, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9555, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x955f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9580, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9581, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9583, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9586, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9587, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9588, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9589, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x958A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x958B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x958C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x958D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x958E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x958F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9590, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9593, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9595, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9596, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9597, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9599, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x959B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x95CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
++ {0x1002, 0x9610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9616, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9642, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
++ {0x1002, 0x9648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
++ {0x1002, 0x9649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
++ {0x1002, 0x964a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x964b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x964c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x964e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
++ {0x1002, 0x964f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
++ {0x1002, 0x9710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9714, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9715, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9804, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9807, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x980A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9904, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9905, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9906, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9907, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9908, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9909, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x990A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x990B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x990C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x990D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x990E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x990F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9910, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9913, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9917, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9918, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9990, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9991, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9993, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9994, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9996, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9998, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x9999, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x999A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x999B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x999C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x999D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x99A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x99A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0x1002, 0x99A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
++ {0, 0, 0}
++
++static struct pci_device_id pciidlist[] = {radeon_PCI_IDS};
++
++//============================================================
++// ati_family
++//============================================================
++
++int ati_family(int vendor, int device)
++{
++ int i = 0;
++ while (pciidlist[i].vendor)
++ {
++ if (pciidlist[i].vendor == vendor && pciidlist[i].device == device)
++ return (pciidlist[i].driver_data & RADEON_FAMILY_MASK);
++ i++;
++ }
++ // Not found, must be newer
++ if (vendor == 0x1002)
++ return CHIP_LAST;
++
++ return 0;
++}
++
++//============================================================
++// ati_is_legacy
++//============================================================
++
++bool ati_is_legacy(int vendor, int device)
++{
++ return (ati_family(vendor, device) < CHIP_CEDAR);
++}
++
++#endif
+\ No newline at end of file
+diff --git a/src/osd/windows/custom_video_pstrip.cpp b/src/osd/windows/custom_video_pstrip.cpp
+new file mode 100644
+index 00000000000..151b6daf63a
+--- /dev/null
++++ b/src/osd/windows/custom_video_pstrip.cpp
+@@ -0,0 +1,529 @@
++/**************************************************************
++
++ custom_video_pstrip.cpp - PowerStrip interface routines
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++/* http://forums.entechtaiwan.com/index.php?topic=5534.msg20902;topicseen#msg20902
++
++ UM_SETCUSTOMTIMING = WM_USER+200;
++ wparam = monitor number, zero-based
++ lparam = atom for string pointer
++ lresult = -1 for failure else current pixel clock (integer in Hz)
++ Note: pass full PowerStrip timing string*
++
++ UM_SETREFRESHRATE = WM_USER+201;
++ wparam = monitor number, zero-based
++ lparam = refresh rate (integer in Hz), or 0 for read-only
++ lresult = -1 for failure else current refresh rate (integer in Hz)
++
++ UM_SETPOLARITY = WM_USER+202;
++ wparam = monitor number, zero-based
++ lparam = polarity bits
++ lresult = -1 for failure else current polarity bits+1
++
++ UM_REMOTECONTROL = WM_USER+210;
++ wparam = 99
++ lparam =
++ 0 to hide tray icon
++ 1 to show tray icon,
++ 2 to get build number
++ 10 to show Performance profiles
++ 11 to show Color profiles
++ 12 to show Display profiles
++ 13 to show Application profiles
++ 14 to show Adapter information
++ 15 to show Monitor information
++ 16 to show Hotkey manager
++ 17 to show Resource manager
++ 18 to show Preferences
++ 19 to show Online services
++ 20 to show About screen
++ 21 to show Tip-of-the-day
++ 22 to show Setup wizard
++ 23 to show Screen fonts
++ 24 to show Advanced timing options
++ 25 to show Custom resolutions
++ 99 to close PS
++ lresult = -1 for failure else lparam+1 for success or build number (e.g., 335)
++ if lparam was 2
++
++ UM_SETGAMMARAMP = WM_USER+203;
++ wparam = monitor number, zero-based
++ lparam = atom for string pointer
++ lresult = -1 for failure, 1 for success
++
++ UM_CREATERESOLUTION = WM_USER+204;
++ wparam = monitor number, zero-based
++ lparam = atom for string pointer
++ lresult = -1 for failure, 1 for success
++ Note: pass full PowerStrip timing string*; reboot is usually necessary to see if
++ the resolution is accepted by the display driver
++
++ UM_GETTIMING = WM_USER+205;
++ wparam = monitor number, zero-based
++ lresult = -1 for failure else GlobalAtom number identifiying the timing string*
++ Note: be sure to call GlobalDeleteAtom after reading the string associated with
++ the atom
++
++ UM_GETSETCLOCKS = WM_USER+206;
++ wparam = monitor number, zero-based
++ lparam = atom for string pointer
++ lresult = -1 for failure else GlobalAtom number identifiying the performance
++ string**
++ Note: pass full PowerStrip performance string** to set the clocks, and ull to
++ get clocks; be sure to call GlobalDeleteAtom after reading the string associated
++ with the atom
++
++ NegativeHorizontalPolarity = 0x02;
++ NegativeVerticalPolarity = 0x04;
++
++ *Timing string parameter definition:
++ 1 = horizontal active pixels
++ 2 = horizontal front porch
++ 3 = horizontal sync width
++ 4 = horizontal back porch
++ 5 = vertical active pixels
++ 6 = vertical front porch
++ 7 = vertical sync width
++ 8 = vertical back porch
++ 9 = pixel clock in hertz
++ 10 = timing flags, where bit:
++ 1 = negative horizontal porlarity
++ 2 = negative vertical polarity
++ 3 = interlaced
++ 5 = composite sync
++ 7 = sync-on-green
++ all other bits reserved
++
++ **Performance string parameter definition:
++ 1 = memory clock in hertz
++ 2 = engine clock in hertz
++ 3 = reserved
++ 4 = reserved
++ 5 = reserved
++ 6 = reserved
++ 7 = reserved
++ 8 = reserved
++ 9 = 2D memory clock in hertz (if different from 3D)
++ 10 = 2D engine clock in hertz (if different from 3D) */
++
++// standard windows headers
++#include <windows.h>
++#include <stdio.h>
++
++// MAME headers
++#include "emu.h"
++
++// PowerStrip header
++#include "custom_video_pstrip.h"
++
++//============================================================
++// GLOBALS
++//============================================================
++
++static HWND hPSWnd;
++static MonitorTiming timing_backup;
++
++//============================================================
++// CONSTANTS
++//============================================================
++
++#define UM_SETCUSTOMTIMING (WM_USER+200)
++#define UM_SETREFRESHRATE (WM_USER+201)
++#define UM_SETPOLARITY (WM_USER+202)
++#define UM_REMOTECONTROL (WM_USER+210)
++#define UM_SETGAMMARAMP (WM_USER+203)
++#define UM_CREATERESOLUTION (WM_USER+204)
++#define UM_GETTIMING (WM_USER+205)
++#define UM_GETSETCLOCKS (WM_USER+206)
++#define UM_SETCUSTOMTIMINGFAST (WM_USER+211) // glitches vertical sync with PS 3.65 build 568
++
++#define NegativeHorizontalPolarity 0x02
++#define NegativeVerticalPolarity 0x04
++#define Interlace 0x08
++
++#define HideTrayIcon 0x00
++#define ShowTrayIcon 0x01
++#define ClosePowerStrip 0x63
++
++//============================================================
++// ps_init
++//============================================================
++
++int ps_init(int monitor_index, modeline *modeline)
++{
++ hPSWnd = FindWindowA("TPShidden", NULL);
++
++ if (hPSWnd)
++ {
++ osd_printf_verbose("PStrip: PowerStrip found!\n");
++ if (ps_get_monitor_timing(monitor_index, &timing_backup) && modeline)
++ {
++ ps_pstiming_to_modeline(&timing_backup, modeline);
++ return 1;
++ }
++ }
++ else
++ osd_printf_verbose("PStrip: Could not get PowerStrip API interface\n");
++
++ return 0;
++}
++
++//============================================================
++// ps_reset
++//============================================================
++
++int ps_reset(int monitor_index)
++{
++ return ps_set_monitor_timing(monitor_index, &timing_backup);
++}
++
++//============================================================
++// ps_get_modeline
++//============================================================
++
++int ps_get_modeline(int monitor_index, modeline *modeline)
++{
++ MonitorTiming timing = {0};
++
++ if (ps_get_monitor_timing(monitor_index, &timing))
++ {
++ ps_pstiming_to_modeline(&timing, modeline);
++ return 1;
++ }
++ else return 0;
++}
++
++//============================================================
++// ps_set_modeline
++//============================================================
++
++int ps_set_modeline(int monitor_index, modeline *modeline)
++{
++ MonitorTiming timing = {0};
++
++ ps_modeline_to_pstiming(modeline, &timing);
++
++ timing.PixelClockInKiloHertz = ps_best_pclock(monitor_index, &timing, timing.PixelClockInKiloHertz);
++
++ if (ps_set_monitor_timing(monitor_index, &timing))
++ return 1;
++ else
++ return 0;
++}
++
++//============================================================
++// ps_get_monitor_timing
++//============================================================
++
++int ps_get_monitor_timing(int monitor_index, MonitorTiming *timing)
++{
++ LRESULT lresult;
++ char in[256];
++
++ if (!hPSWnd) return 0;
++
++ lresult = SendMessage(hPSWnd, UM_GETTIMING, monitor_index, 0);
++
++ if (lresult == -1)
++ {
++ osd_printf_verbose("PStrip: Could not get PowerStrip timing string\n");
++ return 0;
++ }
++
++ if (!GlobalGetAtomNameA(lresult, in, sizeof(in)))
++ {
++ osd_printf_verbose("PStrip: GlobalGetAtomName failed\n");
++ return 0;
++ }
++
++ osd_printf_verbose("PStrip: ps_get_monitor_timing(%d): %s\n", monitor_index, in);
++
++ ps_read_timing_string(in, timing);
++
++ GlobalDeleteAtom(lresult); // delete atom created by PowerStrip
++
++ return 1;
++}
++
++//============================================================
++// ps_set_monitor_timing
++//============================================================
++
++int ps_set_monitor_timing(int monitor_index, MonitorTiming *timing)
++{
++ LRESULT lresult;
++ ATOM atom;
++ char out[256];
++
++ if (!hPSWnd) return 0;
++
++ ps_fill_timing_string(out, timing);
++ atom = GlobalAddAtomA(out);
++
++ if (atom)
++ {
++ lresult = SendMessage(hPSWnd, UM_SETCUSTOMTIMING, monitor_index, atom);
++
++ if (lresult < 0)
++ {
++ osd_printf_verbose("PStrip: SendMessage failed\n");
++ GlobalDeleteAtom(atom);
++ }
++ else
++ {
++ osd_printf_verbose("PStrip: ps_set_monitor_timing(%d): %s\n", monitor_index, out);
++ return 1;
++ }
++ }
++ else osd_printf_verbose("PStrip: ps_set_monitor_timing atom creation failed\n");
++
++ return 0;
++}
++
++//============================================================
++// ps_set_monitor_timing_string
++//============================================================
++
++int ps_set_monitor_timing_string(int monitor_index, char *in)
++{
++ MonitorTiming timing;
++
++ ps_read_timing_string(in, &timing);
++ return ps_set_monitor_timing(monitor_index, &timing);
++}
++
++//============================================================
++// ps_set_refresh
++//============================================================
++
++int ps_set_refresh(int monitor_index, double vfreq)
++{
++ MonitorTiming timing = {0};
++ int hht, vvt, new_vvt;
++ int desired_pClock;
++ int best_pClock;
++
++ memcpy(&timing, &timing_backup, sizeof(MonitorTiming));
++
++ hht = timing.HorizontalActivePixels
++ + timing.HorizontalFrontPorch
++ + timing.HorizontalSyncWidth
++ + timing.HorizontalBackPorch;
++
++ vvt = timing.VerticalActivePixels
++ + timing.VerticalFrontPorch
++ + timing.VerticalSyncWidth
++ + timing.VerticalBackPorch;
++
++ desired_pClock = hht * vvt * vfreq / 1000;
++ best_pClock = ps_best_pclock(monitor_index, &timing, desired_pClock);
++
++ new_vvt = best_pClock * 1000 / (vfreq * hht);
++
++ timing.VerticalBackPorch += (new_vvt - vvt);
++ timing.PixelClockInKiloHertz = best_pClock;
++
++ ps_set_monitor_timing(monitor_index, &timing);
++ ps_get_monitor_timing(monitor_index, &timing);
++
++ return 1;
++}
++
++//============================================================
++// ps_best_pclock
++//============================================================
++
++int ps_best_pclock(int monitor_index, MonitorTiming *timing, int desired_pclock)
++{
++ MonitorTiming timing_read;
++ int best_pclock = 0;
++
++ osd_printf_verbose("PStrip: ps_best_pclock(%d), getting stable dotclocks for %d...\n", monitor_index, desired_pclock);
++
++ for (int i = -50; i <= 50; i += 25)
++ {
++ timing->PixelClockInKiloHertz = desired_pclock + i;
++
++ ps_set_monitor_timing(monitor_index, timing);
++ ps_get_monitor_timing(monitor_index, &timing_read);
++
++ if (abs(timing_read.PixelClockInKiloHertz - desired_pclock) < abs(desired_pclock - best_pclock))
++ best_pclock = timing_read.PixelClockInKiloHertz;
++ }
++
++ osd_printf_verbose("PStrip: ps_best_pclock(%d), new dotclock: %d\n", monitor_index, best_pclock);
++
++ return best_pclock;
++}
++
++//============================================================
++// ps_create_resolution
++//============================================================
++
++int ps_create_resolution(int monitor_index, modeline *modeline)
++{
++ LRESULT lresult;
++ ATOM atom;
++ char out[256];
++ MonitorTiming timing = {0};
++
++ if (!hPSWnd) return 0;
++
++ ps_modeline_to_pstiming(modeline, &timing);
++
++ ps_fill_timing_string(out, &timing);
++ atom = GlobalAddAtomA(out);
++
++ if (atom)
++ {
++ lresult = SendMessage(hPSWnd, UM_CREATERESOLUTION, monitor_index, atom);
++
++ if (lresult < 0)
++ {
++ osd_printf_verbose("PStrip: SendMessage failed\n");
++ GlobalDeleteAtom(atom);
++ }
++ else
++ {
++ osd_printf_verbose("PStrip: ps_create_resolution(%d): %dx%d succeded \n",
++ modeline->width, modeline->height, monitor_index);
++ return 1;
++ }
++ }
++ else osd_printf_verbose("PStrip: ps_create_resolution atom creation failed\n");
++
++ return 0;
++}
++
++//============================================================
++// ps_read_timing_string
++//============================================================
++
++bool ps_read_timing_string(char *in, MonitorTiming *timing)
++{
++ if (sscanf(in,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
++ &timing->HorizontalActivePixels,
++ &timing->HorizontalFrontPorch,
++ &timing->HorizontalSyncWidth,
++ &timing->HorizontalBackPorch,
++ &timing->VerticalActivePixels,
++ &timing->VerticalFrontPorch,
++ &timing->VerticalSyncWidth,
++ &timing->VerticalBackPorch,
++ &timing->PixelClockInKiloHertz,
++ &timing->TimingFlags.w) == 10) return true;
++
++ return false;
++}
++
++//============================================================
++// ps_fill_timing_string
++//============================================================
++
++void ps_fill_timing_string(char *out, MonitorTiming *timing)
++{
++ sprintf(out, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
++ timing->HorizontalActivePixels,
++ timing->HorizontalFrontPorch,
++ timing->HorizontalSyncWidth,
++ timing->HorizontalBackPorch,
++ timing->VerticalActivePixels,
++ timing->VerticalFrontPorch,
++ timing->VerticalSyncWidth,
++ timing->VerticalBackPorch,
++ timing->PixelClockInKiloHertz,
++ timing->TimingFlags.w);
++}
++
++//============================================================
++// ps_modeline_to_pstiming
++//============================================================
++
++int ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing)
++{
++ timing->HorizontalActivePixels = modeline->hactive;
++ timing->HorizontalFrontPorch = modeline->hbegin - modeline->hactive;
++ timing->HorizontalSyncWidth = modeline->hend - modeline->hbegin;
++ timing->HorizontalBackPorch = modeline->htotal - modeline->hend;
++
++ timing->VerticalActivePixels = modeline->vactive;
++ timing->VerticalFrontPorch = modeline->vbegin - modeline->vactive;
++ timing->VerticalSyncWidth = modeline->vend - modeline->vbegin;
++ timing->VerticalBackPorch = modeline->vtotal - modeline->vend;
++
++ timing->PixelClockInKiloHertz = modeline->pclock / 1000;
++
++ if (modeline->hsync == 0)
++ timing->TimingFlags.w |= NegativeHorizontalPolarity;
++ if (modeline->vsync == 0)
++ timing->TimingFlags.w |= NegativeVerticalPolarity;
++ if (modeline->interlace)
++ timing->TimingFlags.w |= Interlace;
++
++ return 0;
++}
++
++//============================================================
++// ps_pstiming_to_modeline
++//============================================================
++
++int ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline)
++{
++ modeline->hactive = timing->HorizontalActivePixels;
++ modeline->hbegin = modeline->hactive + timing->HorizontalFrontPorch;
++ modeline->hend = modeline->hbegin + timing->HorizontalSyncWidth;
++ modeline->htotal = modeline->hend + timing->HorizontalBackPorch;
++
++ modeline->vactive = timing->VerticalActivePixels;
++ modeline->vbegin = modeline->vactive + timing->VerticalFrontPorch;
++ modeline->vend = modeline->vbegin + timing->VerticalSyncWidth;
++ modeline->vtotal = modeline->vend + timing->VerticalBackPorch;
++
++ modeline->width = modeline->hactive;
++ modeline->height = modeline->vactive;
++
++ modeline->pclock = timing->PixelClockInKiloHertz * 1000;
++
++ if (!(timing->TimingFlags.w & NegativeHorizontalPolarity))
++ modeline->hsync = 1;
++
++ if (!(timing->TimingFlags.w & NegativeVerticalPolarity))
++ modeline->vsync = 1;
++
++ if ((timing->TimingFlags.w & Interlace))
++ modeline->interlace = 1;
++
++ modeline->hfreq = modeline->pclock / modeline->htotal;
++ modeline->vfreq = modeline->hfreq / modeline->vtotal * (modeline->interlace?2:1);
++
++ return 0;
++}
++
++//============================================================
++// ps_monitor_index
++//============================================================
++
++int ps_monitor_index (const char *display_name)
++{
++ int monitor_index = 0;
++ char sub_index[2];
++
++ sub_index[0] = display_name[strlen(display_name)-1];
++ sub_index[1] = 0;
++ if (sscanf(sub_index,"%d", &monitor_index) == 1)
++ monitor_index --;
++
++ return monitor_index;
++}
+diff --git a/src/osd/windows/custom_video_pstrip.h b/src/osd/windows/custom_video_pstrip.h
+new file mode 100644
+index 00000000000..b73deb98eca
+--- /dev/null
++++ b/src/osd/windows/custom_video_pstrip.h
+@@ -0,0 +1,63 @@
++/**************************************************************
++
++ custom_video_powerstrip.h - PowerStrip interface routines
++
++ ---------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++//============================================================
++// TYPE DEFINITIONS
++//============================================================
++
++typedef struct
++{
++ int HorizontalActivePixels;
++ int HorizontalFrontPorch;
++ int HorizontalSyncWidth;
++ int HorizontalBackPorch;
++ int VerticalActivePixels;
++ int VerticalFrontPorch;
++ int VerticalSyncWidth;
++ int VerticalBackPorch;
++ int PixelClockInKiloHertz;
++ union
++ {
++ int w;
++ struct
++ {
++ unsigned :1;
++ unsigned HorizontalPolarityNegative:1;
++ unsigned VerticalPolarityNegative:1;
++ unsigned :29;
++ } b;
++ } TimingFlags;
++} MonitorTiming;
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++int ps_init(int monitor_index, modeline *modeline);
++int ps_reset(int monitor_index);
++int ps_get_modeline(int monitor_index, modeline *modeline);
++int ps_set_modeline(int monitor_index, modeline *modeline);
++int ps_get_monitor_timing(int monitor_index, MonitorTiming *timing);
++int ps_set_monitor_timing(int monitor_index, MonitorTiming *timing);
++int ps_set_monitor_timing_string(int monitor_index, char *in);
++int ps_set_refresh(int monitor_index, double vfreq);
++int ps_best_pclock(int monitor_index, MonitorTiming *timing, int desired_pclock);
++int ps_create_resolution(int monitor_index, modeline *modeline);
++bool ps_read_timing_string(char *in, MonitorTiming *timing);
++void ps_fill_timing_string(char *out, MonitorTiming *timing);
++int ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing);
++int ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline);
++int ps_monitor_index (const char *display_name);
+diff --git a/src/osd/windows/switchres_windows.cpp b/src/osd/windows/switchres_windows.cpp
+new file mode 100644
+index 00000000000..5a0c052f49a
+--- /dev/null
++++ b/src/osd/windows/switchres_windows.cpp
+@@ -0,0 +1,516 @@
++/**************************************************************
++
++ switchres_windows.cpp - Windows OSD SwitchRes core routines
++
++ -----------------------------------------------------------
++
++ SwitchRes Modeline generation engine for emulation
++
++ GroovyMAME Integration of SwitchRes into the MAME project
++ Some reworked patches from SailorSat's CabMAME
++
++ License GPL-2.0+
++ Copyright 2010-2016 - Chris Kennedy, Antonio Giner
++
++ **************************************************************/
++
++// standard windows headers
++#include <windows.h>
++
++// MAME headers
++#include "emu.h"
++#include "emuopts.h"
++#include "../../frontend/mame/mameopts.h"
++
++// MAMEOS headers
++#include "winmain.h"
++#include "window.h"
++
++// Custom video headers
++#include "custom_video.h"
++
++#ifdef _MSC_VER
++#define min(a, b) ((a) < (b) ? (a) : (b))
++#else
++#define min(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a < _b ? _a : _b; })
++#endif
++
++//============================================================
++// PROTOTYPES
++//============================================================
++
++int display_get_device_info(const char *screen_option, char *device_name, char *device_id, char *device_key);
++int display_get_available_video_modes(const char *device_name, modeline *mode, modeline *current, config_settings *cs);
++int display_get_desktop_mode(const char *device_name, modeline *current);
++int display_set_desktop_mode(modeline *mode, int flags);
++int display_restore_desktop_mode(int flags);
++
++static void set_option_osd(running_machine &machine, const char *option_ID, bool state);
++static int copy_to_clipboard(char *txt);
++
++//============================================================
++// PARAMETERS
++//============================================================
++
++// display modes
++#define DM_INTERLACED 0x00000002
++#define DISPLAY_MAX 16
++
++//============================================================
++// LOCAL VARIABLES
++//============================================================
++
++static char m_device_name[32];
++static char m_device_id[128];
++static char m_device_key[128];
++static DEVMODEA desktop_devmode;
++
++//============================================================
++// switchres_init_osd
++//============================================================
++
++bool switchres_init_osd(running_machine &machine)
++{
++ config_settings *cs = &machine.switchres.cs;
++ game_info *game = &machine.switchres.game;
++ modeline *mode_table = machine.switchres.video_modes;
++ modeline *user_mode = &machine.switchres.user_mode;
++ monitor_range *range = machine.switchres.range;
++ const char * screen, * aspect;
++ char resolution[32]={'\x00'};
++ modeline desktop_mode;
++
++ windows_options &options = downcast<windows_options &>(machine.options());
++
++ // Initialize structures and config settings
++ memset(&desktop_mode, 0, sizeof(struct modeline));
++ memset(cs, 0, sizeof(struct config_settings));
++ memset(game, 0, sizeof(struct game_info));
++
++ // Init Switchres common info
++ switchres_init(machine);
++
++ // Complete config settings
++ cs->monitor_count = options.numscreens();
++ cs->doublescan = 0;
++
++ // Get device info
++ screen = strcmp(options.screen(0), "auto")? options.screen(0) : options.screen();
++ display_get_device_info(screen, m_device_name, m_device_id, m_device_key);
++
++ // Get current desktop resolution
++ display_get_desktop_mode(m_device_name, &desktop_mode);
++
++ // Initialize custom video
++ custom_video_init(m_device_name, m_device_id, &desktop_mode, user_mode, mode_table,
++ options.powerstrip()? CUSTOM_VIDEO_TIMING_POWERSTRIP : 0,
++ options.powerstrip()? (char *)options.ps_timing() : m_device_key);
++
++ // Get per window resolution
++ strcpy(resolution, strcmp(options.resolution(0), "auto")? options.resolution(0) : options.resolution());
++
++ // Get list of available video modes
++ if (!display_get_available_video_modes(m_device_name, mode_table, &desktop_mode, cs))
++ {
++ set_option_osd(machine, OSDOPTION_SWITCHRES, false);
++ return false;
++ }
++
++ // Get per window aspect
++ aspect = strcmp(options.aspect(0), "auto")? options.aspect(0) : options.aspect();
++ if (strcmp(aspect, "auto"))
++ {
++ float num, den;
++ sscanf(aspect, "%f:%f", &num, &den);
++ cs->monitor_aspect = cs->desktop_rotated? den/num : num/den;
++ }
++ else
++ cs->monitor_aspect = STANDARD_CRT_ASPECT;
++
++ // If monitor is LCD, create automatic specs and force resolution
++ if (!strcmp(cs->monitor, "lcd"))
++ {
++ osd_printf_verbose("SwitchRes: Creating automatic specs for LCD based on %s\n", desktop_mode.hactive? "current timings" : "VESA GTF");
++ if (!desktop_mode.hactive) modeline_vesa_gtf(&desktop_mode);
++ modeline_to_monitor_range(range, &desktop_mode);
++ monitor_show_range(range);
++ sprintf(resolution, "%dx%d@%d", desktop_mode.width, desktop_mode.height, desktop_mode.refresh);
++ }
++ // Otherwise (non-LCD), convert the user defined modeline into a -resolution option
++ else if (user_mode->hactive)
++ sprintf(resolution, "%dx%d", user_mode->hactive, user_mode->vactive);
++
++ // Filter the mode table according the -resolution option
++ if (strcmp(resolution, "auto"))
++ {
++ int i = 1;
++ bool found = false;
++ osd_printf_verbose("SwitchRes: -resolution was forced as %s\n", resolution);
++
++ if ((sscanf(resolution, "%dx%d@%d", &cs->width, &cs->height, &cs->refresh) < 3) &&
++ ((!strstr(resolution, "x") || (sscanf(resolution, "%dx%d", &cs->width, &cs->height) != 2))))
++ osd_printf_info("SwitchRes: illegal -resolution value: %s\n", resolution);
++
++ else while (mode_table[i].width && i < MAX_MODELINES)
++ {
++ // Lock all modes that don't match the user's -resolution rules
++ if (!( (mode_table[i].width == cs->width || (mode_table[i].type & X_RES_EDITABLE && cs->width <= DUMMY_WIDTH) || cs->width == 0)
++ && (mode_table[i].height == cs->height || cs->height == 0)
++ && (mode_table[i].refresh == cs->refresh || cs->refresh == 0) ))
++ mode_table[i].type |= MODE_DISABLED;
++
++ else
++ {
++ // If we have an user defined modeline, link its label to current item in mode table
++ if (user_mode->hactive && !found)
++ {
++ user_mode->width = mode_table[i].width;
++ user_mode->height = mode_table[i].height;
++ user_mode->refresh = mode_table[i].refresh;
++ user_mode->type = mode_table[i].type & ~V_FREQ_EDITABLE & ~X_RES_EDITABLE;
++ }
++ mode_table[i].type &= ~MODE_DISABLED;
++ mode_table[i].type |= MODE_USER_DEF;
++ found = true;
++ }
++ i++;
++ }
++ if (!found)
++ osd_printf_info("SwitchRes: -resolution value not available: %s\n", resolution);
++ }
++
++ // Get game info
++ switchres_get_game_info(machine);
++
++ return true;
++}
++
++//============================================================
++// switchres_modeline_setup
++//============================================================
++
++bool switchres_modeline_setup(running_machine &machine)
++{
++ modeline *best_mode = &machine.switchres.best_mode;
++ windows_options &options = downcast<windows_options &>(machine.options());
++ windows_osd_interface &osd = downcast<windows_osd_interface &>(machine.osd());
++ char modeline_txt[256]={'\x00'};
++
++ osd_printf_verbose("\nSwitchRes: Entering switchres_modeline_setup\n");
++
++ // Find most suitable video mode and generate a modeline for it if we're allowed
++ if (!switchres_get_video_mode(machine))
++ {
++ set_option_osd(machine, OSDOPTION_SWITCHRES, false);
++ return false;
++ }
++
++ // Make the new video timings available to the system
++ if (options.modeline_generation())
++ {
++ if(!custom_video_update_timing(best_mode))
++ {
++ set_option_osd(machine, OSDOPTION_SWITCHRES, false);
++ return false;
++ }
++
++ if (options.verbose())
++ {
++ modeline_print(best_mode, modeline_txt, MS_FULL);
++ copy_to_clipboard(modeline_txt);
++ }
++ }
++
++ // Set MAME common options
++ switchres_set_options(machine);
++
++ // Set MAME OSD specific options
++
++ // Set fullscreen resolution for the OpenGL case
++ if (options.switch_res() && (!strcmp(options.video(), "opengl"))) display_set_desktop_mode(best_mode, CDS_FULLSCREEN);
++
++ // Black frame insertion / multithreading
++ bool black_frame_insertion = options.black_frame_insertion() && best_mode->result.v_scale > 1 && best_mode->vfreq > 100;
++ set_option_osd(machine, OPTION_BLACK_FRAME_INSERTION, black_frame_insertion);
++
++ // Vertical synchronization management (autosync)
++ // Disable -syncrefresh if our vfreq is scaled or out of syncrefresh_tolerance (-triplebuffer will be used instead)
++ // Forcing -syncrefresh will override the -triplebuffer setting
++ bool sync_refresh_effective = black_frame_insertion || !((best_mode->result.weight & R_V_FREQ_OFF) || best_mode->result.v_scale > 1);
++ set_option_osd(machine, OPTION_SYNCREFRESH, options.autosync()? sync_refresh_effective : options.sync_refresh());
++ set_option_osd(machine, WINOPTION_TRIPLEBUFFER, options.autosync()? !sync_refresh_effective : (options.triple_buffer() && !options.sync_refresh()));
++ set_option_osd(machine, OSDOPTION_WAITVSYNC, options.sync_refresh() || options.triple_buffer());
++
++ // Set filter options
++ set_option_osd(machine, OSDOPTION_FILTER, ((best_mode->result.weight & R_RES_STRETCH || best_mode->interlace) && (!strcmp(options.video(), "auto") || !strcmp(options.video(), "d3d"))));
++
++ // Refresh video options
++ osd.extract_video_config();
++
++ return true;
++}
++
++//============================================================
++// switchres_modeline_remove
++//============================================================
++
++bool switchres_modeline_remove(running_machine &machine)
++{
++ windows_options &options = downcast<windows_options &>(machine.options());
++
++ // Restore original video timings
++ if (options.modeline_generation()) custom_video_restore_timing();
++
++ // Set destop resolution for the OpenGL case
++ if (options.switch_res() && !strcmp(options.video(), "opengl")) display_restore_desktop_mode(0);
++
++ // Free custom video api
++ custom_video_close();
++
++ return true;
++}
++
++//============================================================
++// switchres_resolution_change
++//============================================================
++
++bool switchres_resolution_change(win_window_info *window)
++{
++ running_machine &machine = window->machine();
++ modeline *best_mode = &machine.switchres.best_mode;
++ modeline previous_mode;
++
++ // If there's no pending change, just exit
++ if (!switchres_check_resolution_change(machine))
++ return false;
++
++ // Get the new resolution
++ previous_mode = *best_mode;
++ switchres_modeline_setup(machine);
++
++ // Only change resolution if the new one is actually different
++ if (memcmp(&previous_mode, best_mode, offsetof(modeline, result)))
++ {
++ window->m_win_config.width = best_mode->width;
++ window->m_win_config.height = best_mode->height;
++ return true;
++ }
++
++ return false;
++}
++
++//============================================================
++// display_get_desktop_mode
++//============================================================
++
++int display_get_desktop_mode(const char *device_name, modeline *current)
++{
++ memset(&desktop_devmode, 0, sizeof(DEVMODEA));
++ desktop_devmode.dmSize = sizeof(DEVMODEA);
++
++ if (EnumDisplaySettingsExA(!strcmp(device_name, "auto")?NULL:device_name, ENUM_CURRENT_SETTINGS, &desktop_devmode, 0))
++ {
++ if (current)
++ {
++ current->width = desktop_devmode.dmDisplayOrientation == DMDO_DEFAULT || desktop_devmode.dmDisplayOrientation == DMDO_180? desktop_devmode.dmPelsWidth:desktop_devmode.dmPelsHeight;
++ current->height = desktop_devmode.dmDisplayOrientation == DMDO_DEFAULT || desktop_devmode.dmDisplayOrientation == DMDO_180? desktop_devmode.dmPelsHeight:desktop_devmode.dmPelsWidth;
++ current->refresh = desktop_devmode.dmDisplayFrequency;
++ current->interlace = (desktop_devmode.dmDisplayFlags & DM_INTERLACED)?1:0;
++ }
++ return true;
++ }
++ return false;
++}
++
++//============================================================
++// display_restore_desktop_mode
++//============================================================
++
++int display_restore_desktop_mode(int flags)
++{
++ if (ChangeDisplaySettingsExA(m_device_name, &desktop_devmode, NULL, 0, 0) == DISP_CHANGE_SUCCESSFUL)
++ return true;
++
++ return false;
++}
++
++//============================================================
++// display_set_desktop_mode
++//============================================================
++
++int display_set_desktop_mode(modeline *mode, int flags)
++{
++ modeline *backup_mode = custom_video_get_backup_mode();
++ modeline *mode_to_check_interlace = backup_mode->hactive? backup_mode : mode;
++ DEVMODEA lpDevMode;
++
++ display_get_desktop_mode(m_device_name, NULL);
++
++ if (mode)
++ {
++ memset(&lpDevMode, 0, sizeof(DEVMODEA));
++ lpDevMode.dmSize = sizeof(DEVMODEA);
++ lpDevMode.dmPelsWidth = mode->type & MODE_ROTATED? mode->height : mode->width;
++ lpDevMode.dmPelsHeight = mode->type & MODE_ROTATED? mode->width : mode->height;
++ lpDevMode.dmDisplayFrequency = (int)mode->refresh;
++ lpDevMode.dmDisplayFlags = mode_to_check_interlace->interlace?DM_INTERLACED:0;
++ lpDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
++
++ if (ChangeDisplaySettingsExA(m_device_name, &lpDevMode, NULL, flags, 0) == DISP_CHANGE_SUCCESSFUL)
++ return true;
++ }
++
++ return false;
++}
++
++//============================================================
++// display_get_available_video_modes
++//============================================================
++
++int display_get_available_video_modes(const char *device_name, modeline *mode, modeline *current, config_settings *cs)
++{
++ int iModeNum = 0, i = 0, j = 0, k = 1;
++ DEVMODEA lpDevMode;
++
++ if (!strcmp(device_name, "auto"))
++ device_name = NULL;
++
++
++ memset(&lpDevMode, 0, sizeof(DEVMODEA));
++ lpDevMode.dmSize = sizeof(DEVMODEA);
++
++ osd_printf_verbose("Switchres: Searching for custom video modes...\n");
++
++ while (EnumDisplaySettingsExA(device_name, iModeNum, &lpDevMode, cs->lock_unsupported_modes?0:EDS_RAWMODE) != 0)
++ {
++ if (k == MAX_MODELINES)
++ {
++ osd_printf_verbose("SwitchRes: Warning, too many active modelines for storage %d\n", k);
++ break;
++ }
++ else if (lpDevMode.dmBitsPerPel == 32 && lpDevMode.dmDisplayFixedOutput == DMDFO_DEFAULT)
++ {
++ modeline *m = &mode[k];
++ memset(m, 0, sizeof(struct modeline));
++ m->interlace = (lpDevMode.dmDisplayFlags & DM_INTERLACED)?1:0;
++ m->width = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsWidth:lpDevMode.dmPelsHeight;
++ m->height = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsHeight:lpDevMode.dmPelsWidth;
++ m->refresh = lpDevMode.dmDisplayFrequency;
++ m->hactive = m->width;
++ m->vactive = m->height;
++ m->vfreq = m->refresh;
++ m->type |= lpDevMode.dmDisplayOrientation == DMDO_90 || lpDevMode.dmDisplayOrientation == DMDO_270? MODE_ROTATED : MODE_OK;
++
++ for (i = 0; i < k; i++) if (mode[i].width == m->width && mode[i].height == m->height && mode[i].refresh == m->refresh) goto found;
++
++ if (current && m->width == current->width && m->height == current->height && m->refresh == current->refresh)
++ {
++ m->type |= MODE_DESKTOP;
++ if (m->type & MODE_ROTATED) cs->desktop_rotated = true;
++ }
++
++ osd_printf_verbose("Switchres: [%3d] %4dx%4d @%3d%s %s: ", k, m->width, m->height, m->refresh, m->type & MODE_DESKTOP?"*":"", m->type & MODE_ROTATED?"rot":"");
++
++ if (custom_video_get_timing(m))
++ {
++ j++;
++ if (m->type & MODE_DESKTOP) memcpy(current, m, sizeof(modeline));
++ }
++ k++;
++ }
++ found:
++ iModeNum++;
++ }
++ k--;
++ osd_printf_verbose("SwitchRes: Found %d custom of %d active video modes\n", j, k);
++ return k;
++}
++
++//============================================================
++// display_get_device_info
++//============================================================
++
++int display_get_device_info(const char *screen_option, char *device_name, char *device_id, char *device_key)
++{
++ DISPLAY_DEVICEA lpDisplayDevice[DISPLAY_MAX];
++ int idev = 0;
++ int found = -1;
++
++ while (idev < DISPLAY_MAX)
++ {
++ memset(&lpDisplayDevice[idev], 0, sizeof(DISPLAY_DEVICEA));
++ lpDisplayDevice[idev].cb = sizeof(DISPLAY_DEVICEA);
++
++ if (EnumDisplayDevicesA(NULL, idev, &lpDisplayDevice[idev], 0) == FALSE)
++ break;
++
++ if ((!strcmp(screen_option, "auto") && (lpDisplayDevice[idev].StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
++ || !strcmp(screen_option, lpDisplayDevice[idev].DeviceName))
++ found = idev;
++
++ idev++;
++ }
++ if (found != -1)
++ {
++ strncpy(device_name, lpDisplayDevice[found].DeviceName, sizeof(m_device_name));
++ strncpy(device_id, lpDisplayDevice[found].DeviceID, sizeof(m_device_id));
++ osd_printf_verbose("SwitchRes: %s: %s (%s)\n", device_name, lpDisplayDevice[found].DeviceString, device_id);
++
++ char *pch;
++ int i;
++ for (i = 0; i < idev; i++)
++ {
++ pch = strstr(lpDisplayDevice[i].DeviceString, lpDisplayDevice[found].DeviceString);
++ if (pch)
++ {
++ found = i;
++ break;
++ }
++ }
++
++ char *chsrc, *chdst;
++ chdst = device_key;
++
++ for (chsrc = lpDisplayDevice[i].DeviceKey + 18; *chsrc != 0; chsrc++)
++ *chdst++ = *chsrc;
++
++ *chdst = 0;
++ }
++ else
++ {
++ osd_printf_verbose("SwitchRes: Failed obtaining default video registry key\n");
++ return -1;
++ }
++
++ osd_printf_verbose("SwitchRes: Device key: %s\n", device_key);
++ return 0;
++}
++
++//============================================================
++// set_option_osd - option setting wrapper
++//============================================================
++
++static void set_option_osd(running_machine &machine, const char *option_ID, bool state)
++{
++ windows_options &options = downcast<windows_options &>(machine.options());
++
++ options.set_value(option_ID, state, OPTION_PRIORITY_SWITCHRES);
++ osd_printf_verbose("SwitchRes: Setting option -%s%s\n", machine.options().bool_value(option_ID)?"":"no", option_ID);
++}
++
++//============================================================
++// copy_to_clipboard
++//============================================================
++
++static int copy_to_clipboard(char *txt)
++{
++ HGLOBAL hglb;
++ hglb = GlobalAlloc(GMEM_MOVEABLE, 256);
++ memcpy(GlobalLock(hglb), txt, strlen(txt) + 1);
++ GlobalUnlock(hglb);
++ OpenClipboard(NULL);
++ EmptyClipboard();
++ SetClipboardData(CF_TEXT, hglb);
++ CloseClipboard();
++ return 1;
++}
+diff --git a/src/osd/windows/video.cpp b/src/osd/windows/video.cpp
+index c6f4d3a0a89..5f5dbd64971 100644
+--- a/src/osd/windows/video.cpp
++++ b/src/osd/windows/video.cpp
+@@ -190,9 +190,10 @@ void windows_osd_interface::extract_video_config()
+ video_config.mode = VIDEO_MODE_GDI;
+ }
+ video_config.waitvsync = options().wait_vsync();
+- video_config.syncrefresh = options().sync_refresh();
++ video_config.syncrefresh = machine().options().sync_refresh();
+ video_config.triplebuf = options().triple_buffer();
+ video_config.switchres = options().switch_res();
++ video_config.framedelay = options().frame_delay();
+
+ if (video_config.prescale < 1 || video_config.prescale > 8)
+ {
+diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp
+index 8e723ea6246..16f0ba16a23 100644
+--- a/src/osd/windows/window.cpp
++++ b/src/osd/windows/window.cpp
+@@ -55,6 +55,8 @@ using namespace Windows::UI::Core;
+ #define MAKE_DI_SCAN(scan, isextended) (scan & 0x7f) | (isextended ? 0x80 : 0x00)
+ #define WINOSD(machine) downcast<windows_osd_interface*>(&machine.osd())
+
++extern bool switchres_resolution_change(win_window_info *window);
++
+ //============================================================
+ // PARAMETERS
+ //============================================================
+@@ -88,7 +90,7 @@ using namespace Windows::UI::Core;
+
+ static DWORD main_threadid;
+
+-
++static BOOL multithreading_enabled = FALSE;
+
+ //============================================================
+ // LOCAL VARIABLES
+@@ -804,6 +806,11 @@ void win_window_info::create(running_machine &machine, int index, std::shared_pt
+ // handle error conditions
+ if (window->m_init_state == -1)
+ fatalerror("Unable to complete window creation\n");
++
++ // create blitting thread
++ multithreading_enabled = options.triple_buffer();
++ if (multithreading_enabled)
++ window->blit_loop_create();
+ }
+
+ std::shared_ptr<osd_monitor_info> win_window_info::monitor_from_rect(const osd_rect* proposed) const
+@@ -838,6 +845,10 @@ void win_window_info::destroy()
+ {
+ assert(GetCurrentThreadId() == main_threadid);
+
++ // destroy blitting thread
++ if (multithreading_enabled)
++ blit_loop_destroy();
++
+ // remove us from the list
+ osd_common_t::s_window_list.remove(shared_from_this());
+
+@@ -851,6 +862,97 @@ void win_window_info::destroy()
+
+
+
++//============================================================
++// blit_loop_create
++// (blitting thread)
++//============================================================
++
++void win_window_info::blit_loop_create()
++{
++ // set loop as active and lock it until ready
++ m_blitting_active = TRUE;
++ m_blit_lock = TRUE;
++
++ // create blitting events
++ m_blit_pending = CreateEvent(NULL, FALSE, FALSE, NULL);
++ m_blit_done = CreateEvent(NULL, FALSE, FALSE, NULL);
++
++ // create blitting thread
++ CreateThread (NULL, 0, blit_loop, (LPVOID)this, 0, &m_blit_threadid);
++ osd_printf_verbose("Blitting thread created\n");
++}
++
++//============================================================
++// blit_loop_destroy
++// (blitting thread)
++//============================================================
++
++void win_window_info::blit_loop_destroy()
++{
++ // turn off blitting flag and wait until thread ends
++ m_blitting_active = FALSE;
++ WaitForSingleObject(m_blit_done, 100);
++
++ // close blitting events
++ if (m_blit_done) CloseHandle(m_blit_done);
++ if (m_blit_pending) CloseHandle(m_blit_pending);
++
++ osd_printf_verbose("Blitting thread destroyed\n");
++}
++
++//============================================================
++// blit_loop
++// (blitting thread)
++//============================================================
++
++DWORD WINAPI win_window_info::blit_loop(LPVOID lpParameter)
++{
++ return ((win_window_info *)lpParameter)->blit_loop_wt();
++}
++
++DWORD win_window_info::blit_loop_wt()
++{
++ bool m_throttled;
++ osd_printf_verbose("Blitting thread started\n");
++
++ do {
++ WaitForSingleObject(m_blit_pending, INFINITE);
++ m_throttled = machine().video().throttled();
++ if (!m_blit_lock && has_renderer()) draw_video_contents(NULL, FALSE);
++ if (m_throttled) SetEvent(m_blit_done);
++ } while (m_blitting_active);
++
++ osd_printf_verbose("Blitting thread ended\n");
++
++ return -1;
++}
++
++//============================================================
++// blit_lock_set
++// (window thread)
++//============================================================
++
++void win_window_info::blit_lock_set()
++{
++ m_blit_lock = TRUE;
++ osd_printf_verbose("blit_lock = TRUE\n");
++ Sleep(20);
++}
++
++//============================================================
++// blit_lock_release
++// (window thread)
++//============================================================
++
++void win_window_info::blit_lock_release()
++{
++ Sleep(20);
++ m_blit_lock = FALSE;
++ osd_printf_verbose("blit_lock = FALSE\n");
++}
++
++
++
+ //============================================================
+ // winwindow_video_window_update
+ // (main thread)
+@@ -860,6 +962,7 @@ void win_window_info::update()
+ {
+ int targetview, targetorient;
+ render_layer_config targetlayerconfig;
++ static int frame_count = 0;
+
+ assert(GetCurrentThreadId() == main_threadid);
+
+@@ -888,11 +991,7 @@ void win_window_info::update()
+ {
+ bool got_lock = true;
+
+- // only block if we're throttled
+- if (machine().video().throttled() || timeGetTime() - last_update_time > 250)
+- m_render_lock.lock();
+- else
+- got_lock = m_render_lock.try_lock();
++ got_lock = m_render_lock.try_lock();
+
+ // only render if we were able to get the lock
+ if (got_lock)
+@@ -902,6 +1001,33 @@ void win_window_info::update()
+ // don't hold the lock; we just used it to see if rendering was still happening
+ m_render_lock.unlock();
+
++ // check if frame delay has changed
++ static int frame_delay = 0;
++ int new_frame_delay = machine().video().framedelay();
++ if (new_frame_delay != frame_delay)
++ {
++ video_config.framedelay = new_frame_delay;
++
++ bool reset_required = ((bool)frame_delay != (bool)new_frame_delay);
++ frame_delay = new_frame_delay;
++
++ if (reset_required)
++ {
++ reset_fullscreen_renderer();
++ return;
++ }
++ }
++
++ // check resolution change
++ if (renderer().m_switchres_mode != nullptr && video_config.switchres && machine().options().changeres())
++ {
++ if (switchres_resolution_change(this))
++ {
++ reset_fullscreen_renderer();
++ return;
++ }
++ }
++
+ // ensure the target bounds are up-to-date, and then get the primitives
+ primlist = renderer().get_primitives();
+
+@@ -919,7 +1045,20 @@ void win_window_info::update()
+ }
+ else
+ {
+- SendMessage(platform_window(), WM_USER_REDRAW, 0, (LPARAM)primlist);
++ if (multithreading_enabled && !m_blit_lock && video_config.mode != VIDEO_MODE_GDI)
++ {
++ m_primlist = primlist;
++ SetEvent(m_blit_pending);
++ if ((video_config.syncrefresh && machine().video().throttled()))
++ WaitForSingleObject(m_blit_done, 1000);
++ frame_count = 0;
++ }
++ else
++ {
++ m_primlist = primlist;
++ draw_video_contents(NULL, FALSE);
++ if (multithreading_enabled && frame_count++ > 5) blit_lock_release();
++ }
+ }
+ }
+ }
+@@ -1196,8 +1335,7 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
+ case WM_PAINT:
+ {
+ PAINTSTRUCT pstruct;
+- HDC hdc = BeginPaint(wnd, &pstruct);
+- window->draw_video_contents(hdc, true);
++ BeginPaint(wnd, &pstruct);
+ if (window->win_has_menu())
+ DrawMenuBar(window->platform_window());
+ EndPaint(wnd, &pstruct);
+@@ -1340,6 +1478,11 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
+ return DefWindowProc(wnd, message, wparam, lparam);
+ }
+
++ case WM_ACTIVATE:
++ if (window->has_renderer() && window->fullscreen() && (LOWORD(wparam) == WA_INACTIVE) && !is_mame_window(HWND(lparam)))
++ if (osd_common_t::s_window_list.size()) winwindow_toggle_full_screen();
++ break;
++
+ // close: cause MAME to exit
+ case WM_CLOSE:
+ window->machine().schedule_exit();
+@@ -1797,6 +1940,7 @@ void win_window_info::adjust_window_position_after_major_change()
+ }
+
+
++
+ //============================================================
+ // set_fullscreen
+ // (window thread)
+@@ -1815,6 +1959,10 @@ void win_window_info::set_fullscreen(int fullscreen)
+ // FIXME: this cause crash if called when running_machine.m_ui not yet initialised. e.g. when trying to show error/warning messagebox at startup (during auto-switch from full screen to windowed mode).
+ machine().ui().menu_reset();
+
++ // pause blitting thread;
++ if (multithreading_enabled)
++ blit_lock_set();
++
+ // kill off the drawers
+ renderer_reset();
+
+@@ -1883,6 +2031,49 @@ void win_window_info::set_fullscreen(int fullscreen)
+ }
+
+
++//============================================================
++// reset_fullscreen_renderer
++//============================================================
++
++void win_window_info::reset_fullscreen_renderer()
++{
++ // if we're in the right state, punt
++ if (!m_fullscreen)
++ return;
++
++ // pause blitting thread;
++ if (multithreading_enabled)
++ blit_lock_set();
++
++ if (video_config.mode == VIDEO_MODE_D3D)
++ {
++ renderer().restart();
++ return;
++ }
++
++ // kill off the drawers
++ renderer_reset();
++
++ // hide ourself
++ ShowWindow(platform_window(), SW_HIDE);
++
++ // adjust the style
++ SetWindowLong(platform_window(), GWL_STYLE, WINDOW_STYLE);
++ SetWindowLong(platform_window(), GWL_EXSTYLE, WINDOW_STYLE_EX);
++ SetWindowPos(platform_window(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
++
++ SetWindowLong(platform_window(), GWL_STYLE, FULLSCREEN_STYLE);
++ SetWindowLong(platform_window(), GWL_EXSTYLE, FULLSCREEN_STYLE_EX);
++ SetWindowPos(platform_window(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
++
++ ShowWindow(platform_window(), SW_SHOW);
++
++ set_renderer(osd_renderer::make_for_type(video_config.mode, shared_from_this()));
++ if (renderer().create())
++ exit(1);
++}
++
++
+ //============================================================
+ // focus
+ // (main or window thread)
+diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h
+index b04e35aa110..455522781a0 100644
+--- a/src/osd/windows/window.h
++++ b/src/osd/windows/window.h
+@@ -96,6 +96,7 @@ public:
+ // static
+
+ static void create(running_machine &machine, int index, std::shared_ptr<osd_monitor_info> monitor, const osd_window_config *config);
++ static DWORD WINAPI blit_loop(LPVOID lpParameter);
+
+ // static callbacks
+
+@@ -131,6 +132,13 @@ public:
+ int m_lastclickx;
+ int m_lastclicky;
+
++ // blitting thread
++ HANDLE m_blit_pending;
++ HANDLE m_blit_done;
++ DWORD m_blit_threadid;
++ BOOL m_blitting_active;
++ BOOL m_blit_lock;
++
+ private:
+ void draw_video_contents(HDC dc, bool update);
+ int complete_create();
+@@ -146,6 +154,12 @@ private:
+ void adjust_window_position_after_major_change();
+ void set_fullscreen(int fullscreen);
+ std::shared_ptr<osd_monitor_info> monitor_from_rect(const osd_rect* proposed) const;
++ void reset_fullscreen_renderer();
++ void blit_loop_create();
++ void blit_loop_destroy();
++ DWORD blit_loop_wt();
++ void blit_lock_set();
++ void blit_lock_release();
+
+ static POINT s_saved_cursor_pos;
+
+diff --git a/src/osd/windows/winmain.cpp b/src/osd/windows/winmain.cpp
+index 05888769c0d..3eab0ca4867 100644
+--- a/src/osd/windows/winmain.cpp
++++ b/src/osd/windows/winmain.cpp
+@@ -43,6 +43,9 @@ using namespace Windows::UI::Popups;
+
+ #define DEBUG_SLOW_LOCKS 0
+
++extern bool switchres_modeline_setup(running_machine &machine);
++extern bool switchres_modeline_remove(running_machine &machine);
++
+ //**************************************************************************
+ // MACROS
+ //**************************************************************************
+@@ -525,6 +528,10 @@ void windows_osd_interface::init(running_machine &machine)
+ const char *stemp;
+ auto &options = downcast<windows_options &>(machine.options());
+
++ // Switchres
++ switchres_init_osd(machine);
++ switchres_modeline_setup(machine);
++
+ // determine if we are benchmarking, and adjust options appropriately
+ int bench = options.bench();
+ if (bench > 0)
+@@ -603,6 +610,9 @@ void windows_osd_interface::init(running_machine &machine)
+
+ void windows_osd_interface::osd_exit()
+ {
++ // Remove Switchres
++ switchres_modeline_remove(machine());
++
+ // no longer have a machine
+ g_current_machine = nullptr;
+
diff --git a/testing/mame/nonag.patch b/testing/mame/nonag.patch
index f7457b4e96..d3fd0760c8 100644
--- a/testing/mame/nonag.patch
+++ b/testing/mame/nonag.patch
@@ -10,26 +10,3 @@
{
render_primitive *prim = list.alloc(render_primitive::QUAD);
set_render_bounds_xy(prim->bounds, 0.0f, 0.0f, (float)m_width, (float)m_height);
---- a/src/frontend/mame/ui/ui.cpp
-+++ b/src/frontend/mame/ui/ui.cpp
-@@ -313,7 +313,8 @@
- switch (state)
- {
- case 0:
-- if (show_warnings)
-+ // DISABLE INTERACTIVE WARNING MESSAGES
-+ if (show_gameinfo)
- messagebox_text = machine_info().warnings_string();
- if (!messagebox_text.empty())
- {
-@@ -770,6 +771,10 @@
-
- uint32_t mame_ui_manager::handler_messagebox(render_container &container)
- {
-+ // DISABLE INITIALIZING, LOADING & DECRYPTING MESSAGES
-+ if (machine().options().skip_gameinfo())
-+ return 0;
-+
- draw_text_box(container, messagebox_text.c_str(), ui::text_layout::LEFT, 0.5f, 0.5f, messagebox_backcolor);
- return 0;
- }