diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index e7be4c13..79fe5e7e 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -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]++; } diff --git a/3B2/3b2_ctc.c b/3B2/3b2_ctc.c index 3c77d5b4..912c0583 100644 --- a/3B2/3b2_ctc.c +++ b/3B2/3b2_ctc.c @@ -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, diff --git a/3B2/3b2_ctc.h b/3B2/3b2_ctc.h index 08773fe4..62554e89 100644 --- a/3B2/3b2_ctc.h +++ b/3B2/3b2_ctc.h @@ -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; diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index 4bf9dd08..d2ac6706 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -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 */ diff --git a/3B2/3b2_ni.c b/3B2/3b2_ni.c index 88477230..505698be 100644 --- a/3B2/3b2_ni.c +++ b/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; diff --git a/3B2/3b2_ni.h b/3B2/3b2_ni.h index f6cd604b..9011fff4 100644 --- a/3B2/3b2_ni.h +++ b/3B2/3b2_ni.h @@ -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 diff --git a/3B2/3b2_sys.c b/3B2/3b2_sys.c index 79eeb0dc..85260f0d 100644 --- a/3B2/3b2_sys.c +++ b/3B2/3b2_sys.c @@ -82,6 +82,7 @@ const char *sim_stop_messages[] = { "Exception Stack Too Deep", "Unimplemented MMU Feature", "System Powered Off", + "Infinite Loop", "Simulator Error" };