diff --git a/IBM360/ibm360_chan.c b/IBM360/ibm360_chan.c index cd9cb69..4dc481f 100644 --- a/IBM360/ibm360_chan.c +++ b/IBM360/ibm360_chan.c @@ -79,18 +79,21 @@ extern uint32 *M; extern uint8 key[MAXMEMSIZE / 2048]; #define MAX_DEV (MAX_CHAN * 256) + +#define SEL_BASE (SUB_CHANS * MAX_MUX) +#define CHAN_SZ (SEL_BASE + MAX_CHAN) int channels = MAX_CHAN; int subchannels = SUB_CHANS; /* Number of subchannels */ int irq_pend = 0; -uint32 caw[256]; /* Channel command address word */ -uint32 ccw_addr[256]; /* Channel address */ -uint16 ccw_count[256]; /* Channel count */ -uint8 ccw_cmd[256]; /* Channel command and flags */ -uint16 ccw_flags[256]; /* Channel flags */ -uint16 chan_status[256]; /* Channel status */ -uint16 chan_dev[256]; /* Device on channel */ -uint32 chan_buf[256]; /* Channel data buffer */ -uint8 chan_byte[256]; /* Current byte, dirty/full */ +uint32 caw[CHAN_SZ]; /* Channel command address word */ +uint32 ccw_addr[CHAN_SZ]; /* Channel address */ +uint16 ccw_count[CHAN_SZ]; /* Channel count */ +uint8 ccw_cmd[CHAN_SZ]; /* Channel command and flags */ +uint16 ccw_flags[CHAN_SZ]; /* Channel flags */ +uint16 chan_status[CHAN_SZ]; /* Channel status */ +uint16 chan_dev[CHAN_SZ]; /* Device on channel */ +uint32 chan_buf[CHAN_SZ]; /* Channel data buffer */ +uint8 chan_byte[CHAN_SZ]; /* Current byte, dirty/full */ DIB *dev_unit[MAX_DEV]; /* Pointer to Device info block */ uint8 dev_status[MAX_DEV]; /* last device status flags */ @@ -128,17 +131,40 @@ find_chan_dev(uint16 addr) { int find_subchan(uint16 device) { int chan; + int base = 0; if (device > MAX_DEV) return -1; - if (device > 0xff) { - chan = (device >> 8) & 0x7; - if (chan > channels) - return -1; - return subchannels + chan; + chan = (device >> 8) & 0xf; + device &= 0xff; + if (chan > channels) + return -1; + switch(chan) { + case 4: + base += subchannels; + case 0: /* Multiplexer channel */ + if (device >= subchannels) + device = ((device - subchannels) >> 4) & 0xf; + return base + device; + case 1: /* Selector channel */ + case 2: + case 3: + case 5: + case 6: + case 7: + return SEL_BASE + chan; } - if (device < subchannels) - return device; - return ((device - subchannels)>>4) & 0xf; + return -1; +} + +/* find a channel for a given index */ +int +find_chan(int index) { + int chan = 0; + if (index > SEL_BASE) + return index - SEL_BASE; + if (index > subchannels) + return 4; + return 0; } /* Read a full word into memory. @@ -692,11 +718,15 @@ int testio(uint16 addr) { uint16 status; /* Find channel this device is on, if no none return cc=3 */ - if (chan < 0 || dibp == 0) + if (chan < 0 || dibp == 0) { + sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x cc=3\n", addr, chan); return 3; + } uptr = find_chan_dev(addr); - if (uptr == 0) + if (uptr == 0) { + sim_debug(DEBUG_CMD, &cpu_dev, "TIO %x %x uptr cc=3\n", addr, chan); return 3; + } /* If any error pending save csw and return cc=1 */ if (chan_status[chan] & (STATUS_PCI|STATUS_ATTN|STATUS_CHECK|\ @@ -804,7 +834,7 @@ int testchan(uint16 channel) { return 0; if (channel > channels) return 3; - st = chan_status[subchannels + channel]; + st = chan_status[SEL_BASE + channel]; if (st & STATUS_BUSY) return 2; if (st & (STATUS_ATTN|STATUS_PCI|STATUS_EXPT|STATUS_CHECK| @@ -870,14 +900,11 @@ scan_chan(uint16 mask) { return 0; irq_pend = 0; /* Start with channel 0 and work through all channels */ - for (i = 0; i < subchannels + channels; i++) { - /* If onto channel 1 or above shift mask */ - if (i >= subchannels) - imask = imask / 2; - + for (i = 0; i < CHAN_SZ; i++) { /* Check if PCI pending */ if (chan_status[i] & STATUS_PCI) { - sim_debug(DEBUG_EXP, &cpu_dev, "Scan PCI(%x %x %x %x) end\n", i, + imask = 0x8000 >> find_chan(i); + sim_debug(DEBUG_EXP, &cpu_dev, "Scan PCI(%x %x %x %x) end\n", i, chan_status[i], imask, mask); if ((imask & mask) != 0) { pend = chan_dev[i]; @@ -893,9 +920,11 @@ scan_chan(uint16 mask) { else irq_pend = 1; } else { + imask = 0x8000 >> find_chan(i); sim_debug(DEBUG_EXP, &cpu_dev, "Scan(%x %x %x %x) end\n", i, chan_status[i], imask, mask); - if ((imask & mask) != 0 || loading != 0) { + if ((chan_status[i] & STATUS_DEND) != 0 && + (imask & mask) != 0 || loading != 0) { pend = chan_dev[i]; break; } @@ -1048,7 +1077,7 @@ set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc) uptr->u3 |= UNIT_ADDR(devaddr); fprintf(stderr, "Set dev %s %x\n\r", dptr->name, GET_UADDR(uptr->u3)); } else { - fprintf(stderr, "Set dev %s0 %x\n\r", dptr->name, GET_UADDR(uptr->u3)); + fprintf(stderr, "Set dev %s0 %x\n\r", dptr->name, GET_UADDR(uptr->u3)); for (i = 0; i < dibp->numunits; i++) { dev_unit[devaddr + i] = dibp; uptr = &((dibp->units)[i]); diff --git a/IBM360/ibm360_cpu.c b/IBM360/ibm360_cpu.c index 9237638..a5d5597 100644 --- a/IBM360/ibm360_cpu.c +++ b/IBM360/ibm360_cpu.c @@ -21,22 +21,9 @@ */ -#include "ibm360_defs.h" /* simulator defns */ - -#define FEAT_PROT (1 << (UNIT_V_UF + 8)) /* Storage protection feature */ -#define FEAT_DEC (1 << (UNIT_V_UF + 9)) /* Decimal instruction set */ -#define FEAT_FLOAT (1 << (UNIT_V_UF + 10)) /* Floating point instruction set */ -#define FEAT_UNIV (3 << (UNIT_V_UF + 9)) /* All instructions */ -#define FEAT_STOR (1 << (UNIT_V_UF + 11)) /* No alignment restrictions */ -#define FEAT_TIMER (1 << (UNIT_V_UF + 12)) /* Interval timer */ -#define FEAT_DAT (1 << (UNIT_V_UF + 13)) /* Dynamic address translation */ -#define FEAT_EFP (1 << (UNIT_V_UF + 14)) /* Extended floating point */ -#define EXT_IRQ (1 << (UNIT_V_UF_31)) /* External interrupt */ - -#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ -#define UNIT_MSIZE (0xff << UNIT_V_MSIZE) -#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#include "ibm360_defs.h" /* simulator defns */ +#define MEMAMOUNT(x) (x) #define TMR_RTC 0 #define HIST_MAX 5000000 @@ -66,6 +53,20 @@ uint16 loading; /* Doing IPL */ uint8 interval_irq = 0; /* Interval timer IRQ */ uint8 dat_en = 0; /* Translate addresses */ uint32 tlb[256]; /* Translation look aside buffer */ +int page_shift; /* Amount to shift for page */ +int page_index; /* Mask of page index feild */ +int page_mask; /* Mask of bits in page address */ +int seg_shift; /* Amount to shift for segment */ +int seg_mask; /* Mask bits for segment */ +int seg_len; /* Length of segment table */ +int seg_addr; /* Address of segment table */ +int pte_avail; /* Mask of available bit in PTE */ +int pte_mbz; /* Bits that must be zero in PTE */ +int pte_shift; /* Bits to shift a PTE entry */ +int per_en; /* Enable PER tracing */ +uint32 per_mod; /* Module modification mask */ +int per_code; /* Code for PER */ +uint32 per_addr; /* Address of last reference */ #define DAT_ENABLE 0x01 /* DAT enabled */ @@ -118,6 +119,10 @@ uint32 tlb[256]; /* Translation look aside buffer */ #define IRC_FPDIV 0x000f /* Floating pointer divide */ #define IRC_SEG 0x0010 /* Segment translation */ #define IRC_PAGE 0x0011 /* Page translation */ +#define IRC_TRANS 0x0012 /* Translation special */ +#define IRC_SPOP 0x0013 /* Special operation */ +#define IRC_MCE 0x0040 /* Monitor event */ +#define IRC_PER 0x0080 /* Per event */ #define AMASK 0x00ffffff /* Mask address bits */ #define MSIGN 0x80000000 /* Minus sign */ @@ -142,11 +147,13 @@ uint32 tlb[256]; /* Translation look aside buffer */ #define LDDBL(r,x) x = (((t_uint64)regs[r]) << 32) | ((t_uint64)regs[r|1]); \ if (hst_lnt) { hst[hst_p].src1 = ((x) >> 32); \ hst[hst_p].src2 = (x) & FMASK; } -#define STDBL(r,x) regs[r|1] = (uint32)((x) & FMASK); regs[r] = (uint32)(((x) >> 32) & FMASK) +#define STDBL(r,x) regs[r|1] = (uint32)((x) & FMASK); \ + regs[r] = (uint32)(((x) >> 32) & FMASK); \ + per_mod |= 3 << r; #else #define LDDBLx(r,x) x = regs[r]; x##h = regs[r|1]; \ if (hst_lnt) { hst[hst_p].src1 = x; hst[hst_p].src2 = x##h ; } -#define STDBLx(r,x) regs[r|1] = x##h; regs[r] = x; +#define STDBLx(r,x) regs[r|1] = x##h; regs[r] = x; per_mod |= 3 << r; #endif #define R1(x) (((x)>>4) & 0xf) @@ -159,10 +166,6 @@ uint32 tlb[256]; /* Translation look aside buffer */ #define PTE_ADR 0x00fffffe /* Address of table */ #define PTE_VALID 0x00000001 /* table valid */ -#define PTE_PHY 0xfff0 /* Physical page address */ -#define PTE_AVAL 0x0008 /* Page tranlation error */ -#define PTE_MBZ 0x0007 /* Bits must be zero */ - #define TLB_SEG 0x7ffff000 /* Segment address */ #define TLB_VALID 0x80000000 /* Entry valid */ #define TLB_PHY 0x00000fff /* Physical page */ @@ -216,6 +219,7 @@ t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); const char *cpu_description (DEVICE *dptr); +void dec_srp(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2); void dec_add(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2); void dec_mul(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2); void dec_div(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2); @@ -236,7 +240,7 @@ int32 rtc_tps = 300; cpu_mod CPU modifier list */ -UNIT cpu_unit = { UDATA (&rtc_srv, UNIT_BINK, MAXMEMSIZE) }; +UNIT cpu_unit = { UDATA (&rtc_srv, UNIT_IDLE|UNIT_BINK|UNIT_FIX, MAXMEMSIZE) }; REG cpu_reg[] = { { HRDATA (PC, PC, 24) }, @@ -272,14 +276,17 @@ REG cpu_reg[] = { MTAB cpu_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, - { UNIT_MSIZE, MEMAMOUNT(1), "16K", "16K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(2), "32K", "32K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(4), "64K", "64K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(8), "128K", "128K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(12), "196K", "196K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(16), "256K", "256K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(32), "512K", "512K", &cpu_set_size }, - { UNIT_MSIZE, MEMAMOUNT(128), "2M", "2M", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(1), NULL, "16K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(2), NULL, "32K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(4), NULL, "64K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(8), NULL, "128K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(12), NULL, "196K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(16), NULL, "256K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(32), NULL, "512K", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(64), NULL, "1M", &cpu_set_size }, + { MTAB_VDV, MEMAMOUNT(128), NULL, "2M", &cpu_set_size }, + { FEAT_370, FEAT_370, "IBM370", "IBM370", NULL, NULL, NULL, "Sets CPU to be a IBM370"}, + { FEAT_370, 0, "IBM360", "IBM360", NULL, NULL, NULL, "Sets CPU to be a IBM360"}, { FEAT_PROT, 0, NULL, "NOPROT", NULL, NULL, NULL, "No Storage protection"}, { FEAT_PROT, FEAT_PROT, "PROT", "PROT", NULL, NULL, NULL, "Storage protection"}, { FEAT_UNIV, FEAT_UNIV, "UNIV", "UNIV", NULL, NULL, NULL, "Universal instruction"}, @@ -342,16 +349,63 @@ void storepsw(uint32 addr, uint16 ircode) { uint32 word2; irqaddr = addr + 0x40; + if (addr == OPPSW && per_en && per_code) + ircode |= IRC_PER; if (ec_mode) { /* Generate first word */ - word = (((uint32)dat_en) << 26) | - ((irq_en) ? 1<<25:0) | - ((ext_en) ? 1<<24:0) | - (((uint32)st_key) << 16) | - (((uint32)flags) << 16) | - (((uint32)ilc) << 14) | - (((uint32)cc) << 12) | - (((uint32)pmsk) << 8); + if ((cpu_unit.flags & FEAT_370) == 0) { /* IBM 360/67 in EC mode */ + word = (((uint32)dat_en) << 26) | + ((irq_en) ? 1<<25:0) | + ((ext_en) ? 1<<24:0) | + (((uint32)st_key) << 16) | + (((uint32)flags) << 16) | + (((uint32)ilc) << 14) | + (((uint32)cc) << 12) | + (((uint32)pmsk) << 8); + switch(addr) { + case OEPSW: + M[0xc >> 2] = (M[0xc >> 2] & 0xffff0000) | ircode; + break; + case OSPSW: + M[0x10 >> 2] = (M[0x10 >> 2] & 0x0000ffff) | ((uint32)ircode << 16); + break; + case OPPSW: + M[0x10 >> 2] = (M[0x10 >> 2] & 0xffff0000) | ircode; + break; + case OIOPSW: + M[0x14 >> 2] = (M[0x14 >> 2] & 0xffff0000) | ircode; + break; + } + } else { /* IBM 370 under EC mode */ + uint32 code_addr; + word = (((uint32)dat_en) << 26) | + ((per_en) ? 1<<31:0) | + ((irq_en) ? 1<<25:0) | + ((ext_en) ? 1<<24:0) | + 0x80000 | + (((uint32)st_key) << 16) | + (((uint32)flags) << 16) | + (((uint32)cc) << 12) | + (((uint32)pmsk) << 8); + switch(addr) { + case OEPSW: + M[0x84 >> 2] = (M[0x84 >> 2] & 0xffff0000) | ircode; + break; + case OSPSW: + M[0x88 >> 2] = (ilc << 17) | ircode; + break; + case OPPSW: + M[0x8C >> 2] = (ilc << 17) | ircode; + break; + case OIOPSW: + M[0xB8 >> 2] = ircode; + break; + } + if (per_en && per_code) { + M[150 >> 2] = (M[150 >> 2] & 0xFFFF0000) | per_code; + M[152 >> 2] = per_addr; + } + } /* Generate second word. */ word2 = PC; } else { @@ -384,6 +438,28 @@ void storepsw(uint32 addr, uint16 ircode) { irqcode = ircode; } +/* + * For 370 + * PS = 2K page_shift = 11 pte_avail = 0x4 pte_mbz = 0x2 pte_shift = 3 + * PS = 4K page_shift = 12 pte_avail = 0x8 pte_mbz = 0x6 pte_shift = 4 + * + * For 360/67 + * page_shift = 12 pte_avail = 0x8 pte_mbz = 0x6 pte_shift = 4 + * + * For 370 + * SS = 64K page_mask = 0x1F PS=4K page_mask = 0xF + * seg_shift = 16 + * seg_mask = 0xff + * SS = 1M page_mask = 0xff PS=4k page_mask = 07F + * seg_shift = 20 + * seg_mask = 0xF + * For 360/67 + * page_shift = 12 + * page_mask = 0xff + * seg_shift = 20 + * seg_mask = 0xfff + */ + /* * Translate an address from virtual to physical. */ @@ -405,12 +481,12 @@ int TransAddr(uint32 va, uint32 *pa) { return 0; } - seg = va & SEG_MASK; - page = (seg >> 12) & 0xff; + page = (va >> page_shift); + seg = (page & 0xff00) << 4; /* Quick check if TLB correct */ - entry = tlb[page]; + entry = tlb[page & 0xff]; if ((entry & TLB_VALID) != 0 && ((entry ^ seg) & TLB_SEG) == 0) { - *pa = (va & 0xfff) | ((entry & TLB_PHY) << 12); + *pa = (va & page_mask) | ((entry & TLB_PHY) << page_shift); if (va >= MEMSIZE) { storepsw(OPPSW, IRC_ADDR); return 1; @@ -418,24 +494,31 @@ int TransAddr(uint32 va, uint32 *pa) { return 0; } /* TLB not correct, try loading correct entry */ - seg >>= 8; /* Segment number to word address */ - if ((seg >> 4) != 0) { - cregs[2] = va; + seg = (va >> seg_shift) & seg_mask; /* Segment number to word address */ + page = (va >> page_shift) & page_index; + /* Check address against lenght of segment table */ + if (seg > seg_len) { + if ((cpu_unit.flags & FEAT_370) == 0) + cregs[2] = va; + else + M[0x90 >> 2] = va; storepsw(OPPSW, IRC_SEG); /* Not valid address */ return 1; } - addr = ((seg & 0xFFF) << 2) + (cregs[0] & AMASK); - /* Ignore high order bits */ - addr &= AMASK; + addr = (((seg << 2) + seg_addr) & AMASK); if (addr >= MEMSIZE) { storepsw(OPPSW, IRC_ADDR); return 1; } + /* Get pointer to page table */ entry = M[addr >> 2]; /* Check if entry valid and in correct length */ if (entry & PTE_VALID || page > (entry >> 24)) { - cregs[2] = va; + if ((cpu_unit.flags & FEAT_370) == 0) + cregs[2] = va; + else + M[0x90 >> 2] = va; storepsw(OPPSW, IRC_PAGE); return 1; } @@ -451,24 +534,32 @@ int TransAddr(uint32 va, uint32 *pa) { entry >>= (addr & 2) ? 0 : 16; entry &= 0xffff; - if ((entry & (PTE_MBZ)) != 0) { - cregs[2] = va; + if ((entry & pte_mbz) != 0) { + if ((cpu_unit.flags & FEAT_370) == 0) + cregs[2] = va; + else + M[0x90 >> 2] = va; storepsw(OPPSW, IRC_SPEC); return 1; } /* Check if entry valid and in correct length */ - if (entry & PTE_AVAL) { - cregs[2] = va; + if (entry & pte_avail) { + if ((cpu_unit.flags & FEAT_370) == 0) + cregs[2] = va; + else + M[0x90 >> 2] = va; storepsw(OPPSW, IRC_PAGE); return 1; } /* Compute correct entry */ - entry >>= 4; /* Move physical to correct spot */ - entry |= (va & TLB_SEG) | TLB_VALID; - tlb[page] = entry; - *pa = (va & 0xfff) | ((entry & TLB_PHY) << 12); + entry >>= pte_shift; /* Move physical to correct spot */ + page = (va >> page_shift); + entry |= ((page & 0xff00) << 4) | TLB_VALID; + seg = (page & 0xff00) << 4; + tlb[page & 0xff] = entry; + *pa = (va & page_mask) | ((entry & TLB_PHY) << page_shift); if (va >= MEMSIZE) { storepsw(OPPSW, IRC_ADDR); return 1; @@ -504,6 +595,7 @@ int ReadFull(uint32 addr, uint32 *data) { storepsw(OPPSW, IRC_PROT); return 1; } + key[addr >> 9] |= 0x4; } *data = M[addr]; @@ -585,6 +677,19 @@ int WriteFull(uint32 addr, uint32 data) { if (TransAddr(addr, &pa)) return 1; + /* Check if in storage area */ + if (per_en && (cregs[9] & 0x20000000) != 0) { + if (cregs[10] <= cregs[11]) { + if (addr >= cregs[10] && addr <= cregs[11]) { + per_code |= 0x2000; + } + } else { + if (addr >= cregs[11] || addr <= cregs[10]) { + per_code |= 0x2000; + } + } + } + offset = pa & 0x3; pa >>= 2; @@ -599,6 +704,7 @@ int WriteFull(uint32 addr, uint32 data) { storepsw(OPPSW, IRC_PROT); return 1; } + key[pa >> 9] |= 0x6; } /* Check if we handle unaligned access */ @@ -620,6 +726,7 @@ int WriteFull(uint32 addr, uint32 data) { storepsw(OPPSW, IRC_PROT); return 1; } + key[pa2 >> 9] |= 0x6; } } else pa2 = pa + 1; @@ -675,6 +782,7 @@ int WriteByte(uint32 addr, uint32 data) { storepsw(OPPSW, IRC_PROT); return 1; } + key[pa >> 9] |= 0x6; } mask = 0xff; @@ -718,6 +826,7 @@ int WriteHalf(uint32 addr, uint32 data) { storepsw(OPPSW, IRC_PROT); return 1; } + key[pa >> 9] |= 0x6; } if (offset == 3) { @@ -733,6 +842,7 @@ int WriteHalf(uint32 addr, uint32 data) { storepsw(OPPSW, IRC_PROT); return 1; } + key[pa2 >> 9] |= 0x6; } } else pa2 = pa + 1; @@ -794,6 +904,50 @@ sim_instr(void) uint16 ops[3]; //double a, b, c; + if ((cpu_unit.flags & FEAT_370) == 0) { + page_shift = 12; + page_mask = 0xfff; + seg_shift = 20; + seg_mask = AMASK >> 20; + pte_avail = 0x8; + pte_mbz = 0x7; + pte_shift = 3; + seg_addr = cregs[0] & AMASK; + seg_len = (cregs[0] >> 24) & 0xff; + } else { + switch((cregs[0] >> 22) & 03) { + default: /* Generate translation exception */ + case 1: /* 2K pages */ + page_shift = 11; + page_mask = 0x7ff; + pte_avail = 0x4; + pte_mbz = 0x2; + pte_shift = 3; + break; + case 2: /* 4K pages */ + page_shift = 12; + page_mask = 0xfff; + pte_avail = 0x8; + pte_mbz = 0x6; + pte_shift = 4; + break; + } + switch((cregs[0] >> 20) & 07) { + default: /* Generate translation exception */ + case 1: /* 64K segments */ + seg_shift = 16; + seg_mask = AMASK >> 16; + break; + case 2: /* 1M segments */ + seg_shift = 20; + seg_mask = AMASK >> 20; + break; + } + seg_addr = cregs[1] & AMASK; + seg_len = (cregs[1] >> 24) & 0xff; + } + /* Generate pte index mask */ + page_index = (~(seg_mask << seg_shift) & ~page_mask) & AMASK; reason = SCPE_OK; ilc = 0; /* Enable timer if option set */ @@ -804,7 +958,6 @@ sim_instr(void) irq_en |= (loading != 0); while (reason == SCPE_OK) { - wait_loop: if (sim_interval <= 0) { reason = sim_process_event(); @@ -819,6 +972,8 @@ wait_loop: sim_debug(DEBUG_DETAIL, &cpu_dev, "IRQ=%04x %08x\n", irq, PC); if (loading) { irqcode = irq; + if ((cpu_unit.flags & FEAT_370) != 0) + (void)WriteHalf(0xBA, irq); (void)WriteHalf(0x2, irq); loading = 0; irqaddr = 0; @@ -849,6 +1004,8 @@ wait_loop: /* CPU IDLE */ if (flags & WAIT && irq_en == 0 && ext_en == 0) return STOP_HALT; + if (sim_idle_enab) + sim_idle(TMR_RTC, FALSE); sim_interval--; goto wait_loop; } @@ -876,9 +1033,23 @@ wait_loop: (((uint32)st_key) << 16) | (((uint32)flags) << 16) | ((uint32)irqcode), (((uint32)ilc) << 30) | (((uint32)cc) << 28) | (((uint32)pmsk) << 24) | PC); } + per_mod = 0; + per_code = 0; + per_addr = PC; ilc = 0; if (ReadHalf(PC, &dest)) goto supress; + if (per_en && (cregs[9] & 0x40000000) != 0) { + if (cregs[10] <= cregs[11]) { + if (PC >= cregs[10] && PC <= cregs[11]) { + per_code |= 0x4000; + } + } else { + if (PC >= cregs[11] || PC <= cregs[10]) { + per_code |= 0x4000; + } + } + } ops[0] = dest; ilc = 1; if (hst_lnt) @@ -1029,13 +1200,20 @@ opr: case OP_BASR: case OP_BAS: - if ((cpu_unit.flags & FEAT_DAT) == 0) { + if ((cpu_unit.flags & FEAT_370) != 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((cpu_unit.flags & FEAT_DAT) == 0) { storepsw(OPPSW, IRC_OPR); } else { dest = PC; - if (op != OP_BASR || R2(reg) != 0) + if (op != OP_BASR || R2(reg) != 0) { + if (per_en && (cregs[9] & 0x80000000) != 0) { + per_code |= 0x8000; /* Set PER branch */ + } PC = addr1 & AMASK; + } regs[reg1] = dest; + per_mod |= 1 << reg1; } break; @@ -1044,40 +1222,63 @@ opr: dest = (((uint32)ilc) << 30) | ((uint32)(cc & 03) << 28) | (((uint32)pmsk) << 24) | PC; - if (op != OP_BALR || R2(reg) != 0) + if (op != OP_BALR || R2(reg) != 0) { + if (per_en && (cregs[9] & 0x80000000) != 0) { + per_code |= 0x8000; /* Set PER branch */ + } PC = addr1 & AMASK; + } regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_BCTR: case OP_BCT: dest = src1 - 1; - if (dest != 0 && (op != OP_BCTR || R2(reg) != 0)) + if (dest != 0 && (op != OP_BCTR || R2(reg) != 0)) { + if (per_en && (cregs[9] & 0x80000000) != 0) { + per_code |= 0x8000; /* Set PER branch */ + } PC = addr1 & AMASK; + } regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_BCR: case OP_BC: dest = src1; - if (((0x8 >> cc) & reg1) != 0 && (op != OP_BCR || R2(reg) != 0)) + if (((0x8 >> cc) & reg1) != 0 && (op != OP_BCR || R2(reg) != 0)) { + if (per_en && (cregs[9] & 0x80000000) != 0) { + per_code |= 0x8000; /* Set PER branch */ + } PC = addr1 & AMASK; + } break; case OP_BXH: reg = R2(reg); src1 = regs[reg|1]; dest = regs[reg1] = regs[reg1] + regs[reg]; - if ((int32)dest > (int32)src1) - PC = addr1 & AMASK; + if ((int32)dest > (int32)src1) { + if (per_en && (cregs[9] & 0x80000000) != 0) { + per_code |= 0x8000; /* Set PER branch */ + } + PC = addr1 & AMASK; + } break; case OP_BXLE: reg = R2(reg); src1 = regs[reg|1]; dest = regs[reg1] = regs[reg1] + regs[reg]; - if ((int32)dest <= (int32)src1) - PC = addr1 & AMASK; + per_mod |= 1 << reg1; + if ((int32)dest <= (int32)src1) { + if (per_en && (cregs[9] & 0x80000000) != 0) { + per_code |= 0x8000; /* Set PER branch */ + } + PC = addr1 & AMASK; + } break; case OP_SSK: @@ -1093,6 +1294,7 @@ opr: } else if (cpu_unit.flags & FEAT_PROT) { if (TransAddr(addr1, &addr2)) break; + /* 370 mask = 0xfe */ key[addr2 >> 11] = src1 & 0xf8; } break; @@ -1110,9 +1312,11 @@ opr: } else { if (TransAddr(addr1, &addr2)) break; + /* 360 mask = 0xf8 */ dest &= 0xffffff00; dest |= key[addr2 >> 11]; regs[reg1] = dest; + per_mod |= 1 << reg1; } break; @@ -1124,13 +1328,28 @@ opr: if (flags & PROBLEM) { storepsw(OPPSW, IRC_PRIV); } else { - ReadByte(addr1, &src1); + if ((cpu_unit.flags & FEAT_370) != 0 && (cregs[0] & 0x40000000) != 0) { + storepsw(OPPSW, IRC_SPOP); + goto supress; + } + if (ReadByte(addr1, &src1)) + break; if (ec_mode) { - if (src1 & 0xf0) - storepsw(OPPSW, IRC_SPEC); - dat_en = (src1 > 2) & 3; - irq_en = (src1 & 02) != 0; - ext_en = (src1 & 01) != 0; + if ((cpu_unit.flags & FEAT_370) != 0) { + if (src1 & 0xb8) { + storepsw(OPPSW, IRC_SPEC); + break; + } + per_en = ((src1 & 0x40) != 0); + dat_en = (src1 & 04) != 0; + irq_en = (src1 & 02) != 0; + ext_en = (src1 & 01) != 0; + } else { + if (src1 & 0xf8) + storepsw(OPPSW, IRC_SPEC); + else + dat_en = (src1 & 04) != 0; + } } else { sysmsk = (src1 & 0xfe) << 8; irq_en = (sysmsk != 0); @@ -1168,28 +1387,28 @@ opr: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = startio(addr1); + cc = startio(addr1 & 0xfff); break; case OP_TIO: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = testio(addr1); + cc = testio(addr1 & 0xfff); break; case OP_HIO: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = haltio(addr1); + cc = haltio(addr1 & 0xfff); break; case OP_TCH: if (flags & PROBLEM) storepsw(OPPSW, IRC_PRIV); else - cc = testchan(addr1); + cc = testchan(addr1 & 0xfff); break; case OP_DIAG: @@ -1213,6 +1432,7 @@ opr: case OP_LTR: set_cc: regs[reg1] = dest; + per_mod |= 1 << reg1; cc = (dest & MSIGN) ? 1 : (dest == 0) ? 0 : 2; break; @@ -1227,6 +1447,7 @@ set_cc: case OP_LR: dest = src2; regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_C: @@ -1254,6 +1475,7 @@ set_cc: (~src1 & ~src2 & dest)) & MSIGN) != 0) { set_cc3: regs[reg1] = dest; + per_mod |= 1 << reg1; cc = 3; if (pmsk & FIXOVR) storepsw(OPPSW, IRC_FIXOVR); @@ -1275,6 +1497,7 @@ set_cc3: if (dest != 0) cc |= 1; regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_CL: @@ -1313,6 +1536,7 @@ set_cc3: STDBL(reg1, src1L); } else { regs[reg1] = (uint32)(src1L & FMASK); + per_mod |= 1 << reg1; } #else src1h = 0; @@ -1336,8 +1560,10 @@ set_cc3: if (op != OP_MH) { regs[reg1|1] = src1; regs[reg1] = src1h; + per_mod |= 3 << reg1; } else { regs[reg1] = src1; + per_mod |= 1 << reg1; } #endif break; @@ -1420,6 +1646,7 @@ set_cc3: src1 = -src1; regs[reg1] = src1; regs[reg1|1] = dest; + per_mod |= 3 << reg1; break; case OP_NR: @@ -1427,6 +1654,7 @@ set_cc3: dest = src1 & src2; cc = (dest == 0) ? 0 : 1; regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_OR: @@ -1434,6 +1662,7 @@ set_cc3: dest = src1 | src2; cc = (dest == 0) ? 0 : 1; regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_XR: @@ -1441,6 +1670,7 @@ set_cc3: dest = src1 ^ src2; cc = (dest == 0) ? 0 : 1; regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_MVI: @@ -1535,6 +1765,7 @@ char_save: hst[hst_p].src1 = dest; dest = ((uint32)dest) >> (addr1 & 0x3f); regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_SLL: @@ -1543,6 +1774,7 @@ char_save: hst[hst_p].src1 = dest; dest = ((uint32)dest) << (addr1 & 0x3f); regs[reg1] = dest; + per_mod |= 1 << reg1; break; case OP_SRA: @@ -1703,7 +1935,7 @@ save_dbl: reg1++; reg1 &= 0xf; addr1 += 4; - } ; + } break; case OP_LM: @@ -1711,21 +1943,24 @@ save_dbl: for (;;) { if (ReadFull(addr1, ®s[reg1])) goto supress; + per_mod |= 1 << reg1; if (reg1 == reg) break; reg1++; reg1 &= 0xf; addr1 += 4; - }; + } break; case OP_STMC: - if ((cpu_unit.flags & FEAT_DAT) == 0) { + if ((cpu_unit.flags & FEAT_370) != 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((cpu_unit.flags & FEAT_DAT) == 0) { storepsw(OPPSW, IRC_OPR); } else if (flags & PROBLEM) { storepsw(OPPSW, IRC_PRIV); } else { - reg &= 0xf; + reg = R2(reg); for (;;) { dest = 0; switch (reg) { @@ -1760,17 +1995,19 @@ save_dbl: reg1++; reg1 &= 0xf; addr1 += 4; - } ; + } } break; case OP_LMC: - if ((cpu_unit.flags & FEAT_DAT) == 0) { + if ((cpu_unit.flags & FEAT_370) != 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((cpu_unit.flags & FEAT_DAT) == 0) { storepsw(OPPSW, IRC_OPR); } else if (flags & PROBLEM) { storepsw(OPPSW, IRC_PRIV); } else { - reg &= 0xf; + reg = R2(reg); for (;;) { if (ReadFull(addr1, &dest)) goto supress; @@ -1783,6 +2020,8 @@ save_dbl: tlb[temp] = 0; if ((dest & 0x3f) != 0) storepsw(OPPSW, IRC_DATA); + seg_addr = dest & AMASK; + seg_len = (dest >> 24) & 0xff; break; case 0x6: /* Maskes */ sysmsk = (dest >> 16) & 0xfefe; @@ -1817,63 +2056,80 @@ save_dbl: reg1++; reg1 &= 0xf; addr1 += 4; - }; + } } break; case OP_LRA: - if ((cpu_unit.flags & FEAT_DAT) == 0) { + if ((cpu_unit.flags & FEAT_370) != 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((cpu_unit.flags & FEAT_DAT) == 0) { storepsw(OPPSW, IRC_OPR); } else if (flags & PROBLEM) { storepsw(OPPSW, IRC_PRIV); } else { - uint32 seg; - uint32 page; + uint32 seg; + uint32 page; + uint32 entry; /* RX in RS range */ if (X2(reg) != 0) addr1 = (addr1 + regs[X2(reg)]) & AMASK; - addr2 = (addr1 & SEG_MASK) >> 12; - src2 = addr2 & 0xff; - addr2 >>= 8; /* Segment number to word address */ - if ((addr2 >> 4) != 0) { - cc = 1; + addr1 &= AMASK; + seg = (addr1 >> seg_shift) & seg_mask; /* Segment number to word address */ + page = (addr1 >> page_shift) & page_index; + if (seg > seg_len) { + cc = 3; + regs[reg1] = addr1; + per_mod |= 1 << reg1; break; } - addr2 = ((addr2 & 0xFFF) << 2) + (cregs[0] & AMASK); - /* Ignore high order bits */ - addr2 &= AMASK; + addr2 = (((seg << 2) + seg_addr) & AMASK); if (addr2 >= MEMSIZE) { storepsw(OPPSW, IRC_ADDR); + goto supress; + } + entry = M[addr2 >> 2]; + /* Check if entry valid */ + if (entry & PTE_VALID) { + cc = 1; + regs[reg1] = addr2; + per_mod |= 1 << reg1; break; } - dest = M[addr2 >> 2]; - /* Check if entry valid and in correct length */ - if (dest & PTE_VALID || src2 > (dest >> 24)) { - cc = 1; + /* Check if over end of table */ + if (page > (entry >> 24)) { + cc = 3; + regs[reg1] = addr1; + per_mod |= 1 << reg1; break; } /* Now we need to fetch the actual entry */ - addr2 = (((dest & PTE_ADR) >> 1) + src2) << 2; + addr2 = (((entry & PTE_ADR) >> 1) + page) << 2; addr2 &= AMASK; if (addr2 >= MEMSIZE) { - storepsw(OPPSW, IRC_ADDR); - break; + storepsw(OPPSW, IRC_ADDR); + goto supress; } - dest = M[addr2 >> 2]; - dest >>= (addr2 & 2) ? 0 : 16; - dest &= 0xffff; + + entry = M[addr2 >> 2]; + entry >>= (addr2 & 2) ? 0 : 16; + entry &= 0xffff; - if ((dest & (PTE_AVAL)) != 0) { - cc = 2; - } else { - /* Compute correct entry */ - dest = (addr1 & 0xfff) | ((dest & TLB_PHY) << 12); - regs[reg1] = dest; - cc = 0; + /* Check if entry valid */ + if (entry & pte_avail) { + cc = 1; + regs[reg1] = addr2; + per_mod |= 1 << reg1; + break; } + + addr2 = (addr1 & page_mask) | ((entry & TLB_PHY) << page_shift); + cc = 0; + regs[reg1] = addr2; + per_mod |= 1 << reg1; } break; @@ -1953,6 +2209,7 @@ save_dbl: regs[1] |= addr1 & AMASK; regs[2] &= 0xffffff00; regs[2] |= dest & 0xff; + per_mod |= 6; cc = (reg == 0) ? 2 : 1; break; } @@ -2176,6 +2433,7 @@ save_dbl: if (op == OP_EDMK && !e2 && t) { regs[1] &= 0xff000000; regs[1] |= addr1 & AMASK; + per_mod |= 2; } /* Found none zero */ if (t) @@ -2274,6 +2532,551 @@ save_dbl: } break; + /* 370 specific instructions */ + case OP_ICM: + if ((cpu_unit.flags & FEAT_370) != 0) { + int i, j; + + dest = regs[reg1]; + cc = 0; + fill = 0x80; + for (i = 0x8, j=24; i != 0; i >>= 1, j-=8) { + if ((R2(reg) & i) != 0) { + if(ReadByte(addr1, &src1)) + break; + addr1++; + temp = 0xff << j; + dest = (dest & ~temp) | (src1 << j); + if (src1) { + if ((src1 & fill) != 0) { + cc = 1; + } else if (cc == 0) + cc = 2; + } + fill = 0; + } + } + regs[reg1] = dest; + per_mod |= 1 << reg1; + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + + case OP_STCM: + if ((cpu_unit.flags & FEAT_370) != 0) { + int i, j; + + dest = regs[reg1]; + for (i = 0x8, j=24; i != 0; i >>= 1, j-=8) { + if ((R2(reg) & i) != 0) { + src1 = (dest >> j) & 0xff; + if(WriteByte(addr1, src1)) + break; + addr1++; + } + } + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + + case OP_CLM: + if ((cpu_unit.flags & FEAT_370) != 0) { + uint32 rval[4]; + int i, j, k; + + dest = regs[reg1]; + for (i = 0x8, j=24, k=0; i != 0; i >>= 1, j-=8) { + if ((R2(reg) & i) != 0) + rval[k++] = (dest >> j) & 0xff; + } + cc = 0; + for (i = 0; i < k; i++) { + if (ReadByte(addr1, &src1)) + break; + addr1++; + if (rval[i] != src1) { + cc = (rval[i] < src1)? 1: 2; + break; + } + } + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + + case OP_370: + if ((cpu_unit.flags & FEAT_370) != 0) { + if (reg < 2 || reg > 0x13) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + if (reg != 5 && flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + goto supress; + } + switch(reg) { + case 0x2: /* STIDP */ + /* Store CPUID in double word */ + break; + case 0x4: /* SCK */ + /* Load clock with double word */ + break; + case 0x5: /* STCK */ + /* Store TOD clock in location */ + break; + case 0x6: /* SCKC */ + /* Load clock compare with double word */ + break; + case 0x7: /* STCKC */ + /* Store clock compare in double word */ + break; + case 0x8: /* SPT */ + /* Set the CPU timer with double word */ + break; + case 0x9: /* STPT */ + /* Store the CPU timer in double word */ + break; + case 0xa: /* SPKA */ + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + st_key = 0xf0 & addr1; + break; + case 0xb: /* IPK */ + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + regs[2] = (regs[2] & 0xffffff00) | st_key; + break; + case 0xd: /* PTLB */ + if ((cpu_unit.flags & FEAT_DAT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + for (temp = 0; temp < 256; temp++) + tlb[temp] = 0; + break; + case 0x10: /* SPX */ + storepsw(OPPSW, IRC_OPR); + goto supress; + case 0x11: /* STPX */ + storepsw(OPPSW, IRC_OPR); + goto supress; + case 0x12: /* STAP */ + storepsw(OPPSW, IRC_OPR); + goto supress; + case 0x13: /* RRB */ + if ((cpu_unit.flags & FEAT_DAT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + /* Set storage block reference bit to zero */ + break; + default: + storepsw(OPPSW, IRC_OPR); + goto supress; + } + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + case OP_STNSM: + if ((cpu_unit.flags & FEAT_370) != 0) { + if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + goto supress; + } + if ((cpu_unit.flags & FEAT_DAT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + src1 = 0; + if (ec_mode) { + if (dat_en) + src1 |= 0x4; + if (irq_en) + src1 |= 0x2; + if (per_en) + src1 |= 0x40; + if (ext_en) + src1 |= 0x1; + } else { + src1 = (sysmsk >> 8) | ext_en; + } + dest = reg & src1; + if (WriteByte(addr1, src1)) + break; + if (ec_mode) { + dat_en = ((dest & 0x4) != 0); + irq_en = ((dest & 0x2) != 0); + per_en = ((dest & 0x40) != 0); + ext_en = (dest & 1); + } else { + sysmsk = (dest << 8) & 0xfe00; + cregs[6] = (sysmsk << 16); + if (sysmsk) { + cregs[6] |= 0x1000000; + irq_en = 1; + } else + irq_en = 0; + ext_en = (dest & 1); + per_en = 0; + } + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + case OP_STOSM: + if ((cpu_unit.flags & FEAT_370) != 0) { + if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + goto supress; + } + if ((cpu_unit.flags & FEAT_DAT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + src1 = 0; + if (ec_mode) { + if (dat_en) + src1 |= 0x4; + if (irq_en) + src1 |= 0x2; + if (per_en) + src1 |= 0x40; + if (ext_en) + src1 |= 0x1; + } else { + src1 = (sysmsk >> 8) | ext_en; + } + dest = reg | src1; + if (WriteByte(addr1, src1)) + break; + if (ec_mode) { + dat_en = ((dest & 0x4) != 0); + irq_en = ((dest & 0x2) != 0); + per_en = ((dest & 0x40) != 0); + ext_en = (dest & 1); + } else { + sysmsk = (dest << 8) & 0xfe00; + cregs[6] = (sysmsk << 16); + if (sysmsk) { + cregs[6] |= 0x1000000; + irq_en = 1; + } else + irq_en = 0; + per_en = 0; + ext_en = (dest & 1); + } + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + case OP_SIGP: + if ((cpu_unit.flags & FEAT_370) != 0) { + if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + goto supress; + } + } + storepsw(OPPSW, IRC_OPR); + goto supress; + case OP_MC: + if ((cpu_unit.flags & FEAT_370) != 0) { + if ((reg & 0xf0) != 0) { + storepsw(OPPSW, IRC_SPEC); + goto supress; + } + if ((cregs[8] & (1 << reg)) != 0) { + src1 = reg; + addr2 = (0x94 >> 2); + M[addr2] &= 0xffff; + M[addr2] |= src1 << 16; + M[0x9C >> 2] = addr1; + storepsw(OPPSW, IRC_MCE); + goto supress; + } + } else { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + break; + + case OP_LCTL: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + } else { + reg = R2(reg); + for (;;) { + if (ReadFull(addr1, &dest)) + goto supress; + cregs[reg] = dest; + switch (reg) { + case 0x0: /* General controll register */ + switch((dest >> 22) & 03) { + default: /* Generate translation exception */ + case 1: /* 2K pages */ + page_shift = 11; + page_mask = 0x7ff; + pte_avail = 0x4; + pte_mbz = 0x2; + pte_shift = 3; + break; + case 2: /* 4K pages */ + page_shift = 12; + page_mask = 0xfff; + pte_avail = 0x8; + pte_mbz = 0x6; + pte_shift = 4; + break; + } + switch((dest >> 20) & 07) { + default: /* Generate translation exception */ + case 1: /* 64K segments */ + seg_shift = 16; + seg_mask = AMASK >> 16; + break; + case 2: /* 1M segments */ + seg_shift = 20; + seg_mask = AMASK >> 20; + break; + } + /* Generate pte index mask */ + page_index = (~(seg_mask << seg_shift) & ~page_mask) & AMASK; + break; + case 0x1: /* Segment table address and length */ + seg_addr = dest & AMASK; + seg_len = (dest >> 24) & 0xff; + break; + case 0x2: /* Maskes */ + sysmsk = (dest >> 16) & 0xffff; + break; + case 0x3: /* Unassigned */ + case 0x4: /* Unassigned */ + case 0x5: /* Unassigned */ + case 0x7: /* Unassigned */ + case 0x8: /* Monitor mask register */ + case 0x9: /* PER register */ + case 0xA: /* PER register */ + case 0xB: /* PER register */ + case 0xC: /* Partitioning register */ + case 0xD: /* Partitioning register */ + case 0xE: /* Partitioning register */ + case 0xF: /* Unassigned */ + break; + } + if (reg1 == reg) + break; + reg1++; + reg1 &= 0xf; + addr1 += 4; + }; + } + break; + + case OP_STCTL: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((cpu_unit.flags & FEAT_DAT) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + } else { + reg = R2(reg); + for (;;) { + dest = cregs[reg]; + if (WriteFull(addr1, dest)) + goto supress; + if (reg1 == reg) + break; + reg1++; + reg1 &= 0xf; + addr1 += 4; + } ; + } + break; + + case OP_CS: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((addr1 & 0x3) != 0 || (reg1 & 1) != 0 || (reg & 1) != 0) { + storepsw(OPPSW, IRC_SPEC); + } else { + if (ReadFull(addr1, &src2)) + goto supress; + src1 = regs[reg1]; + dest = regs[R2(reg)]; + if (src1 == src2) { + if (WriteFull(addr1, dest)) + goto supress; + cc = 0; + } else { + regs[reg1] = dest; + per_mod |= 1 << reg1; + cc = 1; + } + } + break; + + case OP_CDS: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if ((addr1 & 0x7) != 0) { + storepsw(OPPSW, IRC_SPEC); + } else { + if (ReadFull(addr1, &src2)) + goto supress; + if (ReadFull(addr1+4, &src2h)) + goto supress; + src1 = regs[reg1]; + src1h = regs[reg1|1]; + dest = regs[R2(reg)]; + desth = regs[R2(reg)|1]; + if (src1 == src2 && src1h == src2h) { + if (WriteFull(addr1, dest)) + goto supress; + if (WriteFull(addr1+4, desth)) + goto supress; + cc = 0; + } else { + regs[reg1] = dest; + regs[reg1|1] = desth; + per_mod |= 3 << reg1; + cc = 1; + } + } + break; + case OP_SRP: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } else if ((cpu_unit.flags & FEAT_DEC) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } else { + dec_srp(op, addr1, reg1, addr2, reg & 0xf); + } + break; + case OP_MVCL: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if (reg & 1 || reg1 & 1) { + storepsw(OPPSW, IRC_SPEC); + } else { + addr1 = regs[reg1] & AMASK; + src1 = regs[reg1|1]; + addr2 = regs[R2(reg)] & AMASK; + src2 = regs[R2(reg)|1]; + fill = (src2 >> 24) & 0xff; + src2 &= AMASK; + dest = addr2 + src2; + desth = addr1 + src1; + if ((dest & ~AMASK) != 0) { /* Wrap around */ + if (addr2 > desth || desth <= addr1) { + cc = 3; + break; + } + } else { + if (desth < dest && addr1 >= addr2) { + cc = 3; + break; + } + } + if (src1 == 0) + cc = (src2 == 0) ? 0 : 2; + else + cc = (src1 == src2) ? 0 : 1; + while (src1 != 0) { + if (src2 == 0) { + desth = fill; + } else { + if (ReadByte(addr2, &desth)) + break; + } + if (WriteByte(addr1, desth)) + break; + if (src2 != 0) { + addr2 ++; + src2 --; + } + if (src1 != 0) { + addr1 ++; + src1 --; + } + } + regs[reg1] = addr1 & AMASK; + regs[reg1|1] &= ~AMASK; + regs[reg1|1] |= src1 & AMASK; + regs[R2(reg)] = addr2 & AMASK; + regs[R2(reg)|1] &= ~AMASK; + regs[R2(reg)|1] |= src2 & AMASK; + per_mod |= 3 << reg1; + per_mod |= 3 << R2(reg); + if (src1 == 0 && src2 != 0) + cc = 1; + } + break; + case OP_CLCL: + if ((cpu_unit.flags & FEAT_370) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if (reg & 1 || reg1 & 1) { + storepsw(OPPSW, IRC_SPEC); + } else { + addr1 = regs[reg1] & AMASK; + src1 = regs[reg1|1]; + src1 &= AMASK; + addr2 = regs[R2(reg)] & AMASK; + src2 = regs[R2(reg)|1]; + fill = (src2 >> 24) & 0xff; + src2 &= AMASK; + cc = 0; + while (src1 != 0 && src2 != 0) { + if (src1 == 0) { + dest = fill; + } else { + if (ReadByte(addr1, &dest)) + break; + } + if (src2 == 0) { + desth = fill; + } else { + if (ReadByte(addr2, &desth)) + break; + addr2 ++; + src2 --; + } + if (src1 != 0) { + addr1 ++; + src1 --; + } + if (dest != desth) { + dest = dest - desth; + cc = (dest & MSIGN) ? 1 : 2; + break; + } + } + regs[reg1] = addr1 & AMASK; + regs[reg1|1] &= ~AMASK; + regs[reg1|1] |= src1 & AMASK; + regs[R2(reg)] = addr2 & AMASK; + regs[R2(reg)|1] &= ~AMASK; + regs[R2(reg)|1] |= src2 & AMASK; + per_mod |= 3 << reg1; + per_mod |= 3 << R2(reg); + } + break; /* Floating Half register */ case OP_HDR: @@ -4085,7 +4888,6 @@ fpnorm: fpregs[reg1|1] = desth; fpregs[reg1] = dest; //fprintf(stderr, "FP + res=%08x %08x %d %.12e\n\r", dest, desth, cc, cnvt_float(dest,desth)); - break; //fprintf(stderr, "FP + DP \n\r"); break; @@ -4106,6 +4908,9 @@ fpnorm: goto supress; } + if (per_en && (cregs[9] & 0x10000000) != 0 && (cregs[9] & 0xffff & per_mod) != 0) + per_code |= 0x1000; + if (hst_lnt) { hst[hst_p].dest = dest; hst[hst_p].cc = cc; @@ -4133,6 +4938,8 @@ fpnorm: fpregs[4], fpregs[5], fpregs[6], fpregs[7]); } } + if (per_en && per_code) + storepsw(OPPSW, 0); if (irqaddr != 0) { supress: @@ -4150,24 +4957,31 @@ supress: hst[hst_p].src2 = src2; } lpsw: + if ((cpu_unit.flags & FEAT_370) != 0) + ec_mode = (src1 & 0x00080000) != 0; if (ec_mode) { - dat_en = (src1 >> 26) & 3; - irq_en = (src1 & 0x2000000) != 0; + if ((cpu_unit.flags & FEAT_370) != 0) { + ext_en = (src1 & 0x1000000) != 0; + irq_en = (src1 & 0x2000000) != 0; + per_en = (src1 & 0x40000000) != 0; + } + dat_en = (src1 >> 26) & 1; cc = (src1 >> 12) & 3; pmsk = (src1 >> 8) & 0xf; } else { cregs[6] = src1 & 0xfe000000; sysmsk = (cregs[6] >> 16) & 0xfe00; if (sysmsk) { - cregs[reg] |= 0x1000000; + cregs[6] |= 0x1000000; irq_en = 1; } else irq_en = 0; + per_en = 0; pmsk = (src2 >> 24) & 0xf; cc = (src2 >> 28) & 0x3; + ext_en = (src1 & 0x1000000) != 0; } irqaddr = 0; - ext_en = (src1 & 0x1000000) != 0; st_key = (src1 >> 16) & 0xf0; flags = (src1 >> 16) & 0xf; PC = src2 & AMASK; @@ -4251,6 +5065,80 @@ int dec_store(uint8 *data, uint32 addr, int len, int sign) return 0; } +/* + * Handle SRP instruction. + * + */ +void +dec_srp(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2) +{ + uint8 a[32]; + int i, j; + uint8 acc; + uint8 cy; + int shift = addr2 & 0x3f; + int sa; + int ov = 0; + int zero; + + /* Load first operand. */ + if (dec_load(a, addr1, (int)len1, &sa)) + return; + + cy = 0; + zero = 1; + if (shift & 0x20) { /* Shift to right */ + shift = 0x3F & (~shift + 1); + cy = (a[shift] + len2) > 0x9; + j = shift+1; + for (i = 1; i < len1; i++, j++) { + if (j > len1) + acc = cy; + else + acc = a[j] + cy; + if (acc > 0x9) + acc += 0x6; + a[i] = acc & 0xf; + cy = (acc >> 4) & 0xf; + if ((acc & 0xf) != 0) + zero = 0; + } + } else if (shift != 0) { /* Shift to left */ + /* Check if we would move out any non-zero digits */ + for (j = len1; j > shift; j--) { + if (a[j] != 0) + ov = 1; + } + /* Now shift digits */ + for (i = len1; j > 0; i--, j--) { + a[i] = a[j]; + if (a[i] != 0) + zero = 0; + } + /* Now fill zeros until at bottom */ + for (; i > 0; i--) + a[i] = 0; + } else { + /* Check if number is zero */ + for (i = 1; i < len1; i++) { + if (a[i] != 0) { + zero = 0; + break; + } + } + } + + if (zero && !ov) + sa = 0; + cc = 0; + if (!zero) /* Really not zero */ + cc = (sa)? 1: 2; + dec_store(a, addr1, (int)len1, sa); + if (ov) + cc = 3; + if (ov && pmsk & DECOVR) + storepsw(OPPSW, IRC_DECOVR); +} /* * Handle AP, SP, CP and ZAP instructions. @@ -4569,7 +5457,6 @@ int32 i, clim; uint32 *nM = NULL; int32 max = MEMSIZE >> 2; -val = val >> UNIT_V_MSIZE; val = 16 * 1024 * val; if ((val <= 0) || (val > MAXMEMSIZE)) return SCPE_ARG; @@ -4587,8 +5474,6 @@ free (M); M = nM; fprintf(stderr, "Mem size=%x\n\r", val); MEMSIZE = val; -cpu_unit.flags &= ~UNIT_MSIZE; -cpu_unit.flags |= val / (16 * 1024); reset_all (0); return SCPE_OK; } diff --git a/IBM360/ibm360_defs.h b/IBM360/ibm360_defs.h index 10bd5c7..c403258 100644 --- a/IBM360/ibm360_defs.h +++ b/IBM360/ibm360_defs.h @@ -33,6 +33,44 @@ #define MEMSIZE (cpu_unit.capac) /* actual memory size */ #define MEM_ADDR_OK(x) (((x)) < MEMSIZE) +/* channel: + + Channels 0 and 4 are multiplexer channels. + subchannels = 128 + 0 - 7 0x80-0xff + 8 - 127 0x00-0x7f + 256 - +6 0x1xx - 0x6xx + + subchannels = 192 + 0 - 3 0xd0-0xff + 4 - 192 0x00-0xcf + 384 - +6 0x1xx - 0x6xx + + Channels 1,2,3,5,6 are selector channels. + Devices on channel 0 below number of subchannels have there own + virtual channel. + Devices on channel 0 above the number of subchannels are mapped in + groups of 16 into channels 0 to n. + + Channels 1-n run on channels virtual channels above subchannels. +*/ + +#define MAX_CHAN 6 +#define SUB_CHANS 192 +#define MAX_MUX 2 + +/* Define number of supported units for each device type */ +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CON 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_MT 1 +#define NUM_UNITS_MT 8 +#define NUM_DEVS_DASD 4 +#define NUM_UNITS_DASD 8 +#define NUM_DEVS_COM 1 +#define NUM_UNITS_COM 16 + /* Device information block */ typedef struct dib { uint8 mask; /* Device mask */ @@ -60,15 +98,17 @@ typedef struct dib { #define GET_UADDR(x) ((UNIT_ADDR_MASK & x) >> UNIT_V_ADDR) #define UNIT_ADDR(x) ((x) << UNIT_V_ADDR) -/* CPU options, needed by channel */ -#define FEAT_PROT (1 << (UNIT_V_UF + 8)) /* Storage protection feature */ -#define FEAT_DEC (1 << (UNIT_V_UF + 9)) /* Decimal instruction set */ -#define FEAT_FLOAT (1 << (UNIT_V_UF + 10)) /* Floating point instruction set */ -#define FEAT_UNIV (3 << (UNIT_V_UF + 9)) /* All instructions */ -#define FEAT_STOR (1 << (UNIT_V_UF + 11)) /* No alignment restrictions */ -#define FEAT_TIMER (1 << (UNIT_V_UF + 12)) /* Interval timer */ -#define FEAT_DAT (1 << (UNIT_V_UF + 13)) /* Dynamic address translation */ -#define EXT_IRQ (1 << (UNIT_V_UF_31)) /* External interrupt */ +/* CPU options */ +#define FEAT_PROT (1 << (UNIT_V_UF + 0)) /* Storage protection feature */ +#define FEAT_DEC (1 << (UNIT_V_UF + 1)) /* Decimal instruction set */ +#define FEAT_FLOAT (1 << (UNIT_V_UF + 2)) /* Floating point instruction set */ +#define FEAT_UNIV (3 << (UNIT_V_UF + 1)) /* All instructions */ +#define FEAT_STOR (1 << (UNIT_V_UF + 3)) /* No alignment restrictions */ +#define FEAT_TIMER (1 << (UNIT_V_UF + 4)) /* Interval timer */ +#define FEAT_DAT (1 << (UNIT_V_UF + 5)) /* Dynamic address translation */ +#define FEAT_EFP (1 << (UNIT_V_UF + 6)) /* Extended floating point */ +#define FEAT_370 (1 << (UNIT_V_UF + 7)) /* Is a 370 */ +#define EXT_IRQ (1 << (UNIT_V_UF + 8)) /* External interrupt */ /* low addresses */ #define IPSW 0x00 /* IPSW */ @@ -98,6 +138,8 @@ typedef struct dib { #define OP_ISK 0x09 #define OP_SVC 0x0A #define OP_BASR 0x0D +#define OP_MVCL 0x0E /* 370 Move long */ +#define OP_CLCL 0x0F /* 370 Compare logical long */ #define OP_LPR 0x10 #define OP_LNR 0x11 #define OP_LTR 0x12 @@ -220,9 +262,21 @@ typedef struct dib { #define OP_TIO 0x9D #define OP_HIO 0x9E #define OP_TCH 0x9F -#define OP_STMC 0xB0 +#define OP_STNSM 0xAC /* 370 Store then and system mask */ +#define OP_STOSM 0xAD /* 370 Store then or system mask */ +#define OP_SIGP 0xAE /* 370 Signal processor */ +#define OP_MC 0xAF /* 370 Monitor call */ +#define OP_STMC 0xB0 /* 360/67 Store control */ #define OP_LRA 0xB1 -#define OP_LMC 0xB8 +#define OP_370 0xB2 /* Misc 370 system instructions */ +#define OP_STCTL 0xB6 /* 370 Store control */ +#define OP_LCTL 0xB7 /* 370 Load control */ +#define OP_LMC 0xB8 /* 360/67 Load Control */ +#define OP_CS 0xBA /* 370 Compare and swap */ +#define OP_CDS 0xBB /* 370 Compare double and swap */ +#define OP_CLM 0xBD /* 370 Compare character under mask */ +#define OP_STCM 0xBE /* 370 Store character under mask */ +#define OP_ICM 0xBF /* 370 Insert character under mask */ #define OP_MVN 0xD1 #define OP_MVC 0xD2 #define OP_MVZ 0xD3 @@ -234,6 +288,7 @@ typedef struct dib { #define OP_TRT 0xDD #define OP_ED 0xDE #define OP_EDMK 0xDF +#define OP_SRP 0xF0 /* 370 Shift and round decimal */ #define OP_MVO 0xF1 #define OP_PACK 0xF2 #define OP_UNPK 0xF3 @@ -281,23 +336,6 @@ typedef struct dib { #define STATUS_INTER 0x0002 /* Channel interface check */ #define STATUS_CHAIN 0x0001 /* Channel chain check */ -/* channel: - subchannels = 128 - 0 - 7 0x80-0xff - 8 - 127 0x00-0x7f - 128 - +6 0x1xx - 0x6xx - - Devices on channel 0 below number of subchannels have there own - virtual channel. - Devices on channel 0 above the number of subchannels are mapped in - groups of 16 into channels 0 to n. - - Channels 1-n run on channels virtual channels above subchannels. -*/ - -#define MAX_CHAN 3 -#define SUB_CHANS 192 - void post_extirq(); /* look up device to find subchannel device is on */ @@ -349,15 +387,4 @@ extern DEVICE coml_dev; extern DEVICE com_dev; extern UNIT cpu_unit; -#define NUM_DEVS_CDP 1 -#define NUM_DEVS_CDR 1 -#define NUM_DEVS_CON 1 -#define NUM_DEVS_LPR 1 -#define NUM_DEVS_MT 1 -#define NUM_UNITS_MT 8 -#define NUM_DEVS_DASD 4 -#define NUM_UNITS_DASD 8 -#define NUM_DEVS_COM 1 -#define NUM_UNITS_COM 16 - extern void fprint_inst(FILE *, uint16 *); diff --git a/IBM360/ibm360_lpr.c b/IBM360/ibm360_lpr.c index 84d6090..01031c9 100644 --- a/IBM360/ibm360_lpr.c +++ b/IBM360/ibm360_lpr.c @@ -247,10 +247,19 @@ lpr_srv(UNIT *uptr) { return SCPE_OK; } + if (cmd == 7) { + uptr->u3 &= ~(LPR_FULL|LPR_CMDMSK); + uptr->u6 = 0; + chan_end(addr, SNS_DEVEND|SNS_CHNEND); + return SCPE_OK; + } + if ((uptr->u3 & LPR_FULL) || cmd == 0x3) { print_line(uptr); uptr->u3 &= ~(LPR_FULL|LPR_CMDMSK); uptr->u6 = 0; + if (cmd == 0x3) + chan_end(addr, SNS_CHNEND); set_devattn(addr, SNS_DEVEND); return SCPE_OK; } diff --git a/IBM360/ibm360_mt.c b/IBM360/ibm360_mt.c index 7c9d601..d3cb568 100644 --- a/IBM360/ibm360_mt.c +++ b/IBM360/ibm360_mt.c @@ -855,6 +855,7 @@ t_stat mt_srv(UNIT * uptr) sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit); uptr->u3 &= ~(MT_CMDMSK); r = sim_tape_detach(uptr); + set_devattn(addr, SNS_DEVEND); } break; } diff --git a/IBM360/ibm360_sys.c b/IBM360/ibm360_sys.c index 8ebac38..e946bf1 100644 --- a/IBM360/ibm360_sys.c +++ b/IBM360/ibm360_sys.c @@ -205,6 +205,7 @@ typedef struct _opcode { #define RS 03 #define SI 04 #define SS 05 +#define XX 06 #define LNMSK 07 #define ONEOP 010 #define IMDOP 020 @@ -366,11 +367,24 @@ t_opcode optab[] = { { OP_SP, "SP", SS|TWOOP }, { OP_MP, "MP", SS|TWOOP }, { OP_DP, "DP", SS|TWOOP }, + { OP_MVCL, "MVCL", RR }, + { OP_CLCL, "CLCL", RR }, + { OP_STNSM, "STNSM", SI }, + { OP_STOSM, "STOSM", SI }, + { OP_SIGP, "SIGP", RS }, + { OP_MC, "MC", SI }, + { OP_370, "I370", XX }, + { OP_STCTL, "STCTL", RS }, + { OP_LCTL, "LCTL", RS }, + { OP_CS, "CS", RS }, + { OP_CDS, "CDS", RS }, + { OP_CLM, "CLM", RS }, + { OP_STCM, "STCM", RS }, + { OP_ICM, "ICM", RS }, + { OP_SRP, "SRP", SS|TWOOP }, { 0, NULL, 0 } }; - - void fprint_inst(FILE *of, uint16 *val) { uint8 inst = (val[0] >> 8) & 0xff; int i;