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

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

/*
 * petmem.c - PET memory handling.
 *
 * Written by
 *  Ettore Perazzoli (ettore@comm2000.it)
 *  André Fachat (fachat@physik.tu-chemnitz.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 PET
#define PET                     /* for mkdep */
#endif

#include "vice.h"

#include <stdio.h>

#include "types.h"
#include "memutils.h"
#include "pia.h"
#include "pet.h"
#include "petvia.h"
#include "crtc.h"
#include "kbd.h"
#include "kbdbuf.h"
#include "autostart.h"
#include "resources.h"
#include "cmdline.h"
#include "tapeunit.h"
#include "pets.h"
#include "interrupt.h"
#include "vmachine.h"
#include "maincpu.h"
#include "petmem.h"
#include "emuid.h"
#include "utils.h"

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

/* The PET memory. */

#define RAM_ARRAY 0x20000       /* this includes 8x96 expansion RAM */

BYTE ram[RAM_ARRAY];            /* 128K to make things easier. Real size is 4-128K. */
BYTE rom[PET_ROM_SIZE];
BYTE chargen_rom[PET_CHARGEN_ROM_SIZE];

int ram_size = RAM_ARRAY;       /* FIXME? */

/* Memory read and write tables. */
read_func_ptr_t _mem_read_tab[0x101];
store_func_ptr_t _mem_write_tab[0x101];
read_func_ptr_t _mem_read_tab_watch[0x101];
store_func_ptr_t _mem_write_tab_watch[0x101];
BYTE *_mem_read_base_tab[0x101];

read_func_ptr_t *_mem_read_tab_ptr;
store_func_ptr_t *_mem_write_tab_ptr;
BYTE **_mem_read_base_tab_ptr;

/* Flag: nonzero if the ROM has been loaded. */
int rom_loaded = 0;

/* CRTC register pointer. */
static BYTE crtc_ptr = 0;

/* 8x96 mapping register */
static BYTE map_reg = 0;
static int bank8offset = 0;
static int bankCoffset = 0;

#define IS_NULL(s)  (s == NULL || *s == '\0')

/* prototype */
void set_screen(void);

static trap_t pet4_tape_traps[];
static trap_t pet3_tape_traps[];
static trap_t pet2_tape_traps[];

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

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

static int set_emu_id_enabled(resource_value_t v)
{
    emu_id_enabled = (int)v;
    return 0;
}

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

static resource_t resources[] = {
    { "EmuID", RES_INTEGER, (resource_value_t) 0,
      (resource_value_t *) &emu_id_enabled, set_emu_id_enabled },
    { NULL }
};

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

static cmdline_option_t cmdline_options[] = {
    { "-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" },
    { NULL }
};

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

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

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

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

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

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

static BYTE REGPARM1 read_ext8(ADDRESS addr)
{
    return ram[addr + bank8offset];
}

static void REGPARM2 store_ext8(ADDRESS addr, BYTE value)
{
    ram[addr + bank8offset] = value;
}

static BYTE REGPARM1 read_extC(ADDRESS addr)
{
    return ram[addr + bankCoffset];
}

static void REGPARM2 store_extC(ADDRESS addr, BYTE value)
{
    ram[addr + bankCoffset] = value;
}

static BYTE REGPARM1 read_vmirror(ADDRESS addr)
{
    return ram[0x8000 + (addr & (pet.videoSize - 1))];
}

static void REGPARM2 store_vmirror(ADDRESS addr, BYTE value)
{
    ram[0x8000 + (addr & (pet.videoSize - 1))] = value;
}

BYTE REGPARM1 read_rom(ADDRESS addr)
{
    return rom[addr & 0x7fff];
}

void REGPARM2 store_rom(ADDRESS addr, BYTE value)
{
    rom[addr & 0x7fff] = value;
}

static BYTE REGPARM1 read_unused(ADDRESS addr)
{
    return (addr >> 8) & 0xff;
}

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

/* Functions for watchpoint memory access.  */

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

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

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

/* 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);
}

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

/* The PET have all I/O chips connected to the same select lines.  Only one
   address lines is used as separate (high-active) select input.  I.e. PIA1
   always reacts when address & 0x10 is high, for example $e810, $e830,
   $e850, etc.  PIA2 reacts when address & 0x20 is high, for example $e820,
   $e830 $e860,...  The next two functions try to reflect this behaviour.  */

/* When we write, we write all involved chips.  */

void REGPARM2 store_io(ADDRESS addr, BYTE value)
{
    if (addr & 0x10)
        store_pia1(addr, value);

    if (addr & 0x20)
        store_pia2(addr, value);

    if (addr & 0x40)
        store_via(addr, value);

    if ((addr & 0x80) && pet.crtc) {
        if (addr & 1)
            store_crtc(crtc_ptr, value);
        else
            crtc_ptr = value;
    }
}


/*
 * When we read, we only read sensible values. In real operation,
 * the bus drivers of all involved chips interact and you get strange
 * results...
 */
BYTE REGPARM1 read_io(ADDRESS addr)
{
    BYTE v1, v2, v3, v4;

    if (emu_id_enabled && addr >= 0xE8A0) {
        addr &= 0xff;
        if (addr == 0xff)
            emulator_id[addr - 0xa0] ^= 0xff;
        return emulator_id[addr - 0xa0];
    }

    switch (addr & 0xf0) {
      case 0x10:                /* PIA1 */
        return read_pia1(addr);
      case 0x20:                /* PIA2 */
        return read_pia2(addr);
      case 0x40:
        return read_via(addr);  /* VIA */
      case 0x80:                /* CRTC */
        if (pet.crtc) {
            if (addr & 1)
                return read_crtc(crtc_ptr);
            else
                return 0x9f;    /* Status. */
        }
      case 0x00:
        return addr >> 8;
      default:                  /* 0x30, 0x50, 0x60, 0x70, 0x90-0xf0 */
        if (addr & 0x10)
            v1 = read_pia1(addr);
        else
            v1 = 0xff;
        if (addr & 0x20)
            v2 = read_pia2(addr);
        else
            v2 = 0xff;
        if (addr & 0x40)
            v3 = read_via(addr);
        else
            v3 = 0xff;
        v4 = 0xff;
        if ((addr & 0x80) && pet.crtc) {
            if (addr & 1)
                v4 = read_crtc(crtc_ptr);
            else
                v4 = 0x9f;      /* Status. */
        }
        return v1 & v2 & v3 & v4;
    }
}

static void REGPARM2 store_dummy(ADDRESS addr, BYTE value)
{
    return;
}

/*
 * This sets the standard PET memory configuration from $9000-$10000.
 * It is used in store_8x96() and initialize_memory().
 */
static void set_std_9tof(void)
{
    int i, l;
    static void (*store)(ADDRESS, BYTE);
    int ram9, rama;

    store = (pet.map == 2) ? store_ram : store_dummy;
    ram9 = (pet.map == 2 && pet.mem9) ? 1 : 0;
    rama = (pet.map == 2 && pet.memA) ? 1 : 0;

    /* Setup RAM/ROM at $9000 - $9FFF. */
    for (i = 0x90; i < 0xa0; i++) {
        _mem_read_tab[i] = ram9 ? read_ram : read_rom;
        _mem_write_tab[i] = store;
        _mem_read_base_tab[i] = ram9 ? ram + (i << 8) : rom + ((i & 0x7f) << 8);
    }

    /* Setup RAM/ROM at $9000 - $9FFF. */
    for (i = 0xa0; i < 0xb0; i++) {
        _mem_read_tab[i] = rama ? read_ram : read_rom;
        _mem_write_tab[i] = store;
        _mem_read_base_tab[i] = rama ? ram + (i << 8) : rom + ((i & 0x7f) << 8);
    }

    /* Setup ROM at $B000 - $E7FF. */
    for (i = 0xb0; i <= 0xe7; i++) {
        _mem_read_tab[i] = read_rom;
        _mem_write_tab[i] = store;
        _mem_read_base_tab[i] = rom + ((i & 0x7f) << 8);
    }

    l = ((0xe800 + pet.IOSize) >> 8) & 0xff;

    /* Setup I/O at $e800 - $e800 + pet.IOSize. */
    /* i.e. IO at $e800... */
    _mem_read_tab[0xe8] = read_io;
    _mem_write_tab[0xe8] = store_io;
    _mem_read_base_tab[0xe8] = NULL;

    /* ... and unused address space behind it */
    for (i = 0xe9; i < l; i++) {
        _mem_read_tab[i] = read_unused;
        _mem_write_tab[i] = store;
        _mem_read_base_tab[i] = NULL;;
    }

    /* Setup ROM at $e800 + pet.IOSize - $ffff */
    for (i = l; i <= 0xff; i++) {
        _mem_read_tab[i] = read_rom;
        _mem_write_tab[i] = store;
        _mem_read_base_tab[i] = rom + ((i & 0x7f) << 8);
    }

    _mem_read_base_tab_ptr = _mem_read_base_tab;
}

/* FIXME: TODO! */
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_write_tab_ptr = _mem_write_tab;
    }
}

/*
 * From the PETio.doc (ftp.funet.fi/firmware/pet/)
 *
 * $fff0 register in PET 8x96
 * 8096 exp-mem (64K):
 * The control register is at $FFF0/65520
 * You have 4 16K-banks, 0...3
 *
 * $8000     $9000               $C000           $E800    $F000     $FFFF
 * !----------------------------!!--------------------------------------!
 *         Bank 0 or 2                     Bank 1 or 3
 * !--------!                                    !-------!
 *   screen                                        io
 *
 * Control Register $FFF0:
 * bit 7:  0 normal 8032 configuration (screen, ROMs, IO, ROMs)
 *        80 expansion memory
 * bit 6:  0 RAM $E800-$EFFF (only when bit7=1)
 *        40 IO peek through
 * bit 5:  0 exp-mem $8000-$8FFF (-"-)
 *        20 screen peek through
 * bit 4: 10 not used
 * bit 3:  0 bank 1 $C000-$FFFF
 *        08 bank 3
 * bit 2:  0 bank 0 $8000-$BFFF
 *        04 bank 2
 * bit 1: 02 write protect bank 1/3
 * bit 0: 01 write protect bank 0/2
 * when bit7=0, all other bits are ignored
 *
 * The missing 32K can't be accessed witout hardware modifications.
 * You can only use the 2K "behind" the screen $8800-$8FFF (exact: 34768-
 * 36863), available in the normal configuration.
 * The register is write-only, and the value is written through to the
 * previously selected ram bank.
 *
 */

/* Save old store function for last byte.  */
static void REGPARM2 (*store_ff)(ADDRESS addr, BYTE value) = NULL;

/* Write to last page of memory in 8x96.  */
static void REGPARM2 store_8x96(ADDRESS addr, BYTE value)
{
    BYTE changed;
    int l, protected;

    if (store_ff)
        store_ff(addr, value);

    changed = map_reg ^ value;

    if (addr == 0xfff0 && changed && ((map_reg | changed) & 0x80)) {
#if 0
        printf("Change $fff0 to %02x\n", value);
#endif
        if (value & 0x80) {     /* ext. RAM enabled */
            if (changed & 0xa5) {       /* $8000-$bfff */
                protected = value & 0x01;
                l = 0x80;
                if (value & 0x20) {     /* screen memory mapped through */
                    for (; l < 0x90; l++) {
                        _mem_read_tab[l] = read_ram;
                        _mem_write_tab[l] = store_ram;
                        _mem_read_base_tab[l] = ram + (l << 8);
                    }
                }
                bank8offset = 0x8000 + ((value & 0x04) ? 0x8000 : 0);
                for (; l < 0xc0; l++) {
                    _mem_read_tab[l] = read_ext8;
                    if (protected)
                        _mem_write_tab[l] = store_dummy;
                    else
                        _mem_write_tab[l] = store_ext8;
                    _mem_read_base_tab[l] = ram + bank8offset + (l << 8);
                }
            }
            if (changed & 0xca) {       /* $c000-$ffff */
                protected = value & 0x02;
                bankCoffset = 0x8000 + ((value & 0x08) ? 0x8000 : 0);
                for (l = 0xc0; l < 0x100; l++) {
                    if ((l == 0xe8) && (value & 0x40)) {
                        _mem_read_tab[l] = read_io;
                        _mem_write_tab[l] = store_io;
                        _mem_read_base_tab[l] = NULL;
                    } else {
                        _mem_read_tab[l] = read_extC;
                        if (protected)
                            _mem_write_tab[l] = store_dummy;
                        else
                            _mem_write_tab[l] = store_extC;
                        _mem_read_base_tab[l] = ram + bankCoffset + (l << 8);
                    }
                }
                store_ff = _mem_write_tab[0xff];
                _mem_write_tab[0xff] = store_8x96;
            }
        } else {                /* disable ext. RAM */
            for (l = 0x80; l < 0x90; l++) {
                _mem_read_tab[l] = read_ram;
                _mem_write_tab[l] = store_ram;
                _mem_read_base_tab[l] = ram + (l << 8);
            }
            set_std_9tof();
            store_ff = _mem_write_tab[0xff];
            _mem_write_tab[0xff] = store_8x96;
        }
        map_reg = value;

    }
    return;
}

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

/* This does the plain 8032 configuration, as 8096 stuff only comes up when
   writing to $fff0.  */
void initialize_memory(void)
{
    int i, l;

    l = pet.ramSize << 2;       /* ramSize in kB, l in 256 Byte */
    if (l > 128)
        l = 128;                /* fix 8096 / 8296 */

    /*printf("PET: initialize memory, ramSize=%04x -> l=%d\n", pet.ramSize * 1024,l); */

    /* Setup RAM from $0000 to pet.ramSize */
    for (i = 0x00; i < l; i++) {
        _mem_read_tab[i] = read_ram;
        _mem_write_tab[i] = store_ram;
        _mem_read_base_tab[i] = ram + (i << 8);
    }

    /* Setup unused from pet.ramSize to $7fff */
    for (i = l; i < 0x80; i++) {
        _mem_read_tab[i] = read_unused;
        _mem_write_tab[i] = store_dummy;
        _mem_read_base_tab[i] = NULL;
    }

    l = ((0x8000 + pet.videoSize) >> 8) & 0xff;

    /* Setup RAM from $8000 to $8000 + pet.videoSize */
    for (i = 0x80; i < l; i++) {
        _mem_read_tab[i] = read_ram;
        _mem_write_tab[i] = store_ram;
        _mem_read_base_tab[i] = ram + (i << 8);
    }

    /* Setup unused from $8000 + pet.videoSize to $87ff */
    /* falls through if videoSize >= 0x800 */
    for (; i < 0x88; i++) {
        _mem_read_tab[i] = read_vmirror;
        _mem_write_tab[i] = store_vmirror;
        _mem_read_base_tab[i] = ram + 0x8000 + ((i << 8) & (pet.videoSize - 1));
    }

    /* Setup unused from $8800 to $8fff */
    /* falls through if videoSize >= 0x1000 */
    for (; i < 0x90; i++) {
        _mem_read_tab[i] = read_unused;
        _mem_write_tab[i] = store_dummy;
        _mem_read_base_tab[i] = NULL;
    }

    set_std_9tof();

    if (pet.map) {              /* catch writes to $fff0 register */
        store_ff = _mem_write_tab[0xff];
        _mem_write_tab[0xff] = store_8x96;
    }
    if (pet.pet2k) {            /* map in IEEE488 bug fixes */
        _mem_read_tab[0xef] = read_rom;
        _mem_read_base_tab[0xef] = rom + ((0xef & 0x7f) << 8);
    }
    _mem_read_tab[0x100] = _mem_read_tab[0];
    _mem_write_tab[0x100] = _mem_write_tab[0];
    _mem_read_base_tab[0x100] = _mem_read_base_tab[0];

    map_reg = 0;

    ram_size = pet.ramSize * 1024;
    _mem_read_tab_ptr = _mem_read_tab;
    _mem_write_tab_ptr = _mem_write_tab;

    for (i = 0; i < 0x101; i++) {
        _mem_read_tab_watch[i] = read_watch;
        _mem_write_tab_watch[i] = store_watch;
    }
}

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

void patch_2001(void)
{
    int i;
    int rp;
    char dat0[] = {0xa9, 0x60, 0x85, 0xf0, 0x60};
    char dat1[] = {0x20, 0xb6, 0xf0, 0xa5, 0xf0, 0x20, 0x5b, 0xf1,
                   0x20, 0x87, 0xf1, 0x85, 0xf7,
                   0x20, 0x87, 0xf1, 0x85, 0xf8, 0x60};
    char dat2[] = {0x20, 0x7a, 0xf1, 0x20, 0xe6, 0xf6,
                   0xad, 0x0b, 0x02, 0x60};
    char dat3[] = {0xa9, 0x61, 0x85, 0xf0, 0x60};
    char dat4[] = {0x20, 0xba, 0xf0, 0xa5, 0xf0, 0x20, 0x2c, 0xf1,
                   0xa5, 0xf7, 0x20, 0x67, 0xf1,
                   0xa5, 0xf8, 0x4c, 0x67, 0xf1};
    char dat5[] = {0xae, 0x0c, 0x02, 0x70, 0x46, 0x20, 0x87, 0xf1};
    char dat6[] = {0x20, 0x2c, 0xf1, 0x4c, 0x7e, 0xf1};

    printf("PET: patching 2001 ROM to make IEEE488 work!\n");

    /* Patch PET2001 IEEE488 routines to make them work */
    rom[0x7471] = rom[0x7472] = 0xea;   /* NOP */
    rom[0x7180] = rom[0x7181] = 0xea;   /* NOP */
    rom[0x73ef] = 0xf8;
    rom[0x73f3] = 0xf7;
    rp = 0x6f00;                /* $ef00 */
    rom[0x7370] = rp & 0xff;
    rom[0x7371] = ((rp >> 8) & 0xff) | 0x80;
    for (i = 0; i < 5; i++)
        rom[rp++] = dat0[i];
    rom[0x7379] = rp & 0xff;
    rom[0x737a] = ((rp >> 8) & 0xff) | 0x80;
    for (i = 0; i < 19; i++)
        rom[rp++] = dat1[i];
    rom[0x73cc] = 0x20;
    rom[0x73cd] = rp & 0xff;
    rom[0x73ce] = ((rp >> 8) & 0xff) | 0x80;
    for (i = 0; i < 10; i++)
        rom[rp++] = dat2[i];
    for (i = 0; i < 8; i++)
        rom[0x7381 + i] = dat5[i];

    rom[0x76c1] = rp & 0xff;
    rom[0x76c2] = ((rp >> 8) & 0xff) | 0x80;
    for (i = 0; i < 5; i++)
        rom[rp++] = dat3[i];
    rom[0x76c7] = rp & 0xff;
    rom[0x76c8] = ((rp >> 8) & 0xff) | 0x80;
    for (i = 0; i < 18; i++)
        rom[rp++] = dat4[i];
    rom[0x76f4] = rp & 0xff;
    rom[0x76f5] = ((rp >> 8) & 0xff) | 0x80;
    for (i = 0; i < 6; i++)
        rom[rp++] = dat6[i];

    strcpy(rom + rp, "vice pet2001 rom patch $ef00-$efff");
}

void mem_powerup(void)
{
    int i;

#ifndef __MSDOS__
    printf("Initializing RAM for power-up...\n");
#endif
    for (i = 0; i < RAM_ARRAY; i += 0x80) {
        memset(ram + i, 0, 0x40);
        memset(ram + i + 0x40, 0xff, 0x40);
    }
}

/* Load memory image files.  This also selects the PET model.  */
int mem_load(void)
{
    WORD sum;                   /* ROM checksum */
    int i;
    int rsize, krsize;

    /* De-initialize kbd-buf, autostart and tape stuff here before
       reloading the ROM the traps are installed in.  */
    kbd_buf_init(0, 0, 0, 0);
    autostart_init(0, 0, 0, 0, 0, 0);
    tape_init(0, 0, 0, 0, 0, 0, 0, NULL, 0, 0);

    /* Load chargen ROM - we load 2k, and generate the inverted 2k.  */

    if (mem_load_sys_file(pet.chargenName, chargen_rom, 2048, 2048) < 0) {
        fprintf(stderr, "Couldn't load character ROM.\n");
        return -1;
    }

    /* Copy graphics charom to second part.  */
    memmove(chargen_rom + 2048, chargen_rom + 1024, 1024);

    if (pet.pet2k) {
        int j;

        /* If pet2001 then exchange upper and lower case letters.  */
        for (i = 8; i < (0x1b * 8); i++) {
            j = chargen_rom[0x800 + i];
            chargen_rom[i + 0x800] = chargen_rom[i + 0xa00];
            chargen_rom[i + 0xa00] = j;
        }
    }

    /* Inverted chargen into second half. This is a PET hardware feature.  */
    for (i = 0; i < 1024; i++) {
        chargen_rom[i + 1024] = chargen_rom[i] ^ 0xff;
        chargen_rom[i + 3072] = chargen_rom[i + 2048] ^ 0xff;
    }

    /* Init ROM with 'unused address' values.  */
    for (i = 0; i < PET_ROM_SIZE; i++) {
        rom[i] = 0x80 + ((i >> 8) & 0xff);
    }

    /* Load Kernal ROM.  */
    {
        const char *name =      /* (IS_NULL(kernal_rom_name)
           ? */ pet.kernalName /*: kernal_rom_name) */ ;

        if ((krsize = mem_load_sys_file(name,
                                        rom, 0x2000, PET_ROM_SIZE)) < 0) {
            fprintf(stderr, "Couldn't load ROM `%s'.\n\n", name);
            return -1;
        }
    }

    /* Load extension ROMs.  */
#if 0
    if (!IS_NULL(basic_rom_name)
        && ((rsize = mem_load_sys_file(basic_rom_name,
                                   rom + 0x3000, 0x2000, 0x3000)) < 0)) {
        fprintf(stderr, "Couldn't load ROM `%s'.\n\n",
                basic_rom_name);
        return -1;
    }
#endif

    {
        const char *name = pet.editorName;

        if (!IS_NULL(name)
            && ((rsize = mem_load_sys_file(name, rom + 0x6000,
                                           0x0800, 0x0800)) < 0)) {
            fprintf(stderr, "Couldn't load ROM `%s'.\n\n",
                    name);
            return -1;
        }
    }

    if (!IS_NULL(pet.mem9name)
        && ((rsize = mem_load_sys_file(pet.mem9name,
                                   rom + 0x1000, 0x1000, 0x1000)) < 0)) {
        fprintf(stderr, "Couldn't load ROM `%s'.\n\n",
                pet.mem9name);
        return -1;
    }
    if (!IS_NULL(pet.memAname)
        && ((rsize = mem_load_sys_file(pet.memAname,
                                   rom + 0x2000, 0x1000, 0x1000)) < 0)) {
        fprintf(stderr, "Couldn't load ROM `%s'.\n\n",
                pet.memAname);
        return -1;
    }
    if (!IS_NULL(pet.memBname)) {
        if (krsize <= 0x4000) {
            if ((rsize = mem_load_sys_file(pet.memBname, rom + 0x3000,
                                           0x1000, 0x1000)) < 0) {
                fprintf(stderr, "Couldn't load ROM `%s'.\n\n",
                        pet.memBname);
                return -1;
            }
        } else {
            printf("PET: internal ROM too large for extension ROM at $b000 - "
                   "ignoring `%s'\n", pet.memBname);
        }
    }

    /* Checksum over top 4 kByte PET kernal.  */
    for (i = 0x7000, sum = 0; i < 0x8000; i++)
        sum += rom[i];

    /* 4032 and 8032 have the same kernals, so we have to test more, here
       $E000 - $E800.  */
    for (i = 0x6000; i < 0x6800; i++)
        sum += rom[i];

    printf("PET: Loaded ROM, checksum is %d ($%04X).\n", sum, sum);

    if (pet.pet2k) {
        if (sum != PET2001_CHECKSUM) {
            printf("PET2001 model chosen, but ROM is unknown.  Cannot patch IEEE488!\n");
        } else {
            patch_2001();
        }
    }
    pet.screen_width = 0;
    /* The length of the keyboard buffer might actually differ from 10 - in
       the 4032 and 8032 50Hz editor ROMs it is checked against different
       memory locations (0xe3 and 0x3eb) but by default (power-up) it's 10
       anyway.  AF 30jun1998 */
    if (sum == PET8032_CHECKSUM_A || sum == PET8032_CHECKSUM_B) {
        printf("Identified PET 8032 ROM by checksum.\n");
        pet.screen_width = 80;
        kbd_buf_init(0x26f, 0x9e, 10,
                     PET_PAL_CYCLES_PER_RFSH * PET_PAL_RFSH_PER_SEC);
        autostart_init(3 * PET_PAL_RFSH_PER_SEC * PET_PAL_CYCLES_PER_RFSH, 0,
                       0xa7, 0xc4, 0xc6, -80);
        tape_init(214, 150, 157, 144, 0xe455, 251, 201, pet4_tape_traps,
                  0x26f, 0x9e);
    } else if (sum == PET3032_CHECKSUM_A || sum == PET3032_CHECKSUM_B) {
        printf("Identified PET 3032 ROM by checksum.\n");
        pet.screen_width = 40;
        kbd_buf_init(0x26f, 0x9e, 10,
                     PET_PAL_CYCLES_PER_RFSH * PET_PAL_RFSH_PER_SEC);
        autostart_init(3 * PET_PAL_RFSH_PER_SEC * PET_PAL_CYCLES_PER_RFSH, 0,
                       0xa7, 0xc4, 0xc6, -40);
        tape_init(214, 150, 157, 144, 0xe62e, 251, 201, pet3_tape_traps,
                  0x26f, 0x9e);
    } else if (sum == PET4032_CHECKSUM_A || sum == PET4032_CHECKSUM_B) {
        printf("Identified PET 4032 ROM by checksum.\n");
        pet.screen_width = 40;
        kbd_buf_init(0x26f, 0x9e, 10,
                     PET_PAL_CYCLES_PER_RFSH * PET_PAL_RFSH_PER_SEC);
        autostart_init(3 * PET_PAL_RFSH_PER_SEC * PET_PAL_CYCLES_PER_RFSH, 0,
                       0xa7, 0xc4, 0xc6, -40);
        tape_init(214, 150, 157, 144, 0xe455, 251, 201, pet4_tape_traps,
                  0x26f, 0x9e);
    } else if (sum == PET2001_CHECKSUM) {
        printf("Identified PET 2001 ROM by checksum.\n");
        pet.screen_width = 40;
        kbd_buf_init(0x20f, 0x20d, 10,
                     PET_PAL_CYCLES_PER_RFSH * PET_PAL_RFSH_PER_SEC);
        autostart_init(3 * PET_PAL_RFSH_PER_SEC * PET_PAL_CYCLES_PER_RFSH, 0,
                       0x224, 0xe0, 0xe2, -40);
        tape_init(243, 0x20c, 0x20b, 0x219, 0xe685, 247, 229, pet2_tape_traps,
                  0x20f, 0x20d);
    } else {
        printf("Unknown PET ROM.\n");
    }

    if (pet.screen_width) {
        set_screen();
    }
    rom_loaded = 1;

    return 0;
}

void set_screen(void)
{
    int cols, vmask;

    cols = pet.video;
    vmask = pet.vmask;

    if (!cols) {
        cols = pet.screen_width;
        vmask = (cols == 40) ? 0x3ff : 0x7ff;
    }
    if (!cols) {
        cols = PET_COLS;
        vmask = (cols == 40) ? 0x3ff : 0x7ff;
    }

    printf("Setting screen width to %d columns (vmask=%04x).\n", cols, vmask);
    crtc_set_screen_mode(ram + 0x8000, vmask, cols);
}

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

/* FIXME: this does not work for PET 2001.  */

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

void mem_set_basic_text(ADDRESS start, ADDRESS end)
{
    ram[0x28] = ram[0xc7] = start & 0xff;
    ram[0x29] = ram[0xc8] = start >> 8;
    ram[0x2a] = ram[0x2c] = ram[0x2e] = ram[0xc9] = end & 0xff;
    ram[0x2b] = ram[0x2d] = ram[0x2f] = ram[0xca] = end >> 8;
}

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

int mem_rom_trap_allowed(ADDRESS addr)
{
    return (addr >= 0xf000) && !(map_reg & 0x80);
}

/* Tape traps.  */
static trap_t pet4_tape_traps[] =
{
    {
        "FindHeader",
        0xF5E8,
        0xF5EB,
        {0x20, 0x9A, 0xF8},
        findheader
    },
    {
        "WriteHeader",
        0xF66B,
        0xF66E,
        {0x20, 0xD5, 0xF8},
        writeheader
    },
    {
        "TapeReceive",
        0xF8E0,
        0xFCC0,
        {0x20, 0xE0, 0xFC},
        tapereceive
    },
    {
        NULL,
        0,
        0,
        {0, 0, 0},
        NULL
    }
};

static trap_t pet3_tape_traps[] =
{
    {
        "FindHeader",
        0xF5A9,
        0xF5AC,
        {0x20, 0x55, 0xF8},
        findheader
    },
    {
        "WriteHeader",
        0xF62C,
        0xF62F,
        {0x20, 0x90, 0xF8},
        writeheader
    },
    {
        "TapeReceive",
        0xF89B,
        0xFC7B,
        {0x20, 0x9B, 0xFC},
        tapereceive
    },
    {
        NULL,
        0,
        0,
        {0, 0, 0},
        NULL
    }
};

static trap_t pet2_tape_traps[] =
{
    {
        "FindHeader",
        0xF5B2,
        0xF5B5,
        {0x20, 0x7F, 0xF8},
        findheader
    },
    {
        "WriteHeader",
        0xF63D,
        0xF640,
        {0x20, 0xC4, 0xF8},
        writeheader
    },
    {
        "TapeReceive",
        0xF8A5,
        0xFCFB,
        {0x20, 0x1B, 0xFD},
        tapereceive
    },
    {
        NULL,
        0,
        0,
        {0, 0, 0},
        NULL
    }
};

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

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

static BYTE peek_bank_io(ADDRESS addr)
{
    BYTE v1, v2, v3, v4;

    if (emu_id_enabled && addr >= 0xE8A0) {
        addr &= 0xff;
        if (addr == 0xff)
            emulator_id[addr - 0xa0] ^= 0xff;
        return emulator_id[addr - 0xa0];
    }
    switch (addr & 0xf0) {
      case 0x10:                /* PIA1 */
        return peek_pia1(addr);
      case 0x20:                /* PIA2 */
        return peek_pia2(addr);
      case 0x40:
        return peek_via(addr);  /* VIA */
      case 0x80:                /* CRTC */
        if (pet.crtc) {
            if (addr & 1)
                return read_crtc(crtc_ptr);
            else
                return 0x9f;    /* Status. */
        }
      case 0x00:
        return addr >> 8;
      default:                  /* 0x30, 0x50, 0x60, 0x70, 0x90-0xf0 */
        if (addr & 0x10)
            v1 = peek_pia1(addr);
        else
            v1 = 0xff;
        if (addr & 0x20)
            v2 = peek_pia2(addr);
        else
            v2 = 0xff;
        if (addr & 0x40)
            v3 = peek_via(addr);
        else
            v3 = 0xff;
        v4 = 0xff;
        if ((addr & 0x80) && pet.crtc) {
            if (addr & 1)
                v4 = read_crtc(crtc_ptr);
            else
                v4 = 0x9f;      /* Status. */
        }
        return v1 & v2 & v3 & v4;
    }
    return 0xff;
}

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

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

static int banknums[] = {
    0, 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 4:                   /* extended RAM area (8x96) */
          return ram[addr + 0x10000];
          break;
      case 3:                   /* io */
          if (addr >= 0xE000 && addr <= 0xE0FF) {
              return read_io(addr);
          }
      case 2:                   /* rom */
          if (addr >= 0x9000 && addr <= 0xFFFF) {
              return rom[addr & 0x7fff];
          }
      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 >= 0xE000 && addr <= 0xE0FF) {
            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 4:                   /* extended RAM area (8x96) */
        ram[addr + 0x10000] = byte;
        return;
      case 3:                   /* io */
        if (addr >= 0xE000 && addr <= 0xE0FF) {
            store_io(addr, byte);
            return;
        }
      case 2:                   /* rom */
        if (addr >= 0x9000 && 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.