1
0
mirror of https://github.com/simh/simh.git synced 2026-01-11 23:52:58 +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_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_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_cmd_in(const int32 port);
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 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 attachCPM(UNIT *uptr, int32 readOnly);
static void detachCPM(UNIT *uptr);
@ -121,13 +123,23 @@ const char* simh_description(DEVICE *dptr) {
DEVICE simh_dev = {
"SIMH", &simh_unit, simh_reg, simh_mod,
1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH,
NULL, NULL, &simh_dev_reset,
NULL, NULL, &simh_reset,
NULL, NULL, NULL,
NULL, (DEV_DISABLE | DEV_DEBUG), 0,
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 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.
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 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 getHostFilenamesCmd 29 /* 29 perform wildcard expansion and obtain list of file names */
#define kSimhPseudoDeviceCommands 35 /* Highest AltairZ80 SIMH command */
static const char *cmdNames[kSimhPseudoDeviceCommands] = {
@ -215,7 +228,7 @@ static const char *cmdNames[kSimhPseudoDeviceCommands] = {
"Undefined",
"Undefined",
"getHostOSPathSeparator",
"Undefined",
"getHostFilenames",
"Undefined",
"Undefined",
"Undefined",
@ -225,7 +238,7 @@ static const char *cmdNames[kSimhPseudoDeviceCommands] = {
static char version[] = "SIMH005";
static t_stat simh_dev_reset(DEVICE *dptr) {
static t_stat simh_reset(DEVICE *dptr) {
if (dptr->flags & DEV_DIS) {
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;
}
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) {
int32 i, len = (s100_bus_memr(FCBAddress) & 0x7f); /* 0x80 contains length of command line, discard first char */
for (i = 0; i < len - 1; i++) {
@ -332,17 +371,43 @@ static int32 simh_cmd_in(const int32 port)
result = sim_file_path_separator;
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:
sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" Undefined IN from SIMH pseudo device on port %03xh ignored.\n",
s100_bus_get_addr(), port);
result = lastCommand = 0;
}
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) {
default: /* lastCommand not yet set */
@ -394,6 +459,40 @@ static int32 simh_cmd_out(const int32 port, const int32 data) {
case getHostOSPathSeparatorCmd:
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:
sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" 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 */
}
/* 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)
{
if (io == S100_IO_READ) { /* IN */
if ((simh_unit.flags & UNIT_ATT) == 0) { /* SIMH is not attached */
if (io == S100_IO_READ) { /* IN */
if ((simh_unit.flags & UNIT_ATT) == 0) { /* SIMH is not attached */
if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) {
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);
}
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);
} /* OUT follows */
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
" Command OUT(0x%03x) = 0x%02x\n", s100_bus_get_addr(), port, data);
}
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)
{
int32 ch;
if (io == S100_IO_READ) { /* IN */
if (simh_unit.u3) { /* EOF reached, no more data available */
if (io == S100_IO_READ) { /* IN */
if (simh_unit.u3) { /* EOF reached, no more data available */
if ((simh_dev.dctrl & VERBOSE_MSG) && (warnSIMHEOF < warnLevelSIMH)) {
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);
}
return 0x00;
}
if ((simh_unit.flags & UNIT_ATT) == 0) { /* not attached */
if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) {
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);
}
return 0x00;
}
if ((ch = getc(simh_unit.fileref)) == EOF) { /* end of file? */
simh_unit.u3 = TRUE; /* remember EOF reached */
sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" EOF on read\n", s100_bus_get_addr());
return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */
}
return ch & 0xff;
} /* 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);
/* else ignore data */
}
else if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) {
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);
}
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,
(32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?');
} else {
}
else {
sim_debug(OUT_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT
" OUT(0x%02x) <- %i (0x%02x, '%c')\n", s100_bus_get_addr(),
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_write_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *byteswritten);
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)
{
@ -601,13 +602,13 @@ static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd)
wd->status |= (WD17XX_STAT_DRQ); /* Set DRQ */
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_dataindex = 0;
wd->fdc_write = TRUE;
wd->fdc_write_track = FALSE;
wd->fdc_read = FALSE;
wd->fdc_readadr = FALSE;
wd17xx_set_drq(wd, 1);
sbuf[wd->fdc_dataindex] = wd->data;
break;
@ -626,11 +627,11 @@ static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd)
wd17xx_set_intrq(wd, TRUE);
} else {
wd->status = (WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Set DRQ, BUSY */
wd->drq = 1;
wd->fdc_datacount = 6;
wd->fdc_dataindex = 0;
wd->fdc_read = TRUE;
wd->fdc_readadr = TRUE;
wd17xx_set_drq(wd, 1);
sbuf[0] = wd->track;
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());
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->drq = 0;
wd->fdc_write = FALSE;
wd->fdc_read = FALSE;
wd->fdc_write_track = FALSE;
wd->fdc_readadr = FALSE;
wd->fdc_datacount = 0;
wd->fdc_dataindex = 0;
wd17xx_set_drq(wd, 0);
}
else if (cmd & 0x08) { /* Immediate Interrupt */
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);
}
wd->drq = 1;
wd17xx_set_drq(wd, 1);
break;
/* Type IV Commands */
@ -840,6 +841,12 @@ static void wd17xx_set_intrq(WD17XX_INFO *wd, int32 value)
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)
{
sim_debug(wd->dbg_verbose, wd->dptr, "fdctype: %02X\n", wd->fdctype);