text: /*
   *  6502 Memory manager - The peTI-NESulator Project
   *  memory.c - Taken from the Quick6502 project
   *
   *  Created by Manoël Trapier on 18/09/06.
   *  Copyright (c) 2002-2019 986-Studio.
   *
   */
  
  #include <stdio.h>
  #include <types.h>
  
  #include <os_dependent.h>
  
  #include <memory/manager.h>
  
  /* Private structures */
  
  #define KByte * (1024)
  
  /* 
  * What inside memory manager:
   *
   * Table of attributes
   * Table of original page ptr
   * Table of modded page ptr
   * 
   */
  
  /* Private data */
  
  uint8_t *memory_pages[0x100];
  uint8_t memory_pages_attr[0x100];
  
  func_rdhook rdh_table[0x100];
  func_wrhook wrh_table[0x100];
  
  //#define DEBUG
  #undef DEBUG
  
  #ifdef DEBUG
  #define LOG(s) s
  #else
  #define LOG(s) {}
  #endif
  
  /* Public functions */
  void set_page_ptr(uint8_t page, uint8_t *ptr)
  {
      LOG(console_printf(Console_Default, "Set page 0x%X to ptr %p\n", page, ptr));
      memory_pages[page] = ptr;
  }
  
  void set_page_ptr_1k(uint8_t page, uint8_t *ptr)
  {   /* 1k = 4 * 256 */
      LOG(console_printf(Console_Default, "Set page(1k) 0x%X to ptr %p\n", page, ptr));
      memory_pages[page + 0] = ptr;
      memory_pages[page + 1] = ptr + 0x100;
      memory_pages[page + 2] = ptr + (0x100 * 2);
      memory_pages[page + 3] = ptr + (0x100 * 3);
  }
  
  void set_page_ptr_2k(uint8_t page, uint8_t *ptr)
  {
      LOG(console_printf(Console_Default, "Set page(2k) 0x%X to ptr %p\n", page, ptr));
      memory_pages[page + 0] = ptr;
      memory_pages[page + 1] = ptr + 0x100;
      memory_pages[page + 2] = ptr + (0x100 * 2);
      memory_pages[page + 3] = ptr + (0x100 * 3);
      memory_pages[page + 4] = ptr + (0x100 * 4);
      memory_pages[page + 5] = ptr + (0x100 * 5);
      memory_pages[page + 6] = ptr + (0x100 * 6);
      memory_pages[page + 7] = ptr + (0x100 * 7);
  }
  
  void set_page_ptr_4k(uint8_t page, uint8_t *ptr)
  {
      LOG(console_printf(Console_Default, "Set page(4k) 0x%X to ptr %p\n", page, ptr));
      set_page_ptr_2k(page, ptr);
      set_page_ptr_2k(page + ((4 KByte / 256) / 2), ptr + 2 KByte);
  }
  
  void set_page_ptr_8k(uint8_t page, uint8_t *ptr)
  {
      LOG(console_printf(Console_Default, "Set page(8k) 0x%X to ptr %p\n", page, ptr));
      set_page_ptr_4k(page, ptr);
      set_page_ptr_4k(page + ((8 KByte / 256) / 2), ptr + 4 KByte);
  }
  
  void set_page_ptr_16k(uint8_t page, uint8_t *ptr)
  {
      set_page_ptr_8k(page, ptr);
      set_page_ptr_8k(page + ((16 KByte / 256) / 2), ptr + 8 KByte);
  }
  
  void set_page_ptr_32k(uint8_t page, uint8_t *ptr)
  {
      set_page_ptr_16k(page, ptr);
      set_page_ptr_16k(page + ((32 KByte / 256) / 2), ptr + 16 KByte);
  }
  
  uint8_t *get_page_ptr(uint8_t page)
  {
      return memory_pages[page];
  }
  
  
  /* Functions to set pages attributes */
  
  void set_page_rd_hook(uint8_t page, func_rdhook func)
  {
      if (func == NULL)
      {
          memory_pages_attr[page] &= (~ATTR_PAGE_HAVE_RDHOOK);
          if (memory_pages[page] == (uint8_t *)0x01)
          {
              memory_pages[page] = NULL;
          }
      }
      else
      {
          memory_pages_attr[page] |= ATTR_PAGE_HAVE_RDHOOK;
          if (memory_pages[page] == NULL)
          {
              memory_pages[page] = (uint8_t *)0x01;
          }
      }
  
      rdh_table[page] = func;
  }
  
  void set_page_wr_hook(uint8_t page, func_wrhook func)
  {
      if (func == NULL)
      {
          memory_pages_attr[page] &= (~ATTR_PAGE_HAVE_WRHOOK);
          if (memory_pages[page] == (uint8_t *)0x01)
          {
              memory_pages[page] = NULL;
          }
  
      }
      else
      {
          memory_pages_attr[page] |= ATTR_PAGE_HAVE_WRHOOK;
          if (memory_pages[page] == NULL)
          {
              memory_pages[page] = (uint8_t *)0x01;
          }
      }
  
      wrh_table[page] = func;
  }
  
  void set_page_readable(uint8_t page, uint8_t value)
  {
      if (value == true)
      {
          memory_pages_attr[page] |= ATTR_PAGE_READABLE;
      }
      else
      {
          memory_pages_attr[page] &= (~ATTR_PAGE_READABLE);
      }
  }
  
  void set_page_writeable(uint8_t page, uint8_t value)
  {
      if (value == true)
      {
          memory_pages_attr[page] |= ATTR_PAGE_WRITEABLE;
      }
      else
      {
          memory_pages_attr[page] &= (~ATTR_PAGE_WRITEABLE);
      }
  }
  
  void set_page_ghost(uint8_t page, uint8_t value, uint8_t ghost)
  {
      if (value == true)
      {
          memory_pages_attr[page] = memory_pages_attr[ghost];
          rdh_table[page] = rdh_table[ghost];
          wrh_table[page] = wrh_table[ghost];
          memory_pages[page] = memory_pages[ghost];
      }
  }
  
  uint8_t get_page_attributes(uint8_t page)
  {
      return memory_pages_attr[page];
  }
  
  func_rdhook get_page_rdhook(uint8_t page)
  {
      if (memory_pages_attr[page] & ATTR_PAGE_HAVE_RDHOOK)
      {
          return rdh_table[page];
      }
  
      return NULL;
  }
  
  func_wrhook get_page_wrhook(uint8_t page)
  {
      if (memory_pages_attr[page] & ATTR_PAGE_HAVE_WRHOOK)
      {
          return wrh_table[page];
      }
  
      return NULL;
  }
  
  uint8_t ReadMemory(uint8_t page, uint8_t addr)
  {
      static uint8_t LastRetByte = 0xA5;
      uint8_t *page_ptr;
      uint8_t attributes;
      LOG(console_printf(Console_Default, "Read @ 0x%X-%X\n", page, addr));
      /* Est-ce que la page est mappé ? && Est-ce que la page est "readable" ? */
      if ((page_ptr = memory_pages[page]) &&
          ((attributes = memory_pages_attr[page]) & ATTR_PAGE_READABLE))
      {
          LOG(console_printf(Console_Default, "Page is non null & readable\n"));
          if (attributes & ATTR_PAGE_HAVE_RDHOOK)
          {
              return (LastRetByte = rdh_table[page](addr));
          }
          else
          {
              return (LastRetByte = page_ptr[addr]);
          }
      }
      //console_printf(Console_Default, "Trying to read @ 0x%X-%X\n", page, addr);
      return LastRetByte;
  }
  
  void WriteMemory(uint8_t page, uint8_t addr, uint8_t value)
  {
      uint8_t *page_ptr;
      uint8_t attributes;
      LOG(console_printf(Console_Default, "Write 0x%x @ 0x%X-%X\n", value, page, addr));
      /* Est-ce que la page est mappé ? && Est-ce que la page est "writable" ? */
      if ((page_ptr = memory_pages[page]) &&
          ((attributes = memory_pages_attr[page]) & ATTR_PAGE_WRITEABLE))
      {
          if (attributes & ATTR_PAGE_HAVE_WRHOOK)
          {
  #ifdef DETECT_BUS_CONFLICT
              if ((page >= 0x80) && (memory_pages[page][addr] != value))
                  console_printf(Console_Default, "WriteHook: bus conflict at %02X%02X rom:%02X write:%02X\n", page, addr, memory_pages[page][addr], value);
  #endif
              wrh_table[page](addr, value);
          }
          else
          {
              page_ptr[addr] = value;
          }
      }
      else
      {
          console_printf(Console_Default, "Trying to write 0x%X @ 0x%X-%X\n", value, page, addr);
      }
  }
  
  void DumpMemoryState(FILE *fp)
  {
      int i;
  
      for (i = 0x00 ; i < 0x100 ; i++)
      {
          fprintf(fp,
                  "Page 0x%02X : [%c%c%c%c%c%c] RdH:%p WrH:%p ptr:%p\n",
                  i,
                  (memory_pages_attr[i] & ATTR_PAGE_HAVE_RDHOOK) ? 'r' : '.',
                  (memory_pages_attr[i] & ATTR_PAGE_HAVE_WRHOOK) ? 'w' : '.',
                  (memory_pages_attr[i] & ATTR_PAGE_READABLE) ? 'R' : '.',
                  (memory_pages_attr[i] & ATTR_PAGE_WRITEABLE) ? 'W' : '.',
                  (memory_pages_attr[i] & ATTR_PAGE_GHOST) ? 'G' : '.',
                  (memory_pages_attr[i] & ATTR_PAGE_MAPPED) ? 'M' : '.',
                  rdh_table[i],
                  wrh_table[i],
                  memory_pages[i]
          );
      }
  }
  
  void InitMemory()
  {
      int page;
  
      for (page = 0 ; page < 0x100 ; page++)
      {
          set_page_ptr(page, NULL);
          memory_pages_attr[page] = 0x00;
      }
  }
  