From e5b808dc228a0c5c321b6977bf77fe317f6d4b7d Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Mon, 1 Sep 2025 09:18:15 -0400 Subject: [PATCH] AltairZ80: The Big Beautiful PR This PR contains the following: Adds a new SD Systems SBC200 device. Adds a new SD Systems VFII VersaFloppy II device. Adds ability to load HEX files at a specified address. Adds support for SD Systems ExpandoRAM banked memory. Adds improved DSK image support to the WD179X device. Adds support for formats that don't include the FC index mark to WD179X WRITE_TRACK command. Fixed WD179X VERIFY register. Updated CMakeList.txt Included PR #492 in scp.c Replaced binary notation with hex notation in s100_scb200.c Fixed Warnings Fixed DTR comments This PR passes Howard's AltairZ80 tests. Additional tests were added here: https://github.com/deltecent/altairz80-tests/ Additional packages were added here: https://github.com/deltecent/altairz80-packages/ --- AltairZ80/CMakeLists.txt | 6 +- AltairZ80/altairz80_cpu.c | 46 +- AltairZ80/altairz80_sys.c | 5 + AltairZ80/s100_sbc200.c | 1326 ++++++++++++++++++++++ AltairZ80/s100_vfii.c | 197 ++++ AltairZ80/wd179x.c | 570 +++++++--- Visual Studio Projects/AltairZ80.vcproj | 8 + Visual Studio Projects/AltairZ80.vcxproj | 2 + makefile | 4 +- scp.c | 17 +- scp.h | 1 + sim_imd.h | 2 +- 12 files changed, 2038 insertions(+), 146 deletions(-) create mode 100644 AltairZ80/s100_sbc200.c create mode 100644 AltairZ80/s100_vfii.c diff --git a/AltairZ80/CMakeLists.txt b/AltairZ80/CMakeLists.txt index e32b19f3b..6449a764e 100644 --- a/AltairZ80/CMakeLists.txt +++ b/AltairZ80/CMakeLists.txt @@ -18,6 +18,10 @@ add_simulator(altairz80 SOURCES altairz80_cpu.c altairz80_cpu_nommu.c + wd179x.c + s100_hdc1001.c + s100_sbc200.c + s100_vfii.c s100_tuart.c s100_dazzler.c s100_jair.c @@ -63,8 +67,6 @@ add_simulator(altairz80 s100_scp300f.c s100_tarbell.c s100_tdd.c - wd179x.c - s100_hdc1001.c s100_if3.c s100_adcs6.c m68k/m68kcpu.c diff --git a/AltairZ80/altairz80_cpu.c b/AltairZ80/altairz80_cpu.c index c06a622d4..6510ca5e6 100644 --- a/AltairZ80/altairz80_cpu.c +++ b/AltairZ80/altairz80_cpu.c @@ -572,6 +572,8 @@ static MTAB cpu_mod[] = { NULL, NULL, "Sets the RAM type to Cromemco RAM for 8080 / Z80 / 8086" }, { MTAB_XTD | MTAB_VDV, 4, NULL, "B810", &cpu_set_ramtype, NULL, NULL, "Sets the RAM type AB Digital Design B810 8080 / Z80 / 8086"}, + { MTAB_XTD | MTAB_VDV, 5, NULL, "ERAM", &cpu_set_ramtype, + NULL, NULL, "Sets the RAM type to SD Systems ERAM for 8080 / Z80 / 8086"}, { MTAB_VDV, 4, NULL, "4KB", &cpu_set_size, NULL, NULL, "Sets the RAM size to 4KB for 8080 / Z80 / 8086" }, { MTAB_VDV, 8, NULL, "8KB", &cpu_set_size, @@ -734,7 +736,8 @@ const char* handlerNameForPort(const int32 port) { #define RAM_TYPE_VRAM 2 /* Vector Graphic RAM card */ #define RAM_TYPE_CRAM 3 /* Cromemco RAM card */ #define RAM_TYPE_B810 4 /* AB Digital Design B810 RAM card */ -#define MAX_RAM_TYPE RAM_TYPE_B810 +#define RAM_TYPE_ERAM 5 /* SD Systems ExpandoRAM */ +#define MAX_RAM_TYPE RAM_TYPE_ERAM static int32 ramtype = RAM_TYPE_AZ80; @@ -6760,7 +6763,7 @@ const static CPUFLAG *cpuflags[NUM_CHIP_TYPE] = { cpuflags8080, cpuflagsZ80, cpuflags8086, cpuflagsM68K, }; /* needs to be set for each ramtype <= MAX_RAM_TYPE */ -static const char *ramTypeToString[] = { "AZ80", "HRAM", "VRAM", "CRAM", "B810" }; +static const char *ramTypeToString[] = { "AZ80", "HRAM", "VRAM", "CRAM", "B810", "ERAM" }; static const char* m68kVariantToString[] = { "INVALID", @@ -7000,6 +7003,13 @@ static int32 bankseldev(const int32 port, const int32 io, const int32 data) { sim_printf("Invalid bank select 0x%02x for B810\n", data); } break; + case RAM_TYPE_ERAM: + if (data < 8) { + setBankSelect(data); + } else { + sim_printf("Invalid bank select 0x%02x for ERAM\n", data); + } + break; case RAM_TYPE_AZ80: default: break; @@ -7143,6 +7153,11 @@ static t_stat cpu_set_ramtype(UNIT *uptr, int32 value, CONST char *cptr, void *d sim_printf("Unmapping AB Digital Design B810 RAM\n"); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, "bankseldev", TRUE); break; + case RAM_TYPE_ERAM: + if (cpu_unit.flags & UNIT_CPU_VERBOSE) + sim_printf("Unmapping SD Systems ExpandoRAM\n"); + sim_map_resource(0xff, 1, RESOURCE_TYPE_IO, &bankseldev, "bankseldev", TRUE); + break; case 0: default: if (cpu_unit.flags & UNIT_CPU_VERBOSE) @@ -7171,6 +7186,11 @@ static t_stat cpu_set_ramtype(UNIT *uptr, int32 value, CONST char *cptr, void *d sim_printf("AB Digital Design B810 RAM Selected\n"); sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, "bankseldev", FALSE); break; + case RAM_TYPE_ERAM: + if (cpu_unit.flags & UNIT_CPU_VERBOSE) + sim_printf("SD Systems ExpandoRAM Selected\n"); + sim_map_resource(0xff, 1, RESOURCE_TYPE_IO, &bankseldev, "bankseldev", FALSE); + break; case 0: default: if (cpu_unit.flags & UNIT_CPU_VERBOSE) @@ -7521,11 +7541,21 @@ static t_stat cpu_hex_load(FILE *fileref, CONST char *cptr, CONST char *fnam, in char linebuf[1024], datastr[1024], *bufptr; int32 bytecnt, rectype, databyte, chksum, line = 0, cnt = 0; uint32 makeROM = FALSE; - t_addr addr, org = 0; + t_addr addr, start = 0, offset = 0, org = -1; + CONST char *result; get_glyph(cptr, gbuf, 0); if (strcmp(gbuf, "ROM") == 0) { makeROM = TRUE; + } else { + start = strtotv(cptr, &result, 16) & ADDRMASKEXTENDED; + if (cptr != result) + org = start; + while (isspace(*result)) + result++; + get_glyph(result, gbuf, 0); + if (strcmp(gbuf, "ROM") == 0) + makeROM = TRUE; } while (!feof(fileref)) { @@ -7556,8 +7586,12 @@ static t_stat cpu_hex_load(FILE *fileref, CONST char *cptr, CONST char *fnam, in datastr[sizeof(datastr) - 1] = '\0'; if ((rectype == 0) && (bytecnt > 0) && (addr+bytecnt <= MAXMEMORY)) { - if (cnt == 0) - org = addr; + if (cnt == 0) { + if (org == -1) + org = addr; + else + offset = org - addr; + } do { if (sscanf(bufptr, "%2x", &databyte) != 1) { @@ -7565,7 +7599,7 @@ static t_stat cpu_hex_load(FILE *fileref, CONST char *cptr, CONST char *fnam, in } bufptr += 2; - PutBYTEasROMorRAM(addr++, databyte, makeROM); + PutBYTEasROMorRAM(offset+addr++, databyte, makeROM); chksum += databyte; cnt++; diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c index 41e6f80e1..8caeb5b96 100644 --- a/AltairZ80/altairz80_sys.c +++ b/AltairZ80/altairz80_sys.c @@ -87,12 +87,14 @@ extern DEVICE jairs1_dev; extern DEVICE jairp_dev; extern DEVICE mmd_dev; extern DEVICE mmdm_dev; +extern DEVICE sbc200_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 vfii_dev; extern DEVICE cromfdc_dev; extern DEVICE wd179x_dev; @@ -166,6 +168,9 @@ DEVICE *sim_devices[] = { &djhdc_dev, &mmd_dev, &mmdm_dev, + /* SD Systems */ + &sbc200_dev, + &vfii_dev, /* Processor Technology Devices */ &sol20_dev, &sol20k_dev, diff --git a/AltairZ80/s100_sbc200.c b/AltairZ80/s100_sbc200.c new file mode 100644 index 000000000..8559d5dae --- /dev/null +++ b/AltairZ80/s100_sbc200.c @@ -0,0 +1,1326 @@ +/* s100_sbc200: + + Copyright (c) 2025, 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 + ROBERT M SUPNIK 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 Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. +*/ + +#include "altairz80_defs.h" +#include "sim_tmxr.h" + +#define SBC200_NAME "SD SYSTEMS SBC200" +#define SBC200_SNAME "SBC200" + +#define SBC200_WAIT 250 /* Service Wait Interval */ + +#define SBC200_IOBASE 0x7c +#define SBC200_IOSIZE 4 + +#define SBC200_MON_BASE 0xe000 +#define SBC200_MON_SIZE 2048 +#define SBC200_MON_MASK (SBC200_MON_SIZE-1) + +#define SBC200_DDB_BASE 0xf000 +#define SBC200_DDB_SIZE 2048 +#define SBC200_DDB_MASK (SBC200_DDB_SIZE-1) + +#define SBC200_TDRE 0x01 /* Transmit Data Register Empty */ +#define SBC200_RDRF 0x02 /* Receive Data Register Full */ +#define SBC200_PE 0x08 /* Parity Error */ +#define SBC200_OVRN 0x10 /* Overrun */ +#define SBC200_FE 0x20 /* Framing Error */ +#define SBC200_DSR 0x80 /* Clear to Send */ +#define SBC200_DCD 0x04 /* Data Carrier Detect */ +#define SBC200_IR 0x40 /* Internal Reset */ +#define SBC200_CLK1 0x01 /* Divide Clock by 1 */ +#define SBC200_CLK16 0x02 /* Divide Clock by 16 */ +#define SBC200_CLK64 0x03 /* Divide Clock by 64 */ +#define SBC200_72E 0xf8 /* 7-2-E */ +#define SBC200_72O 0xd8 /* 7-2-O */ +#define SBC200_72N 0xc8 /* 7-2-O */ +#define SBC200_71E 0x78 /* 7-1-E */ +#define SBC200_71O 0x58 /* 7-1-O */ +#define SBC200_71N 0x48 /* 7-1-O */ +#define SBC200_82E 0xfc /* 8-2-E */ +#define SBC200_82O 0xdc /* 8-2-O */ +#define SBC200_82N 0xcc /* 8-2-O */ +#define SBC200_81E 0x7c /* 8-1-E */ +#define SBC200_81O 0xbc /* 8-1-O */ +#define SBC200_81N 0x4c /* 8-1-N */ +#define SBC200_FMTMSK 0xfc /* Length, Parity, Stop Mask */ +#define SBC200_DTR 0x02 /* RTS Bit Mask */ +#define SBC200_RTS 0x20 /* RTS Bit Mask */ +#define SBC200_RIE 0x80 /* Receive Int Enabled */ + +#define SBC200_BAUD 9600 /* Default baud rate */ + +/* SBC-200 Monitor 2.1 @ E000 */ +static uint8 sbc200_mon_rom[SBC200_MON_SIZE] = { + 0xc3, 0x0f, 0xe0, 0xc3, 0x79, 0xe0, 0xc3, 0x0f, + 0xe7, 0xc3, 0x17, 0xe7, 0xc3, 0x25, 0xe7, 0xdb, + 0x7f, 0x21, 0x60, 0xff, 0x22, 0xe6, 0xff, 0xaf, + 0x32, 0xc0, 0xff, 0x3e, 0x4e, 0xd3, 0x7d, 0x3e, + 0x37, 0xd3, 0x7d, 0x3e, 0x45, 0xd3, 0x78, 0x0e, + 0x7d, 0x11, 0x01, 0x00, 0xed, 0x78, 0xf2, 0x2c, + 0xe0, 0x13, 0xed, 0x78, 0xfa, 0x31, 0xe0, 0x31, + 0x59, 0xe0, 0xc1, 0xe1, 0x37, 0xed, 0x52, 0x38, + 0xf9, 0xc1, 0x3e, 0x40, 0xd3, 0x7d, 0x78, 0xd3, + 0x7d, 0x3e, 0x37, 0xd3, 0x7d, 0x79, 0xd3, 0x78, + 0x3e, 0x64, 0x06, 0xc8, 0x10, 0xfe, 0x3d, 0x20, + 0xf9, 0x18, 0x1c, 0x16, 0x00, 0x0d, 0x4e, 0x2c, + 0x00, 0x1a, 0x4e, 0x57, 0x00, 0x34, 0x4e, 0xae, + 0x00, 0x68, 0x4e, 0x5c, 0x01, 0xd0, 0x4e, 0xb8, + 0x02, 0x68, 0x4f, 0xff, 0x7f, 0xd0, 0x4f, 0xdb, + 0x7c, 0xaf, 0x32, 0xfa, 0xff, 0x3c, 0x32, 0xc6, + 0xff, 0x31, 0xc0, 0xff, 0xcd, 0x2f, 0xe7, 0x0e, + 0x2e, 0xcd, 0x0c, 0xe0, 0xcd, 0x21, 0xe7, 0xfe, + 0x2e, 0x28, 0xee, 0xc5, 0xcd, 0x39, 0xe7, 0xcd, + 0xb6, 0xe7, 0xc1, 0x79, 0x21, 0x81, 0xe0, 0xe5, + 0x2a, 0xd2, 0xff, 0xed, 0x5b, 0xd0, 0xff, 0xfd, + 0x21, 0xc0, 0xff, 0xfe, 0x42, 0x20, 0x3c, 0xd5, + 0x3a, 0xc0, 0xff, 0xcb, 0x47, 0x28, 0x10, 0xed, + 0x5b, 0xc4, 0xff, 0x21, 0xc1, 0xff, 0x01, 0x03, + 0x00, 0xed, 0xb0, 0xaf, 0x32, 0xc0, 0xff, 0x3a, + 0xc7, 0xff, 0xa7, 0xe1, 0x28, 0x19, 0x22, 0xc4, + 0xff, 0xe5, 0x11, 0xc1, 0xff, 0x01, 0x03, 0x00, + 0xc5, 0xed, 0xb0, 0x21, 0xe8, 0xe0, 0xc1, 0xd1, + 0xed, 0xb0, 0x3e, 0x01, 0x32, 0xc0, 0xff, 0xc9, + 0xc3, 0xd3, 0xe5, 0xfe, 0x47, 0x20, 0x13, 0x3a, + 0xc7, 0xff, 0xa7, 0x28, 0x05, 0xed, 0x53, 0xfe, + 0xff, 0xf1, 0x3e, 0x01, 0x32, 0xc9, 0xff, 0xc3, + 0x14, 0xe6, 0xfe, 0x52, 0x20, 0x06, 0xcd, 0x16, + 0xe1, 0xc3, 0x2d, 0xf0, 0xfe, 0x57, 0x20, 0x27, + 0xcd, 0x16, 0xe1, 0xc3, 0x30, 0xf0, 0x3a, 0xc7, + 0xff, 0xfe, 0x05, 0xda, 0x49, 0xe4, 0xed, 0x53, + 0x40, 0x00, 0x7d, 0x21, 0x42, 0x00, 0x77, 0x23, + 0x3a, 0xd6, 0xff, 0x77, 0x23, 0x3a, 0xd4, 0xff, + 0x77, 0x23, 0x3a, 0xd8, 0xff, 0x77, 0xc9, 0xfe, + 0x5a, 0x20, 0x45, 0x3a, 0xc7, 0xff, 0x3d, 0xc2, + 0x49, 0xe4, 0x7b, 0x32, 0x42, 0x00, 0xaf, 0x32, + 0x44, 0x00, 0x3c, 0x32, 0x43, 0x00, 0xcd, 0x18, + 0xf0, 0xdb, 0x63, 0xf6, 0x50, 0xd3, 0x63, 0xcd, + 0x33, 0xf0, 0xcd, 0x6e, 0xe1, 0x28, 0x07, 0xcb, + 0xa7, 0xd3, 0x63, 0xcd, 0x33, 0xf0, 0xcd, 0x73, + 0xe1, 0x20, 0xe6, 0xc3, 0x18, 0xf0, 0xdb, 0x63, + 0xcb, 0x6f, 0xc9, 0x3a, 0x44, 0x00, 0x3c, 0x32, + 0x44, 0x00, 0x47, 0x3a, 0x40, 0xf0, 0xb8, 0xc9, + 0xfe, 0x51, 0x20, 0x34, 0x7b, 0x32, 0x42, 0x00, + 0xaf, 0x32, 0x44, 0x00, 0x3e, 0x01, 0x32, 0x43, + 0x00, 0x3a, 0x3f, 0xf0, 0x47, 0xcd, 0x6e, 0xe1, + 0x28, 0x02, 0xcb, 0x20, 0x78, 0x32, 0x45, 0x00, + 0x21, 0x00, 0x01, 0x22, 0x40, 0x00, 0xcd, 0x2d, + 0xf0, 0xcd, 0xbe, 0xe5, 0xcd, 0x73, 0xe1, 0x20, + 0xdb, 0x0e, 0x50, 0xcd, 0x0c, 0xe0, 0x18, 0xd0, + 0xfe, 0x4d, 0x20, 0x23, 0xcd, 0xcb, 0xe5, 0x44, + 0x4d, 0xeb, 0xed, 0x5b, 0xd4, 0xff, 0xb7, 0xed, + 0x52, 0x30, 0x0e, 0x2a, 0xd4, 0xff, 0x09, 0x2b, + 0x54, 0x5d, 0x2a, 0xd2, 0xff, 0xed, 0xb8, 0x18, + 0x05, 0x2a, 0xd0, 0xff, 0xed, 0xb0, 0xc9, 0xfe, + 0x43, 0xca, 0x03, 0xf0, 0xfe, 0x48, 0x20, 0x1d, + 0xe5, 0x19, 0x0e, 0x2b, 0xcd, 0x0c, 0xe0, 0xcd, + 0xf0, 0xe7, 0xcd, 0x39, 0xe7, 0xe1, 0xeb, 0xb7, + 0xed, 0x52, 0x0e, 0x2d, 0xcd, 0x0c, 0xe0, 0xcd, + 0xf0, 0xe7, 0xc3, 0x81, 0xe0, 0xfe, 0x58, 0x20, + 0x15, 0xaf, 0xbb, 0xc4, 0x3e, 0xe7, 0x3a, 0xc7, + 0xff, 0xfe, 0x02, 0x20, 0x06, 0x3a, 0xd2, 0xff, + 0x32, 0xc6, 0xff, 0xc3, 0x88, 0xe6, 0xfe, 0x49, + 0x20, 0x36, 0xfd, 0xcb, 0x0a, 0x86, 0x3a, 0xd0, + 0xff, 0x4f, 0x06, 0x01, 0x3a, 0xc7, 0xff, 0xfe, + 0x02, 0x38, 0x0b, 0x3a, 0xd2, 0xff, 0x47, 0xb7, + 0x20, 0x04, 0xfd, 0xcb, 0x0a, 0xc6, 0x79, 0xcd, + 0xf5, 0xe7, 0xed, 0x78, 0xcd, 0xf5, 0xe7, 0xcd, + 0xbe, 0xe5, 0xc5, 0xcd, 0x2f, 0xe7, 0xc1, 0xfd, + 0xcb, 0x0a, 0x46, 0x20, 0xe9, 0x10, 0xe7, 0xc9, + 0xfe, 0x4f, 0x20, 0x2e, 0xfd, 0xcb, 0x0a, 0x86, + 0x3a, 0xd0, 0xff, 0x4f, 0x3a, 0xd2, 0xff, 0x57, + 0x06, 0x01, 0x3a, 0xc7, 0xff, 0xfe, 0x03, 0x38, + 0x0b, 0x3a, 0xd4, 0xff, 0x47, 0xb7, 0x20, 0x04, + 0xfd, 0xcb, 0x0a, 0xc6, 0xed, 0x51, 0xcd, 0xbe, + 0xe5, 0xfd, 0xcb, 0x0a, 0x46, 0x20, 0xf5, 0x10, + 0xf3, 0xc9, 0xfe, 0x46, 0x20, 0x12, 0x3a, 0xd4, + 0xff, 0xe5, 0xcd, 0xcb, 0xe5, 0xe1, 0x12, 0xe5, + 0xb7, 0xed, 0x52, 0xe1, 0x13, 0x20, 0xf7, 0xc9, + 0xfe, 0x4c, 0x20, 0x4d, 0xcd, 0xcb, 0xe5, 0x3a, + 0xc7, 0xff, 0xd6, 0x03, 0xda, 0x49, 0xe4, 0x47, + 0x3c, 0x32, 0xc7, 0xff, 0x21, 0xd5, 0xff, 0x11, + 0xd6, 0xff, 0x1a, 0x77, 0x23, 0x13, 0x13, 0x10, + 0xf9, 0x3a, 0xc7, 0xff, 0x47, 0x2a, 0xd0, 0xff, + 0x11, 0xd4, 0xff, 0x1a, 0xbe, 0x20, 0x10, 0x23, + 0x13, 0x10, 0xf8, 0x2a, 0xd0, 0xff, 0xcd, 0xf0, + 0xe7, 0xcd, 0x2f, 0xe7, 0xcd, 0xbe, 0xe5, 0x2a, + 0xd2, 0xff, 0xed, 0x5b, 0xd0, 0xff, 0xb7, 0xed, + 0x52, 0xc8, 0x13, 0xed, 0x53, 0xd0, 0xff, 0x18, + 0xd0, 0xfe, 0x56, 0x20, 0x38, 0xcd, 0xcb, 0xe5, + 0xe5, 0xc1, 0xeb, 0xed, 0x5b, 0xd4, 0xff, 0x1a, + 0xed, 0xa1, 0x13, 0x20, 0x03, 0xe0, 0x18, 0xf7, + 0xf5, 0xc5, 0xd5, 0x2b, 0xcd, 0xf0, 0xe7, 0x7e, + 0x23, 0xcd, 0xf5, 0xe7, 0xd1, 0xd5, 0xe5, 0xeb, + 0x2b, 0xcd, 0xf0, 0xe7, 0x7e, 0xcd, 0x4d, 0xe7, + 0xcd, 0x2f, 0xe7, 0xe1, 0xd1, 0xc1, 0xf1, 0xe0, + 0xcd, 0xbe, 0xe5, 0x18, 0xd2, 0xfe, 0x54, 0x20, + 0x3f, 0xeb, 0x13, 0x06, 0x00, 0x2a, 0xd0, 0xff, + 0x7d, 0xac, 0xa8, 0x77, 0x23, 0xe5, 0xb7, 0xed, + 0x52, 0xe1, 0x20, 0xf4, 0x2a, 0xd0, 0xff, 0x7d, + 0xac, 0xa8, 0xbe, 0xc4, 0x61, 0xe3, 0x23, 0xe5, + 0xb7, 0xed, 0x52, 0xe1, 0x20, 0xf1, 0x04, 0xcd, + 0xbe, 0xe5, 0x0e, 0x50, 0xcd, 0x0c, 0xe0, 0x18, + 0xd4, 0xf5, 0xcd, 0xf0, 0xe7, 0xf1, 0xcd, 0xf5, + 0xe7, 0x7e, 0xcd, 0xf5, 0xe7, 0xc3, 0x2f, 0xe7, + 0xfe, 0x44, 0x20, 0x5d, 0xfd, 0xcb, 0x0a, 0x86, + 0xeb, 0x3a, 0xc7, 0xff, 0xfe, 0x02, 0x30, 0x09, + 0x11, 0xff, 0x00, 0xe5, 0x19, 0x22, 0xd2, 0xff, + 0xe1, 0xcd, 0x2f, 0xe7, 0xe5, 0xc1, 0xe5, 0x2a, + 0xd2, 0xff, 0xb7, 0xed, 0x42, 0xda, 0x49, 0xe4, + 0x01, 0x0f, 0x00, 0xb7, 0xed, 0x42, 0x06, 0x10, + 0x28, 0x06, 0xd2, 0xac, 0xe3, 0x7d, 0x80, 0x47, + 0xfd, 0xcb, 0x0a, 0xc6, 0xe1, 0xc5, 0xcd, 0xf0, + 0xe7, 0xc1, 0xcd, 0xa8, 0xe6, 0xcd, 0xbe, 0xe5, + 0xfd, 0xcb, 0x0a, 0x46, 0x28, 0xcb, 0xcd, 0x09, + 0xe0, 0xfe, 0x2e, 0xc8, 0xfe, 0x20, 0x20, 0xf6, + 0xfd, 0xcb, 0x0a, 0x86, 0xcd, 0x2f, 0xe7, 0x18, + 0xaf, 0xfe, 0x45, 0x20, 0x38, 0xeb, 0xcd, 0xf0, + 0xe7, 0x7e, 0xcd, 0x4d, 0xe7, 0x0e, 0x2d, 0xcd, + 0x0c, 0xe0, 0xe5, 0xcd, 0xb6, 0xe7, 0xe1, 0x3a, + 0xc8, 0xff, 0xfe, 0x2e, 0xc8, 0x3a, 0xc7, 0xff, + 0xe6, 0x03, 0x28, 0x0f, 0x3a, 0xd0, 0xff, 0x77, + 0x23, 0x3a, 0xc8, 0xff, 0xfe, 0x0d, 0x28, 0xd6, + 0x2b, 0x18, 0xd3, 0x3a, 0xc8, 0xff, 0xfe, 0x5e, + 0x28, 0xf6, 0x23, 0x18, 0xc9, 0xfe, 0x50, 0x20, + 0x34, 0x3a, 0xd0, 0xff, 0x4f, 0x79, 0xcd, 0xf5, + 0xe7, 0xed, 0x78, 0xcd, 0xf5, 0xe7, 0xc5, 0xcd, + 0xb6, 0xe7, 0xc1, 0x3a, 0xc8, 0xff, 0xfe, 0x2e, + 0xc8, 0x67, 0x3a, 0xc7, 0xff, 0xa7, 0x28, 0x0d, + 0x3a, 0xd0, 0xff, 0xed, 0x79, 0x3e, 0x5e, 0xbc, + 0x28, 0xdb, 0x0c, 0x18, 0xd8, 0x3e, 0x5e, 0xbc, + 0x20, 0xf8, 0x0d, 0x18, 0xd0, 0xfe, 0x53, 0x28, + 0x08, 0x0e, 0x3f, 0xcd, 0x0c, 0xe0, 0xc3, 0x81, + 0xe0, 0xf1, 0xcd, 0x3e, 0xe7, 0x3a, 0xc7, 0xff, + 0xa7, 0x28, 0x04, 0xeb, 0x22, 0xfe, 0xff, 0x3a, + 0xd2, 0xff, 0xa7, 0x20, 0x01, 0x3c, 0x32, 0xc9, + 0xff, 0xaf, 0x32, 0xc7, 0xff, 0xcd, 0xaf, 0xe0, + 0xed, 0x5b, 0xfe, 0xff, 0x1a, 0xfe, 0x40, 0x38, + 0x05, 0xfe, 0xc0, 0xda, 0x53, 0xe5, 0xe6, 0x03, + 0x47, 0x1a, 0x1f, 0x1f, 0xe6, 0x1f, 0xc5, 0x01, + 0x9a, 0xe5, 0x81, 0x6f, 0x60, 0xc1, 0x7e, 0x04, + 0x10, 0x29, 0xe6, 0x03, 0xca, 0x3e, 0xe5, 0xf5, + 0x1a, 0x2a, 0xf4, 0xff, 0xfe, 0xe9, 0xca, 0x07, + 0xe6, 0xfe, 0xc3, 0x28, 0x70, 0xfe, 0xcd, 0x28, + 0x6c, 0xfe, 0xc9, 0x28, 0x5f, 0xfe, 0x10, 0x20, + 0x10, 0x21, 0xf9, 0xff, 0x35, 0x20, 0x47, 0x34, + 0xc3, 0x74, 0xe5, 0xcb, 0x3f, 0xcb, 0x3f, 0x18, + 0xcf, 0xfe, 0x18, 0x28, 0x39, 0xfe, 0x80, 0x30, + 0x0a, 0xee, 0x20, 0x47, 0xe6, 0x67, 0x20, 0xe8, + 0x78, 0x18, 0x0d, 0xe6, 0xc7, 0xfe, 0xc2, 0x28, + 0x06, 0xe6, 0xc3, 0xfe, 0xc0, 0x20, 0x43, 0x1a, + 0xe6, 0x30, 0x21, 0xb9, 0xe5, 0x23, 0xd6, 0x10, + 0x30, 0xfb, 0x3a, 0xfc, 0xff, 0xa6, 0x1a, 0x28, + 0x01, 0x2f, 0xcb, 0x5f, 0x20, 0xc2, 0xf1, 0xf5, + 0xfe, 0x02, 0x38, 0x10, 0x20, 0x17, 0x13, 0x1a, + 0x13, 0x6f, 0x17, 0x26, 0x00, 0x30, 0x01, 0x25, + 0x19, 0x18, 0x30, 0x1b, 0x2a, 0xe6, 0xff, 0x7e, + 0x23, 0x66, 0x6f, 0x18, 0x64, 0x1a, 0xcb, 0x57, + 0x13, 0x1a, 0x6f, 0x13, 0x1a, 0x67, 0x20, 0x59, + 0x18, 0x19, 0x1a, 0xe6, 0xc7, 0xfe, 0xc7, 0x1a, + 0x20, 0x07, 0xe6, 0x38, 0x6f, 0x26, 0x00, 0x18, + 0x48, 0xfe, 0xfb, 0x20, 0x3f, 0x2f, 0x32, 0xfa, + 0xff, 0xeb, 0x23, 0xc3, 0x07, 0xe6, 0x1a, 0x13, + 0xfe, 0xed, 0x20, 0x13, 0x06, 0x03, 0x1a, 0xe6, + 0xf7, 0xfe, 0x45, 0x28, 0xbe, 0xe6, 0xc7, 0xfe, + 0x43, 0x28, 0x22, 0x06, 0x01, 0x18, 0x1e, 0xfe, + 0xdd, 0x2a, 0xea, 0xff, 0x28, 0x03, 0x2a, 0xe8, + 0xff, 0x1a, 0xfe, 0xe9, 0x28, 0xd5, 0x21, 0x95, + 0xe5, 0x01, 0x05, 0x00, 0xed, 0xb1, 0x20, 0x0f, + 0x04, 0x04, 0x04, 0xc5, 0xc1, 0xeb, 0x23, 0x10, + 0xfd, 0xcd, 0xce, 0xe0, 0xc3, 0x14, 0xe6, 0xe6, + 0xfe, 0xfe, 0x34, 0x28, 0xec, 0x1a, 0xe6, 0x07, + 0xfe, 0x06, 0x28, 0xe5, 0x1a, 0xe6, 0xf8, 0xfe, + 0x70, 0x28, 0xde, 0x18, 0xdd, 0x21, 0x22, 0x2a, + 0x36, 0xcb, 0x5d, 0x65, 0x55, 0x65, 0x5e, 0x65, + 0x56, 0x65, 0x7e, 0x65, 0x76, 0x65, 0x7e, 0x65, + 0x76, 0x65, 0xf5, 0x67, 0xb5, 0x6f, 0xb5, 0x67, + 0xb5, 0x63, 0x75, 0x67, 0x75, 0x63, 0x75, 0x67, + 0x75, 0x63, 0x40, 0x01, 0x04, 0x80, 0xcd, 0x06, + 0xe0, 0xc8, 0xcd, 0x09, 0xe0, 0xfe, 0x2e, 0xc0, + 0xc3, 0x81, 0xe0, 0xb7, 0xed, 0x52, 0xda, 0x49, + 0xe4, 0x23, 0xc9, 0xed, 0x73, 0xe6, 0xff, 0x31, + 0x00, 0x00, 0xf5, 0xf5, 0xb7, 0xed, 0x57, 0xf5, + 0xf3, 0xc5, 0xd5, 0xe5, 0xd9, 0x08, 0xf5, 0xc5, + 0xd5, 0xe5, 0x3a, 0xfa, 0xff, 0xe6, 0x04, 0x32, + 0xfa, 0xff, 0xd9, 0x08, 0xdd, 0xe5, 0xfd, 0xe5, + 0x21, 0xc1, 0xff, 0xed, 0x5b, 0xc4, 0xff, 0x01, + 0x03, 0x00, 0xed, 0xb0, 0x2a, 0xc4, 0xff, 0x3e, + 0x80, 0x32, 0xc0, 0xff, 0x22, 0xfe, 0xff, 0x31, + 0xc0, 0xff, 0x18, 0x32, 0xed, 0x7b, 0xe6, 0xff, + 0x2a, 0xfe, 0xff, 0xe5, 0x2a, 0xfc, 0xff, 0xe5, + 0xed, 0x73, 0xe4, 0xff, 0x31, 0xe8, 0xff, 0xfd, + 0xe1, 0xdd, 0xe1, 0xd9, 0x08, 0xe1, 0xd1, 0xc1, + 0xf1, 0xd9, 0x08, 0xe1, 0xd1, 0xc1, 0xf1, 0xed, + 0x47, 0xed, 0x7b, 0xe4, 0xff, 0xea, 0x43, 0xe6, + 0xf1, 0xf3, 0xc9, 0xf1, 0xfb, 0xc9, 0xcd, 0x88, + 0xe6, 0x3a, 0xc0, 0xff, 0x47, 0xaf, 0x32, 0xc0, + 0xff, 0x21, 0xc9, 0xff, 0x35, 0x28, 0x07, 0xcd, + 0x06, 0xe0, 0x20, 0x02, 0x18, 0x23, 0xcd, 0x21, + 0xe7, 0xfe, 0x2e, 0xca, 0x81, 0xe0, 0xfe, 0x0d, + 0x28, 0x15, 0xfe, 0x20, 0x20, 0xf0, 0xaf, 0xbe, + 0x36, 0x00, 0x20, 0xea, 0xcd, 0x2f, 0xe7, 0xcd, + 0x3e, 0xe7, 0x3e, 0x0b, 0xc3, 0x66, 0xe4, 0x36, + 0x01, 0xcd, 0x2f, 0xe7, 0x7e, 0xc3, 0x66, 0xe4, + 0x2a, 0xfe, 0xff, 0xcd, 0xf0, 0xe7, 0x3a, 0xc6, + 0xff, 0x1f, 0x06, 0x01, 0x30, 0x02, 0x06, 0x0c, + 0x21, 0xfd, 0xff, 0x7e, 0xcd, 0x4d, 0xe7, 0x2b, + 0x7e, 0xcd, 0xf5, 0xe7, 0x2b, 0x10, 0xf4, 0xc9, + 0xc5, 0xe5, 0x7e, 0xcd, 0xf5, 0xe7, 0x23, 0x10, + 0xf9, 0xcd, 0x39, 0xe7, 0xcd, 0x39, 0xe7, 0xe1, + 0xc1, 0x7e, 0xe6, 0x7f, 0x4f, 0xfe, 0x20, 0x38, + 0x04, 0xfe, 0x7b, 0x38, 0x02, 0x0e, 0x2e, 0xcd, + 0x0c, 0xe0, 0x23, 0x10, 0xec, 0xc9, 0x50, 0x43, + 0x20, 0x20, 0x20, 0x41, 0x46, 0x20, 0x20, 0x49, + 0x20, 0x49, 0x46, 0x20, 0x20, 0x42, 0x43, 0x20, + 0x20, 0x20, 0x44, 0x45, 0x20, 0x20, 0x20, 0x48, + 0x4c, 0x20, 0x20, 0x41, 0x27, 0x46, 0x27, 0x20, + 0x42, 0x27, 0x43, 0x27, 0x20, 0x44, 0x27, 0x45, + 0x27, 0x20, 0x48, 0x27, 0x4c, 0x27, 0x20, 0x20, + 0x49, 0x58, 0x20, 0x20, 0x20, 0x49, 0x59, 0x20, + 0x20, 0x20, 0x53, 0x50, 0x0d, 0x0a, 0x03, 0xdb, + 0x7d, 0xe6, 0x02, 0xc8, 0x3e, 0xff, 0xc9, 0xcd, + 0x0f, 0xe7, 0x28, 0xfb, 0xdb, 0x7c, 0xe6, 0x7f, + 0xc9, 0xcd, 0x09, 0xe0, 0x4f, 0xdb, 0x7d, 0xe6, + 0x01, 0x28, 0xfa, 0x79, 0xd3, 0x7c, 0xc9, 0x0e, + 0x0d, 0xcd, 0x0c, 0xe0, 0x0e, 0x0a, 0xc3, 0x0c, + 0xe0, 0x0e, 0x20, 0xc3, 0x0c, 0xe0, 0x21, 0xce, + 0xe6, 0x7e, 0xfe, 0x03, 0xc8, 0x4f, 0xcd, 0x0c, + 0xe0, 0x23, 0x18, 0xf5, 0x00, 0xf5, 0x0f, 0x0f, + 0x0f, 0x0f, 0xcd, 0x56, 0xe7, 0xf1, 0xe6, 0x0f, + 0xc6, 0x90, 0x27, 0xce, 0x40, 0x27, 0x4f, 0xc3, + 0x0c, 0xe0, 0xd6, 0x30, 0xfe, 0x0a, 0xf8, 0xd6, + 0x07, 0xc9, 0xfe, 0x30, 0x38, 0x0e, 0xfe, 0x3a, + 0x38, 0x08, 0xfe, 0x40, 0x38, 0x06, 0xfe, 0x47, + 0x30, 0x02, 0xaf, 0xc9, 0xaf, 0x3c, 0xc9, 0xfe, + 0x20, 0xc8, 0xfe, 0x5e, 0x28, 0x08, 0xfe, 0x2e, + 0xca, 0x81, 0xe0, 0xfe, 0x0d, 0xc0, 0xc5, 0xcd, + 0x2f, 0xe7, 0xc1, 0xaf, 0xc9, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0x21, 0x00, 0x00, 0x45, 0xcd, 0x21, + 0xe7, 0x04, 0xcd, 0x7f, 0xe7, 0xc8, 0xcd, 0x6a, + 0xe7, 0xc0, 0x79, 0xcd, 0x62, 0xe7, 0x29, 0x29, + 0x29, 0x29, 0x85, 0x6f, 0x18, 0xe8, 0xaf, 0x21, + 0xd0, 0xff, 0xe5, 0xdd, 0xe1, 0x77, 0x01, 0x09, + 0x00, 0x11, 0xd1, 0xff, 0xed, 0xb0, 0x32, 0xc7, + 0xff, 0xcd, 0x9a, 0xe7, 0xc2, 0x49, 0xe4, 0x79, + 0x32, 0xc8, 0xff, 0xfe, 0x20, 0x28, 0x02, 0x05, + 0xc8, 0xdd, 0x75, 0x00, 0xdd, 0x74, 0x01, 0x3a, + 0xc7, 0xff, 0x3c, 0x32, 0xc7, 0xff, 0xdd, 0x23, + 0xdd, 0x23, 0x79, 0xfe, 0x20, 0x28, 0xda, 0xc9, + 0x7c, 0xcd, 0x4d, 0xe7, 0x7d, 0xc5, 0xcd, 0x4d, + 0xe7, 0xcd, 0x39, 0xe7, 0xc1, 0xc9, 0xff, 0xff, + }; + +/* SBC-200 DDBIOS 3.3 @ F000 */ +static uint8 sbc200_ddb_rom[SBC200_DDB_SIZE] = { + 0x00, 0x1e, 0x00, 0xc3, 0x6c, 0xf0, 0xc3, 0x06, + 0xe0, 0xc3, 0x09, 0xe0, 0xc3, 0x0c, 0xe0, 0xc3, + 0x16, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc3, 0x65, 0xf1, 0xc3, 0x74, 0xf1, 0xc3, 0x79, + 0xf1, 0xc3, 0x7e, 0xf1, 0xc3, 0x83, 0xf1, 0xc3, + 0x88, 0xf1, 0xc3, 0xa4, 0xf1, 0xc3, 0xbe, 0xf3, + 0xc3, 0xcb, 0xf3, 0xc3, 0x0f, 0xf4, 0x00, 0x3a, + 0x80, 0x00, 0x5f, 0x16, 0x00, 0x21, 0x80, 0x1a, + 0x4d, 0x49, 0x06, 0x0b, 0x1b, 0x08, 0x18, 0x1c, + 0x32, 0x4d, 0x54, 0x08, 0x16, 0x10, 0x08, 0x18, + 0x1c, 0x1a, 0x4d, 0x2e, 0x08, 0x16, 0x36, 0x08, + 0x18, 0x1c, 0x12, 0x23, 0x0c, 0x06, 0x0b, 0x08, + 0x0b, 0x1b, 0x1f, 0x1d, 0x23, 0x54, 0x08, 0x16, + 0x10, 0x0b, 0x1b, 0x1f, 0x31, 0x80, 0x00, 0xdb, + 0x7f, 0x7b, 0xfe, 0x04, 0xd2, 0x03, 0xe8, 0xf5, + 0x3e, 0xef, 0xe5, 0xed, 0x73, 0x4d, 0x00, 0xe1, + 0x0f, 0x30, 0x1a, 0xd3, 0x63, 0xcd, 0xb2, 0xf3, + 0xe6, 0x04, 0x28, 0x0d, 0xaf, 0xd3, 0x65, 0x3e, + 0x02, 0x32, 0x44, 0x00, 0x3e, 0x1a, 0xcd, 0x05, + 0xf3, 0xdb, 0x63, 0x18, 0xe3, 0xf1, 0xf5, 0xcd, + 0x65, 0xf1, 0xf1, 0xcd, 0x16, 0xf1, 0x20, 0x19, + 0xcd, 0xcb, 0xf0, 0x21, 0x80, 0x00, 0x7e, 0xe6, + 0xce, 0x20, 0x05, 0xcb, 0x46, 0xc2, 0x80, 0x00, + 0x3e, 0x31, 0x21, 0x00, 0xe8, 0xbe, 0xca, 0x00, + 0xe8, 0x3e, 0xe0, 0x21, 0x02, 0xe0, 0xbe, 0xca, + 0x03, 0xe0, 0x76, 0x21, 0x80, 0x00, 0x22, 0x40, + 0x00, 0xaf, 0x32, 0x44, 0x00, 0x3c, 0x32, 0x43, + 0x00, 0xcd, 0x88, 0xf1, 0xc8, 0x18, 0xe2, 0x3a, + 0x42, 0x00, 0x5f, 0x3e, 0x04, 0xb8, 0x28, 0x19, + 0x3d, 0xb8, 0x28, 0x0c, 0x3d, 0xb8, 0x28, 0x18, + 0x3d, 0xb8, 0x28, 0x1b, 0xf6, 0x01, 0xe1, 0xc9, + 0x7b, 0xcb, 0xb7, 0xcb, 0xbf, 0x32, 0x42, 0x00, + 0xc9, 0x7b, 0xcb, 0xff, 0x32, 0x42, 0x00, 0xc9, + 0x7b, 0xcb, 0xef, 0x32, 0x42, 0x00, 0xc9, 0x7b, + 0xcb, 0xf7, 0x32, 0x42, 0x00, 0xc9, 0x06, 0x04, + 0xe6, 0x0f, 0xf6, 0x40, 0x32, 0x42, 0x00, 0x2a, + 0x40, 0x00, 0x22, 0x51, 0x00, 0xcd, 0x48, 0xf1, + 0x2a, 0x51, 0x00, 0x22, 0x40, 0x00, 0x3a, 0x42, + 0x00, 0xc0, 0x30, 0x02, 0xcb, 0xff, 0x5f, 0x3a, + 0x49, 0x00, 0xb7, 0x7b, 0xcb, 0xa7, 0xf2, 0x43, + 0xf1, 0xcb, 0xe7, 0x32, 0x42, 0x00, 0xaf, 0xc9, + 0xc5, 0xdd, 0xe5, 0xe5, 0xed, 0x73, 0x4d, 0x00, + 0xe1, 0xcd, 0xdf, 0xf1, 0xcd, 0x32, 0xf2, 0xdd, + 0xe1, 0xc1, 0x3a, 0x4b, 0x00, 0x0f, 0xc8, 0x05, + 0xcd, 0xdf, 0xf0, 0x18, 0xe3, 0xed, 0x73, 0x4d, + 0x00, 0xdd, 0xe5, 0xcd, 0xdf, 0xf1, 0xcd, 0xd3, + 0xf1, 0xdd, 0xe1, 0xc9, 0x79, 0x32, 0x42, 0x00, + 0xc9, 0x79, 0x32, 0x44, 0x00, 0xc9, 0x79, 0x32, + 0x43, 0x00, 0xc9, 0xed, 0x43, 0x40, 0x00, 0xc9, + 0x01, 0x01, 0x03, 0xed, 0x43, 0x56, 0x00, 0xdd, + 0xe5, 0xdd, 0x2a, 0x53, 0x00, 0xc5, 0xcd, 0x67, + 0xf2, 0xc1, 0x28, 0x05, 0xcd, 0xbd, 0xf1, 0x18, + 0xf4, 0xdd, 0xe1, 0xc9, 0x01, 0x01, 0x03, 0xed, + 0x43, 0x56, 0x00, 0xdd, 0xe5, 0xdd, 0x2a, 0x53, + 0x00, 0xc5, 0xcd, 0xb8, 0xf2, 0xc1, 0x28, 0xe9, + 0xcd, 0xbd, 0xf1, 0x18, 0xf4, 0x10, 0x13, 0x3a, + 0x57, 0x00, 0x47, 0x0d, 0xc3, 0xcd, 0xf1, 0xf1, + 0xdd, 0xe1, 0xaf, 0x3c, 0xc9, 0xc5, 0xcd, 0xd3, + 0xf1, 0xc1, 0xc9, 0xed, 0x73, 0x4d, 0x00, 0xdd, + 0x7e, 0x06, 0xcd, 0x05, 0xf3, 0xaf, 0xc9, 0x11, + 0x42, 0x00, 0x1a, 0xe6, 0xe0, 0x4f, 0x1a, 0xe6, + 0x03, 0x47, 0x3e, 0x01, 0x28, 0x03, 0x07, 0x10, + 0xfd, 0xb1, 0xe6, 0x7f, 0x47, 0x79, 0xdd, 0x21, + 0x3f, 0xf0, 0xfe, 0x00, 0x28, 0x1c, 0xdd, 0x21, + 0x48, 0xf0, 0xfe, 0x40, 0x28, 0x14, 0xdd, 0x21, + 0x51, 0xf0, 0xfe, 0xc0, 0x28, 0x0c, 0xdd, 0x21, + 0x5a, 0xf0, 0xfe, 0x20, 0x28, 0x04, 0xdd, 0x21, + 0x63, 0xf0, 0xdd, 0x22, 0x53, 0x00, 0xc5, 0xf1, + 0x2f, 0xd3, 0x63, 0x1a, 0x32, 0x55, 0x00, 0xcd, + 0x54, 0xf2, 0xdb, 0x64, 0xe6, 0x80, 0xc2, 0xdc, + 0xf2, 0xc9, 0xcd, 0x3d, 0xf3, 0x21, 0x48, 0x00, + 0x01, 0x67, 0x06, 0x3e, 0xf8, 0x32, 0x46, 0x00, + 0xcd, 0x70, 0xf3, 0x3e, 0xc0, 0xcd, 0x72, 0xf2, + 0x3a, 0x48, 0x00, 0xfe, 0x4d, 0xd2, 0xf9, 0xf2, + 0xd3, 0x65, 0xaf, 0xc9, 0x3a, 0x42, 0x00, 0xcb, + 0x6f, 0x3e, 0x27, 0x28, 0x02, 0x3e, 0x3c, 0x06, + 0x00, 0x10, 0xfe, 0x3d, 0x20, 0xf9, 0xc9, 0xcd, + 0x97, 0xf2, 0x3e, 0x88, 0xcd, 0x8b, 0xf2, 0x2a, + 0x40, 0x00, 0x32, 0x4c, 0x00, 0xed, 0x5b, 0x4c, + 0x00, 0xd5, 0xf3, 0xd3, 0x64, 0x18, 0x00, 0x18, + 0x00, 0xed, 0xb2, 0xd1, 0xed, 0x53, 0x4c, 0x00, + 0xfb, 0x18, 0x45, 0x67, 0x3a, 0x49, 0x00, 0x6f, + 0xe6, 0x01, 0x7c, 0xc8, 0xcb, 0xcf, 0xc9, 0xe1, + 0xed, 0x73, 0x4d, 0x00, 0xe5, 0x3a, 0x42, 0x00, + 0x57, 0x3a, 0x55, 0x00, 0xba, 0x28, 0x06, 0xcd, + 0xdf, 0xf1, 0xcd, 0x32, 0xf2, 0xcd, 0xec, 0xf2, + 0x3e, 0xfe, 0x32, 0x46, 0x00, 0xc3, 0x77, 0xf3, + 0xcd, 0x97, 0xf2, 0x3e, 0xa8, 0xcd, 0x8b, 0xf2, + 0x2a, 0x40, 0x00, 0x32, 0x4c, 0x00, 0xf3, 0xd3, + 0x64, 0x18, 0x00, 0x18, 0x00, 0xed, 0xb3, 0xfb, + 0xcd, 0x3d, 0xf3, 0xdb, 0x64, 0x57, 0x3a, 0x46, + 0x00, 0xa2, 0xc8, 0x7a, 0x32, 0x47, 0x00, 0xcd, + 0x54, 0xf2, 0xf6, 0x01, 0xed, 0x7b, 0x4d, 0x00, + 0xcd, 0xf4, 0xf7, 0xc9, 0xcd, 0x2a, 0xf2, 0xdd, + 0x7e, 0x01, 0x4f, 0x3a, 0x44, 0x00, 0xb9, 0x38, + 0x04, 0x3e, 0x0f, 0x18, 0xdf, 0x4f, 0xdb, 0x65, + 0xb9, 0xc8, 0xdd, 0x7e, 0x08, 0x32, 0x4c, 0x00, + 0x06, 0xd2, 0x10, 0xfe, 0xcd, 0x3d, 0xf3, 0x3a, + 0x44, 0x00, 0xd3, 0x67, 0x3e, 0x80, 0x32, 0x46, + 0x00, 0x3a, 0x4c, 0x00, 0xd3, 0x64, 0x06, 0x0a, + 0x10, 0xfe, 0xcd, 0xd0, 0xf2, 0xcd, 0x54, 0xf2, + 0x3a, 0x4c, 0x00, 0xdd, 0xbe, 0x06, 0xc8, 0xdb, + 0x64, 0xe6, 0x10, 0x20, 0x04, 0xdb, 0x65, 0xb9, + 0xc8, 0x3e, 0x20, 0x18, 0x9f, 0x1e, 0x00, 0xc5, + 0x0e, 0x02, 0xdb, 0x64, 0xe6, 0x01, 0x28, 0x20, + 0x10, 0xf8, 0x1d, 0x20, 0xf5, 0x0d, 0x20, 0xf2, + 0xc1, 0xdb, 0x63, 0xf6, 0x80, 0xd3, 0x60, 0x10, + 0xfe, 0xdb, 0x60, 0xcd, 0xb2, 0xf3, 0xdd, 0x7e, + 0x06, 0xcd, 0x05, 0xf3, 0x3e, 0xfe, 0x18, 0xd3, + 0xc1, 0xdb, 0x63, 0xf6, 0x80, 0xd3, 0x63, 0xc9, + 0xdb, 0x63, 0xe6, 0x7f, 0xd3, 0x63, 0xc9, 0x06, + 0x00, 0xdd, 0x7e, 0x00, 0x3c, 0x4f, 0x3a, 0x43, + 0x00, 0xb9, 0x38, 0x04, 0x06, 0x10, 0x0d, 0x91, + 0xf5, 0xcd, 0x9c, 0xf3, 0xf1, 0xd3, 0x66, 0x01, + 0x67, 0x80, 0x3a, 0x42, 0x00, 0x07, 0x30, 0xd8, + 0x06, 0x00, 0x18, 0xd4, 0xdb, 0x63, 0x2f, 0x5f, + 0xe6, 0x10, 0xb8, 0xc8, 0x7b, 0xe6, 0x6f, 0xb0, + 0x2f, 0xd3, 0x63, 0x06, 0xd2, 0x10, 0xfe, 0xc3, + 0x32, 0xf2, 0x3e, 0xd0, 0xd3, 0x64, 0x3e, 0x0a, + 0x3d, 0x20, 0xfd, 0xdb, 0x64, 0xc9, 0xcd, 0xf4, + 0xf7, 0xcd, 0x88, 0xf1, 0xc0, 0xcd, 0xd8, 0xf3, + 0x20, 0xf7, 0xc9, 0xcd, 0xf4, 0xf7, 0xcd, 0xa4, + 0xf1, 0xc0, 0xcd, 0xd8, 0xf3, 0x20, 0xf7, 0xc9, + 0x2a, 0x40, 0x00, 0x11, 0x80, 0x00, 0x3a, 0x42, + 0x00, 0x07, 0x30, 0x03, 0x11, 0x00, 0x01, 0x19, + 0x22, 0x40, 0x00, 0x21, 0x45, 0x00, 0x35, 0xc8, + 0x2b, 0x2b, 0x34, 0x3a, 0x49, 0x00, 0xb7, 0xdd, + 0xe5, 0xdd, 0x2a, 0x53, 0x00, 0xdd, 0x7e, 0x00, + 0xdd, 0xe1, 0xf2, 0x06, 0xf4, 0x07, 0x3c, 0xbe, + 0xc0, 0x36, 0x01, 0x23, 0x34, 0xb7, 0xc9, 0xcd, + 0xf4, 0xf7, 0xed, 0x73, 0x51, 0x00, 0xfd, 0xe5, + 0xdd, 0xe5, 0xe5, 0x21, 0x00, 0x08, 0x22, 0x40, + 0x00, 0xed, 0x73, 0x4d, 0x00, 0xe1, 0xcd, 0xdf, + 0xf1, 0xcc, 0xd3, 0xf1, 0x20, 0x30, 0xcd, 0x65, + 0xf4, 0xcd, 0xd4, 0xf4, 0xaf, 0xcd, 0x08, 0xf5, + 0xc5, 0xcd, 0x5c, 0xf5, 0xcd, 0xa5, 0xf5, 0xc1, + 0x20, 0x05, 0xdd, 0xbe, 0x01, 0x20, 0xee, 0x3e, + 0x4c, 0x32, 0x44, 0x00, 0xdb, 0x63, 0xcb, 0xaf, + 0xd3, 0x63, 0x11, 0x55, 0x00, 0x1a, 0xcb, 0xef, + 0x12, 0xdd, 0xe1, 0xfd, 0xe1, 0xc9, 0xdd, 0xe1, + 0xed, 0x7b, 0x51, 0x00, 0xc9, 0x2a, 0x40, 0x00, + 0xdd, 0x7e, 0x04, 0x0f, 0x3e, 0x4e, 0xd2, 0x73, + 0xf4, 0x3e, 0xff, 0x5f, 0xdd, 0x46, 0x02, 0xcd, + 0xcf, 0xf4, 0xdd, 0x46, 0x05, 0xcd, 0xcf, 0xf4, + 0xaf, 0xdd, 0x46, 0x03, 0xcd, 0xcf, 0xf4, 0xcd, + 0xc8, 0xf4, 0x3e, 0xfe, 0x77, 0x23, 0xe5, 0xd9, + 0xd1, 0xd9, 0xaf, 0x06, 0x04, 0xcd, 0xcf, 0xf4, + 0x3e, 0xf7, 0x77, 0x23, 0xdd, 0x46, 0x04, 0x78, + 0xcb, 0x3f, 0x3c, 0x4f, 0x7b, 0xcd, 0xcf, 0xf4, + 0x41, 0xaf, 0xcd, 0xcf, 0xf4, 0xcd, 0xc8, 0xf4, + 0x3e, 0xfb, 0x77, 0x23, 0x06, 0x80, 0x3a, 0x42, + 0x00, 0x07, 0x30, 0x02, 0x06, 0x00, 0x3e, 0xe5, + 0xcd, 0xcf, 0xf4, 0x3e, 0xf7, 0x77, 0x23, 0xc9, + 0x7b, 0xb7, 0xf8, 0x3e, 0xf5, 0x06, 0x03, 0x77, + 0x23, 0x10, 0xfc, 0xc9, 0xd9, 0xdd, 0x46, 0x00, + 0x05, 0xd9, 0xed, 0x5b, 0x40, 0x00, 0xe5, 0xb7, + 0xdd, 0x4e, 0x02, 0x06, 0x00, 0xeb, 0x09, 0xeb, + 0xed, 0x52, 0xe5, 0xc1, 0xe1, 0xc5, 0xeb, 0xd9, + 0xd9, 0xed, 0xb0, 0xc1, 0xc5, 0xd9, 0x10, 0xf8, + 0xd9, 0xc1, 0xeb, 0x3a, 0x42, 0x00, 0xcb, 0x67, + 0x3e, 0x80, 0x20, 0x01, 0xaf, 0x6f, 0x65, 0xc9, + 0xe5, 0xc5, 0xd9, 0xc1, 0xe1, 0xd5, 0xfd, 0xe1, + 0xd5, 0x08, 0x16, 0x01, 0xdd, 0x7e, 0x00, 0xfe, + 0x1a, 0x3e, 0x01, 0x28, 0x08, 0xdd, 0x7e, 0x00, + 0xcb, 0x3f, 0x3c, 0x30, 0x26, 0x08, 0xfd, 0x77, + 0x00, 0xb5, 0xf2, 0x32, 0xf5, 0xfd, 0x74, 0x01, + 0xcb, 0xbf, 0x08, 0xfd, 0x77, 0x02, 0xf5, 0x3a, + 0x42, 0x00, 0x07, 0x30, 0x05, 0x3e, 0x01, 0xfd, + 0x77, 0x03, 0xf1, 0xdd, 0xbe, 0x00, 0x28, 0x10, + 0xfd, 0x09, 0x3c, 0x5f, 0x3e, 0x1a, 0xdd, 0xbe, + 0x00, 0x7b, 0x28, 0xd1, 0x7a, 0x53, 0x18, 0xcd, + 0x08, 0xd1, 0xd9, 0xc9, 0xd5, 0xc5, 0xd9, 0x08, + 0xc1, 0xd5, 0x5f, 0x51, 0xdd, 0x46, 0x02, 0x0e, + 0x67, 0x2a, 0x40, 0x00, 0x3e, 0x80, 0x32, 0x46, + 0x00, 0xcd, 0x70, 0xf3, 0x3a, 0x42, 0x00, 0x07, + 0x3e, 0x00, 0x30, 0x02, 0x3e, 0x01, 0x32, 0x50, + 0x00, 0x3e, 0xf4, 0x32, 0x4c, 0x00, 0xf3, 0xd3, + 0x64, 0x3a, 0x50, 0x00, 0xed, 0xb3, 0x42, 0xed, + 0xb3, 0xb7, 0x28, 0x02, 0xed, 0xb3, 0x42, 0x1d, + 0x20, 0xf5, 0xd1, 0xe1, 0x7e, 0xed, 0x79, 0x10, + 0xfc, 0xfb, 0xd9, 0x08, 0xc9, 0x08, 0x3e, 0x2f, + 0x10, 0xfe, 0x3d, 0x20, 0xfb, 0xdb, 0x63, 0xf6, + 0x80, 0xcb, 0x7d, 0x28, 0x10, 0xcb, 0x44, 0xcb, + 0x84, 0x20, 0x0a, 0x24, 0xcb, 0xa7, 0xd3, 0x63, + 0x10, 0xfe, 0x08, 0xbf, 0xc9, 0xcb, 0xe7, 0xd3, + 0x63, 0x08, 0x3c, 0xdd, 0xbe, 0x01, 0xc8, 0x32, + 0x44, 0x00, 0xe5, 0xed, 0x73, 0x4d, 0x00, 0xe1, + 0x10, 0xfe, 0xd9, 0xeb, 0xdd, 0x7e, 0x07, 0xcd, + 0x05, 0xf3, 0xeb, 0xd9, 0xdb, 0x65, 0x47, 0x3a, + 0x44, 0x00, 0xb8, 0x06, 0x00, 0xc9, 0x65, 0x05, + 0x55, 0xff, 0xff, 0x05, 0x23, 0x16, 0x6d, 0x00, + 0x78, 0xff, 0xff, 0x05, 0x24, 0x16, 0x75, 0x06, + 0x31, 0x80, 0x00, 0x21, 0x5b, 0xf6, 0xcd, 0x38, + 0xf6, 0xcd, 0x41, 0xf6, 0x20, 0x20, 0x7d, 0x32, + 0x42, 0x00, 0x7c, 0x32, 0x39, 0x00, 0xcd, 0x18, + 0xf0, 0xdd, 0x2a, 0x53, 0x00, 0x3a, 0x39, 0x00, + 0xcd, 0x7c, 0xf6, 0xcd, 0x18, 0xf0, 0x21, 0x6e, + 0xf6, 0xcd, 0x38, 0xf6, 0x18, 0xd2, 0x0e, 0x3f, + 0xcd, 0x0c, 0xf0, 0xcd, 0xc6, 0xf6, 0x18, 0xc8, + 0xcd, 0x53, 0xf6, 0xc2, 0x41, 0xe7, 0xc3, 0x07, + 0xe7, 0xcd, 0x53, 0xf6, 0xc2, 0x9a, 0xe7, 0xc3, + 0x60, 0xe7, 0xcd, 0x53, 0xf6, 0xc2, 0x4d, 0xe7, + 0xc3, 0x13, 0xe7, 0x47, 0x3a, 0x28, 0xe0, 0xfe, + 0x01, 0x78, 0xc9, 0x54, 0x45, 0x53, 0x54, 0x23, + 0x44, 0x52, 0x56, 0x23, 0x20, 0x28, 0x54, 0x54, + 0x44, 0x44, 0x29, 0x3a, 0x20, 0x03, 0x0a, 0x0d, + 0x54, 0x41, 0x53, 0x4b, 0x20, 0x44, 0x4f, 0x4e, + 0x45, 0x0d, 0x0a, 0x03, 0xfe, 0x00, 0xca, 0xd1, + 0xf7, 0xfe, 0x05, 0x28, 0x19, 0x38, 0x60, 0xfe, + 0xff, 0xc0, 0x21, 0x94, 0xf6, 0xcd, 0x38, 0xf6, + 0xcd, 0x41, 0xf6, 0xe9, 0x41, 0x44, 0x44, 0x52, + 0x45, 0x53, 0x53, 0x3a, 0x20, 0x03, 0xcd, 0x33, + 0xf0, 0xc9, 0x21, 0xcf, 0xf6, 0xcd, 0x38, 0xf6, + 0x3a, 0x4c, 0x00, 0xcd, 0xec, 0xf7, 0x3a, 0x47, + 0x00, 0xcd, 0xec, 0xf7, 0x3a, 0x42, 0x00, 0xcd, + 0xec, 0xf7, 0x3a, 0x44, 0x00, 0xcd, 0xec, 0xf7, + 0x3a, 0x43, 0x00, 0xcd, 0x4a, 0xf6, 0xcd, 0x53, + 0xf6, 0xc2, 0x2f, 0xe7, 0xc3, 0xf5, 0xe6, 0x43, + 0x4d, 0x44, 0x20, 0x53, 0x54, 0x41, 0x54, 0x20, + 0x44, 0x52, 0x56, 0x20, 0x54, 0x52, 0x4b, 0x20, + 0x53, 0x43, 0x54, 0x52, 0x3e, 0x20, 0x03, 0xaf, + 0x32, 0x44, 0x00, 0x3c, 0x32, 0x43, 0x00, 0x18, + 0x54, 0x21, 0x00, 0x08, 0x22, 0x40, 0x00, 0xcd, + 0x2a, 0xf0, 0xc4, 0xa2, 0xf6, 0x21, 0x00, 0x09, + 0x22, 0x40, 0x00, 0xcd, 0x27, 0xf0, 0xc4, 0xa2, + 0xf6, 0x3a, 0x39, 0x00, 0xfe, 0x02, 0x28, 0x0f, + 0xf5, 0xcd, 0x59, 0xf7, 0xf1, 0xfe, 0x03, 0xca, + 0xae, 0xf7, 0xfe, 0x04, 0xca, 0x9e, 0xf7, 0x21, + 0x43, 0x00, 0x34, 0xdd, 0x7e, 0x00, 0xe5, 0x21, + 0x42, 0x00, 0xcb, 0x66, 0xe1, 0x28, 0x01, 0x07, + 0x3c, 0xbe, 0x20, 0x11, 0x36, 0x01, 0x23, 0x34, + 0xdd, 0x7e, 0x01, 0xbe, 0x20, 0x07, 0x36, 0x00, + 0x0e, 0x50, 0xcd, 0x0c, 0xf0, 0xcd, 0x06, 0xf0, + 0x28, 0x06, 0xcd, 0x09, 0xf0, 0xfe, 0x2e, 0xc8, + 0x3a, 0x39, 0x00, 0xfe, 0x02, 0x28, 0xa6, 0x18, + 0x98, 0x21, 0x00, 0x09, 0xeb, 0x21, 0x00, 0x08, + 0x3a, 0x42, 0x00, 0x07, 0x06, 0x80, 0x30, 0x02, + 0x06, 0x00, 0x1a, 0xbe, 0x3e, 0xff, 0x32, 0x47, + 0x00, 0xc2, 0xa2, 0xf6, 0x23, 0x13, 0x10, 0xf2, + 0x21, 0x38, 0x00, 0x11, 0x00, 0x08, 0x06, 0x40, + 0x3a, 0x42, 0x00, 0x07, 0x38, 0x02, 0xcb, 0x20, + 0x7e, 0x0f, 0x38, 0x01, 0x13, 0xed, 0x5f, 0x12, + 0x13, 0x13, 0x10, 0xf9, 0x7e, 0x0f, 0x38, 0x03, + 0x3c, 0x77, 0xc9, 0xaf, 0x77, 0xc9, 0xe5, 0x21, + 0x42, 0x00, 0x7e, 0xe6, 0xf0, 0x77, 0xed, 0x5f, + 0x00, 0xe6, 0x01, 0xb6, 0x77, 0xe1, 0xdd, 0x7e, + 0x00, 0x3c, 0x47, 0xed, 0x5f, 0xe6, 0x1f, 0xb7, + 0x28, 0xf4, 0x32, 0x43, 0x00, 0xb8, 0x30, 0xee, + 0xdd, 0x7e, 0x01, 0x47, 0xed, 0x5f, 0xe6, 0x7f, + 0x32, 0x44, 0x00, 0xb8, 0x30, 0xf2, 0xc3, 0x45, + 0xf7, 0xcd, 0x18, 0xf0, 0xdd, 0x7e, 0x01, 0x3d, + 0xd3, 0x67, 0xdd, 0x7e, 0x07, 0xd3, 0x64, 0xcd, + 0x06, 0xf0, 0x28, 0xed, 0xcd, 0x09, 0xf0, 0xfe, + 0x2e, 0xc8, 0x18, 0xe5, 0xcd, 0x4a, 0xf6, 0x0e, + 0x20, 0xc3, 0x0c, 0xf0, 0x3e, 0xff, 0x32, 0x55, + 0x00, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + +/* Provide RAM if ROMs are disabled */ +static uint8 sbc200_mon_ram[SBC200_MON_SIZE] = {0}; +static uint8 sbc200_ddb_ram[SBC200_DDB_SIZE] = {0}; + +/* Debug flags */ +#define STATUS_MSG (1 << 0) +#define IRQ_MSG (1 << 1) +#define ERROR_MSG (1 << 2) +#define VERBOSE_MSG (1 << 3) + +/* IO Read/Write */ +#define IO_RD 0x00 /* IO Read */ +#define IO_WR 0x01 /* IO Write */ + +typedef struct { + PNP_INFO pnp; /* Must be first */ + uint32 mon_base; /* Monitor Addr */ + uint32 mon_size; /* Monitor Size */ + uint32 ddb_base; /* DDBIOS Addr */ + uint32 ddb_size; /* DDBIOS Size */ + int32 port; /* Port 0 or 1 */ + t_bool conn; /* Connected Status */ + TMLN *tmln; /* TMLN pointer */ + TMXR *tmxr; /* TMXR pointer */ + t_bool mif; /* Mode Instruction */ + int32 mode; /* Mode */ + int32 baud; /* Baud rate */ + int32 dtr; /* DTR Status */ + int32 rts; /* RTS Status */ + int32 rxb; /* Receive Buffer */ + int32 txb; /* Transmit Buffer */ + t_bool txp; /* Transmit Pending */ + int32 stb; /* Status Buffer */ + int32 ctb; /* Control Buffer */ + uint8 rxintenable; /* Interrupt Enable */ + uint8 txintenable; /* Interrupt Enable */ + uint8 rxintvector; /* Interrupt Vector */ + uint8 txintvector; /* Interrupt Vector */ + uint8 rxdatabus; /* Data Bus Value */ + uint8 txdatabus; /* Data Bus Value */ +} SBC200_CTXT; + +extern uint32 getClockFrequency(void); +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 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 uint8 GetBYTEWrapper(const uint32 Addr); +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); + +static const char* sbc200_description(DEVICE *dptr); +static t_stat sbc200_svc(UNIT *uptr); +static t_stat sbc200_reset(DEVICE *dptr); +static t_stat sbc200_attach(UNIT *uptr, CONST char *cptr); +static t_stat sbc200_detach(UNIT *uptr); +static t_stat sbc200_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat sbc200_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc); +static t_stat sbc200_config_line(UNIT *uptr); +static t_stat sbc200_config_dtr(DEVICE *dptr, char rts); +static t_stat sbc200_config_rts(DEVICE *dptr, char rts); +static int32 sbc200_io(int32 addr, int32 io, int32 data); +static int32 sbc200_stat(DEVICE *dptr, int32 io, int32 data); +static int32 sbc200_data(DEVICE *dptr, int32 io, int32 data); +static int32 sbc200_rom(DEVICE *dptr, int32 io, int32 data); +static void sbc200_int(UNIT *uptr, int32 vector, int32 databus); +static int32 sbc200_mon(int32 Addr, int32 rw, int32 Data); +static int32 sbc200_ddb(int32 Addr, int32 rw, int32 Data); + +extern uint32 vectorInterrupt; /* Vector Interrupt bits */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */ + +/* Debug Flags */ +static DEBTAB sbc200_dt[] = { + { "STATUS", STATUS_MSG, "Status messages" }, + { "IRQ", IRQ_MSG, "Interrupt messages" }, + { "ERROR", ERROR_MSG, "Error messages" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { NULL, 0 } +}; + +/* Terminal multiplexer library descriptors */ + +static TMLN sbc200_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMXR sbc200_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + sbc200_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +#define UNIT_V_SBC200_CONSOLE (UNIT_V_UF + 0) /* Port checks console for input */ +#define UNIT_SBC200_CONSOLE (1 << UNIT_V_SBC200_CONSOLE) +#define UNIT_V_SBC200_MONITOR (UNIT_V_UF + 1) /* Monitor ROM */ +#define UNIT_SBC200_MONITOR (1 << UNIT_V_SBC200_MONITOR) +#define UNIT_V_SBC200_DDBIOS (UNIT_V_UF + 2) /* DDBIOS ROM */ +#define UNIT_SBC200_DDBIOS (1 << UNIT_V_SBC200_DDBIOS) + +static MTAB sbc200_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "Sets MITS 2SIO base I/O address" }, + { UNIT_SBC200_CONSOLE, UNIT_SBC200_CONSOLE, "CONSOLE", "CONSOLE", NULL, NULL, NULL, + "Port checks for console input" }, + { UNIT_SBC200_CONSOLE, 0, "NOCONSOLE", "NOCONSOLE", NULL, NULL, NULL, + "Port does not check for console input" }, + { UNIT_SBC200_MONITOR, UNIT_SBC200_MONITOR, "MONITOR", "MONITOR", NULL, NULL, NULL, + "Enable ROM monitor at E000" }, + { UNIT_SBC200_MONITOR, 0, "NOMONITOR", "NOMONITOR", NULL, NULL, NULL, + "Disable ROM monitor at E000" }, + { UNIT_SBC200_DDBIOS, UNIT_SBC200_DDBIOS, "DDBIOS", "DDBIOS", NULL, NULL, NULL, + "Enable ROM DDBIOS at F000" }, + { UNIT_SBC200_DDBIOS, 0, "NODDBIOS", "NODDBIOS", NULL, NULL, NULL, + "Disable ROM DDBIOS at F000" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD", &sbc200_set_baud, &sbc200_show_baud, + NULL, "Set baud rate (default=9600)" }, + { 0 } +}; + +static SBC200_CTXT sbc200_ctx = {{0, 0, SBC200_IOBASE, SBC200_IOSIZE}, + SBC200_MON_BASE, SBC200_MON_SIZE, SBC200_DDB_BASE, SBC200_DDB_SIZE, + 0, 0, sbc200_tmln, &sbc200_tmxr, SBC200_BAUD, 1}; + +static UNIT sbc200_unit[] = { + { UDATA (&sbc200_svc, UNIT_ATTABLE | UNIT_DISABLE | + UNIT_SBC200_CONSOLE | UNIT_SBC200_MONITOR | UNIT_SBC200_DDBIOS, 0), SBC200_WAIT }, +}; + +static REG sbc200_reg[] = { + { HRDATAD (SBCSTA0, sbc200_ctx.stb, 8, "Status register"), }, + { HRDATAD (SBCCTL0, sbc200_ctx.ctb, 8, "Control register"), }, + { HRDATAD (SBCRXD0, sbc200_ctx.rxb, 8, "RX data buffer"), }, + { HRDATAD (SBCTXD0, sbc200_ctx.txb, 8, "TX data buffer"), }, + { FLDATAD (SBCTXP0, sbc200_ctx.txp, 0, "TX data pending"), }, + { FLDATAD (SBCCON0, sbc200_ctx.conn, 0, "Connection status"), }, + { FLDATAD (SBCRTS0, sbc200_ctx.rts, 0, "RTS status (active low)"), }, + { FLDATAD (SBCDTR0, sbc200_ctx.dtr, 0, "DTR status (active low)"), }, + { FLDATAD (SBCRDRF0, sbc200_ctx.stb, 0, "RDRF status"), }, + { FLDATAD (SBCTDRE0, sbc200_ctx.stb, 1, "TDRE status"), }, + { FLDATAD (SBCDCD0, sbc200_ctx.stb, 2, "DCD status (active low)"), }, + { FLDATAD (SBCDSR0, sbc200_ctx.stb, 3, "DSR status (active low)"), }, + { FLDATAD (SBCOVRN0, sbc200_ctx.stb, 4, "OVRN status"), }, + { DRDATAD (SBCWAIT0, sbc200_unit[0].wait, 32, "Wait cycles"), }, + { FLDATAD (SBCRXINTEN0, sbc200_ctx.rxintenable, 1, "Global vectored interrupt enable"), }, + { FLDATAD (SBCTXINTEN0, sbc200_ctx.txintenable, 1, "Global vectored interrupt enable"), }, + { DRDATAD (SBCRXVEC0, sbc200_ctx.rxintvector, 8, "RX interrupt vector"), }, + { DRDATAD (SBCTXVEC0, sbc200_ctx.txintvector, 8, "TX interrupt vector"), }, + { HRDATAD (SBCRXDBVAL0, sbc200_ctx.rxdatabus, 8, "RX data bus value"), }, + { HRDATAD (SBCTXDBVAL0, sbc200_ctx.txdatabus, 8, "TX data bus value"), }, + { NULL } +}; + +DEVICE sbc200_dev = { + SBC200_SNAME, /* name */ + sbc200_unit, /* unit */ + sbc200_reg, /* registers */ + sbc200_mod, /* modifiers */ + 1, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &sbc200_reset, /* reset routine */ + NULL, /* boot routine */ + &sbc200_attach, /* attach routine */ + &sbc200_detach, /* detach routine */ + &sbc200_ctx, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + sbc200_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + NULL, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &sbc200_description /* description */ +}; + +static const char* sbc200_description(DEVICE *dptr) +{ + return SBC200_NAME; +} + +static t_stat sbc200_reset(DEVICE *dptr) +{ + SBC200_CTXT *xptr; + int32 c; + + xptr = (SBC200_CTXT *) dptr->ctxt; + + /* Connect/Disconnect I/O Ports at base address */ + if (sim_map_resource(xptr->pnp.io_base, xptr->pnp.io_size, RESOURCE_TYPE_IO, &sbc200_io, dptr->name, dptr->flags & DEV_DIS) != 0) { + sim_printf("Error mapping I/O resource at 0x%02x.\n", xptr->pnp.io_base); + return SCPE_ARG; + } + + if (sim_map_resource(xptr->mon_base, xptr->mon_size, RESOURCE_TYPE_MEMORY, &sbc200_mon, dptr->name, dptr->flags & DEV_DIS) != 0) { + sim_printf("Error mapping MON resource at 0x%04x\n", xptr->mon_base); + return SCPE_ARG; + } + + if (sim_map_resource(xptr->ddb_base, xptr->ddb_size, RESOURCE_TYPE_MEMORY, &sbc200_ddb, dptr->name, dptr->flags & DEV_DIS) != 0) { + sim_printf("Error mapping DDB resource at 0x%04x\n", xptr->ddb_base); + return SCPE_ARG; + } + + /* Set DEVICE for this UNIT */ + dptr->units[0].dptr = dptr; + c = getClockFrequency() / 5; + dptr->units[0].wait = (c && c < 1000) ? c : 1000; + + /* Enable TMXR modem control passthrough */ + tmxr_set_modem_control_passthru(xptr->tmxr); + + /* Reset status registers */ + xptr->mif = TRUE; + xptr->stb = 0x00; + xptr->txp = TRUE; + if (dptr->units[0].flags & UNIT_ATT) { + sbc200_config_dtr(dptr, 1); /* disable DTR */ + sbc200_config_rts(dptr, 1); /* disable RTS */ + } + + if (!(dptr->flags & DEV_DIS)) { + sim_activate(&dptr->units[0], dptr->units[0].wait); + } else { + sim_cancel(&dptr->units[0]); + } + + sim_debug(STATUS_MSG, dptr, "reset adapter.\n"); + + return SCPE_OK; +} + + +static t_stat sbc200_svc(UNIT *uptr) +{ + SBC200_CTXT *xptr; + int32 c,s,stb; + t_stat r; + + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + /* Check for new incoming connection */ + if (uptr->flags & UNIT_ATT) { + if (tmxr_poll_conn(xptr->tmxr) >= 0) { /* poll connection */ + + xptr->conn = TRUE; /* set connected */ + + sim_debug(STATUS_MSG, uptr->dptr, "new connection.\n"); + } + } + + /* Update incoming modem status bits */ + if (uptr->flags & UNIT_ATT) { + tmxr_set_get_modem_bits(xptr->tmln, 0, 0, &s); + stb = xptr->stb; + + xptr->stb &= ~SBC200_DSR; + xptr->stb |= (s & TMXR_MDM_DSR) ? 0 : SBC200_DSR; /* Active Low */ + if ((stb ^ xptr->stb) & SBC200_DSR) { + sim_debug(STATUS_MSG, uptr->dptr, "DSR state changed to %s.\n", (xptr->stb & SBC200_DSR) ? "LOW" : "HIGH"); + } + + xptr->stb &= ~SBC200_DCD; + xptr->stb |= (s & TMXR_MDM_DCD) ? 0 : SBC200_DCD; /* Active Low */ + if ((stb ^ xptr->stb) & SBC200_DCD) { + sim_debug(STATUS_MSG, uptr->dptr, "DCD state changed to %s.\n", (xptr->stb & SBC200_DCD) ? "LOW" : "HIGH"); + } + + /* Enable receiver if DCD is active low */ + xptr->tmln->rcve = !(xptr->stb & SBC200_DCD); + } + + /* TX data */ + if (xptr->txp) { + if (uptr->flags & UNIT_ATT) { + if (!(xptr->stb & SBC200_DSR)) { /* Active low */ + r = tmxr_putc_ln(xptr->tmln, xptr->txb); + xptr->txp = FALSE; /* Reset TX Pending */ + } else { + r = SCPE_STALL; + } + } else { + r = sim_putchar(xptr->txb); + xptr->txp = FALSE; /* Reset TX Pending */ + } + + if (r == SCPE_LOST) { + xptr->conn = FALSE; /* Connection was lost */ + sim_debug(STATUS_MSG, uptr->dptr, "lost connection.\n"); + } + + /* If TX buffer now empty, send interrupt */ + if (!xptr->txp && xptr->txintenable) { + sim_debug(IRQ_MSG, uptr->dptr, "%s: TxRDY Vector=%d\n", sim_uname(uptr), xptr->txintvector); + sbc200_int(uptr, xptr->txintvector, xptr->txdatabus); + } + + } + + /* Update TDRE if not set and no character pending */ + if (!xptr->txp && !(xptr->stb & SBC200_TDRE)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_tx(xptr->tmxr); + xptr->stb |= (tmxr_txdone_ln(xptr->tmln) && xptr->conn) ? SBC200_TDRE : 0; + } else { + xptr->stb |= SBC200_TDRE; + } + } + + /* Check for Data if RX buffer empty */ + if (!(xptr->stb & SBC200_RDRF)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_rx(xptr->tmxr); + + c = tmxr_getc_ln(xptr->tmln); + } else if (uptr->flags & UNIT_SBC200_CONSOLE) { + c = sim_poll_kbd(); + } else { + c = 0; + } + + if (c & (TMXR_VALID | SCPE_KFLAG)) { + xptr->rxb = c & 0xff; + xptr->stb |= SBC200_RDRF; + xptr->stb &= ~(SBC200_FE | SBC200_OVRN | SBC200_PE); + sim_debug(IRQ_MSG, uptr->dptr, "%s: RxRDY Vector=%d\n", sim_uname(uptr), xptr->rxintvector); + if (xptr->rxintenable) { + sbc200_int(uptr, xptr->rxintvector, xptr->rxdatabus); + } + } + } + + /* Don't let TMXR clobber our wait time */ + uptr->wait = SBC200_WAIT; + + sim_activate_abs(uptr, uptr->wait); + + return SCPE_OK; +} + + +/* Attach routine */ +static t_stat sbc200_attach(UNIT *uptr, CONST char *cptr) +{ + SBC200_CTXT *xptr; + t_stat r; + + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + sim_debug(VERBOSE_MSG, uptr->dptr, "attach (%s).\n", cptr); + + if ((r = tmxr_attach(xptr->tmxr, uptr, cptr)) == SCPE_OK) { + + if (xptr->tmln->serport) { + sbc200_config_dtr(uptr->dptr, xptr->dtr); /* update DTR */ + sbc200_config_rts(uptr->dptr, xptr->rts); /* update RTS */ + } + + xptr->tmln->rcve = 1; + } + + return r; +} + + +/* Detach routine */ +static t_stat sbc200_detach(UNIT *uptr) +{ + SBC200_CTXT *xptr; + + if (uptr->dptr == NULL) { + return SCPE_IERR; + } + + sim_debug(VERBOSE_MSG, uptr->dptr, "detach.\n"); + + if (uptr->flags & UNIT_ATT) { + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + sim_cancel(uptr); + + return (tmxr_detach(xptr->tmxr, uptr)); + } + + return SCPE_UNATT; +} + +static t_stat sbc200_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + SBC200_CTXT *xptr; + int32 baud; + t_stat r = SCPE_ARG; + + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + if (!(uptr->flags & UNIT_ATT)) { + return SCPE_UNATT; + } + + if (cptr != NULL) { + if (sscanf(cptr, "%d", &baud)) { + switch (baud) { + case 110: + case 150: + case 300: + case 1200: + case 1800: + case 2400: + case 4800: + case 9600: + case 19200: + xptr->baud = baud; + r = sbc200_config_line(uptr); + + return r; + + default: + break; + } + } + } + + return r; +} + +static t_stat sbc200_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc) +{ + SBC200_CTXT *xptr; + + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + if (uptr->flags & UNIT_ATT) { + fprintf(st, "Baud rate: %d", xptr->baud); + } + + return SCPE_OK; +} + +static t_stat sbc200_config_line(UNIT *uptr) +{ + SBC200_CTXT *xptr; + char config[20]; + const char *fmt; + t_stat r = SCPE_IERR; + + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + if (xptr != NULL) { + switch (xptr->mode & SBC200_FMTMSK) { + case SBC200_72E: + fmt = "7E2"; + break; + case SBC200_72O: + fmt = "7O2"; + break; + case SBC200_71E: + fmt = "7E1"; + break; + case SBC200_71O: + fmt = "7O1"; + break; + case SBC200_82N: + fmt = "8N2"; + break; + case SBC200_81E: + fmt = "8E1"; + break; + case SBC200_81O: + fmt = "8O1"; + break; + case SBC200_81N: + default: + fmt = "8N1"; + break; + } + + sprintf(config, "%d-%s", xptr->baud, fmt); + + r = tmxr_set_config_line(xptr->tmln, config); + + sim_debug(STATUS_MSG, uptr->dptr, "port configuration set to '%s'.\n", config); + + /* + ** AltairZ80 and TMXR refuse to want to play together + ** nicely when the CLOCK register is set to anything + ** other than 0. + ** + ** This work-around is for those of us that may wish + ** to run irrelevant, old software, that use TMXR and + ** rely on some semblance of timing (Remote CP/M, BYE, + ** RBBS, PCGET/PUT, Xmodem, MEX, Modem7, or most + ** other communications software), on contemporary + ** hardware. + ** + ** Serial ports are self-limiting and sockets will run + ** at the clocked CPU speed. + */ + xptr->tmln->txbps = 0; /* Get TMXR's timing out of our way */ + xptr->tmln->rxbps = 0; /* Get TMXR's timing out of our way */ + } + + return r; +} + +/* +** DTR is active low +** 0 = DTR active +** 1 = DTR inactive +*/ +static t_stat sbc200_config_dtr(DEVICE *dptr, char dtr) +{ + SBC200_CTXT *xptr; + t_stat r = SCPE_OK; + int32 s; + + xptr = (SBC200_CTXT *) dptr->ctxt; + + if (dptr->units[0].flags & UNIT_ATT) { + /* DTR Control */ + s = TMXR_MDM_DTR; + + if (!dtr) { + r = tmxr_set_get_modem_bits(xptr->tmln, s, 0, NULL); + if (xptr->dtr) { + sim_debug(STATUS_MSG, dptr, "DTR state changed to HIGH.\n"); + } + } else { + r = tmxr_set_get_modem_bits(xptr->tmln, 0, s, NULL); + if (!xptr->dtr) { + sim_debug(STATUS_MSG, dptr, "DTR state changed to LOW.\n"); + } + } + } + + xptr->dtr = dtr; /* Active low */ + + return r; +} + +/* +** RTS is active low +** 0 = RTS active +** 1 = RTS inactive +*/ +static t_stat sbc200_config_rts(DEVICE *dptr, char rts) +{ + SBC200_CTXT *xptr; + t_stat r = SCPE_OK; + int32 s; + + xptr = (SBC200_CTXT *) dptr->ctxt; + + if (dptr->units[0].flags & UNIT_ATT) { + /* RTS Control */ + s = TMXR_MDM_RTS; + + if (!rts) { + r = tmxr_set_get_modem_bits(xptr->tmln, s, 0, NULL); + if (xptr->rts) { + sim_debug(STATUS_MSG, dptr, "RTS state changed to HIGH.\n"); + } + } else { + r = tmxr_set_get_modem_bits(xptr->tmln, 0, s, NULL); + if (!xptr->rts) { + sim_debug(STATUS_MSG, dptr, "RTS state changed to LOW.\n"); + } + } + } + + xptr->rts = rts; /* Active low */ + + return r; +} + +static int32 sbc200_io(int32 addr, int32 io, int32 data) +{ + SBC200_CTXT *xptr; + int32 r = 0xff; + + xptr = (SBC200_CTXT *) sbc200_dev.ctxt; + + switch (addr - xptr->pnp.io_base) { + case 0x00: + r = sbc200_data(&sbc200_dev, io, data); + break; + + case 0x01: + r = sbc200_stat(&sbc200_dev, io, data); + break; + + case 0x03: + r = sbc200_rom(&sbc200_dev, io, data); + break; + + default: + break; + + } + + return(r); +} + +static int32 sbc200_stat(DEVICE *dptr, int32 io, int32 data) +{ + SBC200_CTXT *xptr; + int32 r; + + xptr = (SBC200_CTXT *) dptr->ctxt; + + if (io == IO_RD) { + xptr->stb ^= SBC200_DSR; /* Toggle DSR */ + r = xptr->stb; + } else { + if (xptr->mif) { + xptr->mif = FALSE; + xptr->mode = data & 0xff; + + /* Set data bits, parity and stop bits format */ + sbc200_config_line(&dptr->units[0]); + } else { /* Command Mode */ + /* Internal Reset */ + if (data & SBC200_IR) { + xptr->ctb = data & 0xff; /* save control byte */ + sim_debug(STATUS_MSG, dptr, "8251 internal reset.\n"); + xptr->stb &= (SBC200_DSR | SBC200_DCD); /* Reset status register */ + xptr->mif = TRUE; + xptr->rxb = 0x00; + xptr->txp = TRUE; + sbc200_config_dtr(dptr, 1); /* disable DTR */ + sbc200_config_rts(dptr, 1); /* disable RTS */ + } else { + sbc200_config_dtr(dptr, (data & SBC200_DTR)); /* enable DTR */ + sbc200_config_rts(dptr, (data & SBC200_RTS)); /* enable RTS */ + } + } + + r = 0x00; + } + + return(r); +} + +static int32 sbc200_data(DEVICE *dptr, int32 io, int32 data) +{ + SBC200_CTXT *xptr; + int32 r; + + xptr = (SBC200_CTXT *) dptr->ctxt; + + if (io == IO_RD) { + r = xptr->rxb; + xptr->stb &= ~(SBC200_RDRF | SBC200_FE | SBC200_OVRN | SBC200_PE); + } else { + xptr->txb = data; + xptr->stb &= ~(SBC200_TDRE); + xptr->txp = TRUE; + + sbc200_svc(dptr->units); /* Process TX right now */ + + r = 0x00; + } + + return r; +} + +static int32 sbc200_rom(DEVICE *dptr, int32 io, int32 data) +{ + SBC200_CTXT *xptr; + + xptr = (SBC200_CTXT *) dptr->ctxt; + + sim_debug(VERBOSE_MSG, dptr, "Switch %s ROMs\n", data & 0x02 ? "OUT" : "IN"); + + if (sim_map_resource(xptr->mon_base, xptr->mon_size, RESOURCE_TYPE_MEMORY, &sbc200_mon, dptr->name, data & 0x02)) { + sim_printf("Error mapping MON resource at 0x%04x\n", xptr->mon_base); + return SCPE_ARG; + } + + if (sim_map_resource(xptr->ddb_base, xptr->ddb_size, RESOURCE_TYPE_MEMORY, &sbc200_ddb, dptr->name, data & 0x02)) { + sim_printf("Error mapping DDB resource at 0x%04x\n", xptr->ddb_base); + return SCPE_ARG; + } + + return data; +} + +static void sbc200_int(UNIT *uptr, int32 vector, int32 databus) +{ + SBC200_CTXT *xptr; + + xptr = (SBC200_CTXT *) uptr->dptr->ctxt; + + vectorInterrupt |= (1 << vector); + dataBus[vector] = databus; + + sim_debug(IRQ_MSG, uptr->dptr, "%s: IRQ Vector=%d dataBus=%02X Status=%02X\n", sim_uname(uptr), vector, databus, xptr->stb); +} + + +static int32 sbc200_mon(int32 Addr, int32 rw, int32 Data) +{ + /* If ROM enabled, return ROM byte */ + if (sbc200_dev.units->flags & UNIT_SBC200_MONITOR) { + return(sbc200_mon_rom[Addr & SBC200_MON_MASK]); + } else if (rw == IO_WR) { + sbc200_mon_ram[Addr & SBC200_MON_MASK] = Data; + } + + return sbc200_mon_ram[Addr & SBC200_MON_MASK]; +} + +static int32 sbc200_ddb(int32 Addr, int32 rw, int32 Data) +{ + /* If ROM has been disabled, unmap and return byte from RAM */ + if (sbc200_dev.units->flags & UNIT_SBC200_DDBIOS) { + return(sbc200_ddb_rom[Addr & SBC200_DDB_MASK]); + } else if (rw == IO_WR) { + sbc200_ddb_ram[Addr & SBC200_DDB_MASK] = Data; + } + + return sbc200_ddb_ram[Addr & SBC200_DDB_MASK]; +} + diff --git a/AltairZ80/s100_vfii.c b/AltairZ80/s100_vfii.c new file mode 100644 index 000000000..837d776f9 --- /dev/null +++ b/AltairZ80/s100_vfii.c @@ -0,0 +1,197 @@ +/************************************************************************* + * * + * Copyright (c) 2025 Patrick A. Linstruth. * + * https://github.com/deltecent * + * * + * 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 NON- * + * INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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 names of The Authors 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 Authors. * + * * + * Based on s100_tdd.c by Howard M. Harte. * + * * + * Module Description: * + * SD Systems VersaFloppy II Controller module for SIMH. * + * This module is a wrapper around the WD179X FDC module. * + * * + * Reference: * + * http://www.s100computers.com/Hardware%20Manuals/SD%20Systems/VersaFloppyII%20Manual%20(JM).pdf + * * + *************************************************************************/ + +#include "altairz80_defs.h" +#include "wd179x.h" + +/* Debug flags */ +#define STATUS_MSG (1 << 0) +#define DRIVE_MSG (1 << 1) +#define VERBOSE_MSG (1 << 2) +#define IRQ_MSG (1 << 3) + +#define VFII_MAX_DRIVES 4 + +#define VFII_IO_BASE 0x63 +#define VFII_IO_SIZE 0x01 + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint8 creg; /* Control Register */ +} VFII_INFO; + +extern WD179X_INFO_PUB *wd179x_infop; + +static VFII_INFO vfii_info_data = { { 0x0000, 0, VFII_IO_BASE, VFII_IO_SIZE } }; + +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 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 uint8 floorlog2(unsigned int n); + +extern uint32 PCX; /* external view of PC */ + +#define VFII_CAPACITY (77*26*128) /* Default SSSD 8" (IBM 3740) Disk Capacity */ + +#define VFII_DSEL_MASK 0x0f +#define VFII_SIDE_MASK 0x10 +#define VFII_SIZE_MASK 0x20 +#define VFII_DDEN_MASK 0x40 +#define VFII_WAIT_MASK 0x80 + +static t_stat vfii_reset(DEVICE *vfii_dev); + +static int32 vfii_control(const int32 port, const int32 io, const int32 data); +static const char* vfii_description(DEVICE *dptr); + +static UNIT vfii_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_CAPACITY) } +}; + +static REG vfii_reg[] = { + { NULL } +}; + +#define VFII_NAME "SD Systems VersaFloppy II" +#define VFII_SNAME "VFII" + + +static const char* vfii_description(DEVICE *dptr) { + if (dptr == NULL) { + return NULL; + } + return VFII_NAME; +} + +static MTAB vfii_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" }, + { 0 } +}; + +/* Debug Flags */ +static DEBTAB vfii_dt[] = { + { "STATUS", STATUS_MSG, "Status messages" }, + { "DRIVE", DRIVE_MSG, "Drive messages" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { "IRQ", IRQ_MSG, "IRQ messages" }, + { NULL, 0 } +}; + +DEVICE vfii_dev = { + VFII_SNAME, vfii_unit, vfii_reg, vfii_mod, + VFII_MAX_DRIVES, 10, 31, 1, VFII_MAX_DRIVES, VFII_MAX_DRIVES, + NULL, NULL, &vfii_reset, + NULL, &wd179x_attach, &wd179x_detach, + &vfii_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, + vfii_dt, NULL, NULL, NULL, NULL, NULL, &vfii_description +}; + +/* Reset routine */ +static t_stat vfii_reset(DEVICE *dptr) +{ + DEVICE *wd179x = NULL; + PNP_INFO *pnp = (PNP_INFO *) dptr->ctxt; + char ioaddr[5]; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, 1, RESOURCE_TYPE_IO, &vfii_control, "vfii_control", TRUE); + } else { + /* Connect VFII Control Register */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &vfii_control, "vfii_control", FALSE) != 0) { + sim_printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + + /* Enable WD179x device and set I/O port address for VersaFloppy II */ + if ((wd179x = find_dev("WD179X")) != NULL) { + set_dev_enbdis(wd179x, NULL, 1, NULL); + + sprintf(ioaddr, "%X", pnp->io_base + 1); + set_iobase(wd179x->units, 0, ioaddr, ""); + } + } + + return SCPE_OK; +} + +/* VersaFloppy II Control/Status + * + * BIT 0-3 Drive Select + * BIT 4 Side Select (1 = Side 0) + * BIT 5 5"/8" Drive (1 = 8") + * BIT 6 Double/Single Density (1 = SD) + * BIT 7 Wait Enable (Not used in simulator) + * + * All bits are inverted on the VFII + * + */ + +static int32 vfii_control(const int32 port, const int32 io, const int32 data) +{ + int32 result = 0; + if (io) { /* I/O Write */ + if (port == vfii_info_data.pnp.io_base) { + wd179x_infop->sel_drive = floorlog2((~data) & VFII_DSEL_MASK); + wd179x_infop->fdc_head = (data & VFII_SIDE_MASK) ? 0 : 1; + wd179x_infop->ddens = (data & VFII_DDEN_MASK) ? FALSE : TRUE; + wd179x_infop->drivetype = (data & VFII_SIZE_MASK) ? 8 : 5; + + vfii_info_data.creg = data & 0xff; + + sim_debug(DRIVE_MSG, &vfii_dev, VFII_SNAME ": " ADDRESS_FORMAT " WR CTRL(0x%02x) = 0x%02x: Drive:%d Head:%d Size:%d %s-Density.\n", + PCX, port, + data & 0xFF, + wd179x_infop->sel_drive, + wd179x_infop->fdc_head, + wd179x_infop->drivetype, + wd179x_infop->ddens ? "Double" : "Single"); + } + } else { /* I/O Read */ + result = (vfii_info_data.creg); + } + + return result; +} diff --git a/AltairZ80/wd179x.c b/AltairZ80/wd179x.c index 78354f853..3cfb5b627 100644 --- a/AltairZ80/wd179x.c +++ b/AltairZ80/wd179x.c @@ -58,7 +58,7 @@ #define WD179X_SECTOR_LEN 8192 /* 2^(7 + WD179X_MAX_SEC_LEN) == WD179X_SECTOR_LEN */ #define WD179X_MAX_SEC_LEN 6 -#define WD179X_MAX_SECTOR 26 +#define WD179X_MAX_SECTOR 50 /* SD-OS Double Density with 128-byte Sectors */ #define CMD_PHASE 0 #define EXEC_PHASE 1 @@ -136,6 +136,7 @@ typedef struct { uint16 external_fifo_len; uint8 *external_fifo; uint16 fdc_fifo_index; + uint8 dsmask; /* READ ADDRESS double side mask */ WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES]; } WD179X_INFO; @@ -160,6 +161,7 @@ extern uint8 GetBYTEWrapper(const uint32 Addr); #define UNIT_WD179X_WLK (1 << UNIT_V_WD179X_WLK) #define UNIT_V_WD179X_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ #define UNIT_WD179X_VERBOSE (1 << UNIT_V_WD179X_VERBOSE) + #define WD179X_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */ /* Write Track (format) Statemachine states */ @@ -201,8 +203,23 @@ static t_stat wd179x_sectWrite(WD179X_DRIVE_INFO* pDrive, uint8 Cyl, uint8 Head, static uint8 Do1793Command(uint8 cCommand); static t_stat wd179x_trackWrite(WD179X_DRIVE_INFO* pDrive, uint8 Cyl, uint8 Head, uint8 fillbyte, uint32* flags); - -WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 }, 1793, 0, 0 }; +static DISK_INFO *diskOpenExDsk(FILE *fileref, uint32 isVerbose, DEVICE *device, uint32 debugmask, uint32 verbosedebugmask); +static t_stat diskParseDsk(DISK_INFO *myDisk, uint32 isVerbose); +static t_stat sectReadDsk(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *readlen); +static t_stat sectWriteDsk(DISK_INFO *myDisk, uint32 Cyl, uint32 Head, uint32 Sector, uint8 *buf, uint32 buflen, uint32 *flags, uint32 *writelen); +static t_stat trackWriteDsk(DISK_INFO *myDisk, + uint32 Cyl, + uint32 Head, + uint32 numSectors, + uint32 sectorLen, + uint8 *sectorMap, + uint8 mode, + uint8 fillbyte, + uint32 *flags); +static t_stat diskCreateIMD(DISK_INFO *myDisk, int32 Tracks, int32 Heads, uint8 Mode, uint16 nSecs, uint32 sectSize, uint8 startSector); +static t_stat diskShowIMD(DISK_INFO *myDisk); + +WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 }, 1793, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; WD179X_INFO *wd179x_info = &wd179x_info_data; WD179X_INFO_PUB *wd179x_infop = (WD179X_INFO_PUB *)&wd179x_info_data; @@ -225,7 +242,7 @@ static REG wd179x_reg[] = { { FLDATAD(DRIVE, wd179x_info_data.sel_drive, 2, "Selected drive"), }, { FLDATAD(DRIVETYPE, wd179x_info_data.drivetype, 1, "Drive Type"), }, { HRDATAD(STATUS, wd179x_info_data.fdc_status, 8, "Status Register"), }, - { FLDATAD(VERIFY, wd179x_info_data.drivetype, 1, "Type 1 cmd Verify flag"), }, + { FLDATAD(VERIFY, wd179x_info_data.verify, 1, "Type 1 cmd Verify flag"), }, { HRDATAD(DATA, wd179x_info_data.fdc_data, 8, "Data Register"), }, { FLDATAD(READ, wd179x_info_data.fdc_read, 1, "True when reading"), }, { FLDATAD(WRITE, wd179x_info_data.fdc_write, 1, "True when writing"), }, @@ -243,6 +260,7 @@ static REG wd179x_reg[] = { { FLDATAD(STEPDIR, wd179x_info_data.step_dir, 1, "Step direction"), }, { FLDATAD(IDXWAIT, wd179x_info_data.index_pulse_wait, 1, "Waiting for interrupt on next index"), }, { HRDATAD(CMDTYPE, wd179x_info_data.cmdtype, 8, "Current FDC command"), }, + { HRDATAD(DSMASK, wd179x_info_data.dsmask, 8, "Current READ ADDRESS DS mask"), }, { NULL } }; @@ -334,6 +352,7 @@ static t_stat wd179x_reset(DEVICE *dptr) } wd179x_info->cmdtype = 0; + wd179x_info->dsmask = 0; return SCPE_OK; } @@ -393,7 +412,7 @@ uint8 wd179x_get_nheads(void) return 0; } - return(pDrive->nheads); + return(imdGetSides(pDrive->imd)); } @@ -431,18 +450,6 @@ t_stat wd179x_attach(UNIT *uptr, CONST char *cptr) /* Not an IMD, so assume DSK image type. */ uptr->u3 = IMAGE_TYPE_DSK; uptr->capac = sim_fsize(uptr->fileref); - - switch (uptr->capac) { - case WD179X_CAPACITY_SSSD: - sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X[%d]: 8\" SSSD image attached.\n", i); - wd179x_info->drive[i].nheads = 1; - break; - default: - sim_debug(ERROR_MSG, &wd179x_dev, "WD179X[%d]: %d-length disks images are not supported.\n", - i, uptr->capac); - return SCPE_OPENERR; - break; - } } } else { char* file_extension = strrchr(uptr->filename, '.'); @@ -485,8 +492,20 @@ t_stat wd179x_attach(UNIT *uptr, CONST char *cptr) wd179x_info->drive[i].nheads = imdGetSides(wd179x_info->drive[i].imd); } else { - wd179x_info->drive[i].imd = NULL; - wd179x_info->fdc_sec_len = 0; /* 128 byte sectors */ + /* Create a fake IMD record */ + wd179x_info->drive[i].imd = diskOpenExDsk(uptr->fileref, uptr->flags & UNIT_WD179X_VERBOSE, + &wd179x_dev, IMD_MSG, IMD_MSG); + if (wd179x_info->drive[i].imd == NULL) { + sim_printf("WD179X: Could not allocate IMD data for DSK image.\n"); + wd179x_info->drive[i].uptr = NULL; + return SCPE_OPENERR; + } + + wd179x_info->drive[i].nheads = imdGetSides(wd179x_info->drive[i].imd); + + if (uptr->flags & UNIT_WD179X_VERBOSE) { + diskShowIMD(wd179x_info->drive[i].imd); + } } wd179x_info->drive[i].ready = 1; @@ -563,19 +582,12 @@ uint8 floorlog2(unsigned int n) } static uint8 computeSectorSize(const WD179X_DRIVE_INFO *pDrive) { - if (pDrive->uptr->u3 == IMAGE_TYPE_IMD) { - return pDrive->track < MAX_CYL ? floorlog2(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7 : 0xF8; - } - - return(0); /* Hard coded to 128-byte sectors */ + return pDrive->track < MAX_CYL ? floorlog2(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7 : 0xF8; } static uint8 testMode(const WD179X_DRIVE_INFO *pDrive) { - if (pDrive->uptr->u3 == IMAGE_TYPE_IMD) { - return pDrive->track < MAX_CYL ? IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens) : 0; - } - return 0; + return pDrive->track < MAX_CYL ? IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens) : 0; } uint8 WD179X_Read(const uint32 Addr) @@ -630,7 +642,7 @@ uint8 WD179X_Read(const uint32 Addr) wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ cData = sdata.raw[wd179x_info->fdc_dataindex]; if (wd179x_info->fdc_read_addr == TRUE) { - sim_debug(STATUS_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT + sim_debug(RD_DATA_MSG, &wd179x_dev, "WD179X[%d]: " ADDRESS_FORMAT " READ_ADDR[%d/%d] = 0x%02x\n", wd179x_info->sel_drive, PCX, wd179x_info->fdc_dataindex, wd179x_info->fdc_datacount, cData); @@ -763,12 +775,13 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) } if (wd179x_info->fdc_write_track == TRUE) { + if (wd179x_info->fdc_fmt_state == FMT_GAP1) { - if (cData != 0xFC) { + if (cData != 0xFC && (cData != 0x00 && wd179x_info->fdc_gap[0] < 32)) { wd179x_info->fdc_gap[0]++; } else { - sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT + sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT GAP1 Length = %d\n", PCX, wd179x_info->fdc_gap[0]); wd179x_info->fdc_gap[1] = 0; wd179x_info->fdc_fmt_state = FMT_GAP2; @@ -778,7 +791,7 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) wd179x_info->fdc_gap[1]++; } else { - sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT + sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT GAP2 Length = %d\n", PCX, wd179x_info->fdc_gap[1]); wd179x_info->fdc_gap[2] = 0; wd179x_info->fdc_fmt_state = FMT_HEADER; @@ -789,8 +802,8 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) wd179x_info->fdc_gap[2] = 0; wd179x_info->fdc_fmt_state = FMT_GAP3; } else { - sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT - " HEADER[%d]=%02x\n", PCX, wd179x_info->fdc_header_index, cData); + sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT + " HEADER[%d]=%d\n", PCX, wd179x_info->fdc_header_index, cData); switch (wd179x_info->fdc_header_index) { case 0: pDrive->track = cData; @@ -812,7 +825,7 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) wd179x_info->fdc_gap[2]++; } else { - sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT + sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT GAP3 Length = %d\n", PCX, wd179x_info->fdc_gap[2]); wd179x_info->fdc_fmt_state = FMT_DATA; wd179x_info->fdc_dataindex = 0; @@ -836,7 +849,7 @@ uint8 WD179X_Write(const uint32 Addr, uint8 cData) } wd179x_info->fdc_sectormap[wd179x_info->fdc_fmt_sector_count] = wd179x_info->fdc_sector; wd179x_info->fdc_fmt_sector_count++; - sim_debug(VERBOSE_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT + sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT " FMT Data Length = %d\n", PCX, wd179x_info->fdc_dataindex); sim_debug(FMT_MSG, &wd179x_dev, "WD179X: " ADDRESS_FORMAT @@ -1142,9 +1155,6 @@ static uint8 Do1793Command(uint8 cCommand) if (testMode(pDrive)) { wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND; /* Sector not found */ wd179x_info->intrq = 1; - } else if ((pDrive->uptr->u3 == IMAGE_TYPE_DSK) && (wd179x_info->ddens == 1) && (wd179x_info->fdc_sec_len == 0)) { - wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND; /* Sector not found */ - wd179x_info->intrq = 1; } else { wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ wd179x_info->drq = 1; @@ -1154,8 +1164,8 @@ static uint8 Do1793Command(uint8 cCommand) wd179x_info->fdc_read_addr = TRUE; sdata.raw[0] = pDrive->track; - sdata.raw[1] = wd179x_info->fdc_head; - sdata.raw[2] = wd179x_info->fdc_sector; + sdata.raw[1] = wd179x_info->fdc_head | ((imdGetSides(pDrive->imd) > 1) ? wd179x_info->dsmask : 0); + sdata.raw[2] = (wd179x_info->fdc_sector) ? wd179x_info->fdc_sector : 1; sdata.raw[3] = wd179x_info->fdc_sec_len; sdata.raw[4] = 0xAA; /* CRC1 */ sdata.raw[5] = 0x55; /* CRC2 */ @@ -1186,6 +1196,10 @@ static uint8 Do1793Command(uint8 cCommand) wd179x_info->fdc_read_addr = FALSE; wd179x_info->fdc_fmt_state = FMT_GAP1; /* TRUE when writing an entire track */ wd179x_info->fdc_fmt_sector_count = 0; + wd179x_info->fdc_gap[0] = 0; + wd179x_info->fdc_gap[1] = 0; + wd179x_info->fdc_gap[2] = 0; + wd179x_info->fdc_gap[3] = 0; break; /* Type IV Commands */ @@ -1210,11 +1224,7 @@ static uint8 Do1793Command(uint8 cCommand) if (cCommand & 0x04) { wd179x_info->index_pulse_wait = TRUE; if (wd179x_info->sel_drive < WD179X_MAX_DRIVES) { - if (pDrive->uptr->u3 == IMAGE_TYPE_IMD) { - sim_activate (wd179x_unit, ((wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks % 77) == 0) ? CROMFDC_8IN_ROT : CROMFDC_5IN_ROT); /* Generate INDEX pulse */ - } else { - sim_activate(wd179x_unit, CROMFDC_8IN_ROT); /* Generate INDEX pulse */ - } + sim_activate (wd179x_unit, ((wd179x_info->drive[wd179x_info->sel_drive].imd->ntracks % 77) == 0) ? CROMFDC_8IN_ROT : CROMFDC_5IN_ROT); /* Generate INDEX pulse */ } } else { wd179x_info->intrq = 1; @@ -1334,33 +1344,15 @@ static t_stat wd179x_sectRead(WD179X_DRIVE_INFO* pDrive, readlen); break; case IMAGE_TYPE_DSK: - { - uint32 sec_offset; - uint32 rtn; - - /* For DSK images, density information is not encoded in the file format, - * so enforce that 128-byte sectors are single-density. */ - if ((wd179x_info->ddens == 1) && (wd179x_info->fdc_sec_len == 0)) { - status = SCPE_IOERR; - break; - } - sec_offset = (26 * 128 * Cyl) + ((Sector - 1) * 128); - - if (sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET) == 0) { - rtn = sim_fread(sdata.raw, 1, WD179X_SECTOR_LEN_BYTES, - (pDrive->uptr)->fileref); - if (rtn != (WD179X_SECTOR_LEN_BYTES)) { - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: " ADDRESS_FORMAT - " READ: sim_fread error.\n", wd179x_info->sel_drive, PCX); - status = SCPE_IOERR; - } - } else { - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: " ADDRESS_FORMAT - " READ: sim_fseek error.\n", wd179x_info->sel_drive, PCX); - status = SCPE_IOERR; - } + status = sectReadDsk(pDrive->imd, + Cyl, + Head, + Sector, + buf, + WD179X_SECTOR_LEN_BYTES, + flags, + readlen); break; - } default: sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: Unsupported image type 0x%02x.\n", wd179x_info->sel_drive, pDrive->uptr->u3); @@ -1427,33 +1419,15 @@ static t_stat wd179x_sectWrite(WD179X_DRIVE_INFO* pDrive, (uint32 *)readlen); break; case IMAGE_TYPE_DSK: - { - uint32 sec_offset; - uint32 rtn; - - /* For DSK images, density information is not encoded in the file format, - * so enforce that 128-byte sectors are single-density. */ - if ((wd179x_info->ddens == 1) && (wd179x_info->fdc_sec_len == 0)) { - status = SCPE_IOERR; - break; - } - - sec_offset = (26 * 128 * Cyl) + ((Sector - 1) * 128); - - if (sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET) == 0) { - rtn = sim_fwrite(sdata.raw, 1, WD179X_SECTOR_LEN_BYTES, - (pDrive->uptr)->fileref); - if (rtn != (WD179X_SECTOR_LEN_BYTES)) { - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: " ADDRESS_FORMAT - " WRITE: sim_fread error.\n", wd179x_info->sel_drive, PCX); - } - } - else { - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: " ADDRESS_FORMAT - " WRITE: sim_fseek error.\n", wd179x_info->sel_drive, PCX); - } + status = sectWriteDsk(pDrive->imd, + Cyl, + Head, + Sector, + buf, + WD179X_SECTOR_LEN_BYTES, + flags, + (uint32 *)readlen); break; - } default: sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: Unsupported image type 0x%02x.\n", wd179x_info->sel_drive, pDrive->uptr->u3); break; @@ -1488,48 +1462,380 @@ static t_stat wd179x_trackWrite(WD179X_DRIVE_INFO* pDrive, flags); break; case IMAGE_TYPE_DSK: - { - uint32 sec_offset; - uint32 rtn; - uint8 Sector; - uint16 i; - uint8 Fillbuf[128] = { 0 }; - - /* For DSK images, density information is not encoded in the file format, - * so enforce that 128-byte sectors are single-density. */ - if ((wd179x_info->ddens == 1) && (wd179x_info->fdc_sec_len == 0)) { - status = SCPE_IOERR; - break; - } + status = trackWriteDsk(pDrive->imd, + Cyl, + Head, + wd179x_info->fdc_fmt_sector_count, + WD179X_SECTOR_LEN_BYTES, + wd179x_info->fdc_sectormap, + wd179x_info->ddens ? IMD_MODE_500K_MFM : IMD_MODE_250K_FM, /* data mode */ + fillbyte, + flags); + break; + default: + sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: FORMAT_TRACK: Unsupported image type 0x%02x.\n", wd179x_info->sel_drive, pDrive->uptr->u3); + break; + } + } - for (i = 0; i < 128; i++) { - Fillbuf[i] = fillbyte; - } + return(status); +} - for (Sector = 0; Sector < wd179x_info->fdc_fmt_sector_count; Sector++) { - sec_offset = (26 * 128 * Cyl) + (128 * Sector); +/* + * The following functions simulate an IMD disk + * when attaching DSK images. The DSK images are + * then treated as if they were in IMD format. + */ - if (sim_fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET) == 0) { - rtn = sim_fwrite(Fillbuf, 1, WD179X_SECTOR_LEN_BYTES, - (pDrive->uptr)->fileref); - if (rtn != (WD179X_SECTOR_LEN_BYTES)) { - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: " ADDRESS_FORMAT - " FORMAT_TRACK: sim_fread error.\n", wd179x_info->sel_drive, PCX); - } - } - else { - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: " ADDRESS_FORMAT - " FORMAT_TRACK: sim_fseek error.\n", wd179x_info->sel_drive, PCX); - } +typedef struct { + uint8 drivetype; + uint16 ntracks; + uint16 nsects; + uint16 sectsize; + uint8 mode; +} WD179X_DSK_TABLE; + +/* + * Add new disk definitions here + */ +WD179X_DSK_TABLE wd179x_dsk_table[] = { + {8, 77, 26, 128, IMD_MODE_250K_FM}, /* 8" IBM 3740 (default) */ + {8, 77, 26, 256, IMD_MODE_500K_MFM}, /* 8" IBM System 34 */ + {8, 77, 50, 128, IMD_MODE_500K_MFM}, /* SD System Double Density */ + {8, 77, 51, 128, IMD_MODE_500K_MFM}, /* Tarbell Double Density */ + {0, 0, 0, 0, 0} /* End of disk table */ +}; + +static t_stat diskParseDsk(DISK_INFO *myDisk, uint32 isVerbose) +{ + int32 Heads, Tracks, dskIdx; + uint32 fileSize, calcSize = 0; + + if(myDisk == NULL) { + return (SCPE_OPENERR); + } + + memset(myDisk->track, 0, (sizeof(TRACK_INFO)*MAX_CYL*MAX_HEAD)); + + /* Determine disk geometry from disk size */ + fileSize = sim_fsize(myDisk->file); + + for (Heads = 1; Heads <= MAX_HEAD; Heads++) { + for (dskIdx = 0; wd179x_dsk_table[dskIdx].drivetype; dskIdx++) { + calcSize = Heads * wd179x_dsk_table[dskIdx].sectsize * wd179x_dsk_table[dskIdx].ntracks * wd179x_dsk_table[dskIdx].nsects; + + if (calcSize == fileSize) { + Tracks = wd179x_dsk_table[dskIdx].ntracks; + + sim_printf("H:%d SS:%04d T:%02d SPT:%02d = %d\n", + Heads, wd179x_dsk_table[dskIdx].sectsize, Tracks, wd179x_dsk_table[dskIdx].nsects, calcSize); + + break; } - break; } - default: - sim_debug(ERROR_MSG, &wd179x_dev, "WD179x[%d]: FORMAT_TRACK: Unsupported image type 0x%02x.\n", wd179x_info->sel_drive, pDrive->uptr->u3); + + if (calcSize == fileSize) { break; } } - return(status); + /* No match found... set default */ + if (calcSize != fileSize) { + dskIdx = 0; + + Tracks = wd179x_dsk_table[dskIdx].ntracks; + Heads = 1; + } + + return diskCreateIMD(myDisk, Tracks, Heads, + wd179x_dsk_table[dskIdx].mode, + wd179x_dsk_table[dskIdx].nsects, + wd179x_dsk_table[dskIdx].sectsize, + 1 + ); +} + +static DISK_INFO *diskOpenExDsk(FILE *fileref, uint32 isVerbose, DEVICE *device, uint32 debugmask, uint32 verbosedebugmask) +{ + DISK_INFO *myDisk = NULL; + + myDisk = (DISK_INFO*)calloc(1, sizeof(DISK_INFO)); + if (myDisk == NULL) { + sim_printf("%s: %s(): memory allocation failure.\n", __FILE__, __FUNCTION__); + return NULL; + } + + myDisk->file = fileref; + myDisk->device = device; + myDisk->debugmask = debugmask; + myDisk->verbosedebugmask = verbosedebugmask; + + if (diskParseDsk(myDisk, isVerbose) != SCPE_OK) { + free(myDisk); + myDisk = NULL; + } + + return myDisk; +} + +/* Read a sector from a DSK image. */ +static t_stat sectReadDsk(DISK_INFO *myDisk, + uint32 Cyl, + uint32 Head, + uint32 Sector, + uint8 *buf, + uint32 buflen, + uint32 *flags, + uint32 *readlen) +{ + uint32 sectorFileOffset; + uint8 start_sect; + *readlen = 0; + *flags = 0; + + /* Check parameters */ + if(myDisk == NULL) { + *flags |= IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(sectSeek(myDisk, Cyl, Head) != SCPE_OK) { + *flags |= IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(Sector > myDisk->track[Cyl][Head].nsects) { + sim_debug(myDisk->debugmask, myDisk->device, "%s: invalid sector\n", __FUNCTION__); + *flags |= IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(buflen < myDisk->track[Cyl][Head].sectsize) { + sim_printf("%s: Reading C:%d/H:%d/S:%d, len=%d: user buffer too short, need %d\n", __FUNCTION__, Cyl, Head, Sector, buflen, myDisk->track[Cyl][Head].sectsize); + *flags |= IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + start_sect = myDisk->track[Cyl][Head].start_sector; + + sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; + + sim_debug(myDisk->debugmask, myDisk->device, "Reading C:%d/H:%d/S:%d, len=%d, offset=0x%08x\n", Cyl, Head, Sector, buflen, sectorFileOffset); + + (void)sim_fseek(myDisk->file, sectorFileOffset, SEEK_SET); + + if (sim_fread(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file) != myDisk->track[Cyl][Head].sectsize) { + sim_printf("SIM_IMD[%s]: sim_fread error for SECT_RECORD_NORM_DAM.\n", __FUNCTION__); + } + *readlen = myDisk->track[Cyl][Head].sectsize; + + return(SCPE_OK); +} + +/* Write a sector to a DSK image. */ +static t_stat sectWriteDsk(DISK_INFO *myDisk, + uint32 Cyl, + uint32 Head, + uint32 Sector, + uint8 *buf, + uint32 buflen, + uint32 *flags, + uint32 *writelen) +{ + uint32 sectorFileOffset; + uint8 start_sect; + *writelen = 0; + + sim_debug(myDisk->debugmask, myDisk->device, "Writing C:%d/H:%d/S:%d, len=%d\n", Cyl, Head, Sector, buflen); + + /* Check parameters */ + if(myDisk == NULL) { + *flags = IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(sectSeek(myDisk, Cyl, Head) != 0) { + *flags = IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(Sector > myDisk->track[Cyl][Head].nsects) { + sim_debug(myDisk->debugmask, myDisk->device, "%s: invalid sector\n", __FUNCTION__); + *flags = IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(myDisk->flags & FD_FLAG_WRITELOCK) { + sim_printf("Disk write-protected.\n"); + *flags = IMD_DISK_IO_ERROR_WPROT; + return(SCPE_IOERR); + } + + if(buflen < myDisk->track[Cyl][Head].sectsize) { + sim_printf("%s: user buffer too short [buflen %i < sectsize %i]\n", + __FUNCTION__, buflen, myDisk->track[Cyl][Head].sectsize); + *flags = IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + start_sect = myDisk->track[Cyl][Head].start_sector; + + sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; + + (void)sim_fseek(myDisk->file, sectorFileOffset, SEEK_SET); + + sim_fwrite(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file); + *writelen = myDisk->track[Cyl][Head].sectsize; + + return(SCPE_OK); +} + +static t_stat trackWriteDsk(DISK_INFO *myDisk, + uint32 Cyl, + uint32 Head, + uint32 numSectors, + uint32 sectorLen, + uint8 *sectorMap, + uint8 mode, + uint8 fillbyte, + uint32 *flags) +{ + FILE *fileref; + uint8 *sectorData; + uint32 trackOffset; + unsigned long i; + + *flags = 0; + + /* Check parameters */ + if(myDisk == NULL) { + *flags |= IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + if(myDisk->flags & FD_FLAG_WRITELOCK) { + sim_printf("Disk write-protected, cannot format tracks.\n"); + *flags |= IMD_DISK_IO_ERROR_WPROT; + return(SCPE_IOERR); + } + + fileref = myDisk->file; + + sim_debug(myDisk->debugmask, myDisk->device, "Formatting C:%d/H:%d/N:%d, len=%d, Fill=0x%02x\n", Cyl, Head, numSectors, sectorLen, fillbyte); + + /* Side 0 or 1 */ + Head &= 0x01; + + /* Truncate the DSK file when formatting Cyl 0, Head 0 */ + if((Cyl == 0) && (Head == 0)) + { + /* Truncate the DSK file. */ + if (sim_set_fsize(fileref, 0)) { + sim_printf("Disk truncation failed.\n"); + *flags |= IMD_DISK_IO_ERROR_GENERAL; + return(SCPE_IOERR); + } + + myDisk->ntracks = 1; + myDisk->nsides = 1; + + /* Flush and re-parse the IMD file. */ + fflush(fileref); + } + + /* Adjust on the fly */ + if ((Head + 1) > myDisk->nsides) { + myDisk->nsides = Head + 1; + } + + if ((Cyl + 1) > myDisk->ntracks) { + myDisk->ntracks = Cyl + 1; + } + + myDisk->track[Cyl][Head].nsects = numSectors; + myDisk->track[Cyl][Head].sectsize = sectorLen; + myDisk->track[Cyl][Head].mode = mode; + + /* Seek to start of track */ + trackOffset = (((Cyl * myDisk->nsides) + Head) * numSectors * sectorLen); + +// sim_printf("Seeking to %08X T:%d H:%d N:%d L:%d S:%d\n", trackOffset, Cyl, Head, numSectors, sectorLen, myDisk->nsides); + + (void)sim_fseek(myDisk->file, trackOffset, SEEK_SET); + + /* Compute data length, and fill a sector buffer with the + * sector record type as the first byte, and fill the sector + * data with the fillbyte. + */ + sectorData = (uint8 *)malloc(sectorLen); + + if (sectorData == NULL) { + sim_printf("%s: %s(): memory allocation failure.\n", __FILE__, __FUNCTION__); + return SCPE_MEM; + } + + memset(sectorData, fillbyte, sectorLen); + + /* For each sector on the track, write the record type and sector data. */ + for(i=0;itrack[Cyl][Head].sectorOffsetMap[i] = (uint32) sim_ftell(fileref); + + sim_fwrite(sectorData, 1, sectorLen, fileref); + } + + /* Flush the file, and free the sector buffer. */ + fflush(fileref); + free(sectorData); + + return(SCPE_OK); +} + +static t_stat diskCreateIMD(DISK_INFO *myDisk, int32 Tracks, int32 Heads, uint8 Mode, uint16 nSecs, uint32 sectSize, uint8 startSector) +{ + uint8 Track, Side, Sector; + + myDisk->ntracks = Tracks; + myDisk->nsides = Heads; + + for (Track = 0; Track < Tracks; Track++) { + for (Side = 0; Side < Heads; Side++) { + myDisk->track[Track][Side].mode = Mode; + myDisk->track[Track][Side].nsects = (uint8) nSecs; + myDisk->track[Track][Side].sectsize = sectSize; + myDisk->track[Track][Side].start_sector = startSector; + + for (Sector = 0; Sector < myDisk->track[Track][Side].nsects; Sector++) { + myDisk->track[Track][Side].sectorOffsetMap[Sector] = + (((Track * myDisk->nsides) + Side) * + myDisk->track[Track][Side].nsects * + myDisk->track[Track][Side].sectsize) + + (Sector * myDisk->track[Track][Side].sectsize); + myDisk->track[Track][Side].logicalHead[Sector] = 0; + myDisk->track[Track][Side].logicalCyl[Sector] = Track; + } + } + } + + return SCPE_OK; +} + +static t_stat diskShowIMD(DISK_INFO *myDisk) +{ + uint8 Track, Side; + + sim_printf("Tracks: %02d\n", myDisk->ntracks); + sim_printf("Sides: %d\n", myDisk->nsides); + + for (Track = 0; Track < myDisk->ntracks; Track++) { + for (Side = 0; Side < myDisk->nsides; Side++) { + sim_printf("T:%02d H:%d M:%d N:%02d S:%04d\n", + Track, Side, + myDisk->track[Track][Side].mode, + myDisk->track[Track][Side].nsects, + myDisk->track[Track][Side].sectsize); + } + } + + return SCPE_OK; } diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj index 55397dd09..fe6a3bbd2 100644 --- a/Visual Studio Projects/AltairZ80.vcproj +++ b/Visual Studio Projects/AltairZ80.vcproj @@ -390,6 +390,10 @@ RelativePath="..\AltairZ80\s100_pmmi.c" > + + @@ -418,6 +422,10 @@ RelativePath="..\AltairZ80\s100_vdm1.c" > + + diff --git a/Visual Studio Projects/AltairZ80.vcxproj b/Visual Studio Projects/AltairZ80.vcxproj index ad8a0835f..32fad2531 100755 --- a/Visual Studio Projects/AltairZ80.vcxproj +++ b/Visual Studio Projects/AltairZ80.vcxproj @@ -287,6 +287,7 @@ + @@ -294,6 +295,7 @@ + diff --git a/makefile b/makefile index b60a6fee1..fad8d81e4 100644 --- a/makefile +++ b/makefile @@ -1899,6 +1899,9 @@ ALTAIR_OPT = -I ${ALTAIRD} ALTAIRZ80D = ${SIMHD}/AltairZ80 ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/wd179x.c ${ALTAIRZ80D}/s100_hdc1001.c \ + ${ALTAIRZ80D}/s100_sbc200.c \ + ${ALTAIRZ80D}/s100_vfii.c \ ${ALTAIRZ80D}/s100_tuart.c \ ${ALTAIRZ80D}/s100_dazzler.c \ ${ALTAIRZ80D}/s100_jair.c \ @@ -1926,7 +1929,6 @@ ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ ${ALTAIRZ80D}/s100_scp300f.c \ ${ALTAIRZ80D}/s100_tarbell.c \ ${ALTAIRZ80D}/s100_tdd.c \ - ${ALTAIRZ80D}/wd179x.c ${ALTAIRZ80D}/s100_hdc1001.c \ ${ALTAIRZ80D}/s100_if3.c ${ALTAIRZ80D}/s100_adcs6.c \ ${ALTAIRZ80D}/m68k/m68kcpu.c ${ALTAIRZ80D}/m68k/m68kdasm.c ${ALTAIRZ80D}/m68k/m68kasm.c \ ${ALTAIRZ80D}/m68k/m68kopac.c ${ALTAIRZ80D}/m68k/m68kopdm.c \ diff --git a/scp.c b/scp.c index 7d9ea9cf1..3e1348296 100644 --- a/scp.c +++ b/scp.c @@ -1649,7 +1649,9 @@ static const char simh_help2[] = " specified device from the configuration. A DISABLED device is invisible\n" " to running programs. The device can still be RESET, but it cannot be\n" " ATTAChed, DETACHed, or BOOTed. SET ENABLED restores a disabled\n" - " device to a configuration.\n\n" + " device to a configuration. The SET DISABLED command has an\n" + " optional -F switch which will force units to be detached and removed from\n" + " the event queue.\n\n" " Most multi-unit devices allow units to be enabled or disabled:\n\n" "++SET ENABLED\n" "++SET DISABLED\n\n" @@ -6108,8 +6110,12 @@ else { return SCPE_OK; for (i = 0; i < dptr->numunits; i++) { /* check units */ up = (dptr->units) + i; /* att or active? */ - if ((up->flags & UNIT_ATT) || sim_is_active (up)) - return sim_messagef (SCPE_NOFNC, "%s has attached or busy units\n", sim_dname (dptr)); /* can't do it */ + if ((up->flags & UNIT_ATT) || sim_is_active (up)) { + if ((sim_switches & SWMASK ('F')) == 0) /* no forced disable? */ + return sim_messagef (SCPE_NOFNC, "%s has attached or busy units\n", sim_dname (dptr)); /* can't do it */ + sim_cancel (up); + (void)scp_detach_unit (dptr, up); + } } dptr->flags = dptr->flags | DEV_DIS; /* disable */ } @@ -16616,11 +16622,14 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { default: break; } - if (was_disabled) + if (was_disabled) { + sim_switches |= SWMASK ('F'); /* force complete disable */ set_dev_enbdis (dptr, NULL, 0, NULL); + } } else tstat = SCPE_OK; /* can't enable, just skip device */ + sim_switches = saved_switches; if (tstat != SCPE_OK) { stat = tstat; sim_printf ("%s device tests returned: %d - %s\n", dptr->name, SCPE_BARE_STATUS (tstat), sim_error_text (tstat)); diff --git a/scp.h b/scp.h index 04199d65b..e6c0f0263 100644 --- a/scp.h +++ b/scp.h @@ -240,6 +240,7 @@ t_stat sim_exp_check (EXPECT *exp, uint8 data); CONST char *match_ext (CONST char *fnam, const char *ext); int sim_cmp_string (const char *s1, const char *s2); t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_add_debug_flags (DEVICE *dptr, DEBTAB *debflags); diff --git a/sim_imd.h b/sim_imd.h index 88a51831f..19420edf0 100644 --- a/sim_imd.h +++ b/sim_imd.h @@ -64,7 +64,7 @@ typedef struct { #define MAX_CYL 80 #define MAX_HEAD 2 -#define MAX_SPT 26 +#define MAX_SPT 51 /* Tarbell DD has a 51 sector per track format */ #define FD_FLAG_WRITELOCK 1