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:
parent
61e101b5e2
commit
9736cc3116
7 changed files with 108 additions and 30 deletions
|
@ -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]++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
28
3B2/3b2_ni.c
28
3B2/3b2_ni.c
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue