diff --git a/IBM360/ibm360_cdp.c b/IBM360/ibm360_cdp.c index 6fbd801..414241d 100644 --- a/IBM360/ibm360_cdp.c +++ b/IBM360/ibm360_cdp.c @@ -210,7 +210,7 @@ cdp_srv(UNIT *uptr) { if (chan_read_byte(addr, &ch)) { uptr->CMD |= CDP_CARD; } else { - sim_debug(DEBUG_DATA, &cdp_dev, "%d: Char < %02o\n", u, ch); + sim_debug(DEBUG_DATA, &cdp_dev, "%d: Char < %02x\n", u, ch); image[uptr->COL++] = sim_ebcdic_to_hol(ch); if (uptr->COL == 80) { uptr->CMD |= CDP_CARD; @@ -230,15 +230,11 @@ cdp_srv(UNIT *uptr) { t_stat cdp_attach(UNIT * uptr, CONST char *file) { - t_stat r; - - if ((r = sim_card_attach(uptr, file)) != SCPE_OK) - return r; if (uptr->up7 == 0) { uptr->up7 = calloc(80, sizeof(uint16)); uptr->SNS = 0; } - return SCPE_OK; + return sim_card_attach(uptr, file); } t_stat diff --git a/IBM360/ibm360_cdr.c b/IBM360/ibm360_cdr.c index d678bf3..a31de82 100644 --- a/IBM360/ibm360_cdr.c +++ b/IBM360/ibm360_cdr.c @@ -199,6 +199,7 @@ cdr_srv(UNIT *uptr) { chan_write_byte(addr, &ch); chan_end(addr, SNS_CHNEND|SNS_DEVEND); uptr->CMD &= ~(CDR_CMDMSK); + uptr->SNS = 0; return SCPE_OK; } diff --git a/IBM360/ibm360_chan.c b/IBM360/ibm360_chan.c index 326151a..f1fcbf4 100644 --- a/IBM360/ibm360_chan.c +++ b/IBM360/ibm360_chan.c @@ -373,7 +373,7 @@ loop: chan_status[chan] &= 0xff; chan_status[chan] |= dibp->start_cmd(uptr, chan, ccw_cmd[chan]) << 8; if (chan_status[chan] & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) { - sim_debug(DEBUG_DETAIL, &cpu_dev, "Channel %03x abort %04x\n", + sim_debug(DEBUG_DETAIL, &cpu_dev, "Channel %03x abort %04x\n", chan, chan_status[chan]); chan_status[chan] |= STATUS_CEND; ccw_flags[chan] = 0; @@ -385,6 +385,8 @@ loop: chan_status[chan] |= STATUS_CEND; ccw_cmd[chan] = 0; irq_pend = 1; + if ((ccw_flags[chan] & (FLAG_CD|FLAG_CC)) == 0) + return 1; /* No chain? Give imm. SIO response. */ } } if (ccw_flags[chan] & FLAG_PCI) { @@ -732,7 +734,7 @@ startio(uint16 addr) { if (ccw_cmd[chan] != 0 || (ccw_flags[chan] & (FLAG_CD|FLAG_CC)) != 0 || chan_status[chan] != 0) { - sim_debug(DEBUG_CMD, &cpu_dev, "SIO %x %x %08x cc=2\n", addr, chan, + sim_debug(DEBUG_CMD, &cpu_dev, "SIO %x %x %08x cc=2\n", addr, chan, chan_status[chan]); return 2; } @@ -951,6 +953,8 @@ int haltio(uint16 addr) { M[0x44 >> 2] = (((uint32)chan_status[chan]) << 16) | (M[0x44 >> 2] & 0xffff); key[0] |= 0x6; + sim_debug(DEBUG_EXP, &cpu_dev, "Channel store csw %02x %08x\n", + chan, M[0x44 >> 2]); } return cc; } @@ -961,6 +965,7 @@ int haltio(uint16 addr) { M[0x44 >> 2] = (((uint32)chan_status[chan]) << 16) | (M[0x44 >> 2] & 0xffff); key[0] |= 0x6; + sim_debug(DEBUG_EXP, &cpu_dev, "Channel store csw %02x %08x\n", chan, M[0x44 >> 2]); return 1; } @@ -969,8 +974,37 @@ int haltio(uint16 addr) { */ int testchan(uint16 channel) { uint16 st = 0; - channel >>= 8; - if (channel == 0 || channel == 4) + uint8 cc = 0; + + /* 360 Principles of Operation says, "Bit positions 21-23 of the + sum formed by the addition of the content of register B1 and the + content of the D1 field identify the channel to which the + instruction applies. Bit positions 24-31 of the address are ignored.” + /67 Functional Characteristics do not mention any changes in basic or + extended control mode of the TCH instruction behaviour. + However, historic /67 code for MTS suggests that bits 19-20 of the + address indicate the channel controller which should be used to query + the channel. + + Original testchan code did not recognize the channel controller (CC) part + of the address and treats the query as referring to a channel # like so: + CC = 0 channel# 0 1 2 3 4 5 6 + CC = 1 " 8 9 10 11 12 13 14 + CC = 2 " 16 17 18 19 20 21 22 + CC = 3 " 24 25 26 27 28 29 30 + which may interfere with subchannel mapping. + + For the nonce, TCH only indicates that channels connected to CC 0 & 1 are + attached. Channels 0, 4, 8 (0 on CC 1) & 12 (4 on CC 1) are multiplexer + channels. */ + + cc = (channel >> 11) & 0x03; + channel = (channel >> 8) & 0x0f; + if (cc > 1 || channel > channels) { + sim_debug(DEBUG_CMD, &cpu_dev, "TCH CC %x %x cc=3\n", cc, channel); + return 3; + } + if (channel == 0 || channel == 4 || channel == 8 || channel == 12) return 0; if (channel > channels) return 3; diff --git a/IBM360/ibm360_com.c b/IBM360/ibm360_com.c index e6ce36e..ed0220e 100644 --- a/IBM360/ibm360_com.c +++ b/IBM360/ibm360_com.c @@ -272,7 +272,7 @@ uint8 coml_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { switch (cmd & 0x3) { case 0x3: /* Control */ - if (cmd == CMD_NOP) + if ((cmd == CMD_NOP) || (cmd & 0x8) != 0) return SNS_CHNEND|SNS_DEVEND; case 0x2: /* Read command */ case 0x1: /* Write command */ diff --git a/IBM360/ibm360_con.c b/IBM360/ibm360_con.c index a06cce6..c95ac3d 100644 --- a/IBM360/ibm360_con.c +++ b/IBM360/ibm360_con.c @@ -245,6 +245,7 @@ con_srv(UNIT *uptr) { chan_write_byte(addr, &ch); chan_end(addr, SNS_CHNEND|SNS_DEVEND); uptr->CMD &= ~(CON_MSK); + uptr->SNS = 0; break; case CON_WR: diff --git a/IBM360/ibm360_cpu.c b/IBM360/ibm360_cpu.c index c46e91e..38e9134 100644 --- a/IBM360/ibm360_cpu.c +++ b/IBM360/ibm360_cpu.c @@ -26,7 +26,7 @@ The IBM 360 supported 32 bit memory and 16 32 bit registers. Optionally it could have 4 64 bit floating point registers. Optionally the machine could also process packed decimal numbers directly. There was also a - 64 bit processor status word. Up to 16MB of memory could be supported, + 64 bit processor status word. Up to 16MB of memory could be supported, however 1MB or less was much more common. 8MB being the practical max. Instructions ranged from 2 bytes to up to 6 bytes. In the following formats: @@ -363,7 +363,8 @@ MTAB cpu_mod[] = { { FEAT_DAT, FEAT_DAT, "DAT", "DAT", NULL, NULL, NULL, "DAT /67"}, { FEAT_DAT, 0, NULL, "NODAT", NULL, NULL}, { EXT_IRQ, 0, "NOEXT", NULL, NULL, NULL}, - { EXT_IRQ, EXT_IRQ, "EXT", "EXT", NULL, NULL, NULL, "External Irq"}, + { EXT_IRQ, EXT_IRQ, "EXT", "EXT", NULL, NULL, NULL, + "SET CPU EXT causes external interrupt"}, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } @@ -474,7 +475,7 @@ void storepsw(uint32 addr, uint16 ircode) { hst[hst_p].src2 = word2; hst[hst_p].addr1 = ircode; } - sim_debug(DEBUG_INST, &cpu_dev, "store %02x %d %x %03x PSW=%08x %08x\n\r", addr, ilc, + sim_debug(DEBUG_INST, &cpu_dev, "store %02x %d %x %03x PSW=%08x %08x\n", addr, ilc, cc, ircode, word, word2); irqcode = ircode; } @@ -1606,28 +1607,28 @@ opr: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = startio(addr1 & 0xfff); + cc = startio(addr1 & 0x1fff); break; case OP_TIO: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = testio(addr1 & 0xfff); + cc = testio(addr1 & 0x1fff); break; case OP_HIO: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = haltio(addr1 & 0xfff); + cc = haltio(addr1 & 0x1fff); break; case OP_TCH: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = testchan(addr1 & 0xfff); + cc = testchan(addr1 & 0x1fff); break; case OP_DIAG: @@ -1737,6 +1738,15 @@ set_cc3: } src1 = regs[reg1|1]; case OP_MH: +#ifdef USE_64BIT + src1L = (((t_int64) ((t_uint64)src1 << 32)) >> 32) * (int32)src2; + if (op != OP_MH) { + STDBL(reg1, src1L); + } else { + regs[reg1] = (uint32)(src1L & FMASK); + per_mod |= 1 << reg1; + } +#else fill = 0; if (src1 & MSIGN) { @@ -1747,17 +1757,6 @@ set_cc3: fill ^= 1; src2 = NEG(src2); } -#ifdef USE_64BIT - src1L = ((t_uint64)src1) * ((t_uint64)src2); - if (fill) - src1L = NEG(src1L); - if (op != OP_MH) { - STDBL(reg1, src1L); - } else { - regs[reg1] = (uint32)(src1L & FMASK); - per_mod |= 1 << reg1; - } -#else src1h = 0; if (src1 != 0 || src2 != 0) { for (reg = 32; reg > 0; reg--) { @@ -2267,7 +2266,7 @@ save_dbl: goto supress; cregs[reg1] = dest; sim_debug(DEBUG_INST, &cpu_dev, - "Loading: CR %x %06x %08x IC=%08x %x\n\r", + "Loading: CR %x %06x %08x IC=%08x %x\n", reg1, addr1, dest, PC, reg); switch (reg1) { case 0x0: /* Segment table address */ @@ -3275,7 +3274,7 @@ save_dbl: goto supress; cregs[reg1] = dest; sim_debug(DEBUG_INST, &cpu_dev, - "Loading: CR %x %06x %08x IC=%08x %x\n\r", + "Loading: CR %x %06x %08x IC=%08x %x\n", reg1, addr1, dest, PC, reg); switch (reg1) { case 0x0: /* General control register */ @@ -3324,6 +3323,10 @@ save_dbl: ~page_mask) & AMASK) >> page_shift; intval_en = ((dest & 0x400) != 0); tod_en = ((dest & 0x800) != 0); + for (temp = 0; + temp < sizeof(tlb)/sizeof(uint32); + temp++) + tlb[temp] = 0; break; case 0x1: /* Segment table address and length */ seg_addr = dest & AMASK; @@ -5901,73 +5904,181 @@ check_tod_irq() t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { -uint32 addr = (uint32) exta; -uint32 byte; -uint32 offset = 8 * (3 - (addr & 0x3)); + uint32 addr = (uint32) exta; + uint32 byte; + uint32 offset = 8 * (3 - (addr & 0x3)); -if (vptr == NULL) - return SCPE_ARG; -/* Ignore high order bits */ -addr &= AMASK; -if (addr >= MEMSIZE) - return SCPE_NXM; -addr >>= 2; -byte = M[addr] >> offset; -byte &= 0xff; -*vptr = byte; -return SCPE_OK; + if (vptr == NULL) + return SCPE_ARG; + if (sw & SWMASK ('V')) { + /* Virtual address, simulate LRA */ + uint32 seg; + uint32 page; + uint32 entry; + uint32 addr1, addr2; + + if ((cpu_unit[0].flags & FEAT_DAT) == 0) + return SCPE_NXM; + + /* Segment number to word address */ + addr1 = addr & AMASK; + seg = (addr1 >> seg_shift) & seg_mask; + page = (addr1 >> page_shift) & page_index; + if (seg > seg_len) + return SCPE_NXM; + addr2 = (((seg << 2) + seg_addr) & AMASK); + if (addr2 >= MEMSIZE) + return SCPE_NXM; + + /* Check if entry valid */ + entry = M[addr2 >> 2]; + if (entry & PTE_VALID) + return SCPE_NXM; + + if ((cpu_unit[0].flags & FEAT_370) != 0) { + addr2 = (entry >> 28) + 1; + /* Check if over end of table */ + if ((page >> pte_len_shift) > addr2) + return SCPE_NXM; + } else { + addr2 = (entry >> 24); + if (page > addr2) + return SCPE_NXM; + } + + /* Now we need to fetch the actual entry */ + addr2 = (entry & PTE_ADR) + (page << 1); + addr2 &= AMASK; + if (addr2 >= MEMSIZE) + return SCPE_NXM; + + entry = M[addr2 >> 2]; + entry >>= (addr2 & 2) ? 0 : 16; + entry &= 0xffff; + + /* Check if entry valid */ + if (entry & pte_avail) + return SCPE_NXM; + + addr = (addr1 & page_mask) | ((entry & 0xfff8) << 8); + } + + /* Real address, just return it, but ignore high order bits */ + addr &= AMASK; + if (addr >= MEMSIZE) + return SCPE_NXM; + + addr >>= 2; + byte = M[addr] >> offset; + byte &= 0xff; + *vptr = byte; + return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { -uint32 addr = (uint32) exta; -uint32 offset = 8 * (3 - (addr & 0x3)); -uint32 word; -uint32 mask; + uint32 addr = (uint32) exta; + uint32 offset = 8 * (3 - (addr & 0x3)); + uint32 word; + uint32 mask; -/* Ignore high order bits */ -addr &= AMASK; -if (addr >= MEMSIZE) - return SCPE_NXM; -addr >>= 2; -mask = 0xff << offset; -word = M[addr]; -word &= ~mask; -word |= (val & 0xff) << offset; -M[addr] = word; -return SCPE_OK; + if (sw & SWMASK ('V')) { + /* Virtual address, simulate LRA */ + uint32 seg; + uint32 page; + uint32 entry; + uint32 addr1, addr2; + + if ((cpu_unit[0].flags & FEAT_DAT) == 0) + return SCPE_NXM; + + /* Segment number to word address */ + addr1 = addr & AMASK; + seg = (addr1 >> seg_shift) & seg_mask; + page = (addr1 >> page_shift) & page_index; + if (seg > seg_len) + return SCPE_NXM; + addr2 = (((seg << 2) + seg_addr) & AMASK); + if (addr2 >= MEMSIZE) + return SCPE_NXM; + + /* Check if entry valid */ + entry = M[addr2 >> 2]; + if (entry & PTE_VALID) + return SCPE_NXM; + + if ((cpu_unit[0].flags & FEAT_370) != 0) { + addr2 = (entry >> 28) + 1; + /* Check if over end of table */ + if ((page >> pte_len_shift) > addr2) + return SCPE_NXM; + } else { + addr2 = (entry >> 24); + if (page > addr2) + return SCPE_NXM; + } + + /* Now we need to fetch the actual entry */ + addr2 = (entry & PTE_ADR) + (page << 1); + addr2 &= AMASK; + if (addr2 >= MEMSIZE) + return SCPE_NXM; + + entry = M[addr2 >> 2]; + entry >>= (addr2 & 2) ? 0 : 16; + entry &= 0xffff; + + /* Check if entry valid */ + if (entry & pte_avail) + return SCPE_NXM; + + addr = (addr1 & page_mask) | ((entry & 0xfff8) << 8); + } + + /* Ignore high order bits */ + addr &= AMASK; + if (addr >= MEMSIZE) + return SCPE_NXM; + + addr >>= 2; + mask = 0xff << offset; + word = M[addr]; + word &= ~mask; + word |= (val & 0xff) << offset; + M[addr] = word; + return SCPE_OK; } /* Memory allocation */ t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { -int32 mc = 0; -int32 i, clim; -uint32 *nM = NULL; -int32 max = MEMSIZE >> 2; + int32 mc = 0; + int32 i, clim; + uint32 *nM = NULL; + int32 max = MEMSIZE >> 2; -val = 16 * 1024 * val; -if ((val <= 0) || (val > MAXMEMSIZE)) - return SCPE_ARG; -for (i = val>>2; i < max; i++) - mc = mc | M[i]; -if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) - return SCPE_OK; -nM = (uint32 *) calloc (val >> 2, sizeof (uint32)); -if (nM == NULL) - return SCPE_MEM; -clim = ((t_addr)val < MEMSIZE)? val >> 2: max; -for (i = 0; i < clim; i++) - nM[i] = M[i]; -free (M); -M = nM; -fprintf(stderr, "Mem size=%x\n\r", val); -MEMSIZE = val; -reset_all (0); -return SCPE_OK; + val = 16 * 1024 * val; + if ((val <= 0) || (val > MAXMEMSIZE)) + return SCPE_ARG; + for (i = val>>2; i < max; i++) + mc = mc | M[i]; + if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; + nM = (uint32 *) calloc (val >> 2, sizeof (uint32)); + if (nM == NULL) + return SCPE_MEM; + clim = ((t_addr)val < MEMSIZE)? val >> 2: max; + for (i = 0; i < clim; i++) + nM[i] = M[i]; + free (M); + M = nM; + fprintf(stderr, "Mem size=%x\n\r", val); + MEMSIZE = val; + reset_all (0); + return SCPE_OK; } /* Handle execute history */ diff --git a/IBM360/ibm360_defs.h b/IBM360/ibm360_defs.h index 0ea763d..54b0359 100644 --- a/IBM360/ibm360_defs.h +++ b/IBM360/ibm360_defs.h @@ -47,7 +47,7 @@ 384 - +6 0x1xx - 0x6xx Channels 1,2,3,5,6 are selector channels. - Devices on channel 0 below number of subchannels have there own + Devices on channel 0 below number of subchannels have their own virtual channel. Devices on channel 0 above the number of subchannels are mapped in groups of 16 into channels 0 to n. diff --git a/IBM360/ibm360_lpr.c b/IBM360/ibm360_lpr.c index 4b73d08..9fc7bf3 100644 --- a/IBM360/ibm360_lpr.c +++ b/IBM360/ibm360_lpr.c @@ -197,7 +197,7 @@ print_line(UNIT * uptr) /* Print out buffer */ sim_fwrite(&out, 1, i, uptr->fileref); uptr->pos += i; - sim_debug(DEBUG_DETAIL, &lpr_dev, "%s", out); + sim_debug(DEBUG_DETAIL, &lpr_dev, "%s\n", out); } if (l < 4) { @@ -343,9 +343,10 @@ lpr_srv(UNIT *uptr) { int u = (uptr - lpr_unit); int cmd = (uptr->CMD & 0x7); int l = (uptr->CMD >> 3) & 0x1f; + uint8 ch; if (cmd == 4) { - uint8 ch = uptr->SNS; + ch = uptr->SNS; uptr->CMD &= ~(LPR_CMDMSK); chan_write_byte(addr, &ch); chan_end(addr, SNS_DEVEND|SNS_CHNEND); @@ -355,6 +356,7 @@ lpr_srv(UNIT *uptr) { if (cmd == 7) { uptr->CMD &= ~(LPR_FULL|LPR_CMDMSK); uptr->POS = 0; + chan_read_byte(addr, &ch); chan_end(addr, SNS_DEVEND|SNS_CHNEND); return SCPE_OK; } @@ -366,19 +368,19 @@ lpr_srv(UNIT *uptr) { else uptr->CMD |= LPR_DATCHK; uptr->CMD &= ~(LPR_CMDMSK); + chan_read_byte(addr, &ch); chan_end(addr, SNS_DEVEND|SNS_CHNEND); return SCPE_OK; } /* Handle UCS Load */ if ((uptr->CMD & 0xf7) == 0xf3) { - uint8 ch; for (l = 0; l < 240; l++) { if(chan_read_byte(addr, &ch)) break; } - chan_end(addr, SNS_DEVEND|SNS_CHNEND); uptr->CMD &= ~(LPR_CMDMSK); + chan_end(addr, SNS_DEVEND|SNS_CHNEND); return SCPE_OK; } diff --git a/IBM360/ibm360_sys.c b/IBM360/ibm360_sys.c index 730b108..81da83e 100644 --- a/IBM360/ibm360_sys.c +++ b/IBM360/ibm360_sys.c @@ -114,7 +114,7 @@ DEBTAB crd_debug[] = { const char *sim_stop_messages[] = { "Unknown error", - "HALT instruction", + "Uninterruptable wait", "Breakpoint" };