mmixlib.ch 64 KB
Newer Older
1 2
% This is the change file that extracts from mmix-sim.w 
% the various .c files that make up the mmix library
3 4 5 6 7 8 9 10

Types and preprocessor macros go into libtype.h

@x
@* Basics. To get started, we define a type that provides semantic sugar.
@y
@ @(libtype.h@>=
@h
11
#define _LIBTYPE_H_
12 13 14 15 16 17 18 19 20 21 22 23
@<Preprocessor macros@>@;
@<Type declarations@>@;

@* Basics. To get started, we define a type that provides semantic sugar.
@z

@x
@ @<Sub...@>=
void print_hex @,@,@[ARGS((octa))@];@+@t}\6{@>
@y
@ @(libprint.c@>=
#include <stdio.h>
24
#include <setjmp.h>
25
#include "libconfig.h"
26 27 28 29
#include <time.h>
#include "libtype.h"
#include "libglobals.h"
#include "mmixlib.h"
30
#include "libarith.h"
31
#include "libimport.h"
32 33 34 35 36 37

void print_hex @,@,@[ARGS((octa))@];@+@t}\6{@>
@z


The external definitions of mmix-arith
38
should also go into a header file.
39 40 41 42 43 44 45 46 47

@x
@<Sub...@>=
extern octa zero_octa; /* |zero_octa.h=zero_octa.l=0| */
@y
@(libarith.h@>=
extern octa zero_octa; /* |zero_octa.h=zero_octa.l=0| */
@z

48
Fatal errors need to be handled but error messages moves to libconfig.h
49 50 51 52

@x
@d panic(m) {@+fprintf(stderr,"Panic: %s!\n",m);@+exit(-2);@+}
@y
53
@d panic(m) {@+MMIX_ERROR("Panic: %s!\n",m);@+longjmp(mmix_exit,-2);@+}
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
@z

@x
@<Sub...@>=
void print_int @,@,@[ARGS((octa))@];@+@t}\6{@>
@y
@(libprint.c@>=
void print_int @,@,@[ARGS((octa))@];@+@t}\6{@>
@z

The tet field of the mem_tetra may be eliminated.

@x
  tetra tet; /* the tetrabyte of simulated memory */
@y
69 70 71
#ifdef MMIX_MEM_TET
  tetra tet; /* the tetrabyte of simulated memory */
#endif
72 73 74 75 76 77 78 79 80
@z

@x
@<Sub...@>=
mem_node* new_mem @,@,@[ARGS((void))@];@+@t}\6{@>
@y
@(libmem.c@>=
#include <stdlib.h>
#include <stdio.h>
81
#include <setjmp.h>
82
#include "libconfig.h"
83 84 85 86
#include <time.h>
#include "libtype.h"
#include "libglobals.h"
#include "mmixlib.h"
87
#include "libimport.h"
88 89


90

91 92 93



94 95 96
mem_node* new_mem @,@,@[ARGS((void))@];@+@t}\6{@>
@z

97 98 99 100 101 102
We distinguish between different levels of initialization:
persistent data (initialized once when we load the library),
then data that needs to be initialized each time we start the
simulator, 
and finally data that is initialized each time we boot the machine.

103 104 105 106 107 108 109 110 111 112 113 114
@x
@<Initialize...@>=
mem_root=new_mem();
mem_root->loc.h=0x40000000;
last_mem=mem_root;
@y
@<Set up persistent data@>=
mem_root=new_mem();
mem_root->loc.h=0x40000000;
last_mem=mem_root;
@z

115 116
Handling the simulation of memory goes into its own file.

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
@x
@<Sub...@>=
mem_tetra* mem_find @,@,@[ARGS((octa))@];@+@t}\6{@>
@y
@(libmem.c@>=
mem_tetra* mem_find @,@,@[ARGS((octa))@];@+@t}\6{@>
@z

Defining the macro mm conflicts with other macros when using MS Visual C

@x
@d mm 0x98 /* the escape code of \.{mmo} format */
@y
@d mmo_esc 0x98 /* the escape code of \.{mmo} format */
@z

133 134
Loading the object file will go into its own file. 

135 136 137 138 139 140 141 142
@x
@<Initialize everything@>=
mmo_file=fopen(mmo_file_name,"rb");
@y
@<Load object file@>=
mmo_file=fopen(mmo_file_name,"rb");
@z

143 144
How to print errors is defined in libconfig.h and
exit needs to be replaced by a longjmp.
145

146 147 148 149 150 151
@x
    fprintf(stderr,"Can't open the object file %s or %s!\n",
@.Can't open...@>
               mmo_file_name,alt_name);
    exit(-3);
@y
152 153
   MMIX_ERROR("Can't open the object file %s!\n",mmo_file_name);
   longjmp(mmix_exit,-3);
154 155 156
@z


157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
these global varaibles go into libload

@x
@ @<Glob...@>=
FILE *mmo_file; /* the input file */
int postamble; /* have we encountered |lop_post|? */
int byte_count; /* index of the next-to-be-read byte */
byte buf[4]; /* the most recently read bytes */
int yzbytes; /* the two least significant bytes */
int delta; /* difference for relative fixup */
tetra tet; /* |buf| bytes packed big-endianwise */
@y
@ @<Load globals@>=
static FILE *mmo_file; /* the input file */
static int postamble; /* have we encountered |lop_post|? */
static int byte_count; /* index of the next-to-be-read byte */
static byte buf[4]; /* the most recently read bytes */
static int yzbytes; /* the two least significant bytes */
static int delta; /* difference for relative fixup */
static tetra tet; /* |buf| bytes packed big-endianwise */@z
@z

MMIX_ERROR again.

181 182 183 184 185
@x
     fprintf(stderr,"Bad object file! (Try running MMOtype.)\n");
@.Bad object file@>
     exit(-4);
@y   
186 187
     MMIX_ERROR("%s","Bad object file! (Try running MMOtype.)\n");
     longjmp(mmix_exit,-4);
188 189
@z

190 191
The next function goes into libload.

192 193 194 195 196 197 198 199
@x
@<Sub...@>=
void read_tet @,@,@[ARGS((void))@];@+@t}\6{@>
@y
@(libload.c@>=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
200
#include <setjmp.h>
201
#include "libconfig.h"
202 203 204 205
#include <time.h>
#include "libtype.h"
#include "libglobals.h"
#include "mmixlib.h"
206
#include "libarith.h"
207
#include "libname.h"
208
#include "libimport.h"
209 210


211
@<Load globals@>
212 213 214 215 216 217 218

@<Loading subroutines@>@;

@ @<Loading subroutines@>=
void read_tet @,@,@[ARGS((void))@];@+@t}\6{@>
@z

219 220
Also to libload.

221 222 223 224 225 226 227 228
@x
@ @<Sub...@>=
byte read_byte @,@,@[ARGS((void))@];@+@t}\6{@>
@y
@ @<Loading subroutines@>=
byte read_byte @,@,@[ARGS((void))@];@+@t}\6{@>
@z

229 230
Replace mm by mmo_esc.

231 232 233 234 235 236
@x
if (buf[0]!=mm || buf[1]!=lop_pre) mmo_err;
@y
if (buf[0]!=mmo_esc || buf[1]!=lop_pre) mmo_err;
@z

237 238
And again.

239 240 241 242 243 244
@x
 loop:@+if (buf[0]==mm) switch (buf[1]) {
@y
 loop:@+if (buf[0]==mmo_esc) switch (buf[1]) {
@z

245 246 247
The macro mmo_load becomes a function,
which uses MMIX_LDT and MMIX_STT as defined in libconfig.h.

248 249 250 251 252 253 254 255 256
@x
@d mmo_load(loc,val) ll=mem_find(loc), ll->tet^=val
@y
@d mmo_load(loc,val) ll=load_mem_tetra(loc,val)

@(libload.c@>=
static mem_tetra *load_mem_tetra(octa loc, tetra val)  
{ octa x; 
  mem_tetra *ll=mem_find(loc);           
257
  MMIX_LDT(x,loc);     
258
  x.l = x.l^val;             
259
  if (!MMIX_STT(x,loc))  
260 261 262 263
  panic("Unable to store mmo file to RAM");
  return ll;
}

264
@ This function is used next.
265 266
@z

267
Before we increment the line number,
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
we reset the frequency.

@x
    cur_line++;
@y
    ll->freq=0;
    cur_line++;
@z

The next lines of code complete loading the object file.

@x
@ @<Initialize...@>=
cur_loc.h=cur_loc.l=0;
@y
@ @<Load object file@>=
cur_loc.h=cur_loc.l=0;
@z

287 288
mmo files use local file numbers.
As we might load multiple files,
289 290 291
we have to map the file number stored in
the mmo file as the ybyte to the 
filenumbers used inside the library.
292 293
Default conversion functions are defined
in libname.c.
294 295 296 297 298 299 300 301 302

First the case of known files.

@x
case lop_file:@+if (file_info[ybyte].name) {
   if (zbyte) mmo_err;
   cur_file=ybyte;
@y
case lop_file:
303
   if (ybyte2file[ybyte]>=0) {
304
   if (zbyte) mmo_err;
305
   cur_file=ybyte2file[ybyte];
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
@z

Now we handle new files.

@x
 }@+else {
   if (!zbyte) mmo_err;
   file_info[ybyte].name=(char*)calloc(4*zbyte+1,1);
   if (!file_info[ybyte].name) {
     fprintf(stderr,"No room to store the file name!\n");@+exit(-5);
@.No room...@>
   }
   cur_file=ybyte;
   for (j=zbyte,p=file_info[ybyte].name; j>0; j--,p+=4) {
     read_tet();
     *p=buf[0];@+*(p+1)=buf[1];@+*(p+2)=buf[2];@+*(p+3)=buf[3];
   }
 }
@y
 }@+else {
   if (!zbyte) mmo_err;
327 328
   if (4*zbyte+1>FILENAME_MAX) mmo_err;
   for (j=zbyte,p=filename; j>0; j--,p+=4) {
329 330 331
     read_tet();
     *p=buf[0];@+*(p+1)=buf[1];@+*(p+2)=buf[2];@+*(p+3)=buf[3];
   }
332 333
   cur_file=filename2file(filename);
   ybyte2file[ybyte]=cur_file;
334 335 336
 }
@z

337
mm was replaced by mmo_esc.
338 339 340 341 342 343 344

@x
   if (buf[0]==mm) {
@y
   if (buf[0]==mmo_esc) {
@z

345
We load the postamble into the beginning
346 347
of segment~3, also known as \.{Stack\_Segment}).
The stack segment is set up to be used with an unsave instruction.
348 349
On the stack, we have, the local registers (argc and argv) and the 
value of rL, then the global 
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
registers and the special registers rB, rD, rE, rH, rJ, rM, rR, rP, rW, rX, rY, and rZ,
followed by rG and rA packed into eight byte.

@x
@<Load the postamble@>=
aux.h=0x60000000;@+ aux.l=0x18;
ll=mem_find(aux);
(ll-1)->tet=2; /* this will ultimately set |rL=2| */
(ll-5)->tet=argc; /* and $\$0=|argc|$ */
(ll-4)->tet=0x40000000;
(ll-3)->tet=0x8; /* and $\$1=\.{Pool\_Segment}+8$ */
G=zbyte;@+ L=0;@+ O=0;
for (j=G+G;j<256+256;j++,ll++,aux.l+=4) read_tet(), ll->tet=tet;
inst_ptr.h=(ll-2)->tet, inst_ptr.l=(ll-1)->tet; /* \.{Main} */
(ll+2*12)->tet=G<<24;
g[255]=incr(aux,12*8); /* we will \.{UNSAVE} from here, to get going */
@y
@<Load the postamble@>=
{ octa x;
  aux.h=0x60000000;
  x.h=0;@+x.l=0;@+aux.l=0x00;
371
  if (!MMIX_STO(x,aux)) /* $\$0=|argc|$ */
372 373
     panic("Unable to store mmo file to RAM");
  x.h=0x40000000;@+x.l=0x8;@+aux.l=0x08;
374
  if (!MMIX_STO(x,aux)) /* and $\$1=\.{Pool\_Segment}+8$ */
375 376
     panic("Unable to store mmo file to RAM");
  x.h=0;@+x.l=2;@+aux.l=0x10;
377
  if (!MMIX_STO(x,aux)) /* this will ultimately set |rL=2| */
378 379 380 381 382 383
     panic("Unable to store mmo file to RAM");
  G=zbyte;@+ L=0;@+ O=0;
  aux.l=0x18;
  for (j=G;j<256;j++,aux.l+=8) 
  { read_tet(); x.h=tet;
    read_tet(), x.l=tet;
384
    if (!MMIX_STO(x,aux))
385 386
       panic("Unable to store mmo file to RAM");
  }
387 388
  aux=incr(aux,12*8); /* we can |UNSAVE| from here, to get going */
#ifdef MMIX_BOOT
389
  loc.h=0x80000000; loc.l=0;
390
  g[rWW] = x;  /* last octa stored is address of \.{Main} */
391 392 393 394
  g[rBB] = aux;
  g[rXX].h = 0; g[rXX].l = ((tetra)UNSAVE<<24)+255; /* \.{UNSAVE} \$255 */
  rzz = 1;
#else
395 396
  loc.h=0x80000000; loc.l=0;
  inst_ptr = x;  /* last octa stored is address of \.{Main} */
397
  g[255] = aux;
398 399 400 401 402 403 404
  g[rXX].h = 0; g[rXX].l = ((tetra)UNSAVE<<24)+255; /* \.{UNSAVE} \$255 */
  rzz = 1;
  trace_once=interacting;
/*  RESUME 0 will not work for x = 0
    inst_ptr = x;
    g[255] = aux;
    rzz = 0;  pretend \.{RESUME} 0 */
405
#endif
406 407
//  if (interacting) set_break(x,exec_bit);
  x.h=G<<24; x.l=0 /* rA */; 
408
  if (!MMIX_STO(x,aux))
409 410 411 412 413
     panic("Unable to store mmo file to RAM");
  G=g[rG].l; /* restore G to rG because it was changed above */
}
@z

414
The source line buffer is allocated once.
415

416

417 418 419 420 421 422 423 424
@x
@<Initialize...@>=
if (buf_size<72) buf_size=72;
@y
@<Set up persistent data@>=
if (buf_size<72) buf_size=72;
@z

425 426
The display of source lines gets its own file.

427 428 429 430 431 432 433 434 435 436
@x
@<Sub...@>=
void make_map @,@,@[ARGS((void))@];@+@t}\6{@>
@y
@(libshowline.c@>=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
437
#include <setjmp.h>
438
#include "libconfig.h"
439 440 441 442
#include <time.h>
#include "libtype.h"
#include "libglobals.h"
#include "mmixlib.h"
443
#include "libimport.h"
444

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

void make_map @,@,@[ARGS((void))@];@+@t}\6{@>
@z

these include files are needed only in libshowline.c

@x
@<Preprocessor macros@>=
@y
@<Showline macros@>=
@z

@x
@<Sub...@>=
void print_line @,@,@[ARGS((int))@];@+@t}\6{@>
void print_line(k)
  int k;
@y
@(libshowline.c@>=
void print_line(int k)
@z


@x
@ @<Preprocessor macros@>=
@y
@ @<Showline macros@>=
@z

@x
@<Sub...@>=
void show_line @,@,@[ARGS((void))@];@+@t}\6{@>
@y
@(libshowline.c@>=
479
void show_line @,@,@[ARGS((void))@];@+@t}\6{@>
480 481
@z

482 483
Printing the profile has its own file.

484 485 486 487 488 489
@x
@<Sub...@>=
void print_freqs @,@,@[ARGS((mem_node*))@];@+@t}\6{@>
@y
@(libprofile.c@>=
#include <stdio.h>
490
#include <setjmp.h>
491
#include "libconfig.h"
492 493 494 495
#include <time.h>
#include "libtype.h"
#include "libglobals.h"
#include "mmixlib.h"
496
#include "libarith.h"
497
#include "libimport.h"
498 499 500 501

void print_freqs @,@,@[ARGS((mem_node*))@];@+@t}\6{@>
@z

502
We need ll as a local variable here.
503

504 505 506 507
@x
  octa cur_loc;
@y
  octa cur_loc;
508
  MMIX_LOCAL_LL
509 510
@z

511 512
we use MMIX_FETCH.

513 514 515 516 517 518 519
@x
 loc_implied: printf("%10d. %08x%08x: %08x (%s)\n",
      p->dat[j].freq, cur_loc.h, cur_loc.l, p->dat[j].tet,
      info[p->dat[j].tet>>24].name);
@y
 loc_implied:
 { tetra inst; 
520
   MMIX_FETCH(inst,cur_loc);
521 522 523 524 525 526 527 528 529
   printf("%10d. %08x%08x: %08x (%s)\n",
      p->dat[j].freq, cur_loc.h, cur_loc.l, inst,
      info[inst>>24].name);
 }
@z

For the mmixlib, we split performing the
instruction in three parts: 
resuming,fetching, and executing.
530
here we do only the resuming.
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

@x
@<Perform one instruction@>=
{
  if (resuming) loc=incr(inst_ptr,-4), inst=g[rX].l;
  else @<Fetch the next instruction@>;
@y
@<Perform one instruction@>=
{
  if (resuming)
  { loc=incr(inst_ptr,-4), inst=g[rzz?rXX:rX].l;
    if (rzz==0) /* RESUME 0 */
    { if ((loc.h&sign_bit) != (inst_ptr.h&sign_bit))
      { resuming = false;
        goto protection_violation;
      }
      @<Check for security violation@>
    }
  }
@z

This restriction is no longer necessary.

@x
  if (loc.h>=0x20000000) goto privileged_inst;
@y
@z

559
The next two lines are not a propper part of
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
performing an instruction and move to a separate 
function respectively to the main loop.
@x
  @<Trace the current instruction, if requested@>;
  if (resuming && op!=RESUME) resuming=false;
@y
@z

@x
int rop; /* ropcode of a resumed instruction */
@y
int rop; /* ropcode of a resumed instruction */
int rzz; /* Z field of a resumed instruction */
@z

575 576 577 578 579 580 581 582 583 584
We turn |breakpoint| into an int to provide more information on the
kind of breakpoint. Instead of setting |breakpoint| to |true|, we 
(usually) store the respective bit of the |bkpt| field in the |mem_tetra| 
that caused the break. In case of a SWYM instruction, we set it to 
the YZ value shifted by 8 bit to the left. 
In all other cases, we set the |trace_bit|.

@x
bool breakpoint; /* should we pause after the current instruction? */
@y
Martin Ruckert's avatar
Martin Ruckert committed
585
int breakpoint=0; /* what caused the pause after the current instruction? */
586 587 588
@z


589 590 591
@x
bool interacting; /* are we in interactive mode? */
@y
Martin Ruckert's avatar
Martin Ruckert committed
592
bool interacting=false; /* are we in interactive mode? */
593 594
bool show_operating_system = false; /* do we show negative addresses */
bool trace_once=false;
Martin Ruckert's avatar
Martin Ruckert committed
595

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
octa rOlimit={-1,-1}; /* tracing and break only if g[rO]<=rOlimit */
bool interact_after_resume = false;
@z

We make some more variables global.

@x
@ @<Local...@>=
register mmix_opcode op; /* operation code of the current instruction */
register int xx,yy,zz,yz; /* operand fields of the current instruction */
register tetra f; /* properties of the current |op| */
@y
@ @<Glob...@>=
mmix_opcode op; /* operation code of the current instruction */
int xx,yy,zz,yz; /* operand fields of the current instruction */
611
tetra f; /* properties of the current |op| */
612 613 614 615 616 617 618 619 620 621 622

@ @<Local...@>=
@z

and p is no longer needed in performing an instruction.

@x
register char *p; /* current place in a string */
@y
@z

623
loading the instruction is postponed
624 625 626

@x
  inst=ll->tet;
627 628 629
@y
@z

630 631 632 633 634 635 636 637 638
When we hit an execute breakpoint, we set the exec bit in |breakpoint|.

@x
  if (ll->bkpt&exec_bit) breakpoint=true;
@y
  if (ll->bkpt&exec_bit) breakpoint|=exec_bit;
@z


639 640
now before incrementing the instruction pointer we load the instruction.
@x
641 642 643
  inst_ptr=incr(inst_ptr,4);
@y
  @<Check for security violation@>
644 645
  inst=0; /* default TRAP 0,Halt,0 */
  if(!MMIX_FETCH(inst,loc)) 
646 647 648 649 650 651
    goto page_fault;
  inst_ptr=incr(inst_ptr,4);
  if ((inst_ptr.h&sign_bit) && !(loc.h&sign_bit))
    goto protection_violation;
@z

652
We change how to display certain instructions.
653 654 655

@x
{"RESUME",0x00,0,0,5,"{%#b} -> %#z"},@|
656 657 658 659 660
@y
{"RESUME",0x00,0,0,5,"{%#b}, $255 = %x, -> %#z"},@|
@z

@x
661 662 663 664 665 666 667
{"SYNC",0x01,0,0,1,""},@|
{"SWYM",0x00,0,0,1,""},@|
@y
{"SYNC",0x01,0,0,1,"%z"},@|
{"SWYM",0x01,0,0,1,"%r"},@|
@z

668
L,G, and O are made global.
669 670 671 672 673 674 675 676 677

@x
@ @<Local...@>=
register int G,L,O; /* accessible copies of key registers */
@y
@ @<Glob...@>=
int G=255,L=0,O=0; /* accessible copies of key registers */
@z

678

679
Some initialization needs to be done once others at each reboot.
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701

@x
@<Initialize...@>=
g[rK]=neg_one;
g[rN].h=(VERSION<<24)+(SUBVERSION<<16)+(SUBSUBVERSION<<8);
g[rN].l=ABSTIME; /* see comment and warning above */
g[rT].h=0x80000005;
g[rTT].h=0x80000006;
g[rV].h=0x369c2004;
if (lring_size<256) lring_size=256;
lring_mask=lring_size-1;
if (lring_size&lring_mask)
  panic("The number of local registers must be a power of 2");
@.The number of local...@>
l=(octa*)calloc(lring_size,sizeof(octa));
if (!l) panic("No room for the local registers");
@.No room...@>
cur_round=ROUND_NEAR;
@y
@<Set up persistent data@>=
if (lring_size<256) lring_size=256;
lring_mask=lring_size-1;
702 703
if (lring_size&lring_mask) 
  panic("The number of local registers must be a power of 2");
704
l=(octa*)calloc(lring_size,sizeof(octa));
705
if (!l)  panic("No room for the local registers");
706 707 708 709 710

@ @<Initialize...@>=
sclock.l=sclock.h=0;
profile_started=false;
halted=false;
711
stdin_buf_start=stdin_buf_end=stdin_buf;
712 713 714 715 716 717 718 719 720
good_guesses=bad_guesses=0;
profiling=false;
interrupt=false;

@ @<Boot the machine@>=
memset(l,0,lring_size*sizeof(octa));
memset(g,0,sizeof(g));
L=O=S=0;
G=g[rG].l=255;
721
#ifdef MMIX_BOOT
722 723 724 725
g[rK] = zero_octa;
#else
g[rK] = neg_one;
#endif
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
g[rN].h=(VERSION<<24)+(SUBVERSION<<16)+(SUBSUBVERSION<<8);
g[rN].l=ABSTIME; /* see comment and warning above */
g[rT].h=0x80000000;g[rT].l=0x00000000;
g[rTT].h=0x80000000;g[rTT].l=0x00000000;
g[rV].h=0x12340D00;
g[rV].l=0x00002000;
cur_round=ROUND_NEAR;
@z

@x
  if (((S-O-L)&lring_mask)==0) stack_store();
@y
  if (((S-O-L)&lring_mask)==0) stack_store(l[S&lring_mask]);
@z

@x
@d test_store_bkpt(ll) if ((ll)->bkpt&write_bit) breakpoint=tracing=true
@y
Martin Ruckert's avatar
Martin Ruckert committed
744
@d test_store_bkpt(ll) if ((ll)->bkpt&write_bit) breakpoint|=write_bit,tracing=true
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
@z


stack_store must implement the rC register.

@x
@<Sub...@>=
void stack_store @,@,@[ARGS((void))@];@+@t}\6{@>
void stack_store()
{
  register mem_tetra *ll=mem_find(g[rS]);
  register int k=S&lring_mask;
  ll->tet=l[k].h;@+test_store_bkpt(ll);
  (ll+1)->tet=l[k].l;@+test_store_bkpt(ll+1);
  if (stack_tracing) {
    tracing=true;
    if (cur_line) show_line();
    printf("             M8[#%08x%08x]=l[%d]=#%08x%08x, rS+=8\n",
              g[rS].h,g[rS].l,k,l[k].h,l[k].l);
  }
  g[rS]=incr(g[rS],8),  S++;
}
@y
@<Stack store@>=
void stack_store @,@,@[ARGS((octa))@];@+@t}\6{@>
void stack_store(x)
  octa x;
{ unsigned int pw_bit, new_pw_bit;
  mem_tetra *ll;
  pw_bit=g[rQ].h&PW_BIT;
  new_pw_bit=new_Q.h&PW_BIT;
776
  if(!MMIX_STO(x,g[rS])) /* implementing the rC register */
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
  {   /* set CP_BIT */
      g[rQ].l |= CP_BIT;
      new_Q.l |= CP_BIT;
      if (g[rC].l&0x02)  /* Write bit */
      {  int s;
         octa address, base, offset,mask;
         mask.h=mask.l=0xFFFFFFFF;
         g[rQ].h &=~PW_BIT;  /* restore PW_BIT */
         new_Q.h &=~PW_BIT;
         g[rQ].h |= pw_bit;
         new_Q.h |= new_pw_bit;
         s    = (g[rV].h>>8)&0xFF;  /* extract the page size from rV */
         mask = shift_left(mask,s);
         offset.h = g[rS].h&~mask.h,offset.l = g[rS].l&~mask.l;
         mask.h &= 0x0000FFFF;     /* reduce mask to 48 bits */
         base.h = g[rC].h&mask.h,base.l = g[rC].l&mask.l;
         address.h=base.h|offset.h,address.l=base.l|offset.l;
794
         MMIX_STO(x,address);
795 796 797 798 799
      }
  }
  ll=mem_find(g[rS]);
  test_store_bkpt(ll);
  test_store_bkpt(ll+1);
800
  mmix_stack_trace("             M8[#%08x%08x]=#%08x%08x, rS+=8\n",
801 802 803 804 805 806 807 808 809 810 811
              g[rS].h,g[rS].l,x.h,x.l);
  g[rS]=incr(g[rS],8),  S++;
}

@ @<Sub...@>=
@<Stack store@>@;
@z

@x
@d test_load_bkpt(ll) if ((ll)->bkpt&read_bit) breakpoint=tracing=true
@y
Martin Ruckert's avatar
Martin Ruckert committed
812
@d test_load_bkpt(ll) if ((ll)->bkpt&read_bit) breakpoint|=read_bit,tracing=true
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
@z

Same with stack load.
@x
@<Sub...@>=
void stack_load @,@,@[ARGS((void))@];@+@t}\6{@>
@y
@<Sub...@>=
@<Stack load@>@;

@ @<Stack load@>=
void stack_load @,@,@[ARGS((void))@];@+@t}\6{@>
@z

@x
  l[k].h=ll->tet;@+test_load_bkpt(ll);
  l[k].l=(ll+1)->tet;@+test_load_bkpt(ll+1);
@y
  test_load_bkpt(ll);@+test_load_bkpt(ll+1);
832
  MMIX_LDO(l[k],g[rS]);
833 834
@z

835 836
showing lines is part of main.

837
@x
838 839
  if (stack_tracing) {
    tracing=true;
840
    if (cur_line) show_line();
841 842 843
    printf("             rS-=8, l[%d]=M8[#%08x%08x]=#%08x%08x\n",
              k,g[rS].h,g[rS].l,l[k].h,l[k].l);
  }
844
@y
845 846
  mmix_stack_trace("             rS-=8, l[%d]=M8[#%08x%08x]=#%08x%08x\n",
              k,g[rS].h,g[rS].l,l[k].h,l[k].l);
847 848
@z

849

850 851 852 853 854 855 856 857 858 859 860
@x
@<Sub...@>=
int register_truth @,@,@[ARGS((octa,mmix_opcode))@];@+@t}\6{@>
@y
@<Sub...@>=
@<Register truth@>@;

@ @<Register truth@>=
int register_truth @,@,@[ARGS((octa,mmix_opcode))@];@+@t}\6{@>
@z

861
Make sure we do not branch from a positive to a negative address.
862 863 864 865 866 867 868 869
@x
   inst_ptr=z;
@y
   if ((z.h&sign_bit) && !(loc.h&sign_bit))
   goto protection_violation;
   inst_ptr=z;
@z

870 871 872 873 874 875 876
@x
   if (g[rI].l<=2 && g[rI].l && g[rI].h==0) tracing=breakpoint=true;
@y
   if (g[rI].l<=2 && g[rI].l && g[rI].h==0) g[rQ].l |= IN_BIT, new_Q.l |= IN_BIT, tracing=true, breakpoint|=trace_bit;
@z


877
Loading is supposed to use the functions from libconfig
878 879

@x
880 881 882 883 884 885 886
case LDB: case LDBI: case LDBU: case LDBUI:@/
 i=56;@+j=(w.l&0x3)<<3; goto fin_ld;
case LDW: case LDWI: case LDWU: case LDWUI:@/
 i=48;@+j=(w.l&0x2)<<3; goto fin_ld;
case LDT: case LDTI: case LDTU: case LDTUI:@/
 i=32;@+j=0;@+ goto fin_ld;
case LDHT: case LDHTI: i=j=0;
887 888 889 890 891
fin_ld: ll=mem_find(w);@+test_load_bkpt(ll);
 x.h=ll->tet;
 x=shift_right(shift_left(x,j),i,op&0x2);
check_ld:@+if (w.h&sign_bit) goto privileged_inst; 
@y
892
case LDB: case LDBI: case LDBU: case LDBUI:@/
893
 i=56;@+j=56;@+if(!MMIX_LDB(x,w)) goto page_fault; goto fin_ld;
894
case LDW: case LDWI: case LDWU: case LDWUI:@/
895
 i=48;@+j=48;@+if(!MMIX_LDW(x,w)) goto page_fault; goto fin_ld;
896
case LDT: case LDTI: case LDTU: case LDTUI:@/
897 898
 i=32;@+j=32;@+if(!MMIX_LDT(x,w)) goto page_fault; goto fin_ld;
case LDHT: case LDHTI: i=j=0;@+if(!MMIX_LDT(x,w)) goto page_fault;
899
x.h=x.l;x.l=0;
900
fin_ld: ll=mem_find(w);@+test_load_bkpt(ll);
901
 if (op&0x2) x=shift_right(shift_left(x,i),i,op&0x2);
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
check_ld:@+ if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto translation_bypassed_inst;
 goto store_x;
page_fault:
 if ((g[rK].h & g[rQ].h) != 0 || (g[rK].l & g[rQ].l) != 0) 
 { x.h=0, x.l=inst;
   y = w;
   z = zero_octa;
   @<Initiate a trap interrupt@>
   inst_ptr=y=g[rTT];
 }
 break;
@z

@x
case LDO: case LDOI: case LDOU: case LDOUI: case LDUNC: case LDUNCI:
 w.l&=-8;@+ ll=mem_find(w);
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
 x.h=ll->tet;@+ x.l=(ll+1)->tet;
 goto check_ld;
case LDSF: case LDSFI: ll=mem_find(w);@+test_load_bkpt(ll);
 x=load_sf(ll->tet);@+ goto check_ld;
@y
case LDO: case LDOI: case LDOU: case LDOUI: 
 w.l&=-8;@+ ll=mem_find(w);
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
927
 if (!MMIX_LDO(x,w)) goto page_fault;
928 929 930 931
 goto check_ld;
case LDUNC: case LDUNCI:
 w.l&=-8;@+ ll=mem_find(w);
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
932
 if (!MMIX_LDO_UNCACHED(x,w)) goto page_fault;
933 934
 goto check_ld;
case LDSF: case LDSFI: ll=mem_find(w);@+test_load_bkpt(ll);
935
 if (!MMIX_LDT(x,w)) goto page_fault;
936 937 938
 x=load_sf(x.l);@+ goto check_ld;
@z

939
Same for storing.
940 941 942 943

@x
case STB: case STBI: case STBU: case STBUI:@/
 i=56;@+j=(w.l&0x3)<<3; goto fin_pst;
944 945 946 947 948 949 950
@y
case STB: case STBI: case STBU: case STBUI:@/
 if ((op&0x2)==0) {
   a=shift_right(shift_left(b,56),56,0);
   if (a.h!=b.h || a.l!=b.l) exc|=V_BIT;
 }
 if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto translation_bypassed_inst;
951
 if (!MMIX_STB(b,w)) goto page_fault;
952
 goto fin_st;
953 954 955
@z

@x
956 957
case STW: case STWI: case STWU: case STWUI:@/
 i=48;@+j=(w.l&0x2)<<3; goto fin_pst;
958 959 960 961 962 963 964
@y
case STW: case STWI: case STWU: case STWUI:@/
 if ((op&0x2)==0) {
   a=shift_right(shift_left(b,48),48,0);
   if (a.h!=b.h || a.l!=b.l) exc|=V_BIT;
 }
 if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto translation_bypassed_inst;
965
 if (!MMIX_STW(b,w)) goto page_fault;
966
 goto fin_st;
967 968 969
@z

@x
970 971 972 973 974 975 976 977 978
case STT: case STTI: case STTU: case STTUI:@/
 i=32;@+j=0;
fin_pst: ll=mem_find(w);
 if ((op&0x2)==0) {
   a=shift_right(shift_left(b,i),i,0);
   if (a.h!=b.h || a.l!=b.l) exc|=V_BIT;
 }
 ll->tet^=(ll->tet^(b.l<<(i-32-j))) & ((((tetra)-1)<<(i-32))>>j);
 goto fin_st;
979 980 981 982 983 984 985 986
@y
case STT: case STTI: case STTU: case STTUI:@/
 if ((op&0x2)==0) {
   a=shift_right(shift_left(b,32),32,0);
   if (a.h!=b.h || a.l!=b.l) exc|=V_BIT;
 }
fin_pst:
 if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto translation_bypassed_inst;
987
 if (!MMIX_STT(b,w)) goto page_fault;
988
fin_st:
989
 ll=mem_find(w); test_store_bkpt(ll);
990
#ifdef MMIX_MEM_TET
991 992
 w.l&=-8;@+ll=mem_find(w);
 a.h=ll->tet;@+ a.l=(ll+1)->tet; /* for trace output */
993
#endif
994 995 996 997 998
 break;
@z


@x
999 1000 1001
case STSF: case STSFI: ll=mem_find(w);
 ll->tet=store_sf(b);@+exc=exceptions;
 goto fin_st;
1002 1003 1004 1005 1006 1007 1008 1009
@y
case STSF: case STSFI: 
 a.l=b.l= store_sf(b);@+exc=exceptions;
 a.h=b.h=0;
 goto fin_pst;
@z

@x
1010 1011 1012 1013 1014
case STHT: case STHTI: ll=mem_find(w);@+ ll->tet=b.h;
fin_st: test_store_bkpt(ll);
 w.l&=-8;@+ll=mem_find(w);
 a.h=ll->tet;@+ a.l=(ll+1)->tet; /* for trace output */
 goto check_st; 
1015 1016 1017 1018 1019 1020 1021 1022
@y
case STHT: case STHTI: 
  a.l=b.l=b.h;
  b.h=a.h=0;
  goto fin_pst;
@z

@x
1023 1024 1025 1026 1027 1028 1029 1030
case STCO: case STCOI: b.l=xx;
case STO: case STOI: case STOU: case STOUI: case STUNC: case STUNCI:
 w.l&=-8;@+ll=mem_find(w);
 test_store_bkpt(ll);@+ test_store_bkpt(ll+1);
 ll->tet=b.h;@+ (ll+1)->tet=b.l;
check_st:@+if (w.h&sign_bit) goto privileged_inst;
 break;
@y
1031 1032 1033 1034
case STCO: case STCOI: b.l=xx;
case STO: case STOI: case STOU: case STOUI:
 w.l&=-8;
 if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto translation_bypassed_inst;
1035
 if (!MMIX_STO(b,w)) goto page_fault;
1036
 ll=mem_find(w); test_store_bkpt(ll);test_store_bkpt(ll+1);
1037 1038
 break;
case STUNC: case STUNCI:
1039
 w.l&=-8;
1040
 if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto translation_bypassed_inst;
1041
 if (!MMIX_STO_UNCACHED(b,w)) goto page_fault;
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
 ll=mem_find(w); test_store_bkpt(ll);test_store_bkpt(ll+1);
 break;
@z

@x
case CSWAP: case CSWAPI: w.l&=-8;@+ll=mem_find(w);
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
 a=g[rP];
 if (ll->tet==a.h && (ll+1)->tet==a.l) {
   x.h=0, x.l=1;
   test_store_bkpt(ll);@+test_store_bkpt(ll+1);
   ll->tet=b.h, (ll+1)->tet=b.l;
   strcpy(rhs,"M8[%#w]=%#b");
 }@+else {
   b.h=ll->tet, b.l=(ll+1)->tet;
   g[rP]=b;
   strcpy(rhs,"rP=%#b");
 }
@y
case CSWAP: case CSWAPI: w.l&=-8;@+ll=mem_find(w);
 test_load_bkpt(ll);@+test_load_bkpt(ll+1);
 if ((w.h&sign_bit) && !(loc.h&sign_bit)) goto  translation_bypassed_inst;
1064
 if (!MMIX_LDO(a,w)) goto page_fault;
1065 1066 1067
 if (g[rP].h==a.h && g[rP].l==a.l) {
   x.h=0, x.l=1;
   test_store_bkpt(ll);@+test_store_bkpt(ll+1);
1068
   if (!MMIX_STO(b,w)) goto page_fault;
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
   strcpy(rhs,"M8[%#w]=%#b");
 }@+else {
   b=a;
   a = g[rP];
   g[rP]=b;
   x.h=0, x.l=0;
   strcpy(rhs,"rP=%#b");
 }
@z


@x
case GET:@+if (yy!=0 || zz>=32) goto illegal_inst;
  x=g[zz];
  goto store_x;
case PUT: case PUTI:@+ if (yy!=0 || xx>=32) goto illegal_inst;
  strcpy(rhs,"%z = %#z");
  if (xx>=8) {
    if (xx<=11 && xx!=8) goto illegal_inst; /* can't change rN, rO, rS */
    if (xx<=18) goto privileged_inst;
    if (xx==rA) @<Get ready to update rA@>@;
    else if (xx==rL) @<Set $L=z=\min(z,L)$@>@;
    else if (xx==rG) @<Get ready to update rG@>;
  }
  g[xx]=z;@+zz=xx;@+break;
@y
case GET:@+if (yy!=0 || zz>=32) goto illegal_inst;
  x=g[zz];
  if (zz==rQ) { 
      new_Q.h = new_Q.l = 0;
  }
  goto store_x;
case PUT: case PUTI:@+ if (yy!=0 || xx>=32) goto illegal_inst;
  strcpy(rhs,"%z = %#z");
  if (xx>=8) {
    if (xx==9) goto illegal_inst; /* can't change rN */
    if (xx<=18 && !(loc.h&sign_bit)) goto privileged_inst;
    if (xx==rA) @<Get ready to update rA@>@;
    else if (xx==rL) @<Set $L=z=\min(z,L)$@>@;
    else if (xx==rG) @<Get ready to update rG@>@;
    else if (xx==rQ)
    { new_Q.h |= z.h &~ g[rQ].h;@+
      new_Q.l |= z.l &~ g[rQ].l;
      z.l |= new_Q.l;@+
      z.h |= new_Q.h;@+
    }
  }
  g[xx]=z;@+zz=xx;@+break;
@z

@x
case PUSHGO: case PUSHGOI: inst_ptr=w;@+goto push;
case PUSHJ: case PUSHJB: inst_ptr=z;
push:@+if (xx>=G) {
   xx=L++;
   if (((S-O-L)&lring_mask)==0) stack_store();
 }
@y
case PUSHGO: case PUSHGOI: 
if ((w.h&sign_bit) && !(loc.h&sign_bit))
goto protection_violation;
inst_ptr=w;@+goto push;
case PUSHJ: case PUSHJB: 
if ((z.h&sign_bit) && !(loc.h&sign_bit))
goto protection_violation;  
inst_ptr=z;
push:@+if (xx>=G) {
   xx=L++;
   if (((S-O-L)&lring_mask)==0) stack_store(l[S&lring_mask]);
 }
@z

@x
 y=g[rJ];@+ z.l=yz<<2;@+ inst_ptr=oplus(y,z);
@y
 y=g[rJ];@+ z.l=yz<<2;
 { octa tmp;
   tmp=oplus(y,z);
   if ((tmp.h&sign_bit) && !(loc.h&sign_bit))
   goto protection_violation;  
   inst_ptr = tmp;
}
@z

@x
case SAVE:@+if (xx<G || yy!=0 || zz!=0) goto illegal_inst;
 l[(O+L)&lring_mask].l=L, L++;
 if (((S-O-L)&lring_mask)==0) stack_store();
@y
case SAVE:@+if (xx<G || yy!=0 || zz!=0) goto illegal_inst;
 l[(O+L)&lring_mask].l=L, L++;
 if (((S-O-L)&lring_mask)==0) stack_store(l[S&lring_mask]);
@z

@x
 while (g[rO].l!=g[rS].l) stack_store();
@y
 while (g[rO].l!=g[rS].l) stack_store(l[S&lring_mask]);
@z

@x
@<Store |g[k]| in the register stack...@>=
ll=mem_find(g[rS]);
if (k==rZ+1) x.h=G<<24, x.l=g[rA].l;
else x=g[k];
ll->tet=x.h;@+test_store_bkpt(ll);
(ll+1)->tet=x.l;@+test_store_bkpt(ll+1);
if (stack_tracing) {
  tracing=true;
  if (cur_line) show_line();
  if (k>=32) printf("             M8[#%08x%08x]=g[%d]=#%08x%08x, rS+=8\n",
            g[rS].h,g[rS].l,k,x.h,x.l);
  else printf("             M8[#%08x%08x]=%s=#%08x%08x, rS+=8\n",
            g[rS].h,g[rS].l,k==rZ+1? "(rG,rA)": special_name[k],x.h,x.l);
}
S++, g[rS]=incr(g[rS],8);
@y
@<Store |g[k]| in the register stack...@>=
if (k==rZ+1) x.h=G<<24, x.l=g[rA].l;
else x=g[k];
stack_store(x);
@z

@x
@ @<Load |g[k]| from the register stack@>=
g[rS]=incr(g[rS],-8);
ll=mem_find(g[rS]);
test_load_bkpt(ll);@+test_load_bkpt(ll+1);
if (k==rZ+1) {
  x.l=G=g[rG].l=ll->tet>>24, a.l=g[rA].l=(ll+1)->tet&0x3ffff;
  if (G<32) x.l=G=g[rG].l=32;
}@+else g[k].h=ll->tet, g[k].l=(ll+1)->tet;
if (stack_tracing) {
  tracing=true;
  if (cur_line) show_line();
  if (k>=32) printf("             rS-=8, g[%d]=M8[#%08x%08x]=#%08x%08x\n",
            k,g[rS].h,g[rS].l,ll->tet,(ll+1)->tet);
  else if (k==rZ+1) printf("             (rG,rA)=M8[#%08x%08x]=#%08x%08x\n",
            g[rS].h,g[rS].l,ll->tet,(ll+1)->tet);
  else printf("             rS-=8, %s=M8[#%08x%08x]=#%08x%08x\n",
            special_name[k],g[rS].h,g[rS].l,ll->tet,(ll+1)->tet);
}
@y
@ @<Load |g[k]| from the register stack@>=
g[rS]=incr(g[rS],-8);
ll=mem_find(g[rS]);
test_load_bkpt(ll);@+test_load_bkpt(ll+1);
1216 1217
if (k==rZ+1) { 
  if (!MMIX_LDO(a,g[rS])) { w=g[rS]; goto page_fault; }
1218 1219 1220 1221
  x.l=G=g[rG].l=a.h>>24;
  a.l=g[rA].l=a.l&0x3ffff;
}
else
1222 1223
  if (!MMIX_LDO(g[k],g[rS]))  { w=g[rS]; goto page_fault; }
if (k>=32) mmix_stack_trace("             rS-=8, g[%d]=M8[#%08x%08x]=#%08x%08x\n",
1224 1225
          k,g[rS].h,g[rS].l,g[k].h,g[k].l);
else if (k==rZ+1) mmix_stack_trace("             (rG,rA)=M8[#%08x%08x]=#%08x%08x\n",
1226
          g[rS].h,g[rS].l,a.h,a.l);
1227 1228
else mmix_stack_trace("             rS-=8, %s=M8[#%08x%08x]=#%08x%08x\n",
          special_name[k],g[rS].h,g[rS].l,g[k].h,g[k].l);
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
@z

@x
@ The cache maintenance instructions don't affect this simulation,
because there are no caches. But if the user has invoked them, we do
provide a bit of information when tracing, indicating the scope of the
instruction.

@<Cases for ind...@>=
case SYNCID: case SYNCIDI: case PREST: case PRESTI:
case SYNCD: case SYNCDI: case PREGO: case PREGOI:
case PRELD: case PRELDI: x=incr(w,xx);@+break;
@y
@ The cache maintenance instructions do affect this simulation.

@<Cases for ind...@>=
case SYNCID: case SYNCIDI:
1246
 MMIX_DELETE_ICACHE(w,xx+1);
1247
 if (loc.h&sign_bit)
1248
   MMIX_DELETE_DCACHE(w,xx+1);
1249
 else
1250
   MMIX_STORE_DCACHE(w,xx+1);
1251 1252 1253
 break;
case PREST: case PRESTI: x=incr(w,xx);@+break;
case SYNCD: case SYNCDI:  
1254
 MMIX_STORE_DCACHE(w,xx+1);
1255
 if (loc.h&sign_bit)
1256
   MMIX_DELETE_DCACHE(w,xx+1);
1257 1258
 break;
case PREGO: case PREGOI:
1259
 MMIX_PRELOAD_ICACHE(w,xx+1);
1260 1261
 break;
case PRELD: case PRELDI:
1262
 MMIX_PRELOAD_DCACHE(w,xx+1);
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308
 x=incr(w,xx);@+break;
@z

@x
case GO: case GOI: x=inst_ptr;@+inst_ptr=w;@+goto store_x;
case JMP: case JMPB: inst_ptr=z;
case SWYM: break;
case SYNC:@+if (xx!=0 || yy!=0 || zz>7) goto illegal_inst;
 if (zz<=3) break;
case LDVTS: case LDVTSI: privileged_inst: strcpy(lhs,"!privileged");
 goto break_inst;
illegal_inst: strcpy(lhs,"!illegal");
break_inst: breakpoint=tracing=true;
 if (!interacting && !interact_after_break) halted=true;
 break;
@y
case GO: case GOI: 
   if ((w.h&sign_bit) && !(loc.h&sign_bit))
   goto protection_violation;  
   x=inst_ptr;@+inst_ptr=w;@+goto store_x;
case JMP: case JMPB: 
   if ((z.h&sign_bit) && !(loc.h&sign_bit))
   goto protection_violation;  
   inst_ptr=z;@+break;
case SYNC:@+if (xx!=0 || yy!=0 || zz>7) goto illegal_inst;
/* should give a privileged instruction interrupt in case zz  >3 */
 else if (zz==4) /* power save mode */
 {  const unsigned int cycle_speed = 1000000; /* cycles per ms */
    const unsigned int max_wait = 100;
    int d, ms;
    if (g[rI].h!=0 || g[rI].l>max_wait*cycle_speed) /* large rI values */
      ms = max_wait;
    else
      ms = g[rI].l/cycle_speed;
    if (ms>0)
    {  MMIX_DELAY(ms,d); 
       g[rI]=incr(g[rI],-(ms-d)*cycle_speed);
     }
     else if (g[rI].l>1000)
        g[rI].l = g[rI].l-1000;
     else if (g[rI].l>100)
        g[rI].l = g[rI].l-100;
     else if (g[rI].l>10)
        g[rI].l = g[rI].l-10;
 }
 else if (zz==5) /* empty write buffer */
1309
   MMIX_WRITE_DCACHE();
1310
 else if (zz==6) /* clear VAT cache */
1311 1312
 {  MMIX_CLEAR_DVTC();
    MMIX_CLEAR_IVTC();
1313 1314
 }
 else if (zz==7) /* clear instruction and data cache */
1315 1316
 { MMIX_CLEAR_DCACHE();
   MMIX_CLEAR_ICACHE();
1317 1318 1319 1320 1321
 }
 break;
case LDVTS: case LDVTSI:   
{ if (!(loc.h&sign_bit)) goto privileged_inst;
  if (w.h&sign_bit) goto illegal_inst;
1322
  x = MMIX_UPDATE_VTC(w);
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
  goto store_x;
}
break;
case SWYM:
 if ((inst&0xFFFFFF)!=0) 
 {   char buf[256+1];
     int n;
     strcpy(rhs,"$%x,%z");
     z.h=0, z.l=yz;
     x.h=0, x.l=xx;
     tracing=interacting;
1334
     breakpoint=(breakpoint&0xFF) | (yz<<8);
1335 1336 1337 1338 1339
     interrupt=false;
     @<Set |b| from register X@>;
     n=mmgetchars((unsigned char *)buf,256,b,0);
     buf[n]=0;
     if (n>6 && strncmp(buf,"DEBUG ",6)==0) 
1340
     { printf("\n%s!\n",buf+6);
1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
       sprintf(rhs,"rF=#%08X%08X\n",g[rF].h, g[rF].l);
       tracing= true;
     }
 }
 else
   strcpy(rhs,"");
break;
translation_bypassed_inst: strcpy(lhs,"!absolute address");
g[rQ].h |= N_BIT; new_Q.h |= N_BIT; /* set the n bit */
 goto break_inst;
privileged_inst: strcpy(lhs,"!kernel only");
g[rQ].h |= K_BIT; new_Q.h |= K_BIT; /* set the k bit */
 goto break_inst;
illegal_inst: strcpy(lhs,"!broken");
g[rQ].h |= B_BIT; new_Q.h |= B_BIT; /* set the b bit */
 goto break_inst;
protection_violation: strcpy(lhs,"!protected");
g[rQ].h |= P_BIT; new_Q.h |= P_BIT; /* set the p bit */
 goto break_inst;
security_inst: strcpy(lhs,"!insecure");
1361
break_inst: tracing=true, breakpoint|=trace_bit;
1362 1363 1364 1365 1366 1367 1368
 if (!interacting && !interact_after_break) halted=true;
break;
@z

@x
case TRAP:@+if (xx!=0 || yy>max_sys_call) goto privileged_inst;
@y
1369 1370 1371
#ifndef MMIX_TRAP
case TRAP:@+if (xx!=0 || yy>max_sys_call) goto privileged_inst;
@z
1372

1373 1374 1375 1376 1377
@x
 x=g[255]=g[rBB];@+break;
@y
 x=g[255]=g[rBB];@+break;
#else
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
case TRAP:@+if (xx==0 && yy<=max_sys_call) 
      { strcpy(rhs,trap_format[yy]);
        a=incr(b,8);
        @<Prepare memory arguments $|ma|={\rm M}[a]$ and $|mb|={\rm M}[b]$ if needed@>;
      }
     else strcpy(rhs, "%#x -> %#y");
 if (tracing && !show_operating_system) interact_after_resume = true;    
 x.h=sign_bit, x.l=inst;
 @<Initiate a trap interrupt@>
 inst_ptr=y=g[rT];
 break;
1389
#endif
1390 1391
@z

1392 1393 1394 1395 1396 1397
This is the code for TRAP 0,Halt,0
@x
if (!zz) halted=breakpoint=true;
@y
if (!zz) halted=true, breakpoint|=trace_bit;
@z
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410

@x
"$255 = Fopen(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fclose(%!z) = %x",
"$255 = Fread(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fgets(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fgetws(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fwrite(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fputs(%!z,%#b) = %x",
"$255 = Fputws(%!z,%#b) = %x",
"$255 = Fseek(%!z,%b) = %x",
"$255 = Ftell(%!z) = %x"};
@y
1411
#ifdef MMIX_TRAP
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421
"$255 = Fopen(%!z,M8[%#b]=%#q,M8[%#a]=%p) -> %#y",
"$255 = Fclose(%!z) -> %#y",
"$255 = Fread(%!z,M8[%#b]=%#q,M8[%#a]=%p) -> %#y",
"$255 = Fgets(%!z,M8[%#b]=%#q,M8[%#a]=%p) -> %#y",
"$255 = Fgetws(%!z,M8[%#b]=%#q,M8[%#a]=%p) -> %#y",
"$255 = Fwrite(%!z,M8[%#b]=%#q,M8[%#a]=%p) -> %#y",
"$255 = Fputs(%!z,%#b) -> %#y",
"$255 = Fputws(%!z,%#b) -> %#y",
"$255 = Fseek(%!z,%b) -> %#y",
"$255 = Ftell(%!z) -> %#y"};
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
#else
"$255 = Fopen(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fclose(%!z) = %x",
"$255 = Fread(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fgets(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fgetws(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fwrite(%!z,M8[%#b]=%#q,M8[%#a]=%p) = %x",
"$255 = Fputs(%!z,%#b) = %x",
"$255 = Fputws(%!z,%#b) = %x",
"$255 = Fseek(%!z,%b) = %x",
"$255 = Ftell(%!z) = %x"};
#endif
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
@z



@x
@ @<Prepare memory arguments...@>=
if (arg_count[yy]==3) {
  ll=mem_find(b);@+test_load_bkpt(ll);@+test_load_bkpt(ll+1);
  mb.h=ll->tet, mb.l=(ll+1)->tet;
  ll=mem_find(a);@+test_load_bkpt(ll);@+test_load_bkpt(ll+1);
  ma.h=ll->tet, ma.l=(ll+1)->tet;
}
@y
@ @<Prepare memory arguments...@>=
if (arg_count[yy]==3) {
1449 1450
   MMIX_LDO(mb,b);
   MMIX_LDO(ma,a);
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
}
@z

@x
@ The input/output operations invoked by \.{TRAP}s are
done by subroutines in an auxiliary program module called {\mc MMIX-IO}.
Here we need only declare those subroutines, and write three primitive
interfaces on which they depend.

@ @<Glob...@>=
@y
1462
@ @(mmix-io.h@>=
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
@z

@x
@<Sub...@>=
int mmgetchars @,@,@[ARGS((char*,int,octa,int))@];@+@t}\6{@>
int mmgetchars(buf,size,addr,stop)
  char *buf;
  int size;
  octa addr;
  int stop;
{
  register char *p;
  register int m;
  register mem_tetra *ll;
  register tetra x;
  octa a;
  for (p=buf,m=0,a=addr; m<size;) {
    ll=mem_find(a);@+test_load_bkpt(ll);
    x=ll->tet;
    if ((a.l&0x3) || m>size-4) @<Read and store one byte; |return| if done@>@;
    else @<Read and store up to four bytes; |return| if done@>@;
  }
  return size;
}
@y
@(libmmget.c@>=
#include <stdio.h>
1490
#include <setjmp.h>
1491
#include "libconfig.h"
1492 1493 1494 1495
#include <time.h>
#include "libtype.h"
#include "libglobals.h"
#include "mmixlib.h"
1496
#include "libarith.h"
1497
#include "libimport.h"
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508

int mmgetchars(buf,size,addr,stop)
  unsigned char *buf;
  int size;
  octa addr;
  int stop;
{
  register unsigned char *p;
  register int m;
  octa x;
  octa a;
1509
  MMIX_LOCAL_LL
1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542
  for (p=buf,m=0,a=addr; m<size;) {
    if ((a.l&0x7) || m+8>size) @<Read and store one byte; |return| if done@>@;
    else @<Read and store eight bytes; |return| if done@>@;
  }
  return size;
}
@z

@x
@ @<Read and store one byte...@>=
{
  *p=(x>>(8*((~a.l)&0x3)))&0xff;
  if (!*p && stop>=0) {
    if (stop==0) return m;
    if ((a.l&0x1) && *(p-1)=='\0') return m-1;
  }
  p++,m++,a=incr(a,1);
}

@ @<Read and store up to four bytes...@>=
{
  *p=x>>24;
  if (!*p && (stop==0 || (stop>0 && x<0x10000))) return m;
  *(p+1)=(x>>16)&0xff;
  if (!*(p+1) && stop==0) return m+1;
  *(p+2)=(x>>8)&0xff;
  if (!*(p+2) && (stop==0 || (stop>0 && (x&0xffff)==0))) return m+2;
  *(p+3)=x&0xff;
  if (!*(p+3) && stop==0) return m+3;
  p+=4,m+=4,a=incr(a,4);
}
@y
@ @<Read and store one byte...@>=
1543
{ MMIX_LDB(x,a);
1544 1545 1546 1547 1548 1549 1550 1551 1552
    *p=x.l&0xff;
  if (!*p && stop>=0) {
    if (stop==0) return m;
    if ((a.l&0x1) && *(p-1)=='\0') return m-1;
  }
  p++,m++,a=incr(a,1);
}

@ @<Read and store eight bytes...@>=
1553
{ MMIX_LDO(x,a);
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588