/*
 * 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.
 *
 *          Implementation of the Intel 450KX Mars Chipset.
 *
 *          i450GX is way more popular of an option but needs more stuff.
 *
 * Authors: Miran Grca, <mgrca8@gmail.com>
 *          Tiseno100,
 *
 *          Copyright 2021-2024 Miran Grca.
 *          Copyright 2021 Tiseno100.
 */
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include "cpu.h"
#include <86box/timer.h>
#include <86box/io.h>
#include <86box/device.h>
#include <86box/mem.h>
#include <86box/pci.h>
#include <86box/plat_unused.h>
#include <86box/smram.h>
#include <86box/spd.h>
#include <86box/chipset.h>

#ifdef ENABLE_450KX_LOG
int i450kx_do_log = ENABLE_450KX_LOG;

static void
i450kx_log(const char *fmt, ...)
{
    va_list ap;

    if (i450kx_do_log) {
        va_start(ap, fmt);
        pclog_ex(fmt, ap);
        va_end(ap);
    }
}
#else
#    define i450kx_log(fmt, ...)
#endif

/* TODO: Finish the bus index stuff. */
typedef struct i450kx_t {
    smram_t *smram[2];

    uint8_t bus_index;
    uint8_t pb_slot;
    uint8_t mc_slot;
    uint8_t pad;

    uint8_t pb_pci_conf[256];
    uint8_t mc_pci_conf[256];

    uint8_t mem_state[2][256];
} i450kx_t;

static void
i450kx_map(i450kx_t *dev, int bus, uint32_t addr, uint32_t size, int state)
{
    uint32_t base      = addr >> 12;
    int      states[4] = { MEM_READ_EXTANY | MEM_WRITE_EXTANY, MEM_READ_INTERNAL | MEM_WRITE_EXTANY,
                           MEM_READ_EXTANY | MEM_WRITE_INTERNAL, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL };

    state &= 3;
    if (dev->mem_state[bus][base] != state) {
        if (bus)
            mem_set_mem_state_bus_both(addr, size, states[state]);
        else
            mem_set_mem_state_cpu_both(addr, size, states[state]);
        dev->mem_state[bus][base] = state;
        flushmmucache_nopc();
    }
}

static void
i450kx_smram_recalc(i450kx_t *dev, int bus)
{
    const uint8_t *regs = bus ? dev->pb_pci_conf : dev->mc_pci_conf;
    uint32_t       addr;
    uint32_t       size;
    int            enable = bus ? !(regs[0x57] & 0x08) : (regs[0x57] & 0x08);

    smram_disable(dev->smram[bus]);

    addr = ((uint32_t) regs[0xb8] << 16) | ((uint32_t) regs[0xb9] << 24);
    size = (((uint32_t) ((regs[0xbb] >> 4) & 0x0f)) << 16) + 0x00010000;

    if ((addr != 0x00000000) && enable) {
        if (bus)
            smram_enable_ex(dev->smram[bus], addr, addr, size, 0, 0, 0, enable);
        else
            smram_enable_ex(dev->smram[bus], addr, addr, size, 0, 0, enable, 0);
    }

    flushmmucache();
}

static void
i450kx_vid_buf_recalc(i450kx_t *dev, int bus)
{
    const uint8_t *regs = bus ? dev->pb_pci_conf : dev->mc_pci_conf;

    int state = (regs[0x58] & 0x02) ? (MEM_READ_INTERNAL | MEM_WRITE_INTERNAL) :
                                      (MEM_READ_EXTANY | MEM_WRITE_EXTANY);

    if (bus)
        mem_set_mem_state_bus_both(0x000a0000, 0x00020000, state);
    else
        mem_set_mem_state_cpu_both(0x000a0000, 0x00020000, state);

    flushmmucache_nopc();
}

static void
pb_write(int func, int addr, UNUSED(int len), uint8_t val, void *priv)
{
    i450kx_t *dev = (i450kx_t *) priv;

    if (func == 0) {
        i450kx_log("[%04X:%08X] i450KX-PB: [W] dev->pb_pci_conf[%02X] = %02X\n", CS, cpu_state.pc,
                   addr, val);

        switch (addr) {
            case 0x04:
                dev->pb_pci_conf[addr] = (dev->pb_pci_conf[addr] & 0x04) | (val & 0x53);
                break;
            case 0x05:
                dev->pb_pci_conf[addr] = val & 0x01;
                break;

            case 0x07:
                dev->pb_pci_conf[addr] &= ~(val & 0xf9);
                break;

            case 0x0d:
                dev->pb_pci_conf[addr] = val;
                break;

            case 0x0f:
                dev->pb_pci_conf[addr] = val & 0xcf;
                break;

            case 0x40:
            case 0x41:
                dev->pb_pci_conf[addr] = val;
                break;
            case 0x43:
                dev->pb_pci_conf[addr] = val & 0x80;
                break;

            case 0x48:
                dev->pb_pci_conf[addr] = val & 0x06;
                break;

            case 0x4a:
            case 0x4b:
                dev->pb_pci_conf[addr] = val;
#if 0
                if (addr == 0x4a)
                    pci_remap_bus(dev->bus_index, val);
#endif
                break;

            case 0x4c:
                dev->pb_pci_conf[addr] = (dev->pb_pci_conf[addr] & 0x01) | (val & 0xd8);
                break;

            case 0x51:
                dev->pb_pci_conf[addr] = val;
                break;

            case 0x53:
                dev->pb_pci_conf[addr] = val & 0x02;
                break;

            case 0x54:
                dev->pb_pci_conf[addr] = val & 0x7b;
                break;
            case 0x55:
                dev->pb_pci_conf[addr] = val & 0x03;
                break;

            case 0x57:
                dev->pb_pci_conf[addr] = val & 0x08;
                i450kx_smram_recalc(dev, 1);
                break;

            case 0x58:
                dev->pb_pci_conf[addr] = val & 0x02;
                i450kx_vid_buf_recalc(dev, 1);
                break;

            case 0x59: /* PAM0 */
                if ((dev->pb_pci_conf[0x59] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0x80000, 0x20000, val & 0x0f);
                if ((dev->pb_pci_conf[0x59] ^ val) & 0xf0) {
                    i450kx_map(dev, 1, 0xf0000, 0x10000, val >> 4);
                    shadowbios = (val & 0x10);
                }
                dev->pb_pci_conf[0x59] = val & 0x33;
                break;
            case 0x5a: /* PAM1 */
                if ((dev->pb_pci_conf[0x5a] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0xc0000, 0x04000, val & 0xf);
                if ((dev->pb_pci_conf[0x5a] ^ val) & 0xf0)
                    i450kx_map(dev, 1, 0xc4000, 0x04000, val >> 4);
                dev->pb_pci_conf[0x5a] = val & 0x33;
                break;
            case 0x5b: /*PAM2 */
                if ((dev->pb_pci_conf[0x5b] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0xc8000, 0x04000, val & 0xf);
                if ((dev->pb_pci_conf[0x5b] ^ val) & 0xf0)
                    i450kx_map(dev, 1, 0xcc000, 0x04000, val >> 4);
                dev->pb_pci_conf[0x5b] = val & 0x33;
                break;
            case 0x5c: /*PAM3 */
                if ((dev->pb_pci_conf[0x5c] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0xd0000, 0x04000, val & 0xf);
                if ((dev->pb_pci_conf[0x5c] ^ val) & 0xf0)
                    i450kx_map(dev, 1, 0xd4000, 0x04000, val >> 4);
                dev->pb_pci_conf[0x5c] = val & 0x33;
                break;
            case 0x5d: /* PAM4 */
                if ((dev->pb_pci_conf[0x5d] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0xd8000, 0x04000, val & 0xf);
                if ((dev->pb_pci_conf[0x5d] ^ val) & 0xf0)
                    i450kx_map(dev, 1, 0xdc000, 0x04000, val >> 4);
                dev->pb_pci_conf[0x5d] = val & 0x33;
                break;
            case 0x5e: /* PAM5 */
                if ((dev->pb_pci_conf[0x5e] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0xe0000, 0x04000, val & 0xf);
                if ((dev->pb_pci_conf[0x5e] ^ val) & 0xf0)
                    i450kx_map(dev, 1, 0xe4000, 0x04000, val >> 4);
                dev->pb_pci_conf[0x5e] = val & 0x33;
                break;
            case 0x5f: /* PAM6 */
                if ((dev->pb_pci_conf[0x5f] ^ val) & 0x0f)
                    i450kx_map(dev, 1, 0xe8000, 0x04000, val & 0xf);
                if ((dev->pb_pci_conf[0x5f] ^ val) & 0xf0)
                    i450kx_map(dev, 1, 0xec000, 0x04000, val >> 4);
                dev->pb_pci_conf[0x5f] = val & 0x33;
                break;

            case 0x70:
                dev->pb_pci_conf[addr] = val & 0xf8;
                break;

            case 0x71:
                dev->pb_pci_conf[addr] = val & 0x71;
                break;

            case 0x78:
                dev->pb_pci_conf[addr] = val & 0xf0;
                break;
            case 0x79:
                dev->pb_pci_conf[addr] = val & 0xfc;
                break;
            case 0x7a:
                dev->pb_pci_conf[addr] = val;
                break;
            case 0x7b:
                dev->pb_pci_conf[addr] = val & 0x0f;
                break;

            case 0x7c:
                dev->pb_pci_conf[addr] = val & 0x9f;
                break;
            case 0x7d:
                dev->pb_pci_conf[addr] = val & 0x1a;
                break;
            case 0x7e:
                dev->pb_pci_conf[addr] = val & 0xf0;
                break;
            case 0x7f:
                dev->pb_pci_conf[addr] = val;
                break;

            case 0x88:
            case 0x89:
                dev->pb_pci_conf[addr] = val;
                break;
            case 0x8b:
                dev->pb_pci_conf[addr] = val & 0x80;
                break;
            case 0x8c:
            case 0x8d:
                dev->pb_pci_conf[addr] = val;
                break;

            case 0x9c:
                dev->pb_pci_conf[addr] = val & 0x01;
                break;

            case 0xa4:
                dev->pb_pci_conf[addr] = val & 0xf8;
                break;
            case 0xa5:
            case 0xa6:
                dev->pb_pci_conf[addr] = val;
                break;
            case 0xa7:
                dev->pb_pci_conf[addr] = val & 0x0f;
                break;

            case 0xb0:
                dev->pb_pci_conf[addr] = val & 0xe0;
                break;
            case 0xb1:
                dev->pb_pci_conf[addr] = val & /*0x1a*/ 0x1f;
                break;

            case 0xb8:
            case 0xb9:
                dev->pb_pci_conf[addr] = val;
                i450kx_smram_recalc(dev, 1);
                break;
            case 0xbb:
                dev->pb_pci_conf[addr] = val & 0xf0;
                i450kx_smram_recalc(dev, 1);
                break;

            case 0xbc:
                dev->pb_pci_conf[addr] = val & 0x11;
                break;

            case 0xc0:
                dev->pb_pci_conf[addr] = val & 0xdf;
                break;
            case 0xc1:
                dev->pb_pci_conf[addr] = val & 0x3f;
                break;

            case 0xc4:
                dev->pb_pci_conf[addr] &= ~(val & 0x0f);
                break;
            case 0xc5:
                dev->pb_pci_conf[addr] &= ~(val & 0x0a);
                break;
            case 0xc6:
                dev->pb_pci_conf[addr] &= ~(val & 0x1f);
                break;

            case 0xc8:
                dev->pb_pci_conf[addr] = val & 0x1f;
                break;

            case 0xca:
            case 0xcb:
                dev->pb_pci_conf[addr] = val;
                break;

            default:
                break;
        }
    }
}

static uint8_t
pb_read(int func, int addr, UNUSED(int len), void *priv)
{
    const i450kx_t *dev = (i450kx_t *) priv;
    uint8_t   ret = 0xff;

    if (func == 0) {
        ret = dev->pb_pci_conf[addr];

        i450kx_log("[%04X:%08X] i450KX-PB: [R] dev->pb_pci_conf[%02X] = %02X\n", CS, cpu_state.pc,
                   addr, ret);
    }

    return ret;
}

/* A way to use spd_write_drbs_interlaved() and convert the output to what we need. */
static void
mc_fill_drbs(i450kx_t *dev)
{
    spd_write_drbs_interleaved(dev->mc_pci_conf, 0x60, 0x6f, 4);
    for (uint8_t i = 0x60; i <= 0x6f; i++) {
        if (i & 0x01)
            dev->mc_pci_conf[i] = 0x00;
        else
            dev->mc_pci_conf[i] &= 0x7f;
    }
}

static void
mc_write(int func, int addr, UNUSED(int len), uint8_t val, void *priv)
{
    i450kx_t *dev = (i450kx_t *) priv;

    if (func == 0) {
        i450kx_log("[%04X:%08X] i450KX-MC: [W] dev->mc_pci_conf[%02X] = %02X\n", CS, cpu_state.pc,
                   addr, val);

        switch (addr) {
            case 0x4c:
                dev->mc_pci_conf[addr] = val & 0xdf;
                break;
            case 0x4d:
                dev->mc_pci_conf[addr] = val & 0xff;
                break;

            case 0x57:
                dev->mc_pci_conf[addr] = val & 0x08;
                i450kx_smram_recalc(dev, 0);
                break;

            case 0x58:
                dev->mc_pci_conf[addr] = val & 0x02;
                i450kx_vid_buf_recalc(dev, 0);
                break;

            case 0x59: /* PAM0 */
                if ((dev->mc_pci_conf[0x59] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0x80000, 0x20000, val & 0x0f);
                if ((dev->mc_pci_conf[0x59] ^ val) & 0xf0) {
                    i450kx_map(dev, 0, 0xf0000, 0x10000, val >> 4);
                    shadowbios = (val & 0x10);
                }
                dev->mc_pci_conf[0x59] = val & 0x33;
                break;
            case 0x5a: /* PAM1 */
                if ((dev->mc_pci_conf[0x5a] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0xc0000, 0x04000, val & 0xf);
                if ((dev->mc_pci_conf[0x5a] ^ val) & 0xf0)
                    i450kx_map(dev, 0, 0xc4000, 0x04000, val >> 4);
                dev->mc_pci_conf[0x5a] = val & 0x33;
                break;
            case 0x5b: /*PAM2 */
                if ((dev->mc_pci_conf[0x5b] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0xc8000, 0x04000, val & 0xf);
                if ((dev->mc_pci_conf[0x5b] ^ val) & 0xf0)
                    i450kx_map(dev, 0, 0xcc000, 0x04000, val >> 4);
                dev->mc_pci_conf[0x5b] = val & 0x33;
                break;
            case 0x5c: /*PAM3 */
                if ((dev->mc_pci_conf[0x5c] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0xd0000, 0x04000, val & 0xf);
                if ((dev->mc_pci_conf[0x5c] ^ val) & 0xf0)
                    i450kx_map(dev, 0, 0xd4000, 0x04000, val >> 4);
                dev->mc_pci_conf[0x5c] = val & 0x33;
                break;
            case 0x5d: /* PAM4 */
                if ((dev->mc_pci_conf[0x5d] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0xd8000, 0x04000, val & 0xf);
                if ((dev->mc_pci_conf[0x5d] ^ val) & 0xf0)
                    i450kx_map(dev, 0, 0xdc000, 0x04000, val >> 4);
                dev->mc_pci_conf[0x5d] = val & 0x33;
                break;
            case 0x5e: /* PAM5 */
                if ((dev->mc_pci_conf[0x5e] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0xe0000, 0x04000, val & 0xf);
                if ((dev->mc_pci_conf[0x5e] ^ val) & 0xf0)
                    i450kx_map(dev, 0, 0xe4000, 0x04000, val >> 4);
                dev->mc_pci_conf[0x5e] = val & 0x33;
                break;
            case 0x5f: /* PAM6 */
                if ((dev->mc_pci_conf[0x5f] ^ val) & 0x0f)
                    i450kx_map(dev, 0, 0xe8000, 0x04000, val & 0xf);
                if ((dev->mc_pci_conf[0x5f] ^ val) & 0xf0)
                    i450kx_map(dev, 0, 0xec000, 0x04000, val >> 4);
                dev->mc_pci_conf[0x5f] = val & 0x33;
                break;

            case 0x60 ... 0x6f:
                dev->mc_pci_conf[addr] = ((addr & 0x0f) & 0x01) ? 0x00 : (val & 0x7f);
                mc_fill_drbs(dev);
                break;

            case 0x74 ... 0x77:
                dev->mc_pci_conf[addr] = val;
                break;

            case 0x78:
                dev->mc_pci_conf[addr] = val & 0xf0;
                break;
            case 0x79:
                dev->mc_pci_conf[addr] = val & 0xfe;
                break;
            case 0x7a:
                dev->mc_pci_conf[addr] = val;
                break;
            case 0x7b:
                dev->mc_pci_conf[addr] = val & 0x0f;
                break;

            case 0x7c:
                dev->mc_pci_conf[addr] = val & 0x1f;
                break;
            case 0x7d:
                dev->mc_pci_conf[addr] = val & 0x0c;
                break;
            case 0x7e:
                dev->mc_pci_conf[addr] = val & 0xf0;
                break;
            case 0x7f:
                dev->mc_pci_conf[addr] = val;
                break;

            case 0x88:
            case 0x89:
                dev->mc_pci_conf[addr] = val;
                break;
            case 0x8b:
                dev->mc_pci_conf[addr] = val & 0x80;
                break;

            case 0x8c:
            case 0x8d:
                dev->mc_pci_conf[addr] = val;
                break;

            case 0xa4:
                dev->mc_pci_conf[addr] = val & 0x01;
                break;
            case 0xa5:
                dev->pb_pci_conf[addr] = val & 0xf0;
                break;
            case 0xa6:
                dev->mc_pci_conf[addr] = val;
                break;
            case 0xa7:
                dev->mc_pci_conf[addr] = val & 0x0f;
                break;

            case 0xa8:
                dev->mc_pci_conf[addr] = val & 0xfe;
                break;
            case 0xa9 ... 0xab:
                dev->mc_pci_conf[addr] = val;
                break;

            case 0xac ... 0xae:
                dev->mc_pci_conf[addr] = val;
                break;
            case 0xaf:
                dev->mc_pci_conf[addr] = val & 0x7f;
                break;

            case 0xb8:
            case 0xb9:
                dev->mc_pci_conf[addr] = val;
                i450kx_smram_recalc(dev, 0);
                break;
            case 0xbb:
                dev->mc_pci_conf[addr] = val & 0xf0;
                i450kx_smram_recalc(dev, 0);
                break;

            case 0xbc:
                dev->mc_pci_conf[addr] = val & 0x01;
                break;

            case 0xc0:
                dev->mc_pci_conf[addr] = val & 0x07;
                break;

            case 0xc2:
                dev->mc_pci_conf[addr] &= ~(val & 0x03);
                break;

            case 0xc4:
                dev->mc_pci_conf[addr] = val & 0xbf;
                break;
            case 0xc5:
                dev->mc_pci_conf[addr] = val & 0x03;
                break;

            case 0xc6:
                dev->mc_pci_conf[addr] &= ~(val & 0x19);
                break;

            case 0xc8:
                dev->mc_pci_conf[addr] = val & 0x1f;
                break;
            case 0xca:
            case 0xcb:
                dev->mc_pci_conf[addr] = val;
                break;

            default:
                break;
        }
    }
}

static uint8_t
mc_read(int func, int addr, UNUSED(int len), void *priv)
{
    const i450kx_t *dev = (i450kx_t *) priv;
    uint8_t   ret = 0xff;

    if (func == 0) {
        ret = dev->mc_pci_conf[addr];

        i450kx_log("[%04X:%08X] i450KX-MC: [R] dev->mc_pci_conf[%02X] = %02X\n", CS, cpu_state.pc,
                   addr, ret);
    }

    return ret;
}

static void
i450kx_reset(void *priv)
{
    i450kx_t *dev = (i450kx_t *) priv;
    uint32_t  i;

    /* Defaults PB */
    dev->pb_pci_conf[0x00] = 0x86;
    dev->pb_pci_conf[0x01] = 0x80;
    dev->pb_pci_conf[0x02] = 0xc4;
    dev->pb_pci_conf[0x03] = 0x84;
    dev->pb_pci_conf[0x04] = 0x07;
    dev->pb_pci_conf[0x05] = 0x00;
    dev->pb_pci_conf[0x06] = 0x40;
    dev->pb_pci_conf[0x07] = 0x02;
    dev->pb_pci_conf[0x08] = 0x02;
    dev->pb_pci_conf[0x09] = 0x00;
    dev->pb_pci_conf[0x0a] = 0x00;
    dev->pb_pci_conf[0x0b] = 0x06;
    dev->pb_pci_conf[0x0c] = 0x08;
    dev->pb_pci_conf[0x0d] = 0x20;
    dev->pb_pci_conf[0x0e] = 0x00;
    dev->pb_pci_conf[0x0f] = 0x00;
    dev->pb_pci_conf[0x40] = 0x00;
    dev->pb_pci_conf[0x41] = 0x00;
    dev->pb_pci_conf[0x42] = 0x00;
    dev->pb_pci_conf[0x43] = 0x00;
    dev->pb_pci_conf[0x48] = 0x06;
    dev->pb_pci_conf[0x49] = 0x19;
    dev->pb_pci_conf[0x4a] = 0x00;
    dev->pb_pci_conf[0x4b] = 0x00;
    dev->pb_pci_conf[0x4c] = 0x19;
    dev->pb_pci_conf[0x51] = 0x80;
    dev->pb_pci_conf[0x53] = 0x00;
    dev->pb_pci_conf[0x54] = 0x00;
    dev->pb_pci_conf[0x55] = 0x00;
    dev->pb_pci_conf[0x57] = 0x00;
    dev->pb_pci_conf[0x58] = 0x02;
    dev->pb_pci_conf[0x70] = 0x00;
    dev->pb_pci_conf[0x71] = 0x00;
    dev->pb_pci_conf[0x78] = 0x00;
    dev->pb_pci_conf[0x79] = 0x00;
    dev->pb_pci_conf[0x7a] = 0x00;
    dev->pb_pci_conf[0x7b] = 0x00;
    dev->pb_pci_conf[0x7c] = 0x00;
    dev->pb_pci_conf[0x7d] = 0x00;
    dev->pb_pci_conf[0x7e] = 0x00;
    dev->pb_pci_conf[0x7f] = 0x00;
    dev->pb_pci_conf[0x88] = 0x00;
    dev->pb_pci_conf[0x89] = 0x00;
    dev->pb_pci_conf[0x8a] = 0x00;
    dev->pb_pci_conf[0x8b] = 0x00;
    dev->pb_pci_conf[0x8c] = 0x00;
    dev->pb_pci_conf[0x8d] = 0x00;
    dev->pb_pci_conf[0x8e] = 0x00;
    dev->pb_pci_conf[0x8f] = 0x00;
    dev->pb_pci_conf[0x9c] = 0x00;
    dev->pb_pci_conf[0xa4] = 0x01;
    dev->pb_pci_conf[0xa5] = 0xc0;
    dev->pb_pci_conf[0xa6] = 0xfe;
    dev->pb_pci_conf[0xa7] = 0x00;
    /* Note: Do NOT reset these two registers on programmed (TRC) hard reset! */
#if 0
    dev->pb_pci_conf[0xb0] = 0x00;
    dev->pb_pci_conf[0xb1] = 0x00;
#endif
    dev->pb_pci_conf[0xb4] = 0xff;
    dev->pb_pci_conf[0xb5] = 0x00;
    dev->pb_pci_conf[0xb8] = 0x05;
    dev->pb_pci_conf[0xb9] = 0x00;
    dev->pb_pci_conf[0xba] = 0x00;
    dev->pb_pci_conf[0xbb] = 0x00;
    dev->pb_pci_conf[0xbc] = 0x01;
    dev->pb_pci_conf[0xc0] = 0x02;
    dev->pb_pci_conf[0xc1] = 0x00;
    dev->pb_pci_conf[0xc2] = 0x00;
    dev->pb_pci_conf[0xc3] = 0x00;
    dev->pb_pci_conf[0xc4] = 0x00;
    dev->pb_pci_conf[0xc5] = 0x00;
    dev->pb_pci_conf[0xc6] = 0x00;
    dev->pb_pci_conf[0xc7] = 0x00;
    dev->pb_pci_conf[0xc8] = 0x03;
    dev->pb_pci_conf[0xc9] = 0x00;
    dev->pb_pci_conf[0xca] = 0x00;
    dev->pb_pci_conf[0xcb] = 0x00;

#if 0
    pci_remap_bus(dev->bus_index, 0x00);
#endif
    i450kx_smram_recalc(dev, 1);
    i450kx_vid_buf_recalc(dev, 1);
    pb_write(0, 0x59, 1, 0x30, dev);
    for (i = 0x5a; i <= 0x5f; i++)
        pb_write(0, i, 1, 0x33, dev);

    /* Defaults MC */
    dev->mc_pci_conf[0x00] = 0x86;
    dev->mc_pci_conf[0x01] = 0x80;
    dev->mc_pci_conf[0x02] = 0xc5;
    dev->mc_pci_conf[0x03] = 0x84;
    dev->mc_pci_conf[0x04] = 0x00;
    dev->mc_pci_conf[0x05] = 0x00;
    dev->mc_pci_conf[0x06] = 0x80;
    dev->mc_pci_conf[0x07] = 0x00;
    dev->mc_pci_conf[0x08] = 0x04;
    dev->mc_pci_conf[0x09] = 0x00;
    dev->mc_pci_conf[0x0a] = 0x00;
    dev->mc_pci_conf[0x0b] = 0x05;
    dev->mc_pci_conf[0x49] = 0x14;
    dev->mc_pci_conf[0x4c] = 0x0b;
    dev->mc_pci_conf[0x4d] = 0x08;
    dev->mc_pci_conf[0x4e] = 0x00;
    dev->mc_pci_conf[0x4f] = 0x00;
    dev->mc_pci_conf[0x57] = 0x00;
    dev->mc_pci_conf[0x58] = 0x00;
    dev->mc_pci_conf[0x74] = 0x00;
    dev->mc_pci_conf[0x75] = 0x00;
    dev->mc_pci_conf[0x76] = 0x00;
    dev->mc_pci_conf[0x77] = 0x00;
    dev->mc_pci_conf[0x78] = 0x10;
    dev->mc_pci_conf[0x79] = 0x00;
    dev->mc_pci_conf[0x7a] = 0x00;
    dev->mc_pci_conf[0x7b] = 0x00;
    dev->mc_pci_conf[0x7c] = 0x00;
    dev->mc_pci_conf[0x7d] = 0x00;
    dev->mc_pci_conf[0x7e] = 0x10;
    dev->mc_pci_conf[0x7f] = 0x00;
    dev->mc_pci_conf[0x88] = 0x00;
    dev->mc_pci_conf[0x89] = 0x00;
    dev->mc_pci_conf[0x8a] = 0x00;
    dev->mc_pci_conf[0x8b] = 0x00;
    dev->mc_pci_conf[0x8c] = 0x00;
    dev->mc_pci_conf[0x8d] = 0x00;
    dev->mc_pci_conf[0x8e] = 0x00;
    dev->mc_pci_conf[0x8f] = 0x00;
    dev->mc_pci_conf[0xa4] = 0x01;
    dev->mc_pci_conf[0xa5] = 0xc0;
    dev->mc_pci_conf[0xa6] = 0xfe;
    dev->mc_pci_conf[0xa7] = 0x00;
    dev->mc_pci_conf[0xa8] = 0x00;
    dev->mc_pci_conf[0xa9] = 0x00;
    dev->mc_pci_conf[0xaa] = 0x00;
    dev->mc_pci_conf[0xab] = 0x00;
    dev->mc_pci_conf[0xac] = 0x16;
    dev->mc_pci_conf[0xad] = 0x35;
    dev->mc_pci_conf[0xae] = 0xdf;
    dev->mc_pci_conf[0xaf] = 0x30;
    dev->mc_pci_conf[0xb8] = 0x0a;
    dev->mc_pci_conf[0xb9] = 0x00;
    dev->mc_pci_conf[0xba] = 0x00;
    dev->mc_pci_conf[0xbb] = 0x00;
    dev->mc_pci_conf[0xbc] = 0x01;
    dev->mc_pci_conf[0xc0] = 0x00;
    dev->mc_pci_conf[0xc1] = 0x00;
    dev->mc_pci_conf[0xc2] = 0x00;
    dev->mc_pci_conf[0xc3] = 0x00;
    dev->mc_pci_conf[0xc4] = 0x00;
    dev->mc_pci_conf[0xc5] = 0x00;
    dev->mc_pci_conf[0xc6] = 0x00;
    dev->mc_pci_conf[0xc7] = 0x00;

    i450kx_smram_recalc(dev, 0);
    i450kx_vid_buf_recalc(dev, 0);
    mc_write(0, 0x59, 1, 0x03, dev);
    for (i = 0x5a; i <= 0x5f; i++)
        mc_write(0, i, 1, 0x00, dev);
    for (i = 0x60; i <= 0x6f; i++)
        dev->mc_pci_conf[i] = 0x01;
}

static void
i450kx_close(void *priv)
{
    i450kx_t *dev = (i450kx_t *) priv;

    smram_del(dev->smram[1]);
    smram_del(dev->smram[0]);
    free(dev);
}

static void *
i450kx_init(UNUSED(const device_t *info))
{
    i450kx_t *dev = (i450kx_t *) calloc(1, sizeof(i450kx_t));

    pci_add_card(PCI_ADD_NORTHBRIDGE, pb_read, pb_write, dev, &dev->pb_slot);        /* Device 19h: Intel 450KX PCI Bridge PB */
    pci_add_card(PCI_ADD_NORTHBRIDGE_SEC, mc_read, mc_write, dev, &dev->mc_slot);    /* Device 14h: Intel 450KX Memory Controller MC */

    dev->smram[0] = smram_add();
    dev->smram[1] = smram_add();

    cpu_cache_int_enabled = 1;
    cpu_cache_ext_enabled = 1;
    cpu_update_waitstates();

    i450kx_reset(dev);

    return dev;
}

const device_t i450kx_device = {
    .name          = "Intel 450KX (Mars)",
    .internal_name = "i450kx",
    .flags         = DEVICE_PCI,
    .local         = 0,
    .init          = i450kx_init,
    .close         = i450kx_close,
    .reset         = i450kx_reset,
    .available     = NULL,
    .speed_changed = NULL,
    .force_redraw  = NULL,
    .config        = NULL
};
