diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c index 5b834732..2c239175 100644 --- a/AltairZ80/altairz80_sys.c +++ b/AltairZ80/altairz80_sys.c @@ -77,6 +77,12 @@ extern DEVICE pmmi_dev; extern DEVICE hayes_dev; extern DEVICE mmd_dev; extern DEVICE mmdm_dev; +extern DEVICE sol20_dev; +extern DEVICE sol20k_dev; +extern DEVICE sol20t_dev; +extern DEVICE sol20s_dev; +extern DEVICE sol20p_dev; +extern DEVICE vdm1_dev; extern DEVICE cromfdc_dev; extern DEVICE wd179x_dev; @@ -141,6 +147,13 @@ DEVICE *sim_devices[] = { &djhdc_dev, &mmd_dev, &mmdm_dev, + /* Processor Technology Devices */ + &sol20_dev, + &sol20k_dev, + &sol20t_dev, + &sol20s_dev, + &sol20p_dev, + &vdm1_dev, /* MITS 88-2SIO */ &m2sio0_dev, &m2sio1_dev, diff --git a/AltairZ80/s100_vdm1.c b/AltairZ80/s100_vdm1.c new file mode 100644 index 00000000..c824d249 --- /dev/null +++ b/AltairZ80/s100_vdm1.c @@ -0,0 +1,708 @@ +/* s100_vdm1.c: Processor Technology VDM-1 + + Copyright (c) 2023, Patrick Linstruth + + 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 + THE AUTHOR 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 the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + VDM1 - Processor Technology VDM-1 +*/ + +#include "altairz80_defs.h" +#include "sim_video.h" + +#define VDM1_MARGIN (5) +#define VDM1_CHAR_XSIZE (9) /* character x size */ +#define VDM1_CHAR_YSIZE (13) /* character y size */ +#define VDM1_COLS (64) /* number of colums */ +#define VDM1_LINES (16) /* number of rows */ +#define VDM1_XSIZE (VDM1_COLS * VDM1_CHAR_XSIZE + VDM1_MARGIN * 2) /* visible width */ +#define VDM1_YSIZE (VDM1_LINES * VDM1_CHAR_YSIZE + VDM1_MARGIN * 2) /* visible height */ +#define VDM1_PIXELS (VDM1_XSIZE * VDM1_YSIZE) /* total number of pixels */ + +#define VDM1_MEM_BASE 0xcc00 +#define VDM1_MEM_SIZE 1024 +#define VDM1_MEM_MASK (1024 - 1) + +#define VDM1_IO_BASE 0xfe +#define VDM1_IO_SIZE 1 + +/* +** PORT ASSIGNMENTS +*/ +#define VDM1_DSTAT 0xfe /* VDM DISPLAY PARAMETER PORT */ +#define VDM1_DSTAT_RMSK 0xf0 /* START ROW MASK */ +#define VDM1_DSTAT_CMSK 0x0f /* START COL MASK */ + +/* +** Public VID_DISPLAY for other devices that may want +** to access the video display directly, such as keyboard +** events. +*/ +VID_DISPLAY *vdm1_vptr = NULL; +t_stat (*vdm1_kb_callback)(SIM_KEY_EVENT *kev) = NULL; + +static uint8 vdm1_ram[VDM1_MEM_SIZE]; +static uint8 vdm1_dstat = 0x00; +static t_bool vdm1_dirty = FALSE; +static t_bool vdm1_reverse = FALSE; +static t_bool vdm1_blink = FALSE; +static uint16 vdm1_counter = 0; +static t_bool vdm1_active = FALSE; +static uint32 vdm1_surface[VDM1_PIXELS]; +static uint32 vdm1_palette[2]; + +enum vdm1_switch {VDM1_NONE, + VDM1_NORMAL, VDM1_REVERSE, VDM1_BLINK, VDM1_NOBLINK, + VDM1_MODE1, VDM1_MODE2, VDM1_MODE3, VDM1_MODE4 +}; + +static enum vdm1_switch vdm1_ctrl = VDM1_MODE4; +static enum vdm1_switch vdm1_cursor = VDM1_NOBLINK; +static enum vdm1_switch vdm1_display = VDM1_NORMAL; + +const uint8 charset[128][VDM1_CHAR_YSIZE] = + {{0x00,0x7f,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x7f,0x00,0x00,0x00}, + {0x00,0x7f,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00}, + {0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x7f,0x00,0x00,0x00}, + {0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x7f,0x00,0x00,0x00}, + {0x00,0x20,0x10,0x08,0x04,0x3e,0x10,0x08,0x04,0x02,0x00,0x00,0x00}, + {0x00,0x7f,0x41,0x63,0x55,0x49,0x55,0x63,0x41,0x7f,0x00,0x00,0x00}, + {0x00,0x00,0x01,0x02,0x04,0x48,0x50,0x60,0x40,0x00,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x41,0x7f,0x14,0x14,0x77,0x00,0x00,0x00}, + {0x00,0x10,0x20,0x7c,0x22,0x11,0x01,0x01,0x01,0x01,0x00,0x00,0x00}, + {0x00,0x00,0x08,0x04,0x02,0x7f,0x02,0x04,0x08,0x00,0x00,0x00,0x00}, + {0x00,0x7f,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x7f,0x00,0x00,0x00}, + {0x00,0x00,0x08,0x08,0x08,0x49,0x2a,0x1c,0x08,0x00,0x00,0x00,0x00}, + {0x00,0x08,0x08,0x2a,0x1c,0x08,0x49,0x2a,0x1c,0x08,0x00,0x00,0x00}, + {0x00,0x00,0x08,0x10,0x20,0x7f,0x20,0x10,0x08,0x00,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x63,0x55,0x49,0x55,0x63,0x22,0x1c,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x49,0x41,0x41,0x22,0x1c,0x00,0x00,0x00}, + {0x00,0x7f,0x41,0x41,0x41,0x7f,0x41,0x41,0x41,0x7f,0x00,0x00,0x00}, + {0x00,0x1c,0x2a,0x49,0x49,0x4f,0x41,0x41,0x22,0x1c,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x4f,0x49,0x49,0x2a,0x1c,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x79,0x49,0x49,0x2a,0x1c,0x00,0x00,0x00}, + {0x00,0x1c,0x2a,0x49,0x49,0x79,0x41,0x41,0x22,0x1c,0x00,0x00,0x00}, + {0x00,0x00,0x11,0x0a,0x04,0x4a,0x51,0x60,0x40,0x00,0x00,0x00,0x00}, + {0x00,0x3e,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x63,0x00,0x00,0x00}, + {0x00,0x01,0x01,0x01,0x01,0x7f,0x01,0x01,0x01,0x01,0x00,0x00,0x00}, + {0x00,0x7f,0x41,0x22,0x14,0x08,0x14,0x22,0x41,0x7f,0x00,0x00,0x00}, + {0x00,0x08,0x08,0x08,0x1c,0x1c,0x08,0x08,0x08,0x08,0x00,0x00,0x00}, + {0x00,0x3c,0x42,0x42,0x40,0x30,0x08,0x08,0x00,0x08,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x7f,0x41,0x41,0x22,0x1c,0x00,0x00,0x00}, + {0x00,0x7f,0x49,0x49,0x49,0x79,0x41,0x41,0x41,0x7f,0x00,0x00,0x00}, + {0x00,0x7f,0x41,0x41,0x41,0x79,0x49,0x49,0x49,0x7f,0x00,0x00,0x00}, + {0x00,0x7f,0x41,0x41,0x41,0x4f,0x49,0x49,0x49,0x7f,0x00,0x00,0x00}, + {0x00,0x7f,0x49,0x49,0x49,0x4f,0x41,0x41,0x41,0x7f,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x00,0x00,0x00}, + {0x00,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x14,0x14,0x14,0x7f,0x14,0x7f,0x14,0x14,0x14,0x00,0x00,0x00}, + {0x00,0x08,0x3f,0x48,0x48,0x3e,0x09,0x09,0x7e,0x08,0x00,0x00,0x00}, + {0x00,0x20,0x51,0x22,0x04,0x08,0x10,0x22,0x45,0x02,0x00,0x00,0x00}, + {0x00,0x38,0x44,0x44,0x28,0x10,0x29,0x46,0x46,0x39,0x00,0x00,0x00}, + {0x00,0x0c,0x0c,0x08,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x04,0x08,0x10,0x10,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00}, + {0x00,0x10,0x08,0x04,0x04,0x04,0x04,0x04,0x08,0x10,0x00,0x00,0x00}, + {0x00,0x00,0x08,0x49,0x2a,0x1c,0x2a,0x49,0x08,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x08,0x08,0x08,0x7f,0x08,0x08,0x08,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x10,0x20,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00}, + {0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00}, + {0x00,0x3e,0x41,0x43,0x45,0x49,0x51,0x61,0x41,0x3e,0x00,0x00,0x00}, + {0x00,0x08,0x18,0x28,0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x00}, + {0x00,0x3e,0x41,0x01,0x02,0x1c,0x20,0x40,0x40,0x7f,0x00,0x00,0x00}, + {0x00,0x3e,0x41,0x01,0x01,0x1e,0x01,0x01,0x41,0x3e,0x00,0x00,0x00}, + {0x00,0x02,0x06,0x0a,0x12,0x22,0x42,0x7f,0x02,0x02,0x00,0x00,0x00}, + {0x00,0x7f,0x40,0x40,0x7c,0x02,0x01,0x01,0x42,0x3c,0x00,0x00,0x00}, + {0x00,0x1e,0x20,0x40,0x40,0x7e,0x41,0x41,0x41,0x3e,0x00,0x00,0x00}, + {0x00,0x7f,0x41,0x02,0x04,0x08,0x10,0x10,0x10,0x10,0x00,0x00,0x00}, + {0x00,0x3e,0x41,0x41,0x41,0x3e,0x41,0x41,0x41,0x3e,0x00,0x00,0x00}, + {0x00,0x3e,0x41,0x41,0x41,0x3f,0x01,0x01,0x02,0x3c,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x10,0x20,0x00}, + {0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3e,0x00,0x3e,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x10,0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x10,0x00,0x00,0x00}, + {0x00,0x1e,0x21,0x21,0x01,0x06,0x08,0x08,0x00,0x08,0x00,0x00,0x00}, + {0x00,0x1e,0x21,0x4d,0x55,0x55,0x5e,0x40,0x20,0x1e,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x41,0x7f,0x41,0x41,0x41,0x00,0x00,0x00}, + {0x00,0x7e,0x21,0x21,0x21,0x3e,0x21,0x21,0x21,0x7e,0x00,0x00,0x00}, + {0x00,0x1e,0x21,0x40,0x40,0x40,0x40,0x40,0x21,0x1e,0x00,0x00,0x00}, + {0x00,0x7c,0x22,0x21,0x21,0x21,0x21,0x21,0x22,0x7c,0x00,0x00,0x00}, + {0x00,0x7f,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x7f,0x00,0x00,0x00}, + {0x00,0x7f,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00,0x00}, + {0x00,0x1e,0x21,0x40,0x40,0x40,0x4f,0x41,0x21,0x1e,0x00,0x00,0x00}, + {0x00,0x41,0x41,0x41,0x41,0x7f,0x41,0x41,0x41,0x41,0x00,0x00,0x00}, + {0x00,0x3e,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x00}, + {0x00,0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x38,0x00,0x00,0x00}, + {0x00,0x41,0x42,0x44,0x48,0x50,0x68,0x44,0x42,0x41,0x00,0x00,0x00}, + {0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7f,0x00,0x00,0x00}, + {0x00,0x41,0x63,0x55,0x49,0x49,0x41,0x41,0x41,0x41,0x00,0x00,0x00}, + {0x00,0x41,0x61,0x51,0x49,0x45,0x43,0x41,0x41,0x41,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x41,0x41,0x41,0x22,0x1c,0x00,0x00,0x00}, + {0x00,0x7e,0x41,0x41,0x41,0x7e,0x40,0x40,0x40,0x40,0x00,0x00,0x00}, + {0x00,0x1c,0x22,0x41,0x41,0x41,0x49,0x45,0x22,0x1d,0x00,0x00,0x00}, + {0x00,0x7e,0x41,0x41,0x41,0x7e,0x48,0x44,0x42,0x41,0x00,0x00,0x00}, + {0x00,0x3e,0x41,0x40,0x40,0x3e,0x01,0x01,0x41,0x3e,0x00,0x00,0x00}, + {0x00,0x7f,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00}, + {0x00,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x3e,0x00,0x00,0x00}, + {0x00,0x41,0x41,0x41,0x22,0x22,0x14,0x14,0x08,0x08,0x00,0x00,0x00}, + {0x00,0x41,0x41,0x41,0x41,0x49,0x49,0x55,0x63,0x41,0x00,0x00,0x00}, + {0x00,0x41,0x41,0x22,0x14,0x08,0x14,0x22,0x41,0x41,0x00,0x00,0x00}, + {0x00,0x41,0x41,0x22,0x14,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00}, + {0x00,0x7f,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x7f,0x00,0x00,0x00}, + {0x00,0x3c,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00}, + {0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x00,0x00,0x00,0x00}, + {0x00,0x3c,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00}, + {0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,0x00}, + {0x00,0x18,0x18,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3c,0x02,0x3e,0x42,0x42,0x3d,0x00,0x00,0x00}, + {0x00,0x40,0x40,0x40,0x5c,0x62,0x42,0x42,0x62,0x5c,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3c,0x42,0x40,0x40,0x42,0x3c,0x00,0x00,0x00}, + {0x00,0x02,0x02,0x02,0x3a,0x46,0x42,0x42,0x46,0x3a,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3c,0x42,0x7e,0x40,0x40,0x3c,0x00,0x00,0x00}, + {0x00,0x0c,0x12,0x10,0x10,0x7c,0x10,0x10,0x10,0x10,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3a,0x46,0x42,0x46,0x3a,0x02,0x02,0x42,0x3c}, + {0x00,0x40,0x40,0x40,0x5c,0x62,0x42,0x42,0x42,0x42,0x00,0x00,0x00}, + {0x00,0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x06,0x02,0x02,0x02,0x02,0x02,0x02,0x22,0x1c}, + {0x00,0x40,0x40,0x40,0x44,0x48,0x50,0x68,0x44,0x42,0x00,0x00,0x00}, + {0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x1c,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x76,0x49,0x49,0x49,0x49,0x49,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x5c,0x62,0x42,0x42,0x42,0x42,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x5c,0x62,0x42,0x42,0x62,0x5c,0x40,0x40,0x40}, + {0x00,0x00,0x00,0x00,0x3a,0x46,0x42,0x42,0x46,0x3a,0x02,0x02,0x02}, + {0x00,0x00,0x00,0x00,0x5c,0x62,0x40,0x40,0x40,0x40,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x3c,0x42,0x30,0x0c,0x42,0x3c,0x00,0x00,0x00}, + {0x00,0x00,0x10,0x10,0x7c,0x10,0x10,0x10,0x12,0x0c,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x46,0x3a,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x41,0x41,0x41,0x22,0x14,0x08,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x41,0x49,0x49,0x49,0x49,0x36,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x42,0x24,0x18,0x18,0x24,0x42,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x46,0x3a,0x02,0x42,0x3c}, + {0x00,0x00,0x00,0x00,0x7e,0x04,0x08,0x10,0x20,0x7e,0x00,0x00,0x00}, + {0x00,0x0e,0x10,0x10,0x10,0x20,0x10,0x10,0x10,0x0e,0x00,0x00,0x00}, + {0x00,0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x08,0x00,0x00,0x00,0x00}, + {0x00,0x18,0x04,0x04,0x04,0x02,0x04,0x04,0x04,0x18,0x00,0x00,0x00}, + {0x00,0x30,0x49,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x24,0x49,0x12,0x24,0x49,0x12,0x24,0x49,0x12,0x00,0x00,0x00}}; + +/* Debugging Bitmaps */ + +#define DBG_REG 0x0001 /* registers */ + +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_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_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +extern t_stat exdep_cmd(int32 flag, CONST char *cptr); + +static t_stat vdm1_svc(UNIT *uptr); +static t_stat vdm1_reset(DEVICE *dptr); +static t_stat vdm1_boot(int32 unitno, DEVICE *dptr); +static t_stat vdm1_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static int32 vdm1_io(const int32 port, const int32 io, const int32 data); +static int32 vdm1_mem(int32 addr, int32 rw, int32 data); +static const char *vdm1_description(DEVICE *dptr); +static void vdm1_refresh(void); +static void vdm1_render(void); +static void vdm1_render_char(uint8 byte, uint8 x, uint8 y); +static t_stat vdm1_set_ctrl(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat vdm1_show_ctrl(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat vdm1_set_cursor(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat vdm1_show_cursor(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat vdm1_set_display(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat vdm1_show_display(FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +/* VDM1 data structures + + vdm1_dev VDM1 device descriptor + vdm1_unit VDM1 unit descriptor + vdm1_reg VDM1 register list +*/ + +typedef struct { + PNP_INFO pnp; /* Must be first */ +} VDM1_CTX; + +static VDM1_CTX vdm1_ctx = {{VDM1_MEM_BASE, VDM1_MEM_SIZE, VDM1_IO_BASE, VDM1_IO_SIZE}}; + +UNIT vdm1_unit = { + UDATA (&vdm1_svc, 0, 0), 25000 +}; + +REG vdm1_reg[] = { + { HRDATAD (DSTAT, vdm1_dstat, 8, "VDM-1 display parameter register"), }, + { HRDATAD (DIRTY, vdm1_dirty, 1, "VDM-1 dirty register"), }, + { HRDATAD (BLINK, vdm1_blink, 1, "VDM-1 blink register"), }, + { NULL } +}; + +DEBTAB vdm1_debug[] = { + { "REG", DBG_REG, "Register activity" }, + { "VIDEO", SIM_VID_DBG_VIDEO, "Video activity" }, + { 0 } +}; + + +MTAB vdm1_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "VDM-1 base I/O address" }, + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", + &set_membase, &show_membase, NULL, "VDM-1 base memory address" }, + { MTAB_XTD|MTAB_VDV, 0, "CTRL", "CTRL", + &vdm1_set_ctrl, &vdm1_show_ctrl, NULL, "VDM-1 control character switches" }, + { MTAB_XTD|MTAB_VDV, 0, "CURSOR", "CURSOR", + &vdm1_set_cursor, &vdm1_show_cursor, NULL, "VDM-1 cursor switches" }, + { MTAB_XTD|MTAB_VDV, 0, "DISPLAY", "DISPLAY", + &vdm1_set_display, &vdm1_show_display, NULL, "VDM-1 display switches" }, + { 0 } +}; +DEVICE vdm1_dev = { + "VDM1", &vdm1_unit, vdm1_reg, vdm1_mod, + 1, 16, 16, 1, 16, 8, + NULL, NULL, &vdm1_reset, + &vdm1_boot, NULL, NULL, + &vdm1_ctx, DEV_DEBUG | DEV_DIS | DEV_DISABLE, 0, + vdm1_debug, NULL, NULL, &vdm1_help, NULL, NULL, + &vdm1_description +}; + +/* VDM1 routines + + vdm1_svc process event + vdm1_reset process reset +*/ + +t_stat vdm1_svc(UNIT *uptr) +{ + SIM_KEY_EVENT kev; + + vdm1_counter++; + + /* Handle blink */ + if ((vdm1_counter % 10 == 0) && (vdm1_cursor == VDM1_BLINK)) { + vdm1_blink = !vdm1_blink; + vdm1_dirty = TRUE; + } + + if (vdm1_dirty) { + vdm1_refresh(); + vdm1_dirty = TRUE; + } + + if (vdm1_kb_callback != NULL) { + if (vid_poll_kb(&kev) == SCPE_OK) { + (*vdm1_kb_callback)(&kev); + } + } + + sim_activate_after_abs(uptr, uptr->wait); // 25ms refresh rate + + return SCPE_OK; +} + +t_stat vdm1_reset(DEVICE *dptr) +{ + VDM1_CTX *xptr; + t_stat r; + int i; + + xptr = (VDM1_CTX *) dptr->ctxt; + + if (dptr->flags & DEV_DIS) { + sim_map_resource(xptr->pnp.mem_base, xptr->pnp.mem_size, RESOURCE_TYPE_MEMORY, &vdm1_mem, "vdm1", TRUE); + sim_map_resource(xptr->pnp.io_base, xptr->pnp.io_size, RESOURCE_TYPE_IO, &vdm1_io, "vdm1", TRUE); + + sim_cancel(&vdm1_unit); + + if (vdm1_active) { + vdm1_active = FALSE; + return vid_close(); + } + + return SCPE_OK; + } + + sim_map_resource(xptr->pnp.mem_base, xptr->pnp.mem_size, RESOURCE_TYPE_MEMORY, &vdm1_mem, "vdm1", FALSE); + sim_map_resource(xptr->pnp.io_base, xptr->pnp.io_size, RESOURCE_TYPE_IO, &vdm1_io, "vdm1", FALSE); + + if (!vdm1_active) { + r = vid_open_window(&vdm1_vptr, &vdm1_dev, "Display", VDM1_XSIZE, VDM1_YSIZE, SIM_VID_IGNORE_VBAR | SIM_VID_RESIZABLE); /* video buffer size */ + + if (r != SCPE_OK) { + return r; + } + + vid_set_window_size(vdm1_vptr, 800, 600); + + vdm1_palette[0] = vid_map_rgb_window(vdm1_vptr, 0x00, 0x00, 0x00); + vdm1_palette[1] = vid_map_rgb_window(vdm1_vptr, 0x00, 0xFF, 0x30); + + for (i = 0; i < VDM1_PIXELS; i++) { + vdm1_surface[i] = vdm1_palette[0]; + } + + vdm1_active = TRUE; + } + + sim_activate_after_abs(&vdm1_unit, 25); + + return SCPE_OK; +} + +static t_stat vdm1_boot(int32 unitno, DEVICE *dptr) +{ + exdep_cmd(EX_D, "-m 0 MVI A,0"); + exdep_cmd(EX_D, "-m 2 OUT 0FEH"); + exdep_cmd(EX_D, "-m 4 MVI C,0"); + exdep_cmd(EX_D, "-m 6 MVI B,0"); + exdep_cmd(EX_D, "-m 8 LXI H,0CC00H"); + exdep_cmd(EX_D, "-m B DCR B"); + exdep_cmd(EX_D, "-m C MOV M,B"); + exdep_cmd(EX_D, "-m D INX H"); + exdep_cmd(EX_D, "-m E MOV A,H"); + exdep_cmd(EX_D, "-m F CPI 0D0H"); + exdep_cmd(EX_D, "-m 11 JNZ 000BH"); + exdep_cmd(EX_D, "-m 14 DCX H"); + exdep_cmd(EX_D, "-m 15 MOV A,H"); + exdep_cmd(EX_D, "-m 16 ORA A"); + exdep_cmd(EX_D, "-m 17 JNZ 0012H"); + exdep_cmd(EX_D, "-m 1A INR C"); + exdep_cmd(EX_D, "-m 1B MOV B,C"); + exdep_cmd(EX_D, "-m 1C JMP 0008H"); + + *((int32 *) sim_PC->loc) = 0x0000; + + return SCPE_OK; +} + +static int32 vdm1_io(const int32 port, const int32 io, const int32 data) { + int32 result = 0xff; + + if (io == 1) { /* OUT */ + if (port == VDM1_DSTAT) { + vdm1_dstat = data & 0xff; + } + } + + return result; +} + +/* + * VDM-1 1K Video Memory (16 x 64 characters) + */ +static int32 vdm1_mem(int32 addr, int32 rw, int32 data) +{ + + if (rw == 0) { + data = vdm1_ram[addr & VDM1_MEM_MASK]; + } + else { + vdm1_ram[addr & VDM1_MEM_MASK] = data; + vdm1_dirty = TRUE; + } + + return data; +} + +t_stat vdm1_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "\nThe VDM-1 has several switches that control the video display:\n\n"); + + fprintf(st, "SET VDM1 CTRL=MODEx\n"); + fprintf(st, "MODE1 - All control characters suppressed. Only cursor blocks\n"); + fprintf(st, " are displayed. CR and VT enabled.\n"); + fprintf(st, "MODE2 - Control characters blanked. CR and VT enabled.\n"); + fprintf(st, "MODE3 - Control characters displayable. CR and VT enabled.\n"); + fprintf(st, "MODE4 - Control characters displayable. CR and VT disabled. (default)\n\n"); + + fprintf(st, "SET VDM1 CURSOR=NONE,BLINK,NOBLINK\n"); + fprintf(st, "NONE - All cursors suppressed.\n"); + fprintf(st, "BLINK - Blinking cursor.\n"); + fprintf(st, "NOBLINK - Non-blinking cursor. (default)\n\n"); + + fprintf(st, "SET VDM1 DISPLAY=NONE,NORMAL,REVERSE\n"); + fprintf(st, "NONE - No display.\n"); + fprintf(st, "NORMAL - Normal video. (default)\n"); + fprintf(st, "REVERSE - Reverse video.\n\n"); + + fprintf(st, "VDM-1 test program displays all characters on the screen:\n"); + fprintf(st, "BOOT VDM1\n\n"); + + return SCPE_OK; +} + +const char *vdm1_description (DEVICE *dptr) +{ + return "Processor Technology VDM-1 Display"; +} + +/* + * Draw and refresh the screen in the video window + */ +static void vdm1_refresh() { + if (vdm1_active) { + vdm1_render(); + vid_draw_window(vdm1_vptr, VDM1_MARGIN, VDM1_MARGIN, VDM1_XSIZE, VDM1_YSIZE, vdm1_surface); + vid_refresh_window(vdm1_vptr); + } +} + +/* + * The VDM-1 display is make up of 16 64-character rows. Each character occupies + * 1 byte in memory from CC00-CFFF. + */ +static void vdm1_render() +{ + uint8 x,y,s,c,c1; + int addr = 0; + t_bool eol_blank = FALSE; + t_bool eos_blank = FALSE; + + addr += (vdm1_dstat & VDM1_DSTAT_CMSK) * VDM1_COLS; + s = (vdm1_dstat & VDM1_DSTAT_RMSK) >> 4; /* Shadowing */ + + for (y = 0; y < VDM1_LINES; y++) { + for (x = 0; x < VDM1_COLS; x++) { + + c = vdm1_ram[addr++]; + c1 = c & 0x7f; + + /* EOL and EOS blanking */ + if (c1 == 0x0d && (vdm1_ctrl == VDM1_MODE2 || vdm1_ctrl == VDM1_MODE3)) { // CR + eol_blank = TRUE; + } + else if (c1 == 0x0b && (vdm1_ctrl == VDM1_MODE2 || vdm1_ctrl == VDM1_MODE3)) { // VT + eos_blank = TRUE; + } + + /* Blanking control */ + if (vdm1_display == VDM1_NONE || eol_blank || eos_blank || y < s) { + c = ' '; + } + + /* Control character suppression */ + if ((c1 < 0x20 || c1 == 0x7f) && (vdm1_ctrl == VDM1_MODE1 || vdm1_ctrl == VDM1_MODE2)) { + c = ' '; + } + + vdm1_render_char(c, x, y); + + if (addr == VDM1_MEM_SIZE) { + addr = 0; + } + } + } +} + +/* + * The VDM-1 rendered characters one scan line at a time. + * The simulator renders an entire character at a time by + * rendering each character in a rectangle area in the + * video surface buffer. + */ +static void vdm1_render_char(uint8 byte, uint8 x, uint8 y) +{ + uint8 rx,ry,c; + int start,pixel; + + start = (x * VDM1_CHAR_XSIZE) + (VDM1_XSIZE * VDM1_CHAR_YSIZE * y); + + for (ry = 0; ry < VDM1_CHAR_YSIZE; ry++) { + + pixel = start + (VDM1_XSIZE * ry); + + c = charset[byte & 0x7f][ry]; + + if (!vdm1_blink && byte & 0x80) { + c = ~(c & 0xff); + } + + if (vdm1_display == VDM1_REVERSE) { + c = ~(c & 0xff); + } + + for (rx = 0; rx < VDM1_CHAR_XSIZE - 1; rx++) { + vdm1_surface[pixel++] = vdm1_palette[c & (0x80 >> rx) ? !vdm1_reverse : vdm1_reverse]; + } + + vdm1_surface[pixel++] = vdm1_palette[(c & 0x80) ? !vdm1_reverse : vdm1_reverse]; + } +} + +static t_stat vdm1_set_ctrl(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, "MODE1", strlen(cptr))) { + vdm1_ctrl = VDM1_MODE1; + } else if (!strncmp(cptr, "MODE2", strlen(cptr))) { + vdm1_ctrl = VDM1_MODE2; + } else if (!strncmp(cptr, "MODE3", strlen(cptr))) { + vdm1_ctrl = VDM1_MODE3; + } else if (!strncmp(cptr, "MODE4", strlen(cptr))) { + vdm1_ctrl = VDM1_MODE4; + } else { + return SCPE_ARG; + } + + vdm1_dirty = TRUE; + + return SCPE_OK; +} + +static t_stat vdm1_show_ctrl(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (!st) return SCPE_IERR; + + fprintf(st, "CTRL="); + + switch (vdm1_ctrl) { + case VDM1_MODE1: + fprintf(st, "MODE1"); + break; + + case VDM1_MODE2: + fprintf(st, "MODE2"); + break; + + case VDM1_MODE3: + fprintf(st, "MODE3"); + break; + + case VDM1_MODE4: + fprintf(st, "MODE4"); + break; + + default: + fprintf(st, "UNKNOWN"); + break; + } + + return SCPE_OK; +} + +static t_stat vdm1_set_cursor(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, "NONE", strlen(cptr))) { + vdm1_cursor = VDM1_NONE; + } else if (!strncmp(cptr, "BLINK", strlen(cptr))) { + vdm1_cursor = VDM1_BLINK; + } else if (!strncmp(cptr, "NOBLINK", strlen(cptr))) { + vdm1_cursor = VDM1_NOBLINK; + vdm1_blink = FALSE; + } else { + return SCPE_ARG; + } + + vdm1_dirty = TRUE; + + return SCPE_OK; +} + +static t_stat vdm1_show_cursor(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (!st) return SCPE_IERR; + + fprintf(st, "CURSOR="); + + switch (vdm1_cursor) { + case VDM1_NONE: + fprintf(st, "NONE"); + break; + + case VDM1_BLINK: + fprintf(st, "BLINK"); + break; + + case VDM1_NOBLINK: + fprintf(st, "NOBLINK"); + break; + + default: + fprintf(st, "UNKNOWN"); + break; + } + + return SCPE_OK; +} + +static t_stat vdm1_set_display(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, "NONE", strlen(cptr))) { + vdm1_display = VDM1_NONE; + } else if (!strncmp(cptr, "NORMAL", strlen(cptr))) { + vdm1_display = VDM1_NORMAL; + } else if (!strncmp(cptr, "REVERSE", strlen(cptr))) { + vdm1_display = VDM1_REVERSE; + } else { + return SCPE_ARG; + } + + vdm1_dirty = TRUE; + + return SCPE_OK; +} + +static t_stat vdm1_show_display(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (!st) return SCPE_IERR; + + fprintf(st, "DISPLAY="); + + switch (vdm1_display) { + case VDM1_NONE: + fprintf(st, "NONE"); + break; + + case VDM1_NORMAL: + fprintf(st, "NORMAL"); + break; + + case VDM1_REVERSE: + fprintf(st, "REVERSE"); + break; + + default: + fprintf(st, "UNKNOWN"); + break; + } + + return SCPE_OK; +} + diff --git a/AltairZ80/sol20.c b/AltairZ80/sol20.c new file mode 100644 index 00000000..6992e15a --- /dev/null +++ b/AltairZ80/sol20.c @@ -0,0 +1,2274 @@ +/*** 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 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 ROM=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[] = { + { 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 (BAUD, sol20t_ctx.baud, 16, "Tape port baud register"), }, + { HRDATAD (STATUS, sol20t_ctx.baud, 8, "Tape port status register"), }, + { HRDATAD (TXP, sol20t_ctx.tbe, 8, "Tape port TX buffer empty register"), }, + { HRDATAD (TXD, sol20t_ctx.txd, 8, "Tape port TX data register"), }, + { HRDATAD (RXD, sol20t_ctx.rdr, 8, "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, "Modem port baud register"), }, + { HRDATAD (TXP, sol20s_ctx.tbe, 8, "Modem port TX buffer empty register"), }, + { HRDATAD (TXD, sol20s_ctx.txd, 8, "Modem port TX data register"), }, + { HRDATAD (RXD, sol20s_ctx.rxd, 8, "Modem 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 (TXP, sol20p_ctx.tbe, 8, "Printer port buffer empty register"), }, + { HRDATAD (TXD, sol20p_ctx.txd, 8, "Printer port 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); + } +#else + sim_messagef(SCPE_NOFNC, "%s device requires VDM1 video support.\n", SOL20_SNAME); + return SCPE_NOFNC; +#endif + + 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); + } + + 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 SCPE_OK; +} + +/* + * 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\n"); + + + return SCPE_OK; +} + diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj index 8264a028..05140572 100644 --- a/Visual Studio Projects/AltairZ80.vcproj +++ b/Visual Studio Projects/AltairZ80.vcproj @@ -26,7 +26,7 @@ + + + + diff --git a/makefile b/makefile index 1cb12457..b282f0a6 100644 --- a/makefile +++ b/makefile @@ -164,6 +164,10 @@ ifneq (3,${SIM_MAJOR}) ifneq (,$(or $(findstring pdp6,${MAKECMDGOALS}),$(findstring pdp10-ka,${MAKECMDGOALS}),$(findstring pdp10-ki,${MAKECMDGOALS}))) VIDEO_USEFUL = true endif + # building the AltairZ80 could use video support + ifneq (,$(findstring altairz80,${MAKECMDGOALS})) + VIDEO_USEFUL = true + endif endif # building the SEL32 networking can be used ifneq (,$(findstring sel32,${MAKECMDGOALS})) @@ -1875,6 +1879,8 @@ ALTAIR_OPT = -I ${ALTAIRD} ALTAIRZ80D = ${SIMHD}/AltairZ80 ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/sol20.c \ + ${ALTAIRZ80D}/s100_vdm1.c \ ${ALTAIRZ80D}/mmd.c \ ${ALTAIRZ80D}/s100_dj2d.c \ ${ALTAIRZ80D}/s100_djhdc.c \ @@ -1902,7 +1908,7 @@ ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ ${ALTAIRZ80D}/m68k/m68kopac.c ${ALTAIRZ80D}/m68k/m68kopdm.c \ ${ALTAIRZ80D}/m68k/softfloat/softfloat.c \ ${ALTAIRZ80D}/m68k/m68kopnz.c ${ALTAIRZ80D}/m68k/m68kops.c ${ALTAIRZ80D}/m68ksim.c -ALTAIRZ80_OPT = -I ${ALTAIRZ80D} +ALTAIRZ80_OPT = -I ${ALTAIRZ80D} -DUSE_SIM_VIDEO ${VIDEO_CCDEFS} $(VIDEO_LDFLAGS) GRID = ${SIMHD}/GRI diff --git a/sim_video.c b/sim_video.c index b27964a3..3f519443 100644 --- a/sim_video.c +++ b/sim_video.c @@ -313,17 +313,19 @@ static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) */ -#define EVENT_REDRAW 1 /* redraw event for SDL */ -#define EVENT_CLOSE 2 /* close event for SDL */ -#define EVENT_CURSOR 3 /* new cursor for SDL */ -#define EVENT_WARP 4 /* warp mouse position for SDL */ -#define EVENT_DRAW 5 /* draw/blit region for SDL */ -#define EVENT_SHOW 6 /* show SDL capabilities */ -#define EVENT_OPEN 7 /* vid_open request */ -#define EVENT_EXIT 8 /* program exit */ -#define EVENT_SCREENSHOT 9 /* produce screenshot of video window */ -#define EVENT_BEEP 10 /* audio beep */ -#define MAX_EVENTS 20 /* max events in queue */ +#define EVENT_REDRAW 1 /* redraw event for SDL */ +#define EVENT_CLOSE 2 /* close event for SDL */ +#define EVENT_CURSOR 3 /* new cursor for SDL */ +#define EVENT_WARP 4 /* warp mouse position for SDL */ +#define EVENT_DRAW 5 /* draw/blit region for SDL */ +#define EVENT_SHOW 6 /* show SDL capabilities */ +#define EVENT_OPEN 7 /* vid_open request */ +#define EVENT_EXIT 8 /* program exit */ +#define EVENT_SCREENSHOT 9 /* produce screenshot of video window */ +#define EVENT_BEEP 10 /* audio beep */ +#define EVENT_FULLSCREEN 11 /* fullscreen */ +#define EVENT_SIZE 12 /* set window size */ +#define MAX_EVENTS 20 /* max events in queue */ typedef struct { SIM_KEY_EVENT events[MAX_EVENTS]; @@ -372,6 +374,7 @@ t_bool vid_key_state[SDL_NUM_SCANCODES]; VID_DISPLAY *next; t_bool vid_blending; SDL_Rect *vid_dst_last; +SDL_Rect vid_rect; uint32 *vid_data_last; }; @@ -515,7 +518,7 @@ SDL_SetHint (SDL_HINT_RENDER_DRIVER, "software"); SDL_SetHint (SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); #endif -status = SDL_Init (SDL_INIT_VIDEO); +status = SDL_Init (SDL_INIT_EVENTS); if (status) { fprintf (stderr, "SDL Video subsystem can't initialize: %s\n", SDL_GetError ()); @@ -539,8 +542,10 @@ while (1) { if (event.type == SDL_USEREVENT) { if (event.user.code == EVENT_EXIT) break; - if (event.user.code == EVENT_OPEN) + if (event.user.code == EVENT_OPEN) { + SDL_Init (SDL_INIT_VIDEO); vid_video_events ((VID_DISPLAY *)event.user.data1); + } else { if (event.user.code == EVENT_SHOW) vid_show_video_event (); @@ -1615,6 +1620,26 @@ if (SDL_SemWait (vid_mouse_events.sem) == 0) { } } +void vid_set_window_size (VID_DISPLAY *vptr, int32 w, int32 h) +{ +SDL_Event user_event; + +vptr->vid_rect.h = h; +vptr->vid_rect.w = w; + +user_event.type = SDL_USEREVENT; +user_event.user.windowID = vptr->vid_windowID; +user_event.user.code = EVENT_SIZE; +user_event.user.data1 = NULL; +user_event.user.data2 = NULL; +#if defined (SDL_MAIN_AVAILABLE) +while (SDL_PushEvent (&user_event) < 0) + sim_os_ms_sleep (100); +#else + SDL_SetWindowSize(vptr->vid_window, w, h); +#endif +} + t_bool vid_is_fullscreen_window (VID_DISPLAY *vptr) { return SDL_GetWindowFlags (vptr->vid_window) & SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -1627,10 +1652,22 @@ return vid_is_fullscreen_window (&vid_first); t_stat vid_set_fullscreen_window (VID_DISPLAY *vptr, t_bool flag) { +SDL_Event user_event; + +user_event.type = SDL_USEREVENT; +user_event.user.windowID = vptr->vid_windowID; +user_event.user.code = EVENT_FULLSCREEN; +user_event.user.data1 = (flag) ? vptr : NULL; +user_event.user.data2 = NULL; +#if defined (SDL_MAIN_AVAILABLE) +while (SDL_PushEvent (&user_event) < 0) + sim_os_ms_sleep (100); +#else if (flag) SDL_SetWindowFullscreen (vptr->vid_window, SDL_WINDOW_FULLSCREEN_DESKTOP); else SDL_SetWindowFullscreen (vptr->vid_window, 0); +#endif return SCPE_OK; } @@ -1659,6 +1696,12 @@ else { r->x = 0; r->y = (h - r->h) / 2; } +if (vptr->vid_flags & SIM_VID_IGNORE_VBAR) { + r->w = w; + r->h = h; + r->x = 0; + r->y = 0; + } } void vid_update (VID_DISPLAY *vptr) @@ -1769,6 +1812,11 @@ if (!vptr->vid_texture) { vptr->vid_format = SDL_AllocFormat (SDL_PIXELFORMAT_ARGB8888); +if (vptr->vid_flags & SIM_VID_RESIZABLE) { + SDL_SetWindowResizable(vptr->vid_window, SDL_TRUE); + SDL_RenderSetIntegerScale(vptr->vid_renderer, SDL_TRUE); +} + SDL_StopTextInput (); vptr->vid_windowID = SDL_GetWindowID (vptr->vid_window); @@ -2033,22 +2081,27 @@ while (vid_active) { case SDL_WINDOWEVENT_EXPOSED: vid_update (vptr); break; + default: + sim_debug (SIM_VID_DBG_VIDEO, vptr->vid_dev, "Did not handle window event: %d - %s\n", event.window.event, windoweventtypes[event.window.event]); + break; } } break; case SDL_USEREVENT: - /* There are 9 user events generated */ - /* EVENT_REDRAW to update the display */ - /* EVENT_DRAW to update a region in the display texture */ - /* EVENT_SHOW to display the current SDL video capabilities */ - /* EVENT_CURSOR to change the current cursor */ - /* EVENT_WARP to warp the cursor position */ - /* EVENT_OPEN to open a new window */ - /* EVENT_CLOSE to wake up this thread and let */ - /* it notice vid_active has changed */ - /* EVENT_SCREENSHOT to take a screenshot */ - /* EVENT_BEEP to emit a beep sound */ + /* There are 11 user events generated */ + /* EVENT_REDRAW to update the display */ + /* EVENT_DRAW to update a region in the display texture */ + /* EVENT_SHOW to display the current SDL video capabilities */ + /* EVENT_CURSOR to change the current cursor */ + /* EVENT_WARP to warp the cursor position */ + /* EVENT_OPEN to open a new window */ + /* EVENT_CLOSE to wake up this thread and let */ + /* it notice vid_active has changed */ + /* EVENT_SCREENSHOT to take a screenshot */ + /* EVENT_BEEP to emit a beep sound */ + /* EVENT_SIZE to change screen size */ + /* EVENT_FULLSCREEN to change fullscreen */ while (vid_active && event.user.code) { /* Handle Beep first since it isn't a window oriented event */ if (event.user.code == EVENT_BEEP) { @@ -2059,7 +2112,7 @@ while (vid_active) { if (event.user.code != EVENT_OPEN) { vptr = vid_get_event_window (&event, event.user.windowID); if (vptr == NULL) { - sim_debug (SIM_VID_DBG_VIDEO, vptr->vid_dev, "vid_thread() - Ignored event not bound to a window\n"); + sim_printf ("vid_thread() - Ignored event not bound to a window\n"); event.user.code = 0; /* Mark as done */ break; } @@ -2104,6 +2157,17 @@ while (vid_active) { vid_screenshot_event (); event.user.code = 0; /* Mark as done */ } + if (event.user.code == EVENT_SIZE) { + SDL_SetWindowSize (vptr->vid_window, vptr->vid_rect.w, vptr->vid_rect.h); + event.user.code = 0; /* Mark as done */ + } + if (event.user.code == EVENT_FULLSCREEN) { + if (event.user.data1 != NULL) + SDL_SetWindowFullscreen (vptr->vid_window, SDL_WINDOW_FULLSCREEN_DESKTOP); + else + SDL_SetWindowFullscreen (vptr->vid_window, 0); + event.user.code = 0; /* Mark as done */ + } if (event.user.code == EVENT_BEEP) { vid_beep_event (); event.user.code = 0; /* Mark as done */ @@ -2341,6 +2405,8 @@ for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) { if (vid_active) { SDL_RendererInfo info; + info.name = ""; + for (vptr = &vid_first; vptr != NULL; vptr = vptr->next) { if (vptr->vid_active_window) { SDL_GetRendererInfo (vptr->vid_renderer, &info); @@ -2820,6 +2886,11 @@ void vid_set_cursor_position_window (VID_DISPLAY *vptr, int32 x, int32 y) return; } +void vid_set_window_size (VID_DISPLAY *vptr, int32 w, int32 h) +{ +return; +} + const char *vid_key_name (uint32 key) { return ""; diff --git a/sim_video.h b/sim_video.h index 0d6882f1..c74eb3cb 100644 --- a/sim_video.h +++ b/sim_video.h @@ -189,6 +189,8 @@ typedef struct key_event SIM_KEY_EVENT; t_stat vid_open (DEVICE *dptr, const char *title, uint32 width, uint32 height, int flags); #define SIM_VID_INPUTCAPTURED 1 /* Mouse and Keyboard input captured (calling */ /* code responsible for cursor display in video) */ +#define SIM_VID_IGNORE_VBAR 2 /* ignore video buffer aspect ratio */ +#define SIM_VID_RESIZABLE 4 /* video screen is resizable */ typedef void (*VID_QUIT_CALLBACK)(void); t_stat vid_register_quit_callback (VID_QUIT_CALLBACK callback); typedef void (*VID_GAMEPAD_CALLBACK)(int, int, int); @@ -214,6 +216,7 @@ t_stat vid_set_fullscreen (t_bool flag); extern int vid_active; void vid_set_cursor_position (int32 x, int32 y); /* cursor position (set by calling code) */ +void vid_set_window_size (VID_DISPLAY *vptr, int32 x, int32 y); /* window size (set by calling code) */ t_stat vid_open_window (VID_DISPLAY **vptr, DEVICE *dptr, const char *title, uint32 width, uint32 height, int flags); t_stat vid_close_window (VID_DISPLAY *vptr);