Files
86Box/src/machine/m_at_t3100e.c
OBattler 5318bc08d8 The FDC is now a device_t, and the FDC code has been cleaned up;
Merged floppy.c and fdd.c and renamed floppy_*.c (the floppy image format handlers) to fdd_*.c;
Reading the AT or PS/2 keyboard controller status no longer clears the transmit timeout bit, fixes error 8601 (mouse error) on the IBM PS/2 Model 80;
MMU translate and DMA physical reads and writes now go through _mem_exec instead of directly to ram[], should fix the last remaining problems with remapped mappings;
Implemented the Sound gain dialog;
Added the resource for the "New floppy image" dialog and the needed functions for the functionality of exporting the currently mounted floppy image as 86F, both of which should be finished in the next commit;
Applied the CD-ROM fixes from the PCem commit;
Added the "Keep ratio" option for full screen stretch.
2018-01-17 18:43:36 +01:00

728 lines
20 KiB
C

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "../86box.h"
#include "../io.h"
#include "../mouse.h"
#include "../mem.h"
#include "../device.h"
#include "../keyboard.h"
#include "../cpu/cpu.h"
#include "../floppy/fdd.h"
#include "../floppy/fdc.h"
#include "../video/vid_t3100e.h"
#include "machine.h"
#include "m_at_t3100e.h"
/* 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 either 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 function to
* Bit 5: Always 0 } 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.
*
*/
static const int t3100e_log = 0;
extern uint8_t *ram; /* Physical RAM */
/* 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 colour / 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
*/
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);
/* 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];
}
pclog(" %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;
if (t3100e_log)
{
pclog("OUT 0x8084, %02x [ set memory mapping :", val | 0x40);
if (val & 1) pclog("ENABLE_EMS ");
if (val & 2) pclog("ENABLE_XMS ");
if (val & 4) pclog("640K ");
if (val & 8) pclog("X8X ");
if (val & 16) pclog("UPPER_IS_XMS ");
pclog("\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)
{
int c = cpu;
cpu = 0; /* 286/6 */
cpu_set();
cpu = c;
}
else
{
cpu_set();
}
}
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 */
if (t3100e_log) pclog("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 */
if (t3100e_log) pclog("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: if (t3100e_log) pclog("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;
return regs->page[port_to_page(addr)];
}
/* 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);
regs->page_exec[pg & 3] = t3100e_ems_execaddr(regs, pg, val);
if (t3100e_log) pclog("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])
{
if (t3100e_log) pclog("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
{
if (t3100e_log) pclog("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;
//pclog("ems_read_ramw addr=%05x ", addr);
addr = regs->page_exec[pg] + (addr & 0x3FFF);
//pclog("-> %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;
//pclog("ems_write_ramw addr=%05x ", addr);
addr = regs->page_exec[pg] + (addr & 0x3FFF);
//pclog("-> %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;
}
void machine_at_t3100e_init(machine_t *model)
{
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++)
{
if (t3100e_log) pclog("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);
}