/*
 * DANG_BEGIN_MODULE
 *
 *
 *
 * DANG_END_MODULE
 *
 * DANG_BEGIN_CHANGELOG
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 *
 * DANG_END_CHANGELOG
 */

#include "dosemu.h"
#include "base/memory.h"

#include <fcntl.h>
#include <sys/mman.h>

#define A20_SIZE     (0x10000)              /* 64K bytes */
#define A20_AREA     ((caddr_t)(0x100000))  /* Starts at 0x10000:0000 */
#define A20_WRAP     ((off_t)(0))           /* Wraparound to 0000:0000 */
#define A20_XMEM     ((off_t)a20_data)      /* No wraparound */

static Bit8u   *a20_data;
static Boolean  a20_enabled;

static void a20_switch(Boolean enabled)
{
  if (enabled) {
    E_printf("A20: a20 enabled\n");
    munmap(A20_AREA, A20_SIZE);
    if (A20_AREA != mmap(A20_AREA, A20_SIZE,
			 PROT_EXEC | PROT_READ | PROT_WRITE,
			 MAP_SHARED | MAP_FIXED, selfmem_fd,
			 A20_XMEM)) {
      error("A20: a20 enable mmap failed!\n");
      leavedos(2);
    }
  }
  else {
    E_printf("A20: a20 disabled\n");
    munmap(A20_AREA, A20_SIZE);
    if (A20_AREA != mmap(A20_AREA, A20_SIZE,
			 PROT_EXEC | PROT_READ | PROT_WRITE,
			 MAP_SHARED | MAP_FIXED, selfmem_fd,
			 A20_WRAP)) {
      error("A20: a20 disable mmap failed!\n");
      leavedos(2);
    }
  }
  a20_enabled = enabled;
}

void a20_enable(void)
{
  a20_switch(TRUE);
}

void a20_disable(void)
{
  a20_switch(FALSE);
}

void a20_init(void)
{
  if ((a20_data = malloc(A20_SIZE)) == NULL) {
    error("A20: unable to allocate memory for a20 redirection\n");
    leavedos(1);
  }
  if (selfmem_fd < 0) {
    error("A20: selfmem_fd not set for memory mapping, unable to load\n");
    leavedos(1);
  }
}

void a20_reset(void)
{
  a20_enable();
}
