3b2: STRCPY fix, CTC and NI cleanup

- The previous fix for STRCPY introduced a new bug. STRCPY must always
  copy the final NULL terminator of the string, but must NOT increment
  the source or destination pointers for the NULL terminator.

- The CTC simulation did not correctly support streaming mode, which
  can in some cases request reads that are not on 512-byte block
  boundaries.

- To begin to support System V Release 4 UNIX, the NI card (called EMD
  under SVR4) needed to support several more CRC codes for pump code.
This commit is contained in:
Seth Morabito 2019-08-15 07:58:16 -07:00
parent 61e101b5e2
commit 9736cc3116
7 changed files with 108 additions and 30 deletions

View file

@ -2127,6 +2127,11 @@ t_stat sim_instr(void)
break; break;
case BRB: case BRB:
pc_incr = sign_extend_b(dst->embedded.b); pc_incr = sign_extend_b(dst->embedded.b);
/* BRB is commonly used to halt the processor in a tight
* infinite loop. */
if (pc_incr == 0) {
stop_reason = STOP_LOOP;
}
break; break;
case BSBH: case BSBH:
cpu_push_word(R[NUM_PC] + pc_incr); cpu_push_word(R[NUM_PC] + pc_incr);
@ -2554,7 +2559,7 @@ t_stat sim_instr(void)
} }
break; break;
case STREND: case STREND:
while (read_b(R[0], ACC_AF) != 0) { while (read_b(R[0], ACC_AF) != '\0') {
R[0]++; R[0]++;
} }
break; break;
@ -2907,7 +2912,6 @@ t_stat sim_instr(void)
pc_incr = 0; pc_incr = 0;
break; break;
case SPOP: case SPOP:
sim_debug(TRACE_DBG, &cpu_dev, "SPOP\n");
/* Memory fault is signaled when no support processor is /* Memory fault is signaled when no support processor is
active */ active */
if (mau_broadcast(coprocessor_word, 0, 0) != SCPE_OK) { if (mau_broadcast(coprocessor_word, 0, 0) != SCPE_OK) {
@ -2917,7 +2921,6 @@ t_stat sim_instr(void)
case SPOPD2: case SPOPD2:
case SPOPS2: case SPOPS2:
case SPOPT2: case SPOPT2:
sim_debug(TRACE_DBG, &cpu_dev, "SPOP{D|S|T}2\n");
a = cpu_effective_address(src1); a = cpu_effective_address(src1);
b = cpu_effective_address(dst); b = cpu_effective_address(dst);
if (mau_broadcast(coprocessor_word, a, b) != SCPE_OK) { if (mau_broadcast(coprocessor_word, a, b) != SCPE_OK) {
@ -2927,7 +2930,6 @@ t_stat sim_instr(void)
case SPOPRD: case SPOPRD:
case SPOPRS: case SPOPRS:
case SPOPRT: case SPOPRT:
sim_debug(TRACE_DBG, &cpu_dev, "SPOPR{D|S|T}\n");
a = cpu_effective_address(src1); a = cpu_effective_address(src1);
if (mau_broadcast(coprocessor_word, a, 0) != SCPE_OK) { if (mau_broadcast(coprocessor_word, a, 0) != SCPE_OK) {
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
@ -2936,7 +2938,6 @@ t_stat sim_instr(void)
case SPOPWD: case SPOPWD:
case SPOPWS: case SPOPWS:
case SPOPWT: case SPOPWT:
sim_debug(TRACE_DBG, &cpu_dev, "SPOPW{D|S|T}\n");
a = cpu_effective_address(dst); a = cpu_effective_address(dst);
if (mau_broadcast(coprocessor_word, 0, a) != SCPE_OK) { if (mau_broadcast(coprocessor_word, 0, a) != SCPE_OK) {
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
@ -3017,9 +3018,16 @@ t_stat sim_instr(void)
R[NUM_FP] = R[NUM_SP]; R[NUM_FP] = R[NUM_SP];
break; break;
case STRCPY: case STRCPY:
a = 0; /* The STRCPY instruction will always copy the NULL
while ((a = read_b(R[0], ACC_AF)) != '\0') { * terminator of a string. However, copying the NULL
* terminator never increments the source or destination
* pointer! */
while (1) {
a = read_b(R[0], ACC_AF);
write_b(R[1], (uint8) a); write_b(R[1], (uint8) a);
if (a == '\0') {
break;
}
R[0]++; R[0]++;
R[1]++; R[1]++;
} }

View file

@ -262,10 +262,11 @@ static void ctc_cmd(uint8 cid,
cio_entry *cqe, uint8 *capp_data) cio_entry *cqe, uint8 *capp_data)
{ {
uint32 vtoc_addr, pdinfo_addr, ctjob_addr; uint32 vtoc_addr, pdinfo_addr, ctjob_addr;
uint32 maxpass, blkno, delay; uint32 maxpass, blkno, delay, last_byte;
uint8 dev; uint8 dev, c;
uint8 sec_buf[512]; uint8 sec_buf[512];
int32 b, i, j; int32 b, i, j;
int32 block_count, read_bytes, remainder, dest;
t_seccnt secrw = 0; t_seccnt secrw = 0;
struct vtoc vtoc = {{0}}; struct vtoc vtoc = {{0}};
struct pdinfo pdinfo = {0}; struct pdinfo pdinfo = {0};
@ -417,6 +418,7 @@ static void ctc_cmd(uint8 cid,
delay = DELAY_OPEN; delay = DELAY_OPEN;
ctc_state[dev].time = 0; /* Opening always resets session time to 0 */ ctc_state[dev].time = 0; /* Opening always resets session time to 0 */
ctc_state[dev].bytnum = 0;
vtoc_addr = rqe->address; vtoc_addr = rqe->address;
pdinfo_addr = ATOW(rapp_data, 4); pdinfo_addr = ATOW(rapp_data, 4);
@ -522,6 +524,7 @@ static void ctc_cmd(uint8 cid,
cqe->byte_count = rqe->byte_count; cqe->byte_count = rqe->byte_count;
cqe->subdevice = rqe->subdevice; cqe->subdevice = rqe->subdevice;
cqe->address = ATOW(rapp_data, 4); cqe->address = ATOW(rapp_data, 4);
dest = rqe->address;
if (dev == XMF_DEV) { if (dev == XMF_DEV) {
cqe->opcode = CTC_NOTREADY; cqe->opcode = CTC_NOTREADY;
@ -533,19 +536,70 @@ static void ctc_cmd(uint8 cid,
break; break;
} }
/*
* This read routine supports both streaming and block
* oriented modes.
*
* Read requests from the host give a block number, and a
* number of bytes to read. In streaming mode, however, there
* is no requirement that the number of bytes to read has to
* be block-aligned, so we must support reading an arbitrary
* number of bytes from the tape stream and remembering the
* current position in the byte stream.
*
*/
/* The block number to begin reading from is supplied in the
* request queue entry's APP_DATA field. */
blkno = ATOW(rapp_data, 0); blkno = ATOW(rapp_data, 0);
for (b = 0; b < rqe->byte_count / 512; b++) { /* Since we may start reading from the data stream at an
* arbitrary location, we compute the offset of the last byte
* to be read, and use that to figure out how many bytes will
* be left over to read from an "extra" block */
last_byte = ctc_state[dev].bytnum + rqe->byte_count;
remainder = last_byte % 512;
/* The number of blocks we have to read in total is computed
* by looking at the byte count, PLUS any remainder that will
* be left after crossing a block boundary */
block_count = rqe->byte_count / 512;
if (((rqe->byte_count % 512) > 0 || remainder > 0)) {
block_count++;
}
/* Now step over each block, and start reading from the
* necessary location. */
for (b = 0; b < block_count; b++) {
uint32 start_byte;
/* Add some read time to the read time counter */
ctc_state[dev].time += 10; ctc_state[dev].time += 10;
start_byte = ctc_state[dev].bytnum % 512;
lba = blkno + b; lba = blkno + b;
result = sim_disk_rdsect(&ctc_unit, lba, sec_buf, &secrw, 1); result = sim_disk_rdsect(&ctc_unit, lba, sec_buf, &secrw, 1);
if (result == SCPE_OK) { if (result == SCPE_OK) {
sim_debug(TRACE_DBG, &ctc_dev, /* If this is the last "extra" block, we will only
"[ctc_cmd] ... CTC_READ: 512 bytes from block %d (0x%x)\n", * read the remainder of bytes from it. Otherwise, we
lba, lba); * need to consume the whole block. */
for (j = 0; j < 512; j++) { if (b == (block_count - 1) && remainder > 0) {
read_bytes = remainder;
} else {
read_bytes = 512 - start_byte;
}
for (j = 0; j < read_bytes; j++) {
uint32 offset;
/* Drain the buffer */ /* Drain the buffer */
pwrite_b(rqe->address + (b * 512) + j, sec_buf[j]); if (b == 0 && start_byte != 0) {
/* This is a partial read of the first block,
* continuing to read from a previous partial
* block read. */
offset = j + start_byte;
} else {
offset = j;
}
c = sec_buf[offset];
pwrite_b(dest++, c);
ctc_state[dev].bytnum++;
} }
} else { } else {
sim_debug(TRACE_DBG, &ctc_dev, sim_debug(TRACE_DBG, &ctc_dev,

View file

@ -139,6 +139,7 @@ struct pdinfo {
typedef struct { typedef struct {
uint32 time; /* Time used during a tape session (in 25ms chunks) */ uint32 time; /* Time used during a tape session (in 25ms chunks) */
uint32 bytnum; /* Byte number, for streaming mode */
} CTC_STATE; } CTC_STATE;
extern DEVICE ctc_dev; extern DEVICE ctc_dev;

View file

@ -134,7 +134,8 @@ noret __libc_longjmp (jmp_buf buf, int val);
#define STOP_ESTK 6 /* Exception stack too deep */ #define STOP_ESTK 6 /* Exception stack too deep */
#define STOP_MMU 7 /* Unimplemented MMU Feature */ #define STOP_MMU 7 /* Unimplemented MMU Feature */
#define STOP_POWER 8 /* System power-off */ #define STOP_POWER 8 /* System power-off */
#define STOP_ERR 9 /* Other error */ #define STOP_LOOP 9 /* Infinite loop stop */
#define STOP_ERR 10 /* Other error */
/* Exceptional conditions handled within the instruction loop */ /* Exceptional conditions handled within the instruction loop */
#define ABORT_EXC 1 /* CPU exception */ #define ABORT_EXC 1 /* CPU exception */

View file

@ -112,6 +112,21 @@ static t_stat ni_show_queue_common(FILE *st, UNIT *uptr, int32 val,
static t_stat ni_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat ni_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat ni_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat ni_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
/*
* When the NI card is pumped, its CRC depends on what slot it is
* installed in and what version of driver has been installed.
*/
#define NI_DIAG_CRCS_LEN 7
static const uint32 NI_DIAG_CRCS[] = {
0x795268a4,
0xfab1057c,
0x10ca00cd,
0x9b3ddeda,
0x267b19a0,
0x123f36c0,
0xc04ca0ab,
};
/* /*
* Unit 0: Packet reception. * Unit 0: Packet reception.
* Unit 1: Sanity timer. * Unit 1: Sanity timer.
@ -331,14 +346,15 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
/* If the currently running program is a diagnostics program, /* If the currently running program is a diagnostics program,
* we are expected to write results into memory at address * we are expected to write results into memory at address
* 0x200f000 */ * 0x200f000 */
if (ni.crc == NI_DIAG_CRC1 || for (i = 0; i < NI_DIAG_CRCS_LEN; i++) {
ni.crc == NI_DIAG_CRC2 || if (ni.crc == NI_DIAG_CRCS[i]) {
ni.crc == NI_DIAG_CRC3) {
pwrite_h(0x200f000, 0x1); /* Test success */ pwrite_h(0x200f000, 0x1); /* Test success */
pwrite_h(0x200f002, 0x0); /* Test Number */ pwrite_h(0x200f002, 0x0); /* Test Number */
pwrite_h(0x200f004, 0x0); /* Actual */ pwrite_h(0x200f004, 0x0); /* Actual */
pwrite_h(0x200f006, 0x0); /* Expected */ pwrite_h(0x200f006, 0x0); /* Expected */
pwrite_b(0x200f008, 0x1); /* Success flag again */ pwrite_b(0x200f008, 0x1); /* Success flag again */
break;
}
} }
/* Store the sequence byte we were sent for later reply. */ /* Store the sequence byte we were sent for later reply. */
@ -364,8 +380,8 @@ static void ni_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data, t_bool is_exp
sim_debug(DBG_TRACE, &ni_dev, sim_debug(DBG_TRACE, &ni_dev,
"[ni_cmd] Determine Sub-Devices.\n"); "[ni_cmd] Determine Sub-Devices.\n");
/* The system wants us to write sub-device structures /* The system wants us to write sub-device structures at the
* at the supplied address */ * supplied address */
pwrite_h(rentry->address, 0x0); pwrite_h(rentry->address, 0x0);
if (is_exp) { if (is_exp) {
@ -666,7 +682,7 @@ t_stat ni_autoconfig()
{ {
uint8 cid; uint8 cid;
/* Clear the CIO table of PORTS cards */ /* Clear the CIO table of NI cards */
for (cid = 0; cid < CIO_SLOTS; cid++) { for (cid = 0; cid < CIO_SLOTS; cid++) {
if (cio[cid].id == NI_ID) { if (cio[cid].id == NI_ID) {
cio[cid].id = 0; cio[cid].id = 0;

View file

@ -88,9 +88,6 @@
#define NI_QPOLL_FAST 100 #define NI_QPOLL_FAST 100
#define NI_QPOLL_SLOW 50000 #define NI_QPOLL_SLOW 50000
#define NI_DIAG_CRC1 0x795268a4
#define NI_DIAG_CRC2 0xfab1057c
#define NI_DIAG_CRC3 0x10ca00cd
#define NI_PUMP_CRC1 0xfab1057c #define NI_PUMP_CRC1 0xfab1057c
#define NI_PUMP_CRC2 0xf6744bed #define NI_PUMP_CRC2 0xf6744bed

View file

@ -82,6 +82,7 @@ const char *sim_stop_messages[] = {
"Exception Stack Too Deep", "Exception Stack Too Deep",
"Unimplemented MMU Feature", "Unimplemented MMU Feature",
"System Powered Off", "System Powered Off",
"Infinite Loop",
"Simulator Error" "Simulator Error"
}; };