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