Files
86Box/src/machine/m_at_t3100e.c
2020-01-16 20:48:10 +01:00

803 lines
21 KiB
C

/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implementation of the Toshiba T3100e.
*
* The Toshiba 3100e is a 286-based portable.
*
* To bring up the BIOS setup screen hold down the 'Fn' key
* on booting.
*
* Memory management
* ~~~~~~~~~~~~~~~~~
*
* Motherboard memory is divided into:
* - Conventional memory: Either 512k or 640k
* - Upper memory: Either 512k or 384k, depending on
* amount of conventional memory.
* Upper memory can be used as EMS or XMS.
* - High memory: 0-4Mb, depending on RAM installed.
* The BIOS setup screen allows some or
* all of this to be used as EMS; the
* remainder is XMS.
*
* Additional memory (either EMS or XMS) can also be provided
* by ISA expansion cards.
*
* Under test in PCem, the BIOS will boot with up to 65368Kb
* of memory in total (16Mb less 16k). However it will give
* an error with RAM sizes above 8Mb, if any of the high
* memory is allocated as EMS, because the builtin EMS page
* registers can only access up to 8Mb.
*
* Memory is controlled by writes to I/O port 8084h:
* Bit 7: Always 0 }
* Bit 6: Always 1 } These bits select which motherboard
* Bit 5: Always 0 } function to access.
* Bit 4: Set to treat upper RAM as XMS
* Bit 3: Enable external RAM boards?
* Bit 2: Set for 640k conventional memory, clear for 512k
* Bit 1: Enable RAM beyond 1Mb.
* Bit 0: Enable EMS.
*
* The last value written to this port is saved at 0040:0093h,
* and in CMOS memory at offset 0x37. If the top bit of the
* CMOS byte is set, then high memory is being provided by
* an add-on card rather than the mainboard; accordingly,
* the BIOS will not allow high memory to be used as EMS.
*
* EMS is controlled by 16 page registers:
*
* Page mapped at 0xD000 0xD400 0xD800 0xDC00
* ------------------------------------------------------
* Pages 0x00-0x7F 0x208 0x4208 0x8208 0xc208
* Pages 0x80-0xFF 0x218 0x4218 0x8218 0xc218
* Pages 0x100-0x17F 0x258 0x4258 0x8258 0xc258
* Pages 0x180-0x1FF 0x268 0x4268 0x8268 0xc268
*
* The value written has bit 7 set to enable EMS, reset to
* disable it.
*
* So:
* OUT 0x208, 0x80 will page in the first 16k page at 0xD0000.
* OUT 0x208, 0x00 will page out EMS, leaving nothing at 0xD0000.
* OUT 0x4208, 0x80 will page in the first 16k page at 0xD4000.
* OUT 0x218, 0x80 will page in the 129th 16k page at 0xD0000.
* etc.
*
* To use EMS from DOS, you will need the Toshiba EMS driver
* (TOSHEMM.ZIP). This supports the above system, plus further
* ranges of ports at 0x_2A8, 0x_2B8, 0x_2C8.
*
* Features not implemented:
* > Four video fonts.
* > BIOS-controlled mapping of serial ports to IRQs.
* > Custom keyboard controller. This has a number of extra
* commands in the 0xB0-0xBC range, for such things as turbo
* on/off, and switching the keyboard between AT and PS/2
* modes. Currently I have only implemented command 0xBB,
* so that self-test completes successfully. Commands include:
*
* 0xB0: Turbo on
* 0xB1: Turbo off
* 0xB2: Internal display on?
* 0xB3: Internal display off?
* 0xB5: Get settings byte (bottom bit is color/mono setting)
* 0xB6: Set settings byte
* 0xB7: Behave as 101-key PS/2 keyboard
* 0xB8: Behave as 84-key AT keyboard
* 0xBB: Return a byte, bit 2 is Fn key state, other bits unknown.
*
* The other main I/O port needed to POST is:
* 0x8084: System control.
* Top 3 bits give command, bottom 5 bits give parameters.
* 000 => set serial port IRQ / addresses
* bit 4: IRQ5 serial port base: 1 => 0x338, 0 => 0x3E8
* bits 3, 2, 0 specify serial IRQs for COM1, COM2, COM3:
* 00 0 => 4, 3, 5
* 00 1 => 4, 5, 3
* 01 0 => 3, 4, 5
* 01 1 => 3, 5, 4
* 10 0 => 4, -, 3
* 10 1 => 3, -, 4
* 010 => set memory mappings
* bit 4 set if upper RAM is XMS
* bit 3 enable add-on memory boards beyond 5Mb?
* bit 2 set for 640k sysram, clear for 512k sysram
* bit 1 enable mainboard XMS
* bit 0 enable mainboard EMS
* 100 => set parallel mode / LCD settings
* bit 4 set for bidirectional parallel port
* bit 3 set to disable internal CGA
* bit 2 set for single-pixel LCD font
* bits 0,1 for display font
*
* Version: @(#)m_at_t3100e.c 1.0.6 2018/10/22
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include "../86box.h"
#include "../timer.h"
#include "../io.h"
#include "../mouse.h"
#include "../mem.h"
#include "../device.h"
#include "../keyboard.h"
#include "../rom.h"
#include "../cpu/cpu.h"
#include "../floppy/fdd.h"
#include "../floppy/fdc.h"
#include "machine.h"
#include "m_at_t3100e.h"
extern uint8_t *ram; /* Physical RAM */
void at_init();
/* The T3100e motherboard can (and does) dynamically reassign RAM between
* conventional, XMS and EMS. This translates to monkeying with the mappings.
*/
extern mem_mapping_t base_mapping;
extern mem_mapping_t ram_low_mapping; /* This is to switch conventional RAM
* between 512k and 640k */
extern mem_mapping_t ram_mid_mapping; /* This will not be used */
extern mem_mapping_t ram_high_mapping; /* This is RAM beyond 1Mb if any */
extern uint8_t *ram;
static unsigned t3100e_ems_page_reg[] =
{
0x208, 0x4208, 0x8208, 0xc208, /* The first four map the first 2Mb */
/* of RAM into the page frame */
0x218, 0x4218, 0x8218, 0xc218, /* The next four map the next 2Mb */
/* of RAM */
0x258, 0x4258, 0x8258, 0xc258, /* and so on. */
0x268, 0x4268, 0x8268, 0xc268,
};
struct t3100e_ems_regs
{
uint8_t page[16];
mem_mapping_t mapping[4];
uint32_t page_exec[4]; /* Physical location of memory pages */
uint32_t upper_base; /* Start of upper RAM */
uint8_t upper_pages; /* Pages of EMS available from upper RAM */
uint8_t upper_is_ems; /* Upper RAM is EMS? */
mem_mapping_t upper_mapping;
uint8_t notify; /* Notification from keyboard controller */
uint8_t turbo; /* 0 for 6MHz, else full speed */
uint8_t mono; /* Emulates PC/AT 'mono' motherboard switch */
/* Bit 0 is 0 for colour, 1 for mono */
} t3100e_ems;
void t3100e_ems_out(uint16_t addr, uint8_t val, void *p);
#ifdef ENABLE_T3100E_LOG
int t3100e_do_log = ENABLE_T3100E_LOG;
static void
t3100e_log(const char *fmt, ...)
{
va_list ap;
if (t3100e_do_log)
{
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define t3100e_log(fmt, ...)
#endif
/* Given a memory address (which ought to be in the page frame at 0xD0000),
* which page does it relate to? */
static int addr_to_page(uint32_t addr)
{
if ((addr & 0xF0000) == 0xD0000)
{
return ((addr >> 14) & 3);
}
return -1;
}
/* And vice versa: Given a page slot, which memory address does it
* correspond to? */
static uint32_t page_to_addr(int pg)
{
return 0xD0000 + ((pg & 3) * 16384);
}
/* Given an EMS page ID, return its physical address in RAM. */
uint32_t t3100e_ems_execaddr(struct t3100e_ems_regs *regs,
int pg, uint16_t val)
{
uint32_t addr;
if (!(val & 0x80)) return 0; /* Bit 7 reset => not mapped */
val &= 0x7F;
val += (0x80 * (pg >> 2)); /* The high bits of the register bank */
/* are used to extend val to allow up */
/* to 8Mb of EMS to be accessed */
/* Is it in the upper memory range? */
if (regs->upper_is_ems)
{
if (val < regs->upper_pages)
{
addr = regs->upper_base + 0x4000 * val;
return addr;
}
val -= regs->upper_pages;
}
/* Otherwise work down from the top of high RAM (so, the more EMS,
* the less XMS) */
if ((val * 0x4000) + 0x100000 >= (mem_size * 1024))
{
return 0; /* Not enough high RAM for this page */
}
/* High RAM found */
addr = (mem_size * 1024) - 0x4000 * (val + 1);
return addr;
}
/* The registers governing the EMS ports are in rather a nonintuitive order */
static int port_to_page(uint16_t addr)
{
switch (addr)
{
case 0x208: return 0;
case 0x4208: return 1;
case 0x8208: return 2;
case 0xC208: return 3;
case 0x218: return 4;
case 0x4218: return 5;
case 0x8218: return 6;
case 0xC218: return 7;
case 0x258: return 8;
case 0x4258: return 9;
case 0x8258: return 10;
case 0xC258: return 11;
case 0x268: return 12;
case 0x4268: return 13;
case 0x8268: return 14;
case 0xC268: return 15;
}
return -1;
}
/* Used to dump the memory mapping table, for debugging
void dump_mappings()
{
mem_mapping_t *mm = base_mapping.next;
if (!t3100e_log) return;
while (mm)
{
const char *name = "";
uint32_t offset = (uint32_t)(mm->exec - ram);
if (mm == &ram_low_mapping ) name = "LOW ";
if (mm == &ram_mid_mapping ) name = "MID ";
if (mm == &ram_high_mapping) name = "HIGH";
if (mm == &t3100e_ems.upper_mapping) name = "UPPR";
if (mm == &t3100e_ems.mapping[0])
{
name = "EMS0";
offset = t3100e_ems.page_exec[0];
}
if (mm == &t3100e_ems.mapping[1])
{
name = "EMS1";
offset = t3100e_ems.page_exec[1];
}
if (mm == &t3100e_ems.mapping[2])
{
name = "EMS2";
offset = t3100e_ems.page_exec[2];
}
if (mm == &t3100e_ems.mapping[3])
{
name = "EMS3";
offset = t3100e_ems.page_exec[3];
}
t3100e_log(" %p | base=%05x size=%05x %c @ %06x %s\n", mm,
mm->base, mm->size, mm->enable ? 'Y' : 'N',
offset, name);
mm = mm->next;
}
}*/
void t3100e_map_ram(uint8_t val)
{
int n;
int32_t upper_len;
t3100e_log("OUT 0x8084, %02x [ set memory mapping :", val | 0x40);
if (val & 1) t3100e_log("ENABLE_EMS ");
if (val & 2) t3100e_log("ENABLE_XMS ");
if (val & 4) t3100e_log("640K ");
if (val & 8) t3100e_log("X8X ");
if (val & 16) t3100e_log("UPPER_IS_XMS ");
t3100e_log("\n");
/* Bit 2 controls size of conventional memory */
if (val & 4)
{
t3100e_ems.upper_base = 0xA0000;
t3100e_ems.upper_pages = 24;
}
else
{
t3100e_ems.upper_base = 0x80000;
t3100e_ems.upper_pages = 32;
}
upper_len = t3100e_ems.upper_pages * 16384;
mem_mapping_set_addr(&ram_low_mapping, 0, t3100e_ems.upper_base);
/* Bit 0 set if upper RAM is EMS */
t3100e_ems.upper_is_ems = (val & 1);
/* Bit 1 set if high RAM is enabled */
if (val & 2)
{
mem_mapping_enable(&ram_high_mapping);
}
else
{
mem_mapping_disable(&ram_high_mapping);
}
/* Bit 4 set if upper RAM is mapped to high memory
* (and bit 1 set if XMS enabled) */
if ((val & 0x12) == 0x12)
{
mem_mapping_set_addr(&t3100e_ems.upper_mapping,
mem_size * 1024,
upper_len);
mem_mapping_enable(&t3100e_ems.upper_mapping);
mem_mapping_set_exec(&t3100e_ems.upper_mapping, ram + t3100e_ems.upper_base);
}
else
{
mem_mapping_disable(&t3100e_ems.upper_mapping);
}
/* Recalculate EMS mappings */
for (n = 0; n < 4; n++)
{
t3100e_ems_out(t3100e_ems_page_reg[n], t3100e_ems.page[n],
&t3100e_ems);
}
//dump_mappings();
}
void t3100e_notify_set(uint8_t value)
{
t3100e_ems.notify = value;
}
void t3100e_mono_set(uint8_t value)
{
t3100e_ems.mono = value;
}
uint8_t t3100e_mono_get(void)
{
return t3100e_ems.mono;
}
void t3100e_turbo_set(uint8_t value)
{
t3100e_ems.turbo = value;
if (!value)
{
cpu_dynamic_switch(0); /* 286/6 */
}
else
{
cpu_dynamic_switch(cpu);
}
}
uint8_t t3100e_sys_in(uint16_t addr, void *p)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)p;
/* The low 4 bits always seem to be 0x0C. The high 4 are a
* notification sent by the keyboard controller when it detects
* an [Fn] key combination */
t3100e_log("IN 0x8084\n");
return 0x0C | (regs->notify << 4);
}
/* Handle writes to the T3100e system control port at 0x8084 */
void t3100e_sys_out(uint16_t addr, uint8_t val, void *p)
{
// struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)p;
switch (val & 0xE0)
{
case 0x00: /* Set serial port IRQs. Not implemented */
t3100e_log("OUT 0x8084, %02x [ set serial port IRQs]\n", val);
break;
case 0x40: /* Set RAM mappings. */
t3100e_map_ram(val & 0x1F);
break;
case 0x80: /* Set video options. */
t3100e_video_options_set(val & 0x1F); break;
/* Other options not implemented. */
default: t3100e_log("OUT 0x8084, %02x\n", val); break;
}
}
uint8_t t3100e_config_get(void)
{
/* The byte returned:
Bit 7: Set if internal plasma display enabled
Bit 6: Set if running at 6MHz, clear at full speed
Bit 5: Always 1?
Bit 4: Set if the FD2MB jumper is present (internal floppy is ?tri-mode)
Bit 3: Clear if the FD2 jumper is present (two internal floppies)
Bit 2: Set if the internal drive is A:, clear if B:
Bit 1: Set if the parallel port is configured as a floppy connector
for the second drive.
Bit 0: Set if the F2HD jumper is present (internal floppy is 720k)
*/
uint8_t value = 0x28; /* Start with bits 5 and 3 set. */
int type_a = fdd_get_type(0);
int type_b = fdd_get_type(1);
int prt_switch; /* External drive type: 0=> none, 1=>A, 2=>B */
/* Get display setting */
if (t3100e_display_get()) value |= 0x80;
if (!t3100e_ems.turbo) value |= 0x40;
/* Try to determine the floppy types.*/
prt_switch = (type_b ? 2 : 0);
switch(type_a)
{
/* Since a T3100e cannot have an internal 5.25" drive, mark 5.25" A: drive as
* being external, and set the internal type based on type_b. */
case 1: /* 360k */
case 2: /* 1.2Mb */
case 3: /* 1.2Mb RPMx2*/
prt_switch = 1; /* External drive is A: */
switch (type_b)
{
case 1: /* 360k */
case 4: value |= 1; break; /* 720k */
case 6: value |= 0x10; break; /* Tri-mode */
/* All others will be treated as 1.4M */
}
break;
case 4: value |= 0x01; /* 720k */
if (type_a == type_b)
{
value &= (~8); /* Two internal drives */
prt_switch = 0; /* No external drive */
}
break;
case 5: /* 1.4M */
case 7: /* 2.8M */
if (type_a == type_b)
{
value &= (~8); /* Two internal drives */
prt_switch = 0; /* No external drive */
}
break;
case 6: /* 3-mode */
value |= 0x10;
if (type_a == type_b)
{
value &= (~8); /* Two internal drives */
prt_switch = 0; /* No external drive */
}
break;
} /* End switch */
switch (prt_switch)
{
case 0: value |= 4; break; /* No external floppy */
case 1: value |= 2; break; /* External floppy is A: */
case 2: value |= 6; break; /* External floppy is B: */
}
return value;
}
/* Read EMS page register */
uint8_t t3100e_ems_in(uint16_t addr, void *p)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)p;
int page = port_to_page(addr);
if (page >= 0)
return regs->page[page];
else {
fatal("t3100e_ems_in(): invalid address");
return 0xff;
}
}
/* Write EMS page register */
void t3100e_ems_out(uint16_t addr, uint8_t val, void *p)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)p;
int pg = port_to_page(addr);
if (pg == -1)
return;
regs->page_exec[pg & 3] = t3100e_ems_execaddr(regs, pg, val);
t3100e_log("EMS: page %d %02x -> %02x [%06x]\n",
pg, regs->page[pg], val, regs->page_exec[pg & 3]);
regs->page[pg] = val;
pg &= 3;
/* Bit 7 set if page is enabled, reset if page is disabled */
if (regs->page_exec[pg])
{
t3100e_log("Enabling EMS RAM at %05x\n",
page_to_addr(pg));
mem_mapping_enable(&regs->mapping[pg]);
mem_mapping_set_exec(&regs->mapping[pg], ram + regs->page_exec[pg]);
}
else
{
t3100e_log("Disabling EMS RAM at %05x\n",
page_to_addr(pg));
mem_mapping_disable(&regs->mapping[pg]);
}
}
/* Read RAM in the EMS page frame */
static uint8_t ems_read_ram(uint32_t addr, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
int pg = addr_to_page(addr);
if (pg < 0) return 0xFF;
addr = regs->page_exec[pg] + (addr & 0x3FFF);
return ram[addr];
}
static uint16_t ems_read_ramw(uint32_t addr, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
int pg = addr_to_page(addr);
if (pg < 0) return 0xFF;
//t3100e_log("ems_read_ramw addr=%05x ", addr);
addr = regs->page_exec[pg] + (addr & 0x3FFF);
//t3100e_log("-> %06x val=%04x\n", addr, *(uint16_t *)&ram[addr]);
return *(uint16_t *)&ram[addr];
}
static uint32_t ems_read_raml(uint32_t addr, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
int pg = addr_to_page(addr);
if (pg < 0) return 0xFF;
addr = regs->page_exec[pg] + (addr & 0x3FFF);
return *(uint32_t *)&ram[addr];
}
/* Write RAM in the EMS page frame */
static void ems_write_ram(uint32_t addr, uint8_t val, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
int pg = addr_to_page(addr);
if (pg < 0) return;
addr = regs->page_exec[pg] + (addr & 0x3FFF);
ram[addr] = val;
}
static void ems_write_ramw(uint32_t addr, uint16_t val, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
int pg = addr_to_page(addr);
if (pg < 0) return;
//t3100e_log("ems_write_ramw addr=%05x ", addr);
addr = regs->page_exec[pg] + (addr & 0x3FFF);
//t3100e_log("-> %06x val=%04x\n", addr, val);
*(uint16_t *)&ram[addr] = val;
}
static void ems_write_raml(uint32_t addr, uint32_t val, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
int pg = addr_to_page(addr);
if (pg < 0) return;
addr = regs->page_exec[pg] + (addr & 0x3FFF);
*(uint32_t *)&ram[addr] = val;
}
/* Read RAM in the upper area. This is basically what the 'remapped'
* mapping in mem.c does, except that the upper area can move around */
static uint8_t upper_read_ram(uint32_t addr, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
addr = (addr - (1024 * mem_size)) + regs->upper_base;
return ram[addr];
}
static uint16_t upper_read_ramw(uint32_t addr, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
addr = (addr - (1024 * mem_size)) + regs->upper_base;
return *(uint16_t *)&ram[addr];
}
static uint32_t upper_read_raml(uint32_t addr, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
addr = (addr - (1024 * mem_size)) + regs->upper_base;
return *(uint32_t *)&ram[addr];
}
static void upper_write_ram(uint32_t addr, uint8_t val, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
addr = (addr - (1024 * mem_size)) + regs->upper_base;
ram[addr] = val;
}
static void upper_write_ramw(uint32_t addr, uint16_t val, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
addr = (addr - (1024 * mem_size)) + regs->upper_base;
*(uint16_t *)&ram[addr] = val;
}
static void upper_write_raml(uint32_t addr, uint32_t val, void *priv)
{
struct t3100e_ems_regs *regs = (struct t3100e_ems_regs *)priv;
addr = (addr - (1024 * mem_size)) + regs->upper_base;
*(uint32_t *)&ram[addr] = val;
}
int machine_at_t3100e_init(const machine_t *model)
{
int ret;
ret = bios_load_linear(L"roms/machines/t3100e/t3100e.rom",
0x000f0000, 65536, 0);
if (bios_only || !ret)
return ret;
int pg;
memset(&t3100e_ems, 0, sizeof(t3100e_ems));
machine_at_common_ide_init(model);
device_add(&keyboard_at_toshiba_device);
device_add(&fdc_at_device);
/* Hook up system control port */
io_sethandler(0x8084, 0x0001,
t3100e_sys_in, NULL, NULL,
t3100e_sys_out, NULL, NULL, &t3100e_ems);
/* Start monitoring all 16 EMS registers */
for (pg = 0; pg < 16; pg++)
{
io_sethandler(t3100e_ems_page_reg[pg], 0x0001,
t3100e_ems_in, NULL, NULL,
t3100e_ems_out, NULL, NULL, &t3100e_ems);
}
/* Map the EMS page frame */
for (pg = 0; pg < 4; pg++)
{
t3100e_log("Adding memory map at %x for page %d\n", page_to_addr(pg), pg);
mem_mapping_add(&t3100e_ems.mapping[pg],
page_to_addr(pg), 16384,
ems_read_ram, ems_read_ramw, ems_read_raml,
ems_write_ram, ems_write_ramw, ems_write_raml,
NULL, MEM_MAPPING_EXTERNAL,
&t3100e_ems);
/* Start them all off disabled */
mem_mapping_disable(&t3100e_ems.mapping[pg]);
}
/* Mapping for upper RAM when in use as XMS*/
mem_mapping_add(&t3100e_ems.upper_mapping, mem_size * 1024, 384 * 1024,
upper_read_ram, upper_read_ramw, upper_read_raml,
upper_write_ram, upper_write_ramw, upper_write_raml,
NULL, MEM_MAPPING_INTERNAL, &t3100e_ems);
mem_mapping_disable(&t3100e_ems.upper_mapping);
device_add(&t3100e_device);
return ret;
}