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;
|
||||
case BRB:
|
||||
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;
|
||||
case BSBH:
|
||||
cpu_push_word(R[NUM_PC] + pc_incr);
|
||||
|
@ -2554,7 +2559,7 @@ t_stat sim_instr(void)
|
|||
}
|
||||
break;
|
||||
case STREND:
|
||||
while (read_b(R[0], ACC_AF) != 0) {
|
||||
while (read_b(R[0], ACC_AF) != '\0') {
|
||||
R[0]++;
|
||||
}
|
||||
break;
|
||||
|
@ -2907,7 +2912,6 @@ t_stat sim_instr(void)
|
|||
pc_incr = 0;
|
||||
break;
|
||||
case SPOP:
|
||||
sim_debug(TRACE_DBG, &cpu_dev, "SPOP\n");
|
||||
/* Memory fault is signaled when no support processor is
|
||||
active */
|
||||
if (mau_broadcast(coprocessor_word, 0, 0) != SCPE_OK) {
|
||||
|
@ -2917,7 +2921,6 @@ t_stat sim_instr(void)
|
|||
case SPOPD2:
|
||||
case SPOPS2:
|
||||
case SPOPT2:
|
||||
sim_debug(TRACE_DBG, &cpu_dev, "SPOP{D|S|T}2\n");
|
||||
a = cpu_effective_address(src1);
|
||||
b = cpu_effective_address(dst);
|
||||
if (mau_broadcast(coprocessor_word, a, b) != SCPE_OK) {
|
||||
|
@ -2927,7 +2930,6 @@ t_stat sim_instr(void)
|
|||
case SPOPRD:
|
||||
case SPOPRS:
|
||||
case SPOPRT:
|
||||
sim_debug(TRACE_DBG, &cpu_dev, "SPOPR{D|S|T}\n");
|
||||
a = cpu_effective_address(src1);
|
||||
if (mau_broadcast(coprocessor_word, a, 0) != SCPE_OK) {
|
||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||
|
@ -2936,7 +2938,6 @@ t_stat sim_instr(void)
|
|||
case SPOPWD:
|
||||
case SPOPWS:
|
||||
case SPOPWT:
|
||||
sim_debug(TRACE_DBG, &cpu_dev, "SPOPW{D|S|T}\n");
|
||||
a = cpu_effective_address(dst);
|
||||
if (mau_broadcast(coprocessor_word, 0, a) != SCPE_OK) {
|
||||
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
||||
|
@ -3017,9 +3018,16 @@ t_stat sim_instr(void)
|
|||
R[NUM_FP] = R[NUM_SP];
|
||||
break;
|
||||
case STRCPY:
|
||||
a = 0;
|
||||
while ((a = read_b(R[0], ACC_AF)) != '\0') {
|
||||
/* The STRCPY instruction will always copy the NULL
|
||||
* 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);
|
||||
if (a == '\0') {
|
||||
break;
|
||||
}
|
||||
R[0]++;
|
||||
R[1]++;
|
||||
}
|
||||
|
|
|
@ -262,10 +262,11 @@ static void ctc_cmd(uint8 cid,
|
|||
cio_entry *cqe, uint8 *capp_data)
|
||||
{
|
||||
uint32 vtoc_addr, pdinfo_addr, ctjob_addr;
|
||||
uint32 maxpass, blkno, delay;
|
||||
uint8 dev;
|
||||
uint32 maxpass, blkno, delay, last_byte;
|
||||
uint8 dev, c;
|
||||
uint8 sec_buf[512];
|
||||
int32 b, i, j;
|
||||
int32 block_count, read_bytes, remainder, dest;
|
||||
t_seccnt secrw = 0;
|
||||
struct vtoc vtoc = {{0}};
|
||||
struct pdinfo pdinfo = {0};
|
||||
|
@ -417,6 +418,7 @@ static void ctc_cmd(uint8 cid,
|
|||
delay = DELAY_OPEN;
|
||||
|
||||
ctc_state[dev].time = 0; /* Opening always resets session time to 0 */
|
||||
ctc_state[dev].bytnum = 0;
|
||||
|
||||
vtoc_addr = rqe->address;
|
||||
pdinfo_addr = ATOW(rapp_data, 4);
|
||||
|
@ -522,6 +524,7 @@ static void ctc_cmd(uint8 cid,
|
|||
cqe->byte_count = rqe->byte_count;
|
||||
cqe->subdevice = rqe->subdevice;
|
||||
cqe->address = ATOW(rapp_data, 4);
|
||||
dest = rqe->address;
|
||||
|
||||
if (dev == XMF_DEV) {
|
||||
cqe->opcode = CTC_NOTREADY;
|
||||
|
@ -533,19 +536,70 @@ static void ctc_cmd(uint8 cid,
|
|||
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);
|
||||
|
||||
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;
|
||||
start_byte = ctc_state[dev].bytnum % 512;
|
||||
lba = blkno + b;
|
||||
result = sim_disk_rdsect(&ctc_unit, lba, sec_buf, &secrw, 1);
|
||||
if (result == SCPE_OK) {
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
"[ctc_cmd] ... CTC_READ: 512 bytes from block %d (0x%x)\n",
|
||||
lba, lba);
|
||||
for (j = 0; j < 512; j++) {
|
||||
/* If this is the last "extra" block, we will only
|
||||
* read the remainder of bytes from it. Otherwise, we
|
||||
* need to consume the whole block. */
|
||||
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 */
|
||||
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 {
|
||||
sim_debug(TRACE_DBG, &ctc_dev,
|
||||
|
|
|
@ -139,6 +139,7 @@ struct pdinfo {
|
|||
|
||||
typedef struct {
|
||||
uint32 time; /* Time used during a tape session (in 25ms chunks) */
|
||||
uint32 bytnum; /* Byte number, for streaming mode */
|
||||
} CTC_STATE;
|
||||
|
||||
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_MMU 7 /* Unimplemented MMU Feature */
|
||||
#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 */
|
||||
#define ABORT_EXC 1 /* CPU exception */
|
||||
|
|
38
3B2/3b2_ni.c
38
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_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 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,
|
||||
* we are expected to write results into memory at address
|
||||
* 0x200f000 */
|
||||
if (ni.crc == NI_DIAG_CRC1 ||
|
||||
ni.crc == NI_DIAG_CRC2 ||
|
||||
ni.crc == NI_DIAG_CRC3) {
|
||||
pwrite_h(0x200f000, 0x1); /* Test success */
|
||||
pwrite_h(0x200f002, 0x0); /* Test Number */
|
||||
pwrite_h(0x200f004, 0x0); /* Actual */
|
||||
pwrite_h(0x200f006, 0x0); /* Expected */
|
||||
pwrite_b(0x200f008, 0x1); /* Success flag again */
|
||||
for (i = 0; i < NI_DIAG_CRCS_LEN; i++) {
|
||||
if (ni.crc == NI_DIAG_CRCS[i]) {
|
||||
pwrite_h(0x200f000, 0x1); /* Test success */
|
||||
pwrite_h(0x200f002, 0x0); /* Test Number */
|
||||
pwrite_h(0x200f004, 0x0); /* Actual */
|
||||
pwrite_h(0x200f006, 0x0); /* Expected */
|
||||
pwrite_b(0x200f008, 0x1); /* Success flag again */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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,
|
||||
"[ni_cmd] Determine Sub-Devices.\n");
|
||||
|
||||
/* The system wants us to write sub-device structures
|
||||
* at the supplied address */
|
||||
/* The system wants us to write sub-device structures at the
|
||||
* supplied address */
|
||||
pwrite_h(rentry->address, 0x0);
|
||||
|
||||
if (is_exp) {
|
||||
|
@ -666,7 +682,7 @@ t_stat ni_autoconfig()
|
|||
{
|
||||
uint8 cid;
|
||||
|
||||
/* Clear the CIO table of PORTS cards */
|
||||
/* Clear the CIO table of NI cards */
|
||||
for (cid = 0; cid < CIO_SLOTS; cid++) {
|
||||
if (cio[cid].id == NI_ID) {
|
||||
cio[cid].id = 0;
|
||||
|
|
|
@ -88,9 +88,6 @@
|
|||
#define NI_QPOLL_FAST 100
|
||||
#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_CRC2 0xf6744bed
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ const char *sim_stop_messages[] = {
|
|||
"Exception Stack Too Deep",
|
||||
"Unimplemented MMU Feature",
|
||||
"System Powered Off",
|
||||
"Infinite Loop",
|
||||
"Simulator Error"
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue