/* Copyright K. Mitchell 1995. This file has been generated by a program and is not intended to be read by a human. Run the file occam.w through nuweb. This produces a file called occam.tex. Typeset this file using LaTeX and then preview the resulting .dvi file, or just print it out. */ #include #include #include typedef int bool; #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #include #include #include "occam.h" #include "pool.h" #include "interpreter.h" char input_buffer[80]; /* Input characters are buffered here. */ int bufptr = 0; /* A pointer into the above buffer. */ int eof_flag = FALSE; /* Have we exhausted the input yet? */ int label_counter = 1; process_queue active_process = NULL; process_queue prev_process = NULL; process_queue reincarnated_processes = NULL; int memory_used = 0; jump_table in_jt = NULL; int in_fjp = 0; int code_size = 0; instruction *Code; process_list *Bpt; void initialise_display(int nesting_level, stack_slot **the_SP, stack_slot **the_BP) { stack_slot *SP = *the_SP, *BP = *the_BP; (--SP)->as_addr = BP; *the_BP = SP; while (--nesting_level > 0) *(--SP) = *(--BP); (--SP)->as_addr = *the_BP; *the_SP = SP; } code exit_scope_code() { int i = exit_scope(); code c = NULL; while (i != 0) { if (i > 0) c = build_ccode( c, Pop, i, END); else c = build_ccode( c, Dcb, -i / CHANNEL_BLOCK_SIZE, END); i = exit_scope(); } return(c); } #define OVERFLOW_CHECKSUM 0x55555555 process create_process(int PC, int stacksize) { process p; { process q = NULL; p = reincarnated_processes; while ((p != NULL) && (p->stack_size != stacksize)) { q = p; p = p->next; } if (p) /* Unlink it from the reincarnated_processes queue */ if (p == reincarnated_processes) reincarnated_processes = p->next; else q->next = p->next; else { /* No suitable reincarnated process */ int size = sizeof(struct process_block) + stacksize*sizeof(stack_slot); p = malloc(size); memory_used += size; } } p->PC = PC; p->next = NULL; p->children = 0; p->parent = active_process; if (active_process) active_process->children++; p->stack_size = stacksize; p->status = ACTIVE; p->SP = (stack_slot *)((char *)p + sizeof(process_block)) + stacksize; p->BP = NULL; p->stack_overflow_check = OVERFLOW_CHECKSUM; p->step_process = p->trace_process = FALSE; return p; } void process_statistics() { int free_space = 0; process p = reincarnated_processes; while (p != NULL) { free_space += sizeof(struct process_block) + p->stack_size*sizeof(stack_slot); p = p->next; } printf("Total process space allocated = %d, of which %d still in use at exit.\n", memory_used, memory_used - free_space); } code code_args(int op, va_list args) { code c, r; code_entry *n; int arg = 0; int next_op; if (op == END) return NULL; if ((void *)op == NULL) { /* Assume we have found null code pointer */ next_op = va_arg(args,int); c = code_args(next_op, args); } else if (op > 255) { /* Assume we have found some code */ c = (code) op; next_op = va_arg(args,int); if (r = code_args(next_op, args)) { c->last->next = r->first; c->last = r->last; dispose(r); } } else { switch (op) { case Stop: case Wait: case Add : case Sub : case Mul : case Div : case Mod : case Lt : case Leq : case Gt : case Geq : case Eq : case Neq : case Idx : case Ida : case Sta : case Ret : case Out : case Swap: case Input:case Output: { next_op = va_arg(args,int); break; } default: { arg = va_arg(args,int); next_op = va_arg(args,int); break; } } n = new(struct code_entry); n->inst.op = op; n->inst.arg = arg; if (c = code_args(next_op, args)) { n->next = c->first; c->first = n; } else { c = new(struct instruction_sequence); c->first = c->last = n; n->next = NULL; } } return c; } code build_icode(int op, ...) { code c; va_list args; va_start(args,op); c = code_args(op, args); va_end(args); return c; } code build_ccode(code c, ...) { code c1; va_list args; va_start(args,c); c1 = code_args((int) c, args); va_end(args); return c1; } void dispose_code(code c) { if (c) { code_entry *e = c->first; while (e) { code_entry *n = e->next; dispose(e); e = n; } dispose(c); } } int constant_expression(code c, int *v) { code_entry *e = c ? c->first : NULL; int S[20]; int sp = -1; instruction i; while(e) { i = e->inst; e = e->next; switch (i.op) { case Ldc: S[++sp] = i.arg; continue; case Add: if (sp > 0) S[sp-1] += S[sp]; break; case Sub: if (sp > 0) S[sp-1] -= S[sp]; break; case Mul: if (sp > 0) S[sp-1] *= S[sp]; break; case Div: if (sp > 0) S[sp-1] /= S[sp]; break; case Mod: if (sp > 0) S[sp-1] %= S[sp]; break; case Lt : if (sp > 0) S[sp-1] = S[sp-1] < S[sp]; break; case Leq: if (sp > 0) S[sp-1] = S[sp-1] <= S[sp]; break; case Gt : if (sp > 0) S[sp-1] = S[sp-1] > S[sp]; break; case Geq: if (sp > 0) S[sp-1] = S[sp-1] >= S[sp]; break; case Eq : if (sp > 0) S[sp-1] = S[sp-1] == S[sp]; break; case Neq: if (sp > 0) S[sp-1] = S[sp-1] != S[sp]; break; default: return(FALSE); } sp--; } if (sp == 0) { *v = S[0]; return(TRUE); } return(FALSE); } void print_code(code c) { code_entry *e = c ? c->first : NULL; while (e != NULL) { if (e->inst.op == Lab) { printf("%3d : ", e->inst.arg); e = e->next; if (e->inst.op == Lab) { printf("\n"); continue; } } else printf(" "); print_instruction(e->inst); printf("\n"); e = e->next; } } void print_instruction(instruction i) { switch (i.op) { case Stop: { printf("Stop ");break; } case Wait: { printf("Wait ");break; } case Add : { printf("Add "); break; } case Sub : { printf("Sub "); break; } case Mul : { printf("Mul "); break; } case Div : { printf("Div "); break; } case Mod : { printf("Mod "); break; } case Lt : { printf("Lt "); break; } case Leq : { printf("Leq "); break; } case Gt : { printf("Gt "); break; } case Geq : { printf("Geq "); break; } case Eq : { printf("Eq "); break; } case Neq : { printf("Neq "); break; } case Idx : { printf("Idx "); break; } case Ida : { printf("Ida "); break; } case Sta : { printf("Sta "); break; } case Ret : { printf("Ret "); break; } case Out : { printf("Out "); break; } case Swap: { printf("Swap "); break; } case Input:{ printf("Input "); break; } case Output:{printf("Output "); break; } case In : { printf("In %6d", i.arg); break; } case Ind : { printf("Ind %5d", i.arg); break; } case Ldc : { printf("Ldc %5d", i.arg); break; } case Ldo : { printf("Ldo %5d", i.arg); break; } case Lda : { printf("Lda %5d", i.arg); break; } case Ldn : { printf("Ldn %5d", i.arg); break; } case Sto : { printf("Sto %5d", i.arg); break; } case Pop : { printf("Pop %5d", i.arg); break; } case Ujp : { printf("Ujp %5d", i.arg); break; } case Fjp : { printf("Fjp %5d", i.arg); break; } case Alt : { printf("Alt %5d", i.arg); break; } case Ccb : { printf("Ccb %5d", i.arg); break; } case Dcb : { printf("Dcb %5d", i.arg); break; } case Pstk: { printf("Pstk %4d",i.arg); break; } case Call: { printf("Call %4d",i.arg); break; } case Spawn:{ printf("Spawn %3d",i.arg);break; } case Trace:{ printf("Trace %3d",i.arg);break; } case Enter:{ printf("Enter %3d",i.arg);break; } case Leave:{ printf("Leave %3d",i.arg);break; } case Spawnv:{printf("Spawnv %2d",i.arg); break; } default: { printf("?%d",i.op); break; } } } int stack_effect(instruction curr_inst) { switch (curr_inst.op) { case Ldc : return(1); case Ldo : return(1); case Lda : return(1); case Ind : return(0); case Sto : return(-1); case Sta : return(-2); case Ccb : return(2*curr_inst.arg); case Dcb : return(-curr_inst.arg); case Swap : return(0); case Pop : return(-curr_inst.arg); case Ldn : return(curr_inst.arg); case Idx : return(-1); case Ida : return(-1); case Add : case Sub: case Mul: case Div: case Mod: return(-1); case Lt : case Leq: case Gt: case Geq: case Eq: case Neq: return(-1); case Ujp : return(0); case Fjp : return(-1); case Enter : return(curr_inst.arg+1); case Leave : return(-(curr_inst.arg+2)); case Call : return(1); case Ret : return(0); case Spawn : return(-2); case Spawnv : return(-3); case Stop : return(0); case Wait : return(0); case Out : return(-2); case In : return(1); case Alt : return(1); case Input : return(1); case Output : return(-1); case Trace : return(0); case Lab : return(0); case Pstk: return(0); /* Will be handled specially in compute_max_stack */ default: print_instruction(curr_inst); printf("\n"); error("Fatal error", "Missing case in stack_effect\n"); } } jump_table build_jump_table(code c) { code_entry *p = c ? c->first : NULL; jump_table jt = NULL; while (p != NULL) { if (p->inst.op == Lab) { /* Add the entry (p->inst.arg, p) to jump table */ jump_table l; l = new(jump_entry); l->next = jt; l->label = p->inst.arg; l->dest = p; jt = l; } p = p->next; } return(jt); } void dispose_jump_table(jump_table jt) { jump_table n; while (jt != NULL) { n = jt->next; dispose(jt); jt = n; } } code_entry *lookup_jt(int label, jump_table jt) { while (jt != NULL && jt->label != label) jt = jt->next; if (jt == NULL) error("Fatal error", "Jumping to a non-existent label\n"); return(jt->dest); } int max_stack(code c, code_entry *e, int initial_SP, int path_SP, jump_table jt) { instruction curr; int SP = initial_SP, MAX_SP = initial_SP, i; while (e != NULL) { curr = e->inst; e = e->next; i = stack_effect(curr); if (i < 0 && SP < i) { print_code(c); error("Fatal error", "Stack underflow in function max_stack\n"); } SP += i; MAX_SP = (SP > MAX_SP) ? SP : MAX_SP; switch (curr.op) { case Lab: { /* First check to see if we have already seen this label on current path */ int i = 0; while (path_stack[i].label != curr.arg && i < path_SP) i++; if (i == path_SP) { /* i.e. label not seen so update path stack */ path_stack[path_SP].label = curr.arg; path_stack[path_SP++].SP = SP; break; } else if (path_stack[i].SP != SP) { /* Loop encountered with changing SP */ print_code(c); error("Fatal error", "Loop encountered with stack %s in " "function max_stack.\n", (SP > path_stack[i].SP) ? "increasing" : "decreasing"); } else /* We have found a well behaved loop. Stop this particular branch of the traversal and return the maximum value of SP encountered. */ return(MAX_SP); } case Ujp : e = lookup_jt(curr.arg, jt); break; case Pstk: { /* Make sure there is enough space for procedure call */ int newSP = SP + curr.arg; MAX_SP = (newSP > MAX_SP) ? newSP : MAX_SP; break; } case Stop : case Ret : return(MAX_SP); case Fjp : { int fj_max; jump_table old_in_jt = in_jt; if (old_in_jt) in_fjp++; fj_max = max_stack(c, e, SP, path_SP, jt); /* Process code after jump */ if (old_in_jt) in_fjp--; MAX_SP = (fj_max > MAX_SP) ? fj_max : MAX_SP; e = lookup_jt(curr.arg, jt); /* Process the jump */ break; } case In : { jump_table l; l = new(jump_entry); l->next = in_jt; l->dest = lookup_jt(curr.arg, jt); in_jt = l; break; } case Alt : { jump_table bodies = in_jt; if ((SP -= (2*curr.arg - 1)) < 1) { print_code(c); error("Fatal error", "Stack underflow in function max_stack\n"); } if (in_fjp) return(MAX_SP); /* see following paragraphs for explanation */ in_jt = NULL; while (bodies != NULL) { int fj_max; jump_table n; fj_max = max_stack(c,bodies->dest, SP, path_SP, jt); MAX_SP = (fj_max > MAX_SP) ? fj_max : MAX_SP; n = bodies->next; dispose(bodies); bodies = n; } return(MAX_SP); } } } return(MAX_SP); } int compute_max_stack(code c) { int m; jump_table jt; jt = build_jump_table(c); m = max_stack(c, c ? c->first : NULL, 0, 0, jt); dispose_jump_table(jt); return(m); } instruction *load_code(code c) { typedef struct label_entry { int label, code_location; struct label_entry *next; } label_entry, *label_table; int n; label_table labels = NULL; instruction *instruction_array; code_entry *s = c ? c->first : NULL; { code_entry *p = s; n = 0; while (p != NULL) { if (p->inst.op == Lab) { /* Add the entry (p->inst.arg, n) to labels table */ label_table l; l = new(label_entry); l->next = labels; l->label = p->inst.arg; l->code_location = n; labels = l; n--; } if (p->inst.op == Pstk) n--; n++; p = p->next; } } instruction_array = malloc(n * sizeof(instruction)); { int i = 0; while (iinst.op == Lab || s->inst.op == Pstk) { s = s->next; dispose(p); p = s; continue; } instruction_array[i++] = s->inst; if ((s->inst.op == Ujp) /* Check to see if the argument */ || (s->inst.op == Fjp) /* is a reference to a label */ || (s->inst.op == Spawn) /* and replace it if it is. */ || (s->inst.op == Spawnv) || (s->inst.op == Call) || (s->inst.op == In)) { int label = s->inst.arg; label_table p = labels; while ((p != NULL) && (p->label != label)) p = p -> next; if (p == NULL) error("Interpreter error", "Missing label (%d) in code.\n", label); instruction_array[i-1].arg = p->code_location; } s = s->next; dispose(p); } } { label_table p = labels; label_table o = p; while (p != NULL) { o = p; p = p -> next; dispose(o); } } dispose(c); { int i = n-1; Bpt = malloc(n * sizeof(process_list)); while (i >= 0) Bpt[i--] = NULL; } code_size = n; return(instruction_array); } code io_code() { code c, input_process_code, output_process_code; int iss, oss, il, ol; il = new_label(); input_process_code = build_icode( Lab,il, Input, Ldo, -1, Ldc,-CHANNEL_BLOCK_SIZE-2, Idx, Out, Ujp,il, END); { int l1 = new_label(), l2 = new_label(); ol = new_label(); output_process_code = build_icode( Lab,ol, Ldo,-1, Ldc,-2*CHANNEL_BLOCK_SIZE-3, Idx, Lab,l1, Ldo,-3, In, l2, Alt, 1, Lab,l2, Pop, 1, Output, Ujp,l1, END); } /* The occurrence of 3 in the following statements is due to the display */ iss = compute_max_stack(input_process_code) + 3; oss = compute_max_stack(output_process_code)+ 3; return( build_icode( Ujp, 0, input_process_code, output_process_code, Lab,0,Ccb, 1, /* stdin */ Ccb, 1, /* stdout */ Ldc, 2, Ldc, iss, Spawn, il, Ldc, 2, Ldc, oss, Spawn, ol, END)); } void debugger(int PC, stack_slot *SP, stack_slot *BP) { char buffer[256], command[256]; int offset; printf("%X @ %3d: ", active_process, PC); print_instruction(Code[PC]); printf("\n"); reset_terminal(); while (TRUE) { printf("? "); fflush(stdout); if (gets(buffer) == NULL) exit(EXIT_SUCCESS); if (sscanf(buffer, "%s%n", command, &offset) != 1) { printf("%X @ %3d: ", active_process, PC); print_instruction(Code[PC]); printf("\n"); } else if (strlen(command) != 1) printf("Illegal command. Type '?' for help.\n"); else switch (command[0]) { case 'i': { int m, n, i; if (sscanf(&(buffer[offset]), "%d +%d", &m, &n) == 2) ; else if (sscanf(&(buffer[offset]), " +%d", &n) == 1) m = PC; else if (sscanf(&(buffer[offset]), "%d", &m) == 1) n = 1; else { m = PC; n = 1; } for (i = 1; i <= n && m < code_size; i++) { printf("%3d: ", m); print_instruction(Code[m++]); printf("\n"); } break; } case 's': { stack_slot *m = BP, *p; int i; if (sscanf(&(buffer[offset]), "%d", &i) == 1) m = SP+(i-1); for (p = SP; p <= m; p++) printf("%X: %X\n", p, p->as_addr); break; } case 'a': { void *p; int i, n = 1; if (sscanf(&(buffer[offset]), "%x +%d", &p, &n) == 2) ; else if (sscanf(&(buffer[offset]), "%x", &p) == 1) ; else p = SP; for (i = 0; i < n; ((int *)p)++, i++) printf("%X: %X\n", p, *(int *)p); break; } case 't': { process p = NULL; sscanf(&(buffer[offset]), "%x", &p); if (p) p->trace_process = !p->trace_process; else tracing_flag = !tracing_flag; break; } case 'b': { int pc = PC; void *p = NULL; process_list l; if (sscanf(&(buffer[offset]), "%d %x", &pc, &p) == 2) ; else if (sscanf(&(buffer[offset]), "%d", &pc) == 1) ; else { int i; printf("Breakpoints:\n"); for (i = 0; i < code_size; i++) { if (Bpt[i]) { l = Bpt[i]; printf("%3d: ", i); while (l) { if (l->p == NULL) printf("*"); else printf("%X", l->p); if (l = l->next) printf(", "); else printf("\n"); } } } continue; } if (Bpt[pc]) if (Bpt[pc] && (Bpt[pc]->p == p)) /* Remove it */ Bpt[pc] = Bpt[pc]->next; else { process_list prev = Bpt[pc]; l = prev->next; while (l && l->p != p) { prev = l; l = l->next; } if (l) /* Remove it */ { prev->next = l->next; dispose(l); } else { l = new(struct process_list); l->p = (process)p; l->next = Bpt[pc]; Bpt[pc] = l; } } else { l = new(struct process_list); l->p = (process)p; l->next = Bpt[pc]; Bpt[pc] = l; } break; } case 'n': active_process->step_process = TRUE; case 'g': disable_buffering(); return; default : printf("Illegal command.\n\n"); case '?': printf("These are the instructions accepted by the debugger:\n\n"); printf("i [m] [+ n]\n"); printf(" Displays n instructions starting at instruction m.\n"); printf(" If m is omitted it defaults to the current PC, and n defaults to 1.\n\n"); printf("s [n]\n"); printf(" Display the top n entries on the stack. \n"); printf(" If n is omitted then the stack entries between SP and BP are displayed.\n\n"); printf("a m [+ n]\n"); printf(" Print the contents of n addresses starting at hex address m.\n\n"); printf("t [a]\n"); printf(" Toggle tracing for process with hex address $a$,\n"); printf(" or for all processes if no argument is present.\n\n"); printf("b [i [a]]\n"); printf(" Toggle the breakpoint at instruction i. If a is specified then\n"); printf(" toggle the breakpoint for the process with this hex address.\n"); printf(" If no arguments then display a list of breakpoints.\n\n"); printf("n Step to the next instruction.\n\n"); printf("g Continue execution.\n\n"); } } } void interpret(code program) { int stacksize; clock_t start_time; process p; stacksize = compute_max_stack(program) + 2; /* for initial display */ Code = load_code(program); p = create_process(0, stacksize); /* This next dubious line is so that the input and output processes will not be viewed as children of the main process, just in case the main process executes the wait instruction. */ p->children = -2; if (debug_flag) { Bpt[0] = new(struct process_list); Bpt[0]->p = p; Bpt[0]->next = NULL; } initialise_display(1, &p->SP, &p->BP); if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; start_time = clock(); interpreter_loop(); if (pool_flag) { printf("CPU time used in interpreter = %d milliseconds.\n", (clock()-start_time)*10); pool_statistics(); process_statistics(); } } void interpreter_loop() { while (1) { register int PC; /* Indexes into Code array */ stack_slot *BP = NULL, *SP = NULL; register int counter; register instruction curr_inst; load_state: PC = active_process->PC; SP = active_process->SP; BP = active_process->BP; for (counter = rand() % max_slice; counter--; ) { if (active_process->stack_overflow_check != OVERFLOW_CHECKSUM) error("Fatal error", "Stack overflow for process %d with stack size = %d.\n", active_process, BP-SP+1); if (tracing_flag || active_process->trace_process) { int stack_slots = BP-SP+1; printf("Process %X: PC = %3d, instruction = ", active_process, PC); print_instruction(Code[PC]); printf(", SP = %X,\n", SP); printf(" stack depth =%3d", stack_slots); if (stack_slots > 0) { stack_slot *sp; printf(", contents = "); if (stack_slots > 10) { stack_slots = 10; printf("..., "); } sp = SP+(stack_slots-1); while (--stack_slots) printf("%X, ", (sp--)->as_int); printf("%X.\n", sp->as_int); } else printf(".\n"); fflush(stdout); } if (active_process->step_process) { active_process->step_process = FALSE; debugger(PC,SP,BP); } else if (Bpt[PC]) { /* Check list for this process, or the NULL process */ process_list l = Bpt[PC]; while (l && l->p != NULL && l->p != active_process) l = l->next; if (l) debugger(PC,SP,BP); } switch (curr_inst = Code[PC++], curr_inst.op) { case Ldc : { (--SP)->as_int = curr_inst.arg; break; } case Ldo : { (--SP)->as_int = BP[curr_inst.arg].as_int; break; } case Lda : { (--SP)->as_addr = &BP[curr_inst.arg]; break; } case Ind : { int n = curr_inst.arg; while (n--) *SP = *(SP->as_addr); break; } case Sto : { BP[curr_inst.arg] = *(SP++); break; } case Sta : { *((SP+1)->as_addr) = *SP; SP += 2; break; } case Ccb : { int i = curr_inst.arg; stack_slot *start; while (i-- > 0) (--SP)->as_addr = NULL; i = curr_inst.arg; start = SP; while (i-- > 0) (--SP)->as_addr = start++; break; } case Dcb: { SP += curr_inst.arg; break; } case Swap : { stack_slot s = *SP; *SP = *(SP+1); *(SP+1) = s; break; } case Pop : { SP += curr_inst.arg; break; } case Ldn : { int i = curr_inst.arg; while (i-- > 0) (--SP)->as_int = 0; break; } case Idx : { *(SP+1) = ((SP+1)->as_addr)[SP->as_int]; SP++; break; } case Ida : { (SP+1)->as_addr += SP->as_int; SP++; break; } case Add : { (SP+1)->as_int += SP->as_int; SP++; break; } case Sub : { (SP+1)->as_int -= SP->as_int; SP++; break; } case Mul : { (SP+1)->as_int *= SP->as_int; SP++; break; } case Div : { (SP+1)->as_int /= SP->as_int; SP++; break; } case Mod : { (SP+1)->as_int %= SP->as_int; SP++; break; } case Lt : { (SP+1)->as_int = (SP+1)->as_int < SP->as_int; SP++; break; } case Leq : { (SP+1)->as_int = (SP+1)->as_int <= SP->as_int; SP++; break; } case Gt : { (SP+1)->as_int = (SP+1)->as_int > SP->as_int; SP++; break; } case Geq : { (SP+1)->as_int = (SP+1)->as_int >= SP->as_int; SP++; break; } case Eq : { (SP+1)->as_int = (SP+1)->as_int == SP->as_int; SP++; break; } case Neq : { (SP+1)->as_int = (SP+1)->as_int != SP->as_int; SP++; break; } case Ujp : { PC = curr_inst.arg; break; } case Fjp : { if (!(SP++)->as_int) PC = curr_inst.arg; break; } case Enter : initialise_display(curr_inst.arg, &SP, &BP); break; case Leave : { SP = BP; BP = (SP++)->as_addr; break; } case Call : { (--SP)->as_int = PC; PC = curr_inst.arg; break; } case Ret : { PC = (SP++)->as_int; break; } case Spawn : { process p = create_process(curr_inst.arg, (SP++)->as_int); p->BP = BP; initialise_display((SP++)->as_int, &p->SP, &p->BP); if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; break; } case Spawnv: { process p = create_process(curr_inst.arg, (SP++)->as_int); p->BP = BP; initialise_display((SP++)->as_int, &p->SP, &p->BP); *(--(p->SP)) = *(SP++); if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; break; } case Stop: { { process p = active_process; if (p == prev_process) active_process = prev_process = NULL; else prev_process->next = active_process = p->next; if (p->children) { /* Retire as there are still children alive */ p->status = RETIRED; } else do { p->next = reincarnated_processes; reincarnated_processes = p; p->status = REINCARNATED; if (!p->parent) break; p = p->parent; } while ((!--p->children) && (p->status == RETIRED)); if ((p->status == WAITING) && (!p->children)) { if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; } } if (active_process == NULL) return; else goto load_state; } case Wait: { if (active_process->children) { process p = active_process; p->status = WAITING; if (p == prev_process) active_process = prev_process = NULL; else prev_process->next = active_process = p->next; p->SP = SP; p->PC = PC; p->BP = BP; if (active_process == NULL) return; else goto load_state; } else break; } case Out : { { stack_slot *the_chan = SP->as_addr; if (the_chan->as_process != NULL) { /* We have a match */ { process p = the_chan->as_process; /* The partner */ stack_slot *PSP = p->SP; /* The partner's stack pointer */ int index; { int i; if (p->status != SUSPENDED_IN) error("Interpreter error", "Two processes trying to output on the same channel.\n"); for (i = (PSP++)->as_int; i>0; i--) { stack_slot *pchan = (PSP+1)->as_addr; if (pchan == the_chan) { p->PC = PSP->as_inst.arg; index = i-1; } if (pchan) pchan->as_process = NULL; PSP += 2; } } *(--PSP) = *(++SP); SP++; /* Copy across value */ (--PSP)->as_int = index; p->SP = PSP; if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; } goto save_state; /* Save state and relinquish control (for fairness ) */ } else { process p = active_process; the_chan->as_process = p; /* Add the process to the channel queue */ p->status = SUSPENDED_OUT; if (p == prev_process) active_process = prev_process = NULL; else prev_process->next = active_process = p->next; p->SP = SP; p->PC = PC; p->BP = BP; if (active_process == NULL) return; else goto load_state; } }; break; } case In : { (--SP)->as_inst = curr_inst; break; } case Alt : { { stack_slot *the_chan = NULL; int index; if (curr_inst.arg == 0) { { process p = active_process; if (p == prev_process) active_process = prev_process = NULL; else prev_process->next = active_process = p->next; if (p->children) { /* Retire as there are still children alive */ p->status = RETIRED; } else do { p->next = reincarnated_processes; reincarnated_processes = p; p->status = REINCARNATED; if (!p->parent) break; p = p->parent; } while ((!--p->children) && (p->status == RETIRED)); if ((p->status == WAITING) && (!p->children)) { if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; } } if (active_process == NULL) return; else goto load_state; } { int i = curr_inst.arg-1; stack_slot *sp, *a; if (counter%2) for (sp = SP; i>=0; i--, sp += 2) { if ((a = (sp+1)->as_addr) && a->as_process) { /* We have a match */ PC = sp->as_inst.arg; the_chan = a; index = i; break; } } else for (sp = SP + 2*i; i>=0; i--, sp -= 2) { if ((a = (sp+1)->as_addr) && a->as_process) { /* We have a match */ PC = sp->as_inst.arg; the_chan = a; index = curr_inst.arg-i-1; break; } } } if (the_chan == NULL) /* We must wait */ { process p = active_process; stack_slot *a, *sp; int i = curr_inst.arg-1; int alts = 0; if (counter%2) for (sp = SP; i>=0; i--, sp += 2) { if (a = (sp+1)->as_addr) { a->as_process = p; alts++; } } else for (sp = SP+2*i; i>=0; i--, sp -= 2) { if (a = (sp+1)->as_addr) { a->as_process = p; alts++; } } if (alts) { (--SP)->as_int = curr_inst.arg; /* Remember the alt count */ p->status = SUSPENDED_IN; if (p == prev_process) active_process = prev_process = NULL; else prev_process->next = active_process = p->next; p->SP = SP; p->PC = PC; p->BP = BP; if (active_process == NULL) return; else goto load_state; } else { { process p = active_process; if (p == prev_process) active_process = prev_process = NULL; else prev_process->next = active_process = p->next; if (p->children) { /* Retire as there are still children alive */ p->status = RETIRED; } else do { p->next = reincarnated_processes; reincarnated_processes = p; p->status = REINCARNATED; if (!p->parent) break; p = p->parent; } while ((!--p->children) && (p->status == RETIRED)); if ((p->status == WAITING) && (!p->children)) { if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; } } if (active_process == NULL) return; else goto load_state; } } else { SP += 2*curr_inst.arg; /* Pop alternatives off the stack */ { process p = the_chan->as_process; /* The partner */ stack_slot *PSP = p->SP; /* The partner's stack pointer */ if (p->status != SUSPENDED_OUT) error("Interpreter error", "Two processes trying to input on the same channel.\n"); the_chan->as_process = NULL; *(--SP) = *(++PSP); PSP++; /* Copy across value */ (--SP)->as_int = index; p->SP = PSP; if (active_process) { prev_process->next = p; p->next = active_process; prev_process = p; } else { prev_process = active_process = p; p->next = p; } p->status = ACTIVE; } goto save_state; /* Save state and relinquish control (for fairness ) */ } } ; break; } case Input : { int i; char c; if (active_process->next == active_process && BP[-1].as_addr[-2].as_addr == NULL) return; /* Only process running and no-one waiting for input */ if (eof_flag) { (--SP)->as_int = -1; break; } i = read(input_fd, &c, 1); if (i == 0 && input_buffered) { i++; c = '\04'; } if (i == 1) { if (c == '\04') eof_flag = TRUE; if (isdigit(c) || (c == '-' && bufptr == 0)) input_buffer[bufptr++] = c; else if (bufptr > 0) { input_buffer[bufptr] = 0; (--SP)->as_int = atoi(input_buffer); bufptr = 0; break; } } PC--; goto save_state; } case Output : { printf("==> %d\n", (SP++)->as_int); break; } case Trace:{ active_process->trace_process = curr_inst.arg; break; } } } save_state: active_process->PC = PC; active_process->SP = SP; active_process->BP = BP; prev_process = active_process; active_process = active_process->next; } }