7 .ta \nxu +\nxu +\nxu +\nxu +\nxu +\nxu +\nxu +\nxu +\nxu +\nxu
9 { This is an interpreter for EM. It serves as the official machine
10 definition. This interpreter must run on a machine which supports
11 arithmetic with words and memory offsets.
13 Certain aspects of the definition are over specified. In particular:
15 1. The representation of an address on the stack need not be the
16 numerical value of the memory location.
18 2. The state of the stack is not defined after a trap has aborted
19 an instruction in the middle. For example, it is officially un-
20 defined whether the second operand of an ADD instruction has
21 been popped or not if the first one is undefined ( -32768 or
24 3. The memory layout is implementation dependent. Only the most
25 basic checks are performed whenever memory is accessed.
27 4. The representation of an integer or set on the stack is not fixed
30 5. The format and existence of the procedure descriptors depends on
33 6. The result of the compare operators CMI etc. are -1, 0 and 1
34 here, but other negative and positive values will do and they
35 need not be the same each time.
37 7. The shift count for SHL, SHR, ROL and ROR must be in the range 0
38 to object size in bits - 1. The effect of a count not in this
43 program em(tables,prog,input,output);
48 t15 = 32768; { 2**15 }
49 t15m1 = 32767; { 2**15 -1 }
50 t16 = 65536; { 2**16 }
51 t16m1 = 65535; { 2**16 -1 }
52 t31m1 = 2147483647; { 2**31 -1 }
54 wsize = 2; { number of bytes in a word }
55 asize = 2; { number of bytes in an address }
56 fsize = 4; { number of bytes in a floating point number }
57 maxret =4; { number of words in the return value area }
59 signbit = t15; { the power of two indicating the sign bit }
60 negoff = t16; { the next power of two }
61 maxsint = t15m1; { the maximum signed integer }
62 maxuint = t16m1; { the maximum unsigned integer }
63 maxdbl = t31m1; { the maximum double signed integer }
64 maxadr = t16m1; { the maximum address }
65 maxoffs = t15m1; { the maximum offset from an address }
66 maxbitnr= 15; { the number of the highest bit }
68 lineadr = 0; { address of the line number }
69 fileadr = 4; { address of the file name }
70 maxcode = 8191; { highest byte in code address space }
71 maxdata = 8191; { highest byte in data address space }
73 { format of status save area }
74 statd = 4; { how far is static link from lb }
75 dynd = 2; { how far is dynamic link from lb }
76 reta = 0; { how far is the return address from lb }
77 savsize = 4; { size of save area in bytes }
79 { procedure descriptor format }
80 pdlocs = 0; { offset for size of local variables in bytes }
81 pdbase = asize; { offset for the procedure base }
82 pdsize = 4; { size of procedure descriptor in bytes = 2*asize }
92 escape1 = 254; { escape to secondary opcodes }
93 escape2 = 255; { escape to tertiary opcodes }
94 undef = signbit; { the range of integers is -32767 to +32767 }
97 EARRAY = 0; ERANGE = 1; ESET = 2; EIOVFL = 3; EFOVFL = 4;
98 EFUNFL = 5; EIDIVZ = 6; EFDIVZ = 7; EIUND = 8; EFUND = 9;
99 ECONV = 10; ESTACK = 16; EHEAP = 17; EILLINS = 18; EODDZ = 19;
100 ECASE = 20; EMEMFLT = 21; EBADPTR = 22; EBADPC = 23; EBADLAE = 24;
101 EBADMON = 25; EBADLIN = 26; EBADGTO = 27;
104 {---------------------------------------------------------------------------}
106 {---------------------------------------------------------------------------}
109 bitval= 0..1; { one bit }
110 bitnr= 0..maxbitnr; { bits in machine words are numbered 0 to 15 }
111 byte= 0..255; { memory is an array of bytes }
112 adr= {0..maxadr} long; { the range of addresses }
113 word= {0..maxuint} long;{ the range of unsigned integers }
114 offs= -maxoffs..maxoffs; { the range of signed offsets from addresses }
115 size= 0..maxoffs; { the range of sizes is the positive offsets }
116 sword= {-signbit..maxsint} long; { the range of signed integers }
117 full= {-maxuint..maxuint} long; { intermediate results need this range }
118 double={-maxdbl..maxdbl} long; { double precision range }
119 bftype= (andf,iorf,xorf); { tells which boolean operator needed }
120 insclass=(prim,second,tert); { tells which opcode table is in use }
121 instype=(implic,explic); { does opcode have implicit or explicit operand }
122 iflags= (mini,short,sbit,wbit,zbit,ibit);
123 ifset= set of iflags;
126 AAR, ADF, ADI, ADP, ADS, ADU,XAND, ASP, ASS, BEQ,
127 BGE, BGT, BLE, BLM, BLS, BLT, BNE, BRA, CAI, CAL,
128 CFF, CFI, CFU, CIF, CII, CIU, CMF, CMI, CMP, CMS,
129 CMU, COM, CSA, CSB, CUF, CUI, CUU, DCH, DEC, DEE,
130 DEL, DUP, DUS, DVF, DVI, DVU, EXG, FEF, FIF, FIL,
131 GTO, INC, INE, INL, INN, IOR, LAE, LAL, LAR, LDC,
132 LDE, LDF, LDL, LFR, LIL, LIM, LIN, LNI, LOC, LOE,
133 LOF, LOI, LOL, LOR, LOS, LPB, LPI, LXA, LXL, MLF,
134 MLI, MLU, MON, NGF, NGI, NOP, RCK, RET, RMI, RMU,
135 ROL, ROR, RTT, SAR, SBF, SBI, SBS, SBU, SDE, SDF,
136 SDL,XSET, SIG, SIL, SIM, SLI, SLU, SRI, SRU, STE,
137 STF, STI, STL, STR, STS, TEQ, TGE, TGT, TLE, TLT,
138 TNE, TRP, XOR, ZEQ, ZER, ZGE, ZGT, ZLE, ZLT, ZNE,
145 implic: (implicit:sword);
146 explic: (ilength:byte);
151 code: packed array[0..maxcode] of byte; { code space }
152 data: packed array[0..maxdata] of byte; { data space }
153 retarea: array[1..maxret ] of word; { return area }
154 pc,lb,sp,hp,pd: adr; { internal machine registers }
155 i: integer; { integer scratch variable }
156 s,t :word; { scratch variables }
157 sz:size; { scratch variables }
158 ss,st: sword; { scratch variables }
159 k :double; { scratch variables }
160 j:size; { scratch variable used as index }
161 a,b:adr; { scratch variable used for addresses }
162 dt,ds:double; { scratch variables for double precision }
163 rt,rs,x,y:real; { scratch variables for real }
164 found:boolean; { scratch }
165 opcode: byte; { holds the opcode during execution }
166 iclass: insclass; { true for escaped opcodes }
167 dispat: array[insclass,byte] of dispatch;
168 retsize:size; { holds size of last LFR }
169 insr: mnem; { holds the instruction number }
170 halted: boolean; { normally false }
171 exitstatus:word; { parameter of MON 1 }
172 ignmask:word; { ignore mask for traps }
173 uerrorproc:adr; { number of user defined error procedure }
174 intrap:boolean; { Set when executing trap(), to catch recursive calls}
175 trapval:byte; { Set to number of last trap }
176 header: array[1..8] of adr;
178 tables: text; { description of EM instructions }
179 prog: file of byte; { program and initialized data }
182 {---------------------------------------------------------------------------}
183 { Various check routines }
184 {---------------------------------------------------------------------------}
186 { Only the most basic checks are performed. These routines are inherently
187 implementation dependent. }
189 procedure trap(n:byte); forward;
191 procedure memadr(a:adr);
192 begin if (a>maxdata) or ((a<sp) and (a>=hp)) then trap(EMEMFLT) end;
194 procedure wordadr(a:adr);
195 begin memadr(a); if (a mod wsize<>0) then trap(EBADPTR) end;
197 procedure chkadr(a:adr; s:size);
198 begin memadr(a); memadr(a+s-1); { assumption: size is ok }
200 then begin if a mod s<>0 then trap(EBADPTR) end
201 else if a mod wsize<>0 then trap(EBADPTR)
204 procedure newpc(a:double);
205 begin if (a<0) or (a>maxcode) then trap(EBADPC); pc:=a end;
207 procedure newsp(a:adr);
208 begin if (a>lb) or (a<hp) or (a mod wsize<>0) then trap(ESTACK); sp:=a end;
210 procedure newlb(a:adr);
211 begin if (a<sp) or (a mod wsize<>0) then trap(ESTACK); lb:=a end;
213 procedure newhp(a:adr);
214 begin if (a>sp) or (a>maxdata+1) or (a mod wsize<>0)
219 function argc(a:double):sword;
220 begin if (a<-signbit) or (a>maxsint) then trap(EILLINS); argc:=a end;
222 function argd(a:double):double;
223 begin if (a<-maxdbl) or (a>maxdbl) then trap(EILLINS); argd:=a end;
225 function argl(a:double):offs;
226 begin if (a<-maxoffs) or (a>maxoffs) then trap(EILLINS); argl:=a end;
228 function argg(k:double):adr;
229 begin if (k<0) or (k>maxadr) then trap(EILLINS); argg:=k end;
231 function argf(a:double):offs;
232 begin if (a<-maxoffs) or (a>maxoffs) then trap(EILLINS); argf:=a end;
234 function argn(a:double):word;
235 begin if (a<0) or (a>maxuint) then trap(EILLINS); argn:=a end;
237 function args(a:double):size;
238 begin if (a<=0) or (a>maxoffs)
240 else if (a mod wsize)<>0 then trap(EODDZ);
244 function argz(a:double):size;
245 begin if (a<0) or (a>maxoffs)
247 else if (a mod wsize)<>0 then trap(EODDZ);
251 function argo(a:double):size;
252 begin if (a<=0) or (a>maxoffs)
254 else if (a mod wsize<>0) and (wsize mod a<>0) then trap(EODDZ);
258 function argw(a:double):size;
259 begin if (a<=0) or (a>maxoffs) or (a>maxuint)
261 else if (a mod wsize)<>0 then trap(EODDZ);
265 function argp(a:double):size;
266 begin if (a<0) or (a>=header[NPROC]) then trap(EILLINS); argp:=a end;
268 function argr(a:double):word;
269 begin if (a<0) or (a>2) then trap(EILLINS); argr:=a end;
271 procedure argwf(s:double);
272 begin if argw(s)<>fsize then trap(EILLINS) end;
274 function szindex(s:double):integer;
275 begin s:=argw(s); if (s mod wsize <> 0) or (s>2*wsize) then trap(EILLINS);
279 function locadr(l:double):adr;
280 begin l:=argl(l); if l<0 then locadr:=lb+l else locadr:=lb+l+savsize end;
282 function signwd(w:word):sword;
283 begin if w = undef then trap(EIUND);
284 if w >= signbit then signwd:=w-negoff else signwd:=w
287 function dosign(w:word):sword;
288 begin if w >= signbit then dosign:=w-negoff else dosign:=w end;
290 function unsign(w:sword):word;
291 begin if w<0 then unsign:=w+negoff else unsign:=w end;
293 function chopw(dw:double):word;
294 begin chopw:=dw mod negoff end;
296 function fitsw(w:full;trapno:byte):word;
297 { checks whether value fits in signed word, returns unsigned representation}
299 if (w>maxsint) or (w<-signbit) then
301 if w<0 then fitsw:=negoff- (-w)mod negoff
302 else fitsw:=w mod negoff;
304 else fitsw:=unsign(w)
307 function fitd(w:full):double;
309 if abs(w) > maxdbl then trap(ECONV);
314 {---------------------------------------------------------------------------}
315 { Memory access routines }
316 {---------------------------------------------------------------------------}
318 { memw returns a machine word as an unsigned integer
319 memb returns a single byte as a positive integer: 0 <= memb <= 255
320 mems(a,s) fetches an object smaller than a word and returns a word
321 store(a,v) stores the word v at machine address a
322 storea(a,v) stores the address v at machine address a
323 storeb(a,b) stores the byte b at machine address a
324 stores(a,s,v) stores the s least significant bytes of a word at address a
325 memi returns an offset from the instruction space
326 Note that the procedure descriptors are part of instruction space.
327 nextpc returns the next byte addressed by pc, incrementing pc
329 lino changes the line number word.
330 filna changes the pointer to the file name.
332 All routines check to make sure the address is within range and valid for
333 the size of the object. If an addressing error is found, a trap occurs.
337 function memw(a:adr):word;
338 var b:word; i:integer;
339 begin wordadr(a); b:=0;
340 for i:=wsize-1 downto 0 do b:=256*b + data[a+i] ;
344 function memd(a:adr):double; { Always signed }
345 var b:double; i:integer;
346 begin wordadr(a); b:=data[a+2*wsize-1];
347 if b>=128 then b:=b-256;
348 for i:=2*wsize-2 downto 0 do b:=256*b + data[a+i] ;
352 function mema(a:adr):adr;
353 var b:adr; i:integer;
354 begin wordadr(a); b:=0;
355 for i:=asize-1 downto 0 do b:=256*b + data[a+i] ;
359 function mems(a:adr;s:size):word;
360 var i:integer; b:word;
361 begin chkadr(a,s); b:=0; for i:=1 to s do b:=b*256+data[a+s-i]; mems:=b end;
363 function memb(a:adr):byte;
364 begin memadr(a); memb:=data[a] end;
366 procedure store(a:adr; x:word);
369 for i:=0 to wsize-1 do
370 begin data[a+i]:=x mod 256; x:=x div 256 end
373 procedure storea(a:adr; x:adr);
376 for i:=0 to asize-1 do
377 begin data[a+i]:=x mod 256; x:=x div 256 end
380 procedure stores(a:adr;s:size;v:word);
383 for i:=0 to s-1 do begin data[a+i]:=v mod 256; v:=v div 256 end;
386 procedure storeb(a:adr; b:byte);
387 begin memadr(a); data[a]:=b end;
389 function memi(a:adr):adr;
390 var b:adr; i:integer;
391 begin if (a mod wsize<>0) or (a+asize-1>maxcode) then trap(EBADPTR); b:=0;
392 for i:=asize-1 downto 0 do b:=256*b + code[a+i] ;
396 function nextpc:byte;
397 begin if pc>=pd then trap(EBADPC); nextpc:=code[pc]; newpc(pc+1) end;
399 procedure lino(w:word);
400 begin store(lineadr,w) end;
402 procedure filna(a:adr);
403 begin storea(fileadr,a) end;
406 {---------------------------------------------------------------------------}
407 { Stack Manipulation Routines }
408 {---------------------------------------------------------------------------}
410 { push puts a word on the stack
411 pushsw takes a signed one word integer and pushes it on the stack
412 pop removes a machine word from the stack and delivers it as a word
413 popsw removes a machine word from the stack and delivers a signed integer
414 pusha pushes an address on the stack
415 popa removes a machine word from the stack and delivers it as an address
416 pushd pushes a double precision number on the stack
417 popd removes two machine words and returns a double precision integer
418 pushr pushes a float (floating point) number on the stack
419 popr removes several machine words and returns a float number
420 pushx puts an object of arbitrary size on the stack
421 popx removes an object of arbitrary size
424 procedure push(x:word);
425 begin newsp(sp-wsize); store(sp,x) end;
427 procedure pushsw(x:sword);
428 begin newsp(sp-wsize); store(sp,unsign(x)) end;
431 begin pop:=memw(sp); newsp(sp+wsize) end;
433 function popsw:sword;
434 begin popsw:=signwd(pop) end;
436 procedure pusha(x:adr);
437 begin newsp(sp-asize); storea(sp,x) end;
440 begin popa:=mema(sp); newsp(sp+asize) end;
442 procedure pushd(y:double);
443 begin { push double integer onto the stack } newsp(sp-2*wsize) end;
445 function popd:double;
446 begin { pop double integer from the stack } newsp(sp+2*wsize); popd:=0 end;
448 procedure pushr(z:real);
449 begin { Push a float onto the stack } newsp(sp-fsize) end;
452 begin { pop float from the stack } newsp(sp+fsize); popr:=0.0 end;
454 procedure pushx(objsize:size; a:adr);
458 then push(mems(a,objsize))
459 else for i:=1 to objsize div wsize do push(memw(a+objsize-wsize*i))
462 procedure popx(objsize:size; a:adr);
466 then stores(a,objsize,pop)
467 else for i:=1 to objsize div wsize do store(a-wsize+wsize*i,pop)
471 {---------------------------------------------------------------------------}
472 { Bit manipulation routines (extract, shift, rotate) }
473 {---------------------------------------------------------------------------}
475 procedure sleft(var w:sword); { 1 bit left shift }
476 begin w:= dosign(fitsw(2*w,EIOVFL)) end;
478 procedure suleft(var w:word); { 1 bit left shift }
479 begin w := chopw(2*w) end;
481 procedure sdleft(var d:double); { 1 bit left shift }
482 begin { shift two word signed integer } end;
484 procedure sright(var w:sword); { 1 bit right shift with sign extension }
485 begin if w >= 0 then w := w div 2 else w := (w-1) div 2 end;
487 procedure suright(var w:word); { 1 bit right shift without sign extension }
488 begin w := w div 2 end;
490 procedure sdright(var d:double); { 1 bit right shift }
491 begin { shift two word signed integer } end;
493 procedure rleft(var w:word); { 1 bit left rotate }
495 then w:=(w-t15)*2 + 1
499 procedure rright(var w:word); { 1 bit right rotate }
501 then w:=w div 2 + t15
505 function sextend(w:word;s:size):word;
508 for i:=1 to (wsize-s)*8 do rleft(w);
509 for i:=1 to (wsize-s)*8 do sright(w);
513 function bit(b:bitnr; w:word):bitval; { return bit b of the word w }
515 begin for i:= 1 to b do rright(w); bit:= w mod 2 end;
517 function bf(ty:bftype; w1,w2:word):word; { return boolean fcn of 2 words }
520 for i:= maxbitnr downto 0 do
523 andf: if bit(i,w1)+bit(i,w2) = 2 then j:=j+1;
524 iorf: if bit(i,w1)+bit(i,w2) > 0 then j:=j+1;
525 xorf: if bit(i,w1)+bit(i,w2) = 1 then j:=j+1
531 {---------------------------------------------------------------------------}
533 {---------------------------------------------------------------------------}
535 function arraycalc(c:adr):adr; { subscript calculation }
536 var j:full; objsize:size; a:adr;
537 begin j:= popsw - signwd(memw(c));
538 if (j<0) or (j>memw(c+wsize)) then trap(EARRAY);
539 objsize := argo(memw(c+wsize+wsize));
540 a := j*objsize+popa; chkadr(a,objsize);
545 {---------------------------------------------------------------------------}
546 { Double and Real Arithmetic }
547 {---------------------------------------------------------------------------}
549 { All routines for doubles and floats are dummy routines, since the format of
550 doubles and floats is not defined in EM.
553 function doadi(ds,dt:double):double;
554 begin { add two doubles } doadi:=0 end;
556 function dosbi(ds,dt:double):double;
557 begin { subtract two doubles } dosbi:=0 end;
559 function domli(ds,dt:double):double;
560 begin { multiply two doubles } domli:=0 end;
562 function dodvi(ds,dt:double):double;
563 begin { divide two doubles } dodvi:=0 end;
565 function dormi(ds,dt:double):double;
566 begin { modulo of two doubles } dormi:=0 end;
568 function dongi(ds:double):double;
569 begin { negative of a double } dongi:=0 end;
571 function doadf(x,y:real):real;
572 begin { add two floats } doadf:=0.0 end;
574 function dosbf(x,y:real):real;
575 begin { subtract two floats } dosbf:=0.0 end;
577 function domlf(x,y:real):real;
578 begin { multiply two floats } domlf:=0.0 end;
580 function dodvf(x,y:real):real;
581 begin { divide two floats } dodvf:=0.0 end;
583 function dongf(x:real):real;
584 begin { negate a float } dongf:=0.0 end;
586 procedure dofif(x,y:real;var intpart,fraction:real);
587 begin { dismember x*y into integer and fractional parts }
588 intpart:=0.0; { integer part of x*y, same sign as x*y }
590 { fractional part of x*y, 0<=abs(fraction)<1 and same sign as x*y }
593 procedure dofef(x:real;var mantissa:real;var exponent:sword);
594 begin { dismember x into mantissa and exponent parts }
595 mantissa:=0.0; { mantissa of x , >= 1/2 and <1 }
596 exponent:=0; { base 2 exponent of x }
599 {---------------------------------------------------------------------------}
601 {---------------------------------------------------------------------------}
603 procedure call(p:adr); { Perform the call }
606 newlb(sp);newsp(sp - memi(pd + pdsize*p + pdlocs));
607 newpc(memi(pd + pdsize*p+ pdbase))
610 procedure dotrap(n:byte);
613 if (uerrorproc=0) or intrap then
616 writeln('Recursive trap, first trap number was ', trapval:1);
617 writeln('Error ', n:1);
618 writeln('With',ord(insr):4,' arg ',k:1);
621 { Deposit all interpreter variables that need to be saved on
622 the stack. This includes all scratch variables that can
623 be in use at the moment and ( not possible in this interpreter )
624 the internal address of the interpreter where the error occurred.
625 This would make it possible to execute an RTT instruction totally
626 transparent to the user program.
627 It can, for example, occur within an ADD instruction that both
628 operands are undefined and that the result overflows.
629 Although this will generate 3 error traps it must be possible
632 intrap:=true; trapval:=n;
633 for i:=retsize div wsize downto 1 do push(retarea[i]);
634 push(retsize); { saved return area }
635 pusha(mema(fileadr)); { saved current file name pointer }
636 push(memw(lineadr)); { saved line number }
637 push(n); { push error number }
639 uerrorproc:=0; { reset signal }
640 call(a); { call the routine }
641 intrap:=false; { Don't catch recursive traps anymore }
642 goto 8888; { reenter main loop }
646 { This routine is invoked for overflow, and other run time errors.
647 For non-fatal errors, trap returns to the calling routine
650 if n>=16 then dotrap(n) else if bit(n,ignmask)=0 then dotrap(n);
654 { The restoration of file address and line number is not essential.
655 The restoration of the return save area is.
660 newsp(lb); lb:=maxdata+1 ; { to circumvent ESTACK for the popa + pop }
661 newpc(popa); newlb(popa); { So far a plain RET 0 }
662 n:=pop; if (n>=16) and (n<64) then goto 9999 ;
663 lino(pop); filna(popa); retsize:=pop;
664 for i:=1 to retsize div wsize do retarea[i]:=pop ;
667 {---------------------------------------------------------------------------}
669 {---------------------------------------------------------------------------}
672 procedure domon(entry:word);
679 if (entry<=0) or (entry>63) then entry:=63 ;
682 1: begin { exit } exitstatus:=pop; halted:=true end;
683 3: begin { read } dummy:=pop; { All input is from stdin }
684 rwptr:=popa; count:=popa;
686 while (not eof(input)) and (i<count) do
688 if eoln(input) then begin storeb(rwptr,10) ; count:=i end
689 else storeb(rwptr,ord(input^)) ;
690 get(input); rwptr:=rwptr+1 ; i:=i+1 ;
694 4: begin { write } dummy:=pop; { All output is to stdout }
695 rwptr:=popa; count:=popa;
697 begin token:=memb(rwptr); rwptr:=rwptr+1 ;
698 if token=10 then writeln else write(chr(token))
703 54: begin { ioctl, faked } dummy:=popa;dummy:=popa;dummy:=pop;push(0) end ;
704 2, 5, 6, 7, 8, 9, 10,
705 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
706 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
707 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
708 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
709 51, 52, 53, 55, 56, 57, 58, 59, 60,
711 begin push(22); push(22) end;
712 63: { exists only for the trap }
717 {---------------------------------------------------------------------------}
718 { Initialization and debugging }
719 {---------------------------------------------------------------------------}
721 procedure doident; { print line number and file name }
722 var a:adr; i,c:integer; found:boolean;
724 write('at line ',memw(lineadr):1,' ');
725 a:=mema(fileadr); if a<>0 then
726 begin i:=20; found:=false;
727 while (i<>0) and not found do
728 begin c:=memb(a); a:=a+1; found:=true; i:=i-1;
729 if (c>=48) and (c<=57) then
730 begin found:=false; write(chr(ord('0')+c-48)) end;
731 if (c>=65) and (c<=90) then
732 begin found:=false; write(chr(ord('A')+c-65)) end;
733 if (c>=97) and (c<=122) then
734 begin found:=false; write(chr(ord('a')+c-97)) end;
740 procedure initialize; { start the ball rolling }
741 { This is not part of the machine definition }
742 var cset:set of char;
757 function readb(n:integer):double;
759 begin read(prog,b); if n>1 then readb:=readb(n-1)*256+b else readb:=b end;
761 function readbyte:byte;
762 begin readbyte:=readb(1) end;
764 function readword:word;
765 begin readword:=readb(wsize) end;
767 function readadr:adr;
768 begin readadr:=readb(asize) end;
770 function ifind(ordinal:byte):mnem;
774 loopvar:=insr; found:=false;
776 if ordinal=ord(loopvar) then
777 begin found:=true; ifind:=loopvar end;
778 if loopvar<>ZRL then loopvar:=succ(loopvar) else loopvar:=NON;
779 until found or (loopvar=insr) ;
783 type hdrw=0..32767 ; { 16 bit header words }
790 0: if hdr<>3757 then { 07255 }
791 begin writeln('Not an em load file'); halt end;
793 begin writeln('Unsolved references'); halt end;
795 begin writeln('Incorrect load file version'); halt end;
796 4: if hdr<>wsize then
797 begin writeln('Incorrect word size'); halt end;
798 5: if hdr<>asize then
799 begin writeln('Incorrect pointer size'); halt end;
806 begin writeln('Illegal initialization'); halt end;
808 procedure readint(a:adr;s:size);
810 begin { construct integer out of byte sequence }
811 for i:=1 to s do { construct the value and initialize at a }
812 begin storeb(a,readbyte); a:=a+1 end
815 procedure readuns(a:adr;s:size);
816 begin { construct unsigned out of byte sequence }
817 readint(a,s) { identical to readint }
820 procedure readfloat(a:adr;s:size);
822 begin { construct float out of string}
823 if (s<>4) and (s<>8) then noinit; i:=0;
824 repeat { eat the bytes, construct the value and intialize at a }
832 uerrorproc:=0; intrap:=false;
834 { initialize tables }
835 for i:=0 to maxcode do code[i]:=0;
836 for i:=0 to maxdata do data[i]:=0;
837 for iclass:=prim to tert do
839 with dispat[iclass][i] do
840 begin instr:=NON; iflag:=[zbit] end;
842 { read instruction table file. see appendix B }
843 { The table read here is a simple transformation of the table on page xx }
844 { - instruction names were transformed to numbers }
845 { - the '-' flag was transformed to an 'i' flag for 'w' type instructions }
846 { - the 'S' flag was added for instructions having signed operands }
850 read(tables,insno) ; cset:=[]; f:=[];
852 if insr=NON then begin writeln('Incorrect table'); halt end;
853 repeat read(tables,c) until c<>' ' ;
858 if 'm' in cset then f:=f+[mini];
859 if 's' in cset then f:=f+[short];
860 if '-' in cset then f:=f+[zbit];
861 if 'i' in cset then f:=f+[ibit];
862 if 'S' in cset then f:=f+[sbit];
863 if 'w' in cset then f:=f+[wbit];
864 if (mini in f) or (short in f) then read(tables,nops) else nops:=1 ;
865 readln(tables,opcode);
866 if ('4' in cset) or ('8' in cset) then
867 begin iclass:=tert end
868 else if 'e' in cset then
869 begin iclass:=second end
871 for i:=0 to nops-1 do
873 with dispat[iclass,opcode+i] do
875 iflag:=f; instr:=insr;
876 if '2' in cset then ilength:=2
877 else if 'u' in cset then ilength:=2
878 else if '4' in cset then ilength:=4
879 else if '8' in cset then ilength:=8
880 else if (mini in f) or (short in f) then
882 if 'N' in cset then wtemp:=-1-i else wtemp:=i ;
883 if 'o' in cset then wtemp:=wtemp+1 ;
884 if short in f then wtemp:=wtemp*256 ;
891 { read in program text, data and procedure descriptors }
893 readhdr; { verify first header }
894 for i:=1 to 8 do header[i]:=readadr; { read second header }
895 hp:=maxdata+1; sp:=maxdata+1; lino(0);
896 { read program text }
897 if header[NTEXT]+header[NPROC]*pdsize>maxcode then
898 begin writeln('Text size too large'); halt end;
899 if header[SZDATA]>maxdata then
900 begin writeln('Data size too large'); halt end;
901 for i:=0 to header[NTEXT]-1 do code[i]:=readbyte;
904 for i:=1 to header[NDATA] do
909 elem:=readbyte; firsta:=nexta;
911 1: { uninitialized words }
913 begin store(nexta,undef); nexta:=nexta+wsize end;
914 2: { initialized bytes }
916 begin storeb(nexta,readbyte); nexta:=nexta+1 end;
917 3: { initialized words }
919 begin store(nexta,readword); nexta:=nexta+wsize end;
920 4,5: { instruction and data pointers }
922 begin storea(nexta,readadr); nexta:=nexta+asize end;
923 6: { signed integers }
924 begin readint(nexta,elem); nexta:=nexta+elem end;
925 7: { unsigned integers }
926 begin readuns(nexta,elem); nexta:=nexta+elem end;
927 8: { floating point numbers }
928 begin readfloat(nexta,elem); nexta:=nexta+elem end;
933 repc:=readadr; amount:=nexta-firsta;
934 for count:=1 to repc do
936 for ofst:=0 to amount-1 do data[nexta+ofst]:=data[firsta+ofst];
941 if header[SZDATA]<>nexta then writeln('Data initialization error');
943 { read descriptor table }
945 for i:=1 to header[NPROC]*pdsize do code[pd+i-1]:=readbyte;
946 { call the entry point routine }
947 ignmask:=0; { catch all traps, higher numbered traps cannot be ignored}
949 lb:=maxdata; { illegal dynamic link }
950 pc:=maxcode; { illegal return address }
951 push(0); a:=sp; { No environment }
952 push(0); b:=sp; { No args }
956 call(argp(header[ENTRY]));
959 {---------------------------------------------------------------------------}
960 { MAIN LOOP OF THE INTERPRETER }
961 {---------------------------------------------------------------------------}
962 { It should be noted that the interpreter (microprogram) for an EM
963 machine can be written in two fundamentally different ways: (1) the
964 instruction operands are fetched in the main loop, or (2) the in-
965 struction operands are fetched after the 256 way branch, by the exe-
966 cution routines themselves. In this interpreter, method (1) is used
967 to simplify the description of execution routines. The dispatch
968 table dispat is used to determine how the operand is encoded. There
971 0. There is no operand
972 1. The operand and instruction are together in 1 byte (mini)
973 2. The operand is one byte long and follows the opcode byte(s)
974 3. The operand is two bytes long and follows the opcode byte(s)
975 4. The operand is four bytes long and follows the opcode byte(s)
977 In this interpreter, the main loop determines the operand type,
978 fetches it, and leaves it in the global variable k for the execution
979 routines to use. Consequently, instructions such as LOL, which use
980 three different formats, need only be described once in the body of
982 However, for a production interpreter, or a hardware EM
983 machine, it is probably better to use method (2), i.e. to let the
984 execution routines themselves fetch their own operands. The reason
985 for this is that each opcode uniquely determines the operand format,
986 so no table lookup in the dispatch table is needed. The whole table
987 is not needed. Method (2) therefore executes much faster.
988 However, separate execution routines will be needed for LOL with
989 a one byte offset, and LOL with a two byte offset. It is to avoid
990 this additional clutter that method (1) is used here. In a produc-
991 tion interpreter, it is envisioned that the main loop will fetch the
992 next instruction byte, and use it as an index into a 256 word table
993 to find the address of the interpreter routine to jump to. The
994 routine jumped to will begin by fetching its operand, if any,
995 without any table lookup, since it knows which format to expect.
996 After doing the work, it returns to the main loop by jumping in-
997 directly to a register that contains the address of the main loop.
998 A slight variation on this idea is to have the register contain
999 the address of the branch table, rather than the address of the main
1001 Another issue is whether the execution routines for LOL 0, LOL
1002 2, LOL 4, etc. should all be have distinct execution routines. Doing
1003 so provides for the maximum speed, since the operand is implicit in
1004 the routine itself. The disadvantage is that many nearly identical
1005 execution routines will then be needed. Another way of doing it is
1006 to keep the instruction byte fetched from memory (LOL 0, LOL 2, LOL
1007 4, etc.) in some register, and have all the LOL mini format instruc-
1008 tions branch to a common routine. This routine can then determine
1009 the operand by subtracting the code for LOL 0 from the register,
1010 leaving the true operand in the register (as a word quantity of
1011 course). This method makes the interpreter smaller, but is a bit
1014 To make this important point a little clearer, consider how a
1015 production interpreter for the PDP-11 might appear. Let us assume the
1016 following opcodes have been assigned:
1018 31: LOL -2 (2 bytes, i.e. next word)
1021 34: LOL b (format with a one byte offset)
1022 35: LOL w (format with a one word, i.e. two byte offset)
1024 Further assume that each of the 5 opcodes will have its own execution
1025 routine, i.e. we are making a tradeoff in favor of fast execution and
1026 a slightly larger interpreter.
1027 Register r5 is the em program counter.
1028 Register r4 is the em LB register
1029 Register r3 is the em SP register (the stack grows toward low core)
1030 Register r2 contains the interpreter address of the main loop
1032 The main loop looks like this:
1034 movb (r5)+,r0 /fetch the opcode into r0 and increment r5
1035 asl r0 /shift r0 left 1 bit. Now: -256<=r0<=+254
1036 jmp *table(r0) /jump to execution routine
1038 Notice that no operand fetching has been done. The execution routines for
1039 the 5 sample instructions given above might be as follows:
1041 lol2: mov -2(r4),-(sp) /push local -2 onto stack
1042 jmp (r2) /go back to main loop
1043 lol4: mov -4(r4),-(sp) /push local -4 onto stack
1044 jmp (r2) /go back to main loop
1045 lol6: mov -6(r4),-(sp) /push local -6 onto stack
1046 jmp (r2) /go back to main loop
1047 lolb: mov $177400,r0 /prepare to fetch the 1 byte operand
1048 bisb (r5)+,r0 /operand is now in r0
1049 asl r0 /r0 is now offset from LB in bytes, not words
1050 add r4,r0 /r0 is now address of the needed local
1051 mov (r0),-(sp) /push the local onto the stack
1053 lolw: clr r0 /prepare to fetch the 2 byte operand
1054 bisb (r5)+,r0 /fetch high order byte first !!!
1055 swab r0 /insert high order byte in place
1056 bisb (r5)+,r0 /insert low order byte in place
1057 asl r0 /convert offset to bytes, from words
1058 add r4,r0 /r0 is now address of needed local
1059 mov (r0),-(sp) /stack the local
1062 The important thing to notice is where and how the operand fetch occurred:
1063 lol2, lol4, and lol6, (the mini's) have implicit operands
1064 lolb knew it had to fetch one byte, and did so without any table lookup
1065 lolw knew it had to fetch a word, and did so, high order byte first }
1068 {---------------------------------------------------------------------------}
1069 { Routines for the individual instructions }
1070 {---------------------------------------------------------------------------}
1076 LDC: pushd(argd(k));
1077 LOC: pushsw(argc(k));
1078 LOL: push(memw(locadr(k)));
1079 LOE: push(memw(argg(k)));
1080 LIL: push(memw(mema(locadr(k))));
1081 LOF: push(memw(popa+argf(k)));
1082 LAL: pusha(locadr(k));
1083 LAE: pusha(argg(k));
1084 LXL: begin a:=lb; for j:=1 to argn(k) do a:=mema(a+savsize); pusha(a) end;
1086 for j:=1 to argn(k) do a:= mema(a+savsize);
1089 LOI: pushx(argo(k),popa);
1090 LOS: begin k:=argw(k); if k<>wsize then trap(EILLINS);
1091 k:=pop; pushx(argo(k),popa)
1093 LDL: begin a:=locadr(k); push(memw(a+wsize)); push(memw(a)) end;
1094 LDE: begin k:=argg(k); push(memw(k+wsize)); push(memw(k)) end;
1095 LDF: begin k:=argf(k);
1096 a:=popa; push(memw(a+k+wsize)); push(memw(a+k))
1106 STL: store(locadr(k),pop);
1107 STE: store(argg(k),pop);
1108 SIL: store(mema(locadr(k)),pop);
1109 STF: begin a:=popa; store(a+argf(k),pop) end;
1110 STI: popx(argo(k),popa);
1111 STS: begin k:=argw(k); if k<>wsize then trap(EILLINS);
1112 k:=popa; popx(argo(k),popa)
1114 SDL: begin a:=locadr(k); store(a,pop); store(a+wsize,pop) end;
1115 SDE: begin k:=argg(k); store(k,pop); store(k+wsize,pop) end;
1116 SDF: begin k:=argf(k); a:=popa; store(a+k,pop); store(a+k+wsize,pop) end
1124 { SIGNED INTEGER ARITHMETIC }
1125 ADI: case szindex(argw(k)) of
1126 1: begin st:=popsw; ss:=popsw; push(fitsw(ss+st,EIOVFL)) end;
1127 2: begin dt:=popd; ds:=popd; pushd(doadi(ds,dt)) end;
1129 SBI: case szindex(argw(k)) of
1130 1: begin st:=popsw; ss:= popsw; push(fitsw(ss-st,EIOVFL)) end;
1131 2: begin dt:=popd; ds:=popd; pushd(dosbi(ds,dt)) end;
1133 MLI: case szindex(argw(k)) of
1134 1: begin st:=popsw; ss:= popsw; push(fitsw(ss*st,EIOVFL)) end;
1135 2: begin dt:=popd; ds:=popd; pushd(domli(ds,dt)) end;
1137 DVI: case szindex(argw(k)) of
1138 1: begin st:= popsw; ss:= popsw;
1139 if st=0 then trap(EIDIVZ) else pushsw(ss div st)
1141 2: begin dt:=popd; ds:=popd; pushd(dodvi(ds,dt)) end;
1143 RMI: case szindex(argw(k)) of
1144 1: begin st:= popsw; ss:=popsw;
1145 if st=0 then trap(EIDIVZ) else pushsw(ss - (ss div st)*st)
1147 2: begin dt:=popd; ds:=popd; pushd(dormi(ds,dt)) end
1149 NGI: case szindex(argw(k)) of
1150 1: begin st:=popsw; pushsw(-st) end;
1151 2: begin ds:=popd; pushd(dongi(ds)) end
1154 case szindex(argw(k)) of
1156 for i:= 1 to t do sleft(ss); pushsw(ss)
1161 case szindex(argw(k)) of
1163 for i:= 1 to t do sright(ss); pushsw(ss)
1166 for i:= 1 to t do sdright(ss); pushd(ss)
1177 { UNSIGNED INTEGER ARITHMETIC }
1178 ADU: case szindex(argw(k)) of
1179 1: begin t:=pop; s:= pop; push(chopw(s+t)) end;
1182 SBU: case szindex(argw(k)) of
1183 1: begin t:=pop; s:= pop; push(chopw(s-t)) end;
1186 MLU: case szindex(argw(k)) of
1187 1: begin t:=pop; s:= pop; push(chopw(s*t)) end;
1190 DVU: case szindex(argw(k)) of
1191 1: begin t:= pop; s:= pop;
1192 if t=0 then trap(EIDIVZ) else push(s div t)
1196 RMU: case szindex(argw(k)) of
1197 1: begin t:= pop; s:=pop;
1198 if t=0 then trap(EIDIVZ) else push(s - (s div t)*t)
1202 SLU: case szindex(argw(k)) of
1203 1: begin t:=pop; s:=pop;
1204 for i:= 1 to t do suleft(s); push(s)
1208 SRU: case szindex(argw(k)) of
1209 1: begin t:=pop; s:=pop;
1210 for i:= 1 to t do suright(s); push(s)
1220 { FLOATING POINT ARITHMETIC }
1221 ADF: begin argwf(k); rt:=popr; rs:=popr; pushr(doadf(rs,rt)) end;
1222 SBF: begin argwf(k); rt:=popr; rs:=popr; pushr(dosbf(rs,rt)) end;
1223 MLF: begin argwf(k); rt:=popr; rs:=popr; pushr(domlf(rs,rt)) end;
1224 DVF: begin argwf(k); rt:=popr; rs:=popr; pushr(dodvf(rs,rt)) end;
1225 NGF: begin argwf(k); rt:=popr; pushr(dongf(rt)) end;
1226 FIF: begin argwf(k); rt:=popr; rs:=popr;
1227 dofif(rt,rs,x,y); pushr(y); pushr(x)
1229 FEF: begin argwf(k); rt:=popr; dofef(rt,x,ss); pushr(x); pushsw(ss) end
1236 { POINTER ARITHMETIC }
1237 ADP: pusha(popa+argf(k));
1238 ADS: case szindex(argw(k)) of
1239 1: begin st:=popsw; pusha(popa+st) end;
1240 2: begin dt:=popd; pusha(popa+dt) end;
1244 case szindex(argw(k)) of
1245 1: push(fitsw(b-a,EIOVFL));
1256 { INCREMENT/DECREMENT/ZERO }
1257 INC: push(fitsw(popsw+1,EIOVFL));
1258 INL: begin a:=locadr(k); store(a,fitsw(signwd(memw(a))+1,EIOVFL)) end;
1259 INE: begin a:=argg(k); store(a,fitsw(signwd(memw(a))+1,EIOVFL)) end;
1260 DEC: push(fitsw(popsw-1,EIOVFL));
1261 DEL: begin a:=locadr(k); store(a,fitsw(signwd(memw(a))-1,EIOVFL)) end;
1262 DEE: begin a:=argg(k); store(a,fitsw(signwd(memw(a))-1,EIOVFL)) end;
1263 ZRL: store(locadr(k),0);
1264 ZRE: store(argg(k),0);
1265 ZER: for j:=1 to argw(k) div wsize do push(0);
1274 CII: begin s:=pop; t:=pop;
1275 if t<wsize then begin push(sextend(pop,t)); t:=wsize end;
1276 case szindex(argw(t)) of
1277 1: if szindex(argw(s))=2 then pushd(popsw);
1278 2: if szindex(argw(s))=1 then push(fitsw(popd,ECONV))
1281 CIU: case szindex(argw(pop)) of
1282 1: if szindex(argw(pop))=2 then push(unsign(popd mod negoff));
1285 CIF: begin argwf(pop);
1286 case szindex(argw(pop)) of 1:pushr(popsw); 2:pushr(popd) end
1288 CUI: case szindex(argw(pop)) of
1289 1: case szindex(argw(pop)) of
1290 1: begin s:=pop; if s>maxsint then trap(ECONV); push(s) end;
1293 2: case szindex(argw(pop)) of
1298 CUU: case szindex(argw(pop)) of
1299 1: if szindex(argw(pop))=2 then trap(EILLINS);
1302 CUF: begin argwf(pop);
1303 if szindex(argw(pop))=1 then pushr(pop) else trap(EILLINS)
1305 CFI: begin sz:=argw(pop); argwf(pop); rt:=popr;
1307 1: push(fitsw(trunc(rt),ECONV));
1308 2: pushd(fitd(trunc(rt)));
1311 CFU: begin sz:=argw(pop); argwf(pop); rt:=popr;
1313 1: push( chopw(trunc(abs(rt)-0.5)) );
1317 CFF: begin argwf(pop); argwf(pop) end
1328 for j:= 1 to k div wsize do
1329 begin a:=sp+k; t:=pop; store(a,bf(andf,memw(a),t)) end;
1333 for j:= 1 to k div wsize do
1334 begin a:=sp+k; t:=pop; store(a,bf(iorf,memw(a),t)) end;
1338 for j:= 1 to k div wsize do
1339 begin a:=sp+k; t:=pop; store(a,bf(xorf,memw(a),t)) end;
1343 for j:= 1 to k div wsize do
1345 store(sp+k-wsize*j, bf(xorf,memw(sp+k-wsize*j), negoff-1))
1348 ROL: begin k:=argw(k); if k<>wsize then trap(EILLINS);
1349 t:=pop; s:=pop; for i:= 1 to t do rleft(s); push(s)
1351 ROR: begin k:=argw(k); if k<>wsize then trap(EILLINS);
1352 t:=pop; s:=pop; for i:= 1 to t do rright(s); push(s)
1365 i:= t mod 8; t:= t div 8;
1367 begin trap(ESET); s:=0 end
1369 begin s:=memb(sp+t) end;
1370 newsp(sp+k); push(bit(i,s));
1375 i:= t mod 8; t:= t div 8;
1376 for j:= 1 to k div wsize do push(0);
1380 begin s:=1; for j:= 1 to i do rleft(s); storeb(sp+t,s) end
1390 begin k:=argw(k); if k<>wsize then trap(EILLINS); a:=popa;
1391 pushx(argo(memw(a+2*k)),arraycalc(a))
1394 begin k:=argw(k); if k<>wsize then trap(EILLINS); a:=popa;
1395 popx(argo(memw(a+2*k)),arraycalc(a))
1398 begin k:=argw(k); if k<>wsize then trap(EILLINS); a:=popa;
1408 CMI: case szindex(argw(k)) of
1409 1: begin st:=popsw; ss:=popsw;
1410 if ss<st then pushsw(-1) else if ss=st then push(0) else push(1)
1412 2: begin dt:=popd; ds:=popd;
1413 if ds<dt then pushsw(-1) else if ds=dt then push(0) else push(1)
1416 CMU: case szindex(argw(k)) of
1417 1: begin t:=pop; s:=pop;
1418 if s<t then pushsw(-1) else if s=t then push(0) else push(1)
1422 CMP: begin a:=popa; b:=popa;
1423 if b<a then pushsw(-1) else if b=a then push(0) else push(1)
1425 CMF: begin argwf(k); rt:=popr; rs:=popr;
1426 if rs<rt then pushsw(-1) else if rs=rt then push(0) else push(1)
1428 CMS: begin k:=argw(k);
1430 while (j < k) and (t=0) do
1431 begin if memw(sp+j) <> memw(sp+k+j) then t:=1;
1434 newsp(sp+wsize*k); push(t);
1437 TLT: if popsw < 0 then push(1) else push(0);
1438 TLE: if popsw <= 0 then push(1) else push(0);
1439 TEQ: if pop = 0 then push(1) else push(0);
1440 TNE: if pop <> 0 then push(1) else push(0);
1441 TGE: if popsw >= 0 then push(1) else push(0);
1442 TGT: if popsw > 0 then push(1) else push(0);
1446 procedure branchops;
1452 BLT: begin st:=popsw; if popsw < st then newpc(pc+k) end;
1453 BLE: begin st:=popsw; if popsw <= st then newpc(pc+k) end;
1454 BEQ: begin t :=pop ; if pop = t then newpc(pc+k) end;
1455 BNE: begin t :=pop ; if pop <> t then newpc(pc+k) end;
1456 BGE: begin st:=popsw; if popsw >= st then newpc(pc+k) end;
1457 BGT: begin st:=popsw; if popsw > st then newpc(pc+k) end;
1459 ZLT: if popsw < 0 then newpc(pc+k);
1460 ZLE: if popsw <= 0 then newpc(pc+k);
1461 ZEQ: if pop = 0 then newpc(pc+k);
1462 ZNE: if pop <> 0 then newpc(pc+k);
1463 ZGE: if popsw >= 0 then newpc(pc+k);
1464 ZGT: if popsw > 0 then newpc(pc+k)
1472 { PROCEDURE CALL GROUP }
1474 CAI: begin call(argp(popa)) end;
1475 RET: begin k:=argz(k); if k div wsize>maxret then trap(EILLINS);
1476 for j:= 1 to k div wsize do retarea[j]:=pop; retsize:=k;
1477 newsp(lb); lb:=maxdata+1; { To circumvent stack overflow error }
1482 if retsize=wsize then exitstatus:=retarea[1]
1483 else exitstatus:=undef
1488 LFR: begin k:=args(k); if k<>retsize then trap(EILLINS);
1489 for j:=k div wsize downto 1 do push(retarea[j]);
1498 { MISCELLANEOUS GROUP }
1500 begin if insr=ASS then
1501 begin k:=argw(k); if k<>wsize then trap(EILLINS); k:=popsw end;
1504 then for j:= 1 to -k div wsize do push(undef)
1508 begin if insr=BLS then
1509 begin k:=argw(k); if k<>wsize then trap(EILLINS); k:=pop end;
1512 for j := 1 to k div wsize do
1513 store(b-wsize+wsize*j,memw(a-wsize+wsize*j))
1515 CSA: begin k:=argw(k); if k<>wsize then trap(EILLINS);
1517 st:= popsw - signwd(memw(a+asize));
1518 if (st>=0) and (st<=memw(a+wsize+asize)) then
1519 b:=mema(a+2*wsize+asize+asize*st) else b:=mema(a);
1520 if b=0 then trap(ECASE) else newpc(b)
1522 CSB: begin k:=argw(k); if k<>wsize then trap(EILLINS); a:=popa;
1523 t:=pop; i:=1; found:=false;
1524 while (i<=memw(a+asize)) and not found do
1525 if t=memw(a+(asize+wsize)*i) then found:=true else i:=i+1;
1526 if found then b:=memw(a+(asize+wsize)*i+wsize) else b:=memw(a);
1527 if b=0 then trap(ECASE) else newpc(b);
1529 DCH: begin pusha(mema(popa+dynd)) end;
1531 begin if insr=DUS then
1532 begin k:=argw(k); if k<>wsize then trap(EILLINS); k:=pop end;
1534 for i:=1 to k div wsize do push(memw(sp+k-wsize));
1538 for i:=1 to k div wsize do push(memw(sp+k-wsize));
1539 for i:=0 to k div wsize - 1 do
1540 store(sp+k+i*wsize,memw(sp+k+k+i*wsize));
1541 for i:=1 to k div wsize do
1542 begin t:=pop ; store(sp+k+k-wsize,t) end;
1544 FIL: filna(argg(k));
1545 GTO: begin k:=argg(k);
1546 newlb(mema(k+2*asize)); newsp(mema(k+asize)); newpc(mema(k))
1550 LNI: lino(memw(0)+1);
1551 LOR: begin i:=argr(k);
1552 case i of 0:pusha(lb); 1:pusha(sp); 2:pusha(hp) end;
1554 LPB: pusha(popa+statd);
1556 NOP: writeln('NOP at line ',memw(0):5) ;
1558 case szindex(argw(k)) of
1559 1: if (signwd(memw(sp))<signwd(memw(a))) or
1560 (signwd(memw(sp))>signwd(memw(a+wsize))) then trap(ERANGE);
1561 2: if (memd(sp)<memd(a)) or
1562 (memd(sp)>memd(a+2*wsize)) then trap(ERANGE);
1566 SIG: begin a:=popa; pusha(uerrorproc); uerrorproc:=a end;
1568 STR: begin i:=argr(k);
1569 case i of 0: newlb(popa); 1: newsp(popa); 2: newhp(popa) end;
1575 {---------------------------------------------------------------------------}
1577 {---------------------------------------------------------------------------}
1582 opcode := nextpc; { fetch the first byte of the instruction }
1583 if opcode=escape1 then iclass:=second
1584 else if opcode=escape2 then iclass:=tert
1586 if iclass<>prim then opcode := nextpc;
1587 with dispat[iclass][opcode] do
1589 if not (zbit in iflag) then
1590 if ibit in iflag then k:=pop else
1592 if mini in iflag then k:=implicit else
1594 if short in iflag then k:=implicit+nextpc else
1596 if (sbit in iflag) and (k>=128) then k:=k-256;
1597 for i:=2 to ilength do k:=256*k + nextpc
1600 if wbit in iflag then k:=k*wsize;
1608 LDC,LOC,LOL,LOE,LIL,LOF,LAL,LAE,LXL,LXA,LOI,LOS,LDL,LDE,LDF,LPI:
1612 STL,STE,SIL,STF,STI,STS,SDL,SDE,SDF:
1615 { SIGNED INTEGER ARITHMETIC }
1616 ADI,SBI,MLI,DVI,RMI,NGI,SLI,SRI:
1619 { UNSIGNED INTEGER ARITHMETIC }
1620 ADU,SBU,MLU,DVU,RMU,SLU,SRU:
1623 { FLOATING POINT ARITHMETIC }
1624 ADF,SBF,MLF,DVF,NGF,FIF,FEF:
1627 { POINTER ARITHMETIC }
1631 { INCREMENT/DECREMENT/ZERO }
1632 INC,INL,INE,DEC,DEL,DEE,ZRL,ZRE,ZER,ZRF:
1636 CII,CIU,CIF,CUI,CUU,CUF,CFI,CFU,CFF:
1640 XAND,IOR,XOR,COM,ROL,ROR:
1652 CMI,CMU,CMP,CMF,CMS, TLT,TLE,TEQ,TNE,TGE,TGT:
1656 BRA, BLT,BLE,BEQ,BNE,BGE,BGT, ZLT,ZLE,ZEQ,ZNE,ZGE,ZGT:
1659 { PROCEDURE CALL GROUP }
1663 { MISCELLANEOUS GROUP }
1664 ASP,ASS,BLM,BLS,CSA,CSB,DCH,DUP,DUS,EXG,FIL,GTO,LIM,
1665 LIN,LNI,LOR,LPB,MON,NOP,RCK,RTT,SIG,SIM,STR,TRP:
1668 end; { end of case statement }
1669 if not ( (insr=RET) or (insr=ASP) or (insr=BRA) or (insr=GTO) ) then
1673 writeln('halt with exit status: ',exitstatus:1);