diff --git a/src/Makefile.mingw b/src/Makefile.mingw index 55a2c9639..b225252a4 100644 --- a/src/Makefile.mingw +++ b/src/Makefile.mingw @@ -8,7 +8,7 @@ # # Modified Makefile for Win32 (MinGW32) environment. # -# Version: @(#)Makefile.mingw 1.0.51 2017/10/01 +# Version: @(#)Makefile.mingw 1.0.52 2017/10/02 # # Authors: Miran Grca, # Fred N. van Kempen, @@ -224,8 +224,8 @@ endif MAINOBJ := pc.o config.o random.o timer.o io.o dma.o nmi.o pic.o \ pit.o ppi.o pci.o mca.o mcr.o mem.o memregs.o rom.o \ - device.o rtc.o nvr.o nvr_ps2.o intel.o intel_flash.o \ - intel_sio.o + device.o nvr.o nvr_at.o nvr_ps2.o \ + intel.o intel_flash.o intel_sio.o CPUOBJ := cpu.o 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o \ codegen.o \ diff --git a/src/cdrom/cdrom.c b/src/cdrom/cdrom.c index f2aaaf14f..389c2a3ae 100644 --- a/src/cdrom/cdrom.c +++ b/src/cdrom/cdrom.c @@ -9,7 +9,7 @@ * Implementation of the CD-ROM drive with SCSI(-like) * commands, for both ATAPI and SCSI usage. * - * Version: @(#)cdrom.c 1.0.8 2017/10/01 + * Version: @(#)cdrom.c 1.0.9 2017/10/02 * * Author: Miran Grca, * Copyright 2016,2017 Miran Grca. @@ -990,16 +990,16 @@ void cdrom_mode_sense_load(uint8_t id) switch(id) { case 0: - f = _wfopen(nvr_concat(L"cdrom_1_mode_sense.bin"), L"rb"); + f = _wfopen(nvr_path(L"cdrom_1_mode_sense.bin"), L"rb"); break; case 1: - f = _wfopen(nvr_concat(L"cdrom_2_mode_sense.bin"), L"rb"); + f = _wfopen(nvr_path(L"cdrom_2_mode_sense.bin"), L"rb"); break; case 2: - f = _wfopen(nvr_concat(L"cdrom_3_mode_sense.bin"), L"rb"); + f = _wfopen(nvr_path(L"cdrom_3_mode_sense.bin"), L"rb"); break; case 3: - f = _wfopen(nvr_concat(L"cdrom_4_mode_sense.bin"), L"rb"); + f = _wfopen(nvr_path(L"cdrom_4_mode_sense.bin"), L"rb"); break; default: return; @@ -1018,16 +1018,16 @@ void cdrom_mode_sense_save(uint8_t id) switch(id) { case 0: - f = _wfopen(nvr_concat(L"cdrom_1_mode_sense.bin"), L"wb"); + f = _wfopen(nvr_path(L"cdrom_1_mode_sense.bin"), L"wb"); break; case 1: - f = _wfopen(nvr_concat(L"cdrom_2_mode_sense.bin"), L"wb"); + f = _wfopen(nvr_path(L"cdrom_2_mode_sense.bin"), L"wb"); break; case 2: - f = _wfopen(nvr_concat(L"cdrom_3_mode_sense.bin"), L"wb"); + f = _wfopen(nvr_path(L"cdrom_3_mode_sense.bin"), L"wb"); break; case 3: - f = _wfopen(nvr_concat(L"cdrom_4_mode_sense.bin"), L"wb"); + f = _wfopen(nvr_path(L"cdrom_4_mode_sense.bin"), L"wb"); break; default: return; diff --git a/src/config.c b/src/config.c index 2793d9fde..1b7d28f03 100644 --- a/src/config.c +++ b/src/config.c @@ -8,7 +8,7 @@ * * Configuration file handler. * - * Version: @(#)config.c 1.0.10 2017/10/01 + * Version: @(#)config.c 1.0.12 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -30,10 +30,10 @@ #include #include "ibm.h" #include "cpu/cpu.h" +#include "nvr.h" #include "config.h" #include "device.h" #include "lpt.h" -#include "nvr.h" #include "cdrom/cdrom.h" #include "disk/hdd.h" #include "disk/hdc.h" @@ -493,7 +493,6 @@ static void load_machine(void) { char *cat = "Machine"; - wchar_t *wp; char *p; p = config_get_string(cat, "machine", NULL); @@ -537,42 +536,15 @@ load_machine(void) mem_size = 1048576; } - memset(nvr_path, 0x00, sizeof(nvr_path)); - wp = config_get_wstring(cat, "nvr_path", L""); - if (wp != NULL) { - if (wcslen(wp) && (wcslen(wp) <= 992)) - { -#if 1 - /* - * NOTE: - * Temporary hack to remove the absolute - * path currently saved in most config - * files. We should remove this before - * finalizing this release! --FvK - */ - if (! wcsnicmp(wp, exe_path, wcslen(exe_path))) { - /* - * Yep, its absolute and prefixed - * with the EXE path. Just strip - * that off for now... - */ - wcscpy(nvr_path, &wp[wcslen(exe_path)]); - } else -#endif - wcscpy(nvr_path, wp); - } - else - { - wcscpy(nvr_path, L"nvr\\"); - } - } - else wcscpy(nvr_path, L"nvr\\"); - cpu_use_dynarec = !!config_get_int(cat, "cpu_use_dynarec", 0); enable_external_fpu = !!config_get_int(cat, "cpu_enable_fpu", 0); enable_sync = !!config_get_int(cat, "enable_sync", 1); + + /* Remove this after a while.. */ + if (config_get_string(cat, "nvr_path", NULL) != NULL) + config_delete_var(cat, "nvr_path"); } @@ -1433,7 +1405,7 @@ config_load(wchar_t *fn) if (fn == NULL) fn = config_file_default; - pclog("Loading config file '%S'..\n", fn); + pclog("Loading config file '%ws'..\n", fn); i = config_read(fn); if (i == 0) @@ -1667,8 +1639,6 @@ save_machine(void) config_set_int(cat, "mem_size", mem_size); } - config_set_wstring(cat, "nvr_path", nvr_path); - config_set_int(cat, "cpu_use_dynarec", cpu_use_dynarec); if (enable_external_fpu == 0) diff --git a/src/cpu/x86seg.c b/src/cpu/x86seg.c index df1829ed3..5f036245f 100644 --- a/src/cpu/x86seg.c +++ b/src/cpu/x86seg.c @@ -8,7 +8,7 @@ * * x86 CPU segment emulation. * - * Version: @(#)x86seg.c 1.0.1 2017/09/24 + * Version: @(#)x86seg.c 1.0.2 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -29,6 +29,7 @@ #include "386_common.h" #include "cpu.h" + /*Controls whether the accessed bit in a descriptor is set when CS is loaded.*/ #define CS_ACCESSED @@ -63,7 +64,7 @@ void x86abort(const char *format, ...) vprintf(format, ap); va_end(ap); fflush(stdout); - savenvr(); + nvr_save(); dumpregs(1); fflush(stdout); exit(-1); diff --git a/src/floppy/floppy.c b/src/floppy/floppy.c index f833afbf9..89c4e6268 100644 --- a/src/floppy/floppy.c +++ b/src/floppy/floppy.c @@ -9,7 +9,7 @@ * Generic floppy disk interface that communicates with the * other handlers. * - * Version: @(#)floppy.c 1.0.4 2017/09/24 + * Version: @(#)floppy.c 1.0.5 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -153,7 +153,7 @@ void floppy_load(int drive, wchar_t *fn) } c++; } - pclog_w(L"Couldn't load %s %s\n",fn,p); + pclog("Couldn't load %ws %s\n",fn,p); drive_empty[drive] = 1; fdd_set_head(real_drive(drive), 0); memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); diff --git a/src/floppy/floppy_86f.c b/src/floppy/floppy_86f.c index 930891f83..e97aed779 100644 --- a/src/floppy/floppy_86f.c +++ b/src/floppy/floppy_86f.c @@ -10,7 +10,7 @@ * data in the form of FM/MFM-encoded transitions) which also * forms the core of the emulator's floppy disk emulation. * - * Version: @(#)floppy_86f.c 1.0.4 2017/09/24 + * Version: @(#)floppy_86f.c 1.0.5 2017/10/02 * * Author: Miran Grca, * Copyright 2016,2017 Miran Grca. @@ -3471,7 +3471,7 @@ void d86f_load(int drive, wchar_t *fn) if (d86f[drive].is_compressed) { - memcpy(temp_file_name, drive ? nvr_concat(L"TEMP$$$1.$$$") : nvr_concat(L"TEMP$$$0.$$$"), 256); + memcpy(temp_file_name, drive ? nvr_path(L"TEMP$$$1.$$$") : nvr_path(L"TEMP$$$0.$$$"), 256); memcpy(d86f[drive].original_file_name, fn, (wcslen(fn) << 1) + 2); fclose(d86f[drive].f); @@ -3666,7 +3666,7 @@ void d86f_close(int drive) { wchar_t temp_file_name[2048]; - memcpy(temp_file_name, drive ? nvr_concat(L"TEMP$$$1.$$$") : nvr_concat(L"TEMP$$$0.$$$"), 26); + memcpy(temp_file_name, drive ? nvr_path(L"TEMP$$$1.$$$") : nvr_path(L"TEMP$$$0.$$$"), 26); if (d86f[drive].f) { diff --git a/src/ibm.h b/src/ibm.h index 28fe029dc..d1c7f53ad 100644 --- a/src/ibm.h +++ b/src/ibm.h @@ -10,7 +10,7 @@ * * !!!NOTE!!! The goal is to GET RID of this file. Do NOT add stuff !! * - * Version: @(#)ibm.h 1.0.7 2017/10/01 + * Version: @(#)ibm.h 1.0.7 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -480,7 +480,6 @@ extern int gated,speakval,speakon; extern wchar_t exe_path[1024]; extern wchar_t cfg_path[1024]; -extern wchar_t nvr_path[1024]; /*Keyboard*/ @@ -586,8 +585,8 @@ extern int scale; /* Function prototypes. */ extern void pclog(const char *format, ...); -extern void pclog_w(const wchar_t *format, ...); extern void fatal(const char *format, ...); +extern wchar_t *pc_concat(wchar_t *str); extern void pc_init_modules(void); extern void pc_init(int argc, wchar_t *argv[]); extern void pc_close(void); diff --git a/src/intel_flash.c b/src/intel_flash.c index fab8c57c9..eadaf4047 100644 --- a/src/intel_flash.c +++ b/src/intel_flash.c @@ -8,7 +8,7 @@ * * Implementation of the Intel 2 Mbit 8-bit flash devices. * - * Version: @(#)intel_flash.c 1.0.3 2017/09/24 + * Version: @(#)intel_flash.c 1.0.4 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -193,7 +193,7 @@ void *intel_flash_init(uint8_t type) wcscpy(flash_path, flash_name); - pclog_w(L"Flash path: %s\n", flash_name); + pclog("Flash path: %ws\n", flash_name); flash->flash_id = (type & FLASH_IS_BXB) ? 0x95 : 0x94; flash->invert_high_pin = (type & FLASH_INVERT); @@ -255,7 +255,7 @@ void *intel_flash_init(uint8_t type) flash->command = CMD_READ_ARRAY; flash->status = 0; - f = nvrfopen(flash_path, L"rb"); + f = nvr_fopen(flash_path, L"rb"); if (f) { fread(&(flash->array[flash->block_start[BLOCK_MAIN]]), flash->block_len[BLOCK_MAIN], 1, f); @@ -298,7 +298,7 @@ void intel_flash_close(void *p) FILE *f; flash_t *flash = (flash_t *)p; - f = nvrfopen(flash_path, L"wb"); + f = nvr_fopen(flash_path, L"wb"); fwrite(&(flash->array[flash->block_start[BLOCK_MAIN]]), flash->block_len[BLOCK_MAIN], 1, f); fwrite(&(flash->array[flash->block_start[BLOCK_DATA1]]), flash->block_len[BLOCK_DATA1], 1, f); fwrite(&(flash->array[flash->block_start[BLOCK_DATA2]]), flash->block_len[BLOCK_DATA2], 1, f); diff --git a/src/machine/machine_amstrad.c b/src/machine/machine_amstrad.c index 9f18f9516..40f3ca971 100644 --- a/src/machine/machine_amstrad.c +++ b/src/machine/machine_amstrad.c @@ -144,7 +144,10 @@ void machine_amstrad_init(void) mem_add_bios(); amstrad_init(); keyboard_amstrad_init(); - nvr_init(); + + /* FIXME: make sure this is correct? */ + nvr_at_init(1); + nmi_init(); fdc_set_dskchg_activelow(); if (joystick_type != 7) diff --git a/src/machine/machine_at.c b/src/machine/machine_at.c index 4c5196fed..488b76762 100644 --- a/src/machine/machine_at.c +++ b/src/machine/machine_at.c @@ -30,7 +30,7 @@ void machine_at_init(void) pit_set_out_func(&pit, 1, pit_refresh_timer_at); dma16_init(); keyboard_at_init(); - nvr_init(); + nvr_at_init(8); pic2_init(); if (joystick_type != 7) device_add(&gameport_device); diff --git a/src/machine/machine_olivetti_m24.c b/src/machine/machine_olivetti_m24.c index b13e8495d..56802deff 100644 --- a/src/machine/machine_olivetti_m24.c +++ b/src/machine/machine_olivetti_m24.c @@ -42,7 +42,10 @@ void machine_olim24_init(void) machine_common_init(); mem_add_bios(); keyboard_olim24_init(); - nvr_init(); + + /* FIXME: make sure this is correct?? */ + nvr_at_init(8); + olivetti_m24_init(); nmi_init(); if (joystick_type != 7) device_add(&gameport_device); diff --git a/src/machine/machine_ps1.c b/src/machine/machine_ps1.c index 2b2089815..8c1212971 100644 --- a/src/machine/machine_ps1.c +++ b/src/machine/machine_ps1.c @@ -348,7 +348,7 @@ static void machine_ps1_common_init(void) ide_init(); } keyboard_at_init(); - nvr_init(); + nvr_at_init(8); pic2_init(); if (romset != ROM_IBMPS1_2133) { diff --git a/src/machine/machine_ps2_isa.c b/src/machine/machine_ps2_isa.c index 01ea160bd..0001201c0 100644 --- a/src/machine/machine_ps2_isa.c +++ b/src/machine/machine_ps2_isa.c @@ -163,7 +163,7 @@ void machine_ps2_m30_286_init(void) pit_set_out_func(&pit, 1, pit_refresh_timer_at); dma16_init(); keyboard_at_init(); - nvr_init(); + nvr_at_init(8); pic2_init(); ps2board_init(); fdc_set_dskchg_activelow(); diff --git a/src/machine/machine_ps2_mca.c b/src/machine/machine_ps2_mca.c index d546efa5d..1d85d84c3 100644 --- a/src/machine/machine_ps2_mca.c +++ b/src/machine/machine_ps2_mca.c @@ -813,7 +813,7 @@ static void machine_ps2_common_init(void) keyboard_at_init(); keyboard_at_init_ps2(); mouse_ps2_init(); - nvr_init(); + nvr_at_init(8); pic2_init(); pit_ps2_init(); diff --git a/src/nvr.c b/src/nvr.c index 85734995c..7495a69ec 100644 --- a/src/nvr.c +++ b/src/nvr.c @@ -6,376 +6,774 @@ * * This file is part of the 86Box distribution. * - * CMOS NVRAM emulation. + * Implement a more-or-less defacto-standard RTC/NVRAM. * - * Version: @(#)nvr.c 1.0.4 2017/09/24 + * When IBM released the PC/AT machine, it came standard with a + * battery-backed RTC chip to keep the time of day, something + * that was optional on standard PC's with a myriad variants + * being put on the market, often on cheap multi-I/O cards. + * + * The PC/AT had an on-board DS12885-series chip ("the black + * block") which was an RTC/clock chip with onboard oscillator + * and a backup battery (hence the big size.) The chip also had + * a smal amount of RAM bytes available to the user, which was + * used by IBM's ROM BIOS to store machine configuration data. + * + * Since then, pretty much any PC has an implementation of that + * device, which became known as the "nvr" or "cmos". + * + * NOTES Info extracted from the data sheets: + * + * * The century register at location 32h is a BCD register + * designed to automatically load the BCD value 20 as the + * year register changes from 99 to 00. The MSB of this + * register is not affected when the load of 20 occurs, + * and remains at the value written by the user. + * + * * Rate Selector (RS3:RS0) + * These four rate-selection bits select one of the 13 + * taps on the 15-stage divider or disable the divider + * output. The tap selected can be used to generate an + * output square wave (SQW pin) and/or a periodic interrupt. + * + * The user can do one of the following: + * - enable the interrupt with the PIE bit; + * - enable the SQW output pin with the SQWE bit; + * - enable both at the same time and the same rate; or + * - enable neither. + * + * Table 3 lists the periodic interrupt rates and the square + * wave frequencies that can be chosen with the RS bits. + * These four read/write bits are not affected by !RESET. + * + * * Oscillator (DV2:DV0) + * These three bits are used to turn the oscillator on or + * off and to reset the countdown chain. A pattern of 010 + * is the only combination of bits that turn the oscillator + * on and allow the RTC to keep time. A pattern of 11x + * enables the oscillator but holds the countdown chain in + * reset. The next update occurs at 500ms after a pattern + * of 010 is written to DV0, DV1, and DV2. + * + * * Update-In-Progress (UIP) + * This bit is a status flag that can be monitored. When the + * UIP bit is a 1, the update transfer occurs soon. When + * UIP is a 0, the update transfer does not occur for at + * least 244us. The time, calendar, and alarm information + * in RAM is fully available for access when the UIP bit + * is 0. The UIP bit is read-only and is not affected by + * !RESET. Writing the SET bit in Register B to a 1 + * inhibits any update transfer and clears the UIP status bit. + * + * * Daylight Saving Enable (DSE) + * This bit is a read/write bit that enables two daylight + * saving adjustments when DSE is set to 1. On the first + * Sunday in April (or the last Sunday in April in the + * MC146818A), the time increments from 1:59:59 AM to + * 3:00:00 AM. On the last Sunday in October when the time + * first reaches 1:59:59 AM, it changes to 1:00:00 AM. + * + * When DSE is enabled, the internal logic test for the + * first/last Sunday condition at midnight. If the DSE bit + * is not set when the test occurs, the daylight saving + * function does not operate correctly. These adjustments + * do not occur when the DSE bit is 0. This bit is not + * affected by internal functions or !RESET. + * + * * 24/12 + * The 24/12 control bit establishes the format of the hours + * byte. A 1 indicates the 24-hour mode and a 0 indicates + * the 12-hour mode. This bit is read/write and is not + * affected by internal functions or !RESET. + * + * * Data Mode (DM) + * This bit indicates whether time and calendar information + * is in binary or BCD format. The DM bit is set by the + * program to the appropriate format and can be read as + * required. This bit is not modified by internal functions + * or !RESET. A 1 in DM signifies binary data, while a 0 in + * DM specifies BCD data. + * + * * Square-Wave Enable (SQWE) + * When this bit is set to 1, a square-wave signal at the + * frequency set by the rate-selection bits RS3-RS0 is driven + * out on the SQW pin. When the SQWE bit is set to 0, the + * SQW pin is held low. SQWE is a read/write bit and is + * cleared by !RESET. SQWE is low if disabled, and is high + * impedance when VCC is below VPF. SQWE is cleared to 0 on + * !RESET. + * + * * Update-Ended Interrupt Enable (UIE) + * This bit is a read/write bit that enables the update-end + * flag (UF) bit in Register C to assert !IRQ. The !RESET + * pin going low or the SET bit going high clears the UIE bit. + * The internal functions of the device do not affect the UIE + * bit, but is cleared to 0 on !RESET. + * + * * Alarm Interrupt Enable (AIE) + * This bit is a read/write bit that, when set to 1, permits + * the alarm flag (AF) bit in Register C to assert !IRQ. An + * alarm interrupt occurs for each second that the three time + * bytes equal the three alarm bytes, including a don't-care + * alarm code of binary 11XXXXXX. The AF bit does not + * initiate the !IRQ signal when the AIE bit is set to 0. + * The internal functions of the device do not affect the AIE + * bit, but is cleared to 0 on !RESET. + * + * * Periodic Interrupt Enable (PIE) + * The PIE bit is a read/write bit that allows the periodic + * interrupt flag (PF) bit in Register C to drive the !IRQ pin + * low. When the PIE bit is set to 1, periodic interrupts are + * generated by driving the !IRQ pin low at a rate specified + * by the RS3-RS0 bits of Register A. A 0 in the PIE bit + * blocks the !IRQ output from being driven by a periodic + * interrupt, but the PF bit is still set at the periodic + * rate. PIE is not modified b any internal device functions, + * but is cleared to 0 on !RESET. + * + * * SET + * When the SET bit is 0, the update transfer functions + * normally by advancing the counts once per second. When + * the SET bit is written to 1, any update transfer is + * inhibited, and the program can initialize the time and + * calendar bytes without an update occurring in the midst of + * initializing. Read cycles can be executed in a similar + * manner. SET is a read/write bit and is not affected by + * !RESET or internal functions of the device. + * + * * Update-Ended Interrupt Flag (UF) + * This bit is set after each update cycle. When the UIE + * bit is set to 1, the 1 in UF causes the IRQF bit to be + * a 1, which asserts the !IRQ pin. This bit can be + * cleared by reading Register C or with a !RESET. + * + * * Alarm Interrupt Flag (AF) + * A 1 in the AF bit indicates that the current time has + * matched the alarm time. If the AIE bit is also 1, the + * !IRQ pin goes low and a 1 appears in the IRQF bit. This + * bit can be cleared by reading Register C or with a + * !RESET. + * + * * Periodic Interrupt Flag (PF) + * This bit is read-only and is set to 1 when an edge is + * detected on the selected tap of the divider chain. The + * RS3 through RS0 bits establish the periodic rate. PF is + * set to 1 independent of the state of the PIE bit. When + * both PF and PIE are 1s, the !IRQ signal is active and + * sets the IRQF bit. This bit can be cleared by reading + * Register C or with a !RESET. + * + * * Interrupt Request Flag (IRQF) + * The interrupt request flag (IRQF) is set to a 1 when one + * or more of the following are true: + * - PF == PIE == 1 + * - AF == AIE == 1 + * - UF == UIE == 1 + * Any time the IRQF bit is a 1, the !IRQ pin is driven low. + * All flag bits are cleared after Register C is read by the + * program or when the !RESET pin is low. + * + * * Valid RAM and Time (VRT) + * This bit indicates the condition of the battery connected + * to the VBAT pin. This bit is not writeable and should + * always be 1 when read. If a 0 is ever present, an + * exhausted internal lithium energy source is indicated and + * both the contents of the RTC data and RAM data are + * questionable. This bit is unaffected by !RESET. + * + * This file implements an internal RTC clock, plus a generic + * version of the RTC/NVRAM chip, including the later update + * (DS12887A) which implemented a "century" register to be + * compatible with Y2K. + * + * Version: @(#)nvr.c 1.0.5 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, * Mahod, + * Fred N. van Kempen, + * * Copyright 2008-2017 Sarah Walker. * Copyright 2016,2017 Miran Grca. - * Copyright 2016,2017 Mahod. + * Copyright 2017 Fred N. van Kempen. */ #include #include #include #include +#include #include #include "ibm.h" -#include "config.h" #include "cpu/cpu.h" -#include "device.h" -#include "io.h" -#include "machine/machine.h" -#include "machine/machine_europc.h" -#include "mem.h" -#include "nmi.h" -#include "nvr.h" #include "pic.h" -#include "rom.h" #include "timer.h" -#include "rtc.h" +#include "device.h" +#include "machine/machine.h" #include "win/win.h" +#include "nvr.h" -int oldmachine; -int nvrmask=63; -char nvrram[128]; -int nvraddr; -int nvr_dosave = 0; - -static int nvr_onesec_time = 0, nvr_onesec_cnt = 0; -static int rtctime; +#define NVR_FOLDER_PATH L"NVR" -void getnvrtime(void) +int enable_sync; /* configuration variable: enable time sync */ +int nvr_dosave; /* NVR is dirty, needs saved */ + + +static nvr_t *saved_nvr = NULL; +static int8_t days_in_month[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; +static struct { + int sec; + int min; + int hour; + int mday; + int mon; + int year; +} intclk; /* the internal clock */ + + +/* Determine whether or not the year is leap. */ +static int +is_leap(int year) { - time_get(nvrram); + if (year % 400 == 0) return(1); + if (year % 100 == 0) return(0); + if (year % 4 == 0) return(1); + + return(0); } -void nvr_recalc(void) + +/* Determine the days in the current month. */ +static int +get_days(int month, int year) { - int c; - int newrtctime; - c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1); - newrtctime=(int)(RTCCONST * c * (1 << TIMER_SHIFT)); - if (rtctime>newrtctime) rtctime=newrtctime; + if (month != 2) + return(days_in_month[month - 1]); + + return(is_leap(year) ? 29 : 28); } -void nvr_rtc(void *p) + +/* One more second has passed, update the internal clock. */ +static void +rtc_tick(void) { - int c; - if (!(nvrram[RTC_REGA] & RTC_RS)) - { - rtctime=0x7fffffff; - return; - } - c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1); - rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); - nvrram[RTC_REGC] |= RTC_PF; - if (nvrram[RTC_REGB] & RTC_PIE) - { - nvrram[RTC_REGC] |= RTC_IRQF; - if (AMSTRAD) picint(2); - else picint(0x100); - } + /* Ping the internal clock. */ + if (++intclk.sec == 60) { + intclk.sec = 0; + intclk.min++; + } + if (intclk.min == 60) { + intclk.min = 0; + intclk.hour++; + } + if (intclk.hour == 24) { + intclk.hour = 0; + intclk.mday++; + } + if (intclk.mday == (get_days(intclk.mon, intclk.year) + 1)) { + intclk.mday = 1; + intclk.mon++; + } + if (intclk.mon == 13) { + intclk.mon = 1; + intclk.year++; + } } -int nvr_update_status = 0; -#define ALARM_DONTCARE 0xc0 - -int nvr_check_alarm(int nvraddr) +/* Store the broken-down local time into the NVR. */ +static void +rtc_getnvr(uint8_t *nvr, struct tm *tm) { - return (nvrram[nvraddr + 1] == nvrram[nvraddr] || (nvrram[nvraddr + 1] & ALARM_DONTCARE) == ALARM_DONTCARE); -} + if (nvr[RTC_REGB] & REGB_DM) { + /* NVR is in Binary data mode. */ + nvr[RTC_SECONDS] = tm->tm_sec; + nvr[RTC_MINUTES] = tm->tm_min; + nvr[RTC_DOW] = tm->tm_wday+1; + nvr[RTC_DOM] = tm->tm_mday; + nvr[RTC_MONTH] = tm->tm_mon+1; + nvr[RTC_YEAR] = tm->tm_year%100; -int nvr_update_end_count = 0; - -void nvr_update_end(void *p) -{ - if (!(nvrram[RTC_REGB] & RTC_SET)) - { - getnvrtime(); - /* Clear update status. */ - nvr_update_status = 0; - - if (nvr_check_alarm(RTC_SECONDS) && nvr_check_alarm(RTC_MINUTES) && nvr_check_alarm(RTC_HOURS)) - { - nvrram[RTC_REGC] |= RTC_AF; - if (nvrram[RTC_REGB] & RTC_AIE) - { - nvrram[RTC_REGC] |= RTC_IRQF; - if (AMSTRAD) picint(2); - else picint(0x100); - } - } - - /* The flag and interrupt should be issued on update ended, not started. */ - nvrram[RTC_REGC] |= RTC_UF; - if (nvrram[RTC_REGB] & RTC_UIE) - { - nvrram[RTC_REGC] |= RTC_IRQF; - if (AMSTRAD) picint(2); - else picint(0x100); - } - } - - nvr_update_end_count = 0; -} - -void nvr_onesec(void *p) -{ - nvr_onesec_cnt++; - if (nvr_onesec_cnt >= 100) - { - if (!(nvrram[RTC_REGB] & RTC_SET)) - { - nvr_update_status = RTC_UIP; - rtc_tick(); - - nvr_update_end_count = (int)((244.0 + 1984.0) * TIMER_USEC); - } - nvr_onesec_cnt = 0; - } - nvr_onesec_time += (int)(10000 * TIMER_USEC); -} - -void writenvr(uint16_t addr, uint8_t val, void *priv) -{ - int c, old; - if (addr&1) - { - if (nvraddr==RTC_REGC || nvraddr==RTC_REGD) - return; /* Registers C and D are read-only. There's no reason to continue. */ - if (nvraddr > RTC_REGD && nvrram[nvraddr] != val) - nvr_dosave = 1; - - old = nvrram[nvraddr]; - nvrram[nvraddr]=val; - - if (nvraddr == RTC_REGA) - { - if (val & RTC_RS) - { - c = 1 << ((val & RTC_RS) - 1); - rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); - } - else - rtctime = 0x7fffffff; - } - else - { - if (nvraddr == RTC_REGB) - { - if (((old ^ val) & RTC_SET) && (val & RTC_SET)) - { - nvrram[RTC_REGA] &= ~RTC_UIP; /* This has to be done according to the datasheet. */ - nvrram[RTC_REGB] &= ~RTC_UIE; /* This also has to happen per the specification. */ - } - } - - if ((nvraddr < RTC_REGA) || (nvraddr == RTC_CENTURY)) - { - if ((nvraddr != 1) && (nvraddr != 3) && (nvraddr != 5)) - { - if ((old != val) && !enable_sync) - { - time_update(nvrram, nvraddr); - nvr_dosave = 1; - } - } - } - } - } - else - { - nvraddr=val&nvrmask; - nmi_mask = ~val & 0x80; - } -} - -uint8_t readnvr(uint16_t addr, void *priv) -{ - uint8_t temp; - if (addr&1) - { - if (nvraddr == RTC_REGA) - return ((nvrram[RTC_REGA] & 0x7F) | nvr_update_status); - if (nvraddr == RTC_REGD) - nvrram[RTC_REGD] |= RTC_VRT; - if (nvraddr == RTC_REGC) - { - if (AMSTRAD) picintc(2); - else picintc(0x100); - temp = nvrram[RTC_REGC]; - nvrram[RTC_REGC] = 0; - return temp; - } - return nvrram[nvraddr]; - } - return nvraddr; -} - -void loadnvr(void) -{ - FILE *f = NULL; - int c; - nvrmask=63; - oldmachine = machine; - - wchar_t *machine_name; - wchar_t *nvr_name; - - machine_name = (wchar_t *) malloc((strlen(machine_get_internal_name_ex(machine)) << 1) + 2); - mbstowcs(machine_name, machine_get_internal_name_ex(machine), strlen(machine_get_internal_name_ex(machine)) + 1); - nvr_name = (wchar_t *) malloc((wcslen(machine_name) << 1) + 2 + 8); - _swprintf(nvr_name, L"%s.nvr", machine_name); - - pclog_w(L"Opening NVR file: %s...\n", nvr_name); - - if (machine_get_nvrmask(machine) != 0) - { - f = nvrfopen(nvr_name, L"rb"); - nvrmask = machine_get_nvrmask(machine); + if (nvr[RTC_REGB] & REGB_2412) { + /* NVR is in 24h mode. */ + nvr[RTC_HOURS] = tm->tm_hour; + } else { + /* NVR is in 12h mode. */ + nvr[RTC_HOURS] = (tm->tm_hour % 12) ? (tm->tm_hour % 12) : 12; + if (tm->tm_hour > 11) + nvr[RTC_HOURS] |= RTC_AMPM; } + } else { + /* NVR is in BCD data mode. */ + nvr[RTC_SECONDS] = RTC_BCD(tm->tm_sec); + nvr[RTC_MINUTES] = RTC_BCD(tm->tm_min); + nvr[RTC_DOW] = RTC_BCD(tm->tm_wday+1); + nvr[RTC_DOM] = RTC_BCD(tm->tm_mday); + nvr[RTC_MONTH] = RTC_BCD(tm->tm_mon+1); + nvr[RTC_YEAR] = RTC_BCD(tm->tm_year%100); - if (!f || (machine_get_nvrmask(machine) == 0)) - { - if (f) - { - fclose(f); + if (nvr[RTC_REGB] & REGB_2412) { + /* NVR is in 24h mode. */ + nvr[RTC_HOURS] = RTC_BCD(tm->tm_hour); + } else { + /* NVR is in 12h mode. */ + nvr[RTC_HOURS] = (tm->tm_hour % 12) + ? RTC_BCD(tm->tm_hour % 12) + : RTC_BCD(12); + if (tm->tm_hour > 11) + nvr[RTC_HOURS] |= RTC_AMPM; + } + } +} + + +/* Load local time from the NVR. */ +static void +rtc_setnvr(uint8_t *nvr) +{ + int temp; + + if (nvr[RTC_REGB] & REGB_DM) { + intclk.sec = nvr[RTC_SECONDS]; + intclk.min = nvr[RTC_MINUTES]; + temp = nvr[RTC_HOURS]; + intclk.mday = nvr[RTC_DOM]; + intclk.mon = nvr[RTC_MONTH]; + intclk.year = nvr[RTC_YEAR]; + intclk.year += 1900; + } else { + intclk.sec = RTC_DCB(nvr[RTC_SECONDS]); + intclk.min = RTC_DCB(nvr[RTC_MINUTES]); + temp = RTC_DCB(nvr[RTC_HOURS]); + intclk.mday = RTC_DCB(nvr[RTC_DOM]); + intclk.mon = RTC_DCB(nvr[RTC_MONTH]); + intclk.year = RTC_DCB(nvr[RTC_YEAR]); + intclk.year += (RTC_DCB(nvr[RTC_CENTURY]) * 100); + } + + /* Adjust for 12/24 hour mode. */ + if (nvr[RTC_REGB] & REGB_2412) + intclk.hour = temp; + else + intclk.hour = ((temp & ~RTC_AMPM) % 12) + ((temp & RTC_AMPM) ? 12 : 0); +} + + +static void +rtc_sync(uint8_t *nvr) +{ + struct tm *tm; + time_t now; + + /* Get the current time of day, and convert to local time. */ + (void)time(&now); + tm = localtime(&now); + + /* Set the internal clock. */ + intclk.sec = tm->tm_sec; + intclk.min = tm->tm_min; + intclk.hour = tm->tm_hour; + intclk.mday = tm->tm_mday; + intclk.mon = tm->tm_mon+1; + intclk.year = tm->tm_year+1900; + + /* Set the NVR registers. */ + rtc_getnvr(nvr, tm); +} + + +/* This is the RTC one-second timer. */ +static void +onesec_timer(void *priv) +{ + nvr_t *nvr = (nvr_t *)priv; + + if (++nvr->onesec_cnt >= 100) { + if (! (nvr->regs[RTC_REGB] & REGB_SET)) { + nvr->upd_stat = REGA_UIP; + + /* Update the system RTC. */ + rtc_tick(); + + /* Re-calculate the timer. */ + nvr_recalc(); + + nvr->upd_ecount = (int)((244.0 + 1984.0) * TIMER_USEC); + } + nvr->onesec_cnt = 0; + } + + nvr->onesec_time += (int)(10000 * TIMER_USEC); +} + + +/* Check if the current time matches a set alarm time. */ +static int +check_alarm(nvr_t *nvr, int addr) +{ +#define ALARM_DONTCARE 0xc0 + return((nvr->regs[addr+1] == nvr->regs[addr]) || + ((nvr->regs[addr+1] & ALARM_DONTCARE) == ALARM_DONTCARE)); +} + + +/* This is the general update timer. */ +static void +update_timer(void *priv) +{ + nvr_t *nvr = (nvr_t *)priv; + struct tm tm; + int dom, mon, yr, cent, sum, wd; + + if (! (nvr->regs[RTC_REGB] & REGB_SET)) { + /* Get the current time from the internal clock. */ + tm.tm_sec = intclk.sec; + tm.tm_min = intclk.min; + tm.tm_hour = intclk.hour; + dom = intclk.mday; + mon = intclk.mon; + yr = intclk.year % 100; + cent = ((intclk.year - yr) / 100) % 4; + sum = dom+mon+yr+cent; + wd = ((sum + 6) % 7); + tm.tm_wday = wd; + tm.tm_mday = intclk.mday; + tm.tm_mon = intclk.mon-1; + tm.tm_year = intclk.year-1900; + rtc_getnvr(nvr->regs, &tm); + + /* Clear update status. */ + nvr->upd_stat = 0; + + if (check_alarm(nvr, RTC_SECONDS) && + check_alarm(nvr, RTC_MINUTES) && + check_alarm(nvr, RTC_HOURS)) { + nvr->regs[RTC_REGC] |= REGC_AF; + if (nvr->regs[RTC_REGB] & REGB_AIE) { + nvr->regs[RTC_REGC] |= REGC_IRQF; + + /* Generate an interrupt. */ + picint(1<irq); } - memset(nvrram,0xFF,128); - if (!enable_sync) - { - nvrram[RTC_SECONDS] = nvrram[RTC_MINUTES] = nvrram[RTC_HOURS] = 0; - nvrram[RTC_DOM] = nvrram[RTC_MONTH] = 1; - nvrram[RTC_YEAR] = (char) BCD(80); - nvrram[RTC_CENTURY] = BCD(19); - nvrram[RTC_REGB] = RTC_2412; - } - - free(nvr_name); - free(machine_name); - return; - } - fread(nvrram,128,1,f); - if (enable_sync) - time_internal_sync(nvrram); - else - time_internal_set_nvrram(nvrram); /* Update the internal clock state based on the NVR registers. */ - fclose(f); - nvrram[RTC_REGA] = 6; - nvrram[RTC_REGB] = RTC_2412; - c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1); - rtctime += (int)(RTCCONST * c * (1 << TIMER_SHIFT)); - - free(nvr_name); - free(machine_name); -} - -void savenvr(void) -{ - FILE *f = NULL; - - wchar_t *machine_name; - wchar_t *nvr_name; - - if (romset == ROM_EUROPC) - { - europc_save_nvr(); - return; } - machine_name = (wchar_t *) malloc((strlen(machine_get_internal_name_ex(oldmachine)) << 1) + 2); - mbstowcs(machine_name, machine_get_internal_name_ex(oldmachine), strlen(machine_get_internal_name_ex(oldmachine)) + 1); - nvr_name = (wchar_t *) malloc((wcslen(machine_name) << 1) + 2 + 8); - _swprintf(nvr_name, L"%s.nvr", machine_name); + /* + * The flag and interrupt should be issued + * on update ended, not started. + */ + nvr->regs[RTC_REGC] |= REGC_UF; + if (nvr->regs[RTC_REGB] & REGB_UIE) { + nvr->regs[RTC_REGC] |= REGC_IRQF; - pclog_w(L"Saving NVR file: %s...\n", nvr_name); - - if (machine_get_nvrmask(oldmachine) != 0) - { - f = nvrfopen(nvr_name, L"wb"); - } - - if (!f || (machine_get_nvrmask(oldmachine) == 0)) - { - if (f) - { - fclose(f); - } - - free(nvr_name); - free(machine_name); - return; - } - - fwrite(nvrram,128,1,f); - fclose(f); - - free(nvr_name); - free(machine_name); -} - - -void -nvr_init(void) -{ - io_sethandler(0x0070, 0x0002, readnvr, NULL, NULL, writenvr, NULL, NULL, NULL); - timer_add(nvr_rtc, &rtctime, TIMER_ALWAYS_ENABLED, NULL); - timer_add(nvr_onesec, &nvr_onesec_time, TIMER_ALWAYS_ENABLED, NULL); - timer_add(nvr_update_end, &nvr_update_end_count, &nvr_update_end_count, NULL); - -} - - -wchar_t * -nvr_concat(wchar_t *str) -{ - static wchar_t temp[1024]; - wchar_t last; - - /* Get the full prefix in place. */ - memset(temp, 0x00, sizeof(temp)); - append_filename_w(temp, cfg_path, nvr_path, sizeof(temp)-1); - - /* Make sure we have a trailing backslash. */ - if (temp[wcslen(temp) - 1] != L'/') { - if (temp[wcslen(temp) - 1] != L'\\') { - temp[wcslen(temp)] = L'\\'; - temp[wcslen(temp) + 1] = L'\0'; + /* Generate an interrupt. */ + picint(1<irq); } } -#ifndef __unix - /* Save the backslash and zap it. */ - last = temp[wcslen(temp) - 1]; - temp[wcslen(temp) - 1] = 0; + nvr->upd_ecount = 0; +} + +static void +ticker_timer(void *priv) +{ + nvr_t *nvr = (nvr_t *)priv; + int c; + + if (! (nvr->regs[RTC_REGA] & REGA_RS)) { + nvr->rtctime = 0x7fffffff; + return; + } + + /* Update our ticker interval. */ + c = 1 << ((nvr->regs[RTC_REGA] & REGA_RS) - 1); + nvr->rtctime += (int)(RTCCONST*c*(1<regs[RTC_REGC] |= REGC_PF; + if (nvr->regs[RTC_REGB] & REGB_PIE) { + nvr->regs[RTC_REGC] |= REGC_IRQF; + + /* Generate an interrupt. */ + picint(1<irq); + } +} + + +/* Set one of the chip's registers. */ +static void +nvr_write(nvr_t *nvr, uint16_t reg, uint8_t val) +{ + int c, old; + + old = nvr->regs[reg]; + switch(reg) { + case RTC_REGA: + nvr->regs[reg] = val; + if (val & REGA_RS) { + c = 1 << ((val & REGA_RS) - 1); + nvr->rtctime += (int)(RTCCONST*c*(1<rtctime = 0x7fffffff; + } + break; + + case RTC_REGB: + nvr->regs[reg] = val; + if (((old^val) & REGB_SET) && (val®B_SET)) { + /* This has to be done according to the datasheet. */ + nvr->regs[RTC_REGA] &= ~REGA_UIP; + + /* This also has to happen per the specification. */ + nvr->regs[RTC_REGB] &= ~REGB_UIE; + } + break; + + case RTC_REGC: /* R/O */ + case RTC_REGD: /* R/O */ + break; + + default: /* non-RTC registers are just NVRAM */ + if (nvr->regs[reg] != val) { + nvr->regs[reg] = val; + + nvr_dosave = 1; + } + break; + } + + if ((reg < RTC_REGA) || (reg == RTC_CENTURY)) { + if ((reg != 1) && (reg != 3) && (reg != 5)) { + if ((old != val) && !enable_sync) { + /* Update internal clock. */ + rtc_setnvr(nvr->regs); + + nvr_dosave = 1; + } + } + } +} + + +/* Get one of the chip's registers. */ +static uint8_t +nvr_read(nvr_t *nvr, uint16_t reg) +{ + uint8_t ret = 0xff; + + switch(reg) { + case RTC_REGA: + ret = (nvr->regs[RTC_REGA] & 0x7f) | nvr->upd_stat; + break; + + case RTC_REGC: + picintc(1<irq); + ret = nvr->regs[RTC_REGC]; + nvr->regs[RTC_REGC] = 0x00; + break; + + case RTC_REGD: + nvr->regs[RTC_REGD] |= REGD_VRT; + ret = nvr->regs[RTC_REGD]; + break; + + default: + ret = nvr->regs[reg]; + break; + } + + return(ret); +} + + +/* Initialize the virtual RTC/NVRAM chip. */ +void +nvr_init(nvr_t *nvr) +{ + char temp[32]; + int c; + + /* Clear some of it. */ + nvr->upd_stat = 0; + nvr->upd_ecount = 0; + nvr->onesec_time = 0; + nvr->onesec_cnt = 0; + memset(&intclk, 0x00, sizeof(intclk)); + + /* Pre-initialize the NVR file's name here. */ + sprintf(temp, "%s.nvr", machine_get_internal_name_ex(machine)); + c = strlen(temp)+1; + nvr->fname = (wchar_t *)malloc(c*sizeof(wchar_t)); + mbstowcs(nvr->fname, temp, c); + + /* Set up our local handlers. */ + nvr->get = nvr_read; + nvr->set = nvr_write; + + /* Set up our timers. */ + timer_add(ticker_timer, &nvr->rtctime, TIMER_ALWAYS_ENABLED, nvr); + + timer_add(onesec_timer, &nvr->onesec_time, TIMER_ALWAYS_ENABLED, nvr); + + timer_add(update_timer, &nvr->upd_ecount, &nvr->upd_ecount, nvr); + + /* It does not need saving yet. */ + nvr_dosave = 0; + + /* Save the NVR data pointer. */ + saved_nvr = nvr; +} + + +/* Re-calculate the timer values. */ +void +nvr_recalc(void) +{ + int c, nt; + + /* Make sure we have been initialized. */ + if (saved_nvr == NULL) return; + + c = 1 << ((saved_nvr->regs[RTC_REGA] & REGA_RS) - 1); + nt = (int)(RTCCONST * c * (1<rtctime > nt) + saved_nvr->rtctime = nt; +} + + +/* Load an NVR from file. */ +void +nvr_load(void) +{ + FILE *f; + int c; + + /* Make sure we have been initialized. */ + if (saved_nvr == NULL) return; + + /* Clear out any old data. */ + memset(saved_nvr->regs, 0xff, sizeof(saved_nvr->regs)); + memset(saved_nvr->regs, 0x00, RTC_REGS); + +#if 0 + europc_load_nvr(); + + if (saved_nvr->load) + (*saved_nvr->load(); + return; + } +#endif + + f = NULL; + if (saved_nvr->mask != 0) { + pclog("Opening NVR file: %ws...\n", saved_nvr->fname); + f = _wfopen(nvr_path(saved_nvr->fname), L"rb"); + } + + if (f==NULL || saved_nvr->mask==0) { + if (f != NULL) + fclose(f); + + /* No file loaded, or no file available. */ + if (! enable_sync) { + /* No time-sync enabled, so just set the defaults. */ + saved_nvr->regs[RTC_SECONDS] = 0; + saved_nvr->regs[RTC_MINUTES] = 0; + saved_nvr->regs[RTC_HOURS] = 0; + saved_nvr->regs[RTC_DOM] = 1; + saved_nvr->regs[RTC_MONTH] = 1; + saved_nvr->regs[RTC_YEAR] = RTC_BCD(80); + saved_nvr->regs[RTC_REGB] = REGB_2412; + saved_nvr->regs[RTC_CENTURY] = RTC_BCD(19); + } + + return; + } + + /* Read NVR contents from file. */ + fread(saved_nvr->regs, sizeof(saved_nvr->regs), 1, f); + (void)fclose(f); + + /* Update the internal clock state based on the NVR registers. */ + if (enable_sync) + rtc_sync(saved_nvr->regs); + else + rtc_setnvr(saved_nvr->regs); + + saved_nvr->regs[RTC_REGA] = 0x06; + saved_nvr->regs[RTC_REGB] = REGB_2412; + + c = 1 << ((saved_nvr->regs[RTC_REGA] & REGA_RS) - 1); + saved_nvr->rtctime += (int)(RTCCONST * c * (1<save) + (*saved_nvr->save)(); + return; + } +#endif + + f = NULL; + if (saved_nvr->mask != 0) { + pclog("Saving NVR file: %ws...\n", saved_nvr->fname); + f = _wfopen(nvr_path(saved_nvr->fname), L"wb"); + } + if (f==NULL || saved_nvr->mask==0) { + if (f != NULL) + fclose(f); + return; + } + + (void)fwrite(saved_nvr->regs, sizeof(saved_nvr->regs), 1, f); + (void)fclose(f); + + /* Device is clean again. */ + nvr_dosave = 0; +} + + +/* Get an absolute path to the NVR folder. */ +wchar_t * +nvr_path(wchar_t *str) +{ + static wchar_t temp[1024]; + + /* Get the full prefix in place. */ + memset(temp, 0x00, sizeof(temp)); + wcscpy(temp, cfg_path); + wcscat(temp, NVR_FOLDER_PATH); + +#ifndef __unix /* Create the directory if needed. */ if (! DirectoryExists(temp)) CreateDirectory(temp, NULL); - - /* Restore the backslash. */ - temp[wcslen(temp)] = last; #endif /* Now append the actual filename. */ + wcscat(temp, L"\\"); wcscat(temp, str); return(temp); } +/* Open or create a file in the NVR area. */ FILE * -nvrfopen(wchar_t *fn, wchar_t *mode) +nvr_fopen(wchar_t *str, wchar_t *mode) { - wchar_t *p; - - p = nvr_concat(fn); - - return(_wfopen(p, mode)); + return(_wfopen(nvr_path(str), mode)); } diff --git a/src/nvr.h b/src/nvr.h index a0c6ef4a6..bfc4bd0ff 100644 --- a/src/nvr.h +++ b/src/nvr.h @@ -6,32 +6,107 @@ * * This file is part of the 86Box distribution. * - * CMOS NVRAM emulation. + * Definitions for a defacto-standard RTC/NVRAM device. * - * Version: @(#)nvr.h 1.0.2 2017/06/19 + * Version: @(#)nvr.h 1.0.3 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, * Mahod, + * Fred N. van Kempen, + * * Copyright 2008-2017 Sarah Walker. * Copyright 2016-2017 Miran Grca. * Copyright 2016-2017 Mahod. + * Copyright 2017 Fred N. van Kempen. */ #ifndef EMU_NVR_H # define EMU_NVR_H -extern int enable_sync; -extern int nvr_dosave; +/* Conversion from BCD to Binary and vice versa. */ +#define RTC_BCD(x) (((x) % 10) | (((x) / 10) << 4)) +#define RTC_DCB(x) ((((x) & 0xf0) >> 4) * 10 + ((x) & 0x0f)) + +/* RTC registers and bit definitions. */ +#define RTC_SECONDS 0 +#define RTC_ALSECONDS 1 +#define RTC_MINUTES 2 +#define RTC_ALMINUTES 3 +#define RTC_HOURS 4 +# define RTC_AMPM 0x80 /* PM flag if 12h format in use */ +#define RTC_ALHOURS 5 +#define RTC_DOW 6 +#define RTC_DOM 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 +#define RTC_REGA 10 +# define REGA_UIP 0x80 +# define REGA_DV2 0x40 +# define REGA_DV1 0x20 +# define REGA_DV0 0x10 +# define REGA_DV 0x70 +# define REGA_RS3 0x08 +# define REGA_RS2 0x04 +# define REGA_RS1 0x02 +# define REGA_RS0 0x01 +# define REGA_RS 0x0f +#define RTC_REGB 11 +# define REGB_SET 0x80 +# define REGB_PIE 0x40 +# define REGB_AIE 0x20 +# define REGB_UIE 0x10 +# define REGB_SQWE 0x08 +# define REGB_DM 0x04 +# define REGB_2412 0x02 +# define REGB_DSE 0x01 +#define RTC_REGC 12 +# define REGC_IRQF 0x80 +# define REGC_PF 0x40 +# define REGC_AF 0x20 +# define REGC_UF 0x10 +#define RTC_REGD 13 +# define REGD_VRT 0x80 +#define RTC_CENTURY 0x32 /* century register */ +#define RTC_REGS 14 /* number of registers */ -extern wchar_t *nvr_concat(wchar_t *to_concat); -extern void nvr_init(void); -extern FILE *nvrfopen(wchar_t *fn, wchar_t *mode); -extern void time_get(char *nvrram); + +/* Define a (defacto-standard) RTC/NVRAM chip. */ +typedef struct _nvr_ { + uint8_t regs[RTC_REGS+114]; /* these are the registers */ + + int upd_stat, + upd_ecount, + onesec_time, + onesec_cnt, + rtctime, + oldmachine; + + int mask, + irq, + addr; + + void (*set)(struct _nvr_ *, uint16_t, uint8_t); + uint8_t (*get)(struct _nvr_ *, uint16_t); + + wchar_t *fname; +} nvr_t; + + +extern int enable_sync; +extern int nvr_dosave; + + +extern void nvr_init(nvr_t *); +extern void nvr_load(void); +extern void nvr_save(void); extern void nvr_recalc(void); -extern void loadnvr(void); -extern void savenvr(void); + +extern wchar_t *nvr_path(wchar_t *str); +extern FILE *nvr_fopen(wchar_t *str, wchar_t *mode); + +extern void nvr_at_init(int irq); #endif /*EMU_NVR_H*/ diff --git a/src/nvr_at.c b/src/nvr_at.c new file mode 100644 index 000000000..299d9a730 --- /dev/null +++ b/src/nvr_at.c @@ -0,0 +1,108 @@ +/* + * 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. + * + * IBM PC/AT RTC/NVRAM ("CMOS") emulation. + * + * The original PC/AT series had DS12885 series modules; later + * versions and clones used the 12886 and/or 1288(C)7 series, + * or the MC146818 series, all with an external battery. Many + * of those batteries would create corrosion issues later on + * in mainboard life... + * + * Version: @(#)nvr_at.c 1.0.5 2017/10/02 + * + * Authors: Miran Grca, + * Fred N. van Kempen, + * + * Copyright 2016,2017 Miran Grca. + * Copyright 2017 Fred N. van Kempen. + */ +#include +#include +#include +#include +#include +#include "ibm.h" +#include "cpu/cpu.h" +#include "io.h" +#include "device.h" +#include "machine/machine.h" +#include "nvr.h" + + +static void +nvr_write(uint16_t addr, uint8_t val, void *priv) +{ + nvr_t *nvr = (nvr_t *)priv; + + if (! (addr & 1)) { + nvr->addr = (val & nvr->mask); +#if 0 + nvr->nmi_mask = (~val & 0x80); +#endif + + return; + } + + /* Write the chip's registers. */ + (*nvr->set)(nvr, nvr->addr, val); +} + + +static uint8_t +nvr_read(uint16_t addr, void *priv) +{ + nvr_t *nvr = (nvr_t *)priv; + uint8_t ret; + + if (addr & 1) { + /* Read from the chip's registers. */ + ret = (*nvr->get)(nvr, nvr->addr); + } else { + ret = nvr->addr; + } + + return(ret); +} + + +void +nvr_at_close(void *priv) +{ + nvr_t *nvr = (nvr_t *)priv; + + if (nvr->fname != NULL) + free(nvr->fname); + + free(nvr); +} + + +void +nvr_at_init(int irq) +{ + nvr_t *nvr; + + /* Allocate an NVR for this machine. */ + nvr = (nvr_t *)malloc(sizeof(nvr_t)); + if (nvr == NULL) return; + memset(nvr, 0x00, sizeof(nvr_t)); + + /* This is machine specific. */ + nvr->mask = machines[machine].nvrmask; + nvr->irq = irq; + + /* Set up any local handlers here. */ + + /* Initialize the actual NVR. */ + nvr_init(nvr); + + /* Set up the PC/AT handler for this device. */ + io_sethandler(0x0070, 2, + nvr_read, NULL, NULL, nvr_write, NULL, NULL, nvr); +} diff --git a/src/nvr_ps2.c b/src/nvr_ps2.c index 315c65fe8..817e8c43b 100644 --- a/src/nvr_ps2.c +++ b/src/nvr_ps2.c @@ -65,7 +65,7 @@ static void *ps2_nvr_init() switch (romset) { - case ROM_IBMPS2_M80: f = nvrfopen(L"ibmps2_m80_sec.nvr", L"rb"); break; + case ROM_IBMPS2_M80: f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"rb"); break; } if (f) { @@ -85,7 +85,7 @@ static void ps2_nvr_close(void *p) switch (romset) { - case ROM_IBMPS2_M80: f = nvrfopen(L"ibmps2_m80_sec.nvr", L"wb"); break; + case ROM_IBMPS2_M80: f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"wb"); break; } if (f) { diff --git a/src/pc.c b/src/pc.c index af86deb82..193fe62e8 100644 --- a/src/pc.c +++ b/src/pc.c @@ -70,6 +70,7 @@ #include "win/plat_midi.h" #include "win/plat_mouse.h" #include "win/plat_ui.h" +#include "win/win.h" #include "scsi/scsi.h" #include "serial.h" #include "sound/sound.h" @@ -87,10 +88,6 @@ #include "cpu/x86_ops.h" -wchar_t exe_path[1024]; -wchar_t cfg_path[1024]; -wchar_t nvr_path[1024]; - int window_w, window_h, window_x, window_y, window_remember; int dump_on_exit = 0; int start_in_fullscreen = 0; @@ -109,6 +106,8 @@ int pollmouse_delay = 2; int mousecapture; int suppress_overscan = 0; int cpuspeed2; +wchar_t exe_path[1024]; +wchar_t cfg_path[1024]; extern int mmuflush; @@ -129,20 +128,6 @@ pclog(const char *format, ...) } -/* Log something to the logfile or stdout. */ -void -pclog_w(const wchar_t *format, ...) -{ -#ifndef RELEASE_BUILD - va_list ap; - va_start(ap, format); - vwprintf(format, ap); - va_end(ap); - fflush(stdout); -#endif -} - - /* Log a fatal error, and display a UI message before exiting. */ void fatal(const char *format, ...) @@ -157,7 +142,7 @@ fatal(const char *format, ...) va_end(ap); fflush(stdout); - savenvr(); + nvr_save(); config_save(); @@ -177,6 +162,33 @@ fatal(const char *format, ...) } +/* + * This function returns the absolute pathname to a file (str) + * that is to be found in the user (formerly 'nvr_path' area. + */ +wchar_t * +pc_concat(wchar_t *str) +{ + static wchar_t temp[1024]; + + /* Get the full prefix in place. */ + memset(temp, 0x00, sizeof(temp)); + wcscpy(temp, cfg_path); + +#ifndef __unix + /* Create the directory if needed. */ + if (! DirectoryExists(temp)) + CreateDirectory(temp, NULL); +#endif + + /* Now append the actual filename. */ + wcscat(temp, L"\\"); + wcscat(temp, str); + + return(temp); +} + + static void usage(void) { @@ -201,7 +213,7 @@ usage(void) void pc_init(int argc, wchar_t *argv[]) { - wchar_t *config_file = NULL; + wchar_t *cfg = NULL; wchar_t *p; #ifdef WALTJE struct direct *dp; @@ -213,7 +225,7 @@ pc_init(int argc, wchar_t *argv[]) get_executable_name(exe_path, sizeof(exe_path)-1); p = get_filename_w(exe_path); *p = L'\0'; - pclog("exe_path=%S\n", exe_path); + pclog("exe_path=%ws\n", exe_path); /* * Get the current working directory. @@ -233,7 +245,7 @@ usage: !_wcsicmp(argv[c], L"-C")) { if ((c+1) == argc) break; - config_file = argv[++c]; + cfg = argv[++c]; } else if (!_wcsicmp(argv[c], L"--dump") || !_wcsicmp(argv[c], L"-D")) { dump_on_exit = 1; @@ -245,15 +257,15 @@ usage: #ifdef WALTJE dir = opendirw(exe_path); if (dir != NULL) { - printf("Directory '%S':\n", exe_path); + printf("Directory '%ws':\n", exe_path); for (;;) { dp = readdir(dir); if (dp == NULL) break; - printf(">> '%S'\n", dp->d_name); + printf(">> '%ws'\n", dp->d_name); } closedir(dir); } else { - printf("Could not open '%S'..\n", exe_path); + printf("Could not open '%ws'..\n", exe_path); } #endif @@ -276,9 +288,9 @@ usage: (cfg_path[wcslen(cfg_path)-1] != L'/')) { wcscat(cfg_path, L"\\"); } - pclog("cwd_path=%S\n", cfg_path); + pclog("cfg_path=%ws\n", cfg_path); - if (config_file != NULL) { + if (cfg != NULL) { /* * The user specified a configuration file. * @@ -287,14 +299,14 @@ usage: * Otherwise, assume the pathname given is * relative to whatever the cfg_path is. */ - if ((config_file[1] == L':') || /* drive letter present */ - (config_file[0] == L'\\')) /* backslash, root dir */ + if ((cfg[1] == L':') || /* drive letter present */ + (cfg[0] == L'\\')) /* backslash, root dir */ append_filename_w(config_file_default, - NULL, config_file, 511); + NULL, cfg, 511); else append_filename_w(config_file_default, - cfg_path, config_file, 511); - config_file = NULL; + cfg_path, cfg, 511); + cfg = NULL; } else { append_filename_w(config_file_default, cfg_path, CONFIG_FILE_W, 511); } @@ -307,7 +319,7 @@ usage: */ hdd_init(); - config_load(config_file); + config_load(cfg); } @@ -407,7 +419,7 @@ pc_init_modules(void) floppy_load(3, floppyfns[3]); #endif - loadnvr(); + nvr_load(); sound_init(); @@ -490,7 +502,7 @@ resetpchard_close(void) { suppress_overscan = 0; - savenvr(); + nvr_save(); device_close_all(); mouse_emu_close(); @@ -554,7 +566,7 @@ resetpchard_init(void) mouse_emu_init(); - loadnvr(); + nvr_load(); shadowbios = 0; diff --git a/src/rtc.c b/src/rtc.c deleted file mode 100644 index 98814a3af..000000000 --- a/src/rtc.c +++ /dev/null @@ -1,249 +0,0 @@ -/* Emulation of: - Dallas Semiconductor DS12C887 Real Time Clock - - http://datasheets.maximintegrated.com/en/ds/DS12885-DS12C887A.pdf - - http://dev-docs.atariforge.org/files/MC146818A_RTC_1984.pdf - */ -#include -#include -#include -#include -#include -#include -#include "nvr.h" -#include "rtc.h" - - -int enable_sync; - - -struct -{ - int sec; - int min; - int hour; - int mday; - int mon; - int year; -} internal_clock; - -/* Table for days in each month */ -static int rtc_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -/* Called to determine whether the year is leap or not */ -static int rtc_is_leap(int org_year) -{ - if (org_year % 400 == 0) return 1; - if (org_year % 100 == 0) return 0; - if (org_year % 4 == 0) return 1; - return 0; -} - -/* Called to determine the days in the current month */ -static int rtc_get_days(int org_month, int org_year) -{ - if (org_month != 2) - return rtc_days_in_month[org_month - 1]; - else - return rtc_is_leap(org_year) ? 29 : 28; -} - -/* Called when the internal clock gets updated */ -static void rtc_recalc(void) -{ - if (internal_clock.sec == 60) - { - internal_clock.sec = 0; - internal_clock.min++; - } - if (internal_clock.min == 60) - { - internal_clock.min = 0; - internal_clock.hour++; - } - if (internal_clock.hour == 24) - { - internal_clock.hour = 0; - internal_clock.mday++; - } - if (internal_clock.mday == (rtc_get_days(internal_clock.mon, internal_clock.year) + 1)) - { - internal_clock.mday = 1; - internal_clock.mon++; - } - if (internal_clock.mon == 13) - { - internal_clock.mon = 1; - internal_clock.year++; - } -} - -/* Called when ticking the second */ -void rtc_tick(void) -{ - internal_clock.sec++; - rtc_recalc(); -} - -/* Called when modifying the NVR registers */ -void time_update(char *nvrram, int reg) -{ - int temp; - - switch(reg) - { - case RTC_SECONDS: - internal_clock.sec = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_SECONDS] : DCB(nvrram[RTC_SECONDS]); - break; - case RTC_MINUTES: - internal_clock.min = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MINUTES] : DCB(nvrram[RTC_MINUTES]); - break; - case RTC_HOURS: - temp = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_HOURS] : DCB(nvrram[RTC_HOURS]); - - if (nvrram[RTC_REGB] & RTC_2412) - internal_clock.hour = temp; - else - internal_clock.hour = ((temp & ~RTC_AMPM) % 12) + ((temp & RTC_AMPM) ? 12 : 0); - break; - case RTC_DOM: - internal_clock.mday = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_DOM] : DCB(nvrram[RTC_DOM]); - break; - case RTC_MONTH: - internal_clock.mon = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MONTH] : DCB(nvrram[RTC_MONTH]); - break; - case RTC_YEAR: - internal_clock.year = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_YEAR] : DCB(nvrram[RTC_YEAR]); - internal_clock.year += (nvrram[RTC_REGB] & RTC_DM) ? 1900 : (DCB(nvrram[RTC_CENTURY]) * 100); - break; - case RTC_CENTURY: - if (nvrram[RTC_REGB] & RTC_DM) - return; - internal_clock.year %= 100; - internal_clock.year += (DCB(nvrram[RTC_CENTURY]) * 100); - break; - } -} - -/* Called to obtain the current day of the week based on the internal clock */ -static int time_week_day(void) -{ - int day_of_month = internal_clock.mday; - int month2 = internal_clock.mon; - int year2 = internal_clock.year % 100; - int century = ((internal_clock.year - year2) / 100) % 4; - int sum = day_of_month + month2 + year2 + century; - /* (Sum mod 7) gives 0 for Saturday, we need it for Sunday, so +6 for Saturday to get 6 and Sunday 0 */ - int raw_wd = ((sum + 6) % 7); - return raw_wd; -} - -/* Called to get time into the internal clock */ -static void time_internal_get(struct tm *time_var) -{ - time_var->tm_sec = internal_clock.sec; - time_var->tm_min = internal_clock.min; - time_var->tm_hour = internal_clock.hour; - time_var->tm_wday = time_week_day(); - time_var->tm_mday = internal_clock.mday; - time_var->tm_mon = internal_clock.mon - 1; - time_var->tm_year = internal_clock.year - 1900; -} - -static void time_internal_set(struct tm *time_var) -{ - internal_clock.sec = time_var->tm_sec; - internal_clock.min = time_var->tm_min; - internal_clock.hour = time_var->tm_hour; - internal_clock.mday = time_var->tm_mday; - internal_clock.mon = time_var->tm_mon + 1; - internal_clock.year = time_var->tm_year + 1900; -} - -static void time_set_nvrram(char *nvrram, struct tm *cur_time_tm) -{ - if (nvrram[RTC_REGB] & RTC_DM) - { - nvrram[RTC_SECONDS] = cur_time_tm->tm_sec; - nvrram[RTC_MINUTES] = cur_time_tm->tm_min; - nvrram[RTC_DOW] = cur_time_tm->tm_wday + 1; - nvrram[RTC_DOM] = cur_time_tm->tm_mday; - nvrram[RTC_MONTH] = cur_time_tm->tm_mon + 1; - nvrram[RTC_YEAR] = cur_time_tm->tm_year % 100; - - if (nvrram[RTC_REGB] & RTC_2412) - { - nvrram[RTC_HOURS] = cur_time_tm->tm_hour; - } - else - { - nvrram[RTC_HOURS] = (cur_time_tm->tm_hour % 12) ? (cur_time_tm->tm_hour % 12) : 12; - if (cur_time_tm->tm_hour > 11) - nvrram[RTC_HOURS] |= RTC_AMPM; - } - } - else - { - nvrram[RTC_SECONDS] = BCD(cur_time_tm->tm_sec); - nvrram[RTC_MINUTES] = BCD(cur_time_tm->tm_min); - nvrram[RTC_DOW] = BCD(cur_time_tm->tm_wday + 1); - nvrram[RTC_DOM] = BCD(cur_time_tm->tm_mday); - nvrram[RTC_MONTH] = BCD(cur_time_tm->tm_mon + 1); - nvrram[RTC_YEAR] = BCD(cur_time_tm->tm_year % 100); - - if (nvrram[RTC_REGB] & RTC_2412) - { - nvrram[RTC_HOURS] = BCD(cur_time_tm->tm_hour); - } - else - { - nvrram[RTC_HOURS] = (cur_time_tm->tm_hour % 12) ? BCD(cur_time_tm->tm_hour % 12) : BCD(12); - if (cur_time_tm->tm_hour > 11) - nvrram[RTC_HOURS] |= RTC_AMPM; - } - } -} - -void time_internal_set_nvrram(char *nvrram) -{ - int temp; - - /* Load the entire internal clock state from the NVR. */ - internal_clock.sec = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_SECONDS] : DCB(nvrram[RTC_SECONDS]); - internal_clock.min = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MINUTES] : DCB(nvrram[RTC_MINUTES]); - - temp = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_HOURS] : DCB(nvrram[RTC_HOURS]); - - if (nvrram[RTC_REGB] & RTC_2412) - internal_clock.hour = temp; - else - internal_clock.hour = ((temp & ~RTC_AMPM) % 12) + ((temp & RTC_AMPM) ? 12 : 0); - - internal_clock.mday = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_DOM] : DCB(nvrram[RTC_DOM]); - internal_clock.mon = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_MONTH] : DCB(nvrram[RTC_MONTH]); - internal_clock.year = (nvrram[RTC_REGB] & RTC_DM) ? nvrram[RTC_YEAR] : DCB(nvrram[RTC_YEAR]); - internal_clock.year += (nvrram[RTC_REGB] & RTC_DM) ? 1900 : (DCB(nvrram[RTC_CENTURY]) * 100); -} - -void time_internal_sync(char *nvrram) -{ - struct tm *cur_time_tm; - time_t cur_time; - - time(&cur_time); - cur_time_tm = localtime(&cur_time); - - time_internal_set(cur_time_tm); - - time_set_nvrram(nvrram, cur_time_tm); -} - -void time_get(char *nvrram) -{ - struct tm cur_time_tm; - - time_internal_get(&cur_time_tm); - - time_set_nvrram(nvrram, &cur_time_tm); -} diff --git a/src/rtc.h b/src/rtc.h deleted file mode 100644 index 151be0c7c..000000000 --- a/src/rtc.h +++ /dev/null @@ -1,174 +0,0 @@ -#define BCD(X) (((X) % 10) | (((X) / 10) << 4)) -#define DCB(X) ((((X) & 0xF0) >> 4) * 10 + ((X) & 0x0F)) - -enum RTC_ADDR -{ - RTC_SECONDS, - RTC_ALARMSECONDS, - RTC_MINUTES, - RTC_ALARMMINUTES, - RTC_HOURS, - RTC_ALARMHOURS, - RTC_DOW, - RTC_DOM, - RTC_MONTH, - RTC_YEAR, - RTC_REGA, - RTC_REGB, - RTC_REGC, - RTC_REGD -}; - -/* The century register at location 32h is a BCD register designed to automatically load the BCD value 20 as the year register changes from 99 to 00. - The MSB of this register is not affected when the load of 20 occurs, and remains at the value written by the user. */ -#define RTC_CENTURY 0x32 - -/* When the 12-hour format is selected, the higher-order bit of the hours byte represents PM when it is logic 1. */ -#define RTC_AMPM 0x80 - -/* Register A bitflags */ -enum RTC_RA_BITS -{ -/* Rate Selector (RS0) - - These four rate-selection bits select one of the 13 taps on the 15-stage divider or disable the divider output. - The tap selected can be used to generate an output square wave (SQW pin) and/or a periodic interrupt. - The user can do one of the following: - - Enable the interrupt with the PIE bit; - - Enable the SQW output pin with the SQWE bit; - - Enable both at the same time and the same rate; or - - Enable neither. - - Table 3 lists the periodic interrupt rates and the square wave frequencies that can be chosen with the RS bits. - These four read/write bits are not affected by !RESET. */ - RTC_RS = 0xF, -/* DV0 - - These three bits are used to turn the oscillator on or off and to reset the countdown chain. - A pattern of 010 is the only combination of bits that turn the oscillator on and allow the RTC to keep time. - A pattern of 11x enables the oscillator but holds the countdown chain in reset. - The next update occurs at 500ms after a pattern of 010 is written to DV0, DV1, and DV2. */ - RTC_DV0 = 0x70, -/* Update-In-Progress (UIP) - - This bit is a status flag that can be monitored. When the UIP bit is a 1, the update transfer occurs soon. - When UIP is a 0, the update transfer does not occur for at least 244us. - The time, calendar, and alarm information in RAM is fully available for access when the UIP bit is 0. - The UIP bit is read-only and is not affected by !RESET. - Writing the SET bit in Register B to a 1 inhibits any update transfer and clears the UIP status bit. */ - RTC_UIP = 0x80 -}; - -/* Register B bitflags */ -enum RTC_RB_BITS -{ -/* Daylight Saving Enable (DSE) - - This bit is a read/write bit that enables two daylight saving adjustments when DSE is set to 1. - On the first Sunday in April (or the last Sunday in April in the MC146818A), the time increments from 1:59:59 AM to 3:00:00 AM. - On the last Sunday in October when the time first reaches 1:59:59 AM, it changes to 1:00:00 AM. - When DSE is enabled, the internal logic test for the first/last Sunday condition at midnight. - If the DSE bit is not set when the test occurs, the daylight saving function does not operate correctly. - These adjustments do not occur when the DSE bit is 0. This bit is not affected by internal functions or !RESET. */ - RTC_DSE = 0x1, -/* 24/12 - - The 24/12 control bit establishes the format of the hours byte. A 1 indicates the 24-hour mode and a 0 indicates the 12-hour mode. - This bit is read/write and is not affected by internal functions or !RESET. */ - RTC_2412 = 0x2, -/* Data Mode (DM) - - This bit indicates whether time and calendar information is in binary or BCD format. - The DM bit is set by the program to the appropriate format and can be read as required. - This bit is not modified by internal functions or !RESET. A 1 in DM signifies binary data, while a 0 in DM specifies BCD data. */ - RTC_DM = 0x4, -/* Square-Wave Enable (SQWE) - - When this bit is set to 1, a square-wave signal at the frequency set by the rate-selection bits RS3-RS0 is driven out on the SQW pin. - When the SQWE bit is set to 0, the SQW pin is held low. SQWE is a read/write bit and is cleared by !RESET. - SQWE is low if disabled, and is high impedance when VCC is below VPF. SQWE is cleared to 0 on !RESET. */ - RTC_SQWE = 0x8, -/* Update-Ended Interrupt Enable (UIE) - - This bit is a read/write bit that enables the update-end flag (UF) bit in Register C to assert !IRQ. - The !RESET pin going low or the SET bit going high clears the UIE bit. - The internal functions of the device do not affect the UIE bit, but is cleared to 0 on !RESET. */ - RTC_UIE = 0x10, -/* Alarm Interrupt Enable (AIE) - - This bit is a read/write bit that, when set to 1, permits the alarm flag (AF) bit in Register C to assert !IRQ. - An alarm interrupt occurs for each second that the three time bytes equal the three alarm bytes, including a don't-care alarm code of binary 11XXXXXX. - The AF bit does not initiate the !IRQ signal when the AIE bit is set to 0. - The internal functions of the device do not affect the AIE bit, but is cleared to 0 on !RESET. */ - RTC_AIE = 0x20, -/* Periodic Interrupt Enable (PIE) - - The PIE bit is a read/write bit that allows the periodic interrupt flag (PF) bit in Register C to drive the !IRQ pin low. - When the PIE bit is set to 1, periodic interrupts are generated by driving the !IRQ pin low at a rate specified by the RS3-RS0 bits of Register A. - A 0 in the PIE bit blocks the !IRQ output from being driven by a periodic interrupt, but the PF bit is still set at the periodic rate. - PIE is not modified by any internal device functions, but is cleared to 0 on !RESET. */ - RTC_PIE = 0x40, -/* SET - - When the SET bit is 0, the update transfer functions normally by advancing the counts once per second. - When the SET bit is written to 1, any update transfer is inhibited, and the program can initialize the time and calendar bytes without an update - occurring in the midst of initializing. Read cycles can be executed in a similar manner. SET is a read/write bit and is not affected by !RESET or - internal functions of the device. */ - RTC_SET = 0x80 -}; - -/* Register C bitflags */ -enum RTC_RC_BITS -{ -/* Unused - - These bits are unused in Register C. These bits always read 0 and cannot be written. */ - RTC_RC = 0xF, -/* Update-Ended Interrupt Flag (UF) - - This bit is set after each update cycle. When the UIE bit is set to 1, the 1 in UF causes the IRQF bit to be a 1, which asserts the !IRQ pin. - This bit can be cleared by reading Register C or with a !RESET. */ - RTC_UF = 0x10, -/* Alarm Interrupt Flag (AF) - - A 1 in the AF bit indicates that the current time has matched the alarm time. - If the AIE bit is also 1, the !IRQ pin goes low and a 1 appears in the IRQF bit. This bit can be cleared by reading Register C or with a !RESET. */ - RTC_AF = 0x20, -/* Periodic Interrupt Flag (PF) - - This bit is read-only and is set to 1 when an edge is detected on the selected tap of the divider chain. - The RS3 through RS0 bits establish the periodic rate. PF is set to 1 independent of the state of the PIE bit. - When both PF and PIE are 1s, the !IRQ signal is active and sets the IRQF bit. This bit can be cleared by reading Register C or with a !RESET. */ - RTC_PF = 0x40, -/* Interrupt Request Flag (IRQF) - - The interrupt request flag (IRQF) is set to a 1 when one or more of the following are true: - - PF == PIE == 1 - - AF == AIE == 1 - - UF == UIE == 1 - - Any time the IRQF bit is a 1, the !IRQ pin is driven low. - All flag bits are cleared after Register C is read by the program or when the !RESET pin is low. */ - RTC_IRQF = 0x80 -}; - -/* Register D bitflags */ -enum RTC_RD_BITS -{ -/* Unused - - The remaining bits of Register D are not usable. They cannot be written and they always read 0. */ - RTC_RD = 0x7F, -/* Valid RAM and Time (VRT) - - This bit indicates the condition of the battery connected to the VBAT pin. This bit is not writeable and should always be 1 when read. - If a 0 is ever present, an exhausted internal lithium energy source is indicated and both the contents of the RTC data and RAM data are questionable. - This bit is unaffected by !RESET. */ - RTC_VRT = 0x80 -}; - -extern void rtc_tick(void); -extern void time_update(char *nvrram, int reg); -extern void time_get(char *nvrram); -extern void time_internal_set_nvrram(char *nvrram); -extern void time_internal_sync(char *nvrram); diff --git a/src/scsi/scsi_aha154x.c b/src/scsi/scsi_aha154x.c index 0b39fa5c5..a5741232c 100644 --- a/src/scsi/scsi_aha154x.c +++ b/src/scsi/scsi_aha154x.c @@ -12,7 +12,7 @@ * * NOTE: THIS IS CURRENTLY A MESS, but will be cleaned up as I go. * - * Version: @(#)scsi_aha154x.c 1.0.18 2017/09/24 + * Version: @(#)scsi_aha154x.c 1.0.19 2017/10/02 * * Authors: Fred N. van Kempen, * Original Buslogic version by SA1988 and Miran Grca. @@ -1838,7 +1838,7 @@ aha_setbios(aha_t *dev) if (dev->bios_path == NULL) return; /* Open the BIOS image file and make sure it exists. */ - pclog_w(L"%S: loading BIOS from '%s'\n", dev->name, dev->bios_path); + pclog("%s: loading BIOS from '%ws'\n", dev->name, dev->bios_path); if ((f = rom_fopen(dev->bios_path, L"rb")) == NULL) { pclog("%s: BIOS ROM not found!\n", dev->name); return; diff --git a/src/scsi/scsi_buslogic.c b/src/scsi/scsi_buslogic.c index d30e92b6e..01ce43a36 100644 --- a/src/scsi/scsi_buslogic.c +++ b/src/scsi/scsi_buslogic.c @@ -10,7 +10,7 @@ * 0 - BT-545C ISA; * 1 - BT-958D PCI (but BT-545C ISA on non-PCI machines) * - * Version: @(#)scsi_buslogic.c 1.0.14 2017/09/24 + * Version: @(#)scsi_buslogic.c 1.0.15 2017/10/02 * * Authors: TheCollector1995, * Miran Grca, @@ -733,7 +733,7 @@ static void BuslogicInitializeAutoSCSIRam(Buslogic_t *bl) { FILE *f; - f = nvrfopen(BuslogicGetNVRFileName(bl), L"rb"); + f = nvr_fopen(BuslogicGetNVRFileName(bl), L"rb"); if (f) { fread(&(bl->LocalRAM.structured.autoSCSIData), 1, 64, f); @@ -2068,7 +2068,7 @@ BuslogicWrite(uint16_t Port, uint8_t Val, void *p) BuslogicAutoSCSIRamSetDefaults(bl, 3); break; case 1: - f = nvrfopen(BuslogicGetNVRFileName(bl), L"wb"); + f = nvr_fopen(BuslogicGetNVRFileName(bl), L"wb"); if (f) { fwrite(&(bl->LocalRAM.structured.autoSCSIData), 1, 64, f); diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index 5a5d600a4..b53ceee6e 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -777,7 +777,7 @@ void *adgold_init() for (; c >= 0; c--) attenuation[c] = 0; - f = nvrfopen(L"adgold.bin", L"rb"); + f = nvr_fopen(L"adgold.bin", L"rb"); if (f) { fread(adgold->adgold_eeprom, 0x18, 1, f); @@ -817,7 +817,7 @@ void adgold_close(void *p) FILE *f; adgold_t *adgold = (adgold_t *)p; - f = nvrfopen(L"adgold.bin", L"wb"); + f = nvr_fopen(L"adgold.bin", L"wb"); if (f) { fwrite(adgold->adgold_eeprom, 0x18, 1, f); diff --git a/src/tandy_eeprom.c b/src/tandy_eeprom.c index 03c302a15..08819a3b4 100644 --- a/src/tandy_eeprom.c +++ b/src/tandy_eeprom.c @@ -136,10 +136,10 @@ static void *tandy_eeprom_init(void) switch (romset) { case ROM_TANDY1000HX: - f = nvrfopen(L"tandy1000hx.bin", L"rb"); + f = nvr_fopen(L"tandy1000hx.bin", L"rb"); break; case ROM_TANDY1000SL2: - f = nvrfopen(L"tandy1000sl2.bin", L"rb"); + f = nvr_fopen(L"tandy1000sl2.bin", L"rb"); break; } if (f) @@ -164,10 +164,10 @@ void tandy_eeprom_close(void *p) switch (eeprom->romset) { case ROM_TANDY1000HX: - f = nvrfopen(L"tandy1000hx.bin", L"wb"); + f = nvr_fopen(L"tandy1000hx.bin", L"wb"); break; case ROM_TANDY1000SL2: - f = nvrfopen(L"tandy1000sl2.bin", L"wb"); + f = nvr_fopen(L"tandy1000sl2.bin", L"wb"); break; } fwrite(eeprom->store, 128, 1, f); diff --git a/src/video/vid_ati_eeprom.c b/src/video/vid_ati_eeprom.c index 1437919e6..fdb819761 100644 --- a/src/video/vid_ati_eeprom.c +++ b/src/video/vid_ati_eeprom.c @@ -43,7 +43,7 @@ void ati_eeprom_load(ati_eeprom_t *eeprom, wchar_t *fn, int type) FILE *f; eeprom->type = type; wcscpy(eeprom->fn, fn); - f = nvrfopen(eeprom->fn, L"rb"); + f = nvr_fopen(eeprom->fn, L"rb"); if (!f) { memset(eeprom->data, 0, eeprom->type ? 512 : 128); @@ -55,7 +55,7 @@ void ati_eeprom_load(ati_eeprom_t *eeprom, wchar_t *fn, int type) void ati_eeprom_save(ati_eeprom_t *eeprom) { - FILE *f = nvrfopen(eeprom->fn, L"wb"); + FILE *f = nvr_fopen(eeprom->fn, L"wb"); if (!f) return; fwrite(eeprom->data, 1, eeprom->type ? 512 : 128, f); fclose(f); diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index bd5b76cc3..360934034 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -11,7 +11,7 @@ * This is intended to be used by another SVGA driver, * and not as a card in it's own right. * - * Version: @(#)vid_svga.c 1.0.3 2017/09/24 + * Version: @(#)vid_svga.c 1.0.4 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -1939,7 +1939,7 @@ void svga_dump_vram() return; } - f = nvrfopen(L"svga_vram.dmp", L"wb"); + f = nvr_fopen(L"svga_vram.dmp", L"wb"); if (f == NULL) { return; diff --git a/src/win/86Box.rc b/src/win/86Box.rc index 7ac919517..e4df2efb4 100644 --- a/src/win/86Box.rc +++ b/src/win/86Box.rc @@ -8,7 +8,7 @@ * * Windows resource script. * - * Version: @(#)86Box.rc 1.0.13 2017/10/01 + * Version: @(#)86Box.rc 1.0.14 2017/10/01 * * Authors: Miran Grca, * Fred N. van Kempen, @@ -266,9 +266,6 @@ BEGIN 12,12 LTEXT "MB",IDT_1705,123,64,10,10 LTEXT "Memory:",IDT_1706,7,64,30,10 - LTEXT "NVR Path:",IDT_1700,7,83,60,10 - EDITTEXT IDC_EDIT_NVR_PATH,71,82,138,12,ES_AUTOHSCROLL - PUSHBUTTON "&Specify...",IDC_BUTTON_NVR_PATH,214,82,46,12 CONTROL "Dynamic Recompiler",IDC_CHECK_DYNAREC,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,7,100,94,10 CONTROL "Enable FPU",IDC_CHECK_FPU,"Button",BS_AUTOCHECKBOX | @@ -689,7 +686,9 @@ BEGIN IDS_2053 "Invalid number of sectors (valid values are between 1 and 63)" IDS_2054 "Invalid number of heads (valid values are between 1 and 16)" IDS_2055 "Invalid number of cylinders (valid values are between 1 and 266305)" - IDS_2056 "Specify the NVR Path" +#if NOTUSED + IDS_2056 +#endif IDS_2057 "(empty)" IDS_2058 "(host drive %c:)" IDS_2059 "Turbo" diff --git a/src/win/resource.h b/src/win/resource.h index 29e6f8f12..2f73ad4ed 100644 --- a/src/win/resource.h +++ b/src/win/resource.h @@ -9,10 +9,11 @@ * Windows resource defines. * * NOTE: FIXME: Strings 2176 and 2193 are same. + * NOTE: FIXME: string 2052 not in use. * NOTE: FIXME: string 2095 not in use. - * NOTE: FIXME: strings 2152-2154 not in use. + * NOTE: FIXME: strings 2153-2154 not in use. * - * Version: @(#)resource.h 1.0.8 2017/10/01 + * Version: @(#)resource.h 1.0.9 2017/10/01 * * Authors: Sarah Walker, * Miran Grca, @@ -105,8 +106,6 @@ #define IDC_MEMTEXT 1017 #define IDC_MEMSPIN 1018 #define IDC_TEXT_MB IDT_1705 -#define IDC_EDIT_NVR_PATH 1019 -#define IDC_BUTTON_NVR_PATH 1020 #define IDC_VIDEO 1030 /* video config */ #define IDC_COMBO_VIDEO 1031 diff --git a/src/win/win.c b/src/win/win.c index b7943b061..7ee98f0cd 100644 --- a/src/win/win.c +++ b/src/win/win.c @@ -8,7 +8,7 @@ * * The Emulator's Windows core. * - * Version: @(#)win.c 1.0.14 2017/10/01 + * Version: @(#)win.c 1.0.14 2017/10/02 * * Authors: Sarah Walker, * Miran Grca, @@ -338,8 +338,8 @@ void mainthread(LPVOID param) if (frames >= 200 && nvr_dosave) { frames = 0; + nvr_save(); nvr_dosave = 0; - savenvr(); } end_time = timer_read(); main_time += end_time - start_time; @@ -1904,7 +1904,7 @@ int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpsz Sleep(200); TerminateThread(mainthreadh, 0); - savenvr(); + nvr_save(); config_save(); @@ -2022,7 +2022,7 @@ win_pc_reset(int hard) Sleep(100); - savenvr(); + nvr_save(); config_save(); diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 5857b0a58..9780e07db 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -8,7 +8,7 @@ * * Windows 86Box Settings dialog handler. * - * Version: @(#)win_settings.c 1.0.17 2017/10/01 + * Version: @(#)win_settings.c 1.0.17 2017/10/02 * * Author: Miran Grca, * Copyright 2016,2017 Miran Grca. @@ -56,7 +56,6 @@ /* Machine category */ static int temp_machine, temp_cpu_m, temp_cpu, temp_wait_states, temp_mem_size, temp_dynarec, temp_fpu, temp_sync; -static wchar_t temp_nvr_path[520]; /* Video category */ static int temp_gfxcard, temp_video_speed, temp_voodoo; @@ -115,8 +114,6 @@ static void win_settings_init(void) temp_wait_states = cpu_waitstates; temp_cpu = cpu; temp_mem_size = mem_size; - memset(temp_nvr_path, 0, sizeof(temp_nvr_path)); - wcscpy(temp_nvr_path, nvr_path); temp_dynarec = cpu_use_dynarec; temp_fpu = enable_external_fpu; temp_sync = enable_sync; @@ -187,7 +184,6 @@ static int win_settings_changed(void) i = i || (cpu_waitstates != temp_wait_states); i = i || (cpu != temp_cpu); i = i || (mem_size != temp_mem_size); - i = i || wcscmp(temp_nvr_path, nvr_path); i = i || (temp_dynarec != cpu_use_dynarec); i = i || (temp_fpu != enable_external_fpu); i = i || (temp_sync != enable_sync); @@ -292,8 +288,6 @@ static void win_settings_save(void) cpu_waitstates = temp_wait_states; cpu = temp_cpu; mem_size = temp_mem_size; - memset(nvr_path, 0, sizeof(nvr_path)); - wcscpy(nvr_path, temp_nvr_path); cpu_use_dynarec = temp_dynarec; enable_external_fpu = temp_fpu; enable_sync = temp_sync; @@ -548,7 +542,6 @@ static BOOL CALLBACK win_settings_machine_proc(HWND hdlg, UINT message, WPARAM w int d = 0; LPTSTR lptsTemp; char *stransi; - wchar_t *p; switch (message) { @@ -600,9 +593,6 @@ static BOOL CALLBACK win_settings_machine_proc(HWND hdlg, UINT message, WPARAM w win_settings_machine_recalc_machine(hdlg); - h = GetDlgItem(hdlg, IDC_EDIT_NVR_PATH); - SendMessage(h, WM_SETTEXT, 0, (LPARAM) temp_nvr_path); - free(lptsTemp); return TRUE; @@ -644,24 +634,6 @@ static BOOL CALLBACK win_settings_machine_proc(HWND hdlg, UINT message, WPARAM w deviceconfig_open(hdlg, (void *)machine_getdevice(temp_machine)); break; - case IDC_BUTTON_NVR_PATH: - p = BrowseFolder(temp_nvr_path, win_language_get_string_from_id(IDS_2056)); - if (wcscmp(p, L"")) - { - memset(temp_nvr_path, 0, sizeof(temp_nvr_path)); - wcscpy(temp_nvr_path, p); - if (temp_nvr_path[wcslen(temp_nvr_path) - 1] == L'/') - { - temp_nvr_path[wcslen(temp_nvr_path) - 1] = L'\\'; - } - else if (temp_nvr_path[wcslen(temp_nvr_path) - 1] != L'\\') - { - temp_nvr_path[wcslen(temp_nvr_path)] = L'\\'; - } - h = GetDlgItem(hdlg, IDC_EDIT_NVR_PATH); - SendMessage(h, WM_SETTEXT, 0, (LPARAM) temp_nvr_path); - } - break; } return FALSE;