/******************************************************************************
*
* Copyright (C) 2004-2017  Brian Berlin
* 
* 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 3 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, see <http://www.gnu.org/licenses/>
*
******************************************************************************
*
* FILENAME: memory.c
*
* DESCRIPTION:  This contains functions for handling 6502 memory for the system
*               Also handles redirection for memory mapped I/O
*
* AUTHORS:  bberlin
*
* CHANGE LOG
*
*  VER     DATE     CHANGED BY   DESCRIPTION OF CHANGE
* ------  --------  ----------   ------------------------------------
* 0.1     07/18/04  bberlin      Creation
* 0.2     03/09/06  bberlin      Added 4K cart support
*                                Added support for Bounty Bob 3 file
*                                Added many more 16K one chip rom CRCs
* 0.2.3   03/19/06  bberlin      Added Beamrider to one chip rom list in 
*                                  'load_cart'
* 0.3.0   03/27/06  bberlin      Added Missle Command+ to one chip rom list in 
*                                  'load_cart'
* 0.4.0   07/12/06  bberlin      Added Time Runner to one chip rom list in 
*                                  'load_cart'
* 0.5.0   02/07/07  bberlin      Added functions for writing to memory, took
*                                  out cart loading
* 0.6.3   11/29/09  bberlin      Fixed Bounty Bob cart map address for 8-bits
* 0.7.1   02/17/17  bberlin      Fixed Bounty Bob for Atari5200 again
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memory.h"
#include "console.h"
#include "antic.h"
#include "gtia.h"
#include "pokey.h"
#include "pia.h"
#include "debug.h"

KBYTE memory[0x10000];
typedef enum {
    HYB_BANK_MODE_8K,
    HYB_BANK_MODE_16K
} e_hyb_bank_mode;

static KBYTE *bank_image = NULL;
static int bank_image_size = 0;
static KBYTE mem_image[0x4000];
static KBYTE xe_bank_image[4][0x4000];
static KBYTE os_rom[0x4000];
static KBYTE basic_rom[0x2000];

static KBYTE *g_xe_bank_ptr = xe_bank_image[0];
static KBYTE *g_self_test_ptr = &os_rom[0x1000];

static KBYTE g_portb_value = 0xff;
static KBYTE g_mem_image = 0;
static KBYTE g_be_switch = 0xf;
static e_hyb_bank_mode g_hyb_bank_mode = HYB_BANK_MODE_8K;
static int g_hyb_bank = 0;
static int g_hyb_16k_bank = 0;

static KBYTE *sd_image = NULL;
static int sd_image_size = 0;
static KBYTE sd_cmd_buf[42];
static int sd_cmd_count = 0;
static int sd_bfff_phase = 0;  /* 0=idle, 1=busy/ready ($FF), 2=done ($00) */

KBYTE ( *mem_read_func[ FUNC_MEM_MAX ] ) ( KWORD addr );
void ( *mem_write_func[ FUNC_MEM_MAX ] ) ( KWORD addr, KBYTE value );

void write_5200_sram_hyb ( KWORD addr, KBYTE value );
void write_5200_sram_hyb_debug ( KWORD addr, KBYTE value );
void write_5200_sram_hyb_watch ( KWORD addr, KBYTE value );
KBYTE read_5200_rom_be_hyb ( KWORD addr );
KBYTE read_5200_rom_be_hyb_debug ( KWORD addr );

/******************************************************************************
**  Function   :  mem_init
**                            
**  Objective  :  This function initializes the memory function array for use
**                in assignments
**
**  Parameters :  NONE
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_init ( void ) {

    mem_read_func[FUNC_READ_RAM] = read_5200_ram;
    mem_read_func[FUNC_READ_RAM+1] = read_5200_ram_debug;
    mem_read_func[FUNC_READ_ROM] = read_5200_rom;
    mem_read_func[FUNC_READ_ROM+1] = read_5200_rom_debug;
    mem_read_func[FUNC_READ_STACK] = read_5200_stack;
    mem_read_func[FUNC_READ_STACK+1] = read_5200_stack_debug;
    mem_read_func[FUNC_READ_OS] = read_os_rom;
    mem_read_func[FUNC_READ_OS+1] = read_os_rom_debug;
    mem_read_func[FUNC_READ_BASIC] = read_basic_rom;
    mem_read_func[FUNC_READ_BASIC+1] = read_basic_rom_debug;
    mem_read_func[FUNC_READ_SELF] = read_self_test_rom;
    mem_read_func[FUNC_READ_SELF+1] = read_self_test_rom_debug;
    mem_read_func[FUNC_READ_MEM_IMG] = read_mem_image_ram;
    mem_read_func[FUNC_READ_MEM_IMG+1] = read_mem_image_ram_debug;
    mem_read_func[FUNC_READ_XE_BANK] = read_xe_bank_ram;
    mem_read_func[FUNC_READ_XE_BANK+1] = read_xe_bank_ram_debug;
    mem_read_func[FUNC_READ_BB] = read_5200_rom_bb;
    mem_read_func[FUNC_READ_BB+1] = read_5200_rom_bb_debug;
    mem_read_func[FUNC_READ_BB_PC] = read_5200_rom_bb_pc;
    mem_read_func[FUNC_READ_BB_PC+1] = read_5200_rom_bb_pc_debug;
    mem_read_func[FUNC_READ_64K] = read_5200_rom_64k;
    mem_read_func[FUNC_READ_64K+1] = read_5200_rom_64k_debug;
    mem_read_func[FUNC_READ_32K_DB] = read_cart_32k_db;
    mem_read_func[FUNC_READ_32K_DB+1] = read_cart_32k_db_debug;
    mem_read_func[FUNC_READ_XEGS] = read_cart_xegs;
    mem_read_func[FUNC_READ_XEGS+1] = read_cart_xegs_debug;
    mem_read_func[FUNC_READ_SWXEGS] = read_cart_xegs;
    mem_read_func[FUNC_READ_SWXEGS+1] = read_cart_xegs_debug;
    mem_read_func[FUNC_READ_MEGA] = read_cart_xegs;
    mem_read_func[FUNC_READ_MEGA+1] = read_cart_xegs_debug;
    mem_read_func[FUNC_READ_OSS] = read_cart_oss;
    mem_read_func[FUNC_READ_OSS+1] = read_cart_oss_debug;
    mem_read_func[FUNC_READ_OSS2] = read_cart_oss2;
    mem_read_func[FUNC_READ_OSS2+1] = read_cart_oss2_debug;
    mem_read_func[FUNC_READ_ATMAX] = read_cart_xegs;
    mem_read_func[FUNC_READ_ATMAX+1] = read_cart_xegs_debug;
    mem_read_func[FUNC_READ_WILL] = read_cart_will;
    mem_read_func[FUNC_READ_WILL+1] = read_cart_will_debug;
    mem_read_func[FUNC_READ_SDX] = read_cart_sdx;
    mem_read_func[FUNC_READ_SDX+1] = read_cart_sdx_debug;
    mem_read_func[FUNC_READ_BLIZ] = read_cart_blizzard;
    mem_read_func[FUNC_READ_BLIZ+1] = read_cart_blizzard_debug;
    mem_read_func[FUNC_READ_ATRAX] = read_cart_xegs;
    mem_read_func[FUNC_READ_ATRAX+1] = read_cart_xegs_debug;
    mem_read_func[FUNC_READ_16KBE] = read_5200_rom_be16k;
    mem_read_func[FUNC_READ_16KBE+1] = read_5200_rom_be16k_debug;
    mem_read_func[FUNC_READ_32KBE] = read_5200_rom_be32k;
    mem_read_func[FUNC_READ_32KBE+1] = read_5200_rom_be32k_debug;
    mem_read_func[FUNC_READ_512_HYB_BE] = read_5200_rom_be_hyb;
    mem_read_func[FUNC_READ_512_HYB_BE+1] = read_5200_rom_be_hyb_debug;
    mem_read_func[FUNC_READ_512_HYB_BF] = read_5200_rom_512k_hyb;
    mem_read_func[FUNC_READ_512_HYB_BF+1] = read_5200_rom_512k_hyb_debug;
    mem_read_func[FUNC_READ_512_HYB_BANKED] = read_5200_hyb_banked;
    mem_read_func[FUNC_READ_512_HYB_BANKED+1] = read_5200_hyb_banked_debug;
    mem_read_func[FUNC_READ_ANTIC] = antic_read_register;
    mem_read_func[FUNC_READ_ANTIC+1] = antic_read_register_debug;
    mem_read_func[FUNC_READ_GTIA] = gtia_read_register;
    mem_read_func[FUNC_READ_GTIA+1] = gtia_read_register_debug;
    mem_read_func[FUNC_READ_POKEY] = pokey_read_register;
    mem_read_func[FUNC_READ_POKEY+1] = pokey_read_register_debug;
    mem_read_func[FUNC_READ_PIA] = pia_read_register;
    mem_read_func[FUNC_READ_PIA+1] = pia_read_register_debug;

    mem_write_func[FUNC_WRITE_RAM] = write_5200_ram;
    mem_write_func[FUNC_WRITE_RAM+1] = write_5200_ram_debug;
    mem_write_func[FUNC_WRITE_ROM] = write_5200_rom;
    mem_write_func[FUNC_WRITE_ROM+1] = write_5200_rom_debug;
    mem_write_func[FUNC_WRITE_STACK] = write_5200_stack;
    mem_write_func[FUNC_WRITE_STACK+1] = write_5200_stack_debug;
    mem_write_func[FUNC_WRITE_OS] = write_5200_rom;
    mem_write_func[FUNC_WRITE_OS+1] = write_5200_rom_debug;
    mem_write_func[FUNC_WRITE_BASIC] = write_5200_rom;
    mem_write_func[FUNC_WRITE_BASIC+1] = write_5200_rom_debug;
    mem_write_func[FUNC_WRITE_SELF] = write_5200_rom;
    mem_write_func[FUNC_WRITE_SELF+1] = write_5200_rom_debug;
    mem_write_func[FUNC_WRITE_MEM_IMG] = write_mem_image_ram;
    mem_write_func[FUNC_WRITE_MEM_IMG+1] = write_mem_image_ram_debug;
    mem_write_func[FUNC_WRITE_XE_BANK] = write_xe_bank_ram;
    mem_write_func[FUNC_WRITE_XE_BANK+1] = write_xe_bank_ram_debug;
    mem_write_func[FUNC_WRITE_BB] = write_5200_rom_bb;
    mem_write_func[FUNC_WRITE_BB+1] = write_5200_rom_bb_debug;
    mem_write_func[FUNC_WRITE_BB_PC] = write_5200_rom_bb_pc;
    mem_write_func[FUNC_WRITE_BB_PC+1] = write_5200_rom_bb_pc_debug;
    mem_write_func[FUNC_WRITE_64K] = write_5200_rom_64k;
    mem_write_func[FUNC_WRITE_64K+1] = write_5200_rom_64k_debug;
    mem_write_func[FUNC_WRITE_32K_DB] = write_cart_32k_db;
    mem_write_func[FUNC_WRITE_32K_DB+1] = write_cart_32k_db_debug;
    mem_write_func[FUNC_WRITE_XEGS] = write_cart_xegs;
    mem_write_func[FUNC_WRITE_XEGS+1] = write_cart_xegs_debug;
    mem_write_func[FUNC_WRITE_SWXEGS] = write_cart_swxegs;
    mem_write_func[FUNC_WRITE_SWXEGS+1] = write_cart_swxegs_debug;
    mem_write_func[FUNC_WRITE_MEGA] = write_cart_mega;
    mem_write_func[FUNC_WRITE_MEGA+1] = write_cart_mega_debug;
    mem_write_func[FUNC_WRITE_OSS] = write_cart_oss;
    mem_write_func[FUNC_WRITE_OSS+1] = write_cart_oss_debug;
    mem_write_func[FUNC_WRITE_OSS2] = write_cart_oss2;
    mem_write_func[FUNC_WRITE_OSS2+1] = write_cart_oss2_debug;
    mem_write_func[FUNC_WRITE_ATMAX] = write_cart_atmax;
    mem_write_func[FUNC_WRITE_ATMAX+1] = write_cart_atmax_debug;
    mem_write_func[FUNC_WRITE_WILL] = write_cart_will;
    mem_write_func[FUNC_WRITE_WILL+1] = write_cart_will_debug;
    mem_write_func[FUNC_WRITE_SDX] = write_cart_sdx;
    mem_write_func[FUNC_WRITE_SDX+1] = write_cart_sdx_debug;
    mem_write_func[FUNC_WRITE_BLIZ] = write_cart_blizzard;
    mem_write_func[FUNC_WRITE_BLIZ+1] = write_cart_blizzard_debug;
    mem_write_func[FUNC_WRITE_ATRAX] = write_cart_atrax;
    mem_write_func[FUNC_WRITE_ATRAX+1] = write_cart_atrax_debug;
    mem_write_func[FUNC_WRITE_16KBE] = write_5200_rom_be16k;
    mem_write_func[FUNC_WRITE_16KBE+1] = write_5200_rom_be16k_debug;
    mem_write_func[FUNC_WRITE_32KBE] = write_5200_rom_be32k;
    mem_write_func[FUNC_WRITE_32KBE+1] = write_5200_rom_be32k_debug;
    mem_write_func[FUNC_WRITE_512_HYB_BF] = write_5200_rom_512k_hyb;
    mem_write_func[FUNC_WRITE_512_HYB_BF+1] = write_5200_rom_512k_hyb_debug;
    mem_write_func[FUNC_WRITE_512_HYB_SRAM] = write_5200_sram_hyb;
    mem_write_func[FUNC_WRITE_512_HYB_SRAM+1] = write_5200_sram_hyb_debug;
    mem_write_func[FUNC_WRITE_ANTIC] = antic_write_register;
    mem_write_func[FUNC_WRITE_ANTIC+1] = antic_write_register_debug;
    mem_write_func[FUNC_WRITE_GTIA] = gtia_write_register;
    mem_write_func[FUNC_WRITE_GTIA+1] = gtia_write_register_debug;
    mem_write_func[FUNC_WRITE_POKEY] = pokey_write_register;
    mem_write_func[FUNC_WRITE_POKEY+1] = pokey_write_register_debug;
    mem_write_func[FUNC_WRITE_PIA] = pia_write_register;
    mem_write_func[FUNC_WRITE_PIA+1] = pia_write_register_debug;

    if ( bank_image == NULL )
        memory_init_bank ( 0x20000 );

} /* end mem_init */

/******************************************************************************
**  Function   :  mem_get_state_values
**                            
**  Objective  :  This grabs values needed for storage into a state save file
**
**  Parameters :  portb     - memory portb value
**                mem_image - 1 if ram behind cart is on
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_get_state_values ( KBYTE *portb, KBYTE *mem_image, KBYTE *be_switch  ) {

    *portb = g_portb_value;
    *mem_image = g_mem_image;
    *be_switch = g_be_switch;

} /* mem_get_state_values */

/******************************************************************************
**  Function   :  mem_set_state_values
**                            
**  Objective  :  This sets values needed for setting up memory from the state
**                file
**
**  Parameters :  portb     - memory portb value
**                mem_image - 1 if ram behind cart is on
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_set_state_values ( KBYTE portb, KBYTE mem_image, KBYTE be_switch  ) {

    g_portb_value = portb;
    g_mem_image = mem_image;
    g_be_switch = be_switch;

} /* mem_set_state_values */

/******************************************************************************
**  Function   :  mem_get_bank_image
**                            
**  Objective  :  This returns the pointer to the global bank image
**
**  Parameters :  NONE
**                                                
**  return     :  pointer to bank image array
**      
******************************************************************************/ 
KBYTE * mem_get_bank_image ( void ) {

    return bank_image;

} /* mem_get_bank_image */

/******************************************************************************
**  Function   :  mem_get_bank_image_size
**
**  Objective  :  This returns the allocated size of the global bank image
**
**  Parameters :  NONE
**
**  return     :  size of bank image array in bytes
******************************************************************************/
int mem_get_bank_image_size ( void ) {

    return bank_image_size;

} /* mem_get_bank_image_size */

/******************************************************************************
**  Function   :  mem_get_mem_image ; mem_get_mem_image_size
**
**  Objective  :  Access the hidden RAM buffer that sits behind the cartridge
**                so save-states can serialize its contents.
**
**  Parameters :  NONE
**
**  return     :  pointer to / size of the hidden RAM image
******************************************************************************/
KBYTE * mem_get_mem_image ( void ) {

    return mem_image;

} /* mem_get_mem_image */

int mem_get_mem_image_size ( void ) {

    /* element count - states_save_binary stores one byte per KBYTE element */
    return sizeof(mem_image) / sizeof(mem_image[0]);

} /* mem_get_mem_image_size */

/******************************************************************************
**  Function   :  mem_get_hyb_state ; mem_set_hyb_state
**
**  Objective  :  Get/set the CART_512K_HYBRID active bank latch for save-states.
**                memory_init_bank() resets these, so the setter must run after
**                the bank image is restored.  The setter validates the incoming
**                values against bank_image_size so a malformed state cannot make
**                the hybrid read handlers index past bank_image.
**
**  Parameters :  mode   - HYB_BANK_MODE_8K or HYB_BANK_MODE_16K
**                bank8  - active 8K bank index
**                bank16 - active 16K bank index
**
**  return     :  NONE
******************************************************************************/
void mem_get_hyb_state ( int *mode, int *bank8, int *bank16 ) {

    *mode = g_hyb_bank_mode;
    *bank8 = g_hyb_bank;
    *bank16 = g_hyb_16k_bank;

} /* mem_get_hyb_state */

void mem_set_hyb_state ( int mode, int bank8, int bank16 ) {

    g_hyb_bank_mode = ( mode == HYB_BANK_MODE_16K ) ?
                          HYB_BANK_MODE_16K : HYB_BANK_MODE_8K;

    if ( bank8 < 0 || bank8 * 0x2000 + 0x1FFF >= bank_image_size )
        bank8 = 0;
    if ( bank16 < 0 || bank16 * 0x4000 + 0x3FFF >= bank_image_size )
        bank16 = 0;

    g_hyb_bank = bank8;
    g_hyb_16k_bank = bank16;

} /* mem_set_hyb_state */

/******************************************************************************
**  Function   :  mem_get_sd_image ; mem_get_sd_image_size
**
**  Objective  :  Access the hybrid SD backing image so save-states can persist
**                the .dat contents loaded for an Atarimax Ultimate SD cart.
**
**  Parameters :  NONE
**
**  return     :  pointer to / size of the SD backing image
******************************************************************************/
KBYTE * mem_get_sd_image ( void ) {

    return sd_image;

} /* mem_get_sd_image */

int mem_get_sd_image_size ( void ) {

    return sd_image_size;

} /* mem_get_sd_image_size */

/******************************************************************************
**  Function   :  mem_get_sd_cmd_state ; mem_set_sd_cmd_state
**
**  Objective  :  Get/set the transient SD command machine (the 42-byte command
**                buffer, the byte count, and the $BFFF ready/busy phase) so a
**                state captured mid-command-stream resumes exactly.  Must be set
**                after memory_init_sd(), which resets the command state.
**
**  Parameters :  cmd_buf - 42-byte command buffer
**                count   - number of bytes captured so far
**                phase   - $BFFF poll phase
**
**  return     :  NONE
******************************************************************************/
void mem_get_sd_cmd_state ( KBYTE *cmd_buf, int *count, int *phase ) {

    memcpy ( cmd_buf, sd_cmd_buf, sizeof(sd_cmd_buf) );
    *count = sd_cmd_count;
    *phase = sd_bfff_phase;

} /* mem_get_sd_cmd_state */

void mem_set_sd_cmd_state ( const KBYTE *cmd_buf, int count, int phase ) {

    memcpy ( sd_cmd_buf, cmd_buf, sizeof(sd_cmd_buf) );
    if ( count < 0 || count > (int)sizeof(sd_cmd_buf) )
        count = 0;
    sd_cmd_count = count;
    sd_bfff_phase = phase;

} /* mem_set_sd_cmd_state */

/******************************************************************************
**  Function   :  mem_reset
**                            
**  Objective  :  This re-assigns functions and assigns portb to its initial
**                state for XL/XE.
**
**  Parameters :  NONE
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_reset ( void  ) {

    g_portb_value = 0xff;
    g_mem_image = 0;

    /*
     * Kill any bootloader stuff we may have
     */
    memset ( memory, 0, sizeof(KBYTE) * 0x800 );

    mem_assign_ops ( console_get_ptr()->cpu  );

} /* mem_reset */

/******************************************************************************
**  Function   :  mem_assign_ops
**                            
**  Objective  :  This function assigns functions to memory function pointers
**
**  Parameters :  cpu - pointer to cpu struct that contains array of pointers
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_assign_ops ( t_cpu *cpu  ) {

    int i;
    int gtia_addr, antic_addr, pokey_addr;
    int ram_end;

    /*
     * First determine chip address based on the machine
     *   Also go ahead and assign rom sections
     */
    for ( i = 0xc0; i < 0x100; i++ ) {
        cpu->read_mem[i] = read_5200_rom;
        cpu->write_mem[i] = write_5200_rom;
    }
    if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
        gtia_addr = 0xc0;
        antic_addr = 0xd4;
        pokey_addr = 0xe8;
        ram_end = 0x40;
    }
    else {
        gtia_addr = 0xd0;
        antic_addr = 0xd4;
        pokey_addr = 0xd2;
        if ( console_get_ptr()->ram_size == 16 )
            ram_end = 0x40;
        if ( console_get_ptr()->cart->loaded && 
             (console_get_ptr()->cart->media_info.image_size > 0x2000) && 
             (console_get_ptr()->cart->media_info.mapping != CART_OSS) )
            ram_end = 0x80;
        else if ( (!console_get_ptr()->basic_enable) && 
                  (console_get_ptr()->machine_type == MACHINE_TYPE_800) )
            ram_end = 0xc0;
        else
            ram_end = 0xa0;
    }

    /*
     * assign for OS RAM and writable RAM
     */
    for ( i = 0x00; i < ram_end; i++ ) {
        cpu->read_mem[i] = read_5200_ram;
        cpu->write_mem[i] = write_5200_ram;
    }

    /*
     * assign for Stack
     */
    cpu->read_mem[0x01] = read_5200_stack;
    cpu->write_mem[0x01] = write_5200_stack;

    /*
     * assign for Cartridge ROM
     */
    for ( i = ram_end; i < 0xc0; i++ ) {
        cpu->read_mem[i] = read_5200_rom;
        cpu->write_mem[i] = write_5200_rom;
    }

    /*
     * assign for GTIA
     */
    cpu->read_mem[gtia_addr] = gtia_read_register;
    cpu->write_mem[gtia_addr] = gtia_write_register;

    /*
     * assign for ANTIC
     */
    cpu->read_mem[antic_addr] = antic_read_register;
    cpu->write_mem[antic_addr] = antic_write_register;

    /*
     * assign for POKEY
     */
    cpu->read_mem[pokey_addr] = pokey_read_register;
    cpu->write_mem[pokey_addr] = pokey_write_register;

    if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
        for ( i = 0xe9; i < 0xf0; i++ ) {
            cpu->read_mem[i] = pokey_read_register;
            cpu->write_mem[i] = pokey_write_register;
        }
    }

    /*
     * assign for PIA
     */
    if ( console_get_machine_type() != MACHINE_TYPE_5200 ) {
        cpu->read_mem[0xd3] = pia_read_register;
        cpu->write_mem[0xd3] = pia_write_register;
    }

    /*
     * Extra RAM space in the 800
     */
    if ( (console_get_machine_type() == MACHINE_TYPE_800) && 
         (console_get_ptr()->ram_size == 52) ) {
        for ( i = 0xc0; i < 0xd0; i++ ) {
            cpu->read_mem[i] = read_5200_ram;
            cpu->write_mem[i] = write_5200_ram;
        }
    }

    /***************************************/
    /* Now check for bank switching        */
    /***************************************/

    /*
     * Bounty Bob
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_40K ) {
        cpu->read_mem[0x4f] = read_5200_rom_bb;
        cpu->write_mem[0x4f] = write_5200_rom_bb;
        cpu->read_mem[0x5f] = read_5200_rom_bb;
        cpu->write_mem[0x5f] = write_5200_rom_bb;
    }

    if ( console_get_ptr()->cart->media_info.mapping == CART_PC_40K ) {
        cpu->read_mem[0x8f] = read_5200_rom_bb_pc;
        cpu->write_mem[0x8f] = write_5200_rom_bb_pc;
        cpu->read_mem[0x9f] = read_5200_rom_bb_pc;
        cpu->write_mem[0x9f] = write_5200_rom_bb_pc;
    }

    /*
     * 64k bank switching mode
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_64K ) {
        cpu->read_mem[0x3f] = read_5200_rom_64k;
        cpu->write_mem[0x3f] = write_5200_rom_64k;
        cpu->read_mem[0xbf] = read_5200_rom_64k;
        cpu->write_mem[0xbf] = write_5200_rom_64k;
    }

    /*
     * Standard 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_PC_32K ) {
        cpu->read_mem[0xd5] = read_cart_32k_db;
        cpu->write_mem[0xd5] = write_cart_32k_db;
    }

    /*
     * 16kb bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_16K_BE ) {
        cpu->read_mem[0xbf] = read_5200_rom_be16k;
        cpu->write_mem[0xbf] = write_5200_rom_be16k;
    }

    /*
     * 32kb bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_32K_BE ) {
        cpu->read_mem[0xbf] = read_5200_rom_be32k;
        cpu->write_mem[0xbf] = write_5200_rom_be32k;
    }

    /*
     * 512k hybrid bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_512K_HYBRID ) {
        cpu->read_mem[0xbe] = read_5200_rom_be_hyb;
        cpu->read_mem[0xbf] = read_5200_rom_512k_hyb;
        cpu->write_mem[0xbf] = write_5200_rom_512k_hyb;
        for ( i = 0x40; i < 0x80; ++i )
            cpu->read_mem[i] = read_5200_hyb_banked;
        for ( i = 0x40; i < 0x60; ++i )
            cpu->write_mem[i] = write_5200_sram_hyb;
    }

    /*
     * XEGS 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_XEGS ) {
        cpu->read_mem[0xd5] = read_cart_xegs;
        cpu->write_mem[0xd5] = write_cart_xegs;
    }

    /*
     * SWXEGS 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_SWXEGS ) {
        cpu->read_mem[0xd5] = read_cart_xegs;
        cpu->write_mem[0xd5] = write_cart_swxegs;
    }

    /*
     * MEGA 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_MEGA ) {
        cpu->read_mem[0xd5] = read_cart_xegs;
        cpu->write_mem[0xd5] = write_cart_mega;
    }

    /*
     * ATMAX 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_ATMAX ) {
        cpu->read_mem[0xd5] = read_cart_xegs;
        cpu->write_mem[0xd5] = write_cart_atmax;
    }

    /*
     * ATRAX 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_ATRAX ) {
        cpu->read_mem[0xd5] = read_cart_xegs;
        cpu->write_mem[0xd5] = write_cart_atrax;
    }

    /*
     * WILL 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_WILL ) {
        cpu->read_mem[0xd5] = read_cart_will;
        cpu->write_mem[0xd5] = write_cart_will;
    }

    /*
     * SDX,EXP,Diamond 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_SDX ||
         console_get_ptr()->cart->media_info.mapping == CART_EXP ||
         console_get_ptr()->cart->media_info.mapping == CART_DIAMOND ) {
        cpu->read_mem[0xd5] = read_cart_sdx;
        cpu->write_mem[0xd5] = write_cart_sdx;
    }

    /*
     * Blizzard and Phoenix 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_BLIZZARD ||
         console_get_ptr()->cart->media_info.mapping == CART_PHOENIX ) {
        cpu->read_mem[0xd5] = read_cart_blizzard;
        cpu->write_mem[0xd5] = write_cart_blizzard;
    }

    /*
     * OSS 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_OSS ) {
        cpu->read_mem[0xd5] = read_cart_oss;
        cpu->write_mem[0xd5] = write_cart_oss;
    }

    /*
     * OSS2 8-bit bank switching
     */
    if ( console_get_ptr()->cart->media_info.mapping == CART_OSS2 ) {
        cpu->read_mem[0xd5] = read_cart_oss2;
        cpu->write_mem[0xd5] = write_cart_oss2;
    }

    /*
     * Assign Memory behind cartridge if set
     */
    if ( g_mem_image ) {
        for ( i = ram_end; i < 0xc0; ++i ) {
            cpu->read_mem[i] = read_mem_image_ram;
            cpu->write_mem[i] = write_mem_image_ram;
        }
    }

    /*
     * Assign Port B Handler to current value
     */
    if ( console_get_machine_type() == MACHINE_TYPE_XL )
        mem_assign_portb_ops ( console_get_ptr()->cpu, g_portb_value );

} /* mem_assign_ops */

/******************************************************************************
**  Function   :  mem_assign_ops_debug
**                            
**  Objective  :  This function assigns the debug functions based on current
**                breakpoints
**
**  Parameters :  cpu -     pointer to cpu struct that contains array of pointers
**                p_debug - pointer to debug struct that has breakpoints
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_assign_ops_debug ( t_cpu *cpu, t_debug *p_debug  ) {

    int i, j;
    int page;
    int ram_end;

    if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
        ram_end = 0x40;
    }
    else {
        if ( console_get_ptr()->cart->loaded && 
             (console_get_ptr()->cart->media_info.image_size > 0x2000) && 
             (console_get_ptr()->cart->media_info.mapping != CART_OSS) )
            ram_end = 0x80;
        else
            ram_end = 0xa0;
    }

    /*
     * First assign ops as normal
     */
    mem_assign_ops ( cpu );

    /*
     * Now go through the breakpoints and reassign
     */
    for ( i = 0; i < DEBUG_MAX_BK; ++i ) {

        if ( p_debug->breakpoint[i] == BK_WATCH ) {
            page = p_debug->breakpoint_place[i] >> 8;
            if ( (page >= 0x00 && page < ram_end) ) {
                cpu->write_mem[page] = write_5200_ram_watch;
            }
            if ( console_get_ptr()->cart->media_info.mapping == CART_512K_HYBRID &&
                 page >= 0x40 && page < 0x60 ) {
                cpu->write_mem[page] = write_5200_sram_hyb_watch;
            }
            if ( page == 0x01 ) {
                cpu->write_mem[page] = write_5200_stack_watch;
            }
        }

        if ( p_debug->breakpoint[i] != BK_MEM )
            continue;

        /*
         *  Ops are assigned based on memory page
         */
        page = p_debug->breakpoint_place[i] >> 8;
    
        for ( j = 0; j < FUNC_MEM_MAX; j+=2 ) {
            if ( cpu->read_mem[page] == mem_read_func[j] )
                cpu->read_mem[page] = mem_read_func[j + 1];
            if ( cpu->write_mem[page] == mem_write_func[j] )
                cpu->write_mem[page] = mem_write_func[j + 1];
        }
    }

} /* mem_assign_ops_debug */

/******************************************************************************
**  Function   :  mem_assign_antic_hyb_ops
**
**  Objective  :  Override ANTIC's $BE and $BF read handlers with plain ROM
**                reads for hybrid carts. ANTIC copies all CPU read handlers
**                at antic_reset(), but ANTIC DMA should not trigger SD card
**                command side effects. Call after antic_reset().
**
**  Parameters :  NONE
**
**  return     :  NONE
******************************************************************************/
void mem_assign_antic_hyb_ops ( void ) {

    if ( console_get_ptr()->cart->media_info.mapping == CART_512K_HYBRID ) {
        t_antic *antic = console_get_ptr()->antic;
        antic->read_mem[0xbe] = read_5200_rom;
        antic->read_mem[0xbf] = read_5200_rom;
    }
}

KBYTE mem_get_portb_value ( void ) {
    return g_portb_value;
}

/******************************************************************************
**  Function   :  mem_assign_portb_ops
**                            
**  Objective  :  This function assigns functions to memory function pointers
**                according to the portb register.  Only used with XL/XE.
**
**  Parameters :  cpu - pointer to cpu struct that contains array of pointers
**                portb - PORTB register value
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void mem_assign_portb_ops ( t_cpu *cpu,  KBYTE portb  ) {

    int i;
    int debug;
    t_antic *antic = console_get_ptr()->antic;
    int ram_size = console_get_ptr()->ram_size;

    g_portb_value = portb;

    /*
     * OS Rom
     */
    for ( i = 0xc0; i < 0x100; ++i ) {
        if ( i >= 0xd0 && i < 0xd8 )
            continue;
        debug = debug_is_page_in_debug ( i );
        if ( portb & 0x01 ) {
            cpu->read_mem[i] = mem_read_func[FUNC_READ_OS | debug];
            antic->read_mem[i] = mem_read_func[FUNC_READ_OS | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_OS | debug];
        }
        else {
            cpu->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
            antic->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_RAM | debug];
        }
    }

    /*
     * Basic Rom
     */
    if ( !console_get_ptr()->cart->loaded ) {
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            if ( portb & 0x02 ) {
                cpu->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
                antic->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
                cpu->write_mem[i] = mem_write_func[FUNC_WRITE_RAM | debug];
            }
            else {
                cpu->read_mem[i] = mem_read_func[FUNC_READ_BASIC | debug];
                antic->read_mem[i] = mem_read_func[FUNC_READ_BASIC | debug];
                cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
            }
        }
    }

    g_xe_bank_ptr = xe_bank_image[ (portb & 0x0c) >> 2 ];

    /*
     * CPU Switch
     */ 
    if ( ram_size > 64 ) {
        for ( i = 0x40; i < 0x80; ++i ) {
            debug = debug_is_page_in_debug ( i );
            if ( (portb & 0x10) == 0 ) {
                cpu->read_mem[i] = mem_read_func[FUNC_READ_XE_BANK | debug];
                cpu->write_mem[i] = mem_write_func[FUNC_WRITE_XE_BANK | debug];
            }
            else {
                cpu->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
                cpu->write_mem[i] = mem_write_func[FUNC_WRITE_RAM | debug];
            }
        }
    }

    /*
     * Antic Switch
     */ 
    if ( ram_size > 64 ) {
        for ( i = 0x40; i < 0x80; ++i ) {
            debug = debug_is_page_in_debug ( i );
            if ( (portb & 0x20) == 0 ) {
                antic->read_mem[i] = mem_read_func[FUNC_READ_XE_BANK | debug];
            }
            else {
                antic->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
            }
        }
    }

    /*
     * Self-Test Rom
     */ 
    for ( i = 0x50; i < 0x58; ++i ) {
        debug = debug_is_page_in_debug ( i );
        if ( (portb & 0x80)  || !(portb & 0x01) ) {
            cpu->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
            antic->read_mem[i] = mem_read_func[FUNC_READ_RAM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_RAM | debug];
        }
        else {
            cpu->read_mem[i] = mem_read_func[FUNC_READ_SELF | debug];
            antic->read_mem[i] = mem_read_func[FUNC_READ_SELF | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
    }

} /* mem_assign_portb_ops */

/******************************************************************************
**  Function   :  memory_write_rom_bytes
**                                  
**  Objective  :  This function will write the buffer of bytes to rom
**                                                                  
**  Parameters :  addr   - address in system to write to
**                buffer - buffer to write
**                size   - number of bytes to write
**                                                                
**  return     :  value at address                               
******************************************************************************/ 
void memory_write_rom_bytes ( KWORD addr, unsigned char *buffer, int size ) {

    int i;

    for ( i = 0; i < size; ++i ) {
        memory[ addr + i ] = buffer[i];
    }
}

void memory_init_bank ( int size ) {

    if ( bank_image != NULL )
        free ( bank_image );
    bank_image = NULL;
    bank_image_size = size;
    g_be_switch = 0xf;
    g_hyb_bank_mode = HYB_BANK_MODE_8K;
    g_hyb_bank = 0;
    g_hyb_16k_bank = 0;
    if ( size > 0 )
        bank_image = calloc ( size, sizeof(KBYTE) );
}

void memory_exit ( void ) {

    if ( bank_image != NULL ) {
        free ( bank_image );
        bank_image = NULL;
    }
    bank_image_size = 0;
    g_be_switch = 0xf;
    g_hyb_bank_mode = HYB_BANK_MODE_8K;
    g_hyb_bank = 0;
    g_hyb_16k_bank = 0;
    if ( sd_image != NULL ) {
        free ( sd_image );
        sd_image = NULL;
        sd_image_size = 0;
    }
    sd_cmd_count = 0;
    sd_bfff_phase = 0;
}

void memory_clear_sd ( void ) {
    if ( sd_image != NULL )
        free ( sd_image );
    sd_image = NULL;
    sd_image_size = 0;
    sd_cmd_count = 0;
    sd_bfff_phase = 0;
}

void memory_init_sd ( int size ) {

    memory_clear_sd ();
    sd_image = calloc ( size, sizeof(KBYTE) );
    sd_image_size = size;
}

void memory_write_sd_bytes ( int offset, unsigned char *buffer, int size ) {

    int i;

    if ( sd_image == NULL || buffer == NULL || offset < 0 || size < 0 ||
         offset > sd_image_size || size > sd_image_size - offset )
        return;

    for ( i = 0; i < size; ++i ) {
        sd_image[ offset + i ] = buffer[i];
    }
}

void memory_write_bank_bytes ( KWORD addr, unsigned char *buffer, int size ) {

    int i;

    for ( i = 0; i < size; ++i ) {
        bank_image[ addr + i ] = buffer[i];
    }
}

void memory_write_os_bytes ( KWORD addr, unsigned char *buffer, int size ) {

    int i;

    addr = addr & 0x3fff;
    for ( i = 0; i < size; ++i ) {
        os_rom[ addr + i ] = buffer[i];
    }
}

void memory_write_basic_bytes ( KWORD addr, unsigned char *buffer, int size ) {

    int i;

    addr = addr & 0x1fff;
    for ( i = 0; i < size; ++i ) {
        basic_rom[ addr + i ] = buffer[i];
    }
}

/******************************************************************************
**  Function   :  read_5200_ram         
**                                  
**  Objective  :  This function will read from system memory and return value
**                                                                  
**  Parameters :  addr - address in system ram to read from        
**                                                                
**  return     :  value at address                               
******************************************************************************/ 
KBYTE read_5200_ram ( KWORD addr ) {

    return memory[ addr ];
}

KBYTE read_5200_ram_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return memory[ addr ];
}

KBYTE read_5200_bank_switched ( KWORD addr ) {

    return bank_image[ addr ];
}

/******************************************************************************
**  Function   :  read_5200_stack         
**                                  
**  Objective  :  This function will read from the cpu stack (0x100-0x1ff)
**                                                                  
**  Parameters :  addr - address in system ram to read from        
**                                                                
**  return     :  value at address                               
******************************************************************************/ 
KBYTE read_5200_stack ( KWORD addr ) {

    return cpu_get_stack_value( addr & 0xff );
}

KBYTE read_5200_stack_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return cpu_get_stack_value( addr & 0xff );
}

/******************************************************************************
**  Function   :  read_5200_rom 
**                                     
**  Objective  :  This function will read from rom and return value   
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_5200_rom ( KWORD addr ) {

    return memory[ addr ];
}

KBYTE read_5200_rom_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return memory[ addr];
}

/******************************************************************************
**  Function   :  read_os_rom 
**                                     
**  Objective  :  This function will read from the os portion of rom
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_os_rom ( KWORD addr ) {

    return os_rom[ addr & 0x3fff ];
}

KBYTE read_os_rom_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return os_rom[ addr & 0x3fff ];
}

/******************************************************************************
**  Function   :  read_basic_rom 
**                                     
**  Objective  :  This function will read from the os portion of rom
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_basic_rom ( KWORD addr ) {

    return basic_rom[ addr & 0x1fff ];
}

KBYTE read_basic_rom_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return basic_rom[ addr & 0x1fff ];
}

/******************************************************************************
**  Function   :  read_self_test_rom 
**                                     
**  Objective  :  This function will read from the self-test portion of rom
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_self_test_rom ( KWORD addr ) {

    return g_self_test_ptr[ addr & 0x7ff ];
}

KBYTE read_self_test_rom_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return g_self_test_ptr[ addr & 0x7ff ];
}

/******************************************************************************
**  Function   :  read_xe_bank_ram 
**                                     
**  Objective  :  This function will read from the bank switched ram of the XE
**                                      
**  Parameters :  addr - address in ram to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_xe_bank_ram ( KWORD addr ) {

    return g_xe_bank_ptr[ addr & 0x3fff ];
}

KBYTE read_xe_bank_ram_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return g_xe_bank_ptr[ addr & 0x3fff ];
}

KBYTE read_mem_image_ram ( KWORD addr ) {

    return mem_image[ addr & 0x7fff ];
}

KBYTE read_mem_image_ram_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return mem_image[ addr & 0x7fff ];
}

/******************************************************************************
**  Function   :  write_5200_ram   
**                                
**  Objective  :  This function will write a value to system ram
** 
**  Parameters :  addr  - address in system ram to write to
**                value - value to write at address 
**                                             
**  return     :  NONE                        
******************************************************************************/ 
void write_5200_ram ( KWORD addr, KBYTE value ) {

    memory[ addr ] = value;
}

void write_5200_ram_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    memory[ addr ] = value;
}

void write_5200_ram_watch ( KWORD addr, KBYTE value ) {

    debug_break_for_watch (addr, value, memory[addr] );

    memory[ addr ] = value;
}

void write_5200_bank_switched ( KWORD addr, KBYTE value ) {

    bank_image[ addr ] = value;
}

void write_os_rom ( KWORD addr, KBYTE value ) {
    os_rom[ addr ] = value;
}

void write_basic_rom ( KWORD addr, KBYTE value ) {
    basic_rom[ addr ] = value;
}

void write_xe_bank_state ( KWORD addr, KBYTE value ) {
    xe_bank_image[ addr >> 14][ addr & 0x3fff ] = value;
}

KBYTE read_xe_bank_state ( KWORD addr ) {
    return xe_bank_image[ addr >> 14][ addr & 0x3fff ];
}

/******************************************************************************
**  Function   :  write_xe_bank_ram   
**                                
**  Objective  :  This function will write a value to bank switched ram for XE
** 
**  Parameters :  addr  - address in system ram to write to
**                value - value to write at address 
**                                             
**  return     :  NONE                        
******************************************************************************/ 
void write_xe_bank_ram ( KWORD addr, KBYTE value ) {

    g_xe_bank_ptr[ addr & 0x3fff ] = value;
}

void write_xe_bank_ram_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    g_xe_bank_ptr[ addr & 0x3fff ] = value;
}

void write_mem_image_ram ( KWORD addr, KBYTE value ) {

    mem_image[ addr & 0x7fff ] = value;
}

void write_mem_image_ram_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    mem_image[ addr & 0x7fff ] = value;
}

/******************************************************************************
**  Function   :  write_5200_stack 
**                                
**  Objective  :  This function will write a value to the cpu stack (0x100-0x1ff)
** 
**  Parameters :  addr  - address in system ram to write to
**                value - value to write at address 
**                                             
**  return     :  NONE                        
******************************************************************************/ 
void write_5200_stack ( KWORD addr, KBYTE value ) {

    cpu_set_stack_value ( addr & 0xff, value  );    
}

void write_5200_stack_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    cpu_set_stack_value ( addr & 0xff, value  );    
}

void write_5200_stack_watch ( KWORD addr, KBYTE value ) {

    debug_break_for_watch (addr, value, cpu_get_stack_value( addr & 0xff ) );

    cpu_set_stack_value ( addr & 0xff, value  );    
}

/******************************************************************************
**  Function   :  write_5200_rom                
**                                             
**  Objective  :  Placeholder function.  Can't really write to ROM
**
**  Parameters :  addr  - address in rom that had an attempted write
**                value - value to write at address
**                                                 
**  return     :  NONE                            
******************************************************************************/ 
void write_5200_rom ( KWORD addr, KBYTE value ) {

    //print_addr_val ( WRITE_MEM, addr, value );
}

void write_5200_rom_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    //print_addr_val ( WRITE_MEM, addr, value );
}

/******************************************************************************
**  Function   :  read_5200_rom_bb 
**                                     
**  Objective  :  Bounty Bob bank switching check
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_5200_rom_bb ( KWORD addr ) {

    if ( addr > 0x4ff5 && addr < 0x4ffa ) {
        addr -= 0x4ff6;
        memcpy ( &memory[0x4000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else if ( addr > 0x5ff5 && addr < 0x5ffa ) {
        addr -= 0x5ff6;
        memcpy ( &memory[0x5000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else
        return memory[ addr ];
}

KBYTE read_5200_rom_bb_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr > 0x4ff5 && addr < 0x4ffa ) {
        addr -= 0x4ff6;
        memcpy ( &memory[0x4000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else if ( addr > 0x5ff5 && addr < 0x5ffa ) {
        addr -= 0x5ff6;
        memcpy ( &memory[0x5000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else
        return memory[ addr ];
}

KBYTE read_5200_rom_bb_pc ( KWORD addr ) {

    if ( addr > 0x8ff5 && addr < 0x8ffa ) {
        addr -= 0x8ff6;
        memcpy ( &memory[0x8000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else if ( addr > 0x9ff5 && addr < 0x9ffa ) {
        addr -= 0x9ff6;
        memcpy ( &memory[0x9000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else
        return memory[ addr ];
}

KBYTE read_5200_rom_bb_pc_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr > 0x8ff5 && addr < 0x8ffa ) {
        addr -= 0x8ff6;
        memcpy ( &memory[0x8000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else if ( addr > 0x9ff5 && addr < 0x9ffa ) {
        addr -= 0x9ff6;
        memcpy ( &memory[0x9000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
        return 0;
    }
    else
        return memory[ addr ];
}

/******************************************************************************
**  Function   :  write_5200_rom_bb                
**                                             
**  Objective  :  Bounty Bob bank switching check.
**
**  Parameters :  addr  - address in rom that had an attempted write
**                value - value to write at address
**                                                 
**  return     :  NONE                            
******************************************************************************/ 
void write_5200_rom_bb ( KWORD addr, KBYTE value ) {

    if ( addr > 0x4ff5 && addr < 0x4ffa ) {
        addr -= 0x4ff6;
        memcpy ( &memory[0x4000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
    else if ( addr > 0x5ff5 && addr < 0x5ffa ) {
        addr -= 0x5ff6;
        memcpy ( &memory[0x5000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
}

void write_5200_rom_bb_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr > 0x4ff5 && addr < 0x4ffa ) {
        addr -= 0x4ff6;
        memcpy ( &memory[0x4000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
    else if ( addr > 0x5ff5 && addr < 0x5ffa ) {
        addr -= 0x5ff6;
        memcpy ( &memory[0x5000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
}

void write_5200_rom_bb_pc ( KWORD addr, KBYTE value ) {

    if ( addr > 0x8ff5 && addr < 0x8ffa ) {
        addr -= 0x8ff6;
        memcpy ( &memory[0x8000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
    else if ( addr > 0x9ff5 && addr < 0x9ffa ) {
        addr -= 0x9ff6;
        memcpy ( &memory[0x9000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
}

void write_5200_rom_bb_pc_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr > 0x8ff5 && addr < 0x8ffa ) {
        addr -= 0x8ff6;
        memcpy ( &memory[0x8000], &bank_image[addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
    else if ( addr > 0x9ff5 && addr < 0x9ffa ) {
        addr -= 0x9ff6;
        memcpy ( &memory[0x9000], &bank_image[0x4000+addr*0x1000] , 0x1000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  read_5200_rom_64k
**                                     
**  Objective  :  64k bank switching check.       
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_5200_rom_64k ( KWORD addr ) {

    if ( (addr == 0x3ff8) || (addr == 0xbff8) ) {
        memcpy ( &memory[0x4000], &bank_image[0x0000] , 0x8000*sizeof(KBYTE));
        return 0;
    }
    else if ( (addr == 0x3ff9) || (addr == 0xbff9) ) {
        memcpy ( &memory[0x4000], &bank_image[0x8000] , 0x8000*sizeof(KBYTE));
        return 0;
    }
    else
        return memory[ addr ];
}

KBYTE read_5200_rom_64k_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr == 0x3ff8) || (addr == 0xbff8) ) {
        memcpy ( &memory[0x4000], &bank_image[0x0000] , 0x8000*sizeof(KBYTE));
        return 0;
    }
    else if ( (addr == 0x3ff9) || (addr == 0xbff9) ) {
        memcpy ( &memory[0x4000], &bank_image[0x8000] , 0x8000*sizeof(KBYTE));
        return 0;
    }
    else
        return memory[ addr ];
}

/******************************************************************************
**  Function   :  write_5200_rom_64k                
**                                             
**  Objective  :  64k bank switching check.         
**
**  Parameters :  addr  - address in rom that had an attempted write
**                value - value to write at address
**                                                 
**  return     :  NONE                            
******************************************************************************/ 
void write_5200_rom_64k ( KWORD addr, KBYTE value ) {

    if ( (addr == 0x3ff8) || (addr == 0xbff8) ) {
        memcpy ( &memory[0x4000], &bank_image[0x0000] , 0x8000*sizeof(KBYTE));
    }
    else if ( (addr == 0x3ff9) || (addr == 0xbff9) ) {
        memcpy ( &memory[0x4000], &bank_image[0x8000] , 0x8000*sizeof(KBYTE));
    }
}

void write_5200_rom_64k_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr == 0x3ff8) || (addr == 0xbff8) ) {
        memcpy ( &memory[0x4000], &bank_image[0x0000] , 0x8000*sizeof(KBYTE));
    }
    else if ( (addr == 0x3ff9) || (addr == 0xbff9) ) {
        memcpy ( &memory[0x4000], &bank_image[0x8000] , 0x8000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  read_5200_rom_be16k or 32k
**                                     
**  Objective  :  Bryan Edewaard bank switching with 16k or 32k banks
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_5200_rom_be16k ( KWORD addr ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 14) - 1);

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return memory[ addr ];

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x4000] , 0x4000*sizeof(KBYTE));

    return memory[ addr ];
}

KBYTE read_5200_rom_be16k_debug ( KWORD addr ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 14) - 1);

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return memory[ addr ];

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x4000] , 0x4000*sizeof(KBYTE));

    return memory[ addr ];
}

KBYTE read_5200_rom_be32k ( KWORD addr ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 15) - 1);

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return memory[ addr ];

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x8000] , 0x8000*sizeof(KBYTE));

    return memory[ addr ];
}

KBYTE read_5200_rom_be32k_debug ( KWORD addr ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 15) - 1);

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return memory[ addr ];

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x8000] , 0x8000*sizeof(KBYTE));

    return memory[ addr ];
}

void write_5200_rom_be16k ( KWORD addr, KBYTE value ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 14) - 1);

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return;

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x4000] , 0x4000*sizeof(KBYTE));
}

void write_5200_rom_be16k_debug ( KWORD addr, KBYTE value ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 14) - 1);

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return;

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x4000] , 0x4000*sizeof(KBYTE));
}

void write_5200_rom_be32k ( KWORD addr, KBYTE value ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 15) - 1);

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return;

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x8000] , 0x8000*sizeof(KBYTE));
}

void write_5200_rom_be32k_debug ( KWORD addr, KBYTE value ) {

    int mask = ((console_get_ptr()->cart->media_info.image_size >> 15) - 1);

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr & 0xe0) == 0xe0 )
        g_be_switch  = 0xf & mask;

    else if ( (addr & 0xf0) ==0xd0 )
        g_be_switch = ((g_be_switch & 0xc) | (addr >> 2) & 0x3) & mask;

    else if ( (addr & 0xf0) ==0xc0 )
        g_be_switch = ((g_be_switch & 0x3) | (addr & 0xc)) & mask;

    else
        return;

    memcpy ( &memory[0x4000], &bank_image[g_be_switch*0x8000] , 0x8000*sizeof(KBYTE));
}

/******************************************************************************
**  Function   :  read_5200_hyb_banked
**
**  Objective  :  Direct read from the current hybrid cart bank window.
**                $BF80-$BF87 selects a 16K read bank at $4000-$7FFF.
**                $BFC0-$BFFD selects the 8K write/read mode where $4000-$5FFF
**                writes through to the same 8K bank read at $6000-$7FFF.
**                No memcpy needed; the active bank latch is updated on every
**                bank switch.
**
**  Parameters :  addr - address in $4000-$7FFF
**
**  return     :  byte from bank_image
******************************************************************************/
KBYTE read_5200_hyb_banked ( KWORD addr ) {

    if ( g_hyb_bank_mode == HYB_BANK_MODE_16K )
        return bank_image[g_hyb_16k_bank * 0x4000 + (addr & 0x3FFF)];

    return bank_image[g_hyb_bank * 0x2000 + (addr & 0x1FFF)];
}

KBYTE read_5200_hyb_banked_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( g_hyb_bank_mode == HYB_BANK_MODE_16K )
        return bank_image[g_hyb_16k_bank * 0x4000 + (addr & 0x3FFF)];

    return bank_image[g_hyb_bank * 0x2000 + (addr & 0x1FFF)];
}

/******************************************************************************
**  Function   :  read_cart_512k_hyb ; write_cart_512k_hyb
**
**  Objective  :  The 8k/8k bank switching of the Atarimax SD Cart
**
**  Parameters :  addr - address in system rom to read from
**
**  return     :  value at address
******************************************************************************/
KBYTE read_5200_rom_512k_hyb ( KWORD addr ) {

    /* $BFFE: SD controller asserts $FF (ready) after recognizing $55/$AA sync.
     * Outside SD mode, returns ROM byte ($00 = IRQ vector lo for BIOS JMP ($BFFE)). */
    if ( addr == 0xbffe )
        return ( console_get_ptr()->cpu->pc >= 0xf000 ) ? memory[addr] : 0xff;

    /* $BFFF: after SD DMA, return $FF once for the ready/busy poll, then $00 for
     * completion; otherwise return ROM byte.  PC check matches $BFFE: BIOS
     * JMP ($BFFE) always reads the real ROM vector byte. */
    if ( addr == 0xbfff ) {
        if ( sd_bfff_phase > 0 && console_get_ptr()->cpu->pc < 0xf000 )
            return ( sd_bfff_phase++ == 1 ) ? 0xff : ( sd_bfff_phase = 0, 0x00 );
        return memory[addr];
    }

    if ( addr >= 0xbf80 && addr <= 0xbf87 ) {
        g_hyb_bank_mode = HYB_BANK_MODE_16K;
        g_hyb_16k_bank = addr - 0xbf80;
    }
    else if ( addr >= 0xbfc0 && addr <= 0xbffd ) {
        g_hyb_bank_mode = HYB_BANK_MODE_8K;
        g_hyb_bank = addr - 0xbfc0;
    }

    return memory[addr];
}

KBYTE read_5200_rom_512k_hyb_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr == 0xbffe )
        return ( console_get_ptr()->cpu->pc >= 0xf000 ) ? memory[addr] : 0xff;

    if ( addr == 0xbfff ) {
        if ( sd_bfff_phase > 0 && console_get_ptr()->cpu->pc < 0xf000 )
            return ( sd_bfff_phase++ == 1 ) ? 0xff : ( sd_bfff_phase = 0, 0x00 );
        return memory[addr];
    }

    if ( addr >= 0xbf80 && addr <= 0xbf87 ) {
        g_hyb_bank_mode = HYB_BANK_MODE_16K;
        g_hyb_16k_bank = addr - 0xbf80;
    }
    else if ( addr >= 0xbfc0 && addr <= 0xbffd ) {
        g_hyb_bank_mode = HYB_BANK_MODE_8K;
        g_hyb_bank = addr - 0xbfc0;
    }

    return memory[addr];
}

void write_5200_rom_512k_hyb ( KWORD addr, KBYTE value ) {

    if ( addr >= 0xbf80 && addr <= 0xbf87 ) {
        g_hyb_bank_mode = HYB_BANK_MODE_16K;
        g_hyb_16k_bank = addr - 0xbf80;
    }
    else if ( addr >= 0xbfc0 && addr <= 0xbffd ) {
        g_hyb_bank_mode = HYB_BANK_MODE_8K;
        g_hyb_bank = addr - 0xbfc0;
    }
}

void write_5200_rom_512k_hyb_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr >= 0xbf80 && addr <= 0xbf87 ) {
        g_hyb_bank_mode = HYB_BANK_MODE_16K;
        g_hyb_16k_bank = addr - 0xbf80;
    }
    else if ( addr >= 0xbfc0 && addr <= 0xbffd ) {
        g_hyb_bank_mode = HYB_BANK_MODE_8K;
        g_hyb_bank = addr - 0xbfc0;
    }
}

/******************************************************************************
**  Function   :  write_5200_sram_hyb
**
**  Objective  :  Write handler for $4000-$5FFF SRAM window of hybrid cart.
**                Writes go to memory[] and write through to bank_image so
**                they survive bank switches.
**
**  Parameters :  addr  - address to write to
**                value - value to write
**
**  return     :  NONE
******************************************************************************/
void write_5200_sram_hyb ( KWORD addr, KBYTE value ) {

    if ( bank_image != NULL && g_hyb_bank_mode == HYB_BANK_MODE_8K )
        bank_image[g_hyb_bank * 0x2000 + (addr & 0x1FFF)] = value;
}

void write_5200_sram_hyb_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( bank_image != NULL && g_hyb_bank_mode == HYB_BANK_MODE_8K )
        bank_image[g_hyb_bank * 0x2000 + (addr & 0x1FFF)] = value;
}

void write_5200_sram_hyb_watch ( KWORD addr, KBYTE value ) {

    unsigned int index = g_hyb_bank * 0x2000 + (addr & 0x1FFF);

    if ( bank_image != NULL ) {
        if ( g_hyb_bank_mode == HYB_BANK_MODE_16K ) {
            debug_break_for_watch ( addr, value, read_5200_hyb_banked ( addr ) );
            return;
        }
        debug_break_for_watch ( addr, value, bank_image[index] );
        bank_image[index] = value;
    }
}

/******************************************************************************
**  Function   :  sd_execute_command
**
**  Objective  :  Execute the accumulated 42-byte SD card command buffer.
**                Parses the command and DMA's from sd_image into bank_image.
**
**  Parameters :  NONE
**
**  return     :  NONE
******************************************************************************/
static void sd_execute_command ( void ) {

    unsigned int size, offset, dest_offset;
    unsigned int sum = 0;
    unsigned int expected_checksum;
    int checksum_ok;
    int i;

    for ( i = 6; i < 38; ++i )
        sum += sd_cmd_buf[i];
    expected_checksum = sum & 0xff;
    checksum_ok = sd_cmd_buf[38] == expected_checksum &&
        sd_cmd_buf[39] == (expected_checksum ^ 0xff);

    /* The command carries three 32-bit little-endian fields:
     *   bytes  6-9  : destination SRAM byte address (e.g. 0x00060000 = bank 0x30)
     *   bytes 10-13 : transfer size
     *   bytes 14-17 : source offset within the .dat
     * (The earlier reverse-engineered layout mislabeled byte 4 as "dest_bank"
     * and byte 8 as a command code; byte 8 is the high byte of this address.) */
    dest_offset = sd_cmd_buf[6]  | (sd_cmd_buf[7]  << 8) |
                  (sd_cmd_buf[8] << 16) | (sd_cmd_buf[9] << 24);
    size   = sd_cmd_buf[10] | (sd_cmd_buf[11] << 8) |
             (sd_cmd_buf[12] << 16) | (sd_cmd_buf[13] << 24);
    offset = sd_cmd_buf[14] | (sd_cmd_buf[15] << 8) |
             (sd_cmd_buf[16] << 16) | (sd_cmd_buf[17] << 24);

    if ( sd_cmd_buf[0] != 0x55 || sd_cmd_buf[1] != 0xaa )
        return;
    if ( sd_cmd_buf[40] != 0xa5 || sd_cmd_buf[41] != 0x5a )
        return;

    /* Bytes 38-39 are the body checksum (sum of bytes 6-37) and its complement.
     * That body is exactly the DMA-critical payload (dest/size/offset/filename),
     * so a valid checksum is the real guard against a stray $BE read stream
     * triggering an in-range DMA.  Enforced by default; KAT5200_HYB_LAX_CHECKSUM
     * disables it as an escape hatch for any future .hyb with a different scheme. */
    if ( !checksum_ok && getenv("KAT5200_HYB_LAX_CHECKSUM") == NULL )
        return;

    if ( sd_image == NULL || bank_image == NULL )
        return;
    if ( offset >= (unsigned int)sd_image_size || size == 0 ||
         size > (unsigned int)sd_image_size - offset )
        return;
    if ( dest_offset >= (unsigned int)bank_image_size ||
         size > (unsigned int)bank_image_size - dest_offset )
        return;

    memcpy ( &bank_image[dest_offset], &sd_image[offset], size * sizeof(KBYTE) );

    /* Signal the ROM loader's $BFFF poll that the transfer is complete. */
    sd_bfff_phase = 1;
}

static void sd_capture_command_byte ( KBYTE value ) {

    value &= 0xff;

    /* Resynchronize on the $55 $AA preamble so a partial/garbage read stream
     * cannot leave the buffer misaligned. */
    if ( sd_cmd_count == 0 ) {
        if ( value == 0x55 )
            sd_cmd_buf[sd_cmd_count++] = value;
        return;
    }

    if ( sd_cmd_count == 1 ) {
        if ( value == 0xaa )
            sd_cmd_buf[sd_cmd_count++] = value;
        else if ( value != 0x55 )
            sd_cmd_count = 0;
        return;
    }

    sd_cmd_buf[sd_cmd_count++] = value;
    if ( sd_cmd_count >= 42 ) {
        sd_execute_command();
        sd_cmd_count = 0;
    }
}

/******************************************************************************
**  Function   :  read_5200_rom_be_hyb
**
**  Objective  :  SD card command interface for the Atarimax Ultimate SD cart.
**                Each read of $BExx encodes one command byte (addr & 0xFF).
**                After 42 bytes the command is executed.
**
**  Parameters :  addr - address in range $BE00-$BEFF
**
**  return     :  0
******************************************************************************/
KBYTE read_5200_rom_be_hyb ( KWORD addr ) {

    sd_capture_command_byte ( (KBYTE)(addr & 0xff) );
    return memory[addr];
}

KBYTE read_5200_rom_be_hyb_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    sd_capture_command_byte ( (KBYTE)(addr & 0xff) );
    return memory[addr];
}

/******************************************************************************
**  Function   :  read_cart_32k_db ; write_cart_32k_db
**                                     
**  Objective  :  32k DB Bank Switching
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_cart_32k_db ( KWORD addr ) {

    memcpy ( &memory[0x8000], &bank_image[(addr & 0x3) * 0x2000] , 0x2000*sizeof(KBYTE));
    return 0xff;
}

KBYTE read_cart_32k_db_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    memcpy ( &memory[0x8000], &bank_image[(addr & 0x3) * 0x2000] , 0x2000*sizeof(KBYTE));
    return 0xff;
}

void write_cart_32k_db ( KWORD addr, KBYTE value ) {

    memcpy ( &memory[0x8000], &bank_image[(addr & 0x3) * 0x2000] , 0x2000*sizeof(KBYTE));
}

void write_cart_32k_db_debug ( KWORD addr, KBYTE value ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    memcpy ( &memory[0x8000], &bank_image[(addr & 0x3) * 0x2000] , 0x2000*sizeof(KBYTE));
}

/******************************************************************************
**  Function   :  read_cart_xegs ; write_cart_xegs
**                                     
**  Objective  :  XEGS Bank Switching
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_cart_xegs ( KWORD addr ) {

    return 0xff;
}

KBYTE read_cart_xegs_debug ( KWORD addr ) {

    debug_search_for_breakpoint (BK_MEM, addr );

    return 0xff;
}

void write_cart_xegs ( KWORD addr, KBYTE value ) {

    int mask = (console_get_ptr()->cart->media_info.image_size >> 13) - 1;

    memcpy ( &memory[0x8000], &bank_image[(value & mask) * 0x2000] , 0x2000*sizeof(KBYTE));
}

void write_cart_xegs_debug ( KWORD addr, KBYTE value ) {

    int mask = (console_get_ptr()->cart->media_info.image_size >> 13) - 1;

    debug_search_for_breakpoint (BK_MEM, addr );

    memcpy ( &memory[0x8000], &bank_image[(value & mask) * 0x2000] , 0x2000*sizeof(KBYTE));
}

/******************************************************************************
**  Function   :  write_cart_swxegs
**                                     
**  Objective  :  SWXEGS Bank Switching (read is same as XEGS)
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_swxegs ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( value & 0x80 ) {
        g_mem_image = 1;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0x8000], &bank_image[(value & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

void write_cart_swxegs_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( value & 0x80 ) {
        g_mem_image = 1;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0x8000], &bank_image[(value & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  write_cart_mega
**                                     
**  Objective  :  MEGA Cart Bank Switching (read is same as XEGS)
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_mega ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 14) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( value & 0x80 ) {
        g_mem_image = 1;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0x8000], &bank_image[(value & mask) * 0x4000], 0x4000*sizeof(KBYTE));
    }
}

void write_cart_mega_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 14) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( value & 0x80 ) {
        g_mem_image = 1;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0x80; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0x8000], &bank_image[(value & mask) * 0x4000], 0x4000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  write_cart_atmax
**                                     
**  Objective  :  ATMAX Cart Bank Switching (read is same as XEGS)
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_atmax ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;
    int bank;
    int threshold;

    if ( mask == 0x7f ) {
        bank = addr & 0xff;
        threshold = 0x80;
    }
    else {
        bank = addr & 0x3f;
        threshold = 0x10;
        if ( bank >= 0x20 )
            return;
    }

    if ( bank >= threshold ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(bank & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

void write_cart_atmax_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;
    int bank;
    int threshold;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( mask == 0x7f ) {
        bank = addr & 0xff;
        threshold = 0x80;
    }
    else {
        bank = addr & 0x3f;
        threshold = 0x10;
        if ( bank >= 0x20 )
            return;
    }

    if ( bank >= threshold ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(bank & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  write_cart_atrax
**                                     
**  Objective  :  ATRAX Cart Bank Switching (read is same as XEGS)
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_atrax ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( value & 0x80 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(value & 0xf) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

void write_cart_atrax_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( value & 0x80 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(value & 0xf) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  write_cart_will;read_cart_will
**                                     
**  Objective  :  WILL Cart Bank Switching 
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_will ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(addr & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

void write_cart_will_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(addr & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

KBYTE read_cart_will ( KWORD addr ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(addr & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }
    
    return 0xff;
}

KBYTE read_cart_will_debug ( KWORD addr ) {

    int i;
    int debug;
    int mask = ((console_get_ptr()->cart->media_info.image_size >> 13) - 1);
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(addr & mask) * 0x2000], 0x2000*sizeof(KBYTE));
    }

    return 0xff;
}

/******************************************************************************
**  Function   :  write_cart_sdx;read_cart_sdx
**                                     
**  Objective  :  EXP, SDX, and Diamond Cart Bank Switching 
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_sdx ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( console_get_ptr()->cart->media_info.mapping == CART_SDX ) {
        if ( (addr & 0xf0) != 0xe0 ) 
            return;
    }
    else if ( console_get_ptr()->cart->media_info.mapping == CART_EXP ) {
        if ( (addr & 0xf0) != 0x70 ) 
            return;
    }
    else {
        if ( (addr & 0xf0) != 0xd0 ) 
            return;
    }

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(~addr & 0x07) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

void write_cart_sdx_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( console_get_ptr()->cart->media_info.mapping == CART_SDX ) {
        if ( (addr & 0xf0) != 0xe0 ) 
            return;
    }
    else if ( console_get_ptr()->cart->media_info.mapping == CART_EXP ) {
        if ( (addr & 0xf0) != 0x70 ) 
            return;
    }
    else {
        if ( (addr & 0xf0) != 0xd0 ) 
            return;
    }

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(~addr & 0x07) * 0x2000], 0x2000*sizeof(KBYTE));
    }
}

KBYTE read_cart_sdx ( KWORD addr ) {

    int i;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( console_get_ptr()->cart->media_info.mapping == CART_SDX ) {
        if ( (addr & 0xf0) != 0xe0 ) 
            return 0xff;
    }
    else if ( console_get_ptr()->cart->media_info.mapping == CART_EXP ) {
        if ( (addr & 0xf0) != 0x70 ) 
            return 0xff;
    }
    else {
        if ( (addr & 0xf0) != 0xd0 ) 
            return 0xff;
    }

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(~addr & 0x07) * 0x2000], 0x2000*sizeof(KBYTE));
    }
    
    return 0xff;
}

KBYTE read_cart_sdx_debug ( KWORD addr ) {

    int i;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( console_get_ptr()->cart->media_info.mapping == CART_SDX ) {
        if ( (addr & 0xf0) != 0xe0 ) 
            return 0xff;
    }
    else if ( console_get_ptr()->cart->media_info.mapping == CART_EXP ) {
        if ( (addr & 0xf0) != 0x70 ) 
            return 0xff;
    }
    else {
        if ( (addr & 0xf0) != 0xd0 ) 
            return 0xff;
    }

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        memcpy ( &memory[0xa000], &bank_image[(~addr & 0x07) * 0x2000], 0x2000*sizeof(KBYTE));
    }

    return 0xff;
}

/******************************************************************************
**  Function   :  write_cart_blizzard;read_cart_blizzard
**                                     
**  Objective  :  Blizzard and Phoenix Cart Bank Switching 
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
void write_cart_blizzard ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int start = 0x80 | ((console_get_ptr()->cart->media_info.image_size < 0x3000) ? 0x20 : 0x00);
    t_cpu *cpu = console_get_ptr()->cpu;

    g_mem_image = 1;
    for ( i = start; i < 0xc0; ++i ) {
        debug = debug_is_page_in_debug ( i );
        cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
        cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
    }
}

void write_cart_blizzard_debug ( KWORD addr, KBYTE value ) {

    int i;
    int debug;
    int start = 0x80 | ((console_get_ptr()->cart->media_info.image_size < 0x3000) ? 0x20 : 0x00);
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    g_mem_image = 1;
    for ( i = start; i < 0xc0; ++i ) {
        debug = debug_is_page_in_debug ( i );
        cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
        cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
    }
}

KBYTE read_cart_blizzard ( KWORD addr ) {

    int i;
    int debug;
    int start = 0x80 | ((console_get_ptr()->cart->media_info.image_size < 0x3000) ? 0x20 : 0x00);
    t_cpu *cpu = console_get_ptr()->cpu;

    g_mem_image = 1;
    for ( i = start; i < 0xc0; ++i ) {
        debug = debug_is_page_in_debug ( i );
        cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
        cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
    }
    
    return 0xff;
}

KBYTE read_cart_blizzard_debug ( KWORD addr ) {

    int i;
    int debug;
    int start = 0x80 | ((console_get_ptr()->cart->media_info.image_size < 0x3000) ? 0x20 : 0x00);
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    g_mem_image = 1;
    for ( i = start; i < 0xc0; ++i ) {
        debug = debug_is_page_in_debug ( i );
        cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
        cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
    }

    return 0xff;
}

/******************************************************************************
**  Function   :  read_cart_oss ; write_cart_oss
**                                     
**  Objective  :  16k OSS Bank Switching
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_cart_oss ( KWORD addr ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x7) == 0x7 )
            b = 1;
        else
            b = (addr & 0x7)>>1;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }

    return 0xff;
}

KBYTE read_cart_oss_debug ( KWORD addr ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x7) == 0x7 )
            b = 1;
        else
            b = (addr & 0x7)>>1;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }

    return 0xff;
}

void write_cart_oss ( KWORD addr, KBYTE value ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x7) == 0x7 )
            b = 1;
        else
            b = (addr & 0x7)>>1;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }
}

void write_cart_oss_debug ( KWORD addr, KBYTE value ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( addr & 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x7) == 0x7 )
            b = 1;
        else
            b = (addr & 0x7)>>1;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }
}

/******************************************************************************
**  Function   :  read_cart_oss2 ; write_cart_oss2
**                                     
**  Objective  :  16k OSS2 Bank Switching
**                                      
**  Parameters :  addr - address in system rom to read from        
**                                           
**  return     :  value at address          
******************************************************************************/ 
KBYTE read_cart_oss2 ( KWORD addr ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( (addr & 0x09) == 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x9) == 0x0 )
            b = 1;
        else if ( (addr & 0x9) == 0x1 )
            b = 3;
        else
            b = 2;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }

    return 0xff;
}

KBYTE read_cart_oss2_debug ( KWORD addr ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr & 0x09) == 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x9) == 0x0 )
            b = 1;
        else if ( (addr & 0x9) == 0x1 )
            b = 3;
        else
            b = 2;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }

    return 0xff;
}

void write_cart_oss2 ( KWORD addr, KBYTE value ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    if ( (addr & 0x09) == 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x9) == 0x0 )
            b = 1;
        else if ( (addr & 0x9) == 0x1 )
            b = 3;
        else
            b = 2;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }
}

void write_cart_oss2_debug ( KWORD addr, KBYTE value ) {

    int i,b;
    int debug;
    t_cpu *cpu = console_get_ptr()->cpu;

    debug_search_for_breakpoint (BK_MEM, addr );

    if ( (addr & 0x09) == 0x08 ) {
        g_mem_image = 1;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_MEM_IMG | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_MEM_IMG | debug];
        }
    }
    else {
        g_mem_image = 0;
        for ( i = 0xa0; i < 0xc0; ++i ) {
            debug = debug_is_page_in_debug ( i );
            cpu->read_mem[i] = mem_read_func[FUNC_READ_ROM | debug];
            cpu->write_mem[i] = mem_write_func[FUNC_WRITE_ROM | debug];
        }
        if ( (addr & 0x9) == 0x0 )
            b = 1;
        else if ( (addr & 0x9) == 0x1 )
            b = 3;
        else
            b = 2;
        memcpy ( &memory[0xa000], &bank_image[b * 0x1000], 0x1000*sizeof(KBYTE));
    }
}
