// © Copyright 1995, Joseph Bergin. All rights reserved.

#ifndef __Programs__
#define __Programs__

#include <iostream.h>
#include "Queue.h"
#include "Array.h"
#include "Stacks.h"
#include "Association.h"
#include "List.h"
#include "Dictionary.h"

// This module simulates the concurrent execution of several
// programs.  Programs are permitted to execute a few instructions
// and then the next program is allowed a chance.  Number of
// instructions varies.  Scheduling is "round-robin"
// In effect, Simulator is like an operating system managing a single cpu and 
// several processes.  Each process has a program, a PC, indicating where
// the program is executing, and a stack for intermediate results. 
// The Simulator has a memory of numbered cells that all processes share.  The
// Simulator does not prevent processes from using the same cells in the
// memory.  

// The programs use a simple stack based assembly/machine language. It is
// very similar to the language discussed with class CPU. The major difference 
// is that Pushi is used to push a literal value, and Pushv is used to push
// from a memory cell.  The memory cell labels are numeric here, rather than
// characters as was assumed in class CPU.  

enum Instruction{Halt, Sum, Diff, Prod, Quot, Pos, Zero, Jmp, Dup,
					Input, Output, Pushi, Pushv, Pop, Toss, Define};


class Simulator;

class Process
{	public:
		Process(int serialNumber = 0, istream& in = cin, ostream& out = cout);
		void setSerialNumber(int serialNumber);
		void reset(); // Set _pc to zero and clear _stack.
		// The memory is a collection of labeled cells.  The labels are integers, as are
		// the contents. Instruction pushv(v), pushes the contents of the cell with label v
		// Instruction pushi(x) pushes the literal value x.  Instruction pop(v) pops the 
		// stack and stores the result in location with label v.  Arithmetic operations get
		// operands from stack by popping and return results to stack.
		
		void instruction(Instruction opcode, int operand);  
			// Enter an instruction <opcode, operand> into the program. 
			// It is NOT checked for legality (a possible improvment).
			
		// The next 14 instructions simply enter an equiv statement into the next
		// available slot in the _program.  They do not execute the program.  
		// Instructions inserted are implicitly numbered starting with 0. 
		// These are really shorthand forms for use of instruction(). 
		// You may use xx.instruction(Sum,0); OR xx.sum();  The effect is the same. 
		
		void pushi(int i); 	// push literal i
		void pushv(unsigned int v); 	// push variable v
		void pop(unsigned int v); 	// pop to variable v
		void halt();		// Terminate this process
		void sum();			// Add top two stack values (popping) & push result
		void diff();
		void prod();
		void quot();
		void pos(int L); // If pos jump to location L (L is an instruction number)
		void zero(int L); // if zero jump to location L
		void jmp(int L);	// Next instruction is #L
		void input();	// push from input stream in. Halts its process is at eof.
		void output();	// copy to output stream out (no pop)
		void toss();	// discard the top of stack
		void dup(); 	// duplicate the top of stack
		void declare(unsigned int v); // enter defn for variable v if not defined.
			// Only the first def of a variable is effective. Others ignored. 
	protected:
		unsigned int _pc; // only valid when not executing
		unsigned int _state;  //1 = active, 0 = halted.
		unsigned int _instructionCount; //number of instructions executed(reporting only)
		Array< Association<int, int> > _program;
		unsigned int _programLength; // logical length. Where is physical?
		Stack<int> _stack; // Run time stack of this process
		int _serialNumber; // for reporting
		Dictionary<unsigned int, int>* _memory; // Set when entered into simulator.
		istream & _input;
		ostream & _output;
		void insert(Association<int, int>);
	friend class Simulator;
	friend ostream & operator<<(ostream &, const Process &);
};

class Simulator
{	public:
		Simulator();

		void step(unsigned int n = 1);//Run for n steps or all processes halt
		void insertProcess(Process *);  //Enqueue it and give it a memory. 
		void reset(); //Reset all processes and make executable
	private:
		Queue< Process *> _processes;
		unsigned int _processCount; // Number of processes in queue
		unsigned int _instructionCount;
		List <Process *> _haltedProcesses;
		Process * _current; // volatile. Could be a local in step. Here for reporting. 
		unsigned int _quantum; // volatile. Quantum of current
		unsigned int _pc; // volatile 
		Dictionary<unsigned int, int> _memory;
		Simulator(Simulator &);	// Can't make copies.
		Simulator& operator=(Simulator &); // Can't assign.
	friend ostream & operator<<(ostream &, const Simulator &);

};

// When a process halts it goes on the _haltedProcesses list.
// When it loses the cpu it goes back in the queue.

#endif

