diff --git a/src/chipset/wd76c10.c b/src/chipset/wd76c10.c index ae4b30228..ef076b606 100644 --- a/src/chipset/wd76c10.c +++ b/src/chipset/wd76c10.c @@ -8,14 +8,10 @@ * * Implementation of the Western Digital WD76C10 chipset. * - * Note: This chipset has no datasheet, everything were done via - * reverse engineering the BIOS of various machines using it. + * Authors: Miran Grca, * + * Copyright 2024 Miran Grca. * - * - * Authors: Tiseno100 - * - * Copyright 2021 Tiseno100 */ #include #include @@ -37,112 +33,452 @@ #include <86box/hdc_ide.h> #include <86box/lpt.h> #include <86box/mem.h> -#include <86box/plat_unused.h> +#include <86box/nvr.h> #include <86box/port_92.h> #include <86box/serial.h> +#include <86box/plat_fallthrough.h> #include <86box/chipset.h> /* Lock/Unlock Procedures */ -#define LOCK dev->lock -#define UNLOCKED !dev->lock +#define LOCK dev->locked +#define UNLOCKED !dev->locked + +#define WD76C10_ADDR_INVALID 0x80000000 #ifdef ENABLE_WD76C10_LOG int wd76c10_do_log = ENABLE_WD76C10_LOG; - static void wd76c10_log(const char *fmt, ...) { va_list ap; - if (wd76c10_do_log) { + if (wd76c10_do_log) + { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else -# define wd76c10_log(fmt, ...) +#define wd76c10_log(fmt, ...) #endif -typedef struct wd76c10_t { - uint16_t lock_reg; - uint16_t oscillator_40mhz; - uint16_t cache_flush; - uint16_t ems_page_reg; - uint16_t ems_page_reg_pointer; - uint16_t port_shadow; - uint16_t pmc_interrupt; - uint16_t high_mem_protect_boundry; - uint16_t delay_line; - uint16_t diagnostic; - uint16_t nmi_status; - uint16_t pmc_input; - uint16_t pmc_timer; - uint16_t pmc_output; - uint16_t ems_control_low_address_boundry; - uint16_t shadow_ram; - uint16_t split_addr; - uint16_t bank32staddr; - uint16_t bank10staddr; - uint16_t non_page_mode_dram_timing; - uint16_t mem_control; - uint16_t refresh_control; - uint16_t disk_chip_select; - uint16_t prog_chip_sel_addr; - uint16_t bus_timing_power_down_ctl; - uint16_t clk_control; +typedef struct { + uint32_t enable; + uint32_t virt_addr, phys_addr; + uint32_t virt_size, phys_size; +} ram_bank_t; - int lock; +typedef struct { + uint8_t enabled; - fdc_t *fdc_controller; - mem_mapping_t *mem_mapping; - serial_t *uart[2]; + uint32_t virt, phys; + uint32_t size; +} ems_page_t; + +typedef struct +{ + uint8_t ep, p92; + + uint8_t vbios_states[4]; + uint8_t bios_states[8]; + uint8_t high_bios_states[8]; + uint8_t mem_pages[1024]; + + uint16_t toggle, cpuclk, fpu_ctl, mem_ctl, + split_sa, sh_wp, hmwpb, npmdmt, + ems_ctl, ems_pp, ser_par_cs, rtc_disk_cs, + prog_cs, pmc_in; + + union + { + uint16_t bank_base_regs[2]; + uint8_t bank_bases[4]; + }; + + uint16_t ems_page_regs[40]; + + int locked; + + uint32_t mem_top, hmwp_base; + + ram_bank_t ram_banks[5]; + + ems_page_t ems_pages[40]; + + mem_mapping_t ram_mapping; + + nvr_t *nvr; + + fdc_t *fdc; + serial_t *uart[2]; } wd76c10_t; -static void -wd76c10_refresh_control(wd76c10_t *dev) +static uint32_t bank_sizes[4] = { 0x00020000, /* 64 Kbit X 16 = 1024 Kbit = 128 kB, 8x 8 */ + 0x00080000, /* 256 Kbit X 16 = 4096 Kbit = 512 kB, 9x 9 */ + 0x00200000, /* 1 Mbit X 16 = 16 Mbit = 2 MB, 10x10 */ + 0x00800000 }; /* 4 Mbit X 16 = 64 Mbit = 8 MB, 11x11 */ + +static uint32_t +wd76c10_calc_addr(wd76c10_t *dev, uint32_t addr) +{ + uint32_t ret; + uint8_t ems_page; + uint8_t ems_en = (uint8_t) ((dev->ems_ctl >> 10) & 0x03); + ems_page_t *ep; + uint8_t en_res = (uint8_t) ((dev->ems_ctl >> 7) & 0x01); + uint32_t low_boundary = (((uint32_t) (dev->ems_ctl & 0x007f)) << 17) + 131072; + ram_bank_t *rb = &(dev->ram_banks[4]); + + addr &= 0x00ffffff; + ems_page = dev->mem_pages[addr >> 14]; + + ep = &dev->ems_pages[ems_page]; + + ret = addr; + + /* First, do any address translation (EMS, low boundary filtering). */ + if ((ems_page < 0x20) && (ems_en == 0x03)) + /* Low EMS pages. */ + ret = addr - ep->virt + ep->phys; + else if ((ems_page >= 0x20) && (ems_page < 0x2a) && (ems_en >= 0x02) && ep->enabled) + /* High EMS pages. */ + ret = addr - ep->virt + ep->phys; + else if (en_res && (addr >= low_boundary)) + /* EMS low boundary. */ + ret = WD76C10_ADDR_INVALID; + + /* Then, do the split. */ + if (rb->enable && (ret >= rb->virt_addr) && (ret < (rb->virt_addr + rb->virt_size))) + ret = ret - rb->virt_addr + rb->phys_addr; + + /* Then, disable the required amount of on-board memory between 128k and 640k if so requested. */ + if ((ret >= dev->mem_top) && (ret < 0x000a0000)) + ret = WD76C10_ADDR_INVALID; + + /* Then, handle the physical memory banks. */ + if (ret >= (mem_size << 10)) + /* The physical memory address is too high or disabled, which is invalid. */ + ret = WD76C10_ADDR_INVALID; + /* Otherwise, map it to the correct bank so the BIOS can auto-size it correctly. */ + else for (uint8_t i = 0; i < 4; i++) { + rb = &(dev->ram_banks[i]); + if (rb->enable && (ret >= rb->virt_addr) && (ret < (rb->virt_addr + rb->virt_size))) { + if (rb->phys_size == 0x00000000) + ret = WD76C10_ADDR_INVALID; + else + ret = ((ret - rb->virt_addr) % rb->phys_size) + rb->phys_addr; + break; + } + } + + return ret; +} + +static uint8_t +wd76c10_read_ram(uint32_t addr, void *priv) +{ + wd76c10_t *dev = (wd76c10_t *) priv; + uint8_t ret = 0xff; + + addr = wd76c10_calc_addr(dev, addr); + + if (addr != WD76C10_ADDR_INVALID) + ret = mem_read_ram(addr, priv); + + return ret; +} + +static uint16_t +wd76c10_read_ramw(uint32_t addr, void *priv) +{ + wd76c10_t *dev = (wd76c10_t *) priv; + uint16_t ret = 0xffff; + + addr = wd76c10_calc_addr(dev, addr); + + if (addr != WD76C10_ADDR_INVALID) + ret = mem_read_ramw(addr, priv); + + return ret; +} + +static void +wd76c10_write_ram(uint32_t addr, uint8_t val, void *priv) +{ + wd76c10_t *dev = (wd76c10_t *) priv; + + addr = wd76c10_calc_addr(dev, addr); + + if (addr != WD76C10_ADDR_INVALID) + mem_write_ram(addr, val, priv); +} + +static void +wd76c10_write_ramw(uint32_t addr, uint16_t val, void *priv) +{ + wd76c10_t *dev = (wd76c10_t *) priv; + + addr = wd76c10_calc_addr(dev, addr); + + if (addr != WD76C10_ADDR_INVALID) + mem_write_ramw(addr, val, priv); +} + +static void +wd76c10_banks_recalc(wd76c10_t *dev) +{ + for (uint8_t i = 0; i < 4; i++) { + ram_bank_t *rb = &(dev->ram_banks[i]); + uint8_t bit = i << 1; + rb->virt_size = bank_sizes[(dev->mem_ctl >> bit) & 0x03]; + bit = i + 12; + rb->enable = (dev->split_sa >> bit) & 0x01; + rb->virt_addr = ((uint32_t) dev->bank_bases[i]) << 17; + } +} + +static void +wd76c10_split_recalc(wd76c10_t *dev) +{ + uint32_t sp_size = (dev->split_sa >> 8) & 0x03; + uint32_t split_size = ((sp_size - 1) * 65536); + ram_bank_t *rb = &(dev->ram_banks[4]); + + if (rb->enable && (rb->virt_size != 0x00000000)) + mem_set_mem_state(rb->virt_addr, rb->virt_size, MEM_READ_EXTANY | MEM_WRITE_EXTANY); + rb->virt_addr = ((uint32_t) ((dev->split_sa >> 2) & 0x3f)) << 19; + switch (sp_size) { + case 0x00: + rb->virt_size = 0x00000000; + break; + default: + rb->virt_size = 256 * 1024 + split_size; + break; + } + rb->enable = !!sp_size; + if (rb->enable && (rb->virt_size != 0x00000000)) + mem_set_mem_state(rb->virt_addr, rb->virt_size, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); +} + +static void +wd76c10_dis_mem_recalc(wd76c10_t *dev) +{ + uint8_t dis_mem = (uint8_t) ((dev->sh_wp >> 14) & 0x03); + uint32_t mem_top; + + switch (dis_mem) { + case 0x00: + default: + mem_top = 640 * 1024; + break; + case 0x01: + mem_top = 512 * 1024; + break; + case 0x02: + mem_top = 256 * 1024; + break; + case 0x03: + mem_top = 128 * 1024; + break; + } + + dev->mem_top = mem_top; +} + +static void +wd76c10_shadow_ram_do_recalc(uint8_t *new_st, uint8_t *old_st, uint8_t min, uint8_t max, uint32_t addr) +{ + uint32_t base = 0x00000000; + int flags = 0; + + for (uint8_t i = min; i < max; i++) { + if (new_st[i] != old_st[i]) { + old_st[i] = new_st[i]; + base = addr + ((uint32_t) i) * 0x00004000; + flags = (new_st[i] & 0x01) ? MEM_READ_INTERNAL : + ((new_st[i] & 0x04) ? MEM_READ_ROMCS : MEM_READ_EXTERNAL); + flags |= (new_st[i] & 0x02) ? MEM_WRITE_INTERNAL : + ((new_st[i] & 0x04) ? MEM_WRITE_ROMCS : MEM_WRITE_EXTERNAL); + mem_set_mem_state_both(base, 0x00004000, flags); + } + } +} + + +static void +wd76c10_shadow_ram_recalc(wd76c10_t *dev) +{ + uint8_t vbios_states[4] = { 0 }; + uint8_t bios_states[8] = { 0 }; + uint8_t high_bios_states[8] = { 0 }; + uint8_t wp = (uint8_t) ((dev->sh_wp >> 12) & 0x01); + uint8_t shd = (uint8_t) ((dev->sh_wp >> 8) & 0x03); + uint8_t x_mem = (uint8_t) ((dev->sh_wp >> 7) & 0x01); + uint8_t vb_siz = (uint8_t) ((dev->sh_wp >> 4) & 0x03); + uint8_t vb_top = vb_siz + 1; + uint8_t rom_typ = (uint8_t) ((dev->sh_wp >> 2) & 0x03); + + switch (shd) { + case 0x03: + for (uint8_t i = 0; i < vb_top; i++) { + vbios_states[i] |= 0x01; /* Read. */ + if (!wp) + vbios_states[i] |= 0x02; /* Write. */ + } + if (x_mem) { + for (uint8_t i = 2; i < 4; i++) + bios_states[i] |= 0x03; /* Read/write. */ + } + fallthrough; + case 0x01: + for (uint8_t i = 4; i < 8; i++) { + bios_states[i] |= 0x01; /* Read. */ + if (!wp) + bios_states[i] |= 0x02; /* Write. */ + } + break; + case 0x02: + for (uint8_t i = 0; i < 8; i++) { + bios_states[i] |= 0x01; /* Read. */ + if (!wp) + bios_states[i] |= 0x02; /* Write. */ + } + break; + } + + switch (rom_typ) { + case 0x00: + for (uint8_t i = 0; i < 8; i++) { + bios_states[i] |= 0x04; /* CSPROM#. */ + high_bios_states[i] |= 0x04; /* CSPROM#. */ + } + break; + case 0x02: + for (uint8_t i = 0; i < vb_top; i++) + vbios_states[i] |= 0x04; /* CSPROM#. */ + fallthrough; + case 0x01: + for (uint8_t i = 4; i < 8; i++) { + bios_states[i] |= 0x04; /* CSPROM#. */ + high_bios_states[i] |= 0x04; /* CSPROM#. */ + } + break; + } + + wd76c10_shadow_ram_do_recalc(vbios_states, dev->vbios_states, 0, 4, 0x000c0000); + wd76c10_shadow_ram_do_recalc(bios_states, dev->bios_states, 0, 8, 0x000e0000); + + /* This is not shadowed, but there is a CSPROM# (= ROMCS#) toggle. */ + wd76c10_shadow_ram_do_recalc(high_bios_states, dev->high_bios_states, 0, 8, 0x00fe0000); + + flushmmucache_nopc(); +} + +static void +wd76c10_high_mem_wp_recalc(wd76c10_t *dev) +{ + uint8_t hm_wp = (uint8_t) ((dev->sh_wp >> 13) & 0x01); + uint32_t base = ((uint32_t) (dev->hmwpb & 0x00f0)) << 17; + uint32_t size = 0x01000000 - dev->hmwp_base; + + /* ACCESS_NORMAL means both ACCESS_BUS and ACCESS_CPU are set. */ + mem_set_wp(dev->hmwp_base, size, ACCESS_NORMAL, 0); + + size = 0x01000000 - base; + mem_set_wp(base, size, ACCESS_NORMAL, hm_wp); + + dev->hmwp_base = base; +} + +static void +wd76c10_pf_loc_reset(wd76c10_t *dev) +{ + uint32_t base; + + for (uint8_t i = 0x031; i <= 0x03b; i++) { + dev->mem_pages[i] = 0xff; + base = ((uint32_t) i) << 14; + mem_set_mem_state(base, 0x00004000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); + } + + /* Re-apply any ROMCS#, etc. flags. */ + wd76c10_shadow_ram_recalc(dev); +} + +static void +wd76c10_pf_loc_recalc(wd76c10_t *dev) +{ + uint8_t pf_loc = (uint8_t) ((dev->ems_ctl >> 13) & 0x03); + uint8_t ems_en = (uint8_t) ((dev->ems_ctl >> 10) & 0x03); + uint8_t ems_page; + uint32_t base; + + for (uint8_t i = (0x031 + pf_loc); i <= (0x037 + pf_loc); i++) { + ems_page = (i - 0x10) & 0xf7; + dev->mem_pages[i] = ems_page; + base = ((uint32_t) i) << 14; + dev->ems_pages[ems_page].virt = base; + if ((ems_en >= 0x02) && dev->ems_pages[ems_page].enabled) + mem_set_mem_state(dev->ems_pages[ems_page].virt, 0x00004000, + MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); + } +} + +static void +wd76c10_low_pages_recalc(wd76c10_t *dev) +{ + uint8_t ems_page; + uint32_t base; + + for (uint8_t i = 0x008; i <= 0x027; i++) { + ems_page = i & 0x1f; + dev->mem_pages[i] = ems_page; + base = ((uint32_t) i) << 14; + dev->ems_pages[ems_page].virt = base; + } +} + +static void +wd76c10_ser_par_cs_recalc(wd76c10_t *dev) { - serial_remove(dev->uart[1]); /* Serial B */ - switch ((dev->refresh_control >> 1) & 7) { - case 1: + serial_remove(dev->uart[1]); + switch ((dev->ser_par_cs >> 1) & 0x07) { + case 0x01: serial_setup(dev->uart[1], 0x3f8, 3); break; - case 2: + case 0x02: serial_setup(dev->uart[1], 0x2f8, 3); break; - case 3: + case 0x03: serial_setup(dev->uart[1], 0x3e8, 3); break; - case 4: + case 0x04: serial_setup(dev->uart[1], 0x2e8, 3); break; - default: - break; } - serial_remove(dev->uart[0]); /* Serial A */ - switch ((dev->refresh_control >> 5) & 7) { - case 1: + serial_remove(dev->uart[0]); + switch ((dev->ser_par_cs >> 5) & 0x07) { + case 0x01: serial_setup(dev->uart[0], 0x3f8, 4); break; - case 2: + case 0x02: serial_setup(dev->uart[0], 0x2f8, 4); break; - case 3: + case 0x03: serial_setup(dev->uart[0], 0x3e8, 4); break; - case 4: + case 0x04: serial_setup(dev->uart[0], 0x2e8, 4); break; - default: - break; } - lpt1_remove(); /* LPT */ - switch ((dev->refresh_control >> 9) & 3) { + lpt1_remove(); + switch ((dev->ser_par_cs >> 9) & 0x03) { case 1: lpt1_init(0x3bc); lpt1_irq(7); @@ -155,413 +491,463 @@ wd76c10_refresh_control(wd76c10_t *dev) lpt1_init(0x278); lpt1_irq(7); break; - - default: - break; } } static void -wd76c10_split_addr(wd76c10_t *dev) -{ - switch ((dev->split_addr >> 8) & 3) { - case 1: - if (((dev->shadow_ram >> 8) & 3) == 2) - mem_remap_top(256); - break; - case 2: - if (((dev->shadow_ram >> 8) & 3) == 1) - mem_remap_top(320); - break; - case 3: - if (((dev->shadow_ram >> 8) & 3) == 3) - mem_remap_top(384); - break; - default: - break; - } -} - -static void -wd76c10_disk_chip_select(wd76c10_t *dev) +wd76c10_disk_cs_recalc(wd76c10_t *dev) { ide_pri_disable(); - if (!(dev->disk_chip_select & 1)) { - ide_set_base(0, !(dev->disk_chip_select & 0x0010) ? 0x1f0 : 0x170); - ide_set_side(0, !(dev->disk_chip_select & 0x0010) ? 0x3f6 : 0x376); - } - ide_pri_enable(); + ide_set_base(0, (dev->rtc_disk_cs & 0x0010) ? 0x0170 : 0x01f0); + ide_set_side(0, (dev->rtc_disk_cs & 0x0010) ? 0x0376 : 0x03f6); + if (!(dev->rtc_disk_cs & 0x0002)) + ide_pri_enable(); - fdc_remove(dev->fdc_controller); - if (!(dev->disk_chip_select & 2)) - fdc_set_base(dev->fdc_controller, !(dev->disk_chip_select & 0x0010) ? FDC_PRIMARY_ADDR : FDC_SECONDARY_ADDR); + fdc_remove(dev->fdc); + if (!(dev->rtc_disk_cs & 0x0001)) + fdc_set_base(dev->fdc, (dev->rtc_disk_cs & 0x0010) ? FDC_SECONDARY_ADDR : FDC_PRIMARY_ADDR); } static void -wd76c10_shadow_recalc(wd76c10_t *dev) +wd76c10_outb(uint16_t port, uint8_t val, void *priv) { - switch ((dev->shadow_ram >> 14) & 3) { - case 0: - mem_set_mem_state_both(0x20000, 0x80000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); - break; - case 1: - mem_set_mem_state_both(0x80000, 0x20000, MEM_READ_DISABLED | MEM_WRITE_DISABLED); - break; - case 2: - mem_set_mem_state_both(0x40000, 0x60000, MEM_READ_DISABLED | MEM_WRITE_DISABLED); - break; - case 3: - mem_set_mem_state_both(0x20000, 0x80000, MEM_READ_DISABLED | MEM_WRITE_DISABLED); - break; - default: - break; - } + wd76c10_t *dev = (wd76c10_t *)priv; + uint8_t lk_psw = (uint8_t) ((dev->rtc_disk_cs >> 2) & 0x01); + uint8_t valxor; - switch ((dev->shadow_ram >> 8) & 3) { - case 0: - mem_set_mem_state_both(0xe0000, 0x20000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); - mem_set_mem_state_both(0xc0000, 0x10000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); - break; - case 1: - mem_set_mem_state_both(0xf0000, 0x10000, MEM_READ_INTERNAL | (!!(dev->shadow_ram & 0x1000) ? MEM_WRITE_DISABLED : MEM_WRITE_INTERNAL)); - break; - case 2: - mem_set_mem_state_both(0xe0000, 0x20000, MEM_READ_INTERNAL | (!!(dev->shadow_ram & 0x1000) ? MEM_WRITE_DISABLED : MEM_WRITE_INTERNAL)); - break; - case 3: - mem_set_mem_state_both(0x20000, 0x80000, MEM_READ_DISABLED | (!!(dev->shadow_ram & 0x1000) ? MEM_WRITE_DISABLED : MEM_WRITE_INTERNAL)); - break; - default: + switch (port) { + case 0x0092: + if ((lk_psw && (val & 0x08)) || ((dev->p92 & 0x08) && !(val & 0x08))) + val = (val & 0xf7) | (dev->p92 & 0x08); + + valxor = (dev->p92 ^ val) & 0x08; + dev->p92 = (val & 0x08) | 0xf7; + + if (valxor) + nvr_lock_set(0x38, 0x08, (val & 0x08) ? 0x03 : 0x00, dev->nvr); break; } } static void -wd76c10_write(uint16_t addr, uint16_t val, void *priv) +wd76c10_outw(uint16_t port, uint16_t val, void *priv) { - wd76c10_t *dev = (wd76c10_t *) priv; + wd76c10_t *dev = (wd76c10_t *)priv; + uint8_t inc = (uint8_t) ((dev->ems_ctl >> 15) & 0x01); + uint8_t ems_en; - if (UNLOCKED) { - switch (addr) { - case 0x1072: - dev->clk_control = val; - break; + if (!dev->locked || (port < 0x1072) || (port > 0xf872) || + (port == 0xe072) || (port == 0xe872) || (port == 0xf073)) switch (port) { + case 0x1072: + dev->cpuclk = val; + break; - case 0x1872: - dev->bus_timing_power_down_ctl = val; - break; + case 0x1872: + dev->fpu_ctl = val; + break; - case 0x2072: - dev->refresh_control = val; - wd76c10_refresh_control(dev); - break; + case 0x2072: + dev->ser_par_cs = val; + wd76c10_ser_par_cs_recalc(dev); + break; - case 0x2872: - dev->disk_chip_select = val; - wd76c10_disk_chip_select(dev); - break; + case 0x2872: + dev->rtc_disk_cs = val; + wd76c10_disk_cs_recalc(dev); + break; - case 0x3072: - dev->prog_chip_sel_addr = val; - break; + case 0x3072: + dev->prog_cs = val; + break; - case 0x3872: - dev->non_page_mode_dram_timing = val; - break; + /* TODO: Log this to determine how the BIOS does bank sizing. */ + case 0x3872: + dev->mem_ctl = val; + wd76c10_banks_recalc(dev); + break; - case 0x4072: - dev->mem_control = val; - break; + case 0x4072: + dev->npmdmt = val; + break; - case 0x4872: - dev->bank10staddr = val; - break; + /* A17-A24 */ + case 0x4872: + dev->bank_base_regs[0] = val; + wd76c10_banks_recalc(dev); + break; - case 0x5072: - dev->bank32staddr = val; - break; + /* A17-A24 */ + case 0x5072: + dev->bank_base_regs[1] = val; + wd76c10_banks_recalc(dev); + break; - case 0x5872: - dev->split_addr = val; - wd76c10_split_addr(dev); - break; + case 0x5872: + dev->split_sa = val; + wd76c10_banks_recalc(dev); + wd76c10_split_recalc(dev); + break; - case 0x6072: - dev->shadow_ram = val & 0xffbf; - wd76c10_shadow_recalc(dev); - break; + case 0x6072: + dev->sh_wp = val; + wd76c10_dis_mem_recalc(dev); + wd76c10_pf_loc_reset(dev); + wd76c10_pf_loc_recalc(dev); + wd76c10_low_pages_recalc(dev); + wd76c10_high_mem_wp_recalc(dev); + break; - case 0x6872: - dev->ems_control_low_address_boundry = val & 0xecff; - break; + case 0x6872: + dev->ems_ctl = val; + wd76c10_pf_loc_reset(dev); + wd76c10_pf_loc_recalc(dev); + wd76c10_low_pages_recalc(dev); + break; - case 0x7072: - dev->pmc_output = (val >> 8) & 0x00ff; - break; + case 0x8872: + dev->pmc_in = val; + break; - case 0x7872: - dev->pmc_output = val & 0xff00; - break; + case 0xc072: + dev->hmwpb = val; + wd76c10_high_mem_wp_recalc(dev); + break; - case 0x8072: - dev->pmc_timer = val; - break; - - case 0x8872: - dev->pmc_input = val; - break; - - case 0x9072: - dev->nmi_status = val & 0x00fc; - break; - - case 0x9872: - dev->diagnostic = val & 0xfdff; - break; - - case 0xa072: - dev->delay_line = val; - break; - - case 0xc872: - dev->pmc_interrupt = val & 0xfcfc; - break; - - case 0xf072: - dev->oscillator_40mhz = 0; - break; - - case 0xf472: - dev->oscillator_40mhz = 1; - break; - - case 0xf872: - dev->cache_flush = val; - flushmmucache(); - break; - - default: - break; - } - wd76c10_log("WD76C10: dev->regs[%04x] = %04x\n", addr, val); - } - - switch (addr) { case 0xe072: - dev->ems_page_reg_pointer = val & 0x003f; + dev->ems_pp = val; + dev->ep = (val & 0x3f) % 40; break; case 0xe872: - dev->ems_page_reg = val & 0x8fff; + ems_en = (uint8_t) ((dev->ems_ctl >> 10) & 0x03); + if (ems_en) { + dev->ems_page_regs[dev->ep] = val; + dev->ems_pages[dev->ep].phys = ((uint32_t) (val & 0x0fff)) << 14; + if (dev->ep >= 32) { + dev->ems_pages[dev->ep].enabled = !!(val & 0x8000); + if (ems_en >= 0x02) { + wd76c10_pf_loc_reset(dev); + wd76c10_pf_loc_recalc(dev); + } + } else { + dev->ems_pages[dev->ep].enabled = (ems_en == 0x03); + if (ems_en == 0x03) + wd76c10_low_pages_recalc(dev); + } + } + if (inc) + dev->ep = (dev->ep + 1) % 40; break; case 0xf073: - dev->lock_reg = val & 0x00ff; - LOCK = !(val & 0x00da); + dev->locked = ((val & 0x00ff) != 0x00da); break; - default: + case 0xf872: + flushmmucache(); break; } } -static uint16_t -wd76c10_read(uint16_t addr, void *priv) +static uint8_t +wd76c10_inb(uint16_t port, void *priv) { - const wd76c10_t *dev = (wd76c10_t *) priv; + wd76c10_t *dev = (wd76c10_t *)priv; + uint8_t ret = 0xff; - wd76c10_log("WD76C10: R dev->regs[%04x]\n", addr); - switch (addr) { + switch (port) { + case 0x0092: + ret = (dev->p92 & 0x08) | 0xf7; + break; + } + + return ret; +} + +static uint16_t +wd76c10_inw(uint16_t port, void *priv) +{ + wd76c10_t *dev = (wd76c10_t *)priv; + uint8_t inc = (uint8_t) ((dev->ems_ctl >> 15) & 0x01); + uint16_t ret = 0xffff; + + wd76c10_log("WD76C10: R dev->regs[%04x]\n", port); + + if (!dev->locked || (port < 0x1072) || (port > 0xf872) || + (port == 0xe072) || (port == 0xe872) || (port == 0xf073)) switch (port) { case 0x1072: - return dev->clk_control; + ret = dev->cpuclk; + break; case 0x1872: - return dev->bus_timing_power_down_ctl; + ret = dev->fpu_ctl; + break; case 0x2072: - return dev->refresh_control; + ret = dev->ser_par_cs; + break; case 0x2872: - return dev->disk_chip_select; + ret = dev->rtc_disk_cs; + break; case 0x3072: - return dev->prog_chip_sel_addr; + ret = dev->prog_cs; + break; case 0x3872: - return dev->non_page_mode_dram_timing; + ret = dev->mem_ctl; + break; case 0x4072: - return dev->mem_control; + ret = dev->npmdmt; + break; case 0x4872: - return dev->bank10staddr; + ret = dev->bank_base_regs[0]; + break; case 0x5072: - return dev->bank32staddr; + ret = dev->bank_base_regs[1]; + break; case 0x5872: - return dev->split_addr; + ret = dev->split_sa; + break; case 0x6072: - return dev->shadow_ram; + ret = dev->sh_wp; + break; case 0x6872: - return dev->ems_control_low_address_boundry; - - case 0x7072: - return (dev->pmc_output << 8) & 0xff00; - - case 0x7872: - return (dev->pmc_output) & 0xff00; - - case 0x8072: - return dev->pmc_timer; + ret = dev->ems_ctl; + break; case 0x8872: - return dev->pmc_input; - - case 0x9072: - return dev->nmi_status; - - case 0x9872: - return dev->diagnostic; - - case 0xa072: - return dev->delay_line; + ret = dev->pmc_in; + break; case 0xb872: - return (inb(0x040b) << 8) | inb(0x04d6); + ret = dma[0].mode; + ret |= (((uint16_t) dma[1].mode) << 8); + break; - case 0xc872: - return dev->pmc_interrupt; + case 0xc072: + ret = dev->hmwpb; + break; case 0xd072: - return dev->port_shadow; + ret = (serial_read(0x0002, dev->uart[0]) & 0xc0) << 8; + ret |= (serial_read(0x0002, dev->uart[1]) & 0xc0) << 6; + ret |= (lpt_read_port(0, 0x0002) & 0x0f) << 8; + ret |= lpt_read_port(0, 0x0000); + break; case 0xe072: - return dev->ems_page_reg_pointer; + ret = (dev->ems_pp & 0xffc0) | dev->ep; + break; case 0xe872: - return dev->ems_page_reg; + ret = dev->ems_page_regs[dev->ep]; + if (inc) + dev->ep = (dev->ep + 1) % 40; + break; case 0xfc72: - return 0x0ff0; - - default: - return 0xffff; + ret = ((lpt_read_status(0) & 0x20) >> 2); + ret |= (((uint16_t) dma_m) << 4); + ret |= dev->toggle; + dev->toggle ^= 0x8000; + break; } + + return ret; } static void wd76c10_close(void *priv) { - wd76c10_t *dev = (wd76c10_t *) priv; + wd76c10_t *dev = (wd76c10_t *)priv; free(dev); } -static void * -wd76c10_init(UNUSED(const device_t *info)) + +static void +wd76c10_reset(void *priv) { - wd76c10_t *dev = (wd76c10_t *) malloc(sizeof(wd76c10_t)); - memset(dev, 0, sizeof(wd76c10_t)); + wd76c10_t *dev = (wd76c10_t *)priv; + + dev->locked = 1; + dev->toggle = 0; + + dev->p92 = 0xf7; + + dev->cpuclk = 0x1000; + dev->fpu_ctl = 0x00ca; + dev->mem_ctl = 0x0000; + dev->bank_base_regs[0] = 0x0000; + dev->bank_base_regs[1] = 0x0000; + dev->split_sa = 0x0000; + dev->sh_wp = 0x0000; + dev->hmwpb = 0x0000; + dev->npmdmt = 0x0000; + dev->ems_ctl = 0x0000; + dev->ems_pp = 0x0000; + dev->ser_par_cs = 0x0000; + dev->rtc_disk_cs = 0x0000; + + for (uint8_t i = 0; i < 40; i++) { + dev->ems_page_regs[i] = 0x0000; + dev->ems_pages[i].enabled = 0; + dev->ems_pages[i].phys = 0x00000000; + } + + nvr_lock_set(0x38, 0x08, 0x00, dev->nvr); + + wd76c10_banks_recalc(dev); + wd76c10_split_recalc(dev); + wd76c10_dis_mem_recalc(dev); + wd76c10_high_mem_wp_recalc(dev); + wd76c10_pf_loc_reset(dev); + wd76c10_pf_loc_recalc(dev); + wd76c10_low_pages_recalc(dev); + wd76c10_ser_par_cs_recalc(dev); + wd76c10_disk_cs_recalc(dev); +} + + +static void * +wd76c10_init(const device_t *info) +{ + wd76c10_t *dev = (wd76c10_t *) calloc(1, sizeof(wd76c10_t)); + uint32_t total_mem = mem_size << 10; + uint32_t accum_mem = 0x00000000; + ram_bank_t *rb; + + /* Calculate the physical RAM banks. */ + for (uint8_t i = 0; i < 4; i++) { + rb = &(dev->ram_banks[i]); + uint32_t size = 0x00000000; + for (int8_t j = 3; j >= 0; j--) { + uint32_t *bs = &(bank_sizes[j]); + if (*bs <= total_mem) { + size = *bs; + break; + } + } + if (size != 0x00000000) { + rb->phys_addr = accum_mem; + rb->phys_size = size; + total_mem -= size; + accum_mem += size; + } + } + + rb = &(dev->ram_banks[4]); + rb->phys_addr = 0x000a0000; + rb->phys_size = 0x00060000; + + memset(dev->mem_pages, 0xff, sizeof(dev->mem_pages)); + for (uint8_t i = 0x008; i < 0x01f; i++) + dev->mem_pages[i] = i; + for (uint8_t i = 0x020; i < 0x027; i++) + dev->mem_pages[i] = i - 0x20; device_add(&port_92_inv_device); - dev->uart[0] = device_add_inst(&ns16450_device, 1); - dev->uart[1] = device_add_inst(&ns16450_device, 2); - dev->fdc_controller = device_add(&fdc_at_device); + dev->nvr = device_add(&amstrad_megapc_nvr_device); + dev->uart[0] = device_add_inst(&ns16450_device, 1); + dev->uart[1] = device_add_inst(&ns16450_device, 2); + dev->fdc = device_add(&fdc_at_device); device_add(&ide_isa_device); - /* Lock Configuration */ - LOCK = 1; + wd76c10_reset(dev); + + /* Password Lock */ + io_sethandler(0x0092, 1, wd76c10_inb, NULL, NULL, wd76c10_outb, NULL, NULL, dev); /* Clock Control */ - io_sethandler(0x1072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + io_sethandler(0x1072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* Bus Timing & Power Down Control */ - io_sethandler(0x1872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* FPU Bus Timing & Power Down Control */ + io_sethandler(0x1872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* Refresh Control(Serial & Parallel) */ - io_sethandler(0x2072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* Refresh Control, Serial and Parallel Chip Selects */ + io_sethandler(0x2072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* Disk Chip Select */ - io_sethandler(0x2872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* RTC, PVGA, 80287 Timing, and Disk Chip Selects */ + io_sethandler(0x2872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* Programmable Chip Select Address(Needs more further examination!) */ - io_sethandler(0x3072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* Programmable Chip Select Address */ + io_sethandler(0x3072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); + + /* Memory Control */ + io_sethandler(0x3872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); + + /* Non-page Mode DRAM Memory Timing */ + io_sethandler(0x4072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Bank 1 & 0 Start Address */ - io_sethandler(0x4872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + io_sethandler(0x4872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Bank 3 & 2 Start Address */ - io_sethandler(0x5072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + io_sethandler(0x5072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Split Address */ - io_sethandler(0x5872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + io_sethandler(0x5872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* EMS Control & EMS Low level boundry */ - io_sethandler(0x6072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* RAM Shadow And Write Protect */ + io_sethandler(0x6072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* EMS Control & EMS Low level boundry */ - io_sethandler(0x6872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* EMS Control And Lower EMS Boundary */ + io_sethandler(0x6872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* PMC Output */ - io_sethandler(0x7072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* PMC Inputs */ + io_sethandler(0x8872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* PMC Output */ - io_sethandler(0x7872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* DMA Mode Shadow Register */ + io_sethandler(0xb872, 1, NULL, wd76c10_inw, NULL, NULL, NULL, NULL, dev); - /* PMC Status */ - io_sethandler(0x8072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + /* High Memory Write Protect Boundry */ + io_sethandler(0xc072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); - /* PMC Status */ - io_sethandler(0x8872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); - - /* NMI Status (Needs further checkup) */ - io_sethandler(0x9072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); - - /* Diagnostics */ - io_sethandler(0x9872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); - - /* Delay Line */ - io_sethandler(0xa072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); - - /* DMA Mode Shadow(Needs Involvement on the DMA code) */ - io_sethandler(0xb872, 1, NULL, wd76c10_read, NULL, NULL, NULL, NULL, dev); - - /* High Memory Protection Boundry */ - io_sethandler(0xc072, 1, NULL, wd76c10_read, NULL, NULL, NULL, NULL, dev); - - /* PMC Interrupt Enable */ - io_sethandler(0xc872, 1, NULL, wd76c10_read, NULL, NULL, NULL, NULL, dev); - - /* Port Shadow (Needs further lookup) */ - io_sethandler(0xd072, 1, NULL, wd76c10_read, NULL, NULL, NULL, NULL, dev); + /* Shadow Register */ + io_sethandler(0xd072, 1, NULL, wd76c10_inw, NULL, NULL, NULL, NULL, dev); /* EMS Page Register Pointer */ - io_sethandler(0xe072, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + io_sethandler(0xe072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* EMS Page Register */ - io_sethandler(0xe872, 1, NULL, wd76c10_read, NULL, NULL, wd76c10_write, NULL, dev); + io_sethandler(0xe872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Lock/Unlock Configuration */ - io_sethandler(0xf073, 1, NULL, NULL, NULL, NULL, wd76c10_write, NULL, dev); - - /* 40Mhz Oscillator Enable Disable */ - io_sethandler(0xf072, 1, NULL, NULL, NULL, NULL, wd76c10_write, NULL, dev); - io_sethandler(0xf472, 1, NULL, NULL, NULL, NULL, wd76c10_write, NULL, dev); - - /* Lock Status */ - io_sethandler(0xfc72, 1, NULL, wd76c10_read, NULL, NULL, NULL, NULL, dev); + io_sethandler(0xf073, 1, NULL, NULL, NULL, NULL, wd76c10_outw, NULL, dev); /* Cache Flush */ - io_sethandler(0xf872, 1, NULL, wd76c10_read, NULL, NULL, NULL, NULL, dev); + io_sethandler(0xf872, 1, NULL, NULL, NULL, NULL, wd76c10_outw, NULL, dev); + + /* Lock Status */ + io_sethandler(0xfc72, 1, NULL, wd76c10_inw, NULL, NULL, NULL, NULL, dev); dma_ext_mode_init(); - wd76c10_shadow_recalc(dev); - wd76c10_refresh_control(dev); - wd76c10_disk_chip_select(dev); + mem_mapping_add(&dev->ram_mapping, + 0x00000000, + (mem_size + 384) << 10, + wd76c10_read_ram, + wd76c10_read_ramw, + NULL, + wd76c10_write_ram, + wd76c10_write_ramw, + NULL, + ram, + MEM_MAPPING_INTERNAL, + dev); + mem_mapping_disable(&ram_low_mapping); + mem_mapping_disable(&ram_mid_mapping); + mem_mapping_disable(&ram_high_mapping); + mem_mapping_enable(&dev->ram_mapping); + return dev; } diff --git a/src/include/86box/device.h b/src/include/86box/device.h index f5efb5dbb..71a76c6b1 100644 --- a/src/include/86box/device.h +++ b/src/include/86box/device.h @@ -41,23 +41,41 @@ #ifndef EMU_DEVICE_H #define EMU_DEVICE_H -#define CONFIG_END -1 -#define CONFIG_STRING 0 -#define CONFIG_INT 1 -#define CONFIG_BINARY 2 -#define CONFIG_SELECTION 3 -#define CONFIG_MIDI_OUT 4 -#define CONFIG_FNAME 5 -#define CONFIG_SPINNER 6 -#define CONFIG_HEX16 7 -#define CONFIG_HEX20 8 -#define CONFIG_MAC 9 -#define CONFIG_MIDI_IN 10 -#define CONFIG_BIOS 11 -#define CONFIG_SERPORT 12 +#define CONFIG_END -1 /* N/A */ -#define CONFIG_ONBOARD 256 /* only avaialble on the on-board variant */ -#define CONFIG_STANDALONE 257 /* not available on the on-board variant */ +#define CONFIG_SHIFT 4 + +#define CONFIG_TYPE_INT (0 << CONFIG_SHIFT) +#define CONFIG_TYPE_STRING (1 << CONFIG_SHIFT) +#define CONFIG_TYPE_HEX16 (2 << CONFIG_SHIFT) +#define CONFIG_TYPE_HEX20 (3 << CONFIG_SHIFT) +#define CONFIG_TYPE_MAC (4 << CONFIG_SHIFT) + +#define CONFIG_INT (0 | CONFIG_TYPE_INT) /* config_get_int() */ +#define CONFIG_BINARY (1 | CONFIG_TYPE_INT) /* config_get_int() */ +#define CONFIG_SELECTION (2 | CONFIG_TYPE_INT) /* config_get_int() */ +#define CONFIG_MIDI_OUT (3 | CONFIG_TYPE_INT) /* config_get_int() */ +#define CONFIG_SPINNER (4 | CONFIG_TYPE_INT) /* config_get_int() */ +#define CONFIG_MIDI_IN (5 | CONFIG_TYPE_INT) /* config_get_int() */ + +#define CONFIG_STRING (0 | CONFIG_TYPE_STRING) /* config_get_string() */ +#define CONFIG_FNAME (1 | CONFIG_TYPE_STRING) /* config_get_string() */ +#define CONFIG_SERPORT (2 | CONFIG_TYPE_STRING) /* config_get_string() */ +#define CONFIG_BIOS (3 | CONFIG_TYPE_STRING) /* config_get_string() */ + +#define CONFIG_HEX16 (0 | CONFIG_TYPE_HEX16) /* config_get_hex16() */ + +#define CONFIG_HEX20 (0 | CONFIG_TYPE_HEX20) /* config_get_hex20() */ + +#define CONFIG_MAC (0 | CONFIG_TYPE_MAC) /* N/A */ + +#define CONFIG_SUBTYPE_MASK (CONFIG_IS_STRING - 1) + +#define CONFIG_DEP (16 << CONFIG_SHIFT) +#define CONFIG_TYPE_MASK (CONFIG_DEP - 1) + +// #define CONFIG_ONBOARD 256 /* only avaialble on the on-board variant */ +// #define CONFIG_STANDALONE 257 /* not available on the on-board variant */ enum { DEVICE_PCJR = 2, /* requires an IBM PCjr */ @@ -100,38 +118,49 @@ enum { #define BIOS_INTERLEAVED_INVERT 8 #define BIOS_HIGH_BIT_INVERT 16 +#define device_common_config_t \ + const char *name; \ + const char *description; \ + int type; \ + const char *default_string; \ + int default_int; \ + const char *file_filter; \ + const device_config_spinner_t spinner; \ + const device_config_selection_t selection[32] + typedef struct device_config_selection_t { const char *description; int value; } device_config_selection_t; -typedef struct device_config_bios_t { - const char *name; - const char *internal_name; - int bios_type; - int files_no; - uint32_t local; - uint32_t size; - void *dev1; - void *dev2; - const char *files[9]; -} device_config_bios_t; - typedef struct device_config_spinner_t { int16_t min; int16_t max; int16_t step; } device_config_spinner_t; -typedef struct device_config_t { - const char *name; - const char *description; - int type; - const char *default_string; - int default_int; - const char *file_filter; - const device_config_spinner_t spinner; - const device_config_selection_t selection[32]; +typedef struct _device_dep_config_ { + device_common_config_t; +} device_dep_config_t; + +typedef struct device_config_bios_t { + const char *name; + const char *internal_name; + int bios_type; + int files_no; + uint32_t local; + uint32_t size; + void *dev1; + void *dev2; + const char *files[9]; + /* Configuration options that depend on the device variant. + To prevent excessive nesting, there is no CONFIG_BIOS + option a dep_config struct */ + const device_dep_config_t *dep_config; +} device_config_bios_t; + +typedef struct _device_config_ { + device_common_config_t; const device_config_bios_t bios[32]; } device_config_t; diff --git a/src/include/86box/serial.h b/src/include/86box/serial.h index bd7e85a91..08f77ea13 100644 --- a/src/include/86box/serial.h +++ b/src/include/86box/serial.h @@ -124,11 +124,11 @@ extern void serial_set_next_inst(int ni); extern void serial_standalone_init(void); extern void serial_set_clock_src(serial_t *dev, double clock_src); extern void serial_reset_port(serial_t *dev); +extern uint8_t serial_read(uint16_t addr, void *priv); extern void serial_device_timeout(void *priv); - -extern void serial_set_cts(serial_t *dev, uint8_t enabled); -extern void serial_set_dsr(serial_t *dev, uint8_t enabled); -extern void serial_set_dcd(serial_t *dev, uint8_t enabled); +extern void serial_set_cts(serial_t *dev, uint8_t enabled); +extern void serial_set_dsr(serial_t *dev, uint8_t enabled); +extern void serial_set_dcd(serial_t *dev, uint8_t enabled); extern const device_t ns8250_device; extern const device_t ns8250_pcjr_device; diff --git a/src/machine/m_at_286_386sx.c b/src/machine/m_at_286_386sx.c index 0153c0fe7..a688181b4 100644 --- a/src/machine/m_at_286_386sx.c +++ b/src/machine/m_at_286_386sx.c @@ -554,7 +554,7 @@ machine_at_wd76c10_init(const machine_t *model) if (bios_only || !ret) return ret; - machine_at_common_init(model); + machine_at_common_init_ex(model, 2); if (gfxcard[0] == VID_INTERNAL) device_add(¶dise_wd90c11_megapc_device);