add safety check to Instruction.Shift() constructor
[fleet.git] / src / edu / berkeley / fleet / api / Instruction.java
1 package edu.berkeley.fleet.api;
2 import java.util.*;
3
4 /** a Fleet instruction; includes execution location but not full dispatch path */
5 public abstract class Instruction {
6
7     /** the dock which is to execute this instruction */
8     public final Dock      dock;
9
10     /** true if the instruction is an outer-looping instruction */
11     public final boolean   looping;
12
13     /** the instruction's predicate */
14     public final Predicate predicate;
15
16     Instruction(Dock dock, boolean looping, Predicate predicate) {
17         if (dock==null)      throw new RuntimeException("dock may not be null");
18         if (predicate==null) throw new RuntimeException("predicate may not be null");
19         this.dock = dock;
20         this.looping = looping;
21         this.predicate = predicate;
22     }
23
24     //public abstract Instruction withLooping(boolean newLooping);
25     //public abstract Instruction withPredicate(Predicate newPredicate);
26
27     public String toString() {
28         String s = predicate.toString();
29         if (s.length()>0) s = "["+s+"] ";
30         if (looping) s += "[Rq] ";
31         return dock+": "+s;
32     }
33
34     /**
35      *  A <tt>set</tt> instruction.
36      *
37      *  Note that immediates are supplied as Java <tt>long</tt> values
38      *  because they are actual integers with two's complement
39      *  sign-extension semantics.  This is in contrast to most other
40      *  immediate values in the API which are raw bit sequences of
41      *  fixed length -- these are represented by instances of
42      *  <tt>BitVector</tt>.
43      */
44     public static class Set extends Instruction {
45
46         /** the destination (latch to be set) */
47         public final SetDest        dest;
48
49         /** the source (what will be put in the <tt>dest</tt> latch) */
50         public final SetSource      source;
51
52         /** if <tt>source</tt> is <tt>Immediate</tt>, this is the immediate value; an integer */
53         public final long           immediate;
54
55         /** if <tt>dest</tt> is <tt>Flags</tt>, this is the truth table to update flag "a" */
56         public final FlagFunction   newFlagA;
57
58         /** if <tt>dest</tt> is <tt>Flags</tt>, this is the truth table to update flag "b" */
59         public final FlagFunction   newFlagB;
60
61         /** basic constructor */
62         public Set(Dock dock, SetDest dest, SetSource source) { this(dock, false, Predicate.Default, dest, source); }
63         public Set(Dock dock, boolean looping, Predicate predicate, SetDest dest, SetSource source) {
64             super(dock, looping, predicate);
65             OUTER: switch(dest) {
66                 case InnerLoopCounter:
67                     switch(source) {
68                         case Infinity: case DataLatch: case Immediate: break OUTER;
69                         default: break;
70                     }
71                 case OuterLoopCounter:
72                     switch(source) {
73                         case Decrement: case DataLatch: case Immediate: break OUTER;
74                         default: break;
75                     }
76                 case DataLatch:
77                     if (source==SetSource.Immediate) break;
78                 default: throw new RuntimeException("cannot set " + dest + " to " + source);
79             }
80             this.source = source;
81             this.dest = dest;
82             this.immediate = 0;
83             this.newFlagA = null;
84             this.newFlagB = null;
85         }
86
87         /** constructor for set instructions with immediates */
88         public Set(Dock dock, SetDest dest, long immediate) { this(dock, false, Predicate.Default, dest, immediate); }
89         public Set(Dock dock, boolean looping, Predicate predicate, SetDest dest, long immediate) {
90             super(dock, looping, predicate);
91             if (dest!=SetDest.InnerLoopCounter && dest!=SetDest.OuterLoopCounter && dest!=SetDest.DataLatch)
92                 throw new RuntimeException("a set instruction with dest="+dest+" may not take an immediate");
93             this.source = SetSource.Immediate;
94             this.dest = dest;
95             this.immediate = immediate;
96             this.newFlagA = null;
97             this.newFlagB = null;
98         }
99
100         /** constructor for <tt>set flags</tt> instructions */
101         public Set(Dock dock, FlagFunction newFlagA, FlagFunction newFlagB) { this(dock, false, Predicate.Default, newFlagA, newFlagB); }
102         public Set(Dock dock, boolean looping, Predicate predicate, FlagFunction newFlagA, FlagFunction newFlagB) {
103             super(dock, looping, predicate);
104             this.source = SetSource.Immediate;
105             this.dest = SetDest.Flags;
106             this.immediate = 0;
107             this.newFlagA = newFlagA;
108             this.newFlagB = newFlagB;
109         }
110
111         /** possible sources for the Set instruction */
112         public static enum SetSource {
113             Infinity, DataLatch, Immediate, Decrement;
114         }
115         /** possible destinations for the Set instruction */
116         public static enum SetDest {
117             InnerLoopCounter, OuterLoopCounter, Flags, DataLatch;
118         }
119
120         /**
121          *  (Immutable) a truth table describing how to update a flag
122          *  based on the value of other flags; it is the logical OR of
123          *  a set of flag predicates.  This class is immutable; all
124          *  methods that alter the function return a new object.
125          */
126         public static class FlagFunction implements Iterable<Predicate> {
127
128             /** the function that always assigns zero */
129             public static final FlagFunction ZERO = new FlagFunction();
130
131             /** the function that always assigns one */
132             public static final FlagFunction ONE  = ZERO.add(Predicate.FlagA).add(Predicate.NotFlagA);
133
134             private final java.util.Set<Predicate> predicates;
135
136             public Iterator<Predicate> iterator() { return predicates.iterator(); }
137
138             private FlagFunction() { this(Collections.EMPTY_SET); }
139             private FlagFunction(java.util.Set<Predicate> set) { this.predicates = Collections.unmodifiableSet(set); }
140
141             /** returns the function which is the logical OR of this function and <tt>ff</tt> */
142             public FlagFunction add(FlagFunction ff) {
143                 FlagFunction ret = this;
144                 for(Predicate p : ff) ret = ret.add(p);
145                 return ret;
146             }
147
148             /** returns the function which is the logical OR of this function and <tt>p</tt> */
149             public FlagFunction add(Predicate p) {
150                 HashSet h = new HashSet();
151                 h.addAll(predicates);
152                 switch(p) {
153                     case FlagA: case NotFlagA:
154                     case FlagB: case NotFlagB:
155                     case FlagC: case NotFlagC:
156                         break;
157                     default:
158                         throw new RuntimeException("invalid predicate in FlagFunction: " + p);
159                 }
160                 h.add(p);
161                 return new FlagFunction(h);
162             }
163
164             /** remove <tt>p</tt> from the set of terms which this function is the logical OR of */
165             public FlagFunction remove(Predicate p) {
166                 HashSet h = new HashSet();
167                 h.addAll(predicates);
168                 h.remove(p);
169                 return new FlagFunction(h);
170             }
171
172             public String toString() {
173                 if (predicates.isEmpty()) return "0";
174                 if (predicates.contains(Predicate.FlagA) && predicates.contains(Predicate.NotFlagA)) return "1";
175                 if (predicates.contains(Predicate.FlagB) && predicates.contains(Predicate.NotFlagB)) return "1";
176                 if (predicates.contains(Predicate.FlagC) && predicates.contains(Predicate.NotFlagC)) return "1";
177                 StringBuffer ret = new StringBuffer();
178                 boolean empty = true;
179                 for(Predicate p : new Predicate[] {
180                         Predicate.FlagA, Predicate.NotFlagA,
181                         Predicate.FlagB, Predicate.NotFlagB,
182                         Predicate.FlagC, Predicate.NotFlagC })
183                     if (predicates.contains(p)) {
184                         if (!empty) ret.append("| ");
185                         ret.append(p);
186                         empty = false;
187                     }
188                 return ret.toString();
189             }
190         }
191
192         public String toString() {
193             switch(dest) {
194                 case InnerLoopCounter:
195                     switch(source) {
196                         case Infinity: return super.toString()+"set ilc=*;";
197                         case DataLatch: return super.toString()+"set ilc=data;";
198                         case Immediate: return super.toString()+"set ilc="+immediate+";";
199                     }
200                 case OuterLoopCounter:
201                     switch(source) {
202                         case Decrement: return super.toString()+"set olc--;";
203                         case DataLatch: return super.toString()+"set olc=data;";
204                         case Immediate: return super.toString()+"set olc="+immediate+";";
205                     }
206                 case Flags: return super.toString()+"set flags a="+newFlagA+", b="+newFlagB+";";
207                 case DataLatch: return super.toString()+"set word="+immediate+";";
208             }
209             throw new Error("impossible");
210         }
211     }
212
213     /** shifts an immediate into the low-order bits of the data latch */
214     public static class Shift extends Instruction {
215         public final BitVector immediate;
216         public Shift(Dock dock, BitVector immediate) { this(dock, false, Predicate.Default, immediate); }
217         public Shift(Dock dock, boolean looping, Predicate predicate, BitVector immediate) {
218             super(dock, looping, predicate);
219             this.immediate = immediate;
220             this.immediate.setImmutable();
221             if (immediate.length() != dock.getShip().getFleet().getShiftWidth())
222                 throw new RuntimeException("attempt to create a Shift instruction with a "+immediate.length()+
223                                            "-bit immediate on a Fleet that uses "+dock.getShip().getFleet().getShiftWidth()+
224                                            "-bit shift instructions");
225         }
226         public String toString() { return super.toString()+"shift "+immediate; }
227     }
228
229     public static class Flush extends Instruction {
230         public Flush(Dock dock) { this(dock, false, Predicate.Default); }
231         public Flush(Dock dock, boolean looping, Predicate predicate) {
232             super(dock, looping, predicate);
233             if (!dock.isInputDock()) throw new RuntimeException("Flush is only allowed at input docks");
234         }
235         public String toString() { return super.toString()+"flush"; }
236     }
237
238     /** all communication is performed with this instruction */
239     public static class Move extends Instruction {
240
241         /** if true, this instruction is vulnerable to torpedoes */
242         public final boolean     interruptible;
243
244         /** if non-null, the path to load into the path latch */
245         public final Path        path;
246
247         /** if true, a token will be consumed before execution */
248         public final boolean     tokenIn;
249
250         /** if true, data will be consumed before execution */
251         public final boolean     dataIn;
252
253         /** if true, the data consumed will be copied into the data latch */
254         public final boolean     latchData;
255
256         /** if true, the data consumed will be copied into the path latch */
257         public final boolean     latchPath;
258
259         /** if true, the value in the data latch will be transmitted (to the location in the path latch if at an output dock) */
260         public final boolean     dataOut;
261
262         /** if true, the a token will be transmitted to the location in the path latch */
263         public final boolean     tokenOut;
264
265         public Move(Dock        dock,
266                     Path        path,
267                     boolean     tokenIn,
268                     boolean     dataIn,
269                     boolean     latchData,
270                     boolean     latchPath,
271                     boolean     dataOut,
272                     boolean     tokenOut
273                     ) {
274             this(dock, false, Predicate.Default, false, path, tokenIn, dataIn, latchData, latchPath, dataOut, tokenOut); }
275         public Move(Dock        dock,
276                     boolean     looping,
277                     Predicate   predicate,
278                     boolean     interruptible,
279                     Path        path,
280                     boolean     tokenIn,
281                     boolean     dataIn,
282                     boolean     latchData,
283                     boolean     latchPath,
284                     boolean     dataOut,
285                     boolean     tokenOut
286                     ) {
287             super(dock, looping, predicate);
288             this.path = path;
289             this.tokenIn = tokenIn;
290             this.dataIn = dataIn;
291             this.latchData = latchData;
292             this.latchPath = latchPath;
293             this.dataOut = dataOut;
294             this.tokenOut = tokenOut;
295             this.interruptible = interruptible;
296             if (dock != null && dock.isInputDock() && tokenIn && dataIn)
297                 throw new RuntimeException("cannot have two \"recv\"s: " + this);
298             if (dock != null && dock.isOutputDock() && tokenOut && dataOut)
299                 throw new RuntimeException("cannot have two \"send\"s: " + this);
300             if (latchData && !dataIn)
301                 throw new RuntimeException("cannot have latchData bit set without dataIn bit: " + this);
302             if (latchPath && !dataIn)
303                 throw new RuntimeException("cannot have latchPath bit set without dataIn bit: " + this);
304             if (latchPath && path!=null)
305                 throw new RuntimeException("cannot have latchPath and a non-null path: " + this);
306         }
307
308         public String toString() {
309             StringBuffer ret = new StringBuffer();
310             if (tokenIn)                        ret.append(", recv token");
311             if (dataIn) {
312                 if (latchPath)                  ret.append(!dock.isInputDock() ? ", collect path" : ", recv path");
313                 if (latchData)                  ret.append(!dock.isInputDock() ? ", collect"      : ", recv");
314                 if (!latchPath && !latchData)   ret.append(", discard");
315             }
316             if (dataOut && dock.isInputDock())  ret.append(", deliver");
317             if (dataOut && !dock.isInputDock()) ret.append(path==null ? ", send"  : ", send to "  + path.getDestination().getDock());
318             if (tokenOut)                       ret.append(path==null ? ", token" : ", send token to " + path.getDestination().getDock());
319             String s = ret.toString();
320             s = s.equals("") ? "nop" : s.substring(2);
321             if (interruptible) s = "[T] " + s;
322             return super.toString()+s+";";
323         }
324     }
325
326     /** marks the end of a loop; closes the hatch */
327     public static class Tail extends Instruction {
328         public Tail(Dock dock) { super(dock, false, Predicate.IgnoreOLC); }
329         public String toString() { return dock+": tail;"; }
330     }
331
332 }