1582 lines
55 KiB
C
1582 lines
55 KiB
C
/*
|
|
* 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 Chips&Technology's SCAT (82C235) chipset.
|
|
*
|
|
* Re-worked version based on the 82C235 datasheet and errata.
|
|
*
|
|
*
|
|
*
|
|
* Authors: Original by GreatPsycho for PCem.
|
|
* Fred N. van Kempen, <decwiz@yahoo.com>
|
|
*
|
|
* Copyright 2017-2019 GreatPsycho.
|
|
* Copyright 2017-2019 Fred N. van Kempen.
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <86box/86box.h>
|
|
#include <86box/device.h>
|
|
#include "cpu.h"
|
|
#include "x86.h"
|
|
#include <86box/io.h>
|
|
#include <86box/mem.h>
|
|
#include <86box/nmi.h>
|
|
#include <86box/port_92.h>
|
|
#include <86box/rom.h>
|
|
#include <86box/chipset.h>
|
|
|
|
#define SCAT_DMA_WAIT_STATE_CONTROL 0x01
|
|
#define SCAT_VERSION 0x40
|
|
#define SCAT_CLOCK_CONTROL 0x41
|
|
#define SCAT_PERIPHERAL_CONTROL 0x44
|
|
#define SCAT_MISCELLANEOUS_STATUS 0x45
|
|
#define SCAT_POWER_MANAGEMENT 0x46
|
|
#define SCAT_ROM_ENABLE 0x48
|
|
#define SCAT_RAM_WRITE_PROTECT 0x49
|
|
#define SCAT_SHADOW_RAM_ENABLE_1 0x4A
|
|
#define SCAT_SHADOW_RAM_ENABLE_2 0x4B
|
|
#define SCAT_SHADOW_RAM_ENABLE_3 0x4C
|
|
#define SCAT_DRAM_CONFIGURATION 0x4D
|
|
#define SCAT_EXTENDED_BOUNDARY 0x4E
|
|
#define SCAT_EMS_CONTROL 0x4F
|
|
|
|
#define SCATSX_LAPTOP_FEATURES 0x60
|
|
#define SCATSX_FAST_VIDEO_CONTROL 0x61
|
|
#define SCATSX_FAST_VIDEORAM_ENABLE 0x62
|
|
#define SCATSX_HIGH_PERFORMANCE_REFRESH 0x63
|
|
#define SCATSX_CAS_TIMING_FOR_DMA 0x64
|
|
|
|
typedef struct ems_page_t {
|
|
uint8_t valid;
|
|
uint8_t pad;
|
|
|
|
uint8_t regs_2x8;
|
|
uint8_t regs_2x9;
|
|
|
|
struct scat_t *scat;
|
|
} ems_page_t;
|
|
|
|
typedef struct scat_t {
|
|
uint8_t max_reg;
|
|
uint8_t reg_2xA;
|
|
|
|
uint8_t regs[256];
|
|
|
|
uint32_t xms_bound;
|
|
|
|
int type;
|
|
int indx;
|
|
|
|
int external_is_RAS;
|
|
|
|
ems_page_t null_page;
|
|
ems_page_t page[32];
|
|
|
|
mem_mapping_t low_mapping[32];
|
|
mem_mapping_t remap_mapping[6];
|
|
mem_mapping_t efff_mapping[44];
|
|
mem_mapping_t ems_mapping[32];
|
|
} scat_t;
|
|
|
|
static const uint8_t max_map[32] = {
|
|
// clang-format off
|
|
0, 1, 1, 1, 2, 3, 4, 8,
|
|
4, 8, 12, 16, 20, 24, 28, 32,
|
|
0, 5, 9, 13, 6, 10, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0
|
|
// clang-format om
|
|
};
|
|
static const uint8_t max_map_sx[32] = {
|
|
// clang-format off
|
|
0, 1, 2, 1, 3, 4, 6, 10,
|
|
5, 9, 13, 4, 8, 12, 16, 14,
|
|
18, 22, 26, 20, 24, 28, 32, 18,
|
|
20, 32, 0, 0, 0, 0, 0, 0
|
|
// clang-format om
|
|
};
|
|
static const uint8_t scatsx_external_is_RAS[33] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 1, 0, 0, 1, 1,
|
|
0, 0, 0, 0, 0, 0, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1,
|
|
0
|
|
};
|
|
|
|
static uint8_t scat_in(uint16_t port, void *priv);
|
|
static void scat_out(uint16_t port, uint8_t val, void *priv);
|
|
|
|
static void
|
|
shadow_state_update(scat_t *dev)
|
|
{
|
|
int val;
|
|
|
|
uint32_t base;
|
|
uint32_t bit;
|
|
uint32_t romcs;
|
|
uint32_t shflags = 0;
|
|
|
|
shadowbios = shadowbios_write = 0;
|
|
|
|
for (uint8_t i = 0; i < 24; i++) {
|
|
if ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0xf) < 4)
|
|
val = 0;
|
|
else
|
|
val = (dev->regs[SCAT_SHADOW_RAM_ENABLE_1 + (i >> 3)] >> (i & 7)) & 1;
|
|
|
|
base = 0xa0000 + (i << 14);
|
|
bit = (base - 0xc0000) >> 15;
|
|
romcs = 0;
|
|
|
|
if (base >= 0xc0000)
|
|
romcs = dev->regs[SCAT_ROM_ENABLE] & (1 << bit);
|
|
|
|
if (base >= 0xe0000) {
|
|
shadowbios |= val;
|
|
shadowbios_write |= val;
|
|
}
|
|
|
|
shflags = val ? MEM_READ_INTERNAL : (romcs ? MEM_READ_EXTANY : MEM_READ_EXTERNAL);
|
|
shflags |= (val ? MEM_WRITE_INTERNAL : (romcs ? MEM_WRITE_EXTANY : MEM_WRITE_EXTERNAL));
|
|
|
|
mem_set_mem_state(base, 0x4000, shflags);
|
|
}
|
|
|
|
flushmmucache();
|
|
}
|
|
|
|
static void
|
|
set_xms_bound(scat_t *dev, uint8_t val)
|
|
{
|
|
uint32_t xms_max = ((dev->regs[SCAT_VERSION] & 0xf0) != 0 && ((val & 0x10) != 0)) || (dev->regs[SCAT_VERSION] >= 4) ? 0xfe0000 : 0xfc0000;
|
|
|
|
switch (val & 0x0f) {
|
|
case 1:
|
|
dev->xms_bound = 0x100000;
|
|
break;
|
|
|
|
case 2:
|
|
dev->xms_bound = 0x140000;
|
|
break;
|
|
|
|
case 3:
|
|
dev->xms_bound = 0x180000;
|
|
break;
|
|
|
|
case 4:
|
|
dev->xms_bound = 0x200000;
|
|
break;
|
|
|
|
case 5:
|
|
dev->xms_bound = 0x300000;
|
|
break;
|
|
|
|
case 6:
|
|
dev->xms_bound = 0x400000;
|
|
break;
|
|
|
|
case 7:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0x600000 : 0x500000;
|
|
break;
|
|
|
|
case 8:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0x800000 : 0x700000;
|
|
break;
|
|
|
|
case 9:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0xa00000 : 0x800000;
|
|
break;
|
|
|
|
case 10:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0xc00000 : 0x900000;
|
|
break;
|
|
|
|
case 11:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0xe00000 : 0xa00000;
|
|
break;
|
|
|
|
case 12:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? xms_max : 0xb00000;
|
|
break;
|
|
|
|
case 13:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? xms_max : 0xc00000;
|
|
break;
|
|
|
|
case 14:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? xms_max : 0xd00000;
|
|
break;
|
|
|
|
case 15:
|
|
dev->xms_bound = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? xms_max : 0xf00000;
|
|
break;
|
|
|
|
default:
|
|
dev->xms_bound = xms_max;
|
|
break;
|
|
}
|
|
|
|
if ((((dev->regs[SCAT_VERSION] & 0xf0) == 0) && (val & 0x40) == 0 && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) == 3) || (((dev->regs[SCAT_VERSION] & 0xf0) != 0) && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) == 3)) {
|
|
if ((val & 0x0f) == 0 || dev->xms_bound > 0x160000)
|
|
dev->xms_bound = 0x160000;
|
|
|
|
if (dev->xms_bound > 0x100000)
|
|
mem_set_mem_state(0x100000, dev->xms_bound - 0x100000,
|
|
MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
|
|
|
|
if (dev->xms_bound < 0x160000)
|
|
mem_set_mem_state(dev->xms_bound, 0x160000 - dev->xms_bound,
|
|
MEM_READ_EXTANY | MEM_WRITE_EXTANY);
|
|
} else {
|
|
if (dev->xms_bound > xms_max)
|
|
dev->xms_bound = xms_max;
|
|
|
|
if (dev->xms_bound > 0x100000)
|
|
mem_set_mem_state(0x100000, dev->xms_bound - 0x100000,
|
|
MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
|
|
|
|
if (dev->xms_bound < ((uint32_t) mem_size << 10))
|
|
mem_set_mem_state(dev->xms_bound, (mem_size << 10) - dev->xms_bound,
|
|
MEM_READ_EXTANY | MEM_WRITE_EXTANY);
|
|
}
|
|
|
|
mem_mapping_set_addr(&dev->low_mapping[31], 0xf80000,
|
|
((dev->regs[SCAT_VERSION] & 0xf0) != 0 && ((val & 0x10) != 0)) || (dev->regs[SCAT_VERSION] >= 4) ? 0x60000 : 0x40000);
|
|
if (dev->regs[SCAT_VERSION] & 0xf0) {
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
if (val & 0x10)
|
|
mem_mapping_disable(&bios_high_mapping);
|
|
else
|
|
mem_mapping_enable(&bios_high_mapping);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
get_addr(scat_t *dev, uint32_t addr, ems_page_t *p)
|
|
{
|
|
#if 1
|
|
int nbanks_2048k;
|
|
int nbanks_512k;
|
|
uint32_t addr2;
|
|
int nbank;
|
|
#else
|
|
uint32_t nbanks_2048k, nbanks_512k, addr2, nbank;
|
|
#endif
|
|
|
|
if (p && p->valid && (dev->regs[SCAT_EMS_CONTROL] & 0x80) && (p->regs_2x9 & 0x80))
|
|
addr = (addr & 0x3fff) | (((p->regs_2x9 & 3) << 8) | p->regs_2x8) << 14;
|
|
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0) {
|
|
switch ((dev->regs[SCAT_EXTENDED_BOUNDARY] & ((dev->regs[SCAT_VERSION] & 0x0f) > 3 ? 0x40 : 0)) | (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f)) {
|
|
case 0x41:
|
|
nbank = addr >> 19;
|
|
if (nbank < 4)
|
|
nbank = 1;
|
|
else if (nbank == 4)
|
|
nbank = 0;
|
|
else
|
|
nbank -= 3;
|
|
break;
|
|
|
|
case 0x42:
|
|
nbank = addr >> 19;
|
|
if (nbank < 8)
|
|
nbank = 1 + (nbank >> 2);
|
|
else if (nbank == 8)
|
|
nbank = 0;
|
|
else
|
|
nbank -= 6;
|
|
break;
|
|
|
|
case 0x43:
|
|
nbank = addr >> 19;
|
|
if (nbank < 12)
|
|
nbank = 1 + (nbank >> 2);
|
|
else if (nbank == 12)
|
|
nbank = 0;
|
|
else
|
|
nbank -= 9;
|
|
break;
|
|
|
|
case 0x44:
|
|
nbank = addr >> 19;
|
|
if (nbank < 4)
|
|
nbank = 2;
|
|
else if (nbank < 6)
|
|
nbank -= 4;
|
|
else
|
|
nbank -= 3;
|
|
break;
|
|
|
|
case 0x45:
|
|
nbank = addr >> 19;
|
|
if (nbank < 8)
|
|
nbank = 2 + (nbank >> 2);
|
|
else if (nbank < 10)
|
|
nbank -= 8;
|
|
else
|
|
nbank -= 6;
|
|
break;
|
|
|
|
default:
|
|
nbank = addr >> (((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) < 8 && (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x40) == 0) ? 19 : 21);
|
|
break;
|
|
}
|
|
|
|
nbank &= (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80) ? 7 : 3;
|
|
|
|
if ((dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x40) == 0 && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) == 3 && nbank == 2 && (addr & 0x7ffff) < 0x60000 && mem_size > 640) {
|
|
nbank = 1;
|
|
addr ^= 0x70000;
|
|
}
|
|
|
|
if (dev->external_is_RAS && (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80) == 0) {
|
|
if (nbank == 3)
|
|
nbank = 7;
|
|
else
|
|
return 0xffffffff;
|
|
} else if (!dev->external_is_RAS && dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80) {
|
|
switch (nbank) {
|
|
case 7:
|
|
nbank = 3;
|
|
break;
|
|
|
|
/* Note - In the following cases, the chipset accesses multiple memory banks
|
|
at the same time, so it's impossible to predict which memory bank
|
|
is actually accessed. */
|
|
case 5:
|
|
case 1:
|
|
nbank = 1;
|
|
break;
|
|
|
|
case 3:
|
|
nbank = 2;
|
|
break;
|
|
|
|
default:
|
|
nbank = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((dev->regs[SCAT_VERSION] & 0x0f) > 3 && (mem_size > 2048) && (mem_size & 1536)) {
|
|
if ((mem_size & 1536) == 512) {
|
|
if (nbank == 0)
|
|
addr &= 0x7ffff;
|
|
else
|
|
addr = 0x80000 + ((addr & 0x1fffff) | ((nbank - 1) << 21));
|
|
} else {
|
|
if (nbank < 2)
|
|
addr = (addr & 0x7ffff) | (nbank << 19);
|
|
else
|
|
addr = 0x100000 + ((addr & 0x1fffff) | ((nbank - 2) << 21));
|
|
}
|
|
} else {
|
|
if (mem_size <= ((dev->regs[SCAT_VERSION] & 0x0f) > 3 ? 2048 : 4096) && (((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) < 8) || dev->external_is_RAS)) {
|
|
nbanks_2048k = 0;
|
|
nbanks_512k = mem_size >> 9;
|
|
} else {
|
|
nbanks_2048k = mem_size >> 11;
|
|
nbanks_512k = (mem_size & 1536) >> 9;
|
|
}
|
|
|
|
if (nbank < nbanks_2048k || (nbanks_2048k > 0 && nbank >= nbanks_2048k + nbanks_512k + ((mem_size & 511) >> 7))) {
|
|
addr &= 0x1fffff;
|
|
addr |= (nbank << 21);
|
|
} else if (nbank < nbanks_2048k + nbanks_512k || nbank >= nbanks_2048k + nbanks_512k + ((mem_size & 511) >> 7)) {
|
|
addr &= 0x7ffff;
|
|
addr |= (nbanks_2048k << 21) | ((nbank - nbanks_2048k) << 19);
|
|
} else {
|
|
addr &= 0x1ffff;
|
|
addr |= (nbanks_2048k << 21) | (nbanks_512k << 19) | ((nbank - nbanks_2048k - nbanks_512k) << 17);
|
|
}
|
|
}
|
|
} else {
|
|
switch (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) {
|
|
case 0x02:
|
|
case 0x04:
|
|
nbank = addr >> 19;
|
|
if ((nbank & (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80 ? 7 : 3)) < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else
|
|
addr2 = addr >> 10;
|
|
break;
|
|
|
|
case 0x03:
|
|
nbank = addr >> 19;
|
|
if ((nbank & (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80 ? 7 : 3)) < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else if ((nbank & (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80 ? 7 : 3)) == 2 && (addr & 0x7ffff) < 0x60000) {
|
|
addr ^= 0x1f0000;
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else
|
|
addr2 = addr >> 10;
|
|
break;
|
|
|
|
case 0x05:
|
|
nbank = addr >> 19;
|
|
if ((nbank & (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80 ? 7 : 3)) < 4) {
|
|
nbank = (addr >> 10) & 3;
|
|
addr2 = addr >> 12;
|
|
} else
|
|
addr2 = addr >> 10;
|
|
break;
|
|
|
|
case 0x06:
|
|
nbank = addr >> 19;
|
|
if (nbank < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else {
|
|
nbank = 2 + ((addr - 0x100000) >> 21);
|
|
addr2 = (addr - 0x100000) >> 11;
|
|
}
|
|
break;
|
|
|
|
case 0x07:
|
|
case 0x0f:
|
|
nbank = addr >> 19;
|
|
if (nbank < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else if (nbank < 10) {
|
|
nbank = 2 + (((addr - 0x100000) >> 11) & 1);
|
|
addr2 = (addr - 0x100000) >> 12;
|
|
} else {
|
|
nbank = 4 + ((addr - 0x500000) >> 21);
|
|
addr2 = (addr - 0x500000) >> 11;
|
|
}
|
|
break;
|
|
|
|
case 0x08:
|
|
nbank = addr >> 19;
|
|
if (nbank < 4) {
|
|
nbank = 1;
|
|
addr2 = addr >> 11;
|
|
} else if (nbank == 4) {
|
|
nbank = 0;
|
|
addr2 = addr >> 10;
|
|
} else {
|
|
nbank -= 3;
|
|
addr2 = addr >> 10;
|
|
}
|
|
break;
|
|
|
|
case 0x09:
|
|
nbank = addr >> 19;
|
|
if (nbank < 8) {
|
|
nbank = 1 + ((addr >> 11) & 1);
|
|
addr2 = addr >> 12;
|
|
} else if (nbank == 8) {
|
|
nbank = 0;
|
|
addr2 = addr >> 10;
|
|
} else {
|
|
nbank -= 6;
|
|
addr2 = addr >> 10;
|
|
}
|
|
break;
|
|
|
|
case 0x0a:
|
|
nbank = addr >> 19;
|
|
if (nbank < 8) {
|
|
nbank = 1 + ((addr >> 11) & 1);
|
|
addr2 = addr >> 12;
|
|
} else if (nbank < 12) {
|
|
nbank = 3;
|
|
addr2 = addr >> 11;
|
|
} else if (nbank == 12) {
|
|
nbank = 0;
|
|
addr2 = addr >> 10;
|
|
} else {
|
|
nbank -= 9;
|
|
addr2 = addr >> 10;
|
|
}
|
|
break;
|
|
|
|
case 0x0b:
|
|
nbank = addr >> 21;
|
|
addr2 = addr >> 11;
|
|
break;
|
|
|
|
case 0x0c:
|
|
case 0x0d:
|
|
nbank = addr >> 21;
|
|
if ((nbank & (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80 ? 7 : 3)) < 2) {
|
|
nbank = (addr >> 11) & 1;
|
|
addr2 = addr >> 12;
|
|
} else
|
|
addr2 = addr >> 11;
|
|
break;
|
|
|
|
case 0x0e:
|
|
case 0x13:
|
|
nbank = addr >> 21;
|
|
if ((nbank & (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80 ? 7 : 3)) < 4) {
|
|
nbank = (addr >> 11) & 3;
|
|
addr2 = addr >> 13;
|
|
} else
|
|
addr2 = addr >> 11;
|
|
break;
|
|
|
|
case 0x10:
|
|
case 0x11:
|
|
nbank = addr >> 19;
|
|
if (nbank < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else if (nbank < 10) {
|
|
nbank = 2 + (((addr - 0x100000) >> 11) & 1);
|
|
addr2 = (addr - 0x100000) >> 12;
|
|
} else if (nbank < 18) {
|
|
nbank = 4 + (((addr - 0x500000) >> 11) & 1);
|
|
addr2 = (addr - 0x500000) >> 12;
|
|
} else {
|
|
nbank = 6 + ((addr - 0x900000) >> 21);
|
|
addr2 = (addr - 0x900000) >> 11;
|
|
}
|
|
break;
|
|
|
|
case 0x12:
|
|
nbank = addr >> 19;
|
|
if (nbank < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else if (nbank < 10) {
|
|
nbank = 2 + (((addr - 0x100000) >> 11) & 1);
|
|
addr2 = (addr - 0x100000) >> 12;
|
|
} else {
|
|
nbank = 4 + (((addr - 0x500000) >> 11) & 3);
|
|
addr2 = (addr - 0x500000) >> 13;
|
|
}
|
|
break;
|
|
|
|
case 0x14:
|
|
case 0x15:
|
|
nbank = addr >> 21;
|
|
if ((nbank & 7) < 4) {
|
|
nbank = (addr >> 11) & 3;
|
|
addr2 = addr >> 13;
|
|
} else if ((nbank & 7) < 6) {
|
|
nbank = 4 + (((addr - 0x800000) >> 11) & 1);
|
|
addr2 = (addr - 0x800000) >> 12;
|
|
} else {
|
|
nbank = 6 + (((addr - 0xc00000) >> 11) & 3);
|
|
addr2 = (addr - 0xc00000) >> 13;
|
|
}
|
|
break;
|
|
|
|
case 0x16:
|
|
nbank = ((addr >> 21) & 4) | ((addr >> 11) & 3);
|
|
addr2 = addr >> 13;
|
|
break;
|
|
|
|
case 0x17:
|
|
if (dev->external_is_RAS && (addr & 0x800) == 0)
|
|
return 0xffffffff;
|
|
nbank = addr >> 19;
|
|
if (nbank < 2) {
|
|
nbank = (addr >> 10) & 1;
|
|
addr2 = addr >> 11;
|
|
} else {
|
|
nbank = 2 + ((addr - 0x100000) >> 23);
|
|
addr2 = (addr - 0x100000) >> 12;
|
|
}
|
|
break;
|
|
|
|
case 0x18:
|
|
if (dev->external_is_RAS && (addr & 0x800) == 0)
|
|
return 0xffffffff;
|
|
nbank = addr >> 21;
|
|
if (nbank < 4) {
|
|
nbank = 1;
|
|
addr2 = addr >> 12;
|
|
} else if (nbank == 4) {
|
|
nbank = 0;
|
|
addr2 = addr >> 11;
|
|
} else {
|
|
nbank -= 3;
|
|
addr2 = addr >> 11;
|
|
}
|
|
break;
|
|
|
|
case 0x19:
|
|
if (dev->external_is_RAS && (addr & 0x800) == 0)
|
|
return 0xffffffff;
|
|
nbank = addr >> 23;
|
|
if ((nbank & 3) < 2) {
|
|
nbank = (addr >> 12) & 1;
|
|
addr2 = addr >> 13;
|
|
} else
|
|
addr2 = addr >> 12;
|
|
break;
|
|
|
|
default:
|
|
if ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) < 6) {
|
|
nbank = addr >> 19;
|
|
addr2 = (addr >> 10) & 0x1ff;
|
|
} else if ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) < 0x17) {
|
|
nbank = addr >> 21;
|
|
addr2 = (addr >> 11) & 0x3ff;
|
|
} else {
|
|
nbank = addr >> 23;
|
|
addr2 = (addr >> 12) & 0x7ff;
|
|
}
|
|
break;
|
|
}
|
|
|
|
nbank &= (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80) ? 7 : 3;
|
|
|
|
if ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) > 0x16 && nbank == 3)
|
|
return 0xffffffff;
|
|
|
|
if (dev->external_is_RAS && (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80) == 0) {
|
|
if (nbank == 3)
|
|
nbank = 7;
|
|
else
|
|
return 0xffffffff;
|
|
} else if (!dev->external_is_RAS && dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x80) {
|
|
switch (nbank) {
|
|
case 7:
|
|
nbank = 3;
|
|
break;
|
|
|
|
/* Note - In the following cases, the chipset accesses multiple memory banks
|
|
at the same time, so it's impossible to predict which memory bank
|
|
is actually accessed. */
|
|
case 5:
|
|
case 1:
|
|
nbank = 1;
|
|
break;
|
|
|
|
case 3:
|
|
nbank = 2;
|
|
break;
|
|
|
|
default:
|
|
nbank = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (mem_size & ~511) {
|
|
case 1024:
|
|
case 1536:
|
|
addr &= 0x3ff;
|
|
if (nbank < 2)
|
|
addr |= (nbank << 10) | ((addr2 & 0x1ff) << 11);
|
|
else
|
|
addr |= ((addr2 & 0x1ff) << 10) | (nbank << 19);
|
|
break;
|
|
|
|
case 2048:
|
|
if ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) == 5) {
|
|
addr &= 0x3ff;
|
|
if (nbank < 4)
|
|
addr |= (nbank << 10) | ((addr2 & 0x1ff) << 12);
|
|
else
|
|
addr |= ((addr2 & 0x1ff) << 10) | (nbank << 19);
|
|
} else {
|
|
addr &= 0x7ff;
|
|
addr |= ((addr2 & 0x3ff) << 11) | (nbank << 21);
|
|
}
|
|
break;
|
|
|
|
case 2560:
|
|
if (nbank == 0)
|
|
addr = (addr & 0x3ff) | ((addr2 & 0x1ff) << 10);
|
|
else {
|
|
addr &= 0x7ff;
|
|
addr2 &= 0x3ff;
|
|
addr = addr + 0x80000 + ((addr2 << 11) | ((nbank - 1) << 21));
|
|
}
|
|
break;
|
|
|
|
case 3072:
|
|
if (nbank < 2)
|
|
addr = (addr & 0x3ff) | (nbank << 10) | ((addr2 & 0x1ff) << 11);
|
|
else
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 11) | ((nbank - 2) << 21));
|
|
break;
|
|
|
|
case 4096:
|
|
case 6144:
|
|
addr &= 0x7ff;
|
|
if (nbank < 2)
|
|
addr |= (nbank << 11) | ((addr2 & 0x3ff) << 12);
|
|
else
|
|
addr |= ((addr2 & 0x3ff) << 11) | (nbank << 21);
|
|
break;
|
|
|
|
case 4608:
|
|
if (((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) >= 8 && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) <= 0x0a) || ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) == 0x18)) {
|
|
if (nbank == 0)
|
|
addr = (addr & 0x3ff) | ((addr2 & 0x1ff) << 10);
|
|
else if (nbank < 3)
|
|
addr = 0x80000 + ((addr & 0x7ff) | ((nbank - 1) << 11) | ((addr2 & 0x3ff) << 12));
|
|
else
|
|
addr = 0x480000 + ((addr & 0x3ff) | ((addr2 & 0x1ff) << 10) | ((nbank - 3) << 19));
|
|
} else if (nbank == 0)
|
|
addr = (addr & 0x3ff) | ((addr2 & 0x1ff) << 10);
|
|
else {
|
|
addr &= 0x7ff;
|
|
addr2 &= 0x3ff;
|
|
addr = addr + 0x80000 + ((addr2 << 11) | ((nbank - 1) << 21));
|
|
}
|
|
break;
|
|
|
|
case 5120:
|
|
case 7168:
|
|
if (nbank < 2)
|
|
addr = (addr & 0x3ff) | (nbank << 10) | ((addr2 & 0x1ff) << 11);
|
|
else if (nbank < 4)
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 12) | ((nbank & 1) << 11));
|
|
else
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 11) | ((nbank - 2) << 21));
|
|
break;
|
|
|
|
case 6656:
|
|
if (((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) >= 8 && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) <= 0x0a) || ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) == 0x18)) {
|
|
if (nbank == 0)
|
|
addr = (addr & 0x3ff) | ((addr2 & 0x1ff) << 10);
|
|
else if (nbank < 3)
|
|
addr = 0x80000 + ((addr & 0x7ff) | ((nbank - 1) << 11) | ((addr2 & 0x3ff) << 12));
|
|
else if (nbank == 3)
|
|
addr = 0x480000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 11));
|
|
else
|
|
addr = 0x680000 + ((addr & 0x3ff) | ((addr2 & 0x1ff) << 10) | ((nbank - 4) << 19));
|
|
} else if (nbank == 0)
|
|
addr = (addr & 0x3ff) | ((addr2 & 0x1ff) << 10);
|
|
else if (nbank == 1) {
|
|
addr &= 0x7ff;
|
|
addr2 &= 0x3ff;
|
|
addr = addr + 0x80000 + (addr2 << 11);
|
|
} else {
|
|
addr &= 0x7ff;
|
|
addr2 &= 0x3ff;
|
|
addr = addr + 0x280000 + ((addr2 << 12) | ((nbank & 1) << 11) | (((nbank - 2) & 6) << 21));
|
|
}
|
|
break;
|
|
|
|
case 8192:
|
|
addr &= 0x7ff;
|
|
if (nbank < 4)
|
|
addr |= (nbank << 11) | ((addr2 & 0x3ff) << 13);
|
|
else
|
|
addr |= ((addr2 & 0x3ff) << 11) | (nbank << 21);
|
|
break;
|
|
|
|
case 9216:
|
|
if (nbank < 2)
|
|
addr = (addr & 0x3ff) | (nbank << 10) | ((addr2 & 0x1ff) << 11);
|
|
else if (dev->external_is_RAS) {
|
|
if (nbank < 6)
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 12) | ((nbank & 1) << 11));
|
|
else
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 11) | ((nbank - 2) << 21));
|
|
} else
|
|
addr = 0x100000 + ((addr & 0xfff) | ((addr2 & 0x7ff) << 12) | ((nbank - 2) << 23));
|
|
break;
|
|
|
|
case 10240:
|
|
if (dev->external_is_RAS) {
|
|
addr &= 0x7ff;
|
|
if (nbank < 4)
|
|
addr |= (nbank << 11) | ((addr2 & 0x3ff) << 13);
|
|
else
|
|
addr |= ((addr2 & 0x3ff) << 11) | (nbank << 21);
|
|
} else if (nbank == 0)
|
|
addr = (addr & 0x7ff) | ((addr2 & 0x3ff) << 11);
|
|
else {
|
|
addr &= 0xfff;
|
|
addr2 &= 0x7ff;
|
|
addr = addr + 0x200000 + ((addr2 << 12) | ((nbank - 1) << 23));
|
|
}
|
|
break;
|
|
|
|
case 11264:
|
|
if (nbank < 2)
|
|
addr = (addr & 0x3ff) | (nbank << 10) | ((addr2 & 0x1ff) << 11);
|
|
else if (nbank < 6)
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 12) | ((nbank & 1) << 11));
|
|
else
|
|
addr = 0x100000 + ((addr & 0x7ff) | ((addr2 & 0x3ff) << 11) | ((nbank - 2) << 21));
|
|
break;
|
|
|
|
case 12288:
|
|
if (dev->external_is_RAS) {
|
|
addr &= 0x7ff;
|
|
if (nbank < 4)
|
|
addr |= (nbank << 11) | ((addr2 & 0x3ff) << 13);
|
|
else if (nbank < 6)
|
|
addr |= ((nbank & 1) << 11) | ((addr2 & 0x3ff) << 12) | ((nbank & 4) << 21);
|
|
else
|
|
addr |= ((addr2 & 0x3ff) << 11) | (nbank << 21);
|
|
} else {
|
|
if (nbank < 2)
|
|
addr = (addr & 0x7ff) | (nbank << 11) | ((addr2 & 0x3ff) << 12);
|
|
else
|
|
addr = 0x400000 + ((addr & 0xfff) | ((addr2 & 0x7ff) << 12) | ((nbank - 2) << 23));
|
|
}
|
|
break;
|
|
|
|
case 13312:
|
|
if (nbank < 2)
|
|
addr = (addr & 0x3FF) | (nbank << 10) | ((addr2 & 0x1FF) << 11);
|
|
else if (nbank < 4)
|
|
addr = 0x100000 + ((addr & 0x7FF) | ((addr2 & 0x3FF) << 12) | ((nbank & 1) << 11));
|
|
else
|
|
addr = 0x500000 + ((addr & 0x7FF) | ((addr2 & 0x3FF) << 13) | ((nbank & 3) << 11));
|
|
break;
|
|
|
|
case 14336:
|
|
addr &= 0x7ff;
|
|
if (nbank < 4)
|
|
addr |= (nbank << 11) | ((addr2 & 0x3ff) << 13);
|
|
else if (nbank < 6)
|
|
addr |= ((nbank & 1) << 11) | ((addr2 & 0x3ff) << 12) | ((nbank & 4) << 21);
|
|
else
|
|
addr |= ((addr2 & 0x3ff) << 11) | (nbank << 21);
|
|
break;
|
|
|
|
case 16384:
|
|
if (dev->external_is_RAS) {
|
|
addr &= 0x7ff;
|
|
addr2 &= 0x3ff;
|
|
addr |= ((nbank & 3) << 11) | (addr2 << 13) | ((nbank & 4) << 21);
|
|
} else {
|
|
addr &= 0xfff;
|
|
addr2 &= 0x7ff;
|
|
if (nbank < 2)
|
|
addr |= (addr2 << 13) | (nbank << 12);
|
|
else
|
|
addr |= (addr2 << 12) | (nbank << 23);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (mem_size < 2048 || ((mem_size & 1536) == 512) || (mem_size == 2048 && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) < 6)) {
|
|
addr &= 0x3ff;
|
|
addr2 &= 0x1ff;
|
|
addr |= (addr2 << 10) | (nbank << 19);
|
|
} else if (mem_size < 8192 || (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) < 0x17) {
|
|
addr &= 0x7ff;
|
|
addr2 &= 0x3ff;
|
|
addr |= (addr2 << 11) | (nbank << 21);
|
|
} else {
|
|
addr &= 0xfff;
|
|
addr2 &= 0x7ff;
|
|
addr |= (addr2 << 12) | (nbank << 23);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
static void
|
|
set_global_EMS_state(scat_t *dev, int state)
|
|
{
|
|
uint32_t base_addr;
|
|
uint32_t virt_addr;
|
|
int conf;
|
|
|
|
for (uint32_t i = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0 : 24; i < 32; i++) {
|
|
base_addr = (i + 16) << 14;
|
|
|
|
if (i >= 24)
|
|
base_addr += 0x30000;
|
|
if (state && (dev->page[i].regs_2x9 & 0x80)) {
|
|
virt_addr = get_addr(dev, base_addr, &dev->page[i]);
|
|
if (i < 24)
|
|
mem_mapping_disable(&dev->efff_mapping[i]);
|
|
else
|
|
mem_mapping_disable(&dev->efff_mapping[i + 12]);
|
|
mem_mapping_enable(&dev->ems_mapping[i]);
|
|
|
|
if (virt_addr < ((uint32_t) mem_size << 10))
|
|
mem_mapping_set_exec(&dev->ems_mapping[i], ram + virt_addr);
|
|
else
|
|
mem_mapping_set_exec(&dev->ems_mapping[i], NULL);
|
|
} else {
|
|
mem_mapping_set_exec(&dev->ems_mapping[i], ram + base_addr);
|
|
mem_mapping_disable(&dev->ems_mapping[i]);
|
|
|
|
conf = (dev->regs[SCAT_VERSION] & 0xf0) ? (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f)
|
|
: (dev->regs[SCAT_DRAM_CONFIGURATION] & 0xf) | ((dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x40) >> 2);
|
|
if (i < 24) {
|
|
if (conf > 1 || (conf == 1 && i < 16))
|
|
mem_mapping_enable(&dev->efff_mapping[i]);
|
|
else
|
|
mem_mapping_disable(&dev->efff_mapping[i]);
|
|
} else if (conf > 3 || ((dev->regs[SCAT_VERSION] & 0xf0) != 0 && conf == 2))
|
|
mem_mapping_enable(&dev->efff_mapping[i + 12]);
|
|
else
|
|
mem_mapping_disable(&dev->efff_mapping[i + 12]);
|
|
}
|
|
}
|
|
|
|
flushmmucache();
|
|
}
|
|
|
|
static void
|
|
memmap_state_update(scat_t *dev)
|
|
{
|
|
uint32_t addr;
|
|
|
|
for (uint8_t i = (((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0 : 16); i < 44; i++) {
|
|
addr = get_addr(dev, 0x40000 + (i << 14), &dev->null_page);
|
|
mem_mapping_set_exec(&dev->efff_mapping[i],
|
|
addr < ((uint32_t) mem_size << 10) ? ram + addr : NULL);
|
|
}
|
|
|
|
addr = get_addr(dev, 0, &dev->null_page);
|
|
mem_mapping_set_exec(&dev->low_mapping[0],
|
|
addr < ((uint32_t) mem_size << 10) ? ram + addr : NULL);
|
|
|
|
addr = get_addr(dev, 0xf0000, &dev->null_page);
|
|
mem_mapping_set_exec(&dev->low_mapping[1],
|
|
addr < ((uint32_t) mem_size << 10) ? ram + addr : NULL);
|
|
|
|
for (uint8_t i = 2; i < 32; i++) {
|
|
addr = get_addr(dev, i << 19, &dev->null_page);
|
|
mem_mapping_set_exec(&dev->low_mapping[i],
|
|
addr < ((uint32_t) mem_size << 10) ? ram + addr : NULL);
|
|
}
|
|
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0) {
|
|
uint8_t j = 0;
|
|
|
|
for (j = 0; j < max_map[(dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) | ((dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x40) >> 2)]; j++)
|
|
mem_mapping_enable(&dev->low_mapping[j]);
|
|
|
|
for (; j < 32; j++)
|
|
mem_mapping_disable(&dev->low_mapping[j]);
|
|
|
|
for (j = 24; j < 36; j++) {
|
|
if (((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) | (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x40)) < 4)
|
|
mem_mapping_disable(&dev->efff_mapping[j]);
|
|
else
|
|
mem_mapping_enable(&dev->efff_mapping[j]);
|
|
}
|
|
} else {
|
|
uint8_t j = 0;
|
|
for (j = 0; j < max_map_sx[dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f]; j++)
|
|
mem_mapping_enable(&dev->low_mapping[j]);
|
|
|
|
for (; j < 32; j++)
|
|
mem_mapping_disable(&dev->low_mapping[j]);
|
|
|
|
for (j = 24; j < 36; j++) {
|
|
if ((dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) < 2 || (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) == 3)
|
|
mem_mapping_disable(&dev->efff_mapping[j]);
|
|
else
|
|
mem_mapping_enable(&dev->efff_mapping[j]);
|
|
}
|
|
}
|
|
|
|
if ((((dev->regs[SCAT_VERSION] & 0xf0) == 0) && (dev->regs[SCAT_EXTENDED_BOUNDARY] & 0x40) == 0) || ((dev->regs[SCAT_VERSION] & 0xf0) != 0)) {
|
|
if ((((dev->regs[SCAT_VERSION] & 0xf0) == 0) && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) == 3) || (((dev->regs[SCAT_VERSION] & 0xf0) != 0) && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) == 3)) {
|
|
mem_mapping_disable(&dev->low_mapping[2]);
|
|
|
|
for (uint8_t i = 0; i < 6; i++) {
|
|
addr = get_addr(dev, 0x100000 + (i << 16), &dev->null_page);
|
|
mem_mapping_set_exec(&dev->remap_mapping[i],
|
|
addr < ((uint32_t) mem_size << 10) ? ram + addr : NULL);
|
|
mem_mapping_enable(&dev->remap_mapping[i]);
|
|
}
|
|
} else {
|
|
for (uint8_t i = 0; i < 6; i++)
|
|
mem_mapping_disable(&dev->remap_mapping[i]);
|
|
|
|
if ((((dev->regs[SCAT_VERSION] & 0xf0) == 0) && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x0f) > 4) || (((dev->regs[SCAT_VERSION] & 0xf0) != 0) && (dev->regs[SCAT_DRAM_CONFIGURATION] & 0x1f) > 3))
|
|
mem_mapping_enable(&dev->low_mapping[2]);
|
|
}
|
|
} else {
|
|
for (uint8_t i = 0; i < 6; i++)
|
|
mem_mapping_disable(&dev->remap_mapping[i]);
|
|
|
|
mem_mapping_enable(&dev->low_mapping[2]);
|
|
}
|
|
|
|
set_global_EMS_state(dev, dev->regs[SCAT_EMS_CONTROL] & 0x80);
|
|
|
|
flushmmucache_nopc();
|
|
}
|
|
|
|
static void
|
|
scat_out(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
scat_t *dev = (scat_t *) priv;
|
|
uint8_t reg_valid = 0;
|
|
uint8_t shadow_update = 0;
|
|
uint8_t map_update = 0;
|
|
uint8_t indx;
|
|
uint32_t base_addr;
|
|
uint32_t virt_addr;
|
|
|
|
switch (port) {
|
|
case 0x22:
|
|
dev->indx = val;
|
|
break;
|
|
|
|
case 0x23:
|
|
switch (dev->indx) {
|
|
case SCAT_DMA_WAIT_STATE_CONTROL:
|
|
case SCAT_CLOCK_CONTROL:
|
|
case SCAT_PERIPHERAL_CONTROL:
|
|
reg_valid = 1;
|
|
break;
|
|
|
|
case SCAT_EMS_CONTROL:
|
|
io_removehandler(0x0208, 0x0003, scat_in, NULL, NULL, scat_out, NULL, NULL, dev);
|
|
io_removehandler(0x0218, 0x0003, scat_in, NULL, NULL, scat_out, NULL, NULL, dev);
|
|
|
|
if (val & 0x40) {
|
|
if (val & 1)
|
|
io_sethandler(0x0218, 3, scat_in, NULL, NULL, scat_out, NULL, NULL, dev);
|
|
else
|
|
io_sethandler(0x0208, 3, scat_in, NULL, NULL, scat_out, NULL, NULL, dev);
|
|
}
|
|
set_global_EMS_state(dev, val & 0x80);
|
|
reg_valid = 1;
|
|
break;
|
|
|
|
case SCAT_POWER_MANAGEMENT:
|
|
/* TODO - Only use AUX parity disable bit for this version.
|
|
Other bits should be implemented later. */
|
|
val &= (dev->regs[SCAT_VERSION] & 0xf0) == 0 ? 0x40 : 0x60;
|
|
reg_valid = 1;
|
|
break;
|
|
|
|
case SCAT_DRAM_CONFIGURATION:
|
|
map_update = 1;
|
|
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0) {
|
|
cpu_waitstates = (val & 0x70) == 0 ? 1 : 2;
|
|
cpu_update_waitstates();
|
|
}
|
|
|
|
reg_valid = 1;
|
|
break;
|
|
|
|
case SCAT_EXTENDED_BOUNDARY:
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0) {
|
|
if (dev->regs[SCAT_VERSION] < 4) {
|
|
val &= 0xbf;
|
|
set_xms_bound(dev, val & 0x0f);
|
|
} else {
|
|
val = (val & 0x7f) | 0x80;
|
|
set_xms_bound(dev, val & 0x4f);
|
|
}
|
|
} else
|
|
set_xms_bound(dev, val & 0x1f);
|
|
|
|
mem_set_mem_state(0x40000, 0x60000, (val & 0x20) ? MEM_READ_EXTANY | MEM_WRITE_EXTANY : MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
|
|
if ((val ^ dev->regs[SCAT_EXTENDED_BOUNDARY]) & 0xc0)
|
|
map_update = 1;
|
|
reg_valid = 1;
|
|
break;
|
|
|
|
case SCAT_ROM_ENABLE:
|
|
case SCAT_RAM_WRITE_PROTECT:
|
|
case SCAT_SHADOW_RAM_ENABLE_1:
|
|
case SCAT_SHADOW_RAM_ENABLE_2:
|
|
case SCAT_SHADOW_RAM_ENABLE_3:
|
|
reg_valid = 1;
|
|
shadow_update = 1;
|
|
break;
|
|
|
|
case SCATSX_LAPTOP_FEATURES:
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) != 0) {
|
|
val = (val & ~8) | (dev->regs[SCATSX_LAPTOP_FEATURES] & 8);
|
|
reg_valid = 1;
|
|
}
|
|
break;
|
|
|
|
case SCATSX_FAST_VIDEO_CONTROL:
|
|
case SCATSX_FAST_VIDEORAM_ENABLE:
|
|
case SCATSX_HIGH_PERFORMANCE_REFRESH:
|
|
case SCATSX_CAS_TIMING_FOR_DMA:
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) != 0)
|
|
reg_valid = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (reg_valid)
|
|
dev->regs[dev->indx] = val;
|
|
|
|
if (shadow_update)
|
|
shadow_state_update(dev);
|
|
|
|
if (map_update)
|
|
memmap_state_update(dev);
|
|
break;
|
|
|
|
case 0x208:
|
|
case 0x218:
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x41) == (0x40 | ((port & 0x10) >> 4))) {
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0)
|
|
indx = dev->reg_2xA & 0x1f;
|
|
else
|
|
indx = ((dev->reg_2xA & 0x40) >> 4) + (dev->reg_2xA & 0x3) + 24;
|
|
dev->page[indx].regs_2x8 = val;
|
|
base_addr = (indx + 16) << 14;
|
|
if (indx >= 24)
|
|
base_addr += 0x30000;
|
|
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x80) && (dev->page[indx].regs_2x9 & 0x80)) {
|
|
virt_addr = get_addr(dev, base_addr, &dev->page[indx]);
|
|
if (virt_addr < ((uint32_t) mem_size << 10))
|
|
mem_mapping_set_exec(&dev->ems_mapping[indx], ram + virt_addr);
|
|
else
|
|
mem_mapping_set_exec(&dev->ems_mapping[indx], NULL);
|
|
flushmmucache();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x209:
|
|
case 0x219:
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x41) == (0x40 | ((port & 0x10) >> 4))) {
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0)
|
|
indx = dev->reg_2xA & 0x1f;
|
|
else
|
|
indx = ((dev->reg_2xA & 0x40) >> 4) + (dev->reg_2xA & 0x3) + 24;
|
|
dev->page[indx].regs_2x9 = val;
|
|
base_addr = (indx + 16) << 14;
|
|
if (indx >= 24)
|
|
base_addr += 0x30000;
|
|
|
|
if (dev->regs[SCAT_EMS_CONTROL] & 0x80) {
|
|
if (val & 0x80) {
|
|
virt_addr = get_addr(dev, base_addr, &dev->page[indx]);
|
|
if (indx < 24)
|
|
mem_mapping_disable(&dev->efff_mapping[indx]);
|
|
else
|
|
mem_mapping_disable(&dev->efff_mapping[indx + 12]);
|
|
if (virt_addr < ((uint32_t) mem_size << 10))
|
|
mem_mapping_set_exec(&dev->ems_mapping[indx], ram + virt_addr);
|
|
else
|
|
mem_mapping_set_exec(&dev->ems_mapping[indx], NULL);
|
|
mem_mapping_enable(&dev->ems_mapping[indx]);
|
|
} else {
|
|
mem_mapping_set_exec(&dev->ems_mapping[indx], ram + base_addr);
|
|
mem_mapping_disable(&dev->ems_mapping[indx]);
|
|
if (indx < 24)
|
|
mem_mapping_enable(&dev->efff_mapping[indx]);
|
|
else
|
|
mem_mapping_enable(&dev->efff_mapping[indx + 12]);
|
|
}
|
|
|
|
flushmmucache();
|
|
}
|
|
|
|
if (dev->reg_2xA & 0x80)
|
|
dev->reg_2xA = (dev->reg_2xA & 0xe0) | ((dev->reg_2xA + 1) & (((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? 0x1f : 3));
|
|
}
|
|
break;
|
|
|
|
case 0x20a:
|
|
case 0x21a:
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x41) == (0x40 | ((port & 0x10) >> 4)))
|
|
dev->reg_2xA = ((dev->regs[SCAT_VERSION] & 0xf0) == 0) ? val : val & 0xc3;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint8_t
|
|
scat_in(uint16_t port, void *priv)
|
|
{
|
|
const scat_t *dev = (scat_t *) priv;
|
|
uint8_t ret = 0xff;
|
|
uint8_t indx;
|
|
|
|
switch (port) {
|
|
case 0x23:
|
|
switch (dev->indx) {
|
|
case SCAT_MISCELLANEOUS_STATUS:
|
|
ret = (dev->regs[dev->indx] & 0x3f) | (~nmi_mask & 0x80) | ((mem_a20_key & 2) << 5);
|
|
break;
|
|
|
|
case SCAT_DRAM_CONFIGURATION:
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0)
|
|
ret = (dev->regs[dev->indx] & 0x8f) | (cpu_waitstates == 1 ? 0 : 0x10);
|
|
else
|
|
ret = dev->regs[dev->indx];
|
|
break;
|
|
|
|
case SCAT_EXTENDED_BOUNDARY:
|
|
ret = dev->regs[dev->indx];
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0) {
|
|
if ((dev->regs[SCAT_VERSION] & 0x0f) >= 4)
|
|
ret |= 0x80;
|
|
else
|
|
ret &= 0xaf;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (dev->indx <= dev->max_reg)
|
|
ret = dev->regs[dev->indx];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x208:
|
|
case 0x218:
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x41) == (0x40 | ((port & 0x10) >> 4))) {
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0)
|
|
indx = dev->reg_2xA & 0x1f;
|
|
else
|
|
indx = ((dev->reg_2xA & 0x40) >> 4) + (dev->reg_2xA & 0x3) + 24;
|
|
ret = dev->page[indx].regs_2x8;
|
|
}
|
|
break;
|
|
|
|
case 0x209:
|
|
case 0x219:
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x41) == (0x40 | ((port & 0x10) >> 4))) {
|
|
if ((dev->regs[SCAT_VERSION] & 0xf0) == 0)
|
|
indx = dev->reg_2xA & 0x1f;
|
|
else
|
|
indx = ((dev->reg_2xA & 0x40) >> 4) + (dev->reg_2xA & 0x3) + 24;
|
|
ret = dev->page[indx].regs_2x9;
|
|
}
|
|
break;
|
|
|
|
case 0x20a:
|
|
case 0x21a:
|
|
if ((dev->regs[SCAT_EMS_CONTROL] & 0x41) == (0x40 | ((port & 0x10) >> 4)))
|
|
ret = dev->reg_2xA;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t
|
|
mem_read_scatb(uint32_t addr, void *priv)
|
|
{
|
|
ems_page_t *page = (ems_page_t *) priv;
|
|
scat_t *dev = (scat_t *) page->scat;
|
|
uint8_t val = 0xff;
|
|
|
|
addr = get_addr(dev, addr, page);
|
|
if (addr < ((uint32_t) mem_size << 10))
|
|
val = ram[addr];
|
|
|
|
return val;
|
|
}
|
|
|
|
static uint16_t
|
|
mem_read_scatw(uint32_t addr, void *priv)
|
|
{
|
|
ems_page_t *page = (ems_page_t *) priv;
|
|
scat_t *dev = (scat_t *) page->scat;
|
|
uint16_t val = 0xffff;
|
|
|
|
addr = get_addr(dev, addr, page);
|
|
if (addr < ((uint32_t) mem_size << 10))
|
|
val = *(uint16_t *) &ram[addr];
|
|
|
|
return val;
|
|
}
|
|
|
|
static uint32_t
|
|
mem_read_scatl(uint32_t addr, void *priv)
|
|
{
|
|
ems_page_t *page = (ems_page_t *) priv;
|
|
scat_t *dev = (scat_t *) page->scat;
|
|
uint32_t val = 0xffffffff;
|
|
|
|
addr = get_addr(dev, addr, page);
|
|
if (addr < ((uint32_t) mem_size << 10))
|
|
val = *(uint32_t *) &ram[addr];
|
|
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
mem_write_scatb(uint32_t addr, uint8_t val, void *priv)
|
|
{
|
|
ems_page_t *page = (ems_page_t *) priv;
|
|
scat_t *dev = (scat_t *) page->scat;
|
|
uint32_t oldaddr = addr;
|
|
uint32_t chkaddr;
|
|
|
|
addr = get_addr(dev, addr, page);
|
|
chkaddr = page->valid ? addr : oldaddr;
|
|
if ((chkaddr >= 0xc0000) && (chkaddr < 0x100000)) {
|
|
if (dev->regs[SCAT_RAM_WRITE_PROTECT] & (1 << ((chkaddr - 0xc0000) >> 15)))
|
|
return;
|
|
}
|
|
|
|
if (addr < ((uint32_t) mem_size << 10))
|
|
ram[addr] = val;
|
|
}
|
|
|
|
static void
|
|
mem_write_scatw(uint32_t addr, uint16_t val, void *priv)
|
|
{
|
|
ems_page_t *page = (ems_page_t *) priv;
|
|
scat_t *dev = (scat_t *) page->scat;
|
|
uint32_t oldaddr = addr;
|
|
uint32_t chkaddr;
|
|
|
|
addr = get_addr(dev, addr, page);
|
|
chkaddr = page->valid ? addr : oldaddr;
|
|
if ((chkaddr >= 0xc0000) && (chkaddr < 0x100000)) {
|
|
if (dev->regs[SCAT_RAM_WRITE_PROTECT] & (1 << ((chkaddr - 0xc0000) >> 15)))
|
|
return;
|
|
}
|
|
|
|
if (addr < ((uint32_t) mem_size << 10))
|
|
*(uint16_t *) &ram[addr] = val;
|
|
}
|
|
|
|
static void
|
|
mem_write_scatl(uint32_t addr, uint32_t val, void *priv)
|
|
{
|
|
ems_page_t *page = (ems_page_t *) priv;
|
|
scat_t *dev = (scat_t *) page->scat;
|
|
uint32_t oldaddr = addr;
|
|
uint32_t chkaddr;
|
|
|
|
addr = get_addr(dev, addr, page);
|
|
chkaddr = page->valid ? addr : oldaddr;
|
|
if ((chkaddr >= 0xc0000) && (chkaddr < 0x100000)) {
|
|
if (dev->regs[SCAT_RAM_WRITE_PROTECT] & (1 << ((chkaddr - 0xc0000) >> 15)))
|
|
return;
|
|
}
|
|
|
|
if (addr < ((uint32_t) mem_size << 10))
|
|
*(uint32_t *) &ram[addr] = val;
|
|
}
|
|
|
|
static void
|
|
scat_close(void *priv)
|
|
{
|
|
scat_t *dev = (scat_t *) priv;
|
|
|
|
free(dev);
|
|
}
|
|
|
|
static void *
|
|
scat_init(const device_t *info)
|
|
{
|
|
scat_t *dev;
|
|
uint32_t j;
|
|
uint32_t k;
|
|
int sx;
|
|
|
|
dev = (scat_t *) malloc(sizeof(scat_t));
|
|
memset(dev, 0x00, sizeof(scat_t));
|
|
dev->type = info->local;
|
|
|
|
sx = (dev->type == 32) ? 1 : 0;
|
|
|
|
dev->max_reg = sx ? 0x64 : 0x4f;
|
|
|
|
for (uint32_t i = 0; i < sizeof(dev->regs); i++)
|
|
dev->regs[i] = 0xff;
|
|
|
|
if (sx) {
|
|
dev->regs[SCAT_VERSION] = 0x13;
|
|
dev->regs[SCAT_CLOCK_CONTROL] = 6;
|
|
dev->regs[SCAT_PERIPHERAL_CONTROL] = 0;
|
|
dev->regs[SCAT_DRAM_CONFIGURATION] = 1;
|
|
dev->regs[SCATSX_LAPTOP_FEATURES] = 0;
|
|
dev->regs[SCATSX_FAST_VIDEO_CONTROL] = 0;
|
|
dev->regs[SCATSX_FAST_VIDEORAM_ENABLE] = 0;
|
|
dev->regs[SCATSX_HIGH_PERFORMANCE_REFRESH] = 8;
|
|
dev->regs[SCATSX_CAS_TIMING_FOR_DMA] = 3;
|
|
} else {
|
|
switch (dev->type) {
|
|
case 4:
|
|
dev->regs[SCAT_VERSION] = 4;
|
|
break;
|
|
|
|
default:
|
|
dev->regs[SCAT_VERSION] = 1;
|
|
break;
|
|
}
|
|
dev->regs[SCAT_CLOCK_CONTROL] = 2;
|
|
dev->regs[SCAT_PERIPHERAL_CONTROL] = 0x80;
|
|
dev->regs[SCAT_DRAM_CONFIGURATION] = cpu_waitstates == 1 ? 2 : 0x12;
|
|
}
|
|
dev->regs[SCAT_DMA_WAIT_STATE_CONTROL] = 0;
|
|
dev->regs[SCAT_MISCELLANEOUS_STATUS] = 0x37;
|
|
dev->regs[SCAT_ROM_ENABLE] = 0xc0;
|
|
dev->regs[SCAT_RAM_WRITE_PROTECT] = 0;
|
|
dev->regs[SCAT_POWER_MANAGEMENT] = 0;
|
|
dev->regs[SCAT_SHADOW_RAM_ENABLE_1] = 0;
|
|
dev->regs[SCAT_SHADOW_RAM_ENABLE_2] = 0;
|
|
dev->regs[SCAT_SHADOW_RAM_ENABLE_3] = 0;
|
|
dev->regs[SCAT_EXTENDED_BOUNDARY] = 0;
|
|
dev->regs[SCAT_EMS_CONTROL] = 0;
|
|
|
|
/* Disable all system mappings, we will override them. */
|
|
mem_mapping_disable(&ram_low_mapping);
|
|
if (!sx)
|
|
mem_mapping_disable(&ram_mid_mapping);
|
|
mem_mapping_disable(&ram_high_mapping);
|
|
|
|
k = sx ? 0x80000 : 0x40000;
|
|
|
|
dev->null_page.valid = 0;
|
|
dev->null_page.regs_2x8 = 0xff;
|
|
dev->null_page.regs_2x9 = 0xff;
|
|
dev->null_page.scat = dev;
|
|
|
|
mem_mapping_add(&dev->low_mapping[0], 0, k,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
ram, MEM_MAPPING_INTERNAL, &dev->null_page);
|
|
|
|
mem_mapping_add(&dev->low_mapping[1], 0xf0000, 0x10000,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
ram + 0xf0000, MEM_MAPPING_INTERNAL, &dev->null_page);
|
|
|
|
for (uint8_t i = 2; i < 32; i++) {
|
|
mem_mapping_add(&dev->low_mapping[i], (i << 19), 0x80000,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
ram + (i << 19), MEM_MAPPING_INTERNAL, &dev->null_page);
|
|
}
|
|
|
|
if (sx) {
|
|
j = 16;
|
|
k = 0x40000;
|
|
} else {
|
|
j = 0;
|
|
k = (dev->regs[SCAT_VERSION] < 4) ? 0x40000 : 0x60000;
|
|
}
|
|
mem_mapping_set_addr(&dev->low_mapping[31], 0xf80000, k);
|
|
|
|
for (; j < 44; j++) {
|
|
mem_mapping_add(&dev->efff_mapping[j], 0x40000 + (j << 14), 0x4000,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
mem_size > (256 + (j << 4)) ? ram + 0x40000 + (j << 14) : NULL,
|
|
MEM_MAPPING_INTERNAL, &dev->null_page);
|
|
|
|
if (sx)
|
|
mem_mapping_enable(&dev->efff_mapping[j]);
|
|
}
|
|
|
|
if (sx) {
|
|
for (uint8_t i = 24; i < 32; i++) {
|
|
dev->page[i].valid = 1;
|
|
dev->page[i].regs_2x8 = 0xff;
|
|
dev->page[i].regs_2x9 = 0x03;
|
|
dev->page[i].scat = dev;
|
|
mem_mapping_add(&dev->ems_mapping[i], (i + 28) << 14, 0x04000,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
ram + ((i + 28) << 14), 0, &dev->page[i]);
|
|
mem_mapping_disable(&dev->ems_mapping[i]);
|
|
}
|
|
} else {
|
|
for (uint8_t i = 0; i < 32; i++) {
|
|
dev->page[i].valid = 1;
|
|
dev->page[i].regs_2x8 = 0xff;
|
|
dev->page[i].regs_2x9 = 0x03;
|
|
dev->page[i].scat = dev;
|
|
mem_mapping_add(&dev->ems_mapping[i], (i + (i >= 24 ? 28 : 16)) << 14, 0x04000,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
ram + ((i + (i >= 24 ? 28 : 16)) << 14),
|
|
0, &dev->page[i]);
|
|
}
|
|
}
|
|
|
|
for (uint8_t i = 0; i < 6; i++) {
|
|
mem_mapping_add(&dev->remap_mapping[i], 0x100000 + (i << 16), 0x10000,
|
|
mem_read_scatb, mem_read_scatw, mem_read_scatl,
|
|
mem_write_scatb, mem_write_scatw, mem_write_scatl,
|
|
mem_size >= 1024 ? ram + get_addr(dev, 0x100000 + (i << 16), &dev->null_page) : NULL,
|
|
MEM_MAPPING_INTERNAL, &dev->null_page);
|
|
}
|
|
|
|
if (sx) {
|
|
dev->external_is_RAS = scatsx_external_is_RAS[mem_size >> 9];
|
|
} else {
|
|
dev->external_is_RAS = (dev->regs[SCAT_VERSION] > 3) || (((mem_size & ~2047) >> 11) + ((mem_size & 1536) >> 9) + ((mem_size & 511) >> 7)) > 4;
|
|
}
|
|
|
|
set_xms_bound(dev, 0);
|
|
memmap_state_update(dev);
|
|
shadow_state_update(dev);
|
|
|
|
io_sethandler(0x0022, 2,
|
|
scat_in, NULL, NULL, scat_out, NULL, NULL, dev);
|
|
|
|
device_add(&port_92_device);
|
|
|
|
return dev;
|
|
}
|
|
|
|
const device_t scat_device = {
|
|
.name = "C&T SCAT (v1)",
|
|
.internal_name = "scat",
|
|
.flags = 0,
|
|
.local = 0,
|
|
.init = scat_init,
|
|
.close = scat_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const device_t scat_4_device = {
|
|
.name = "C&T SCAT (v4)",
|
|
.internal_name = "scat_4",
|
|
.flags = 0,
|
|
.local = 4,
|
|
.init = scat_init,
|
|
.close = scat_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const device_t scat_sx_device = {
|
|
.name = "C&T SCATsx",
|
|
.internal_name = "scat_sx",
|
|
.flags = 0,
|
|
.local = 32,
|
|
.init = scat_init,
|
|
.close = scat_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|