1
0
mirror of https://github.com/simh/simh.git synced 2026-04-27 20:38:04 +00:00

Altair8800: SIMH device and WD17XX API corrections

SIMH: Adds support for wildcard filename transfers
WD17XX: Corrects a problem with DRQ status
This commit is contained in:
Patrick Linstruth
2025-11-23 19:25:32 -05:00
parent aad5351080
commit d81cb4fc48
2 changed files with 144 additions and 24 deletions

View File

@@ -36,13 +36,15 @@
#include "s100_cpu.h" #include "s100_cpu.h"
#include "s100_simh.h" #include "s100_simh.h"
static t_stat simh_dev_reset(DEVICE *dptr); static t_stat simh_reset(DEVICE *dptr);
static int32 simh_io_status(const int32 port, const int32 io, const int32 data); static int32 simh_io_status(const int32 port, const int32 io, const int32 data);
static int32 simh_io_data(const int32 port, const int32 io, const int32 data); static int32 simh_io_data(const int32 port, const int32 io, const int32 data);
static int32 simh_io_cmd(const int32 port, const int32 io, const int32 data); static int32 simh_io_cmd(const int32 port, const int32 io, const int32 data);
static int32 simh_cmd_in(const int32 port); static int32 simh_cmd_in(const int32 port);
static int32 simh_cmd_out(const int32 port, const int32 data); static int32 simh_cmd_out(const int32 port, const int32 data);
static t_stat simh_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); static t_stat simh_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
static void deleteNameList(void);
static void processDirEntry(const char *directory, const char *filename, t_offset FileSize, const struct stat *filestat, void *context);
static void createCPMCommandLine(void); static void createCPMCommandLine(void);
static void attachCPM(UNIT *uptr, int32 readOnly); static void attachCPM(UNIT *uptr, int32 readOnly);
static void detachCPM(UNIT *uptr); static void detachCPM(UNIT *uptr);
@@ -121,13 +123,23 @@ const char* simh_description(DEVICE *dptr) {
DEVICE simh_dev = { DEVICE simh_dev = {
"SIMH", &simh_unit, simh_reg, simh_mod, "SIMH", &simh_unit, simh_reg, simh_mod,
1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH,
NULL, NULL, &simh_dev_reset, NULL, NULL, &simh_reset,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, (DEV_DISABLE | DEV_DEBUG), 0, NULL, (DEV_DISABLE | DEV_DEBUG), 0,
generic_dt, NULL, NULL, &simh_show_help, NULL, NULL, &simh_description generic_dt, NULL, NULL, &simh_show_help, NULL, NULL, &simh_description
}; };
typedef struct NameNode {
char *name;
struct NameNode *next;
} NameNode_t;
static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH]; static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH];
static NameNode_t *nameListHead = NULL;
static NameNode_t *currentName = NULL;
static int32 currentNameIndex = 0;
static int32 lastPathSeparatorIndex = 0;
static int32 firstPathCharacterIndex = 0;
/* Z80 or 8080 programs communicate with the SIMH pseudo device via port 0xfe. /* Z80 or 8080 programs communicate with the SIMH pseudo device via port 0xfe.
The following principles apply: The following principles apply:
@@ -183,6 +195,7 @@ static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH];
#define setZ80CPUCmd 19 /* 19 set the CPU to a Z80 */ #define setZ80CPUCmd 19 /* 19 set the CPU to a Z80 */
#define set8080CPUCmd 20 /* 20 set the CPU to an 8080 */ #define set8080CPUCmd 20 /* 20 set the CPU to an 8080 */
#define getHostOSPathSeparatorCmd 28 /* 28 obtain the file path separator of the OS under which SIMH runs */ #define getHostOSPathSeparatorCmd 28 /* 28 obtain the file path separator of the OS under which SIMH runs */
#define getHostFilenamesCmd 29 /* 29 perform wildcard expansion and obtain list of file names */
#define kSimhPseudoDeviceCommands 35 /* Highest AltairZ80 SIMH command */ #define kSimhPseudoDeviceCommands 35 /* Highest AltairZ80 SIMH command */
static const char *cmdNames[kSimhPseudoDeviceCommands] = { static const char *cmdNames[kSimhPseudoDeviceCommands] = {
@@ -215,7 +228,7 @@ static const char *cmdNames[kSimhPseudoDeviceCommands] = {
"Undefined", "Undefined",
"Undefined", "Undefined",
"getHostOSPathSeparator", "getHostOSPathSeparator",
"Undefined", "getHostFilenames",
"Undefined", "Undefined",
"Undefined", "Undefined",
"Undefined", "Undefined",
@@ -225,7 +238,7 @@ static const char *cmdNames[kSimhPseudoDeviceCommands] = {
static char version[] = "SIMH005"; static char version[] = "SIMH005";
static t_stat simh_dev_reset(DEVICE *dptr) { static t_stat simh_reset(DEVICE *dptr) {
if (dptr->flags & DEV_DIS) { if (dptr->flags & DEV_DIS) {
s100_bus_remio(0xfe, 1, &simh_io_cmd); /* Command Port */ s100_bus_remio(0xfe, 1, &simh_io_cmd); /* Command Port */
} }
@@ -245,6 +258,32 @@ static t_stat simh_dev_reset(DEVICE *dptr) {
return SCPE_OK; return SCPE_OK;
} }
static void deleteNameList(void)
{
while (nameListHead != NULL) {
NameNode_t *next = nameListHead -> next;
free(nameListHead -> name);
free(nameListHead);
nameListHead = next;
}
currentName = NULL;
currentNameIndex = 0;
}
static void processDirEntry(const char *directory, const char *filename, t_offset FileSize, const struct stat *filestat, void *context)
{
if (filename != NULL) {
NameNode_t *top = (NameNode_t *) malloc(sizeof(NameNode_t));
if (top) {
top->name = strdup(filename);
top->next = nameListHead;
nameListHead = top;
}
}
}
static void createCPMCommandLine(void) { static void createCPMCommandLine(void) {
int32 i, len = (s100_bus_memr(FCBAddress) & 0x7f); /* 0x80 contains length of command line, discard first char */ int32 i, len = (s100_bus_memr(FCBAddress) & 0x7f); /* 0x80 contains length of command line, discard first char */
for (i = 0; i < len - 1; i++) { for (i = 0; i < len - 1; i++) {
@@ -332,17 +371,43 @@ static int32 simh_cmd_in(const int32 port)
result = sim_file_path_separator; result = sim_file_path_separator;
break; break;
case getHostFilenamesCmd:
if (nameListHead != NULL) {
if (currentName == NULL) {
deleteNameList();
lastCommand = 0;
}
else if (firstPathCharacterIndex <= lastPathSeparatorIndex) {
result = cpmCommandLine[firstPathCharacterIndex++];
}
else {
result = currentName -> name[currentNameIndex];
if (result == 0) {
currentName = currentName -> next;
firstPathCharacterIndex = currentNameIndex = 0;
}
else {
currentNameIndex++;
}
}
}
break;
default: default:
sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" Undefined IN from SIMH pseudo device on port %03xh ignored.\n", " Undefined IN from SIMH pseudo device on port %03xh ignored.\n",
s100_bus_get_addr(), port); s100_bus_get_addr(), port);
result = lastCommand = 0; result = lastCommand = 0;
} }
return result; return result;
} }
static int32 simh_cmd_out(const int32 port, const int32 data) { static int32 simh_cmd_out(const int32 port, const int32 data)
{
t_stat result;
switch(lastCommand) { switch(lastCommand) {
default: /* lastCommand not yet set */ default: /* lastCommand not yet set */
@@ -394,6 +459,40 @@ static int32 simh_cmd_out(const int32 port, const int32 data) {
case getHostOSPathSeparatorCmd: case getHostOSPathSeparatorCmd:
break; break;
case getHostFilenamesCmd: /* list files of host file directory */
if (nameListHead == NULL) {
createCPMCommandLine();
lastPathSeparatorIndex = 0;
while (cpmCommandLine[lastPathSeparatorIndex]) {
lastPathSeparatorIndex++;
}
while ((lastPathSeparatorIndex >= 0) && (cpmCommandLine[lastPathSeparatorIndex] != sim_file_path_separator)) {
lastPathSeparatorIndex--;
}
firstPathCharacterIndex = 0;
deleteNameList();
result = sim_dir_scan(cpmCommandLine, processDirEntry, NULL);
if (result == SCPE_OK) {
currentName = nameListHead;
currentNameIndex = 0;
}
else {
deleteNameList();
sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT " Cannot expand '%s'. Error is %s.\n",
s100_bus_get_addr(), cpmCommandLine, sim_error_text(result));
}
}
break;
default: default:
sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" Unknown command (%i) to SIMH pseudo device on port %03xh ignored.\n", " Unknown command (%i) to SIMH pseudo device on port %03xh ignored.\n",
@@ -404,20 +503,22 @@ static int32 simh_cmd_out(const int32 port, const int32 data) {
return 0xff; /* ignored, since OUT */ return 0xff; /* ignored, since OUT */
} }
/* port 0xfc is a device for communication SIMH <--> Altair machine */ /* port 0x12 is a device for communication SIMH <--> Altair machine */
static int32 simh_io_status(const int32 port, const int32 io, const int32 data) static int32 simh_io_status(const int32 port, const int32 io, const int32 data)
{ {
if (io == S100_IO_READ) { /* IN */ if (io == S100_IO_READ) { /* IN */
if ((simh_unit.flags & UNIT_ATT) == 0) { /* SIMH is not attached */ if ((simh_unit.flags & UNIT_ATT) == 0) { /* SIMH is not attached */
if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) { if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) {
warnUnattachedSIMH++; warnUnattachedSIMH++;
/*06*/ sim_debug(VERBOSE_MSG, &simh_dev, "PTR: " ADDRESS_FORMAT
sim_debug(VERBOSE_MSG, &simh_dev, "PTR: " ADDRESS_FORMAT
" Attempt to test status of unattached SIMH[0x%02x]. 0x02 returned.\n", s100_bus_get_addr(), port); " Attempt to test status of unattached SIMH[0x%02x]. 0x02 returned.\n", s100_bus_get_addr(), port);
} }
return SIMH_CAN_WRITE; return SIMH_CAN_WRITE;
} }
/* if EOF then SIMH_CAN_WRITE else
(SIMH_CAN_WRITE and SIMH_CAN_READ) */ /* if EOF then SIMH_CAN_WRITE else (SIMH_CAN_WRITE and SIMH_CAN_READ) */
return simh_unit.u3 ? SIMH_CAN_WRITE : (SIMH_CAN_READ | SIMH_CAN_WRITE); return simh_unit.u3 ? SIMH_CAN_WRITE : (SIMH_CAN_READ | SIMH_CAN_WRITE);
} /* OUT follows */ } /* OUT follows */
if (data == SIMH_RESET) { if (data == SIMH_RESET) {
@@ -425,47 +526,58 @@ static int32 simh_io_status(const int32 port, const int32 io, const int32 data)
sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" Command OUT(0x%03x) = 0x%02x\n", s100_bus_get_addr(), port, data); " Command OUT(0x%03x) = 0x%02x\n", s100_bus_get_addr(), port, data);
} }
return 0x00; /* ignored since OUT */ return 0x00; /* ignored since OUT */
} }
/* port 0xfd is a device for communication SIMH <--> Altair machine */ /* port 0x13 is a device for communication SIMH <--> Altair machine */
static int32 simh_io_data(const int32 port, const int32 io, const int32 data) static int32 simh_io_data(const int32 port, const int32 io, const int32 data)
{ {
int32 ch; int32 ch;
if (io == S100_IO_READ) { /* IN */ if (io == S100_IO_READ) { /* IN */
if (simh_unit.u3) { /* EOF reached, no more data available */ if (simh_unit.u3) { /* EOF reached, no more data available */
if ((simh_dev.dctrl & VERBOSE_MSG) && (warnSIMHEOF < warnLevelSIMH)) { if ((simh_dev.dctrl & VERBOSE_MSG) && (warnSIMHEOF < warnLevelSIMH)) {
warnSIMHEOF++; warnSIMHEOF++;
/*07*/ sim_debug(VERBOSE_MSG, &simh_dev, "PTR: " ADDRESS_FORMAT
sim_debug(VERBOSE_MSG, &simh_dev, "PTR: " ADDRESS_FORMAT
" SIMH[0x%02x] attempted to read past EOF. 0x00 returned.\n", s100_bus_get_addr(), port); " SIMH[0x%02x] attempted to read past EOF. 0x00 returned.\n", s100_bus_get_addr(), port);
} }
return 0x00; return 0x00;
} }
if ((simh_unit.flags & UNIT_ATT) == 0) { /* not attached */ if ((simh_unit.flags & UNIT_ATT) == 0) { /* not attached */
if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) { if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) {
warnUnattachedSIMH++; warnUnattachedSIMH++;
/*08*/ sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" Attempt to read from unattached SIMH[0x%02x]. 0x00 returned.\n", s100_bus_get_addr(), port); " Attempt to read from unattached SIMH[0x%02x]. 0x00 returned.\n", s100_bus_get_addr(), port);
} }
return 0x00; return 0x00;
} }
if ((ch = getc(simh_unit.fileref)) == EOF) { /* end of file? */ if ((ch = getc(simh_unit.fileref)) == EOF) { /* end of file? */
simh_unit.u3 = TRUE; /* remember EOF reached */ simh_unit.u3 = TRUE; /* remember EOF reached */
sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" EOF on read\n", s100_bus_get_addr()); " EOF on read\n", s100_bus_get_addr());
return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */ return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */
} }
return ch & 0xff; return ch & 0xff;
} /* OUT follows */ } /* OUT follows */
if (simh_unit.flags & UNIT_ATT) /* unit must be attached */
if (simh_unit.flags & UNIT_ATT) { /* unit must be attached */
putc(data, simh_unit.fileref); putc(data, simh_unit.fileref);
/* else ignore data */ }
else if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) { else if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) {
warnUnattachedSIMH++; warnUnattachedSIMH++;
/*09*/ sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" Attempt to output '0x%02x' to unattached SIMH[0x%02x] - ignored.\n", s100_bus_get_addr(), data, port); " Attempt to output '0x%02x' to unattached SIMH[0x%02x] - ignored.\n", s100_bus_get_addr(), data, port);
} }
return 0x00; /* ignored since OUT */ return 0x00; /* ignored since OUT */
} }
@@ -481,7 +593,8 @@ static int32 simh_io_cmd(const int32 port, const int32 io, const int32 data)
port, result, result, port, result, result,
(32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?'); (32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?');
} else { }
else {
sim_debug(OUT_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT sim_debug(OUT_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" OUT(0x%02x) <- %i (0x%02x, '%c')\n", s100_bus_get_addr(), " OUT(0x%02x) <- %i (0x%02x, '%c')\n", s100_bus_get_addr(),
port, data, data, port, data, data,

View File

@@ -45,6 +45,7 @@ static uint8 wd17xx_sec_len(WD17XX_INFO *wd);
static t_stat wd17xx_read_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *bytesread); static t_stat wd17xx_read_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *bytesread);
static t_stat wd17xx_write_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *byteswritten); static t_stat wd17xx_write_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *byteswritten);
static void wd17xx_set_intrq(WD17XX_INFO *wd, int32 value); static void wd17xx_set_intrq(WD17XX_INFO *wd, int32 value);
static void wd17xx_set_drq(WD17XX_INFO *wd, int32 value);
WD17XX_INFO * wd17xx_init(DEVICE *dptr) WD17XX_INFO * wd17xx_init(DEVICE *dptr)
{ {
@@ -601,13 +602,13 @@ static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd)
wd->status |= (WD17XX_STAT_DRQ); /* Set DRQ */ wd->status |= (WD17XX_STAT_DRQ); /* Set DRQ */
wd->status |= (wd->dsk->unit->flags & UNIT_RO) ? WD17XX_STAT_WP : 0; /* Set WP */ wd->status |= (wd->dsk->unit->flags & UNIT_RO) ? WD17XX_STAT_WP : 0; /* Set WP */
wd->drq = 1;
wd->fdc_datacount = dsk_sector_size(wd->dsk, wd->track, wd->side); wd->fdc_datacount = dsk_sector_size(wd->dsk, wd->track, wd->side);
wd->fdc_dataindex = 0; wd->fdc_dataindex = 0;
wd->fdc_write = TRUE; wd->fdc_write = TRUE;
wd->fdc_write_track = FALSE; wd->fdc_write_track = FALSE;
wd->fdc_read = FALSE; wd->fdc_read = FALSE;
wd->fdc_readadr = FALSE; wd->fdc_readadr = FALSE;
wd17xx_set_drq(wd, 1);
sbuf[wd->fdc_dataindex] = wd->data; sbuf[wd->fdc_dataindex] = wd->data;
break; break;
@@ -626,11 +627,11 @@ static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd)
wd17xx_set_intrq(wd, TRUE); wd17xx_set_intrq(wd, TRUE);
} else { } else {
wd->status = (WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Set DRQ, BUSY */ wd->status = (WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Set DRQ, BUSY */
wd->drq = 1;
wd->fdc_datacount = 6; wd->fdc_datacount = 6;
wd->fdc_dataindex = 0; wd->fdc_dataindex = 0;
wd->fdc_read = TRUE; wd->fdc_read = TRUE;
wd->fdc_readadr = TRUE; wd->fdc_readadr = TRUE;
wd17xx_set_drq(wd, 1);
sbuf[0] = wd->track; sbuf[0] = wd->track;
sbuf[1] = wd->side; sbuf[1] = wd->side;
@@ -676,13 +677,13 @@ static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd)
sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=FORCE_INTR\n", s100_bus_get_addr()); sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=FORCE_INTR\n", s100_bus_get_addr());
if ((cmd & WD17XX_CMD_MASK) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */ if ((cmd & WD17XX_CMD_MASK) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */
wd->status &= ~(WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Clear DRQ, BUSY */ wd->status &= ~(WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Clear DRQ, BUSY */
wd->drq = 0;
wd->fdc_write = FALSE; wd->fdc_write = FALSE;
wd->fdc_read = FALSE; wd->fdc_read = FALSE;
wd->fdc_write_track = FALSE; wd->fdc_write_track = FALSE;
wd->fdc_readadr = FALSE; wd->fdc_readadr = FALSE;
wd->fdc_datacount = 0; wd->fdc_datacount = 0;
wd->fdc_dataindex = 0; wd->fdc_dataindex = 0;
wd17xx_set_drq(wd, 0);
} }
else if (cmd & 0x08) { /* Immediate Interrupt */ else if (cmd & 0x08) { /* Immediate Interrupt */
wd17xx_set_intrq(wd, TRUE); wd17xx_set_intrq(wd, TRUE);
@@ -757,7 +758,7 @@ static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd)
s100_bus_int(1 << wd->intvector, wd->intvector * 2); s100_bus_int(1 << wd->intvector, wd->intvector * 2);
} }
wd->drq = 1; wd17xx_set_drq(wd, 1);
break; break;
/* Type IV Commands */ /* Type IV Commands */
@@ -840,6 +841,12 @@ static void wd17xx_set_intrq(WD17XX_INFO *wd, int32 value)
wd->drq = !wd->intrq; wd->drq = !wd->intrq;
} }
static void wd17xx_set_drq(WD17XX_INFO *wd, int32 value)
{
wd->drq = (value) ? TRUE : FALSE; /* INTRQ and DRQ are mutually exclusive */
wd->intrq = !wd->drq;
}
void wd17xx_show(WD17XX_INFO *wd) void wd17xx_show(WD17XX_INFO *wd)
{ {
sim_debug(wd->dbg_verbose, wd->dptr, "fdctype: %02X\n", wd->fdctype); sim_debug(wd->dbg_verbose, wd->dptr, "fdctype: %02X\n", wd->fdctype);