1 /* hp2100_cpu5.c: HP 1000 RTE-6/VM VMA and RTE-IV EMA instructions
2 
3    Copyright (c) 2007-2008, Holger Veit
4    Copyright (c) 2006-2012, J. David Bryan
5 
6    Permission is hereby granted, free of charge, to any person obtaining a
7    copy of this software and associated documentation files (the "Software"),
8    to deal in the Software without restriction, including without limitation
9    the rights to use, copy, modify, merge, publish, distribute, sublicense,
10    and/or sell copies of the Software, and to permit persons to whom the
11    Software is furnished to do so, subject to the following conditions:
12 
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15 
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19    THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 
23    Except as contained in this notice, the name of the authors shall not be
24    used in advertising or otherwise to promote the sale, use or other dealings
25    in this Software without prior written authorization from the authors.
26 
27    CPU5         RTE-6/VM and RTE-IV firmware option instructions
28 
29    23-Mar-12    JDB     Added sign extension for dim count in "cpu_ema_resolve"
30    28-Dec-11    JDB     Eliminated unused variable in "cpu_ema_vset"
31    11-Sep-08    JDB     Moved microcode function prototypes to hp2100_cpu1.h
32    05-Sep-08    JDB     Removed option-present tests (now in UIG dispatchers)
33    30-Jul-08    JDB     Redefined ABORT to pass address, moved def to hp2100_cpu.h
34    26-Jun-08    JDB     Rewrote device I/O to model backplane signals
35    01-May-08    HV      Fixed mapping bug in "cpu_ema_emap"
36    21-Apr-08    JDB     Added EMA support from Holger
37    25-Nov-07    JDB     Added TF fix from Holger
38    07-Nov-07    HV      VMACK diagnostic tests 1...32 passed
39    19-Oct-07    JDB     Corrected $LOC operand profile to OP_CCCACC
40    03-Oct-07    HV      Moved RTE-6/VM instrs from hp2100_cpu0.c
41    26-Sep-06    JDB     Created
42 
43    Primary references:
44    - HP 1000 M/E/F-Series Computers Technical Reference Handbook
45         (5955-0282, Mar-1980)
46    - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
47         (92851-90001, Mar-1981)
48    - Macro/1000 Reference Manual (92059-90001, Dec-1992)
49 
50    Additional references are listed with the associated firmware
51    implementations, as are the HP option model numbers pertaining to the
52    applicable CPUs.
53 */
54 
55 #include <setjmp.h>
56 #include "hp2100_defs.h"
57 #include "hp2100_cpu.h"
58 #include "hp2100_cpu1.h"
59 
60 
61 /* RTE-6/VM Virtual Memory Area Instructions
62 
63    RTE-6/VM (product number 92084A) introduced Virtual Memory Area (VMA)
64    instructions -- a superset of the RTE-IV EMA instructions.  Different
65    microcode was supplied with the operating system that replaced the microcode
66    used with RTE-IV.  Microcode was limited to the E/F-Series, and the M-Series
67    used software equivalents.
68 
69    Option implementation by CPU was as follows:
70 
71       2114    2115    2116    2100   1000-M  1000-E  1000-F
72      ------  ------  ------  ------  ------  ------  ------
73       N/A     N/A     N/A     N/A     N/A    92084A  92084A
74 
75    The routines are mapped to instruction codes as follows:
76 
77      Instr.  1000-E/F   Description
78      ------  --------  ----------------------------------------------
79      .PMAP    105240   Map VMA page into map register
80      $LOC     105241   Load on call
81      [test]   105242   [self test]
82      .SWP     105243   [Swap A and B registers]
83      .STAS    105244   [STA B; LDA SP]
84      .LDAS    105245   [LDA SP]
85      .MYAD    105246   [NOP in microcode]
86      .UMPY    105247   [Unsigned multiply and add]
87 
88      .IMAP    105250   Integer element resolve address and map
89      .IMAR    105251   Integer element resolve address
90      .JMAP    105252   Double integer element resolve address and map
91      .JMAR    105253   Double integer element resolve address
92      .LPXR    105254   Map pointer in P+1 plus offset in P+2
93      .LPX     105255   Map pointer in A/B plus offset in P+1
94      .LBPR    105256   Map pointer in P+1
95      .LBP     105257   Map pointer in A/B registers
96 
97    Implementation notes:
98 
99     1. The opcodes 105243-247 are undocumented and do not appear to be used in
100        any HP software.
101 
102     2. The opcode list in the CE Handbook incorrectly shows 105246 as ".MYAD -
103        multiply 2 signed integers."  The microcode listing shows that this
104        instruction was deleted, and the opcode is now a NOP.
105 
106     3. RTE-IV EMA and RTE-6 VMA instructions shared the same address space, so a
107        given machine could run one or the other, but not both.
108 
109    Additional references:
110     - RTE-6/VM VMA/EMA Microcode Source (92084-18828, revision 3).
111     - RTE-6/VM Technical Specifications (92084-90015, Apr-1983).
112     - M/E/F-Series Computer Systems CE Handbook (5950-3767, Jul-1984).
113 */
114 
115 static const OP_PAT op_vma[16] = {
116   OP_N,    OP_CCCACC, OP_N,    OP_N,                    /* .PMAP  $LOC   [test] .SWAP  */
117   OP_N,    OP_N,      OP_N,    OP_K,                    /* .STAS  .LDAS  .MYAD  .UMPY  */
118   OP_A,    OP_A,      OP_A,    OP_A,                    /* .IMAP  .IMAR  .JMAP  .JMAR  */
119   OP_AA,   OP_A,      OP_A,    OP_N                     /* .LPXR  .LPX   .LBPR  .LBP   */
120   };
121 
122 /* some addresses in page0 of RTE-6/VM */
123 static const uint32 idx     = 0001645;
124 static const uint32 xmata   = 0001646;
125 static const uint32 xi      = 0001647;
126 static const uint32 xeqt    = 0001717;
127 static const uint32 vswp    = 0001776;
128 static const uint32 umaps   = 0003740;
129 static const uint32 page30  = 0074000;
130 static const uint32 page31  = 0076000;
131 static const uint32 ptemiss = 0176000;
132 
133 /* frequent constants in paging */
134 #define SUITMASK 0176000
135 #define NILPAGE  0176000
136 #define PAGEIDX  0001777
137 #define MSEGMASK 0076000
138 #define RWPROT   0141777
139 
140 
141 /* microcode version of resolve(): allows a much higher # of indirection levels. Used for
142    instance for LBP microcode diagnostics which will check > 100 levels.
143  */
144 #define VMA_INDMAX 200
145 
vma_resolve(uint32 MA,uint32 * addr,t_bool debug)146 static t_stat vma_resolve (uint32 MA, uint32 *addr, t_bool debug)
147 {
148 uint32 i;
149 uint32 faultma = MA;
150 
151 for (i = 0; (i < VMA_INDMAX) && (MA & I_IA); i++) { /* resolve multilevel */
152     MA = ReadW (MA & VAMASK);                       /* follow address chain */
153     }
154 
155 if (MA & I_IA) {
156     if (debug)
157         fprintf(sim_deb,">>CPU VMA: vma_resolve indirect loop addr=%06o\n",faultma);
158     return STOP_IND;                                /* indirect loop */
159     }
160 
161 *addr = MA;
162 return SCPE_OK;
163 }
164 
165 /* $LOC
166    ASSEMBLER CALLING SEQUENCE:
167 
168    $MTHK NOP             RETURN ADDRESS OF CALL (REDONE AFTER THIS ROUTINE)
169          JSB $LOC
170    .DTAB OCT LGPG#       LOGICAL PAGE # AT WHICH THE NODE TO
171   *                      BE MAPPED IN BELONGS  (0-31)
172          OCT RELPG       RELATIVE PAGE OFFSET FROM BEGINING
173   *                      OF PARTITION OF WHERE THAT NODE RESIDES.
174   *                      (0 - 1023)
175          OCT RELBP       RELATIVE PAGE OFFSET FROM BEGINING OF
176   *                      PARTITION OF WHERE BASE PAGE RESIDES
177   *                      (0 - 1023)
178    CNODE DEF .CNOD       THIS IS THE ADDRESS OF CURRENT PATH # WORD
179    .ORD  OCT XXXXX       THIS NODE'S LEAF # (IE PATH #)
180    .NOD# OCT XXXXX       THIS NODE'S ORDINAL #
181 */
182 
cpu_vma_loc(OPS op,uint32 intrq,t_bool debug)183 static t_stat cpu_vma_loc(OPS op,uint32 intrq,t_bool debug)
184 {
185 uint32 eqt,mls,pnod,lstpg,fstpg,rotsz,lgpg,relpg,relbp,matloc,ptnpg,physpg,cnt,pgs,umapr;
186 
187 eqt = ReadIO(xeqt,UMAP);                            /* get ID segment */
188 mls = ReadIO(eqt+33,SMAP);                          /* get word33 of alternate map */
189 if ((mls & 0x8000) == 0) {                          /* this is not an MLS prog! */
190     PC = err_PC;
191     if (debug)
192         fprintf(sim_deb,">>CPU VMA: cpu_vma_loc at P=%06o: not an MLS program\n", PC);
193     if (mp_control) MP_ABORT (eqt+33);              /* allow an MP abort */
194     return STOP_HALT;                               /* FATAL error! */
195     }
196 
197 pnod = mls & 01777;                                 /* get #pages of mem res nodes */
198 if (pnod == 0) {                                    /* no pages? FATAL! */
199     PC = err_PC;
200     if (debug)
201         fprintf(sim_deb,">>CPU VMA: cpu_vma_loc at P=%06o: no mem resident pages\n", PC);
202     if (mp_control) MP_ABORT (eqt+33);              /* allow an MP abort */
203     return STOP_HALT;
204     }
205 
206 lstpg = (ReadIO(eqt+29,SMAP) >> 10) - 1;            /* last page# of code */
207 fstpg =  ReadIO(eqt+23,SMAP) >> 10;                 /* index to 1st addr + mem nodes */
208 rotsz =  fstpg - (ReadIO(eqt+22,SMAP) >> 10);       /* #pages in root */
209 lgpg = op[0].word;
210 
211 /* lets do some consistency checks, CPU halt if they fail */
212 if (lstpg < lgpg || lgpg < fstpg) {                 /* assert LSTPG >= LGPG# >= FSTPG */
213     PC = err_PC;
214     if (debug)
215         fprintf(sim_deb,
216                 ">>CPU VMA: $LOC at P=%06o: failed check LSTPG >= LGPG# >= FSTPG\n",PC);
217     if (mp_control) MP_ABORT (eqt+22);              /* allow an MP abort */
218     return STOP_HALT;
219     }
220 
221 relpg = op[1].word;
222 if (pnod < relpg || relpg < (rotsz+1)) {            /* assert #PNOD >= RELPG >= ROTSZ+1 */
223     PC = err_PC;
224     if (debug)
225         fprintf(sim_deb,
226                 ">>CPU VMA: $LOC at %06o: failed check #PNOD >= RELPG >= ROTSZ+1\n",PC);
227     if (mp_control) MP_ABORT (eqt+22);              /* allow an MP abort */
228     return STOP_HALT;
229     }
230 
231 relbp = op[2].word;
232 if (relbp != 0)                                     /* assert RELBP == 0 OR */
233     if (pnod < relbp || relbp < (rotsz+1)) {        /* #PNOD >= RELBP >= ROTSZ+1 */
234         PC = err_PC;
235         if (debug)
236             fprintf(sim_deb,
237                     ">>CPU VMA: $LOC at P=%06o: failed check: #PNOD >= RELBP >= ROTSZ+1\n",PC);
238         if (mp_control) MP_ABORT (eqt+22);          /* allow an MP abort */
239         return STOP_HALT;
240     }
241 
242 cnt = lstpg - lgpg + 1;                             /* #pages to map */
243 pgs = pnod - relpg + 1;                             /* #pages from start node to end of code */
244 if (pgs < cnt) cnt = pgs;                           /* ensure minimum, so not to map into EMA */
245 
246 matloc  = ReadIO(xmata,UMAP);                       /* get MAT $LOC address */
247 ptnpg  = ReadIO(matloc+3,SMAP) & 01777;             /* index to start phys pg */
248 physpg = ptnpg + relpg;                             /* phys pg # of node */
249 umapr = 32 + lgpg;                                  /* map register to start */
250 
251 /* do an XMS with AR=umapr,BR=physpg,XR=cnt */
252 if (debug)
253     fprintf(sim_deb,
254             ">>CPU VMA: $LOC map %d pgs from phys=%06o to mapr=%d\n",
255             cnt,physpg,umapr);
256 while (cnt != 0) {
257     dms_wmap (umapr, physpg);                       /* map pages of new overlay segment */
258     cnt = (cnt - 1) & DMASK;
259     umapr = (umapr + 1) & DMASK;
260     physpg = (physpg + 1) & DMASK;
261     }
262 
263 dms_wmap(32,relbp+ptnpg);                           /* map base page again */
264 WriteW(op[3].word,op[4].word);                      /* path# we are going to */
265 
266 PC = (PC - 8) & DMASK;                              /* adjust PC to return address */
267                                                     /* word before the $LOC microinstr. */
268 PC = (ReadW(PC) - 1) & DMASK;                       /* but the call has to be rerun, */
269                                                     /* so must skip back to the original call */
270                                                     /* which will now lead to the real routine */
271 if (debug)
272     fprintf(sim_deb,">>CPU VMA: $LOC done: path#=%06o, P=%06o\n",op[4].word,PC);
273 return SCPE_OK;
274 }
275 
276 /* map pte into last page
277    return FALSE if page fault, nil flag in PTE or suit mismatch
278    return TRUE if suit match, physpg = physical page
279                 or page=0 -> last+1 page
280 */
cpu_vma_ptevl(uint32 pagid,uint32 * physpg)281 static t_bool cpu_vma_ptevl(uint32 pagid,uint32* physpg)
282 {
283 uint32 suit;
284 uint32 pteidx = pagid & 0001777;                    /* build index */
285 uint32 reqst  = pagid & SUITMASK;                   /* required suit */
286 uint32 pteval = ReadW(page31 | pteidx);             /* get PTE entry */
287 *physpg = pteval & 0001777;                         /* store physical page number */
288 suit = pteval & SUITMASK;                           /* suit number seen */
289 if (pteval == NILPAGE) return FALSE;                /* NIL value in PTE */
290 return suit == reqst || !*physpg;                   /* good page or last+1 */
291 }
292 
293 /* handle page fault */
cpu_vma_fault(uint32 x,uint32 y,int32 mapr,uint32 ptepg,uint32 ptr,uint32 faultpc,t_bool debug)294 static t_stat cpu_vma_fault(uint32 x,uint32 y,int32 mapr,
295             uint32 ptepg,uint32 ptr,uint32 faultpc, t_bool debug)
296 {
297 uint32 pre = ReadIO(xi,UMAP);                       /* get program preamble */
298 uint32 ema = ReadIO(pre+2,UMAP);                    /* get address of $EMA$/$VMA$ */
299 WriteIO(ema,faultpc,UMAP);                          /* write addr of fault instr */
300 XR = x;                                             /* X = faulting page */
301 YR = y;                                             /* Y = faulting address for page */
302 
303 if (mapr>0)
304     dms_wmap(mapr+UMAP,ptepg);                      /* map PTE into specified user dmsmap */
305 
306 /* do a safety check: first instr of $EMA$/$VMA$ must be a DST instr */
307 if (ReadIO(ema+1,UMAP) != 0104400) {
308     if (debug)
309         fprintf(sim_deb, ">>CPU VMA: pg fault: no EMA/VMA user code present\n");
310     if (mp_control) MP_ABORT (ema+1);               /* allow an MP abort */
311     return STOP_HALT;                               /* FATAL: no EMA/VMA! */
312     }
313 
314 PC = (ema+1) & VAMASK;                              /* restart $EMA$ user code, */
315                                                     /* will return to fault instruction */
316 
317 AR = (ptr >> 16) & DMASK;                           /* restore A, B */
318 BR = ptr & DMASK;
319 E = 0;                                              /* enforce E = 0 */
320 if (debug)
321     fprintf(sim_deb,
322             ">>CPU VMA: Call pg fault OS exit, AR=%06o BR=%06o P=%06o\n",
323             AR, BR, PC);
324 return SCPE_OK;
325 }
326 
327 /* map in PTE into last page, return false, if page fault */
cpu_vma_mapte(uint32 * ptepg)328 static t_bool cpu_vma_mapte(uint32* ptepg)
329 {
330 uint32 idext,idext2;
331 uint32 dispatch = ReadIO(vswp,UMAP) & 01777;        /* get fresh dispatch flag */
332 t_bool swapflag = TRUE;
333 
334 if (dispatch == 0) {                                /* not yet set */
335     idext = ReadIO(idx,UMAP);                       /* go into IDsegment extent */
336     if (idext != 0) {                               /* is ema/vma program? */
337         dispatch = ReadWA(idext+1) & 01777;         /* get 1st ema page: new vswp */
338         WriteIO(vswp,dispatch,UMAP);                /* move into $VSWP */
339         idext2 = ReadWA(idext+2);                   /* get swap bit */
340         swapflag = (idext2 & 020000) != 0;          /* bit 13 = swap bit */
341         }
342     }
343 
344 if (dispatch) {                                     /* some page is defined */
345     dms_wmap(31 + UMAP,dispatch);                   /* map $VSWP to register 31 */
346     *ptepg = dispatch;                              /* return PTEPG# for later */
347     }
348 
349 return swapflag;                                    /* true for swap bit set */
350 }
351 
352 /*  .LBP
353     ASSEMBLER CALLING SEQUENCE:
354 
355     DLD PONTR       TRANSLATE 32 BIT POINTER TO 15
356     JSB .LBP        BIT POINTER.
357     <RETURN - B = LOGICAL ADDRESS, A = PAGID>
358 
359     32 bit pointer:
360     ----------AR------------ -----BR-----
361     15 14....10 9....4 3...0 15.10 9....0
362     L<----------------------------------- L=1 local reference bit
363        XXXXXXXX<------------------------- 5 bit unused
364                 PPPPPP PPPPP PPPPP<------ 16 bit PAGEID
365                 SSSSSS<------------------ SUIT# within PAGEID
366                        PPPPP PPPPP<------ 10 bit PAGEID index into PTE
367                                    OOOOOO 10 bit OFFSET
368 */
369 
cpu_vma_lbp(uint32 ptr,uint32 aoffset,uint32 faultpc,uint32 intrq,t_bool debug)370 static t_stat cpu_vma_lbp(uint32 ptr,uint32 aoffset,uint32 faultpc,uint32 intrq,t_bool debug)
371 {
372 uint32 pagid,offset,ptrl,pgidx,ptepg;
373 uint16 p30,p31,suit;
374 t_stat reason = SCPE_OK;
375 uint32 faultab = ptr;                               /* remember A,B for page fault */
376 ptr += aoffset;                                     /* add the offset e.g. for .LPX */
377 
378 if (debug)
379     fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: ptr=%o/%o\n",
380             (ptr>>16) & DMASK,ptr & DMASK);
381 
382 O = 0;                                              /* clear overflow */
383 if (ptr & 0x80000000) {                             /* is it a local reference? */
384     ptrl = ptr & VAMASK;
385     if ((ptr&I_IA) && (reason = vma_resolve (ReadW (ptrl), &ptrl, debug)))
386         return reason;                              /* yes, resolve indirect ref */
387     BR = ptrl & VAMASK;                             /* address is local */
388     AR = (ptr >> 16) & DMASK;
389     if (debug)
390         fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: local ref AR=%06o BR=%06o\n",AR,BR);
391     return SCPE_OK;
392     }
393 
394 pagid  = (ptr >> 10) & DMASK;                       /* extract page id (16 bit idx, incl suit*/
395 offset = ptr & 01777;                               /* and offset */
396 suit   = pagid & SUITMASK;                          /* suit of page */
397 pgidx  = pagid & PAGEIDX;                           /* index into PTE */
398 
399 if (!cpu_vma_mapte(&ptepg))                         /* map in PTE */
400     return cpu_vma_fault(65535,ptemiss,-1,ptepg,faultab,faultpc, debug); /* oops, must init PTE */
401 
402 /* ok, we have the PTE mapped to page31 */
403 /* the microcode tries to reads two consecutive data pages into page30 and page31 */
404 
405 /* read the 1st page value from PTE */
406 p30 = ReadW(page31 | pgidx) ^ suit;
407 if (!p30)                                           /* matched suit for 1st page */
408     return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug);
409 
410 /* suit switch situation: 1st page is in last idx of PTE, then following page
411  * must be in idx 0 of PTE */
412 if (pgidx==01777) {                                 /* suit switch situation */
413     pgidx = 0;                                      /* select correct idx 0 */
414     suit = pagid+1;                                 /* suit needs increment */
415     if (suit==0) {                                  /* is it page 65536? */
416         offset += 02000;                            /* adjust to 2nd page */
417         suit = NILPAGE;
418         pgidx = 01777;
419         }
420 } else
421     pgidx++;                                        /* select next page */
422 
423 p31 = ReadW(page31 | pgidx) ^ suit;
424 if (!p31) {                                         /* matched suit for 2nd page */
425     dms_wmap(31+UMAP,p30);
426     if (p30 & SUITMASK)
427         return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug);
428     if (!(p31 ^ NILPAGE))                           /* suit is 63: fault */
429         return cpu_vma_fault(pagid+1,page31,31,ptepg,faultab,faultpc,debug);
430 
431     offset += 02000;                                /* adjust offset to last user map because */
432                                                     /* the address requested page 76xxx */
433     }
434 else {
435     dms_wmap(30+UMAP,p30);
436     if (p30 & SUITMASK)
437         return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug);
438     dms_wmap(31+UMAP,p31);
439     if (p31 & SUITMASK)
440         return cpu_vma_fault(pagid+1,page31,31,ptepg,faultab,faultpc,debug);
441     }
442 
443 AR = pagid;                                         /* return pagid in A */
444 BR = page30+offset;                                 /* mapped address in B */
445 if (debug)
446     fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: map done AR=%06o BR=%o6o\n",AR,BR);
447 return SCPE_OK;
448 }
449 
450 /*  .PMAP
451     ASSEMBLER CALLING SEQUENCE:
452 
453     LDA UMAPR          (MSEG - 31)
454     LDB PAGID          (0-65535)
455     JSB .PMAP          GO MAP IT IN
456     <ERROR RETURN>     A-REG = REASON, NOTE 1
457     <RETURN A=A+1, B=B+1,E=0 >> SEE NOTE 2>
458 
459     NOTE 1 : IF BIT 15 OF A-REG SET, THEN ALL NORMAL BRANCHES TO THE
460           $EMA$/$VMA$ CODE WILL BE CHANGED TO P+1 EXIT.  THE A-REG
461           WILL BE THE REASON THE MAPPING WAS NOT SUCCESSFUL IF BIT 15
462           OF THE A-REG WAS NOT SET.
463           THIS WAS DONE SO THAT A ROUTINE ($VMA$) CAN DO A MAPPING
464           WITHOUT THE POSSIBILITY OF BEING RE-CURRED.  IT IS USED
465           BY $VMA$ AND PSTVM IN THE PRIVLEDGED MODE.
466     NOTE 2: E-REG WILL = 1 IF THE LAST+1 PAGE IS REQUESTED AND
467             MAPPED READ/WRITE PROTECTED ON A GOOD P+2 RETURN.
468 */
cpu_vma_pmap(uint32 umapr,uint32 pagid,t_bool debug)469 static t_stat cpu_vma_pmap(uint32 umapr,uint32 pagid, t_bool debug)
470 {
471 uint32 physpg, ptr, pgpte;
472 uint32 mapnm = umapr & 0x7fff;                      /* strip off bit 15 */
473 
474 if (debug)
475     fprintf(sim_deb, ">>CPU VMA: .PMAP AR=%06o(umapr) BR=%06o(pagid)\n",umapr,pagid);
476 
477 if (mapnm > 31) {                                   /* check for invalid map register */
478     AR = 80;                                        /* error: corrupt EMA/VMA system */
479     if (debug)
480         fprintf(sim_deb, ">>CPU VMA: .PMAP invalid mapr: AR=80, exit P+1\n");
481     return SCPE_OK;                                 /* return exit PC+1 */
482     }
483 
484 ptr = (umapr << 16) | (pagid & DMASK);              /* build the ptr argument for vma_fault */
485 if (!cpu_vma_mapte(&pgpte)) {                       /* map the PTE */
486     if (umapr & 0x8000) {
487         XR = 65535;
488         YR = ptemiss;
489         if (debug)
490             fprintf(sim_deb,
491                     ">>CPU VMA: .PMAP pg fault&bit15: XR=%06o YR=%06o, exit P+1\n",
492                     XR, YR);
493         return SCPE_OK;                             /* use PC+1 error exit */
494         }
495     return cpu_vma_fault(65535,ptemiss,-1,pgpte,ptr,PC-1,debug);  /* oops: fix PTE */
496     }
497 
498 /* PTE is successfully mapped to page31 and dmsmap[63] */
499 if (!cpu_vma_ptevl(pagid,&physpg)) {
500     if (umapr & 0x8000) {
501         XR = pagid;
502         YR = page31;
503         if (debug)
504             fprintf(sim_deb,
505                     ">>CPU VMA: .PMAP pg map&bit15: XR=%06o YR=%06o, exit P+1\n",
506                     XR, YR);
507         return SCPE_OK;                             /* use PC+1 error exit*/
508         }
509     return cpu_vma_fault(pagid,page31,31,pgpte,ptr,PC-1,debug);   /* page not present */
510     }
511 
512 E = 1;
513 if (physpg == 0)                                    /* last+1 page ? */
514     physpg = RWPROT;                                /* yes, use page 1023 RW/Protected */
515 else E = 0;                                         /* normal page to map */
516 
517 dms_wmap(mapnm+UMAP,physpg);                        /* map page to user page reg */
518 if (mapnm != 31)                                    /* unless already unmapped, */
519     dms_wmap(31+UMAP,RWPROT);                       /* unmap PTE */
520 
521 AR = (umapr + 1) & DMASK;                           /* increment mapr for next call */
522 BR = (pagid + 1) & DMASK;                           /* increment pagid for next call */
523 O = 0;                                              /* clear overflow */
524 PC = (PC + 1) & VAMASK;                             /* normal PC+2 return */
525 if (debug)
526     fprintf(sim_deb,">>CPU VMA: .PMAP map done: AR=%06o BR=%o6o exit P+2\n",AR,BR);
527 return SCPE_OK;
528 }
529 
530 /* array calc helper for .imar, .jmar, .imap, .jmap
531    ij=in_s: 16 bit descriptors
532    ij=in_d: 32 bit descriptors
533 
534    This helper expects mainly the following arguments:
535    dtbl: pointer to an array descriptor table
536    atbl: pointer to the table of actual subscripts
537 
538    where subscript table is the following:
539    atbl-> DEF last_subscript,I      (point to single or double integer)
540           ...
541           DEF first subscript,I     (point to single or double integer)
542 
543    where Descriptor_table is the following table:
544    dtbl-> DEC #dimensions
545           DEC/DIN next-to-last dimension    (single or double integer)
546           ...
547           DEC/DIN first dimension           (single or double integer)
548           DEC elementsize in words
549           DEC high,low offset from start of EMA to element(0,0...0)
550 
551     Note that subscripts are counting from 0
552 */
cpu_vma_ijmar(OPSIZE ij,uint32 dtbl,uint32 atbl,uint32 * dimret,uint32 intrq,t_bool debug)553 static t_stat cpu_vma_ijmar(OPSIZE ij,uint32 dtbl,uint32 atbl,uint32* dimret,
554                             uint32 intrq,t_bool debug)
555 {
556 t_stat reason = SCPE_OK;
557 uint32 ndim,MA,i,ws;
558 int32 accu,ax,dx;
559 OP din;
560 int opsz = ij==in_d ? 2 : 1;
561 
562 ndim = ReadW(dtbl++);                               /* get #dimensions itself */
563 if (debug) {
564     fprintf(sim_deb, ">>CPU VMA array calc #dim=%d, size=%d\n",ndim,opsz);
565     fprintf(sim_deb, ">>CPU VMA: array actual subscripts (");
566     for (i=0; i<ndim; i++) {
567         MA = ReadW(atbl+i);
568         if (resolve (MA, &MA, intrq)) break;
569         din = ReadOp(MA,ij);
570         if (i>0) fputc(',',sim_deb);
571         fprintf(sim_deb,"%d",ij==in_d?INT32(din.dword) : INT16(din.word));
572         }
573 
574     fprintf(sim_deb,")\n>>CPU VMA: array descriptor table (");
575     if (ndim) {
576         for (i=0; i<ndim-1; i++) {
577             din = ReadOp(dtbl+i*opsz,ij);
578             if (i>0) fputc(',',sim_deb);
579             fprintf(sim_deb,"%d",ij==in_d?INT32(din.dword) : INT16(din.word));
580             }
581         i = dtbl+1+(ndim-1)*opsz;
582         ws = ReadW(i-1);
583         }
584     else {
585         i = dtbl;
586         ws = 1;
587         }
588     fprintf(sim_deb,")\n>>CPU VMA: array elemsz=%d base=%o/%o\n",
589             ws,ReadW(i),ReadW(i+1));
590     }
591 
592 if (dimret) *dimret = ndim;                         /* return dimensions */
593 if (ndim == 0) {                                    /* no dimensions:  */
594     AR = ReadW(dtbl++);                             /* return the array base itself */
595     BR = ReadW(dtbl);
596     if (debug)
597         fprintf(sim_deb,">>CPU VMA: #dim=0, AR=%06o, BR=%06o\n",AR,BR);
598     return SCPE_OK;
599     }
600 
601 /* calculate
602  *  (...(An*Dn-1)+An-1)*Dn-2)+An-2....)+A2)*D1)+A1)*#words + Array base
603  * Depending on ij, Ax and Dx can be 16 or 32 bit
604  */
605 accu = 0;
606 while (ndim-- > 0) {
607     MA = ReadW(atbl++);                             /* get addr of subscript */
608     if ((reason = resolve (MA, &MA, intrq)))        /* and resolve it */
609         return reason;
610     din = ReadOp(MA,ij);                            /* get actual subscript value */
611     ax = ij==in_d ? INT32(din.dword) : INT16(din.word);
612     accu += ax;                                     /* add to accu */
613 
614     if (ndim==0) ij = in_s;                         /* #words is single */
615     din = ReadOp(dtbl,ij);                          /* get dimension from descriptor table */
616     if (ij==in_d) {
617         dx = INT32(din.dword);                      /* either get double or single dimension */
618         dtbl += 2;
619     } else {
620         dx = INT16(din.word);
621         dtbl++;
622         }
623     accu *= dx;                                     /* multiply */
624     }
625 
626 din = ReadOp(dtbl,in_d);                            /* add base address */
627 accu += din.dword;
628 
629 AR = (accu >> 16) & DMASK;                          /* transfer to AB */
630 BR = accu & DMASK;
631 if (debug)
632     fprintf(sim_deb,">>CPU VMA: resulting virt addr=%o (AR=%06o, BR=%06o)\n",accu,AR,BR);
633 return reason;
634 }
635 
636 /*
637  * This is the main handler for the RTE6/VMA microcodes */
cpu_rte_vma(uint32 IR,uint32 intrq)638 t_stat cpu_rte_vma (uint32 IR, uint32 intrq)
639 {
640 t_stat reason = SCPE_OK;
641 OPS op;
642 OP_PAT pattern;
643 uint32 entry,t32,ndim;
644 uint32 dtbl,atbl;                                   /* descriptor table ptr, actual args ptr */
645 OP dop0,dop1;
646 uint32 pcsave = (PC+1) & VAMASK;                    /* save PC to check for redo in imap/jmap */
647 t_bool debug = DEBUG_PRI (cpu_dev, DEB_VMA);
648 
649 entry = IR & 017;                                   /* mask to entry point */
650 pattern = op_vma[entry];                            /* get operand pattern */
651 
652 if (pattern != OP_N)
653     if ((reason = cpu_ops (pattern, op, intrq)))    /* get instruction operands */
654         return reason;
655 
656 if (debug) {                                            /* debugging? */
657     fprintf (sim_deb, ">>CPU VMA: IR = %06o (", IR);    /* print preamble and IR */
658     fprint_sym (sim_deb, err_PC, (t_value *) &IR,       /* print instruction mnemonic */
659                 NULL, SWMASK('M'));
660     fprintf (sim_deb, "), P = %06o, XEQT = %06o",       /* print location and program ID */
661              err_PC, ReadW (xeqt));
662 
663     fprint_ops (pattern, op);                           /* print operands */
664     fputc ('\n', sim_deb);                              /* terminate line */
665     }
666 
667 switch (entry) {                                    /* decode IR<3:0> */
668 
669     case 000:                                       /* .PMAP 105240 (OP_N) */
670         reason = cpu_vma_pmap(AR,BR,debug);         /* map pages */
671         break;
672 
673     case 001:                                       /* $LOC  105241 (OP_CCCACC) */
674         reason = cpu_vma_loc(op,intrq,debug);       /* handle the coroutine switch */
675         break;
676 
677     case 002:                                       /* [test] 105242 (OP_N) */
678         XR = 3;                                     /* refer to src code 92084-18828 rev 3 */
679         SR = 0102077;                               /* HLT 77 instruction */
680         YR = 1;                                     /* ROMs correctly installed */
681         PC = (PC+1) & VAMASK;                       /* skip instr if VMA/EMA ROM installed */
682         break;
683 
684     case 003:                                       /* [swap] 105243 (OP_N) */
685         t32 = AR;                                   /* swap A and B registers */
686         AR = BR;
687         BR = t32;
688         break;
689 
690     case 004:                                       /* [---] 105244 (OP_N) */
691         reason = stop_inst;                         /* fragment of dead code */
692         break;                                      /* in microrom */
693 
694     case 005:                                       /* [---] 105245 (OP_N) */
695         reason = stop_inst;                         /* fragment of dead code */
696         break;                                      /* in microrom */
697 
698     case 006:                                       /* [nop] 105246 (OP_N) */
699         break;                                      /* do nothing */
700 
701     case 007:                                       /* [umpy] 105247 (OP_K) */
702         t32 = AR * op[0].word;                      /* get multiplier */
703         t32 += BR;                                  /* add B */
704         AR = (t32 >> 16) & DMASK;                   /* move result back to AB */
705         BR = t32 & DMASK;
706         O = 0;                                      /* instr clears OV */
707         break;
708 
709     case 010:                                       /* .IMAP 105250 (OP_A) */
710         dtbl = op[0].word;
711         atbl = PC;
712         if ((reason = cpu_vma_ijmar(in_s,dtbl,atbl,&ndim,intrq,debug)))   /* calc the virt address to AB */
713             return reason;
714         t32 = (AR << 16) | (BR & DMASK);
715         if ((reason = cpu_vma_lbp(t32,0,PC-2,intrq,debug)))
716             return reason;
717         if (PC==pcsave)
718             PC = (PC+ndim) & VAMASK;                /* adjust PC: skip ndim subscript words */
719         break;
720 
721     case 011:                                       /* .IMAR 105251 (OP_A) */
722         dtbl = ReadW(op[0].word);
723         atbl = (op[0].word+1) & VAMASK;
724         reason = cpu_vma_ijmar(in_s,dtbl,atbl,0,intrq,debug); /* calc the virt address to AB */
725     break;
726 
727     case 012:                                       /* .JMAP 105252 (OP_A) */
728         dtbl = op[0].word;
729         atbl = PC;
730         if ((reason = cpu_vma_ijmar(in_d,dtbl,atbl,&ndim,intrq,debug)))   /* calc the virtual address to AB */
731             return reason;
732         t32 = (AR << 16) | (BR & DMASK);
733         if ((reason = cpu_vma_lbp(t32,0,PC-2,intrq,debug)))
734             return reason;
735         if (PC==pcsave)
736             PC = (PC + ndim) & VAMASK;              /* adjust PC: skip ndim subscript dword ptr */
737         break;
738 
739     case 013:                                       /* .JMAR 105253 (OP_A) */
740         dtbl = ReadW(op[0].word);
741         atbl = (op[0].word+1) & VAMASK;
742         reason = cpu_vma_ijmar(in_d,dtbl,atbl,0,intrq,debug); /* calc the virt address to AB */
743         break;
744 
745     case 014:                                       /* .LPXR 105254 (OP_AA) */
746         dop0 = ReadOp(op[0].word,in_d);             /* get pointer from arg */
747         dop1 = ReadOp(op[1].word,in_d);
748         t32 = dop0.dword + dop1.dword;              /* add offset to it */
749         reason = cpu_vma_lbp(t32,0,PC-3,intrq,debug);
750         break;
751 
752     case 015:                                       /* .LPX  105255 (OP_A) */
753         t32 = (AR << 16) | (BR & DMASK);            /* pointer in AB */
754         dop0 = ReadOp(op[0].word,in_d);
755         reason = cpu_vma_lbp(t32,dop0.dword,PC-2,intrq,debug);
756         break;
757 
758     case 016:                                       /* .LBPR 105256 (OP_A) */
759         dop0 = ReadOp(op[0].word,in_d);             /* get the pointer */
760         reason = cpu_vma_lbp(dop0.dword,0,PC-2,intrq,debug);
761         break;
762 
763     case 017:                                       /* .LBP  105257 (OP_N) */
764         t32 = (AR << 16) | (BR & DMASK);
765         reason = cpu_vma_lbp(t32,0,PC-1,intrq,debug);
766         break;
767     }
768 
769 return reason;
770 }
771 
772 
773 /* RTE-IV Extended Memory Area Instructions
774 
775    The RTE-IV operating system (HP product number 92067A) introduced the
776    Extended Memory Area (EMA) instructions.  EMA provided a mappable data area
777    up to one megaword in size.  These three instructions accelerated data
778    accesses to variables stored in EMA partitions.  Support was limited to
779    E/F-Series machines; M-Series machines used software equivalents.
780 
781    Option implementation by CPU was as follows:
782 
783       2114    2115    2116    2100   1000-M  1000-E  1000-F
784      ------  ------  ------  ------  ------  ------  ------
785       N/A     N/A     N/A     N/A     N/A    92067A  92067A
786 
787    The routines are mapped to instruction codes as follows:
788 
789      Instr.  1000-E/F   Description
790      ------  --------  ----------------------------------------------
791      .EMIO    105240   EMA I/O
792      MMAP     105241   Map physical to logical memory
793      [test]   105242   [self test]
794      .EMAP    105257   Resolve array element address
795 
796    Notes:
797 
798      1. RTE-IV EMA and RTE-6 VMA instructions share the same address space, so a
799         given machine can run one or the other, but not both.
800 
801      2. The EMA diagnostic (92067-16013) reports bogus MMAP failures if it is
802         not loaded at the start of its partition (e.g., because of a LOADR "LO"
803         command).  The "ICMPS" map comparison check in the diagnostic assumes
804         that the starting page of the program's partition contains the first
805         instruction of the program and prints "MMAP ERROR" if it does not.
806 
807    Additional references:
808     - RTE-IVB Programmer's Reference Manual (92068-90004, Dec-1983).
809     - RTE-IVB Technical Specifications (92068-90013, Jan-1980).
810 */
811 
812 static const OP_PAT op_ema[16] = {
813   OP_AKA,  OP_AKK,    OP_N,    OP_N,                    /* .EMIO  MMAP   [test]  ---   */
814   OP_N,    OP_N,      OP_N,    OP_N,                    /*  ---    ---    ---    ---   */
815   OP_N,    OP_N,      OP_N,    OP_N,                    /*  ---    ---    ---    ---   */
816   OP_N,    OP_N,      OP_N,    OP_AAA                   /*  ---    ---    ---   .EMAP  */
817   };
818 
819 /* calculate the 32 bit EMA subscript for an array */
cpu_ema_resolve(uint32 dtbl,uint32 atbl,uint32 * sum)820 static t_bool cpu_ema_resolve(uint32 dtbl,uint32 atbl,uint32* sum)
821 {
822 int32 sub, act, low, sz, ndim;
823 uint32 MA, base;
824 
825 ndim = ReadW(dtbl++);                                   /* # dimensions */
826 ndim = SEXT(ndim);                                      /* sign extend */
827 if (ndim < 0) return FALSE;                             /* invalid? */
828 
829 *sum = 0;                                               /* accu for index calc */
830 while (ndim > 0) {
831     MA = ReadW(atbl++);                                 /* get address of A(N) */
832     resolve (MA, &MA, 0);
833     act = ReadW(MA);                                    /* A(N) */
834     low = ReadW(dtbl++);                                /* -L(N) */
835     sub = SEXT(act) + SEXT(low);                        /* subscript */
836     if (sub & 0xffff8000) return FALSE;                 /* overflow? */
837     *sum += sub;                                        /* accumulate */
838     sz = ReadW(dtbl++);
839     sz = SEXT(sz);
840     if (sz < 0) return FALSE;
841     *sum *= sz;
842     if (*sum > (512*1024)) return FALSE;                /* overflow? */
843     ndim--;
844 }
845 base = (ReadW(dtbl+1)<<16) | (ReadW(dtbl) & 0xffff);    /* base of array in EMA */
846 if (base & 0x8000000) return FALSE;
847 *sum += base;                                           /* calculate address into EMA */
848 if (*sum & 0xf8000000) return FALSE;                    /* overflow? */
849 return TRUE;
850 }
851 
852 /* implementation of VIS RTE-IVB EMA support
853  * .ERES microcode routine, resolves only EMA addresses
854  *  Call:
855  *    .OCT 101474B
856  *    DEF RTN          error return (rtn), good return is rtn+1
857  *    DEF DUMMY        dummy argument for compatibility with .EMAP
858  *    DEF TABLE[,I]    array declaration (dtbl)
859  *    DEF A(N)[,I]     actual subscripts (atbl)
860  *    DEF A(N-1)[,I]
861  *    ...
862  *    DEF A(2)[,I]
863  *    DEF A(1)[,I]
864  *  RTN EQU *          error return A="20", B="EM"
865  *  RTN+1 EQU *+1      good return B=logical address
866  *
867  *  TABLE DEC #        # dimensions
868  *        DEC -L(N)
869  *        DEC D(N-1)
870  *        DEC -L(N-1)  lower bound (n-1)st dim
871  *        DEC D(N-2)   (n-2)st dim
872  *        ...
873  *        DEC D(1)     1st dim
874  *        DEC -L(1)    lower bound 1st dim
875  *        DEC #        # words/element
876  *        OFFSET 1     EMA Low
877  *        OFFSET 2     EMA High
878  */
cpu_ema_eres(uint32 * rtn,uint32 dtbl,uint32 atbl,t_bool debug)879 t_stat cpu_ema_eres(uint32 *rtn,uint32 dtbl,uint32 atbl,t_bool debug)
880 {
881 uint32 sum;
882 if (cpu_ema_resolve(dtbl,atbl,&sum)) {                  /* calculate subscript */
883     AR = sum & 0xffff;
884     BR = sum >> 16;
885     if (!(BR & SIGN)) {                                 /* no overflow? */
886         (*rtn)++;                                       /* return via good exit */
887         return SCPE_OK;
888     }
889 }
890 AR = 0x3230;                                            /* error condition: */
891 BR = 0x454d;                                            /* AR = '20', BR = 'EM' */
892 return SCPE_OK;                                         /* return via unmodified rtn */
893 }
894 
895 /* implementation of VIS RTE-IVB EMA support
896  * .ESEG microcode routine
897  *  Call:
898  *    LDA FIRST        first map to set
899  *    LDB N            # of maps to set
900  *    .OCT 101475B/105475B
901  *    DEF RTN          ptr to return
902  *    DEF TABLE        map table
903  *    RTN EQU *        error return A="21", B="EM"
904  *    RTN+1 EQU *+1    good return B=logical address
905  *
906  * load maps FIRST to FIRST+N from TABLE, with FIRST = FIRST + LOG_START MSEG
907  * update map table in base page. Set LOG_START MSEG=0 if opcode==105475
908  */
cpu_ema_eseg(uint32 * rtn,uint32 IR,uint32 tbl,t_bool debug)909 t_stat cpu_ema_eseg(uint32* rtn, uint32 IR, uint32 tbl, t_bool debug)
910 {
911 uint32 xidex,eqt,idext0,idext1;
912 uint32 msegsz,phys,msegn,last,emasz,pg0,pg1,pg,i,lp;
913 
914 if ((BR & SIGN) || BR==0) goto em21;                    /* #maps not positive? */
915 xidex = ReadIO(idx,UMAP);                               /* read ID extension */
916 if (xidex==0) goto em21;
917 idext0 = ReadWA(xidex+0);                               /* get 1st word idext */
918 msegsz = idext0 & 037;                                  /* S7 MSEG size */
919 WriteIO(xidex+0, idext0 | 0100000, SMAP);               /* enforce nonstd MSEG */
920 idext1 = ReadWA(xidex+1);                               /* get 2nd word idext */
921 phys = idext1 & 01777;                                  /* S5 phys start of EMA */
922 msegn = (idext1 >> 11) & 037;                           /* S9 get logical start MSEG# */
923 if (IR & 04000) {                                       /* opcode == 105475? (.VPRG) */
924     msegn = 0;                                          /* log start = 0 */
925     msegsz = 32;                                        /* size = full range */
926 }
927 last = AR-1 + BR;                                       /* last page */
928 if (last > msegsz) goto em21;                           /* too many? error */
929 eqt = ReadIO(xeqt,UMAP);
930 emasz = (ReadWA(eqt+28) & 01777) - 1;                   /* S6 EMA size in pages */
931 
932 /* locations 1740...1777 of user base page contain the map entries we need.
933  * They are normally hidden by BP fence, therefore they have to be accessed by
934  * another fence-less map register. uCode uses #1 temporarily */
935 pg0 = dms_rmap(UMAP+0);                                 /* read map #0 */
936 pg1 = dms_rmap(UMAP+1);                                 /* save map #1 */
937 dms_wmap(UMAP+1,pg0);                                   /* copy #0 into reg #1 */
938 lp = AR + msegn;                                        /* first */
939 for (i=0; i<BR; i++) {                                  /* loop over N entries */
940     pg = ReadW(tbl++);                                  /* get value from table */
941     if ((pg & SIGN) || pg > emasz) pg |= 0140000;       /* write protect if outside */
942     pg += phys;                                         /* adjust into EMA page range */
943     WriteIO(umaps+lp+i, pg, UMAP);                      /* copy pg to user map */
944 /* printf("MAP val %oB to reg %d (addr=%oB)\n",pg,lp+i,umaps+lp+i); */
945     dms_wmap(UMAP+lp+i, pg);                            /* set DMS reg */
946 }
947 dms_wmap(UMAP+1,pg1);                                   /* restore map #1 */
948 O = 0;                                                  /* clear overflow */
949 (*rtn)++;                                               /* return via good exit */
950 return SCPE_OK;
951 
952 em21:
953 AR = 0x3231;                                            /* error condition: */
954 BR = 0x454d;                                            /* AR = '21', BR = 'EM' */
955 return SCPE_OK;                                         /* return via unmodified rtn */
956 }
957 
958 /* implementation of VIS RTE-IVB EMA support
959  * .VSET microcode routine
960  *  Call:
961  *    .OCT 101476B
962  *    DEF RTN          return address
963  *    DEF VIN          input vector
964  *    DEF VOUT         output vector
965  *    DEF MAPS
966  *    OCT #SCALARS
967  *    OCT #VECTORS
968  *    OCT K            1024/(#words/element)
969  *    RTN EQU *        error return  (B,A) = "VI22"
970  *    RTN+1 EQU *+1    hard return, A = K/IMAX
971  *    RTN+2 EQU *+2    easy return, A = 0, B = 2* #VCTRS
972  */
cpu_ema_vset(uint32 * rtn,OPS op,t_bool debug)973 t_stat cpu_ema_vset(uint32* rtn, OPS op, t_bool debug)
974 {
975 uint32 vin     = op[0].word;                            /* S1 */
976 uint32 vout    = op[1].word;                            /* S2 */
977 uint32 maps    = op[2].word;                            /* S3 */
978 uint32 scalars = op[3].word;                            /* S4 */
979 uint32 vectors = op[4].word;                            /* S5 */
980 uint32 k       = op[5].word;                            /* S6 */
981 uint32 imax    = 0;                                     /* imax S11*/
982 uint32 xidex, idext1, mseg, addr, i, MA;
983 t_bool negflag = FALSE;
984 
985 for (i=0; i<scalars; i++) {                             /* copy scalars */
986     XR = ReadW(vin++);
987     WriteW(vout++, XR);
988 }
989 xidex = ReadIO(idx,UMAP);                               /* get ID extension */
990 if (xidex==0) goto vi22;                                /* NO EMA? error */
991 idext1 = ReadWA(xidex+1);
992 mseg = (idext1 >> 1) & MSEGMASK;                        /* S9 get logical start MSEG */
993 
994 for (i=0; i<vectors; i++) {                             /* copy vector addresses */
995     MA = ReadW(vin++);
996     resolve (MA, &MA, 0);
997     addr = ReadW(MA) & 0177777;                         /* LSB */
998     addr |= (ReadW(MA+1)<<16);                          /* MSB, build address */
999     WriteW(vout++, mseg + (addr & 01777));              /* build and write log addr of vector */
1000     addr = (addr >> 10) & 0xffff;                       /* get page */
1001     WriteW(maps++, addr);                               /* save page# */
1002     WriteW(maps++, addr+1);                             /* save next page# as well */
1003     MA = ReadW(vin++);                                  /* get index into Y */
1004     resolve(MA, &MA, 0);
1005     YR = ReadW(MA);                                     /* get index value */
1006     WriteW(vout++, MA);                                 /* copy address of index */
1007     if (YR & SIGN) {                                    /* index is negative */
1008          negflag = TRUE;                                /* mark a negative index (HARD) */
1009          YR = (~YR + 1) & DMASK;                        /* make index positive */
1010     }
1011     if (imax < YR) imax = YR;                           /* set maximum index */
1012     mseg += 04000;                                      /* incr mseg address by 2 more pages */
1013 }
1014 MA = ReadW(vin);                                        /* get N index into Y */
1015 resolve(MA, &MA, 0);
1016 YR = ReadW(MA);
1017 WriteW(vout++, MA); vin++;                              /* copy address of N */
1018 
1019 if (imax==0) goto easy;                                 /* easy case */
1020 AR = k / imax; AR++;                                    /* calculate K/IMAX */
1021 if (negflag) goto hard;                                 /* had a negative index? */
1022 if (YR > AR) goto hard;
1023 
1024 easy:
1025 (*rtn)++;                                               /* return via exit 2 */
1026 AR = 0;
1027 
1028 hard:
1029 (*rtn)++;                                               /* return via exit 1 */
1030 BR = 2 * op[4].word;                                    /* B = 2* vectors */
1031 return SCPE_OK;
1032 
1033 vi22:                                                   /* error condition */
1034   AR=0x3232;                                            /* AR = '22' */
1035   BR=0x5649;                                            /* BR = 'VI' */
1036   return SCPE_OK;                                       /* return via unmodified e->rtn */
1037 }
1038 
1039 typedef struct ema4 {
1040     uint32 mseg;                                         /* logical start of MSEG */
1041     uint32 msegsz;                                       /* size of std mseg in pgs */
1042     uint32 pgoff;                                        /* pg # in EMA containing element */
1043     uint32 offs;                                         /* offset into page of element */
1044     uint32 msoff;                                        /* total offset to element in MSEG */
1045     uint32 emasz;                                        /* size of ema in pgs */
1046     uint32 msegno;                                       /* # of std mseg */
1047     uint32 ipgs;                                         /* # of pgs to start of MSEG */
1048     uint32 npgs;                                         /* # of pgs needed */
1049     uint32 spmseg;                                       /* first phys pg of MSEG */
1050 } EMA4;
1051 
cpu_ema_emas(uint32 dtbl,uint32 atbl,EMA4 * e)1052 static t_bool cpu_ema_emas(uint32 dtbl,uint32 atbl,EMA4* e)
1053 {
1054 uint32 xidex, eqt;
1055 uint32 sum, msegsz,pgoff,offs,emasz,msegno,msoff,ipgs;
1056 
1057 if (!cpu_ema_resolve(dtbl,atbl,&sum)) return FALSE;     /* calculate 32 bit index */
1058 
1059 xidex = ReadIO(idx,UMAP);                               /* read ID extension */
1060 msegsz = ReadWA(xidex+0) & 037;                         /* S5 # pgs for std MSEG */
1061 pgoff = sum >> 10;                                      /* S2 page containing element */
1062 offs = sum & 01777;                                     /* S6 offset in page to element */
1063 if (pgoff > 1023) return FALSE;                         /* overflow? */
1064 eqt = ReadIO(xeqt,UMAP);
1065 emasz = ReadWA(eqt+28) & 01777;                         /* S EMA size in pages */
1066 if (pgoff > emasz) return FALSE;                        /* outside EMA? */
1067 msegno = pgoff / msegsz;                                /* S4 # of MSEG */
1068 msoff = pgoff % msegsz;                                 /* offset within MSEG in pgs */
1069 ipgs = pgoff - msoff;                                   /* S7 # pgs to start of MSEG */
1070 msoff = msoff << 10;                                    /* offset within MSEG in words */
1071 msoff += offs;                                          /* S1 offset to element in words */
1072 
1073 e->msegsz = msegsz;                                     /* return calculated data */
1074 e->pgoff = pgoff;
1075 e->offs = offs;
1076 e->emasz = emasz;
1077 e->msegno = msegno;
1078 e->ipgs = ipgs;
1079 e->msoff = msoff;
1080 return TRUE;
1081 }
1082 
cpu_ema_mmap01(EMA4 * e)1083 static t_bool cpu_ema_mmap01(EMA4* e)
1084 {
1085 uint32 xidex,idext0, pg, pg0, pg1, i;
1086 
1087 uint32 base = e->mseg >> 10;                            /* get the # of first MSEG DMS reg */
1088 xidex = ReadIO(idx,UMAP);                               /* get ID extension */
1089 idext0 = ReadWA(xidex+1);
1090 
1091 if (e->npgs==0) return FALSE;                           /* no pages to map? */
1092 if ((e->npgs+1+e->ipgs) <= e->emasz) e->npgs++;         /* actually map npgs+1 pgs */
1093 
1094 /* locations 1740...1777 of user base page contain the map entries we need.
1095  * They are normally hidden by BP fence, therefore they have to be accessed by
1096  * another fence-less map register. uCode uses #1, macro code uses $DVCT (==2)
1097  */
1098 pg0 = dms_rmap(UMAP+0);                                 /* read base page map# */
1099 pg1 = dms_rmap(UMAP+1);                                 /* save map# 1 */
1100 dms_wmap(UMAP+1,pg0);                                   /* map #0 into reg #1 */
1101 for (i=0; (base+i)<32; i++) {
1102     pg = i<e->npgs ? e->spmseg : 0140000;               /* write protect if outside */
1103     WriteIO(umaps+base+i, pg, UMAP);                    /* copy pg to user map */
1104 /* printf("MAP val %d to reg %d (addr=%o)\n",pg,base+i,umaps+base+i); */
1105     dms_wmap(UMAP+base+i, pg);                          /* set DMS reg */
1106     e->spmseg++;
1107 }
1108 dms_wmap(UMAP+1,pg1);                                   /* restore map #1 */
1109 
1110 xidex = ReadIO(idx,UMAP);                               /* get ID extension */
1111 idext0 = ReadWA(xidex+0);
1112 if (e->msegno == 0xffff)                                /* non std mseg */
1113     idext0 |= 0x8000;                                   /* set nonstd marker */
1114 else
1115     idext0 = (idext0 & 037) | (e->msegno<<5);           /* set new current mseg# */
1116 WriteIO(xidex, idext0, SMAP);                           /* save back value */
1117 AR = 0;                                                 /* was successful */
1118 return TRUE;
1119 }
1120 
cpu_ema_mmap02(EMA4 * e)1121 static t_bool cpu_ema_mmap02(EMA4* e)
1122 {
1123 uint32 xidex, eqt, idext1;
1124 uint32 mseg,phys,spmseg,emasz,msegsz,msegno;
1125 
1126 xidex = ReadIO(idx,UMAP);                               /* get ID extension */
1127 msegsz = ReadWA(xidex+0) & 037;                         /* P size of std MSEG */
1128 idext1 = ReadWA(xidex+1);
1129 mseg = (idext1 >> 1) & MSEGMASK;                        /* S9 get logical start MSEG */
1130 phys = idext1 & 01777;                                  /* S phys start of EMA */
1131 spmseg = phys + e->ipgs;                                /* S7 phys pg# of MSEG */
1132 msegno = e->ipgs / msegsz;
1133 if ((e->ipgs % msegsz) != 0)                            /* non std MSEG? */
1134     msegno = 0xffff;                                    /* S4 yes, set marker */
1135 if (e->npgs > msegsz) return FALSE;                     /* map more pages than MSEG sz? */
1136 eqt = ReadIO(xeqt,UMAP);
1137 emasz = ReadWA(eqt+28) & 01777;                         /* B EMA size in pages */
1138 if ((e->ipgs+e->npgs) > emasz) return FALSE;            /* outside EMA? */
1139 if ((e->ipgs+msegsz) > emasz)                           /* if MSEG overlaps end of EMA */
1140     e->npgs = emasz - e->ipgs;                          /* only map until end of EMA */
1141 
1142 e->emasz = emasz;                                       /* copy arguments */
1143 e->msegsz = msegsz;
1144 e->msegno = msegno;
1145 e->spmseg = spmseg;
1146 e->mseg = mseg;
1147 return cpu_ema_mmap01(e);
1148 }
1149 
cpu_ema_mmap(uint32 ipage,uint32 npgs,t_bool debug)1150 static t_stat cpu_ema_mmap(uint32 ipage,uint32 npgs, t_bool debug)
1151 {
1152 uint32 xidex;
1153 EMA4 ema4, *e = &ema4;
1154 
1155 e->ipgs = ipage;                                        /* S6 set the arguments */
1156 e->npgs = npgs;                                         /* S5 */
1157 
1158 AR = 0;
1159 xidex = ReadIO(idx,UMAP);
1160 if ((ipage & SIGN) ||                                   /* negative page displacement? */
1161     (npgs & SIGN) ||                                    /* negative # of pages? */
1162     xidex == 0 ||                                       /* no EMA? */
1163     !cpu_ema_mmap02(e))                                 /* mapping failed? */
1164     AR = 0177777;                                       /* return with error */
1165 return SCPE_OK;                                         /* leave */
1166 }
1167 
cpu_ema_emat(EMA4 * e)1168 static t_bool cpu_ema_emat(EMA4* e)
1169 {
1170 uint32 xidex,idext0;
1171 uint32 curmseg,phys,msnum,lastpgs;
1172 
1173 xidex = ReadIO(idx,UMAP);                               /* read ID extension */
1174 idext0 = ReadWA(xidex+0);                               /* get current segment */
1175 curmseg = idext0 >> 5;
1176 if ((idext0 & 0100000) ||                               /* was nonstd MSEG? */
1177     curmseg != e->msegno) {                             /* or different MSEG last time? */
1178     phys = ReadWA(xidex+1) & 01777;                     /* physical start pg of EMA */
1179     e->spmseg = phys + e->ipgs;                         /* physical start pg of MSEG */
1180     msnum = e->emasz / e->msegsz;                       /* find last MSEG# */
1181     lastpgs = e->emasz % e->msegsz;                     /* #pgs in last MSEG */
1182     if (lastpgs==0) msnum--;                            /* adjust # of last MSEG */
1183     e->npgs = msnum==e->msegno ? lastpgs : e->msegsz;   /* for last MSEG, only map available pgs */
1184     if (!cpu_ema_mmap01(e)) return FALSE;               /* map npgs pages at ipgs */
1185 }
1186 BR = e->mseg + e->msoff;                                /* return address of element */
1187 return TRUE;                                            /* and everything done */
1188 }
1189 
1190 /* .EMIO microcode routine, resolves element addr for EMA array
1191  * and maps the appropriate map segment
1192  *
1193  *  Call:
1194  *    OCT 105250B
1195  *    DEF RTN          error return (rtn), good return is rtn+1
1196  *    DEF BUFLEN       length of buffer in words (bufl)
1197  *    DEF TABLE[,I]    array declaration (dtbl)
1198  *    DEF A(N)[,I]     actual subscripts (atbl)
1199  *    DEF A(N-1)[,I]
1200  *    ...
1201  *    DEF A(2)[,I]
1202  *    DEF A(1)[,I]
1203  *  RTN EQU *          error return A="15", B="EM"
1204  *  RTN+1 EQU *+1      good return B=logical address
1205  *
1206  *  TABLE DEC #        # dimensions
1207  *        DEC -L(N)
1208  *        DEC D(N-1)
1209  *        DEC -L(N-1)  lower bound (n-1)st dim
1210  *        DEC D(N-2)   (n-2)st dim
1211  *        ...
1212  *        DEC D(1)     1st dim
1213  *        DEC -L(1)    lower bound 1st dim
1214  *        DEC #        # words/element
1215  *        OFFSET 1     EMA Low
1216  *        OFFSET 2     EMA High
1217  */
cpu_ema_emio(uint32 * rtn,uint32 bufl,uint32 dtbl,uint32 atbl,t_bool debug)1218 static t_stat cpu_ema_emio(uint32* rtn,uint32 bufl,uint32 dtbl,uint32 atbl,t_bool debug)
1219 {
1220 uint32 xidex, idext1;
1221 uint32 mseg, bufpgs, npgs;
1222 EMA4 ema4, *e = &ema4;
1223 
1224 xidex = ReadIO(idx,UMAP);                               /* read ID extension */
1225 if (bufl & SIGN ||                                      /* buffer length negative? */
1226     xidex==0) goto em16;                                /* no EMA declared? */
1227 
1228 idext1 = ReadWA(xidex+1);                               /* |logstrt mseg|d|physstrt ema| */
1229 mseg = (idext1 >> 1) & MSEGMASK;                        /* get logical start MSEG */
1230 if (!cpu_ema_emas(dtbl,atbl,e)) goto em16;              /* resolve address */
1231 bufpgs = (bufl + e->offs) >> 10;                        /* # of pgs reqd for buffer */
1232 if ((bufl + e->offs) & 01777) bufpgs++;                 /* S11 add 1 if not at pg boundary */
1233 if ((bufpgs + e->pgoff) > e->emasz) goto em16;          /* exceeds EMA limit? */
1234 npgs = (e->msoff + bufl) >> 10;                         /* # of pgs reqd for MSEG */
1235 if ((e->msoff + bufl) & 01777) npgs++;                  /* add 1 if not at pg boundary */
1236 if (npgs < e->msegsz) {
1237     e->mseg = mseg;                                     /* logical stat of MSEG */
1238     if (!cpu_ema_emat(e)) goto em16;                    /* do a std mapping */
1239 } else {
1240     BR = mseg + e->offs;                                /* logical start of buffer */
1241     e->npgs = bufpgs;                                   /* S5 # pgs required */
1242     e->ipgs = e->pgoff;                                 /* S6 page offset to reqd pg */
1243     if (!cpu_ema_mmap02(e)) goto em16;                  /* do nonstd mapping */
1244 }
1245 (*rtn)++;                                               /* return via good exit */
1246 return SCPE_OK;
1247 
1248 em16:                                                   /* error condition */
1249 AR=0x3136;                                              /* AR = '16' */
1250 BR=0x454d;                                              /* BR = 'EM' */
1251 return SCPE_OK;                                         /* return via unmodified rtn */
1252 }
1253 
1254 /* .EMAP microcode routine, resolves both EMA/non-EMA calls
1255  *  Call:
1256  *    OCT 105257B
1257  *    DEF RTN          error return (rtn), good return is rtn+1
1258  *    DEF ARRAY[,I]    array base (abase)
1259  *    DEF TABLE[,I]    array declaration (dtbl)
1260  *    DEF A(N)[,I]     actual subscripts (atbl)
1261  *    DEF A(N-1)[,I]
1262  *    ...
1263  *    DEF A(2)[,I]
1264  *    DEF A(1)[,I]
1265  *  RTN EQU *          error return A="15", B="EM"
1266  *  RTN+1 EQU *+1      good return B=logical address
1267  *
1268  *  TABLE DEC #        # dimensions
1269  *        DEC -L(N)
1270  *        DEC D(N-1)
1271  *        DEC -L(N-1)  lower bound (n-1)st dim
1272  *        DEC D(N-2)   (n-2)st dim
1273  *        ...
1274  *        DEC D(1)     1st dim
1275  *        DEC -L(1)    lower bound 1st dim
1276  *        DEC #        # words/element
1277  *        OFFSET 1     EMA Low
1278  *        OFFSET 2     EMA High
1279  */
cpu_ema_emap(uint32 * rtn,uint32 abase,uint32 dtbl,uint32 atbl,t_bool debug)1280 static t_stat cpu_ema_emap(uint32* rtn,uint32 abase,uint32 dtbl,uint32 atbl,t_bool debug)
1281 {
1282 uint32 xidex, eqt, idext0, idext1;
1283 int32 sub, act, low, ndim, sz;
1284 uint32 offs, pgoff, emasz, phys, msgn, mseg, sum, MA, pg0, pg1;
1285 
1286 xidex = ReadIO(idx,UMAP);                               /* read ID Extension */
1287 if (xidex) {                                            /* is EMA declared? */
1288     idext1 = ReadWA(xidex+1);                           /* get word 1 of idext */
1289     mseg = (idext1 >> 1) & MSEGMASK;                    /* get logical start MSEG */
1290     if (abase >= mseg) {                                /* EMA reference? */
1291         if (!cpu_ema_resolve(dtbl,atbl,&sum))           /* calculate subscript */
1292             goto em15;
1293         offs = sum & 01777;                             /* address offset within page */
1294         pgoff = sum >> 10;                              /* ema offset in pages */
1295         if (pgoff > 1023) goto em15;                    /* overflow? */
1296         eqt = ReadIO(xeqt,UMAP);
1297         emasz = ReadWA(eqt+28) & 01777;                 /* EMA size in pages */
1298         phys = idext1 & 01777;                          /* physical start pg of EMA */
1299         if (pgoff > emasz) goto em15;                   /* outside EMA range? */
1300 
1301         msgn = mseg >> 10;                              /* get # of 1st MSEG reg */
1302         phys += pgoff;
1303 
1304         pg0 = dms_rmap(UMAP+0);                         /* read base page map# */
1305         pg1 = dms_rmap(UMAP+1);                         /* save map# 1 */
1306         dms_wmap(UMAP+1,pg0);                           /* map #0 into reg #1 */
1307 
1308         WriteIO(umaps+msgn, phys, UMAP);                /* store 1st mapped pg in user map */
1309         dms_wmap(UMAP+msgn, phys);                      /* and set the map register */
1310         phys = (pgoff+1)==emasz ? 0140000 : phys+1;     /* protect 2nd map if end of EMA */
1311         WriteIO(umaps+msgn+1, phys, UMAP);              /* store 2nd mapped pg in user map */
1312         dms_wmap(UMAP+msgn+1, phys);                    /* and set the map register */
1313 
1314         dms_wmap(UMAP+1,pg1);                           /* restore map #1 */
1315 
1316         idext0 = ReadWA(xidex+0) | 0100000;             /* set NS flag in id extension */
1317         WriteIO(xidex+0, idext0, SMAP);                 /* save back value */
1318         AR = 0;                                         /* was successful */
1319         BR = mseg + offs;                               /* calculate log address */
1320         (*rtn)++;                                       /* return via good exit */
1321         return SCPE_OK;
1322     }
1323 }                                                       /* not EMA reference */
1324 ndim = ReadW(dtbl++);
1325 if (ndim<0) goto em15;                                  /* negative �dimensions */
1326 sum = 0;                                                /* accu for index calc */
1327 while (ndim > 0) {
1328     MA = ReadW(atbl++);                                 /* get address of A(N) */
1329     resolve (MA, &MA, 0);
1330     act = ReadW(MA);                                    /* A(N) */
1331     low = ReadW(dtbl++);                                /* -L(N) */
1332     sub = SEXT(act) + SEXT(low);                        /* subscript */
1333     if (sub & 0xffff8000) goto em15;                    /* overflow? */
1334     sum += sub;                                         /* accumulate */
1335     sz = ReadW(dtbl++);
1336     sz = SEXT(sz);
1337     if (sz < 0) goto em15;
1338     sum *= sz;                                          /* and multiply with sz of dimension */
1339     if (sum & 0xffff8000) goto em15;                    /* overflow? */
1340     ndim--;
1341 }
1342 BR = abase + sum;                                       /* add displacement */
1343 (*rtn)++;                                               /* return via good exit */
1344 return SCPE_OK;
1345 
1346 em15:                                                   /* error condition */
1347   AR=0x3135;                                            /* AR = '15' */
1348   BR=0x454d;                                            /* BR = 'EM' */
1349   return SCPE_OK;                                       /* return via unmodified e->rtn */
1350 }
1351 
cpu_rte_ema(uint32 IR,uint32 intrq)1352 t_stat cpu_rte_ema (uint32 IR, uint32 intrq)
1353 {
1354 t_stat reason = SCPE_OK;
1355 OPS op;
1356 OP_PAT pattern;
1357 uint32 entry, rtn;
1358 t_bool debug = DEBUG_PRI (cpu_dev, DEB_EMA);
1359 
1360 entry = IR & 017;                                       /* mask to entry point */
1361 pattern = op_ema[entry];                                /* get operand pattern */
1362 
1363 if (pattern != OP_N)
1364     if ((reason = cpu_ops (pattern, op, intrq)))        /* get instruction operands */
1365         return reason;
1366 
1367 if (debug) {                                            /* debugging? */
1368     fprintf (sim_deb, ">>CPU EMA: PC = %06o, IR = %06o (", err_PC,IR);    /* print preamble and IR */
1369     fprint_sym (sim_deb, err_PC, (t_value *) &IR,       /* print instruction mnemonic */
1370                 NULL, SWMASK('M'));
1371     fputc (')', sim_deb);
1372 
1373     fprint_ops (pattern, op);                           /* print operands */
1374     fputc ('\n', sim_deb);                              /* terminate line */
1375     }
1376 
1377 switch (entry) {                                        /* decode IR<3:0> */
1378     case 000:                                           /* .EMIO 105240 (OP_A) */
1379         rtn = op[0].word;
1380         reason = cpu_ema_emio(&rtn, op[1].word,
1381                               op[2].word, PC, debug);   /* handle the EMIO instruction */
1382         PC = rtn;
1383         if (debug)
1384             fprintf (sim_deb, ">>CPU EMA: return .EMIO: AR = %06o, BR = %06o, rtn=%s\n",
1385                      AR, BR, PC==op[0].word?"error":"good");
1386         break;
1387 
1388     case 001:                                           /* .MMAP  105241 (OP_AKK) */
1389         reason = cpu_ema_mmap(op[1].word,
1390                               op[2].word, debug);       /* handle the MMAP instruction */
1391         if (debug)
1392             fprintf (sim_deb, ">>CPU EMA: return .MMAP: AR = %06o\n",AR);
1393         break;
1394 
1395     case 002:                                           /* [test] 105242 (OP_N) */
1396         /* effectively, this code just returns without error:
1397          * real microcode will set S register to 102077B when in single step mode */
1398         if (sim_step==1) {
1399             if (debug)
1400                 fprintf(sim_deb, ">>CPU EMA: EMA option 92067 correctly installed: S=102077\n");
1401             SR = 0102077;
1402         }
1403         break;
1404 
1405     case 017:                                           /* .EMAP  105247 (OP_A) */
1406         rtn = op[0].word;                               /* error return */
1407         reason = cpu_ema_emap(&rtn, op[1].word,
1408                               op[2].word, PC, debug);   /* handle the EMAP instruction */
1409         PC = rtn;
1410         if (debug) {
1411             fprintf (sim_deb, ">>CPU EMA: return .EMAP: AR = %06o, BR = %06o, rtn=%s\n",
1412                      AR, BR, PC==op[0].word?"error":"good");
1413         }
1414         break;
1415 
1416     default:                                            /* others undefined */
1417         reason = stop_inst;
1418     }
1419 
1420 return reason;
1421 }
1422