ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/c64/c64mem.c

This is c64mem.c in view mode; [Download] [Up]

/*
 * c64mem.c -- C64 memory handling.
 *
 * Written by
 *  Ettore Perazzoli (ettore@comm2000.it)
 *  Andreas Boose (boose@unixserv.rz.fh-hannover.de)
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

#ifndef CBM64
#define CBM64                   /* for mkdep */
#endif

#include "vice.h"

#include <stdio.h>

#include "c64cia.h"
#include "c64mem.h"
#include "c64tpi.h"
#include "cartridge.h"
#include "cmdline.h"
#include "emuid.h"
#include "interrupt.h"
#include "maincpu.h"
#include "memutils.h"
#include "mon.h"
#include "parallel.h"
#include "patchrom.h"
#include "resources.h"
#include "reu.h"
#include "sid.h"
#include "tapeunit.h"
#include "true1541.h"
#include "utils.h"
#include "vicii.h"
#include "vmachine.h"

#ifdef HAVE_RS232
#include "c64acia.h"
#endif

/* ------------------------------------------------------------------------- */

/* C64 memory-related resources.  */

/* Name of the character ROM.  */
static char *chargen_rom_name;

/* Name of the BASIC ROM.  */
static char *basic_rom_name;

/* Name of the Kernal ROM.  */
static char *kernal_rom_name;

/* Kernal revision for ROM patcher.  */
static char *kernal_revision;

/* Flag: Do we enable the Emulator ID?  */
static int emu_id_enabled;

/* Flag: Do we enable the IEEE488 interface emulation?  */
static int ieee488_enabled;

/* Flag: Do we enable the external REU?  */
static int reu_enabled;

/* Type of the cartridge attached.  */
static int mem_cartridge_type = CARTRIDGE_NONE;

#ifdef HAVE_RS232
/* Flag: Do we enable the $DE** ACIA RS232 interface emulation?  */
static int acia_de_enabled;

/* Flag: Do we enable the $D6** ACIA RS232 interface emulation?  */
static int acia_d6_enabled;
#endif

static void cartridge_config_changed(BYTE mode);

/* FIXME: Should load the new character ROM.  */
static int set_chargen_rom_name(resource_value_t v)
{
    const char *name = (const char *) v;

    if (chargen_rom_name != NULL && name != NULL
        && strcmp(name, chargen_rom_name) == 0)
        return 0;

    string_set(&chargen_rom_name, name);
    return 0;
}

/* FIXME: Should load the new Kernal ROM.  */
static int set_kernal_rom_name(resource_value_t v)
{
    const char *name = (const char *) v;

    if (kernal_rom_name != NULL && name != NULL
        && strcmp(name, kernal_rom_name) == 0)
        return 0;

    string_set(&kernal_rom_name, name);
    return 0;
}

/* FIXME: Should load the new BASIC ROM.  */
static int set_basic_rom_name(resource_value_t v)
{
    const char *name = (const char *) v;

    if (basic_rom_name != NULL
        && name != NULL
        && strcmp(name, basic_rom_name) == 0)
        return 0;

    string_set(&basic_rom_name, name);
    return 0;
}

static int set_emu_id_enabled(resource_value_t v)
{
    if (!(int) v) {
        emu_id_enabled = 0;
        return 0;
    } else if (mem_cartridge_type == CARTRIDGE_NONE) {
        emu_id_enabled = 1;
        return 0;
    } else {
        /* Other extensions share the same address space, so they cannot be
           enabled at the same time.  */
        return -1;
    }
}

static int set_ieee488_enabled(resource_value_t v)
{
    if (!(int) v) {
        ieee488_enabled = 0;
        return 0;
    } else if (!reu_enabled && (mem_cartridge_type == CARTRIDGE_NONE)) {
        ieee488_enabled = 1;
        return 0;
    } else {
        /* The REU and the IEEE488 interface share the same address space, so
           they cannot be enabled at the same time.  */
        return -1;
    }
}

/* FIXME: Should initialize the REU when turned on.  */
static int set_reu_enabled(resource_value_t v)
{
    if (!(int) v) {
        reu_enabled = 0;
        return 0;
    } else if (!ieee488_enabled && (mem_cartridge_type == CARTRIDGE_NONE)) {
        reu_enabled = 1;
        return 0;
    } else {
        /* The REU and the IEEE488 interface share the same address space, so
           they cannot be enabled at the same time.  */
        return -1;
    }
}

/* FIXME: Should patch the ROM on-the-fly.  */
static int set_kernal_revision(resource_value_t v)
{
    const char *rev = (const char *) v;

    string_set(&kernal_revision, rev);
    return 0;
}

#ifdef HAVE_RS232
static int set_acia_d6_enabled(resource_value_t v)
{
    acia_d6_enabled = (int) v;
    return 0;
}

static int set_acia_de_enabled(resource_value_t v)
{
    acia_de_enabled = (int) v;
    return 0;
}
#endif

static resource_t resources[] = {
    { "ChargenName", RES_STRING, (resource_value_t) "chargen",
     (resource_value_t *) &chargen_rom_name, set_chargen_rom_name },
    { "KernalName", RES_STRING, (resource_value_t) "kernal",
     (resource_value_t *) &kernal_rom_name, set_kernal_rom_name },
    { "BasicName", RES_STRING, (resource_value_t) "basic",
     (resource_value_t *) &basic_rom_name, set_basic_rom_name },
    { "REU", RES_INTEGER, (resource_value_t) 0,
     (resource_value_t *) &reu_enabled, set_reu_enabled },
    { "IEEE488", RES_INTEGER, (resource_value_t) 0,
     (resource_value_t *) &ieee488_enabled, set_ieee488_enabled },
    { "EmuID", RES_INTEGER, (resource_value_t) 0,
     (resource_value_t *) &emu_id_enabled, set_emu_id_enabled },
    { "KernalRev", RES_STRING, (resource_value_t) NULL,
     (resource_value_t *) &kernal_revision, set_kernal_revision },
#ifdef HAVE_RS232
    { "AciaDE", RES_INTEGER, (resource_value_t) 0,
     (resource_value_t *) &acia_de_enabled, set_acia_de_enabled },
    { "AciaD6", RES_INTEGER, (resource_value_t) 0,
     (resource_value_t *) &acia_d6_enabled, set_acia_d6_enabled },
#endif
    { NULL }
};

int c64_mem_init_resources(void)
{
    return resources_register(resources);
}

/* ------------------------------------------------------------------------- */

/* C64 memory-related command-line options.  */
/* FIXME: Maybe the `-kernal', `-basic' and `-chargen' options should not
   really affect resources.  */

static cmdline_option_t cmdline_options[] =
{
    { "-kernal", SET_RESOURCE, 1, NULL, NULL, "KernalName", NULL,
      "<name>", "Specify name of Kernal ROM image" },
    { "-basic", SET_RESOURCE, 1, NULL, NULL, "BasicName", NULL,
      "<name>", "Specify name of BASIC ROM image" },
    { "-chargen", SET_RESOURCE, 1, NULL, NULL, "ChargenName", NULL,
      "<name>", "Specify name of character generator ROM image" },
    { "-reu", SET_RESOURCE, 0, NULL, NULL, "REU", (resource_value_t) 1,
      NULL, "Enable the 512K RAM expansion unit" },
    { "+reu", SET_RESOURCE, 0, NULL, NULL, "REU", (resource_value_t) 0,
      NULL, "Disable the 512K RAM expansion unit" },
    { "-emuid", SET_RESOURCE, 0, NULL, NULL, "EmuID", (resource_value_t) 1,
      NULL, "Enable emulator identification" },
    { "+emuid", SET_RESOURCE, 0, NULL, NULL, "EmuID", (resource_value_t) 0,
      NULL, "Disable emulator identification" },
    { "-ieee488", SET_RESOURCE, 0, NULL, NULL, "IEEE488", (resource_value_t) 1,
      NULL, "Enable the IEEE488 interface emulation" },
    { "+ieee488", SET_RESOURCE, 0, NULL, NULL, "IEEE488", (resource_value_t) 0,
      NULL, "Disable the IEEE488 interface emulation" },
    { "-kernalrev", SET_RESOURCE, 1, NULL, NULL, "KernalRev", NULL,
      "<revision>", "Patch the Kernal ROM to the specified <revision>" },
#ifdef HAVE_RS232
    { "-acia1", SET_RESOURCE, 0, NULL, NULL, "AciaDE", (resource_value_t) 1,
      NULL, "Enable the $DE** ACIA RS232 interface emulation" },
    { "+acia1", SET_RESOURCE, 0, NULL, NULL, "AciaDE", (resource_value_t) 0,
      NULL, "Disable the $DE** ACIA RS232 interface emulation" },
#if 0
    { "-acia2", SET_RESOURCE, 0, NULL, NULL, "AciaD6", (resource_value_t) 1,
      NULL, "Enable the $D6** ACIA RS232 interface emulation" },
    { "+acia2", SET_RESOURCE, 0, NULL, NULL, "AciaD6", (resource_value_t) 0,
      NULL, "Disable the $D6** ACIA RS232 interface emulation" },
#endif
#endif
    { NULL }
};

int c64_mem_init_cmdline_options(void)
{
    return cmdline_register_options(cmdline_options);
}

/* ------------------------------------------------------------------------- */

/* Number of possible memory configurations.  */
#define NUM_CONFIGS     32
/* Number of possible video banks (16K each).  */
#define NUM_VBANKS      4

/* The C64 memory.  */
BYTE ram[C64_RAM_SIZE];
BYTE basic_rom[C64_BASIC_ROM_SIZE];
BYTE kernal_rom[C64_KERNAL_ROM_SIZE];
BYTE chargen_rom[C64_CHARGEN_ROM_SIZE];

/* Size of RAM...  */
int ram_size = C64_RAM_SIZE;

/* Flag: nonzero if the Kernal and BASIC ROMs have been loaded.  */
int rom_loaded = 0;

/* Pointers to the currently used memory read and write tables.  */
read_func_ptr_t *_mem_read_tab_ptr;
store_func_ptr_t *_mem_write_tab_ptr;
BYTE **_mem_read_base_tab_ptr;

/* Memory read and write tables.  */
static store_func_ptr_t mem_write_tab[NUM_VBANKS][NUM_CONFIGS][0x101];
static read_func_ptr_t mem_read_tab[NUM_CONFIGS][0x101];
static BYTE *mem_read_base_tab[NUM_CONFIGS][0x101];

static store_func_ptr_t mem_write_tab_watch[0x101];
static read_func_ptr_t mem_read_tab_watch[0x101];

/* Processor port.  */
static struct {
    BYTE dir, data;
} pport;

/* Expansion port signals.  */
static struct {
    int exrom;
    int game;
} export;

/* Current video bank (0, 1, 2 or 3).  */
static int vbank;

/* Current memory configuration.  */
static int mem_config;

/* Tape sense status: 1 = some button pressed, 0 = no buttons pressed.  */
static int tape_sense = 0;

/* Exansion port ROML/ROMH images.  */
BYTE roml_banks[0x8000], romh_banks[0x8000];

/* Exansion port RAM images.  */
BYTE export_ram0[0x2000];

/* Expansion port ROML/ROMH/RAM banking.  */
static int roml_bank, romh_bank, export_ram;

/* ------------------------------------------------------------------------- */

BYTE REGPARM1 read_watch(ADDRESS addr)
{
    mon_watch_push_load_addr(addr, e_comp_space);
    return mem_read_tab[mem_config][addr >> 8](addr);
}


void REGPARM2 store_watch(ADDRESS addr, BYTE value)
{
    mon_watch_push_store_addr(addr, e_comp_space);
    mem_write_tab[vbank][mem_config][addr >> 8](addr, value);
}

/* ------------------------------------------------------------------------- */

static inline void pla_config_changed(void)
{
    mem_config = (((~pport.dir | pport.data) & 0x7) | (export.exrom << 3)
                  | (export.game << 4));

    /* Bit 4: tape sense.  0 = some button pressed, 1 = no buttons pressed.  */
    if (tape_sense)
        ram[1] = (pport.data | ~pport.dir) & 0x2f;
    else
        ram[1] = (pport.data | ~pport.dir) & 0x3f;

    ram[0] = pport.dir;

    if (any_watchpoints(e_comp_space)) {
        _mem_read_tab_ptr = mem_read_tab_watch;
        _mem_write_tab_ptr = mem_write_tab_watch;
    } else {
        _mem_read_tab_ptr = mem_read_tab[mem_config];
        _mem_write_tab_ptr = mem_write_tab[vbank][mem_config];
    }

    _mem_read_base_tab_ptr = mem_read_base_tab[mem_config];
}

void mem_toggle_watchpoints(int flag)
{
    if (flag) {
        _mem_read_tab_ptr = mem_read_tab_watch;
        _mem_write_tab_ptr = mem_write_tab_watch;
    } else {
        _mem_read_tab_ptr = mem_read_tab[mem_config];
        _mem_write_tab_ptr = mem_write_tab[vbank][mem_config];
    }
}

BYTE REGPARM1 read_zero(ADDRESS addr)
{
    return ram[addr & 0xff];
}

void REGPARM2 store_zero(ADDRESS addr, BYTE value)
{
    addr &= 0xff;

    switch ((BYTE) addr) {
      case 0:
        if (pport.dir != value) {
            pport.dir = value;
            pla_config_changed();
        }
        break;
      case 1:
        if (pport.data != value) {
            pport.data = value;
            pla_config_changed();
        }
        break;
      default:
        ram[addr] = value;
    }
}

BYTE REGPARM1 read_basic(ADDRESS addr)
{
    return basic_rom[addr & 0x1fff];
}

BYTE REGPARM1 read_kernal(ADDRESS addr)
{
    return kernal_rom[addr & 0x1fff];
}

BYTE REGPARM1 read_chargen(ADDRESS addr)
{
    return chargen_rom[addr & 0xfff];
}

BYTE REGPARM1 read_ram(ADDRESS addr)
{
    return ram[addr];
}

void REGPARM2 store_ram(ADDRESS addr, BYTE value)
{
    ram[addr] = value;
}

void REGPARM2 store_ram_hi(ADDRESS addr, BYTE value)
{
    ram[addr] = value;
    if (reu_enabled && addr == 0xff00)
        reu_dma(-1);
}

void REGPARM2 store_io2(ADDRESS addr, BYTE value)
{
    if ((addr & 0xff00) == 0xdf00) {
        if (reu_enabled)
            store_reu(addr & 0x0f, value);
        if (ieee488_enabled)
            store_tpi(addr & 0x07, value);
    }
    if ((mem_cartridge_type == CARTRIDGE_ACTION_REPLAY) && export_ram)
        export_ram0[0x1f00 + (addr & 0xff)] = value;
    if (mem_cartridge_type == CARTRIDGE_KCS_POWER)
        export_ram0[0x1f00 + (addr & 0xff)] = value;
    return;
}

BYTE REGPARM1 read_io2(ADDRESS addr)
{
    if (emu_id_enabled && addr >= 0xdfa0) {
        addr &= 0xff;
        if (addr == 0xff)
            emulator_id[addr - 0xa0] ^= 0xff;
        return emulator_id[addr - 0xa0];
    }
    if ((addr & 0xff00) == 0xdf00) {
        if (reu_enabled)
            return read_reu(addr & 0x0f);
        if (ieee488_enabled)
            return read_tpi(addr & 0x07);

        if (mem_cartridge_type == CARTRIDGE_ACTION_REPLAY) {
            if (export_ram)
                return export_ram0[0x1f00 + (addr & 0xff)];
            switch (roml_bank) {
              case 0:
                return roml_banks[addr & 0x1fff];
              case 1:
                return roml_banks[(addr & 0x1fff) + 0x2000];
              case 2:
                return roml_banks[(addr & 0x1fff) + 0x4000];
              case 3:
                return roml_banks[(addr & 0x1fff) + 0x6000];
            }
        }
        if (mem_cartridge_type == CARTRIDGE_KCS_POWER)
            return export_ram0[0x1f00 + (addr & 0xff)];
    }
    return rand();
}

void REGPARM2 store_io1(ADDRESS addr, BYTE value)
{
    if ((addr & 0xff00) == 0xde00) {
	if (mem_cartridge_type == CARTRIDGE_ACTION_REPLAY)
	    cartridge_config_changed(value);
	if (mem_cartridge_type == CARTRIDGE_KCS_POWER)
	    cartridge_config_changed(1);
	if (mem_cartridge_type == CARTRIDGE_SIMONS_BASIC)
	    cartridge_config_changed(1);
    }
#ifdef HAVE_RS232
    if (acia_de_enabled)
        store_acia1(addr & 0x03, value);
#endif
    return;
}

BYTE REGPARM1 read_io1(ADDRESS addr)
{
    if ((addr & 0xff00) == 0xde00) {
	if (mem_cartridge_type == CARTRIDGE_ACTION_REPLAY)
	    return rand();
	if (mem_cartridge_type == CARTRIDGE_KCS_POWER) {
	    cartridge_config_changed(0);
	    return roml_banks[0x1e00 + (addr & 0xff)];
	}
	if (mem_cartridge_type == CARTRIDGE_SIMONS_BASIC)
	    cartridge_config_changed(0);
    }
#ifdef HAVE_RS232
    if (acia_de_enabled)
        return read_acia1(addr & 0x03);
#endif
    return rand();
}

void REGPARM2 store_d6(ADDRESS addr, BYTE value)
{
#if 0                           /*def HAVE_RS232 */
    if (acia_d6_enabled)
        store_acia2(addr, value);
    else
#endif
        store_sid(addr, value);
    return;
}

BYTE REGPARM1 read_d6(ADDRESS addr)
{
#if 0                           /*def HAVE_RS232 */
    if (acia_d6_enabled)
        return read_acia2(addr);
#endif
    return read_sid(addr);
}

BYTE REGPARM1 read_rom(ADDRESS addr)
{
    switch (addr & 0xf000) {
      case 0xa000:
      case 0xb000:
        return read_basic(addr);
      case 0xd000:
        return read_chargen(addr);
      case 0xe000:
      case 0xf000:
        return read_kernal(addr);
    }

    return 0;
}

void REGPARM2 store_rom(ADDRESS addr, BYTE value)
{
    switch (addr & 0xf000) {
      case 0xa000:
      case 0xb000:
        basic_rom[addr & 0x1fff] = value;
        break;
      case 0xd000:
        chargen_rom[addr & 0x0fff] = value;
        break;
      case 0xe000:
      case 0xf000:
        kernal_rom[addr & 0x1fff] = value;
        break;
    }
}

BYTE REGPARM1 read_roml(ADDRESS addr)
{
    if (export_ram)
        return export_ram0[addr & 0x1fff];
    switch (roml_bank) {
      case 0:
        return roml_banks[addr & 0x1fff];
      case 1:
        return roml_banks[(addr & 0x1fff) + 0x2000];
      case 2:
        return roml_banks[(addr & 0x1fff) + 0x4000];
      case 3:
        return roml_banks[(addr & 0x1fff) + 0x6000];
    }
    return 0;
}

BYTE REGPARM1 read_romh(ADDRESS addr)
{
    switch (romh_bank) {
      case 0:
        return romh_banks[addr & 0x1fff];
      case 1:
        return romh_banks[(addr & 0x1fff) + 0x2000];
      case 2:
        return romh_banks[(addr & 0x1fff) + 0x4000];
      case 3:
        return romh_banks[(addr & 0x1fff) + 0x6000];
    }
    return 0;
}

void REGPARM2 store_roml(ADDRESS addr, BYTE value)
{
    if (export_ram)
        export_ram0[addr & 0x1fff] = value;
    return;
}

/* ------------------------------------------------------------------------- */

/* Generic memory access.  */

void REGPARM2 mem_store(ADDRESS addr, BYTE value)
{
    _mem_write_tab_ptr[addr >> 8](addr, value);
}

BYTE REGPARM1 mem_read(ADDRESS addr)
{
    return _mem_read_tab_ptr[addr >> 8](addr);
}

/* ------------------------------------------------------------------------- */

static void set_write_hook(int config, int page, store_func_t * f)
{
    int i;

    for (i = 0; i < NUM_VBANKS; i++) {
        mem_write_tab[i][config][page] = f;
    }
}

void initialize_memory(void)
{
    int i, j, k;
    /* IO is enabled at memory configs 5, 6, 7 and Ultimax.  */
    int io_config[32] = { 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
                          1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 };
    /* ROML is enabled at memory configs 11, 15, 27, 31 and Ultimax.  */
    int roml_config[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
                            1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1 };
    /* ROMH is enabled at memory configs 10, 11, 14, 15, 26, 27, 30, 31
       and Ultimax.  */
    int romh_config[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                            1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 };
    /* ROMH is mapped to $A000-$BFFF at memory configs 10, 11, 14, 15, 26,
       27, 30, 31.  If Ultimax is enabled it is mapped to $E000-$FFFF.  */
    int romh_mapping[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                             0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
                             0x00, 0x00, 0xa0, 0xa0, 0x00, 0x00, 0xa0, 0xa0 };

    /* Default is RAM.  */
    for (i = 0; i <= 0xff; i++) {
        mem_read_tab_watch[i] = read_watch;
        mem_write_tab_watch[i] = store_watch;
    }

    for (i = 0; i < NUM_CONFIGS; i++) {
        set_write_hook(i, 0, store_zero);
        mem_read_tab[i][0] = read_zero;
        mem_read_base_tab[i][0] = ram;
        for (j = 1; j <= 0xfe; j++) {
            mem_read_tab[i][j] = read_ram;
            mem_read_base_tab[i][j] = ram + (j << 8);
            for (k = 0; k < NUM_VBANKS; k++) {
                if ((j & 0xc0) == (k << 6)) {
                    switch (j & 0x3fff) {
                      case 0x39:
                        mem_write_tab[k][i][j] = store_vbank_39xx;
                        break;
                      case 0x3f:
                        mem_write_tab[k][i][j] = store_vbank_3fxx;
                        break;
                      default:
                        mem_write_tab[k][i][j] = store_vbank;
                    }
                } else {
                    mem_write_tab[k][i][j] = store_ram;
                }
            }
        }
        mem_read_tab[i][0xff] = read_ram;
        mem_read_base_tab[i][0xff] = ram + 0xff00;

        /* FIXME: we do not care about vbank writes here, but we probably
           should.  Anyway, the $FFxx addresses are not so likely to contain
           sprites or other stuff that really needs the special handling, and
           it's much easier this way.  */
        set_write_hook(i, 0xff, store_ram_hi);
    }

    /* Setup BASIC ROM at $A000-$BFFF (memory configs 3, 7, 11, 15).  */
    for (i = 0xa0; i <= 0xbf; i++) {
        mem_read_tab[3][i] = read_basic;
        mem_read_tab[7][i] = read_basic;
        mem_read_tab[11][i] = read_basic;
        mem_read_tab[15][i] = read_basic;
        mem_read_base_tab[3][i] = basic_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[7][i] = basic_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[11][i] = basic_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[15][i] = basic_rom + ((i & 0x1f) << 8);
    }

    /* Setup character generator ROM at $D000-$DFFF (memory configs 1, 2,
       3, 9, 10, 11, 25, 26, 27).  */
    for (i = 0xd0; i <= 0xdf; i++) {
        mem_read_tab[1][i] = read_chargen;
        mem_read_tab[2][i] = read_chargen;
        mem_read_tab[3][i] = read_chargen;
        mem_read_tab[9][i] = read_chargen;
        mem_read_tab[10][i] = read_chargen;
        mem_read_tab[11][i] = read_chargen;
        mem_read_tab[25][i] = read_chargen;
        mem_read_tab[26][i] = read_chargen;
        mem_read_tab[27][i] = read_chargen;
        mem_read_base_tab[1][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[2][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[3][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[9][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[10][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[11][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[25][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[26][i] = chargen_rom + ((i & 0x0f) << 8);
        mem_read_base_tab[27][i] = chargen_rom + ((i & 0x0f) << 8);
    }

    /* Setup I/O at $D000-$DFFF (memory configs 5, 6, 7).  */
    for (j = 0; j < NUM_CONFIGS; j++) {
        if (io_config[j]) {
            for (i = 0xd0; i <= 0xd3; i++) {
                mem_read_tab[j][i] = read_vic;
                set_write_hook(j, i, store_vic);
            }
            for (i = 0xd4; i <= 0xd5; i++) {
                mem_read_tab[j][i] = read_sid;
                set_write_hook(j, i, store_sid);
            }
            for (i = 0xd6; i <= 0xd7; i++) {
                mem_read_tab[j][i] = read_d6;
                set_write_hook(j, i, store_d6);
            }
            for (i = 0xd8; i <= 0xdb; i++) {
                mem_read_tab[j][i] = read_colorram;
                set_write_hook(j, i, store_colorram);
            }

            mem_read_tab[j][0xdc] = read_cia1;
            set_write_hook(j, 0xdc, store_cia1);
            mem_read_tab[j][0xdd] = read_cia2;
            set_write_hook(j, 0xdd, store_cia2);

            mem_read_tab[j][0xde] = read_io1;
            set_write_hook(j, 0xde, store_io1);
            mem_read_tab[j][0xdf] = read_io2;
            set_write_hook(j, 0xdf, store_io2);

            for (i = 0xd0; i <= 0xdf; i++)
                mem_read_base_tab[j][i] = NULL;
        }
    }

    /* Setup Kernal ROM at $E000-$FFFF (memory configs 2, 3, 6, 7, 10,
       11, 14, 15, 26, 27, 30, 31).  */
    for (i = 0xe0; i <= 0xff; i++) {
        mem_read_tab[2][i] = read_kernal;
        mem_read_tab[3][i] = read_kernal;
        mem_read_tab[6][i] = read_kernal;
        mem_read_tab[7][i] = read_kernal;
        mem_read_tab[10][i] = read_kernal;
        mem_read_tab[11][i] = read_kernal;
        mem_read_tab[14][i] = read_kernal;
        mem_read_tab[15][i] = read_kernal;
        mem_read_tab[26][i] = read_kernal;
        mem_read_tab[27][i] = read_kernal;
        mem_read_tab[30][i] = read_kernal;
        mem_read_tab[31][i] = read_kernal;
        mem_read_base_tab[2][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[3][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[6][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[7][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[10][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[11][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[14][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[15][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[26][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[27][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[30][i] = kernal_rom + ((i & 0x1f) << 8);
        mem_read_base_tab[31][i] = kernal_rom + ((i & 0x1f) << 8);
    }

    /* Setup ROML at $8000-$9FFF.  */
    for (j = 0; j < NUM_CONFIGS; j++) {
        if (roml_config[j]) {
            for (i = 0x80; i <= 0x9f; i++) {
                mem_read_tab[j][i] = read_roml;
                mem_read_base_tab[j][i] = NULL;
            }
        }
    }
    for (j = 16; j < 24; j++)
        for (i = 0x80; i <= 0x9f; i++)
            set_write_hook(j, i, store_roml);

    /* Setup ROMH at $A000-$BFFF and $E000-$FFFF.  */
    for (j = 0; j < NUM_CONFIGS; j++) {
        if (romh_config[j]) {
            for (i = romh_mapping[j]; i <= (romh_mapping[j] + 0x1f); i++) {
                mem_read_tab[j][i] = read_romh;
                mem_read_base_tab[j][i] = NULL;
            }
        }
    }

    for (i = 0; i < NUM_CONFIGS; i++) {
        mem_read_tab[i][0x100] = mem_read_tab[i][0];
        for (j = 0; j < NUM_VBANKS; j++) {
            mem_write_tab[j][i][0x100] = mem_write_tab[j][i][0];
        }
        mem_read_base_tab[i][0x100] = mem_read_base_tab[i][0];
    }

    _mem_read_tab_ptr = mem_read_tab[7];
    _mem_write_tab_ptr = mem_write_tab[vbank][7];
    _mem_read_base_tab_ptr = mem_read_base_tab[7];

    pport.data = 0xff;
    pport.dir = 0x0;
    export.exrom = 0;
    export.game = 0;

    /* Setup initial memory configuration.  */
    pla_config_changed();

    switch (mem_cartridge_type) {
      case CARTRIDGE_ACTION_REPLAY:
      case CARTRIDGE_KCS_POWER:
      case CARTRIDGE_GENERIC_8KB:
	cartridge_config_changed(0);
	break;
      case CARTRIDGE_SIMONS_BASIC:
      case CARTRIDGE_GENERIC_16KB:
	cartridge_config_changed(1);
	break;
    }
}

/* ------------------------------------------------------------------------- */

/* Initialize RAM for power-up.  */
void mem_powerup(void)
{
    int i;

#ifndef __MSDOS__
    printf("Initializing RAM for power-up...\n");
#endif

    for (i = 0; i < 0x10000; i += 0x80) {
        memset(ram + i, 0, 0x40);
        memset(ram + i + 0x40, 0xff, 0x40);
    }
}

/* Load ROMs at startup.  This is half-stolen from the old `load_mem()' in
   `memory.c'.  */
int mem_load(void)
{
    WORD sum;                   /* ROM checksum */
    int id;                     /* ROM identification number */
    int i;

    mem_powerup();

    /* Load Kernal ROM.  */
    if (mem_load_sys_file(kernal_rom_name,
                          kernal_rom, C64_KERNAL_ROM_SIZE,
                          C64_KERNAL_ROM_SIZE) < 0) {
        fprintf(stderr, "Couldn't load kernal ROM `%s'.\n",
                kernal_rom_name);
        return -1;
    }
    /* Check Kernal ROM.  */
    for (i = 0, sum = 0; i < C64_KERNAL_ROM_SIZE; i++)
        sum += kernal_rom[i];

    id = read_rom(0xff80);

    printf("Kernal rev #%d.\n", id);

    if ((id == 0
         && sum != C64_KERNAL_CHECKSUM_R00)
        || (id == 3
            && sum != C64_KERNAL_CHECKSUM_R03
            && sum != C64_KERNAL_CHECKSUM_R03swe)
        || (id == 0x43
            && sum != C64_KERNAL_CHECKSUM_R43)
        || (id == 0x64
            && sum != C64_KERNAL_CHECKSUM_R64)) {
        fprintf(stderr,
                "Warning: Unknown Kernal image `%s'.  Sum: %d ($%04X)\n",
                kernal_rom_name, sum, sum);
    } else if (kernal_revision != NULL) {
        if (patch_rom(kernal_revision) < 0)
            return -1;
    }
    /* Load Basic ROM.  */
    if (mem_load_sys_file(basic_rom_name,
                          basic_rom, C64_BASIC_ROM_SIZE,
                          C64_BASIC_ROM_SIZE) < 0) {
        fprintf(stderr, "Couldn't load basic ROM `%s'.\n",
                basic_rom_name);
        return -1;
    }
    /* Check Basic ROM.  */

    for (i = 0, sum = 0; i < C64_BASIC_ROM_SIZE; i++)
        sum += basic_rom[i];

    if (sum != C64_BASIC_CHECKSUM)
        fprintf(stderr,
                "Warning: Unknown Basic image `%s'.  Sum: %d ($%04X)\n",
                basic_rom_name, sum, sum);

    /* Load chargen ROM.  */

    if (mem_load_sys_file(chargen_rom_name,
                          chargen_rom, C64_CHARGEN_ROM_SIZE,
                          C64_CHARGEN_ROM_SIZE) < 0) {
        fprintf(stderr, "Couldn't load character ROM `%s'.\n",
                chargen_rom_name);
        return -1;
    }
    rom_loaded = 1;

    return 0;
}

void mem_attach_cartridge(int type, BYTE * rawcart)
{
    mem_cartridge_type = type;
    switch (type) {
      case CARTRIDGE_GENERIC_8KB:
	memcpy(roml_banks, rawcart, 0x2000);
	cartridge_config_changed(0);
	break;
      case CARTRIDGE_SIMONS_BASIC:
      case CARTRIDGE_GENERIC_16KB:
	memcpy(roml_banks, rawcart, 0x2000);
	memcpy(romh_banks, &rawcart[0x2000], 0x2000);
	cartridge_config_changed(1);
	break;
      case CARTRIDGE_ACTION_REPLAY:
	memcpy(roml_banks, rawcart, 0x8000);
	memcpy(romh_banks, rawcart, 0x8000);
	cartridge_config_changed(0);
	break;
      case CARTRIDGE_KCS_POWER:
	memcpy(roml_banks, rawcart, 0x2000);
	memcpy(romh_banks, &rawcart[0x2000], 0x2000);
	cartridge_config_changed(0);
	break;
      default:
	mem_cartridge_type = CARTRIDGE_NONE;
    }
    return;
}

void mem_detach_cartridge(int type)
{
    cartridge_config_changed(6);
    mem_cartridge_type = CARTRIDGE_NONE;
    return;
}

void mem_freeze_cartridge(int type)
{
    if (type == CARTRIDGE_ACTION_REPLAY)
	cartridge_config_changed(35);
    if (type == CARTRIDGE_KCS_POWER)
	cartridge_config_changed(3);
}

/* ------------------------------------------------------------------------- */

/* Change the current video bank.  */
void mem_set_vbank(int new_vbank)
{
    if (new_vbank != vbank) {
        vbank = new_vbank;
        _mem_write_tab_ptr = mem_write_tab[new_vbank][mem_config];
        vic_ii_set_vbank(new_vbank);
    }
}

/* Set the tape sense status.  */
void mem_set_tape_sense(int sense)
{
    tape_sense = sense;
    pla_config_changed();
}

/* Enable/disable the REU.  FIXME: should initialize the REU if necessary?  */
void mem_toggle_reu(int flag)
{
    reu_enabled = flag;
}

/* Enable/disable the IEEE488 interface.  */
void mem_toggle_ieee488(int flag)
{
    ieee488_enabled = flag;
}

/* Enable/disable the Emulator ID.  */
void mem_toggle_emu_id(int flag)
{
    emu_id_enabled = flag;
}

static void cartridge_config_changed(BYTE mode)
{
    export.game = mode & 1;
    export.exrom = ((mode >> 1) & 1) ^ 1;
    roml_bank = (mode >> 3) & 3;
    export_ram = (mode >> 5) & 1;
    pla_config_changed();
    if (mode & 0x40)
        cartridge_release_freeze();
}

/* ------------------------------------------------------------------------- */

/* FIXME: this part needs to be checked.  */

void mem_get_basic_text(ADDRESS * start, ADDRESS * end)
{
    if (start != NULL)
        *start = ram[0x2b] | (ram[0x2c] << 8);
    if (end != NULL)
        *end = ram[0x2d] | (ram[0x2e] << 8);
}

void mem_set_basic_text(ADDRESS start, ADDRESS end)
{
    ram[0x2b] = ram[0xac] = start & 0xff;
    ram[0x2c] = ram[0xad] = start >> 8;
    ram[0x2d] = ram[0x2f] = ram[0x31] = ram[0xae] = end & 0xff;
    ram[0x2e] = ram[0x30] = ram[0x32] = ram[0xaf] = end >> 8;
}

/* ------------------------------------------------------------------------- */

int mem_rom_trap_allowed(ADDRESS addr)
{
    return addr >= 0xe000 && (mem_config & 0x2);
}

/* ------------------------------------------------------------------------- */

/* Banked memory access functions for the monitor.  */

/* FIXME: peek, cartridge support */

static void store_bank_io(ADDRESS addr, BYTE byte)
{
    switch (addr &= 0xff00) {
      case 0xd000:
      case 0xd100:
      case 0xd200:
      case 0xd300:
        store_vic(addr, byte);
        break;
      case 0xd400:
      case 0xd500:
        store_sid(addr, byte);
        break;
      case 0xd600:
      case 0xd700:
        store_d6(addr, byte);
        break;
      case 0xd800:
      case 0xd900:
      case 0xda00:
      case 0xdb00:
        store_colorram(addr, byte);
        break;
      case 0xdc00:
        store_cia1(addr, byte);
        break;
      case 0xdd00:
        store_cia2(addr, byte);
        break;
      case 0xde00:
        store_io1(addr, byte);
        break;
      case 0xdf00:
        store_io2(addr, byte);
        break;
    }
    return;
}

static BYTE read_bank_io(ADDRESS addr)
{
    switch (addr &= 0xff00) {
      case 0xd000:
      case 0xd100:
      case 0xd200:
      case 0xd300:
        return read_vic(addr);
      case 0xd400:
      case 0xd500:
        return read_sid(addr);
      case 0xd600:
      case 0xd700:
        return read_d6(addr);
      case 0xd800:
      case 0xd900:
      case 0xda00:
      case 0xdb00:
        return read_colorram(addr);
      case 0xdc00:
        return read_cia1(addr);
      case 0xdd00:
        return read_cia2(addr);
      case 0xde00:
        return read_io1(addr);
      case 0xdf00:
        return read_io2(addr);
    }
    return 0xff;
}

static BYTE peek_bank_io(ADDRESS addr)
{
    switch (addr &= 0xff00) {
      case 0xd000:
      case 0xd100:
      case 0xd200:
      case 0xd300:
        return read_vic(addr);  /* FIXME */
      case 0xd400:
      case 0xd500:
        return read_sid(addr);
      case 0xd600:
      case 0xd700:
        return read_d6(addr);   /* FIXME */
      case 0xd800:
      case 0xd900:
      case 0xda00:
      case 0xdb00:
        return read_colorram(addr);
      case 0xdc00:
        return peek_cia1(addr);
      case 0xdd00:
        return peek_cia2(addr);
      case 0xde00:
        return read_io1(addr);  /* FIXME */
      case 0xdf00:
        return read_io2(addr);  /* FIXME */
    }
    return 0xff;
}

/* ------------------------------------------------------------------------- */

/* Exported banked memory access functions for the monitor.  */

static const char *banknames[] =
{
    "default", "cpu", "ram", "rom", "io", "cart", NULL
};

static int banknums[] =
{
    1, 0, 1, 2, 3, 4
};

const char **mem_bank_list(void)
{
    return banknames;
}

int mem_bank_from_name(const char *name)
{
    int i = 0;

    while (banknames[i]) {
        if (!strcmp(name, banknames[i])) {
            return banknums[i];
        }
        i++;
    }
    return -1;
}

BYTE mem_bank_read(int bank, ADDRESS addr)
{
    switch (bank) {
      case 0:                   /* current */
        return mem_read(addr);
        break;
      case 3:                   /* io */
        if (addr >= 0xd000 && addr < 0xe000) {
            return read_bank_io(addr);
        }
      case 4:                   /* cart */
        if (addr >= 0x8000 && addr <= 0x9FFF) {
            return roml_banks[addr & 0x1fff];
        }
        if (addr >= 0xA000 && addr <= 0xBFFF) {
            return romh_banks[addr & 0x1fff];
        }
      case 2:                   /* rom */
        if (addr >= 0xA000 && addr <= 0xBFFF) {
            return basic_rom[addr & 0x1fff];
        }
        if (addr >= 0xD000 && addr <= 0xDfff) {
            return chargen_rom[addr & 0x1fff];
        }
        if (addr >= 0xE000 && addr <= 0xffff) {
            return kernal_rom[addr & 0x1fff];
        }
      case 1:                   /* ram */
    }
    return ram[addr];
}

BYTE mem_bank_peek(int bank, ADDRESS addr)
{
    switch (bank) {
      case 0:                   /* current */
        return mem_read(addr);  /* FIXME */
        break;
      case 3:                   /* io */
        if (addr >= 0xd000 && addr < 0xe000) {
            return peek_bank_io(addr);
        }
    }
    return mem_bank_read(bank, addr);
}

void mem_bank_write(int bank, ADDRESS addr, BYTE byte)
{
    switch (bank) {
      case 0:                   /* current */
        mem_store(addr, byte);
        return;
      case 3:                   /* io */
        if (addr >= 0xd000 && addr < 0xe000) {
            store_bank_io(addr, byte);
            return;
        }
      case 2:                   /* rom */
        if (addr >= 0xA000 && addr <= 0xBFFF) {
            return;
        }
        if (addr >= 0xD000 && addr <= 0xDfff) {
            return;
        }
        if (addr >= 0xE000 && addr <= 0xffff) {
            return;
        }
      case 1:                   /* ram */
    }
    ram[addr] = byte;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.