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 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 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 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 mame_ui_manager::slider_init(running_machine &machine } #endif + config_apply(); + std::vector 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& 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> m_sliders; + std::vector> 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 make_for_type(int mode, std::shared_ptr 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)->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)->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 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 +#include +#include +#endif + #if defined(SDLMAME_MACOSX) || defined(OSD_MAC) #include #include @@ -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 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 + +#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(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(¤t, 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(¤t); + modeline_to_monitor_range(range, ¤t); + 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(machine.options()); + sdl_osd_interface &osd = downcast(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(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 + +#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 +#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 + +#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 +#include + +// 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 + +// 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(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(machine.options()); + windows_osd_interface &osd = downcast(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(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(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(&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 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 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 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(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;