/*
 * 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.
 *
 *		Definitions for the memory interface.
 *
 *
 *
 * Authors:	Sarah Walker, <tommowalker@tommowalker.co.uk>
 *		Fred N. van Kempen, <decwiz@yahoo.com>
 *		Miran Grca, <mgrca8@gmail.com>
 *
 *		Copyright 2008-2020 Sarah Walker.
 *		Copyright 2017-2020 Fred N. van Kempen.
 *		Copyright 2016-2020 Miran Grca.
 */

#ifndef EMU_MEM_H
# define EMU_MEM_H


#define MEM_MAP_TO_SHADOW_RAM_MASK 1
#define MEM_MAP_TO_RAM_ADDR_MASK   2

#define STATE_CPU		 0
#define STATE_BUS		 2

#define ACCESS_CPU		    1	/* Update CPU non-SMM access. */
#define ACCESS_CPU_SMM		    2	/* Update CPU SMM access. */
#define ACCESS_BUS		    4	/* Update bus access. */
#define ACCESS_BUS_SMM		    8	/* Update bus SMM access. */
#define ACCESS_NORMAL		    5	/* Update CPU and bus non-SMM accesses. */
#define ACCESS_SMM		   10	/* Update CPU and bus SMM accesses. */
#define ACCESS_CPU_BOTH		    3	/* Update CPU non-SMM and SMM accesses. */
#define ACCESS_BUS_BOTH		   12	/* Update bus non-SMM and SMM accesses. */
#define ACCESS_ALL		   15	/* Update all accesses. */

#define ACCESS_INTERNAL		    1
#define ACCESS_ROMCS		    2
#define ACCESS_SMRAM		    4
#define ACCESS_CACHE		    8
#define ACCESS_DISABLED		   16

#define ACCESS_X_INTERNAL	    1
#define ACCESS_X_ROMCS		    2
#define ACCESS_X_SMRAM		    4
#define ACCESS_X_CACHE		    8
#define ACCESS_X_DISABLED	   16
#define ACCESS_W_INTERNAL	   32
#define ACCESS_W_ROMCS		   64
#define ACCESS_W_SMRAM		  128
#define ACCESS_W_CACHE		  256
#define ACCESS_W_DISABLED	  512
#define ACCESS_R_INTERNAL	 1024
#define ACCESS_R_ROMCS		 2048
#define ACCESS_R_SMRAM		 4096
#define ACCESS_R_CACHE		 8192
#define ACCESS_R_DISABLED	16384

#define ACCESS_EXECUTE		    0
#define ACCESS_READ		    1
#define ACCESS_WRITE		    2

#define ACCESS_SMRAM_OFF	    0
#define ACCESS_SMRAM_X		    1
#define ACCESS_SMRAM_W		    2
#define ACCESS_SMRAM_WX		    3
#define ACCESS_SMRAM_R		    4
#define ACCESS_SMRAM_RX		    5
#define ACCESS_SMRAM_RW		    6
#define ACCESS_SMRAM_RWX	    7

/* Conversion #define's - we need these to seamlessly convert the old mem_set_mem_state() calls to
   the new stuff in order to make this a drop in replacement.

   Read here includes execute access since the old code also used read access for execute access,
   with some exceptions. */

#define MEM_READ_DISABLED	(ACCESS_X_DISABLED | ACCESS_R_DISABLED)
#define MEM_READ_INTERNAL	(ACCESS_X_INTERNAL | ACCESS_R_INTERNAL)
#define MEM_READ_EXTERNAL	0
/* These two are going to be identical - on real hardware, chips that don't care about ROMCS#,
   are not magically disabled. */
#define MEM_READ_ROMCS		(ACCESS_X_ROMCS | ACCESS_R_ROMCS)
#define MEM_READ_EXTANY		MEM_READ_ROMCS
/* Internal execute access, external read access. */
#define MEM_READ_EXTERNAL_EX	0
#define MEM_READ_SMRAM		(ACCESS_X_SMRAM | ACCESS_R_SMRAM)
#define MEM_READ_CACHE		(ACCESS_X_CACHE | ACCESS_R_CACHE)
#define MEM_READ_SMRAM_EX	(ACCESS_X_SMRAM)
#define MEM_EXEC_SMRAM		MEM_READ_SMRAM_EX
#define MEM_READ_SMRAM_2	(ACCESS_R_SMRAM)
/* Theese two are going to be identical. */
#define MEM_READ_DISABLED_EX	MEM_READ_DISABLED
#define MEM_READ_MASK		0x7c1f

#define MEM_WRITE_DISABLED	(ACCESS_W_DISABLED)
#define MEM_WRITE_INTERNAL	(ACCESS_W_INTERNAL)
#define MEM_WRITE_EXTERNAL	0
/* These two are going to be identical - on real hardware, chips that don't care about ROMCS#,
   are not magically disabled. */
#define MEM_WRITE_ROMCS		(ACCESS_W_ROMCS)
#define MEM_WRITE_EXTANY	(ACCESS_W_ROMCS)
#define MEM_WRITE_SMRAM		(ACCESS_W_SMRAM)
#define MEM_WRITE_CACHE		(ACCESS_W_CACHE)
/* Theese two are going to be identical. */
#define MEM_WRITE_DISABLED_EX	MEM_READ_DISABLED
#define MEM_WRITE_MASK		0x03e0

#define MEM_MAPPING_EXTERNAL	 1	/* On external bus (ISA/PCI). */
#define MEM_MAPPING_INTERNAL	 2	/* On internal bus (RAM). */
#define MEM_MAPPING_ROM_WS	 4	/* Executing from ROM may involve additional wait states. */
#define MEM_MAPPING_IS_ROM	 8	/* Responds to ROMCS#. */
#define MEM_MAPPING_ROM		(MEM_MAPPING_ROM_WS | MEM_MAPPING_IS_ROM)
#define MEM_MAPPING_ROMCS	16	/* If it responds to ROMCS#, it requires ROMCS# asserted. */
#define MEM_MAPPING_SMRAM	32	/* On internal bus (RAM) but SMRAM. */
#define MEM_MAPPING_CACHE	64	/* Cache or MTRR - please avoid such mappings unless
					   stricly necessary (eg. for CoreBoot). */

/* #define's for memory granularity, currently 4k, less does
   not work because of internal 4k pages. */
#define MEM_GRANULARITY_BITS	12
#define MEM_GRANULARITY_SIZE	(1 << MEM_GRANULARITY_BITS)
#define MEM_GRANULARITY_HBOUND	(MEM_GRANULARITY_SIZE - 2)
#define MEM_GRANULARITY_QBOUND	(MEM_GRANULARITY_SIZE - 4)
#define MEM_GRANULARITY_MASK	(MEM_GRANULARITY_SIZE - 1)
#define MEM_GRANULARITY_HMASK	((1 << (MEM_GRANULARITY_BITS - 1)) - 1)
#define MEM_GRANULARITY_QMASK	((1 << (MEM_GRANULARITY_BITS - 2)) - 1)
#define MEM_GRANULARITY_PMASK	((1 << (MEM_GRANULARITY_BITS - 3)) - 1)
#define MEM_MAPPINGS_NO		((0x100000 >> MEM_GRANULARITY_BITS) << 12)
#define MEM_GRANULARITY_PAGE	(MEM_GRANULARITY_MASK & ~0xfff)
#define MEM_GRANULARITY_BASE	(~MEM_GRANULARITY_MASK)

/* Compatibility #defines. */
#define mem_set_state(smm, mode, base, size, access) \
	mem_set_access((smm ? ACCESS_SMM : ACCESS_NORMAL), mode, base, size, access)
#define mem_set_mem_state_common(smm, base, size, access) \
	mem_set_access((smm ? ACCESS_SMM : ACCESS_NORMAL), 0, base, size, access)
#define mem_set_mem_state(base, size, access) \
	mem_set_access(ACCESS_NORMAL, 0, base, size, access)
#define mem_set_mem_state_smm(base, size, access) \
	mem_set_access(ACCESS_SMM, 0, base, size, access)
#define mem_set_mem_state_both(base, size, access) \
	mem_set_access(ACCESS_ALL, 0, base, size, access)
#define mem_set_mem_state_cpu_both(base, size, access) \
	mem_set_access(ACCESS_CPU_BOTH, 0, base, size, access)
#define mem_set_mem_state_bus_both(base, size, access) \
	mem_set_access(ACCESS_BUS_BOTH, 0, base, size, access)
#define mem_set_mem_state_smram(smm, base, size, is_smram) \
	mem_set_access((smm ? ACCESS_SMM : ACCESS_NORMAL), 1, base, size, is_smram)
#define mem_set_mem_state_smram_ex(smm, base, size, is_smram) \
	mem_set_access((smm ? ACCESS_SMM : ACCESS_NORMAL), 2, base, size, is_smram)
#define mem_set_access_smram_cpu(smm, base, size, is_smram) \
	mem_set_access((smm ? ACCESS_CPU_SMM : ACCESS_CPU), 1, base, size, is_smram)
#define mem_set_access_smram_bus(smm, base, size, is_smram) \
	mem_set_access((smm ? ACCESS_BUS_SMM : ACCESS_BUS), 1, base, size, is_smram)
#define flushmmucache_cr3 \
	flushmmucache_nopc


typedef struct {
    uint16_t	x	:5,
		w	:5,
		r	:5,
		pad	:1;
} state_t;

typedef union {
    uint16_t	vals[4];
    state_t	states[4];
} mem_state_t;

typedef struct _mem_mapping_ {
    struct _mem_mapping_ *prev, *next;

    int		enable;

    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	flags;

    /* There is never a needed to pass a pointer to the mapping itself, it is much preferable to
       prepare a structure with the requires data (usually, the base address and mask) instead. */
    void	*p;		/* backpointer to device */
} mem_mapping_t;

#ifdef USE_NEW_DYNAREC
extern uint64_t *byte_dirty_mask;
extern uint64_t *byte_code_present_mask;

#define PAGE_BYTE_MASK_SHIFT 6
#define PAGE_BYTE_MASK_OFFSET_MASK 63
#define PAGE_BYTE_MASK_MASK  63

#define EVICT_NOT_IN_LIST ((uint32_t)-1)
typedef struct page_t
{
    void	(*write_b)(uint32_t addr, uint8_t val, struct page_t *p);
    void	(*write_w)(uint32_t addr, uint16_t val, struct page_t *p);
    void	(*write_l)(uint32_t addr, uint32_t val, struct page_t *p);

    uint8_t	*mem;

    uint16_t	block, block_2;

    /*Head of codeblock tree associated with this page*/
    uint16_t head;

    uint64_t code_present_mask, dirty_mask;

    uint32_t evict_prev, evict_next;

    uint64_t *byte_dirty_mask;
    uint64_t *byte_code_present_mask;
} page_t;

extern uint32_t purgable_page_list_head;
static inline int
page_in_evict_list(page_t *p)
{
    return (p->evict_prev != EVICT_NOT_IN_LIST);
}
void page_remove_from_evict_list(page_t *p);
void page_add_to_evict_list(page_t *p);
#else
typedef struct _page_ {
    void	(*write_b)(uint32_t addr, uint8_t val, struct _page_ *p);
    void	(*write_w)(uint32_t addr, uint16_t val, struct _page_ *p);
    void	(*write_l)(uint32_t addr, uint32_t val, struct _page_ *p);

    uint8_t	*mem;

    uint64_t	code_present_mask[4],
		dirty_mask[4];

    struct codeblock_t *block[4], *block_2[4];

    /*Head of codeblock tree associated with this page*/
    struct codeblock_t *head;
} page_t;
#endif


extern uint8_t		*ram, *ram2;
extern uint32_t		rammask;

extern uint8_t		*rom;
extern uint32_t		biosmask, biosaddr;

extern int		readlookup[256];
extern uintptr_t *	readlookup2;
extern uintptr_t	old_rl2;
extern uint8_t		uncached;
extern int		readlnext;
extern int		writelookup[256];
extern uintptr_t *	writelookup2;
extern int		writelnext;
extern uint32_t		ram_mapped_addr[64];

extern mem_mapping_t	ram_low_mapping,
#if 1
			ram_mid_mapping,
#endif
			ram_remapped_mapping,
			ram_high_mapping,
			ram_2gb_mapping,
			bios_mapping,
			bios_high_mapping;

extern uint32_t		mem_logical_addr;

extern page_t		*pages,
			**page_lookup;

extern uint32_t		get_phys_virt, get_phys_phys;

extern int		shadowbios,
			shadowbios_write;
extern int		readlnum,
			writelnum;

extern int		memspeed[11];

extern int		mmu_perm;
extern uint8_t		high_page;		/* if a high (> 4 gb) page was detected */

extern int		mem_a20_state,
			mem_a20_alt,
			mem_a20_key;


extern uint8_t	read_mem_b(uint32_t addr);
extern uint16_t	read_mem_w(uint32_t addr);
extern void	write_mem_b(uint32_t addr, uint8_t val);
extern void	write_mem_w(uint32_t addr, uint16_t val);

extern uint8_t	readmembl(uint32_t addr);
extern void	writemembl(uint32_t addr, uint8_t val);
extern uint16_t	readmemwl(uint32_t addr);
extern void	writememwl(uint32_t addr, uint16_t val);
extern uint32_t	readmemll(uint32_t addr);
extern void	writememll(uint32_t addr, uint32_t val);
extern uint64_t	readmemql(uint32_t addr);
extern void	writememql(uint32_t addr, uint64_t val);

extern uint8_t	readmembl_no_mmut(uint32_t addr, uint32_t a64);
extern void	writemembl_no_mmut(uint32_t addr, uint32_t a64, uint8_t val);
extern uint16_t	readmemwl_no_mmut(uint32_t addr, uint32_t *a64);
extern void	writememwl_no_mmut(uint32_t addr, uint32_t *a64, uint16_t val);
extern uint32_t	readmemll_no_mmut(uint32_t addr, uint32_t *a64);
extern void	writememll_no_mmut(uint32_t addr, uint32_t *a64, uint32_t val);

extern void	do_mmutranslate(uint32_t addr, uint32_t *a64, int num, int write);

extern uint8_t	*getpccache(uint32_t a);
extern uint64_t	mmutranslatereal(uint32_t addr, int rw);
extern uint32_t	mmutranslatereal32(uint32_t addr, int rw);
extern void	addreadlookup(uint32_t virt, uint32_t phys);
extern void	addwritelookup(uint32_t virt, uint32_t phys);

extern void	mem_mapping_set(mem_mapping_t *,
                    uint32_t base,
                    uint32_t size,
                    uint8_t  (*read_b)(uint32_t addr, void *p),
                    uint16_t (*read_w)(uint32_t addr, void *p),
                    uint32_t (*read_l)(uint32_t addr, void *p),
                    void (*write_b)(uint32_t addr, uint8_t  val, void *p),
                    void (*write_w)(uint32_t addr, uint16_t val, void *p),
                    void (*write_l)(uint32_t addr, uint32_t val, void *p),
                    uint8_t *exec,
                    uint32_t flags,
                    void *p);
extern void	mem_mapping_add(mem_mapping_t *,
                    uint32_t base,
                    uint32_t size,
                    uint8_t  (*read_b)(uint32_t addr, void *p),
                    uint16_t (*read_w)(uint32_t addr, void *p),
                    uint32_t (*read_l)(uint32_t addr, void *p),
                    void (*write_b)(uint32_t addr, uint8_t  val, void *p),
                    void (*write_w)(uint32_t addr, uint16_t val, void *p),
                    void (*write_l)(uint32_t addr, uint32_t val, void *p),
                    uint8_t *exec,
                    uint32_t flags,
                    void *p);

extern void	mem_mapping_set_handler(mem_mapping_t *,
                    uint8_t  (*read_b)(uint32_t addr, void *p),
                    uint16_t (*read_w)(uint32_t addr, void *p),
                    uint32_t (*read_l)(uint32_t addr, void *p),
                    void (*write_b)(uint32_t addr, uint8_t  val, void *p),
                    void (*write_w)(uint32_t addr, uint16_t val, void *p),
                    void (*write_l)(uint32_t addr, uint32_t val, void *p));

extern void	mem_mapping_set_p(mem_mapping_t *, void *p);

extern void	mem_mapping_set_addr(mem_mapping_t *,
				     uint32_t base, uint32_t size);
extern void	mem_mapping_set_exec(mem_mapping_t *, uint8_t *exec);
extern void	mem_mapping_disable(mem_mapping_t *);
extern void	mem_mapping_enable(mem_mapping_t *);
extern void	mem_mapping_recalc(uint64_t base, uint64_t size);

extern void	mem_set_access(uint8_t bitmap, int mode, uint32_t base, uint32_t size, uint16_t access);

extern uint8_t	mem_readb_phys(uint32_t addr);
extern uint16_t	mem_readw_phys(uint32_t addr);
extern uint32_t	mem_readl_phys(uint32_t addr);
extern void	mem_read_phys(void *dest, uint32_t addr, int tranfer_size);
extern void	mem_writeb_phys(uint32_t addr, uint8_t val);
extern void	mem_writew_phys(uint32_t addr, uint16_t val);
extern void	mem_writel_phys(uint32_t addr, uint32_t val);
extern void	mem_write_phys(void *src, uint32_t addr, int tranfer_size);

extern uint8_t	mem_read_ram(uint32_t addr, void *priv);
extern uint16_t	mem_read_ramw(uint32_t addr, void *priv);
extern uint32_t	mem_read_raml(uint32_t addr, void *priv);
extern void	mem_write_ram(uint32_t addr, uint8_t val, void *priv);
extern void	mem_write_ramw(uint32_t addr, uint16_t val, void *priv);
extern void	mem_write_raml(uint32_t addr, uint32_t val, void *priv);

extern uint8_t	mem_read_ram_2gb(uint32_t addr, void *priv);
extern uint16_t	mem_read_ram_2gbw(uint32_t addr, void *priv);
extern uint32_t	mem_read_ram_2gbl(uint32_t addr, void *priv);
extern void	mem_write_ram_2gb(uint32_t addr, uint8_t val, void *priv);
extern void	mem_write_ram_2gbw(uint32_t addr, uint16_t val, void *priv);
extern void	mem_write_ram_2gbl(uint32_t addr, uint32_t val, void *priv);

extern int	mem_addr_is_ram(uint32_t addr);

extern uint64_t	mmutranslate_noabrt(uint32_t addr, int rw);

extern void	mem_invalidate_range(uint32_t start_addr, uint32_t end_addr);

extern void	mem_write_ramb_page(uint32_t addr, uint8_t val, page_t *p);
extern void	mem_write_ramw_page(uint32_t addr, uint16_t val, page_t *p);
extern void	mem_write_raml_page(uint32_t addr, uint32_t val, page_t *p);
extern void	mem_flush_write_page(uint32_t addr, uint32_t virt);

extern void	mem_reset_page_blocks(void);

extern void     flushmmucache(void);
extern void	flushmmucache_nopc(void);
extern void     mmu_invalidate(uint32_t addr);

extern void	mem_a20_init(void);
extern void	mem_a20_recalc(void);

extern void	mem_init(void);
extern void	mem_close(void);
extern void	mem_reset(void);
extern void	mem_remap_top(int kb);


#ifdef EMU_CPU_H
static __inline uint32_t get_phys(uint32_t addr)
{
    uint64_t pa64;

    if (!((addr ^ get_phys_virt) & ~0xfff))
	return get_phys_phys | (addr & 0xfff);

    get_phys_virt = addr;

    if (!(cr0 >> 31)) {
	get_phys_phys = (addr & rammask) & ~0xfff;
	return addr & rammask;
    }

    if (((int) (readlookup2[addr >> 12])) != -1)
	get_phys_phys = ((uintptr_t)readlookup2[addr >> 12] + (addr & ~0xfff)) - (uintptr_t)ram;
    else {
	pa64 = mmutranslatereal(addr, 0);
	if (pa64 > 0xffffffffULL)
		get_phys_phys = 0xffffffff;
	else
		get_phys_phys = (uint32_t) pa64;
	get_phys_phys = (get_phys_phys & rammask) & ~0xfff;
	if (!cpu_state.abrt && mem_addr_is_ram(get_phys_phys))
		addreadlookup(get_phys_virt, get_phys_phys);
    }

    return get_phys_phys | (addr & 0xfff);
}


static __inline uint32_t get_phys_noabrt(uint32_t addr)
{
    uint64_t phys_addr;
    uint32_t phys_addr32;

    if (!(cr0 >> 31))
	return addr & rammask;

    if (((int) (readlookup2[addr >> 12])) != -1)
	return ((uintptr_t)readlookup2[addr >> 12] + addr) - (uintptr_t)ram;

    phys_addr = mmutranslate_noabrt(addr, 0);
    phys_addr32 = (uint32_t) phys_addr;
    if ((phys_addr != 0xffffffffffffffffULL) && (phys_addr <= 0xffffffffULL) &&
	mem_addr_is_ram(phys_addr32 & rammask))
	addreadlookup(addr, phys_addr32 & rammask);

    if (phys_addr > 0xffffffffULL)
	phys_addr32 = 0xffffffff;

    return phys_addr32;
}
#endif


#endif	/*EMU_MEM_H*/
