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