/*
* 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.
*
* Memory handling and MMU.
*
* Authors: Sarah Walker,
* Miran Grca,
* Fred N. van Kempen,
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca.
* Copyright 2017-2020 Fred N. van Kempen.
*/
#include
#include
#include
#include
#include
#include
#include
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/version.h>
#include "cpu.h"
#include "x86_ops.h"
#include "x86.h"
#include "x86seg_common.h"
#include <86box/machine.h>
#include <86box/m_xt_xi8088.h>
#include <86box/config.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/plat.h>
#include <86box/rom.h>
#include <86box/gdbstub.h>
#ifdef USE_DYNAREC
# include "codegen_public.h"
#else
# ifdef USE_NEW_DYNAREC
# define PAGE_MASK_SHIFT 6
# else
# define PAGE_MASK_INDEX_MASK 3
# define PAGE_MASK_INDEX_SHIFT 10
# define PAGE_MASK_SHIFT 4
# endif
# define PAGE_MASK_MASK 63
#endif
#if (!defined(USE_DYNAREC) && defined(USE_NEW_DYNAREC))
# define BLOCK_PC_INVALID 0xffffffff
# define BLOCK_INVALID 0
#endif
mem_mapping_t ram_low_mapping; /* 0..640K mapping */
mem_mapping_t ram_mid_mapping; /* 640..1024K mapping */
mem_mapping_t ram_mid_mapping2; /* 640..1024K mapping, second part, for SiS 471 in relocate mode */
mem_mapping_t ram_remapped_mapping; /* 640..1024K mapping */
mem_mapping_t ram_remapped_mapping2; /* 640..1024K second mapping, for SiS 471 mode */
mem_mapping_t ram_high_mapping; /* 1024K+ mapping */
mem_mapping_t ram_2gb_mapping; /* 1024M+ mapping */
mem_mapping_t ram_split_mapping;
mem_mapping_t bios_mapping;
mem_mapping_t bios_high_mapping;
page_t *pages; /* RAM page table */
page_t **page_lookup; /* pagetable lookup */
uint32_t pages_sz; /* #pages in table */
uint8_t *ram; /* the virtual RAM */
uint8_t *ram2; /* the virtual RAM */
uint8_t page_ff[4096];
uint32_t rammask;
uint32_t addr_space_size;
uint8_t *rom; /* the virtual ROM */
uint32_t biosmask;
uint32_t biosaddr;
uint32_t pccache;
uint8_t *pccache2;
int readlnext;
int readlookup[256];
uintptr_t *readlookup2;
uintptr_t old_rl2;
uint8_t uncached = 0;
int writelnext;
int writelookup[256];
uintptr_t *writelookup2;
uint32_t mem_logical_addr;
int shadowbios = 0;
int shadowbios_write;
int readlnum = 0;
int writelnum = 0;
int cachesize = 256;
uint32_t get_phys_virt;
uint32_t get_phys_phys;
int mem_a20_key = 0;
int mem_a20_alt = 0;
int mem_a20_state = 0;
int mmuflush = 0;
int mmu_perm = 4;
#ifdef USE_NEW_DYNAREC
uint64_t *byte_dirty_mask;
uint64_t *byte_code_present_mask;
uint32_t purgable_page_list_head = 0;
int purgeable_page_count = 0;
#endif
uint8_t high_page = 0; /* if a high (> 4 gb) page was detected */
mem_mapping_t *read_mapping[MEM_MAPPINGS_NO];
mem_mapping_t *write_mapping[MEM_MAPPINGS_NO];
uint8_t *_mem_exec[MEM_MAPPINGS_NO];
/* FIXME: re-do this with a 'mem_ops' struct. */
static uint8_t *page_lookupp; /* pagetable mmu_perm lookup */
static uint8_t *readlookupp;
static uint8_t *writelookupp;
static mem_mapping_t *base_mapping;
static mem_mapping_t *last_mapping;
static mem_mapping_t *read_mapping_bus[MEM_MAPPINGS_NO];
static mem_mapping_t *write_mapping_bus[MEM_MAPPINGS_NO];
static uint8_t _mem_wp[MEM_MAPPINGS_NO];
static uint8_t _mem_wp_bus[MEM_MAPPINGS_NO];
static uint8_t ff_pccache[4] = { 0xff, 0xff, 0xff, 0xff };
static mem_state_t _mem_state[MEM_MAPPINGS_NO];
static uint32_t remap_start_addr;
static uint32_t remap_start_addr2;
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
static size_t ram_size = 0;
static size_t ram2_size = 0;
#else
static size_t ram_size = 0;
#endif
#ifdef ENABLE_MEM_LOG
int mem_do_log = ENABLE_MEM_LOG;
static void
mem_log(const char *fmt, ...)
{
va_list ap;
if (mem_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define mem_log(fmt, ...)
#endif
#ifdef USE_DEBUG_REGS_486
/* As below, 1 = exec, 4 = read. */
int read_type = 4;
/* Set trap for data address breakpoints - 1 = exec, 2 = write, 4 = read. */
void
mem_debug_check_addr(uint32_t addr, int flags)
{
uint32_t bp_addr;
uint32_t bp_mask;
uint32_t len_type_pair;
int bp_enabled;
uint8_t match_flags[4] = { 0, 2, 0, 6 };
if (cpu_state.abrt || ((flags == 1) && (cpu_state.eflags & RF_FLAG)))
return;
if (dr[7] & 0x000000ff) for (uint8_t i = 0; i < 4; i++) {
bp_addr = dr[i];
bp_enabled = (dr[7] >> (i << 1)) & 0x03;
len_type_pair = (dr[7] >> (16 + (i << 2))) & 0x0f;
bp_mask = ~((len_type_pair >> 2) & 0x03);
if ((flags & match_flags[len_type_pair & 0x03]) && ((bp_addr & bp_mask) == (addr & bp_mask))) {
/*
From the Intel i386 documemntation:
(Note that the processor sets Bn regardless of whether Gn or
Ln is set. If more than one breakpoint condition occurs at one time and if
the breakpoint trap occurs due to an enabled condition other than n, Bn may
be set, even though neither Gn nor Ln is set.)
*/
dr[6] |= (1 << i);
if (bp_enabled)
trap |= (read_type == 1) ? 8 : 4;
}
}
}
#endif
int
mem_addr_is_ram(uint32_t addr)
{
const mem_mapping_t *mapping = read_mapping[addr >> MEM_GRANULARITY_BITS];
return (mapping == &ram_low_mapping) || (mapping == &ram_high_mapping) || (mapping == &ram_mid_mapping) ||
(mapping == &ram_mid_mapping2) || (mapping == &ram_remapped_mapping);
}
void
resetreadlookup(void)
{
/* Initialize the page lookup table. */
memset(page_lookup, 0x00, (1 << 20) * sizeof(page_t *));
/* Initialize the tables for lower (<= 1024K) RAM. */
for (uint16_t c = 0; c < 256; c++) {
readlookup[c] = 0xffffffff;
writelookup[c] = 0xffffffff;
}
/* Initialize the tables for high (> 1024K) RAM. */
memset(readlookup2, 0xff, (1 << 20) * sizeof(uintptr_t));
memset(readlookupp, 0x04, (1 << 20) * sizeof(uint8_t));
memset(writelookup2, 0xff, (1 << 20) * sizeof(uintptr_t));
memset(writelookupp, 0x04, (1 << 20) * sizeof(uint8_t));
readlnext = 0;
writelnext = 0;
pccache = 0xffffffff;
high_page = 0;
}
void
flushmmucache(void)
{
for (uint16_t c = 0; c < 256; c++) {
if (readlookup[c] != (int) 0xffffffff) {
readlookup2[readlookup[c]] = LOOKUP_INV;
readlookupp[readlookup[c]] = 4;
readlookup[c] = 0xffffffff;
}
if (writelookup[c] != (int) 0xffffffff) {
page_lookup[writelookup[c]] = NULL;
page_lookupp[writelookup[c]] = 4;
writelookup2[writelookup[c]] = LOOKUP_INV;
writelookupp[writelookup[c]] = 4;
writelookup[c] = 0xffffffff;
}
}
mmuflush++;
pccache = (uint32_t) 0xffffffff;
pccache2 = (uint8_t *) 0xffffffff;
#ifdef USE_DYNAREC
codegen_flush();
#endif
}
void
flushmmucache_nopc(void)
{
for (uint16_t c = 0; c < 256; c++) {
if (readlookup[c] != (int) 0xffffffff) {
readlookup2[readlookup[c]] = LOOKUP_INV;
readlookupp[readlookup[c]] = 4;
readlookup[c] = 0xffffffff;
}
if (writelookup[c] != (int) 0xffffffff) {
page_lookup[writelookup[c]] = NULL;
page_lookupp[writelookup[c]] = 4;
writelookup2[writelookup[c]] = LOOKUP_INV;
writelookupp[writelookup[c]] = 4;
writelookup[c] = 0xffffffff;
}
}
}
void
mem_flush_write_page(uint32_t addr, uint32_t virt)
{
const page_t *page_target = &pages[addr >> 12];
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
uint32_t a;
#endif
for (uint16_t c = 0; c < 256; c++) {
if (writelookup[c] != (int) 0xffffffff) {
#if (defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)
uintptr_t target = (uintptr_t) &ram[(uintptr_t) (addr & ~0xfff) - (virt & ~0xfff)];
#else
a = (uintptr_t) (addr & ~0xfff) - (virt & ~0xfff);
uintptr_t target;
if ((addr & ~0xfff) >= (1 << 30))
target = (uintptr_t) &ram2[a - (1 << 30)];
else
target = (uintptr_t) &ram[a];
#endif
if (writelookup2[writelookup[c]] == target || page_lookup[writelookup[c]] == page_target) {
writelookup2[writelookup[c]] = LOOKUP_INV;
page_lookup[writelookup[c]] = NULL;
writelookup[c] = 0xffffffff;
}
}
}
}
#define mmutranslate_read(addr) mmutranslatereal(addr, 0)
#define mmutranslate_write(addr) mmutranslatereal(addr, 1)
#define rammap(x) ((uint32_t *) (_mem_exec[(x) >> MEM_GRANULARITY_BITS]))[((x) >> 2) & MEM_GRANULARITY_QMASK]
#define rammap64(x) ((uint64_t *) (_mem_exec[(x) >> MEM_GRANULARITY_BITS]))[((x) >> 3) & MEM_GRANULARITY_PMASK]
static __inline uint64_t
mmutranslatereal_normal(uint32_t addr, int rw)
{
uint32_t temp;
uint32_t temp2;
uint32_t temp3;
uint32_t addr2;
if (cpu_state.abrt)
return 0xffffffffffffffffULL;
addr2 = ((cr3 & ~0xfff) + ((addr >> 20) & 0xffc));
temp = temp2 = rammap(addr2);
if (!(temp & 1)) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
if ((temp & 0x80) && (cr4 & CR4_PSE)) {
/*4MB page*/
if (((CPL == 3) && !(temp & 4) && !cpl_override) || (rw && !(temp & 2) && (((CPL == 3) && !cpl_override) || ((is486 || isibm486) && (cr0 & WP_FLAG))))) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
mmu_perm = temp & 4;
rammap(addr2) |= (rw ? 0x60 : 0x20);
return (temp & ~0x3fffff) + (addr & 0x3fffff);
}
temp = rammap((temp & ~0xfff) + ((addr >> 10) & 0xffc));
temp3 = temp & temp2;
if (!(temp & 1) || ((CPL == 3) && !(temp3 & 4) && !cpl_override) || (rw && !(temp3 & 2) && (((CPL == 3) && !cpl_override) || ((is486 || isibm486) && (cr0 & WP_FLAG))))) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
mmu_perm = temp & 4;
rammap(addr2) |= 0x20;
rammap((temp2 & ~0xfff) + ((addr >> 10) & 0xffc)) |= (rw ? 0x60 : 0x20);
return (uint64_t) ((temp & ~0xfff) + (addr & 0xfff));
}
static __inline uint64_t
mmutranslatereal_pae(uint32_t addr, int rw)
{
uint64_t temp;
uint64_t temp2;
uint64_t temp3;
uint64_t temp4;
uint64_t addr2;
uint64_t addr3;
uint64_t addr4;
if (cpu_state.abrt)
return 0xffffffffffffffffULL;
addr2 = (cr3 & ~0x1f) + ((addr >> 27) & 0x18);
temp = temp2 = rammap64(addr2) & 0x000000ffffffffffULL;
if (!(temp & 1)) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
addr3 = (temp & ~0xfffULL) + ((addr >> 18) & 0xff8);
temp = temp4 = rammap64(addr3) & 0x000000ffffffffffULL;
temp3 = temp & temp2;
if (!(temp & 1)) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
if (temp & 0x80) {
/*2MB page*/
if (((CPL == 3) && !(temp & 4) && !cpl_override) || (rw && !(temp & 2) && (((CPL == 3) && !cpl_override) || (cr0 & WP_FLAG)))) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
mmu_perm = temp & 4;
rammap64(addr3) |= (rw ? 0x60 : 0x20);
return ((temp & ~0x1fffffULL) + (addr & 0x1fffffULL)) & 0x000000ffffffffffULL;
}
addr4 = (temp & ~0xfffULL) + ((addr >> 9) & 0xff8);
temp = rammap64(addr4) & 0x000000ffffffffffULL;
temp3 = temp & temp4;
if (!(temp & 1) || ((CPL == 3) && !(temp3 & 4) && !cpl_override) || (rw && !(temp3 & 2) && (((CPL == 3) && !cpl_override) || (cr0 & WP_FLAG)))) {
cr2 = addr;
temp &= 1;
if (CPL == 3)
temp |= 4;
if (rw)
temp |= 2;
cpu_state.abrt = ABRT_PF;
abrt_error = temp;
return 0xffffffffffffffffULL;
}
mmu_perm = temp & 4;
rammap64(addr3) |= 0x20;
rammap64(addr4) |= (rw ? 0x60 : 0x20);
return ((temp & ~0xfffULL) + ((uint64_t) (addr & 0xfff))) & 0x000000ffffffffffULL;
}
uint64_t
mmutranslatereal(uint32_t addr, int rw)
{
/* Fast path to return invalid without any call if an exception has occurred beforehand. */
if (cpu_state.abrt)
return 0xffffffffffffffffULL;
if (cr4 & CR4_PAE)
return mmutranslatereal_pae(addr, rw);
else
return mmutranslatereal_normal(addr, rw);
}
/* This is needed because the old recompiler calls this to check for page fault. */
uint32_t
mmutranslatereal32(uint32_t addr, int rw)
{
/* Fast path to return invalid without any call if an exception has occurred beforehand. */
if (cpu_state.abrt)
return (uint32_t) 0xffffffffffffffffULL;
return (uint32_t) mmutranslatereal(addr, rw);
}
static __inline uint64_t
mmutranslate_noabrt_normal(uint32_t addr, int rw)
{
uint32_t temp;
uint32_t temp2;
uint32_t temp3;
uint32_t addr2;
if (cpu_state.abrt)
return 0xffffffffffffffffULL;
addr2 = ((cr3 & ~0xfff) + ((addr >> 20) & 0xffc));
temp = temp2 = rammap(addr2);
if (!(temp & 1))
return 0xffffffffffffffffULL;
if ((temp & 0x80) && (cr4 & CR4_PSE)) {
/*4MB page*/
if (((CPL == 3) && !(temp & 4) && !cpl_override) || (rw && !(temp & 2) && ((CPL == 3) || (cr0 & WP_FLAG))))
return 0xffffffffffffffffULL;
return (temp & ~0x3fffff) + (addr & 0x3fffff);
}
temp = rammap((temp & ~0xfff) + ((addr >> 10) & 0xffc));
temp3 = temp & temp2;
if (!(temp & 1) || ((CPL == 3) && !(temp3 & 4) && !cpl_override) || (rw && !(temp3 & 2) && ((CPL == 3) || (cr0 & WP_FLAG))))
return 0xffffffffffffffffULL;
return (uint64_t) ((temp & ~0xfff) + (addr & 0xfff));
}
static __inline uint64_t
mmutranslate_noabrt_pae(uint32_t addr, int rw)
{
uint64_t temp;
uint64_t temp2;
uint64_t temp3;
uint64_t temp4;
uint64_t addr2;
uint64_t addr3;
uint64_t addr4;
if (cpu_state.abrt)
return 0xffffffffffffffffULL;
addr2 = (cr3 & ~0x1f) + ((addr >> 27) & 0x18);
temp = temp2 = rammap64(addr2) & 0x000000ffffffffffULL;
if (!(temp & 1))
return 0xffffffffffffffffULL;
addr3 = (temp & ~0xfffULL) + ((addr >> 18) & 0xff8);
temp = temp4 = rammap64(addr3) & 0x000000ffffffffffULL;
temp3 = temp & temp2;
if (!(temp & 1))
return 0xffffffffffffffffULL;
if (temp & 0x80) {
/*2MB page*/
if (((CPL == 3) && !(temp & 4) && !cpl_override) || (rw && !(temp & 2) && ((CPL == 3) || (cr0 & WP_FLAG))))
return 0xffffffffffffffffULL;
return ((temp & ~0x1fffffULL) + (addr & 0x1fffff)) & 0x000000ffffffffffULL;
}
addr4 = (temp & ~0xfffULL) + ((addr >> 9) & 0xff8);
temp = rammap64(addr4) & 0x000000ffffffffffULL;
temp3 = temp & temp4;
if (!(temp & 1) || ((CPL == 3) && !(temp3 & 4) && !cpl_override) || (rw && !(temp3 & 2) && ((CPL == 3) || (cr0 & WP_FLAG))))
return 0xffffffffffffffffULL;
return ((temp & ~0xfffULL) + ((uint64_t) (addr & 0xfff))) & 0x000000ffffffffffULL;
}
uint64_t
mmutranslate_noabrt(uint32_t addr, int rw)
{
/* Fast path to return invalid without any call if an exception has occurred beforehand. */
if (cpu_state.abrt)
return 0xffffffffffffffffULL;
if (cr4 & CR4_PAE)
return mmutranslate_noabrt_pae(addr, rw);
else
return mmutranslate_noabrt_normal(addr, rw);
}
uint8_t
mem_addr_range_match(uint32_t addr, uint32_t start, uint32_t len)
{
if (addr < start)
return 0;
else if (addr >= (start + len))
return 0;
else
return 1;
}
uint32_t
mem_addr_translate(uint32_t addr, uint32_t chunk_start, uint32_t len)
{
uint32_t mask = len - 1;
return chunk_start + (addr & mask);
}
void
addreadlookup(uint32_t virt, uint32_t phys)
{
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
uint32_t a;
#endif
if (virt == 0xffffffff)
return;
if (readlookup2[virt >> 12] != (uintptr_t) LOOKUP_INV)
return;
if (readlookup[readlnext] != (int) 0xffffffff) {
if ((readlookup[readlnext] == ((es + DI) >> 12)) || (readlookup[readlnext] == ((es + EDI) >> 12)))
uncached = 1;
readlookup2[readlookup[readlnext]] = LOOKUP_INV;
}
#if (defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)
readlookup2[virt >> 12] = (uintptr_t) &ram[(uintptr_t) (phys & ~0xFFF) - (uintptr_t) (virt & ~0xfff)];
#else
a = ((uint32_t) (phys & ~0xfff) - (uint32_t) (virt & ~0xfff));
if ((phys & ~0xfff) >= (1 << 30))
readlookup2[virt >> 12] = (uintptr_t) &ram2[a - (1 << 30)];
else
readlookup2[virt >> 12] = (uintptr_t) &ram[a];
#endif
readlookupp[virt >> 12] = mmu_perm;
readlookup[readlnext++] = virt >> 12;
readlnext &= (cachesize - 1);
cycles -= 9;
}
void
addwritelookup(uint32_t virt, uint32_t phys)
{
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
uint32_t a;
#endif
if (virt == 0xffffffff)
return;
if (page_lookup[virt >> 12])
return;
if (writelookup[writelnext] != -1) {
page_lookup[writelookup[writelnext]] = NULL;
writelookup2[writelookup[writelnext]] = LOOKUP_INV;
}
#ifdef USE_NEW_DYNAREC
# ifdef USE_DYNAREC
if (pages[phys >> 12].block || (phys & ~0xfff) == recomp_page) {
# else
if (pages[phys >> 12].block) {
# endif
#else
# ifdef USE_DYNAREC
if (pages[phys >> 12].block[0] || pages[phys >> 12].block[1] || pages[phys >> 12].block[2] || pages[phys >> 12].block[3] || (phys & ~0xfff) == recomp_page) {
# else
if (pages[phys >> 12].block[0] || pages[phys >> 12].block[1] || pages[phys >> 12].block[2] || pages[phys >> 12].block[3]) {
# endif
#endif
page_lookup[virt >> 12] = &pages[phys >> 12];
page_lookupp[virt >> 12] = mmu_perm;
} else {
#if (defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)
writelookup2[virt >> 12] = (uintptr_t) &ram[(uintptr_t) (phys & ~0xFFF) - (uintptr_t) (virt & ~0xfff)];
#else
a = ((uint32_t) (phys & ~0xfff) - (uint32_t) (virt & ~0xfff));
if ((phys & ~0xfff) >= (1 << 30))
writelookup2[virt >> 12] = (uintptr_t) &ram2[a - (1 << 30)];
else
writelookup2[virt >> 12] = (uintptr_t) &ram[a];
#endif
}
writelookupp[virt >> 12] = mmu_perm;
writelookup[writelnext++] = virt >> 12;
writelnext &= (cachesize - 1);
cycles -= 9;
}
uint8_t *
getpccache(uint32_t a)
{
uint64_t a64 = (uint64_t) a;
#if (defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)
uint8_t *p;
#endif
uint32_t a2;
a2 = a;
if (cr0 >> 31) {
a64 = mmutranslate_read(a64);
if (a64 == 0xffffffffffffffffULL)
return ram;
}
a64 &= rammask;
if (_mem_exec[a64 >> MEM_GRANULARITY_BITS]) {
if (is286) {
if (read_mapping[a64 >> MEM_GRANULARITY_BITS] && (read_mapping[a64 >> MEM_GRANULARITY_BITS]->flags & MEM_MAPPING_ROM_WS))
cpu_prefetch_cycles = cpu_rom_prefetch_cycles;
else
cpu_prefetch_cycles = cpu_mem_prefetch_cycles;
}
#if (defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)
p = &_mem_exec[a64 >> MEM_GRANULARITY_BITS][(uintptr_t) (a64 & MEM_GRANULARITY_PAGE) - (uintptr_t) (a2 & ~0xfff)];
return (uint8_t *) (((uintptr_t) p & 0x00000000ffffffffULL) | ((uintptr_t) &_mem_exec[a64 >> MEM_GRANULARITY_BITS][0] & 0xffffffff00000000ULL));
#else
return &_mem_exec[a64 >> MEM_GRANULARITY_BITS][(uintptr_t) (a64 & MEM_GRANULARITY_PAGE) - (uintptr_t) (a2 & ~0xfff)];
#endif
}
mem_log("Bad getpccache %08X%08X\n", (uint32_t) (a64 >> 32), (uint32_t) (a64 & 0xffffffffULL));
return (uint8_t *) &ff_pccache;
}
uint8_t
read_mem_b(uint32_t addr)
{
mem_mapping_t *map;
uint8_t ret = 0xff;
int old_cycles = cycles;
mem_logical_addr = addr;
addr &= rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_b)
ret = map->read_b(addr, map->priv);
resub_cycles(old_cycles);
return ret;
}
uint16_t
read_mem_w(uint32_t addr)
{
mem_mapping_t *map;
uint16_t ret = 0xffff;
int old_cycles = cycles;
mem_logical_addr = addr;
addr &= rammask;
if (addr & 1)
ret = read_mem_b(addr) | (read_mem_b(addr + 1) << 8);
else {
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_w)
ret = map->read_w(addr, map->priv);
else if (map && map->read_b)
ret = map->read_b(addr, map->priv) | (map->read_b(addr + 1, map->priv) << 8);
}
resub_cycles(old_cycles);
return ret;
}
void
write_mem_b(uint32_t addr, uint8_t val)
{
mem_mapping_t *map;
int old_cycles = cycles;
mem_logical_addr = addr;
addr &= rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_b)
map->write_b(addr, val, map->priv);
resub_cycles(old_cycles);
}
void
write_mem_w(uint32_t addr, uint16_t val)
{
mem_mapping_t *map;
int old_cycles = cycles;
mem_logical_addr = addr;
addr &= rammask;
if (addr & 1) {
write_mem_b(addr, val);
write_mem_b(addr + 1, val >> 8);
} else {
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map) {
if (map->write_w)
map->write_w(addr, val, map->priv);
else if (map->write_b) {
map->write_b(addr, val, map->priv);
map->write_b(addr + 1, val >> 8, map->priv);
}
}
}
resub_cycles(old_cycles);
}
uint8_t
readmembl(uint32_t addr)
{
mem_mapping_t *map;
uint64_t a;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 1);
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr, read_type);
#endif
addr64 = (uint64_t) addr;
mem_logical_addr = addr;
high_page = 0;
if (cr0 >> 31) {
a = mmutranslate_read(addr);
addr64 = (uint32_t) a;
if (a > 0xffffffffULL)
return 0xff;
}
addr = (uint32_t) (addr64 & rammask);
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_b)
return map->read_b(addr, map->priv);
return 0xff;
}
void
writemembl(uint32_t addr, uint8_t val)
{
mem_mapping_t *map;
uint64_t a;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 1);
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr, 2);
#endif
addr64 = (uint64_t) addr;
mem_logical_addr = addr;
high_page = 0;
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_b) {
page_lookup[addr >> 12]->write_b(addr, val, page_lookup[addr >> 12]);
return;
}
if (cr0 >> 31) {
a = mmutranslate_write(addr);
addr64 = (uint32_t) a;
if (a > 0xffffffffULL)
return;
}
addr = (uint32_t) (addr64 & rammask);
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_b)
map->write_b(addr, val, map->priv);
}
/* Read a byte from memory without MMU translation - result of previous MMU translation passed as value. */
uint8_t
readmembl_no_mmut(uint32_t addr, uint32_t a64)
{
mem_mapping_t *map;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 1);
mem_logical_addr = addr;
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return 0xff;
addr = a64 & rammask;
} else
addr &= rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_b)
return map->read_b(addr, map->priv);
return 0xff;
}
/* Write a byte to memory without MMU translation - result of previous MMU translation passed as value. */
void
writemembl_no_mmut(uint32_t addr, uint32_t a64, uint8_t val)
{
mem_mapping_t *map;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 1);
mem_logical_addr = addr;
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_b) {
page_lookup[addr >> 12]->write_b(addr, val, page_lookup[addr >> 12]);
return;
}
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return;
addr = a64 & rammask;
} else
addr &= rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_b)
map->write_b(addr, val, map->priv);
}
uint16_t
readmemwl(uint32_t addr)
{
mem_mapping_t *map;
uint64_t a;
addr64a[0] = addr;
addr64a[1] = addr + 1;
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr, read_type);
mem_debug_check_addr(addr + 1, read_type);
#endif
GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_READ, 2);
mem_logical_addr = addr;
high_page = 0;
if (addr & 1) {
if (!cpu_cyrix_alignment || (addr & 7) == 7)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffe) {
if (cr0 >> 31) {
for (uint8_t i = 0; i < 2; i++) {
a = mmutranslate_read(addr + i);
addr64a[i] = (uint32_t) a;
if (a > 0xffffffffULL)
return 0xffff;
}
}
return readmembl_no_mmut(addr, addr64a[0]) | (((uint16_t) readmembl_no_mmut(addr + 1, addr64a[1])) << 8);
} else if (readlookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = readlookupp[addr >> 12];
return *(uint16_t *) (readlookup2[addr >> 12] + addr);
}
}
if (cr0 >> 31) {
a = mmutranslate_read(addr);
addr64a[0] = (uint32_t) a;
if (a > 0xffffffffULL)
return 0xffff;
} else
addr64a[0] = (uint64_t) addr;
addr = addr64a[0] & rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_w)
return map->read_w(addr, map->priv);
if (map && map->read_b) {
return map->read_b(addr, map->priv) | ((uint16_t) (map->read_b(addr + 1, map->priv)) << 8);
}
return 0xffff;
}
void
writememwl(uint32_t addr, uint16_t val)
{
mem_mapping_t *map;
uint64_t a;
addr64a[0] = addr;
addr64a[1] = addr + 1;
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr, 2);
mem_debug_check_addr(addr + 1, 2);
#endif
GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_WRITE, 2);
mem_logical_addr = addr;
high_page = 0;
if (addr & 1) {
if (!cpu_cyrix_alignment || (addr & 7) == 7)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffe) {
if (cr0 >> 31) {
for (uint8_t i = 0; i < 2; i++) {
/* Do not translate a page that has a valid lookup, as that is by definition valid
and the whole purpose of the lookup is to avoid repeat identical translations. */
if (!page_lookup[(addr + i) >> 12] || !page_lookup[(addr + i) >> 12]->write_b) {
a = mmutranslate_write(addr + i);
addr64a[i] = (uint32_t) a;
if (a > 0xffffffffULL)
return;
}
}
}
/* No need to waste precious CPU host cycles on mmutranslate's that were already done, just pass
their result as a parameter to be used if needed. */
writemembl_no_mmut(addr, addr64a[0], val);
writemembl_no_mmut(addr + 1, addr64a[1], val >> 8);
return;
} else if (writelookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = writelookupp[addr >> 12];
*(uint16_t *) (writelookup2[addr >> 12] + addr) = val;
return;
}
}
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_w) {
page_lookup[addr >> 12]->write_w(addr, val, page_lookup[addr >> 12]);
mmu_perm = page_lookupp[addr >> 12];
return;
}
if (cr0 >> 31) {
a = mmutranslate_write(addr);
addr64a[0] = (uint32_t) a;
if (a > 0xffffffffULL)
return;
}
addr = addr64a[0] & rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_w) {
map->write_w(addr, val, map->priv);
return;
}
if (map && map->write_b) {
map->write_b(addr, val, map->priv);
map->write_b(addr + 1, val >> 8, map->priv);
return;
}
}
/* Read a word from memory without MMU translation - results of previous MMU translation passed as array. */
uint16_t
readmemwl_no_mmut(uint32_t addr, uint32_t *a64)
{
mem_mapping_t *map;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 2);
mem_logical_addr = addr;
if (addr & 1) {
if (!cpu_cyrix_alignment || (addr & 7) == 7)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffe) {
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return 0xffff;
}
return readmembl_no_mmut(addr, a64[0]) | (((uint16_t) readmembl_no_mmut(addr + 1, a64[1])) << 8);
} else if (readlookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = readlookupp[addr >> 12];
return *(uint16_t *) (readlookup2[addr >> 12] + addr);
}
}
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return 0xffff;
addr = (uint32_t) (a64[0] & rammask);
} else
addr &= rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_w)
return map->read_w(addr, map->priv);
if (map && map->read_b) {
return map->read_b(addr, map->priv) | ((uint16_t) (map->read_b(addr + 1, map->priv)) << 8);
}
return 0xffff;
}
/* Write a word to memory without MMU translation - results of previous MMU translation passed as array. */
void
writememwl_no_mmut(uint32_t addr, uint32_t *a64, uint16_t val)
{
mem_mapping_t *map;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 2);
mem_logical_addr = addr;
if (addr & 1) {
if (!cpu_cyrix_alignment || (addr & 7) == 7)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffe) {
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return;
}
writemembl_no_mmut(addr, a64[0], val);
writemembl_no_mmut(addr + 1, a64[1], val >> 8);
return;
} else if (writelookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = writelookupp[addr >> 12];
*(uint16_t *) (writelookup2[addr >> 12] + addr) = val;
return;
}
}
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_w) {
mmu_perm = page_lookupp[addr >> 12];
page_lookup[addr >> 12]->write_w(addr, val, page_lookup[addr >> 12]);
return;
}
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return;
addr = (uint32_t) (a64[0] & rammask);
} else
addr &= rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_w) {
map->write_w(addr, val, map->priv);
return;
}
if (map && map->write_b) {
map->write_b(addr, val, map->priv);
map->write_b(addr + 1, val >> 8, map->priv);
return;
}
}
uint32_t
readmemll(uint32_t addr)
{
mem_mapping_t *map;
int i;
uint64_t a = 0x0000000000000000ULL;
for (i = 0; i < 4; i++) {
addr64a[i] = (uint64_t) (addr + i);
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr + i, read_type);
#endif
}
GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_READ, 4);
mem_logical_addr = addr;
high_page = 0;
if (addr & 3) {
if (!cpu_cyrix_alignment || (addr & 7) > 4)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffc) {
if (cr0 >> 31) {
for (i = 0; i < 4; i++) {
if (i == 0) {
a = mmutranslate_read(addr + i);
addr64a[i] = (uint32_t) a;
} else if (!((addr + i) & 0xfff)) {
a = mmutranslate_read(addr + 3);
addr64a[i] = (uint32_t) a;
if (!cpu_state.abrt) {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
} else {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
if (a > 0xffffffffULL)
return 0xffff;
}
}
/* No need to waste precious CPU host cycles on mmutranslate's that were already done, just pass
their result as a parameter to be used if needed. */
return readmemwl_no_mmut(addr, addr64a) | (((uint32_t) readmemwl_no_mmut(addr + 2, &(addr64a[2]))) << 16);
} else if (readlookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = readlookupp[addr >> 12];
return *(uint32_t *) (readlookup2[addr >> 12] + addr);
}
}
if (cr0 >> 31) {
a = mmutranslate_read(addr);
addr64a[0] = (uint32_t) a;
if (a > 0xffffffffULL)
return 0xffffffff;
}
addr = addr64a[0] & rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_l)
return map->read_l(addr, map->priv);
if (map && map->read_w)
return map->read_w(addr, map->priv) | ((uint32_t) (map->read_w(addr + 2, map->priv)) << 16);
if (map && map->read_b)
return map->read_b(addr, map->priv) | ((uint32_t) (map->read_b(addr + 1, map->priv)) << 8) | ((uint32_t) (map->read_b(addr + 2, map->priv)) << 16) | ((uint32_t) (map->read_b(addr + 3, map->priv)) << 24);
return 0xffffffff;
}
void
writememll(uint32_t addr, uint32_t val)
{
mem_mapping_t *map;
int i;
uint64_t a = 0x0000000000000000ULL;
for (i = 0; i < 4; i++) {
addr64a[i] = (uint64_t) (addr + i);
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr + i, 2);
#endif
}
GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_WRITE, 4);
mem_logical_addr = addr;
high_page = 0;
if (addr & 3) {
if (!cpu_cyrix_alignment || (addr & 7) > 4)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffc) {
if (cr0 >> 31) {
for (i = 0; i < 4; i++) {
/* Do not translate a page that has a valid lookup, as that is by definition valid
and the whole purpose of the lookup is to avoid repeat identical translations. */
if (!page_lookup[(addr + i) >> 12] || !page_lookup[(addr + i) >> 12]->write_b) {
if (i == 0) {
a = mmutranslate_write(addr + i);
addr64a[i] = (uint32_t) a;
} else if (!((addr + i) & 0xfff)) {
a = mmutranslate_write(addr + 3);
addr64a[i] = (uint32_t) a;
if (!cpu_state.abrt) {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
} else {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
if (a > 0xffffffffULL)
return;
}
}
}
/* No need to waste precious CPU host cycles on mmutranslate's that were already done, just pass
their result as a parameter to be used if needed. */
writememwl_no_mmut(addr, &(addr64a[0]), val);
writememwl_no_mmut(addr + 2, &(addr64a[2]), val >> 16);
return;
} else if (writelookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = writelookupp[addr >> 12];
*(uint32_t *) (writelookup2[addr >> 12] + addr) = val;
return;
}
}
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_l) {
mmu_perm = page_lookupp[addr >> 12];
page_lookup[addr >> 12]->write_l(addr, val, page_lookup[addr >> 12]);
return;
}
if (cr0 >> 31) {
a = mmutranslate_write(addr);
addr64a[0] = (uint32_t) a;
if (a > 0xffffffffULL)
return;
}
addr = addr64a[0] & rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_l) {
map->write_l(addr, val, map->priv);
return;
}
if (map && map->write_w) {
map->write_w(addr, val, map->priv);
map->write_w(addr + 2, val >> 16, map->priv);
return;
}
if (map && map->write_b) {
map->write_b(addr, val, map->priv);
map->write_b(addr + 1, val >> 8, map->priv);
map->write_b(addr + 2, val >> 16, map->priv);
map->write_b(addr + 3, val >> 24, map->priv);
return;
}
}
/* Read a long from memory without MMU translation - results of previous MMU translation passed as array. */
uint32_t
readmemll_no_mmut(uint32_t addr, uint32_t *a64)
{
mem_mapping_t *map;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_READ, 4);
mem_logical_addr = addr;
if (addr & 3) {
if (!cpu_cyrix_alignment || (addr & 7) > 4)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffc) {
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return 0xffffffff;
}
return readmemwl_no_mmut(addr, a64) | ((uint32_t) (readmemwl_no_mmut(addr + 2, &(a64[2]))) << 16);
} else if (readlookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = readlookupp[addr >> 12];
return *(uint32_t *) (readlookup2[addr >> 12] + addr);
}
}
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return 0xffffffff;
addr = (uint32_t) (a64[0] & rammask);
} else
addr &= rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_l)
return map->read_l(addr, map->priv);
if (map && map->read_w)
return map->read_w(addr, map->priv) | ((uint32_t) (map->read_w(addr + 2, map->priv)) << 16);
if (map && map->read_b)
return map->read_b(addr, map->priv) | ((uint32_t) (map->read_b(addr + 1, map->priv)) << 8) | ((uint32_t) (map->read_b(addr + 2, map->priv)) << 16) | ((uint32_t) (map->read_b(addr + 3, map->priv)) << 24);
return 0xffffffff;
}
/* Write a long to memory without MMU translation - results of previous MMU translation passed as array. */
void
writememll_no_mmut(uint32_t addr, uint32_t *a64, uint32_t val)
{
mem_mapping_t *map;
GDBSTUB_MEM_ACCESS(addr, GDBSTUB_MEM_WRITE, 4);
mem_logical_addr = addr;
if (addr & 3) {
if (!cpu_cyrix_alignment || (addr & 7) > 4)
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xffc) {
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return;
}
writememwl_no_mmut(addr, &(a64[0]), val);
writememwl_no_mmut(addr + 2, &(a64[2]), val >> 16);
return;
} else if (writelookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = writelookupp[addr >> 12];
*(uint32_t *) (writelookup2[addr >> 12] + addr) = val;
return;
}
}
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_l) {
mmu_perm = page_lookupp[addr >> 12];
page_lookup[addr >> 12]->write_l(addr, val, page_lookup[addr >> 12]);
return;
}
if (cr0 >> 31) {
if (cpu_state.abrt || high_page)
return;
addr = (uint32_t) (a64[0] & rammask);
} else
addr &= rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_l) {
map->write_l(addr, val, map->priv);
return;
}
if (map && map->write_w) {
map->write_w(addr, val, map->priv);
map->write_w(addr + 2, val >> 16, map->priv);
return;
}
if (map && map->write_b) {
map->write_b(addr, val, map->priv);
map->write_b(addr + 1, val >> 8, map->priv);
map->write_b(addr + 2, val >> 16, map->priv);
map->write_b(addr + 3, val >> 24, map->priv);
return;
}
}
uint64_t
readmemql(uint32_t addr)
{
mem_mapping_t *map;
int i;
uint64_t a = 0x0000000000000000ULL;
for (i = 0; i < 8; i++) {
addr64a[i] = (uint64_t) (addr + i);
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr + i, read_type);
#endif
}
GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_READ, 8);
mem_logical_addr = addr;
high_page = 0;
if (addr & 7) {
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xff8) {
if (cr0 >> 31) {
for (i = 0; i < 8; i++) {
if (i == 0) {
a = mmutranslate_read(addr + i);
addr64a[i] = (uint32_t) a;
} else if (!((addr + i) & 0xfff)) {
a = mmutranslate_read(addr + 7);
addr64a[i] = (uint32_t) a;
if (!cpu_state.abrt) {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
} else {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
if (a > 0xffffffffULL)
return 0xffff;
}
}
/* No need to waste precious CPU host cycles on mmutranslate's that were already done, just pass
their result as a parameter to be used if needed. */
return readmemll_no_mmut(addr, addr64a) | (((uint64_t) readmemll_no_mmut(addr + 4, &(addr64a[4]))) << 32);
} else if (readlookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = readlookupp[addr >> 12];
return *(uint64_t *) (readlookup2[addr >> 12] + addr);
}
}
if (cr0 >> 31) {
a = mmutranslate_read(addr);
addr64a[0] = (uint32_t) a;
if (a > 0xffffffffULL)
return 0xffffffffffffffffULL;
}
addr = addr64a[0] & rammask;
map = read_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->read_l)
return map->read_l(addr, map->priv) | ((uint64_t) map->read_l(addr + 4, map->priv) << 32);
return readmemll(addr) | ((uint64_t) readmemll(addr + 4) << 32);
}
void
writememql(uint32_t addr, uint64_t val)
{
mem_mapping_t *map;
int i;
uint64_t a = 0x0000000000000000ULL;
for (i = 0; i < 8; i++) {
addr64a[i] = (uint64_t) (addr + i);
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr + i, 2);
#endif
}
GDBSTUB_MEM_ACCESS_FAST(addr64a, GDBSTUB_MEM_WRITE, 8);
mem_logical_addr = addr;
high_page = 0;
if (addr & 7) {
cycles -= timing_misaligned;
if ((addr & 0xfff) > 0xff8) {
if (cr0 >> 31) {
for (i = 0; i < 8; i++) {
/* Do not translate a page that has a valid lookup, as that is by definition valid
and the whole purpose of the lookup is to avoid repeat identical translations. */
if (!page_lookup[(addr + i) >> 12] || !page_lookup[(addr + i) >> 12]->write_b) {
if (i == 0) {
a = mmutranslate_write(addr + i);
addr64a[i] = (uint32_t) a;
} else if (!((addr + i) & 0xfff)) {
a = mmutranslate_write(addr + 7);
addr64a[i] = (uint32_t) a;
if (!cpu_state.abrt) {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
} else {
a = (a & ~0xfffLL) | ((uint64_t) ((addr + i) & 0xfff));
addr64a[i] = (uint32_t) a;
}
if (addr64a[i] > 0xffffffffULL)
return;
}
}
}
/* No need to waste precious CPU host cycles on mmutranslate's that were already done, just pass
their result as a parameter to be used if needed. */
writememll_no_mmut(addr, addr64a, val);
writememll_no_mmut(addr + 4, &(addr64a[4]), val >> 32);
return;
} else if (writelookup2[addr >> 12] != (uintptr_t) LOOKUP_INV) {
mmu_perm = writelookupp[addr >> 12];
*(uint64_t *) (writelookup2[addr >> 12] + addr) = val;
return;
}
}
if (page_lookup[addr >> 12] && page_lookup[addr >> 12]->write_l) {
mmu_perm = page_lookupp[addr >> 12];
page_lookup[addr >> 12]->write_l(addr, val, page_lookup[addr >> 12]);
page_lookup[addr >> 12]->write_l(addr + 4, val >> 32, page_lookup[addr >> 12]);
return;
}
if (cr0 >> 31) {
addr64a[0] = mmutranslate_write(addr);
if (addr64a[0] > 0xffffffffULL)
return;
}
addr = addr64a[0] & rammask;
map = write_mapping[addr >> MEM_GRANULARITY_BITS];
if (map && map->write_l) {
map->write_l(addr, val, map->priv);
map->write_l(addr + 4, val >> 32, map->priv);
return;
}
if (map && map->write_w) {
map->write_w(addr, val, map->priv);
map->write_w(addr + 2, val >> 16, map->priv);
map->write_w(addr + 4, val >> 32, map->priv);
map->write_w(addr + 6, val >> 48, map->priv);
return;
}
if (map && map->write_b) {
map->write_b(addr, val, map->priv);
map->write_b(addr + 1, val >> 8, map->priv);
map->write_b(addr + 2, val >> 16, map->priv);
map->write_b(addr + 3, val >> 24, map->priv);
map->write_b(addr + 4, val >> 32, map->priv);
map->write_b(addr + 5, val >> 40, map->priv);
map->write_b(addr + 6, val >> 48, map->priv);
map->write_b(addr + 7, val >> 56, map->priv);
return;
}
}
void
do_mmutranslate(uint32_t addr, uint32_t *a64, int num, int write)
{
int i;
int cond = 1;
uint32_t last_addr = addr + (num - 1);
uint64_t a = 0x0000000000000000ULL;
#ifdef USE_DEBUG_REGS_486
mem_debug_check_addr(addr, write ? 2 : read_type);
#endif
for (i = 0; i < num; i++)
a64[i] = (uint64_t) addr;
if (cr0 >> 31) for (i = 0; i < num; i++) {
if (write && ((i == 0) || !(addr & 0xfff)))
cond = (!page_lookup[addr >> 12] || !page_lookup[addr >> 12]->write_b);
if (cond) {
/* If we have encountered at least one page fault, mark all subsequent addresses as
having page faulted, prevents false negatives in readmem*l_no_mmut. */
if ((i > 0) && cpu_state.abrt && !high_page)
a64[i] = a64[i - 1];
/* If we are on the same page, there is no need to translate again, as we can just
reuse the previous result. */
else if (i == 0) {
a = mmutranslatereal(addr, write);
a64[i] = (uint32_t) a;
high_page = high_page || (!cpu_state.abrt && (a > 0xffffffffULL));
} else if (!(addr & 0xfff)) {
a = mmutranslatereal(last_addr, write);
a64[i] = (uint32_t) a;
high_page = high_page || (!cpu_state.abrt && (a64[i] > 0xffffffffULL));
if (!cpu_state.abrt) {
a = (a & 0xfffffffffffff000ULL) | ((uint64_t) (addr & 0xfff));
a64[i] = (uint32_t) a;
}
} else {
a = (a & 0xfffffffffffff000ULL) | ((uint64_t) (addr & 0xfff));
a64[i] = (uint32_t) a;
}
} else
mmu_perm = page_lookupp[addr >> 12];
addr++;
}
}
uint8_t
mem_readb_phys(uint32_t addr)
{
mem_mapping_t *map = read_mapping_bus[addr >> MEM_GRANULARITY_BITS];
uint8_t ret = 0xff;
mem_logical_addr = 0xffffffff;
if (map) {
if (cpu_use_exec && map->exec)
ret = map->exec[(addr - map->base) & map->mask];
else if (map->read_b)
ret = map->read_b(addr, map->priv);
}
return ret;
}
uint16_t
mem_readw_phys(uint32_t addr)
{
mem_mapping_t *map = read_mapping_bus[addr >> MEM_GRANULARITY_BITS];
uint16_t ret;
const uint16_t *p;
mem_logical_addr = 0xffffffff;
if (cpu_use_exec && ((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_HBOUND) && (map && map->exec)) {
p = (uint16_t *) &(map->exec[(addr - map->base) & map->mask]);
ret = *p;
} else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_HBOUND) && (map && map->read_w))
ret = map->read_w(addr, map->priv);
else {
ret = mem_readb_phys(addr + 1) << 8;
ret |= mem_readb_phys(addr);
}
return ret;
}
uint32_t
mem_readl_phys(uint32_t addr)
{
mem_mapping_t *map = read_mapping_bus[addr >> MEM_GRANULARITY_BITS];
uint32_t ret;
const uint32_t *p;
mem_logical_addr = 0xffffffff;
if (cpu_use_exec && ((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_QBOUND) && (map && map->exec)) {
p = (uint32_t *) &(map->exec[(addr - map->base) & map->mask]);
ret = *p;
} else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_QBOUND) && (map && map->read_l))
ret = map->read_l(addr, map->priv);
else {
ret = mem_readw_phys(addr + 2) << 16;
ret |= mem_readw_phys(addr);
}
return ret;
}
void
mem_read_phys(void *dest, uint32_t addr, int transfer_size)
{
uint8_t *pb;
uint16_t *pw;
uint32_t *pl;
if (transfer_size == 4) {
pl = (uint32_t *) dest;
*pl = mem_readl_phys(addr);
} else if (transfer_size == 2) {
pw = (uint16_t *) dest;
*pw = mem_readw_phys(addr);
} else if (transfer_size == 1) {
pb = (uint8_t *) dest;
*pb = mem_readb_phys(addr);
}
}
void
mem_writeb_phys(uint32_t addr, uint8_t val)
{
mem_mapping_t *map = write_mapping_bus[addr >> MEM_GRANULARITY_BITS];
mem_logical_addr = 0xffffffff;
if (map) {
if (cpu_use_exec && map->exec)
map->exec[(addr - map->base) & map->mask] = val;
else if (map->write_b)
map->write_b(addr, val, map->priv);
}
}
void
mem_writew_phys(uint32_t addr, uint16_t val)
{
mem_mapping_t *map = write_mapping_bus[addr >> MEM_GRANULARITY_BITS];
uint16_t *p;
mem_logical_addr = 0xffffffff;
if (cpu_use_exec && ((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_HBOUND) && (map && map->exec)) {
p = (uint16_t *) &(map->exec[(addr - map->base) & map->mask]);
*p = val;
} else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_HBOUND) && (map && map->write_w))
map->write_w(addr, val, map->priv);
else {
mem_writeb_phys(addr, val & 0xff);
mem_writeb_phys(addr + 1, (val >> 8) & 0xff);
}
}
void
mem_writel_phys(uint32_t addr, uint32_t val)
{
mem_mapping_t *map = write_mapping_bus[addr >> MEM_GRANULARITY_BITS];
uint32_t *p;
mem_logical_addr = 0xffffffff;
if (cpu_use_exec && ((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_QBOUND) && (map && map->exec)) {
p = (uint32_t *) &(map->exec[(addr - map->base) & map->mask]);
*p = val;
} else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_QBOUND) && (map && map->write_l))
map->write_l(addr, val, map->priv);
else {
mem_writew_phys(addr, val & 0xffff);
mem_writew_phys(addr + 2, (val >> 16) & 0xffff);
}
}
void
mem_write_phys(void *src, uint32_t addr, int transfer_size)
{
const uint8_t *pb;
const uint16_t *pw;
const uint32_t *pl;
if (transfer_size == 4) {
pl = (uint32_t *) src;
mem_writel_phys(addr, *pl);
} else if (transfer_size == 2) {
pw = (uint16_t *) src;
mem_writew_phys(addr, *pw);
} else if (transfer_size == 1) {
pb = (uint8_t *) src;
mem_writeb_phys(addr, *pb);
}
}
uint8_t
mem_read_ram(uint32_t addr, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Read B %02X from %08X\n", ram[addr], addr);
#endif
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return ram[addr];
}
uint16_t
mem_read_ramw(uint32_t addr, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Read W %04X from %08X\n", *(uint16_t *) &ram[addr], addr);
#endif
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return *(uint16_t *) &ram[addr];
}
uint32_t
mem_read_raml(uint32_t addr, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Read L %08X from %08X\n", *(uint32_t *) &ram[addr], addr);
#endif
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return *(uint32_t *) &ram[addr];
}
uint8_t
mem_read_ram_2gb(uint32_t addr, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Read B %02X from %08X\n", ram[addr], addr);
#endif
addreadlookup(mem_logical_addr, addr);
return ram2[addr - (1 << 30)];
}
uint16_t
mem_read_ram_2gbw(uint32_t addr, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Read W %04X from %08X\n", *(uint16_t *) &ram[addr], addr);
#endif
addreadlookup(mem_logical_addr, addr);
return *(uint16_t *) &ram2[addr - (1 << 30)];
}
uint32_t
mem_read_ram_2gbl(uint32_t addr, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Read L %08X from %08X\n", *(uint32_t *) &ram[addr], addr);
#endif
addreadlookup(mem_logical_addr, addr);
return *(uint32_t *) &ram2[addr - (1 << 30)];
}
#ifdef USE_NEW_DYNAREC
static inline int
page_index(page_t *page)
{
return ((uintptr_t) page - (uintptr_t) pages) / sizeof(page_t);
}
void
page_add_to_evict_list(page_t *page)
{
pages[purgable_page_list_head].evict_prev = page_index(page);
page->evict_next = purgable_page_list_head;
page->evict_prev = 0;
purgable_page_list_head = pages[purgable_page_list_head].evict_prev;
purgeable_page_count++;
}
void
page_remove_from_evict_list(page_t *page)
{
if (!page_in_evict_list(page))
fatal("page_remove_from_evict_list: not in evict list!\n");
if (page->evict_prev)
pages[page->evict_prev].evict_next = page->evict_next;
else
purgable_page_list_head = page->evict_next;
if (page->evict_next)
pages[page->evict_next].evict_prev = page->evict_prev;
page->evict_prev = EVICT_NOT_IN_LIST;
purgeable_page_count--;
}
void
mem_write_ramb_page(uint32_t addr, uint8_t val, page_t *page)
{
if (page == NULL)
return;
# ifdef USE_DYNAREC
if ((page->mem == NULL) || (page->mem == page_ff) || (val != page->mem[addr & 0xfff]) || codegen_in_recompile) {
# else
if ((page->mem == NULL) || (page->mem == page_ff) || (val != page->mem[addr & 0xfff])) {
# endif
uint64_t mask = (uint64_t) 1 << ((addr >> PAGE_MASK_SHIFT) & PAGE_MASK_MASK);
int byte_offset = (addr >> PAGE_BYTE_MASK_SHIFT) & PAGE_BYTE_MASK_OFFSET_MASK;
uint64_t byte_mask = (uint64_t) 1 << (addr & PAGE_BYTE_MASK_MASK);
page->mem[addr & 0xfff] = val;
page->dirty_mask |= mask;
if ((page->code_present_mask & mask) && !page_in_evict_list(page))
page_add_to_evict_list(page);
page->byte_dirty_mask[byte_offset] |= byte_mask;
if ((page->byte_code_present_mask[byte_offset] & byte_mask) && !page_in_evict_list(page))
page_add_to_evict_list(page);
}
}
void
mem_write_ramw_page(uint32_t addr, uint16_t val, page_t *page)
{
if (page == NULL)
return;
# ifdef USE_DYNAREC
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint16_t *) &page->mem[addr & 0xfff]) || codegen_in_recompile) {
# else
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint16_t *) &page->mem[addr & 0xfff])) {
# endif
uint64_t mask = (uint64_t) 1 << ((addr >> PAGE_MASK_SHIFT) & PAGE_MASK_MASK);
int byte_offset = (addr >> PAGE_BYTE_MASK_SHIFT) & PAGE_BYTE_MASK_OFFSET_MASK;
uint64_t byte_mask = (uint64_t) 1 << (addr & PAGE_BYTE_MASK_MASK);
if ((addr & 0xf) == 0xf)
mask |= (mask << 1);
*(uint16_t *) &page->mem[addr & 0xfff] = val;
page->dirty_mask |= mask;
if ((page->code_present_mask & mask) && !page_in_evict_list(page))
page_add_to_evict_list(page);
if ((addr & PAGE_BYTE_MASK_MASK) == PAGE_BYTE_MASK_MASK) {
page->byte_dirty_mask[byte_offset + 1] |= 1;
if ((page->byte_code_present_mask[byte_offset + 1] & 1) && !page_in_evict_list(page))
page_add_to_evict_list(page);
} else
byte_mask |= (byte_mask << 1);
page->byte_dirty_mask[byte_offset] |= byte_mask;
if ((page->byte_code_present_mask[byte_offset] & byte_mask) && !page_in_evict_list(page))
page_add_to_evict_list(page);
}
}
void
mem_write_raml_page(uint32_t addr, uint32_t val, page_t *page)
{
if (page == NULL)
return;
# ifdef USE_DYNAREC
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint32_t *) &page->mem[addr & 0xfff]) || codegen_in_recompile) {
# else
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint32_t *) &page->mem[addr & 0xfff])) {
# endif
uint64_t mask = (uint64_t) 1 << ((addr >> PAGE_MASK_SHIFT) & PAGE_MASK_MASK);
int byte_offset = (addr >> PAGE_BYTE_MASK_SHIFT) & PAGE_BYTE_MASK_OFFSET_MASK;
uint64_t byte_mask = (uint64_t) 0xf << (addr & PAGE_BYTE_MASK_MASK);
if ((addr & 0xf) >= 0xd)
mask |= (mask << 1);
*(uint32_t *) &page->mem[addr & 0xfff] = val;
page->dirty_mask |= mask;
page->byte_dirty_mask[byte_offset] |= byte_mask;
if (!page_in_evict_list(page) && ((page->code_present_mask & mask) || (page->byte_code_present_mask[byte_offset] & byte_mask)))
page_add_to_evict_list(page);
if ((addr & PAGE_BYTE_MASK_MASK) > (PAGE_BYTE_MASK_MASK - 3)) {
uint32_t byte_mask_2 = 0xf >> (4 - (addr & 3));
page->byte_dirty_mask[byte_offset + 1] |= byte_mask_2;
if ((page->byte_code_present_mask[byte_offset + 1] & byte_mask_2) && !page_in_evict_list(page))
page_add_to_evict_list(page);
}
}
}
#else
void
mem_write_ramb_page(uint32_t addr, uint8_t val, page_t *page)
{
if (page == NULL)
return;
# ifdef USE_DYNAREC
if ((page->mem == NULL) || (page->mem == page_ff) || (val != page->mem[addr & 0xfff]) || codegen_in_recompile) {
# else
if ((page->mem == NULL) || (page->mem == page_ff) || (val != page->mem[addr & 0xfff])) {
# endif
uint64_t mask = (uint64_t) 1 << ((addr >> PAGE_MASK_SHIFT) & PAGE_MASK_MASK);
page->dirty_mask[(addr >> PAGE_MASK_INDEX_SHIFT) & PAGE_MASK_INDEX_MASK] |= mask;
page->mem[addr & 0xfff] = val;
}
}
void
mem_write_ramw_page(uint32_t addr, uint16_t val, page_t *page)
{
if (page == NULL)
return;
# ifdef USE_DYNAREC
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint16_t *) &page->mem[addr & 0xfff]) || codegen_in_recompile) {
# else
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint16_t *) &page->mem[addr & 0xfff])) {
# endif
uint64_t mask = (uint64_t) 1 << ((addr >> PAGE_MASK_SHIFT) & PAGE_MASK_MASK);
if ((addr & 0xf) == 0xf)
mask |= (mask << 1);
page->dirty_mask[(addr >> PAGE_MASK_INDEX_SHIFT) & PAGE_MASK_INDEX_MASK] |= mask;
*(uint16_t *) &page->mem[addr & 0xfff] = val;
}
}
void
mem_write_raml_page(uint32_t addr, uint32_t val, page_t *page)
{
if (page == NULL)
return;
# ifdef USE_DYNAREC
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint32_t *) &page->mem[addr & 0xfff]) || codegen_in_recompile) {
# else
if ((page->mem == NULL) || (page->mem == page_ff) || (val != *(uint32_t *) &page->mem[addr & 0xfff])) {
# endif
uint64_t mask = (uint64_t) 1 << ((addr >> PAGE_MASK_SHIFT) & PAGE_MASK_MASK);
if ((addr & 0xf) >= 0xd)
mask |= (mask << 1);
page->dirty_mask[(addr >> PAGE_MASK_INDEX_SHIFT) & PAGE_MASK_INDEX_MASK] |= mask;
*(uint32_t *) &page->mem[addr & 0xfff] = val;
}
}
#endif
void
mem_write_ram(uint32_t addr, uint8_t val, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Write B %02X to %08X\n", val, addr);
#endif
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_ramb_page(addr, val, &pages[addr >> 12]);
} else
ram[addr] = val;
}
void
mem_write_ramw(uint32_t addr, uint16_t val, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Write W %04X to %08X\n", val, addr);
#endif
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_ramw_page(addr, val, &pages[addr >> 12]);
} else
*(uint16_t *) &ram[addr] = val;
}
void
mem_write_raml(uint32_t addr, uint32_t val, UNUSED(void *priv))
{
#ifdef ENABLE_MEM_LOG
if ((addr >= 0xa0000) && (addr <= 0xbffff))
mem_log("Write L %08X to %08X\n", val, addr);
#endif
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_raml_page(addr, val, &pages[addr >> 12]);
} else
*(uint32_t *) &ram[addr] = val;
}
static uint8_t
mem_read_remapped(uint32_t addr, UNUSED(void *priv))
{
addr = 0xA0000 + (addr - remap_start_addr);
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return ram[addr];
}
static uint16_t
mem_read_remappedw(uint32_t addr, UNUSED(void *priv))
{
addr = 0xA0000 + (addr - remap_start_addr);
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return *(uint16_t *) &ram[addr];
}
static uint32_t
mem_read_remappedl(uint32_t addr, UNUSED(void *priv))
{
addr = 0xA0000 + (addr - remap_start_addr);
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return *(uint32_t *) &ram[addr];
}
static uint8_t
mem_read_remapped2(uint32_t addr, UNUSED(void *priv))
{
addr = 0xD0000 + (addr - remap_start_addr2);
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return ram[addr];
}
static uint16_t
mem_read_remappedw2(uint32_t addr, UNUSED(void *priv))
{
addr = 0xD0000 + (addr - remap_start_addr2);
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return *(uint16_t *) &ram[addr];
}
static uint32_t
mem_read_remappedl2(uint32_t addr, UNUSED(void *priv))
{
addr = 0xD0000 + (addr - remap_start_addr2);
if (cpu_use_exec)
addreadlookup(mem_logical_addr, addr);
return *(uint32_t *) &ram[addr];
}
static void
mem_write_remapped(uint32_t addr, uint8_t val, UNUSED(void *priv))
{
uint32_t oldaddr = addr;
addr = 0xA0000 + (addr - remap_start_addr);
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_ramb_page(addr, val, &pages[oldaddr >> 12]);
} else
ram[addr] = val;
}
static void
mem_write_remappedw(uint32_t addr, uint16_t val, UNUSED(void *priv))
{
uint32_t oldaddr = addr;
addr = 0xA0000 + (addr - remap_start_addr);
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_ramw_page(addr, val, &pages[oldaddr >> 12]);
} else
*(uint16_t *) &ram[addr] = val;
}
static void
mem_write_remappedl(uint32_t addr, uint32_t val, UNUSED(void *priv))
{
uint32_t oldaddr = addr;
addr = 0xA0000 + (addr - remap_start_addr);
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_raml_page(addr, val, &pages[oldaddr >> 12]);
} else
*(uint32_t *) &ram[addr] = val;
}
static void
mem_write_remapped2(uint32_t addr, uint8_t val, UNUSED(void *priv))
{
uint32_t oldaddr = addr;
addr = 0xD0000 + (addr - remap_start_addr2);
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_ramb_page(addr, val, &pages[oldaddr >> 12]);
} else
ram[addr] = val;
}
static void
mem_write_remappedw2(uint32_t addr, uint16_t val, UNUSED(void *priv))
{
uint32_t oldaddr = addr;
addr = 0xD0000 + (addr - remap_start_addr2);
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_ramw_page(addr, val, &pages[oldaddr >> 12]);
} else
*(uint16_t *) &ram[addr] = val;
}
static void
mem_write_remappedl2(uint32_t addr, uint32_t val, UNUSED(void *priv))
{
uint32_t oldaddr = addr;
addr = 0xD0000 + (addr - remap_start_addr2);
if (cpu_use_exec) {
addwritelookup(mem_logical_addr, addr);
mem_write_raml_page(addr, val, &pages[oldaddr >> 12]);
} else
*(uint32_t *) &ram[addr] = val;
}
void
mem_invalidate_range(uint32_t start_addr, uint32_t end_addr)
{
#ifdef USE_NEW_DYNAREC
page_t *page;
start_addr &= ~PAGE_MASK_MASK;
end_addr = (end_addr + PAGE_MASK_MASK) & ~PAGE_MASK_MASK;
for (; start_addr <= end_addr; start_addr += 0x1000) {
if ((start_addr >> 12) >= pages_sz)
continue;
page = &pages[start_addr >> 12];
if (page) {
page->dirty_mask = 0xffffffffffffffffULL;
if ((page->mem != page_ff) && page->byte_dirty_mask)
memset(page->byte_dirty_mask, 0xff, 64 * sizeof(uint64_t));
if (!page_in_evict_list(page))
page_add_to_evict_list(page);
}
}
#else
uint32_t cur_addr;
start_addr &= ~PAGE_MASK_MASK;
end_addr = (end_addr + PAGE_MASK_MASK) & ~PAGE_MASK_MASK;
for (; start_addr <= end_addr; start_addr += 0x1000) {
/* Do nothing if the pages array is empty or DMA reads/writes to/from PCI device memory addresses
may crash the emulator. */
cur_addr = (start_addr >> 12);
if (cur_addr < pages_sz)
memset(pages[cur_addr].dirty_mask, 0xff, sizeof(pages[cur_addr].dirty_mask));
}
#endif
}
static __inline int
mem_mapping_access_allowed(uint32_t flags, uint16_t access)
{
int ret = 0;
if (!(access & ACCESS_DISABLED)) {
if (access & ACCESS_CACHE)
ret = (flags & MEM_MAPPING_CACHE);
else if (access & ACCESS_SMRAM)
ret = (flags & MEM_MAPPING_SMRAM);
else if (!(access & ACCESS_INTERNAL)) {
if (flags & MEM_MAPPING_IS_ROM) {
if (access & ACCESS_ROMCS)
ret = (flags & MEM_MAPPING_ROMCS);
else
ret = !(flags & MEM_MAPPING_ROMCS);
} else
ret = 1;
ret = ret && !(flags & MEM_MAPPING_INTERNAL) && !(flags & MEM_MAPPING_SMRAM);
} else
ret = !(flags & MEM_MAPPING_EXTERNAL) && !(flags & MEM_MAPPING_SMRAM);
} else {
/* Still allow SMRAM if access is DISABLED but also has CACHE and/or SMRAM flags set. */
if (access & ACCESS_CACHE)
ret = (flags & MEM_MAPPING_CACHE);
else if (access & ACCESS_SMRAM)
ret = (flags & MEM_MAPPING_SMRAM);
}
return ret;
}
void
mem_mapping_recalc(uint64_t base, uint64_t size)
{
mem_mapping_t *map;
int n;
uint64_t c;
uint8_t wp;
if (!size || (base_mapping == NULL))
return;
map = base_mapping;
/* Clear out old mappings. */
for (c = base; c < base + size; c += MEM_GRANULARITY_SIZE) {
_mem_exec[c >> MEM_GRANULARITY_BITS] = NULL;
write_mapping[c >> MEM_GRANULARITY_BITS] = NULL;
read_mapping[c >> MEM_GRANULARITY_BITS] = NULL;
write_mapping_bus[c >> MEM_GRANULARITY_BITS] = NULL;
read_mapping_bus[c >> MEM_GRANULARITY_BITS] = NULL;
}
/* Walk mapping list. */
while (map != NULL) {
/* In range? */
if (map->enable && (uint64_t) map->base < ((uint64_t) base + (uint64_t) size) &&
((uint64_t) map->base + (uint64_t) map->size) > (uint64_t) base) {
uint64_t start = (map->base < base) ? map->base : base;
uint64_t end = (((uint64_t) map->base + (uint64_t) map->size) < (base + size)) ?
((uint64_t) map->base + (uint64_t) map->size) : (base + size);
if (start < map->base)
start = map->base;
for (c = start; c < end; c += MEM_GRANULARITY_SIZE) {
/* CPU */
n = !!in_smm;
wp = _mem_wp[c >> MEM_GRANULARITY_BITS];
if (map->exec && mem_mapping_access_allowed(map->flags,
_mem_state[c >> MEM_GRANULARITY_BITS].states[n].x))
_mem_exec[c >> MEM_GRANULARITY_BITS] = map->exec + (c - map->base);
if (!wp && (map->write_b || map->write_w || map->write_l) &&
mem_mapping_access_allowed(map->flags,
_mem_state[c >> MEM_GRANULARITY_BITS].states[n].w))
write_mapping[c >> MEM_GRANULARITY_BITS] = map;
if ((map->read_b || map->read_w || map->read_l) &&
mem_mapping_access_allowed(map->flags,
_mem_state[c >> MEM_GRANULARITY_BITS].states[n].r))
read_mapping[c >> MEM_GRANULARITY_BITS] = map;
/* Bus */
n |= STATE_BUS;
wp = _mem_wp_bus[c >> MEM_GRANULARITY_BITS];
if (!wp && (map->write_b || map->write_w || map->write_l) &&
mem_mapping_access_allowed(map->flags,
_mem_state[c >> MEM_GRANULARITY_BITS].states[n].w))
write_mapping_bus[c >> MEM_GRANULARITY_BITS] = map;
if ((map->read_b || map->read_w || map->read_l) &&
mem_mapping_access_allowed(map->flags,
_mem_state[c >> MEM_GRANULARITY_BITS].states[n].r))
read_mapping_bus[c >> MEM_GRANULARITY_BITS] = map;
}
}
map = map->next;
}
flushmmucache_nopc();
#ifdef ENABLE_MEM_LOG
pclog("\nMemory map:\n");
mem_mapping_t *write = (mem_mapping_t *) -1, *read = (mem_mapping_t *) -1, *write_bus = (mem_mapping_t *) -1, *read_bus = (mem_mapping_t *) -1;
for (c = 0; c < (sizeof(write_mapping) / sizeof(write_mapping[0])); c++) {
if ((write_mapping[c] == write) && (read_mapping[c] == read) && (write_mapping_bus[c] == write_bus) && (read_mapping_bus[c] == read_bus))
continue;
write = write_mapping[c];
read = read_mapping[c];
write_bus = write_mapping_bus[c];
read_bus = read_mapping_bus[c];
pclog("%08X | ", c << MEM_GRANULARITY_BITS);
if (read) {
pclog("R%c%c%c %08X+% 8X",
read->read_b ? 'b' : ' ', read->read_w ? 'w' : ' ', read->read_l ? 'l' : ' ',
read->base, read->size);
} else {
pclog(" ");
}
if (write) {
pclog(" | W%c%c%c %08X+% 8X",
write->write_b ? 'b' : ' ', write->write_w ? 'w' : ' ', write->write_l ? 'l' : ' ',
write->base, write->size);
} else {
pclog(" | ");
}
pclog(" | %c\n", _mem_exec[c] ? 'X' : ' ');
if ((write != write_bus) || (read != read_bus)) {
pclog(" ^ bus | ");
if (read_bus) {
pclog("R%c%c%c %08X+% 8X",
read_bus->read_b ? 'b' : ' ', read_bus->read_w ? 'w' : ' ', read_bus->read_l ? 'l' : ' ',
read_bus->base, read_bus->size);
} else {
pclog(" ");
}
if (write_bus) {
pclog(" | W%c%c%c %08X+% 8X",
write_bus->write_b ? 'b' : ' ', write_bus->write_w ? 'w' : ' ', write_bus->write_l ? 'l' : ' ',
write_bus->base, write_bus->size);
} else {
pclog(" | ");
}
pclog(" |\n");
}
}
pclog("\n");
#endif
}
void
mem_set_wp(uint64_t base, uint64_t size, uint8_t flags, uint8_t wp)
{
uint64_t c;
uint64_t end = base + size;
for (c = base; c < end; c += MEM_GRANULARITY_SIZE) {
if (flags & ACCESS_BUS)
_mem_wp_bus[c >> MEM_GRANULARITY_BITS] = wp;
if (flags & ACCESS_CPU)
_mem_wp[c >> MEM_GRANULARITY_BITS] = wp;
}
mem_mapping_recalc(base, size);
}
void
mem_mapping_set(mem_mapping_t *map,
uint32_t base,
uint32_t size,
uint8_t (*read_b)(uint32_t addr, void *priv),
uint16_t (*read_w)(uint32_t addr, void *priv),
uint32_t (*read_l)(uint32_t addr, void *priv),
void (*write_b)(uint32_t addr, uint8_t val, void *priv),
void (*write_w)(uint32_t addr, uint16_t val, void *priv),
void (*write_l)(uint32_t addr, uint32_t val, void *priv),
uint8_t *exec,
uint32_t fl,
void *priv)
{
if (size != 0x00000000)
map->enable = 1;
else
map->enable = 0;
map->base = base;
map->size = size;
map->mask = (map->size ? 0xffffffff : 0x00000000);
map->read_b = read_b;
map->read_w = read_w;
map->read_l = read_l;
map->write_b = write_b;
map->write_w = write_w;
map->write_l = write_l;
map->exec = exec;
map->flags = fl;
map->priv = priv;
map->next = NULL;
mem_log("mem_mapping_add(): Linked list structure: %08X -> %08X -> %08X\n", map->prev, map, map->next);
/* If the mapping is disabled, there is no need to recalc anything. */
if (size != 0x00000000)
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_add(mem_mapping_t *map,
uint32_t base,
uint32_t size,
uint8_t (*read_b)(uint32_t addr, void *priv),
uint16_t (*read_w)(uint32_t addr, void *priv),
uint32_t (*read_l)(uint32_t addr, void *priv),
void (*write_b)(uint32_t addr, uint8_t val, void *priv),
void (*write_w)(uint32_t addr, uint16_t val, void *priv),
void (*write_l)(uint32_t addr, uint32_t val, void *priv),
uint8_t *exec,
uint32_t fl,
void *priv)
{
/* Do a sanity check */
if ((base_mapping == NULL) && (last_mapping != NULL)) {
fatal("mem_mapping_add(): NULL base mapping with non-NULL last mapping\n");
return;
} else if ((base_mapping != NULL) && (last_mapping == NULL)) {
fatal("mem_mapping_add(): Non-NULL base mapping with NULL last mapping\n");
return;
} else if ((base_mapping != NULL) && (base_mapping->prev != NULL)) {
fatal("mem_mapping_add(): Base mapping with a preceding mapping\n");
return;
} else if ((last_mapping != NULL) && (last_mapping->next != NULL)) {
fatal("mem_mapping_add(): Last mapping with a following mapping\n");
return;
}
/* Add mapping to the beginning of the list if necessary.*/
if (base_mapping == NULL)
base_mapping = map;
/* Add mapping to the end of the list.*/
if (last_mapping == NULL)
map->prev = NULL;
else {
map->prev = last_mapping;
last_mapping->next = map;
}
last_mapping = map;
mem_mapping_set(map, base, size, read_b, read_w, read_l,
write_b, write_w, write_l, exec, fl, priv);
}
void
mem_mapping_do_recalc(mem_mapping_t *map)
{
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_set_handler(mem_mapping_t *map,
uint8_t (*read_b)(uint32_t addr, void *priv),
uint16_t (*read_w)(uint32_t addr, void *priv),
uint32_t (*read_l)(uint32_t addr, void *priv),
void (*write_b)(uint32_t addr, uint8_t val, void *priv),
void (*write_w)(uint32_t addr, uint16_t val, void *priv),
void (*write_l)(uint32_t addr, uint32_t val, void *priv))
{
map->read_b = read_b;
map->read_w = read_w;
map->read_l = read_l;
map->write_b = write_b;
map->write_w = write_w;
map->write_l = write_l;
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_set_addr(mem_mapping_t *map, uint32_t base, uint32_t size)
{
/* Remove old mapping. */
map->enable = 0;
mem_mapping_recalc(map->base, map->size);
/* Set new mapping. */
map->enable = 1;
map->base = base;
map->size = size;
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_set_exec(mem_mapping_t *map, uint8_t *exec)
{
map->exec = exec;
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_set_mask(mem_mapping_t *map, uint32_t mask)
{
map->mask = mask;
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_set_p(mem_mapping_t *map, void *priv)
{
map->priv = priv;
}
void
mem_mapping_disable(mem_mapping_t *map)
{
map->enable = 0;
mem_mapping_recalc(map->base, map->size);
}
void
mem_mapping_enable(mem_mapping_t *map)
{
map->enable = 1;
mem_mapping_recalc(map->base, map->size);
}
void
mem_set_access(uint8_t bitmap, int mode, uint32_t base, uint32_t size, uint16_t access)
{
uint16_t mask;
uint16_t smstate = 0x0000;
const uint16_t smstates[4] = { 0x0000, (MEM_READ_SMRAM | MEM_WRITE_SMRAM),
MEM_READ_SMRAM_EX, (MEM_READ_DISABLED_EX | MEM_WRITE_DISABLED_EX) };
if (mode)
mask = 0x2d6b;
else
mask = 0x1084;
if (mode) {
if (mode == 1)
access = !!access;
if (mode == 3) {
if (access & ACCESS_SMRAM_X)
smstate |= MEM_EXEC_SMRAM;
if (access & ACCESS_SMRAM_R)
smstate |= MEM_READ_SMRAM_2;
if (access & ACCESS_SMRAM_W)
smstate |= MEM_WRITE_SMRAM;
} else
smstate = smstates[access & 0x07];
} else
smstate = access & 0x6f7b;
for (uint32_t c = 0; c < size; c += MEM_GRANULARITY_SIZE) {
for (uint8_t i = 0; i < 4; i++) {
if (bitmap & (1 << i)) {
_mem_state[(c + base) >> MEM_GRANULARITY_BITS].vals[i] = (_mem_state[(c + base) >> MEM_GRANULARITY_BITS].vals[i] & mask) | smstate;
}
}
#ifdef ENABLE_MEM_LOG
if (((c + base) >= 0xa0000) && ((c + base) <= 0xbffff)) {
mem_log("Set mem state for block at %08X to %04X with bitmap %02X\n",
c + base, smstate, bitmap);
}
#endif
}
mem_mapping_recalc(base, size);
}
void
mem_a20_init(void)
{
if (is286) {
mem_a20_key = mem_a20_alt = mem_a20_state = 0;
rammask = cpu_16bitbus ? 0xffffff : 0xffffffff;
if (is6117)
rammask |= 0x03000000;
flushmmucache();
#if 0
mem_a20_state = mem_a20_key | mem_a20_alt;
#endif
} else {
rammask = 0xfffff;
flushmmucache();
mem_a20_key = mem_a20_alt = mem_a20_state = 0;
}
}
/* Close all the memory mappings. */
void
mem_close(void)
{
mem_mapping_t *map = base_mapping;
mem_mapping_t *next;
while (map != NULL) {
next = map->next;
map->prev = map->next = NULL;
map = next;
}
base_mapping = last_mapping = 0;
}
static void
mem_add_ram_mapping(mem_mapping_t *mapping, uint32_t base, uint32_t size)
{
mem_mapping_add(mapping, base, size,
mem_read_ram, mem_read_ramw, mem_read_raml,
mem_write_ram, mem_write_ramw, mem_write_raml,
ram + base, MEM_MAPPING_INTERNAL, NULL);
}
static void
mem_init_ram_mapping(mem_mapping_t *mapping, uint32_t base, uint32_t size)
{
mem_set_mem_state_both(base, size, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
mem_add_ram_mapping(mapping, base, size);
}
/* Reset the memory state. */
void
mem_reset(void)
{
size_t m;
memset(page_ff, 0xff, sizeof(page_ff));
#ifdef USE_NEW_DYNAREC
if (byte_dirty_mask) {
free(byte_dirty_mask);
byte_dirty_mask = NULL;
}
if (byte_code_present_mask) {
free(byte_code_present_mask);
byte_code_present_mask = NULL;
}
#endif
/* Free the old pages array, if necessary. */
if (pages) {
free(pages);
pages = NULL;
}
if (ram != NULL) {
plat_munmap(ram, ram_size);
ram = NULL;
ram_size = 0;
}
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
if (ram2 != NULL) {
plat_munmap(ram2, ram2_size);
ram2 = NULL;
ram2_size = 0;
}
if (mem_size > 2097152)
mem_size = 2097152;
#endif
m = 1024UL * (size_t) mem_size;
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
if (mem_size > 1048576) {
ram_size = 1 << 30;
ram = (uint8_t *) plat_mmap(ram_size, 0); /* allocate and clear the RAM block of the first 1 GB */
if (ram == NULL) {
fatal("Failed to allocate primary RAM block. Make sure you have enough RAM available.\n");
return;
}
memset(ram, 0x00, ram_size);
ram2_size = m - (1 << 30);
/* Allocate 16 extra bytes of RAM to mitigate some dynarec recompiler memory access quirks. */
ram2 = (uint8_t *) plat_mmap(ram2_size + 16, 0); /* allocate and clear the RAM block above 1 GB */
if (ram2 == NULL) {
if (config_changed == 2)
fatal(EMU_NAME " must be restarted for the memory amount change to be applied.\n");
else
fatal("Failed to allocate secondary RAM block. Make sure you have enough RAM available.\n");
return;
}
memset(ram2, 0x00, ram2_size + 16);
} else
#endif
{
ram_size = m;
/* Allocate 16 extra bytes of RAM to mitigate some dynarec recompiler memory access quirks. */
ram = (uint8_t *) plat_mmap(ram_size + 16, 0); /* allocate and clear the RAM block */
if (ram == NULL) {
fatal("Failed to allocate RAM block. Make sure you have enough RAM available.\n");
return;
}
memset(ram, 0x00, ram_size + 16);
if (mem_size > 1048576)
ram2 = &(ram[1 << 30]);
}
/*
* Allocate the page table based on how much RAM we have.
* We re-allocate the table on each (hard) reset, as the
* memory amount could have changed.
*/
if (is286) {
if (cpu_16bitbus) {
/* 80286/386SX; maximum address space is 16MB. */
m = 4096;
/* ALi M6117; maximum address space is 64MB. */
if (is6117)
m <<= 2;
} else {
/* 80386DX+; maximum address space is 4GB. */
m = 1048576;
}
} else {
/* 8088/86; maximum address space is 1MB. */
m = 256;
}
addr_space_size = m;
/*
* Allocate and initialize the (new) page table.
*/
pages_sz = m;
pages = (page_t *) malloc(m * sizeof(page_t));
memset(page_lookup, 0x00, (1 << 20) * sizeof(page_t *));
memset(page_lookupp, 0x04, (1 << 20) * sizeof(uint8_t));
memset(pages, 0x00, pages_sz * sizeof(page_t));
#ifdef USE_NEW_DYNAREC
byte_dirty_mask = malloc((mem_size * 1024) / 8);
memset(byte_dirty_mask, 0, (mem_size * 1024) / 8);
byte_code_present_mask = malloc((mem_size * 1024) / 8);
memset(byte_code_present_mask, 0, (mem_size * 1024) / 8);
#endif
for (uint32_t c = 0; c < pages_sz; c++) {
if ((c << 12) >= (mem_size << 10))
pages[c].mem = page_ff;
else {
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
if (mem_size > 1048576) {
if ((c << 12) < (1 << 30))
pages[c].mem = &ram[c << 12];
else
pages[c].mem = &ram2[(c << 12) - (1 << 30)];
} else
pages[c].mem = &ram[c << 12];
#else
pages[c].mem = &ram[c << 12];
#endif
}
if (c < m) {
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
}
#ifdef USE_NEW_DYNAREC
pages[c].evict_prev = EVICT_NOT_IN_LIST;
pages[c].byte_dirty_mask = &byte_dirty_mask[c * 64];
pages[c].byte_code_present_mask = &byte_code_present_mask[c * 64];
#endif
}
memset(_mem_exec, 0x00, sizeof(_mem_exec));
memset(_mem_wp, 0x00, sizeof(_mem_wp));
memset(_mem_wp_bus, 0x00, sizeof(_mem_wp_bus));
memset(write_mapping, 0x00, sizeof(write_mapping));
memset(read_mapping, 0x00, sizeof(read_mapping));
memset(write_mapping_bus, 0x00, sizeof(write_mapping_bus));
memset(read_mapping_bus, 0x00, sizeof(read_mapping_bus));
base_mapping = last_mapping = NULL;
/* Set the entire memory space as external. */
memset(_mem_state, 0x00, sizeof(_mem_state));
/* Set the low RAM space as internal. */
mem_init_ram_mapping(&ram_low_mapping, 0x000000, (mem_size > 640) ? 0xa0000 : mem_size * 1024);
if (mem_size > 1024) {
if (cpu_16bitbus && !is6117 && mem_size > 16256)
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (16256 - 1024) * 1024);
else if (cpu_16bitbus && is6117 && mem_size > 65408)
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (65408 - 1024) * 1024);
else {
if (mem_size > 1048576) {
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (1048576 - 1024) * 1024);
mem_set_mem_state_both((1 << 30), (mem_size - 1048576) * 1024,
MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
mem_mapping_add(&ram_2gb_mapping, (1 << 30),
((mem_size - 1048576) * 1024),
mem_read_ram_2gb, mem_read_ram_2gbw, mem_read_ram_2gbl,
mem_write_ram, mem_write_ramw, mem_write_raml,
ram2, MEM_MAPPING_INTERNAL, NULL);
} else
mem_init_ram_mapping(&ram_high_mapping, 0x100000, (mem_size - 1024) * 1024);
}
}
if (mem_size > 768) {
mem_add_ram_mapping(&ram_mid_mapping, 0xa0000, 0x60000);
mem_add_ram_mapping(&ram_mid_mapping2, 0xa0000, 0x60000);
mem_mapping_disable(&ram_mid_mapping2);
}
mem_mapping_add(&ram_remapped_mapping, mem_size * 1024, 256 * 1024,
mem_read_remapped, mem_read_remappedw, mem_read_remappedl,
mem_write_remapped, mem_write_remappedw, mem_write_remappedl,
ram + 0xa0000, MEM_MAPPING_INTERNAL, NULL);
mem_mapping_disable(&ram_remapped_mapping);
/* Mapping for SiS 471 relocation which relocates A0000-BFFFF, D0000-EFFFF, which is non-contiguous. */
mem_mapping_add(&ram_remapped_mapping2, mem_size * 1024, 256 * 1024,
mem_read_remapped2, mem_read_remappedw2, mem_read_remappedl2,
mem_write_remapped2, mem_write_remappedw2, mem_write_remappedl2,
ram + 0xd0000, MEM_MAPPING_INTERNAL, NULL);
mem_mapping_disable(&ram_remapped_mapping2);
mem_a20_init();
#ifdef USE_NEW_DYNAREC
purgable_page_list_head = 0;
purgeable_page_count = 0;
#endif
}
void
mem_init(void)
{
/* Perform a one-time init. */
ram = rom = NULL;
ram2 = NULL;
pages = NULL;
/* Allocate the lookup tables. */
page_lookup = (page_t **) malloc((1 << 20) * sizeof(page_t *));
page_lookupp = (uint8_t *) malloc((1 << 20) * sizeof(uint8_t));
readlookup2 = malloc((1 << 20) * sizeof(uintptr_t));
readlookupp = malloc((1 << 20) * sizeof(uint8_t));
writelookup2 = malloc((1 << 20) * sizeof(uintptr_t));
writelookupp = malloc((1 << 20) * sizeof(uint8_t));
}
static void
umc_page_recalc(uint32_t c, int set)
{
if (set) {
pages[c].mem = &ram[(c & 0xff) << 12];
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
} else {
pages[c].mem = page_ff;
pages[c].write_b = NULL;
pages[c].write_w = NULL;
pages[c].write_l = NULL;
}
#ifdef USE_NEW_DYNAREC
pages[c].evict_prev = EVICT_NOT_IN_LIST;
pages[c].byte_dirty_mask = &byte_dirty_mask[(c & 0xff) * 64];
pages[c].byte_code_present_mask = &byte_code_present_mask[(c & 0xff) * 64];
#endif
}
void
umc_smram_recalc(uint32_t start, int set)
{
for (uint32_t c = start; c < (start + 0x0020); c++)
umc_page_recalc(c, set);
}
void
mem_remap_top(int kb)
{
uint32_t c;
uint32_t start = (mem_size >= 1024) ? mem_size : 1024;
int offset;
int size = mem_size - 640;
int set = 1;
static int old_kb = 0;
int sis_mode = 0;
uint32_t start_addr = 0;
uint32_t addr = 0;
mem_log("MEM: remapping top %iKB (mem=%i)\n", kb, mem_size);
if (mem_size <= 640)
return;
/* SiS 471 special mode. */
if (kb == -256) {
kb = 256;
sis_mode = 1;
}
/* Do not remap if we're have more than (16 MB - RAM) memory. */
if ((kb != 0) && (mem_size >= (16384 - kb)))
return;
if (kb == 0) {
kb = old_kb;
set = 0;
} else
old_kb = kb;
if (size > kb)
size = kb;
remap_start_addr = start << 10;
remap_start_addr2 = (start << 10) + 0x00020000;
for (c = ((start * 1024) >> 12); c < (((start + size) * 1024) >> 12); c++) {
offset = c - ((start * 1024) >> 12);
/* Use A0000-BFFFF, D0000-EFFFF instead of C0000-DFFFF, E0000-FFFFF. */
addr = 0xa0000 + (offset << 12);
if (sis_mode) {
/* A0000-DFFFF -> A0000-BFFFF, D0000-EFFFF */
if (addr >= 0x000c0000)
addr += 0x00010000;
}
if (start_addr == 0)
start_addr = addr;
pages[c].mem = set ? &ram[addr] : page_ff;
pages[c].write_b = set ? mem_write_ramb_page : NULL;
pages[c].write_w = set ? mem_write_ramw_page : NULL;
pages[c].write_l = set ? mem_write_raml_page : NULL;
#ifdef USE_NEW_DYNAREC
pages[c].evict_prev = EVICT_NOT_IN_LIST;
pages[c].byte_dirty_mask = &byte_dirty_mask[(addr >> 12) * 64];
pages[c].byte_code_present_mask = &byte_code_present_mask[(addr >> 12) * 64];
#endif
}
mem_set_mem_state_both(start * 1024, size * 1024, set ? (MEM_READ_INTERNAL | MEM_WRITE_INTERNAL) : (MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL));
for (c = 0xa0; c < 0xf0; c++) {
if ((c >= 0xc0) && (c <= 0xcf))
continue;
if (sis_mode || ((c << 12) >= (mem_size << 10)))
pages[c].mem = page_ff;
else {
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
if (mem_size > 1048576) {
if ((c << 12) < (1 << 30))
pages[c].mem = &ram[c << 12];
else
pages[c].mem = &ram2[(c << 12) - (1 << 30)];
} else
pages[c].mem = &ram[c << 12];
#else
pages[c].mem = &ram[c << 12];
#endif
}
if (!sis_mode && (c < addr_space_size)) {
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
} else {
pages[c].write_b = NULL;
pages[c].write_w = NULL;
pages[c].write_l = NULL;
}
#ifdef USE_NEW_DYNAREC
pages[c].evict_prev = EVICT_NOT_IN_LIST;
pages[c].byte_dirty_mask = &byte_dirty_mask[c * 64];
pages[c].byte_code_present_mask = &byte_code_present_mask[c * 64];
#endif
}
if (set) {
if (sis_mode) {
mem_mapping_set_addr(&ram_remapped_mapping, start * 1024, 0x00020000);
mem_mapping_set_exec(&ram_remapped_mapping, ram + 0x000a0000);
mem_mapping_set_addr(&ram_remapped_mapping2, (start * 1024) + 0x00020000, 0x00020000);
mem_mapping_set_exec(&ram_remapped_mapping2, ram + 0x000d0000);
mem_mapping_set_addr(&ram_mid_mapping, 0x000c0000, 0x00010000);
mem_mapping_set_exec(&ram_mid_mapping, ram + 0x000c0000);
mem_mapping_set_addr(&ram_mid_mapping2, 0x000f0000, 0x00010000);
mem_mapping_set_exec(&ram_mid_mapping2, ram + 0x000f0000);
} else {
mem_mapping_set_addr(&ram_remapped_mapping, start * 1024, size * 1024);
mem_mapping_set_exec(&ram_remapped_mapping, ram + start_addr);
mem_mapping_disable(&ram_remapped_mapping2);
mem_mapping_set_addr(&ram_mid_mapping, 0x000a0000, 0x00060000);
mem_mapping_set_exec(&ram_mid_mapping, ram + 0x000a0000);
mem_mapping_disable(&ram_mid_mapping2);
}
} else {
mem_mapping_disable(&ram_remapped_mapping);
mem_mapping_disable(&ram_remapped_mapping2);
mem_mapping_set_addr(&ram_mid_mapping, 0x000a0000, 0x00060000);
mem_mapping_set_exec(&ram_mid_mapping, ram + 0x000a0000);
mem_mapping_disable(&ram_mid_mapping2);
}
flushmmucache();
}
void
mem_reset_page_blocks(void)
{
if (pages == NULL)
return;
for (uint32_t c = 0; c < pages_sz; c++) {
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
#ifdef USE_NEW_DYNAREC
pages[c].block = BLOCK_INVALID;
pages[c].block_2 = BLOCK_INVALID;
pages[c].head = BLOCK_INVALID;
#else
pages[c].block[0] = pages[c].block[1] = pages[c].block[2] = pages[c].block[3] = NULL;
pages[c].block_2[0] = pages[c].block_2[1] = pages[c].block_2[2] = pages[c].block_2[3] = NULL;
pages[c].head = NULL;
#endif
}
}
void
mem_a20_recalc(void)
{
int state;
if (!is286) {
rammask = 0xfffff;
flushmmucache();
mem_a20_key = mem_a20_alt = mem_a20_state = 0;
return;
}
state = mem_a20_key | mem_a20_alt;
if (state && !mem_a20_state) {
rammask = cpu_16bitbus ? 0xffffff : 0xffffffff;
if (is6117)
rammask |= 0x03000000;
flushmmucache();
} else if (!state && mem_a20_state) {
rammask = cpu_16bitbus ? 0xefffff : 0xffefffff;
if (is6117)
rammask |= 0x03000000;
flushmmucache();
}
mem_a20_state = state;
}