DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dosbox.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 /* NTS: Valgrind hunting shows memory leak from C++ new operator somewhere
00020  *      with the JACK library indirectly invoked by SDL audio. Can we resolve
00021  *      that too eventually? */
00022 
00023 /* NTS: Valgrind hunting also shows one of the section INIT functions (I can't
00024  *      yet tell which one because the stack trace doesn't show it) is allocating
00025  *      something and is not freeing it. */
00026 
00027 /* NTS: Valgrind hunting has a moderate to high signal-to-noise ratio because
00028  *      of memory leaks (lazy memory allocation) from other libraries in the
00029  *      system, including:
00030  *
00031  *         ncurses
00032  *         libSDL
00033  *         libX11 and libXCB
00034  *         libasound (ALSA sound library)
00035  *         PulseAudio library calls
00036  *         JACK library calls
00037  *         libdl (the dlopen/dlclose functions allocate something and never free it)
00038  *         and a whole bunch of unidentified malloc calls without a matching free.
00039  *
00040  *      On my dev system, a reported leak of 450KB (77KB possibly lost + 384KB still reachable
00041  *      according to Valgrind) is normal.
00042  *
00043  *      Now you ask: why do I care so much about Valgrind, memory leaks, and cleaning
00044  *      up the code? The less spurious memory leaks, the easier it is to identify
00045  *      actual leaks among the noise and to patch them up. Thus, "valgrind hunting" --J.C. */
00046 
00047 #include <stdlib.h>
00048 #include <stdarg.h>
00049 #include <stdio.h>
00050 #include <string.h>
00051 #include <ctime>
00052 #include <unistd.h>
00053 #include "dosbox.h"
00054 #include "debug.h"
00055 #include "cpu.h"
00056 #include "video.h"
00057 #include "pic.h"
00058 #include "cpu.h"
00059 #include "ide.h"
00060 #include "callback.h"
00061 #include "inout.h"
00062 #include "mixer.h"
00063 #include "timer.h"
00064 #include "dos_inc.h"
00065 #include "setup.h"
00066 #include "control.h"
00067 #include "cross.h"
00068 #include "programs.h"
00069 #include "support.h"
00070 #include "mapper.h"
00071 #include "ints/int10.h"
00072 #include "menu.h"
00073 #include "render.h"
00074 #include "pci_bus.h"
00075 #include "parport.h"
00076 #include "clockdomain.h"
00077 #include "zip.h"
00078 #include "unzip.h"
00079 #include "ioapi.h"
00080 #include "shell.h"
00081 #include "build_timestamp.h"
00082 #define MAXU32 0xffffffff
00083 #include "vs2015/zlib/contrib/minizip/zip.c"
00084 #include "vs2015/zlib/contrib/minizip/unzip.c"
00085 #include "vs2015/zlib/contrib/minizip/ioapi.c"
00086 
00087 #if C_EMSCRIPTEN
00088 # include <emscripten.h>
00089 #endif
00090 
00091 #ifdef WIN32
00092 #define WIN32_LEAN_AND_MEAN
00093 #include <windows.h>
00094 #endif
00095 
00096 #if defined(unix) || defined(__APPLE__)
00097 # include <utime.h>
00098 #endif
00099 
00100 #include <list>
00101 
00102 /*===================================TODO: Move to it's own file==============================*/
00103 #if defined(__SSE__) && !(defined(_M_AMD64) || defined(__e2k__))
00104 bool sse2_available = false;
00105 
00106 # ifdef __GNUC__
00107 #  define cpuid(func,ax,bx,cx,dx)\
00108     __asm__ __volatile__ ("cpuid":\
00109     "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));
00110 # endif /* __GNUC__ */
00111 
00112 # if defined(_MSC_VER)
00113 #  define cpuid(func,a,b,c,d)\
00114     __asm mov eax, func\
00115     __asm cpuid\
00116     __asm mov a, eax\
00117     __asm mov b, ebx\
00118     __asm mov c, ecx\
00119     __asm mov d, edx
00120 # endif /* _MSC_VER */
00121 
00122 void CheckSSESupport()
00123 {
00124 #if (defined (__GNUC__) || (_MSC_VER)) && !defined(EMSCRIPTEN)
00125     Bitu a, b, c, d;
00126     cpuid(1, a, b, c, d);
00127     sse2_available = ((d >> 26) & 1)?true:false;
00128 #endif
00129 }
00130 #endif
00131 /*=============================================================================*/
00132 
00133 extern void         GFX_SetTitle(Bit32s cycles,Bits frameskip,Bits timing,bool paused);
00134 
00135 extern bool         force_nocachedir;
00136 extern bool         freesizecap;
00137 extern bool         wpcolon;
00138 
00139 extern Bitu         frames;
00140 extern Bitu         cycle_count;
00141 extern bool         sse2_available;
00142 extern bool         dynamic_dos_kernel_alloc;
00143 extern Bitu         DOS_PRIVATE_SEGMENT_Size;
00144 extern bool         VGA_BIOS_dont_duplicate_CGA_first_half;
00145 extern bool         VIDEO_BIOS_always_carry_14_high_font;
00146 extern bool         VIDEO_BIOS_always_carry_16_high_font;
00147 extern bool         VIDEO_BIOS_enable_CGA_8x8_second_half;
00148 extern bool         allow_more_than_640kb;
00149 
00150 Bit32u              guest_msdos_LoL = 0;
00151 Bit16u              guest_msdos_mcb_chain = 0;
00152 int                 boothax = BOOTHAX_NONE;
00153 
00154 bool                want_fm_towns = false;
00155 bool                force_load_state = false;
00156 
00157 bool                dos_con_use_int16_to_detect_input = true;
00158 
00159 bool                dbg_zero_on_dos_allocmem = true;
00160 bool                dbg_zero_on_xms_allocmem = true;
00161 bool                dbg_zero_on_ems_allocmem = true;
00162 
00163 /* the exact frequency of the NTSC color subcarrier ~3.579545454...MHz or 315/88 */
00164 /* see: http://en.wikipedia.org/wiki/Colorburst */
00165 #define             NTSC_COLOR_SUBCARRIER_NUM       (315000000ULL)
00166 #define             NTSC_COLOR_SUBCARRIER_DEN       (88ULL)
00167 
00168 /* PCI bus clock
00169  * Usual setting: 100MHz / 3 = 33.333MHz
00170  *                 90MHz / 3 = 30.000MHz */
00171 ClockDomain         clockdom_PCI_BCLK(100000000,3);     /* MASTER 100MHz / 3 = 33.33333MHz */
00172 
00173 /* ISA bus OSC clock (14.31818MHz), using a crystal that is 4x the NTSC subcarrier frequency 3.5795454..MHz */
00174 ClockDomain         clockdom_ISA_OSC(NTSC_COLOR_SUBCARRIER_NUM*4,NTSC_COLOR_SUBCARRIER_DEN);
00175 
00176 /* ISA bus clock (varies between 4.77MHz to 8.333MHz)
00177  * PC/XT: ISA oscillator clock (14.31818MHz / 3) = 4.77MHz
00178  * Some systems keep CPU synchronous to bus clock: 4.77MHz, 6MHz, 8MHz, 8.333MHz
00179  * Later systems: 25MHz / 3 = 8.333MHz
00180  *                33MHz / 4 = 8.333MHz
00181  * PCI bus systems: PCI bus clock 33MHz / 4 = 8.333MHz (especially Intel chipsets according to PIIX datasheets) */
00182 ClockDomain         clockdom_ISA_BCLK(25000000,3);      /* MASTER 25000000Hz / 3 = 8.333333MHz */
00183 
00184 Config*             control;
00185 MachineType         machine;
00186 bool                PS1AudioCard;       // Perhaps have PS1 as a machine type...?
00187 SVGACards           svgaCard;
00188 bool                SDLNetInited;
00189 Bit32s              ticksDone;
00190 Bit32u              ticksScheduled;
00191 bool                ticksLocked;
00192 bool                mono_cga=false;
00193 bool                ignore_opcode_63 = true;
00194 int             dynamic_core_cache_block_size = 32;
00195 Bitu                VGA_BIOS_Size_override = 0;
00196 Bitu                VGA_BIOS_SEG = 0xC000;
00197 Bitu                VGA_BIOS_SEG_END = 0xC800;
00198 Bitu                VGA_BIOS_Size = 0x8000;
00199 
00200 Bit32u                  emulator_speed = 100;
00201 
00202 static Bit32u           ticksRemain;
00203 static Bit32u           ticksRemainSpeedFrac;
00204 static Bit32u           ticksLast;
00205 static Bit32u           ticksLastFramecounter;
00206 static Bit32u           ticksLastRTcounter;
00207 static double           ticksLastRTtime;
00208 static Bit32u           ticksAdded;
00209 static Bit32u           Ticks = 0;
00210 extern double           rtdelta;
00211 static LoopHandler*     loop;
00212 
00213 void increaseticks();
00214 
00215 /* The whole load of startups for all the subfunctions */
00216 void                MEM_Init(Section *);
00217 void                ISAPNP_Cfg_Init(Section *);
00218 void                ROMBIOS_Init(Section *);
00219 void                CALLBACK_Init(Section*);
00220 void                PROGRAMS_Init(Section*);
00221 void                RENDER_Init(Section*);
00222 void                VGA_VsyncInit(Section*);
00223 void                VGA_Init(Section*);
00224 void                DOS_Init(Section*);
00225 void                CPU_Init(Section*);
00226 #if C_FPU
00227 void                FPU_Init(Section*);
00228 #endif
00229 void                DMA_Init(Section*);
00230 void                MIXER_Init(Section*);
00231 void                MIDI_Init(Section*);
00232 void                HARDWARE_Init(Section*);
00233 void                PCIBUS_Init(Section*);
00234 void                PCI_Init(Section*);
00235 void                VOODOO_Init(Section*);
00236 
00237 void                IDE_Primary_Init(Section*);
00238 void                IDE_Secondary_Init(Section*);
00239 void                IDE_Tertiary_Init(Section*);
00240 void                IDE_Quaternary_Init(Section*);
00241 void                IDE_Quinternary_Init(Section*);
00242 void                IDE_Sexternary_Init(Section*);
00243 void                IDE_Septernary_Init(Section*);
00244 void                IDE_Octernary_Init(Section*);
00245 
00246 void                FDC_Primary_Init(Section*);
00247 
00248 void                KEYBOARD_Init(Section*);    //TODO This should setup INT 16 too but ok ;)
00249 void                JOYSTICK_Init(Section*);
00250 void                MOUSE_Init(Section*);
00251 void                SBLASTER_Init(Section*);
00252 void                GUS_Init(Section*);
00253 void                MPU401_Init(Section*);
00254 void                PCSPEAKER_Init(Section*);
00255 void                TANDYSOUND_Init(Section*);
00256 void                DISNEY_Init(Section*);
00257 void                PS1SOUND_Init(Section*);
00258 void                INNOVA_Init(Section*);
00259 void                SERIAL_Init(Section*); 
00260 void                DONGLE_Init(Section*);
00261 #if C_IPX
00262 void                IPX_Init(Section*);
00263 #endif
00264 void                PIC_Init(Section*);
00265 void                TIMER_Init(Section*);
00266 void                BIOS_Init(Section*);
00267 void                DEBUG_Init(Section*);
00268 void                CMOS_Init(Section*);
00269 void                MSCDEX_Init(Section*);
00270 void                DRIVES_Init(Section*);
00271 void                CDROM_Image_Init(Section*);
00272 void                EMS_Init(Section*);
00273 void                XMS_Init(Section*);
00274 void                DOS_KeyboardLayout_Init(Section*);
00275 void                AUTOEXEC_Init(Section*);
00276 void                INT10_Init(Section*);
00277 #if C_PRINTER
00278 void                PRINTER_Init(Section*);
00279 #endif
00280 
00281 signed long long time_to_clockdom(const ClockDomain &src,double t) {
00282     signed long long lt = (signed long long)t;
00283 
00284     lt *= (signed long long)src.freq;
00285     lt /= (signed long long)src.freq_div;
00286     return lt;
00287 }
00288 
00289 unsigned long long update_clockdom_from_now(ClockDomain &dst) {
00290     signed long long s;
00291 
00292     /* PIC_Ticks (if I read the code correctly) is millisecond ticks, units of 1/1000 seconds.
00293      * PIC_TickIndexND() units of submillisecond time in units of 1/CPU_CycleMax. */
00294     s  = (signed long long)((unsigned long long)PIC_Ticks * (unsigned long long)dst.freq);
00295     s += (signed long long)(((unsigned long long)PIC_TickIndexND() * (unsigned long long)dst.freq) / (unsigned long long)CPU_CycleMax);
00296     /* convert down to frequency counts, not freq x 1000 */
00297     s /= (signed long long)(1000ULL * (unsigned long long)dst.freq_div);
00298 
00299     /* guard against time going backwards slightly (as PIC_TickIndexND() will do sometimes by tiny amounts) */
00300     if (dst.counter < (unsigned long long)s) dst.counter = (unsigned long long)s;
00301 
00302     return dst.counter;
00303 }
00304 
00305 #include "paging.h"
00306 
00307 extern bool rom_bios_vptable_enable;
00308 extern bool rom_bios_8x8_cga_font;
00309 extern bool allow_port_92_reset;
00310 extern bool allow_keyb_reset;
00311 
00312 extern bool DOSBox_Paused();
00313 
00314 //#define DEBUG_CYCLE_OVERRUN_CALLBACK
00315 
00316 //For trying other delays
00317 #define wrap_delay(a) SDL_Delay(a)
00318 
00319 static Bitu Normal_Loop(void) {
00320     bool saved_allow = dosbox_allow_nonrecursive_page_fault;
00321     Bits ret;
00322 
00323     if (!menu.hidecycles || menu.showrt) { /* sdlmain.cpp/render.cpp doesn't even maintain the frames count when hiding cycles! */
00324         Bit32u ticksNew = GetTicks();
00325         if (ticksNew >= Ticks) {
00326             Bit32u interval = ticksNew - ticksLastFramecounter;
00327             double rtnow = PIC_FullIndex();
00328 
00329             if (interval == 0) interval = 1; // avoid divide by zero
00330 
00331             rtdelta = rtnow - ticksLastRTtime;
00332             rtdelta = (rtdelta * 1000) / interval;
00333 
00334             ticksLastRTtime = rtnow;
00335             ticksLastFramecounter = Ticks;
00336             Ticks = ticksNew + 500;     // next update in 500ms
00337             frames = (frames * 1000) / interval; // compensate for interval, be more exact (FIXME: so can we adjust for fractional frame rates)
00338             GFX_SetTitle((Bit32s)CPU_CycleMax,-1,-1,false);
00339             frames = 0;
00340         }
00341     }
00342 
00343     try {
00344         while (1) {
00345             if (PIC_RunQueue()) {
00346                 /* now is the time to check for the NMI (Non-maskable interrupt) */
00347                 CPU_Check_NMI();
00348 
00349                 saved_allow = dosbox_allow_nonrecursive_page_fault;
00350                 dosbox_allow_nonrecursive_page_fault = true;
00351                 ret = (*cpudecoder)();
00352                 dosbox_allow_nonrecursive_page_fault = saved_allow;
00353 
00354                 if (GCC_UNLIKELY(ret<0))
00355                     return 1;
00356 
00357                 if (ret>0) {
00358                     if (GCC_UNLIKELY((unsigned int)ret >= CB_MAX))
00359                         return 0;
00360 
00361                     extern unsigned int last_callback;
00362                     unsigned int p_last_callback = last_callback;
00363                     last_callback = (unsigned int)ret;
00364 
00365                     dosbox_allow_nonrecursive_page_fault = false;
00366                     Bitu blah = (*CallBack_Handlers[ret])();
00367                     dosbox_allow_nonrecursive_page_fault = saved_allow;
00368 
00369                     last_callback = p_last_callback;
00370 
00371 #ifdef DEBUG_CYCLE_OVERRUN_CALLBACK
00372                     {
00373                         extern char* CallBack_Description[CB_MAX];
00374 
00375                         /* I/O delay can cause negative CPU_Cycles and PIC event / audio rendering issues */
00376                         cpu_cycles_count_t overrun = -std::min(CPU_Cycles,(cpu_cycles_count_t)0);
00377 
00378                         if (overrun > (CPU_CycleMax/100))
00379                             LOG_MSG("Normal loop: CPU cycles count overrun by %ld (%.3fms) after callback '%s'\n",(signed long)overrun,(double)overrun / CPU_CycleMax,CallBack_Description[ret]);
00380                     }
00381 #endif
00382 
00383                     if (GCC_UNLIKELY(blah > 0U))
00384                         return blah;
00385                 }
00386 #if C_DEBUG
00387                 if (DEBUG_ExitLoop())
00388                     return 0;
00389 #endif
00390             } else {
00391                 GFX_Events();
00392                 if (DOSBox_Paused() == false && ticksRemain > 0) {
00393                     TIMER_AddTick();
00394                     ticksRemain--;
00395                 } else {
00396                     increaseticks();
00397                     return 0;
00398                 }
00399             }
00400         }
00401     }
00402     catch (const GuestPageFaultException& pf) {
00403         Bitu FillFlags(void);
00404 
00405         ret = 0;
00406         FillFlags();
00407         dosbox_allow_nonrecursive_page_fault = false;
00408         CPU_Exception(EXCEPTION_PF, pf.faultcode);
00409         dosbox_allow_nonrecursive_page_fault = saved_allow;
00410     }
00411     catch (int x) {
00412         dosbox_allow_nonrecursive_page_fault = saved_allow;
00413         if (x == 4/*CMOS shutdown*/) {
00414             ret = 0;
00415 //          LOG_MSG("CMOS shutdown reset acknowledged");
00416         }
00417         else {
00418             throw;
00419         }
00420     }
00421     return 0;
00422 }
00423 
00424 void increaseticks() { //Make it return ticksRemain and set it in the function above to remove the global variable.
00425     static Bit32s lastsleepDone = -1;
00426     static Bitu sleep1count = 0;
00427     if (GCC_UNLIKELY(ticksLocked)) { // For Fast Forward Mode
00428         ticksRemainSpeedFrac = 0;
00429         ticksRemain = 5;
00430         /* Reset any auto cycle guessing for this frame */
00431         ticksLast = GetTicks();
00432         ticksAdded = 0;
00433         ticksDone = 0;
00434         ticksScheduled = 0;
00435         return;
00436     }
00437     Bit32u ticksNew = GetTicks();
00438     ticksScheduled += ticksAdded;
00439 
00440     if (ticksNew <= ticksLast) { //lower should not be possible, only equal.
00441         ticksAdded = 0;
00442 
00443         if (!CPU_CycleAutoAdjust || CPU_SkipCycleAutoAdjust || sleep1count < 3) {
00444             wrap_delay(1);
00445         }
00446         else {
00447             /* Certain configurations always give an exact sleepingtime of 1, this causes problems due to the fact that
00448                DOSBox-X keeps track of full blocks.
00449                This code introduces some randomness to the time slept, which improves stability on those configurations
00450              */
00451             static const Bit32u sleeppattern[] = { 2, 2, 3, 2, 2, 4, 2 };
00452             static Bit32u sleepindex = 0;
00453             if (ticksDone != lastsleepDone) sleepindex = 0;
00454             wrap_delay(sleeppattern[sleepindex++]);
00455             sleepindex %= sizeof(sleeppattern) / sizeof(sleeppattern[0]);
00456         }
00457         Bit32s timeslept = (Bit32s)(GetTicks() - ticksNew);
00458         // Count how many times in the current block (of 250 ms) the time slept was 1 ms
00459         if (CPU_CycleAutoAdjust && !CPU_SkipCycleAutoAdjust && timeslept == 1) sleep1count++;
00460         lastsleepDone = ticksDone;
00461 
00462         // Update ticksDone with the time spent sleeping
00463         ticksDone -= timeslept;
00464 
00465         if (ticksDone < 0)
00466             ticksDone = 0;
00467         return;
00468     }
00469 
00470     //ticksNew > ticksLast
00471     ticksRemain = ticksNew - ticksLast;
00472 
00473     if (emulator_speed != 100u) {
00474         ticksRemain *= emulator_speed;
00475         ticksRemain += ticksRemainSpeedFrac;
00476         ticksRemainSpeedFrac = ticksRemain % 100u;
00477         ticksRemain /= 100u;
00478     }
00479     else {
00480         ticksRemainSpeedFrac = 0;
00481     }
00482 
00483     ticksLast = ticksNew;
00484     ticksDone += (Bit32s)ticksRemain;
00485     if (ticksRemain > 20) {
00486         ticksRemain = 20;
00487     }
00488     ticksAdded = ticksRemain;
00489 
00490     // Is the system in auto cycle mode guessing? If not just exit. (It can be temporarily disabled)
00491     if (!CPU_CycleAutoAdjust || CPU_SkipCycleAutoAdjust)
00492         return;
00493 
00494     if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5)) {
00495         if (ticksDone < 1) ticksDone = 1; // Protect against div by zero
00496         /* ratio we are aiming for is around 90% usage*/
00497         Bit32s ratio = (Bit32s)((ticksScheduled * (CPU_CyclePercUsed * 90 * 1024 / 100 / 100)) / ticksDone);
00498         Bit32s new_cmax = (Bit32s)CPU_CycleMax;
00499         Bit64s cproc = (Bit64s)CPU_CycleMax * (Bit64s)ticksScheduled;
00500         if (cproc > 0) {
00501             /* ignore the cycles added due to the IO delay code in order
00502                to have smoother auto cycle adjustments */
00503             double ratioremoved = (double)CPU_IODelayRemoved / (double)cproc;
00504             if (ratioremoved < 1.0) {
00505                 double ratio_not_removed = 1 - ratioremoved;
00506                 ratio = (Bit32s)((double)ratio * ratio_not_removed);
00507                 /* Don't allow very high ratio which can cause us to lock as we don't scale down
00508                  * for very low ratios. High ratio might result because of timing resolution */
00509                 if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 16384)
00510                     ratio = 16384;
00511 
00512                 // Limit the ratio even more when the cycles are already way above the realmode default.
00513                 if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 5120 && CPU_CycleMax > 50000)
00514                     ratio = 5120;
00515 
00516                 // When downscaling multiple times in a row, ensure a minimum amount of downscaling
00517                 if (ticksAdded > 15 && ticksScheduled >= 5 && ticksScheduled <= 20 && ratio > 800)
00518                     ratio = 800;
00519 
00520                 if (ratio <= 1024) {
00521                     // ratio_not_removed = 1.0; //enabling this restores the old formula
00522                     double r = (1.0 + ratio_not_removed) / (ratio_not_removed + 1024.0 / (static_cast<double>(ratio)));
00523                     new_cmax = 1 + static_cast<Bit32s>(CPU_CycleMax * r);
00524                 }
00525                 else {
00526                     Bit64s ratio_with_removed = (Bit64s)((((double)ratio - 1024.0) * ratio_not_removed) + 1024.0);
00527                     Bit64s cmax_scaled = (Bit64s)CPU_CycleMax * ratio_with_removed;
00528                     new_cmax = (Bit32s)(1 + (CPU_CycleMax >> 1) + cmax_scaled / (Bit64s)2048);
00529                 }
00530             }
00531         }
00532 
00533         if (new_cmax < CPU_CYCLES_LOWER_LIMIT)
00534             new_cmax = CPU_CYCLES_LOWER_LIMIT;
00535 
00536         /*
00537            LOG_MSG("cyclelog: current %6d   cmax %6d   ratio  %5d  done %3d   sched %3d",
00538            CPU_CycleMax,
00539            new_cmax,
00540            ratio,
00541            ticksDone,
00542            ticksScheduled);
00543            */
00544            /* ratios below 1% are considered to be dropouts due to
00545               temporary load imbalance, the cycles adjusting is skipped */
00546         if (ratio > 10) {
00547             /* ratios below 12% along with a large time since the last update
00548                has taken place are most likely caused by heavy load through a
00549                different application, the cycles adjusting is skipped as well */
00550             if ((ratio > 120) || (ticksDone < 700)) {
00551                 CPU_CycleMax = new_cmax;
00552                 if (CPU_CycleLimit > 0) {
00553                     if (CPU_CycleMax > CPU_CycleLimit) CPU_CycleMax = CPU_CycleLimit;
00554                 }
00555                 else if (CPU_CycleMax > 2000000) CPU_CycleMax = 2000000; //Hardcoded limit, if no limit was specified.
00556             }
00557         }
00558 
00559         //Reset cycleguessing parameters.
00560         CPU_IODelayRemoved = 0;
00561         ticksDone = 0;
00562         ticksScheduled = 0;
00563         lastsleepDone = -1;
00564         sleep1count = 0;
00565     }
00566     else if (ticksAdded > 15) {
00567         /* ticksAdded > 15 but ticksScheduled < 5, lower the cycles
00568            but do not reset the scheduled/done ticks to take them into
00569            account during the next auto cycle adjustment */
00570         CPU_CycleMax /= 3;
00571         if (CPU_CycleMax < CPU_CYCLES_LOWER_LIMIT)
00572             CPU_CycleMax = CPU_CYCLES_LOWER_LIMIT;
00573     }
00574 }
00575 
00576 LoopHandler *DOSBOX_GetLoop(void) {
00577     return loop;
00578 }
00579 
00580 void DOSBOX_SetLoop(LoopHandler * handler) {
00581     loop=handler;
00582 }
00583 
00584 void DOSBOX_SetNormalLoop() {
00585     loop=Normal_Loop;
00586 }
00587 
00588 //#define DEBUG_RECURSION
00589 
00590 #ifdef DEBUG_RECURSION
00591 volatile int runmachine_recursion = 0;
00592 #endif
00593 
00594 void DOSBOX_RunMachine(void){
00595     Bitu ret;
00596 
00597     extern unsigned int last_callback;
00598     unsigned int p_last_callback = last_callback;
00599     last_callback = 0;
00600 
00601 #ifdef DEBUG_RECURSION
00602     if (runmachine_recursion++ != 0)
00603         LOG_MSG("RunMachine recursion");
00604 #endif
00605 
00606     do {
00607         ret=(*loop)();
00608     } while (!ret);
00609 
00610 #ifdef DEBUG_RECURSION
00611     if (--runmachine_recursion < 0)
00612         LOG_MSG("RunMachine recursion leave error");
00613 #endif
00614 
00615     last_callback = p_last_callback;
00616 }
00617 
00618 static void DOSBOX_UnlockSpeed( bool pressed ) {
00619     static bool autoadjust = false;
00620     if (pressed) {
00621         LOG_MSG("Fast Forward ON");
00622         ticksLocked = true;
00623         if (CPU_CycleAutoAdjust) {
00624             autoadjust = true;
00625             CPU_CycleAutoAdjust = false;
00626             CPU_CycleMax /= 3;
00627             if (CPU_CycleMax<1000) CPU_CycleMax=1000;
00628         }
00629     } else {
00630         LOG_MSG("Fast Forward OFF");
00631         ticksLocked = false;
00632         if (autoadjust) {
00633             autoadjust = false;
00634             CPU_CycleAutoAdjust = true;
00635         }
00636     }
00637     GFX_SetTitle(-1,-1,-1,false);
00638 }
00639 
00640 void DOSBOX_UnlockSpeed2( bool pressed ) {
00641     if (pressed) {
00642         ticksLocked = !ticksLocked;
00643         DOSBOX_UnlockSpeed(ticksLocked?true:false);
00644 
00645         /* make sure the menu item keeps up with our state */
00646         mainMenu.get_item("mapper_speedlock2").check(ticksLocked).refresh_item(mainMenu);
00647     }
00648 }
00649 
00650 void DOSBOX_NormalSpeed( bool pressed ) {
00651     if (pressed) {
00652         /* should also cancel turbo mode */
00653         if (ticksLocked)
00654             DOSBOX_UnlockSpeed2(true);
00655 
00656         LOG_MSG("Emulation speed restored to normal (100%%)");
00657 
00658         emulator_speed = 100;
00659         ticksRemainSpeedFrac = 0;
00660     }
00661 }
00662 
00663 void DOSBOX_SpeedUp( bool pressed ) {
00664     if (pressed) {
00665         ticksRemainSpeedFrac = 0;
00666         if (emulator_speed >= 5)
00667             emulator_speed += 5;
00668         else
00669             emulator_speed = 5;
00670 
00671         LOG_MSG("Emulation speed increased to (%u%%)",(unsigned int)emulator_speed);
00672     }
00673 }
00674 
00675 void DOSBOX_SlowDown( bool pressed ) {
00676     if (pressed) {
00677         ticksRemainSpeedFrac = 0;
00678         if (emulator_speed  > 5)
00679             emulator_speed -= 5;
00680         else
00681             emulator_speed = 1;
00682 
00683         LOG_MSG("Emulation speed decreased to (%u%%)",(unsigned int)emulator_speed);
00684     }
00685 }
00686 
00687 namespace
00688 {
00689 std::string getTime()
00690 {
00691     const time_t current = time(NULL);
00692     tm* timeinfo;
00693     timeinfo = localtime(&current); //convert to local time
00694     char buffer[50];
00695     ::strftime(buffer, 50, "%H:%M:%S", timeinfo);
00696     return buffer;
00697 }
00698 
00699 class SlotPos
00700 {
00701 public:
00702     SlotPos() : slot(0) {}
00703 
00704     void next()
00705     {
00706         ++slot;
00707         slot %= SaveState::SLOT_COUNT;
00708     }
00709 
00710     void previous()
00711     {
00712         slot += SaveState::SLOT_COUNT - 1;
00713         slot %= SaveState::SLOT_COUNT;
00714     }
00715 
00716     void set(int value)
00717     {
00718         slot = value;
00719     }
00720 
00721     operator size_t() const
00722     {
00723         return slot;
00724     }
00725 private:
00726     size_t slot;
00727 } currentSlot;
00728 
00729 void notifyError(const std::string& message)
00730 {
00731 #ifdef WIN32
00732     ::MessageBox(0, message.c_str(), "Error", 0);
00733 #endif
00734     LOG_MSG("%s",message.c_str());
00735 }
00736 
00737 size_t GetGameState(void) {
00738     return currentSlot;
00739 }
00740 
00741 void SetGameState(int value) {
00742         char name[6]="slot0";
00743         name[4]='0'+currentSlot;
00744         mainMenu.get_item(name).check(false).refresh_item(mainMenu);
00745     currentSlot.set(value);
00746         name[4]='0'+currentSlot;
00747         mainMenu.get_item(name).check(true).refresh_item(mainMenu);
00748         
00749         const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
00750     LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1,  emptySlot ? "[Empty]" : "");
00751 }
00752 
00753 void SaveGameState(bool pressed) {
00754     if (!pressed) return;
00755 
00756     try
00757     {
00758         SaveState::instance().save(currentSlot);
00759         LOG_MSG("[%s]: State %d saved!", getTime().c_str(), (int)currentSlot + 1);
00760                 char name[6]="slot0";
00761                 name[4]='0'+currentSlot;
00762                 std::string command=SaveState::instance().getName(currentSlot);
00763                 std::string str="Slot "+(currentSlot>=9?"10":std::string(1, '1'+currentSlot))+(command=="[Empty]"?" [Empty slot]":(command==""?"":" (Program: "+command+")"));
00764                 mainMenu.get_item(name).set_text(str.c_str()).refresh_item(mainMenu);
00765     }
00766     catch (const SaveState::Error& err)
00767     {
00768         notifyError(err);
00769     }
00770 }
00771 
00772 void LoadGameState(bool pressed) {
00773     if (!pressed) return;
00774 
00775 //    if (SaveState::instance().isEmpty(currentSlot))
00776 //    {
00777 //        LOG_MSG("[%s]: State %d is empty!", getTime().c_str(), currentSlot + 1);
00778 //        return;
00779 //    }
00780     try
00781     {
00782         SaveState::instance().load(currentSlot);
00783         LOG_MSG("[%s]: State %d loaded!", getTime().c_str(), (int)currentSlot + 1);
00784     }
00785     catch (const SaveState::Error& err)
00786     {
00787         notifyError(err);
00788     }
00789 }
00790 
00791 void NextSaveSlot(bool pressed) {
00792     if (!pressed) return;
00793 
00794         char name[6]="slot0";
00795         name[4]='0'+currentSlot;
00796         mainMenu.get_item(name).check(false).refresh_item(mainMenu);
00797     currentSlot.next();
00798         name[4]='0'+currentSlot;
00799         mainMenu.get_item(name).check(true).refresh_item(mainMenu);
00800 
00801     const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
00802     LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1, emptySlot ? "[Empty]" : "");
00803 }
00804 
00805 
00806 void PreviousSaveSlot(bool pressed) {
00807     if (!pressed) return;
00808 
00809         char name[6]="slot0";
00810         name[4]='0'+currentSlot;
00811         mainMenu.get_item(name).check(false).refresh_item(mainMenu);
00812     currentSlot.previous();
00813         name[4]='0'+currentSlot;
00814         mainMenu.get_item(name).check(true).refresh_item(mainMenu);
00815 
00816     const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
00817     LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1, emptySlot ? "[Empty]" : "");
00818 }
00819 }
00820 
00821 std::string GetPlatform(void) {
00822         char platform[30];
00823         strcpy(platform, 
00824 #if defined(WIN32)
00825         "Windows"
00826 #elif defined(LINUX)
00827         "Linux"
00828 #elif unix
00829     "Unix"
00830 #elif defined(MACOSX)
00831     "macOS"
00832 #else
00833     "Other"
00834 #endif
00835 );
00836 #if defined(_M_X64) || defined (_M_AMD64) || defined (_M_ARM64)
00837         strcat(platform, " 64");
00838 #else
00839         strcat(platform, " 32");
00840 #endif
00841         strcat(platform, "-bit build");
00842         return std::string(platform);
00843 }
00844 
00845 size_t GetGameState_Run(void) { return GetGameState(); }
00846 void SetGameState_Run(int value) { SetGameState(value); }
00847 void SaveGameState_Run(void) { SaveGameState(true); }
00848 void LoadGameState_Run(void) { LoadGameState(true); }
00849 void NextSaveSlot_Run(void) { NextSaveSlot(true); }
00850 void PreviousSaveSlot_Run(void) { PreviousSaveSlot(true); }
00851 
00852 /* TODO: move to utility header */
00853 #ifdef _MSC_VER /* Microsoft C++ does not have strtoull */
00854 # if _MSC_VER < 1800 /* But Visual Studio 2013 apparently does (http://www.vogons.org/viewtopic.php?f=41&t=31881&sid=49ff69ebc0459ed6523f5a250daa4d8c&start=400#p355770) */
00855 unsigned long long strtoull(const char *s,char **endptr,int base) {
00856     return _strtoui64(s,endptr,base); /* pfff... whatever Microsoft */
00857 }
00858 # endif
00859 #endif
00860 
00861 /* utility function. rename as appropriate and move to utility collection */
00862 void parse_busclk_setting_str(ClockDomain *cd,const char *s) {
00863     const char *d;
00864 
00865     /* we're expecting an integer, a float, or an integer ratio */
00866     d = strchr(s,'/');
00867     if (d != NULL) { /* it has a slash therefore an integer ratio */
00868         unsigned long long num,den;
00869 
00870         while (*d == ' ' || *d == '/') d++;
00871         num = strtoull(s,NULL,0);
00872         den = strtoull(d,NULL,0);
00873         if (num >= 1ULL && den >= 1ULL) cd->set_frequency(num,den);
00874     }
00875     else {
00876         d = strchr(s,'.');
00877         if (d != NULL) { /* it has a dot, floating point */
00878             double f = atof(s);
00879             unsigned long long fi = (unsigned long long)floor((f*1000000)+0.5);
00880             unsigned long long den = 1000000;
00881 
00882             while (den > 1ULL) {
00883                 if ((fi%10ULL) == 0) {
00884                     den /= 10ULL;
00885                     fi /= 10ULL;
00886                 }
00887                 else {
00888                     break;
00889                 }
00890             }
00891 
00892             if (fi >= 1ULL) cd->set_frequency(fi,den);
00893         }
00894         else {
00895             unsigned long long f = strtoull(s,NULL,10);
00896             if (f >= 1ULL) cd->set_frequency(f,1);
00897         }
00898     }
00899 }
00900 
00901 unsigned int dosbox_shell_env_size = 0;
00902 
00903 void Null_Init(Section *sec) {
00904         (void)sec;
00905 }
00906 
00907 extern Bit8u cga_comp;
00908 extern bool new_cga;
00909 
00910 bool dpi_aware_enable = true;
00911 
00912 std::string dosbox_title;
00913 
00914 void DOSBOX_InitTickLoop() {
00915     LOG(LOG_MISC,LOG_DEBUG)("Initializing tick loop management");
00916 
00917     ticksRemain = 0;
00918     ticksLocked = false;
00919     ticksLastRTtime = 0;
00920     ticksLast = GetTicks();
00921     ticksLastRTcounter = GetTicks();
00922     ticksLastFramecounter = GetTicks();
00923     DOSBOX_SetLoop(&Normal_Loop);
00924 }
00925 
00926 void Init_VGABIOS() {
00927     Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
00928     assert(section != NULL);
00929 
00930     if (IS_PC98_ARCH) {
00931         // There IS no VGA BIOS, this is PC-98 mode!
00932         VGA_BIOS_SEG = 0xC000;
00933         VGA_BIOS_SEG_END = 0xC000; // Important: DOS kernel uses this to determine where to place the private area!
00934         VGA_BIOS_Size = 0;
00935         return;
00936     }
00937 
00938     // log
00939     LOG(LOG_MISC,LOG_DEBUG)("Init_VGABIOS: Initializing VGA BIOS and parsing it's settings");
00940 
00941     // mem init must have already happened.
00942     // We can remove this once the device callout system is in place.
00943     assert(MemBase != NULL);
00944 
00945     force_nocachedir = section->Get_bool("nocachedir");
00946         freesizecap = section->Get_bool("freesizecap");
00947     wpcolon = section->Get_bool("leading colon write protect image");
00948 
00949     VGA_BIOS_Size_override = (Bitu)section->Get_int("vga bios size override");
00950     if (VGA_BIOS_Size_override > 0) VGA_BIOS_Size_override = (VGA_BIOS_Size_override+0x7FFU)&(~0xFFFU);
00951 
00952     VGA_BIOS_dont_duplicate_CGA_first_half = section->Get_bool("video bios dont duplicate cga first half rom font");
00953     VIDEO_BIOS_always_carry_14_high_font = section->Get_bool("video bios always offer 14-pixel high rom font");
00954     VIDEO_BIOS_always_carry_16_high_font = section->Get_bool("video bios always offer 16-pixel high rom font");
00955     VIDEO_BIOS_enable_CGA_8x8_second_half = section->Get_bool("video bios enable cga second half rom font");
00956     /* NTS: mainline compatible mapping demands the 8x8 CGA font */
00957     rom_bios_8x8_cga_font = section->Get_bool("rom bios 8x8 CGA font");
00958     rom_bios_vptable_enable = section->Get_bool("rom bios video parameter table");
00959 
00960     /* sanity check */
00961     if (VGA_BIOS_dont_duplicate_CGA_first_half && !rom_bios_8x8_cga_font) /* can't point at the BIOS copy if it's not there */
00962         VGA_BIOS_dont_duplicate_CGA_first_half = false;
00963 
00964     if (VGA_BIOS_Size_override >= 512 && VGA_BIOS_Size_override <= 65536)
00965         VGA_BIOS_Size = (VGA_BIOS_Size_override + 0x7FFU) & (~0xFFFU);
00966     else if (IS_VGA_ARCH) {
00967         if (svgaCard == SVGA_S3Trio)
00968             VGA_BIOS_Size = 0x4000;
00969         else
00970             VGA_BIOS_Size = 0x4000; // FIXME: Why does 0x3800 cause Windows 3.0 386 enhanced mode to hang?
00971     }
00972     else if (machine == MCH_EGA) {
00973         if (VIDEO_BIOS_always_carry_16_high_font)
00974             VGA_BIOS_Size = 0x3000;
00975         else
00976             VGA_BIOS_Size = 0x2000;
00977     }
00978     else {
00979         if (VIDEO_BIOS_always_carry_16_high_font && VIDEO_BIOS_always_carry_14_high_font)
00980             VGA_BIOS_Size = 0x3000;
00981         else if (VIDEO_BIOS_always_carry_16_high_font || VIDEO_BIOS_always_carry_14_high_font)
00982             VGA_BIOS_Size = 0x2000;
00983         else
00984             VGA_BIOS_Size = 0;
00985     }
00986     VGA_BIOS_SEG = 0xC000;
00987     VGA_BIOS_SEG_END = (VGA_BIOS_SEG + (VGA_BIOS_Size >> 4));
00988 
00989     /* clear for VGA BIOS (FIXME: Why does Project Angel like our BIOS when we memset() here, but don't like it if we memset() in the INT 10 ROM setup routine?) */
00990     if (VGA_BIOS_Size != 0)
00991         memset((char*)MemBase+0xC0000,0x00,VGA_BIOS_Size);
00992 }
00993 
00994 void SetCyclesCount_mapper_shortcut(bool pressed);
00995 void DOSBOX_RealInit() {
00996     DOSBoxMenu::item *item;
00997 
00998     LOG(LOG_MISC,LOG_DEBUG)("DOSBOX_RealInit: loading settings and initializing");
00999 
01000     MAPPER_AddHandler(DOSBOX_UnlockSpeed, MK_rightarrow, MMODHOST,"speedlock","Speedlock");
01001     {
01002         MAPPER_AddHandler(DOSBOX_UnlockSpeed2, MK_nothing, 0, "speedlock2", "Speedlock2", &item);
01003         item->set_description("Toggle emulation speed, to allow running faster than realtime (fast forward)");
01004         item->set_text("Turbo (Fast Forward)");
01005     }
01006     {
01007         MAPPER_AddHandler(DOSBOX_NormalSpeed, MK_leftarrow, MMODHOST, "speednorm","SpeedNrm", &item);
01008         item->set_description("Restore normal emulation speed");
01009         item->set_text("Normal speed");
01010     }
01011     {
01012         MAPPER_AddHandler(DOSBOX_SpeedUp, MK_rbracket, MMODHOST, "speedup","SpeedUp", &item);
01013         item->set_text("Speed up");
01014     }
01015     {
01016         MAPPER_AddHandler(DOSBOX_SlowDown, MK_lbracket, MMODHOST,"slowdown","SlowDn", &item);
01017         item->set_text("Slow down");
01018     }
01019         {
01020                 MAPPER_AddHandler(&SetCyclesCount_mapper_shortcut, MK_nothing, 0, "editcycles", "EditCycles", &item);
01021                 item->set_text("Edit cycles");
01022         }
01023 
01024         //add support for loading/saving game states
01025         MAPPER_AddHandler(SaveGameState, MK_f9, MMOD1|MMOD2,"savestate","SaveState", &item);
01026         item->set_text("Save state");
01027         MAPPER_AddHandler(LoadGameState, MK_f10, MMOD1|MMOD2,"loadstate","LoadState", &item);
01028         item->set_text("Load state");
01029         MAPPER_AddHandler(PreviousSaveSlot, MK_f7, MMOD1|MMOD2,"prevslot","PrevSlot", &item);
01030         item->set_text("Previous slot");
01031         MAPPER_AddHandler(NextSaveSlot, MK_f8, MMOD1|MMOD2,"nextslot","NextSlot", &item);
01032         item->set_text("Next slot");
01033 
01034     Section_prop *section = static_cast<Section_prop *>(control->GetSection("dosbox"));
01035     assert(section != NULL);
01036 
01037     // TODO: allow change at any time. in fact if it were possible for DOSBox-X configuration
01038     //       schema code to attach event callbacks when a setting changes, we would set one
01039     //       on the title= setting now to auto-update the titlebar when this changes.
01040     dosbox_title = section->Get_string("title");
01041 
01042     // TODO: these should be parsed by DOS kernel at startup
01043     dosbox_shell_env_size = (unsigned int)section->Get_int("shell environment size");
01044 
01045     // TODO: a bit of a challenge: if we put it in the ROM area as mainline DOSBox does then the init
01046     //       needs to read this from the BIOS where it can map the memory appropriately. if the allocation
01047     //       is dynamic and the private area is down at the base of memory like real DOS, then the BIOS
01048     //       should ignore it and the DOS kernel should parse it. If we're going to put it into upper
01049     //       areas as well, then we should also consider making it look like adapter ROM at startup
01050     //       so it can be enumerated properly by DOS programs scanning the ROM area.
01051     /* private area size param in bytes. round up to nearest paragraph */
01052     DOS_PRIVATE_SEGMENT_Size = (Bitu)((section->Get_int("private area size") + 8) / 16);
01053 
01054     // TODO: these should be parsed by BIOS startup
01055     allow_more_than_640kb = section->Get_bool("allow more than 640kb base memory");
01056 
01057     // TODO: should be parsed by motherboard emulation
01058     allow_port_92_reset = section->Get_bool("allow port 92 reset");
01059 
01060     // CGA/EGA/VGA-specific
01061     extern unsigned char vga_p3da_undefined_bits;
01062     vga_p3da_undefined_bits = (unsigned char)section->Get_hex("vga 3da undefined bits");
01063 
01064     // TODO: should be parsed by motherboard emulation or lower level equiv..?
01065     std::string cmd_machine;
01066     if (control->cmdline->FindString("-machine",cmd_machine,true)){
01067         //update value in config (else no matching against suggested values
01068         section->HandleInputline(std::string("machine=") + cmd_machine);
01069     }
01070 
01071     // TODO: should be parsed by...? perhaps at some point we support machine= for backwards compat
01072     //       but translate it into two separate params that specify what machine vs what video hardware.
01073     //       or better yet as envisioned, a possible dosbox-x.conf schema that allows a machine with no
01074     //       base video of it's own, and then to specify an ISA or PCI card attached to the bus that
01075     //       provides video.
01076     std::string mtype(section->Get_string("machine"));
01077     svgaCard = SVGA_None;
01078     machine = MCH_VGA;
01079     int10.vesa_nolfb = false;
01080     int10.vesa_oldvbe = false;
01081     if      (mtype == "cga")           { machine = MCH_CGA; mono_cga = false; }
01082     else if (mtype == "cga_mono")      { machine = MCH_CGA; mono_cga = true; }
01083     else if (mtype == "cga_rgb")       { machine = MCH_CGA; mono_cga = false; cga_comp = 2; }
01084     else if (mtype == "cga_composite") { machine = MCH_CGA; mono_cga = false; cga_comp = 1; new_cga = false; }
01085     else if (mtype == "cga_composite2"){ machine = MCH_CGA; mono_cga = false; cga_comp = 1; new_cga = true; }
01086     else if (mtype == "mcga")          { machine = MCH_MCGA; }
01087     else if (mtype == "tandy")         { machine = MCH_TANDY; }
01088     else if (mtype == "pcjr")          { machine = MCH_PCJR; }
01089     else if (mtype == "hercules")      { machine = MCH_HERC; }
01090     else if (mtype == "mda")           { machine = MCH_MDA; }
01091     else if (mtype == "ega")           { machine = MCH_EGA; }
01092     else if (mtype == "svga_s3")       { svgaCard = SVGA_S3Trio; }
01093     else if (mtype == "vesa_nolfb")    { svgaCard = SVGA_S3Trio; int10.vesa_nolfb = true;}
01094     else if (mtype == "vesa_oldvbe")   { svgaCard = SVGA_S3Trio; int10.vesa_oldvbe = true;}
01095     else if (mtype == "svga_et4000")   { svgaCard = SVGA_TsengET4K; }
01096     else if (mtype == "svga_et3000")   { svgaCard = SVGA_TsengET3K; }
01097     else if (mtype == "svga_paradise") { svgaCard = SVGA_ParadisePVGA1A; }
01098     else if (mtype == "vgaonly")       { svgaCard = SVGA_None; }
01099     else if (mtype == "amstrad")       { machine = MCH_AMSTRAD; }
01100     else if (mtype == "pc98")          { machine = MCH_PC98; }
01101     else if (mtype == "pc9801")        { machine = MCH_PC98; } /* Future differentiation */
01102     else if (mtype == "pc9821")        { machine = MCH_PC98; } /* Future differentiation */
01103 
01104     else if (mtype == "fm_towns")      { machine = MCH_VGA; want_fm_towns = true; /*machine = MCH_FM_TOWNS;*/ }
01105 
01106     else E_Exit("DOSBOX:Unknown machine type %s",mtype.c_str());
01107 
01108     // TODO: should be parsed by motherboard emulation
01109     // FIXME: This re-uses the existing ISA bus delay code for C-BUS in PC-98 mode
01110     std::string isabclk;
01111 
01112     if (IS_PC98_ARCH)
01113         isabclk = section->Get_string("cbus bus clock");
01114     else
01115         isabclk = section->Get_string("isa bus clock");
01116 
01117     if (isabclk == "std10")
01118         clockdom_ISA_BCLK.set_frequency(PIT_TICK_RATE_PC98_10MHZ * 4ul,1);          /* 10Mhz (PC-98) */
01119     else if (isabclk == "std8.3")
01120         clockdom_ISA_BCLK.set_frequency(25000000,3);    /* 25MHz / 3 = 8.333MHz, early 386 systems did this, became an industry standard "norm" afterwards */
01121     else if (isabclk == "std8") {
01122         if (IS_PC98_ARCH)
01123             clockdom_ISA_BCLK.set_frequency(PIT_TICK_RATE_PC98_8MHZ * 4ul,1);       /* 8Mhz (PC-98) */
01124         else
01125             clockdom_ISA_BCLK.set_frequency(8000000,1);                             /* 8Mhz (IBM PC) */
01126     }
01127     else if (isabclk == "std6")
01128         clockdom_ISA_BCLK.set_frequency(6000000,1);     /* 6MHz */
01129     else if (isabclk == "std5")
01130         clockdom_ISA_BCLK.set_frequency(PIT_TICK_RATE_PC98_10MHZ * 2ul,1);          /* 5Mhz (PC-98) */
01131     else if (isabclk == "std4.77")
01132         clockdom_ISA_BCLK.set_frequency(clockdom_ISA_OSC.freq,clockdom_ISA_OSC.freq_div*3LL); /* 14.31818MHz / 3 = 4.77MHz */
01133     else if (isabclk == "oc10")
01134         clockdom_ISA_BCLK.set_frequency(10000000,1);    /* 10MHz */
01135     else if (isabclk == "oc12")
01136         clockdom_ISA_BCLK.set_frequency(12000000,1);    /* 12MHz */
01137     else if (isabclk == "oc15")
01138         clockdom_ISA_BCLK.set_frequency(15000000,1);    /* 15MHz */
01139     else if (isabclk == "oc16")
01140         clockdom_ISA_BCLK.set_frequency(16000000,1);    /* 16MHz */
01141     else
01142         parse_busclk_setting_str(&clockdom_ISA_BCLK,isabclk.c_str());
01143 
01144     std::string pcibclk = section->Get_string("pci bus clock");
01145     if (pcibclk == "std33.3")
01146         clockdom_PCI_BCLK.set_frequency(100000000,3);   /* 100MHz / 3 = 33.333MHz, VERY common PCI speed */
01147     else if (pcibclk == "std30")
01148         clockdom_PCI_BCLK.set_frequency(30000000,1);    /* 30Mhz */
01149     else if (pcibclk == "std25")
01150         clockdom_PCI_BCLK.set_frequency(25000000,1);    /* 25MHz */
01151     else
01152         parse_busclk_setting_str(&clockdom_PCI_BCLK,pcibclk.c_str());
01153 
01154     LOG_MSG("%s BCLK: %.3fHz (%llu/%llu)",
01155         IS_PC98_ARCH ? "C-BUS" : "ISA",
01156         (double)clockdom_ISA_BCLK.freq / clockdom_ISA_BCLK.freq_div,
01157         (unsigned long long)clockdom_ISA_BCLK.freq,
01158         (unsigned long long)clockdom_ISA_BCLK.freq_div);
01159 
01160     clockdom_ISA_OSC.set_name("ISA OSC");
01161     clockdom_ISA_BCLK.set_name("ISA BCLK");
01162     clockdom_PCI_BCLK.set_name("PCI BCLK");
01163 
01164     // FM TOWNS is stub so far. According to sources like Wikipedia though,
01165     // it boots from DOS in ROM that then loads bootcode from CD-ROM. So
01166     // for now, allow booting into FM TOWNS mode with a warning. The
01167     // switch to FM Towns will begin in the BOOT command with a flag to
01168     // indicate the ISO is intended for FM TOwns.
01169     if (IS_FM_TOWNS || want_fm_towns) LOG_MSG("FM Towns emulation not yet implemented. It's currently just a stub for future development.");
01170 }
01171 
01172 void DOSBOX_SetupConfigSections(void) {
01173     Prop_int* Pint;
01174     Prop_hex* Phex;
01175     Prop_bool* Pbool;
01176     Prop_string* Pstring;
01177     Prop_double* Pdouble;
01178     Prop_multival* Pmulti;
01179     Section_prop * secprop;
01180     Prop_multival_remain* Pmulti_remain;
01181 
01182     // Some frequently used option sets
01183     const char* vsyncrate[] = { "%u", 0 };
01184     const char* force[] = { "", "forced", 0 };
01185     const char* cyclest[] = { "auto","fixed","max","%u",0 };
01186     const char* mputypes[] = { "intelligent", "uart", "none", 0 };
01187     const char* vsyncmode[] = { "off", "on" ,"force", "host", 0 };
01188     const char* captureformats[] = { "default", "avi-zmbv", "mpegts-h264", 0 };
01189     const char* blocksizes[] = {"1024", "2048", "4096", "8192", "512", "256", 0};
01190     const char* capturechromaformats[] = { "auto", "4:4:4", "4:2:2", "4:2:0", 0};
01191     const char* controllertypes[] = { "auto", "at", "xt", "pcjr", "pc98", 0}; // Future work: Tandy(?) and USB
01192     const char* auxdevices[] = {"none","2button","3button","intellimouse","intellimouse45",0};
01193     const char* cputype_values[] = {"auto", "8086", "8086_prefetch", "80186", "80186_prefetch", "286", "286_prefetch", "386", "386_prefetch", "486old", "486old_prefetch", "486", "486_prefetch", "pentium", "pentium_mmx", "ppro_slow", 0};
01194     const char* rates[] = {  "44100", "48000", "32000","22050", "16000", "11025", "8000", "49716", 0 };
01195     const char* oplrates[] = {   "44100", "49716", "48000", "32000","22050", "16000", "11025", "8000", 0 };
01196 #ifdef C_FLUIDSYNTH
01197     const char* devices[] = { "default", "win32", "alsa", "oss", "coreaudio", "coremidi", "mt32", "synth", "fluidsynth", "timidity", "none", 0};
01198 #else
01199     const char* devices[] = { "default", "win32", "alsa", "oss", "coreaudio", "coremidi", "mt32", "timidity", "none", 0}; // FIXME: add some way to offer the actually available choices.
01200 #endif
01201     const char* apmbiosversions[] = { "auto", "1.0", "1.1", "1.2", 0 };
01202     const char* driveletters[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", 0};
01203     const char *mt32log[] = {"off", "on",0};
01204     const char *mt32thread[] = {"off", "on",0};
01205     const char *mt32ReverseStereo[] = {"off", "on",0};
01206     const char *mt32DACModes[] = {"0", "1", "2", "3", "auto",0};
01207     const char *mt32reverbModes[] = {"0", "1", "2", "3", "auto",0};
01208     const char *mt32reverbTimes[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
01209     const char *mt32reverbLevels[] = {"0", "1", "2", "3", "4", "5", "6", "7",0};
01210     const char* gustypes[] = { "classic", "classic37", "max", "interwave", 0 };
01211     const char* sbtypes[] = { "sb1", "sb2", "sbpro1", "sbpro2", "sb16", "sb16vibra", "gb", "ess688", "reveal_sc400", "none", 0 };
01212     const char* oplmodes[]={ "auto", "cms", "opl2", "dualopl2", "opl3", "opl3gold", "none", "hardware", "hardwaregb", 0};
01213     const char* serials[] = { "dummy", "disabled", "modem", "nullmodem", "serialmouse", "directserial", "log", "file", 0 };
01214     const char* acpi_rsd_ptr_settings[] = { "auto", "bios", "ebda", 0 };
01215     const char* cpm_compat_modes[] = { "auto", "off", "msdos2", "msdos5", "direct", 0 };
01216     const char* dosv_settings[] = { "off", "japanese", "chinese", "korean", 0 };
01217     const char* acpisettings[] = { "off", "1.0", "1.0b", "2.0", "2.0a", "2.0b", "2.0c", "3.0", "3.0a", "3.0b", "4.0", "4.0a", "5.0", "5.0a", "6.0", 0 };
01218     const char* guspantables[] = { "old", "accurate", "default", 0 };
01219     const char *sidbaseno[] = { "240", "220", "260", "280", "2a0", "2c0", "2e0", "300", 0 };
01220     const char* joytypes[] = { "auto", "2axis", "4axis", "4axis_2", "fcs", "ch", "none",0};
01221 //    const char* joydeadzone[] = { "0.26", 0 };
01222 //    const char* joyresponse[] = { "1.0", 0 };
01223     const char* iosgus[] = { "240", "220", "260", "280", "2a0", "2c0", "2e0", "300", "210", "230", "250", 0 };
01224     const char* mpubases[] = {
01225         "0",                                                                                    /* Auto */
01226         "300", "310", "320", "330", "332", "334", "336", "340", "360",                          /* IBM PC */
01227         "c0d0","c8d0","d0d0","d8d0","e0d0","e8d0","f0d0","f8d0",                                /* NEC PC-98 MPU98 */
01228         "80d2","80d4","80d6","80d8","80da","80dc","80de",                                       /* NEC PC-98 SB16 */
01229         0 };
01230     const char* ios[] = {
01231         "220", "240", "260", "280", "2a0", "2c0", "2e0",            /* IBM PC      (base+port i.e. 220h base, 22Ch is DSP) */
01232         "d2",  "d4",  "d6",  "d8",  "da",  "dc",  "de",             /* NEC PC-98   (base+(port << 8) i.e. 00D2h base, 2CD2h is DSP) */
01233         0 };
01234     const char* ems_settings[] = { "true", "emsboard", "emm386", "false", 0};
01235     const char* lfn_settings[] = { "true", "false", "auto", "autostart", 0};
01236     const char* irqsgus[] = { "5", "3", "7", "9", "10", "11", "12", 0 };
01237     const char* irqssb[] = { "7", "5", "3", "9", "10", "11", "12", 0 };
01238     const char* dmasgus[] = { "3", "0", "1", "5", "6", "7", 0 };
01239     const char* dmassb[] = { "1", "5", "0", "3", "6", "7", 0 };
01240     const char* oplemus[] = { "default", "compat", "fast", "nuked", "mame", 0 };
01241     const char *qualityno[] = { "0", "1", "2", "3", 0 };
01242     const char* tandys[] = { "auto", "on", "off", 0};
01243     const char* ps1opt[] = { "on", "off", 0};
01244     const char* numopt[] = { "on", "off", "", 0};
01245     const char* truefalseautoopt[] = { "true", "false", "1", "0", "auto", 0};
01246     const char* pc98fmboards[] = { "auto", "off", "false", "board14", "board26k", "board86", "board86c", 0};
01247     const char* pc98videomodeopt[] = { "", "24khz", "31khz", "15khz", 0};
01248     const char* aspectmodes[] = { "false", "true", "0", "1", "yes", "no", "nearest", "bilinear", 0};
01249     const char *vga_ac_mapping_settings[] = { "", "auto", "4x4", "4low", "first16", 0 };
01250 
01251     const char* irqhandler[] = {
01252         "", "simple", "cooperative_2nd", 0 };
01253 
01254     /* Setup all the different modules making up DOSBox-X */
01255     const char* machines[] = {
01256         "hercules", "cga", "cga_mono", "cga_rgb", "cga_composite", "cga_composite2", "tandy", "pcjr", "ega",
01257         "vgaonly", "svga_s3", "svga_et3000", "svga_et4000",
01258         "svga_paradise", "vesa_nolfb", "vesa_oldvbe", "amstrad", "pc98", "pc9801", "pc9821",
01259 
01260         "fm_towns", // STUB
01261 
01262         "mcga", "mda",
01263 
01264         0 };
01265 
01266     const char* scalers[] = { 
01267         "none", "normal2x", "normal3x", "normal4x", "normal5x",
01268 #if RENDER_USE_ADVANCED_SCALERS>2
01269         "advmame2x", "advmame3x", "advinterp2x", "advinterp3x", "hq2x", "hq3x", "2xsai", "super2xsai", "supereagle",
01270 #endif
01271 #if RENDER_USE_ADVANCED_SCALERS>0
01272         "tv2x", "tv3x", "rgb2x", "rgb3x", "scan2x", "scan3x", "gray", "gray2x",
01273 #endif
01274         "hardware_none", "hardware2x", "hardware3x", "hardware4x", "hardware5x",
01275 #if C_XBRZ
01276         "xbrz", "xbrz_bilinear",
01277 #endif
01278         0 };
01279 
01280     const char* cores[] = { "auto",
01281 #if (C_DYNAMIC_X86) || (C_DYNREC)
01282         "dynamic",
01283 #endif
01284         "normal", "full", "simple", 0 };
01285 
01286     const char* voodoo_settings[] = {
01287         "false",
01288         "software",
01289 #if C_OPENGL
01290         "opengl",
01291 #endif
01292         "auto",
01293         0
01294     };
01295 
01296 #if defined(__SSE__) && !(defined(_M_AMD64) || defined(__e2k__)) && !defined(EMSCRIPTEN)
01297     CheckSSESupport();
01298 #endif
01299     SDLNetInited = false;
01300 
01301     secprop=control->AddSection_prop("dosbox",&Null_Init);
01302     Pstring = secprop->Add_path("language",Property::Changeable::Always,"");
01303     Pstring->Set_help("Select another language file.");
01304 
01305     Pstring = secprop->Add_path("title",Property::Changeable::Always,"");
01306     Pstring->Set_help("Additional text to place in the title bar of the window");
01307 
01308     Pbool = secprop->Add_bool("enable 8-bit dac",Property::Changeable::OnlyAtStart,true);
01309     Pbool->Set_help("If set, allow VESA BIOS calls in IBM PC mode to set DAC width. Has no effect in PC-98 mode.");
01310 
01311     Pstring = secprop->Add_string("dpi aware",Property::Changeable::OnlyAtStart,"auto");
01312     Pstring->Set_values(truefalseautoopt);
01313     Pstring->Set_help("Set this option (auto by default) to indicate to your OS that DOSBox-X is DPI aware.\n"
01314             "If it is not set, Windows Vista/7/8/10 and higher may upscale the DOSBox-X window\n"
01315             "on higher resolution monitors which is probably not what you want.");
01316 
01317     Pbool = secprop->Add_bool("keyboard hook", Property::Changeable::Always, false);
01318     Pbool->Set_help("Use keyboard hook (currently only on Windows) to catch special keys and synchronize the keyboard LEDs with the host");
01319 
01320     // STUB OPTION, NOT YET FULLY IMPLEMENTED
01321     Pbool = secprop->Add_bool("weitek",Property::Changeable::WhenIdle,false);
01322     Pbool->Set_help("If set, emulate the Weitek coprocessor. This option only has effect if cputype=386 or cputype=486.");
01323 
01324     Pbool = secprop->Add_bool("bochs debug port e9",Property::Changeable::WhenIdle,false);
01325     Pbool->Set_help("If set, emulate Bochs debug port E9h. ASCII text written to this I/O port is assumed to be debug output, and logged.");
01326 
01327     Pstring = secprop->Add_string("machine",Property::Changeable::OnlyAtStart,"svga_s3");
01328     Pstring->Set_values(machines);
01329     Pstring->Set_help("The type of machine DOSBox-X tries to emulate.");
01330 
01331     Phex = secprop->Add_hex("svga lfb base", Property::Changeable::OnlyAtStart, 0);
01332     Phex->Set_help("If nonzero, define the physical memory address of the linear framebuffer.");
01333 
01334     Pbool = secprop->Add_bool("pci vga",Property::Changeable::WhenIdle,true);
01335     Pbool->Set_help("If set, SVGA is emulated as if a PCI device (when enable pci bus=true)");
01336 
01337     Pint = secprop->Add_int("vmemdelay", Property::Changeable::WhenIdle,0);
01338     Pint->SetMinMax(-1,100000);
01339     Pint->Set_help( "VGA Memory I/O delay in nanoseconds. Set to -1 to use default, 0 to disable.\n"
01340             "Default off. Enable this option (-1 or nonzero) if you are running a game or\n"
01341             "demo that needs slower VGA memory (like that of older ISA hardware) to work properly.\n"
01342             "If your game is not sensitive to VGA RAM I/O speed, then turning on this option\n"
01343             "will do nothing but cause a significant drop in frame rate which is probably not\n"
01344             "what you want. Recommended values -1, 0 to 2000.");
01345 
01346     Pint = secprop->Add_int("vmemsize", Property::Changeable::WhenIdle,-1);
01347     Pint->SetMinMax(-1,16);
01348     Pint->Set_help(
01349         "Amount of video memory in megabytes.\n"
01350         "  The maximum resolution and color depth the svga_s3 will be able to display\n"
01351         "  is determined by this value.\n "
01352         " -1: auto (vmemsizekb is ignored)\n"
01353         "  0: 512k (800x600  at 256 colors) if vmemsizekb=0\n"
01354         "  1: 1024x768  at 256 colors or 800x600  at 64k colors\n"
01355         "  2: 1600x1200 at 256 colors or 1024x768 at 64k colors or 640x480 at 16M colors\n"
01356         "  4: 1600x1200 at 64k colors or 1024x768 at 16M colors\n"
01357         "  8: up to 1600x1200 at 16M colors\n"
01358         "For build engine games, use more memory than in the list above so it can\n"
01359         "use triple buffering and thus won't flicker.\n"
01360         );
01361 
01362     Pint = secprop->Add_int("vmemsizekb", Property::Changeable::WhenIdle,0);
01363     Pint->SetMinMax(0,1024);
01364     Pint->Set_help(
01365         "Amount of video memory in kilobytes, in addition to vmemsize");
01366 
01367     Pstring = secprop->Add_path("captures",Property::Changeable::Always,"capture");
01368     Pstring->Set_help("Directory where things like wave, midi, screenshot get captured.");
01369 
01370     /* will change to default true unless this causes compatibility issues with other users or their editing software */
01371     Pbool = secprop->Add_bool("skip encoding unchanged frames",Property::Changeable::WhenIdle,false);
01372     Pbool->Set_help("Unchanged frames will not be sent to the video codec as a possible performance and bandwidth optimization.");
01373 
01374     Pstring = secprop->Add_string("capture chroma format", Property::Changeable::OnlyAtStart,"auto");
01375     Pstring->Set_values(capturechromaformats);
01376     Pstring->Set_help("Chroma format to use when capturing to H.264. 'auto' picks the best quality option.\n"
01377             "4:4:4       Chroma is at full resolution. This provides the best quality, however not widely supported by editing software.\n"
01378             "4:2:2       Chroma is at half horizontal resolution.\n"
01379             "4:2:0       Chroma is at quarter resolution, which may cause minor color smearing.\n"
01380             "            However, this chroma format is most likely to be compatible with video editing software.");
01381 
01382     Pstring = secprop->Add_string("capture format", Property::Changeable::OnlyAtStart,"default");
01383     Pstring->Set_values(captureformats);
01384     Pstring->Set_help("Capture format to use when capturing video. The availability of the format depends on how DOSBox-X was compiled.\n"
01385             "default                     Use compiled-in default (avi-zmbv)\n"
01386             "avi-zmbv                    Use DOSBox-style AVI + ZMBV codec with PCM audio\n"
01387             "mpegts-h264                 Use MPEG transport stream + H.264 + AAC audio. Resolution & refresh rate changes can be contained\n"
01388             "                            within one file with this choice, however not all software can support mid-stream format changes.");
01389 
01390     Pint = secprop->Add_int("shell environment size",Property::Changeable::OnlyAtStart,0);
01391     Pint->SetMinMax(0,65280);
01392     Pint->Set_help("Size of the initial DOSBox-X shell environment block, in bytes. This does not affect the environment block of sub-processes spawned from the shell.\n"
01393             "This option has no effect unless dynamic kernel allocation is enabled.");
01394 
01395     Pint = secprop->Add_int("private area size",Property::Changeable::OnlyAtStart,32768); // DOSBox mainline compatible 32KB region
01396     Pint->SetMinMax(16,128*1024);
01397     Pint->Set_help("Set DOSBox-X private memory area size. This area contains private memory structures used by the DOS kernel.\n"
01398             "It is discarded when you boot into another OS. Mainline DOSBox uses 32KB. Testing shows that it is possible\n"
01399             "to run DOSBox with as little as 4KB. If DOSBox-X aborts with error \"not enough memory for internal tables\"\n"
01400             "then you need to increase this value.");
01401 
01402     // NOTE: This will be revised as I test the DOSLIB code against more VGA/SVGA hardware!
01403     Pstring = secprop->Add_string("vga attribute controller mapping",Property::Changeable::WhenIdle,"auto");
01404     Pstring->Set_values(vga_ac_mapping_settings);
01405     Pstring->Set_help(
01406             "This affects how the attribute controller maps colors, especially in 256-color mode.\n"
01407             "Some SVGA cards handle the attribute controller palette differently than most SVGA cards.\n"
01408             "  auto                         Automatically pick the mapping based on the SVGA chipset.\n"
01409             "  4x4                          Split into two 4-bit nibbles, map through AC, recombine. This is standard VGA behavior including clone SVGA cards.\n"
01410             "  4low                         Split into two 4-bit nibbles, remap only the low 4 bits, recombine. This is standard ET4000 behavior.\n"
01411             "\n"
01412             "NOTES:\n"
01413             "  Demoscene executable 'COPPER.EXE' requires the '4low' behavior in order to display line-fading effects\n"
01414             "  (including scrolling credits) correctly, else those parts of the demo show up as a blank screen.\n"
01415             "  \n"
01416             "  4low behavior is default for ET4000 emulation.");
01417 
01418     Pstring = secprop->Add_string("a20",Property::Changeable::WhenIdle,"mask");
01419     Pstring->Set_help("A20 gate emulation mode.\n"
01420               "The on/off/on_fake/off_fake options are intended for testing and debugging DOS development,\n"
01421               "or to emulate obscure hardware, or to work around potential extended memory problems with DOS programs.\n"
01422               "on_fake/off_fake are intended to test whether a program carries out a memory test to ensure the A20\n"
01423               "gate is set as intended (as HIMEM.SYS does). If it goes by the gate bit alone, it WILL crash.\n"
01424               "This parameter is also changeable from the builtin A20GATE command.\n"
01425               "  fast                         Emulate A20 gating by remapping the first 64KB @ 1MB boundary (fast, mainline DOSBox behavior)\n"
01426               "  mask                         Emulate A20 gating by masking memory I/O address (accurate)\n"
01427               "  off                          Lock A20 gate off (Software/OS cannot enable A20)\n"
01428               "  on                           Lock A20 gate on (Software/OS cannot disable A20)\n"
01429               "  off_fake                     Lock A20 gate off but allow bit to toggle (hope your DOS game tests the HMA!)\n"
01430               "  on_fake                      Lock A20 gate on but allow bit to toggle");
01431 
01432     Pbool = secprop->Add_bool("turn off a20 gate on boot",Property::Changeable::WhenIdle,true);
01433     Pbool->Set_help("If enabled, A20 gate is switched off when booting a guest OS.\n"
01434                     "Enabled by default. Recommended for MS-DOS when HIMEM.SYS is not installed in the guest OS.\n"
01435                     "If disabled, and MS-DOS does not load HIMEM.SYS, programs and features that rely on the 1MB wraparound will fail.");
01436 
01437     /* Ref:
01438      *
01439      * "Except the first generation, which C-Bus was synchronous with its 5MHz 8086, PC-98s
01440      *  before the age of SuperIO and PCI use either 10MHz (9.8304MHz) or 8MHz (7.9872MHz)
01441      *  for its C-Bus.
01442      * 
01443      *  It's determined by the CPU clock base (2.4756Mhz or 1.9968MHz). For example, on a
01444      *  16MHz 386, C-Bus runs at 8MHz and on a 25MHz 386, C-Bus runs at 10MHz.
01445      *
01446      *  After NEC brought SuperIO and PCI to PC-98, C-Bus clock no longer ties to the CPU
01447      *  oscillator and got fixed to 10MHz." -Yksoft1
01448      *
01449      * Assuming this is true, the selection is given below */
01450 
01451     Pstring = secprop->Add_string("cbus bus clock",Property::Changeable::WhenIdle,"std10");
01452     Pstring->Set_help("C-BUS BCLK frequency (PC-98), used to emulate I/O delay.\n"
01453               "WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime.\n"
01454               "  std10                        10MHz (CPU speed multiple of 5MHz or PCI-based)\n"
01455               "  std8                         8MHz (CPU speed multiple of 4MHz)\n"
01456               "  std5                         5MHz (older PC-9801)\n"
01457               "  <integer or float>           Any integer or floating point value will be used as the clock frequency in Hz\n"
01458               "  <integer/integer ratio>      If a ratio is given (num/den), the ratio will be used as the clock frequency");
01459 
01460     Pstring = secprop->Add_string("isa bus clock",Property::Changeable::WhenIdle,"std8.3");
01461     Pstring->Set_help("ISA BCLK frequency, used to emulate I/O delay.\n"
01462               "WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime.\n"
01463               "  std8.3                       8.333MHz (typical 386-class or higher)\n"
01464               "  std8                         8MHz\n"
01465               "  std6                         6MHz\n"
01466               "  std4.77                      4.77MHz (precisely 1/3 x 14.31818MHz). Bus frequency of older PC/XT systems.\n"
01467               "  oc10                         10MHz\n"
01468               "  oc12                         12MHz\n"
01469               "  oc15                         15MHz\n"
01470               "  oc16                         16MHz\n"
01471               "  <integer or float>           Any integer or floating point value will be used as the clock frequency in Hz\n"
01472               "  <integer/integer ratio>      If a ratio is given (num/den), the ratio will be used as the clock frequency");
01473 
01474     Pstring = secprop->Add_string("pci bus clock",Property::Changeable::WhenIdle,"std33.3");
01475     Pstring->Set_help("PCI bus frequency, used to emulate I/O delay.\n"
01476               "WARNING: In future revisions, PCI/motherboard chipset emulation will allow the guest OS/program to alter this value at runtime.\n"
01477               "  std33.3                      33.333MHz (very common setting on motherboards)\n"
01478               "  std30                        30MHz (some older mid-1990's Pentium systems)\n"
01479               "  std25                        25MHz\n"
01480               "  <integer or float>           Any integer or floating point value will be used as the clock frequency in Hz\n"
01481               "  <integer/integer ratio>      If a ratio is given (num/den), the ratio will be used as the clock frequency");
01482 
01483     Pstring = secprop->Add_string("call binary on reset",Property::Changeable::WhenIdle,"");
01484     Pstring->Set_help("If set, this is the path of a binary blob to load into the ROM BIOS area and execute immediately after CPU reset.\n"
01485                       "It will be executed before the BIOS POST routine, only ONCE. The binary blob is expected either to IRET or to\n"
01486                       "jump directly to F000:FFF0 to return control to the BIOS.\n"
01487                       "This can be used for x86 assembly language experiments and automated testing against the CPU emulation.");
01488 
01489     Pstring = secprop->Add_string("unhandled irq handler",Property::Changeable::WhenIdle,"");
01490     Pstring->Set_values(irqhandler);
01491     Pstring->Set_help("Determines how unhandled IRQs are handled. This may help some errant DOS applications.\n"
01492                       "Leave unset for default behavior (simple).\n"
01493                       "simple               Acknowledge the IRQ, and the master (if slave IRQ)\n"
01494                       "mask_isr             Acknowledge IRQs in service on master and slave and mask IRQs still in service, to deal with errant handlers (em-dosbox method)");
01495 
01496     Pstring = secprop->Add_string("call binary on boot",Property::Changeable::WhenIdle,"");
01497     Pstring->Set_help("If set, this is the path of a binary blob to load into the ROM BIOS area and execute immediately before booting the DOS system.\n"
01498                       "This can be used for x86 assembly language experiments and automated testing against the CPU emulation.");
01499 
01500     Pint = secprop->Add_int("rom bios allocation max",Property::Changeable::OnlyAtStart,0);
01501     Pint->SetMinMax(0,128);
01502     Pint->Set_help("Maximum size (top down from 1MB) allowed for ROM BIOS dynamic allocation in KB");
01503 
01504     Pint = secprop->Add_int("rom bios minimum size",Property::Changeable::OnlyAtStart,0);
01505     Pint->SetMinMax(0,128);
01506     Pint->Set_help("Once ROM BIOS layout is finalized, trim total region down to a minimum amount in KB");
01507 
01508     Pint = secprop->Add_int("irq delay ns", Property::Changeable::WhenIdle,-1);
01509     Pint->SetMinMax(-1,100000);
01510     Pint->Set_help( "IRQ delay in nanoseconds. Set to -1 to use default, 0 to disable.\n"
01511                     "This is a more precise version of the irqdelay= setting.\n"
01512                     "There are some old DOS games and demos that have race conditions with IRQs that need a nonzero value here to work properly.");
01513 
01514     Pint = secprop->Add_int("iodelay", Property::Changeable::WhenIdle,-1);
01515     Pint->SetMinMax(-1,100000);
01516     Pint->Set_help( "I/O delay in nanoseconds for I/O port access. Set to -1 to use default, 0 to disable.\n"
01517             "A value of 1000 (1us) is recommended for ISA bus type delays. If your game\n"
01518             "or demo is not sensitive to I/O port and ISA bus timing, you can turn this option off\n"
01519             "(set to 0) to increase game performance.");
01520 
01521     Pint = secprop->Add_int("iodelay16", Property::Changeable::WhenIdle,-1);
01522     Pint->SetMinMax(-1,100000);
01523     Pint->Set_help( "I/O delay for 16-bit transfers. -1 to use default, 0 to disable.");
01524 
01525     Pint = secprop->Add_int("iodelay32", Property::Changeable::WhenIdle,-1);
01526     Pint->SetMinMax(-1,100000);
01527     Pint->Set_help( "I/O delay for 32-bit transfers. -1 to use default, 0 to disable.");
01528 
01529     // STUB OPTION, NOT YET FULLY IMPLEMENTED
01530     Pstring = secprop->Add_string("acpi", Property::Changeable::OnlyAtStart,"off");
01531     Pstring->Set_values(acpisettings);
01532     Pstring->Set_help("ACPI emulation, and what version of the specification to follow.\n"
01533             "WARNING: This option is very experimental at this time and should not be enabled unless you're willing to accept the consequences.\n"
01534             "         Intended for use with ACPI-aware OSes including Linux and Windows 98/ME. This option will also slightly reduce available\n"
01535             "         system memory to make room for the ACPI tables, just as real BIOSes do, and reserve an IRQ for ACPI functions.");
01536 
01537     // STUB OPTION, NOT YET FULLY IMPLEMENTED
01538     Pstring = secprop->Add_string("acpi rsd ptr location", Property::Changeable::OnlyAtStart,"auto");
01539     Pstring->Set_values(acpi_rsd_ptr_settings);
01540     Pstring->Set_help("Where to store the Root System Description Pointer structure. You can have it stored in the ROM BIOS area, or the Extended Bios Data Area.");
01541 
01542     // STUB OPTION, NOT YET FULLY IMPLEMENTED
01543     Pint = secprop->Add_int("acpi sci irq", Property::Changeable::WhenIdle,-1);
01544     Pint->Set_help("IRQ to assign as ACPI system control interrupt. set to -1 to automatically assign.");
01545 
01546     // STUB OPTION, NOT YET FULLY IMPLEMENTED
01547     Phex = secprop->Add_hex("acpi iobase",Property::Changeable::WhenIdle,0);
01548     Phex->Set_help("I/O port base for the ACPI device Power Management registers. Set to 0 for automatic assignment.");
01549 
01550     // STUB OPTION, NOT YET FULLY IMPLEMENTED
01551     Pint = secprop->Add_int("acpi reserved size", Property::Changeable::WhenIdle,0);
01552     Pint->Set_help("Amount of memory at top to reserve for ACPI structures and tables. Set to 0 for automatic assignment.");
01553 
01554 #if defined(C_EMSCRIPTEN)
01555     Pint = secprop->Add_int("memsize", Property::Changeable::WhenIdle,4);
01556 #else
01557     Pint = secprop->Add_int("memsize", Property::Changeable::WhenIdle,16);
01558 #endif
01559     Pint->SetMinMax(1,3584); // 3.5GB
01560     Pint->Set_help(
01561         "Amount of memory DOSBox-X has in megabytes.\n"
01562         "This value is best left at its default to avoid problems with some games,\n"
01563         "although other games and applications may require a higher value.\n"
01564         "Programs that use 286 protected mode like Windows 3.0 in Standard Mode may crash with more than 15MB.");
01565 
01566     Pint = secprop->Add_int("memsizekb", Property::Changeable::WhenIdle,0);
01567     Pint->SetMinMax(0,524288);
01568     Pint->Set_help(
01569         "Amount of memory DOSBox-X has in kilobytes.\n"
01570         "This value should normally be set to 0.\n"
01571         "If nonzero, it is added to the memsize parameter.\n"
01572         "Finer grained control of total memory may be useful in\n"
01573         "emulating ancient DOS machines with less than 640KB of\n"
01574         "RAM or early 386 systems with odd extended memory sizes.");
01575 
01576     Pint = secprop->Add_int("dos mem limit", Property::Changeable::WhenIdle,0);
01577     Pint->SetMinMax(0,1023);
01578     Pint->Set_help( "Limit DOS conventional memory to this amount. Does not affect extended memory.\n"
01579             "Setting this option to a value in the range 636-639 can be used to simulate modern BIOSes\n"
01580             "that maintain an EBDA (Extended BIOS Data Area) at the top of conventional memory.\n"
01581             "You may also play with this option for diagnostic purposes or to stress test DOS programs in limited memory setups.\n"
01582             "\n"
01583             "A few DOS games & demos require this option to be set:\n"
01584             "     Majic 12 \"Show\": If UMBs are enabled, set this option to 639 to avoid MCB chain corruption error.");
01585 
01586     Pbool = secprop->Add_bool("isa memory hole at 512kb",Property::Changeable::WhenIdle,false);
01587     Pbool->Set_help("If set, emulate an ISA memory hole at the 512KB to 640KB area (0x80000-0x9FFFF).");
01588 
01589     Pint = secprop->Add_int("reboot delay", Property::Changeable::WhenIdle,-1);
01590     Pint->SetMinMax(-1,10000);
01591     Pint->Set_help(
01592         "Reboot delay. How long to pause at BIOS POST after reboot in milliseconds.\n"
01593         "This option is provided so that it is possible to see what the guest application\n"
01594         "or OS might have written to the screen before resetting the system. A value of\n"
01595         "-1 means to use a reasonable default.");
01596 
01597     Pint = secprop->Add_int("memalias", Property::Changeable::WhenIdle,0);
01598     Pint->SetMinMax(0,32);
01599     Pint->Set_help(
01600         "Memory aliasing emulation, in number of valid address bits.\n"
01601         "Many 386/486 class motherboards and processors prior to 1995\n"
01602         "suffered from memory aliasing for various technical reasons. If the software you are\n"
01603         "trying to run assumes aliasing, or otherwise plays cheap tricks with paging,\n"
01604         "enabling this option can help. Note that enabling this option can cause slight performance degredation. Set to 0 to disable.\n"
01605         "Recommended values when enabled:\n"
01606         "    24: 16MB aliasing. Common on 386SX systems (CPU had 24 external address bits)\n"
01607         "        or 386DX and 486 systems where the CPU communicated directly with the ISA bus (A24-A31 tied off)\n"
01608         "    26: 64MB aliasing. Some 486s had only 26 external address bits, some motherboards tied off A26-A31");
01609 
01610     Pbool = secprop->Add_bool("nocachedir",Property::Changeable::WhenIdle,false);
01611     Pbool->Set_help("If set, MOUNT commands will mount with -nocachedir by default.");
01612 
01613     Pbool = secprop->Add_bool("freesizecap",Property::Changeable::WhenIdle,true);
01614     Pbool->Set_help("If set, the value of MOUNT -freesize will be applied only if the actual free size is greater than the specified value.");
01615 
01616     Pbool = secprop->Add_bool("leading colon write protect image",Property::Changeable::WhenIdle,true);
01617     Pbool->Set_help("If set, BOOT and IMGMOUNT commands will put an image file name with a leading colon (:) in write-protect mode.");
01618 
01619     Pint = secprop->Add_int("vga bios size override", Property::Changeable::WhenIdle,0);
01620     Pint->SetMinMax(512,65536);
01621     Pint->Set_help("VGA BIOS size override. Override the size of the VGA BIOS (normally 32KB in compatible or 12KB in non-compatible).");
01622 
01623     Pbool = secprop->Add_bool("video bios dont duplicate cga first half rom font",Property::Changeable::WhenIdle,false);
01624     Pbool->Set_help("If set, save 4KB of EGA/VGA ROM space by pointing to the copy in the ROM BIOS of the first 128 chars");
01625 
01626     Pbool = secprop->Add_bool("video bios always offer 14-pixel high rom font",Property::Changeable::WhenIdle,false);
01627     Pbool->Set_help("If set, video BIOS will always carry the 14-pixel ROM font. If clear, 14-pixel rom font will not be offered except for EGA/VGA emulation.");
01628 
01629     Pbool = secprop->Add_bool("video bios always offer 16-pixel high rom font",Property::Changeable::WhenIdle,false);
01630     Pbool->Set_help("If set, video BIOS will always carry the 16-pixel ROM font. If clear, 16-pixel rom font will not be offered except for VGA emulation.");
01631 
01632     Pbool = secprop->Add_bool("video bios enable cga second half rom font",Property::Changeable::WhenIdle,true);
01633     Pbool->Set_help("If set, and emulating CGA/PCjr/Tandy, automatically provide the second half of the 8x8 ROM font.\n"
01634             "This setting is ignored for EGA/VGA emulation. If not set, you will need a utility like GRAFTABL.COM to load the second half of the ROM font for graphics.\n"
01635             "NOTE: if you disable the 14 & 16 pixel high font AND the second half when machine=cga, you will disable video bios completely.");
01636 
01637     Pstring = secprop->Add_string("forcerate",Property::Changeable::Always,"");
01638     Pstring->Set_help("Force the VGA framerate to a specific value(ntsc, pal, or specific hz), no matter what");
01639 
01640     Pbool = secprop->Add_bool("sierra ramdac",Property::Changeable::WhenIdle,true);
01641     Pbool->Set_help("Whether or not to emulate a Sierra or compatible RAMDAC at port 3C6h-3C9h.\n"
01642             "Some DOS games expect to access port 3C6h to enable highcolor/truecolor SVGA modes on older chipsets.\n"
01643             "Disable if you wish to emulate SVGA hardware that lacks a RAMDAC or (depending on the chipset) does\n"
01644             "not emulate a RAMDAC that is accessible through port 3C6h. This option has no effect for non-VGA video hardware.");
01645 
01646     Pbool = secprop->Add_bool("sierra ramdac lock 565",Property::Changeable::WhenIdle,false);
01647     Pbool->Set_help("When emulating High Sierra highcolor RAMDAC, assume 5:6:5 at all times if set. Else,\n"
01648             "bit 6 of the DAC command selects between 5:5:5 and 5:6:5. Set this option for demos or\n"
01649             "games that got the command byte wrong (MFX Transgrassion 2) or any other demo that is\n"
01650             "not rendering highcolor 16bpp correctly.");
01651 
01652     Pbool = secprop->Add_bool("page flip debug line",Property::Changeable::Always,false);
01653     Pbool->Set_help("VGA debugging switch. If set, an inverse line will be drawn on the exact scanline that the CRTC display offset registers were written.\n"
01654             "This can be used to help diagnose whether or not the DOS game is page flipping properly according to vertical retrace if the display on-screen is flickering.");
01655 
01656     Pbool = secprop->Add_bool("vertical retrace poll debug line",Property::Changeable::Always,false);
01657     Pbool->Set_help("VGA debugging switch. If set, an inverse green dotted line will be drawn on the exact scanline that the CRTC status port (0x3DA) was read.\n"
01658             "This can be used to help diagnose whether the DOS game is propertly waiting for vertical retrace.");
01659 
01660     Pbool = secprop->Add_bool("cgasnow",Property::Changeable::WhenIdle,true);
01661     Pbool->Set_help("When machine=cga, determines whether or not to emulate CGA snow in 80x25 text mode");
01662 
01663     /* Default changed to 0x04 for "Blues Brothers" at Allofich's request [https://github.com/joncampbell123/dosbox-x/issues/1273] */
01664     Phex = secprop->Add_hex("vga 3da undefined bits",Property::Changeable::WhenIdle,0x04);
01665     Phex->Set_help("VGA status port 3BA/3DAh only defines bits 0 and 3. This setting allows you to assign a bit pattern to the undefined bits.\n"
01666                    "The purpose of this hack is to deal with demos that read and handle port 3DAh in ways that might crash if all are zero.");
01667 
01668     Pbool = secprop->Add_bool("unmask timer on int 10 setmode",Property::Changeable::OnlyAtStart,false);
01669     Pbool->Set_help("If set, INT 10h will unmask IRQ 0 (timer) when setting video modes.");
01670 
01671     Pbool = secprop->Add_bool("unmask keyboard on int 16 read",Property::Changeable::OnlyAtStart,true);
01672     Pbool->Set_help("If set, INT 16h will unmask IRQ 1 (keyboard) when asked to read keyboard input.\n"
01673                     "It is strongly recommended that you set this option if running Windows 3.11 Windows for Workgroups in DOSBox-X.");
01674 
01675     Pbool = secprop->Add_bool("int16 keyboard polling undocumented cf behavior",Property::Changeable::OnlyAtStart,false);
01676     Pbool->Set_help("If set, INT 16h function AH=01h will also set/clear the carry flag depending on whether input was available.\n"
01677                     "There are some old DOS games and demos that rely on this behavior to sense keyboard input, and this behavior\n"
01678                     "has been verified to occur on some old (early 90s) BIOSes.");
01679 
01680     Pbool = secprop->Add_bool("allow port 92 reset",Property::Changeable::OnlyAtStart,true);
01681     Pbool->Set_help("If set (default), allow the application to reset the CPU through port 92h");
01682 
01683     Pbool = secprop->Add_bool("enable port 92",Property::Changeable::WhenIdle,true);
01684     Pbool->Set_help("Emulate port 92h (PS/2 system control port A). If you want to emulate a system that pre-dates the PS/2, set to 0.");
01685 
01686     Pbool = secprop->Add_bool("enable 1st dma controller",Property::Changeable::WhenIdle,true);
01687     Pbool->Set_help("Emulate 1st (AT) DMA controller (default). Set to 0 if you wish to emulate a system that lacks DMA (PCjr and some Tandy systems)");
01688 
01689     Pbool = secprop->Add_bool("enable 2nd dma controller",Property::Changeable::WhenIdle,true);
01690     Pbool->Set_help("Emulate 2nd (AT) DMA controller (default). Set to 0 if you wish to emulate a PC/XT system without 16-bit DMA.\n"
01691             "Note: mainline DOSBox automatically disables 16-bit DMA when machine=cga or machine=hercules, while DOSBox-X does not.");
01692 
01693     Pbool = secprop->Add_bool("allow dma address decrement",Property::Changeable::WhenIdle,true);
01694     Pbool->Set_help("If set, allow increment & decrement modes as specified in the 8237 datasheet.\n"
01695             "If clear, always increment the address (as if to emulate clone 8237 implementations that skipped the inc/dec bit).");
01696 
01697     Pstring = secprop->Add_string("enable 128k capable 16-bit dma", Property::Changeable::OnlyAtStart,"auto");
01698     Pstring->Set_values(truefalseautoopt);
01699     Pstring->Set_help("If true, DMA controller emulation models ISA hardware that permits 16-bit DMA to span 128KB.\n"
01700                     "If false, DMA controller emulation models PCI hardware that limits 16-bit DMA to 64KB boundaries.\n"
01701                     "If auto, the choice is made according to other factors in hardware emulation");
01702 
01703     Pbool = secprop->Add_bool("enable dma extra page registers",Property::Changeable::WhenIdle,true);
01704     Pbool->Set_help("If set, emulate the extra page registers (I/O ports 0x80, 0x84-0x86, 0x88, 0x8C-0x8E), like actual hardware.\n"
01705             "Note that mainline DOSBox behavior is to NOT emulate these registers.");
01706 
01707     Pbool = secprop->Add_bool("dma page registers write-only",Property::Changeable::WhenIdle,false);
01708     Pbool->Set_help("Normally (on AT hardware) the DMA page registers are read/write. Set this option if you want to emulate PC/XT hardware where the page registers are write-only.");
01709 
01710     Pbool = secprop->Add_bool("cascade interrupt never in service",Property::Changeable::WhenIdle,false);
01711     Pbool->Set_help("If set, PIC emulation will never mark cascade interrupt as in service. This is OFF by default. It is a hack for troublesome games.");
01712 
01713     // TODO: "Special mode" which apparently triggers this alternate behavior and used by default on PC-98, is configurable
01714     //       by software through the PIC control words, and should control this setting if this is "auto".
01715     //       It's time for "auto" default setting to end once and for all the running gag that PC-98 games will not run
01716     //       properly without having to add "cascade interrupt ignore in service=true" to your dosbox-x.conf all the time.
01717     Pstring = secprop->Add_string("cascade interrupt ignore in service",Property::Changeable::WhenIdle,"auto");
01718     Pstring->Set_values(truefalseautoopt);
01719     Pstring->Set_help("If true, PIC emulation will allow slave pic interrupts even if the cascade interrupt is still \"in service\" (common PC-98 behavior)\n"
01720                     "If false, PIC emulation will consider cascade in-service state when deciding which interrupt to signal (common IBM PC behavior)\n"
01721                     "If auto, setting is chosen based on machine type and other configuration.");
01722 
01723     Pbool = secprop->Add_bool("enable slave pic",Property::Changeable::WhenIdle,true);
01724     Pbool->Set_help("Enable slave PIC (IRQ 8-15). Set this to 0 if you want to emulate a PC/XT type arrangement with IRQ 0-7 and no IRQ 2 cascade.");
01725 
01726     Pbool = secprop->Add_bool("enable pc nmi mask",Property::Changeable::WhenIdle,true);
01727     Pbool->Set_help("Enable PC/XT style NMI mask register (0xA0). Note that this option conflicts with the secondary PIC and will be ignored if the slave PIC is enabled.");
01728 
01729     Pbool = secprop->Add_bool("rom bios 8x8 CGA font",Property::Changeable::Always,true);
01730     Pbool->Set_help("If set, or mainline DOSBox compatible BIOS mapping, a legacy 8x8 CGA font (first 128 characters) is stored at 0xF000:0xFA6E. DOS programs that do not use INT 10h to locate fonts might require that font to be located there.");
01731 
01732     Pbool = secprop->Add_bool("rom bios video parameter table",Property::Changeable::Always,true);
01733     Pbool->Set_help("If set, or mainline DOSBox compatible BIOS mapping, DOSBox-X will emulate the video parameter table and assign that to INT 1Dh. If clear, table will not be provided.");
01734 
01735     Pbool = secprop->Add_bool("allow more than 640kb base memory",Property::Changeable::Always,false);
01736     Pbool->Set_help("If set, and space is available, allow conventional memory to extend past 640KB.\n"
01737             "For example, if machine=cga, conventional memory can extend out to 0xB800 and provide up to 736KB of RAM.\n"
01738             "This allows you to emulate PC/XT style memory extensions.");
01739 
01740     Pbool = secprop->Add_bool("int 10h points at vga bios",Property::Changeable::Always,true);
01741     Pbool->Set_help("If set, INT 10h points at the VGA BIOS. If clear, INT 10h points into the system BIOS. This option only affects EGA/VGA/SVGA emulation.\n"
01742                     "This option is needed for some older DOS applications that make additional checks before detecting EGA/VGA hardware (SuperCalc).");
01743 
01744     Pbool = secprop->Add_bool("vesa zero buffer on get information",Property::Changeable::Always,true);
01745     Pbool->Set_help("This setting affects VESA BIOS function INT 10h AX=4F00h. If set, the VESA BIOS will zero the\n"
01746                     "256-byte buffer defined by the standard at ES:DI, then fill in the structure. If clear, only\n"
01747                     "the structure members will be filled in, and memory outside the initial 20-32 bytes will remain\n"
01748                     "unmodified. This setting is ON by default. Some very early 1990s DOS games that support VESA\n"
01749                     "BIOS standards may need this setting turned OFF if the programmer did not provide enough space\n"
01750                     "for the entire 256 byte structure and the game crashes if it detects VESA BIOS extensions.\n"
01751                     "Needed for:\n"
01752                     "  GETSADAM.EXE");
01753 
01754     /* should be set to zero unless for very specific demos:
01755      *  - "Melvindale" by MFX (1996): Set this to 2, the nightmarish visual rendering code appears to draw 2 scanlines
01756      *    upward from the VESA linear framebuffer base we return, causing DOSBox to emit warnings about illegal read/writes
01757      *    from 0xBFFFF000-0xBFFFFFFF (just before the base of the framebuffer at 0xC0000000). It also has the effect of
01758      *    properly centering the picture on the screen. I suppose it's a miracle the demo didn't crash people's computers
01759      *    writing to undefined areas like that. */
01760     Pint = secprop->Add_int("vesa lfb base scanline adjust",Property::Changeable::WhenIdle,0);
01761     Pint->Set_help("If non-zero, the VESA BIOS will report the linear framebuffer offset by this many scanlines.\n"
01762             "This does not affect the linear framebuffer's location. It only affects the linear framebuffer\n"
01763             "location reported by the VESA BIOS. Set to nonzero for DOS games with sloppy VESA graphics pointer management.\n"
01764             "    MFX \"Melvindale\" (1996): Set this option to 2 to center the picture properly.");
01765 
01766     /* If set, all VESA BIOS modes map 128KB of video RAM at A0000-BFFFF even though VESA BIOS emulation
01767      * reports a 64KB window. Some demos like the 1996 Wired report
01768      * (ftp.scene.org/pub/parties/1995/wired95/misc/e-w95rep.zip) assume they can write past the window
01769      * by spilling into B0000 without bank switching. */
01770     Pbool = secprop->Add_bool("vesa map non-lfb modes to 128kb region",Property::Changeable::Always,false);
01771     Pbool->Set_help("If set, VESA BIOS SVGA modes will be set to map 128KB of video memory to A0000-BFFFF instead of\n"
01772                     "64KB at A0000-AFFFF. This does not affect the SVGA window size or granularity.\n"
01773                     "Some games or demoscene productions assume that they can render into the next SVGA window/bank\n"
01774                     "by writing to video memory beyond the current SVGA window address and will not appear correctly\n"
01775                     "without this option.");
01776 
01777     Pbool = secprop->Add_bool("allow hpel effects",Property::Changeable::Always,false);
01778     Pbool->Set_help("If set, allow the DOS demo or program to change the horizontal pel (panning) register per scanline.\n"
01779             "Some early DOS demos use this to create waving or sinus effects on the picture. Not very many VGA\n"
01780             "chipsets allow this, so far, only ATI chipsets are known to support this effect. Disabled by default.");
01781 
01782     Pbool = secprop->Add_bool("allow hretrace effects",Property::Changeable::Always,false);
01783     Pbool->Set_help("If set, allow the DOS demo or program to make the picture wavy by playing with the 'start horizontal"
01784             "retrace' register of the CRTC during the active picture. Some early DOS demos (Copper by Surprise!"
01785             "productions) need this option set for some demo effects to work. Disabled by default.");
01786 
01787     Pdouble = secprop->Add_double("hretrace effect weight",Property::Changeable::Always,4.0);
01788     Pdouble->Set_help("If emulating hretrace effects, this parameter adds 'weight' to the offset to smooth it out.\n"
01789             "the larger the number, the more averaging is applied. This is intended to emulate the inertia\n"
01790             "of the electron beam in a CRT monitor");
01791 
01792     Pint = secprop->Add_int("vesa modelist cap",Property::Changeable::Always,0);
01793     Pint->Set_help("IF nonzero, the VESA modelist is capped so that it contains no more than the specified number of video modes.");
01794 
01795     Pint = secprop->Add_int("vesa modelist width limit",Property::Changeable::Always,1280);
01796     Pint->Set_help("IF nonzero, VESA modes with horizontal resolution higher than the specified pixel count will not be listed.\n"
01797             "This is another way the modelist can be capped for DOS applications that have trouble with long modelists.");
01798 
01799     Pint = secprop->Add_int("vesa modelist height limit",Property::Changeable::Always,1024);
01800     Pint->Set_help("IF nonzero, VESA modes with vertical resolution higher than the specified pixel count will not be listed.\n"
01801             "This is another way the modelist can be capped for DOS applications that have trouble with long modelists.");
01802 
01803     Pbool = secprop->Add_bool("vesa vbe put modelist in vesa information",Property::Changeable::Always,false);
01804     Pbool->Set_help("If set, the VESA modelist is placed in the VESA information structure itself when the DOS application\n"
01805                     "queries information on the VESA BIOS. Setting this option may help with some games, though it limits\n"
01806                     "the mode list reported to the DOS application.");
01807 
01808     Pbool = secprop->Add_bool("vesa vbe 1.2 modes are 32bpp",Property::Changeable::Always,true);
01809     Pbool->Set_help("If set, truecolor (16M color) VESA BIOS modes in the 0x100-0x11F range are 32bpp. If clear, they are 24bpp.\n"
01810             "Some DOS games and demos assume one bit depth or the other and do not enumerate VESA BIOS modes, which is why this\n"
01811             "option exists.");
01812 
01813     Pbool = secprop->Add_bool("allow low resolution vesa modes",Property::Changeable::Always,true);
01814     Pbool->Set_help("If set, allow low resolution VESA modes (320x200x16/24/32bpp and so on). You could set this to false to simulate\n"
01815             "SVGA hardware with a BIOS that does not support the lowres modes for testing purposes.");
01816 
01817     Pbool = secprop->Add_bool("allow explicit 24bpp vesa modes",Property::Changeable::Always,false);
01818     Pbool->Set_help("If set, additional 24bpp modes are listed in the modelist regardless whether modes 0x100-0x11F are\n"
01819                     "configured to be 24bpp or 32bpp. Setting this option can provide the best testing and development\n"
01820                     "environment for new retro DOS code. If clear, 24bpp will only be available in the 0x100-0x11F range\n"
01821                     "if the \"vesa vbe 1.2 modes are 32bpp\" is false. Setting to false helps to emulate typical SVGA\n"
01822                     "hardware in which either 24bpp is supported, or 32bpp is supported, but not both. Disabled by default.");
01823 
01824     Pbool = secprop->Add_bool("allow high definition vesa modes",Property::Changeable::Always,false);
01825     Pbool->Set_help("If set, offer HD video modes in the VESA modelist (such as 1280x720 aka 720p or 1920x1080 aka 1080p).\n"
01826                     "This option also offers 4:3 versions (960x720 and 1440x1080) for DOS games that cannot properly handle\n"
01827                     "a 16:9 aspect ratio, and several other HD modes. The modes enabled by this option are still limited by the\n"
01828                     "width and height limits and available video memory.\n"
01829                     "This is unusual for VESA BIOSes to do and is disabled by default.");
01830 
01831     Pbool = secprop->Add_bool("allow unusual vesa modes",Property::Changeable::Always,false);
01832     Pbool->Set_help("If set, unusual (uncommon) modes are added to the list. The modes reflect uncommon resolutions\n"
01833                     "added by external drivers (UNIVBE), some VESA BIOSes, some laptop and netbook displays, and\n"
01834                     "some added by DOSBox-X for additional fun. Disabled by default.");
01835 
01836     Pbool = secprop->Add_bool("allow 32bpp vesa modes",Property::Changeable::Always,true);
01837     Pbool->Set_help("If the DOS game or demo has problems with 32bpp VESA modes, set to 'false'");
01838 
01839     Pbool = secprop->Add_bool("allow 24bpp vesa modes",Property::Changeable::Always,true);
01840     Pbool->Set_help("If the DOS game or demo has problems with 24bpp VESA modes, set to 'false'");
01841 
01842     Pbool = secprop->Add_bool("allow 16bpp vesa modes",Property::Changeable::Always,true);
01843     Pbool->Set_help("If the DOS game or demo has problems with 16bpp VESA modes, set to 'false'");
01844 
01845     Pbool = secprop->Add_bool("allow 15bpp vesa modes",Property::Changeable::Always,true);
01846     Pbool->Set_help("If the DOS game or demo has problems with 15bpp VESA modes, set to 'false'");
01847 
01848     Pbool = secprop->Add_bool("allow 8bpp vesa modes",Property::Changeable::Always,true);
01849     Pbool->Set_help("If the DOS game or demo has problems with 8bpp VESA modes, set to 'false'");
01850 
01851     Pbool = secprop->Add_bool("allow 4bpp vesa modes",Property::Changeable::Always,true);
01852     Pbool->Set_help("If the DOS game or demo has problems with 4bpp VESA modes, set to 'false'.\n"
01853                     "These modes have the same 16-color planar memory layout as standard VGA, but\n"
01854                     "at SVGA resolution.");
01855 
01856     Pbool = secprop->Add_bool("allow 4bpp packed vesa modes",Property::Changeable::Always,false);
01857     Pbool->Set_help("If the DOS game or demo has problems with 4bpp packed VESA modes, set to 'false'.\n"
01858                     "4bpp (16-color) packed is an unusual novelty mode only seen on specific Chips & Tech 65550\n"
01859                     "VESA BIOSes such as the one in a Toshiba Libretto laptop. Disabled by default.");
01860 
01861     Pbool = secprop->Add_bool("allow tty vesa modes",Property::Changeable::Always,true);
01862     Pbool->Set_help("If the DOS game or demo has problems with text VESA modes, set to 'false'");
01863 
01864     Pbool = secprop->Add_bool("double-buffered line compare",Property::Changeable::Always,false);
01865     Pbool->Set_help("This setting affects the VGA Line Compare register. Set to false (default value) to emulate most VGA behavior\n"
01866             "Set to true for the value to latch once at the start of the frame.");
01867 
01868     Pbool = secprop->Add_bool("ignore vblank wraparound",Property::Changeable::Always,false);
01869     Pbool->Set_help("DOSBox-X can handle active display properly if games or demos reprogram vertical blanking to end in the active picture area.\n"
01870             "If the wraparound handling prevents the game from displaying properly, set this to false. Out of bounds vblank values will be ignored.\n");
01871 
01872     Pbool = secprop->Add_bool("enable vga resize delay",Property::Changeable::Always,false);
01873     Pbool->Set_help("If the DOS game you are running relies on certain VGA raster tricks that affect active display area, enable this option.\n"
01874             "This adds a delay between VGA mode changes and window updates. It also means that if you are capturing a demo or game,\n"
01875             "that your capture will also show a few garbled frames at any point mode changes occur, which is why this option is disabled\n"
01876             "by default. If you intend to run certain DOS games and demos like DoWhackaDo, enable this option.");
01877 
01878     Pbool = secprop->Add_bool("resize only on vga active display width increase",Property::Changeable::Always,false);
01879     Pbool->Set_help("If set, changes to the Display End register of the CRTC do not trigger DOSBox-X to resize its window\n"
01880             "IF the value written is less than the current value. Some demos like DoWhackaDo need this option set\n"
01881             "because of the way its raster effects work. If the DOSBox-X window rapidly changes size during a demo\n"
01882             "try setting this option. Else, leave it turned off. Changes to other VGA CRTC registers will trigger\n"
01883             "a DOSBox-X mode change as normal regardless of this setting.");
01884 
01885     Pbool = secprop->Add_bool("enable pci bus",Property::Changeable::OnlyAtStart,true);
01886     Pbool->Set_help("Enable PCI bus emulation");
01887 
01888     Pbool = secprop->Add_bool("vga palette update on full load",Property::Changeable::Always,true);
01889     Pbool->Set_help("If set, all three bytes of the palette entry must be loaded before taking the color,\n"
01890                     "which is fairly typical SVGA behavior. If not set, partial changes are allowed.");
01891 
01892     Pbool = secprop->Add_bool("ignore odd-even mode in non-cga modes",Property::Changeable::Always,false);
01893     Pbool->Set_help("Some demoscene productions use VGA Mode X but accidentally enable odd/even mode.\n"
01894                     "Setting this option can correct for that and render the demo properly.\n"
01895                     "This option forces VGA emulation to ignore odd/even mode except in text and CGA modes.");
01896                                         
01897     secprop=control->AddSection_prop("pc98",&Null_Init);
01898         Pbool = secprop->Add_bool("pc-98 BIOS copyright string",Property::Changeable::WhenIdle,false);
01899     Pbool->Set_help("If set, the PC-98 BIOS copyright string is placed at E800:0000. Enable this for software that detects PC-98 vs Epson.");
01900 
01901     Pbool = secprop->Add_bool("pc-98 int 1b fdc timer wait",Property::Changeable::WhenIdle,false);
01902     Pbool->Set_help("If set, INT 1Bh floppy access will wait for the timer to count down before returning.\n"
01903                     "This is needed for Ys II to run without crashing.");
01904 
01905     Pbool = secprop->Add_bool("pc-98 pic init to read isr",Property::Changeable::WhenIdle,true);
01906     Pbool->Set_help("If set, the programmable interrupt controllers are initialized by default (if PC-98 mode)\n"
01907                     "so that the in-service interrupt status can be read immediately. There seems to be a common\n"
01908                     "convention in PC-98 games to program and/or assume this mode for cooperative interrupt handling.\n"
01909                     "This option is enabled by default for best compatibility with PC-98 games.");
01910 
01911     Pstring = secprop->Add_string("pc-98 fm board",Property::Changeable::Always,"auto");
01912     Pstring->Set_values(pc98fmboards);
01913     Pstring->Set_help("In PC-98 mode, selects the FM music board to emulate.");
01914 
01915     Pint = secprop->Add_int("pc-98 fm board irq", Property::Changeable::WhenIdle,0);
01916     Pint->Set_help("If set, helps to determine the IRQ of the FM board. A setting of zero means to auto-determine the IRQ.");
01917 
01918     Phex = secprop->Add_hex("pc-98 fm board io port", Property::Changeable::WhenIdle,0);
01919     Phex->Set_help("If set, helps to determine the base I/O port of the FM board. A setting of zero means to auto-determine the port number.");
01920 
01921     Pbool = secprop->Add_bool("pc-98 sound bios",Property::Changeable::WhenIdle,false);
01922     Pbool->Set_help("Set Sound BIOS enabled bit in MEMSW 4 for some games that require it.\n"
01923                     "TODO: Real emulation of PC-9801-26K/86 Sound BIOS");
01924 
01925     Pbool = secprop->Add_bool("pc-98 load sound bios rom file",Property::Changeable::WhenIdle,true);
01926     Pbool->Set_help("If set, load SOUND.ROM if available and prsent that to the guest instead of trying to emulate directly.\n"
01927                     "This is strongly recommended, and is default enabled.\n"
01928                     "SOUND.ROM is a snapshot of the FM board BIOS taken from real PC-98 hardware.");
01929 
01930     Pbool = secprop->Add_bool("pc-98 buffer page flip",Property::Changeable::WhenIdle,false);
01931     Pbool->Set_help("If set, the game's request to page flip will be delayed to vertical retrace, which can eliminate tearline artifacts.\n"
01932                     "Note that this is NOT the behavior of actual hardware. This option is provided for the user's preference.");
01933 
01934     Pbool = secprop->Add_bool("pc-98 enable 256-color planar",Property::Changeable::WhenIdle,true);
01935     Pbool->Set_help("Allow 256-color planar graphics mode if set, disable if not set.\n"
01936                     "This is a form of memory access in 256-color mode that existed for a short\n"
01937                     "time before later PC-9821 models removed it. This option must be enabled\n"
01938                     "to use DOSBox-X with Windows 3.1 and it's built-in 256-color driver.");
01939 
01940     Pbool = secprop->Add_bool("pc-98 enable 256-color",Property::Changeable::WhenIdle,true);
01941     Pbool->Set_help("Allow 256-color graphics mode if set, disable if not set");
01942 
01943     Pbool = secprop->Add_bool("pc-98 enable 16-color",Property::Changeable::WhenIdle,true);
01944     Pbool->Set_help("Allow 16-color graphics mode if set, disable if not set");
01945 
01946     Pbool = secprop->Add_bool("pc-98 enable grcg",Property::Changeable::WhenIdle,true);
01947     Pbool->Set_help("Allow GRCG graphics functions if set, disable if not set");
01948 
01949     Pbool = secprop->Add_bool("pc-98 enable egc",Property::Changeable::WhenIdle,true);
01950     Pbool->Set_help("Allow EGC graphics functions if set, disable if not set");
01951 
01952     Pbool = secprop->Add_bool("pc-98 enable 188 user cg",Property::Changeable::WhenIdle,true);
01953     Pbool->Set_help("Allow 188+ user-defined CG cells if set");
01954 
01955     Pbool = secprop->Add_bool("pc-98 start gdc at 5mhz",Property::Changeable::WhenIdle,false);
01956     Pbool->Set_help("Start GDC at 5MHz if set, 2.5MHz if clear. May be required for some games.");
01957 
01958     Pbool = secprop->Add_bool("pc-98 allow scanline effect",Property::Changeable::WhenIdle,true);
01959     Pbool->Set_help("If set, PC-98 emulation will allow the DOS application to enable the 'scanline effect'\n"
01960                     "in 200-line graphics modes upconverted to 400-line raster display. When enabled, odd\n"
01961                     "numbered scanlines are blanked instead of doubled");
01962 
01963     Pbool = secprop->Add_bool("pc-98 bus mouse",Property::Changeable::WhenIdle,true);
01964     Pbool->Set_help("Enable PC-98 bus mouse emulation. Disabling this option does not disable INT 33h emulation.");
01965 
01966     Pstring = secprop->Add_string("pc-98 video mode",Property::Changeable::WhenIdle,"");
01967     Pstring->Set_values(pc98videomodeopt);
01968     Pstring->Set_help("Specify the preferred PC-98 video mode.\n"
01969                       "Valid values are 15, 24, or 31 for each specific horizontal refresh rate on the platform.\n"
01970                       "24khz is default and best supported at this time.\n"
01971                       "15khz is not implemented at this time.\n"
01972                       "31khz is experimental at this time.");
01973 
01974     Pstring = secprop->Add_string("pc-98 timer always cycles",Property::Changeable::WhenIdle,"auto");
01975     Pstring->Set_values(truefalseautoopt);
01976     Pstring->Set_help("This controls PIT 1 PC speaker behavior related to turning the output on and off.\n"
01977                       "Default setting is 'auto' to let the emulator choose for you.\n"
01978                       "true:  PIT 1 will always cycle whether or not the speaker is on (PC-9801 behavior).\n"
01979                       "false: PIT 1 will only cycle when the speaker is on (PC-9821 behavior).\n"
01980                       "Some older games will require the PC-9801 behavior to function properly.");
01981 
01982     Pint = secprop->Add_int("pc-98 timer master frequency", Property::Changeable::WhenIdle,0);
01983     Pint->SetMinMax(0,2457600);
01984     Pint->Set_help("8254 timer clock frequency (NEC PC-98). Depending on the CPU frequency the clock frequency is one of two common values.\n"
01985                    "If your setting is neither of the below the closest appropriate value will be chosen.\n"
01986                    "This setting affects the master clock rate that DOS applications must divide down from to program the timer\n"
01987                    "at the correct rate, which affects timer interrupt, PC speaker, and the COM1 RS-232C serial port baud rate.\n"
01988                    "8MHz is treated as an alias for 4MHz and 10MHz is treated as an alias for 5MHz.\n"
01989                    "    0: Use default (auto)\n"
01990                    "    4: 1.996MHz (as if 4MHz or multiple thereof CPU clock)\n"
01991                    "    5: 2.457MHz (as if 5MHz or multiple thereof CPU clock)");
01992 
01993     Pint = secprop->Add_int("pc-98 allow 4 display partition graphics", Property::Changeable::WhenIdle,-1);
01994     Pint->SetMinMax(-1,1);
01995     Pint->Set_help("According to NEC graphics controller documentation, graphics mode is supposed to support only\n"
01996                    "2 display partitions. Some games rely on hardware flaws that allowed 4 partitions.\n"
01997                    "   -1: Default (choose automatically)\n"
01998                    "    0: Disable\n"
01999                    "    1: Enable");
02000 
02001     Pbool = secprop->Add_bool("pc-98 force ibm keyboard layout",Property::Changeable::WhenIdle,false);
02002     Pbool->Set_help("Force to use a default keyboard layout like IBM US-English for PC-98 emulation.\n"
02003                     "Will only work with apps and games using BIOS for keyboard.");
02004 
02005     /* Explanation: NEC's mouse driver MOUSE.COM enables the graphics layer on startup and when INT 33h AX=0 is called.
02006      *              Some games by "Orange House" assume this behavior and do not make any effort on their
02007      *              own to show and enable graphics. Without this option, those games will not show any
02008      *              graphics. PC-98 systems have been confirmed to boot up with the graphics layer disabled
02009      *              and set to 640x200 8-color planar mode. This has been confirmed on real hardware.
02010      *              See also [https://github.com/joncampbell123/dosbox-x/issues/1305] */
02011     Pbool = secprop->Add_bool("pc-98 show graphics layer on initialize",Property::Changeable::WhenIdle,true);
02012     Pbool->Set_help("If PC-98 mode and INT 33h emulation is enabled, the graphics layer will be automatically enabled\n"
02013                     "at driver startup AND when INT 33h AX=0 is called. This is NEC MOUSE.COM behavior and default\n"
02014                     "enabled. To emulate other drivers like QMOUSE that do not follow this behavior, set to false.");
02015 
02016     secprop=control->AddSection_prop("render",&Null_Init,true);
02017     Pint = secprop->Add_int("frameskip",Property::Changeable::Always,0);
02018     Pint->SetMinMax(0,10);
02019     Pint->Set_help("How many frames DOSBox-X skips before drawing one.");
02020 
02021     Pbool = secprop->Add_bool("alt render",Property::Changeable::Always,false);
02022     Pbool->Set_help("If set, use a new experimental rendering engine");
02023 
02024     Pstring = secprop->Add_string("aspect", Property::Changeable::Always, "false");
02025     Pstring->Set_values(aspectmodes);
02026     Pstring->Set_help(
02027         "Aspect ratio correction mode. Can be set to the following values:\n"
02028         "  'false' (default):\n"
02029         "      'direct3d'/opengl outputs: image is simply scaled to full window/fullscreen size, possibly resulting in disproportional image\n"
02030         "      'surface' output: it does no aspect ratio correction (default), resulting in disproportional images if VGA mode pixel ratio is not 4:3\n"
02031         "  'true':\n"
02032         "      'direct3d'/opengl outputs: uses output driver functions to scale / pad image with black bars, correcting output to proportional 4:3 image\n"
02033         "          In most cases image degradation should not be noticeable (it all depends on the video adapter and how much the image is upscaled).\n"
02034         "          Should have none to negligible impact on performance, mostly being done in hardware\n"
02035         "      'surface' output: inherits old DOSBox aspect ratio correction method (adjusting rendered image line count to correct output to 4:3 ratio)\n"
02036         "          Due to source image manipulation this mode does not mix well with scalers, i.e. multiline scalers like hq2x/hq3x will work poorly\n"
02037         "          Slightly degrades visual image quality. Has a tiny impact on performance"
02038 #if C_XBRZ
02039         "\n"
02040         "          When using xBRZ scaler with 'surface' output, aspect ratio correction is done by the scaler itself, so none of the above apply"
02041 #endif
02042 #if C_SURFACE_POSTRENDER_ASPECT
02043         "\n"
02044         "  'nearest':\n"
02045         "      'direct3d'/opengl outputs: not available, fallbacks to 'true' mode automatically\n"
02046         "      'surface' output: scaler friendly aspect ratio correction, works by rescaling rendered image using nearest neighbor scaler\n"
02047         "          Complex scalers work. Image quality is on par with 'true' mode (and better with scalers). More CPU intensive than 'true' mode\n"
02048 #if C_XBRZ
02049         "          When using xBRZ scaler with 'surface' output, aspect ratio correction is done by the scaler itself, so it fallbacks to 'true' mode\n"
02050 #endif
02051         "  'bilinear':\n"
02052         "      'direct3d'/opengl outputs: not available, fallbacks to 'true' mode automatically\n"
02053         "      'surface' output: scaler friendly aspect ratio correction, works by rescaling rendered image using bilinear scaler\n"
02054         "          Complex scalers work. Image quality is much better, should be on par with using 'direct3d' output + 'true' mode\n"
02055         "          Very CPU intensive, high end CPU may be required"
02056 #if C_XBRZ
02057         "\n"
02058         "          When using xBRZ scaler with 'surface' output, aspect ratio correction is done by the scaler itself, so it fallbacks to 'true' mode"
02059 #endif
02060 #endif
02061     );
02062 
02063     Pbool = secprop->Add_bool("char9",Property::Changeable::Always,true);
02064     Pbool->Set_help("Allow 9-pixel wide text mode fonts.");
02065 
02066     /* NTS: In the original code borrowed from yhkong, this was named "multiscan". All it really does is disable
02067      *      the doublescan down-rezzing DOSBox normally does with 320x240 graphics so that you get the full rendition of what a VGA output would emit. */
02068     Pbool = secprop->Add_bool("doublescan",Property::Changeable::Always,true);
02069     Pbool->Set_help("If set, doublescanned output emits two scanlines for each source line, in the\n"
02070             "same manner as the actual VGA output (320x200 is rendered as 640x400 for example).\n"
02071             "If clear, doublescanned output is rendered at the native source resolution (320x200 as 320x200).\n"
02072             "This affects the raster PRIOR to the software or hardware scalers. Choose wisely.\n");
02073 
02074     Pmulti = secprop->Add_multi("scaler",Property::Changeable::Always," ");
02075     Pmulti->SetValue("normal2x",/*init*/true);
02076     Pmulti->Set_help("Scaler used to enlarge/enhance low resolution modes. If 'forced' is appended,\n"
02077                      "then the scaler will be used even if the result might not be desired.\n"
02078                      "To fit a scaler in the resolution used at full screen may require a border or side bars.\n"
02079                      "To fill the screen entirely, depending on your hardware, a different scaler/fullresolution might work.");
02080     Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"normal2x");
02081     Pstring->Set_values(scalers);
02082 
02083     Pstring = Pmulti->GetSection()->Add_string("force",Property::Changeable::Always,"");
02084     Pstring->Set_values(force);
02085 
02086 #if C_XBRZ
02087     Pint = secprop->Add_int("xbrz slice",Property::Changeable::OnlyAtStart,16);
02088     Pint->SetMinMax(1,1024);
02089     Pint->Set_help("Number of screen lines to process in single xBRZ scaler taskset task, affects xBRZ performance, 16 is the default");
02090 
02091     Pint = secprop->Add_int("xbrz fixed scale factor",Property::Changeable::OnlyAtStart, 0);
02092     Pint->SetMinMax(0,6);
02093     Pint->Set_help("To use fixed xBRZ scale factor (i.e. to attune performance), set it to 2-6, 0 - use automatic calculation (default)");
02094 
02095     Pint = secprop->Add_int("xbrz max scale factor",Property::Changeable::OnlyAtStart, 0);
02096     Pint->SetMinMax(0,6);
02097     Pint->Set_help("To cap maximum xBRZ scale factor used (i.e. to attune performance), set it to 2-6, 0 - use scaler allowed maximum (default)");
02098 #endif
02099 
02100     Pbool = secprop->Add_bool("autofit",Property::Changeable::Always,true);
02101     Pbool->Set_help(
02102         "Best fits image to window\n"
02103         "- Intended for output=direct3d, fullresolution=original, aspect=true");
02104 
02105     Pmulti = secprop->Add_multi("monochrome_pal",Property::Changeable::Always," ");
02106     Pmulti->SetValue("green",/*init*/true);
02107     Pmulti->Set_help("Specify the color of monochrome display.\n"
02108         "Possible values: green, amber, gray, white\n"
02109         "Append 'bright' for a brighter look.");
02110     Pstring = Pmulti->GetSection()->Add_string("color",Property::Changeable::Always,"green");
02111     const char* monochrome_pal_colors[]={
02112       "green","amber","gray","white",0
02113     };
02114     Pstring->Set_values(monochrome_pal_colors);
02115     Pstring = Pmulti->GetSection()->Add_string("bright",Property::Changeable::Always,"");
02116     const char* bright[] = { "", "bright", 0 };
02117     Pstring->Set_values(bright);
02118 
02119     secprop=control->AddSection_prop("vsync",&Null_Init,true);//done
02120 
02121     Pstring = secprop->Add_string("vsyncmode",Property::Changeable::WhenIdle,"off");
02122     Pstring->Set_values(vsyncmode);
02123     Pstring->Set_help("Synchronize vsync timing to the host display. Requires calibration within DOSBox-X.");
02124     Pstring = secprop->Add_string("vsyncrate",Property::Changeable::WhenIdle,"75");
02125     Pstring->Set_values(vsyncrate);
02126     Pstring->Set_help("Vsync rate used if vsync is enabled. Ignored if vsyncmode is set to host (win32).");
02127 
02128     secprop=control->AddSection_prop("cpu",&Null_Init,true);//done
02129     Pstring = secprop->Add_string("core",Property::Changeable::WhenIdle,"auto");
02130     Pstring->Set_values(cores);
02131     Pstring->Set_help("CPU Core used in emulation. auto will switch to dynamic if available and appropriate.\n"
02132             "WARNING: Do not use dynamic or auto setting core with Windows 95 or other preemptive\n"
02133             "multitasking OSes with protected mode paging, you should use the normal core instead.");
02134 
02135     Pbool = secprop->Add_bool("fpu",Property::Changeable::Always,true);
02136     Pbool->Set_help("Enable FPU emulation");
02137 
02138     Pbool = secprop->Add_bool("segment limits",Property::Changeable::Always,true);
02139     Pbool->Set_help("Enforce segment limits");
02140 
02141     Pbool = secprop->Add_bool("double fault",Property::Changeable::Always,true);
02142     Pbool->Set_help("Emulate double fault exception");
02143 
02144     Pbool = secprop->Add_bool("reset on triple fault",Property::Changeable::Always,true);
02145     Pbool->Set_help("Reset CPU on triple fault condition (failure to handle double fault)");
02146 
02147     Pbool = secprop->Add_bool("always report double fault",Property::Changeable::Always,false);
02148     Pbool->Set_help("Always report (to log file) double faults if set. Else, a double fault is reported only once. Set this option for debugging purposes.");
02149 
02150     Pbool = secprop->Add_bool("always report triple fault",Property::Changeable::Always,false);
02151     Pbool->Set_help("Always report (to log file) triple faults if set. Else, a triple fault is reported only once. Set this option for debugging purposes.");
02152 
02153     Pbool = secprop->Add_bool("enable msr",Property::Changeable::Always,true);
02154     Pbool->Set_help("Allow RDMSR/WRMSR instructions. This option is only meaningful when cputype=pentium.\n"
02155             "WARNING: Leaving this option enabled while installing Windows 95/98/ME can cause crashes.");
02156 
02157     Pbool = secprop->Add_bool("enable cmpxchg8b",Property::Changeable::Always,true);
02158     Pbool->Set_help("Enable Pentium CMPXCHG8B instruction. Enable this explicitly if using software that uses this instruction.\n"
02159             "You must enable this option to run Windows ME because portions of the kernel rely on this instruction.");
02160 
02161     Pbool = secprop->Add_bool("ignore undefined msr",Property::Changeable::Always,false);
02162     Pbool->Set_help("Ignore RDMSR/WRMSR on undefined registers. Normally the CPU will fire an Invalid Opcode exception in that case.\n"
02163             "This option is off by default, enable if using software or drivers that assumes the presence of\n"
02164             "certain MSR registers without checking. If you are using certain versions of the 3Dfx glide drivers for MS-DOS\n"
02165             "you will need to set this to TRUE as 3Dfx appears to have coded GLIDE2.OVL to assume the presence\n"
02166             "of Pentium Pro/Pentium II MTRR registers.\n"
02167             "WARNING: Leaving this option enabled while installing Windows 95/98/ME can cause crashes.");
02168 
02169     /* NTS: This setting is honored by all cpu cores except dynamic core */
02170     Pint = secprop->Add_int("interruptible rep string op",Property::Changeable::Always,-1);
02171     Pint->SetMinMax(-1,65536);
02172     Pint->Set_help("if nonzero, REP string instructions (LODS/MOVS/STOS/INS/OUTS) are interruptible (by interrupts or other events).\n"
02173             "if zero, REP string instructions are carried out in full before processing events and interrupts.\n"
02174             "Set to -1 for a reasonable default setting based on cpu type and other configuration.\n"
02175             "A setting of 0 can improve emulation speed at the expense of emulation accuracy.\n"
02176             "A nonzero setting (1-8) may be needed for DOS games and demos that use the IRQ 0 interrupt to play digitized samples\n"
02177             "while doing VGA palette animation at the same time (use case of REP OUTS), where the non-interruptible version\n"
02178             "would cause an audible drop in audio pitch.");
02179 
02180     Pint = secprop->Add_int("dynamic core cache block size",Property::Changeable::Always,32);
02181     Pint->SetMinMax(1,65536);
02182     Pint->Set_help("dynamic core cache block size. default value is 32. change this value carefully.\n"
02183             "according to forum discussion, setting this to 1 can aid debugging, however doing so\n"
02184             "also causes problems with 32-bit protected mode DOS games and reduces the performance\n"
02185             "of the dynamic core.\n");
02186 
02187     Pstring = secprop->Add_string("cputype",Property::Changeable::Always,"auto");
02188     Pstring->Set_values(cputype_values);
02189     Pstring->Set_help("CPU Type used in emulation. auto emulates a 486 which tolerates Pentium instructions.");
02190 
02191     Pmulti_remain = secprop->Add_multiremain("cycles",Property::Changeable::Always," ");
02192     Pmulti_remain->Set_help(
02193         "Amount of instructions DOSBox-X tries to emulate each millisecond.\n"
02194         "Setting this value too high results in sound dropouts and lags.\n"
02195         "Cycles can be set in 3 ways:\n"
02196         "  'auto'          tries to guess what a game needs.\n"
02197         "                  It usually works, but can fail for certain games.\n"
02198         "  'fixed #number' will set a fixed amount of cycles. This is what you usually\n"
02199         "                  need if 'auto' fails (Example: fixed 4000).\n"
02200         "  'max'           will allocate as much cycles as your computer is able to\n"
02201         "                  handle.");
02202 
02203     Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::Always,"auto");
02204     Pmulti_remain->SetValue("auto",/*init*/true);
02205     Pstring->Set_values(cyclest);
02206 
02207     Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::Always,"");
02208 
02209     Pint = secprop->Add_int("cycleup",Property::Changeable::Always,10);
02210     Pint->SetMinMax(1,1000000);
02211     Pint->Set_help("Amount of cycles to decrease/increase with keycombos.(CTRL-F11/CTRL-F12)");
02212 
02213     Pint = secprop->Add_int("cycledown",Property::Changeable::Always,20);
02214     Pint->SetMinMax(1,1000000);
02215     Pint->Set_help("Setting it lower than 100 will be a percentage.");
02216 
02217     Pbool = secprop->Add_bool("use dynamic core with paging on",Property::Changeable::Always,true);
02218     Pbool->Set_help("Allow dynamic core with 386 paging enabled. This is generally OK for DOS games and Windows 3.1.\n"
02219                     "If the game becomes unstable, turn off this option.\n"
02220                     "WARNING: Do NOT use this option with preemptive multitasking OSes including Windows 95 and Windows NT.");
02221             
02222     Pbool = secprop->Add_bool("ignore opcode 63",Property::Changeable::Always,true);
02223     Pbool->Set_help("When debugging, do not report illegal opcode 0x63.\n"
02224             "Enable this option to ignore spurious errors while debugging from within Windows 3.1/9x/ME");
02225 
02226     Pbool = secprop->Add_bool("apmbios",Property::Changeable::WhenIdle,true);
02227     Pbool->Set_help("Emulate Advanced Power Management BIOS calls");
02228 
02229     Pbool = secprop->Add_bool("apmbios pnp",Property::Changeable::WhenIdle,false);
02230     Pbool->Set_help("If emulating ISA PnP BIOS, announce APM BIOS in PnP enumeration.\n"
02231             "Warning: this can cause Windows 95 OSR2 and later to enumerate the APM BIOS twice and cause problems.");
02232 
02233     Pstring = secprop->Add_string("apmbios version",Property::Changeable::WhenIdle,"auto");
02234     Pstring->Set_values(apmbiosversions);
02235     Pstring->Set_help("What version of the APM BIOS specification to emulate.\n"
02236             "You will need at least APM BIOS v1.1 for emulation to work with Windows 95/98/ME");
02237 
02238     Pbool = secprop->Add_bool("apmbios allow realmode",Property::Changeable::WhenIdle,true);
02239     Pbool->Set_help("Allow guest OS to connect from real mode.");
02240 
02241     Pbool = secprop->Add_bool("apmbios allow 16-bit protected mode",Property::Changeable::WhenIdle,true);
02242     Pbool->Set_help("Allow guest OS to connect from 16-bit protected mode.");
02243 
02244     Pbool = secprop->Add_bool("apmbios allow 32-bit protected mode",Property::Changeable::WhenIdle,true);
02245     Pbool->Set_help("Allow guest OS to connect from 32-bit protected mode.\n"
02246             "If you want power management in Windows 95/98/ME (beyond using the APM to shutdown the computer) you MUST enable this option.\n"
02247             "Windows 95/98/ME does not support the 16-bit real and protected mode APM BIOS entry points.\n"
02248             "Please note at this time that 32-bit APM is unstable under Windows ME");
02249 
02250     Pbool = secprop->Add_bool("integration device",Property::Changeable::WhenIdle,false);
02251     Pbool->Set_help("Enable DOSBox-X integration I/O device. This can be used by the guest OS to match mouse pointer position, for example. EXPERIMENTAL!");
02252 
02253     Pbool = secprop->Add_bool("integration device pnp",Property::Changeable::WhenIdle,false);
02254     Pbool->Set_help("List DOSBox-X integration I/O device as part of ISA PnP enumeration. This has no purpose yet.");
02255 
02256     Pbool = secprop->Add_bool("isapnpbios",Property::Changeable::WhenIdle,true);
02257     Pbool->Set_help("Emulate ISA Plug & Play BIOS. Enable if using DOSBox-X to run a PnP aware DOS program or if booting Windows 9x.\n"
02258             "Do not disable if Windows 9x is configured around PnP devices, you will likely confuse it.");
02259 
02260     Pbool = secprop->Add_bool("realbig16",Property::Changeable::WhenIdle,false);
02261     Pbool->Set_help("Allow the B (big) bit in real mode. If set, allow the DOS program to set the B bit,\n"
02262         "then jump to realmode with B still set (aka Huge Unreal mode). Needed for Project Angel.");
02263 
02264     secprop=control->AddSection_prop("keyboard",&Null_Init);
02265     Pbool = secprop->Add_bool("aux",Property::Changeable::OnlyAtStart,true);
02266     Pbool->Set_help("Enable emulation of the 8042 auxiliary port. PS/2 mouse emulation requires this to be enabled.\n"
02267             "You should enable this if you will be running Windows ME or any other OS that does not use the BIOS to receive mouse events.");
02268 
02269     Pbool = secprop->Add_bool("allow output port reset",Property::Changeable::OnlyAtStart,true);
02270     Pbool->Set_help("If set (default), allow the application to reset the CPU through the keyboard controller.\n"
02271             "This option is required to allow Windows ME to reboot properly, whereas Windows 9x and earlier\n"
02272             "will reboot without this option using INT 19h");
02273 
02274     Pstring = secprop->Add_string("controllertype",Property::Changeable::OnlyAtStart,"auto");
02275     Pstring->Set_values(controllertypes);
02276     Pstring->Set_help("Type of keyboard controller (and keyboard) attached.\n"
02277                       "auto     Automatically pick according to machine type\n"
02278                       "at       AT (PS/2) type keyboard\n"
02279                       "xt       IBM PC/XT type keyboard\n"
02280                       "pcjr     IBM PCjr type keyboard (only if machine=pcjr)\n"
02281                       "pc98     PC-98 keyboard emulation (only if machine=pc98)");
02282 
02283     Pstring = secprop->Add_string("auxdevice",Property::Changeable::OnlyAtStart,"intellimouse");
02284     Pstring->Set_values(auxdevices);
02285     Pstring->Set_help("Type of PS/2 mouse attached to the AUX port");
02286 
02287     secprop=control->AddSection_prop("pci",&Null_Init,false); //PCI bus
02288 
02289     Pstring = secprop->Add_string("voodoo",Property::Changeable::WhenIdle,"auto");
02290     Pstring->Set_values(voodoo_settings);
02291     Pstring->Set_help("Enable VOODOO support.");
02292 
02293     secprop=control->AddSection_prop("mixer",&Null_Init);
02294     Pbool = secprop->Add_bool("nosound",Property::Changeable::OnlyAtStart,false);
02295     Pbool->Set_help("Enable silent mode, sound is still emulated though.");
02296 
02297     Pbool = secprop->Add_bool("sample accurate",Property::Changeable::OnlyAtStart,false);
02298     Pbool->Set_help("Enable sample accurate mixing, at the expense of some emulation performance. Enable this option for DOS games and demos that\n"
02299             "require such accuracy for correct Tandy/OPL output including digitized speech. This option can also help eliminate minor\n"
02300             "errors in Gravis Ultrasound emulation that result in random echo/attenuation effects.");
02301 
02302     Pbool = secprop->Add_bool("swapstereo",Property::Changeable::OnlyAtStart,false); 
02303     Pbool->Set_help("Swaps the left and right stereo channels."); 
02304 
02305     Pint = secprop->Add_int("rate",Property::Changeable::OnlyAtStart,44100);
02306     Pint->SetMinMax(8000,192000);
02307     Pint->Set_help("Mixer sample rate, setting any device's rate higher than this will probably lower their sound quality.");
02308 
02309     Pint = secprop->Add_int("blocksize",Property::Changeable::OnlyAtStart,1024);
02310     Pint->Set_values(blocksizes);
02311     Pint->Set_help("Mixer block size, larger blocks might help sound stuttering but sound will also be more lagged.");
02312 
02313     Pint = secprop->Add_int("prebuffer",Property::Changeable::OnlyAtStart,25);
02314     Pint->SetMinMax(0,100);
02315     Pint->Set_help("How many milliseconds of data to keep on top of the blocksize.");
02316 
02317     secprop=control->AddSection_prop("midi",&Null_Init,true);//done
02318 
02319     Pstring = secprop->Add_string("mpu401",Property::Changeable::WhenIdle,"intelligent");
02320     Pstring->Set_values(mputypes);
02321     Pstring->Set_help("Type of MPU-401 to emulate.");
02322 
02323     Phex = secprop->Add_hex("mpubase",Property::Changeable::WhenIdle,0/*default*/);
02324     Phex->Set_values(mpubases);
02325     Phex->Set_help("The IO address of the MPU-401.\n"
02326                    "Set to 0 to use a default I/O address.\n"
02327                    "300h to 330h are for use with IBM PC mode.\n"
02328                    "C0D0h to F8D0h (in steps of 800h) are for use with NEC PC-98 mode (MPU98).\n"
02329                    "80D2h through 80DEh are for use with NEC PC-98 Sound Blaster 16 MPU-401 emulation.\n"
02330                    "If not assigned (0), 330h is the default for IBM PC and E0D0h is the default for PC-98.");
02331 
02332     Pstring = secprop->Add_string("mididevice",Property::Changeable::WhenIdle,"default");
02333     Pstring->Set_values(devices);
02334     Pstring->Set_help("Device that will receive the MIDI data from MPU-401.");
02335 
02336     Pstring = secprop->Add_string("midiconfig",Property::Changeable::WhenIdle,"");
02337     Pstring->Set_help("Special configuration options for the device driver. This is usually the id or part of the name of the device you want to use (find the id/name with mixer/listmidi).\n"
02338                       "Or in the case of coreaudio or synth, you can specify a soundfont here.\n"
02339                       "When using a Roland MT-32 rev. 0 as midi output device, some games may require a delay in order to prevent 'buffer overflow' issues.\n"
02340                       "In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n"
02341                       "See the README/Manual for more details.");
02342 
02343     Pint = secprop->Add_int("samplerate",Property::Changeable::WhenIdle,44100);
02344     Pint->Set_values(rates);
02345     Pint->Set_help("Sample rate for MIDI synthesizer, if applicable.");
02346     
02347     Pint = secprop->Add_int("mpuirq",Property::Changeable::WhenIdle,-1);
02348     Pint->SetMinMax(-1,15);
02349     Pint->Set_help("MPU-401 IRQ. -1 to automatically choose.");
02350 
02351     /* mt32.romdir added from DOSBox ECE by request. This romdir code taken from DOSBox ECE */
02352     Pstring = secprop->Add_string("mt32.romdir",Property::Changeable::WhenIdle,"");
02353     Pstring->Set_help("Name of the directory where MT-32 Control and PCM ROM files can be found. Emulation requires these files to work.\n"
02354             "  Accepted file names are as follows:\n"
02355             "    MT32_CONTROL.ROM or CM32L_CONTROL.ROM - control ROM file.\n"
02356             "    MT32_PCM.ROM or CM32L_PCM.ROM - PCM ROM file.");
02357 
02358     Pstring = secprop->Add_string("mt32.reverse.stereo",Property::Changeable::WhenIdle,"off");
02359     Pstring->Set_values(mt32ReverseStereo);
02360     Pstring->Set_help("Reverse stereo channels for MT-32 output");
02361 
02362     Pstring = secprop->Add_string("mt32.verbose",Property::Changeable::WhenIdle,"off");
02363     Pstring->Set_values(mt32log);
02364     Pstring->Set_help("MT-32 debug logging");
02365 
02366     Pstring = secprop->Add_string("mt32.thread",Property::Changeable::WhenIdle,"off");
02367     Pstring->Set_values(mt32thread);
02368     Pstring->Set_help("MT-32 rendering in separate thread");
02369 
02370     Pstring = secprop->Add_string("mt32.dac",Property::Changeable::WhenIdle,"auto");
02371     Pstring->Set_values(mt32DACModes);
02372     Pstring->Set_help("MT-32 DAC input emulation mode\n"
02373         "Nice = 0 - default\n"
02374         "Produces samples at double the volume, without tricks.\n"
02375         "Higher quality than the real devices\n\n"
02376 
02377         "Pure = 1\n"
02378         "Produces samples that exactly match the bits output from the emulated LA32.\n"
02379         "Nicer overdrive characteristics than the DAC hacks (it simply clips samples within range)\n"
02380         "Much less likely to overdrive than any other mode.\n"
02381         "Half the volume of any of the other modes, meaning its volume relative to the reverb\n"
02382         "output when mixed together directly will sound wrong. So, reverb level must be lowered.\n"
02383         "Perfect for developers while debugging :)\n\n"
02384 
02385         "GENERATION1 = 2\n"
02386         "Re-orders the LA32 output bits as in early generation MT-32s (according to Wikipedia).\n"
02387         "Bit order at DAC (where each number represents the original LA32 output bit number, and XX means the bit is always low):\n"
02388         "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 XX\n\n"
02389 
02390         "GENERATION2 = 3\n"
02391         "Re-orders the LA32 output bits as in later geneerations (personally confirmed on my CM-32L - KG).\n"
02392         "Bit order at DAC (where each number represents the original LA32 output bit number):\n"
02393         "15 13 12 11 10 09 08 07 06 05 04 03 02 01 00 14\n");
02394 
02395     Pstring = secprop->Add_string("mt32.reverb.mode",Property::Changeable::WhenIdle,"auto");
02396     Pstring->Set_values(mt32reverbModes);
02397     Pstring->Set_help("MT-32 reverb mode");
02398 
02399     Pint = secprop->Add_int("mt32.reverb.time",Property::Changeable::WhenIdle,5);
02400     Pint->Set_values(mt32reverbTimes);
02401     Pint->Set_help("MT-32 reverb decaying time"); 
02402 
02403     Pint = secprop->Add_int("mt32.reverb.level",Property::Changeable::WhenIdle,3);
02404     Pint->Set_values(mt32reverbLevels);
02405     Pint->Set_help("MT-32 reverb level");
02406 
02407     Pint = secprop->Add_int("mt32.partials",Property::Changeable::WhenIdle,32);
02408     Pint->SetMinMax(0,256);
02409     Pint->Set_help("MT-32 max partials allowed (0-256)");
02410 
02411 #ifdef C_FLUIDSYNTH
02412         const char *fluiddrivers[] = {"pulseaudio", "alsa", "oss", "coreaudio", "dsound", "portaudio", "sndman", "jack", "file", "default",0};
02413         Pstring = secprop->Add_string("fluid.driver",Property::Changeable::WhenIdle,"default");
02414         Pstring->Set_values(fluiddrivers);
02415         Pstring->Set_help("Driver to use with Fluidsynth, not needed under Windows. Available drivers depend on what Fluidsynth was compiled with.");
02416 
02417         Pstring = secprop->Add_string("fluid.soundfont",Property::Changeable::WhenIdle,"");
02418         Pstring->Set_help("Soundfont to use with Fluidsynth. One must be specified.");
02419 
02420         Pstring = secprop->Add_string("fluid.samplerate",Property::Changeable::WhenIdle,"48000");
02421         Pstring->Set_help("Sample rate to use with Fluidsynth.");
02422 
02423         Pstring = secprop->Add_string("fluid.gain",Property::Changeable::WhenIdle,".6");
02424         Pstring->Set_help("Fluidsynth gain.");
02425 
02426         Pint = secprop->Add_int("fluid.polyphony",Property::Changeable::WhenIdle,256);
02427         Pint->Set_help("Fluidsynth polyphony.");
02428 
02429         Pstring = secprop->Add_string("fluid.cores",Property::Changeable::WhenIdle,"default");
02430         Pstring->Set_help("Fluidsynth CPU cores to use, or default.");
02431 
02432         Pstring = secprop->Add_string("fluid.periods",Property::Changeable::WhenIdle,"default");
02433         Pstring->Set_help("Fluidsynth periods, or default.");
02434 
02435         Pstring = secprop->Add_string("fluid.periodsize",Property::Changeable::WhenIdle,"default");
02436         Pstring->Set_help("Fluidsynth period size, or default.");
02437 
02438         const char *fluidreverb[] = {"no", "yes",0};
02439         Pstring = secprop->Add_string("fluid.reverb",Property::Changeable::WhenIdle,"yes");     
02440         Pstring->Set_values(fluidreverb);
02441         Pstring->Set_help("Fluidsynth use reverb.");
02442 
02443         const char *fluidchorus[] = {"no", "yes",0};
02444         Pstring = secprop->Add_string("fluid.chorus",Property::Changeable::WhenIdle,"yes");     
02445         Pstring->Set_values(fluidchorus);
02446         Pstring->Set_help("Fluidsynth use chorus.");
02447 
02448         Pstring = secprop->Add_string("fluid.reverb.roomsize",Property::Changeable::WhenIdle,".61");
02449         Pstring->Set_help("Fluidsynth reverb room size.");
02450 
02451         Pstring = secprop->Add_string("fluid.reverb.damping",Property::Changeable::WhenIdle,".23");
02452         Pstring->Set_help("Fluidsynth reverb damping.");
02453 
02454         Pstring = secprop->Add_string("fluid.reverb.width",Property::Changeable::WhenIdle,".76");
02455         Pstring->Set_help("Fluidsynth reverb width.");
02456 
02457         Pstring = secprop->Add_string("fluid.reverb.level",Property::Changeable::WhenIdle,".57");
02458         Pstring->Set_help("Fluidsynth reverb level.");
02459 
02460         Pint = secprop->Add_int("fluid.chorus.number",Property::Changeable::WhenIdle,3);        
02461         Pint->Set_help("Fluidsynth chorus voices");
02462 
02463         Pstring = secprop->Add_string("fluid.chorus.level",Property::Changeable::WhenIdle,"1.2");
02464         Pstring->Set_help("Fluidsynth chorus level.");
02465 
02466         Pstring = secprop->Add_string("fluid.chorus.speed",Property::Changeable::WhenIdle,".3");
02467         Pstring->Set_help("Fluidsynth chorus speed.");
02468 
02469         Pstring = secprop->Add_string("fluid.chorus.depth",Property::Changeable::WhenIdle,"8.0");
02470         Pstring->Set_help("Fluidsynth chorus depth.");
02471 
02472         const char *fluidchorustypes[] = {"0", "1",0};
02473         Pint = secprop->Add_int("fluid.chorus.type",Property::Changeable::WhenIdle,0);
02474         Pint->Set_values(fluidchorustypes);
02475         Pint->Set_help("Fluidsynth chorus type. 0 is sine wave, 1 is triangle wave.");
02476 #endif
02477 
02478     secprop=control->AddSection_prop("sblaster",&Null_Init,true);
02479     
02480     Pstring = secprop->Add_string("sbtype",Property::Changeable::WhenIdle,"sb16");
02481     Pstring->Set_values(sbtypes);
02482     Pstring->Set_help("Type of Sound Blaster to emulate. 'gb' is Game Blaster.");
02483 
02484     Phex = secprop->Add_hex("sbbase",Property::Changeable::WhenIdle,0x220);
02485     Phex->Set_values(ios);
02486     Phex->Set_help("The IO address of the Sound Blaster.\n"
02487                    "220h to 2E0h are for use with IBM PC Sound Blaster emulation.\n"
02488                    "D2h to DEh are for use with NEC PC-98 Sound Blaster 16 emulation.");
02489 
02490     Pint = secprop->Add_int("irq",Property::Changeable::WhenIdle,7);
02491     Pint->Set_values(irqssb);
02492     Pint->Set_help("The IRQ number of the Sound Blaster. Set to -1 to start DOSBox-X with the IRQ unassigned");
02493 
02494     Pint = secprop->Add_int("mindma",Property::Changeable::OnlyAtStart,-1);
02495     Pint->Set_help( "Minimum DMA transfer left to increase attention across DSP blocks, in milliseconds. Set to -1 for default.\n"
02496             "There are some DOS games/demos that use single-cycle DSP playback in their music tracker and they micromanage\n"
02497             "the DMA transfer per block poorly in a way that causes popping and artifacts. Setting this option to 0 for\n"
02498             "such DOS applications may reduce audible popping and artifacts.");
02499 
02500     /* Sound Blaster IRQ hacks.
02501      *
02502      * These hacks reduce emulation accuracy but can be set to work around bugs or mistakes in some old
02503      * games and demos related to handling the Sound Blaster IRQ.
02504      *
02505      * - Saga by Dust (1993):
02506      *     Sound Blaster support has a fatal flaw in that the Sound Blaster interrupt handler it installs assumes
02507      *     DS == CS. It uses the DS register to read local variables needed to manage the Sound Blaster card but
02508      *     it makes no attempt to push DS and then load the DS segment value it needs. While the demo may seem to
02509      *     run normally at first, eventually the interrupt is fired at just the right time to catch the demo in
02510      *     the middle of it's graphics routines (DS=A000). Since the ISR uses DS to load the Sound Blaster DSP
02511      *     I/O port, it reads some random value from *video RAM* and then hangs in a loop waiting for that I/O
02512      *     port to clear bit 7! Setting 'cs_equ_ds' works around that bug by instructing PIC emulation not to
02513      *     fire the interrupt unless segment registers CS and DS match. */
02514     Pstring = secprop->Add_string("irq hack",Property::Changeable::WhenIdle,"none");
02515     Pstring->Set_help("Specify a hack related to the Sound Blaster IRQ to avoid crashes in a handful of games and demos.\n"
02516             "    none                   Emulate IRQs normally\n"
02517             "    cs_equ_ds              Do not fire IRQ unless two CPU segment registers match: CS == DS. Read Dosbox-X Wiki or source code for details.");
02518 
02519     Pint = secprop->Add_int("dma",Property::Changeable::WhenIdle,1);
02520     Pint->Set_values(dmassb);
02521     Pint->Set_help("The DMA number of the Sound Blaster. Set to -1 to start DOSBox-X with the DMA unassigned");
02522 
02523     Pint = secprop->Add_int("hdma",Property::Changeable::WhenIdle,5);
02524     Pint->Set_values(dmassb);
02525     Pint->Set_help("The High DMA number of the Sound Blaster. Set to -1 to start DOSBox-X with the High DMA unassigned");
02526 
02527     Pbool = secprop->Add_bool("pic unmask irq",Property::Changeable::WhenIdle,false);
02528     Pbool->Set_help("Start the DOS virtual machine with the Sound Blaster IRQ already unmasked at the PIC.\n"
02529             "Some early DOS games/demos that support Sound Blaster expect the IRQ to fire but make\n"
02530             "no attempt to unmask the IRQ. If audio cuts out no matter what IRQ you try, then try\n"
02531             "setting this option.\n"
02532             "Option is needed for:\n"
02533             "   Public NMI \"jump\" demo (1992)");
02534 
02535     Pbool = secprop->Add_bool("enable speaker",Property::Changeable::WhenIdle,false);
02536     Pbool->Set_help("Start the DOS virtual machine with the Sound Blaster speaker enabled.\n"
02537                     "Sound Blaster Pro and older cards have a speaker disable/enable command.\n"
02538                     "Normally the card boots up with the speaker disabled. If a DOS game or demo\n"
02539                     "attempts to play without enabling the speaker, set this option to true to\n"
02540                     "compensate. This setting has no meaning if emulating a Sound Blaster 16 card.");
02541 
02542     Pbool = secprop->Add_bool("enable asp",Property::Changeable::WhenIdle,false);
02543     Pbool->Set_help("If set, emulate the presence of the Sound Blaster 16 Advanced Sound Processor/Creative Sound Processor chip.\n"
02544             "NOTE: This only emulates it's presence and the basic DSP commands to communicate with it. Actual ASP/CSP functions are not yet implemented.");
02545 
02546     Pbool = secprop->Add_bool("disable filtering",Property::Changeable::WhenIdle,false);
02547     Pbool->Set_help("By default DOSBox-X filters Sound Blaster output to emulate lowpass filters and analog output limitations.\n"
02548             "Set this option to true to disable filtering. Note that doing so disables emulation of the Sound Blaster Pro\n"
02549             "output filter and ESS AudioDrive lowpass filter.");
02550 
02551     Pbool = secprop->Add_bool("dsp write buffer status must return 0x7f or 0xff",Property::Changeable::WhenIdle,false);
02552     Pbool->Set_help("If set, force port 22Ch (DSP write buffer status) to return 0x7F or 0xFF. If not set, the port\n"
02553             "may return 0x7F or 0xFF depending on what type of Sound Blaster is being emulated.\n"
02554             "Set this option for some early DOS demos that make that assumption about port 22Ch.\n"
02555             "Option is needed for:\n"
02556             "   Overload by Hysteria (1992) - Audio will crackle/saturate (8-bit overflow) except when sbtype=sb16");
02557 
02558     Pbool = secprop->Add_bool("pre-set sbpro stereo",Property::Changeable::WhenIdle,false);
02559     Pbool->Set_help("Start the DOS virtual machine with the Sound Blaster Pro stereo bit set (in the mixer).\n"
02560             "A few demos support Sound Blaster Pro but forget to set this bit.\n"
02561             "Option is needed for:\n"
02562             "   Inconexia by Iguana (1993)");
02563 
02564     Pbool = secprop->Add_bool("sbmixer",Property::Changeable::WhenIdle,true);
02565     Pbool->Set_help("Allow the Sound Blaster mixer to modify the DOSBox-X mixer.");
02566 
02567     Pstring = secprop->Add_string("oplmode",Property::Changeable::WhenIdle,"auto");
02568     Pstring->Set_values(oplmodes);
02569     Pstring->Set_help("Type of OPL emulation. On 'auto' the mode is determined by the 'sbtype' setting.\n"
02570                         "All OPL modes are AdLib-compatible, except for 'cms' (set 'sbtype=none' with 'cms' for a Game Blaster).");
02571 
02572     Pbool = secprop->Add_bool("adlib force timer overflow on detect",Property::Changeable::WhenIdle,false);
02573     Pbool->Set_help("If set, Adlib/OPL emulation will signal 'overflow' on timers after 50 I/O reads.\n"
02574             "This is a temporary hack to work around timing bugs noted in DOSBox-X. Certain\n"
02575             "games (Wolfenstein 3D) poll the Adlib status port a fixed number of times assuming\n"
02576             "that the poll loop takes long enough for the Adlib timer to run out. If the game\n"
02577             "cannot reliably detect Adlib at higher cycles counts, but can reliably detect at\n"
02578             "lower cycles counts, set this option.\n"
02579             "NOTE: Technically this decreases emulation accuracy, however it also reflects the\n"
02580             "      fact that DOSBox-X's I/O timing code needs some work to better match the\n"
02581             "      slowness of the ISA bus per I/O read in consideration of DOS games. So this\n"
02582             "      option is ON by default.");
02583     /* NTS: The reason I mention Wolfenstein 3D is that it seems coded not to probe for Sound Blaster unless it
02584      *      first detects the Adlib at port 0x388. No Adlib, no Sound Blaster. */
02585     /* ^ NTS: To see what I mean, download Wolf3d source code, look at ID_SD.C line 1585 (Adlib detection routine).
02586      *        Note it sets Timer 1, then reads port 388h 100 times before reading status to detect whether the
02587      *        timer "overflowed" (fairly typical Adlib detection code).
02588      *        Some quick math: 8333333Hz ISA BCLK / 6 cycles per read (3 wait states) = 1388888 reads/second possible
02589      *                         100 I/O reads * (1 / 1388888) = 72us */ 
02590 
02591     Pstring = secprop->Add_string("oplemu",Property::Changeable::WhenIdle,"default");
02592     Pstring->Set_values(oplemus);
02593     Pstring->Set_help("Provider for the OPL emulation. 'compat' might provide better quality.\n"
02594                 "'nuked' is the most accurate (but the most CPU-intensive). See oplrate as well.");
02595 
02596     Pint = secprop->Add_int("oplrate",Property::Changeable::WhenIdle,44100);
02597     Pint->Set_values(oplrates);
02598     Pint->Set_help("Sample rate of OPL music emulation. Use 49716 for highest quality (set the mixer rate accordingly).");
02599 
02600     Phex = secprop->Add_hex("hardwarebase",Property::Changeable::WhenIdle,0x220);
02601     Phex->Set_help("base address of the real hardware Sound Blaster:\n"\
02602         "210,220,230,240,250,260,280");
02603 
02604     Pbool = secprop->Add_bool("force dsp auto-init",Property::Changeable::WhenIdle,false);
02605     Pbool->Set_help("Treat all single-cycle DSP commands as auto-init to keep playback going.\n"
02606             "This option is a workaround for DOS games or demos that use single-cycle DSP playback commands and\n"
02607             "have problems with missing the Sound Blaster IRQ under load. Do not enable unless you need this workaround.\n"
02608             "Needed for:\n"
02609             "  - Extreme \"lunatic\" demo (1993)");
02610 
02611     Pbool = secprop->Add_bool("force goldplay",Property::Changeable::WhenIdle,false);
02612     Pbool->Set_help("Always render Sound Blaster output sample-at-a-time. Testing option. You probably don't want to enable this.");
02613 
02614     Pbool = secprop->Add_bool("goldplay",Property::Changeable::WhenIdle,true);
02615     Pbool->Set_help("Enable goldplay emulation.");
02616 
02617     Pbool = secprop->Add_bool("goldplay stereo",Property::Changeable::WhenIdle,true);
02618     Pbool->Set_help("Enable workaround for goldplay stereo playback. Many DOS demos using this technique\n"
02619             "don't seem to know they need to double the frequency when programming the DSP time constant for Pro stereo output.\n"
02620             "If stereo playback seems to have artifacts consider enabling this option. For accurate emulation of Sound Blaster\n"
02621             "hardware, disable this option.");
02622 
02623     Pstring = secprop->Add_string("dsp require interrupt acknowledge",Property::Changeable::WhenIdle,"auto");
02624     Pstring->Set_help("If set, the DSP will halt DMA playback until IRQ acknowledgement occurs even in auto-init mode (SB16 behavior).\n"
02625             "If clear, IRQ acknowledgement will have no effect on auto-init playback (SB Pro and earlier & clone behavior)\n"
02626             "If set to 'auto' then behavior is determined by sbtype= setting.\n"
02627             "This is a setting for hardware accuracy in emulation. If audio briefly plays then stops then your DOS game\n"
02628             "and it's not using IRQ (but using DMA), try setting this option to 'false'");
02629 
02630     Pint = secprop->Add_int("dsp write busy delay",Property::Changeable::WhenIdle,-1);
02631     Pint->Set_help("Amount of time in nanoseconds the DSP chip signals 'busy' after writing to the DSP (port 2xCh). Set to -1 to use card-specific defaults.\n"
02632             "WARNING: Setting the value too high (above 20000ns) may have detrimental effects to DOS games that use IRQ 0 and DSP command 0x10 to play audio.\n"
02633             "         Setting the value way too high (above 1000000ns) can cause significant lag in DOS games.");
02634 
02635     Pbool = secprop->Add_bool("blaster environment variable",Property::Changeable::WhenIdle,true);
02636     Pbool->Set_help("Whether or not to set the BLASTER environment variable automatically at startup");
02637 
02638     Pbool = secprop->Add_bool("sample rate limits",Property::Changeable::WhenIdle,true);
02639     Pbool->Set_help("If set (default), limit DSP sample rate to what real hardware is limited to");
02640 
02641     /* recommended for:
02642      *   1992 demo "overload" (if set, Sound Blaster support can run at 24KHz without causing demo to hang in the IRQ 0 timer)
02643      *   1993 demo "xmas 93" (superiority complex) because the demo's Sound Blaster mode writes at the timer interrupt rate without polling the DSP to check busy state */
02644     Pbool = secprop->Add_bool("instant direct dac",Property::Changeable::WhenIdle,false);
02645     Pbool->Set_help("If set, direct DAC output commands are instantaneous. This option is intended as a quick fix for\n"
02646             "games or demos that play direct DAC music/sound from the IRQ 0 timer who a) write the DSP command\n"
02647             "and data without polling the DSP to ensure it's ready or b) can get locked into the IRQ 0 handler\n"
02648             "waiting for DSP status when instructed to play at or beyond the DSP's maximum direct DAC sample rate.\n"
02649             "This fix allows broken Sound Blaster code to work and should not be enabled unless necessary.");
02650 
02651     /* accuracy emulation: SB16 does not honor SBPro stereo bit in the mixer */
02652     Pbool = secprop->Add_bool("stereo control with sbpro only",Property::Changeable::WhenIdle,true);
02653     Pbool->Set_help("Default on. If set, Sound Blaster Pro stereo is not available when emulating sb16 or sb16vibra.\n"
02654             "If clear, sb16 emulation will honor the sbpro stereo bit. Note that Creative SB16 cards do not\n"
02655             "honor the stereo bit, and this option allows DOSBox-X emulate that fact. Accuracy setting.");
02656 
02657     /* NTS: It turns out (SB16 at least) the DSP will periodically set bit 7 (busy) by itself at some
02658      *      clock rate even if it's idle. Casual testing on an old Pentium system with a ViBRA shows
02659      *      it's possible to see both 0x7F and 0xFF come back if you repeatedly type "i 22c" in DOS
02660      *      DEBUG.EXE.  FIXME: At what clock rate and duty cycle does this happen? */
02661     Pint = secprop->Add_int("dsp busy cycle rate",Property::Changeable::WhenIdle,-1/*default*/);
02662     Pint->Set_help("Sound Blaster 16 DSP chips appear to go busy periodically at some high clock rate\n"
02663             "whether the DSP is actually doing anything for the system or not. This is an accuracy\n"
02664             "option for Sound Blaster emulation. If this option is nonzero, it will be interpreted\n"
02665             "as the busy cycle rate in Hz. If zero, busy cycle will not be emulated. If -1, sound\n"
02666             "blaster emulation will automatically choose a setting based on the sbtype= setting");
02667 
02668     Pint = secprop->Add_int("dsp busy cycle always",Property::Changeable::WhenIdle,-1/*default*/);
02669     Pint->Set_help("If set, the DSP busy cycle always happens. If clear, DSP busy cycle only happens when\n"
02670             "audio playback is running. Default setting is to pick according to the sound card.");
02671 
02672     Pint = secprop->Add_int("dsp busy cycle duty",Property::Changeable::WhenIdle,-1/*default*/);
02673     Pint->Set_help("If emulating SB16 busy cycle, this value (0 to 100) controls the duty cycle of the busy cycle.\n"
02674             "If this option is set to -1, Sound Blaster emulation will choose a value automatically according\n"
02675             "to sbtype=. If 0, busy cycle emulation is disabled.");
02676 
02677     /* NTS: Confirmed: My Sound Blaster 2.0 (at least) mirrors the DSP on port 22Ch and 22Dh. This option
02678      *      will only take effect with sbtype sb1 and sb2, so make it enabled by default. Accuracy setting. */
02679     Pbool = secprop->Add_bool("io port aliasing",Property::Changeable::WhenIdle,true);
02680     Pbool->Set_help("If set, Sound Blaster ports alias by not decoding the LSB of the I/O port.\n"
02681             "This option only applies when sbtype is set to sb1 or sb2 (not SBPro or SB16).\n"
02682             "This is a hack for the Electromotive Force 'Internal Damage' demo which apparently\n"
02683             "relies on this behavior for Sound Blaster output and should be enabled for accuracy in emulation.");
02684 
02685     secprop=control->AddSection_prop("gus",&Null_Init,true); //done
02686     Pbool = secprop->Add_bool("gus",Property::Changeable::WhenIdle,false);  
02687     Pbool->Set_help("Enable the Gravis Ultrasound emulation.");
02688 
02689     Pbool = secprop->Add_bool("autoamp",Property::Changeable::WhenIdle,false);
02690     Pbool->Set_help("If set, GF1 output will reduce in volume automatically if the sum of all channels exceeds full volume.\n"
02691                     "If not set, then loud music will clip to full volume just as it would on real hardware.\n"
02692                     "Enable this option for loud music if you want a more pleasing rendition without saturation and distortion.");
02693 
02694     Pbool = secprop->Add_bool("unmask dma",Property::Changeable::WhenIdle,false);
02695     Pbool->Set_help("Start the DOS virtual machine with the DMA channel already unmasked at the controller.\n"
02696             "Use this for DOS applications that expect to operate the GUS but forget to unmask the DMA channel.");
02697 
02698     Pbool = secprop->Add_bool("ignore channel count while active",Property::Changeable::WhenIdle,false);
02699     Pbool->Set_help("Ignore writes to the active channel count register when the DAC is enabled (bit 1 of GUS reset)\n"
02700                     "This is a HACK for demoscene prod 'Ice Fever' without which the music sounds wrong.\n"
02701                     "According to current testing real hardware does not behave this way.");
02702 
02703     Pbool = secprop->Add_bool("pic unmask irq",Property::Changeable::WhenIdle,false);
02704     Pbool->Set_help("Start the DOS virtual machine with the GUS IRQ already unmasked at the PIC.");
02705 
02706     Pbool = secprop->Add_bool("startup initialized",Property::Changeable::WhenIdle,false);
02707     Pbool->Set_help("If set, start the GF1 in a fully initialized state (as if ULTRINIT had been run).\n"
02708                     "If clear, leave the card in an uninitialized state (as if cold boot).\n"
02709                     "Some DOS games or demoscene productions will hang or fail to use the Ultrasound hardware\n"
02710                     "because they assume the card is initialized and their hardware detect does not fully initialize the card.");
02711 
02712     Pbool = secprop->Add_bool("dma enable on dma control polling",Property::Changeable::WhenIdle,false);
02713     Pbool->Set_help("If set, automatically enable GUS DMA transfer bit in specific cases when the DMA control register is being polled.\n"
02714                     "THIS IS A HACK. Some games and demoscene productions need this hack to avoid hanging while uploading sample data\n"
02715                     "to the Gravis Ultrasound due to bugs in their implementation.");
02716 
02717     Pbool = secprop->Add_bool("clear dma tc irq if excess polling",Property::Changeable::WhenIdle,false);
02718     Pbool->Set_help("If the DOS application is seen polling the IRQ status register rapidly, automatically clear the DMA TC IRQ status.\n"
02719             "This is a hack that should only be used with DOS applications that need it to avoid bugs in their GUS support code.\n"
02720             "Needed for:\n"
02721             "  Warcraft II by Blizzard ............. if using GUS for music and sound, set this option to prevent the game from\n"
02722             "                                        hanging when you click on the buttons in the main menu.");
02723 
02724     /* some DOS demos, especially where the programmers wrote their own tracker, forget to set "master IRQ enable" on the GUS,
02725      * and then wonder why music isn't playing. prior to some GUS bugfixes they happend to work anyway because DOSBox also
02726      * ignored master IRQ enable. you can restore that buggy behavior here.
02727      *
02728      * DOS games & demos that need this:
02729      *   - "Juice" by Psychic Link (writes 0x300 to GUS reset which only enables DAC and takes card out of reset, does not enable IRQ) */
02730     Pbool = secprop->Add_bool("force master irq enable",Property::Changeable::WhenIdle,false);
02731     Pbool->Set_help("Set this option if a DOS game or demo initializes the GUS but is unable to play any music.\n"
02732             "Usually the cause is buggy GUS support that resets the GUS but fails to set the Master IRQ enable bit.");
02733 
02734     Pstring = secprop->Add_string("gus panning table",Property::Changeable::WhenIdle,"default");
02735     Pstring->Set_values(guspantables);
02736     Pstring->Set_help("Controls which table or equation is used for the Gravis Ultrasound panning emulation.\n"
02737             "accurate emulation attempts to better reflect how the actual hardware handles panning,\n"
02738             "while the old emulation uses a simpler idealistic mapping.");
02739 
02740     Pint = secprop->Add_int("gusrate",Property::Changeable::WhenIdle,44100);
02741     Pint->Set_values(rates);
02742     Pint->Set_help("Sample rate of Ultrasound emulation.");
02743 
02744     Pbool = secprop->Add_bool("gus fixed render rate",Property::Changeable::WhenIdle,false);
02745     Pbool->Set_help("If set, Gravis Ultrasound audio output is rendered at a fixed sample rate specified by 'gusrate'. This can provide better quality than real hardware,\n"
02746             "if desired. Else, Gravis Ultrasound emulation will change the sample rate of it's output according to the number of active channels, just like real hardware.\n"
02747             "Note: DOSBox-X defaults to 'false', while mainline DOSBox SVN is currently hardcoded to render as if this setting is 'true'.");
02748 
02749     Pint = secprop->Add_int("gusmemsize",Property::Changeable::WhenIdle,-1);
02750     Pint->SetMinMax(-1,1024);
02751     Pint->Set_help("Amount of RAM on the Gravis Ultrasound in KB. Set to -1 for default.");
02752 
02753     Pdouble = secprop->Add_double("gus master volume",Property::Changeable::WhenIdle,0);
02754     Pdouble->SetMinMax(-120.0,6.0);
02755     Pdouble->Set_help("Master Gravis Ultrasound GF1 volume, in decibels. Reducing the master volume can help with games or demoscene productions where the music is too loud and clipping");
02756 
02757     Phex = secprop->Add_hex("gusbase",Property::Changeable::WhenIdle,0x240);
02758     Phex->Set_values(iosgus);
02759     Phex->Set_help("The IO base address of the Gravis Ultrasound.");
02760 
02761     Pint = secprop->Add_int("gusirq",Property::Changeable::WhenIdle,5);
02762     Pint->Set_values(irqsgus);
02763     Pint->Set_help("The IRQ number of the Gravis Ultrasound.");
02764 
02765     Pint = secprop->Add_int("gusdma",Property::Changeable::WhenIdle,3);
02766     Pint->Set_values(dmasgus);
02767     Pint->Set_help("The DMA channel of the Gravis Ultrasound.");
02768  
02769     Pstring = secprop->Add_string("irq hack",Property::Changeable::WhenIdle,"none");
02770     Pstring->Set_help("Specify a hack related to the Gravis Ultrasound IRQ to avoid crashes in a handful of games and demos.\n"
02771             "    none                   Emulate IRQs normally\n"
02772             "    cs_equ_ds              Do not fire IRQ unless two CPU segment registers match: CS == DS. Read Dosbox-X Wiki or source code for details.");
02773 
02774     Pstring = secprop->Add_string("gustype",Property::Changeable::WhenIdle,"classic");
02775     Pstring->Set_values(gustypes);
02776     Pstring->Set_help(  "Type of Gravis Ultrasound to emulate.\n"
02777                 "classic             Original Gravis Ultrasound chipset\n"
02778                 "classic37           Original Gravis Ultrasound with ICS Mixer (rev 3.7)\n"
02779                 "max                 Gravis Ultrasound MAX emulation (with CS4231 codec)\n"
02780                 "interwave           Gravis Ultrasound Plug & Play (interwave)");
02781 
02782     Pstring = secprop->Add_string("ultradir",Property::Changeable::WhenIdle,"C:\\ULTRASND");
02783     Pstring->Set_help(
02784         "Path to Ultrasound directory. In this directory\n"
02785         "there should be a MIDI directory that contains\n"
02786         "the patch files for GUS playback. Patch sets used\n"
02787         "with Timidity should work fine.");
02788 
02789     secprop = control->AddSection_prop("innova",&Null_Init,true);//done
02790     Pbool = secprop->Add_bool("innova",Property::Changeable::WhenIdle,false);
02791     Pbool->Set_help("Enable the Innovation SSI-2001 emulation.");
02792     Pint = secprop->Add_int("samplerate",Property::Changeable::WhenIdle,22050);
02793     Pint->Set_values(rates);
02794     Pint->Set_help("Sample rate of Innovation SSI-2001 emulation");
02795     Phex = secprop->Add_hex("sidbase",Property::Changeable::WhenIdle,0x280);
02796     Phex->Set_values(sidbaseno);
02797     Phex->Set_help("SID base port (typically 280h).");
02798     Pint = secprop->Add_int("quality",Property::Changeable::WhenIdle,0);
02799     Pint->Set_values(qualityno);
02800     Pint->Set_help("Set SID emulation quality level (0 to 3).");
02801 
02802     secprop = control->AddSection_prop("speaker",&Null_Init,true);//done
02803     Pbool = secprop->Add_bool("pcspeaker",Property::Changeable::WhenIdle,true);
02804     Pbool->Set_help("Enable PC-Speaker emulation.");
02805 
02806     /* added for "baoxiao-sanguozhi" which for some reason uses both port 61h bit 4 (DRAM refresh) and PIT timer 2 (PC speaker)
02807      * for game timing IN ADDITION TO the BIOS timer counter in the BIOS data area. Game does not set bit 0 itself, so if the
02808      * bit wasn't set, the game will hang when asking for a password. Setting this option to "true" tells the BIOS to start the
02809      * system with that bit set so games like that can run. [https://github.com/joncampbell123/dosbox-x/issues/1274].
02810      *
02811      * Note that setting clock gate enable will not make audible sound through the PC speaker unless bit 1 (output gate enable)
02812      * is also set. Setting bits [1:0] = to 01 is a way to cycle PIT timer 2 without making audible noise. */
02813     Pbool = secprop->Add_bool("pcspeaker clock gate enable at startup",Property::Changeable::WhenIdle,false);
02814     Pbool->Set_help("Start system with the clock gate (bit 0 of port 61h) on. Needed for some games that use the PC speaker for timing on IBM compatible systems.\n"
02815                     "This option has no effect in PC-98 mode.");
02816 
02817     Pint = secprop->Add_int("initial frequency",Property::Changeable::WhenIdle,-1);
02818     Pint->Set_help("PC speaker PIT timer is programmed to this frequency on startup. If the DOS game\n"
02819             "or demo causes a long audible beep at startup (leaving the gate open) try setting\n"
02820             "this option to 0 to silence the PC speaker until reprogrammed by the demo.\n"
02821             "Set to 0 for some early Abaddon demos including \"Torso\" and \"Cycling\".");
02822 
02823     Pint = secprop->Add_int("pcrate",Property::Changeable::WhenIdle,44100);
02824     Pint->Set_values(rates);
02825     Pint->Set_help("Sample rate of the PC-Speaker sound generation.");
02826 
02827     Pstring = secprop->Add_string("tandy",Property::Changeable::WhenIdle,"auto");
02828     Pstring->Set_values(tandys);
02829     Pstring->Set_help("Enable Tandy Sound System emulation. For 'auto', emulation is present only if machine is set to 'tandy'.");
02830 
02831     Pint = secprop->Add_int("tandyrate",Property::Changeable::WhenIdle,44100);
02832     Pint->Set_values(rates);
02833     Pint->Set_help("Sample rate of the Tandy 3-Voice generation.");
02834 
02835     Pbool = secprop->Add_bool("disney",Property::Changeable::WhenIdle,false);
02836     Pbool->Set_help("Enable Disney Sound Source emulation. (Covox Voice Master and Speech Thing compatible).");
02837     Pstring = secprop->Add_string("ps1audio",Property::Changeable::WhenIdle,"off");
02838     Pstring->Set_values(ps1opt);
02839     Pstring->Set_help("Enable PS1 audio emulation.");
02840     Pint = secprop->Add_int("ps1audiorate",Property::Changeable::OnlyAtStart,22050);
02841     Pint->Set_values(rates);
02842     Pint->Set_help("Sample rate of the PS1 audio emulation.");
02843 
02844     secprop=control->AddSection_prop("joystick",&Null_Init,false);//done
02845     Pstring = secprop->Add_string("joysticktype",Property::Changeable::WhenIdle,"auto");
02846     Pstring->Set_values(joytypes);
02847     Pstring->Set_help(
02848         "Type of joystick to emulate: auto (default), none,\n"
02849         "2axis (supports two joysticks),\n"
02850         "4axis (supports one joystick, first joystick used),\n"
02851         "4axis_2 (supports one joystick, second joystick used),\n"
02852         "fcs (Thrustmaster), ch (CH Flightstick).\n"
02853         "none disables joystick emulation.\n"
02854         "auto chooses emulation depending on real joystick(s).\n"
02855         "(Remember to reset dosbox's mapperfile if you saved it earlier)");
02856 
02857     Pbool = secprop->Add_bool("timed",Property::Changeable::WhenIdle,true);
02858     Pbool->Set_help("enable timed intervals for axis. Experiment with this option, if your joystick drifts (away).");
02859 
02860     Pbool = secprop->Add_bool("autofire",Property::Changeable::WhenIdle,false);
02861     Pbool->Set_help("continuously fires as long as you keep the button pressed.");
02862 
02863     Pbool = secprop->Add_bool("swap34",Property::Changeable::WhenIdle,false);
02864     Pbool->Set_help("swap the 3rd and the 4th axis. can be useful for certain joysticks.");
02865 
02866     Pbool = secprop->Add_bool("buttonwrap",Property::Changeable::WhenIdle,false);
02867     Pbool->Set_help("enable button wrapping at the number of emulated buttons.");
02868 
02869         /*improved joystick
02870          * each axis has its own deadzone and response
02871          * each axis index can be remapped, e.g. fix poor driver mappings
02872          */
02873 
02874         /* logical axes settings*/
02875         std::vector<int> sticks = { 2, 1 };
02876         for (auto i = 0u; i < sticks.size(); i++)
02877         {
02878                 const auto count = sticks[i];
02879                 for (auto j = 0; j < count; j++)
02880                 {
02881                         const auto joy = std::to_string(i + 1);
02882                         const auto stick = std::to_string(j + 1);
02883                         const auto name = "joy" + joy + "deadzone" + stick;
02884                         const auto help = "deadzone for joystick " + joy + " thumbstick " + stick + ".";
02885                         Pdouble = secprop->Add_double(name, Property::Changeable::WhenIdle, 0.25);
02886                         Pdouble->SetMinMax(0.0, 1.0);
02887                         Pdouble->Set_help(help);
02888                 }
02889         }
02890         for (auto i = 0u; i < sticks.size(); i++)
02891         {
02892                 const auto count = sticks[i];
02893                 for (auto j = 0; j < count; j++)
02894                 {
02895                         const auto joy = std::to_string(i + 1);
02896                         const auto stick = std::to_string(j + 1);
02897                         const auto name = "joy" + joy + "response" + stick;
02898                         const auto help = "response for joystick " + joy + " thumbstick " + stick + ".";
02899                         Pdouble = secprop->Add_double(name, Property::Changeable::WhenIdle, 1.0);
02900                         Pdouble->SetMinMax(-5.0, 5.0);
02901                         Pdouble->Set_help(help);
02902                 }
02903         }
02904 
02905         const auto joysticks = 2;
02906         const auto axes = 8;
02907         for (auto i = 0; i < joysticks; i++)
02908         {
02909                 for (auto j = 0; j < axes; j++)
02910                 {
02911                         const auto joy = std::to_string(i + 1);
02912                         const auto axis = std::to_string(j);
02913                         const auto propname = "joy" + joy + "axis" + axis;
02914                         Pint = secprop->Add_int(propname, Property::Changeable::WhenIdle, j);
02915                         Pint->SetMinMax(0, axes - 1);
02916                         const auto help = "axis for joystick " + joy + " axis " + axis + ".";
02917                         Pint->Set_help(help);
02918                 }
02919         }
02920         /*physical axes settings*/
02921         secprop = control->AddSection_prop("mapper", &Null_Init, true);
02922 
02923         const auto directions = 2;
02924         for (auto i = 0; i < joysticks; i++)
02925         {
02926                 for (auto j = 0; j < axes; j++)
02927                 {
02928                         for (auto k = 0; k < directions; k++)
02929                         {
02930                                 const auto joy = std::to_string(i + 1);
02931                                 const auto axis = std::to_string(j);
02932                                 const auto dir = k == 0 ? "-" : "+";
02933                                 const auto name = "joy" + joy + "deadzone" + axis + dir;
02934                                 Pdouble = secprop->Add_double(name, Property::Changeable::WhenIdle, 0.6);
02935                                 Pdouble->SetMinMax(0.0, 1.0);
02936                                 const auto help = "deadzone for joystick " + joy + " axis " + axis + dir;
02937                                 Pdouble->Set_help(help);
02938                         }
02939                 }
02940         }
02941 
02942 
02943     secprop=control->AddSection_prop("serial",&Null_Init,true);
02944 
02945     Pmulti_remain = secprop->Add_multiremain("serial1",Property::Changeable::WhenIdle," ");
02946     Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"dummy");
02947     Pmulti_remain->SetValue("dummy",/*init*/true);
02948     Pstring->Set_values(serials);
02949     Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
02950     Pmulti_remain->Set_help(
02951         "set type of device connected to com port.\n"
02952         "Can be disabled, dummy, modem, nullmodem, directserial.\n"
02953         "Additional parameters must be in the same line in the form of\n"
02954         "parameter:value. Parameter for all types is irq (optional).\n"
02955         "for directserial: realport (required), rxdelay (optional).\n"
02956         "                 (realport:COM1 realport:ttyS0).\n"
02957         "for modem: listenport (optional).\n"
02958         "for nullmodem: server, rxdelay, txdelay, telnet, usedtr,\n"
02959         "               transparent, port, inhsocket, nonlocal (all optional).\n"
02960         "               connections are limited to localhost unless you specify nonlocal:1\n"
02961         "Example: serial1=modem listenport:5000");
02962 
02963     Pmulti_remain = secprop->Add_multiremain("serial2",Property::Changeable::WhenIdle," ");
02964     Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"dummy");
02965     Pmulti_remain->SetValue("dummy",/*init*/true);
02966     Pstring->Set_values(serials);
02967     Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
02968     Pmulti_remain->Set_help("see serial1");
02969 
02970     Pmulti_remain = secprop->Add_multiremain("serial3",Property::Changeable::WhenIdle," ");
02971     Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"disabled");
02972     Pmulti_remain->SetValue("disabled",/*init*/true);
02973     Pstring->Set_values(serials);
02974     Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
02975     Pmulti_remain->Set_help("see serial1");
02976 
02977     Pmulti_remain = secprop->Add_multiremain("serial4",Property::Changeable::WhenIdle," ");
02978     Pstring = Pmulti_remain->GetSection()->Add_string("type",Property::Changeable::WhenIdle,"disabled");
02979     Pmulti_remain->SetValue("disabled",/*init*/true);
02980     Pstring->Set_values(serials);
02981     Pmulti_remain->GetSection()->Add_string("parameters",Property::Changeable::WhenIdle,"");
02982     Pmulti_remain->Set_help("see serial1");
02983 
02984     Pstring = secprop->Add_path("phonebookfile", Property::Changeable::OnlyAtStart, "phonebook-" VERSION ".txt");
02985     Pstring->Set_help("File used to map fake phone numbers to addresses.");
02986 
02987     // printer redirection parameters
02988     secprop = control->AddSection_prop("printer", &Null_Init);
02989     Pbool = secprop->Add_bool("printer", Property::Changeable::WhenIdle, true);
02990     Pbool->Set_help("Enable printer emulation.");
02991     //secprop->Add_string("fontpath","%%windir%%\\fonts");
02992     Pint = secprop->Add_int("dpi", Property::Changeable::WhenIdle, 360);
02993     Pint->Set_help("Resolution of printer (default 360).");
02994     Pint = secprop->Add_int("width", Property::Changeable::WhenIdle, 85);
02995     Pint->Set_help("Width of paper in 1/10 inch (default 85 = 8.5'').");
02996     Pint = secprop->Add_int("height", Property::Changeable::WhenIdle, 110);
02997     Pint->Set_help("Height of paper in 1/10 inch (default 110 = 11.0'').");
02998 #ifdef C_LIBPNG
02999     Pstring = secprop->Add_string("printoutput", Property::Changeable::WhenIdle, "png");
03000 #else
03001     Pstring = secprop->Add_string("printoutput", Property::Changeable::WhenIdle, "ps");
03002 #endif
03003     Pstring->Set_help("Output method for finished pages: \n"
03004 #ifdef C_LIBPNG
03005         "  png     : Creates PNG images (default)\n"
03006 #endif
03007         "  ps      : Creates PostScript\n"
03008         "  bmp     : Creates BMP images (very huge files, not recommended)\n"
03009 #if defined (WIN32)
03010         "  printer : Send to an actual printer (Print dialog will appear)"
03011 #endif
03012     );
03013 
03014     Pbool = secprop->Add_bool("multipage", Property::Changeable::WhenIdle, false);
03015     Pbool->Set_help("Adds all pages to one PostScript file or printer job until CTRL-F2 is pressed.");
03016 
03017     Pstring = secprop->Add_string("docpath", Property::Changeable::WhenIdle, ".");
03018     Pstring->Set_help("The path where the output files are stored.");
03019 
03020     Pint = secprop->Add_int("timeout", Property::Changeable::WhenIdle, 0);
03021     Pint->Set_help("(in milliseconds) if nonzero: the time the page will be ejected automatically after when no more data arrives at the printer.");
03022 
03023     // parallel ports
03024     secprop=control->AddSection_prop("parallel",&Null_Init,true);
03025     Pstring = secprop->Add_string("parallel1",Property::Changeable::WhenIdle,"disabled");
03026     Pstring->Set_help(
03027             "parallel1-3 -- set type of device connected to lpt port.\n"
03028             "Can be:\n"
03029             "   reallpt (direct parallel port passthrough),\n"
03030             "   file (records data to a file or passes it to a device),\n"
03031             "   printer (virtual dot-matrix printer, see [printer] section)\n"
03032             "       disney (attach Disney Sound Source emulation to this port)\n"
03033             "Additional parameters must be in the same line in the form of\n"
03034             "parameter:value.\n"
03035             "  for reallpt:\n"
03036             "  Windows:\n"
03037             "    realbase (the base address of your real parallel port).\n"
03038             "      Default: 378\n"
03039             "    ecpbase (base address of the ECP registers, optional).\n"
03040             "  Linux: realport (the parallel port device i.e. /dev/parport0).\n"
03041             "  for file: \n"
03042             "    dev:<devname> (i.e. dev:lpt1) to forward data to a device,\n"
03043             "    or append:<file> appends data to the specified file.\n"
03044             "    Without the above parameters data is written to files in the capture dir.\n"
03045             "    Additional parameters: timeout:<milliseconds> = how long to wait before\n"
03046             "    closing the file on inactivity (default:500), addFF to add a formfeed when\n"
03047             "    closing, addLF to add a linefeed if the app doesn't, cp:<codepage number>\n"
03048             "    to perform codepage translation, i.e. cp:437\n"
03049             "  for printer:\n"
03050             "    printer still has it's own configuration section above."
03051     );
03052     Pstring = secprop->Add_string("parallel2",Property::Changeable::WhenIdle,"disabled");
03053     Pstring->Set_help("see parallel1");
03054     Pstring = secprop->Add_string("parallel3",Property::Changeable::WhenIdle,"disabled");
03055     Pstring->Set_help("see parallel1");
03056 
03057     Pbool = secprop->Add_bool("dongle",Property::Changeable::WhenIdle,false);
03058     Pbool->Set_help("Enable dongle");
03059 
03060     /* All the DOS Related stuff, which will eventually start up in the shell */
03061     secprop=control->AddSection_prop("dos",&Null_Init,false);//done
03062     Pbool = secprop->Add_bool("xms",Property::Changeable::WhenIdle,true);
03063     Pbool->Set_help("Enable XMS support.");
03064 
03065     Pint = secprop->Add_int("xms handles",Property::Changeable::WhenIdle,0);
03066     Pint->Set_help("Number of XMS handles available for the DOS environment, or 0 to use a reasonable default");
03067 
03068     Pbool = secprop->Add_bool("shell configuration as commands",Property::Changeable::WhenIdle,false);
03069     Pbool->Set_help("Allow entering dosbox-x.conf configuration parameters as shell commands to get and set settings.\n"
03070                     "This is disabled by default to avoid conflicts between commands and executables.\n"
03071                     "It is recommended to get and set dosbox-x.conf settings using the CONFIG command instead.\n"
03072                     "Compatibility with DOSBox SVN can be improved by enabling this option.");
03073 
03074     Pbool = secprop->Add_bool("hma",Property::Changeable::WhenIdle,true);
03075     Pbool->Set_help("Report through XMS that HMA exists (not necessarily available)");
03076 
03077     Pbool = secprop->Add_bool("hma allow reservation",Property::Changeable::WhenIdle,true);
03078     Pbool->Set_help("Allow TSR and application (anything other than the DOS kernel) to request control of the HMA.\n"
03079             "They will not be able to request control however if the DOS kernel is configured to occupy the HMA (DOS=HIGH)");
03080 
03081     Pint = secprop->Add_int("hard drive data rate limit",Property::Changeable::WhenIdle,-1);
03082     Pint->Set_help("Slow down (limit) hard disk throughput. This setting controls the limit in bytes/second.\n"
03083                    "Set to 0 to disable the limit, or -1 to use a reasonable default.");
03084 
03085     Pstring = secprop->Add_string("drive z is remote",Property::Changeable::WhenIdle,"auto");
03086     Pstring->Set_values(truefalseautoopt);
03087     Pstring->Set_help("If set, DOS will report drive Z as remote. If not set, DOS will report drive Z as local.\n"
03088                       "If auto (default), DOS will report drive Z as remote or local depending on the program.\n"
03089                       "Set this option to true to prevent SCANDISK.EXE from attempting scan and repair drive Z:\n"
03090                       "which is impossible since Z: is a virtual drive not backed by a disk filesystem.");
03091 
03092     Pint = secprop->Add_int("hma minimum allocation",Property::Changeable::WhenIdle,0);
03093     Pint->Set_help("Minimum allocation size for HMA in bytes (equivalent to /HMAMIN= parameter).");
03094 
03095     Pbool = secprop->Add_bool("ansi.sys",Property::Changeable::WhenIdle,true);
03096     Pbool->Set_help("If set (by default), ANSI.SYS emulation is on. If clear, ANSI.SYS is not emulated and will not appear to be installed.\n"
03097                     "NOTE: This option has no effect in PC-98 mode where MS-DOS systems integrate ANSI.SYS into the DOS kernel.");
03098 
03099     Pbool = secprop->Add_bool("log console",Property::Changeable::WhenIdle,false);
03100     Pbool->Set_help("If set, log DOS CON output to the log file.");
03101 
03102     Pint = secprop->Add_int("dos sda size",Property::Changeable::WhenIdle,0);
03103     Pint->Set_help("SDA (swappable data area) size, in bytes. Set to 0 to use a reasonable default.");
03104 
03105     Pint = secprop->Add_int("hma free space",Property::Changeable::WhenIdle,34*1024); /* default 34KB (TODO: How much does MS-DOS 5.0 usually occupy?) */
03106     Pint->Set_help("Controls the amount of free space available in HMA. This setting is not meaningful unless the\n"
03107             "DOS kernel occupies HMA and the emulated DOS version is at least 5.0.");
03108 
03109     Pstring = secprop->Add_string("cpm compatibility mode",Property::Changeable::WhenIdle,"auto");
03110     Pstring->Set_values(cpm_compat_modes);
03111     Pstring->Set_help(
03112             "This controls how the DOS kernel sets up the CP/M compatibility code in the PSP segment.\n"
03113             "Several options are provided to emulate one of several undocumented behaviors related to the CP/M entry point.\n"
03114             "If set to auto, DOSBox-X will pick the best option to allow it to work properly.\n"
03115             "Unless set to 'off', this option will require the DOS kernel to occupy the first 256 bytes of the HMA memory area\n"
03116             "to prevent crashes when the A20 gate is switched on.\n"
03117             "   auto      Pick the best option\n"
03118             "   off       Turn off the CP/M entry point (program will abort if called)\n"
03119             "   msdos2    MS-DOS 2.x behavior, offset field also doubles as data segment size\n"
03120             "   msdos5    MS-DOS 5.x behavior, entry point becomes one of two fixed addresses\n"
03121             "   direct    Non-standard behavior, encode the CALL FAR directly to the entry point rather than indirectly");
03122 
03123     Pbool = secprop->Add_bool("share",Property::Changeable::WhenIdle,true);
03124     Pbool->Set_help("Report SHARE.EXE as resident. Does not actually emulate SHARE functions.");
03125 
03126     Phex = secprop->Add_hex("minimum dos initial private segment", Property::Changeable::WhenIdle,0);
03127     Phex->Set_help("In non-mainline mapping mode, where DOS structures are allocated from base memory, this sets the\n"
03128             "minimum segment value. Recommended value is 0x70. You may reduce the value down to 0x50 if freeing\n"
03129             "up more memory is important. Set to 0 for default.");
03130 
03131     Phex = secprop->Add_hex("minimum mcb segment", Property::Changeable::WhenIdle,0);
03132     Phex->Set_help("Minimum segment value to begin memory allocation from, in hexadecimal. Set to 0 for default.\n"
03133             "You can increase available DOS memory by reducing this value down to as low as 0x51, however\n"
03134             "setting it to low can cause some DOS programs to crash or run erratically, and some DOS games\n"
03135             "and demos to cause intermittent static noises when using Sound Blaster output. DOS programs\n"
03136             "compressed with Microsoft EXEPACK will not run if the minimum MCB segment is below 64KB.");
03137 
03138     Phex = secprop->Add_hex("minimum mcb free", Property::Changeable::WhenIdle,0);
03139     Phex->Set_help("Minimum free segment value to leave free. At startup, the DOS kernel will allocate memory\n"
03140                    "up to this point. This can be used to deal with EXEPACK issues or DOS programs that cannot\n"
03141                    "be loaded too low in memory. This differs from 'minimum mcb segment' in that this affects\n"
03142                    "the lowest free block instead of the starting point of the mcb chain.");
03143 
03144     // TODO: Enable by default WHEN the 'SD' signature becomes valid, and a valid device list within
03145     //       is emulated properly.
03146     Pbool = secprop->Add_bool("enable dummy device mcb",Property::Changeable::OnlyAtStart,false);
03147     Pbool->Set_help("If set (default), allocate a fake device MCB at the base of conventional memory.\n"
03148             "Clearing this option can reclaim a small amount of conventional memory at the expense of\n"
03149             "some minor DOS compatibility.");
03150 
03151     Pint = secprop->Add_int("maximum environment block size on exec", Property::Changeable::WhenIdle,-1);
03152     Pint->SetMinMax(-1,65535);
03153     Pint->Set_help("Maximum environment block size to copy for child processes. Set to -1 for default.");
03154 
03155     Pint = secprop->Add_int("additional environment block size on exec", Property::Changeable::WhenIdle,-1);
03156     Pint->SetMinMax(-1,65535);
03157     Pint->Set_help("When executing a program, compute the size of the parent block then add this amount to allow for a few additional variables.\n"
03158             "If the subprocesses will never add/modify the environment block, you can free up a few additional bytes by setting this to 0.\n"
03159             "Set to -1 for default setting.");
03160 
03161     // DEPRECATED, REMOVE
03162     Pbool = secprop->Add_bool("enable a20 on windows init",Property::Changeable::OnlyAtStart,false);
03163     Pbool->Set_help("If set, DOSBox-X will enable the A20 gate when Windows 3.1/9x broadcasts the INIT message\n"
03164             "at startup. Windows 3.1 appears to make assumptions at some key points on startup about\n"
03165             "A20 that don't quite hold up and cause Windows 3.1 to crash when you set A20 emulation\n"
03166             "to a20=mask as opposed to a20=fast. This option is enabled by default.");
03167 
03168     Pbool = secprop->Add_bool("zero memory on xms memory allocation",Property::Changeable::OnlyAtStart,false);
03169     Pbool->Set_help("If set, memory returned by XMS allocation call is zeroed first. This is NOT what\n"
03170             "DOS actually does, but if set, can help certain DOS games and demos cope with problems\n"
03171             "related to uninitialized variables in extended memory. When enabled this option may\n"
03172             "incur a slight to moderate performance penalty.");
03173 
03174     Pstring = secprop->Add_string("dosv",Property::Changeable::WhenIdle,"off");
03175     Pstring->Set_values(dosv_settings);
03176     Pstring->Set_help("Enable DOS/V emulation and specify which version to emulate. This option is intended for\n"
03177             "use with games or software originating from Asia that use the double byte character set\n"
03178             "encodings and the DOS/V extensions to display Japanese, Chinese, or Korean text.\n"
03179             "Note that enabling DOS/V replaces 80x25 text mode (INT 10h mode 3) with a EGA/VGA graphics\n"
03180             "mode that emulates text mode to display the characters and may be incompatible with non-Asian\n"
03181             "software that assumes direct access to the text mode via segment 0xB800.\n"
03182             "WARNING: This option is very experimental at this time.");
03183 
03184     Pstring = secprop->Add_string("ems",Property::Changeable::WhenIdle,"true");
03185     Pstring->Set_values(ems_settings);
03186     Pstring->Set_help("Enable EMS support. The default (=true) provides the best\n"
03187         "compatibility but certain applications may run better with\n"
03188         "other choices, or require EMS support to be disabled (=false)\n"
03189         "to work at all.");
03190 
03191     Pbool = secprop->Add_bool("vcpi",Property::Changeable::OnlyAtStart,true);
03192     Pbool->Set_help("If set and expanded memory is enabled, also emulate VCPI.");
03193 
03194     Pbool = secprop->Add_bool("unmask timer on disk io",Property::Changeable::OnlyAtStart,false);
03195     Pbool->Set_help("If set, INT 21h emulation will unmask IRQ 0 (timer interrupt) when the application opens/closes/reads/writes files.");
03196 
03197     Pbool = secprop->Add_bool("zero int 67h if no ems",Property::Changeable::OnlyAtStart,true);
03198     Pbool->Set_help("If ems=false, leave interrupt vector 67h zeroed out (default true).\n"
03199             "This is a workaround for games or demos that try to detect EMS by whether or not INT 67h is 0000:0000 rather than a proper test.\n"
03200             "This option also affects whether INT 67h is zeroed when booting a guest OS");
03201 
03202     Pbool = secprop->Add_bool("zero unused int 68h",Property::Changeable::OnlyAtStart,false);
03203     Pbool->Set_help("Leave INT 68h zero at startup.\n"
03204             "Set this to true for certain games that use INT 68h in unusual ways that require a zero value.\n"
03205             "Note that the vector is left at zero anyway when machine=cga.\n"
03206             "This is needed to properly run 1988 game 'PopCorn'.");
03207 
03208     /* FIXME: The vm86 monitor in src/ints/ems.cpp is not very stable! Option is default OFF until stabilized! */
03209     Pbool = secprop->Add_bool("emm386 startup active",Property::Changeable::OnlyAtStart,false);
03210     Pbool->Set_help("If set and expanded memory is set to emulate emm386, start the DOS machine with EMM386.EXE active\n"
03211             "(running the 16-bit DOS environment from within Virtual 8086 mode). If you will be running anything\n"
03212             "that involves a DOS extender you will also need to enable the VCPI interface as well.");
03213 
03214     Pbool = secprop->Add_bool("zero memory on ems memory allocation",Property::Changeable::OnlyAtStart,false);
03215     Pbool->Set_help("If set, memory returned by EMS allocation call is zeroed first. This is NOT what\n"
03216             "DOS actually does, but if set, can help certain DOS games and demos cope with problems\n"
03217             "related to uninitialized variables in expanded memory. When enabled this option may\n"
03218             "incur a slight to moderate performance penalty.");
03219 
03220     Pint = secprop->Add_int("ems system handle memory size",Property::Changeable::WhenIdle,384);
03221     Pint->Set_help("Amount of memory associated with system handle, in KB");
03222 
03223     Pbool = secprop->Add_bool("ems system handle on even megabyte",Property::Changeable::WhenIdle,false);
03224     Pbool->Set_help("If set, try to allocate the EMM system handle on an even megabyte.\n"
03225             "If the DOS game or demo fiddles with the A20 gate while using EMM386.EXE emulation in virtual 8086 mode, setting this option may help prevent crashes.\n"
03226             "However, forcing allocation on an even megabyte will also cause some extended memory fragmentation and reduce the\n"
03227             "overall amount of extended memory available to the DOS game depending on whether it expects large contiguous chunks\n"
03228             "of extended memory.");
03229 
03230     Pbool = secprop->Add_bool("umb",Property::Changeable::WhenIdle,true);
03231     Pbool->Set_help("Enable UMB support.");
03232 
03233     Phex = secprop->Add_hex("umb start",Property::Changeable::OnlyAtStart,0); /* <- (0=auto) 0xD000 is mainline DOSBox compatible behavior */
03234     Phex->Set_help("UMB region starting segment");
03235 
03236     Phex = secprop->Add_hex("umb end",Property::Changeable::OnlyAtStart,0); /* <- (0=auto) 0xEFFF is mainline DOSBox compatible (where base=0xD000 and size=0x2000) */
03237     Phex->Set_help("UMB region last segment");
03238 
03239     Pbool = secprop->Add_bool("kernel allocation in umb",Property::Changeable::OnlyAtStart,false);
03240     Pbool->Set_help("If set, dynamic kernel allocation=1, and private area in umb=1, all kernel structures will be allocated from the private area in UMB.\n"
03241             "If you intend to run Windows 3.1 in DOSBox-X, you must set this option to false else Windows 3.1 will not start.");
03242 
03243     Pbool = secprop->Add_bool("keep umb on boot",Property::Changeable::OnlyAtStart,false);
03244     Pbool->Set_help("If emulating UMBs, keep the UMB around after boot (Mainline DOSBox behavior). If clear, UMB is unmapped when you boot an operating system.");
03245 
03246     Pbool = secprop->Add_bool("keep private area on boot",Property::Changeable::OnlyAtStart,false);
03247     Pbool->Set_help("If set, keep the DOSBox-X private area around after boot (Mainline DOSBox behavior). If clear, unmap and discard the private area when you boot an operating system.");
03248 
03249     Pbool = secprop->Add_bool("private area in umb",Property::Changeable::WhenIdle,true);
03250     Pbool->Set_help("If set, keep private DOS segment in upper memory block, usually segment 0xC800 (Mainline DOSBox behavior)\n"
03251             "If clear, place private DOS segment at the base of system memory (just below the MCB)");
03252 
03253     Pstring = secprop->Add_string("ver",Property::Changeable::WhenIdle,"");
03254     Pstring->Set_help("Set DOS version. Specify as major.minor format. A single number is treated as the major version (compatible with LFN support). Common settings are:\n"
03255             "auto (or unset)                  Pick a DOS kernel version automatically\n"
03256             "3.3                              MS-DOS 3.3 emulation (not tested!)\n"
03257             "5.0                              MS-DOS 5.0 emulation (recommended for DOS gaming)\n"
03258             "6.22                             MS-DOS 6.22 emulation\n"
03259             "7.0                              MS-DOS 7.0 (or Windows 95 pure DOS mode) emulation\n"
03260             "7.1                              MS-DOS 7.1 (or Windows 98 pure DOS mode) emulation\n"
03261             "Long filename (LFN) support will be enabled with a reported DOS version of 7.0 or higher with \"lfn=auto\" (default).\n"
03262                         "Similarly, FAT32 disk images will be supported with a reported DOS version of 7.1 or higher.\n");
03263 
03264     Pstring = secprop->Add_string("lfn",Property::Changeable::WhenIdle,"auto");
03265     Pstring->Set_values(lfn_settings);
03266     Pstring->Set_help("Enable long filename support. If set to auto (default), it is enabled if the reported DOS version is at least 7.0.\n"
03267                       "If set to autostart, the builtin VER command won't activate/disactivate LFN support according to the reported DOS version.");
03268 
03269     Pbool = secprop->Add_bool("automount",Property::Changeable::WhenIdle,true);
03270     Pbool->Set_help("Enable automatic drive mounting in Windows.");
03271 
03272     Pbool = secprop->Add_bool("automountall",Property::Changeable::OnlyAtStart,false);
03273     Pbool->Set_help("Automatically mount all available Windows drives at start.");
03274 
03275     Pbool = secprop->Add_bool("int33",Property::Changeable::WhenIdle,true);
03276     Pbool->Set_help("Enable INT 33H (mouse) support.");
03277 
03278     Pbool = secprop->Add_bool("int33 hide host cursor if interrupt subroutine",Property::Changeable::WhenIdle,true);
03279     Pbool->Set_help("If set, the cursor on the host will be hidden if the DOS application provides it's own\n"
03280                     "interrupt subroutine for the mouse driver to call, which is usually an indication that\n"
03281                     "the DOS game wishes to draw the cursor with it's own support routines (DeluxePaint II).");
03282 
03283     Pbool = secprop->Add_bool("int33 hide host cursor when polling",Property::Changeable::WhenIdle,false);
03284     Pbool->Set_help("If set, the cursor on the host will be hidden even if the DOS application has also\n"
03285                     "hidden the cursor in the guest, as long as the DOS application is polling position\n"
03286                     "and button status. This can be useful for DOS programs that draw the cursor on their\n"
03287                     "own instead of using the mouse driver, including most games and DeluxePaint II.");
03288 
03289     Pbool = secprop->Add_bool("int33 disable cell granularity",Property::Changeable::WhenIdle,false);
03290     Pbool->Set_help("If set, the mouse pointer position is reported at full precision (as if 640x200 coordinates) in all modes.\n"
03291                     "If not set, the mouse pointer position is rounded to the top-left corner of a character cell in text modes.\n"
03292                     "This option is OFF by default.");
03293 
03294     Pbool = secprop->Add_bool("int 13 extensions",Property::Changeable::WhenIdle,true);
03295     Pbool->Set_help("Enable INT 13h extensions (functions 0x40-0x48). You will need this enabled if the virtual hard drive image is 8.4GB or larger.");
03296 
03297     Pbool = secprop->Add_bool("biosps2",Property::Changeable::OnlyAtStart,true);
03298     Pbool->Set_help("Emulate BIOS INT 15h PS/2 mouse services\n"
03299         "Note that some OS's like Microsoft Windows neither use INT 33h nor\n"
03300         "probe the AUX port directly and depend on this BIOS interface exclusively\n"
03301         "for PS/2 mouse support. In other cases there is no harm in leaving this enabled");
03302 
03303     /* bugfix for Yodel "mayday" demo */
03304     /* TODO: Set this option to default to "true" if it turns out most BIOSes unmask the IRQ during INT 15h AH=86 WAIT */
03305     Pbool = secprop->Add_bool("int15 wait force unmask irq",Property::Changeable::OnlyAtStart,true);
03306     Pbool->Set_help("Some demos or games mistakingly use INT 15h AH=0x86 (WAIT) while leaving the IRQs needed for it masked.\n"
03307             "If this option is set (by default), the necessary IRQs will be unmasked when INT 15 AH=0x86 is used so that the game or demo does not hang.");
03308 
03309     Pbool = secprop->Add_bool("int15 mouse callback does not preserve registers",Property::Changeable::OnlyAtStart,false);
03310     Pbool->Set_help("Set to true if the guest OS or DOS program assigns an INT 15h mouse callback,\n"
03311             "but does not properly preserve CPU registers. Diagnostic function only (default off).");
03312 
03313     Pstring = secprop->Add_string("keyboardlayout",Property::Changeable::WhenIdle, "auto");
03314     Pstring->Set_help("Language code of the keyboard layout (or none).");
03315 
03316     Pbool = secprop->Add_bool("dbcs",Property::Changeable::OnlyAtStart,true);
03317     Pbool->Set_help("Enable DBCS table.\n"
03318             "CAUTION: Some software will crash without the DBCS table, including the Open Watcom installer.\n");
03319 
03320     Pbool = secprop->Add_bool("filenamechar",Property::Changeable::OnlyAtStart,true);
03321     Pbool->Set_help("Enable filename char table");
03322 
03323     Pbool = secprop->Add_bool("collating and uppercase",Property::Changeable::OnlyAtStart,true);
03324     Pbool->Set_help("Enable collating and uppercase table");
03325 
03326     // DEPRECATED, REMOVE
03327     Pbool = secprop->Add_bool("con device use int 16h to detect keyboard input",Property::Changeable::OnlyAtStart,true);
03328     Pbool->Set_help("If set, use INT 16h to detect keyboard input (MS-DOS 6.22 behavior). If clear, detect keyboard input by\n"
03329             "peeking into the BIOS keyboard buffer (Mainline DOSBox behavior). You will need to set this\n"
03330             "option for programs that hook INT 16h to handle keyboard input ahead of the DOS console.\n"
03331             "Microsoft Scandisk needs this option to respond to keyboard input correctly.");
03332 
03333     Pbool = secprop->Add_bool("zero memory on int 21h memory allocation",Property::Changeable::OnlyAtStart,false);
03334     Pbool->Set_help("If set, memory returned by the INT 21h allocation call is zeroed first. This is NOT what\n"
03335             "DOS actually does, but if set, can help certain DOS games and demos cope with problems\n"
03336             "related to uninitialized variables in the data or stack segment. If you intend to run a\n"
03337             "game or demo known to have this problem (Second Unreal, for example), set to true, else\n"
03338             "set to false. When enabled this option may incur a slight to moderate performance penalty.");
03339 
03340     Pstring = secprop->Add_string("dos clipboard device enable",Property::Changeable::WhenIdle, "false");
03341     Pstring->Set_help("If enabled, a DOS device will be added for bidirectional communications with the Windows clipboard.\n"
03342                         "Setting to \"read\" will only allow read access, and setting to \"write\" will only allow write access.\n"
03343                         "Setting to \"full\" or \"true\" enables both; setting to \"dummy\" or \"false\" disables the access or device.\n"
03344             "The default device name is CLIP$, but can be changed with the \"dos clipboard device name\" setting below.");
03345 
03346     Pstring = secprop->Add_string("dos clipboard device name",Property::Changeable::WhenIdle, "CLIP$");
03347     Pstring->Set_help("Set DOS device name (up to 8 characters) for bidirectional communications with the Windows clipboard.\n"
03348             "If unset or invalid, the default name CLIP$ will be used (e.g. \"TYPE CLIP$\" shows the clipboard contents).\n"
03349                         "It has no effect if \"dos clipboard device enable\" is false, and it is deactivated if the secure mode is enabled.");
03350 
03351     secprop=control->AddSection_prop("ipx",&Null_Init,true);
03352     Pbool = secprop->Add_bool("ipx",Property::Changeable::WhenIdle, false);
03353     Pbool->Set_help("Enable ipx over UDP/IP emulation.");
03354 
03355     secprop=control->AddSection_prop("ne2000",&Null_Init,true);
03356     MSG_Add("NE2000_CONFIGFILE_HELP",
03357         "macaddr -- The physical address the emulator will use on your network.\n"
03358         "           If you have multiple DOSBoxes running on your network,\n"
03359         "           this has to be changed. Modify the last three number blocks.\n"
03360         "           I.e. AC:DE:48:88:99:AB.\n"
03361         "realnic -- Specifies which of your network interfaces is used.\n"
03362         "           Write \'list\' here to see the list of devices in the\n"
03363         "           Status Window. Then make your choice and put either the\n"
03364         "           interface number (2 or something) or a part of your adapters\n"
03365         "           name, e.g. VIA here.\n"
03366 
03367     );
03368 
03369     Pbool = secprop->Add_bool("ne2000", Property::Changeable::WhenIdle, false);
03370     Pbool->Set_help("Enable Ethernet passthrough. Requires [Win]Pcap.");
03371 
03372     Phex = secprop->Add_hex("nicbase", Property::Changeable::WhenIdle, 0x300);
03373     Phex->Set_help("The base address of the NE2000 board.");
03374 
03375     Pint = secprop->Add_int("nicirq", Property::Changeable::WhenIdle, 3);
03376     Pint->Set_help("The interrupt it uses. Note serial2 uses IRQ3 as default.");
03377 
03378     Pstring = secprop->Add_string("macaddr", Property::Changeable::WhenIdle,"AC:DE:48:88:99:AA");
03379     Pstring->Set_help("The physical address the emulator will use on your network.\n"
03380         "If you have multiple DOSBoxes running on your network,\n"
03381         "this has to be changed for each. AC:DE:48 is an address range reserved for\n"
03382         "private use, so modify the last three number blocks.\n"
03383         "I.e. AC:DE:48:88:99:AB.");
03384 
03385     /* TODO: Change default to "nat" and then begin implementing support for emulating
03386      *       an ethernet connection with DOSBox-X as a NAT/firewall between the guest
03387      *       and the OS. Sort of like "NAT" mode in VirtualBox. When that works, we
03388      *       can then compile NE2000 support with and without libpcap/winpcap support. */
03389     Pstring = secprop->Add_string("realnic", Property::Changeable::WhenIdle,"list");
03390     Pstring->Set_help("Specifies which of your network interfaces is used.\n"
03391         "Write \'list\' here to see the list of devices in the\n"
03392         "Status Window. Then make your choice and put either the\n"
03393         "interface number (2 or something) or a part of your adapters\n"
03394         "name, e.g. VIA here.");
03395 
03396     /* floppy controller emulation options and setup */
03397     secprop=control->AddSection_prop("fdc, primary",&Null_Init,false);
03398 
03399     /* Primary FDC on by default, secondary is not. Most PCs have only one floppy controller. */
03400     Pbool = secprop->Add_bool("enable",Property::Changeable::OnlyAtStart,false);
03401     Pbool->Set_help("Enable floppy controller interface");
03402 
03403     Pbool = secprop->Add_bool("pnp",Property::Changeable::OnlyAtStart,true);
03404     Pbool->Set_help("List floppy controller in ISA PnP BIOS enumeration");
03405 
03406     Pint = secprop->Add_int("irq",Property::Changeable::WhenIdle,0/*use FDC default*/);
03407     Pint->Set_help("IRQ used by floppy controller. Set to 0 for default.\n"
03408         "WARNING: Setting the IRQ to non-standard values will not work unless the guest OS is using the ISA PnP BIOS to detect the floppy controller.\n"
03409         "         Setting the IRQ to one already occupied by another device or IDE controller will trigger \"resource conflict\" errors in Windows 95.\n"
03410         "         Normally, floppy controllers use IRQ 6.");
03411 
03412     Phex = secprop->Add_hex("io",Property::Changeable::WhenIdle,0/*use FDC default*/);
03413     Phex->Set_help("Base I/O port for floppy controller. Set to 0 for default.\n"
03414         "WARNING: Setting the I/O port to non-standard values will not work unless the guest OS is using the ISA PnP BIOS to detect the IDE controller.\n"
03415         "         Standard I/O ports are 3F0 and 370.");
03416 
03417     Pint = secprop->Add_int("dma",Property::Changeable::WhenIdle,-1/*use FDC default*/);
03418     Pint->Set_help("DMA channel for floppy controller. Set to -1 for default.\n"
03419         "WARNING: Setting the DMA channel to non-standard values will not work unless the guest OS is using the ISA PnP BIOS to detect the IDE controller.\n"
03420         "         Standard DMA channel is 2.");
03421 
03422     Pbool = secprop->Add_bool("int13fakev86io",Property::Changeable::WhenIdle,false);
03423     Pbool->Set_help(
03424         "If set, certain INT 13h commands will cause floppy emulation to issue fake CPU I/O\n"
03425         "traps (GPF) in virtual 8086 mode and a fake IRQ signal. You must enable this\n"
03426         "option if you want 32-bit floppy access in Windows 95 to work with DOSBox-X.");
03427 
03428     Pbool = secprop->Add_bool("instant mode",Property::Changeable::WhenIdle,false);
03429     Pbool->Set_help(
03430         "If set, all floppy operations are 'instantaneous', they are carried\n"
03431         "out without any delay. Real hardware of course has motor, command\n"
03432         "and data I/O delays and so this option is off by default for realistic\n"
03433         "emulation.");
03434 
03435     Pbool = secprop->Add_bool("auto-attach to int 13h",Property::Changeable::WhenIdle,true);
03436     Pbool->Set_help(
03437         "If set, DOSBox-X will automatically attach a disk image as being\n"
03438         "inserted into a floppy drive attached to the controller when imgmount is used\n"
03439         "to mount a disk image to drive 0/1 or A/B. If not set, you must specify\n"
03440         "the -fdc option to imgmount to attach drive A/B to the floppy controller\n"
03441         "manually. You must use the -fdc option regardless if loading floppies into\n"
03442         "drives attached to any other FDC than the primary controller");
03443 
03444     /* FIXME: From http://wiki.osdev.org/Floppy_Disk_Controller#Configure
03445      *
03446      *    "The three modes are PC-AT mode, PS/2 mode, and Model 30 mode. The most likely mode ... is model 30 mode.
03447      *    You may find some pre-1996 Pentium machines using PS/2 mode. You can ignore PC-AT mode."
03448      *
03449      *    What? What the fuck are you talking about?
03450      *
03451      *    "AT mode" seems to imply the presense of port 3F7. PS/2 mode seems to imply the presense of 3F0-3F1 and 3F7.
03452      *    A Toshiba laptop (Satellite Pro 465CDX) has port 3F7 but not 3F0-3F1. By other documentation I've found, that
03453      *    means this laptop (which came out late 1997) is running in AT mode! There's plenty of hardware running in both
03454      *    PS/2 and AT mode, even some very old stuff in my pile of junk dating back to 1990!
03455      *
03456      *    Somehow I think this information is as correct as their ATAPI programming docs on how to read CD-ROM
03457      *    sectors: it's a start but it's mostly wrong. Hopefully DOSLIB will shed light on what the real differences
03458      *    are and what is most common. --J.C. */
03459     Pstring = secprop->Add_string("mode",Property::Changeable::WhenIdle,"ps2");
03460     Pstring->Set_help(
03461         "Floppy controller mode. What the controller acts like.\n"
03462         "  ps2                          PS/2 mode (most common)\n"
03463         "  ps2_model30                  PS/2 model 30\n"
03464         "  at                           AT mode\n"
03465         "  xt                           PC/XT mode");
03466 
03467     /* FIXME: Not yet implemented. Future plans */
03468     Pstring = secprop->Add_string("chip",Property::Changeable::WhenIdle,"82077aa");
03469     Pstring->Set_help(
03470         "Floppy controller chipset\n"
03471         "  82077aa                      Intel 82077AA chipset\n"
03472         "  82072                        Intel 82072 chipset\n"
03473         "  nec_uPD765                   NEC uPD765 chipset\n"
03474         "  none                         No chipset (For PC/XT mode)");
03475 
03476     /* IDE emulation options and setup */
03477     for (size_t i=0;i < MAX_IDE_CONTROLLERS;i++) {
03478         secprop=control->AddSection_prop(ide_names[i],&Null_Init,false);//done
03479 
03480         /* Primary and Secondary are on by default, Teritary and Quaternary are off by default.
03481          * Throughout the life of the IDE interface it was far more common for a PC to have just
03482          * a Primary and Secondary interface */
03483         Pbool = secprop->Add_bool("enable",Property::Changeable::OnlyAtStart,(i < 2) ? true : false);
03484         if (i == 0) Pbool->Set_help("Enable IDE interface");
03485 
03486         Pbool = secprop->Add_bool("pnp",Property::Changeable::OnlyAtStart,true);
03487         if (i == 0) Pbool->Set_help("List IDE device in ISA PnP BIOS enumeration");
03488 
03489         Pint = secprop->Add_int("irq",Property::Changeable::WhenIdle,0/*use IDE default*/);
03490         if (i == 0) Pint->Set_help("IRQ used by IDE controller. Set to 0 for default.\n"
03491                 "WARNING: Setting the IRQ to non-standard values will not work unless the guest OS is using the ISA PnP BIOS to detect the IDE controller.\n"
03492                 "         Setting the IRQ to one already occupied by another device or IDE controller will trigger \"resource conflict\" errors in Windows 95.\n"
03493                 "         Using IRQ 9, 12, 13, or IRQ 2-7 may cause problems with MS-DOS CD-ROM drivers.");
03494 
03495         Phex = secprop->Add_hex("io",Property::Changeable::WhenIdle,0/*use IDE default*/);
03496         if (i == 0) Phex->Set_help("Base I/O port for IDE controller. Set to 0 for default.\n"
03497                 "WARNING: Setting the I/O port to non-standard values will not work unless the guest OS is using the ISA PnP BIOS to detect the IDE controller.\n"
03498                 "         Using any port other than 1F0, 170, 1E8 or 168 can prevent MS-DOS CD-ROM drivers from detecting the IDE controller.");
03499 
03500         Phex = secprop->Add_hex("altio",Property::Changeable::WhenIdle,0/*use IDE default*/);
03501         if (i == 0) Phex->Set_help("Alternate I/O port for IDE controller (alt status, etc). Set to 0 for default.\n"
03502                 "WARNING: Setting the I/O port to non-standard values will not work unless the guest OS is using the ISA PnP BIOS to detect the IDE controller.\n"
03503                 "         For best compatability set this value to io+0x206, for example, io=1F0 altio=3F6.\n"
03504                 "         The primary IDE controller will not claim port 3F7 if the primary floppy controller is enabled due to I/O port overlap in the 3F0-3F7 range.");
03505 
03506         Pbool = secprop->Add_bool("int13fakeio",Property::Changeable::WhenIdle,false);
03507         if (i == 0) Pbool->Set_help(
03508                 "If set, force IDE state change on certain INT 13h commands.\n"
03509                 "IDE registers will be changed as if BIOS had carried out the action.\n"
03510                 "If you are running Windows 3.11 or Windows 3.11 Windows for Workgroups\n"
03511                 "you must enable this option (and use -reservecyl 1) if you want 32-bit\n"
03512                 "disk access to work correctly in DOSBox-X.");
03513 
03514         Pbool = secprop->Add_bool("int13fakev86io",Property::Changeable::WhenIdle,false);
03515         if (i == 0) Pbool->Set_help(
03516                 "If set, and int13fakeio is set, certain INT 13h commands will cause IDE emulation to\n"
03517                 "issue fake CPU I/O traps (GPF) in virtual 8086 mode and a fake IRQ signal. You must\n"
03518                 "enable this option if you want 32-bit disk access in Windows 95 to work with DOSBox-X.");
03519 
03520         Pbool = secprop->Add_bool("enable pio32",Property::Changeable::WhenIdle,false);
03521         if (i == 0) Pbool->Set_help(
03522                 "If set, 32-bit I/O reads and writes are handled directly (much like PCI IDE implementations)\n"
03523                 "If clear, 32-bit I/O will be handled as if two 16-bit I/O (much like ISA IDE implementations)");
03524 
03525         Pbool = secprop->Add_bool("ignore pio32",Property::Changeable::WhenIdle,false);
03526         if (i == 0) Pbool->Set_help(
03527                 "If 32-bit I/O is enabled, attempts to read/write 32-bit I/O will be ignored entirely.\n"
03528                 "In this way, you can have DOSBox-X emulate one of the strange quirks of 1995-1997 era\n"
03529                 "laptop hardware");
03530 
03531         Pint = secprop->Add_int("cd-rom spinup time",Property::Changeable::WhenIdle,0/*use IDE or CD-ROM default*/);
03532         if (i == 0) Pint->Set_help("Emulated CD-ROM time in ms to spin up if CD is stationary.\n"
03533                 "Set to 0 to use controller or CD-ROM drive-specific default.");
03534 
03535         Pint = secprop->Add_int("cd-rom spindown timeout",Property::Changeable::WhenIdle,0/*use IDE or CD-ROM default*/);
03536         if (i == 0) Pint->Set_help("Emulated CD-ROM time in ms that drive will spin down automatically when not in use\n"
03537                 "Set to 0 to use controller or CD-ROM drive-specific default.");
03538 
03539         Pint = secprop->Add_int("cd-rom insertion delay",Property::Changeable::WhenIdle,0/*use IDE or CD-ROM default*/);
03540         if (i == 0) Pint->Set_help("Emulated CD-ROM time in ms that drive will report \"medium not present\"\n"
03541                 "to emulate the time it takes for someone to take out a CD and insert a new one when\n"
03542                 "DOSBox-X is instructed to swap or change CDs.\n"
03543                 "When running Windows 95 or higher a delay of 4000ms is recommended to ensure that\n"
03544                 "auto-insert notification triggers properly.\n"
03545                 "Set to 0 to use controller or CD-ROM drive-specific default.");
03546     }
03547 
03548     /* CONFIG.SYS options (stub) */
03549     secprop=control->AddSection_prop("config",&Null_Init,false);
03550 
03551     Pstring = secprop->Add_string("rem",Property::Changeable::OnlyAtStart,"This section is DOS's CONFIG.SYS file, not all CONFIG.SYS options supported");
03552         Pstring->Set_help("Records comments (remarks).");
03553     Pstring = secprop->Add_string("break",Property::Changeable::OnlyAtStart,"off");
03554         Pstring->Set_help("Sets or clears extended CTRL+C checking.");
03555     Pstring->Set_values(ps1opt);
03556     Pstring = secprop->Add_string("numlock",Property::Changeable::OnlyAtStart,"");
03557         Pstring->Set_help("Sets the initial state of the NumLock key.");
03558     Pstring->Set_values(numopt);
03559     Pstring = secprop->Add_string("dos",Property::Changeable::OnlyAtStart,"high, umb");
03560         Pstring->Set_help("Reports whether DOS occupies HMA and allocates UMB memory (if available).");
03561     Pint = secprop->Add_int("fcbs",Property::Changeable::OnlyAtStart,100);
03562     Pint->Set_help("Number of FCB handles available to DOS programs (1-255).");
03563     Pint = secprop->Add_int("files",Property::Changeable::OnlyAtStart,127);
03564     Pint->Set_help("Number of file handles available to DOS programs (8-255).");
03565     Pint = secprop->Add_int("country",Property::Changeable::OnlyAtStart,1);
03566     Pint->Set_help("Sets the country code for country-specific date/time formats.");
03567     Pstring = secprop->Add_string("lastdrive",Property::Changeable::OnlyAtStart,"a");
03568         Pstring->Set_help("The maximum drive letter that can be accessed by programs.");
03569     Pstring->Set_values(driveletters);
03570 
03571     //TODO ?
03572     control->AddSection_line("autoexec",&Null_Init);
03573     MSG_Add("AUTOEXEC_CONFIGFILE_HELP",
03574         "Lines in this section will be run at startup.\n"
03575         "You can put your MOUNT lines here.\n"
03576     );
03577     MSG_Add("CONFIGFILE_INTRO",
03578             "# This is the configuration file for DOSBox-X %s. (Please use the latest version of DOSBox-X)\n"
03579             "# Lines starting with a # are comment lines and are ignored by DOSBox-X.\n"
03580             "# They are used to (briefly) document the effect of each option.\n"
03581         "# To write out ALL options, use command 'config -all' with -wc or -writeconf options.\n");
03582     MSG_Add("CONFIG_SUGGESTED_VALUES", "Possible values");
03583 }
03584 
03585 int utf8_encode(char **ptr, const char *fence, uint32_t code) {
03586     int uchar_size=1;
03587     char *p = *ptr;
03588 
03589     if (!p) return UTF8ERR_NO_ROOM;
03590     if (code >= (uint32_t)0x80000000UL) return UTF8ERR_INVALID;
03591     if (p >= fence) return UTF8ERR_NO_ROOM;
03592 
03593     if (code >= 0x4000000) uchar_size = 6;
03594     else if (code >= 0x200000) uchar_size = 5;
03595     else if (code >= 0x10000) uchar_size = 4;
03596     else if (code >= 0x800) uchar_size = 3;
03597     else if (code >= 0x80) uchar_size = 2;
03598 
03599     if ((p+uchar_size) > fence) return UTF8ERR_NO_ROOM;
03600 
03601     switch (uchar_size) {
03602         case 1: *p++ = (char)code;
03603             break;
03604         case 2: *p++ = (char)(0xC0 | (code >> 6));
03605             *p++ = (char)(0x80 | (code & 0x3F));
03606             break;
03607         case 3: *p++ = (char)(0xE0 | (code >> 12));
03608             *p++ = (char)(0x80 | ((code >> 6) & 0x3F));
03609             *p++ = (char)(0x80 | (code & 0x3F));
03610             break;
03611         case 4: *p++ = (char)(0xF0 | (code >> 18));
03612             *p++ = (char)(0x80 | ((code >> 12) & 0x3F));
03613             *p++ = (char)(0x80 | ((code >> 6) & 0x3F));
03614             *p++ = (char)(0x80 | (code & 0x3F));
03615             break;
03616         case 5: *p++ = (char)(0xF8 | (code >> 24));
03617             *p++ = (char)(0x80 | ((code >> 18) & 0x3F));
03618             *p++ = (char)(0x80 | ((code >> 12) & 0x3F));
03619             *p++ = (char)(0x80 | ((code >> 6) & 0x3F));
03620             *p++ = (char)(0x80 | (code & 0x3F));
03621             break;
03622         case 6: *p++ = (char)(0xFC | (code >> 30));
03623             *p++ = (char)(0x80 | ((code >> 24) & 0x3F));
03624             *p++ = (char)(0x80 | ((code >> 18) & 0x3F));
03625             *p++ = (char)(0x80 | ((code >> 12) & 0x3F));
03626             *p++ = (char)(0x80 | ((code >> 6) & 0x3F));
03627             *p++ = (char)(0x80 | (code & 0x3F));
03628             break;
03629     }
03630 
03631     *ptr = p;
03632     return 0;
03633 }
03634 
03635 int utf8_decode(const char **ptr,const char *fence) {
03636     const char *p = *ptr;
03637     int uchar_size=1;
03638     int ret = 0,c;
03639 
03640     if (!p) return UTF8ERR_NO_ROOM;
03641     if (p >= fence) return UTF8ERR_NO_ROOM;
03642 
03643     ret = (unsigned char)(*p);
03644     if (ret >= 0xFE) { p++; return UTF8ERR_INVALID; }
03645     else if (ret >= 0xFC) uchar_size=6;
03646     else if (ret >= 0xF8) uchar_size=5;
03647     else if (ret >= 0xF0) uchar_size=4;
03648     else if (ret >= 0xE0) uchar_size=3;
03649     else if (ret >= 0xC0) uchar_size=2;
03650     else if (ret >= 0x80) { p++; return UTF8ERR_INVALID; }
03651 
03652     if ((p+uchar_size) > fence)
03653         return UTF8ERR_NO_ROOM;
03654 
03655     switch (uchar_size) {
03656         case 1: p++;
03657             break;
03658         case 2: ret = (ret&0x1F)<<6; p++;
03659             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03660             ret |= c&0x3F;
03661             break;
03662         case 3: ret = (ret&0xF)<<12; p++;
03663             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03664             ret |= (c&0x3F)<<6;
03665             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03666             ret |= c&0x3F;
03667             break;
03668         case 4: ret = (ret&0x7)<<18; p++;
03669             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03670             ret |= (c&0x3F)<<12;
03671             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03672             ret |= (c&0x3F)<<6;
03673             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03674             ret |= c&0x3F;
03675             break;
03676         case 5: ret = (ret&0x3)<<24; p++;
03677             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03678             ret |= (c&0x3F)<<18;
03679             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03680             ret |= (c&0x3F)<<12;
03681             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03682             ret |= (c&0x3F)<<6;
03683             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03684             ret |= c&0x3F;
03685             break;
03686         case 6: ret = (ret&0x1)<<30; p++;
03687             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03688             ret |= (c&0x3F)<<24;
03689             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03690             ret |= (c&0x3F)<<18;
03691             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03692             ret |= (c&0x3F)<<12;
03693             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03694             ret |= (c&0x3F)<<6;
03695             c = (unsigned char)(*p++); if ((c&0xC0) != 0x80) return UTF8ERR_INVALID;
03696             ret |= c&0x3F;
03697             break;
03698     }
03699 
03700     *ptr = p;
03701     return ret;
03702 }
03703 
03704 int utf16le_encode(char **ptr, const char *fence, uint32_t code) {
03705     char *p = *ptr;
03706 
03707     if (!p) return UTF8ERR_NO_ROOM;
03708     if (code > 0x10FFFF) return UTF8ERR_INVALID;
03709     if (code > 0xFFFF) { /* UTF-16 surrogate pair */
03710         uint32_t lo = (code - 0x10000) & 0x3FF;
03711         uint32_t hi = ((code - 0x10000) >> 10) & 0x3FF;
03712         if ((p+2+2) > fence) return UTF8ERR_NO_ROOM;
03713         *p++ = (char)( (hi+0xD800)       & 0xFF);
03714         *p++ = (char)(((hi+0xD800) >> 8) & 0xFF);
03715         *p++ = (char)( (lo+0xDC00)       & 0xFF);
03716         *p++ = (char)(((lo+0xDC00) >> 8) & 0xFF);
03717     }
03718     else if ((code&0xF800) == 0xD800) { /* do not allow accidental surrogate pairs (0xD800-0xDFFF) */
03719         return UTF8ERR_INVALID;
03720     }
03721     else {
03722         if ((p+2) > fence) return UTF8ERR_NO_ROOM;
03723         *p++ = (char)( code       & 0xFF);
03724         *p++ = (char)((code >> 8) & 0xFF);
03725     }
03726 
03727     *ptr = p;
03728     return 0;
03729 }
03730 
03731 int utf16le_decode(const char **ptr,const char *fence) {
03732     const char *p = *ptr;
03733     unsigned int ret,b=2;
03734 
03735     if (!p) return UTF8ERR_NO_ROOM;
03736     if ((p+1) >= fence) return UTF8ERR_NO_ROOM;
03737 
03738     ret = (unsigned char)p[0];
03739     ret |= ((unsigned int)((unsigned char)p[1])) << 8;
03740     if (ret >= 0xD800U && ret <= 0xDBFFU)
03741         b=4;
03742     else if (ret >= 0xDC00U && ret <= 0xDFFFU)
03743         { p++; return UTF8ERR_INVALID; }
03744 
03745     if ((p+b) > fence)
03746         return UTF8ERR_NO_ROOM;
03747 
03748     p += 2;
03749     if (ret >= 0xD800U && ret <= 0xDBFFU) {
03750         /* decode surrogate pair */
03751         unsigned int hi = ret & 0x3FFU;
03752         unsigned int lo = (unsigned char)p[0];
03753         lo |= ((unsigned int)((unsigned char)p[1])) << 8;
03754         p += 2;
03755         if (lo < 0xDC00U || lo > 0xDFFFU) return UTF8ERR_INVALID;
03756         lo &= 0x3FFU;
03757         ret = ((hi << 10U) | lo) + 0x10000U;
03758     }
03759 
03760     *ptr = p;
03761     return (int)ret;
03762 }
03763 
03764 extern void POD_Save_Sdlmain( std::ostream& stream );
03765 extern void POD_Load_Sdlmain( std::istream& stream );
03766 
03767 // save state support
03768 
03769 namespace
03770 {
03771 class SerializeDosbox : public SerializeGlobalPOD
03772 {
03773 public:
03774         SerializeDosbox() : SerializeGlobalPOD("Dosbox-x")
03775         {}
03776 
03777 private:
03778         virtual void getBytes(std::ostream& stream)
03779         {
03780 
03781                 //******************************************
03782                 //******************************************
03783                 //******************************************
03784 
03785                 SerializeGlobalPOD::getBytes(stream);
03786 
03787                 //******************************************
03788                 //******************************************
03789                 //******************************************
03790 
03791                 POD_Save_Sdlmain(stream);
03792         }
03793 
03794         virtual void setBytes(std::istream& stream)
03795         {
03796 
03797                 //******************************************
03798                 //******************************************
03799                 //******************************************
03800 
03801                 SerializeGlobalPOD::setBytes(stream);
03802 
03803                 //******************************************
03804                 //******************************************
03805                 //******************************************
03806 
03807                 POD_Load_Sdlmain(stream);
03808 
03809                 //*******************************************
03810                 //*******************************************
03811                 //*******************************************
03812 
03813                 // Reset any auto cycle guessing for this frame
03814                 ticksRemain=5;
03815                 ticksLast = GetTicks();
03816                 ticksAdded = 0;
03817                 ticksDone = 0;
03818                 ticksScheduled = 0;
03819         }
03820 } dummy;
03821 }
03822 
03823 #include "zlib.h"
03824 #ifdef WIN32
03825 #include "direct.h"
03826 #endif
03827 #include "cross.h"
03828 #include "logging.h"
03829 #if defined (__APPLE__)
03830 #else
03831 #include <malloc.h>
03832 #endif
03833 #include <cstring>
03834 #include <fstream>
03835 
03836 #include <stdio.h>
03837 #include <sys/stat.h>
03838 #include <sys/types.h>
03839 #include <string>
03840 #include <stdlib.h>
03841 #include "SDL.h"
03842 
03843 #ifndef WIN32
03844 char* itoa(int value, char* str, int radix) {
03850                 // check that the radix if valid
03851                 if (radix < 2 || radix > 36) { *str = '\0'; return str; }
03852 
03853                 char* ptr = str, *ptr1 = str, tmp_char;
03854                 int tmp_value;
03855 
03856                 do {
03857                         tmp_value = value;
03858                         value /= radix;
03859                         *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * radix)];
03860                 } while ( value );
03861 
03862                 // Apply negative sign
03863                 if (tmp_value < 0) *ptr++ = '-';
03864                 *ptr-- = '\0';
03865                 while(ptr1 < ptr) {
03866                         tmp_char = *ptr;
03867                         *ptr--= *ptr1;
03868                         *ptr1++ = tmp_char;
03869                 }
03870                 return str;
03871 }
03872 #endif
03873 
03874 SaveState& SaveState::instance() {
03875     static SaveState singleton;
03876     return singleton;
03877 }
03878 
03879 void SaveState::registerComponent(const std::string& uniqueName, Component& comp) {
03880     components.insert(std::make_pair(uniqueName, CompData(comp)));
03881 }
03882 
03883 namespace Util {
03884 std::string compress(const std::string& input) { //throw (SaveState::Error)
03885         if (input.empty())
03886                 return input;
03887 
03888         const uLong bufferSize = ::compressBound(input.size());
03889 
03890         std::string output;
03891         output.resize(bufferSize);
03892 
03893         uLongf actualSize = bufferSize;
03894         if (::compress2(reinterpret_cast<Bytef*>(&output[0]), &actualSize,
03895                                         reinterpret_cast<const Bytef*>(input.c_str()), input.size(), Z_BEST_SPEED) != Z_OK)
03896                 throw SaveState::Error("Compression failed!");
03897 
03898         output.resize(actualSize);
03899 
03900         //save size of uncompressed data
03901         const size_t uncompressedSize = input.size(); //save size of uncompressed data
03902         output.resize(output.size() + sizeof(uncompressedSize)); //won't trigger a reallocation
03903         ::memcpy(&output[0] + output.size() - sizeof(uncompressedSize), &uncompressedSize, sizeof(uncompressedSize));
03904 
03905         return std::string(&output[0], output.size()); //strip reserved space
03906 }
03907 
03908 std::string decompress(const std::string& input) { //throw (SaveState::Error)
03909         if (input.empty())
03910                 return input;
03911 
03912         //retrieve size of uncompressed data
03913         size_t uncompressedSize = 0;
03914         ::memcpy(&uncompressedSize, &input[0] + input.size() - sizeof(uncompressedSize), sizeof(uncompressedSize));
03915 
03916         std::string output;
03917         output.resize(uncompressedSize);
03918 
03919         uLongf actualSize = uncompressedSize;
03920         if (::uncompress(reinterpret_cast<Bytef*>(&output[0]), &actualSize,
03921                                          reinterpret_cast<const Bytef*>(input.c_str()), input.size() - sizeof(uncompressedSize)) != Z_OK)
03922                 throw SaveState::Error("Decompression failed!");
03923 
03924         output.resize(actualSize); //should be superfluous!
03925 
03926         return output;
03927 }
03928 }
03929 
03930 inline void SaveState::RawBytes::set(const std::string& stream) {
03931         bytes = stream;
03932         isCompressed = false;
03933         dataExists   = true;
03934 }
03935 
03936 inline std::string SaveState::RawBytes::get() const { //throw (Error){
03937         if (isCompressed)
03938                 (Util::decompress(bytes)).swap(bytes);
03939         isCompressed = false;
03940         return bytes;
03941 }
03942 
03943 inline void SaveState::RawBytes::compress() const { //throw (Error)
03944         if (!isCompressed)
03945                 (Util::compress(bytes)).swap(bytes);
03946         isCompressed = true;
03947 }
03948 
03949 inline bool SaveState::RawBytes::dataAvailable() const {
03950         return dataExists;
03951 }
03952 
03953 #define CASESENSITIVITY (0)
03954 #define MAXFILENAME (256)
03955 
03956 int mymkdir(const char* dirname)
03957 {
03958     int ret=0;
03959 #ifdef _WIN32
03960     ret = _mkdir(dirname);
03961 #elif unix
03962     ret = mkdir (dirname,0775);
03963 #elif __APPLE__
03964     ret = mkdir (dirname,0775);
03965 #endif
03966     return ret;
03967 }
03968 
03969 int makedir(const char *newdir)
03970 {
03971   char *buffer ;
03972   char *p;
03973   int  len = (int)strlen(newdir);
03974 
03975   if (len <= 0)
03976     return 0;
03977 
03978   buffer = (char*)malloc(len+1);
03979         if (buffer==NULL)
03980         {
03981                 printf("Error allocating memory\n");
03982                 return UNZ_INTERNALERROR;
03983         }
03984   strcpy(buffer,newdir);
03985 
03986   if (buffer[len-1] == '/') {
03987     buffer[len-1] = '\0';
03988   }
03989   if (mymkdir(buffer) == 0)
03990     {
03991       free(buffer);
03992       return 1;
03993     }
03994 
03995   p = buffer+1;
03996   while (1)
03997     {
03998       char hold;
03999 
04000       while(*p && *p != '\\' && *p != '/')
04001         p++;
04002       hold = *p;
04003       *p = 0;
04004       if ((mymkdir(buffer) == -1) && (errno == ENOENT))
04005         {
04006           printf("couldn't create directory %s\n",buffer);
04007           free(buffer);
04008           return 0;
04009         }
04010       if (hold == 0)
04011         break;
04012       *p++ = hold;
04013     }
04014   free(buffer);
04015   return 1;
04016 }
04017 
04018 void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date)
04019 {
04020     (void)dosdate;
04021 #ifdef _WIN32
04022   HANDLE hFile;
04023   FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
04024 
04025   hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
04026                       0,NULL,OPEN_EXISTING,0,NULL);
04027   GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
04028   DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
04029   LocalFileTimeToFileTime(&ftLocal,&ftm);
04030   SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
04031   CloseHandle(hFile);
04032 #else
04033 #if defined(unix) || defined(__APPLE__)
04034   struct utimbuf ut;
04035   struct tm newdate;
04036   newdate.tm_sec = tmu_date.tm_sec;
04037   newdate.tm_min=tmu_date.tm_min;
04038   newdate.tm_hour=tmu_date.tm_hour;
04039   newdate.tm_mday=tmu_date.tm_mday;
04040   newdate.tm_mon=tmu_date.tm_mon;
04041   if (tmu_date.tm_year > 1900)
04042       newdate.tm_year=tmu_date.tm_year - 1900;
04043   else
04044       newdate.tm_year=tmu_date.tm_year ;
04045   newdate.tm_isdst=-1;
04046 
04047   ut.actime=ut.modtime=mktime(&newdate);
04048   utime(filename,&ut);
04049 #endif
04050 #endif
04051 }
04052 
04053 int do_extract_currentfile(unzFile uf, const int* popt_extract_without_path, int* popt_overwrite, const char* password)
04054 {
04055     char filename_inzip[256];
04056     char* filename_withoutpath;
04057     char* p;
04058     int err=UNZ_OK;
04059     FILE *fout=NULL;
04060     void* buf;
04061     uInt size_buf;
04062 
04063     unz_file_info64 file_info;
04064     uLong ratio=0;
04065     err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
04066 
04067     (void)ratio;
04068 
04069     if (err!=UNZ_OK)
04070     {
04071         printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
04072         return err;
04073     }
04074 
04075     size_buf = 8192;
04076     buf = (void*)malloc(size_buf);
04077     if (buf==NULL)
04078     {
04079         printf("Error allocating memory\n");
04080         return UNZ_INTERNALERROR;
04081     }
04082 
04083     p = filename_withoutpath = filename_inzip;
04084     while ((*p) != '\0')
04085     {
04086         if (((*p)=='/') || ((*p)=='\\'))
04087             filename_withoutpath = p+1;
04088         p++;
04089     }
04090 
04091     if ((*filename_withoutpath)=='\0')
04092     {
04093         if ((*popt_extract_without_path)==0)
04094         {
04095             printf("creating directory: %s\n",filename_inzip);
04096             mymkdir(filename_inzip);
04097         }
04098     }
04099     else
04100     {
04101         const char* write_filename;
04102         int skip=0;
04103 
04104         if ((*popt_extract_without_path)==0)
04105             write_filename = filename_inzip;
04106         else
04107             write_filename = filename_withoutpath;
04108 
04109         err = unzOpenCurrentFilePassword(uf,password);
04110         if (err!=UNZ_OK)
04111         {
04112             printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
04113         }
04114 
04115         if (((*popt_overwrite)==0) && (err==UNZ_OK))
04116         {
04117             char rep=0;
04118             FILE* ftestexist;
04119             ftestexist = FOPEN_FUNC(write_filename,"rb");
04120             if (ftestexist!=NULL)
04121             {
04122                 fclose(ftestexist);
04123                 do
04124                 {
04125                     char answer[128];
04126                     int ret;
04127 
04128                     printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
04129                     ret = scanf("%1s",answer);
04130                     if (ret != 1)
04131                     {
04132                        exit(EXIT_FAILURE);
04133                     }
04134                     rep = answer[0] ;
04135                     if ((rep>='a') && (rep<='z'))
04136                         rep -= 0x20;
04137                 }
04138                 while ((rep!='Y') && (rep!='N') && (rep!='A'));
04139             }
04140 
04141             if (rep == 'N')
04142                 skip = 1;
04143 
04144             if (rep == 'A')
04145                 *popt_overwrite=1;
04146         }
04147 
04148         if ((skip==0) && (err==UNZ_OK))
04149         {
04150             fout=FOPEN_FUNC(write_filename,"wb");
04151             /* some zipfile don't contain directory alone before file */
04152             if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
04153                                 (filename_withoutpath!=(char*)filename_inzip))
04154             {
04155                 char c=*(filename_withoutpath-1);
04156                 *(filename_withoutpath-1)='\0';
04157                 makedir(write_filename);
04158                 *(filename_withoutpath-1)=c;
04159                 fout=FOPEN_FUNC(write_filename,"wb");
04160             }
04161 
04162             if (fout==NULL)
04163             {
04164                 printf("error opening %s\n",write_filename);
04165             }
04166         }
04167 
04168         if (fout!=NULL)
04169         {
04170             printf(" extracting: %s\n",write_filename);
04171 
04172             do
04173             {
04174                 err = unzReadCurrentFile(uf,buf,size_buf);
04175                 if (err<0)
04176                 {
04177                     printf("error %d with zipfile in unzReadCurrentFile\n",err);
04178                     break;
04179                 }
04180                 if (err>0)
04181                     if (fwrite(buf,err,1,fout)!=1)
04182                     {
04183                         printf("error in writing extracted file\n");
04184                         err=UNZ_ERRNO;
04185                         break;
04186                     }
04187             }
04188             while (err>0);
04189             if (fout)
04190                     fclose(fout);
04191 
04192             if (err==0)
04193                 change_file_date(write_filename,file_info.dosDate,
04194                                  file_info.tmu_date);
04195         }
04196 
04197         if (err==UNZ_OK)
04198         {
04199             err = unzCloseCurrentFile (uf);
04200             if (err!=UNZ_OK)
04201             {
04202                 printf("error %d with zipfile in unzCloseCurrentFile\n",err);
04203             }
04204         }
04205         else
04206             unzCloseCurrentFile(uf); /* don't lose the error */
04207     }
04208 
04209     free(buf);
04210     return err;
04211 }
04212 
04213 int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char* password)
04214 {
04215     uLong i;
04216     unz_global_info64 gi;
04217     int err;
04218     FILE* fout=NULL;
04219 
04220     (void)fout;
04221 
04222     err = unzGetGlobalInfo64(uf,&gi);
04223     if (err!=UNZ_OK) {
04224         printf("error %d with zipfile in unzGetGlobalInfo \n",err);
04225         return 0;
04226     }
04227 
04228     for (i=0;i<gi.number_entry;i++)
04229     {
04230         if (do_extract_currentfile(uf,&opt_extract_without_path,
04231                                       &opt_overwrite,
04232                                       password) != UNZ_OK)
04233             break;
04234 
04235         if ((i+1)<gi.number_entry)
04236         {
04237             err = unzGoToNextFile(uf);
04238             if (err!=UNZ_OK)
04239             {
04240                 printf("error %d with zipfile in unzGoToNextFile\n",err);
04241                 break;
04242             }
04243         }
04244     }
04245 
04246     return 0;
04247 }
04248 
04249 int do_extract_onefile(unzFile uf, const char* filename, int opt_extract_without_path, int opt_overwrite, const char* password)
04250 {
04251     int err = UNZ_OK;
04252     (void)err;
04253     if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
04254     {
04255         printf("file %s not found in the zipfile\n",filename);
04256         return 2;
04257     }
04258 
04259     if (do_extract_currentfile(uf,&opt_extract_without_path,
04260                                       &opt_overwrite,
04261                                       password) == UNZ_OK)
04262         return 0;
04263     else
04264         return 1;
04265 }
04266 
04267 int my_miniunz(char ** savefile, const char * savefile2, const char * savedir) {
04268     const char *zipfilename=NULL;
04269     const char *filename_to_extract=NULL;
04270     const char *password=NULL;
04271     char filename_try[MAXFILENAME+16] = "";
04272     int ret_value=0;
04273     int opt_do_extract=1;
04274     int opt_do_extract_withoutpath=0;
04275     int opt_overwrite=0;
04276     int opt_extractdir=0;
04277     const char *dirname=NULL;
04278     unzFile uf=NULL;
04279 
04280                 opt_do_extract = opt_do_extract_withoutpath = 1;
04281                 opt_overwrite=1;
04282                 opt_extractdir=1;
04283                 dirname=savedir;
04284         zipfilename = (const char *)savefile;
04285         filename_to_extract = savefile2;
04286 
04287     if (zipfilename!=NULL)
04288     {
04289 
04290 #        ifdef USEWIN32IOAPI
04291         zlib_filefunc64_def ffunc;
04292 #        endif
04293 
04294         strncpy(filename_try, zipfilename,MAXFILENAME-1);
04295         /* strncpy doesnt append the trailing NULL, of the string is too long. */
04296         filename_try[ MAXFILENAME ] = '\0';
04297 
04298 #        ifdef USEWIN32IOAPI
04299         fill_win32_filefunc64A(&ffunc);
04300         uf = unzOpen2_64(zipfilename,&ffunc);
04301 #        else
04302         uf = unzOpen64(zipfilename);
04303 #        endif
04304     }
04305 
04306     if (uf==NULL)
04307     {
04308         //printf("Cannot open %s\n",zipfilename,zipfilename);
04309         return 1;
04310     }
04311     //printf("%s opened\n",filename_try);
04312 
04313         if (opt_do_extract==1)
04314     {
04315                 char cCurrentPath[FILENAME_MAX];
04316                 char *ret=getcwd(cCurrentPath, sizeof(cCurrentPath));
04317         if (opt_extractdir && chdir(dirname))
04318         {
04319           printf("Error changing into %s, aborting\n", dirname);
04320           exit(-1);
04321         }
04322 
04323         if (filename_to_extract == NULL)
04324             ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
04325         else
04326             ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password);
04327                 if (ret!=NULL) chdir(cCurrentPath);
04328     }
04329 
04330     unzClose(uf);
04331 
04332     return ret_value;
04333 }
04334 
04335 #ifdef _WIN32
04336 uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
04337 {
04338   int ret = 0;
04339   {
04340       FILETIME ftLocal;
04341       HANDLE hFind;
04342       WIN32_FIND_DATAA ff32;
04343 
04344       hFind = FindFirstFileA(f,&ff32);
04345       if (hFind != INVALID_HANDLE_VALUE)
04346       {
04347         FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
04348         FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
04349         FindClose(hFind);
04350         ret = 1;
04351       }
04352   }
04353   return ret;
04354 }
04355 #else
04356 #if defined(unix) || defined(__APPLE__)
04357 uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
04358 {
04359     (void)dt;
04360   int ret=0;
04361   struct stat s;        /* results of stat() */
04362   struct tm* filedate;
04363   time_t tm_t=0;
04364 
04365   if (strcmp(f,"-")!=0)
04366   {
04367     char name[MAXFILENAME+1];
04368     int len = strlen(f);
04369     if (len > MAXFILENAME)
04370       len = MAXFILENAME;
04371 
04372     strncpy(name, f,MAXFILENAME-1);
04373     /* strncpy doesnt append the trailing NULL, of the string is too long. */
04374     name[ MAXFILENAME ] = '\0';
04375 
04376     if (name[len - 1] == '/')
04377       name[len - 1] = '\0';
04378     /* not all systems allow stat'ing a file with / appended */
04379     if (stat(name,&s)==0)
04380     {
04381       tm_t = s.st_mtime;
04382       ret = 1;
04383     }
04384   }
04385   filedate = localtime(&tm_t);
04386 
04387   tmzip->tm_sec  = filedate->tm_sec;
04388   tmzip->tm_min  = filedate->tm_min;
04389   tmzip->tm_hour = filedate->tm_hour;
04390   tmzip->tm_mday = filedate->tm_mday;
04391   tmzip->tm_mon  = filedate->tm_mon ;
04392   tmzip->tm_year = filedate->tm_year;
04393 
04394   return ret;
04395 }
04396 #else
04397 uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
04398 {
04399     return 0;
04400 }
04401 #endif
04402 #endif
04403 
04404 #ifdef __APPLE__
04405 // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
04406 #define FOPEN_FUNC(filename, mode) fopen(filename, mode)
04407 #define FTELLO_FUNC(stream) ftello(stream)
04408 #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
04409 #else
04410 #define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
04411 #define FTELLO_FUNC(stream) ftello64(stream)
04412 #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
04413 #endif
04414 
04415 int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
04416 {
04417     unsigned long calculate_crc=0;
04418     int err=ZIP_OK;
04419     FILE * fin = FOPEN_FUNC(filenameinzip,"rb");
04420 
04421     unsigned long size_read = 0;
04422     unsigned long total_read = 0;
04423     if (fin==NULL)
04424        err = ZIP_ERRNO;
04425 
04426     if (err == ZIP_OK)
04427         do
04428         {
04429             err = ZIP_OK;
04430             size_read = (int)fread(buf,1,size_buf,fin);
04431             if (size_read < size_buf)
04432                 if (feof(fin)==0)
04433             {
04434                 printf("error in reading %s\n",filenameinzip);
04435                 err = ZIP_ERRNO;
04436             }
04437 
04438             if (size_read>0)
04439                 calculate_crc = crc32(calculate_crc,(const Bytef*)buf,size_read);
04440             total_read += size_read;
04441 
04442         } while ((err == ZIP_OK) && (size_read>0));
04443 
04444     if (fin)
04445         fclose(fin);
04446 
04447     *result_crc=calculate_crc;
04448     printf("file %s crc %lx\n", filenameinzip, calculate_crc);
04449     return err;
04450 }
04451 
04452 int isLargeFile(const char* filename)
04453 {
04454   int largeFile = 0;
04455   ZPOS64_T pos = 0;
04456   FILE* pFile = FOPEN_FUNC(filename, "rb");
04457 
04458   if(pFile != NULL)
04459   {
04460     int n = FSEEKO_FUNC(pFile, 0, SEEK_END);
04461     pos = FTELLO_FUNC(pFile);
04462     (void)n;
04463 
04464                 printf("File : %s is %lld bytes\n", filename, pos);
04465 
04466     if(pos >= 0xffffffff)
04467      largeFile = 1;
04468 
04469                 fclose(pFile);
04470   }
04471 
04472  return largeFile;
04473 }
04474 
04475 int my_minizip(char ** savefile, char ** savefile2) {
04476     int opt_overwrite=0;
04477     int opt_compress_level=Z_DEFAULT_COMPRESSION;
04478     int opt_exclude_path=0;
04479     int zipfilenamearg = 0;
04480     (void)zipfilenamearg;
04481     //char filename_try[MAXFILENAME16];
04482     int err=0;
04483     int size_buf=0;
04484     void* buf=NULL;
04485     const char* password=NULL;
04486 
04487         opt_overwrite = 2;
04488         opt_compress_level = 9;
04489         opt_exclude_path = 1;
04490 
04491     size_buf = 16384;
04492     buf = (void*)malloc(size_buf);
04493     if (buf==NULL)
04494     {
04495         //printf("Error allocating memory\n");
04496         return ZIP_INTERNALERROR;
04497     }
04498 
04499     {
04500         zipFile zf;
04501         int errclose;
04502 #        ifdef USEWIN32IOAPI
04503         zlib_filefunc64_def ffunc;
04504         fill_win32_filefunc64A(&ffunc);
04505         zf = zipOpen2_64(savefile,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc);
04506 #        else
04507         zf = zipOpen64(savefile,(opt_overwrite==2) ? 2 : 0);
04508 #        endif
04509 
04510         if (zf == NULL)
04511         {
04512             //printf("error opening %s\n",savefile);
04513             err= ZIP_ERRNO;
04514         }
04515         else
04516             //printf("creating %s\n",savefile);
04517 
04518             {
04519                 FILE *fin = NULL;
04520                 int size_read;
04521                 char* filenameinzip = (char *)savefile2;
04522                 const char *savefilenameinzip;
04523                 zip_fileinfo zi;
04524                 unsigned long crcFile=0;
04525                 int zip64 = 0;
04526 
04527                 zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
04528                 zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
04529                 zi.dosDate = 0;
04530                 zi.internal_fa = 0;
04531                 zi.external_fa = 0;
04532                 filetime(filenameinzip,&zi.tmz_date,&zi.dosDate);
04533 
04534                 if ((password != NULL) && (err==ZIP_OK))
04535                     err = getFileCrc(filenameinzip,buf,size_buf,&crcFile);
04536 
04537                 zip64 = isLargeFile(filenameinzip);
04538 
04539                                                          /* The path name saved, should not include a leading slash. */
04540                /*if it did, windows/xp and dynazip couldn't read the zip file. */
04541                  savefilenameinzip = filenameinzip;
04542                  while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' )
04543                  {
04544                      savefilenameinzip++;
04545                  }
04546 
04547                  /*should the zip file contain any path at all?*/
04548                  if( opt_exclude_path )
04549                  {
04550                      const char *tmpptr;
04551                      const char *lastslash = 0;
04552                      for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++)
04553                      {
04554                          if( *tmpptr == '\\' || *tmpptr == '/')
04555                          {
04556                              lastslash = tmpptr;
04557                          }
04558                      }
04559                      if( lastslash != NULL )
04560                      {
04561                          savefilenameinzip = lastslash+1; // base filename follows last slash.
04562                      }
04563                  }
04564 
04565                  
04566                 err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi,
04567                                  NULL,0,NULL,0,NULL /* comment*/,
04568                                  (opt_compress_level != 0) ? Z_DEFLATED : 0,
04569                                  opt_compress_level,0,
04570                                  /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
04571                                  -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
04572                                  password,crcFile, zip64);
04573 
04574                 if (err != ZIP_OK) {
04575                     //printf("error in opening %s in zipfile\n",filenameinzip);
04576                                 }
04577                 else
04578                 {
04579                     fin = fopen64(filenameinzip,"rb");
04580                     if (fin==NULL)
04581                     {
04582                         err=ZIP_ERRNO;
04583                         //printf("error in opening %s for reading\n",filenameinzip);
04584                     }
04585                 }
04586 
04587                 if (err == ZIP_OK)
04588                     do
04589                     {
04590                         err = ZIP_OK;
04591                         size_read = (int)fread(buf,1,size_buf,fin);
04592                         if (size_read < size_buf)
04593                             if (feof(fin)==0)
04594                         {
04595                             //printf("error in reading %s\n",filenameinzip);
04596                             err = ZIP_ERRNO;
04597                         }
04598 
04599                         if (size_read>0)
04600                         {
04601                             err = zipWriteInFileInZip (zf,buf,size_read);
04602                             if (err<0)
04603                             {
04604                                 //printf("error in writing %s in the zipfile\n",
04605                                 //                 filenameinzip);
04606                             }
04607 
04608                         }
04609                     } while ((err == ZIP_OK) && (size_read>0));
04610 
04611                 if (fin)
04612                     fclose(fin);
04613 
04614                 if (err<0)
04615                     err=ZIP_ERRNO;
04616                 else
04617                 {
04618                     err = zipCloseFileInZip(zf);
04619                     if (err!=ZIP_OK) {
04620                         //printf("error in closing %s in the zipfile\n",
04621                         //            filenameinzip);
04622                                         }
04623                 }
04624             }
04625         errclose = zipClose(zf,NULL);
04626         if (errclose != ZIP_OK) {
04627             //printf("error in closing %s\n",savefile);
04628                 }
04629     }
04630 
04631     free(buf);
04632     return 0;
04633 }
04634 
04635 void SaveState::save(size_t slot) { //throw (Error)
04636         if (slot >= SLOT_COUNT)  return;
04637         SDL_PauseAudio(0);
04638         bool save_err=false;
04639         if((MEM_TotalPages()*4096/1024/1024)>1000) {
04640                 LOG_MSG("Stopped. 1000 MB is the maximum memory size for saving/loading states.");
04641 #if defined(WIN32)
04642                 MessageBox(GetHWND(),"Unsupported memory size.","Error",MB_OK);
04643 #endif
04644                 return;
04645         }
04646         bool create_version=false;
04647         bool create_title=false;
04648         bool create_memorysize=false;
04649         extern const char* RunningProgram;
04650         std::string path;
04651         bool Get_Custom_SaveDir(std::string& savedir);
04652         if(Get_Custom_SaveDir(path)) {
04653                 path+=CROSS_FILESPLIT;
04654         } else {
04655                 extern std::string capturedir;
04656                 const size_t last_slash_idx = capturedir.find_last_of("\\/");
04657                 if (std::string::npos != last_slash_idx) {
04658                         path = capturedir.substr(0, last_slash_idx);
04659                 } else {
04660                         path = ".";
04661                 }
04662                 path+=CROSS_FILESPLIT;
04663                 path+="save";
04664                 Cross::CreateDir(path);
04665                 path+=CROSS_FILESPLIT;
04666         }
04667 
04668         std::string temp, save2;
04669         std::stringstream slotname;
04670         slotname << slot+1;
04671         temp=path;
04672         std::string save=temp+slotname.str()+".sav";
04673         remove(save.c_str());
04674         std::ofstream file (save.c_str());
04675         file << "";
04676         file.close();
04677         try {
04678                 for (CompEntry::iterator i = components.begin(); i != components.end(); ++i) {
04679                         std::ostringstream ss;
04680                         i->second.comp.getBytes(ss);
04681                         i->second.rawBytes[slot].set(ss.str());
04682                         
04683                         //LOG_MSG("Component is %s",i->first.c_str());
04684 
04685                         if(!create_version) {
04686                                 std::string tempname = temp+"DOSBox-X_Version";
04687                                 std::ofstream emulatorversion (tempname.c_str(), std::ofstream::binary);
04688                                 emulatorversion << "DOSBox-X " << VERSION << " (" << SDL_STRING << ")" << std::endl << GetPlatform() << std::endl << UPDATED_STR;
04689                                 create_version=true;
04690                                 emulatorversion.close();
04691                         }
04692 
04693                         if(!create_title) {
04694                                 std::string tempname = temp+"Program_Name";
04695                                 std::ofstream programname (tempname.c_str(), std::ofstream::binary);
04696                                 programname << RunningProgram;
04697                                 create_title=true;
04698                                 programname.close();
04699                         }
04700 
04701                         if(!create_memorysize) {
04702                  std::string tempname = temp+"Memory_Size";
04703                                   std::ofstream memorysize (tempname.c_str(), std::ofstream::binary);
04704                                   memorysize << MEM_TotalPages();
04705                                   create_memorysize=true;
04706                                   memorysize.close();
04707                         }
04708                         std::string realtemp;
04709                         realtemp = temp + i->first;
04710                         std::ofstream outfile (realtemp.c_str(), std::ofstream::binary);
04711                         outfile << (Util::compress(ss.str()));
04712                         //compress all other saved states except position "slot"
04713                         //const std::vector<RawBytes>& rb = i->second.rawBytes;
04714                         //std::for_each(rb.begin(), rb.begin() + slot, std::mem_fun_ref(&RawBytes::compress));
04715                         //std::for_each(rb.begin() + slot + 1, rb.end(), std::mem_fun_ref(&RawBytes::compress));
04716                         outfile.close();
04717                         ss.clear();
04718                         if(outfile.fail()) {
04719                                 LOG_MSG("Save failed! - %s", realtemp.c_str());
04720                                 save_err=true;
04721                                 remove(save.c_str());
04722                                 goto delete_all;
04723                         }
04724                 }
04725         }
04726         catch (const std::bad_alloc&) {
04727                 LOG_MSG("Save failed! Out of Memory!");
04728                 save_err=true;
04729                 remove(save.c_str());
04730                 goto delete_all;
04731         }
04732 
04733         for (CompEntry::iterator i = components.begin(); i != components.end(); ++i) {
04734                 save2=temp+i->first;
04735                 my_minizip((char **)save.c_str(), (char **)save2.c_str());
04736         }
04737         save2=temp+"DOSBox-X_Version";
04738         my_minizip((char **)save.c_str(), (char **)save2.c_str());
04739         save2=temp+"Program_Name";
04740         my_minizip((char **)save.c_str(), (char **)save2.c_str());
04741         save2=temp+"Memory_Size";
04742         my_minizip((char **)save.c_str(), (char **)save2.c_str());
04743 
04744 delete_all:
04745         for (CompEntry::iterator i = components.begin(); i != components.end(); ++i) {
04746                 save2=temp+i->first;
04747                 remove(save2.c_str());
04748         }
04749         save2=temp+"DOSBox-X_Version";
04750         remove(save2.c_str());
04751         save2=temp+"Program_Name";
04752         remove(save2.c_str());
04753         save2=temp+"Memory_Size";
04754         remove(save2.c_str());
04755         if (save_err) {
04756 #if defined(WIN32)
04757                 MessageBox(GetHWND(),"Failed to save the current state.","Error",MB_OK);
04758 #endif
04759         } else
04760                 LOG_MSG("Saved. (Slot %d)",(int)slot+1);
04761 }
04762 
04763 void SaveState::load(size_t slot) const { //throw (Error)
04764 //      if (isEmpty(slot)) return;
04765         bool load_err=false;
04766         if((MEM_TotalPages()*4096/1024/1024)>1000) {
04767                 LOG_MSG("Stopped. 1000 MB is the maximum memory size for saving/loading states.");
04768 #if defined(WIN32)
04769                 MessageBox(GetHWND(),"Unsupported memory size.","Error",MB_OK);
04770 #endif
04771                 return;
04772         }
04773         SDL_PauseAudio(0);
04774         extern const char* RunningProgram;
04775         bool read_version=false;
04776         bool read_title=false;
04777         bool read_memorysize=false;
04778         std::string path;
04779         bool Get_Custom_SaveDir(std::string& savedir);
04780         if(Get_Custom_SaveDir(path)) {
04781                 path+=CROSS_FILESPLIT;
04782         } else {
04783                 extern std::string capturedir;
04784                 const size_t last_slash_idx = capturedir.find_last_of("\\/");
04785                 if (std::string::npos != last_slash_idx) {
04786                         path = capturedir.substr(0, last_slash_idx);
04787                 } else {
04788                         path = ".";
04789                 }
04790                 path += CROSS_FILESPLIT;
04791                 path +="save";
04792                 path += CROSS_FILESPLIT;
04793         }
04794         std::string temp;
04795         temp = path;
04796         std::stringstream slotname;
04797         slotname << slot+1;
04798         std::string save=temp+slotname.str()+".sav";
04799         std::ifstream check_slot;
04800         check_slot.open(save.c_str(), std::ifstream::in);
04801         if(check_slot.fail()) {
04802                 LOG_MSG("No saved slot - %d (%s)",(int)slot+1,save.c_str());
04803 #if defined(WIN32)
04804                 MessageBox(GetHWND(),"The selected save slot is empty.","Error",MB_OK);
04805 #endif
04806                 load_err=true;
04807                 return;
04808         }
04809 
04810         for (CompEntry::const_iterator i = components.begin(); i != components.end(); ++i) {
04811                 std::filebuf * fb;
04812                 std::ifstream ss;
04813                 std::ifstream check_file;
04814                 fb = ss.rdbuf();
04815                 
04816                 //LOG_MSG("Component is %s",i->first.c_str());
04817 
04818                 my_miniunz((char **)save.c_str(),i->first.c_str(),temp.c_str());
04819 
04820                 if(!read_version) {
04821                         my_miniunz((char **)save.c_str(),"DOSBox-X_Version",temp.c_str());
04822                         std::ifstream check_version;
04823                         int length = 8;
04824 
04825                         std::string tempname = temp+"DOSBox-X_Version";
04826                         check_version.open(tempname.c_str(), std::ifstream::in);
04827                         if(check_version.fail()) {
04828                                 LOG_MSG("Save state corrupted! Program in inconsistent state! - DOSBox-X_Version");
04829 #if defined(WIN32)
04830                                 MessageBox(GetHWND(),"Save state corrupted!","Error",MB_OK);
04831 #endif
04832                                 load_err=true;
04833                                 goto delete_all;
04834                         }
04835                         check_version.seekg (0, std::ios::end);
04836                         length = check_version.tellg();
04837                         check_version.seekg (0, std::ios::beg);
04838 
04839                         char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
04840                         check_version.read (buffer, length);
04841                         check_version.close();
04842                         buffer[length]='\0';
04843                         char *p=strrchr(buffer, '\n');
04844                         if (p!=NULL) *p=0;
04845                         std::string emulatorversion = std::string("DOSBox-X ") + VERSION + std::string(" (") + SDL_STRING + std::string(")\n") + GetPlatform();
04846                         if (p==NULL||strcasecmp(buffer,emulatorversion.c_str())) {
04847 #if defined(WIN32)
04848                                 if(!force_load_state&&MessageBox(GetHWND(),"DOSBox-X version mismatch. Load the state anyway?","Warning",MB_YESNO|MB_DEFBUTTON2)==IDNO) {
04849 #else
04850                                 if(!force_load_state) {
04851 #endif
04852                                         LOG_MSG("Aborted. Check your DOSBox-X version: %s",buffer);
04853                                         load_err=true;
04854                                         goto delete_all;
04855                                 }
04856                         }
04857                         read_version=true;
04858                 }
04859 
04860                 if(!read_title) {
04861                         my_miniunz((char **)save.c_str(),"Program_Name",temp.c_str());
04862                         std::ifstream check_title;
04863                         int length = 8;
04864 
04865                         std::string tempname = temp+"Program_Name";
04866                         check_title.open(tempname.c_str(), std::ifstream::in);
04867                         if(check_title.fail()) {
04868                                 LOG_MSG("Save state corrupted! Program in inconsistent state! - Program_Name");
04869 #if defined(WIN32)
04870                                 MessageBox(GetHWND(),"Save state corrupted!","Error",MB_OK);
04871 #endif
04872                                 load_err=true;
04873                                 goto delete_all;
04874                         }
04875                         check_title.seekg (0, std::ios::end);
04876                         length = check_title.tellg();
04877                         check_title.seekg (0, std::ios::beg);
04878 
04879                         char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
04880                         check_title.read (buffer, length);
04881                         check_title.close();
04882                         if (strncmp(buffer,RunningProgram,length)||!length) {
04883 #if defined(WIN32)
04884                                 if(!force_load_state&&MessageBox(GetHWND(),"Program name mismatch. Load the state anyway?","Warning",MB_YESNO|MB_DEFBUTTON2)==IDNO) {
04885 #else
04886                                 if(!force_load_state) {
04887 #endif
04888                                         buffer[length]='\0';
04889                                         LOG_MSG("Aborted. Check your program name: %s",buffer);
04890                                         load_err=true;
04891                                         goto delete_all;
04892                                 }
04893                                 if (length<9) {
04894                                         static char pname[9];
04895                                         if (length) {
04896                                                 strncpy(pname,buffer,length);
04897                                                 pname[length]=0;
04898                                         } else
04899                                                 strcpy(pname, "DOSBOX-X");
04900                                         RunningProgram=pname;
04901                                         GFX_SetTitle(-1,-1,-1,false);
04902                                 }
04903                         }
04904                         read_title=true;
04905                 }
04906 
04907                 if(!read_memorysize) {
04908                         my_miniunz((char **)save.c_str(),"Memory_Size",temp.c_str());
04909                         std::fstream check_memorysize;
04910                         int length = 8;
04911 
04912                         std::string tempname = temp+"Memory_Size";
04913                         check_memorysize.open(tempname.c_str(), std::ifstream::in);
04914                         if(check_memorysize.fail()) {
04915                                 LOG_MSG("Save state corrupted! Program in inconsistent state! - Memory_Size");
04916                                 load_err=true;
04917                                 goto delete_all;
04918                         }
04919                         check_memorysize.seekg (0, std::ios::end);
04920                         length = check_memorysize.tellg();
04921                         check_memorysize.seekg (0, std::ios::beg);
04922 
04923                         char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
04924                         check_memorysize.read (buffer, length);
04925                         check_memorysize.close();
04926                         char str[10];
04927                         itoa(MEM_TotalPages(), str, 10);
04928                         if(strncmp(buffer,str,length)) {
04929 #if defined(WIN32)
04930                                 if(!force_load_state&&MessageBox(GetHWND(),"Memory size mismatch. Load the state anyway?","Warning",MB_YESNO|MB_DEFBUTTON2)==IDNO) {
04931 #else
04932                                 if(!force_load_state) {
04933 #endif
04934                                         buffer[length]='\0';
04935                                         LOG_MSG("Aborted. Check your memory size.");
04936                                         load_err=true;
04937                                         goto delete_all;
04938                                 }
04939                         }
04940                         read_memorysize=true;
04941                 }
04942                 std::string realtemp;
04943                 realtemp = temp + i->first;
04944                 check_file.open(realtemp.c_str(), std::ifstream::in);
04945                 check_file.close();
04946                 if(check_file.fail()) {
04947                         LOG_MSG("Save state corrupted! Program in inconsistent state! - %s",i->first.c_str());
04948 #if defined(WIN32)
04949                         MessageBox(GetHWND(),"Save state corrupted!","Error",MB_OK);
04950 #endif
04951                         load_err=true;
04952                         goto delete_all;
04953                 }
04954 
04955                 fb->open(realtemp.c_str(),std::ios::in | std::ios::binary);
04956                 std::string str((std::istreambuf_iterator<char>(ss)), std::istreambuf_iterator<char>());
04957                 std::stringstream mystream;
04958                 mystream << (Util::decompress(str));
04959                 i->second.comp.setBytes(mystream);
04960                 if (mystream.rdbuf()->in_avail() != 0 || mystream.eof()) { //basic consistency check
04961                         LOG_MSG("Save state corrupted! Program in inconsistent state! - %s",i->first.c_str());
04962 #if defined(WIN32)
04963                         MessageBox(GetHWND(),"Save state corrupted!","Error",MB_OK);
04964 #endif
04965                         load_err=true;
04966                         goto delete_all;
04967                 }
04968                 //compress all other saved states except position "slot"
04969                 //const std::vector<RawBytes>& rb = i->second.rawBytes;
04970                 //std::for_each(rb.begin(), rb.begin() + slot, std::mem_fun_ref(&RawBytes::compress));
04971                 //std::for_each(rb.begin() + slot + 1, rb.end(), std::mem_fun_ref(&RawBytes::compress));
04972                 fb->close();
04973                 mystream.clear();
04974         }
04975 delete_all:
04976         std::string save2;
04977         for (CompEntry::const_iterator i = components.begin(); i != components.end(); ++i) {
04978                 save2=temp+i->first;
04979                 remove(save2.c_str());
04980         }
04981         save2=temp+"DOSBox-X_Version";
04982         remove(save2.c_str());
04983         save2=temp+"Program_Name";
04984         remove(save2.c_str());
04985         save2=temp+"Memory_Size";
04986         remove(save2.c_str());
04987         if (!load_err) LOG_MSG("Loaded. (Slot %d)",(int)slot+1);
04988 }
04989 
04990 bool SaveState::isEmpty(size_t slot) const {
04991         if (slot >= SLOT_COUNT) return true;
04992         std::string path;
04993         bool Get_Custom_SaveDir(std::string& savedir);
04994         if(Get_Custom_SaveDir(path)) {
04995                 path+=CROSS_FILESPLIT;
04996         } else {
04997                 extern std::string capturedir;
04998                 const size_t last_slash_idx = capturedir.find_last_of("\\/");
04999                 if (std::string::npos != last_slash_idx) {
05000                         path = capturedir.substr(0, last_slash_idx);
05001                 } else {
05002                         path = ".";
05003                 }
05004                 path += CROSS_FILESPLIT;
05005                 path +="save";
05006                 path += CROSS_FILESPLIT;
05007         }
05008         std::string temp;
05009         temp = path;
05010         std::stringstream slotname;
05011         slotname << slot+1;
05012         std::string save=temp+slotname.str()+".sav";
05013         std::ifstream check_slot;
05014         check_slot.open(save.c_str(), std::ifstream::in);
05015         return check_slot.fail();
05016 }
05017 
05018 std::string SaveState::getName(size_t slot) const {
05019         if (slot >= SLOT_COUNT) return "[Empty]";
05020         std::string path;
05021         bool Get_Custom_SaveDir(std::string& savedir);
05022         if(Get_Custom_SaveDir(path)) {
05023                 path+=CROSS_FILESPLIT;
05024         } else {
05025                 extern std::string capturedir;
05026                 const size_t last_slash_idx = capturedir.find_last_of("\\/");
05027                 if (std::string::npos != last_slash_idx) {
05028                         path = capturedir.substr(0, last_slash_idx);
05029                 } else {
05030                         path = ".";
05031                 }
05032                 path += CROSS_FILESPLIT;
05033                 path +="save";
05034                 path += CROSS_FILESPLIT;
05035         }
05036         std::string temp;
05037         temp = path;
05038         std::stringstream slotname;
05039         slotname << slot+1;
05040         std::string save=temp+slotname.str()+".sav";
05041         std::ifstream check_slot;
05042         check_slot.open(save.c_str(), std::ifstream::in);
05043         if (check_slot.fail()) return "[Empty]";
05044         my_miniunz((char **)save.c_str(),"Program_Name",temp.c_str());
05045         std::ifstream check_title;
05046         int length = 8;
05047         std::string tempname = temp+"Program_Name";
05048         check_title.open(tempname.c_str(), std::ifstream::in);
05049         if (check_title.fail()) {
05050                 remove(tempname.c_str());
05051                 return "";
05052         }
05053         check_title.seekg (0, std::ios::end);
05054         length = check_title.tellg();
05055         check_title.seekg (0, std::ios::beg);
05056         char * const buffer = (char*)alloca( (length+1) * sizeof(char));
05057         check_title.read (buffer, length);
05058         check_title.close();
05059         remove(tempname.c_str());
05060         buffer[length]='\0';
05061         return std::string(buffer);
05062 }