/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  Here we start!
  Two functions are skipped, getFrameToReplace and returnVirtualPage.
  Further, just the first three algorithms are implemented. ;-)
  0/ FIFO  -  First in, first out.
  1/ FIFO second chance  -  As FIFO but better.
  2/ LRU  -  Least recently used.
  
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void memService(void) {
  int i;
  for (i = 0; i < PhysMemorySize; i++)
    FT[i].ref = 0;
}

int vmRead(int p, int vAdr) {
  int page, offset;
  if ((vAdr < 0) || (vAdr > MaxVirtAdr)) {
     adr_trap(p, vAdr);
     return -2;
  }
  vmTranslateAdr(vAdr, &page, &offset);
  if (PT[p].PTBR[page].i_vpt == -1)
     PT[p].PTBR[page].i_vpt = getNewVirtualPage();
  if (PT[p].PTBR[page].prot == prot_r) {
     seg_trap(p, vAdr);
     return -1;
  }
  if (!(VPT[PT[p].PTBR[page].i_vpt].mapped))
     pageFault(p, PT[p].PTBR[page].i_vpt);
  PT[p].memRead++;
  FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].ref = 1;
  if (ParPageRepl == 2) {
     if (++clockl == 4294967295UL) {
        clockl = 0;
	clockh++;
     }
     FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].rl = clockl;
     FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].rh = clockh;
  }
  if (Interrupt && !(++memaccess % INTERVAL)) {
     memService();
     memaccess = 0;
  }
  return mem[VPT[PT[p].PTBR[page].i_vpt].i_ft][offset];
}

void vmWrite(int p, int vAdr, int data) {
  int page, offset;
  if ((vAdr < 0) || (vAdr > MaxVirtAdr)) {
      adr_trap(p, vAdr);
      return;
  }
  vmTranslateAdr(vAdr, &page, &offset);
  if (PT[p].PTBR[page].i_vpt == -1)
     PT[p].PTBR[page].i_vpt = getNewVirtualPage();
  if (PT[p].PTBR[page].prot == prot_w) {
     seg_trap(p, vAdr);
     return;
  }
  if (!(VPT[PT[p].PTBR[page].i_vpt].mapped))
     pageFault(p, PT[p].PTBR[page].i_vpt);
  mem[VPT[PT[p].PTBR[page].i_vpt].i_ft][offset] = data;
  FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].ref = 1;
  if (PT[p].PTBR[page].prot == prot_cow) {
     writePage(VPT[PT[p].PTBR[page].i_vpt].i_ft);
     FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].dirty = 0;
  } else
     FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].dirty = 1;
  if (ParPageRepl == 2) {
     if (++clockl == 4294967295UL) {
        clockh++;
	clockl = 0;
     }
     FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].rl = clockl;
     FT[VPT[PT[p].PTBR[page].i_vpt].i_ft].rh = clockh;
  }
  PT[p].memWrite++;
  if (Interrupt && !(++memaccess % INTERVAL)) {
     memService();
     memaccess = 0;
  }
}

static void pageFault(int p, int i_vmpt) {
  int i = 0,
      locked = 0,
      frame = -1;
  PT[p].nbrPageFaults++;
  for (i = 0; i < PhysMemorySize; i++)
      if (!FT[i].in_use) {
	 frame = i;
	 FT[i].in_use = 1;
	 FT[i].i_vpt = i_vmpt;
	 VPT[i_vmpt].i_ft = i;
	 VPT[i_vmpt].mapped = 1;
	 FT[i].ref = 0;
	 break;
      }
  if (frame == -1) {
      switch (ParPageRepl) {
	case 0:  for (i = 0; i < PhysMemorySize; i++) {
	             frame = FIFO_dequeue();
	             if (FT[frame].lock)
		        FIFO_enqueue(frame);
	             else
		        break;
	         }
	         if (i == PhysMemorySize) {
                    sens();
                    err_raise("OUT OF MEMORY!\n");
	            exit(1);
	         }
	         break;
	case 1:  i = 0;
	         locked = 0;
	         while(1) {
	           frame = FIFO_dequeue();
	           if (FT[frame].lock || FT[frame].ref) {
		      if (FT[frame].ref)
		         FT[frame].ref = 0;
		      if (FT[frame].lock)
		         if (++locked > MaxPhysMemory) {
                            sens();
			    err_raise("OUT OF MEMORY!\n");
			    exit(1);
		         }
		      FIFO_enqueue(frame);
                   } else
		   break;
	         }
	         break;
	case 2:  for (i = 0; i < PhysMemorySize; i++) {
	             if (!(FT[i].lock))
		        break;
	         }
	         if (i == PhysMemorySize) {
                    sens();
	            err_raise("OUT OF MEMORY!\n");
	            exit(1);
	         }
	         frame = i;
	         for (i = i + 1; i < PhysMemorySize; i++) {
	             if (!(FT[i].lock))
		        if ((FT[i].rh < FT[frame].rh) ||
                            ((FT[i].rh == FT[frame].rh) &&
                            (FT[i].rl < FT[frame].rl)))
		           frame = i;
	         }
	         break;
	default: sens();
                 err_raise("ALGORITM NOT IMPLEMENTED!\n");
	         exit(1);
      }
      if (!(ParDirtyBit && !FT[frame].dirty))
	 writePage(frame);
      VPT[FT[frame].i_vpt].mapped = 0;
      FT[frame].ref = 0;
  }
  switch (ParPageRepl) {
    case 0:
    case 1:  FIFO_enqueue(frame);
             break;
    case 2:  FT[frame].rh = clockh;
             FT[frame].rl = clockl;
             break;
    default: sens();
             err_raise("ALGORITM NOT IMPLEMENTED!\n");
             exit(1);
  }
  if (VPT[i_vmpt].ref_count == 0)
     VPT[i_vmpt].ref_count = 1;
  else
     readPage(i_vmpt, frame);
  VPT[i_vmpt].mapped = 1;
  VPT[i_vmpt].i_ft = frame;
  FT[frame].in_use = 1;
  FT[frame].i_vpt = i_vmpt;
  FT[frame].ref = 0;
  FT[frame].dirty = 0;
}

void vmMap (int p1, int pg1, int p2, int pg2) {
  if (PT[p1].PTBR[pg1].i_vpt == -1)
     PT[p1].PTBR[pg1].i_vpt = getNewVirtualPage();
  vmUnMap(p2, pg2);
  PT[p2].PTBR[pg2].i_vpt = PT[p1].PTBR[pg1].i_vpt;
  VPT[PT[p1].PTBR[pg1].i_vpt].ref_count++;
}

void vmUnMap(int p, int pg) {
  if (PT[p].PTBR[pg].i_vpt != -1) {
     if (!(--(VPT[PT[p].PTBR[pg].i_vpt].ref_count))) {
        VPT[PT[p].PTBR[pg].i_vpt].mapped = 0;
        FT[VPT[PT[p].PTBR[pg].i_vpt].i_ft].i_vpt = -1;
        FT[VPT[PT[p].PTBR[pg].i_vpt].i_ft].in_use = 0;
        if ((!ParPageRepl) || (ParPageRepl == 1))
           FIFO_dequeueElem(VPT[PT[p].PTBR[pg].i_vpt].i_ft);
     }
     PT[p].PTBR[pg].prot = 0;
     PT[p].PTBR[pg].i_vpt = -1;
  }
}

void vmSetLock(int p, int i_vmpt, int l) {
  int i;
  for (i = 0; i < MaxPagesPerProcess; i++) {
      if (PT[p].PTBR[i].i_vpt == i_vmpt) {
	  if (l)
	     vmRead(p, i * MaxPageSize);
	  FT[VPT[i_vmpt].i_ft].lock = l;
	  break;
      }
  }
}

void vmSetProtection(int p, int page, Protection prot) {
  PT[p].PTBR[page].prot = prot;
}

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  Here we end!

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
