diff --git a/AltairZ80/CMakeLists.txt b/AltairZ80/CMakeLists.txt
index 0e5f8591..a58ae42a 100644
--- a/AltairZ80/CMakeLists.txt
+++ b/AltairZ80/CMakeLists.txt
@@ -18,6 +18,7 @@ add_simulator(altairz80
SOURCES
altairz80_cpu.c
altairz80_cpu_nommu.c
+ s100_dazzler.c
s100_jair.c
sol20.c
s100_vdm1.c
diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c
index 473a0ce1..26915c7f 100644
--- a/AltairZ80/altairz80_sys.c
+++ b/AltairZ80/altairz80_sys.c
@@ -79,6 +79,8 @@ extern DEVICE m2sio0_dev;
extern DEVICE m2sio1_dev;
extern DEVICE pmmi_dev;
extern DEVICE hayes_dev;
+extern DEVICE daz_dev;
+extern DEVICE js1_dev;
extern DEVICE jair_dev;
extern DEVICE jairs0_dev;
extern DEVICE jairs1_dev;
@@ -175,6 +177,9 @@ DEVICE *sim_devices[] = {
&pmmi_dev,
/* HAYES MODEM */
&hayes_dev,
+ /* IDE/CF */
+ &daz_dev,
+ &js1_dev,
/* JAIR SBC */
&jair_dev,
&jairs0_dev,
diff --git a/AltairZ80/s100_dazzler.c b/AltairZ80/s100_dazzler.c
new file mode 100644
index 00000000..aca07e25
--- /dev/null
+++ b/AltairZ80/s100_dazzler.c
@@ -0,0 +1,779 @@
+/* s100_dazzler.c: Cromemco DAZZLER and JS-1 Joystick
+
+ Copyright (c) 2024, 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.
+
+ ----
+
+ This device simulates the Cromemco Dazzler and D+7A with JS-1 Joystick
+ Console.
+*/
+
+#include "altairz80_defs.h"
+#include "sim_video.h"
+
+#define DAZ_PIXELS (128 * 128) /* total number of pixels */
+
+#define DAZ_IO_BASE 0x0e
+#define DAZ_IO_SIZE 2
+#define DAZ_MEM_SIZE 2048
+#define DAZ_MEM_MASK (2048 - 1)
+
+#define DAZ_ON 0x80 /* On/Off */
+#define DAZ_RESX4 0x40 /* Resolution X 4 */
+#define DAZ_2K 0x20 /* Picture in 2K bytes of memory */
+#define DAZ_COLOR 0x10 /* Picture in 2K bytes of memory */
+#define DAZ_HIGH 0x08 /* High intensity color */
+#define DAZ_BLUE 0x04 /* Blue */
+#define DAZ_GREEN 0x02 /* Green */
+#define DAZ_RED 0x01 /* Red */
+#define DAZ_EOF 0x40 /* End of Frame */
+#define DAZ_EVEN 0x80 /* Even Line */
+
+#define JS1_IO_BASE 0x18
+#define JS1_IO_SIZE 8
+
+#define JS1_NUM_STICKS 2
+#define JS1_NUM_BUTTONS 4
+
+/*
+** Public VID_DISPLAY for other devices that may want
+** to access the video display directly, such as joystick
+** events.
+*/
+VID_DISPLAY *daz_vptr = NULL;
+
+static t_bool daz_0e = 0x00;
+static t_bool daz_0f = 0x80;
+static uint32 daz_addr = 0x0000;
+static t_bool daz_frame = 0x3f;
+static uint8 daz_res = 32;
+static uint16 daz_pages = 1;
+static uint16 daz_window_width = 640;
+static uint16 daz_window_height = 640;
+static uint16 daz_screen_width = 32;
+static uint16 daz_screen_height = 32;
+static uint16 daz_screen_pixels = 32 * 32;
+static uint8 daz_color = 0;
+static uint32 daz_surface[DAZ_PIXELS];
+static uint32 daz_cpalette[16];
+static uint32 daz_gpalette[16];
+
+static uint8 js1_buttons[JS1_NUM_STICKS] = {0x0f, 0x0f};
+static uint8 js1_joyx[JS1_NUM_STICKS];
+static uint8 js1_joyy[JS1_NUM_STICKS];
+
+#define DAZ_SHOW_VIDEO(b) (b & DAZ_ON) ? "ON" : "OFF"
+#define DAZ_SHOW_RES(b) (b & DAZ_RESX4) ? "X4" : "NORMAL"
+#define DAZ_SHOW_MEMSIZE(b) (b & DAZ_2K) ? "2K" : "512"
+#define DAZ_SHOW_COLOR(b) (b & DAZ_COLOR) ? "COLOR" : "B/W"
+
+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_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);
+extern uint8 GetBYTEWrapper(const uint32 Addr);
+
+static const char *daz_description(DEVICE *dptr);
+static t_stat daz_svc(UNIT *uptr);
+static t_stat daz_reset(DEVICE *dptr);
+static t_stat daz_boot(int32 unitno, DEVICE *dptr);
+static void daz_set_0f(uint8 val);
+static t_stat daz_set_video(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
+static t_stat daz_show_video(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
+static t_stat daz_set_resolution(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
+static t_stat daz_show_resolution(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
+static t_stat daz_set_memsize(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
+static t_stat daz_show_memsize(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
+static t_stat daz_set_color(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
+static t_stat daz_show_color(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
+static int32 daz_io(const int32 port, const int32 io, const int32 data);
+static t_stat daz_open_video(void);
+static t_stat daz_close_video(void);
+static void daz_resize_video(void);
+static void daz_refresh(void);
+static void daz_render_normal(void);
+static void daz_render_x4(void);
+static int32 daz_quad_surfacex(int q);
+static int32 daz_quad_surfacey(int q);
+
+static const char *js1_description(DEVICE *dptr);
+static t_stat js1_svc(UNIT *uptr);
+static t_stat js1_reset(DEVICE *dptr);
+static int32 js1_io(const int32 port, const int32 io, const int32 data);
+static void js1_joy_motion (int device, int axis, int value);
+static void js1_joy_button (int device, int button, int state);
+
+/* Debug flags */
+#define VERBOSE_MSG (1 << 0)
+
+/*
+ DAZZLER data structures
+
+ daz_dev DAZ device descriptor
+ daz_unit DAZ unit descriptor
+ daz_reg DAZ register list
+*/
+
+typedef struct {
+ PNP_INFO pnp; /* Must be first */
+} DAZ_CTX;
+
+static DAZ_CTX daz_ctx = {{1, 0, DAZ_IO_BASE, DAZ_IO_SIZE}};
+
+UNIT daz_unit = {
+ UDATA (&daz_svc, 0, 0), 33000 /* 30 fps */
+};
+
+REG daz_reg[] = {
+ { NULL }
+};
+
+DEBTAB daz_debug[] = {
+ { "VERBOSE", VERBOSE_MSG, "Verbose messages" },
+ { "JOYSTICK", SIM_VID_DBG_JOYSTICK, "Joystick messages" },
+ { "VIDEO", SIM_VID_DBG_VIDEO, "Video messages" },
+ { 0 }
+};
+
+MTAB daz_mod[] = {
+ { MTAB_XTD|MTAB_VDV, 0, "VIDEO", "VIDEO",
+ &daz_set_video, &daz_show_video, NULL, "DAZZLER Video [ ON | OFF ]" },
+ { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE",
+ &set_iobase, &show_iobase, NULL, "DAZZLER Base I/O Address" },
+ { MTAB_XTD|MTAB_VDV, 0, "MEMSIZE", "MEMSIZE",
+ &daz_set_memsize, &daz_show_memsize, NULL, "DAZZLER Memory Size [ 512 | 2K ]" },
+ { MTAB_XTD|MTAB_VDV, 0, "RESOLUTION", "RESOLUTION",
+ &daz_set_resolution, &daz_show_resolution, NULL, "DAZZLER Resolution [ NORMAL | HIGH ]" },
+ { MTAB_XTD|MTAB_VDV, 0, "COLOR", "COLOR",
+ &daz_set_color, &daz_show_color, NULL, "DAZZLER Color [ BW | COLOR ]" },
+ { 0 }
+};
+
+DEVICE daz_dev = {
+ "DAZZLER", &daz_unit, daz_reg, daz_mod,
+ 1, 16, 16, 1, 16, 8,
+ NULL, NULL, &daz_reset,
+ &daz_boot, NULL, NULL,
+ &daz_ctx, DEV_DEBUG | DEV_DIS | DEV_DISABLE, 0,
+ daz_debug, NULL, NULL, NULL, NULL, NULL,
+ &daz_description
+};
+
+/*
+ D+7A/JS-1 data structures
+
+ js1_dev D+7A device descriptor
+ js1_unit D+7A unit descriptor
+ js1_reg D+7A register list
+*/
+
+typedef struct {
+ PNP_INFO pnp; /* Must be first */
+} JS1_CTX;
+
+static JS1_CTX js1_ctx = {{1, 0, JS1_IO_BASE, JS1_IO_SIZE}};
+
+UNIT js1_unit = {
+ UDATA (&js1_svc, 0, 0), 20000
+};
+
+REG js1_reg[] = {
+ { NULL }
+};
+
+DEBTAB js1_debug[] = {
+ { "VERBOSE", VERBOSE_MSG, "Verbose" },
+ { 0 }
+};
+
+MTAB js1_mod[] = {
+ { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE",
+ &set_iobase, &show_iobase, NULL, "DAZZLER base I/O address" },
+ { 0 }
+};
+
+DEVICE js1_dev = {
+ "JS1", &js1_unit, js1_reg, js1_mod,
+ 1, 16, 16, 1, 16, 8,
+ NULL, NULL, &js1_reset,
+ NULL, NULL, NULL,
+ &js1_ctx, DEV_DEBUG | DEV_DIS | DEV_DISABLE, 0,
+ js1_debug, NULL, NULL, NULL, NULL, NULL,
+ &js1_description
+};
+
+/*
+ DAZZLER routines
+
+ daz_description
+ daz_svc
+ daz_reset
+*/
+static const char *daz_description (DEVICE *dptr)
+{
+ return "Cromemco Dazzler";
+}
+
+static t_stat daz_svc(UNIT *uptr)
+{
+ daz_refresh();
+
+ sim_activate_after_abs(uptr, uptr->wait);
+
+ return SCPE_OK;
+}
+
+static t_stat daz_reset(DEVICE *dptr)
+{
+ t_stat r = SCPE_OK;
+
+ if (dptr->flags & DEV_DIS) {
+ sim_map_resource(daz_ctx.pnp.io_base, daz_ctx.pnp.io_size, RESOURCE_TYPE_IO, &daz_io, "dazio", TRUE);
+
+ sim_cancel(&daz_unit);
+
+ if (daz_vptr != NULL) {
+ return daz_close_video();
+ }
+ } else {
+ r = sim_map_resource(daz_ctx.pnp.io_base, daz_ctx.pnp.io_size, RESOURCE_TYPE_IO, &daz_io, "dazio", FALSE);
+
+ if (daz_vptr == NULL) {
+ daz_open_video();
+ } else {
+ sim_activate_after_abs(&daz_unit, daz_unit.wait);
+ }
+ }
+
+ return r;
+}
+
+static t_stat daz_boot(int32 unitno, DEVICE *dptr)
+{
+ if (chiptype == CHIP_TYPE_8080) {
+ exdep_cmd(EX_D, "-m 100 MVI A,01H");
+ exdep_cmd(EX_D, "-m 102 ORI 80H");
+ exdep_cmd(EX_D, "-m 104 OUT 0EH");
+ exdep_cmd(EX_D, "-m 106 MVI A,10H");
+ exdep_cmd(EX_D, "-m 108 OUT 0FH");
+ exdep_cmd(EX_D, "-m 10A LXI H,200H");
+ exdep_cmd(EX_D, "-m 10D MVI C,32");
+ exdep_cmd(EX_D, "-m 10F MVI B,16");
+ exdep_cmd(EX_D, "-m 111 XRA A");
+ exdep_cmd(EX_D, "-m 112 MOV M,A");
+ exdep_cmd(EX_D, "-m 113 ADI 11H");
+ exdep_cmd(EX_D, "-m 115 INX H");
+ exdep_cmd(EX_D, "-m 116 DCR B");
+ exdep_cmd(EX_D, "-m 117 JNZ 112H");
+ exdep_cmd(EX_D, "-m 11A DCR C");
+ exdep_cmd(EX_D, "-m 11B JNZ 10FH");
+ exdep_cmd(EX_D, "-m 11E JMP 11EH");
+ } else if (chiptype == CHIP_TYPE_Z80) {
+ exdep_cmd(EX_D, "-m 100 LD A,01H");
+ exdep_cmd(EX_D, "-m 102 OR 80H");
+ exdep_cmd(EX_D, "-m 104 OUT (0EH),A");
+ exdep_cmd(EX_D, "-m 106 LD A,10H");
+ exdep_cmd(EX_D, "-m 108 OUT (0FH),A");
+ exdep_cmd(EX_D, "-m 10A LD HL,200H");
+ exdep_cmd(EX_D, "-m 10D LD C,32");
+ exdep_cmd(EX_D, "-m 10F LD B,16");
+ exdep_cmd(EX_D, "-m 111 XOR A");
+ exdep_cmd(EX_D, "-m 112 LD (HL),A");
+ exdep_cmd(EX_D, "-m 113 ADD A,11H");
+ exdep_cmd(EX_D, "-m 115 INC HL");
+ exdep_cmd(EX_D, "-m 116 DEC B");
+ exdep_cmd(EX_D, "-m 117 JP NZ,112H");
+ exdep_cmd(EX_D, "-m 11A DEC C");
+ exdep_cmd(EX_D, "-m 11B JP NZ,10FH");
+ exdep_cmd(EX_D, "-m 11E JP 11EH");
+ }
+
+ *((int32 *) sim_PC->loc) = 0x0100;
+
+ return SCPE_OK;
+}
+
+static int32 daz_io(const int32 port, const int32 io, const int32 data)
+{
+ int32 p = port - daz_ctx.pnp.io_base;
+
+ if (io == 0) { /* IN */
+ switch (p) {
+ case 0x00: /* 0E */
+ daz_frame = 0x7f;
+ if ((sim_os_msec() % 30) > 25) {
+ daz_frame &= ~DAZ_EOF;
+ } else {
+ daz_frame |= (sim_os_msec() & 1) ? 0x00 : DAZ_EVEN;
+ }
+
+ return daz_frame;
+
+ case 0x01: /* 0F */
+ sim_debug(VERBOSE_MSG, &daz_dev, "Unspecified IN 0x%02X\n", port);
+ break;
+ }
+ } else { /* OUT */
+ switch (p) {
+ case 0x00: /* 0E */
+ daz_0e = data;
+ daz_addr = (data & 0x7f) << 9;
+
+ sim_debug(VERBOSE_MSG, &daz_dev, "New video address 0x%04X Video is %s\n", daz_addr, daz_0e & DAZ_ON ? "ON" : "OFF");
+ break;
+
+ case 0x01: /* 0F */
+ daz_set_0f(data);
+ break;
+ }
+ }
+
+ return 0xff;
+}
+
+static t_stat daz_open_video(void)
+{
+ t_stat r = SCPE_OK;
+ int i;
+
+ if (daz_vptr == NULL) {
+ sim_debug(VERBOSE_MSG, &daz_dev, "Opening new video window w:%d h:%d\n", daz_window_width, daz_window_height);
+
+ r = vid_open_window(&daz_vptr, &daz_dev, "Display", daz_window_width, daz_window_height, SIM_VID_IGNORE_VBAR | SIM_VID_RESIZABLE); /* video buffer size */
+
+ if (r != SCPE_OK) {
+ sim_printf("Could not open video window r=%X\n", r);
+ return r;
+ }
+
+ daz_resize_video();
+
+ daz_cpalette[0] = vid_map_rgb_window(daz_vptr, 0x00, 0x00, 0x00);
+ daz_cpalette[1] = vid_map_rgb_window(daz_vptr, 0x80, 0x00, 0x00);
+ daz_cpalette[2] = vid_map_rgb_window(daz_vptr, 0x00, 0x80, 0x00);
+ daz_cpalette[3] = vid_map_rgb_window(daz_vptr, 0x80, 0x80, 0x00);
+ daz_cpalette[4] = vid_map_rgb_window(daz_vptr, 0x00, 0x00, 0x80);
+ daz_cpalette[5] = vid_map_rgb_window(daz_vptr, 0x80, 0x00, 0x80);
+ daz_cpalette[6] = vid_map_rgb_window(daz_vptr, 0x00, 0x80, 0x80);
+ daz_cpalette[7] = vid_map_rgb_window(daz_vptr, 0x80, 0x80, 0x80);
+ daz_cpalette[8] = vid_map_rgb_window(daz_vptr, 0x00, 0x00, 0x00);
+ daz_cpalette[9] = vid_map_rgb_window(daz_vptr, 0xff, 0x00, 0x00);
+ daz_cpalette[10] = vid_map_rgb_window(daz_vptr, 0x00, 0xff, 0x00);
+ daz_cpalette[11] = vid_map_rgb_window(daz_vptr, 0xff, 0xff, 0x00);
+ daz_cpalette[12] = vid_map_rgb_window(daz_vptr, 0x00, 0x00, 0xff);
+ daz_cpalette[13] = vid_map_rgb_window(daz_vptr, 0xff, 0x00, 0xff);
+ daz_cpalette[14] = vid_map_rgb_window(daz_vptr, 0x00, 0xff, 0xff);
+ daz_cpalette[15] = vid_map_rgb_window(daz_vptr, 0xff, 0xff, 0xff);
+ daz_gpalette[0] = vid_map_rgb_window(daz_vptr, 0x00, 0x00, 0x00);
+ daz_gpalette[1] = vid_map_rgb_window(daz_vptr, 0x10, 0x10, 0x10);
+ daz_gpalette[2] = vid_map_rgb_window(daz_vptr, 0x20, 0x20, 0x20);
+ daz_gpalette[3] = vid_map_rgb_window(daz_vptr, 0x30, 0x30, 0x30);
+ daz_gpalette[4] = vid_map_rgb_window(daz_vptr, 0x40, 0x40, 0x40);
+ daz_gpalette[5] = vid_map_rgb_window(daz_vptr, 0x50, 0x50, 0x50);
+ daz_gpalette[6] = vid_map_rgb_window(daz_vptr, 0x60, 0x60, 0x60);
+ daz_gpalette[7] = vid_map_rgb_window(daz_vptr, 0x70, 0x70, 0x70);
+ daz_gpalette[8] = vid_map_rgb_window(daz_vptr, 0x80, 0x80, 0x80);
+ daz_gpalette[9] = vid_map_rgb_window(daz_vptr, 0x90, 0x90, 0x90);
+ daz_gpalette[10] = vid_map_rgb_window(daz_vptr, 0xa0, 0xa0, 0xa0);
+ daz_gpalette[11] = vid_map_rgb_window(daz_vptr, 0xb0, 0xb0, 0xb0);
+ daz_gpalette[12] = vid_map_rgb_window(daz_vptr, 0xc0, 0xc0, 0xc0);
+ daz_gpalette[13] = vid_map_rgb_window(daz_vptr, 0xd0, 0xd0, 0xd0);
+ daz_gpalette[14] = vid_map_rgb_window(daz_vptr, 0xe0, 0xe0, 0xe0);
+ daz_gpalette[15] = vid_map_rgb_window(daz_vptr, 0xff, 0xff, 0xff);
+
+ for (i = 0; i < daz_screen_pixels; i++) {
+ daz_surface[i] = 0;
+ }
+
+ vid_register_gamepad_motion_callback(js1_joy_motion);
+ vid_register_gamepad_button_callback(js1_joy_button);
+ }
+
+ sim_activate_after_abs(&daz_unit, daz_unit.wait);
+
+ return r;
+}
+
+static t_stat daz_close_video(void)
+{
+ t_stat r;
+
+ sim_debug(VERBOSE_MSG, &daz_dev, "Closing video window\n");
+
+ if ((r = vid_close_window(daz_vptr)) == SCPE_OK) {
+ sim_cancel(&daz_unit);
+
+ daz_vptr = NULL;
+ }
+
+ return r;
+}
+
+static void daz_resize_video(void)
+{
+ if (daz_vptr != NULL) {
+ vid_render_set_logical_size(daz_vptr, daz_screen_width, daz_screen_height);
+ if (!sim_is_running) {
+ daz_refresh();
+ }
+ }
+}
+
+/*
+ * Draw and refresh the screen in the video window
+ */
+static void daz_refresh(void) {
+ if (daz_vptr != NULL) {
+ if (daz_0f & DAZ_RESX4) {
+ daz_render_x4();
+ } else {
+ daz_render_normal();
+ }
+ vid_draw_window(daz_vptr, 0, 0, daz_screen_width, daz_screen_height, daz_surface);
+ vid_refresh_window(daz_vptr);
+ }
+}
+
+static void daz_render_normal(void)
+{
+ int q, x, y;
+ int32 maddr = daz_addr;
+ int32 saddr = 0;
+
+ for (q = 0; q < daz_pages; q++) {
+ for (y = daz_quad_surfacey(q); y < daz_quad_surfacey(q) + 32; y++) {
+ for (x = daz_quad_surfacex(q); x < daz_quad_surfacex(q) + 32; x+= 2) {
+ saddr = (y * daz_res) + x;
+ if (!(daz_0e & DAZ_ON)) {
+ daz_surface[saddr] = 0x00;
+ daz_surface[saddr+1] = 0x00;
+ } else if (daz_0f & DAZ_COLOR) {
+ daz_surface[saddr] = daz_cpalette[GetBYTEWrapper(maddr) & 0x0f];
+ daz_surface[saddr+1] = daz_cpalette[(GetBYTEWrapper(maddr) & 0xf0) >> 4];
+ } else {
+ daz_surface[saddr] = daz_gpalette[GetBYTEWrapper(maddr) & 0x0f];
+ daz_surface[saddr+1] = daz_gpalette[(GetBYTEWrapper(maddr) & 0xf0) >> 4];
+ }
+ maddr++;
+ }
+ }
+ }
+}
+
+static void daz_render_x4(void)
+{
+ int b, q, x, y;
+ int32 maddr = daz_addr;
+ int32 saddr = 0;
+ int32 soffset[] = {0, 1, daz_res, daz_res + 1, 2, 3, daz_res + 2, daz_res + 3};
+ uint32 color;
+
+ if (daz_0f & DAZ_COLOR) {
+ color = daz_cpalette[daz_color];
+ } else {
+ color = daz_gpalette[daz_color];
+ }
+
+ for (q = 0; q < daz_pages; q++) {
+ for (y = daz_quad_surfacey(q); y < daz_quad_surfacey(q) + 64; y+=2) {
+ for (x = daz_quad_surfacex(q); x < daz_quad_surfacex(q) + 64; x += 4) {
+ saddr = (y * daz_res) + x;
+ for (b = 0; b < 8; b++) {
+ if (daz_0e & DAZ_ON) {
+ daz_surface[saddr + soffset[b]] = (GetBYTEWrapper(maddr) & (1 << b)) ? color : 0;
+ } else {
+ daz_surface[saddr + soffset[b]] = 0x00;
+ }
+ }
+ maddr++;
+ }
+ }
+ }
+}
+
+static int32 daz_quad_surfacex(int q)
+{
+ if (q == 1 || q == 3) {
+ return daz_res / ((daz_0f & DAZ_RESX4) ? 2 : 2);
+ }
+
+ return 0;
+}
+
+static int32 daz_quad_surfacey(int q)
+{
+ if (q == 2 || q == 3) {
+ return daz_res / ((daz_0f & DAZ_RESX4) ? 2 : 2);
+ }
+
+ return 0;
+}
+
+static void daz_set_0f(uint8 val) {
+ uint8 old = daz_0f;
+
+ /* Update daz_0f register */
+ daz_0f = val;
+ daz_color = daz_0f & 0x0f;
+
+ /* Did resolution change? */
+ if ((daz_0f & (DAZ_RESX4 | DAZ_2K)) != (old & (DAZ_RESX4 | DAZ_2K))) {
+ daz_res = 32;
+ daz_pages = 1;
+ if (daz_0f & DAZ_RESX4) {
+ daz_res *= 2;
+ }
+ if (daz_0f & DAZ_2K) {
+ daz_pages = 4;
+ daz_res *= 2;
+ }
+
+ sim_debug(VERBOSE_MSG, &daz_dev, "Setting resolution to %02X %dx%d (%d pages) %s %s\n",
+ daz_0f, daz_res, daz_res, daz_pages, DAZ_SHOW_RES(daz_0f), DAZ_SHOW_MEMSIZE(daz_0f));
+
+ daz_screen_width = daz_res;
+ daz_screen_height = daz_res;
+ daz_screen_pixels = daz_screen_width * daz_screen_height;
+
+ daz_resize_video();
+ }
+
+ if (!sim_is_running) {
+ daz_refresh();
+ }
+}
+
+static t_stat daz_set_video(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, "OFF", strlen(cptr))) {
+ daz_0e &= ~DAZ_ON;
+ } else if (!strncmp(cptr, "ON", strlen(cptr))) {
+ daz_0e |= DAZ_ON;
+ } else {
+ return SCPE_ARG;
+ }
+
+ if (!sim_is_running) {
+ daz_refresh();
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat daz_show_video(FILE *st, UNIT *uptr, int32 val, CONST void *desc) {
+ if (!st) return SCPE_IERR;
+
+ fprintf(st, "VIDEO=%s", DAZ_SHOW_VIDEO(daz_0e));
+
+ return SCPE_OK;
+}
+
+static t_stat daz_set_resolution(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
+{
+ uint8 old = daz_0f;
+
+ 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))) {
+ old &= ~DAZ_RESX4;
+ } else if (!strncmp(cptr, "HIGH", strlen(cptr))) {
+ old |= DAZ_RESX4;
+ } else {
+ return SCPE_ARG;
+ }
+
+ daz_set_0f(old);
+
+ return SCPE_OK;
+}
+
+static t_stat daz_show_resolution(FILE *st, UNIT *uptr, int32 val, CONST void *desc) {
+ if (!st) return SCPE_IERR;
+
+ fprintf(st, "RES=%s", DAZ_SHOW_RES(daz_0f));
+
+ return SCPE_OK;
+}
+
+static t_stat daz_set_memsize(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
+{
+ uint8 old = daz_0f;
+
+ if (!cptr) return SCPE_IERR;
+ if (!strlen(cptr)) return SCPE_ARG;
+
+ /* this assumes that the parameter has already been upcased */
+ if (!strncmp(cptr, "512", strlen(cptr))) {
+ old &= ~DAZ_2K;
+ } else if (!strncmp(cptr, "2K", strlen(cptr))) {
+ old |= DAZ_2K;
+ } else {
+ return SCPE_ARG;
+ }
+
+ daz_set_0f(old);
+
+ return SCPE_OK;
+}
+
+static t_stat daz_show_memsize(FILE *st, UNIT *uptr, int32 val, CONST void *desc) {
+ if (!st) return SCPE_IERR;
+
+ fprintf(st, "MEMSIZE=%s @ %04X", DAZ_SHOW_MEMSIZE(daz_0f), daz_addr);
+
+ return SCPE_OK;
+}
+
+static t_stat daz_set_color(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
+{
+ uint8 old = daz_0f;
+
+ if (!cptr) return SCPE_IERR;
+ if (!strlen(cptr)) return SCPE_ARG;
+
+ /* this assumes that the parameter has already been upcased */
+ if (!strncmp(cptr, "BW", strlen(cptr))) {
+ old &= ~DAZ_COLOR;
+ } else if (!strncmp(cptr, "COLOR", strlen(cptr))) {
+ old |= DAZ_COLOR;
+ } else {
+ return SCPE_ARG;
+ }
+
+ daz_set_0f(old);
+
+ return SCPE_OK;
+}
+
+static t_stat daz_show_color(FILE *st, UNIT *uptr, int32 val, CONST void *desc) {
+ if (!st) return SCPE_IERR;
+
+ fprintf(st, "%s", DAZ_SHOW_COLOR(daz_0f));
+
+ return SCPE_OK;
+}
+
+/*
+ D+7A / JS-1 routines
+
+ js1_description
+ js1_svc
+ js1_reset
+*/
+static const char *js1_description (DEVICE *dptr)
+{
+ return "Cromemco D+7A";
+}
+
+t_stat js1_svc(UNIT *uptr)
+{
+ return SCPE_OK;
+}
+
+static t_stat js1_reset(DEVICE *dptr)
+{
+ t_stat r = SCPE_OK;
+
+ if (dptr->flags & DEV_DIS) {
+ sim_map_resource(js1_ctx.pnp.io_base, js1_ctx.pnp.io_size, RESOURCE_TYPE_IO, &js1_io, "js1io", TRUE);
+ } else {
+ r = sim_map_resource(js1_ctx.pnp.io_base, js1_ctx.pnp.io_size, RESOURCE_TYPE_IO, &js1_io, "js1io", FALSE);
+ }
+
+ return r;
+}
+
+static int32 js1_io(const int32 port, const int32 io, const int32 data)
+{
+ int32 p = port - js1_ctx.pnp.io_base;
+
+ if (io == 0) { /* IN */
+ switch (p) {
+ case 0x00: /* 18 */
+ return (js1_buttons[0] & 0x0f) + ((js1_buttons[1] & 0x0f) << 4);
+ break;
+
+ case 0x01: /* 19 */
+ return js1_joyx[0];
+ break;
+
+ case 0x02: /* 1A */
+ return js1_joyy[0];
+ break;
+
+ case 0x03: /* 1B */
+ return js1_joyx[1];
+ break;
+
+ case 0x04: /* 1C */
+ return js1_joyy[1];
+ break;
+ }
+ } else { /* OUT */
+ switch (p) {
+ case 0x01: /* 19 - Sound not supported... yet */
+ case 0x03: /* 1B - Sound not supported... yet */
+ break;
+ }
+ }
+
+ return 0xff;
+}
+
+static void js1_joy_motion (int device, int axis, int value)
+{
+ uint8 v;
+
+ if (device < JS1_NUM_STICKS && axis < 2) {
+ if (value < -32000) value = -32000;
+ if (value > 32000) value = 32000;
+
+ v = (uint8) (value >> 8);
+
+ if (axis == 0) {
+ js1_joyx[device] = v;
+ sim_debug(SIM_VID_DBG_JOYSTICK, &daz_dev, "Joystick device=%d, axis=%d, value=%d x=%02X\n", device, axis, value, js1_joyx[device]);
+ } else {
+ js1_joyy[device] = -v;
+ sim_debug(SIM_VID_DBG_JOYSTICK, &daz_dev, "Joystick device=%d, axis=%d, value=%d y=%02X\n", device, axis, value, js1_joyy[device]);
+ }
+ }
+}
+
+static void js1_joy_button (int device, int button, int state)
+{
+ if (device < JS1_NUM_STICKS && button < JS1_NUM_BUTTONS) {
+ js1_buttons[device] |= 1 << button;
+ if (state) js1_buttons[device] &= ~(1 << button);
+ sim_debug(SIM_VID_DBG_JOYSTICK, &daz_dev, "Button device=%d, button=%d, state=%d\n", device, button, state);
+ }
+}
+
diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj
index 89ff08f4..164ac452 100644
--- a/Visual Studio Projects/AltairZ80.vcproj
+++ b/Visual Studio Projects/AltairZ80.vcproj
@@ -322,6 +322,10 @@
RelativePath="..\AltairZ80\s100_adcs6.c"
>
+
+
diff --git a/Visual Studio Projects/AltairZ80.vcxproj b/Visual Studio Projects/AltairZ80.vcxproj
index 6635c661..4b58f61f 100755
--- a/Visual Studio Projects/AltairZ80.vcxproj
+++ b/Visual Studio Projects/AltairZ80.vcxproj
@@ -270,6 +270,7 @@
+
diff --git a/Visual Studio Projects/AltairZ80.vcxproj.filters b/Visual Studio Projects/AltairZ80.vcxproj.filters
index a10506ad..d15603f8 100755
--- a/Visual Studio Projects/AltairZ80.vcxproj.filters
+++ b/Visual Studio Projects/AltairZ80.vcxproj.filters
@@ -102,6 +102,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -309,4 +312,4 @@
Header Files
-
\ No newline at end of file
+
diff --git a/makefile b/makefile
index 631d3747..d7cea2fb 100644
--- a/makefile
+++ b/makefile
@@ -1894,6 +1894,7 @@ ALTAIR_OPT = -I ${ALTAIRD}
ALTAIRZ80D = ${SIMHD}/AltairZ80
ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \
+ ${ALTAIRZ80D}/s100_dazzler.c \
${ALTAIRZ80D}/s100_jair.c \
${ALTAIRZ80D}/sol20.c \
${ALTAIRZ80D}/s100_vdm1.c \
diff --git a/sim_video.c b/sim_video.c
index 05a8794c..5e6c6cdc 100644
--- a/sim_video.c
+++ b/sim_video.c
@@ -327,6 +327,7 @@ static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst)
#define EVENT_BEEP 10 /* audio beep */
#define EVENT_FULLSCREEN 11 /* fullscreen */
#define EVENT_SIZE 12 /* set window size */
+#define EVENT_LOGICAL 13 /* set window logical size */
#define MAX_EVENTS 20 /* max events in queue */
typedef struct {
@@ -397,6 +398,7 @@ SDL_MouseButtonEvent *bev;
SDL_MouseMotionEvent *mev;
SDL_WindowEvent *wev;
SDL_UserEvent *uev;
+SDL_version ver;
if (windowID == lastID)
return last_display;
@@ -474,9 +476,12 @@ switch (ev->type) {
break;
}
+SDL_GetVersion(&ver);
+
sim_messagef (SCPE_OK,
"\nSIMH has encountered a bug in SDL2. An upgrade to SDL2\n"
-"version 2.0.14 should fix this problem.\n");
+"version 2.0.14 should fix this problem. You are running "
+"version %d.%d.%d.\n", ver.major, ver.minor, ver.patch);
return NULL;
}
@@ -642,6 +647,8 @@ SDL_GetVersion(&ver);
vid_gamepad_ok = (ver.major > 2 ||
(ver.major == 2 && (ver.minor > 0 || ver.patch >= 4)));
+sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_JOYSTICK, dev, "SDL %d.%d.%d %s support game controllers.\n", ver.major, ver.minor, ver.patch, vid_gamepad_ok ? "DOES" : "DOES NOT");
+
if (vid_gamepad_ok)
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
else
@@ -667,20 +674,22 @@ if (vid_gamepad_ok && SDL_GameControllerEventState (SDL_ENABLE) < 0) {
n = SDL_NumJoysticks();
+sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_JOYSTICK, dev, "Game controllers found: %d\n", n);
+
for (i = 0; i < n; i++) {
if (vid_gamepad_ok && SDL_IsGameController (i)) {
SDL_GameController *x = SDL_GameControllerOpen (i);
if (x != NULL) {
- sim_debug (SIM_VID_DBG_VIDEO, dev,
+ sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_JOYSTICK, dev,
"Game controller: %s\n", SDL_GameControllerNameForIndex(i));
}
}
else {
y = SDL_JoystickOpen (i);
if (y != NULL) {
- sim_debug (SIM_VID_DBG_VIDEO, dev,
- "Joystick: %s\n", SDL_JoystickNameForIndex(i));
- sim_debug (SIM_VID_DBG_VIDEO, dev,
+ sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_JOYSTICK, dev,
+ "%s\n", SDL_JoystickNameForIndex(i));
+ sim_debug (SIM_VID_DBG_VIDEO|SIM_VID_DBG_JOYSTICK, dev,
"Number of axes: %d, buttons: %d\n",
SDL_JoystickNumAxes(y),
SDL_JoystickNumButtons(y));
@@ -1642,6 +1651,26 @@ while (SDL_PushEvent (&user_event) < 0)
#endif
}
+void vid_render_set_logical_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_LOGICAL;
+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_RenderSetLogicalSize(vptr->vid_renderer, w, h);
+#endif
+}
+
t_bool vid_is_fullscreen_window (VID_DISPLAY *vptr)
{
return SDL_GetWindowFlags (vptr->vid_window) & SDL_WINDOW_FULLSCREEN_DESKTOP;
@@ -2085,6 +2114,9 @@ while (vid_active) {
case SDL_WINDOWEVENT_EXPOSED:
vid_update (vptr);
break;
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ 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;
@@ -2104,8 +2136,9 @@ while (vid_active) {
/* 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 */
+ /* EVENT_SIZE to change screen size */
+ /* EVENT_LOGICAL to change screen size */
while (vid_active && event.user.code) {
/* Handle Beep first since it isn't a window oriented event */
if (event.user.code == EVENT_BEEP) {
@@ -2165,6 +2198,10 @@ while (vid_active) {
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_LOGICAL) {
+ SDL_RenderSetLogicalSize (vptr->vid_renderer, 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);
diff --git a/sim_video.h b/sim_video.h
index c74eb3cb..dea05078 100644
--- a/sim_video.h
+++ b/sim_video.h
@@ -217,6 +217,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) */
+void vid_render_set_logical_size (VID_DISPLAY *vptr, int32 w, int32 h);
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);
@@ -245,10 +246,11 @@ t_stat vid_set_alpha_mode (VID_DISPLAY *vptr, int mode);
*/
extern int (*vid_display_kb_event_process)(SIM_KEY_EVENT *kev);
-#define SIM_VID_DBG_MOUSE 0x10000000
-#define SIM_VID_DBG_CURSOR 0x20000000
-#define SIM_VID_DBG_KEY 0x40000000
-#define SIM_VID_DBG_VIDEO 0x80000000
+#define SIM_VID_DBG_JOYSTICK 0x08000000
+#define SIM_VID_DBG_MOUSE 0x10000000
+#define SIM_VID_DBG_CURSOR 0x20000000
+#define SIM_VID_DBG_KEY 0x40000000
+#define SIM_VID_DBG_VIDEO 0x80000000
#ifdef __cplusplus
}