#include <stdio.h>
#include <stdlib.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "nvr.h"
#include "nvr_tc8521.h"
#include "pic.h"
#include "timer.h"
#include "rtc.h"
#include "paths.h"
#include "config.h"
#include "model.h"
#include "nmi.h"
#include "t1000.h"
#include "x86.h"

int oldromset;
int nvrmask = 63;
uint8_t nvrram[128];
int nvraddr;

int nvr_dosave = 0;

typedef struct nvr_t {
        pc_timer_t rtc_timer;
        pc_timer_t onesec_timer;
        pc_timer_t update_end_timer;

        int onesec_cnt;
} nvr_t;

FILE *nvrfopen(char *fn, char *mode) {
        char s[512];
        FILE *f;

        strcpy(s, nvr_path);
        put_backslash(s);
        strcat(s, config_name);
        strcat(s, ".");
        strcat(s, fn);
        pclog("NVR try opening %s\n", s);
        f = fopen(s, mode);
        if (f)
                return f;

        if (mode[0] == 'r') {
                snprintf(s, 512, "%s%s", nvr_default_path, fn);
                return fopen(s, mode);
        } else {
                pclog("Failed to open file '%s' for write\n", s);
                return NULL;
        }
}

void getnvrtime() { time_get(nvrram); }

static void nvr_speed_changed(void *p) {
        nvr_t *nvr = (nvr_t *)p;

        if (!(nvrram[RTC_REGA] & RTC_RS)) {
                timer_disable(&nvr->rtc_timer);
                return;
        } else {
                int c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1);
                timer_set_delay_u64(&nvr->rtc_timer, (uint64_t)(RTCCONST * c));
        }
}

static void nvr_rtc(void *p) {
        nvr_t *nvr = (nvr_t *)p;

        if (!(nvrram[RTC_REGA] & RTC_RS)) {
                timer_disable(&nvr->rtc_timer);
                return;
        } else {
                int c = 1 << ((nvrram[RTC_REGA] & RTC_RS) - 1);
                timer_advance_u64(&nvr->rtc_timer, (uint64_t)(RTCCONST * c));
                //                pclog("RTCtime now %f\n",rtctime);
                nvrram[RTC_REGC] |= RTC_PF;
                if (nvrram[RTC_REGB] & RTC_PIE) {
                        nvrram[RTC_REGC] |= RTC_IRQF;
                        if (AMSTRAD)
                                picint(2);
                        else
                                picint(0x100);
                        //                        pclog("RTC int\n");
                }
        }
}

int nvr_update_status = 0;

#define ALARM_DONTCARE 0xc0

int nvr_check_alarm(int nvraddr) {
        return (nvrram[nvraddr + 1] == nvrram[nvraddr] || (nvrram[nvraddr + 1] & ALARM_DONTCARE) == ALARM_DONTCARE);
}

static void nvr_update_end(void *p) {
        //        nvr_t *nvr = (nvr_t *)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);
                }
        }

        //                pclog("RTC onesec\n");
}

static void nvr_onesec(void *p) {
        nvr_t *nvr = (nvr_t *)p;

        nvr->onesec_cnt++;
        if (nvr->onesec_cnt >= 100) {
                if (!(nvrram[RTC_REGB] & RTC_SET)) {
                        nvr_update_status = RTC_UIP;
                        rtc_tick();

                        timer_set_delay_u64(&nvr->update_end_timer, (uint64_t)((244.0 + 1984.0) * TIMER_USEC));
                }
                nvr->onesec_cnt = 0;
        }
        timer_advance_u64(&nvr->onesec_timer, (uint64_t)(10000 * TIMER_USEC));
}

static void writenvr(uint16_t addr, uint8_t val, void *p) {
        nvr_t *nvr = (nvr_t *)p;
        int c, old;

        cycles -= ISA_CYCLES(8);
        //        printf("Write NVR %03X %02X %02X %04X:%04X %i\n",addr,nvraddr,val,cs>>4,pc,ins);
        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 == 0x33) pclog("NVRWRITE33 %02X %04X:%04X %i\n",val,CS,pc,ins);
                if (nvraddr > RTC_REGD && nvrram[nvraddr] != val)
                        nvr_dosave = 1;

                old = nvrram[nvraddr];
                nvrram[nvraddr] = val;

                if (nvraddr == RTC_REGA) {
                        //                        pclog("NVR rate %i\n",val&0xF);
                        if (val & RTC_RS) {
                                c = 1 << ((val & RTC_RS) - 1);
                                timer_set_delay_u64(&nvr->rtc_timer, (uint64_t)(RTCCONST * c));
                        } else
                                timer_disable(&nvr->rtc_timer);
                } 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;
                /*PS/2 BIOSes will disable NMIs and expect the watchdog timer to still be able
                  to fire them. I suspect the watchdog is exempt from NMI masking. Currently NMIs
                  are always enabled for PS/2 machines - this would mean that other peripherals
                  could fire NMIs regardless of the mask state, but as there aren't any emulated
                  MCA peripherals that do this it's currently a moot point.*/

                /* Also don't update the NMI mask on Amstrad PCs - actually
                 * ought not to do it for any XT because their NMI mask
                 * register is at 0xA0. But particularly important on the
                 * PC200 and PPC because their video subsystem issues NMIs */
                if (!(models[model]->flags & (MODEL_MCA | MODEL_AMSTRAD))) {
                        nmi_mask = ~val & 0x80;
                }
        }
}

uint8_t readnvr(uint16_t addr, void *p) {
        //        nvr_t *nvr = (nvr_t *)p;
        uint8_t temp;
        //        printf("Read NVR %03X %02X %02X %04X:%04X\n",addr,nvraddr,nvrram[nvraddr],cs>>4,pc);
        cycles -= ISA_CYCLES(8);
        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;
                }
                //                if (AMIBIOS && nvraddr==0x36) return 0;
                //                if (nvraddr==0xA) nvrram[0xA]^=0x80;
                return nvrram[nvraddr];
        }
        return nvraddr;
}

void loadnvr() {
        FILE *f;

        nvrmask = 63;
        oldromset = romset;
        switch (romset) {
        case ROM_PC1512:
                f = nvrfopen("pc1512.nvr", "rb");
                break;
        case ROM_PC1640:
                f = nvrfopen("pc1640.nvr", "rb");
                break;
        case ROM_PC200:
                f = nvrfopen("pc200.nvr", "rb");
                break;
        case ROM_PC2086:
                f = nvrfopen("pc2086.nvr", "rb");
                break;
        case ROM_PC3086:
                f = nvrfopen("pc3086.nvr", "rb");
                break;
        case ROM_PC5086:
                f = nvrfopen("pc5086.nvr", "rb");
                break;
        case ROM_PPC512:
                f = nvrfopen("ppc512.nvr", "rb");
                break;
        case ROM_IBMAT:
                f = nvrfopen("at.nvr", "rb");
                break;
        case ROM_IBMXT286:
                f = nvrfopen("ibmxt286.nvr", "rb");
                break;
        case ROM_IBMPS1_2011:
                f = nvrfopen("ibmps1_2011.nvr", "rb"); /*nvrmask = 127; */
                break;
        case ROM_IBMPS1_2121:
                f = nvrfopen("ibmps1_2121.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_IBMPS2_M30_286:
                f = nvrfopen("ibmps2_m30_286.nvr", "rb"); /*nvrmask = 127; */
                break;
        case ROM_IBMPS2_M50:
                f = nvrfopen("ibmps2_m50.nvr", "rb");
                break;
        case ROM_IBMPS2_M55SX:
                f = nvrfopen("ibmps2_m55sx.nvr", "rb");
                break;
        case ROM_IBMPS2_M80:
                f = nvrfopen("ibmps2_m80.nvr", "rb");
                break;
        case ROM_CMDPC30:
                f = nvrfopen("cmdpc30.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_AMI286:
                f = nvrfopen("ami286.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_TG286M:
                f = nvrfopen("tg286m.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_AWARD286:
                f = nvrfopen("award286.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_GDC212M:
                f = nvrfopen("gdc212m.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_HYUNDAI_SUPER286TR:
                f = nvrfopen("super286tr.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_GRID1520:
                f = nvrfopen("grid1520.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_GW286CT:
                f = nvrfopen("gw286ct.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_SPC4200P:
                f = nvrfopen("spc4200p.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_SPC4216P:
                f = nvrfopen("spc4216p.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_SPC4620P:
                f = nvrfopen("spc4620p.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_SPC6000A:
                f = nvrfopen("spc6000a.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_SPC6033P:
                f = nvrfopen("spc6033p.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_DELL200:
                f = nvrfopen("dell200.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_IBMAT386:
                f = nvrfopen("at386.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_DESKPRO_386:
                f = nvrfopen("deskpro386.nvr", "rb");
                break;
        case ROM_ACER386:
                f = nvrfopen("acer386.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_KMXC02:
                f = nvrfopen("kmxc02.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_MEGAPC:
                f = nvrfopen("megapc.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_AMA932J:
                f = nvrfopen("ama932j.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_AMI386SX:
                f = nvrfopen("ami386.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_AMI486:
                f = nvrfopen("ami486.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_WIN486:
                f = nvrfopen("win486.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_PCI486:
                f = nvrfopen("hot-433.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_SIS496:
                f = nvrfopen("sis496.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_P55VA:
                f = nvrfopen("p55va.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_P55TVP4:
                f = nvrfopen("p55tvp4.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_430VX:
                f = nvrfopen("430vx.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_REVENGE:
                f = nvrfopen("revenge.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_ENDEAVOR:
                f = nvrfopen("endeavor.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_P55T2P4:
                f = nvrfopen("p55t2p4.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_PX386:
                f = nvrfopen("px386.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_DTK386:
                f = nvrfopen("dtk386.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_MR386DX_OPTI495:
                f = nvrfopen("mr386dx_opti495.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_AMI386DX_OPTI495:
                f = nvrfopen("ami386dx_opti495.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_EPSON_PCAX:
                f = nvrfopen("epson_pcax.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_EPSON_PCAX2E:
                f = nvrfopen("epson_pcax2e.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_PB_L300SX:
                f = nvrfopen("pb_l300sx.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_EPSON_PCAX3:
                f = nvrfopen("epson_pcax3.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_COMPAQ_PII:
                f = nvrfopen("compaq_pii.nvr", "rb");
                break;
        case ROM_T3100E:
                f = nvrfopen("t3100e.nvr", "rb");
                break;
        case ROM_T1000:
                tc8521_loadnvr();
                t1000_configsys_loadnvr();
                t1000_emsboard_loadnvr();
                return;
        case ROM_T1200:
                tc8521_loadnvr();
                t1200_state_loadnvr();
                t1000_emsboard_loadnvr();
                return;
        case ROM_ELX_PC425X:
                f = nvrfopen("elx_pc425.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_PB570:
                f = nvrfopen("pb570.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_ZAPPA:
                f = nvrfopen("zappa.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_ITAUTEC_INFOWAYM:
                f = nvrfopen("infowaym.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_PB520R:
                f = nvrfopen("pb520r.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_XI8088:
                f = nvrfopen("xi8088.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_IBMPS2_M70_TYPE3:
                f = nvrfopen("ibmps2_m70_type3.nvr", "rb");
                break;
        case ROM_IBMPS2_M70_TYPE4:
                f = nvrfopen("ibmps2_m70_type4.nvr", "rb");
                break;
        case ROM_TULIP_TC7:
                f = nvrfopen("tulip_tc7.nvr", "rb");
                break;
        case ROM_PB410A:
                f = nvrfopen("pb410a.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_BULL_MICRAL_45:
                f = nvrfopen("bull_micral_45.nvr", "rb");
                break;
        case ROM_FIC_VA503P:
                f = nvrfopen("fic_va503p.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_CBM_SL386SX25:
                f = nvrfopen("cbm_sl386sx25.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_IBMPS1_2133_451:
                f = nvrfopen("ibmps1_2133.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_ECS_386_32:
                f = nvrfopen("ecs_386_32.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_VS440FX:
                f = nvrfopen("vs440fx.nvr", "rb");
                nvrmask = 127;
                break;
        case ROM_GA686BX:
                f = nvrfopen("ga686bx.nvr", "rb");
                nvrmask = 127;
                break;

        default:
                return;
        }
        if (!f) {
                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] = BCD(80);
                        nvrram[RTC_CENTURY] = BCD(19);
                        nvrram[RTC_REGB] = RTC_2412;
                }
                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;
}
void savenvr() {
        FILE *f;
        switch (oldromset) {
        case ROM_PC1512:
                f = nvrfopen("pc1512.nvr", "wb");
                break;
        case ROM_PC1640:
                f = nvrfopen("pc1640.nvr", "wb");
                break;
        case ROM_PC200:
                f = nvrfopen("pc200.nvr", "wb");
                break;
        case ROM_PC2086:
                f = nvrfopen("pc2086.nvr", "wb");
                break;
        case ROM_PC3086:
                f = nvrfopen("pc3086.nvr", "wb");
                break;
        case ROM_PC5086:
                f = nvrfopen("pc5086.nvr", "wb");
                break;
        case ROM_PPC512:
                f = nvrfopen("ppc512.nvr", "wb");
                break;
        case ROM_IBMAT:
                f = nvrfopen("at.nvr", "wb");
                break;
        case ROM_IBMXT286:
                f = nvrfopen("ibmxt286.nvr", "wb");
                break;
        case ROM_IBMPS1_2011:
                f = nvrfopen("ibmps1_2011.nvr", "wb");
                break;
        case ROM_IBMPS1_2121:
                f = nvrfopen("ibmps1_2121.nvr", "wb");
                break;
        case ROM_IBMPS2_M30_286:
                f = nvrfopen("ibmps2_m30_286.nvr", "wb");
                break;
        case ROM_IBMPS2_M50:
                f = nvrfopen("ibmps2_m50.nvr", "wb");
                break;
        case ROM_IBMPS2_M55SX:
                f = nvrfopen("ibmps2_m55sx.nvr", "wb");
                break;
        case ROM_IBMPS2_M80:
                f = nvrfopen("ibmps2_m80.nvr", "wb");
                break;
        case ROM_CMDPC30:
                f = nvrfopen("cmdpc30.nvr", "wb");
                break;
        case ROM_AMI286:
                f = nvrfopen("ami286.nvr", "wb");
                break;
        case ROM_TG286M:
                f = nvrfopen("tg286m.nvr", "wb");
                break;
        case ROM_AWARD286:
                f = nvrfopen("award286.nvr", "wb");
                break;
        case ROM_GDC212M:
                f = nvrfopen("gdc212m.nvr", "wb");
                break;
        case ROM_HYUNDAI_SUPER286TR:
                f = nvrfopen("super286tr.nvr", "wb");
                break;
        case ROM_GRID1520:
                f = nvrfopen("grid1520.nvr", "wb");
                break;
        case ROM_GW286CT:
                f = nvrfopen("gw286ct.nvr", "wb");
                break;
        case ROM_SPC4200P:
                f = nvrfopen("spc4200p.nvr", "wb");
                break;
        case ROM_SPC4216P:
                f = nvrfopen("spc4216p.nvr", "wb");
                break;
        case ROM_SPC4620P:
                f = nvrfopen("spc4620p.nvr", "wb");
                break;
        case ROM_SPC6000A:
                f = nvrfopen("spc6000a.nvr", "wb");
                break;
        case ROM_SPC6033P:
                f = nvrfopen("spc6033p.nvr", "wb");
                break;
        case ROM_DELL200:
                f = nvrfopen("dell200.nvr", "wb");
                break;
        case ROM_IBMAT386:
                f = nvrfopen("at386.nvr", "wb");
                break;
        case ROM_DESKPRO_386:
                f = nvrfopen("deskpro386.nvr", "wb");
                break;
        case ROM_ACER386:
                f = nvrfopen("acer386.nvr", "wb");
                break;
        case ROM_KMXC02:
                f = nvrfopen("kmxc02.nvr", "wb");
                break;
        case ROM_MEGAPC:
                f = nvrfopen("megapc.nvr", "wb");
                break;
        case ROM_AMA932J:
                f = nvrfopen("ama932j.nvr", "wb");
                break;
        case ROM_AMI386SX:
                f = nvrfopen("ami386.nvr", "wb");
                break;
        case ROM_AMI486:
                f = nvrfopen("ami486.nvr", "wb");
                break;
        case ROM_WIN486:
                f = nvrfopen("win486.nvr", "wb");
                break;
        case ROM_PCI486:
                f = nvrfopen("hot-433.nvr", "wb");
                break;
        case ROM_SIS496:
                f = nvrfopen("sis496.nvr", "wb");
                break;
        case ROM_P55VA:
                f = nvrfopen("p55va.nvr", "wb");
                break;
        case ROM_P55TVP4:
                f = nvrfopen("p55tvp4.nvr", "wb");
                break;
        case ROM_430VX:
                f = nvrfopen("430vx.nvr", "wb");
                break;
        case ROM_REVENGE:
                f = nvrfopen("revenge.nvr", "wb");
                break;
        case ROM_ENDEAVOR:
                f = nvrfopen("endeavor.nvr", "wb");
                break;
        case ROM_P55T2P4:
                f = nvrfopen("p55t2p4.nvr", "wb");
                break;
        case ROM_PX386:
                f = nvrfopen("px386.nvr", "wb");
                break;
        case ROM_DTK386:
                f = nvrfopen("dtk386.nvr", "wb");
                break;
        case ROM_MR386DX_OPTI495:
                f = nvrfopen("mr386dx_opti495.nvr", "wb");
                break;
        case ROM_AMI386DX_OPTI495:
                f = nvrfopen("ami386dx_opti495.nvr", "wb");
                break;
        case ROM_EPSON_PCAX:
                f = nvrfopen("epson_pcax.nvr", "wb");
                break;
        case ROM_EPSON_PCAX2E:
                f = nvrfopen("epson_pcax2e.nvr", "wb");
                break;
        case ROM_EPSON_PCAX3:
                f = nvrfopen("epson_pcax3.nvr", "wb");
                break;
        case ROM_COMPAQ_PII:
                f = nvrfopen("compaq_pii.nvr", "wb");
                break;
        case ROM_PB_L300SX:
                f = nvrfopen("pb_l300sx.nvr", "wb");
                break;
        case ROM_T3100E:
                f = nvrfopen("t3100e.nvr", "wb");
                break;
        case ROM_T1000:
                tc8521_savenvr();
                t1000_configsys_savenvr();
                t1000_emsboard_savenvr();
                return;
        case ROM_T1200:
                tc8521_savenvr();
                t1200_state_savenvr();
                t1000_emsboard_savenvr();
                return;
        case ROM_ELX_PC425X:
                f = nvrfopen("elx_pc425.nvr", "wb");
                break;
        case ROM_PB570:
                f = nvrfopen("pb570.nvr", "wb");
                break;
        case ROM_ZAPPA:
                f = nvrfopen("zappa.nvr", "wb");
                break;
        case ROM_ITAUTEC_INFOWAYM:
                f = nvrfopen("infowaym.nvr", "wb");
                break;
        case ROM_PB520R:
                f = nvrfopen("pb520r.nvr", "wb");
                break;
        case ROM_XI8088:
                f = nvrfopen("xi8088.nvr", "wb");
                break;
        case ROM_IBMPS2_M70_TYPE3:
                f = nvrfopen("ibmps2_m70_type3.nvr", "wb");
                break;
        case ROM_IBMPS2_M70_TYPE4:
                f = nvrfopen("ibmps2_m70_type4.nvr", "wb");
                break;
        case ROM_TULIP_TC7:
                f = nvrfopen("tulip_tc7.nvr", "wb");
                break;
        case ROM_PB410A:
                f = nvrfopen("pb410a.nvr", "wb");
                break;
        case ROM_BULL_MICRAL_45:
                f = nvrfopen("bull_micral_45.nvr", "wb");
                break;
        case ROM_FIC_VA503P:
                f = nvrfopen("fic_va503p.nvr", "wb");
                break;
        case ROM_CBM_SL386SX25:
                f = nvrfopen("cbm_sl386sx25.nvr", "wb");
                break;
        case ROM_IBMPS1_2133_451:
                f = nvrfopen("ibmps1_2133.nvr", "wb");
                break;
        case ROM_ECS_386_32:
                f = nvrfopen("ecs_386_32.nvr", "wb");
                break;
        case ROM_VS440FX:
                f = nvrfopen("vs440fx.nvr", "wb");
                break;
        case ROM_GA686BX:
                f = nvrfopen("ga686bx.nvr", "wb");
                break;

        default:
                return;
        }
        fwrite(nvrram, 128, 1, f);
        fclose(f);
}

static void *nvr_init() {
        nvr_t *nvr = (nvr_t *)malloc(sizeof(nvr_t));
        memset(nvr, 0, sizeof(nvr_t));

        io_sethandler(0x0070, 0x0002, readnvr, NULL, NULL, writenvr, NULL, NULL, nvr);
        timer_add(&nvr->rtc_timer, nvr_rtc, nvr, 1);
        timer_add(&nvr->onesec_timer, nvr_onesec, nvr, 1);
        timer_add(&nvr->update_end_timer, nvr_update_end, nvr, 0);

        return nvr;
}

static void nvr_close(void *p) {
        nvr_t *nvr = (nvr_t *)p;

        free(nvr);
}

device_t nvr_device = {"Motorola MC146818 RTC", 0, nvr_init, nvr_close, NULL, nvr_speed_changed, NULL, NULL, NULL};
