/*** sol20.c: Processor Technology Sol-20

    Created by Patrick Linstruth (patrick@deltecent.com)

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    Except as contained in this notice, the name of Patrick Linstruth shall not
    be used in advertising or otherwise to promote the sale, use or other dealings
    in this Software without prior written authorization from Patrick Linstruth.

    These functions support the Processor Techology Sol-20

    There are four devices defined, SOL20, SOL20T, SOL20S, and SOL20P. The SOL20
    device provides personality modules. The SOL20T device supports an emulated
    tape drive. The SOL20S device supports the serial port. The SOL20P device
    supports the printer port.

    Devices:

    SOL20K - Keyboard
    SOL20S - Serial Port
    SOL20P - Printer Port
    SOL20T - Tape Port

    The Keyboard, serial and printer ports support TMXR which allow these
    ports to be attached to real serial ports the sockets. If no TMXR
    interfaces are attaached, the serial and parallel ports will use
    the SIMH console for input and output. This allows pasting
    ENT files into SOLOS using the SIMH console. The keyboard will
    use the video window and the SIMH console for input.

    The tape device allows attaching files that can be read from and
    written simulating reading and writing to cassettes.

    The Sol-20 displays its output on the VDM-1 video display.
    The most commonly used disk controller with the Sol-20 was the North Star
    hard-sector controller. The first North Star controller sold was SSSD and
    was typically used to run North Star DOS or CP/M 1.4. The single density
    format allowed about 88K of storage per diskette. Later, the DSDD North
    Star controller increased capacity to 350K of storage per diskette.

    Both SSSD (MDSA) and SSDD/DSDD (MDSAD) are supported by AltairZ80.

    Disk images of North Star DOS and CP/M for both of the North Star
    controllers, along with utilities for disk image transfer, are available at
    https://deramp.com/downloads/processor_technology/sol-20/software/

    Example:

    SET SOL20 ENA
    SET SOL20 VER=13C
    SET MDSA ENA
    ATTACH MDSA0 CPM22b14-48K-SDC-SOL.NSI
    BOOT SOL20

    At the '>' prompt, enter "EX E800" to boot CP/M
*/

#include "altairz80_defs.h"
#include "sim_imd.h"
#include "sim_tmxr.h"
#include "sim_video.h"

/********/
/* SIMH */
/********/

extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
                               int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap);
extern t_stat set_dev_enbdis(DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
extern t_stat (*vdm1_kb_callback)(SIM_KEY_EVENT *kev);
extern t_stat set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
extern t_stat set_cmd(int32 flag, CONST char *cptr);

extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);
extern uint32 nmiInterrupt;

/* Debug flags */
#define VERBOSE_MSG         (1 << 0)
#define ERROR_MSG           (1 << 1)
#define STATUS_MSG          (1 << 2)

/* Debug Flags */
static DEBTAB sol20_dt[] = {
    { "VERBOSE",    VERBOSE_MSG,        "Verbose messages" },
    { "ERROR",      ERROR_MSG,          "Error messages"   },
    { "STATUS",     STATUS_MSG,         "Status messages"  },
    { NULL,         0                                      }
};

/*****************************/
/* Local function prototypes */
/*****************************/

static t_stat sol20_reset(DEVICE *dptr);
static t_stat sol20_kb_reset(DEVICE *dptr);
static t_stat sol20_port_reset(DEVICE *dptr);
static t_stat sol20_svc(UNIT *uptr);
static t_stat sol20_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
static t_stat sol20_boot(int32 unitno, DEVICE *dptr);
static t_stat sol20_attach_tape(UNIT *uptr, CONST char *cptr);
static t_stat sol20_detach_tape(UNIT *uptr);
static t_stat sol20_attach_mux(UNIT *uptr, CONST char *cptr);
static t_stat sol20_detach_mux(UNIT *uptr);
static const char* sol20_description(DEVICE *dptr);
static t_stat sol20_config_line(DEVICE *dev, TMLN *tmln, int baud);
static const char* sol20k_description(DEVICE *dptr);
static const char* sol20t_description(DEVICE *dptr);
static const char* sol20s_description(DEVICE *dptr);
static const char* sol20p_description(DEVICE *dptr);
static t_stat sol20_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc);
static t_stat sol20_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc);
static t_stat sol20_set_rom(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat sol20_show_rom(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat sol20_show_ports(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat sol20_set_tape(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat sol20_show_tape(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat sol20_rewind(UNIT *uptr);
static t_stat sol20_erase(UNIT *uptr);
static int32 sol20io(int32 addr, int32 rw, int32 data);
static int32 sol20rom(int32 addr, int32 rw, int32 data);
static uint8 sol20_io_in(uint32 addr);
static uint8 sol20_io_out(uint32 addr, int32 data);
static t_stat sol20_kb_callback(SIM_KEY_EVENT *kev);
static uint8 translate_key(SIM_KEY_EVENT *kev);

/***********/
/* RAM/ROM */
/***********/

#define SOL20_ROM_BASE   0xc000
#define SOL20_ROM_SIZE   2048
#define SOL20_ROM_MASK   (SOL20_ROM_SIZE-1)

/* SOLOS 1.3 ROM */
static uint8 sol20_rom_13[SOL20_ROM_SIZE] = {
    0x00, 0xc3, 0xaf, 0xc1, 0xc3, 0xc9, 0xc1, 0xc3,
    0xe0, 0xc5, 0xc3, 0x03, 0xc6, 0xc3, 0x46, 0xc6,
    0xc3, 0x83, 0xc6, 0xc3, 0xcb, 0xc6, 0xc3, 0x7f,
    0xc7, 0x3a, 0x07, 0xc8, 0xc3, 0x3b, 0xc0, 0x3a,
    0x06, 0xc8, 0xe5, 0x21, 0x9a, 0xc2, 0xe6, 0x03,
    0x07, 0x85, 0x6f, 0xc3, 0x27, 0xc2, 0xdb, 0xfa,
    0x2f, 0xe6, 0x01, 0xc8, 0xdb, 0xfc, 0xc9, 0x00,
    0xc3, 0x01, 0xc0, 0xe5, 0x21, 0x92, 0xc2, 0xc3,
    0x26, 0xc0, 0xdb, 0xf8, 0xe6, 0x40, 0xc8, 0xdb,
    0xf9, 0xc9, 0xdb, 0xf8, 0x17, 0xd2, 0x4a, 0xc0,
    0x78, 0xd3, 0xf9, 0xc9, 0xe5, 0xd5, 0xc5, 0x3a,
    0x0c, 0xc8, 0xb7, 0xc2, 0x5f, 0xc1, 0x78, 0xe6,
    0x7f, 0x47, 0xca, 0x7c, 0xc0, 0x21, 0x73, 0xc2,
    0xcd, 0x82, 0xc0, 0xcd, 0x1c, 0xc1, 0x7e, 0xf6,
    0x80, 0x77, 0x2a, 0x0a, 0xc8, 0x2c, 0xaf, 0x2b,
    0xbc, 0xc2, 0x77, 0xc0, 0xc1, 0xd1, 0xe1, 0xc9,
    0x23, 0x23, 0x7e, 0xb7, 0xca, 0x94, 0xc0, 0xb8,
    0x23, 0xc2, 0x80, 0xc0, 0xe5, 0xcd, 0x36, 0xc1,
    0xe3, 0xc3, 0x27, 0xc2, 0x78, 0xfe, 0x7f, 0xc8,
    0xcd, 0x1c, 0xc1, 0x70, 0x3a, 0x08, 0xc8, 0xfe,
    0x3f, 0xda, 0xc1, 0xc0, 0x3a, 0x09, 0xc8, 0xfe,
    0x0f, 0xc2, 0xc1, 0xc0, 0xaf, 0x32, 0x08, 0xc8,
    0x4f, 0xcd, 0x23, 0xc1, 0xaf, 0xcd, 0xfa, 0xc0,
    0x3a, 0x0a, 0xc8, 0x3c, 0xe6, 0x0f, 0xc3, 0xee,
    0xc0, 0x3a, 0x08, 0xc8, 0x3c, 0xe6, 0x3f, 0x32,
    0x08, 0xc8, 0xc0, 0x3a, 0x09, 0xc8, 0x3c, 0xe6,
    0x0f, 0x32, 0x09, 0xc8, 0xc9, 0x21, 0x00, 0xcc,
    0x36, 0xa0, 0x23, 0x36, 0x20, 0x23, 0x7c, 0xfe,
    0xd0, 0xda, 0xdb, 0xc0, 0x37, 0x3e, 0x00, 0x32,
    0x09, 0xc8, 0x32, 0x08, 0xc8, 0xd0, 0xd3, 0xfe,
    0x32, 0x0a, 0xc8, 0xc9, 0xcd, 0x1c, 0xc1, 0x3a,
    0x08, 0xc8, 0xfe, 0x40, 0xd0, 0x36, 0x20, 0x23,
    0x3c, 0xc3, 0xfa, 0xc0, 0x3a, 0x09, 0xc8, 0x3d,
    0xc3, 0xcf, 0xc0, 0x3a, 0x08, 0xc8, 0x3d, 0xe6,
    0x3f, 0x32, 0x08, 0xc8, 0xc9, 0x3a, 0x08, 0xc8,
    0x3c, 0xc3, 0x0f, 0xc1, 0x3a, 0x08, 0xc8, 0x4f,
    0x3a, 0x09, 0xc8, 0x6f, 0x3a, 0x0a, 0xc8, 0x85,
    0x0f, 0x0f, 0x6f, 0xe6, 0x03, 0xc6, 0xcc, 0x67,
    0x7d, 0xe6, 0xc0, 0x81, 0x6f, 0xc9, 0xcd, 0x1c,
    0xc1, 0x7e, 0xe6, 0x7f, 0x77, 0xc9, 0xcd, 0x0b,
    0xc1, 0xcd, 0x1c, 0xc1, 0x36, 0x20, 0xc9, 0xcd,
    0xf4, 0xc0, 0xc3, 0x0f, 0xc1, 0x3a, 0x09, 0xc8,
    0x3c, 0xe6, 0x0f, 0xc2, 0xd1, 0xc0, 0xc3, 0xb0,
    0xc0, 0x3e, 0xff, 0x32, 0x0c, 0xc8, 0xc9, 0xcd,
    0x36, 0xc1, 0xcd, 0x68, 0xc1, 0xc3, 0x6b, 0xc0,
    0x3a, 0x0c, 0xc8, 0xfe, 0xff, 0xca, 0x90, 0xc1,
    0x21, 0x0c, 0xc8, 0x36, 0x00, 0xfe, 0x02, 0xda,
    0x88, 0xc1, 0xca, 0x8c, 0xc1, 0xfe, 0x08, 0xca,
    0x98, 0xc5, 0xfe, 0x09, 0xda, 0x98, 0xc0, 0xc0,
    0x78, 0xc3, 0x0f, 0xc1, 0x78, 0xc3, 0xcf, 0xc0,
    0x78, 0xfe, 0x03, 0xca, 0xa6, 0xc1, 0xfe, 0x04,
    0xc2, 0xa2, 0xc1, 0x44, 0x4d, 0xe1, 0xd1, 0xc5,
    0xe5, 0xaf, 0x32, 0x0c, 0xc8, 0xc9, 0x21, 0x08,
    0xc8, 0x46, 0x23, 0x4e, 0xc3, 0x9d, 0xc1, 0xaf,
    0x4f, 0x21, 0x00, 0xc8, 0x77, 0x23, 0x0c, 0xc2,
    0xb4, 0xc1, 0x31, 0xff, 0xcb, 0xcd, 0xd5, 0xc0,
    0xaf, 0xd3, 0xfa, 0x32, 0x07, 0xc8, 0x32, 0x06,
    0xc8, 0x31, 0xff, 0xcb, 0x3a, 0x07, 0xc8, 0xf5,
    0xaf, 0x32, 0x07, 0xc8, 0xcd, 0xf1, 0xc2, 0xcd,
    0xe4, 0xc1, 0xf1, 0x32, 0x07, 0xc8, 0xcd, 0x05,
    0xc2, 0xc3, 0xc9, 0xc1, 0xcd, 0x1f, 0xc0, 0xca,
    0xe4, 0xc1, 0xe6, 0x7f, 0xca, 0xc0, 0xc1, 0x47,
    0xfe, 0x0d, 0xca, 0xf4, 0xc0, 0xfe, 0x0a, 0xc8,
    0xfe, 0x7f, 0xc2, 0xff, 0xc1, 0x06, 0x5f, 0xcd,
    0x19, 0xc0, 0xc3, 0xe4, 0xc1, 0xcd, 0x36, 0xc1,
    0x0e, 0x01, 0xcd, 0x20, 0xc1, 0xeb, 0x21, 0x00,
    0xc0, 0xe5, 0xcd, 0x2e, 0xc3, 0xca, 0x80, 0xc4,
    0xeb, 0x11, 0x4a, 0xc2, 0xcd, 0x31, 0xc2, 0xcc,
    0x2e, 0xc2, 0xca, 0x81, 0xc4, 0x13, 0xeb, 0x7e,
    0x23, 0x66, 0x6f, 0xe3, 0x7d, 0xc9, 0x11, 0x3c,
    0xc8, 0x1a, 0xb7, 0xc8, 0xe5, 0xbe, 0x13, 0xc2,
    0x43, 0xc2, 0x23, 0x1a, 0xbe, 0xc2, 0x43, 0xc2,
    0xe1, 0xb7, 0xc9, 0x13, 0x13, 0x13, 0xe1, 0xc3,
    0x31, 0xc2, 0x54, 0x45, 0x67, 0xc3, 0x44, 0x55,
    0xbf, 0xc3, 0x45, 0x4e, 0x23, 0xc4, 0x45, 0x58,
    0x5e, 0xc4, 0x47, 0x45, 0xa7, 0xc4, 0x53, 0x41,
    0xe6, 0xc4, 0x58, 0x45, 0xa6, 0xc4, 0x43, 0x41,
    0x2b, 0xc5, 0x53, 0x45, 0x7a, 0xc5, 0x43, 0x55,
    0xbd, 0xc5, 0x00, 0x0b, 0xd5, 0xc0, 0x17, 0x04,
    0xc1, 0x1a, 0xcb, 0xc0, 0x01, 0x0b, 0xc1, 0x13,
    0x15, 0xc1, 0x0e, 0xe5, 0xc0, 0x0d, 0x47, 0xc1,
    0x0a, 0x4d, 0xc1, 0x5f, 0x3e, 0xc1, 0x1b, 0x59,
    0xc1, 0x00, 0x54, 0xc0, 0x4a, 0xc0, 0xe6, 0xc2,
    0xd2, 0xc2, 0x2e, 0xc0, 0x42, 0xc0, 0xdd, 0xc2,
    0xcb, 0xc2, 0x54, 0x41, 0x8e, 0xc5, 0x53, 0x3d,
    0x99, 0xc5, 0x49, 0x3d, 0x9d, 0xc5, 0x4f, 0x3d,
    0xa1, 0xc5, 0x4e, 0x3d, 0xb5, 0xc5, 0x43, 0x49,
    0xa5, 0xc5, 0x43, 0x4f, 0xa9, 0xc5, 0x58, 0x45,
    0xb1, 0xc5, 0x54, 0x59, 0xad, 0xc5, 0x43, 0x52,
    0xb9, 0xc5, 0x00, 0xe5, 0x2a, 0x00, 0xc8, 0xc3,
    0xd6, 0xc2, 0xe5, 0x2a, 0x02, 0xc8, 0x7d, 0xb4,
    0xca, 0xc0, 0xc1, 0xe3, 0xc9, 0xdb, 0xfa, 0x2f,
    0xe6, 0x02, 0xc8, 0xdb, 0xfd, 0xc9, 0xdb, 0xfa,
    0xe6, 0x04, 0xc2, 0xe6, 0xc2, 0x78, 0xd3, 0xfd,
    0xc9, 0xcd, 0xf9, 0xc2, 0x06, 0x3e, 0xc3, 0x19,
    0xc0, 0x06, 0x0a, 0xcd, 0x19, 0xc0, 0x06, 0x0d,
    0xcd, 0x19, 0xc0, 0x3a, 0x10, 0xc8, 0x4f, 0x0d,
    0xf8, 0xaf, 0xcd, 0x1f, 0xc4, 0xc3, 0x07, 0xc3,
    0xcd, 0x1b, 0xc3, 0x3e, 0x01, 0xc8, 0xcd, 0x40,
    0xc3, 0x7d, 0xc9, 0x0e, 0x0c, 0x1a, 0xfe, 0x20,
    0xca, 0x2e, 0xc3, 0x13, 0xfe, 0x3d, 0xca, 0x2e,
    0xc3, 0x0d, 0xc2, 0x1d, 0xc3, 0xc9, 0x0e, 0x0a,
    0x1a, 0xfe, 0x20, 0xc0, 0x13, 0x0d, 0xc8, 0xc3,
    0x30, 0xc3, 0xcd, 0x1b, 0xc3, 0xca, 0x80, 0xc4,
    0x21, 0x00, 0x00, 0x1a, 0xfe, 0x20, 0xc8, 0xfe,
    0x2f, 0xc8, 0xfe, 0x3a, 0xc8, 0x29, 0x29, 0x29,
    0x29, 0xcd, 0x5d, 0xc3, 0xd2, 0x80, 0xc4, 0x85,
    0x6f, 0x13, 0xc3, 0x43, 0xc3, 0xd6, 0x30, 0xfe,
    0x0a, 0xd8, 0xd6, 0x07, 0xfe, 0x10, 0xc9, 0xcd,
    0x10, 0xc3, 0x32, 0x06, 0xc8, 0xcd, 0x10, 0xc3,
    0x32, 0x07, 0xc8, 0xcd, 0x2e, 0xc0, 0xca, 0x8b,
    0xc3, 0x47, 0xfe, 0x80, 0xca, 0xc0, 0xc1, 0xda,
    0x88, 0xc3, 0xcd, 0x54, 0xc0, 0xc3, 0x8b, 0xc3,
    0xcd, 0x19, 0xc0, 0xcd, 0x1f, 0xc0, 0xca, 0x73,
    0xc3, 0xe6, 0x7f, 0xca, 0x73, 0xc3, 0x47, 0xfe,
    0x1b, 0xd2, 0xb9, 0xc3, 0xfe, 0x0d, 0xca, 0xb9,
    0xc3, 0xfe, 0x0a, 0xca, 0xb9, 0xc3, 0x3a, 0x0c,
    0xc8, 0xb7, 0xc2, 0xb9, 0xc3, 0xc5, 0x06, 0x1b,
    0xcd, 0x54, 0xc0, 0x06, 0x07, 0xcd, 0x54, 0xc0,
    0xc1, 0xcd, 0x54, 0xc0, 0xc3, 0x73, 0xc3, 0xcd,
    0x3a, 0xc3, 0xe5, 0xcd, 0x10, 0xc3, 0xd1, 0xeb,
    0xcd, 0xf9, 0xc2, 0xcd, 0xe8, 0xc3, 0xcd, 0x06,
    0xc4, 0x0e, 0x10, 0x7e, 0xc5, 0xcd, 0xed, 0xc3,
    0x7d, 0x93, 0x7c, 0x9a, 0xd2, 0xc9, 0xc1, 0xc1,
    0x23, 0x0d, 0xc2, 0xd3, 0xc3, 0xc3, 0xc8, 0xc3,
    0x7c, 0xcd, 0x0b, 0xc4, 0x7d, 0xcd, 0x0b, 0xc4,
    0xcd, 0x1f, 0xc0, 0xca, 0x06, 0xc4, 0xe6, 0x7f,
    0xca, 0xc9, 0xc1, 0xfe, 0x20, 0xc2, 0x06, 0xc4,
    0xcd, 0x1f, 0xc0, 0xca, 0x00, 0xc4, 0x06, 0x20,
    0xc3, 0x19, 0xc0, 0x4f, 0x0f, 0x0f, 0x0f, 0x0f,
    0xcd, 0x14, 0xc4, 0x79, 0xe6, 0x0f, 0xc6, 0x30,
    0xfe, 0x3a, 0xda, 0x1f, 0xc4, 0xc6, 0x07, 0x47,
    0xc3, 0x19, 0xc0, 0xcd, 0x3a, 0xc3, 0xe5, 0xaf,
    0x32, 0x07, 0xc8, 0xcd, 0xf9, 0xc2, 0x06, 0x3a,
    0xcd, 0xff, 0xc1, 0xcd, 0x36, 0xc1, 0x0e, 0x01,
    0xcd, 0x20, 0xc1, 0xeb, 0x0e, 0x03, 0xcd, 0x30,
    0xc3, 0xca, 0x2b, 0xc4, 0xfe, 0x2f, 0xca, 0xc0,
    0xc1, 0xcd, 0x40, 0xc3, 0xfe, 0x3a, 0xca, 0x59,
    0xc4, 0x7d, 0xe1, 0x77, 0x23, 0xe5, 0xc3, 0x3c,
    0xc4, 0xe3, 0x13, 0xc3, 0x3c, 0xc4, 0xcd, 0x3a,
    0xc3, 0xe5, 0x21, 0x00, 0xc0, 0xc9, 0x21, 0x1c,
    0xc8, 0xcd, 0x1b, 0xc3, 0x06, 0x06, 0x1a, 0xfe,
    0x20, 0xca, 0x86, 0xc4, 0xfe, 0x2f, 0xca, 0x86,
    0xc4, 0x77, 0x13, 0x23, 0x05, 0xc2, 0x6e, 0xc4,
    0xeb, 0x36, 0x3f, 0xc3, 0xc0, 0xc1, 0x36, 0x00,
    0x23, 0x05, 0xc2, 0x86, 0xc4, 0xfe, 0x2f, 0x3e,
    0x01, 0xc2, 0x9a, 0xc4, 0x13, 0xcd, 0x2e, 0xc3,
    0xd6, 0x30, 0xe6, 0x01, 0x3e, 0x80, 0xc2, 0xa2,
    0xc4, 0x1f, 0x32, 0x54, 0xc8, 0xc9, 0x3e, 0xaf,
    0xf5, 0x21, 0x2c, 0xc8, 0xcd, 0x69, 0xc4, 0x21,
    0x00, 0x00, 0xcd, 0x10, 0xc3, 0xeb, 0x21, 0x2c,
    0xc8, 0x7e, 0xb7, 0xc2, 0xc1, 0xc4, 0x21, 0x1c,
    0xc8, 0xe5, 0xcd, 0x48, 0xc5, 0xe1, 0xcd, 0xcb,
    0xc6, 0xda, 0x14, 0xc5, 0xcd, 0x50, 0xc5, 0xf1,
    0xb7, 0xc8, 0x3a, 0x22, 0xc8, 0xb7, 0xfa, 0x14,
    0xc5, 0x3a, 0x21, 0xc8, 0xb7, 0xc2, 0x14, 0xc5,
    0x2a, 0x27, 0xc8, 0xc3, 0x61, 0xc4, 0xcd, 0x66,
    0xc4, 0xcd, 0x3a, 0xc3, 0xe5, 0xcd, 0x3a, 0xc3,
    0xe3, 0xe5, 0xcd, 0x10, 0xc3, 0x22, 0x25, 0xc8,
    0xe1, 0xd1, 0xe5, 0x7b, 0x95, 0x6f, 0x7a, 0x9c,
    0x67, 0x23, 0x22, 0x23, 0xc8, 0xe5, 0xcd, 0x48,
    0xc5, 0x21, 0x1c, 0xc8, 0xcd, 0xaf, 0xc7, 0xd1,
    0xe1, 0xc3, 0x90, 0xc7, 0xcd, 0xf9, 0xc2, 0x16,
    0x06, 0x21, 0x25, 0xc5, 0xcd, 0x6a, 0xc5, 0xcd,
    0x50, 0xc5, 0xc3, 0xc0, 0xc1, 0x45, 0x52, 0x52,
    0x4f, 0x52, 0x20, 0xcd, 0x66, 0xc4, 0xcd, 0xf9,
    0xc2, 0xcd, 0x48, 0xc5, 0x06, 0x01, 0xcd, 0xef,
    0xc7, 0xcd, 0x23, 0xc7, 0xda, 0xc0, 0xc1, 0xc2,
    0x39, 0xc5, 0xcd, 0x50, 0xc5, 0xc3, 0x39, 0xc5,
    0x21, 0x54, 0xc8, 0x3a, 0x0d, 0xc8, 0xb6, 0xc9,
    0x16, 0x08, 0x21, 0x1b, 0xc8, 0xcd, 0x6a, 0xc5,
    0xcd, 0x06, 0xc4, 0x2a, 0x25, 0xc8, 0xcd, 0xe8,
    0xc3, 0x2a, 0x23, 0xc8, 0xcd, 0xe8, 0xc3, 0xc3,
    0xf9, 0xc2, 0x7e, 0xb7, 0xc2, 0x71, 0xc5, 0x3e,
    0x20, 0xcd, 0x1f, 0xc4, 0x23, 0x15, 0xc2, 0x6a,
    0xc5, 0xc9, 0xcd, 0x1b, 0xc3, 0xca, 0x80, 0xc4,
    0xd5, 0xcd, 0x3a, 0xc3, 0xe3, 0x11, 0xa2, 0xc2,
    0xcd, 0x31, 0xc2, 0xc3, 0x22, 0xc2, 0xb7, 0xca,
    0x94, 0xc5, 0x3e, 0x20, 0x32, 0x0d, 0xc8, 0xc9,
    0x78, 0x32, 0x0b, 0xc8, 0xc9, 0x32, 0x06, 0xc8,
    0xc9, 0x32, 0x07, 0xc8, 0xc9, 0x22, 0x00, 0xc8,
    0xc9, 0x22, 0x02, 0xc8, 0xc9, 0x32, 0x22, 0xc8,
    0xc9, 0x22, 0x27, 0xc8, 0xc9, 0x32, 0x10, 0xc8,
    0xc9, 0x32, 0x11, 0xc8, 0xc9, 0xcd, 0x66, 0xc4,
    0x21, 0xc9, 0xc1, 0xcd, 0x10, 0xc3, 0xe5, 0x21,
    0x1c, 0xc8, 0xcd, 0x2e, 0xc2, 0xca, 0xd3, 0xc5,
    0x1b, 0x36, 0x00, 0x7e, 0x12, 0x13, 0x23, 0x7e,
    0x12, 0x13, 0xe1, 0xeb, 0x73, 0x23, 0x72, 0xc9,
    0xe5, 0xcd, 0x33, 0xc6, 0xc2, 0xfa, 0xc5, 0x36,
    0x01, 0x23, 0x77, 0x23, 0x77, 0x11, 0x63, 0xc8,
    0x3a, 0x54, 0xc8, 0x82, 0x57, 0xc1, 0xb7, 0xc3,
    0xb6, 0xc6, 0xe1, 0xd1, 0xaf, 0x37, 0xc9, 0x3d,
    0x37, 0xd1, 0xc9, 0xcd, 0x33, 0xc6, 0xc8, 0xb7,
    0x3c, 0x36, 0x00, 0xc8, 0x23, 0x23, 0x7e, 0x7e,
    0xcd, 0xbf, 0xc6, 0xc5, 0x21, 0x07, 0x00, 0x09,
    0xb7, 0xca, 0x2b, 0xc6, 0xe5, 0x77, 0x23, 0x36,
    0x00, 0x23, 0x73, 0x23, 0x72, 0x60, 0x69, 0xcd,
    0x7c, 0xc7, 0xe1, 0xaf, 0x77, 0x23, 0x77, 0xe1,
    0xc3, 0x7c, 0xc7, 0x21, 0x55, 0xc8, 0x1f, 0xe6,
    0x01, 0x32, 0x54, 0xc8, 0xca, 0x42, 0xc6, 0x21,
    0x5c, 0xc8, 0x7e, 0xb7, 0x37, 0xc9, 0xcd, 0x33,
    0xc6, 0xc8, 0x3c, 0xfa, 0xfc, 0xc5, 0x36, 0xff,
    0x23, 0x7e, 0xe5, 0x23, 0xcd, 0xbf, 0xc6, 0xe1,
    0xb7, 0xc2, 0x75, 0xc6, 0xd5, 0xe5, 0x23, 0xcd,
    0xa6, 0xc6, 0xcd, 0xc8, 0xc6, 0xda, 0xfa, 0xc5,
    0xe1, 0x7b, 0xb2, 0xca, 0xff, 0xc5, 0x73, 0x23,
    0x36, 0x00, 0x2b, 0x7b, 0xd1, 0x3d, 0x77, 0x23,
    0x7e, 0x34, 0x83, 0x5f, 0xd2, 0x80, 0xc6, 0x14,
    0x1a, 0xb7, 0xc9, 0xcd, 0x33, 0xc6, 0xc8, 0x3c,
    0xc8, 0x36, 0xfe, 0x23, 0x23, 0x78, 0xf5, 0xe5,
    0xcd, 0xbf, 0xc6, 0xe1, 0x7e, 0x83, 0x5f, 0xd2,
    0x9b, 0xc6, 0x14, 0xf1, 0x12, 0xb7, 0x34, 0xc0,
    0xcd, 0xa6, 0xc6, 0xc3, 0x7c, 0xc7, 0xcd, 0xbf,
    0xc6, 0xc5, 0x21, 0x06, 0x00, 0x09, 0x01, 0x00,
    0x01, 0xcd, 0xb6, 0xc6, 0xe1, 0xc9, 0x23, 0x71,
    0x23, 0x70, 0x23, 0x73, 0x23, 0x72, 0xc9, 0x23,
    0x4e, 0x23, 0x46, 0x23, 0x5e, 0x23, 0x56, 0xc9,
    0xcd, 0xde, 0xc7, 0xd5, 0x06, 0x03, 0xcd, 0xef,
    0xc7, 0xdb, 0xfb, 0xe5, 0xcd, 0x23, 0xc7, 0xe1,
    0xda, 0x06, 0xc7, 0xc2, 0xd3, 0xc6, 0xe5, 0x11,
    0x1c, 0xc8, 0xcd, 0xd2, 0xc7, 0xe1, 0xc2, 0xd3,
    0xc6, 0xd1, 0x7a, 0xb3, 0x2a, 0x23, 0xc8, 0xeb,
    0xc2, 0xf6, 0xc6, 0x2a, 0x25, 0xc8, 0xd5, 0xcd,
    0x15, 0xc7, 0xca, 0x10, 0xc7, 0xcd, 0x44, 0xc7,
    0xda, 0x06, 0xc7, 0xca, 0xf7, 0xc6, 0xaf, 0x37,
    0xc3, 0x11, 0xc7, 0x06, 0x01, 0xcd, 0xf1, 0xc7,
    0xaf, 0xd3, 0xfa, 0xd1, 0xc9, 0xaf, 0x47, 0xb2,
    0xc2, 0x20, 0xc7, 0xb3, 0xc8, 0x43, 0x5a, 0xc9,
    0x15, 0xb7, 0xc9, 0x06, 0x0a, 0xcd, 0x5d, 0xc7,
    0xd8, 0xdb, 0xfb, 0xb7, 0xc2, 0x23, 0xc7, 0x05,
    0xc2, 0x25, 0xc7, 0xcd, 0x6f, 0xc7, 0xd8, 0xfe,
    0x01, 0xda, 0x33, 0xc7, 0xc2, 0x23, 0xc7, 0x21,
    0x1c, 0xc8, 0x06, 0x10, 0x0e, 0x00, 0xcd, 0x6f,
    0xc7, 0xd8, 0x77, 0x23, 0xcd, 0xa8, 0xc7, 0x05,
    0xc2, 0x46, 0xc7, 0xcd, 0x6f, 0xc7, 0xa9, 0xc8,
    0x3a, 0x11, 0xc8, 0x3c, 0xc9, 0xdb, 0xfa, 0xe6,
    0x40, 0xc0, 0xcd, 0x1f, 0xc0, 0xca, 0x5d, 0xc7,
    0xe6, 0x7f, 0xc2, 0x5d, 0xc7, 0x37, 0xc9, 0xcd,
    0x5d, 0xc7, 0xd8, 0xdb, 0xfa, 0xe6, 0x18, 0xdb,
    0xfb, 0xc8, 0x37, 0xc9, 0xcd, 0xde, 0xc7, 0xe5,
    0xcd, 0xaf, 0xc7, 0xe1, 0x11, 0x07, 0x00, 0x19,
    0x5e, 0x23, 0x56, 0x23, 0x7e, 0x23, 0x66, 0x6f,
    0xe5, 0xcd, 0x15, 0xc7, 0xca, 0x0b, 0xc7, 0xcd,
    0xc3, 0xc7, 0xc3, 0x91, 0xc7, 0xf5, 0xdb, 0xfa,
    0xe6, 0x80, 0xca, 0x9e, 0xc7, 0xf1, 0xd3, 0xfb,
    0x91, 0x4f, 0xa9, 0x2f, 0x91, 0x4f, 0xc9, 0xcd,
    0xed, 0xc7, 0x16, 0x32, 0xaf, 0xcd, 0x9d, 0xc7,
    0x15, 0xc2, 0xb4, 0xc7, 0x3e, 0x01, 0xcd, 0x9d,
    0xc7, 0x06, 0x10, 0x0e, 0x00, 0x7e, 0xcd, 0x9d,
    0xc7, 0x05, 0x23, 0xc2, 0xc5, 0xc7, 0x79, 0xc3,
    0x9d, 0xc7, 0x06, 0x05, 0x1a, 0xbe, 0xc0, 0x05,
    0xc8, 0x23, 0x13, 0xc3, 0xd4, 0xc7, 0x3a, 0x54,
    0xc8, 0xb7, 0x3a, 0x0d, 0xc8, 0xc2, 0xea, 0xc7,
    0xc6, 0x40, 0xc6, 0x40, 0xc9, 0x06, 0x04, 0xd3,
    0xfa, 0x11, 0x00, 0x00, 0x1b, 0x7a, 0xb3, 0xc2,
    0xf4, 0xc7, 0x05, 0xc2, 0xf1, 0xc7, 0xc9, 0xfa,
};

/* SOLOS 1.3C ROM for CP/M */
static uint8 sol20_rom_13c[SOL20_ROM_SIZE] = {
    0x00, 0xc3, 0xaf, 0xc1, 0xc3, 0xc9, 0xc1, 0xc3,
    0xe0, 0xc5, 0xc3, 0x03, 0xc6, 0xc3, 0x46, 0xc6,
    0xc3, 0x83, 0xc6, 0xc3, 0xcb, 0xc6, 0xc3, 0x7f,
    0xc7, 0x3a, 0x07, 0xc8, 0xc3, 0x3b, 0xc0, 0x3a,
    0x06, 0xc8, 0xe5, 0x21, 0x9a, 0xc2, 0xe6, 0x03,
    0x07, 0x85, 0x6f, 0xc3, 0x27, 0xc2, 0xdb, 0xfa,
    0x2f, 0xe6, 0x01, 0xc8, 0xdb, 0xfc, 0xc9, 0x00,
    0xc3, 0x01, 0xc0, 0xe5, 0x21, 0x92, 0xc2, 0xc3,
    0x26, 0xc0, 0xdb, 0xf8, 0xe6, 0x40, 0xc8, 0xdb,
    0xf9, 0xc9, 0xdb, 0xf8, 0x17, 0xd2, 0x4a, 0xc0,
    0x78, 0xd3, 0xf9, 0xc9, 0xe5, 0xd5, 0xc5, 0x3a,
    0x0c, 0xc8, 0xb7, 0xc2, 0x5f, 0xc1, 0x78, 0xe6,
    0x7f, 0x47, 0xca, 0x7c, 0xc0, 0x21, 0x73, 0xc2,
    0xcd, 0x82, 0xc0, 0xcd, 0x1c, 0xc1, 0x7e, 0xf6,
    0x80, 0x77, 0x2a, 0x0a, 0xc8, 0x2c, 0xaf, 0x2b,
    0xbc, 0xc2, 0x77, 0xc0, 0xc1, 0xd1, 0xe1, 0xc9,
    0x23, 0x23, 0x7e, 0xb7, 0xca, 0x94, 0xc0, 0xb8,
    0x23, 0xc2, 0x80, 0xc0, 0xe5, 0xcd, 0x36, 0xc1,
    0xe3, 0xc3, 0x27, 0xc2, 0x78, 0xfe, 0x7f, 0xc8,
    0xcd, 0x1c, 0xc1, 0x70, 0x3a, 0x08, 0xc8, 0xfe,
    0x3f, 0xda, 0xc1, 0xc0, 0x3a, 0x09, 0xc8, 0xfe,
    0x0f, 0xc2, 0xc1, 0xc0, 0xaf, 0x32, 0x08, 0xc8,
    0x4f, 0xcd, 0x23, 0xc1, 0xaf, 0xcd, 0xfa, 0xc0,
    0x3a, 0x0a, 0xc8, 0x3c, 0xe6, 0x0f, 0xc3, 0xee,
    0xc0, 0x3a, 0x08, 0xc8, 0x3c, 0xe6, 0x3f, 0x32,
    0x08, 0xc8, 0xc0, 0x3a, 0x09, 0xc8, 0x3c, 0xe6,
    0x0f, 0x32, 0x09, 0xc8, 0xc9, 0x21, 0x00, 0xcc,
    0x36, 0xa0, 0x23, 0x36, 0x20, 0x23, 0x7c, 0xfe,
    0xd0, 0xda, 0xdb, 0xc0, 0x37, 0x3e, 0x00, 0x32,
    0x09, 0xc8, 0x32, 0x08, 0xc8, 0xd0, 0xd3, 0xfe,
    0x32, 0x0a, 0xc8, 0xc9, 0xcd, 0x1c, 0xc1, 0x3a,
    0x08, 0xc8, 0xfe, 0x40, 0xd0, 0x36, 0x20, 0x23,
    0x3c, 0xc3, 0xfa, 0xc0, 0x3a, 0x09, 0xc8, 0x3d,
    0xc3, 0xcf, 0xc0, 0x3a, 0x08, 0xc8, 0x3d, 0xe6,
    0x3f, 0x32, 0x08, 0xc8, 0xc9, 0x3a, 0x08, 0xc8,
    0x3c, 0xc3, 0x0f, 0xc1, 0x3a, 0x08, 0xc8, 0x4f,
    0x3a, 0x09, 0xc8, 0x6f, 0x3a, 0x0a, 0xc8, 0x85,
    0x0f, 0x0f, 0x6f, 0xe6, 0x03, 0xc6, 0xcc, 0x67,
    0x7d, 0xe6, 0xc0, 0x81, 0x6f, 0xc9, 0xcd, 0x1c,
    0xc1, 0x7e, 0xe6, 0x7f, 0x77, 0xc9, 0xcd, 0x0b,
    0xc1, 0xcd, 0x1c, 0xc1, 0x36, 0x20, 0xc9, 0xcd,
    0xf4, 0xc0, 0xc3, 0x0f, 0xc1, 0x3a, 0x09, 0xc8,
    0x3c, 0xe6, 0x0f, 0xc2, 0xd1, 0xc0, 0xc3, 0xb0,
    0xc0, 0x3e, 0xff, 0x32, 0x0c, 0xc8, 0xc9, 0xcd,
    0x36, 0xc1, 0xcd, 0x68, 0xc1, 0xc3, 0x6b, 0xc0,
    0x3a, 0x0c, 0xc8, 0xfe, 0xff, 0xca, 0x90, 0xc1,
    0x21, 0x0c, 0xc8, 0x36, 0x00, 0xfe, 0x02, 0xda,
    0x88, 0xc1, 0xca, 0x8c, 0xc1, 0xfe, 0x08, 0xca,
    0x98, 0xc5, 0xfe, 0x09, 0xda, 0x98, 0xc0, 0xc0,
    0x78, 0xc3, 0x0f, 0xc1, 0x78, 0xc3, 0xcf, 0xc0,
    0x78, 0xfe, 0x03, 0xca, 0xa6, 0xc1, 0xfe, 0x04,
    0xc2, 0xa2, 0xc1, 0x44, 0x4d, 0xe1, 0xd1, 0xc5,
    0xe5, 0xaf, 0x32, 0x0c, 0xc8, 0xc9, 0x21, 0x08,
    0xc8, 0x46, 0x23, 0x4e, 0xc3, 0x9d, 0xc1, 0xaf,
    0x4f, 0x21, 0x00, 0xc8, 0x77, 0x23, 0x0c, 0xc2,
    0xb4, 0xc1, 0x31, 0xff, 0xcb, 0xcd, 0xd5, 0xc0,
    0xaf, 0xd3, 0xfa, 0x32, 0x07, 0xc8, 0x32, 0x06,
    0xc8, 0x31, 0xff, 0xcb, 0x3a, 0x07, 0xc8, 0xf5,
    0xaf, 0x32, 0x07, 0xc8, 0xcd, 0xf1, 0xc2, 0xcd,
    0xe4, 0xc1, 0xf1, 0x32, 0x07, 0xc8, 0xcd, 0x05,
    0xc2, 0xc3, 0xc9, 0xc1, 0xcd, 0x1f, 0xc0, 0xca,
    0xe4, 0xc1, 0xfe, 0x8c, 0xca, 0x00, 0xe8, 0xe6,
    0x7f, 0xca, 0xc0, 0xc1, 0x47, 0xfe, 0x0d, 0xc8,
    0xfe, 0x7f, 0xc2, 0xff, 0xc1, 0x06, 0x5f, 0xcd,
    0x19, 0xc0, 0xc3, 0xe4, 0xc1, 0xcd, 0x36, 0xc1,
    0x0e, 0x01, 0xcd, 0x20, 0xc1, 0xeb, 0x21, 0x00,
    0xc0, 0xe5, 0xcd, 0x2e, 0xc3, 0xca, 0x80, 0xc4,
    0xeb, 0x11, 0x4a, 0xc2, 0xcd, 0x31, 0xc2, 0xcc,
    0x2e, 0xc2, 0xca, 0x81, 0xc4, 0x13, 0xeb, 0x7e,
    0x23, 0x66, 0x6f, 0xe3, 0x7d, 0xc9, 0x11, 0x3c,
    0xc8, 0x1a, 0xb7, 0xc8, 0xe5, 0xbe, 0x13, 0xc2,
    0x43, 0xc2, 0x23, 0x1a, 0xbe, 0xc2, 0x43, 0xc2,
    0xe1, 0xb7, 0xc9, 0x13, 0x13, 0x13, 0xe1, 0xc3,
    0x31, 0xc2, 0x54, 0x45, 0x67, 0xc3, 0x44, 0x55,
    0xbf, 0xc3, 0x45, 0x4e, 0x23, 0xc4, 0x45, 0x58,
    0x5e, 0xc4, 0x47, 0x45, 0xa7, 0xc4, 0x53, 0x41,
    0xe6, 0xc4, 0x58, 0x45, 0xa6, 0xc4, 0x43, 0x41,
    0x2b, 0xc5, 0x53, 0x45, 0x7a, 0xc5, 0x43, 0x55,
    0xbd, 0xc5, 0x00, 0x0b, 0xd5, 0xc0, 0x17, 0x04,
    0xc1, 0x1a, 0xcb, 0xc0, 0x01, 0x0b, 0xc1, 0x13,
    0x15, 0xc1, 0x0e, 0xe5, 0xc0, 0x0d, 0x47, 0xc1,
    0x0a, 0x4d, 0xc1, 0x5f, 0x3e, 0xc1, 0x1b, 0x59,
    0xc1, 0x00, 0x54, 0xc0, 0x4a, 0xc0, 0xe6, 0xc2,
    0xd2, 0xc2, 0x2e, 0xc0, 0x42, 0xc0, 0xdd, 0xc2,
    0xcb, 0xc2, 0x54, 0x41, 0x8e, 0xc5, 0x53, 0x3d,
    0x99, 0xc5, 0x49, 0x3d, 0x9d, 0xc5, 0x4f, 0x3d,
    0xa1, 0xc5, 0x4e, 0x3d, 0xb5, 0xc5, 0x43, 0x49,
    0xa5, 0xc5, 0x43, 0x4f, 0xa9, 0xc5, 0x58, 0x45,
    0xb1, 0xc5, 0x54, 0x59, 0xad, 0xc5, 0x43, 0x52,
    0xb9, 0xc5, 0x00, 0xe5, 0x2a, 0x00, 0xc8, 0xc3,
    0xd6, 0xc2, 0xe5, 0x2a, 0x02, 0xc8, 0x7d, 0xb4,
    0xca, 0xc0, 0xc1, 0xe3, 0xc9, 0xdb, 0xfa, 0x2f,
    0xe6, 0x02, 0xc8, 0xdb, 0xfd, 0xc9, 0xdb, 0xfa,
    0xe6, 0x04, 0xc2, 0xe6, 0xc2, 0x78, 0xd3, 0xfd,
    0xc9, 0xcd, 0xf9, 0xc2, 0x06, 0x3e, 0xc3, 0x19,
    0xc0, 0x06, 0x0a, 0xcd, 0x19, 0xc0, 0x06, 0x0d,
    0xcd, 0x19, 0xc0, 0x3a, 0x10, 0xc8, 0x4f, 0x0d,
    0xf8, 0xaf, 0xcd, 0x1f, 0xc4, 0xc3, 0x07, 0xc3,
    0xcd, 0x1b, 0xc3, 0x3e, 0x01, 0xc8, 0xcd, 0x40,
    0xc3, 0x7d, 0xc9, 0x0e, 0x0c, 0x1a, 0xfe, 0x20,
    0xca, 0x2e, 0xc3, 0x13, 0xfe, 0x3d, 0xca, 0x2e,
    0xc3, 0x0d, 0xc2, 0x1d, 0xc3, 0xc9, 0x0e, 0x0a,
    0x1a, 0xfe, 0x20, 0xc0, 0x13, 0x0d, 0xc8, 0xc3,
    0x30, 0xc3, 0xcd, 0x1b, 0xc3, 0xca, 0x80, 0xc4,
    0x21, 0x00, 0x00, 0x1a, 0xfe, 0x20, 0xc8, 0xfe,
    0x2f, 0xc8, 0xfe, 0x3a, 0xc8, 0x29, 0x29, 0x29,
    0x29, 0xcd, 0x5d, 0xc3, 0xd2, 0x80, 0xc4, 0x85,
    0x6f, 0x13, 0xc3, 0x43, 0xc3, 0xd6, 0x30, 0xfe,
    0x0a, 0xd8, 0xd6, 0x07, 0xfe, 0x10, 0xc9, 0xcd,
    0x10, 0xc3, 0x32, 0x06, 0xc8, 0xcd, 0x10, 0xc3,
    0x32, 0x07, 0xc8, 0xcd, 0x2e, 0xc0, 0xca, 0x8b,
    0xc3, 0x47, 0xfe, 0x80, 0xca, 0xc0, 0xc1, 0xda,
    0x88, 0xc3, 0xcd, 0x54, 0xc0, 0xc3, 0x8b, 0xc3,
    0xcd, 0x19, 0xc0, 0xcd, 0x1f, 0xc0, 0xca, 0x73,
    0xc3, 0xe6, 0x7f, 0xca, 0x73, 0xc3, 0x47, 0xfe,
    0x1b, 0xd2, 0xb9, 0xc3, 0xfe, 0x0d, 0xca, 0xb9,
    0xc3, 0xfe, 0x0a, 0xca, 0xb9, 0xc3, 0x3a, 0x0c,
    0xc8, 0xb7, 0xc2, 0xb9, 0xc3, 0xc5, 0x06, 0x1b,
    0xcd, 0x54, 0xc0, 0x06, 0x07, 0xcd, 0x54, 0xc0,
    0xc1, 0xcd, 0x54, 0xc0, 0xc3, 0x73, 0xc3, 0xcd,
    0x3a, 0xc3, 0xe5, 0xcd, 0x10, 0xc3, 0xd1, 0xeb,
    0xcd, 0xf9, 0xc2, 0xcd, 0xe8, 0xc3, 0xcd, 0x06,
    0xc4, 0x0e, 0x10, 0x7e, 0xc5, 0xcd, 0xed, 0xc3,
    0x7d, 0x93, 0x7c, 0x9a, 0xd2, 0xc9, 0xc1, 0xc1,
    0x23, 0x0d, 0xc2, 0xd3, 0xc3, 0xc3, 0xc8, 0xc3,
    0x7c, 0xcd, 0x0b, 0xc4, 0x7d, 0xcd, 0x0b, 0xc4,
    0xcd, 0x1f, 0xc0, 0xca, 0x06, 0xc4, 0xe6, 0x7f,
    0xca, 0xc9, 0xc1, 0xfe, 0x20, 0xc2, 0x06, 0xc4,
    0xcd, 0x1f, 0xc0, 0xca, 0x00, 0xc4, 0x06, 0x20,
    0xc3, 0x19, 0xc0, 0x4f, 0x0f, 0x0f, 0x0f, 0x0f,
    0xcd, 0x14, 0xc4, 0x79, 0xe6, 0x0f, 0xc6, 0x30,
    0xfe, 0x3a, 0xda, 0x1f, 0xc4, 0xc6, 0x07, 0x47,
    0xc3, 0x19, 0xc0, 0xcd, 0x3a, 0xc3, 0xe5, 0xaf,
    0x32, 0x07, 0xc8, 0xcd, 0xf9, 0xc2, 0x06, 0x3a,
    0xcd, 0xff, 0xc1, 0xcd, 0x36, 0xc1, 0x0e, 0x01,
    0xcd, 0x20, 0xc1, 0xeb, 0x0e, 0x03, 0xcd, 0x30,
    0xc3, 0xca, 0x2b, 0xc4, 0xfe, 0x2f, 0xca, 0xc0,
    0xc1, 0xcd, 0x40, 0xc3, 0xfe, 0x3a, 0xca, 0x59,
    0xc4, 0x7d, 0xe1, 0x77, 0x23, 0xe5, 0xc3, 0x3c,
    0xc4, 0xe3, 0x13, 0xc3, 0x3c, 0xc4, 0xcd, 0x3a,
    0xc3, 0xe5, 0x21, 0x00, 0xc0, 0xc9, 0x21, 0x1c,
    0xc8, 0xcd, 0x1b, 0xc3, 0x06, 0x06, 0x1a, 0xfe,
    0x20, 0xca, 0x86, 0xc4, 0xfe, 0x2f, 0xca, 0x86,
    0xc4, 0x77, 0x13, 0x23, 0x05, 0xc2, 0x6e, 0xc4,
    0xeb, 0x36, 0x3f, 0xc3, 0xc0, 0xc1, 0x36, 0x00,
    0x23, 0x05, 0xc2, 0x86, 0xc4, 0xfe, 0x2f, 0x3e,
    0x01, 0xc2, 0x9a, 0xc4, 0x13, 0xcd, 0x2e, 0xc3,
    0xd6, 0x30, 0xe6, 0x01, 0x3e, 0x80, 0xc2, 0xa2,
    0xc4, 0x1f, 0x32, 0x54, 0xc8, 0xc9, 0x3e, 0xaf,
    0xf5, 0x21, 0x2c, 0xc8, 0xcd, 0x69, 0xc4, 0x21,
    0x00, 0x00, 0xcd, 0x10, 0xc3, 0xeb, 0x21, 0x2c,
    0xc8, 0x7e, 0xb7, 0xc2, 0xc1, 0xc4, 0x21, 0x1c,
    0xc8, 0xe5, 0xcd, 0x48, 0xc5, 0xe1, 0xcd, 0xcb,
    0xc6, 0xda, 0x14, 0xc5, 0xcd, 0x50, 0xc5, 0xf1,
    0xb7, 0xc8, 0x3a, 0x22, 0xc8, 0xb7, 0xfa, 0x14,
    0xc5, 0x3a, 0x21, 0xc8, 0xb7, 0xc2, 0x14, 0xc5,
    0x2a, 0x27, 0xc8, 0xc3, 0x61, 0xc4, 0xcd, 0x66,
    0xc4, 0xcd, 0x3a, 0xc3, 0xe5, 0xcd, 0x3a, 0xc3,
    0xe3, 0xe5, 0xcd, 0x10, 0xc3, 0x22, 0x25, 0xc8,
    0xe1, 0xd1, 0xe5, 0x7b, 0x95, 0x6f, 0x7a, 0x9c,
    0x67, 0x23, 0x22, 0x23, 0xc8, 0xe5, 0xcd, 0x48,
    0xc5, 0x21, 0x1c, 0xc8, 0xcd, 0xaf, 0xc7, 0xd1,
    0xe1, 0xc3, 0x90, 0xc7, 0xcd, 0xf9, 0xc2, 0x16,
    0x06, 0x21, 0x25, 0xc5, 0xcd, 0x6a, 0xc5, 0xcd,
    0x50, 0xc5, 0xc3, 0xc0, 0xc1, 0x45, 0x52, 0x52,
    0x4f, 0x52, 0x20, 0xcd, 0x66, 0xc4, 0xcd, 0xf9,
    0xc2, 0xcd, 0x48, 0xc5, 0x06, 0x01, 0xcd, 0xef,
    0xc7, 0xcd, 0x23, 0xc7, 0xda, 0xc0, 0xc1, 0xc2,
    0x39, 0xc5, 0xcd, 0x50, 0xc5, 0xc3, 0x39, 0xc5,
    0x21, 0x54, 0xc8, 0x3a, 0x0d, 0xc8, 0xb6, 0xc9,
    0x16, 0x08, 0x21, 0x1b, 0xc8, 0xcd, 0x6a, 0xc5,
    0xcd, 0x06, 0xc4, 0x2a, 0x25, 0xc8, 0xcd, 0xe8,
    0xc3, 0x2a, 0x23, 0xc8, 0xcd, 0xe8, 0xc3, 0xc3,
    0xf9, 0xc2, 0x7e, 0xb7, 0xc2, 0x71, 0xc5, 0x3e,
    0x20, 0xcd, 0x1f, 0xc4, 0x23, 0x15, 0xc2, 0x6a,
    0xc5, 0xc9, 0xcd, 0x1b, 0xc3, 0xca, 0x80, 0xc4,
    0xd5, 0xcd, 0x3a, 0xc3, 0xe3, 0x11, 0xa2, 0xc2,
    0xcd, 0x31, 0xc2, 0xc3, 0x22, 0xc2, 0xb7, 0xca,
    0x94, 0xc5, 0x3e, 0x20, 0x32, 0x0d, 0xc8, 0xc9,
    0x78, 0x32, 0x0b, 0xc8, 0xc9, 0x32, 0x06, 0xc8,
    0xc9, 0x32, 0x07, 0xc8, 0xc9, 0x22, 0x00, 0xc8,
    0xc9, 0x22, 0x02, 0xc8, 0xc9, 0x32, 0x22, 0xc8,
    0xc9, 0x22, 0x27, 0xc8, 0xc9, 0x32, 0x10, 0xc8,
    0xc9, 0x32, 0x11, 0xc8, 0xc9, 0xcd, 0x66, 0xc4,
    0x21, 0xc9, 0xc1, 0xcd, 0x10, 0xc3, 0xe5, 0x21,
    0x1c, 0xc8, 0xcd, 0x2e, 0xc2, 0xca, 0xd3, 0xc5,
    0x1b, 0x36, 0x00, 0x7e, 0x12, 0x13, 0x23, 0x7e,
    0x12, 0x13, 0xe1, 0xeb, 0x73, 0x23, 0x72, 0xc9,
    0xe5, 0xcd, 0x33, 0xc6, 0xc2, 0xfa, 0xc5, 0x36,
    0x01, 0x23, 0x77, 0x23, 0x77, 0x11, 0x63, 0xc8,
    0x3a, 0x54, 0xc8, 0x82, 0x57, 0xc1, 0xb7, 0xc3,
    0xb6, 0xc6, 0xe1, 0xd1, 0xaf, 0x37, 0xc9, 0x3d,
    0x37, 0xd1, 0xc9, 0xcd, 0x33, 0xc6, 0xc8, 0xb7,
    0x3c, 0x36, 0x00, 0xc8, 0x23, 0x23, 0x7e, 0x7e,
    0xcd, 0xbf, 0xc6, 0xc5, 0x21, 0x07, 0x00, 0x09,
    0xb7, 0xca, 0x2b, 0xc6, 0xe5, 0x77, 0x23, 0x36,
    0x00, 0x23, 0x73, 0x23, 0x72, 0x60, 0x69, 0xcd,
    0x7c, 0xc7, 0xe1, 0xaf, 0x77, 0x23, 0x77, 0xe1,
    0xc3, 0x7c, 0xc7, 0x21, 0x55, 0xc8, 0x1f, 0xe6,
    0x01, 0x32, 0x54, 0xc8, 0xca, 0x42, 0xc6, 0x21,
    0x5c, 0xc8, 0x7e, 0xb7, 0x37, 0xc9, 0xcd, 0x33,
    0xc6, 0xc8, 0x3c, 0xfa, 0xfc, 0xc5, 0x36, 0xff,
    0x23, 0x7e, 0xe5, 0x23, 0xcd, 0xbf, 0xc6, 0xe1,
    0xb7, 0xc2, 0x75, 0xc6, 0xd5, 0xe5, 0x23, 0xcd,
    0xa6, 0xc6, 0xcd, 0xc8, 0xc6, 0xda, 0xfa, 0xc5,
    0xe1, 0x7b, 0xb2, 0xca, 0xff, 0xc5, 0x73, 0x23,
    0x36, 0x00, 0x2b, 0x7b, 0xd1, 0x3d, 0x77, 0x23,
    0x7e, 0x34, 0x83, 0x5f, 0xd2, 0x80, 0xc6, 0x14,
    0x1a, 0xb7, 0xc9, 0xcd, 0x33, 0xc6, 0xc8, 0x3c,
    0xc8, 0x36, 0xfe, 0x23, 0x23, 0x78, 0xf5, 0xe5,
    0xcd, 0xbf, 0xc6, 0xe1, 0x7e, 0x83, 0x5f, 0xd2,
    0x9b, 0xc6, 0x14, 0xf1, 0x12, 0xb7, 0x34, 0xc0,
    0xcd, 0xa6, 0xc6, 0xc3, 0x7c, 0xc7, 0xcd, 0xbf,
    0xc6, 0xc5, 0x21, 0x06, 0x00, 0x09, 0x01, 0x00,
    0x01, 0xcd, 0xb6, 0xc6, 0xe1, 0xc9, 0x23, 0x71,
    0x23, 0x70, 0x23, 0x73, 0x23, 0x72, 0xc9, 0x23,
    0x4e, 0x23, 0x46, 0x23, 0x5e, 0x23, 0x56, 0xc9,
    0xcd, 0xde, 0xc7, 0xd5, 0x06, 0x03, 0xcd, 0xef,
    0xc7, 0xdb, 0xfb, 0xe5, 0xcd, 0x23, 0xc7, 0xe1,
    0xda, 0x06, 0xc7, 0xc2, 0xd3, 0xc6, 0xe5, 0x11,
    0x1c, 0xc8, 0xcd, 0xd2, 0xc7, 0xe1, 0xc2, 0xd3,
    0xc6, 0xd1, 0x7a, 0xb3, 0x2a, 0x23, 0xc8, 0xeb,
    0xc2, 0xf6, 0xc6, 0x2a, 0x25, 0xc8, 0xd5, 0xcd,
    0x15, 0xc7, 0xca, 0x10, 0xc7, 0xcd, 0x44, 0xc7,
    0xda, 0x06, 0xc7, 0xca, 0xf7, 0xc6, 0xaf, 0x37,
    0xc3, 0x11, 0xc7, 0x06, 0x01, 0xcd, 0xf1, 0xc7,
    0xaf, 0xd3, 0xfa, 0xd1, 0xc9, 0xaf, 0x47, 0xb2,
    0xc2, 0x20, 0xc7, 0xb3, 0xc8, 0x43, 0x5a, 0xc9,
    0x15, 0xb7, 0xc9, 0x06, 0x0a, 0xcd, 0x5d, 0xc7,
    0xd8, 0xdb, 0xfb, 0xb7, 0xc2, 0x23, 0xc7, 0x05,
    0xc2, 0x25, 0xc7, 0xcd, 0x6f, 0xc7, 0xd8, 0xfe,
    0x01, 0xda, 0x33, 0xc7, 0xc2, 0x23, 0xc7, 0x21,
    0x1c, 0xc8, 0x06, 0x10, 0x0e, 0x00, 0xcd, 0x6f,
    0xc7, 0xd8, 0x77, 0x23, 0xcd, 0xa8, 0xc7, 0x05,
    0xc2, 0x46, 0xc7, 0xcd, 0x6f, 0xc7, 0xa9, 0xc8,
    0x3a, 0x11, 0xc8, 0x3c, 0xc9, 0xdb, 0xfa, 0xe6,
    0x40, 0xc0, 0xcd, 0x1f, 0xc0, 0xca, 0x5d, 0xc7,
    0xe6, 0x7f, 0xc2, 0x5d, 0xc7, 0x37, 0xc9, 0xcd,
    0x5d, 0xc7, 0xd8, 0xdb, 0xfa, 0xe6, 0x18, 0xdb,
    0xfb, 0xc8, 0x37, 0xc9, 0xcd, 0xde, 0xc7, 0xe5,
    0xcd, 0xaf, 0xc7, 0xe1, 0x11, 0x07, 0x00, 0x19,
    0x5e, 0x23, 0x56, 0x23, 0x7e, 0x23, 0x66, 0x6f,
    0xe5, 0xcd, 0x15, 0xc7, 0xca, 0x0b, 0xc7, 0xcd,
    0xc3, 0xc7, 0xc3, 0x91, 0xc7, 0xf5, 0xdb, 0xfa,
    0xe6, 0x80, 0xca, 0x9e, 0xc7, 0xf1, 0xd3, 0xfb,
    0x91, 0x4f, 0xa9, 0x2f, 0x91, 0x4f, 0xc9, 0xcd,
    0xed, 0xc7, 0x16, 0x32, 0xaf, 0xcd, 0x9d, 0xc7,
    0x15, 0xc2, 0xb4, 0xc7, 0x3e, 0x01, 0xcd, 0x9d,
    0xc7, 0x06, 0x10, 0x0e, 0x00, 0x7e, 0xcd, 0x9d,
    0xc7, 0x05, 0x23, 0xc2, 0xc5, 0xc7, 0x79, 0xc3,
    0x9d, 0xc7, 0x06, 0x05, 0x1a, 0xbe, 0xc0, 0x05,
    0xc8, 0x23, 0x13, 0xc3, 0xd4, 0xc7, 0x3a, 0x54,
    0xc8, 0xb7, 0x3a, 0x0d, 0xc8, 0xc2, 0xea, 0xc7,
    0xc6, 0x40, 0xc6, 0x40, 0xc9, 0x06, 0x04, 0xd3,
    0xfa, 0x11, 0x00, 0x00, 0x1b, 0x7a, 0xb3, 0xc2,
    0xf4, 0xc7, 0x05, 0xc2, 0xf1, 0xc7, 0xc9, 0x00,
};

/* SOLOS 4.1 ROM */
static uint8 sol20_rom_41[SOL20_ROM_SIZE] = {
    0x00, 0xc3, 0xaf, 0xc1, 0xc3, 0xc9, 0xc1, 0xc3,
    0xe0, 0xc5, 0xc3, 0x03, 0xc6, 0xc3, 0x46, 0xc6,
    0xc3, 0x83, 0xc6, 0xc3, 0xcb, 0xc6, 0xc3, 0x7f,
    0xc7, 0x3a, 0x07, 0xc8, 0xc3, 0x3b, 0xc0, 0x3a,
    0x06, 0xc8, 0xe5, 0x21, 0x9a, 0xc2, 0xe6, 0x03,
    0x07, 0x85, 0x6f, 0xc3, 0x27, 0xc2, 0xdb, 0xfa,
    0x2f, 0xe6, 0x01, 0xc8, 0xdb, 0xfc, 0xc9, 0x00,
    0xc3, 0x01, 0xc0, 0xe5, 0x21, 0x92, 0xc2, 0xc3,
    0x26, 0xc0, 0xdb, 0xf8, 0xe6, 0x40, 0xc8, 0xdb,
    0xf9, 0xc9, 0xdb, 0xf8, 0x17, 0xd2, 0x4a, 0xc0,
    0x78, 0xd3, 0xf9, 0xc9, 0xe5, 0xd5, 0xc5, 0x3a,
    0x0c, 0xc8, 0xb7, 0xc2, 0x5f, 0xc1, 0x78, 0xe6,
    0x7f, 0x47, 0xca, 0x7c, 0xc0, 0x21, 0x73, 0xc2,
    0xcd, 0x82, 0xc0, 0xcd, 0x1c, 0xc1, 0x7e, 0xf6,
    0x80, 0x77, 0x2a, 0x0a, 0xc8, 0x2c, 0xaf, 0x2b,
    0xbc, 0xc2, 0x77, 0xc0, 0xc1, 0xd1, 0xe1, 0xc9,
    0x23, 0x23, 0x7e, 0xb7, 0xca, 0x94, 0xc0, 0xb8,
    0x23, 0xc2, 0x80, 0xc0, 0xe5, 0xcd, 0x36, 0xc1,
    0xe3, 0xc3, 0x27, 0xc2, 0x78, 0xfe, 0x7f, 0xc8,
    0xcd, 0x1c, 0xc1, 0x70, 0x3a, 0x08, 0xc8, 0xfe,
    0x3f, 0xda, 0xc1, 0xc0, 0x3a, 0x09, 0xc8, 0xfe,
    0x0f, 0xc2, 0xc1, 0xc0, 0xaf, 0x32, 0x08, 0xc8,
    0x4f, 0xcd, 0x23, 0xc1, 0xaf, 0xcd, 0xfa, 0xc0,
    0x3a, 0x0a, 0xc8, 0x3c, 0xe6, 0x0f, 0xc3, 0xee,
    0xc0, 0x3a, 0x08, 0xc8, 0x3c, 0xe6, 0x3f, 0x32,
    0x08, 0xc8, 0xc0, 0x3a, 0x09, 0xc8, 0x3c, 0xe6,
    0x0f, 0x32, 0x09, 0xc8, 0xc9, 0x21, 0x00, 0xcc,
    0x36, 0xa0, 0x23, 0x36, 0x20, 0x23, 0x7c, 0xfe,
    0xd0, 0xda, 0xdb, 0xc0, 0x37, 0x3e, 0x00, 0x32,
    0x09, 0xc8, 0x32, 0x08, 0xc8, 0xd0, 0xd3, 0xfe,
    0x32, 0x0a, 0xc8, 0xc9, 0xcd, 0x1c, 0xc1, 0x3a,
    0x08, 0xc8, 0xfe, 0x40, 0xd0, 0x36, 0x20, 0x23,
    0x3c, 0xc3, 0xfa, 0xc0, 0x3a, 0x09, 0xc8, 0x3d,
    0xc3, 0xcf, 0xc0, 0x3a, 0x08, 0xc8, 0x3d, 0xe6,
    0x3f, 0x32, 0x08, 0xc8, 0xc9, 0x3a, 0x08, 0xc8,
    0x3c, 0xc3, 0x0f, 0xc1, 0x3a, 0x08, 0xc8, 0x4f,
    0x3a, 0x09, 0xc8, 0x6f, 0x3a, 0x0a, 0xc8, 0x85,
    0x0f, 0x0f, 0x6f, 0xe6, 0x03, 0xc6, 0xcc, 0x67,
    0x7d, 0xe6, 0xc0, 0x81, 0x6f, 0xc9, 0xcd, 0x1c,
    0xc1, 0x7e, 0xe6, 0x7f, 0x77, 0xc9, 0xcd, 0x0b,
    0xc1, 0xcd, 0x1c, 0xc1, 0x36, 0x20, 0xc9, 0xcd,
    0xf4, 0xc0, 0xc3, 0x0f, 0xc1, 0x3a, 0x09, 0xc8,
    0x3c, 0xe6, 0x0f, 0xc2, 0xd1, 0xc0, 0xc3, 0xb0,
    0xc0, 0x3e, 0xff, 0x32, 0x0c, 0xc8, 0xc9, 0xcd,
    0x36, 0xc1, 0xcd, 0x68, 0xc1, 0xc3, 0x6b, 0xc0,
    0x3a, 0x0c, 0xc8, 0xfe, 0xff, 0xca, 0x90, 0xc1,
    0x21, 0x0c, 0xc8, 0x36, 0x00, 0xfe, 0x02, 0xda,
    0x88, 0xc1, 0xca, 0x8c, 0xc1, 0xfe, 0x08, 0xca,
    0x98, 0xc5, 0xfe, 0x09, 0xda, 0x98, 0xc0, 0xc0,
    0x78, 0xc3, 0x0f, 0xc1, 0x78, 0xc3, 0xcf, 0xc0,
    0x78, 0xfe, 0x03, 0xca, 0xa6, 0xc1, 0xfe, 0x04,
    0xc2, 0xa2, 0xc1, 0x44, 0x4d, 0xe1, 0xd1, 0xc5,
    0xe5, 0xaf, 0x32, 0x0c, 0xc8, 0xc9, 0x21, 0x08,
    0xc8, 0x46, 0x23, 0x4e, 0xc3, 0x9d, 0xc1, 0xaf,
    0x4f, 0x21, 0x00, 0xc8, 0x77, 0x23, 0x0c, 0xc2,
    0xb4, 0xc1, 0x31, 0xff, 0xcb, 0xcd, 0xd5, 0xc0,
    0xaf, 0xd3, 0xfa, 0x32, 0x07, 0xc8, 0x32, 0x06,
    0xc8, 0x31, 0xff, 0xcb, 0x3a, 0x07, 0xc8, 0xf5,
    0xaf, 0x32, 0x07, 0xc8, 0xcd, 0xf1, 0xc2, 0xcd,
    0xe4, 0xc1, 0xf1, 0x32, 0x07, 0xc8, 0xcd, 0x05,
    0xc2, 0xc3, 0xc9, 0xc1, 0xcd, 0x1f, 0xc0, 0xca,
    0xe4, 0xc1, 0xe6, 0x7f, 0xca, 0xc0, 0xc1, 0x47,
    0xfe, 0x0d, 0xca, 0xf4, 0xc0, 0xfe, 0x0a, 0xc8,
    0xfe, 0x7f, 0xc2, 0xff, 0xc1, 0x06, 0x5f, 0xcd,
    0x19, 0xc0, 0xc3, 0xe4, 0xc1, 0xcd, 0x36, 0xc1,
    0x0e, 0x01, 0xcd, 0x20, 0xc1, 0xeb, 0x21, 0x00,
    0xc0, 0xe5, 0xcd, 0x2e, 0xc3, 0xca, 0x80, 0xc4,
    0xeb, 0x11, 0x4a, 0xc2, 0xcd, 0x31, 0xc2, 0xcc,
    0x2e, 0xc2, 0xca, 0x81, 0xc4, 0x13, 0xeb, 0x7e,
    0x23, 0x66, 0x6f, 0xe3, 0x7d, 0xc9, 0x11, 0x3c,
    0xc8, 0x1a, 0xb7, 0xc8, 0xe5, 0xbe, 0x13, 0xc2,
    0x43, 0xc2, 0x23, 0x1a, 0xbe, 0xc2, 0x43, 0xc2,
    0xe1, 0xb7, 0xc9, 0x13, 0x13, 0x13, 0xe1, 0xc3,
    0x31, 0xc2, 0x54, 0x45, 0x67, 0xc3, 0x44, 0x55,
    0xbf, 0xc3, 0x45, 0x4e, 0x23, 0xc4, 0x45, 0x58,
    0x5e, 0xc4, 0x47, 0x45, 0xa7, 0xc4, 0x53, 0x41,
    0xe6, 0xc4, 0x58, 0x45, 0xa6, 0xc4, 0x43, 0x41,
    0x2b, 0xc5, 0x53, 0x45, 0x7a, 0xc5, 0x43, 0x55,
    0xbd, 0xc5, 0x00, 0x0b, 0xd5, 0xc0, 0x17, 0x04,
    0xc1, 0x1a, 0xcb, 0xc0, 0x01, 0x0b, 0xc1, 0x13,
    0x15, 0xc1, 0x0e, 0xe5, 0xc0, 0x0d, 0x47, 0xc1,
    0x0a, 0x4d, 0xc1, 0x5f, 0x3e, 0xc1, 0x1b, 0x59,
    0xc1, 0x00, 0x54, 0xc0, 0x4a, 0xc0, 0xe6, 0xc2,
    0xd2, 0xc2, 0x2e, 0xc0, 0x42, 0xc0, 0xdd, 0xc2,
    0xcb, 0xc2, 0x54, 0x41, 0x8e, 0xc5, 0x53, 0x3d,
    0x99, 0xc5, 0x49, 0x3d, 0x9d, 0xc5, 0x4f, 0x3d,
    0xa1, 0xc5, 0x4e, 0x3d, 0xb5, 0xc5, 0x43, 0x49,
    0xa5, 0xc5, 0x43, 0x4f, 0xa9, 0xc5, 0x58, 0x45,
    0xb1, 0xc5, 0x54, 0x59, 0xad, 0xc5, 0x43, 0x52,
    0xb9, 0xc5, 0x00, 0xe5, 0x2a, 0x00, 0xc8, 0xc3,
    0xd6, 0xc2, 0xe5, 0x2a, 0x02, 0xc8, 0x7d, 0xb4,
    0xca, 0xc0, 0xc1, 0xe3, 0xc9, 0xdb, 0xfa, 0x2f,
    0xe6, 0x02, 0xc8, 0xdb, 0xfd, 0xc9, 0xdb, 0xfa,
    0xe6, 0x04, 0xc2, 0xe6, 0xc2, 0x78, 0xd3, 0xfd,
    0xc9, 0xcd, 0xf9, 0xc2, 0x06, 0x3e, 0xc3, 0x19,
    0xc0, 0x06, 0x0a, 0xcd, 0x19, 0xc0, 0x06, 0x0d,
    0xcd, 0x19, 0xc0, 0x3a, 0x10, 0xc8, 0x4f, 0x0d,
    0xf8, 0xaf, 0xcd, 0x1f, 0xc4, 0xc3, 0x07, 0xc3,
    0xcd, 0x1b, 0xc3, 0x3e, 0x01, 0xc8, 0xcd, 0x40,
    0xc3, 0x7d, 0xc9, 0x0e, 0x0c, 0x1a, 0xfe, 0x20,
    0xca, 0x2e, 0xc3, 0x13, 0xfe, 0x3d, 0xca, 0x2e,
    0xc3, 0x0d, 0xc2, 0x1d, 0xc3, 0xc9, 0x0e, 0x0a,
    0x1a, 0xfe, 0x20, 0xc0, 0x13, 0x0d, 0xc8, 0xc3,
    0x30, 0xc3, 0xcd, 0x1b, 0xc3, 0xca, 0x80, 0xc4,
    0x21, 0x00, 0x00, 0x1a, 0xfe, 0x20, 0xc8, 0xfe,
    0x2f, 0xc8, 0xfe, 0x3a, 0xc8, 0x29, 0x29, 0x29,
    0x29, 0xcd, 0x5d, 0xc3, 0xd2, 0x80, 0xc4, 0x85,
    0x6f, 0x13, 0xc3, 0x43, 0xc3, 0xd6, 0x30, 0xfe,
    0x0a, 0xd8, 0xd6, 0x07, 0xfe, 0x10, 0xc9, 0xcd,
    0x10, 0xc3, 0x32, 0x06, 0xc8, 0xcd, 0x10, 0xc3,
    0x32, 0x07, 0xc8, 0xcd, 0x2e, 0xc0, 0xca, 0x8b,
    0xc3, 0x47, 0xfe, 0x80, 0xca, 0xc0, 0xc1, 0xda,
    0x88, 0xc3, 0xcd, 0x54, 0xc0, 0xc3, 0x8b, 0xc3,
    0xcd, 0x19, 0xc0, 0xcd, 0x1f, 0xc0, 0xca, 0x73,
    0xc3, 0xe6, 0x7f, 0xca, 0x73, 0xc3, 0x47, 0xfe,
    0x1b, 0xd2, 0xb9, 0xc3, 0xfe, 0x0d, 0xca, 0xb9,
    0xc3, 0xfe, 0x0a, 0xca, 0xb9, 0xc3, 0x3a, 0x0c,
    0xc8, 0xb7, 0xc2, 0xb9, 0xc3, 0xc5, 0x06, 0x1b,
    0xcd, 0x54, 0xc0, 0x06, 0x07, 0xcd, 0x54, 0xc0,
    0xc1, 0xcd, 0x54, 0xc0, 0xc3, 0x73, 0xc3, 0xcd,
    0x3a, 0xc3, 0xe5, 0xcd, 0x10, 0xc3, 0xd1, 0xeb,
    0xcd, 0xf9, 0xc2, 0xcd, 0xe8, 0xc3, 0xcd, 0x06,
    0xc4, 0x0e, 0x10, 0x7e, 0xc5, 0xcd, 0xed, 0xc3,
    0x7d, 0x93, 0x7c, 0x9a, 0xd2, 0xc9, 0xc1, 0xc1,
    0x23, 0x0d, 0xc2, 0xd3, 0xc3, 0xc3, 0xc8, 0xc3,
    0x7c, 0xcd, 0x0b, 0xc4, 0x7d, 0xcd, 0x0b, 0xc4,
    0xcd, 0x1f, 0xc0, 0xca, 0x06, 0xc4, 0xe6, 0x7f,
    0xca, 0xc9, 0xc1, 0xfe, 0x20, 0xc2, 0x06, 0xc4,
    0xcd, 0x1f, 0xc0, 0xca, 0x00, 0xc4, 0x06, 0x20,
    0xc3, 0x19, 0xc0, 0x4f, 0x0f, 0x0f, 0x0f, 0x0f,
    0xcd, 0x14, 0xc4, 0x79, 0xe6, 0x0f, 0xc6, 0x30,
    0xfe, 0x3a, 0xda, 0x1f, 0xc4, 0xc6, 0x07, 0x47,
    0xc3, 0x19, 0xc0, 0xcd, 0x3a, 0xc3, 0xe5, 0xaf,
    0x32, 0x07, 0xc8, 0xcd, 0xf9, 0xc2, 0x06, 0x3a,
    0xcd, 0xff, 0xc1, 0xcd, 0x36, 0xc1, 0x0e, 0x01,
    0xcd, 0x20, 0xc1, 0xeb, 0x0e, 0x03, 0xcd, 0x30,
    0xc3, 0xca, 0x2b, 0xc4, 0xfe, 0x2f, 0xca, 0xc0,
    0xc1, 0xcd, 0x40, 0xc3, 0xfe, 0x3a, 0xca, 0x59,
    0xc4, 0x7d, 0xe1, 0x77, 0x23, 0xe5, 0xc3, 0x3c,
    0xc4, 0xe3, 0x13, 0xc3, 0x3c, 0xc4, 0xcd, 0x3a,
    0xc3, 0xe5, 0x21, 0x00, 0xc0, 0xc9, 0x21, 0x1c,
    0xc8, 0xcd, 0x1b, 0xc3, 0x06, 0x06, 0x1a, 0xfe,
    0x20, 0xca, 0x86, 0xc4, 0xfe, 0x2f, 0xca, 0x86,
    0xc4, 0x77, 0x13, 0x23, 0x05, 0xc2, 0x6e, 0xc4,
    0xeb, 0x36, 0x3f, 0xc3, 0xc0, 0xc1, 0x36, 0x00,
    0x23, 0x05, 0xc2, 0x86, 0xc4, 0xfe, 0x2f, 0x3e,
    0x01, 0xc2, 0x9a, 0xc4, 0x13, 0xcd, 0x2e, 0xc3,
    0xd6, 0x30, 0xe6, 0x01, 0x3e, 0x80, 0xc2, 0xa2,
    0xc4, 0x1f, 0x32, 0x54, 0xc8, 0xc9, 0x3e, 0xaf,
    0xf5, 0x21, 0x2c, 0xc8, 0xcd, 0x69, 0xc4, 0x21,
    0x00, 0x00, 0xcd, 0x10, 0xc3, 0xeb, 0x21, 0x2c,
    0xc8, 0x7e, 0xb7, 0xc2, 0xc1, 0xc4, 0x21, 0x1c,
    0xc8, 0xe5, 0xcd, 0x48, 0xc5, 0xe1, 0xcd, 0xcb,
    0xc6, 0xda, 0x14, 0xc5, 0xcd, 0x50, 0xc5, 0xf1,
    0xb7, 0xc8, 0x3a, 0x22, 0xc8, 0xb7, 0xfa, 0x14,
    0xc5, 0x3a, 0x21, 0xc8, 0xb7, 0xc2, 0x14, 0xc5,
    0x2a, 0x27, 0xc8, 0xc3, 0x61, 0xc4, 0xcd, 0x66,
    0xc4, 0xcd, 0x3a, 0xc3, 0xe5, 0xcd, 0x3a, 0xc3,
    0xe3, 0xe5, 0xcd, 0x10, 0xc3, 0x22, 0x25, 0xc8,
    0xe1, 0xd1, 0xe5, 0x7b, 0x95, 0x6f, 0x7a, 0x9c,
    0x67, 0x23, 0x22, 0x23, 0xc8, 0xe5, 0xcd, 0x48,
    0xc5, 0x21, 0x1c, 0xc8, 0xcd, 0xaf, 0xc7, 0xd1,
    0xe1, 0xc3, 0x90, 0xc7, 0xcd, 0xf9, 0xc2, 0x16,
    0x06, 0x21, 0x25, 0xc5, 0xcd, 0x6a, 0xc5, 0xcd,
    0x50, 0xc5, 0xc3, 0xc0, 0xc1, 0x45, 0x52, 0x52,
    0x4f, 0x52, 0x20, 0xcd, 0x66, 0xc4, 0xcd, 0xf9,
    0xc2, 0xcd, 0x48, 0xc5, 0x06, 0x01, 0xcd, 0xef,
    0xc7, 0xcd, 0x23, 0xc7, 0xda, 0xc0, 0xc1, 0xc2,
    0x39, 0xc5, 0xcd, 0x50, 0xc5, 0xc3, 0x39, 0xc5,
    0x21, 0x54, 0xc8, 0x3a, 0x0d, 0xc8, 0xb6, 0xc9,
    0x16, 0x08, 0x21, 0x1b, 0xc8, 0xcd, 0x6a, 0xc5,
    0xcd, 0x06, 0xc4, 0x2a, 0x25, 0xc8, 0xcd, 0xe8,
    0xc3, 0x2a, 0x23, 0xc8, 0xcd, 0xe8, 0xc3, 0xc3,
    0xf9, 0xc2, 0x7e, 0xb7, 0xc2, 0x71, 0xc5, 0x3e,
    0x20, 0xcd, 0x1f, 0xc4, 0x23, 0x15, 0xc2, 0x6a,
    0xc5, 0xc9, 0xcd, 0x1b, 0xc3, 0xca, 0x80, 0xc4,
    0xd5, 0xcd, 0x3a, 0xc3, 0xe3, 0x11, 0xa2, 0xc2,
    0xcd, 0x31, 0xc2, 0xc3, 0x22, 0xc2, 0xb7, 0xca,
    0x94, 0xc5, 0x3e, 0x20, 0x32, 0x0d, 0xc8, 0xc9,
    0x78, 0x32, 0x0b, 0xc8, 0xc9, 0x32, 0x06, 0xc8,
    0xc9, 0x32, 0x07, 0xc8, 0xc9, 0x22, 0x00, 0xc8,
    0xc9, 0x22, 0x02, 0xc8, 0xc9, 0x32, 0x22, 0xc8,
    0xc9, 0x22, 0x27, 0xc8, 0xc9, 0x32, 0x10, 0xc8,
    0xc9, 0x32, 0x11, 0xc8, 0xc9, 0xcd, 0x66, 0xc4,
    0x21, 0xc9, 0xc1, 0xcd, 0x10, 0xc3, 0xe5, 0x21,
    0x1c, 0xc8, 0xcd, 0x2e, 0xc2, 0xca, 0xd3, 0xc5,
    0x1b, 0x36, 0x00, 0x7e, 0x12, 0x13, 0x23, 0x7e,
    0x12, 0x13, 0xe1, 0xeb, 0x73, 0x23, 0x72, 0xc9,
    0xe5, 0xcd, 0x33, 0xc6, 0xc2, 0xfa, 0xc5, 0x36,
    0x01, 0x23, 0x77, 0x23, 0x77, 0x11, 0x63, 0xc8,
    0x3a, 0x54, 0xc8, 0x82, 0x57, 0xc1, 0xb7, 0xc3,
    0xb6, 0xc6, 0xe1, 0xd1, 0xaf, 0x37, 0xc9, 0x3d,
    0x37, 0xd1, 0xc9, 0xcd, 0x33, 0xc6, 0xc8, 0xb7,
    0x3c, 0x36, 0x00, 0xc8, 0x23, 0x23, 0x7e, 0x7e,
    0xcd, 0xbf, 0xc6, 0xc5, 0x21, 0x07, 0x00, 0x09,
    0xb7, 0xca, 0x2b, 0xc6, 0xe5, 0x77, 0x23, 0x36,
    0x00, 0x23, 0x73, 0x23, 0x72, 0x60, 0x69, 0xcd,
    0x7c, 0xc7, 0xe1, 0xaf, 0x77, 0x23, 0x77, 0xe1,
    0xc3, 0x7c, 0xc7, 0x21, 0x55, 0xc8, 0x1f, 0xe6,
    0x01, 0x32, 0x54, 0xc8, 0xca, 0x42, 0xc6, 0x21,
    0x5c, 0xc8, 0x7e, 0xb7, 0x37, 0xc9, 0xcd, 0x33,
    0xc6, 0xc8, 0x3c, 0xfa, 0xfc, 0xc5, 0x36, 0xff,
    0x23, 0x7e, 0xe5, 0x23, 0xcd, 0xbf, 0xc6, 0xe1,
    0xb7, 0xc2, 0x75, 0xc6, 0xd5, 0xe5, 0x23, 0xcd,
    0xa6, 0xc6, 0xcd, 0xc8, 0xc6, 0xda, 0xfa, 0xc5,
    0xe1, 0x7b, 0xb2, 0xca, 0xff, 0xc5, 0x73, 0x23,
    0x36, 0x00, 0x2b, 0x7b, 0xd1, 0x3d, 0x77, 0x23,
    0x7e, 0x34, 0x83, 0x5f, 0xd2, 0x80, 0xc6, 0x14,
    0x1a, 0xb7, 0xc9, 0xcd, 0x33, 0xc6, 0xc8, 0x3c,
    0xc8, 0x36, 0xfe, 0x23, 0x23, 0x78, 0xf5, 0xe5,
    0xcd, 0xbf, 0xc6, 0xe1, 0x7e, 0x83, 0x5f, 0xd2,
    0x9b, 0xc6, 0x14, 0xf1, 0x12, 0xb7, 0x34, 0xc0,
    0xcd, 0xa6, 0xc6, 0xc3, 0x7c, 0xc7, 0xcd, 0xbf,
    0xc6, 0xc5, 0x21, 0x06, 0x00, 0x09, 0x01, 0x00,
    0x01, 0xcd, 0xb6, 0xc6, 0xe1, 0xc9, 0x23, 0x71,
    0x23, 0x70, 0x23, 0x73, 0x23, 0x72, 0xc9, 0x23,
    0x4e, 0x23, 0x46, 0x23, 0x5e, 0x23, 0x56, 0xc9,
    0xcd, 0xde, 0xc7, 0xd5, 0x06, 0x03, 0xcd, 0xef,
    0xc7, 0xdb, 0xfb, 0xe5, 0xcd, 0x23, 0xc7, 0xe1,
    0xda, 0x06, 0xc7, 0xc2, 0xd3, 0xc6, 0xe5, 0x11,
    0x1c, 0xc8, 0xcd, 0xd2, 0xc7, 0xe1, 0xc2, 0xd3,
    0xc6, 0xd1, 0x7a, 0xb3, 0x2a, 0x23, 0xc8, 0xeb,
    0xc2, 0xf6, 0xc6, 0x2a, 0x25, 0xc8, 0xd5, 0xcd,
    0x15, 0xc7, 0xca, 0x10, 0xc7, 0xcd, 0x44, 0xc7,
    0xda, 0x06, 0xc7, 0xca, 0xf7, 0xc6, 0xaf, 0x37,
    0xc3, 0x11, 0xc7, 0x06, 0x01, 0xcd, 0xf1, 0xc7,
    0xaf, 0xd3, 0xfa, 0xd1, 0xc9, 0xaf, 0x47, 0xb2,
    0xc2, 0x20, 0xc7, 0xb3, 0xc8, 0x43, 0x5a, 0xc9,
    0x15, 0xb7, 0xc9, 0x06, 0x0a, 0xcd, 0x5d, 0xc7,
    0xd8, 0xdb, 0xfb, 0xb7, 0xc2, 0x23, 0xc7, 0x05,
    0xc2, 0x25, 0xc7, 0xcd, 0x6f, 0xc7, 0xd8, 0xfe,
    0x01, 0xda, 0x33, 0xc7, 0xc2, 0x23, 0xc7, 0x21,
    0x1c, 0xc8, 0x06, 0x10, 0x0e, 0x00, 0xcd, 0x6f,
    0xc7, 0xd8, 0x77, 0x23, 0xcd, 0xa8, 0xc7, 0x05,
    0xc2, 0x46, 0xc7, 0xcd, 0x6f, 0xc7, 0xa9, 0xc8,
    0x3a, 0x11, 0xc8, 0x3c, 0xc9, 0xdb, 0xfa, 0xe6,
    0x40, 0xc0, 0xcd, 0x1f, 0xc0, 0xca, 0x5d, 0xc7,
    0xe6, 0x7f, 0xc2, 0x5d, 0xc7, 0x37, 0xc9, 0xcd,
    0x5d, 0xc7, 0xd8, 0xdb, 0xfa, 0xe6, 0x18, 0xdb,
    0xfb, 0xc8, 0x37, 0xc9, 0xcd, 0xde, 0xc7, 0xe5,
    0xcd, 0xaf, 0xc7, 0xe1, 0x11, 0x07, 0x00, 0x19,
    0x5e, 0x23, 0x56, 0x23, 0x7e, 0x23, 0x66, 0x6f,
    0xe5, 0xcd, 0x15, 0xc7, 0xca, 0x0b, 0xc7, 0xcd,
    0xc3, 0xc7, 0xc3, 0x91, 0xc7, 0xf5, 0xdb, 0xfa,
    0xe6, 0x80, 0xca, 0x9e, 0xc7, 0xf1, 0xd3, 0xfb,
    0x91, 0x4f, 0xa9, 0x2f, 0x91, 0x4f, 0xc9, 0xcd,
    0xed, 0xc7, 0x16, 0x32, 0xaf, 0xcd, 0x9d, 0xc7,
    0x15, 0xc2, 0xb4, 0xc7, 0x3e, 0x01, 0xcd, 0x9d,
    0xc7, 0x06, 0x10, 0x0e, 0x00, 0x7e, 0xcd, 0x9d,
    0xc7, 0x05, 0x23, 0xc2, 0xc5, 0xc7, 0x79, 0xc3,
    0x9d, 0xc7, 0x06, 0x05, 0x1a, 0xbe, 0xc0, 0x05,
    0xc8, 0x23, 0x13, 0xc3, 0xd4, 0xc7, 0x3a, 0x54,
    0xc8, 0xb7, 0x3a, 0x0d, 0xc8, 0xc2, 0xea, 0xc7,
    0xc6, 0x40, 0xc6, 0x40, 0xc9, 0x06, 0x04, 0xd3,
    0xfa, 0x11, 0x00, 0x00, 0x1b, 0x7a, 0xb3, 0xc2,
    0xf4, 0xc7, 0x05, 0xc2, 0xf1, 0xc7, 0xc9, 0x00,
};

static uint8 *sol20_rom = sol20_rom_41;    /* Default 4.1 ROM */

/*********************/
/* SOL20 Definitions */
/*********************/

/*
**  PORT ASSIGNMENTS
*/
#define SOL20_SERST   0xf8    /* SERIAL STATUS PORT              */
#define SOL20_SDATA   0xf9    /* SERIAL DATA                     */
#define SOL20_STAPT   0xfa    /* STATUS PORT GENERAL             */
#define SOL20_TDATA   0xfb    /* TAPE DATA                       */
#define SOL20_KDATA   0xfc    /* KEYBOARD DATA                   */
#define SOL20_PDATA   0xfd    /* PARALLEL DATA                   */
/*
**  BIT ASSIGNMENT MASKS
*/
#define SOL20_SCD     0x01    /* SERIAL CARRIER DETECT           */
#define SOL20_SDSR    0x02    /* SERIAL DATA SET READY           */
#define SOL20_SPE     0x04    /* SERIAL PARITY ERROR             */
#define SOL20_SFE     0x08    /* SERIAL FRAMING ERROR            */
#define SOL20_SOE     0x10    /* SERIAL OVERRUN ERROR            */
#define SOL20_SCTS    0x20    /* SERIAL CLEAR TO SEND            */
#define SOL20_SDR     0x40    /* SERIAL DATA READY               */
#define SOL20_STBE    0x80    /* SERIAL TRANSMITTER BUFFER EMPTY */

#define SOL20_KDR     0x01    /* KEYBOARD DATA READY             */
#define SOL20_PDR     0x02    /* PARALLEL DATA READY             */
#define SOL20_PXDR    0x04    /* PARALLEL DEVICE READY           */
#define SOL20_TFE     0x08    /* TAPE FRAMING ERROR              */
#define SOL20_TOE     0x10    /* TAPE OVERRUN ERROR              */
#define SOL20_TDR     0x40    /* TAPE DATA READY                 */
#define SOL20_TTBE    0x80    /* TAPE TRANSMITTER BUFFER EMPTY   */

#define SOL20_TAPE1   0x80    /* 1=TURN TAPE ONE ON              */
#define SOL20_TAPE2   0x40    /* 1=TURN TAPE TWO ON              */

/****************/
/* SOL20 Device */
/****************/

#define SOL20_NAME  "Processor Technology Sol-20"
#define SOL20_SNAME "SOL20"

/*
** SOL20 Registers and Interface Controls
*/

typedef struct {
    uint32    rom_base;     /* Memory Base Address */
    uint32    rom_size;     /* Memory Address space requirement */
    uint32    io_base;      /* I/O Base Address */
    uint32    io_size;      /* I/O Address Space requirement */
} SOL20_CTX;

static SOL20_CTX sol20_ctx = {
    SOL20_ROM_BASE, SOL20_ROM_SIZE, SOL20_STAPT, 1
};

static UNIT sol20_unit[] = {
    { UDATA (NULL, 0, 0) }
};

static REG sol20_reg[] = {
    { NULL }
};

static MTAB sol20_mod[] = {
    { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ROM", "ROM",
        &set_membase, &show_membase, NULL, "ROM address"},
    { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "VER", "VER={13,13C,41}",
        &sol20_set_rom, &sol20_show_rom, NULL, "ROM version"},
    { MTAB_XTD|MTAB_VDV, 0, "PORT", "PORT",
        NULL, sol20_show_ports, NULL, "I/O port address" },
    { 0 }
};

DEVICE sol20_dev = {
    SOL20_SNAME,                          /* name */
    sol20_unit,                           /* unit */
    sol20_reg,                            /* registers */
    sol20_mod,                            /* modifiers */
    1,                                    /* # units */
    10,                                   /* address radix */
    31,                                   /* address width */
    1,                                    /* addr increment */
    8,                                    /* data radix */
    8,                                    /* data width */
    NULL,                                 /* examine routine */
    NULL,                                 /* deposit routine */
    &sol20_reset,                         /* reset routine */
    &sol20_boot,                          /* boot routine */
    NULL,                                 /* attach routine */
    NULL,                                 /* detach routine */
    &sol20_ctx,                           /* context */
    (DEV_DISABLE | DEV_DIS | DEV_DEBUG),  /* flags */
    ERROR_MSG,                            /* debug control */
    sol20_dt,                             /* debug flags */
    NULL,                                 /* mem size routine */
    NULL,                                 /* logical name */
    &sol20_help,                          /* help */
    NULL,                                 /* attach help */
    NULL,                                 /* context for help */
    &sol20_description                    /* description */
};

/****************/
/* GENERIC PORT */
/****************/

typedef struct {
    PNP_INFO  pnp;          /* Must be first */
    int32     conn;         /* Connected Status */
    uint16    baud;         /* Baud rate */
    uint8     status;       /* Status Byte */
    uint8     rdr;          /* Receive Data Ready */
    uint8     rxd;          /* Receive Data Buffer */
    uint8     txd;          /* Transmit Data Buffer */
    uint8     tbe;          /* Transmit Buffer Empty */
    TMLN     *tmln;         /* TMLN pointer */
    TMXR     *tmxr;         /* TMXR pointer */
} SOL20_PORT_CTX;

/**************************/
/* SOL20K Keyboard Device */
/**************************/

#define SOL20K_NAME  "Sol-20 Keyboard"
#define SOL20K_SNAME "SOL20K"

static TMLN sol20k_tmln[1] = { /* line descriptors */
    { 0 }
};

static TMXR sol20k_tmxr = { /* multiplexer descriptor */
    1,                      /* number of terminal lines */
    0,                      /* listening port (reserved) */
    0,                      /* master socket  (reserved) */
    sol20k_tmln,            /* line descriptor array */
    NULL,                   /* line connection order */
    NULL                    /* multiplexer device (derived internally) */
};

static SOL20_PORT_CTX sol20k_ctx = {
    {0, 0, SOL20_KDATA, 1}, 0, 9600, 0,
    0, 0, 0, 0, sol20k_tmln, &sol20k_tmxr
};

static REG sol20k_reg[] = {
    { HRDATAD (RDR, sol20k_ctx.rdr, 1, "Keyboard port RX data ready"), },
    { HRDATAD (RXD, sol20k_ctx.rxd, 8, "Keyboard port RX data register"), },
    { NULL }
};

static UNIT sol20k_unit[] = {
    { UDATA (&sol20_svc, UNIT_ATTABLE, 0), 500 }
};

static MTAB sol20k_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "PORT", "PORT",
        NULL, sol20_show_ports, NULL, "Show serial I/O ports" },
    { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD",
        &sol20_set_baud, &sol20_show_baud, NULL, "Set baud rate (default=9600)" },
    { 0 }
};

DEVICE sol20k_dev = {
    SOL20K_SNAME,                                   /* name */
    sol20k_unit,                                    /* unit */
    sol20k_reg,                                     /* registers */
    sol20k_mod,                                     /* modifiers */
    1,                                              /* # units */
    10,                                             /* address radix */
    31,                                             /* address width */
    1,                                              /* addr increment */
    8,                                              /* data radix */
    8,                                              /* data width */
    NULL,                                           /* examine routine */
    NULL,                                           /* deposit routine */
    &sol20_kb_reset,                                /* reset routine */
    NULL,                                           /* boot routine */
    &sol20_attach_mux,                              /* attach routine */
    &sol20_detach_mux,                              /* detach routine */
    &sol20k_ctx,                                    /* context */
    (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX),  /* flags */
    ERROR_MSG,                                      /* debug control */
    sol20_dt,                                       /* debug flags */
    NULL,                                           /* mem size routine */
    NULL,                                           /* logical name */
    NULL,                                           /* help */
    NULL,                                           /* attach help */
    NULL,                                           /* context for help */
    &sol20k_description                             /* description */
};

/**********************/
/* SOL20T Tape Device */
/**********************/

#define SOL20T_NAME  "Sol-20 Tape Port"
#define SOL20T_SNAME "SOL20T"

#define SOL20_TAPE_NORMAL     4000
#define SOL20_TAPE_FAST       100

static TMLN sol20t_tmln[1] = { /* line descriptors */
    { 0 }
};

static TMXR sol20t_tmxr = { /* multiplexer descriptor */
    1,                      /* number of terminal lines */
    0,                      /* listening port (reserved) */
    0,                      /* master socket  (reserved) */
    sol20t_tmln,            /* line descriptor array */
    NULL,                   /* line connection order */
    NULL                    /* multiplexer device (derived internally) */
};

static SOL20_PORT_CTX sol20t_ctx = {
    {0, 0, SOL20_TDATA, 1}, 0, 9600, 0,
    0, 0, 0, 0, sol20t_tmln, &sol20t_tmxr
};

static REG sol20t_reg[] = {
    { HRDATAD (STATUS, sol20t_ctx.status, 8, "Tape port status register"), },
    { HRDATAD (TBE, sol20t_ctx.tbe,       1, "Tape port TX buffer empty register"), },
    { HRDATAD (TXD, sol20t_ctx.txd,       8, "Tape port TX data register"), },
    { HRDATAD (RDR, sol20t_ctx.rdr,       1, "Tape port RX data ready"), },
    { HRDATAD (RXD, sol20t_ctx.rxd,       8, "Tape port RX data register"), },
    { NULL }
};

static UNIT sol20t_unit[] = {
    { UDATA (&sol20_svc, UNIT_ATTABLE | UNIT_ROABLE, 0), SOL20_TAPE_NORMAL }
};

static MTAB sol20t_mod[] = {
    { MTAB_XTD|MTAB_VDV,    0,                  "PORT",     "PORT",
        NULL, sol20_show_ports, NULL, "Show serial I/O ports" },
    { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "TAPE", "TAPE",
        &sol20_set_tape, &sol20_show_tape, NULL, "Tape Speed"},
    { 0 }
};

DEVICE sol20t_dev = {
    SOL20T_SNAME,                         /* name */
    sol20t_unit,                          /* unit */
    sol20t_reg,                           /* registers */
    sol20t_mod,                           /* modifiers */
    1,                                    /* # units */
    10,                                   /* address radix */
    31,                                   /* address width */
    1,                                    /* addr increment */
    8,                                    /* data radix */
    8,                                    /* data width */
    NULL,                                 /* examine routine */
    NULL,                                 /* deposit routine */
    &sol20_port_reset,                    /* reset routine */
    NULL,                                 /* boot routine */
    &sol20_attach_tape,                   /* attach routine */
    &sol20_detach_tape,                   /* detach routine */
    &sol20t_ctx,                          /* context */
    (DEV_DISABLE | DEV_DIS | DEV_DEBUG),  /* flags */
    ERROR_MSG,                            /* debug control */
    sol20_dt,                             /* debug flags */
    NULL,                                 /* mem size routine */
    NULL,                                 /* logical name */
    NULL,                                 /* help */
    NULL,                                 /* attach help */
    NULL,                                 /* context for help */
    &sol20t_description                   /* description */
};

/************************/
/* SOL20S Serial Device */
/************************/

#define SOL20S_NAME  "Sol-20 Serial Port"
#define SOL20S_SNAME "SOL20S"

static TMLN sol20s_tmln[1] = { /* line descriptors */
    { 0 }
};

static TMXR sol20s_tmxr = {  /* multiplexer descriptor */
    1,                       /* number of terminal lines */
    0,                       /* listening port (reserved) */
    0,                       /* master socket  (reserved) */
    sol20s_tmln,             /* line descriptor array */
    NULL,                    /* line connection order */
    NULL                     /* multiplexer device (derived internally) */
};

static SOL20_PORT_CTX sol20s_ctx = {
    {0, 0, SOL20_SERST, 2}, 0, 9600, 0,
    0, 0, 0, 0, sol20s_tmln, &sol20s_tmxr
};

static REG sol20s_reg[] = {
    { HRDATAD (BAUD, sol20s_ctx.baud, 16, "Serial port baud register"), },
    { HRDATAD (TBE, sol20s_ctx.tbe, 1, "Serial port TX buffer empty register"), },
    { HRDATAD (TXD, sol20s_ctx.txd, 8, "Serial port TX data register"), },
    { HRDATAD (RDR, sol20s_ctx.rdr, 1, "Serial port RX data ready"), },
    { HRDATAD (RXD, sol20s_ctx.rxd, 8, "Serial port RX register"), },
    { NULL }
};

static UNIT sol20s_unit[] = {
    { UDATA (&sol20_svc, UNIT_ATTABLE, 0), 1000 }
};

static MTAB sol20s_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "PORT", "PORT",
        NULL, sol20_show_ports, NULL, "Show serial I/O ports" },
    { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD",
        &sol20_set_baud, &sol20_show_baud, NULL, "Set baud rate (default=9600)" },
    { 0 }
};

DEVICE sol20s_dev = {
    SOL20S_SNAME,                                   /* name */
    sol20s_unit,                                    /* unit */
    sol20s_reg,                                     /* registers */
    sol20s_mod,                                     /* modifiers */
    1,                                              /* # units */
    10,                                             /* address radix */
    31,                                             /* address width */
    1,                                              /* addr increment */
    8,                                              /* data radix */
    8,                                              /* data width */
    NULL,                                           /* examine routine */
    NULL,                                           /* deposit routine */
    &sol20_port_reset,                              /* reset routine */
    NULL,                                           /* boot routine */
    &sol20_attach_mux,                              /* attach routine */
    &sol20_detach_mux,                              /* detach routine */
    &sol20s_ctx,                                    /* context */
    (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX),  /* flags */
    ERROR_MSG,                                      /* debug control */
    sol20_dt,                                       /* debug flags */
    NULL,                                           /* mem size routine */
    NULL,                                           /* logical name */
    NULL,                                           /* help */
    NULL,                                           /* attach help */
    NULL,                                           /* context for help */
    &sol20s_description                             /* description */
};

/*************************/
/* SOL20P Printer Device */
/*************************/

#define SOL20P_NAME  "Sol-20 Printer Port"
#define SOL20P_SNAME "SOL20P"

static TMLN sol20p_tmln[1] = { /* line descriptors */
    { 0 }
};

static TMXR sol20p_tmxr = {  /* multiplexer descriptor */
    1,                       /* number of terminal lines */
    0,                       /* listening port (reserved) */
    0,                       /* master socket  (reserved) */
    sol20p_tmln,             /* line descriptor array */
    NULL,                    /* line connection order */
    NULL                     /* multiplexer device (derived internally) */
};

static SOL20_PORT_CTX sol20p_ctx = {
    {0, 0, SOL20_PDATA, 1}, 0, 9600, 0,
    0, 0, 0, 0, sol20p_tmln, &sol20p_tmxr
};

static REG sol20p_reg[] = {
    { HRDATAD (BAUD, sol20s_ctx.baud, 16, "Serial port baud register"), },
    { HRDATAD (TBE, sol20p_ctx.tbe, 1, "Printer port buffer empty register"), },
    { HRDATAD (TXD, sol20p_ctx.txd, 8, "Printer port data register"), },
    { HRDATAD (RDR, sol20k_ctx.rdr, 1, "Printer port RX data ready"), },
    { HRDATAD (RXD, sol20p_ctx.rxd, 8, "Printer port RX data register"), },
    { NULL }
};

static UNIT sol20p_unit[] = {
    { UDATA (&sol20_svc, UNIT_ATTABLE, 0), 1000 }
};

static MTAB sol20p_mod[] = {
    { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD",
        &sol20_set_baud, &sol20_show_baud, NULL, "Set baud rate (default=9600)" },
    { 0 }
};

DEVICE sol20p_dev = {
    SOL20P_SNAME,                                   /* name */
    sol20p_unit,                                    /* unit */
    sol20p_reg,                                     /* registers */
    sol20p_mod,                                     /* modifiers */
    1,                                              /* # units */
    10,                                             /* address radix */
    31,                                             /* address width */
    1,                                              /* addr increment */
    8,                                              /* data radix */
    8,                                              /* data width */
    NULL,                                           /* examine routine */
    NULL,                                           /* deposit routine */
    &sol20_port_reset,                              /* reset routine */
    NULL,                                           /* boot routine */
    &sol20_attach_mux,                              /* attach routine */
    &sol20_detach_mux,                              /* detach routine */
    &sol20p_ctx,                                    /* context */
    (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX),  /* flags */
    ERROR_MSG,                                      /* debug control */
    sol20_dt,                                       /* debug flags */
    NULL,                                           /* mem size routine */
    NULL,                                           /* logical name */
    NULL,                                           /* help */
    NULL,                                           /* attach help */
    NULL,                                           /* context for help */
    &sol20p_description                             /* description */
};

static const char* sol20_description(DEVICE *dptr) {
    return SOL20_NAME;
}

static const char* sol20k_description(DEVICE *dptr) {
    return SOL20K_NAME;
}

static const char* sol20t_description(DEVICE *dptr) {
    return SOL20T_NAME;
}

static const char* sol20s_description(DEVICE *dptr) {
    return SOL20S_NAME;
}

static const char* sol20p_description(DEVICE *dptr) {
    return SOL20P_NAME;
}

/*
 * Reset function for the main SOL20 device.
 *
 * Enables all other Sol-20 devices
 */
static t_stat sol20_reset(DEVICE *dptr)
{
    static uint8 first = 1;
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
    DEVICE *vdm1;
#endif

    if (dptr->flags & DEV_DIS) { /* Disconnect Resources */
        sim_map_resource(sol20_ctx.rom_base, sol20_ctx.rom_size, RESOURCE_TYPE_MEMORY, &sol20rom, "sol20rom", TRUE);
        sim_map_resource(sol20_ctx.io_base, sol20_ctx.io_size, RESOURCE_TYPE_IO, &sol20io, "sol20io", TRUE);
    }
    else {
        /*
        * If this is the first reset of the SOL20 device,
        * enable the VDM-1 and SOL20 devices.
        */
        if (first--) {
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
            if ((vdm1 = find_dev("VDM1")) != NULL) {
                set_dev_enbdis(vdm1, NULL, 1, NULL);
            }

            set_dev_enbdis(&sol20k_dev, NULL, 1, NULL);
            set_dev_enbdis(&sol20t_dev, NULL, 1, NULL);
            set_dev_enbdis(&sol20s_dev, NULL, 1, NULL);
            set_dev_enbdis(&sol20p_dev, NULL, 1, NULL);
#else
            sim_messagef(SCPE_NOFNC, "%s device requires VDM1 video support.\n", SOL20_SNAME);
            return SCPE_NOFNC;
#endif
        }

        if (sim_map_resource(sol20_ctx.rom_base, sol20_ctx.rom_size, RESOURCE_TYPE_MEMORY, &sol20rom, "sol20rom", FALSE) != 0) {
            sim_debug(ERROR_MSG, &sol20_dev, "Error mapping MEM resource at 0x%04x\n", sol20_ctx.rom_base);
            return SCPE_ARG;
        }
        /* Connect I/O Ports at base address */
        if (sim_map_resource(sol20_ctx.io_base, sol20_ctx.io_size, RESOURCE_TYPE_IO, &sol20io, "sol20io", FALSE) != 0) {
            sim_debug(ERROR_MSG, &sol20_dev, "Error mapping I/O resource at 0x%02x\n", sol20_ctx.io_base);
            return SCPE_ARG;
        }
    }

    sim_debug(STATUS_MSG, &sol20_dev, "reset controller.\n");

    return SCPE_OK;
}

/*
 * The BOOT command will enter the SOLOS ROM at 0xC000
 */
static t_stat sol20_boot(int32 unitno, DEVICE *dptr)
{
    sim_printf("%s: Booting using ROM at 0x%04x\n", SOL20_SNAME, sol20_ctx.rom_base);

    *((int32 *) sim_PC->loc) = sol20_ctx.rom_base;

    return SCPE_OK;
}

/*
 * Manage VDM1 keyboard callback
 */
static t_stat sol20_kb_reset(DEVICE *dptr) {
    if (dptr->flags & DEV_DIS) { /* Disconnect VDM1 keyboard callback */
        vdm1_kb_callback = NULL;
    }
    else {
        vdm1_kb_callback = sol20_kb_callback;
    }

    return sol20_port_reset(dptr);
}

static t_stat sol20_port_reset(DEVICE *dptr) {
    SOL20_PORT_CTX *port;
    uint32 u;

    port = (SOL20_PORT_CTX *) dptr->ctxt;

    for (u = 0; u < dptr->numunits; u++) {
        dptr->units[u].dptr = dptr;
    }

    if (dptr->flags & DEV_DIS) { /* Disconnect I/O Port(s) */
        sim_map_resource(port->pnp.io_base, port->pnp.io_size, RESOURCE_TYPE_IO, &sol20io, dptr->name, TRUE);
        for (u = 0; u < dptr->numunits; u++) {
            sim_cancel(&dptr->units[u]);  /* cancel timer */
        }
    }
    else {
        /* Connect I/O Ports at base address */
        if (sim_map_resource(port->pnp.io_base, port->pnp.io_size, RESOURCE_TYPE_IO, &sol20io, dptr->name, FALSE) != 0) {
            sim_debug(ERROR_MSG, dptr, "Error mapping I/O resource at 0x%02x\n", port->pnp.io_base);
            return SCPE_ARG;
        }

        port->status = 0x00;
        port->rdr = FALSE;
        port->tbe = TRUE;

        for (u = 0; u < dptr->numunits; u++) {
            sim_activate_after_abs(&dptr->units[u], dptr->units[u].wait);  /* activate timer */
        }
    }

    return SCPE_OK;
}

/*
 * Sol-20 service routine
 *
 * The Sol-20 simulator has 4 I/O devices
 *
 * SOL20K - Keyboard device that supports TMXR
 * SOL20S - Serial device that supports TMXR
 * SOL20P - Parallel device that supports TMXR
 * SOL20T - Tape device that supports files
 */
static t_stat sol20_svc(UNIT *uptr)
{
    SOL20_PORT_CTX *port;
    int32 c = 0;
    t_stat r = SCPE_OK;

    port = (SOL20_PORT_CTX *) uptr->dptr->ctxt;

    /* Check for new incoming connection */
    if ((uptr->dptr->flags & DEV_MUX) && (uptr->flags & UNIT_ATT)) {
        if (tmxr_poll_conn(port->tmxr) >= 0) {      /* poll connection */

            port->conn = 1;          /* set connected   */

            sim_printf("%s: new connection.\n", uptr->dptr->name);
        }
    }

    /* TX byte pending? */
    if (port->tbe == FALSE) {
        if (uptr->flags & UNIT_ATT) {
            if (!(uptr->dptr->flags & DEV_MUX)) {
                r = (sim_fwrite(&port->txd, 1, 1, uptr->fileref) == 1) ? SCPE_OK : SCPE_IOERR;
            }
            else {
                if (port->conn) {
                    if ((r = tmxr_putc_ln(&port->tmln[0], port->txd)) == SCPE_OK) {
                        tmxr_poll_tx(port->tmxr);
                    }
                    else if (r == SCPE_LOST) {
                        port->conn = 0;          /* Connection was lost */
                        sim_printf("%s: lost connection.\n", uptr->dptr->name);
                    }
                }
            }
        }
        else {
            sim_putchar(port->txd);
        }

        port->tbe = TRUE;
    }

    /* Check for Data if RX buffer empty */
    if (port->rdr == FALSE) {
        if (uptr->flags & UNIT_ATT) {
            if (!(uptr->dptr->flags & DEV_MUX)) {
                if (sim_fread(&c, 1, 1, uptr->fileref) == 1) {
                    c |= SCPE_KFLAG;
                }
            }
            else {
                tmxr_poll_rx(port->tmxr);
                c = tmxr_getc_ln(&port->tmln[0]);
            }
        }
        else if (uptr == sol20k_unit) {
            c = sim_poll_kbd();
        }

        if (c & (TMXR_VALID | SCPE_KFLAG)) {
            port->rxd = c & 0xff;
            port->rdr = TRUE;
        }
    }

    sim_activate_after_abs(uptr, uptr->wait);  /* reactivate timer */

    return r;
}

/*
 * Used to attach (insert) tapes into the SOL20T device
 */
static t_stat sol20_attach_tape(UNIT *uptr, CONST char *cptr)
{
    t_stat r;

    r = attach_unit(uptr, cptr);    /* attach unit  */
    if (r != SCPE_OK) {             /* error?       */
        sim_debug(ERROR_MSG, &sol20_dev, "ATTACH error=%d\n", r);
        return r;
    }

    sim_printf("%s: Inserted '%s'%s\n", uptr->dptr->name, cptr, uptr->flags & UNIT_RO ? " (Record Protected)" : "");

    return SCPE_OK;
}

/*
 * Used to attach (connect) MUX interfaces from the
 * SOL20K, SOL20S, and SOL20P devices
 */
static t_stat sol20_attach_mux(UNIT *uptr, CONST char *cptr)
{
    SOL20_PORT_CTX *xptr;
    t_stat r;

    xptr = (SOL20_PORT_CTX *) uptr->dptr->ctxt;

    if ((r = tmxr_attach(xptr->tmxr, uptr, cptr)) == SCPE_OK) {
        xptr->tmln[0].rcve = 1;
        sim_printf("%s: attached '%s' to interface.\n", uptr->dptr->name, cptr);
    }

    return r;
}

/*
 * Used to detach (eject) tapes from the SOL20T device
 */
static t_stat sol20_detach_tape(UNIT *uptr)
{
    t_stat r;

    r = detach_unit(uptr);  /* detach unit */

    if (r != SCPE_OK) {
        return r;
    }

    sim_printf("%s: Tape ejected.\n", uptr->dptr->name);

    return SCPE_OK;
}

/*
 * Used to detach (disconnect) MUX interfaces from the
 * SOL20K, SOL20S, and SOL20P devices
 */
static t_stat sol20_detach_mux(UNIT *uptr)
{
    SOL20_PORT_CTX *xptr;
    t_stat r;

    xptr = (SOL20_PORT_CTX *) uptr->dptr->ctxt;

    if ((r = tmxr_detach(xptr->tmxr, uptr)) == SCPE_OK) {
        sim_printf("%s: detached.\n", uptr->dptr->name);
    }

    return r;
}

static t_stat sol20_show_ports(FILE *st, UNIT *uptr, int32 val, CONST void *desc) {
    SOL20_PORT_CTX *port;

    port = (SOL20_PORT_CTX *) uptr->dptr->ctxt;

    fprintf(st, "I/O=0x%02X-0x%02X", port->pnp.io_base, port->pnp.io_base + port->pnp.io_size - 1);
    return SCPE_OK;
}

static t_stat sol20_config_line(DEVICE *dev, TMLN *tmln, int baud)
{
    char config[20];
    const char *fmt;
    t_stat r = SCPE_IERR;

    if (tmln->serport) {
        fmt = "8N1";

        sprintf(config, "%d-%s", baud, fmt);

        r = tmxr_set_config_line(tmln, config);

        sim_debug(STATUS_MSG, dev, "port configuration set to '%s'.\n", config);
    }

    return r;
}

static t_stat sol20_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc)
{
    int32 baud;
    t_stat r = SCPE_ARG;
    SOL20_PORT_CTX *port;

    port = (SOL20_PORT_CTX *) uptr->dptr->ctxt;

    if (!(uptr->flags & UNIT_ATT)) {
        return SCPE_UNATT;
    }

    if (cptr != NULL) {
        if (sscanf(cptr, "%d", &baud)) {
            port->baud = baud;
            r = sol20_config_line(uptr->dptr, port->tmln, port->baud);
        }
    }

    return r;
}

static t_stat sol20_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc)
{
    SOL20_PORT_CTX *port;

    port = (SOL20_PORT_CTX *) uptr->dptr->ctxt;

    if (uptr->flags & UNIT_ATT) {
        fprintf(st, "Baud rate: %d", port->baud);
    }

    return SCPE_OK;
}

static t_stat sol20_set_rom(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    if (!cptr) return SCPE_IERR;
    if (!strlen(cptr)) return SCPE_ARG;

    /* this assumes that the parameter has already been upcased */
    if (!strncmp(cptr, "13", strlen(cptr))) {
        sol20_rom = sol20_rom_13;
    }
    else if (!strncmp(cptr, "13C", strlen(cptr))) {
        sol20_rom = sol20_rom_13c;
    }
    else if (!strncmp(cptr, "41", strlen(cptr))) {
        sol20_rom = sol20_rom_41;
    }
    else {
        return SCPE_ARG;
    }

    return SCPE_OK;
}

static t_stat sol20_show_rom(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
    if (sol20_rom == sol20_rom_13) {
        fprintf(st, "ROM=v1.3");
    }
    else if (sol20_rom == sol20_rom_13c) {
        fprintf(st, "ROM=v1.3C");
    }
    else if (sol20_rom == sol20_rom_41) {
        fprintf(st, "ROM=v4.1");
    }
    else {
        fprintf(st, "ROM=v?.?");
    }

    return SCPE_OK;
}

/*
 * Sets tape speed to NORMAL or FAST
 */
static t_stat sol20_set_tape(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    if (!cptr) return SCPE_IERR;
    if (!strlen(cptr)) return SCPE_ARG;

    /* this assumes that the parameter has already been upcased */
    if (!strncmp(cptr, "NORMAL", strlen(cptr))) {
        uptr->wait = SOL20_TAPE_NORMAL;
    }
    else if (!strncmp(cptr, "FAST", strlen(cptr))) {
        uptr->wait = SOL20_TAPE_FAST;
    }
    else {
        return SCPE_ARG;
    }

    return SCPE_OK;
}

static t_stat sol20_show_tape(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
    if (uptr->wait == SOL20_TAPE_NORMAL) {
        fprintf(st, "TAPE=NORMAL");
    }
    else if (uptr->wait == SOL20_TAPE_FAST) {
        fprintf(st, "TAPE=FAST");
    }

    return SCPE_OK;
}

/*
 * "Rewinds" a tape by seeking to th first byte of
 * the attached file
 */
static t_stat sol20_rewind(UNIT *uptr)
{
    if (uptr->flags & UNIT_ATT && uptr->fileref != NULL) {
        rewind(uptr->fileref);
        sim_printf("%s: Rewound '%s'\n", uptr->dptr->name, uptr->filename);
    }
    else {
        sim_messagef(SCPE_UNATT, "No tape inserted\n");
    }

    return SCPE_OK;
}

/*
 * "Erases" a tape by truncating the attached file
 */
static t_stat sol20_erase(UNIT *uptr)
{
    if ((uptr->flags & UNIT_ATT) && (uptr->fileref != NULL)) {
        sim_set_fsize(uptr->fileref, 0);
        rewind(uptr->fileref);
        sim_printf("%s: Erased '%s'\n", uptr->dptr->name, uptr->filename);
    }
    else {
        sim_messagef(SCPE_UNATT, "No tape inserted\n");
    }

    return SCPE_OK;
}

/*
 * Handles memory reads from ROM
 */
static int32 sol20rom(int32 addr, int32 rw, int32 data)
{
    return(sol20_rom[addr & SOL20_ROM_MASK]);
}

/*
 * Handles I/O input and output
 */
static int32 sol20io(int32 addr, int32 rw, int32 data)
{
    if (rw == 0) {
        return(sol20_io_in(addr));
    }
    else {
        return(sol20_io_out(addr, data));
    }
}

static uint8 sol20_io_in(uint32 addr)
{
    uint8 data = 0xff;

    switch(addr & 0xff) {
        case SOL20_SERST:
            data = (sol20s_ctx.rdr) ? SOL20_SDR : 0x00;
            data |= (sol20s_ctx.tbe) ? SOL20_STBE : 0x00;
            break;

        case SOL20_STAPT:
            data = (sol20k_ctx.rdr) ? 0x00 : SOL20_KDR;    /* Inverted */
            data |= (sol20t_ctx.rdr) ? SOL20_TDR : 0x00;
            data |= (sol20t_ctx.tbe) ? SOL20_TTBE : 0x00;
            data |= (sol20p_ctx.rdr) ? 0x00 : SOL20_PDR;   /* Inverted */
            data |= (sol20p_ctx.tbe) ? SOL20_PXDR : 0x00;
            break;

        case SOL20_KDATA:
            data = sol20k_ctx.rxd;
            sol20k_ctx.rdr = FALSE;
            break;

        case SOL20_SDATA:
            data = sol20s_ctx.rxd;
            sol20s_ctx.rdr = FALSE;
            break;

        case SOL20_TDATA:
            data = sol20t_ctx.rxd;
            sol20t_ctx.rdr = FALSE;
            break;

        case SOL20_PDATA:
            data = sol20p_ctx.rxd;
            sol20p_ctx.rdr = FALSE;
            break;

        default:
            sim_printf("Invalid IO Read %02x\n", addr);

            sim_debug(ERROR_MSG, &sol20_dev, "READ Invalid I/O Address %02x (%02x)\n",
                addr & 0xFF, addr & 0x01);
            break;
    }

    return (data);
}

static uint8 sol20_io_out(uint32 addr, int32 data)
{
    switch(addr & 0xff) {
        case SOL20_SDATA:
            sol20s_ctx.txd = data;
            sol20s_ctx.tbe = FALSE;
            break;

        case SOL20_STAPT:
            if ((sol20t_ctx.status & SOL20_TAPE1) != (data & SOL20_TAPE1)) {
                sim_printf("%s: Tape 1 %s\n", SOL20T_SNAME, data & SOL20_TAPE1 ? "ON" : "OFF");
            }
            if ((sol20t_ctx.status & SOL20_TAPE2) != (data & SOL20_TAPE2)) {
                sim_printf("%s: Tape 2 %s\n", SOL20T_SNAME, data & SOL20_TAPE2 ? "ON" : "OFF");
            }
            sol20t_ctx.status = data;
            break;

        case SOL20_TDATA:
            sol20t_ctx.txd = data;
            sol20t_ctx.tbe = FALSE;
            break;

        case SOL20_PDATA:
            sol20p_ctx.txd = data;
            sol20p_ctx.tbe = FALSE;
            break;

        default:
            sim_debug(ERROR_MSG, &sol20_dev, "WRITE Invalid I/O Address %02x (%02x)\n",
                addr & 0xFF, addr & 0x01);
            break;
    }

    return(0xff);
}

/*
 * Callback routine for VDM1 device keyboard events
 *
 * The VDM1 device will call this function when it receives
 * a KEYBOARD event.
 */
static t_stat sol20_kb_callback(SIM_KEY_EVENT *kev)
{
    uint8 c;

    if ((c = translate_key(kev))) {
        sol20k_ctx.rxd = c;
        sol20k_ctx.rdr = TRUE;
    }

    return SCPE_OK;
}

#define KEY(NORMAL, SHIFTED) (shifted ? (SHIFTED) : (NORMAL))
#define CAPS(NORMAL, SHIFTED) (shifted || caps ? (SHIFTED) : (NORMAL))
#define CTL(NORMAL, SHIFTED, CTLED) \
  (control ? (CTLED) : CAPS(NORMAL, SHIFTED))

/*
 * Translate KEYBOARD event keys to Sol-20 ASCII
 */
static uint8 translate_key(SIM_KEY_EVENT *kev)
{
    static t_bool shifted = FALSE;
    static t_bool caps = FALSE;
    static t_bool control = FALSE;
    static t_bool erase = FALSE;

    if (kev->key != SIM_KEY_F3) {  /* Clear erase cassette flag */
        erase = FALSE;
    }

    if (kev->state == SIM_KEYPRESS_UP) {
        switch (kev->key) {
            case SIM_KEY_SHIFT_L:
            case SIM_KEY_SHIFT_R:
              shifted = FALSE;
              break;

            case SIM_KEY_CTRL_L:
            case SIM_KEY_CTRL_R:
              control = FALSE;
              break;
        }
    }
    else { /* SIM_KEYPRESS_DOWN */
        switch (kev->key) {
            case SIM_KEY_SHIFT_L:
            case SIM_KEY_SHIFT_R:
                shifted = TRUE;
                break;

            case SIM_KEY_CAPS_LOCK:
                caps = !caps;
                break;

            case SIM_KEY_CTRL_L:
            case SIM_KEY_CTRL_R:
                control = TRUE;
                break;

            case SIM_KEY_0:
                return KEY ('0', ')');

            case SIM_KEY_1:
                return KEY ('1', '!');

            case SIM_KEY_2:
                return KEY ('2', '@');

            case SIM_KEY_3:
                return KEY ('3', '#');

            case SIM_KEY_4:
                return KEY ('4', '$');

            case SIM_KEY_5:
                return KEY ('5', '%');

            case SIM_KEY_6:
                return KEY ('6', '^');

            case SIM_KEY_7:
                return KEY ('7', '&');

            case SIM_KEY_8:
                return KEY ('8', '*');

            case SIM_KEY_9:
                return KEY ('9', '(');

            case SIM_KEY_A:
                return CTL ('a', 'A', 0x01);

            case SIM_KEY_B:
                return CTL ('b', 'B', 0x02);

            case SIM_KEY_C:
                return CTL ('c', 'C', 0x03);

            case SIM_KEY_D:
                return CTL ('d', 'D', 0x04);

            case SIM_KEY_E:
                return CTL ('e', 'E', 0x05);

            case SIM_KEY_F:
                return CTL ('f', 'F', 0x06);

            case SIM_KEY_G:
                return CTL ('g', 'G', 0x07);

            case SIM_KEY_H:
                return CTL ('h', 'H', 0x08);

            case SIM_KEY_I:
                return CTL ('i', 'I', 0x09);

            case SIM_KEY_J:
                return CTL ('j', 'J', 0x0a);

            case SIM_KEY_K:
                return CTL ('k', 'K', 0x0b);

            case SIM_KEY_L:
                return CTL ('l', 'L', 0x0c);

            case SIM_KEY_M:
                return CTL ('m', 'M', 0x0d);

            case SIM_KEY_N:
                return CTL ('n', 'N', 0x0e);

            case SIM_KEY_O:
                return CTL ('o', 'O', 0x0f);

            case SIM_KEY_P:
                return CTL ('p', 'P', 0x10);

            case SIM_KEY_Q:
                return CTL ('q', 'Q', 0x11);

            case SIM_KEY_R:
                return CTL ('r', 'R', 0x12);

            case SIM_KEY_S:
                return CTL ('s', 'S', 0x13);

            case SIM_KEY_T:
                return CTL ('t', 'T', 0x14);

            case SIM_KEY_U:
                return CTL ('u', 'U', 0x15);

            case SIM_KEY_V:
                return CTL ('v', 'V', 0x16);

            case SIM_KEY_W:
                return CTL ('w', 'W', 0x17);

            case SIM_KEY_X:
                return CTL ('x', 'X', 0x18);

            case SIM_KEY_Y:
                return CTL ('y', 'Y', 0x19);

            case SIM_KEY_Z:
                return CTL ('z', 'Z', 0x1a);

            case SIM_KEY_BACKQUOTE:
                return KEY('`', '~');

            case SIM_KEY_KP_END:
                return '1';

            case SIM_KEY_KP_DOWN:
                return '2';

            case SIM_KEY_KP_PAGE_DOWN:
                return '3';

            case SIM_KEY_KP_LEFT:
                return '4';

            case SIM_KEY_KP_5:
                return '5';

            case SIM_KEY_KP_RIGHT:
                return '6';

            case SIM_KEY_KP_HOME:
                return '7';

            case SIM_KEY_KP_UP:
                return '8';

            case SIM_KEY_KP_PAGE_UP:
                return '9';

            case SIM_KEY_KP_INSERT:
                return '0';

            case SIM_KEY_MINUS:
                return KEY ('-', '_');

            case SIM_KEY_EQUALS:
                return KEY ('=', '+');

            case SIM_KEY_LEFT_BRACKET:
                return KEY ('[', '{');

            case SIM_KEY_RIGHT_BRACKET:
                return KEY (']', '}');

            case SIM_KEY_SEMICOLON:
                return KEY (';', ':');

            case SIM_KEY_SINGLE_QUOTE:
                return KEY ('\'', '"');

            case SIM_KEY_BACKSLASH:
            case SIM_KEY_LEFT_BACKSLASH:
                return KEY ('\\', '|');

            case SIM_KEY_COMMA:
                return KEY (',', '<');

            case SIM_KEY_PERIOD:
                return KEY ('.', '>');

            case SIM_KEY_SLASH:
                return KEY ('/', '?');

            case SIM_KEY_KP_ADD:
                return '+';

            case SIM_KEY_KP_SUBTRACT:
                return '-';

            case SIM_KEY_KP_MULTIPLY:
                return '*';

            case SIM_KEY_KP_DIVIDE:
                return '/';

            case SIM_KEY_ESC:
                return 0x1b;

            case SIM_KEY_BACKSPACE:
            case SIM_KEY_DELETE:
                return 0x7f;

            case SIM_KEY_TAB:
                return 0x09;

            case SIM_KEY_ENTER:
            case SIM_KEY_KP_ENTER:
                return 0x0d;

            case SIM_KEY_SPACE:
                return ' ';

            case SIM_KEY_UP:
                return 0x97;

            case SIM_KEY_DOWN:
                return 0x9a;

            case SIM_KEY_LEFT:
                return 0x81;

            case SIM_KEY_RIGHT:
                return 0x93;

            case SIM_KEY_HOME:
                return 0x8e;

            case SIM_KEY_F1: /* Rewind Tape */
                sol20_rewind(sol20t_unit);
                break;

            case SIM_KEY_F2: /* Eject Tape */
                sol20_detach_tape(sol20t_unit);
                break;

            case SIM_KEY_F3: /* Erase Tape */
                if (sol20t_unit->flags & UNIT_RO) {
                  sim_printf("%s: Cassette is record protected.\n", SOL20T_SNAME);
                  break;
                }

                if (erase == TRUE) {
                    sol20_erase(sol20t_unit);
                }
                else {
                    sim_printf("%s: Press F3 again to erase cassette.\n", SOL20T_SNAME);
                    erase = TRUE;
                }
                break;

            case SIM_KEY_F4: /* Tape Speed */
                sol20t_unit->wait = (sol20t_unit->wait == SOL20_TAPE_NORMAL) ? SOL20_TAPE_FAST : SOL20_TAPE_NORMAL;
                sim_printf("%s: Tape speed = %s\n", SOL20T_SNAME, (sol20t_unit->wait == SOL20_TAPE_NORMAL) ? "NORMAL" : "FAST");
                break;

            case SIM_KEY_F5: /* Load Key */
                return 0x8c;

            case SIM_KEY_F6: /* Mode Select Key */
                return 0x80;

            case SIM_KEY_F7: /* Clear Key */
                return 0x8b;

            case SIM_KEY_F8: /* CAPS */
                caps = !caps;
                sim_printf("%s: CAPS Lock %s\n", caps ? "ON" : "OFF", SOL20_SNAME);
                break;

            case SIM_KEY_F10: /* Reboot */
                PutBYTEWrapper(0x0066, 0xc3); /* JMP */
                PutBYTEWrapper(0x0067, 0x00); /* JMP */
                PutBYTEWrapper(0x0068, 0xc0); /* JMP */
                nmiInterrupt = TRUE;
                sim_printf("%s: Rebooting...\n", SOL20_SNAME);
                break;

            default:
                sim_messagef(SCPE_OK, "Unmapped key = %02X\n", kev->key);
                break;
        }
    }

    return 0;
}

/*
 * Display Sol-20 function key help
 */
static t_stat sol20_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
    fprintf(st, "Sol-20 Function Keys:\n");
    fprintf(st, "F1: Rewind tape\n");
    fprintf(st, "F2: Eject tape\n");
    fprintf(st, "F3: Erase tape\n");
    fprintf(st, "F4: Tape speed\n");
    fprintf(st, "F5: LOAD\n");
    fprintf(st, "F6: MODE SELECT\n");
    fprintf(st, "F7: CLEAR\n");
    fprintf(st, "F8: CAPS lock\n");
    fprintf(st, "F10: Reboot System\n");


    return SCPE_OK;
}

