2070 lines
59 KiB
C
2070 lines
59 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Main emulator module where most things are controlled.
|
|
*
|
|
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
|
* Jasmine Iwanek, <jriwanek@gmail.com>
|
|
*
|
|
* Copyright 2008-2020 Sarah Walker.
|
|
* Copyright 2016-2020 Miran Grca.
|
|
* Copyright 2017-2020 Fred N. van Kempen.
|
|
* Copyright 2021 Laci bá'
|
|
* Copyright 2021 dob205
|
|
* Copyright 2021 Andreas J. Reichel.
|
|
* Copyright 2021-2025 Jasmine Iwanek.
|
|
*/
|
|
#include <inttypes.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <wchar.h>
|
|
#include <stdatomic.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
|
|
#ifndef _WIN32
|
|
# include <pwd.h>
|
|
#endif
|
|
#ifdef __APPLE__
|
|
# include <string.h>
|
|
# include <dispatch/dispatch.h>
|
|
# ifdef __aarch64__
|
|
# include <pthread.h>
|
|
# endif
|
|
#endif
|
|
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include <86box/config.h>
|
|
#include <86box/mem.h>
|
|
#include "cpu.h"
|
|
#ifdef USE_DYNAREC
|
|
# include "codegen_public.h"
|
|
#endif
|
|
#include "x86_ops.h"
|
|
#include <86box/io.h>
|
|
#include <86box/rom.h>
|
|
#include <86box/dma.h>
|
|
#include <86box/pci.h>
|
|
#include <86box/pic.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/device.h>
|
|
#include <86box/pit.h>
|
|
#include <86box/random.h>
|
|
#include <86box/nvr.h>
|
|
#include <86box/machine.h>
|
|
#include <86box/bugger.h>
|
|
#include <86box/postcard.h>
|
|
#include <86box/unittester.h>
|
|
#include <86box/novell_cardkey.h>
|
|
#include <86box/isamem.h>
|
|
#include <86box/isarom.h>
|
|
#include <86box/isartc.h>
|
|
#include <86box/lpt.h>
|
|
#include <86box/serial.h>
|
|
#include <86box/serial_passthrough.h>
|
|
#include <86box/keyboard.h>
|
|
#include <86box/mouse.h>
|
|
#include <86box/gameport.h>
|
|
#include <86box/fdd.h>
|
|
#include <86box/fdc.h>
|
|
#include <86box/fdc_ext.h>
|
|
#include <86box/hdd.h>
|
|
#include <86box/hdc.h>
|
|
#include <86box/hdc_ide.h>
|
|
#include <86box/scsi.h>
|
|
#include <86box/scsi_device.h>
|
|
#include <86box/cdrom.h>
|
|
#include <86box/cdrom_interface.h>
|
|
#include <86box/rdisk.h>
|
|
#include <86box/mo.h>
|
|
#include <86box/scsi_disk.h>
|
|
#include <86box/cdrom_image.h>
|
|
#include <86box/thread.h>
|
|
#include <86box/network.h>
|
|
#include <86box/sound.h>
|
|
#include <86box/midi.h>
|
|
#include <86box/snd_speaker.h>
|
|
#include <86box/video.h>
|
|
#include <86box/ui.h>
|
|
#include <86box/path.h>
|
|
#include <86box/plat.h>
|
|
#include <86box/version.h>
|
|
#include <86box/gdbstub.h>
|
|
#include <86box/machine_status.h>
|
|
#include <86box/apm.h>
|
|
#include <86box/acpi.h>
|
|
#include <86box/nv/vid_nv_rivatimer.h>
|
|
|
|
// Disable c99-designator to avoid the warnings about int ng
|
|
#ifdef __clang__
|
|
# if __has_warning("-Wunused-but-set-variable")
|
|
# pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
|
# endif
|
|
#endif
|
|
|
|
/* Stuff that used to be globally declared in plat.h but is now extern there
|
|
and declared here instead. */
|
|
int dopause = 1; /* system is paused */
|
|
atomic_flag doresize; /* screen resize requested */
|
|
volatile int is_quit; /* system exit requested */
|
|
uint64_t timer_freq;
|
|
char emu_version[200]; /* version ID string */
|
|
|
|
#ifdef MTR_ENABLED
|
|
int tracing_on = 0;
|
|
#endif
|
|
|
|
/* Commandline options. */
|
|
int dump_on_exit = 0; /* (O) dump regs on exit */
|
|
int start_in_fullscreen = 0; /* (O) start in fullscreen */
|
|
#ifdef _WIN32
|
|
int force_debug = 0; /* (O) force debug output */
|
|
#endif
|
|
#ifdef USE_WX
|
|
int video_fps = RENDER_FPS; /* (O) render speed in fps */
|
|
#endif
|
|
int settings_only = 0; /* (O) show only the settings dialog */
|
|
int confirm_exit_cmdl = 1; /* (O) do not ask for confirmation on quit if set to 0 */
|
|
#ifdef _WIN32
|
|
uint64_t unique_id = 0;
|
|
uint64_t source_hwnd = 0;
|
|
#endif
|
|
char rom_path[1024] = { '\0' }; /* (O) full path to ROMs */
|
|
rom_path_t rom_paths = { "", NULL }; /* (O) full paths to ROMs */
|
|
char log_path[1024] = { '\0' }; /* (O) full path of logfile */
|
|
char vm_name[1024] = { '\0' }; /* (O) display name of the VM */
|
|
int do_nothing = 0;
|
|
int dump_missing = 0;
|
|
int clear_cmos = 0;
|
|
#ifdef USE_INSTRUMENT
|
|
uint8_t instru_enabled = 0;
|
|
uint64_t instru_run_ms = 0;
|
|
#endif
|
|
int clear_flash = 0;
|
|
int auto_paused = 0;
|
|
|
|
/* Configuration values. */
|
|
int window_remember;
|
|
int vid_resize; /* (C) allow resizing */
|
|
int invert_display = 0; /* (C) invert the display */
|
|
int suppress_overscan = 0; /* (C) suppress overscans */
|
|
int lang_id = 0; /* (G) language id */
|
|
int scale = 0; /* (C) screen scale factor */
|
|
int dpi_scale = 0; /* (C) DPI scaling of the emulated
|
|
screen */
|
|
int vid_api = 0; /* (C) video renderer */
|
|
int vid_cga_contrast = 0; /* (C) video */
|
|
int video_fullscreen = 0; /* (C) video */
|
|
int video_fullscreen_scale = 0; /* (C) video */
|
|
int enable_overscan = 0; /* (C) video */
|
|
int force_43 = 0; /* (C) video */
|
|
int video_filter_method = 1; /* (C) video */
|
|
int video_vsync = 0; /* (C) video */
|
|
int video_framerate = -1; /* (C) video */
|
|
bool serial_passthrough_enabled[SERIAL_MAX - 1] = { 0, 0, 0, 0, 0, 0, 0 }; /* (C) activation and kind of
|
|
pass-through for serial ports */
|
|
int bugger_enabled = 0; /* (C) enable ISAbugger */
|
|
int novell_keycard_enabled = 0; /* (C) enable Novell NetWare 2.x key card emulation. */
|
|
int postcard_enabled = 0; /* (C) enable POST card */
|
|
int unittester_enabled = 0; /* (C) enable unit tester device */
|
|
int gameport_type[GAMEPORT_MAX] = { 0, 0 }; /* (C) enable gameports */
|
|
int isamem_type[ISAMEM_MAX] = { 0, 0, 0, 0 }; /* (C) enable ISA mem cards */
|
|
int isarom_type[ISAROM_MAX] = { 0, 0, 0, 0 }; /* (C) enable ISA ROM cards */
|
|
int isartc_type = 0; /* (C) enable ISA RTC card */
|
|
int gfxcard[GFXCARD_MAX] = { 0, 0 }; /* (C) graphics/video card */
|
|
int show_second_monitors = 1; /* (C) show non-primary monitors */
|
|
int sound_is_float = 1; /* (C) sound uses FP values */
|
|
int voodoo_enabled = 0; /* (C) video option */
|
|
int ibm8514_standalone_enabled = 0; /* (C) video option */
|
|
int xga_standalone_enabled = 0; /* (C) video option */
|
|
int da2_standalone_enabled = 0; /* (C) video option */
|
|
uint32_t mem_size = 0; /* (C) memory size (Installed on
|
|
system board)*/
|
|
uint32_t isa_mem_size = 0; /* (C) memory size (ISA Memory Cards) */
|
|
int cpu_use_dynarec = 0; /* (C) cpu uses/needs Dyna */
|
|
int cpu = 0; /* (C) cpu type */
|
|
int fpu_type = 0; /* (C) fpu type */
|
|
int fpu_softfloat = 0; /* (C) fpu uses softfloat */
|
|
int time_sync = 0; /* (C) enable time sync */
|
|
int confirm_reset = 1; /* (G) enable reset confirmation */
|
|
int confirm_exit = 1; /* (G) enable exit confirmation */
|
|
int confirm_save = 1; /* (G) enable save confirmation */
|
|
int enable_discord = 0; /* (C) enable Discord integration */
|
|
int pit_mode = -1; /* (C) force setting PIT mode */
|
|
int fm_driver = 0; /* (C) select FM sound driver */
|
|
int open_dir_usr_path = 0; /* (G) default file open dialog directory
|
|
of usr_path */
|
|
int video_fullscreen_scale_maximized = 0; /* (C) Whether fullscreen scaling settings
|
|
also apply when maximized. */
|
|
int do_auto_pause = 0; /* (C) Auto-pause the emulator on focus
|
|
loss */
|
|
int force_constant_mouse = 0; /* (C) Force constant updating of the mouse */
|
|
int hook_enabled = 1; /* (C) Keyboard hook is enabled */
|
|
int test_mode = 0; /* (C) Test mode */
|
|
char uuid[MAX_UUID_LEN] = { '\0' }; /* (C) UUID or machine identifier */
|
|
int sound_muted = 0; /* (C) Is sound muted? */
|
|
int jumpered_internal_ecp_dma = 0; /* (C) Jumpered internal EPC DMA */
|
|
int inhibit_multimedia_keys; /* (G) Inhibit multimedia keys on Windows. */
|
|
int force_10ms; /* (C) Force 10ms CPU frame intervals. */
|
|
int vmm_disabled = 0; /* (G) disable built-in manager */
|
|
char vmm_path_cfg[1024] = { '\0' }; /* (G) VMs path (unless -E is used)*/
|
|
|
|
int other_ide_present = 0; /* IDE controllers from non-IDE cards are
|
|
present */
|
|
int other_scsi_present = 0; /* SCSI controllers from non-SCSI cards are
|
|
present */
|
|
|
|
int is_pcjr = 0; /* The current machine is PCjr. */
|
|
int portable_mode = 0; /* We are running in portable mode
|
|
(global dirs = exe path) */
|
|
int global_cfg_overridden = 0; /* Global config file was overriden on command line */
|
|
|
|
int monitor_edid = 0; /* (C) Which EDID to use. 0=default, 1=custom. */
|
|
char monitor_edid_path[1024] = { 0 }; /* (C) Path to custom EDID */
|
|
|
|
double video_gl_input_scale = 1.0; /* (C) OpenGL 3.x input scale */
|
|
int video_gl_input_scale_mode = FULLSCR_SCALE_FULL; /* (C) OpenGL 3.x input stretch mode */
|
|
int color_scheme = 0; /* (C) Color scheme of UI (Windows-only) */
|
|
|
|
// Accelerator key array
|
|
struct accelKey acc_keys[NUM_ACCELS];
|
|
|
|
// Default accelerator key values
|
|
struct accelKey def_acc_keys[NUM_ACCELS] = {
|
|
{
|
|
.name="send_ctrl_alt_del",
|
|
.desc="Send Control+Alt+Del",
|
|
.seq="Ctrl+F12"
|
|
},
|
|
{
|
|
.name="send_ctrl_alt_esc",
|
|
.desc="Send Control+Alt+Escape",
|
|
.seq="Ctrl+F10"
|
|
},
|
|
{
|
|
.name="fullscreen",
|
|
.desc="Toggle fullscreen",
|
|
.seq="Ctrl+Alt+PgUp"
|
|
},
|
|
{
|
|
.name="screenshot",
|
|
.desc="Screenshot",
|
|
.seq="Ctrl+F11"
|
|
},
|
|
{
|
|
.name="release_mouse",
|
|
.desc="Release mouse pointer",
|
|
.seq="Ctrl+End"
|
|
},
|
|
{
|
|
.name="hard_reset",
|
|
.desc="Hard reset",
|
|
.seq="Ctrl+Alt+F12"
|
|
},
|
|
{
|
|
.name="pause",
|
|
.desc="Toggle pause",
|
|
.seq="Ctrl+Alt+F1"
|
|
},
|
|
{
|
|
.name="mute",
|
|
.desc="Toggle mute",
|
|
.seq="Ctrl+Alt+M"
|
|
}
|
|
};
|
|
|
|
char vmm_path[1024] = { '\0' }; /* VM manager path to scan for VMs */
|
|
int start_vmm = 1;
|
|
|
|
/* Statistics. */
|
|
extern int mmuflush;
|
|
extern int readlnum;
|
|
extern int writelnum;
|
|
|
|
/* emulator % */
|
|
int fps;
|
|
int framecount;
|
|
|
|
extern int CPUID;
|
|
extern int output;
|
|
int atfullspeed;
|
|
|
|
extern double exp_pow_table[0x800];
|
|
|
|
char exe_path[2048]; /* path (dir) of executable */
|
|
char usr_path[1024]; /* path (dir) of user data */
|
|
char cfg_path[1024]; /* full path of config file */
|
|
char global_cfg_path[1024]; /* full path of config file */
|
|
FILE *stdlog = NULL; /* file to log output to */
|
|
#if 0
|
|
int scrnsz_x = SCREEN_RES_X; /* current screen size, X */
|
|
int scrnsz_y = SCREEN_RES_Y; /* current screen size, Y */
|
|
#endif
|
|
int config_changed; /* config has changed */
|
|
int title_update;
|
|
int framecountx = 0;
|
|
int hard_reset_pending = 0;
|
|
|
|
#if 0
|
|
int unscaled_size_x = SCREEN_RES_X; /* current unscaled size X */
|
|
int unscaled_size_y = SCREEN_RES_Y; /* current unscaled size Y */
|
|
int efscrnsz_y = SCREEN_RES_Y;
|
|
#endif
|
|
|
|
__thread int is_cpu_thread = 0;
|
|
|
|
static wchar_t mouse_msg[3][200];
|
|
|
|
static volatile atomic_int do_pause_ack = 0;
|
|
static volatile atomic_int pause_ack = 0;
|
|
|
|
#define LOG_SIZE_BUFFER 8192 /* Log size buffer */
|
|
|
|
#ifndef RELEASE_BUILD
|
|
|
|
static char buff[LOG_SIZE_BUFFER];
|
|
|
|
static int seen = 0;
|
|
|
|
static int suppr_seen = 1;
|
|
|
|
// Functions only used in this translation unit
|
|
void pclog_ensure_stdlog_open(void);
|
|
#endif
|
|
|
|
/*
|
|
Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic
|
|
*/
|
|
void pclog_ensure_stdlog_open(void)
|
|
{
|
|
#ifndef RELEASE_BUILD
|
|
if (stdlog == NULL) {
|
|
if (log_path[0] != '\0') {
|
|
stdlog = plat_fopen(log_path, "w");
|
|
if (stdlog == NULL)
|
|
stdlog = stdout;
|
|
} else
|
|
stdlog = stdout;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Log something to the logfile or stdout.
|
|
*
|
|
* To avoid excessively-large logfiles because some
|
|
* module repeatedly logs, we keep track of what is
|
|
* being logged, and catch repeating entries.
|
|
*/
|
|
void
|
|
pclog_ex(UNUSED(const char *fmt), UNUSED(va_list ap))
|
|
{
|
|
#ifndef RELEASE_BUILD
|
|
char temp[LOG_SIZE_BUFFER];
|
|
|
|
if (strcmp(fmt, "") == 0)
|
|
return;
|
|
|
|
pclog_ensure_stdlog_open();
|
|
|
|
vsprintf(temp, fmt, ap);
|
|
if (suppr_seen && !strcmp(buff, temp))
|
|
seen++;
|
|
else {
|
|
if (suppr_seen && seen)
|
|
fprintf(stdlog, "*** %d repeats ***\n", seen);
|
|
seen = 0;
|
|
strcpy(buff, temp);
|
|
fprintf(stdlog, "%s", temp);
|
|
}
|
|
|
|
fflush(stdlog);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
pclog_toggle_suppr(void)
|
|
{
|
|
#ifndef RELEASE_BUILD
|
|
suppr_seen ^= 1;
|
|
#endif
|
|
}
|
|
|
|
/* Log something. We only do this in non-release builds. */
|
|
void
|
|
pclog(UNUSED(const char *fmt), ...)
|
|
{
|
|
#ifndef RELEASE_BUILD
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
#endif
|
|
}
|
|
|
|
/* Log something even in release builds. */
|
|
void
|
|
always_log(const char *fmt, ...)
|
|
{
|
|
char temp[LOG_SIZE_BUFFER];
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (stdlog == NULL) {
|
|
if (log_path[0] != '\0') {
|
|
stdlog = plat_fopen(log_path, "w");
|
|
if (stdlog == NULL)
|
|
stdlog = stdout;
|
|
} else
|
|
stdlog = stdout;
|
|
}
|
|
|
|
vsprintf(temp, fmt, ap);
|
|
fprintf(stdlog, "%s", temp);
|
|
fflush(stdlog);
|
|
va_end(ap);
|
|
}
|
|
|
|
/* Log a fatal error, and display a UI message before exiting. */
|
|
void
|
|
fatal(const char *fmt, ...)
|
|
{
|
|
char temp[LOG_SIZE_BUFFER];
|
|
va_list ap;
|
|
char *sp;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (stdlog == NULL) {
|
|
if (log_path[0] != '\0') {
|
|
stdlog = plat_fopen(log_path, "w");
|
|
if (stdlog == NULL)
|
|
stdlog = stdout;
|
|
} else
|
|
stdlog = stdout;
|
|
}
|
|
|
|
vsprintf(temp, fmt, ap);
|
|
fprintf(stdlog, "%s", temp);
|
|
fflush(stdlog);
|
|
va_end(ap);
|
|
|
|
nvr_save();
|
|
|
|
config_save();
|
|
|
|
#ifdef ENABLE_808X_LOG
|
|
dumpregs(1);
|
|
#endif
|
|
|
|
/* Make sure the message does not have a trailing newline. */
|
|
if ((sp = strchr(temp, '\n')) != NULL)
|
|
*sp = '\0';
|
|
|
|
do_pause(2);
|
|
|
|
ui_msgbox(MBX_ERROR | MBX_FATAL | MBX_ANSI, temp);
|
|
|
|
/* Cleanly terminate all of the emulator's components so as
|
|
to avoid things like threads getting stuck. */
|
|
do_stop();
|
|
|
|
fflush(stdlog);
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
void
|
|
fatal_ex(const char *fmt, va_list ap)
|
|
{
|
|
char temp[LOG_SIZE_BUFFER];
|
|
char *sp;
|
|
|
|
if (stdlog == NULL) {
|
|
if (log_path[0] != '\0') {
|
|
stdlog = plat_fopen(log_path, "w");
|
|
if (stdlog == NULL)
|
|
stdlog = stdout;
|
|
} else
|
|
stdlog = stdout;
|
|
}
|
|
|
|
vsprintf(temp, fmt, ap);
|
|
fprintf(stdlog, "%s", temp);
|
|
fflush(stdlog);
|
|
|
|
nvr_save();
|
|
|
|
config_save();
|
|
|
|
#ifdef ENABLE_808X_LOG
|
|
dumpregs(1);
|
|
#endif
|
|
|
|
/* Make sure the message does not have a trailing newline. */
|
|
if ((sp = strchr(temp, '\n')) != NULL)
|
|
*sp = '\0';
|
|
|
|
do_pause(2);
|
|
|
|
ui_msgbox(MBX_ERROR | MBX_FATAL | MBX_ANSI, temp);
|
|
|
|
/* Cleanly terminate all of the emulator's components so as
|
|
to avoid things like threads getting stuck. */
|
|
do_stop();
|
|
|
|
fflush(stdlog);
|
|
}
|
|
|
|
/* Log a warning error, and display a UI message without exiting. */
|
|
void
|
|
warning(const char *fmt, ...)
|
|
{
|
|
char temp[LOG_SIZE_BUFFER];
|
|
va_list ap;
|
|
char *sp;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (stdlog == NULL) {
|
|
if (log_path[0] != '\0') {
|
|
stdlog = plat_fopen(log_path, "w");
|
|
if (stdlog == NULL)
|
|
stdlog = stdout;
|
|
} else
|
|
stdlog = stdout;
|
|
}
|
|
|
|
vsprintf(temp, fmt, ap);
|
|
fprintf(stdlog, "%s", temp);
|
|
fflush(stdlog);
|
|
va_end(ap);
|
|
|
|
/* Make sure the message does not have a trailing newline. */
|
|
if ((sp = strchr(temp, '\n')) != NULL)
|
|
*sp = '\0';
|
|
|
|
do_pause(2);
|
|
|
|
ui_msgbox(MBX_ERROR | MBX_ANSI, temp);
|
|
|
|
fflush(stdlog);
|
|
|
|
do_pause(0);
|
|
}
|
|
|
|
void
|
|
warning_ex(const char *fmt, va_list ap)
|
|
{
|
|
char temp[LOG_SIZE_BUFFER];
|
|
char *sp;
|
|
|
|
if (stdlog == NULL) {
|
|
if (log_path[0] != '\0') {
|
|
stdlog = plat_fopen(log_path, "w");
|
|
if (stdlog == NULL)
|
|
stdlog = stdout;
|
|
} else
|
|
stdlog = stdout;
|
|
}
|
|
|
|
vsprintf(temp, fmt, ap);
|
|
fprintf(stdlog, "%s", temp);
|
|
fflush(stdlog);
|
|
|
|
/* Make sure the message does not have a trailing newline. */
|
|
if ((sp = strchr(temp, '\n')) != NULL)
|
|
*sp = '\0';
|
|
|
|
do_pause(2);
|
|
|
|
ui_msgbox(MBX_ERROR | MBX_ANSI, temp);
|
|
|
|
fflush(stdlog);
|
|
|
|
do_pause(0);
|
|
}
|
|
|
|
#ifdef ENABLE_PC_LOG
|
|
int pc_do_log = ENABLE_PC_LOG;
|
|
|
|
static void
|
|
pc_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (pc_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define pc_log(fmt, ...)
|
|
#endif
|
|
|
|
static void
|
|
delete_nvr_file(uint8_t flash)
|
|
{
|
|
char *fn = NULL;
|
|
int c;
|
|
|
|
/* Set up the NVR file's name. */
|
|
c = strlen(machine_get_nvr_name()) + 5;
|
|
fn = (char *) malloc(c + 1);
|
|
|
|
if (fn == NULL)
|
|
fatal("Error allocating memory for the removal of the %s file\n",
|
|
flash ? "BIOS flash" : "CMOS");
|
|
|
|
if (flash)
|
|
sprintf(fn, "%s.bin", machine_get_nvr_name());
|
|
else
|
|
sprintf(fn, "%s.nvr", machine_get_nvr_name());
|
|
|
|
remove(nvr_path(fn));
|
|
|
|
free(fn);
|
|
fn = NULL;
|
|
}
|
|
|
|
extern void device_find_all_descs(void);
|
|
|
|
static void
|
|
pc_show_usage(char *s)
|
|
{
|
|
char p[8192] = { 0 };
|
|
|
|
sprintf(p,
|
|
"\n%sUsage: 86box [options] [cfg-file]\n\n"
|
|
"Valid options are:\n\n"
|
|
"-? or --help\t\t\t- show this information\n"
|
|
#ifdef SHOW_EXTRA_PARAMS
|
|
"-C or --config path\t\t- set 'path' to be config file\n"
|
|
#endif
|
|
#ifdef _WIN32
|
|
"-D or --debug\t\t\t- force debug output logging\n"
|
|
#endif
|
|
#ifndef USE_SDL_UI
|
|
"-E or --vmmpath\t\t- vm manager path\n"
|
|
#endif
|
|
"-F or --fullscreen\t\t- start in fullscreen mode\n"
|
|
"-G or --lang langid\t\t- start with specified language\n"
|
|
"\t\t\t\t (e.g. en-US, or system)\n"
|
|
#ifdef SHOW_EXTRA_PARAMS
|
|
#ifdef _WIN32
|
|
"-H or --hwnd id,hwnd\t\t- sends back the main dialog's hwnd\n"
|
|
#endif
|
|
#endif
|
|
"-I or --image d:path\t\t- load 'path' as floppy image on drive d\n"
|
|
#ifdef USE_INSTRUMENT
|
|
"-J or --instrument name\t- set 'name' to be the profiling instrument\n"
|
|
#endif
|
|
"-L or --logfile path\t\t- set 'path' to be the logfile\n"
|
|
"-M or --missing\t\t- dump missing machines and video cards\n"
|
|
"-N or --noconfirm\t\t- do not ask for confirmation on quit\n"
|
|
"-P or --vmpath path\t\t- set 'path' to be root for vm\n"
|
|
"-O or --global path\t\t- set 'path' to be global config file\n"
|
|
"-R or --rompath path\t\t- set 'path' to be ROM path\n"
|
|
#ifndef USE_SDL_UI
|
|
"-S or --settings\t\t\t- show only the settings dialog\n"
|
|
#endif
|
|
#ifdef SHOW_EXTRA_PARAMS
|
|
"-T or --testmode\t\t- test mode: execute the test mode entry\n"
|
|
"\t\t\t\t point on init/hard reset\n"
|
|
#endif
|
|
"-V or --vmname name\t\t- overrides the name of the running VM\n"
|
|
#ifdef _WIN32
|
|
"-W or --nohook\t\t- disables keyboard hook\n"
|
|
#else
|
|
"-W or --nohook\t\t- alters keyboard behavior\n"
|
|
#endif
|
|
"-X or --clear what\t\t- clears the 'what' (cmos/flash/both)\n"
|
|
#ifdef SHOW_EXTRA_PARAMS
|
|
"-Y or --donothing\t\t- do not show any UI or run the emulation\n"
|
|
#endif
|
|
"-Z or --lastvmpath\t\t- the last param. is VM path rather than config\n"
|
|
"\nA config file can be specified. If none is, the default file will be used.\n",
|
|
s);
|
|
|
|
#ifdef _WIN32
|
|
ui_msgbox(MBX_ANSI | ((s == NULL) ? MBX_INFO : MBX_WARNING), p);
|
|
#else
|
|
if (s == NULL)
|
|
always_log("%s", p);
|
|
else
|
|
ui_msgbox(MBX_ANSI | MBX_WARNING, p);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Perform initial startup of the PC.
|
|
*
|
|
* This is the platform-indepenent part of the startup,
|
|
* where we check commandline arguments and load a
|
|
* configuration file.
|
|
*/
|
|
int
|
|
pc_init(int argc, char *argv[])
|
|
{
|
|
char *ppath = NULL;
|
|
char *rpath = NULL;
|
|
char *cfg = NULL;
|
|
char *global = NULL;
|
|
char *p;
|
|
char temp[2048];
|
|
char *fn[FDD_NUM] = { NULL };
|
|
char drive = 0;
|
|
char *temp2 = NULL;
|
|
char *what;
|
|
const struct tm *info;
|
|
time_t now;
|
|
int c;
|
|
int lvmp = 0;
|
|
#ifdef ENABLE_NG
|
|
int ng = 0;
|
|
#endif
|
|
#ifdef _WIN32
|
|
uint32_t *uid;
|
|
uint32_t *shwnd;
|
|
#endif
|
|
int lang_init = 0;
|
|
|
|
/* Grab the executable's full path. */
|
|
plat_get_exe_name(exe_path, sizeof(exe_path) - 1);
|
|
p = path_get_filename(exe_path);
|
|
*p = '\0';
|
|
#if defined(__APPLE__)
|
|
c = strlen(exe_path);
|
|
if ((c >= 16) && !strcmp(&exe_path[c - 16], "/Contents/MacOS/")) {
|
|
exe_path[c - 16] = '\0';
|
|
p = path_get_filename(exe_path);
|
|
*p = '\0';
|
|
}
|
|
#elif !defined(_WIN32)
|
|
/* Grab the actual path if we are an AppImage. */
|
|
p = getenv("APPIMAGE");
|
|
if (p && (p[0] != '\0'))
|
|
path_get_dirname(exe_path, p);
|
|
#endif
|
|
|
|
path_normalize(exe_path);
|
|
path_slash(exe_path);
|
|
|
|
/*
|
|
* Determine if we are running in portable mode.
|
|
*
|
|
* We enable portable mode if the EXE path
|
|
* contains the global config file.
|
|
*/
|
|
path_append_filename(temp, exe_path, GLOBAL_CONFIG_FILE);
|
|
|
|
FILE *fp = fopen(temp, "r");
|
|
if (fp) {
|
|
portable_mode = 1;
|
|
fclose(fp);
|
|
}
|
|
|
|
/*
|
|
* Get the current working directory.
|
|
*
|
|
* This is normally the directory from where the
|
|
* program was run. If we have been started via
|
|
* a shortcut (desktop icon), however, the CWD
|
|
* could have been set to something else.
|
|
*/
|
|
plat_getcwd(usr_path, sizeof(usr_path) - 1);
|
|
plat_getcwd(rom_path, sizeof(rom_path) - 1);
|
|
|
|
for (c = 1; c < argc; c++) {
|
|
if (argv[c][0] != '-')
|
|
break;
|
|
|
|
if (!strcasecmp(argv[c], "--help") || !strcasecmp(argv[c], "-?")) {
|
|
usage:
|
|
for (uint8_t i = 0; i < FDD_NUM; i++) {
|
|
if (fn[i] != NULL) {
|
|
free(fn[i]);
|
|
fn[i] = NULL;
|
|
}
|
|
}
|
|
|
|
pc_show_usage("");
|
|
return 0;
|
|
} else if (!strcasecmp(argv[c], "--lastvmpath") || !strcasecmp(argv[c], "-Z")) {
|
|
lvmp = 1;
|
|
#ifdef _WIN32
|
|
} else if (!strcasecmp(argv[c], "--debug") || !strcasecmp(argv[c], "-D")) {
|
|
force_debug = 1;
|
|
#endif
|
|
#ifndef USE_SDL_UI
|
|
} else if (!strcasecmp(argv[c], "--vmmpath") ||
|
|
!strcasecmp(argv[c], "-E")) {
|
|
/* Using this variable for vm manager path
|
|
Temporary solution!*/
|
|
if ((c+1) == argc) goto usage;
|
|
char *vp = argv[++c];
|
|
if ((strlen(vp) + 1) >= sizeof(vmm_path))
|
|
memcpy(vmm_path, vp, sizeof(vmm_path));
|
|
else
|
|
memcpy(vmm_path, vp, strlen(vp) + 1);
|
|
#endif
|
|
} else if (!strcasecmp(argv[c], "--fullscreen") || !strcasecmp(argv[c], "-F")) {
|
|
start_in_fullscreen = 1;
|
|
} else if (!strcasecmp(argv[c], "--logfile") || !strcasecmp(argv[c], "-L")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
strcpy(log_path, argv[++c]);
|
|
} else if (!strcasecmp(argv[c], "--vmpath") || !strcasecmp(argv[c], "-P")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
ppath = argv[++c];
|
|
start_vmm = 0;
|
|
} else if (!strcasecmp(argv[c], "--rompath") || !strcasecmp(argv[c], "-R")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
rpath = argv[++c];
|
|
rom_add_path(rpath);
|
|
} else if (!strcasecmp(argv[c], "--config") || !strcasecmp(argv[c], "-C")) {
|
|
if ((c + 1) == argc || plat_dir_check(argv[c + 1]))
|
|
goto usage;
|
|
|
|
cfg = argv[++c];
|
|
start_vmm = 0;
|
|
} else if (!strcasecmp(argv[c], "--global") || !strcasecmp(argv[c], "-O")) {
|
|
if ((c + 1) == argc || plat_dir_check(argv[c + 1]))
|
|
goto usage;
|
|
|
|
global_cfg_overridden = 1;
|
|
global = argv[++c];
|
|
} else if (!strcasecmp(argv[c], "--image") || !strcasecmp(argv[c], "-I")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
temp2 = (char *) calloc(2048, 1);
|
|
if (sscanf(argv[++c], "%c:%2047s", &drive, temp2) != 2) {
|
|
fprintf(stderr, "Invalid input format for --image option.\n");
|
|
free(temp2);
|
|
goto usage;
|
|
}
|
|
if (drive > 0x40)
|
|
drive = (drive & 0x1f) - 1;
|
|
else
|
|
drive = drive & 0x1f;
|
|
if (drive < 0)
|
|
drive = 0;
|
|
if (drive >= FDD_NUM)
|
|
drive = FDD_NUM - 1;
|
|
fn[(int) drive] = (char *) calloc(2048, 1);
|
|
strcpy(fn[(int) drive], temp2);
|
|
pclog("Drive %c: %s\n", drive + 0x41, fn[(int) drive]);
|
|
free(temp2);
|
|
temp2 = NULL;
|
|
} else if (!strcasecmp(argv[c], "--vmname") || !strcasecmp(argv[c], "-V")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
strcpy(vm_name, argv[++c]);
|
|
#ifndef USE_SDL_UI
|
|
} else if (!strcasecmp(argv[c], "--settings") || !strcasecmp(argv[c], "-S")) {
|
|
settings_only = 1;
|
|
#endif
|
|
} else if (!strcasecmp(argv[c], "--testmode") || !strcasecmp(argv[c], "-T")) {
|
|
test_mode = 1;
|
|
} else if (!strcasecmp(argv[c], "--noconfirm") || !strcasecmp(argv[c], "-N")) {
|
|
confirm_exit_cmdl = 0;
|
|
} else if (!strcasecmp(argv[c], "--missing") || !strcasecmp(argv[c], "-M")) {
|
|
dump_missing = 1;
|
|
} else if (!strcasecmp(argv[c], "--donothing") || !strcasecmp(argv[c], "-Y")) {
|
|
do_nothing = 1;
|
|
} else if (!strcasecmp(argv[c], "--nohook") || !strcasecmp(argv[c], "-W")) {
|
|
hook_enabled = 0;
|
|
} else if (!strcasecmp(argv[c], "--clear") || !strcasecmp(argv[c], "-X")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
what = argv[++c];
|
|
|
|
if (!strcasecmp(what, "cmos"))
|
|
clear_cmos = 1;
|
|
else if (!strcasecmp(what, "flash"))
|
|
clear_flash = 1;
|
|
else if (!strcasecmp(what, "both")) {
|
|
clear_cmos = 1;
|
|
clear_flash = 1;
|
|
} else
|
|
goto usage;
|
|
#ifdef _WIN32
|
|
} else if (!strcasecmp(argv[c], "--hwnd") || !strcasecmp(argv[c], "-H")) {
|
|
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
|
|
uid = (uint32_t *) &unique_id;
|
|
shwnd = (uint32_t *) &source_hwnd;
|
|
sscanf(argv[++c], "%08X%08X,%08X%08X", uid + 1, uid, shwnd + 1, shwnd);
|
|
#endif
|
|
} else if (!strcasecmp(argv[c], "--lang") || !strcasecmp(argv[c], "-G")) {
|
|
// This function is currently unimplemented for *nix but has placeholders.
|
|
|
|
lang_init = plat_language_code(argv[++c]);
|
|
if (!lang_init)
|
|
always_log("\nWarning: Invalid language code, ignoring --lang parameter.\n\n");
|
|
|
|
// The return value of 0 only means that the code is invalid,
|
|
// not related to that translation is exists or not for the
|
|
// selected language.
|
|
} else if (!strcasecmp(argv[c], "--test") || !strcasecmp(argv[c], "-T")) {
|
|
/* some (undocumented) test function here.. */
|
|
|
|
/* .. and then exit. */
|
|
return 0;
|
|
#ifdef USE_INSTRUMENT
|
|
} else if (!strcasecmp(argv[c], "--instrument") || !strcasecmp(argv[c], "-J")) {
|
|
if ((c + 1) == argc)
|
|
goto usage;
|
|
instru_enabled = 1;
|
|
sscanf(argv[++c], "%llu", &instru_run_ms);
|
|
#endif
|
|
}
|
|
|
|
/* Uhm... out of options here.. */
|
|
else
|
|
goto usage;
|
|
}
|
|
|
|
/* One argument (config file) allowed. */
|
|
if (c < argc) {
|
|
if (lvmp)
|
|
ppath = argv[c++];
|
|
else
|
|
cfg = argv[c++];
|
|
|
|
start_vmm = 0;
|
|
}
|
|
|
|
if (c != argc)
|
|
goto usage;
|
|
|
|
path_slash(usr_path);
|
|
path_slash(rom_path);
|
|
|
|
/*
|
|
* If the user provided a path for files, use that
|
|
* instead of the current working directory. We do
|
|
* make sure that if that was a relative path, we
|
|
* make it absolute.
|
|
*/
|
|
if (ppath != NULL) {
|
|
if (!path_abs(ppath)) {
|
|
/*
|
|
* This looks like a relative path.
|
|
*
|
|
* Add it to the current working directory
|
|
* to convert it (back) to an absolute path.
|
|
*/
|
|
strcat(usr_path, ppath);
|
|
} else {
|
|
/*
|
|
* The user-provided path seems like an
|
|
* absolute path, so just use that.
|
|
*/
|
|
strcpy(usr_path, ppath);
|
|
}
|
|
|
|
/* If the specified path does not yet exist,
|
|
create it. */
|
|
if (!plat_dir_check(usr_path))
|
|
plat_dir_create(usr_path);
|
|
}
|
|
|
|
// Add the VM-local ROM path.
|
|
path_append_filename(temp, usr_path, "roms");
|
|
rom_add_path(temp);
|
|
|
|
// Add the standard ROM path in the same directory as the executable.
|
|
path_append_filename(temp, exe_path, "roms");
|
|
rom_add_path(temp);
|
|
|
|
plat_init_rom_paths();
|
|
|
|
/*
|
|
* If the user provided a path for ROMs, use that
|
|
* instead of the current working directory. We do
|
|
* make sure that if that was a relative path, we
|
|
* make it absolute.
|
|
*/
|
|
if (rpath != NULL) {
|
|
if (!path_abs(rpath)) {
|
|
/*
|
|
* This looks like a relative path.
|
|
*
|
|
* Add it to the current working directory
|
|
* to convert it (back) to an absolute path.
|
|
*/
|
|
strcat(rom_path, rpath);
|
|
} else {
|
|
/*
|
|
* The user-provided path seems like an
|
|
* absolute path, so just use that.
|
|
*/
|
|
strcpy(rom_path, rpath);
|
|
}
|
|
|
|
/* If the specified path does not yet exist,
|
|
create it. */
|
|
if (!plat_dir_check(rom_path))
|
|
plat_dir_create(rom_path);
|
|
} else
|
|
rom_path[0] = '\0';
|
|
|
|
/* Grab the name of the configuration file. */
|
|
if (cfg == NULL)
|
|
cfg = CONFIG_FILE;
|
|
|
|
/*
|
|
* If the configuration file name has (part of)
|
|
* a pathname, consider that to be part of the
|
|
* actual working directory.
|
|
*
|
|
* This can happen when people load a config
|
|
* file using the UI, for example.
|
|
*/
|
|
p = path_get_filename(cfg);
|
|
if (cfg != p) {
|
|
/*
|
|
* OK, the configuration file name has a
|
|
* path component. Separate the two, and
|
|
* add the path component to the cfg path.
|
|
*/
|
|
*(p - 1) = '\0';
|
|
|
|
/*
|
|
* If this is an absolute path, keep it, as
|
|
* there is probably have a reason to do so.
|
|
* Otherwise, assume the pathname given is
|
|
* relative to whatever the usr_path is.
|
|
*/
|
|
if (path_abs(cfg))
|
|
strcpy(usr_path, cfg);
|
|
else
|
|
strcat(usr_path, cfg);
|
|
}
|
|
|
|
/* Make sure we have a trailing backslash. */
|
|
path_slash(usr_path);
|
|
if (rom_path[0] != '\0')
|
|
path_slash(rom_path);
|
|
|
|
/* At this point, we can safely create the full path name. */
|
|
path_append_filename(cfg_path, usr_path, p);
|
|
|
|
/* Build the global configuration file path. */
|
|
if (global == NULL) {
|
|
plat_get_global_config_dir(global_cfg_path, sizeof(global_cfg_path));
|
|
path_append_filename(global_cfg_path, global_cfg_path, GLOBAL_CONFIG_FILE);
|
|
} else {
|
|
strncpy(global_cfg_path, global, sizeof(global_cfg_path) - 1);
|
|
}
|
|
|
|
/*
|
|
* Get the current directory's name
|
|
*
|
|
* At this point usr_path is perfectly initialized.
|
|
* If no --vmname parameter specified we'll use the
|
|
* working directory name as the VM's name.
|
|
*/
|
|
if (strlen(vm_name) == 0) {
|
|
char ltemp[1024] = { '\0' };
|
|
path_get_dirname(ltemp, usr_path);
|
|
strcpy(vm_name, path_get_filename(ltemp));
|
|
}
|
|
|
|
/*
|
|
* This is where we start outputting to the log file,
|
|
* if there is one. Create a little info header first.
|
|
*/
|
|
struct tm time_buf;
|
|
|
|
(void) time(&now);
|
|
#ifdef _WIN32
|
|
if (localtime_s(&time_buf, &now) == 0)
|
|
info = &time_buf;
|
|
else
|
|
info = NULL;
|
|
#else
|
|
info = localtime_r(&now, &time_buf);
|
|
#endif
|
|
|
|
if (info)
|
|
strftime(temp, sizeof(temp), "%Y/%m/%d %H:%M:%S", info);
|
|
else
|
|
strcpy(temp, "unknown");
|
|
|
|
pclog("#\n# %ls v%ls logfile, created %s\n#\n",
|
|
EMU_NAME_W, EMU_VERSION_FULL_W, temp);
|
|
|
|
if (portable_mode) {
|
|
pclog("# Portable mode enabled.\n");
|
|
}
|
|
|
|
pclog("# Emulator path: %s\n", exe_path);
|
|
pclog("# Global configuration file: %s\n", global_cfg_path);
|
|
|
|
/* Load the global configuration file. */
|
|
config_load_global();
|
|
config_save_global(); // hack
|
|
|
|
/* Determine whether to start the VM manager. */
|
|
#ifndef USE_SDL_UI
|
|
if (vmm_disabled && start_vmm)
|
|
#endif
|
|
{
|
|
start_vmm = 0;
|
|
#ifdef __APPLE__
|
|
if (!strncmp(exe_path, "/private/var/folders/", 21)) {
|
|
ui_msgbox_header(MBX_FATAL, L"App Translocation", EMU_NAME_W L" cannot determine the emulated machine's location due to a macOS security feature. Please move the " EMU_NAME_W L" app to another folder (not /Applications), or make a copy of it and open that copy instead.");
|
|
return 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef USE_SDL_UI
|
|
if (strlen(vmm_path) != 0) {
|
|
/* -E specified on the command line. */
|
|
start_vmm = 1;
|
|
} else {
|
|
strncpy(vmm_path, vmm_path_cfg, sizeof(vmm_path) - 1);
|
|
}
|
|
|
|
if (start_vmm) {
|
|
pclog("# VM Manager enabled. Path: %s\n", vmm_path);
|
|
strncpy(usr_path, vmm_path, sizeof(usr_path) - 1);
|
|
} else
|
|
#endif
|
|
{
|
|
pclog("# VM: %s\n#\n", vm_name);
|
|
pclog("# Configuration file: %s\n#\n\n", cfg_path);
|
|
pclog("# Userfiles path: %s\n", usr_path);
|
|
|
|
for (rom_path_t *rom_path = &rom_paths; rom_path != NULL; rom_path = rom_path->next) {
|
|
pclog("# ROM path: %s\n", rom_path->path);
|
|
}
|
|
|
|
/*
|
|
* We are about to read the configuration file, which MAY
|
|
* put data into global variables (the hard- and floppy
|
|
* disks are an example) so we have to initialize those
|
|
* modules before we load the config..
|
|
*/
|
|
hdd_init();
|
|
network_init();
|
|
mouse_init();
|
|
cdrom_global_init();
|
|
rdisk_global_init();
|
|
mo_global_init();
|
|
|
|
/* Initialize the keyboard accelerator list with default values */
|
|
for (int x = 0; x < NUM_ACCELS; x++) {
|
|
strcpy(acc_keys[x].name, def_acc_keys[x].name);
|
|
strcpy(acc_keys[x].desc, def_acc_keys[x].desc);
|
|
strcpy(acc_keys[x].seq, def_acc_keys[x].seq);
|
|
}
|
|
|
|
/* Load the configuration file. */
|
|
config_load();
|
|
|
|
/* Clear the CMOS and/or BIOS flash file, if we were started with
|
|
the relevant parameter(s). */
|
|
if (clear_cmos) {
|
|
delete_nvr_file(0);
|
|
clear_cmos = 0;
|
|
}
|
|
|
|
if (clear_flash) {
|
|
delete_nvr_file(1);
|
|
clear_flash = 0;
|
|
}
|
|
|
|
for (uint8_t i = 0; i < FDD_NUM; i++) {
|
|
if (fn[i] != NULL) {
|
|
if (strlen(fn[i]) <= 511)
|
|
strncpy(floppyfns[i], fn[i], 511);
|
|
free(fn[i]);
|
|
fn[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Load the desired language */
|
|
if (lang_init)
|
|
lang_id = lang_init;
|
|
|
|
gdbstub_init();
|
|
|
|
/* All good! */
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
pc_speed_changed(void)
|
|
{
|
|
if (cpu_s->cpu_type >= CPU_286)
|
|
pit_set_clock(cpu_s->rspeed);
|
|
else
|
|
pit_set_clock((uint32_t) 14318184.0);
|
|
}
|
|
|
|
void
|
|
pc_full_speed(void)
|
|
{
|
|
if (!atfullspeed) {
|
|
pc_log("Set fullspeed - %i %i\n", is386, is486);
|
|
pc_speed_changed();
|
|
}
|
|
atfullspeed = 1;
|
|
}
|
|
|
|
/* Initialize modules, ran once, after pc_init. */
|
|
int
|
|
pc_init_roms(void)
|
|
{
|
|
int c;
|
|
int m;
|
|
char tempc[512];
|
|
|
|
if (dump_missing) {
|
|
dump_missing = 0;
|
|
|
|
c = m = 0;
|
|
while (machine_get_internal_name_ex(c) != NULL) {
|
|
m = machine_available(c);
|
|
if (!m)
|
|
pclog("Missing machine: %s\n", machine_getname_ex(c));
|
|
c++;
|
|
}
|
|
|
|
c = m = 0;
|
|
while (video_get_internal_name(c) != NULL) {
|
|
memset(tempc, 0, sizeof(tempc));
|
|
device_get_name(video_card_getdevice(c), 0, tempc);
|
|
if ((c > 1) && !(tempc[0]))
|
|
break;
|
|
m = video_card_available(c);
|
|
if (!m)
|
|
pclog("Missing video card: %s\n", tempc);
|
|
c++;
|
|
}
|
|
}
|
|
|
|
pc_log("Scanning for ROM images:\n");
|
|
c = m = 0;
|
|
while (machine_get_internal_name_ex(m) != NULL) {
|
|
c += machine_available(m);
|
|
m++;
|
|
}
|
|
if (c == 0) {
|
|
/* No usable ROMs found, aborting. */
|
|
return 0;
|
|
}
|
|
pc_log("A total of %d ROM sets have been loaded.\n", c);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
pc_init_modules(void)
|
|
{
|
|
int c;
|
|
wchar_t temp[512];
|
|
char tempc[512];
|
|
|
|
/* Load the ROMs for the selected machine. */
|
|
if (!machine_available(machine)) {
|
|
swprintf(temp, sizeof_w(temp), plat_get_string(STRING_HW_NOT_AVAILABLE_MACHINE), machine_getname());
|
|
c = 0;
|
|
machine = -1;
|
|
while (machine_get_internal_name_ex(c) != NULL) {
|
|
if (machine_available(c)) {
|
|
ui_msgbox_header(MBX_INFO, plat_get_string(STRING_HW_NOT_AVAILABLE_TITLE), temp);
|
|
machine = c;
|
|
config_save();
|
|
break;
|
|
}
|
|
c++;
|
|
}
|
|
if (machine == -1) {
|
|
fatal("No available machines\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
/* Make sure we have a usable video card. */
|
|
if (!video_card_available(gfxcard[0])) {
|
|
memset(tempc, 0, sizeof(tempc));
|
|
device_get_name(video_card_getdevice(gfxcard[0]), 0, tempc);
|
|
swprintf(temp, sizeof_w(temp), plat_get_string(STRING_HW_NOT_AVAILABLE_VIDEO), tempc);
|
|
c = 0;
|
|
while (video_get_internal_name(c) != NULL) {
|
|
gfxcard[0] = -1;
|
|
if (video_card_available(c)) {
|
|
ui_msgbox_header(MBX_INFO, plat_get_string(STRING_HW_NOT_AVAILABLE_TITLE), temp);
|
|
gfxcard[0] = c;
|
|
config_save();
|
|
break;
|
|
}
|
|
c++;
|
|
}
|
|
if (gfxcard[0] == -1) {
|
|
fatal("No available video cards\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
// TODO
|
|
for (uint8_t i = 1; i < GFXCARD_MAX; i ++) {
|
|
if (!video_card_available(gfxcard[i])) {
|
|
char tempc[512] = { 0 };
|
|
device_get_name(video_card_getdevice(gfxcard[i]), 0, tempc);
|
|
swprintf(temp, sizeof_w(temp), plat_get_string(STRING_HW_NOT_AVAILABLE_VIDEO2), tempc);
|
|
ui_msgbox_header(MBX_INFO, plat_get_string(STRING_HW_NOT_AVAILABLE_TITLE), temp);
|
|
gfxcard[i] = 0;
|
|
}
|
|
}
|
|
|
|
atfullspeed = 0;
|
|
|
|
random_init();
|
|
|
|
mem_init();
|
|
|
|
#ifdef USE_DYNAREC
|
|
# if defined(__APPLE__) && defined(__aarch64__)
|
|
if (__builtin_available(macOS 11.0, *)) {
|
|
pthread_jit_write_protect_np(0);
|
|
}
|
|
# endif
|
|
codegen_init();
|
|
# if defined(__APPLE__) && defined(__aarch64__)
|
|
if (__builtin_available(macOS 11.0, *)) {
|
|
pthread_jit_write_protect_np(1);
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
keyboard_init();
|
|
joystick_init();
|
|
|
|
video_init();
|
|
|
|
fdd_init();
|
|
|
|
sound_init();
|
|
|
|
hdc_init();
|
|
|
|
video_reset_close();
|
|
|
|
machine_status_init();
|
|
|
|
serial_set_next_inst(0);
|
|
|
|
lpt_set_3bc_used(0);
|
|
lpt_set_next_inst(0);
|
|
|
|
for (c = 0; c <= 0x7ff; c++) {
|
|
int64_t exp = c - 1023; /* 1023 = BIAS64 */
|
|
exp_pow_table[c] = pow(2.0, (double) exp);
|
|
}
|
|
|
|
if (do_nothing) {
|
|
do_nothing = 0;
|
|
exit(-1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
pc_send_ca(uint16_t sc)
|
|
{
|
|
if (keyboard_mode >= 0x81) {
|
|
/* Use R-Alt because PS/55 DOS and OS/2 assign L-Alt Kanji */
|
|
keyboard_input(1, 0x1D); /* Ctrl key pressed */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(1, 0x138); /* R-Alt key pressed */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(1, sc);
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
usleep(50000);
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(0, sc);
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(0, 0x138); /* R-Alt key released */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(0, 0x1D); /* Ctrl key released */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
} else {
|
|
keyboard_input(1, 0x1D); /* Ctrl key pressed */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(1, 0x38); /* Alt key pressed */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(1, sc);
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
usleep(50000);
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(0, sc);
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(0, 0x38); /* Alt key released */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
keyboard_input(0, 0x1D); /* Ctrl key released */
|
|
if (keyboard_get_in_reset())
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Send the machine a Control-Alt-DEL sequence. */
|
|
void
|
|
pc_send_cad(void)
|
|
{
|
|
pc_send_ca(0x153);
|
|
}
|
|
|
|
/* Send the machine a Control-Alt-ESC sequence. */
|
|
void
|
|
pc_send_cae(void)
|
|
{
|
|
pc_send_ca(1);
|
|
}
|
|
|
|
/*
|
|
Currently available API:
|
|
|
|
extern void resetx86(void);
|
|
extern void softresetx86(void);
|
|
extern void hardresetx86(void);
|
|
|
|
extern void prefetch_queue_set_pos(int pos);
|
|
extern void prefetch_queue_set_ip(uint16_t ip);
|
|
extern void prefetch_queue_set_prefetching(int p);
|
|
extern int prefetch_queue_get_pos(void);
|
|
extern uint16_t prefetch_queue_get_ip(void);
|
|
extern int prefetch_queue_get_prefetching(void);
|
|
extern int prefetch_queue_get_size(void);
|
|
*/
|
|
static void
|
|
pc_test_mode_entry_point(void)
|
|
{
|
|
pclog("Test mode entry point\n=====================\n");
|
|
}
|
|
|
|
void
|
|
pc_reset_hard_close(void)
|
|
{
|
|
ui_sb_set_ready(0);
|
|
|
|
/* Close all the memory mappings. */
|
|
mem_close();
|
|
|
|
suppress_overscan = 0;
|
|
|
|
/* Turn off timer processing to avoid potential segmentation faults. */
|
|
timer_close();
|
|
|
|
lpt_devices_close();
|
|
|
|
nvr_save();
|
|
nvr_close();
|
|
|
|
mouse_close();
|
|
|
|
device_close_all();
|
|
|
|
scsi_device_close_all();
|
|
|
|
midi_out_close();
|
|
|
|
midi_in_close();
|
|
|
|
cdrom_close();
|
|
|
|
rdisk_close();
|
|
|
|
mo_close();
|
|
|
|
scsi_disk_close();
|
|
|
|
closeal();
|
|
|
|
video_reset_close();
|
|
|
|
cpu_close();
|
|
|
|
serial_set_next_inst(0);
|
|
|
|
lpt_set_3bc_used(0);
|
|
lpt_set_next_inst(0);
|
|
}
|
|
|
|
/*
|
|
* This is basically the spot where we start up the actual machine,
|
|
* by issuing a 'hard reset' to the entire configuration. Order is
|
|
* somewhat important here. Functions here should be named _reset
|
|
* really, as that is what they do.
|
|
*/
|
|
void
|
|
pc_reset_hard_init(void)
|
|
{
|
|
/*
|
|
* First, we reset the modules that are not part of
|
|
* the actual machine, but which support some of the
|
|
* modules that are.
|
|
*/
|
|
|
|
keyboard_init();
|
|
|
|
/* Reset the IDE and SCSI presences */
|
|
other_ide_present = other_scsi_present = 0;
|
|
|
|
/* Mark ACPI as unavailable */
|
|
acpi_enabled = 0;
|
|
|
|
/* Reset the general machine support modules. */
|
|
io_init();
|
|
|
|
/* Turn on and (re)initialize timer processing. */
|
|
timer_init();
|
|
|
|
device_init();
|
|
|
|
sound_reset();
|
|
|
|
scsi_reset();
|
|
scsi_device_init();
|
|
|
|
ide_hard_reset();
|
|
|
|
/* Initialize the actual machine and its basic modules. */
|
|
machine_init();
|
|
|
|
/* Reset some basic devices. */
|
|
speaker_init();
|
|
shadowbios = 0;
|
|
|
|
/*
|
|
* Once the machine has been initialized, all that remains
|
|
* should be resetting all devices set up for it, to their
|
|
* current configurations !
|
|
*
|
|
* For now, we will call their reset functions here, but
|
|
* that will be a call to device_reset_all() later !
|
|
*/
|
|
|
|
/* Reset and reconfigure the Sound Card layer. */
|
|
sound_card_reset();
|
|
|
|
/* Initialize parallel devices. */
|
|
/* note: PLIP LPT side has to be initialized before the network side */
|
|
lpt_standalone_init();
|
|
lpt_devices_init();
|
|
|
|
/* Reset and reconfigure the serial ports. */
|
|
/* note: SLIP COM side has to be initialized before the network side */
|
|
serial_standalone_init();
|
|
serial_passthrough_init();
|
|
|
|
/* Reset and reconfigure the Network Card layer. */
|
|
network_reset();
|
|
|
|
/*
|
|
* Reset the mouse, this will attach it to any port needed.
|
|
*/
|
|
mouse_reset();
|
|
|
|
/* Reset the Hard Disk Controller module. */
|
|
hdc_reset();
|
|
|
|
fdc_card_init();
|
|
|
|
fdd_reset();
|
|
|
|
/* Reset and reconfigure the SCSI layer. */
|
|
scsi_card_init();
|
|
|
|
scsi_disk_hard_reset();
|
|
|
|
cdrom_hard_reset();
|
|
|
|
/* Reset the CD-ROM Controller module. */
|
|
cdrom_interface_reset();
|
|
|
|
mo_hard_reset();
|
|
|
|
rdisk_hard_reset();
|
|
|
|
|
|
/* Reset any ISA ROM cards. */
|
|
isarom_reset();
|
|
|
|
/* Reset any ISA RTC cards. */
|
|
isartc_reset();
|
|
|
|
/* Initialize the Voodoo cards here inorder to minimize
|
|
the chances of the SCSI controller ending up on the bridge. */
|
|
video_voodoo_init();
|
|
|
|
/* installs first game port if no device provides one, must be late */
|
|
if (joystick_type[0])
|
|
gameport_update_joystick_type(0);
|
|
|
|
ui_sb_update_panes();
|
|
|
|
if (config_changed) {
|
|
config_save();
|
|
|
|
config_changed = 0;
|
|
} else
|
|
ui_sb_set_ready(1);
|
|
|
|
/* Needs the status bar... */
|
|
if (bugger_enabled)
|
|
device_add(&bugger_device);
|
|
if (postcard_enabled)
|
|
device_add(&postcard_device);
|
|
if (unittester_enabled)
|
|
device_add(&unittester_device);
|
|
|
|
if (novell_keycard_enabled)
|
|
device_add(&novell_keycard_device);
|
|
|
|
if (IS_ARCH(machine, MACHINE_BUS_PCI)) {
|
|
pci_register_cards();
|
|
device_reset_all(DEVICE_PCI);
|
|
}
|
|
|
|
/* Mark IDE shadow drives (slaves with a present master) as such in case
|
|
the IDE controllers present are not some form of PCI. */
|
|
ide_drives_set_shadow();
|
|
|
|
/* Reset the CPU module. */
|
|
resetx86();
|
|
dma_reset();
|
|
pci_pic_reset();
|
|
cpu_cache_int_enabled = cpu_cache_ext_enabled = 0;
|
|
|
|
atfullspeed = 0;
|
|
pc_full_speed();
|
|
|
|
cycles = 0;
|
|
#ifdef FPU_CYCLES
|
|
fpu_cycles = 0;
|
|
#endif
|
|
#ifdef USE_DYNAREC
|
|
cycles_main = 0;
|
|
#endif
|
|
|
|
update_mouse_msg();
|
|
|
|
if (test_mode)
|
|
pc_test_mode_entry_point();
|
|
|
|
ui_hard_reset_completed();
|
|
}
|
|
|
|
void
|
|
update_mouse_msg(void)
|
|
{
|
|
wchar_t wcpufamily[2048];
|
|
wchar_t wcpu[2048];
|
|
wchar_t wmachine[2048];
|
|
wchar_t *wcp;
|
|
|
|
mbstowcs(wmachine, machine_getname(), strlen(machine_getname()) + 1);
|
|
|
|
if (!cpu_override)
|
|
mbstowcs(wcpufamily, cpu_f->name, strlen(cpu_f->name) + 1);
|
|
else
|
|
swprintf(wcpufamily, sizeof_w(wcpufamily), L"[U] %hs", cpu_f->name);
|
|
|
|
wcp = wcschr(wcpufamily, L'(');
|
|
if (wcp) /* remove parentheses */
|
|
*(wcp - 1) = L'\0';
|
|
mbstowcs(wcpu, cpu_s->name, strlen(cpu_s->name) + 1);
|
|
#ifdef _WIN32
|
|
swprintf(mouse_msg[0], sizeof_w(mouse_msg[0]), L"%%i.%%i%%%% - %ls",
|
|
plat_get_string(STRING_MOUSE_CAPTURE));
|
|
swprintf(mouse_msg[1], sizeof_w(mouse_msg[1]), L"%%i.%%i%%%% - %ls",
|
|
(mouse_get_buttons() > 2) ? plat_get_string(STRING_MOUSE_RELEASE) : plat_get_string(STRING_MOUSE_RELEASE_MMB));
|
|
wcsncpy(mouse_msg[2], L"%i.%i%%", sizeof_w(mouse_msg[2]));
|
|
#else
|
|
swprintf(mouse_msg[0], sizeof_w(mouse_msg[0]), L"%ls v%ls - %%i.%%i%%%% - %ls - %ls/%ls - %ls",
|
|
EMU_NAME_W, EMU_VERSION_FULL_W, wmachine, wcpufamily, wcpu,
|
|
plat_get_string(STRING_MOUSE_CAPTURE));
|
|
swprintf(mouse_msg[1], sizeof_w(mouse_msg[1]), L"%ls v%ls - %%i.%%i%%%% - %ls - %ls/%ls - %ls",
|
|
EMU_NAME_W, EMU_VERSION_FULL_W, wmachine, wcpufamily, wcpu,
|
|
(mouse_get_buttons() > 2) ? plat_get_string(STRING_MOUSE_RELEASE) : plat_get_string(STRING_MOUSE_RELEASE_MMB));
|
|
swprintf(mouse_msg[2], sizeof_w(mouse_msg[2]), L"%ls v%ls - %%i.%%i%%%% - %ls - %ls/%ls",
|
|
EMU_NAME_W, EMU_VERSION_FULL_W, wmachine, wcpufamily, wcpu);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
pc_reset_hard(void)
|
|
{
|
|
hard_reset_pending = 1;
|
|
}
|
|
|
|
void
|
|
pc_close(UNUSED(thread_t *ptr))
|
|
{
|
|
/* Wait a while so things can shut down. */
|
|
plat_delay_ms(200);
|
|
|
|
/* Claim the video blitter. */
|
|
startblit();
|
|
|
|
/* Terminate the UI thread. */
|
|
is_quit = 1;
|
|
|
|
nvr_save();
|
|
|
|
config_save();
|
|
|
|
plat_mouse_capture(0);
|
|
|
|
/* Close all the memory mappings. */
|
|
mem_close();
|
|
|
|
/* Turn off timer processing to avoid potential segmentation faults. */
|
|
timer_close();
|
|
|
|
lpt_devices_close();
|
|
|
|
for (uint8_t i = 0; i < FDD_NUM; i++)
|
|
fdd_close(i);
|
|
|
|
#ifdef ENABLE_808X_LOG
|
|
if (dump_on_exit)
|
|
dumpregs(0);
|
|
#endif
|
|
|
|
video_close();
|
|
|
|
device_close_all();
|
|
|
|
scsi_device_close_all();
|
|
|
|
midi_out_close();
|
|
|
|
midi_in_close();
|
|
|
|
network_close();
|
|
|
|
sound_cd_thread_end();
|
|
|
|
cdrom_close();
|
|
|
|
rdisk_close();
|
|
|
|
mo_close();
|
|
|
|
scsi_disk_close();
|
|
|
|
gdbstub_close();
|
|
|
|
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
|
|
mem_free();
|
|
#endif
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
static void
|
|
_ui_window_title(void *s)
|
|
{
|
|
ui_window_title((wchar_t *) s);
|
|
free(s);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
ack_pause(void)
|
|
{
|
|
if (atomic_load(&do_pause_ack)) {
|
|
atomic_store(&do_pause_ack, 0);
|
|
atomic_store(&pause_ack, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
pc_run(void)
|
|
{
|
|
int mouse_msg_idx;
|
|
wchar_t temp[200];
|
|
|
|
/* Trigger a hard reset if one is pending. */
|
|
if (hard_reset_pending) {
|
|
hard_reset_pending = 0;
|
|
pc_reset_hard_close();
|
|
pc_reset_hard_init();
|
|
}
|
|
|
|
/* Update the guest-CPU independent timer for devices with independent clock speed */
|
|
rivatimer_update_all();
|
|
|
|
/* Run a block of code. */
|
|
startblit();
|
|
cpu_exec((int32_t) cpu_s->rspeed / (force_10ms ? 100 : 1000));
|
|
ack_pause();
|
|
#ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */
|
|
if (gdbstub_step == GDBSTUB_EXEC) {
|
|
#endif
|
|
if (!mouse_timed)
|
|
mouse_process();
|
|
#ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */
|
|
}
|
|
#endif
|
|
joystick_process(0); // Gameport 0
|
|
endblit();
|
|
|
|
/* Done with this frame, update statistics. */
|
|
framecount++;
|
|
if (++framecountx >= (force_10ms ? 100 : 1000)) {
|
|
framecountx = 0;
|
|
frames = 0;
|
|
}
|
|
|
|
if (title_update) {
|
|
mouse_msg_idx = ((mouse_type == MOUSE_TYPE_NONE) || (mouse_input_mode >= 1)) ? 2 : !!mouse_capture;
|
|
swprintf(temp, sizeof_w(temp), mouse_msg[mouse_msg_idx], fps / (force_10ms ? 1 : 10), force_10ms ? 0 : (fps % 10));
|
|
#ifdef __APPLE__
|
|
/* Needed due to modifying the UI on the non-main thread is a big no-no. */
|
|
dispatch_async_f(dispatch_get_main_queue(), wcsdup((const wchar_t *) temp), _ui_window_title);
|
|
#else
|
|
ui_window_title(temp);
|
|
#endif
|
|
title_update = 0;
|
|
}
|
|
}
|
|
|
|
/* Handler for the 1-second timer to refresh the window title. */
|
|
void
|
|
pc_onesec(void)
|
|
{
|
|
fps = framecount;
|
|
framecount = 0;
|
|
|
|
title_update = 1;
|
|
}
|
|
|
|
void
|
|
set_screen_size_monitor(int x, int y, int monitor_index)
|
|
{
|
|
int temp_overscan_x = monitors[monitor_index].mon_overscan_x;
|
|
int temp_overscan_y = monitors[monitor_index].mon_overscan_y;
|
|
int is_svga = (video_get_type_monitor(monitor_index) == VIDEO_FLAG_TYPE_SPECIAL) ||
|
|
(video_get_type_monitor(monitor_index) == VIDEO_FLAG_TYPE_8514);
|
|
double dx;
|
|
double dy;
|
|
double dtx;
|
|
double dty;
|
|
|
|
/* Make sure we keep usable values. */
|
|
#if 0
|
|
pc_log("SetScreenSize(%d, %d) resize=%d\n", x, y, vid_resize);
|
|
#endif
|
|
if (x < 320)
|
|
x = 320;
|
|
if (y < 200)
|
|
y = 200;
|
|
if (x > 2048)
|
|
x = 2048;
|
|
if (y > 2048)
|
|
y = 2048;
|
|
|
|
/* Save the new values as "real" (unscaled) resolution. */
|
|
monitors[monitor_index].mon_unscaled_size_x = x;
|
|
monitors[monitor_index].mon_efscrnsz_y = y;
|
|
|
|
if (suppress_overscan)
|
|
temp_overscan_x = temp_overscan_y = 0;
|
|
|
|
if (force_43) {
|
|
dx = (double) x;
|
|
dtx = (double) temp_overscan_x;
|
|
|
|
dy = (double) y;
|
|
dty = (double) temp_overscan_y;
|
|
|
|
/* Account for possible overscan. */
|
|
if (!is_svga && (temp_overscan_y == 16)) {
|
|
/* CGA */
|
|
dy = (((dx - dtx) / 4.0) * 3.0) + dty;
|
|
} else if (!is_svga && (temp_overscan_y < 16)) {
|
|
/* MDA/Hercules */
|
|
dy = (dx / 4.0) * 3.0;
|
|
} else {
|
|
if (enable_overscan) {
|
|
/* EGA/(S)VGA with overscan */
|
|
dy = (((dx - dtx) / 4.0) * 3.0) + dty;
|
|
} else {
|
|
/* EGA/(S)VGA without overscan */
|
|
dy = (dx / 4.0) * 3.0;
|
|
}
|
|
}
|
|
monitors[monitor_index].mon_unscaled_size_y = (int) dy;
|
|
} else
|
|
monitors[monitor_index].mon_unscaled_size_y = monitors[monitor_index].mon_efscrnsz_y;
|
|
|
|
switch (scale) {
|
|
case 0: /* 50% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x >> 1);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y >> 1);
|
|
break;
|
|
|
|
case 1: /* 100% */
|
|
monitors[monitor_index].mon_scrnsz_x = monitors[monitor_index].mon_unscaled_size_x;
|
|
monitors[monitor_index].mon_scrnsz_y = monitors[monitor_index].mon_unscaled_size_y;
|
|
break;
|
|
|
|
case 2: /* 150% */
|
|
monitors[monitor_index].mon_scrnsz_x = ((monitors[monitor_index].mon_unscaled_size_x * 3) >> 1);
|
|
monitors[monitor_index].mon_scrnsz_y = ((monitors[monitor_index].mon_unscaled_size_y * 3) >> 1);
|
|
break;
|
|
|
|
case 3: /* 200% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x << 1);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y << 1);
|
|
break;
|
|
|
|
case 4: /* 300% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x * 3);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y * 3);
|
|
break;
|
|
|
|
case 5: /* 400% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x << 2);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y << 2);
|
|
break;
|
|
|
|
case 6: /* 500% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x * 5);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y * 5);
|
|
break;
|
|
|
|
case 7: /* 600% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x * 6);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y * 6);
|
|
break;
|
|
|
|
case 8: /* 700% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x * 7);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y * 7);
|
|
break;
|
|
|
|
case 9: /* 800% */
|
|
monitors[monitor_index].mon_scrnsz_x = (monitors[monitor_index].mon_unscaled_size_x << 3);
|
|
monitors[monitor_index].mon_scrnsz_y = (monitors[monitor_index].mon_unscaled_size_y << 3);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
plat_resize_request(monitors[monitor_index].mon_scrnsz_x, monitors[monitor_index].mon_scrnsz_y, monitor_index);
|
|
}
|
|
|
|
void
|
|
set_screen_size(int x, int y)
|
|
{
|
|
set_screen_size_monitor(x, y, monitor_index_global);
|
|
}
|
|
|
|
void
|
|
reset_screen_size_monitor(int monitor_index)
|
|
{
|
|
set_screen_size(monitors[monitor_index].mon_unscaled_size_x, monitors[monitor_index].mon_efscrnsz_y);
|
|
}
|
|
|
|
void
|
|
reset_screen_size(void)
|
|
{
|
|
for (uint8_t i = 0; i < MONITORS_NUM; i++)
|
|
set_screen_size(monitors[i].mon_unscaled_size_x, monitors[i].mon_efscrnsz_y);
|
|
}
|
|
|
|
void
|
|
set_screen_size_natural(void)
|
|
{
|
|
for (uint8_t i = 0; i < MONITORS_NUM; i++)
|
|
set_screen_size(monitors[i].mon_unscaled_size_x, monitors[i].mon_unscaled_size_y);
|
|
}
|
|
|
|
int
|
|
get_actual_size_x(void)
|
|
{
|
|
return (unscaled_size_x);
|
|
}
|
|
|
|
int
|
|
get_actual_size_y(void)
|
|
{
|
|
return (efscrnsz_y);
|
|
}
|
|
|
|
void
|
|
do_pause(int p)
|
|
{
|
|
int old_p = dopause;
|
|
|
|
if ((p == 1) && !old_p)
|
|
do_pause_ack = p;
|
|
dopause = !!p;
|
|
if ((p == 1) && !old_p) {
|
|
while (!atomic_load(&pause_ack))
|
|
;
|
|
}
|
|
atomic_store(&pause_ack, 0);
|
|
}
|
|
|
|
// Helper to find an accelerator key and return it's index in acc_keys
|
|
int FindAccelerator(const char *name) {
|
|
for (int x = 0; x < NUM_ACCELS; x++) {
|
|
if(strcmp(acc_keys[x].name, name) == 0)
|
|
return(x);
|
|
}
|
|
|
|
// No key was found
|
|
return -1;
|
|
}
|