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