#include <sys/mman.h>
#include <stdint.h>
#include "riscos.h"
#include "heap.h"

// Keep global so that all RISC OS modules can access it.
MEMC *memc;

// If start==0, then host operating system will suggest an address
struct MEMC::memchunk MEMC::Alloc (const char *name,
				   uint32_t start,
				   uint32_t length, uint32_t maxsize)
{
  struct memchunk m;
  strcpy (m.name, name);

  int mapflags = MAP_PRIVATE | MAP_ANONYMOUS;
  if (start)
    mapflags |= MAP_FIXED;

  long retval = target_mmap (start, length,
			     PROT_READ | PROT_WRITE,
			     mapflags,
			     -1, 0);
  if (retval == -1)
    {
      perror ("MEMC: Cannot allocate page");
      exit (-1);
    }

  m.base = (void *) retval;
  m.size = length;
  m.maxsize = maxsize;
  return m;
}

void MEMC::Resize (int chunk, uint32_t extent)
{
  int mapflags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;

  long retval;

  if (extent < memory_map[chunk].size)
    {
      //      printf ("Resize (unmap): base=%08x, size=%08x, new size=%08x\n", 
      //	      (uint32_t) memory_map[chunk].base + extent,
      //	      memory_map[chunk].size - extent, extent);
      retval = target_munmap ((uint32_t) memory_map[chunk].base + extent,
			      memory_map[chunk].size - extent);
    }
  else
    {
      //printf ("Resize (map)  : base=%08x, size=%08x, extent=%08x\n", 
      //      memory_map[chunk].base, memory_map[chunk].size, extent);
      retval = target_mmap ((uint32_t) memory_map[chunk].base
			    + memory_map[chunk].size,
			    extent - memory_map[chunk].size,
			    PROT_READ | PROT_WRITE,
			    mapflags,
			    -1, 0);
    }
  if (retval == -1)
    {
      perror ("MEMC::Resize: Cannot allocate page");
      exit (-1);
    }

  memory_map[chunk].size = extent;
  memory_map[chunk].maxsize = extent;
}

void MEMC::Free (uint32_t start)
{
}

MEMC::MEMC (int os_version)
{
  const int mb = 1024 * 1024;
  const int kb = 1024;

  for (int x = 0; x < 16; x++)
    memory_map[x].base = NULL;

  // Build the RISC OS memory map.

  // Some RISC OS modules rely on a zero page.
  // We cannot map the zero page to address zero, so we must allocate
  // it somewhere and fudge QEMU to do address computations.
  memory_map[MEMC_CHUNK_ZERO] = Alloc ("Zero Page", 0, 32 * kb, 32 * kb);

  if (os_version == 0)
    {
      // The QEMU/RISC OS machine uses no strict memory mapping scheme
      // and instead just uses whatever memory addresses the host
      // operating system provides.

      memory_map[MEMC_CHUNK_ROM] = Alloc ("ROM", 0, 4 * mb, 4 * mb);
      memory_map[MEMC_CHUNK_RMA] = Alloc ("RMA", 0, 2 * mb, 128 * mb);
      memory_map[MEMC_CHUNK_SYSTEM] = Alloc ("System Heap",
					     0, 64 * kb, 128 * mb);
      memory_map[MEMC_CHUNK_SCREEN] = Alloc ("Screen",
					     0x4f40000, 32 * kb, 0x200000);
      memory_map[MEMC_CHUNK_SPRITE] = Alloc ("Sprite",
					     0x8200000, 32 * kb, 0x2200000);
      memory_map[MEMC_CHUNK_FONT] = Alloc ("Font",
					   0xb400000, 32 * kb, 0x2200000);
      memory_map[MEMC_CHUNK_RAMFS] = Alloc ("RAMFS",
					    0xa400000, 32 * kb, 0x1000000);
      memory_map[MEMC_CHUNK_FREE] = Alloc ("Freespace",
					   0x6000000, 32 * kb, 0x2200000);
    }
  else if (os_version == 370)
    {
      // Logical memory map, for RISC OS 3.7
      memory_map[MEMC_CHUNK_ROM] = Alloc ("ROM", 0x3400000, 4 * mb, 4 * mb);
      memory_map[MEMC_CHUNK_RMA] = Alloc ("RMA", 0x2100000, 2 * mb, 0xb00000);
      memory_map[MEMC_CHUNK_SYSTEM] = Alloc ("System Heap",
					     0x1c02000, 64 * kb, 0x1fe000);
      memory_map[MEMC_CHUNK_SCREEN] = Alloc ("Screen",
					     0x4f40000, 32 * kb, 0x200000);
      memory_map[MEMC_CHUNK_SPRITE] = Alloc ("Sprite",
					     0x8200000, 32 * kb, 0x2200000);
      memory_map[MEMC_CHUNK_FONT] = Alloc ("Font",
					   0xb400000, 32 * kb, 0x2200000);
      memory_map[MEMC_CHUNK_RAMFS] = Alloc ("RAMFS",
					    0xa400000, 32 * kb, 0x1000000);
      memory_map[MEMC_CHUNK_FREE] = Alloc ("Freespace",
					   0x6000000, 32 * kb, 0x2200000);
    }

  // Application memory is always allocated starting at address 0x8000.
  memory_map[MEMC_CHUNK_APP] = Alloc ("Application",
				      0x8000, 64 * mb, 64 * mb);

  // Initialise heap managed areas.
  rma_heap = new Heap ();
  system_heap = new Heap ();

  rma_heap->Initialise (memory_map[MEMC_CHUNK_RMA].base,
			memory_map[MEMC_CHUNK_RMA].size);
  system_heap->Initialise (memory_map[MEMC_CHUNK_SYSTEM].base,
			   memory_map[MEMC_CHUNK_SYSTEM].size);

  // Reserve 2Kbytes for the SVC, IRQ and FIQ stacks
  svc_stack = system_heap->Alloc (2048);
  irq_stack = system_heap->Alloc (2048);
  fiq_stack = system_heap->Alloc (2048);

  svc_stack_top = (void *) ((uint32_t) svc_stack + 2044);
  irq_stack_top = (void *) ((uint32_t) irq_stack + 2044);
  fiq_stack_top = (void *) ((uint32_t) fiq_stack + 2044);

  // Each instantiated module has 4 bytes reserved for private use.
  // We allocate it out of this buffer.
  module_private_word = system_heap->Alloc (1024);
  memset (module_private_word, 0xff, 1024);

  // See comment in main.cc::cpu_loop ().
  swi_code_return = system_heap->Alloc (16);
  {
    // Not really the right place to do this, but sort that out later.
    // Just build an SWI instruction with code 0xDEADBE.
    *((uint32_t *) swi_code_return) = 0xef000000 | 0xdeadbe;
  }

  // A small buffer for passing errors from SWI to application
  error_buf = system_heap->Alloc (260);

#if 0
  printf ("MEMC memory map:\n");
  printf ("%16s | Base addr  | %13s | %13s\n", "Area name", "Size", "Max Size");
  printf ("--------------------------------------------------------------\n");
  for (int x = 0; x < 16; x++)
    {
      if (memory_map[x].base != NULL)
	printf ("%16s | %010p | %10d Kb | %10d Kb\n",
		memory_map[x].name, memory_map[x].base,
		memory_map[x].size, memory_map[x].maxsize);
    }
  printf ("--------------------------------------------------------------\n");
#endif

  memory_size = memory_map[MEMC_CHUNK_APP].size;
  wimpslot = memory_map[MEMC_CHUNK_APP].base;
}
