package text_area;
/* A cache simulation
 * Updated 24 March 1997 for JDK1.1
 */

import java.applet.*;
import java.awt.*;
import java.util.Vector;
import eduni.simanim.*;
import eduni.simjava.*;
import eduni.simdiag.*;

// A single cache block frame
class CacheFrame extends Sim_entity {
  private Sim_port io;
  private int state;    // Do we contain info? - EMPTY | FULL
  private int addr_tag; // If we do what memory address is it?

  private static final int EMPTY = 0;
  private static final int FULL  = 1;

  public CacheFrame(String name, int x, int y) {
    super(name, "frame", x, y);
    io = new Sim_port("io", "port", Anim_port.LEFT, 4); add_port(io);
    state = EMPTY;
    addr_tag = -1;
    add_param(new Anim_param("State", Anim_param.STATE, "empty"));  
    add_param(new Anim_param("Tag", Anim_param.NAME_VALUE, "Empty", 52, 12));
  }

  // Cache can send us:
  // tag: 0, data: address - You now hold the data at this address
  // tag: 1, data: address - Do you have this address?
  // We can reply:
  // tag:  0 no we are empty
  // tag:  1 yes we do
  // tag: -1 no, we have some other address
  public void body() {
    Sim_event ev = new Sim_event();

    //dump_state();
    while(true) {
      sim_wait(ev);
      sim_hold(0.01);
      if(ev.get_tag() == 1) {   // An enquire to see if we have an address
        if(state == EMPTY) {
          // Tell cache we are empty
          sim_schedule(io, 0.0, 0);
          sim_trace(1, "S io E");
        } else {
          // We are full, do we have the right address?
          if(addr_tag == ((Integer)ev.get_data()).intValue()) {
            // OK send back the data
            sim_schedule(io, 0.0, 1);
            sim_trace(1, "S io H");
          } else {
            // Tell cache it missed
            sim_schedule(io, 0.0, -1);
            sim_trace(1, "S io M");
          }
        }
      } else {                  // An update to tell us our new address
        state = FULL;
        addr_tag = ((Integer)ev.get_data()).intValue();
        dump_state();
      }
    }
  }

  public void dump_state() {
    if(state == EMPTY) sim_trace(1, "P empty "+addr_tag);
    if(state == FULL) sim_trace(1, "P full "+addr_tag);
  }
}


class Cache extends Sim_entity {
  private Sim_port frames[];
  private Sim_port mem, cpu;
  private int assoc, hits, requests;

  public Cache(String name, int assoc, int num_frames, int x, int y) {
    super(name, "cache", x, y);
    cpu = new Sim_port("cpu", "port", Anim_port.TOP, 2); add_port(cpu);
    mem = new Sim_port("mem", "port", Anim_port.BOTTOM, 2); add_port(mem);
    frames = new Sim_port[num_frames];
    for(int i=0; i<frames.length; i++) {
      frames[i] = new Sim_port("frame_"+i, "port", Anim_port.RIGHT, 8*i);
      add_port(frames[i]);
    }
    this.frames = frames;
    this.assoc = assoc;
    hits = 0;
    requests = 0;
    add_param(new Anim_param("Hits", Anim_param.NAME_VALUE, "0", -103, 20));
    add_param(new Anim_param("Hit ratio", Anim_param.NAME_VALUE, "0", -103, 36));
  }

  public void body() {
    int address, free, base_frame, i;
    boolean found;
    Sim_event ev = new Sim_event();

    while(true) {
      sim_get_next(ev); // get mem request from CPU
      sim_hold(0.01);
      address = ev.get_tag();
      requests++;

      // Ask frames for address
      base_frame = (address % (frames.length / assoc)) * assoc;
      found = false;
      free = -1;
      for(i=base_frame; i<(base_frame+assoc); i++) {
        sim_schedule(frames[i], 0.0, 1, new Integer(address));
        sim_trace(1, "S frame_"+i+" R");
      }
      for(i=0; i<assoc; i++) {
        sim_get_next(ev);
        if(ev.get_tag() == 1) found = true;        // record that we have found addr
        if(ev.get_tag() == 0) free = i+base_frame; // record suitable replacement
      }
      sim_hold(0.01);
      if(!found) {
        // Fetch from memory
        sim_schedule(mem, 0.0, address);
        sim_trace(1, "S mem "+address);
        sim_get_next(ev);
        sim_hold(0.01);
        // Update cache
        if(free == -1) // If there are no free frames then pick one at random
          free = ((int)Math.floor(base_frame + assoc*Math.random()));
        sim_schedule(frames[free], 0.0, 0, new Integer(address));
        sim_trace(1, "S frame_"+free+" W");   
      } else {
        hits++;
      }
      sim_trace(1, "P "+hits+" "+((float)hits/(float)requests));
      // send it to the CPU
      sim_schedule(cpu, 0.0, address);
      sim_trace(1, "S cpu "+address);
    }
  }
}

class CPU extends Sim_entity {
  private Sim_port io;
  private int requests, num_requests;
  private Sim_uniform_obj addr;

  public CPU(String name, int requests, int x, int y) {
    super(name, "cpu", x, y);
    io = new Sim_port("io", "port", Anim_port.BOTTOM, 10); add_port(io);
    this.requests = requests;
    addr = new Sim_uniform_obj("Addresses", 0, 16, (int)System.currentTimeMillis());
    num_requests = 0;
    add_param(new Anim_param("Requests", Anim_param.NAME_VALUE, "0", 36, 20));
  }

  public void body() {
    int i;
    int address;

    for(i=0; i<requests; i++) {
      address = (int)addr.sample();
      sim_schedule(io, 0.0, address);  // Request a memory address
      sim_trace(1, "S io "+address);
      num_requests++;
      sim_trace(1, "P "+num_requests);
      sim_wait(null);                  // Wait for it to come back
      sim_hold(0.01);
    }
  }
}

class Memory extends Sim_entity {
  private Sim_port io;
  private int access_count;

  public Memory(String name, int x, int y) {
    super(name, "mem", x, y);
    io = new Sim_port("io", "port", Anim_port.TOP, 10); add_port(io);
    access_count = 0;
    add_param(new Anim_param("Accesses", Anim_param.NAME_VALUE, "0", 36, 20));
  }

  public void body() {
    int addr;
    Sim_event ev = new Sim_event();
    while(true) {
      sim_wait(ev);
      sim_hold(5.00);
      addr = ev.get_tag();
      access_count++;
      sim_schedule(io, 0.0, addr);
      sim_trace(1, "S io "+addr);
      sim_trace(1, "P "+access_count);
    }
  }
}

class FredLayout implements LayoutManager {
  Vector v = new Vector(); // Components
  Vector p = new Vector(); // Pos
  
  public  FredLayout()
  {
    System.out.println("making FredLayout");
  }

  public void addLayoutComponent(String  name,  Component  comp)
  {
    v.addElement(comp);
    System.out.println("Adding "+name);
  }
  /** Set xp coords of component */
  public void setXY(int x, int y) {
    System.out.println("Adding point");
    p.addElement(new Point(x,y));
  }
      
  public  void layoutContainer(Container  parent)
  {
    System.out.println("Laying out container");
    for (int i=0; i<parent.getComponentCount(); i++) {
      Component c = parent.getComponent(i);
      Point pp = (Point)p.elementAt(i);
      c.setBounds(pp.x, pp.y, c.getPreferredSize().width, c.getPreferredSize().height);
      System.out.println("Layout "+i);
    }
    //c = parent.getComponent(0);
   
    //   c.reshape(0, 0, c.preferredSize().width, c.preferredSize().height);
  }
  
  public  Dimension      
  minimumLayoutSize(Container  parent)
  {
    return (new Dimension(60,60));
  }
  
  public  Dimension  
  preferredLayoutSize(Container  parent)
  {
    return (new Dimension(100,100));
  }
  
  public  void       
  removeLayoutComponent(Component  comp)
  {
  }
}

 
public class Applet5 extends Anim_applet implements Runnable {
  Panel inputs;
  Label reqLabel, assocLabel;
  TextField reqField;
  Choice assocChoice;
  TextArea t1,t2;

  public void anim_init() {
    int i;
    inputs = new Panel();
    inputs.setLayout(new GridLayout(0,2));
    reqLabel = new Label("Memory requests:", Label.RIGHT);
    inputs.add(reqLabel);
    reqField = new TextField("5",3);
    inputs.add(reqField);

    assocLabel = new Label("Cache associativity:", Label.RIGHT);
    inputs.add(assocLabel);
    assocChoice = new Choice();
    assocChoice.addItem("direct mapped");
    for(i=2; i<8; i*=2) assocChoice.addItem(" "+i+" way");
    assocChoice.addItem("fully associative");
    assocChoice.select(2);
    inputs.add(assocChoice);

    // Add a trace saver
    trace_out.addTraceListener( new TraceSaver("tracefile") );
    FredLayout fl = new FredLayout();
    trace_out.setLayout(fl);

    t1 = new  TextArea("Text 1",5,10);
    t2 = new  TextArea("Text 2",5,2);
    fl.setXY(100,20);
    fl.setXY(100,218);

    trace_out.add(t1);
    trace_out.add(t2);
    
    this.add("North", inputs);
  }

  //public boolean handleEvent(Event e) {
  //  return super.handleEvent(e);
  //}

  public void anim_layout() {
    int reqs, assoc, i;
    int NUM_FRAMES = 8;

    t1.setLocation(100,20);
    t2.setLocation(100,218);

    // Extract GUI info
    reqs = Integer.parseInt(reqField.getText());
    assoc = 1<<assocChoice.getSelectedIndex();

    // Build ents
    Sim_system.add(new CPU("CPU", reqs, 100, 20));
    Sim_system.add(new Memory("Memory", 100, 218));
    for(i=0; i<NUM_FRAMES; i++) {
      Sim_system.add(new CacheFrame("Frame_"+i, 170, 56+i*20));
    }
    Sim_system.add(new Cache("Cache", assoc, NUM_FRAMES, 108, 102));

    // Link ents
    Sim_system.link_ports("CPU", "io", "Cache", "cpu");
    Sim_system.link_ports("Memory", "io", "Cache", "mem");
    for(i=0; i<NUM_FRAMES; i++) {
      Sim_system.link_ports("Cache", "frame_"+i, "Frame_"+i, "io");
    }
  }
}
