/*
 *  This file is part of x48, an emulator of the HP-48sx Calculator.
 *  Copyright (C) 1994  Eddie C. Dost  (ecd@dressler.de)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $Log: memory.c,v $
 * Revision 1.15  1995/01/11  18:20:01  ecd
 * major update to support HP48 G/GX
 *
 * Revision 1.14  1994/12/08  22:14:50  ecd
 * corrected setting of display.lines
 *
 * Revision 1.13  1994/12/07  20:20:50  ecd
 * minor fixes
 *
 * Revision 1.12  1994/11/28  02:00:51  ecd
 * corrected setting of display.contrast
 *
 * Revision 1.11  1994/11/02  15:07:54  ecd
 * deleted trailing NULL characters
 *
 * Revision 1.10  1994/11/02  14:44:28  ecd
 * minor changes
 *
 * Revision 1.9  1994/10/09  20:32:02  ecd
 * added support for bit offset, changed handling of display_t
 * variables.
 *
 * Revision 1.8  1994/10/06  16:30:05  ecd
 * changed char to unsigned
 *
 * Revision 1.7  1994/10/05  08:36:44  ecd
 * removed display addr queue
 *
 * Revision 1.6  1994/10/01  10:12:53  ecd
 * added schedule_event = 0 for any display touch
 *
 * Revision 1.5  1994/09/30  12:37:09  ecd
 * support for faster display and scheduler
 *
 * Revision 1.4  1994/09/18  15:29:22  ecd
 * turned off unused rcsid message
 *
 * Revision 1.3  1994/09/13  16:57:00  ecd
 * changed to plain X11
 *
 * Revision 1.2  1994/08/31  18:23:21  ecd
 * changed memory access routines.
 *
 * Revision 1.1  1994/08/26  11:09:02  ecd
 * Initial revision
 *
 * $Id: memory.c,v 1.15 1995/01/11 18:20:01 ecd Exp ecd $
 */

#include "global.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#ifdef SUNOS
#include <memory.h>
#endif

#include "hp48.h"
#include "device.h"
#include "hp48_emu.h"
#include "x48_x11.h"
#include "romio.h"
#include "resources.h"
#include "mmu.h"

extern int   device_check;
extern short port1_is_ram;
extern long  port1_mask;
extern short port2_is_ram;
extern long  port2_mask;

#define DEBUG_UNKNOWN 1
/* #define DEBUG_SERIAL 1 */
/* #define DEBUG_SERIALb 1 */
/* #define DEBUG_DISPLAY 1 */
/* #define DEBUG_IR 1 */
/* #define DEBUG_CONTRAST 1 */
/* #define DEBUG_CARDS 1 */
/* #define DEBUG_BAD_MEM 1 */
/* #define DEBUG_BASE_NIBBLE 1 */
/* #define DEBUG_BANK_SWITCH 1 */

long nibble_masks[16] = {
  0x0000000f,
  0x000000f0,
  0x00000f00,
  0x0000f000,
  0x000f0000,
  0x00f00000,
  0x0f000000,
  0xf0000000,
  0x0000000f,
  0x000000f0,
  0x00000f00,
  0x0000f000,
  0x000f0000,
  0x00f00000,
  0x0f000000,
  0xf0000000
};

void	(*write_nibble) __ProtoType__((long addr, int val));
int	(*read_nibble) __ProtoType__((long addr));
int	(*read_nibble_crc) __ProtoType__((long addr));

static int line_counter = -1;

static inline int
#ifdef __FunctionProto__
calc_crc(int nib)
#else
calc_crc(nib)
int nib;
#endif
{
  saturn.crc = (saturn.crc >> 4) ^ (((saturn.crc ^ nib) & 0xf) * 0x1081);
  return nib;
}

void
#ifdef __FunctionProto__
write_dev_mem(long addr, int val)
#else
write_dev_mem(addr, val)
long addr;
int val;
#endif
{
  static int old_line_offset = -1;

  device_check = 1;
  schedule_event = 0;
  switch ((int)addr) {
    case 0x100:					/* DISPIO */
      if (val != saturn.disp_io) {
        saturn.disp_io = val;
        display.on = (val & 0x8) >> 3;
        display.offset = val & 0x7;
        disp.offset = 2 * display.offset;
        if (display.offset > 3)
          display.nibs_per_line =
                  (NIBBLES_PER_ROW+saturn.line_offset+2) & 0xfff;
        else
          display.nibs_per_line =
                  (NIBBLES_PER_ROW+saturn.line_offset) & 0xfff;
        display.disp_end = display.disp_start +
		           (display.nibs_per_line * (display.lines + 1));
        device.display_touched = DISP_INSTR_OFF;
      }
#ifdef DEBUG_DISPLAY
      fprintf(stderr, "%.5lx: DISP ON: %x\n", saturn.PC, display.on);
#endif
      return;
    case 0x101:						/* CONTRAST CONTROL */
      saturn.contrast_ctrl = val;
      display.contrast &= ~0x0f;
      display.contrast |= val;
#ifdef DEBUG_CONTRAST
      fprintf(stderr, "%.5lx: Contrast: 0x%x\n", saturn.PC, display.contrast);
#endif
      device.contrast_touched = 1;
      return;
    case 0x102:						/* DISPLAY TEST */
      display.contrast &= ~0xf0;
      display.contrast |= ((val & 0x1) << 4);
#ifdef DEBUG_CONTRAST
      fprintf(stderr, "%.5lx: Contrast: 0x%x\n", saturn.PC, display.contrast);
#endif
      device.contrast_touched = 1;
      /* Fall through */
    case 0x103:						/* DISPLAY TEST */
      saturn.disp_test &= ~nibble_masks[addr - 0x102];
      saturn.disp_test |= val << ((addr - 0x102) * 4);
#ifdef DEBUG_DISPLAY
      fprintf(stderr, "%.5lx: DISP TEST: %x\n", saturn.PC, saturn.disp_test);
#endif
      device.disp_test_touched = 1;
      return;
    case 0x104: case 0x105: case 0x106: case 0x107:	/* CRC */
      saturn.crc &= ~nibble_masks[addr - 0x104];
      saturn.crc |= val << ((addr - 0x104) * 4);
      return;
    case 0x108:						/* POWER STATUS */
      saturn.power_status = val;
      device.power_status_touched = 1;
      return;
    case 0x109:						/* POWER CONTROL */
      saturn.power_ctrl = val;
      device.power_ctrl_touched = 1;
      return;
    case 0x10a:						/* MODE */
      saturn.mode = val;
      device.mode_touched = 1;
      return;
    case 0x10b: case 0x10c:				/* ANNUNC */
      saturn.annunc &= ~nibble_masks[addr - 0x10b];
      saturn.annunc |= val << ((addr - 0x10b) * 4);
      display.annunc = saturn.annunc;
      device.ann_touched = 1;
      return;
    case 0x10d:						/* BAUD */
      saturn.baud = val;
      device.baud_touched = 1;
#ifdef DEBUG_SERIALb
      fprintf(stderr, "%.5lx: BAUD write: %x\n", saturn.PC, saturn.baud);
#endif
      return;
    case 0x10e:						/* CARD CONTROL */
      saturn.card_ctrl = val;
      if (saturn.card_ctrl & 0x02)
	saturn.MP = 1;
      if (saturn.card_ctrl & 0x01)
	do_interupt();
      device.card_ctrl_touched = 1;
#ifdef DEBUG_CARDS
      fprintf(stderr, "%.5lx: CardControl write: %x\n",
              saturn.PC, saturn.card_ctrl);
#endif
      return;
    case 0x10f:						/* CARD STATUS */
#ifdef DEBUG_CARDS
      fprintf(stderr, "%.5lx: CardStatus write: %x\n",
              saturn.PC, saturn.card_status);
#endif
      return;
    case 0x110:						/* IO CONTROL */
      saturn.io_ctrl = val;
      device.ioc_touched = 1;
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: IOC write: %x\n", saturn.PC, saturn.io_ctrl);
#endif
      return;
    case 0x111:						/* RCS */
      saturn.rcs = val;
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: RCS: %x\n", saturn.PC, saturn.rcs);
#endif
      return;
    case 0x112:						/* TCS */
      saturn.tcs = val;
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: TCS: %x\n", saturn.PC, saturn.tcs);
#endif
      return;
    case 0x113:						/* CRER */
      saturn.rcs &= 0x0b;
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: CRER, RCS: %x\n", saturn.PC, saturn.rcs);
#endif
      return;
    case 0x114: case 0x115:				/* RBR */
      return;
    case 0x116: case 0x117:				/* TBR */
      saturn.tbr &= ~nibble_masks[addr - 0x116];
      saturn.tbr |= val << ((addr - 0x116) * 4);
      saturn.tcs |= 0x01;
      device.tbr_touched = 1;
      return;
    case 0x118: case 0x119:				/* SERVICE REQ */
      saturn.sreq &= ~nibble_masks[addr - 0x118];
      saturn.sreq |= val << ((addr - 0x118) * 4);
      device.sreq_touched = 1;
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: SREQ? write: %x\n", saturn.PC, saturn.sreq);
#endif
      return;
    case 0x11a:						/* IR CONTROL */
      saturn.ir_ctrl = val;
      device.ir_ctrl_touched = 1;
#ifdef DEBUG_IR
      fprintf(stderr, "%.5lx: IRC write: %x\n", saturn.PC, saturn.ir_ctrl);
#endif
      return;
    case 0x11b:						/* BASE NIB OFFSET */
      saturn.base_off = val;
      device.base_off_touched = 1;
      return;
    case 0x11c:						/* LED CONTROL */
      saturn.lcr = val;
      device.lcr_touched = 1;
#ifdef DEBUG_IR
      fprintf(stderr, "%.5lx: LCR write: %x\n", saturn.PC, saturn.lcr);
#endif
      return;
    case 0x11d:						/* LED BUFFER */
      saturn.lbr = val;
      device.lbr_touched = 1;
#ifdef DEBUG_IR
      fprintf(stderr, "%.5lx: LBR write: %x\n", saturn.PC, saturn.lbr);
#endif
      return;
    case 0x11e:						/* SCRATCH PAD */
      saturn.scratch = val;
      device.scratch_touched = 1;
      return;
    case 0x11f:						/* BASENIBBLE */
      saturn.base_nibble = val;
      device.base_nibble_touched = 1;
#ifdef DEBUG_BASE_NIBBLE
      if (opt_gx)
      fprintf(stderr, "%.5lx: BASENIB: %x\n", saturn.PC, saturn.base_nibble);
#endif
      return;
    case 0x120: case 0x121: case 0x122: case 0x123:	/* DISP_ADDR */
    case 0x124:
      saturn.disp_addr &= ~nibble_masks[addr - 0x120];
      saturn.disp_addr |= val << ((addr - 0x120) * 4);
      if (display.disp_start != (saturn.disp_addr & 0xffffe)) {
        display.disp_start = saturn.disp_addr & 0xffffe;
        display.disp_end = display.disp_start +
		           (display.nibs_per_line * (display.lines + 1));
        device.display_touched = DISP_INSTR_OFF;
      }
#ifdef DEBUG_DISPLAY
      fprintf(stderr, "%.5lx: DISPLAY: %lx\n", saturn.PC, display.disp_start);
      fprintf(stderr, "%.5lx: DISP END: %lx\n", saturn.PC, display.disp_end);
#endif
      return;
    case 0x125: case 0x126: case 0x127:			/* LINE_OFFSET */
      saturn.line_offset &= ~nibble_masks[addr - 0x125];
      saturn.line_offset |= val << ((addr - 0x125) * 4);
      if (saturn.line_offset != old_line_offset) {
        old_line_offset = saturn.line_offset;
        if (display.offset > 3)
          display.nibs_per_line =
                  (NIBBLES_PER_ROW+saturn.line_offset+2) & 0xfff;
        else
          display.nibs_per_line =
                  (NIBBLES_PER_ROW+saturn.line_offset) & 0xfff;
        display.disp_end = display.disp_start +
		           (display.nibs_per_line * (display.lines + 1));
        device.display_touched = DISP_INSTR_OFF;
      }
#ifdef DEBUG_DISPLAY
      fprintf(stderr, "%.5lx: DISP LINE SIZE: %x\n",
              saturn.PC, display.nibs_per_line);
      fprintf(stderr, "%.5lx: DISP END: %lx\n", saturn.PC, display.disp_end);
#endif
      return;
    case 0x128: case 0x129:				/* LINE_COUNT */
      saturn.line_count &= ~nibble_masks[addr - 0x128];
      saturn.line_count |= val << ((addr - 0x128) * 4);
      line_counter = -1;
      if (display.lines != (saturn.line_count & 0x3f)) {
        display.lines = saturn.line_count & 0x3f;
	if (display.lines == 0)
          display.lines = 63;
        disp.lines = 2 * display.lines;
        display.disp_end = display.disp_start +
		           (display.nibs_per_line * (display.lines + 1));
        device.display_touched = DISP_INSTR_OFF;
      }
#ifdef DEBUG_DISPLAY
      fprintf(stderr, "%.5lx: DISP LINES: %x\n", saturn.PC, display.lines);
      fprintf(stderr, "%.5lx: DISP END: %lx\n", saturn.PC, display.disp_end);
#endif
      return;
    case 0x12a: case 0x12b: case 0x12c: case 0x12d:	/* Dont know yet */
      saturn.unknown &= ~nibble_masks[addr - 0x12a];
      saturn.unknown |= val << ((addr - 0x12a) * 4);
#ifdef DEBUG_UNKNOWN 
      fprintf(stderr, "Unknown device @0x%ld: %.4x\n", addr, saturn.unknown);
#endif
      device.unknown_touched = 1;
      return;
    case 0x12e:						/* TIMER 1 CONTROL */
      saturn.t1_ctrl = val;
      device.t1_ctrl_touched = 1;
      return;
    case 0x12f:						/* TIMER 2 CONTROL */
      saturn.t2_ctrl = val;
      device.t2_ctrl_touched = 1;
      return;
    case 0x130: case 0x131: case 0x132: case 0x133:	/* MENU_ADDR */
    case 0x134:
      saturn.menu_addr &= ~nibble_masks[addr - 0x130];
      saturn.menu_addr |= val << ((addr - 0x130) * 4);
      if (display.menu_start != saturn.menu_addr) {
        display.menu_start = saturn.menu_addr;
        display.menu_end = display.menu_start + 0x110;
        device.display_touched = DISP_INSTR_OFF;
      }
      return;
    case 0x135: case 0x136:				/* Dont know yet 2 */
      saturn.unknown2 &= ~nibble_masks[addr - 0x135];
      saturn.unknown2 |= val << ((addr - 0x135) * 4);
#ifdef DEBUG_UNKNOWN 
      fprintf(stderr, "Unknown device @0x%ld: %.2x\n", addr, saturn.unknown2);
#endif
      device.unknown2_touched = 1;
      return;
    case 0x137:						/* TIMER1 */
      saturn.timer1 = val;
      device.t1_touched = 1;
      return;
    case 0x138: case 0x139: case 0x13a: case 0x13b:
    case 0x13c: case 0x13d: case 0x13e: case 0x13f:	/* TIMER2 */
      saturn.timer2 &= ~nibble_masks[addr - 0x138];
      saturn.timer2 |= val << ((addr - 0x138) * 4);
      device.t2_touched = 1;
      return;
    default:
      if (!quiet)
        fprintf(stderr, "%.5lx: UNKNOWN DEVICE WRITE AT 0x%lx !!!\n",
                        saturn.PC, addr);
      return;
  }
}

int
#ifdef __FunctionProto__
read_dev_mem(long addr)
#else
read_dev_mem(addr)
long addr;
#endif
{
  switch ((int)addr) {
    case 0x100:						/* DISPLAY IO */
      return saturn.disp_io & 0x0f;
    case 0x101:						/* CONTRAST CONTROL */
      return saturn.contrast_ctrl & 0x0f;
    case 0x102: case 0x103:				/* DISPLAY TEST */
      return (saturn.disp_test >> ((addr - 0x102) * 4)) & 0x0f;
    case 0x104: case 0x105: case 0x106: case 0x107:	/* CRC */
      return (saturn.crc >> ((addr - 0x104) * 4)) & 0x0f;
    case 0x108:						/* POWER STATUS */
      return saturn.power_status & 0x0f;
    case 0x109:						/* POWER CONTROL */
      return saturn.power_ctrl & 0x0f;
    case 0x10a:						/* MODE */
      return saturn.mode & 0x0f;
    case 0x10b: case 0x10c:				/* ANNUNC */
      return (saturn.annunc >> ((addr - 0x10b) * 4)) & 0x0f;
    case 0x10d:						/* BAUD */
#ifdef DEBUG_SERIALb
      fprintf(stderr, "%.5lx: BAUD read: %x\n", saturn.PC, saturn.baud);
#endif
      return saturn.baud & 0x0f;
    case 0x10e:						/* CARD CONTROL */
      return saturn.card_ctrl & 0x0f;
    case 0x10f:						/* CARD STATUS */
      return saturn.card_status & 0x0f;
    case 0x110:						/* IO CONTROL */
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: IOC read: %x\n", saturn.PC, saturn.io_ctrl);
#endif
      return saturn.io_ctrl & 0x0f;
    case 0x111:						/* RCS */
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: RCS read: %x\n", saturn.PC, saturn.rcs);
#endif
      return saturn.rcs & 0x0f;
    case 0x112:						/* TCS */
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: TCS read: %x\n", saturn.PC, saturn.tcs);
#endif
      return saturn.tcs & 0x0f;
    case 0x113:						/* CRER */
      return 0x00;
    case 0x114: case 0x115:				/* RBR */
      saturn.rcs &= 0x0e;
      device.rbr_touched = 1;
      device_check = 1;
      schedule_event = 0;
      return (saturn.rbr >> ((addr - 0x114) * 4)) & 0x0f;
    case 0x116: case 0x117:				/* TBR */
      return 0x00;
    case 0x118: case 0x119:				/* SERVICE REQ */
#ifdef DEBUG_SERIAL
      fprintf(stderr, "%.5lx: SREQ? read: %x\n", saturn.PC, saturn.sreq);
#endif
      return (saturn.sreq >> ((addr - 0x118) * 4)) & 0x0f;
    case 0x11a:						/* IR CONTROL */
#ifdef DEBUG_IR
      fprintf(stderr, "%.5lx: IRC read: %x\n", saturn.PC, saturn.ir_ctrl);
#endif
      return saturn.ir_ctrl & 0x0f;
    case 0x11b:						/* BASE NIB OFFSET */
      return saturn.base_off & 0x0f;
    case 0x11c:						/* LED CONTROL */
#if 0
#ifdef DEBUG_IR
      fprintf(stderr, "%.5lx: LCR read: %x\n", saturn.PC, saturn.lcr);
#endif
#endif
      return saturn.lcr & 0x0f;
    case 0x11d:						/* LED BUFFER */
#if 0
#ifdef DEBUG_IR
      fprintf(stderr, "%.5lx: LBR read: %x\n", saturn.PC, saturn.lbr);
#endif
#endif
      return saturn.lbr & 0x0f;
    case 0x11e:						/* SCRATCH PAD */
      return saturn.scratch & 0x0f;
    case 0x11f:						/* BASENIBBLE */
      return saturn.base_nibble & 0x0f;
    case 0x120: case 0x121: case 0x122: case 0x123:	/* DISP_ADDR */
    case 0x124:
      return (saturn.disp_addr >> ((addr - 0x120) * 4)) & 0x0f;
    case 0x125: case 0x126: case 0x127:			/* LINE_OFFSET */
      return (saturn.line_offset >> ((addr - 0x125) * 4)) & 0x0f;
    case 0x128:	case 0x129:				/* LINE_COUNT */
      line_counter++;
      if (line_counter > 0x3f)
        line_counter = -1;
      return (((saturn.line_count & 0xc0) | (line_counter & 0x3f)) >>
             ((addr - 0x128) * 4)) & 0x0f;
    case 0x12a: case 0x12b: case 0x12c: case 0x12d:	/* Dont know yet */
      return (saturn.unknown >> ((addr - 0x12a) * 4)) & 0x0f;
    case 0x12e:						/* TIMER 1 CONTROL */
      return saturn.t1_ctrl & 0x0f;
    case 0x12f:						/* TIMER 2 CONTROL */
      return saturn.t2_ctrl & 0x0f;
    case 0x130: case 0x131: case 0x132: case 0x133:	/* MENU_ADDR */
    case 0x134:
      return (saturn.menu_addr >> ((addr - 0x130) * 4)) & 0x0f;
    case 0x135: case 0x136:				/* Dont know yet 2 */
      return (saturn.unknown2 >> ((addr - 0x135) * 4)) & 0x0f;
    case 0x137:
      return saturn.timer1 & 0xf;
    case 0x138: case 0x139: case 0x13a: case 0x13b:
    case 0x13c: case 0x13d: case 0x13e: case 0x13f:
      return (saturn.timer2 >> ((addr - 0x138) * 4)) & 0xf;
    default:
      if (!quiet)
        fprintf(stderr, "%.5lx: UNKNOWN DEVICE READ AT 0x%lx !!!\n",
                        saturn.PC, addr);
      return 0x00;
  }
}

void
#ifdef __FunctionProto__
write_nibble_sx(long addr, int val)
#else
write_nibble_sx(addr, val)
long addr;
int val;
#endif
{
  addr &= 0xfffff;
  val &= 0x0f;
  switch ((int)(addr >> 16) & 0x0f) {
    case 0:
      if (addr < 0x140 && addr >= 0x100 &&
          saturn.mem_cntl[MCTL_MMIO_SX].config[0] == 0x100)
        {
          write_dev_mem(addr, val);
          return;
        }
#ifdef DEBUG_BAD_MEM
      fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
              saturn.PC, addr);
#endif
      return;
    case 1: case 2: case 3: case 4: case 5: case 6:
#ifdef DEBUG_BAD_MEM
      fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
              saturn.PC, addr);
#endif
      return;
    case 7:
      if (saturn.mem_cntl[MCTL_SysRAM_SX].config[0] == 0x70000)
        {
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xfc000
              && addr < 0x74000)
            {
              saturn.ram[addr - 0x70000] = val;
              break;
            }
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xfe000
              && addr < 0x72000)
            {
              saturn.ram[addr - 0x70000] = val;
              break;
            }
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xf0000)
            {
              saturn.ram[addr - 0x70000] = val;
              break;
            }
        }
#ifdef DEBUG_BAD_MEM
      fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
              saturn.PC, addr);
#endif
      return;
    case 8: case 9: case 0xa: case 0xb:
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0x80000)
        {
          if (port1_is_ram)
            saturn.port1[(addr - 0x80000) & port1_mask] = val;
          return;
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0x80000)
        {
          if (port2_is_ram)
            saturn.port2[(addr - 0x80000) & port2_mask] = val;
          return;
        }
#ifdef DEBUG_BAD_MEM
      fprintf(stderr, "%.5lx: write to NULL at %.5lx\n",
              saturn.PC, addr);
#endif
      return;
    case 0xc: case 0xd: case 0xe:
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0xc0000)
        {
          if (port1_is_ram)
            saturn.port1[(addr - 0xc0000) & port1_mask] = val;
          return;
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0xc0000)
        {
          if (port2_is_ram)
            saturn.port2[(addr - 0xc0000) & port2_mask] = val;
          return;
        }
#ifdef DEBUG_BAD_MEM
      fprintf(stderr, "%.5lx: write to NULL at %.5lx\n",
              saturn.PC, addr);
#endif
      return;
    case 0xf:
      if (saturn.mem_cntl[MCTL_SysRAM_SX].config[0] == 0xf0000)
        {
          saturn.ram[addr - 0xf0000] = val;
          break;
        }
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0xc0000)
        {
          if (port1_is_ram)
            saturn.port1[(addr - 0xc0000) & port1_mask] = val;
          return;
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0xc0000)
        {
          if (port2_is_ram)
            saturn.port2[(addr - 0xc0000) & port2_mask] = val;
          return;
        }
#ifdef DEBUG_BAD_MEM
      fprintf(stderr, "%.5lx: write to NULL at %.5lx\n",
              saturn.PC, addr);
#endif
      return;
  }
  if (device.display_touched || !disp.mapped)
    return;
  if (addr >= display.disp_start && addr < display.disp_end)
    {
      disp_draw_nibble(addr, val);
    }
  if (display.lines == 63)
    return;
  if (addr >= display.menu_start && addr < display.menu_end)
    {
      menu_draw_nibble(addr, val);
    }
}

void
#ifdef __FunctionProto__
write_nibble_gx(long addr, int val)
#else
write_nibble_gx(addr, val)
long addr;
int val;
#endif
{
  addr &= 0xfffff;
  val &= 0x0f;
  switch ((int)(addr >> 16) & 0x0f)
    {
      case 0:
        if (addr < 0x140 && addr >= 0x100 &&
            saturn.mem_cntl[MCTL_MMIO_GX].config[0] == 0x100)
          {
            write_dev_mem(addr, val);
            return;
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 1: case 2: case 3: case 5: case 6:
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: BAD WRITE TO ROM AT ADDRESS %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 4:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x40000)
          {
            saturn.ram[addr - 0x40000] = val;
            break;
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: BAD WRITE TO ROM AT ADDRESS %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 7:
        if (addr >= 0x7f000 &&
            saturn.mem_cntl[MCTL_BANK_GX].config[0] == 0x7f000)
          {
#ifdef DEBUG_BANK_SWITCH
            fprintf(stderr, "%.5lx: write to bank switch at %.5lx\n",
                    saturn.PC, addr);
#endif
            return;
          }
        if (addr >= 0x7e000 && addr < 0x7f000 &&
            saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0x7e000)
          {
#ifdef DEBUG_PORTS
            fprintf(stderr, "%.5lx: write to port 1 at %.5lx\n",
                    saturn.PC, addr);
#endif
            return;
          }
        if (addr >= 0x7e000 && addr < 0x7f000 &&
            saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0x7e000)
          {
#ifdef DEBUG_PORTS
            fprintf(stderr, "%.5lx: write to port 2 at %.5lx\n",
                    saturn.PC, addr);
#endif
            return;
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 8:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          {
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfc000
                && addr < 0x84000)
              {
                saturn.ram[addr - 0x80000] = val;
                break;
              }
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfe000
                && addr < 0x82000)
              {
                saturn.ram[addr - 0x80000] = val;
                break;
              }
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xf0000)
              {
                saturn.ram[addr - 0x80000] = val;
                break;
              }
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
              {
                saturn.ram[addr - 0x80000] = val;
                break;
              }
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 9:
        if (saturn.mem_cntl[MCTL_BANK_GX].config[0] == 0x90000)
          {
            if (addr < 0x91000)
              {
#ifdef DEBUG_BANK_SWITCH
                fprintf(stderr, "%.5lx: write to bank switch at %.5lx\n",
                        saturn.PC, addr);
#endif
                return;
              }
          }
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            {
              saturn.ram[addr - 0x80000] = val;
              break;
            }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 0xa:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            {
              saturn.ram[addr - 0x80000] = val;
              break;
            }
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xa0000)
          {
            if (port1_is_ram)
              saturn.port1[(addr - 0xa0000) & port1_mask] = val;
            return;
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 0xb:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            {
              saturn.ram[addr - 0x80000] = val;
              break;
            }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xb0000)
          {
            if (port2_is_ram)
              saturn.port2[((saturn.bank_switch << 18) + (addr - 0xb0000))
                           & port2_mask] = val;
/*
            if (port2_size > (saturn.bank_switch << 18))
              {
                if (port2_is_ram)
                  saturn.port2[(saturn.bank_switch << 18)
                               + (addr - 0xb0000)] = val;
              }
*/
            return;
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 0xc:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0xc0000)
          {
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfc000
                && addr < 0xc4000)
              {
                saturn.ram[addr - 0xc0000] = val;
                break;
              }
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfe000
                && addr < 0xc2000)
              {
                saturn.ram[addr - 0xc0000] = val;
                break;
              }
            saturn.ram[addr - 0xc0000] = val;
            break;
          }
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xc0000)
          {
            if (port1_is_ram)
              saturn.port1[(addr - 0xc0000) & port1_mask] = val;
            return;
          }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xc0000)
          {
            if (port2_is_ram)
              saturn.port2[((saturn.bank_switch << 18) + (addr - 0xc0000))
                           & port2_mask] = val;
/*
            if (port2_size > (saturn.bank_switch << 18))
              {
                if (port2_is_ram)
                  saturn.port2[(saturn.bank_switch << 18)
                               + (addr - 0xc0000)] = val;
              }
*/
            return;
          }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
      case 0xd: case 0xe: case 0xf:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            {
              saturn.ram[addr - 0xc0000] = val;
              break;
            }
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_PORT1_GX].config[1] == 0xc0000)
            {
              if (port1_is_ram)
                saturn.port1[(addr - 0xc0000) & port1_mask] = val;
              return;
            }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_PORT2_GX].config[1] == 0xc0000)
            {
              if (port2_is_ram)
                saturn.port2[((saturn.bank_switch << 18) + (addr - 0xc0000))
                             & port2_mask] = val;
/*
              if (port2_size > (saturn.bank_switch << 18))
                {
                  if (port2_is_ram)
                    saturn.port2[(saturn.bank_switch << 18)
                                 + (addr - 0xc0000)] = val;
                }
*/
              return;
            }
#ifdef DEBUG_BAD_MEM
        fprintf(stderr, "%.5lx: write to ROM at %.5lx\n",
                saturn.PC, addr);
#endif
        return;
    }
  if (device.display_touched || !disp.mapped)
    return;
  if (addr >= display.disp_start && addr < display.disp_end)
    {
      disp_draw_nibble(addr, val);
    }
  if (display.lines == 63)
    return;
  if (addr >= display.menu_start && addr < display.menu_end)
    {
      menu_draw_nibble(addr, val);
    }
  return;
}

int
#ifdef __FunctionProto__
read_nibble_sx(long addr)
#else
read_nibble_sx(addr)
long addr;
#endif
{
  addr &= 0xfffff;
  switch ((int)(addr >> 16) & 0x0f) {
    case 0:
      if (addr < 0x140 && addr >= 0x100)
        {
          if (saturn.mem_cntl[MCTL_MMIO_SX].config[0] == 0x100)
            return read_dev_mem(addr);
          else
            return 0x00;
        }
      return saturn.rom[addr];
    case 1: case 2: case 3: case 4: case 5: case 6:
      return saturn.rom[addr];
    case 7:
      if (saturn.mem_cntl[MCTL_SysRAM_SX].config[0] == 0x70000)
        {
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xfc000
              && addr < 0x74000)
            return saturn.ram[addr - 0x70000];
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xfe000
              && addr < 0x72000)
            return saturn.ram[addr - 0x70000];
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xf0000)
            return saturn.ram[addr - 0x70000];
        }
      return saturn.rom[addr];
    case 8: case 9: case 0xa: case 0xb:
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0x80000)
        {
          return saturn.port1[(addr - 0x80000) & port1_mask];
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0x80000)
        {
          return saturn.port2[(addr - 0x80000) & port2_mask];
        }
      return 0x00;
    case 0xc: case 0xd: case 0xe:
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0xc0000)
        {
          return saturn.port1[(addr - 0xc0000) & port1_mask];
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0xc0000)
        {
          return saturn.port2[(addr - 0xc0000) & port2_mask];
        }
      return 0x00;
    case 0xf:
      if (saturn.mem_cntl[MCTL_SysRAM_SX].config[0] == 0xf0000)
        return saturn.ram[addr - 0xf0000];
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0xc0000)
        {
          return saturn.port1[(addr - 0xc0000) & port1_mask];
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0xc0000)
        {
          return saturn.port2[(addr - 0xc0000) & port2_mask];
        }
      return 0x00;
  }
  return 0x00;
}

int
#ifdef __FunctionProto__
read_nibble_gx(long addr)
#else
read_nibble_gx(addr)
long addr;
#endif
{
  addr &= 0xfffff;
  switch ((int)(addr >> 16) & 0x0f)
    {
      case 0:
        if (addr < 0x140 && addr >= 0x100)
          {
            if (saturn.mem_cntl[0].config[0] == 0x100)
              return read_dev_mem(addr);
            else
              return 0x00;
          }
        return saturn.rom[addr];
      case 1: case 2: case 3: case 5: case 6:
        return saturn.rom[addr];
      case 4:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x40000)
          return saturn.ram[addr - 0x40000];
        return saturn.rom[addr];
      case 7:
        if (addr >= 0x7f000 &&
            saturn.mem_cntl[MCTL_BANK_GX].config[0] == 0x7f000)
          {
            if (addr == 0x7f000)
              {
                saturn.bank_switch = 0;
#ifdef DEBUG_BANK_SWITCH
                fprintf(stderr, "%.5lx: disable bank switch\n", saturn.PC);
#endif
              }
            if (addr >= 0x7f040 && addr < 0x7f080)
              {
                saturn.bank_switch = (addr - 0x7f040) / 2;
#ifdef DEBUG_BANK_SWITCH
                fprintf(stderr, "%.5lx: switch to bank %d\n",
                        saturn.PC, saturn.bank_switch);
#endif
              }
            return 0x7;
          }
        if (addr >= 0x7e000 && addr < 0x7f000 &&
            saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0x7e000)
          {
#ifdef DEBUG_PORTS
            fprintf(stderr, "%.5lx: read from port 1 at %.5lx\n",
                    saturn.PC, addr);
#endif
            return 0x7;
          }
        if (addr >= 0x7e000 && addr < 0x7f000 &&
            saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0x7e000)
          {
#ifdef DEBUG_PORTS
            fprintf(stderr, "%.5lx: read from port 2 at %.5lx\n",
                    saturn.PC, addr);
#endif
            return 0x7;
          }
        return saturn.rom[addr];
      case 8:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          {
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfc000
                && addr < 0x84000)
              return saturn.ram[addr - 0x80000];
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfe000
                && addr < 0x82000)
              return saturn.ram[addr - 0x80000];
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xf0000)
              return saturn.ram[addr - 0x80000];
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
              return saturn.ram[addr - 0x80000];
          }
        return saturn.rom[addr];
      case 9:
        if (saturn.mem_cntl[0].config[0] == 0x90000)
          {
            if (addr < 0x91000)
              {
                if (addr == 0x90000)
                  {
                    saturn.bank_switch = 0;
#ifdef DEBUG_BANK_SWITCH
                    fprintf(stderr, "%.5lx: disable bank switch\n", saturn.PC);
#endif
                  }
                if (addr >= 0x90040 && addr < 0x90080)
                  {
                    saturn.bank_switch = (addr - 0x90040) / 2;
#ifdef DEBUG_BANK_SWITCH
                    fprintf(stderr, "%.5lx: switch to bank %d\n",
                            saturn.PC, saturn.bank_switch);
#endif
                  }
                return 0x7;
              }
          }
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return saturn.ram[addr - 0x80000];
        return saturn.rom[addr];
      case 0xa:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return saturn.ram[addr - 0x80000];
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xa0000)
          {
            return saturn.port1[(addr - 0xa0000) & port1_mask];
          }
        return saturn.rom[addr];
      case 0xb:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return saturn.ram[addr - 0x80000];
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xb0000)
          {
            return saturn.port2[((saturn.bank_switch << 18) + (addr - 0xb0000))
                                & port2_mask];
/*
            if (port2_size > (saturn.bank_switch << 18))
              {
                return saturn.port2[(saturn.bank_switch << 18)
                                    + (addr - 0xb0000)];
              }
            return 0x00;
*/
          }
        return saturn.rom[addr];
      case 0xc:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0xc0000)
          {
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfc000
                && addr < 0xc4000)
              return saturn.ram[addr - 0xc0000];
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfe000
                && addr < 0xc2000)
              return saturn.ram[addr - 0xc0000];
            return saturn.ram[addr - 0xc0000];
          }
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xc0000)
          {
            return saturn.port1[(addr - 0xc0000) & port1_mask];
          }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xc0000)
          {
            return saturn.port2[((saturn.bank_switch << 18) + (addr - 0xc0000))
                                & port2_mask];
/*
            if (port2_size > (saturn.bank_switch << 18))
              {
                return saturn.port2[(saturn.bank_switch << 18)
                                    + (addr - 0xc0000)];
              }
            return 0x00;
*/
          }
        return saturn.rom[addr];
      case 0xd: case 0xe: case 0xf:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return saturn.ram[addr - 0xc0000];
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_PORT1_GX].config[1] == 0xc0000)
            {
              return saturn.port1[(addr - 0xc0000) & port1_mask];
            }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_PORT2_GX].config[1] == 0xc0000)
            {
              return saturn.port2[((saturn.bank_switch << 18) +
                                  (addr - 0xc0000)) & port2_mask];
/*
              if (port2_size > (saturn.bank_switch << 18))
                {
                  return saturn.port2[(saturn.bank_switch << 18)
                                      + (addr - 0xc0000)];
                }
              return 0x00;
*/
            }
        return saturn.rom[addr];
    }
  return 0x00;
}

int
#ifdef __FunctionProto__
read_nibble_crc_sx(long addr)
#else
read_nibble_crc_sx(addr)
long addr;
#endif
{
  addr &= 0xfffff;
  switch ((int)(addr >> 16) & 0x0f) {
    case 0:
      if (addr < 0x140 && addr >= 0x100)
        {
          if (saturn.mem_cntl[MCTL_MMIO_SX].config[0] == 0x100)
            return read_dev_mem(addr);
          else
            return calc_crc(0x00);
        }
      return calc_crc(saturn.rom[addr]);
    case 1: case 2: case 3: case 4: case 5: case 6:
      return calc_crc(saturn.rom[addr]);
    case 7:
      if (saturn.mem_cntl[MCTL_SysRAM_SX].config[0] == 0x70000)
        {
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xfc000
              && addr < 0x74000)
            return calc_crc(saturn.ram[addr - 0x70000]);
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xfe000
              && addr < 0x72000)
            return calc_crc(saturn.ram[addr - 0x70000]);
          if (saturn.mem_cntl[MCTL_SysRAM_SX].config[1] == 0xf0000)
            return calc_crc(saturn.ram[addr - 0x70000]);
        }
      return calc_crc(saturn.rom[addr]);
    case 8: case 9: case 0xa: case 0xb:
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0x80000)
        {
          return calc_crc(saturn.port1[(addr - 0x80000) & port1_mask]);
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0x80000)
        {
          return calc_crc(saturn.port2[(addr - 0x80000) & port2_mask]);
        }
      return 0x00;
    case 0xc: case 0xd: case 0xe:
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0xc0000)
        {
          return calc_crc(saturn.port1[(addr - 0xc0000) & port1_mask]);
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0xc0000)
        {
          return calc_crc(saturn.port2[(addr - 0xc0000) & port2_mask]);
        }
      return 0x00;
    case 0xf:
      if (saturn.mem_cntl[MCTL_SysRAM_SX].config[0] == 0xf0000)
        return calc_crc(saturn.ram[addr - 0xf0000]);
      if (saturn.mem_cntl[MCTL_PORT1_SX].config[0] == 0xc0000)
        {
          return calc_crc(saturn.port1[(addr - 0xc0000) & port1_mask]);
        }
      if (saturn.mem_cntl[MCTL_PORT2_SX].config[0] == 0xc0000)
        {
          return calc_crc(saturn.port2[(addr - 0xc0000) & port2_mask]);
        }
      return 0x00;
  }
  return 0x00;
}

int
#ifdef __FunctionProto__
read_nibble_crc_gx(long addr)
#else
read_nibble_crc_gx(addr)
long addr;
#endif
{
  addr &= 0xfffff;
  switch ((int)(addr >> 16) & 0x0f)
    {
      case 0:
        if (addr < 0x140 && addr >= 0x100)
          {
            if (saturn.mem_cntl[MCTL_MMIO_GX].config[0] == 0x100)
              return read_dev_mem(addr);
            else
              return calc_crc(0x00);
          }
        return calc_crc(saturn.rom[addr]);
      case 1: case 2: case 3: case 5: case 6:
        return calc_crc(saturn.rom[addr]);
      case 4:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x40000)
          return calc_crc(saturn.ram[addr - 0x40000]);
        return calc_crc(saturn.rom[addr]);
      case 7:
        if (addr >= 0x7f000 &&
            saturn.mem_cntl[MCTL_BANK_GX].config[0] == 0x7f000)
          {
            if (addr == 0x7f000)
              {
                saturn.bank_switch = 0;
#ifdef DEBUG_BANK_SWITCH
                fprintf(stderr, "%.5lx: disable bank switch\n", saturn.PC);
#endif
              }
            if (addr >= 0x7f040 && addr < 0x7f080)
              {
                saturn.bank_switch = (addr - 0x7f040) / 2;
#ifdef DEBUG_BANK_SWITCH
                fprintf(stderr, "%.5lx: switch to bank %d\n",
                        saturn.PC, saturn.bank_switch);
#endif
              }
            return 0x7;
          }
        if (addr >= 0x7e000 && addr < 0x7f000 &&
            saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0x7e000)
          {
#ifdef DEBUG_PORTS
            fprintf(stderr, "%.5lx: read from port 1 at %.5lx\n",
                    saturn.PC, addr);
#endif
            return 0x7;
          }
        if (addr >= 0x7e000 && addr < 0x7f000 &&
            saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0x7e000)
          {
#ifdef DEBUG_PORTS
            fprintf(stderr, "%.5lx: read from port 2 at %.5lx\n",
                    saturn.PC, addr);
#endif
            return 0x7;
          }
        return calc_crc(saturn.rom[addr]);
      case 8:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          {
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfc000
                && addr < 0x84000)
              return calc_crc(saturn.ram[addr - 0x80000]);
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfe000
                && addr < 0x82000)
              return calc_crc(saturn.ram[addr - 0x80000]);
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xf0000)
              return calc_crc(saturn.ram[addr - 0x80000]);
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
              return calc_crc(saturn.ram[addr - 0x80000]);
          }
        return calc_crc(saturn.rom[addr]);
      case 9:
        if (saturn.mem_cntl[0].config[0] == 0x90000)
          {
            if (addr < 0x91000)
              {
                if (addr == 0x90000)
                  {
                    saturn.bank_switch = 0;
#ifdef DEBUG_BANK_SWITCH
                    fprintf(stderr, "%.5lx: disable bank switch\n", saturn.PC);
#endif
                  }
                if (addr >= 0x90040 && addr < 0x90080)
                  {
                    saturn.bank_switch = (addr - 0x90040) / 2;
#ifdef DEBUG_BANK_SWITCH
                    fprintf(stderr, "%.5lx: switch to bank %d\n",
                            saturn.PC, saturn.bank_switch);
#endif
                  }
                return 0x7;
              }
          }
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return calc_crc(saturn.ram[addr - 0x80000]);
        return calc_crc(saturn.rom[addr]);
      case 0xa:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return calc_crc(saturn.ram[addr - 0x80000]);
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xa0000)
          {
            return calc_crc(saturn.port1[(addr - 0xa0000) & port1_mask]);
          }
        return calc_crc(saturn.rom[addr]);
      case 0xb:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0x80000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return calc_crc(saturn.ram[addr - 0x80000]);
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xb0000)
          {
            return calc_crc(saturn.port2[((saturn.bank_switch << 18) +
                                         (addr - 0xb0000)) & port2_mask]);
/*
            if (port2_size > (saturn.bank_switch << 18))
              {
                return calc_crc(saturn.port2[(saturn.bank_switch << 18)
                                             + (addr - 0xb0000)]);
              }
            return 0x00;
*/
          }
        return calc_crc(saturn.rom[addr]);
      case 0xc:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0xc0000)
          {
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfc000
                && addr < 0xc4000)
              return calc_crc(saturn.ram[addr - 0xc0000]);
            if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xfe000
                && addr < 0xc2000)
              return calc_crc(saturn.ram[addr - 0xc0000]);
            return calc_crc(saturn.ram[addr - 0xc0000]);
          }
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xc0000)
          {
            return calc_crc(saturn.port1[(addr - 0xc0000) & port1_mask]);
          }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xc0000)
          {
            return calc_crc(saturn.port2[((saturn.bank_switch << 18) +
                                         (addr - 0xc0000)) & port2_mask]);
/*
            if (port2_size > (saturn.bank_switch << 18))
              {
                return calc_crc(saturn.port2[(saturn.bank_switch << 18)
                                             + (addr - 0xc0000)]);
              }
            return 0x00;
*/
          }
        return calc_crc(saturn.rom[addr]);
      case 0xd: case 0xe: case 0xf:
        if (saturn.mem_cntl[MCTL_SysRAM_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_SysRAM_GX].config[1] == 0xc0000)
            return calc_crc(saturn.ram[addr - 0xc0000]);
        if (saturn.mem_cntl[MCTL_PORT1_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_PORT1_GX].config[1] == 0xc0000)
            {
              return calc_crc(saturn.port1[(addr - 0xc0000) & port1_mask]);
            }
        if (saturn.mem_cntl[MCTL_PORT2_GX].config[0] == 0xc0000)
          if (saturn.mem_cntl[MCTL_PORT2_GX].config[1] == 0xc0000)
            {
              return calc_crc(saturn.port2[((saturn.bank_switch << 18) +
                                  (addr - 0xc0000)) & port2_mask]);
/*
              if (port2_size > (saturn.bank_switch << 18))
                {
                  return calc_crc(saturn.port2[(saturn.bank_switch << 18)
                                               + (addr - 0xc0000)]);
                }
              return 0x00;
*/
            }
        return calc_crc(saturn.rom[addr]);
    }
  return 0x00;
}

long
#ifdef __FunctionProto__
read_nibbles(long addr, int len)
#else
read_nibbles(addr, len)
long addr;
int len;
#endif
{
  long val = 0;

  addr += len;
  while (len-- > 0) {
    val = (val << 4) | read_nibble(--addr);
  }
  return val;
}

void
#ifdef __FunctionProto__
write_nibbles(long addr, long val, int len)
#else
write_nibbles(addr, val, len)
long addr;
long val;
int len;
#endif
{
  while (len-- > 0) {
    write_nibble(addr++, val);
    val >>= 4;
  }
}

void
#ifdef __FunctionProto__
dev_memory_init(void)
#else
dev_memory_init()
#endif
{
  if (opt_gx)
    {
      read_nibble = read_nibble_gx;
      read_nibble_crc = read_nibble_crc_gx;
      write_nibble = write_nibble_gx;
    }
  else
    {
      read_nibble = read_nibble_sx;
      read_nibble_crc = read_nibble_crc_sx;
      write_nibble = write_nibble_sx;
    }
  memset(&device, 0, sizeof(device));
}

