summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/i386
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/i386')
-rw-r--r--src/devices/cpu/i386/cache.h258
-rw-r--r--src/devices/cpu/i386/cpuidmsrs.hxx94
-rw-r--r--src/devices/cpu/i386/i386.cpp236
-rw-r--r--src/devices/cpu/i386/i386.h52
-rw-r--r--src/devices/cpu/i386/i386priv.h32
-rw-r--r--src/devices/cpu/i386/i486ops.hxx8
-rw-r--r--src/devices/cpu/i386/pentops.hxx13
7 files changed, 637 insertions, 56 deletions
diff --git a/src/devices/cpu/i386/cache.h b/src/devices/cpu/i386/cache.h
new file mode 100644
index 00000000000..a04f2cacf2e
--- /dev/null
+++ b/src/devices/cpu/i386/cache.h
@@ -0,0 +1,258 @@
+// license:BSD-3-Clause
+// copyright-holders:Samuele Zannoli
+
+#ifndef MAME_CPU_I386_CACHE_H
+#define MAME_CPU_I386_CACHE_H
+
+#pragma once
+
+/* To test it outside of Mame
+#include <cstdlib>
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+*/
+
+enum {
+ CacheLineBytes16 = 4,
+ CacheLineBytes32 = 5,
+ CacheLineBytes64 = 6,
+ CacheLineBytes128 = 7,
+ CacheLineBytes256 = 8,
+};
+
+enum {
+ CacheDirectMapped = 0,
+ Cache2Way = 1,
+ Cache4Way = 2,
+ Cache8Way = 3,
+ Cache16Way = 4
+};
+
+enum {
+ CacheRead = 0,
+ CacheWrite = 1
+};
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+class cpucache {
+public:
+ // Constructor
+ cpucache();
+ // Reset the cache
+ void reset();
+ // Find the cacheline containing data at address
+ template <int ReadWrite> u8* search(u32 address);
+ // Allocate a cacheline for data at address
+ template <int ReadWrite> bool allocate(u32 address, u8 **data);
+ // Get the address where the cacheline data should be written back to
+ u32 old();
+ // Get the address of the first byte of the cacheline that contains data at address
+ u32 base(u32 address);
+ // Compose the cacheline parameters into an address
+ u32 address(u32 tag, u32 set, u32 offset);
+ // Get the data of the first cacheline marked as dirty
+ u8* first_dirty(u32 &base, bool clean);
+ // Get the data of the next cacheline marked as dirty
+ u8* next_dirty(u32 &base, bool clean);
+
+private:
+ static const int Ways = 1 << WayBits;
+ static const int LineBytes = 1 << LineBits;
+ static const int Sets = 1 << SetBits;
+ static const u32 LineMask = (1 << LineBits) - 1;
+ static const u32 SetMask = ((1 << SetBits) - 1) << LineBits;
+ static const u32 WayMask = (1 << WayBits) - 1;
+ static const int TagShift = LineBits + SetBits;
+
+ struct cacheline {
+ u8 data[LineBytes];
+ bool allocated;
+ bool dirty;
+ u32 tag;
+ u32 debug_address;
+ };
+
+ struct cacheset {
+ cacheline lines[Ways];
+ int nextway;
+ };
+
+ cacheset sets[Sets];
+ u32 writeback_base;
+ int last_set;
+ int last_way;
+};
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+cpucache<TagBits, SetBits, WayBits, LineBits>::cpucache()
+{
+ reset();
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+void cpucache<TagBits, SetBits, WayBits, LineBits>::reset()
+{
+ for (int s = 0; s < Sets; s++)
+ for (int w = 0; w < Ways; w++)
+ {
+ sets[s].nextway = 0;
+ sets[s].lines[w].allocated = false;
+ sets[s].lines[w].dirty = false;
+ sets[s].lines[w].debug_address = 0;
+ }
+ last_set = -1;
+ last_way = -1;
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+template<int ReadWrite>
+u8* cpucache<TagBits, SetBits, WayBits, LineBits>::search(u32 address)
+{
+ const int addresset = (address & SetMask) >> LineBits;
+ const int addrestag = address >> TagShift;
+
+ for (int w = 0; w < Ways; w++)
+ if ((sets[addresset].lines[w].allocated) && (sets[addresset].lines[w].tag == addrestag))
+ {
+ if (ReadWrite != 0)
+ sets[addresset].lines[w].dirty = true;
+ return sets[addresset].lines[w].data;
+ }
+ return nullptr;
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+template<int ReadWrite>
+bool cpucache<TagBits, SetBits, WayBits, LineBits>::allocate(u32 address, u8 **data)
+{
+ const int addresset = (address & SetMask) >> LineBits;
+ const int addrestag = address >> TagShift;
+ const int victimway = sets[addresset].nextway;
+ bool old_allocated, old_dirty;
+ bool ret;
+
+ sets[addresset].nextway = (victimway + 1) & WayMask; // decide wich way will be allocated next
+ old_allocated = sets[addresset].lines[victimway].allocated;
+ old_dirty = sets[addresset].lines[victimway].dirty;
+ writeback_base = (sets[addresset].lines[victimway].tag << TagShift) | (address & SetMask);
+ sets[addresset].lines[victimway].tag = addrestag;
+ sets[addresset].lines[victimway].allocated = true;
+ if (ReadWrite == 0)
+ sets[addresset].lines[victimway].dirty = false; // caller must write back the cacheline if told so
+ else
+ sets[addresset].lines[victimway].dirty = true; // line is allocated to write into it
+ *data = sets[addresset].lines[victimway].data;
+ sets[addresset].lines[victimway].debug_address = address;
+ ret = old_allocated; // ret = old_allocated && old_dirty
+ if (!old_dirty)
+ ret = false;
+ return ret; // true if caller must write back the cacheline
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+u32 cpucache<TagBits, SetBits, WayBits, LineBits>::old()
+{
+ return writeback_base;
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+u32 cpucache<TagBits, SetBits, WayBits, LineBits>::base(u32 address)
+{
+ return address & ~LineMask;
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+u32 cpucache<TagBits, SetBits, WayBits, LineBits>::address(u32 tag, u32 set, u32 offset)
+{
+ return (tag << TagShift) | (set << LineBits) | offset;
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+u8* cpucache<TagBits, SetBits, WayBits, LineBits>::first_dirty(u32 &base, bool clean)
+{
+ for (int s = 0; s < Sets; s++)
+ for (int w = 0; w < Ways; w++)
+ if (sets[s].lines[w].dirty == true)
+ {
+ if (clean)
+ sets[s].lines[w].dirty = false;
+ last_set = s;
+ last_way = w;
+ base = address(sets[s].lines[w].tag, s, 0);
+ return sets[s].lines[w].data;
+ }
+ return nullptr;
+}
+
+template<int TagBits, int SetBits, int WayBits, int LineBits>
+u8* cpucache<TagBits, SetBits, WayBits, LineBits>::next_dirty(u32 &base, bool clean)
+{
+ if (last_set < 0)
+ return nullptr;
+ while (true)
+ {
+ last_way++;
+ if (last_way == Ways)
+ {
+ last_way = 0;
+ last_set++;
+ if (last_set == Sets)
+ {
+ last_set = -1;
+ last_way = -1;
+ return nullptr;
+ }
+ }
+ if (sets[last_set].lines[last_way].dirty == true)
+ {
+ if (clean)
+ sets[last_set].lines[last_way].dirty = false;
+ base = address(sets[last_set].lines[last_way].tag, last_set, 0);
+ return sets[last_set].lines[last_way].data;
+ }
+ }
+}
+
+#endif
+
+/* To test it outside of Mame
+const int memorysize = 256 * 1024;
+u8 memory[memorysize];
+
+void readline(u8 *data, u32 address)
+{
+ for (int n = 0; n < 64; n++)
+ data[n] = memory[address + n];
+}
+
+void writeline(u8 *data, u32 address)
+{
+ for (int n = 0; n < 64; n++)
+ memory[address + n] = data[n];
+}
+
+void cache_tester()
+{
+ cpucache<18, 8, 6, 2> cache;
+ bool r;
+ u8 *data;
+ int address;
+ u8 value;
+
+ for (int n = 0; n < memorysize; n++)
+ memory[n] = 0xaa ^ n;
+ address = std::rand() & (memorysize - 1);
+ r = cache.search(address, &data);
+ if (r == false)
+ {
+ r = cache.allocate(address, &data);
+ if (r == true)
+ writeline(data, cache.base(address));
+ readline(data, cache.base(address));
+ }
+ value = data[address & 63];
+ if (value != memory[address])
+ printf("Error reading address %d\n\r", address);
+}
+*/
diff --git a/src/devices/cpu/i386/cpuidmsrs.hxx b/src/devices/cpu/i386/cpuidmsrs.hxx
index e7abcf27154..842ff85b4b2 100644
--- a/src/devices/cpu/i386/cpuidmsrs.hxx
+++ b/src/devices/cpu/i386/cpuidmsrs.hxx
@@ -1,3 +1,5 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde, Barry Rodewald, Carl, Philip Bennett, Samuele Zannoli
uint64_t pentium_device::opcode_rdmsr(bool &valid_msr)
{
@@ -253,49 +255,85 @@ void athlonxp_device::opcode_cpuid()
uint64_t athlonxp_device::opcode_rdmsr(bool &valid_msr)
{
+ uint64_t ret;
uint32_t offset = REG32(ECX);
+ ret = 0;
switch (offset)
{
case 0x10: // TSC
break;
case 0x1b: // APIC_BASE
break;
+ case 0xfe: // MTRRcap
+ // 7-0 MTRRCapVCnt - Number of variable range MTRRs (8)
+ // 8 MtrrCapFix - Fixed range MTRRs available (1)
+ // 10 MtrrCapWc - Write combining memory type available (1)
+ ret = 0x508;
+ break;
case 0x17b: // MCG_CTL
break;
case 0x200: // MTRRphysBase0-7
- case 0x201: // MTRRphysMask0-7
case 0x202:
- case 0x203:
case 0x204:
- case 0x205:
case 0x206:
- case 0x207:
case 0x208:
- case 0x209:
case 0x20a:
- case 0x20b:
case 0x20c:
- case 0x20d:
case 0x20e:
+ // 7-0 Type - Memory type for this memory range
+ // 39-12 PhyBase27-0 - Base address for this memory range
+ /* Format of type field:
+ Bits 2-0 specify the memory type with the following encoding
+ 0 UC Uncacheable
+ 1 WC Write Combining
+ 4 WT Write Through
+ 5 WP Write Protect
+ 6 WB Write Back
+ 7 UC Uncacheable used only in PAT register
+ Bit 3 WrMem 1 write to memory 0 write to mmio, present only in fixed range MTRRs
+ Bit 4 RdMem 1 read from memory 0 read from mmio, present only in fixed range MTRRs
+ Other bits are unused
+ */
+ break;
+ case 0x201: // MTRRphysMask0-7
+ case 0x203:
+ case 0x205:
+ case 0x207:
+ case 0x209:
+ case 0x20b:
+ case 0x20d:
case 0x20f:
+ // 11 Valid - Memory range active
+ // 39-12 PhyMask27-0 - Address mask
break;
case 0x2ff: // MTRRdefType
+ // 7-0 MtrrDefMemType - Default memory type
+ // 10 MtrrDefTypeFixEn - Enable fixed range MTRRs
+ // 11 MtrrDefTypeEn - Enable MTRRs
break;
case 0x250: // MTRRfix64K_00000
+ // 8 bits for each 64k block starting at address 0
+ ret = m_msr_mtrrfix[0];
break;
case 0x258: // MTRRfix16K_80000
+ // 8 bits for each 16k block starting at address 0x80000
+ ret = m_msr_mtrrfix[1];
break;
case 0x259: // MTRRfix16K_A0000
- break;
- case 0x268: // MTRRfix4K_C0000-F8000
- case 0x269:
- case 0x26a:
- case 0x26b:
- case 0x26c:
- case 0x26d:
- case 0x26e:
- case 0x26f:
+ // 8 bits for each 16k block starting at address 0xa0000
+ ret = m_msr_mtrrfix[2];
+ break;
+ case 0x268: // MTRRfix4K_C0000
+ case 0x269: // MTRRfix4K_C8000
+ case 0x26a: // MTRRfix4K_D0000
+ case 0x26b: // MTRRfix4K_D8000
+ case 0x26c: // MTRRfix4K_E0000
+ case 0x26d: // MTRRfix4K_E8000
+ case 0x26e: // MTRRfix4K_F0000
+ case 0x26f: // MTRRfix4K_F8000
+ // 8 bits for each 4k block
+ ret = m_msr_mtrrfix[3 + offset - 0x268];
break;
case 0x400: // MC0_CTL
break;
@@ -306,15 +344,25 @@ uint64_t athlonxp_device::opcode_rdmsr(bool &valid_msr)
case 0x40c: // MC3_CTL
break;
case 0xC0010010: // SYS_CFG
+ // 20 MtrrVarDramEn - Enable top of memory address and I/O range registers
+ // 19 MtrrFixDramModEn - Enable modification of RdDram and WrDram bits in fixed MTRRs
+ // 18 MtrrFixDramEn - Enable RdDram and WrDram attributes in fixed MTRRs
break;
case 0xC0010015: // HWCR
break;
- case 0xC0010016: // IORRBase
- case 0xC0010017: // IORRMask
+ case 0xC0010016: // IORRBase0-1
case 0xC0010018:
+ // 39-12 Base27-0 - Base address for this memory range
+ // 4 RdDram - Read from DRAM
+ // 3 WrDram - Write to DRAM
+ break;
+ case 0xC0010017: // IORRMask0-1
case 0xC0010019:
+ // 39-12 Mask27-0 - Address mask
+ // 11 V - Register enabled
break;
case 0xC001001A: // TOP_MEM
+ // 39-23 TOM16-0 - Top of Memory, accesses from this address onward are directed to mmio
break;
case 0xC001001D: // TOP_MEM2
break;
@@ -322,7 +370,7 @@ uint64_t athlonxp_device::opcode_rdmsr(bool &valid_msr)
break;
}
valid_msr = true;
- return 0;
+ return ret;
}
void athlonxp_device::opcode_wrmsr(uint64_t data, bool &valid_msr)
@@ -355,10 +403,16 @@ void athlonxp_device::opcode_wrmsr(uint64_t data, bool &valid_msr)
case 0x2ff: // MTRRdefType
break;
case 0x250: // MTRRfix64K_00000
+ m_msr_mtrrfix[0] = data;
+ parse_mtrrfix(data, 0, 64);
break;
case 0x258: // MTRRfix16K_80000
+ m_msr_mtrrfix[1] = data;
+ parse_mtrrfix(data, 0x80000, 16);
break;
case 0x259: // MTRRfix16K_A0000
+ m_msr_mtrrfix[2] = data;
+ parse_mtrrfix(data, 0xa0000, 16);
break;
case 0x268: // MTRRfix4K_C0000-F8000
case 0x269:
@@ -368,6 +422,8 @@ void athlonxp_device::opcode_wrmsr(uint64_t data, bool &valid_msr)
case 0x26d:
case 0x26e:
case 0x26f:
+ m_msr_mtrrfix[3 + offset - 0x268] = data;
+ parse_mtrrfix(data, 0xc0000 + (offset - 0x268) * 0x8000, 4);
break;
case 0x400: // MC0_CTL
break;
diff --git a/src/devices/cpu/i386/i386.cpp b/src/devices/cpu/i386/i386.cpp
index a4408c69175..f372dba1239 100644
--- a/src/devices/cpu/i386/i386.cpp
+++ b/src/devices/cpu/i386/i386.cpp
@@ -3367,15 +3367,10 @@ void i386_device::i386_common_init()
m_program = &space(AS_PROGRAM);
if(m_program->data_width() == 16) {
- auto cache = m_program->cache<1, 0, ENDIANNESS_LITTLE>();
- m_pr8 = [cache](offs_t address) -> u8 { return cache->read_byte(address); };
- m_pr16 = [cache](offs_t address) -> u16 { return cache->read_word(address); };
- m_pr32 = [cache](offs_t address) -> u32 { return cache->read_dword(address); };
+ // for the 386sx
+ macache16 = m_program->cache<1, 0, ENDIANNESS_LITTLE>();
} else {
- auto cache = m_program->cache<2, 0, ENDIANNESS_LITTLE>();
- m_pr8 = [cache](offs_t address) -> u8 { return cache->read_byte(address); };
- m_pr16 = [cache](offs_t address) -> u16 { return cache->read_word(address); };
- m_pr32 = [cache](offs_t address) -> u32 { return cache->read_dword(address); };
+ macache32 = m_program->cache<2, 0, ENDIANNESS_LITTLE>();
}
m_io = &space(AS_IO);
@@ -4726,6 +4721,10 @@ void athlonxp_device::device_reset()
m_cpuid_id2 = ('D' << 24) | ('M' << 16) | ('A' << 8) | 'c'; // cAMD
memset(m_processor_name_string, 0, 48);
strcpy((char *)m_processor_name_string, "AMD Athlon(tm) Processor");
+ for (int n = 0; n < 11; n++)
+ m_msr_mtrrfix[n] = 0;
+ for (int n = 0; n < (1024 / 4); n++)
+ m_memory_ranges_1m[n] = 0; // change the 0 to 6 to test the cache just after reset
m_cpuid_max_input_value_eax = 0x01;
m_cpu_version = REG32(EDX);
@@ -4736,6 +4735,227 @@ void athlonxp_device::device_reset()
CHANGE_PC(m_eip);
}
+void athlonxp_device::parse_mtrrfix(u64 mtrr, offs_t base, int kblock)
+{
+ int nb = kblock / 4;
+ int range = (int)(base >> 12); // base must never be higher than 1 megabyte
+
+ for (int n = 0; n < 8; n++)
+ {
+ uint8_t type = mtrr & 0xff;
+
+ for (int b = 0; b < nb; b++)
+ {
+ m_memory_ranges_1m[range] = type;
+ range++;
+ }
+ mtrr = mtrr >> 8;
+ }
+}
+
+int athlonxp_device::check_cacheable(offs_t address)
+{
+ offs_t block;
+ int disabled;
+
+ disabled = 0;
+ if (m_cr[0] & (1 << 30))
+ disabled = 128;
+ if (address >= 0x100000)
+ return disabled;
+ block = address >> 12;
+ return m_memory_ranges_1m[block] | disabled;
+}
+
+template <class dt, offs_t xorle>
+dt athlonxp_device::opcode_read_cache(offs_t address)
+{
+ int mode = check_cacheable(address);
+ bool nocache = false;
+ u8 *data;
+
+ if ((mode & 7) == 0)
+ nocache = true;
+ if (mode & 1)
+ nocache = true;
+ if (nocache == false)
+ {
+ int offset = (address & 63) ^ xorle;
+ data = cache.search<CacheRead>(address);
+ if (data)
+ return *(dt *)(data + offset);
+ if (!(mode & 128))
+ {
+ bool dirty = cache.allocate<CacheRead>(address, &data);
+ address = cache.base(address);
+ if (dirty)
+ {
+ offs_t old_address = cache.old();
+
+ for (int w = 0; w < 64; w += 4)
+ macache32->write_dword(old_address + w, *(u32 *)(data + w));
+ }
+ for (int r = 0; r < 64; r += 4)
+ *(u32 *)(data + r) = macache32->read_dword(address + r);
+ return *(dt *)(data + offset);
+ }
+ else
+ {
+ if (sizeof(dt) == 1)
+ return macache32->read_byte(address);
+ else if (sizeof(dt) == 2)
+ return macache32->read_word(address);
+ else
+ return macache32->read_dword(address);
+ }
+ }
+ else
+ {
+ if (sizeof(dt) == 1)
+ return macache32->read_byte(address);
+ else if (sizeof(dt) == 2)
+ return macache32->read_word(address);
+ else
+ return macache32->read_dword(address);
+ }
+}
+
+template <class dt, offs_t xorle>
+dt athlonxp_device::program_read_cache(offs_t address)
+{
+ int mode = check_cacheable(address);
+ bool nocache = false;
+ u8 *data;
+
+ if ((mode & 7) == 0)
+ nocache = true;
+ if (mode & 1)
+ nocache = true;
+ if (nocache == false)
+ {
+ int offset = (address & 63) ^ xorle;
+ data = cache.search<CacheRead>(address);
+ if (data)
+ return *(dt *)(data + offset);
+ if (!(mode & 128))
+ {
+ bool dirty = cache.allocate<CacheRead>(address, &data);
+ address = cache.base(address);
+ if (dirty)
+ {
+ offs_t old_address = cache.old();
+
+ for (int w = 0; w < 64; w += 4)
+ m_program->write_dword(old_address + w, *(u32 *)(data + w));
+ }
+ for (int r = 0; r < 64; r += 4)
+ *(u32 *)(data + r) = m_program->read_dword(address + r);
+ return *(dt *)(data + offset);
+ }
+ else
+ {
+ if (sizeof(dt) == 1)
+ return m_program->read_byte(address);
+ else if (sizeof(dt) == 2)
+ return m_program->read_word(address);
+ else
+ return m_program->read_dword(address);
+ }
+ }
+ else
+ {
+ if (sizeof(dt) == 1)
+ return m_program->read_byte(address);
+ else if (sizeof(dt) == 2)
+ return m_program->read_word(address);
+ else
+ return m_program->read_dword(address);
+ }
+}
+
+template <class dt, offs_t xorle>
+void athlonxp_device::program_write_cache(offs_t address, dt data)
+{
+ int mode = check_cacheable(address);
+ bool nocache = false;
+ u8 *dataw;
+
+ if ((mode & 7) == 0)
+ nocache = true;
+ if (mode & 1)
+ nocache = true;
+ if (nocache == false)
+ {
+ int offset = (address & 63) ^ xorle;
+ dataw = cache.search<CacheWrite>(address);
+ if (dataw)
+ {
+ *(dt *)(dataw + offset) = data;
+ return;
+ }
+ if (!(mode & 128))
+ {
+ bool dirty = cache.allocate<CacheWrite>(address, &dataw);
+ address = cache.base(address);
+ if (dirty)
+ {
+ offs_t old_address = cache.old();
+
+ for (int w = 0; w < 64; w += 4)
+ m_program->write_dword(old_address + w, *(u32 *)(dataw + w));
+ }
+ for (int r = 0; r < 64; r += 4)
+ *(u32 *)(dataw + r) = m_program->read_dword(address + r);
+ *(dt *)(dataw + offset) = data;
+ }
+ else
+ {
+ if (sizeof(dt) == 1)
+ m_program->write_byte(address, data);
+ else if (sizeof(dt) == 2)
+ m_program->write_word(address, data);
+ else
+ m_program->write_dword(address, data);
+ }
+ }
+ else
+ {
+ if (sizeof(dt) == 1)
+ m_program->write_byte(address, data);
+ else if (sizeof(dt) == 2)
+ m_program->write_word(address, data);
+ else
+ m_program->write_dword(address, data);
+ }
+}
+
+void athlonxp_device::invalidate_cache(bool writeback)
+{
+ u32 base;
+ u8 *data;
+
+ data = cache.first_dirty(base, true);
+ while (data != nullptr)
+ {
+ if (writeback)
+ for (int w = 0; w < 64; w += 4)
+ m_program->write_dword(base + w, *(u32 *)(data + w));
+ data = cache.next_dirty(base, true);
+ }
+ cache.reset();
+}
+
+void athlonxp_device::opcode_invd()
+{
+ invalidate_cache(false);
+}
+
+void athlonxp_device::opcode_wbinvd()
+{
+ invalidate_cache(true);
+}
+
+
/*****************************************************************************/
/* Intel Pentium 4 */
diff --git a/src/devices/cpu/i386/i386.h b/src/devices/cpu/i386/i386.h
index e7a460488c0..90aab9f0667 100644
--- a/src/devices/cpu/i386/i386.h
+++ b/src/devices/cpu/i386/i386.h
@@ -16,6 +16,7 @@
#include "divtlb.h"
#include "i386dasm.h"
+#include "cache.h"
#define INPUT_LINE_A20 1
#define INPUT_LINE_SMI 2
@@ -71,10 +72,24 @@ protected:
virtual int get_mode() const override;
// routines for opcodes whose operation can vary between cpu models
- // default implementations just log an error message
+ // default implementations usually just log an error message
virtual void opcode_cpuid();
virtual uint64_t opcode_rdmsr(bool &valid_msr);
virtual void opcode_wrmsr(uint64_t data, bool &valid_msr);
+ virtual void opcode_invd() {}
+ virtual void opcode_wbinvd() {}
+
+ // routine to access memory
+ virtual u8 mem_pr8(offs_t address) { return macache32->read_byte(address); }
+ virtual u16 mem_pr16(offs_t address) { return macache32->read_word(address); }
+ virtual u32 mem_pr32(offs_t address) { return macache32->read_dword(address); }
+
+ virtual u8 mem_prd8(offs_t address) { return m_program->read_byte(address); }
+ virtual u16 mem_prd16(offs_t address) { return m_program->read_word(address); }
+ virtual u32 mem_prd32(offs_t address) { return m_program->read_dword(address); }
+ virtual void mem_pwd8(offs_t address, u8 data) { m_program->write_byte(address, data); }
+ virtual void mem_pwd16(offs_t address, u16 data) { m_program->write_word(address, data); }
+ virtual void mem_pwd32(offs_t address, u32 data) { m_program->write_dword(address, data); }
address_space_config m_program_config;
address_space_config m_io_config;
@@ -280,11 +295,10 @@ protected:
uint8_t m_irq_state;
address_space *m_program;
- std::function<u8 (offs_t)> m_pr8;
- std::function<u16 (offs_t)> m_pr16;
- std::function<u32 (offs_t)> m_pr32;
address_space *m_io;
uint32_t m_a20_mask;
+ memory_access_cache<1, 0, ENDIANNESS_LITTLE> *macache16;
+ memory_access_cache<2, 0, ENDIANNESS_LITTLE> *macache32;
int m_cpuid_max_input_value_eax; // Highest CPUID standard function available
uint32_t m_cpuid_id0, m_cpuid_id1, m_cpuid_id2;
@@ -1495,8 +1509,12 @@ class i386sx_device : public i386_device
public:
// construction/destruction
i386sx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
-};
+protected:
+ virtual u8 mem_pr8(offs_t address) override { return macache16->read_byte(address); };
+ virtual u16 mem_pr16(offs_t address) override { return macache16->read_word(address); };
+ virtual u32 mem_pr32(offs_t address) override { return macache16->read_dword(address); };
+};
class i486_device : public i386_device
{
@@ -1614,10 +1632,34 @@ protected:
virtual void opcode_cpuid() override;
virtual uint64_t opcode_rdmsr(bool &valid_msr) override;
virtual void opcode_wrmsr(uint64_t data, bool &valid_msr) override;
+ virtual void opcode_invd() override;
+ virtual void opcode_wbinvd() override;
virtual void device_start() override;
virtual void device_reset() override;
+ virtual u8 mem_pr8(offs_t address) override { return opcode_read_cache<u8, NATIVE_ENDIAN_VALUE_LE_BE(0, 3)>(address); }
+ virtual u16 mem_pr16(offs_t address) override { return opcode_read_cache<u16, NATIVE_ENDIAN_VALUE_LE_BE(0, 2)>(address); }
+ virtual u32 mem_pr32(offs_t address) override { return opcode_read_cache<u32, 0>(address); }
+ virtual u8 mem_prd8(offs_t address) override { return program_read_cache<u8, NATIVE_ENDIAN_VALUE_LE_BE(0, 3)>(address); }
+ virtual u16 mem_prd16(offs_t address) override { return program_read_cache<u16, NATIVE_ENDIAN_VALUE_LE_BE(0, 2)>(address); }
+ virtual u32 mem_prd32(offs_t address) override { return program_read_cache<u32, 0>(address); }
+ virtual void mem_pwd8(offs_t address, u8 data) override { program_write_cache<u8, NATIVE_ENDIAN_VALUE_LE_BE(0, 3)>(address, data); }
+ virtual void mem_pwd16(offs_t address, u16 data) override { program_write_cache<u16, NATIVE_ENDIAN_VALUE_LE_BE(0, 2)>(address, data); }
+ virtual void mem_pwd32(offs_t address, u32 data) override { program_write_cache<u32, 0>(address, data); }
+
+private:
+ void parse_mtrrfix(u64 mtrr, offs_t base, int kblock);
+ int check_cacheable(offs_t address);
+ void invalidate_cache(bool writeback);
+
+ template <class dt, offs_t xorle> dt opcode_read_cache(offs_t address);
+ template <class dt, offs_t xorle> dt program_read_cache(offs_t address);
+ template <class dt, offs_t xorle> void program_write_cache(offs_t address, dt data);
+
uint8_t m_processor_name_string[48];
+ uint64_t m_msr_mtrrfix[11];
+ uint8_t m_memory_ranges_1m[1024 / 4];
+ cpucache<17, 9, Cache2Way, CacheLineBytes64> cache; // 512 sets, 2 ways (cachelines per set), 64 bytes per cacheline
};
diff --git a/src/devices/cpu/i386/i386priv.h b/src/devices/cpu/i386/i386priv.h
index 954e49ee6c7..0343cf74ef1 100644
--- a/src/devices/cpu/i386/i386priv.h
+++ b/src/devices/cpu/i386/i386priv.h
@@ -386,7 +386,7 @@ uint8_t i386_device::FETCH()
if(!translate_address(m_CPL,TRANSLATE_FETCH,&address,&error))
PF_THROW(error);
- value = m_pr8(address & m_a20_mask);
+ value = mem_pr8(address & m_a20_mask);
#ifdef DEBUG_MISSING_OPCODE
m_opcode_bytes[m_opcode_bytes_length] = value;
m_opcode_bytes_length = (m_opcode_bytes_length + 1) & 15;
@@ -407,7 +407,7 @@ uint16_t i386_device::FETCH16()
if(!translate_address(m_CPL,TRANSLATE_FETCH,&address,&error))
PF_THROW(error);
address &= m_a20_mask;
- value = m_pr16(address);
+ value = mem_pr16(address);
m_eip += 2;
m_pc += 2;
}
@@ -428,7 +428,7 @@ uint32_t i386_device::FETCH32()
PF_THROW(error);
address &= m_a20_mask;
- value = m_pr32(address);
+ value = mem_pr32(address);
m_eip += 4;
m_pc += 4;
}
@@ -443,7 +443,7 @@ uint8_t i386_device::READ8(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- return m_program->read_byte(address);
+ return mem_prd8(address);
}
uint16_t i386_device::READ16(uint32_t ea)
{
@@ -458,7 +458,7 @@ uint16_t i386_device::READ16(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- value = m_program->read_word( address );
+ value = mem_prd16( address );
}
return value;
}
@@ -477,7 +477,7 @@ uint32_t i386_device::READ32(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- value = m_program->read_dword( address );
+ value = mem_prd32( address );
}
return value;
}
@@ -501,8 +501,8 @@ uint64_t i386_device::READ64(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- value = (((uint64_t) m_program->read_dword( address+0 )) << 0);
- value |= (((uint64_t) m_program->read_dword( address+4 )) << 32);
+ value = (((uint64_t) mem_prd32( address+0 )) << 0);
+ value |= (((uint64_t) mem_prd32( address+4 )) << 32);
}
return value;
}
@@ -514,7 +514,7 @@ uint8_t i386_device::READ8PL0(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- return m_program->read_byte(address);
+ return mem_prd8(address);
}
uint16_t i386_device::READ16PL0(uint32_t ea)
{
@@ -529,7 +529,7 @@ uint16_t i386_device::READ16PL0(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- value = m_program->read_word( address );
+ value = mem_prd16( address );
}
return value;
}
@@ -549,7 +549,7 @@ uint32_t i386_device::READ32PL0(uint32_t ea)
PF_THROW(error);
address &= m_a20_mask;
- value = m_program->read_dword( address );
+ value = mem_prd32( address );
}
return value;
}
@@ -569,7 +569,7 @@ void i386_device::WRITE8(uint32_t ea, uint8_t value)
PF_THROW(error);
address &= m_a20_mask;
- m_program->write_byte(address, value);
+ mem_pwd8(address, value);
}
void i386_device::WRITE16(uint32_t ea, uint16_t value)
{
@@ -583,7 +583,7 @@ void i386_device::WRITE16(uint32_t ea, uint16_t value)
PF_THROW(error);
address &= m_a20_mask;
- m_program->write_word(address, value);
+ mem_pwd16(address, value);
}
}
void i386_device::WRITE32(uint32_t ea, uint32_t value)
@@ -600,7 +600,7 @@ void i386_device::WRITE32(uint32_t ea, uint32_t value)
PF_THROW(error);
ea &= m_a20_mask;
- m_program->write_dword(address, value);
+ mem_pwd32(address, value);
}
}
@@ -622,8 +622,8 @@ void i386_device::WRITE64(uint32_t ea, uint64_t value)
PF_THROW(error);
ea &= m_a20_mask;
- m_program->write_dword(address+0, value & 0xffffffff);
- m_program->write_dword(address+4, (value >> 32) & 0xffffffff);
+ mem_pwd32(address+0, value & 0xffffffff);
+ mem_pwd32(address+4, (value >> 32) & 0xffffffff);
}
}
diff --git a/src/devices/cpu/i386/i486ops.hxx b/src/devices/cpu/i386/i486ops.hxx
index 77e9ee3dd2d..6946e463796 100644
--- a/src/devices/cpu/i386/i486ops.hxx
+++ b/src/devices/cpu/i386/i486ops.hxx
@@ -44,13 +44,15 @@ void i386_device::i486_cpuid() // Opcode 0x0F A2
void i386_device::i486_invd() // Opcode 0x0f 08
{
- // Nothing to do ?
+ // TODO: manage the cache if present
+ opcode_invd();
CYCLES(CYCLES_INVD);
}
void i386_device::i486_wbinvd() // Opcode 0x0f 09
{
- // Nothing to do ?
+ // TODO: manage the cache if present
+ opcode_wbinvd();
}
void i386_device::i486_cmpxchg_rm8_r8() // Opcode 0x0f b0
@@ -233,6 +235,8 @@ void i386_device::i486_group0F01_16() // Opcode 0x0f 01
ea = GetEA(modrm,1);
}
WRITE16(ea, m_gdtr.limit);
+ // Win32s requires all 32 bits to be stored here, despite various Intel docs
+ // claiming that the upper 8 bits are either zeroed or undefined in 16-bit mode
WRITE32(ea + 2, m_gdtr.base);
CYCLES(CYCLES_SGDT);
break;
diff --git a/src/devices/cpu/i386/pentops.hxx b/src/devices/cpu/i386/pentops.hxx
index d5f6f6ca592..b44ce8c40ed 100644
--- a/src/devices/cpu/i386/pentops.hxx
+++ b/src/devices/cpu/i386/pentops.hxx
@@ -203,6 +203,7 @@ void i386_device::pentium_prefetch_m8() // Opcode 0x0f 18
{
uint8_t modrm = FETCH();
uint32_t ea = GetEA(modrm,0);
+ // TODO: manage the cache if present
CYCLES(1+(ea & 1)); // TODO: correct cycle count
}
@@ -1045,7 +1046,7 @@ void i386_device::pentium_movnti_m16_r16() // Opcode 0f c3
// unsupported by cpu
CYCLES(1); // TODO: correct cycle count
} else {
- // since cache is not implemented
+ // TODO: manage the cache if present
uint32_t ea = GetEA(modrm, 0);
WRITE16(ea,LOAD_RM16(modrm));
CYCLES(1); // TODO: correct cycle count
@@ -1059,7 +1060,7 @@ void i386_device::pentium_movnti_m32_r32() // Opcode 0f c3
// unsupported by cpu
CYCLES(1); // TODO: correct cycle count
} else {
- // since cache is not implemented
+ // TODO: manage the cache if present
uint32_t ea = GetEA(modrm, 0);
WRITE32(ea,LOAD_RM32(modrm));
CYCLES(1); // TODO: correct cycle count
@@ -1116,7 +1117,7 @@ void i386_device::pentium_movntq_m64_r64() // Opcode 0f e7
if( modrm >= 0xc0 ) {
CYCLES(1); // unsupported
} else {
- // since cache is not implemented
+ // TODO: manage the cache if present
uint32_t ea = GetEA(modrm, 0);
WRITEMMX(ea, MMX((modrm >> 3) & 0x7));
CYCLES(1); // TODO: correct cycle count
@@ -3317,7 +3318,7 @@ void i386_device::sse_movntps_m128_r128() // Opcode 0f 2b
// unsupported by cpu
CYCLES(1); // TODO: correct cycle count
} else {
- // since cache is not implemented
+ // TODO: manage the cache if present
uint32_t ea = GetEA(modrm, 0);
WRITEXMM(ea, XMM((modrm >> 3) & 0x7));
CYCLES(1); // TODO: correct cycle count
@@ -5962,7 +5963,7 @@ void i386_device::sse_movntdq_m128_r128() // Opcode 66 0f e7
if( modrm >= 0xc0 ) {
CYCLES(1); // unsupported
} else {
- // since cache is not implemented
+ // TODO: manage the cache if present
uint32_t ea = GetEA(modrm, 0);
WRITEXMM(ea, XMM((modrm >> 3) & 0x7));
CYCLES(1); // TODO: correct cycle count
@@ -6276,7 +6277,7 @@ void i386_device::sse_movntpd_m128_r128() // Opcode 66 0f 2b
// unsupported by cpu
CYCLES(1); // TODO: correct cycle count
} else {
- // since cache is not implemented
+ // TODO: manage the cache if present
uint32_t ea = GetEA(modrm, 0);
WRITEXMM(ea, XMM((modrm >> 3) & 0x7));
CYCLES(1); // TODO: correct cycle count