1 /*
2 * cHardwareGX.cc
3 * Avida
4 *
5 * Copyright 1999-2011 Michigan State University. All rights reserved.
6 *
7 * This file is part of Avida.
8 *
9 * Avida is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 *
12 * Avida is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License along with Avida.
16 * If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "cHardwareGX.h"
21
22 #include "avida/core/WorldDriver.h"
23
24 #include "cAvidaContext.h"
25 #include "cCPUTestInfo.h"
26 #include "cEnvironment.h"
27 #include "cHardwareManager.h"
28 #include "cHardwareTracer.h"
29 #include "cInstSet.h"
30 #include "cOrganism.h"
31 #include "cPhenotype.h"
32 #include "cStringUtil.h"
33 #include "cTestCPU.h"
34 #include "cWorld.h"
35 #include "tInstLibEntry.h"
36
37 #include <climits>
38 #include <fstream>
39 #include <algorithm>
40
41 using namespace std;
42 using namespace Avida;
43 using namespace AvidaTools;
44
45 //! A small helper struct to make deleting a little easier.
46 struct delete_functor {
operator ()delete_functor47 template <typename T> void operator()(T *ptr) { delete ptr; }
48 };
49
50
51 tInstLib<cHardwareGX::tMethod>* cHardwareGX::s_inst_slib = cHardwareGX::initInstLib();
52 const double cHardwareGX::EXECUTABLE_COPY_PROCESSIVITY = 1.0;
53 const double cHardwareGX::READABLE_COPY_PROCESSIVITY = 1.0;
54
initInstLib(void)55 tInstLib<cHardwareGX::tMethod>* cHardwareGX::initInstLib(void)
56 {
57 struct cNOPEntryCPU {
58 cString name;
59 int nop_mod;
60 cNOPEntryCPU(const cString &name, int nop_mod)
61 : name(name), nop_mod(nop_mod) {}
62 };
63 static const cNOPEntryCPU s_n_array[] = {
64 cNOPEntryCPU("nop-A", REG_AX),
65 cNOPEntryCPU("nop-B", REG_BX),
66 cNOPEntryCPU("nop-C", REG_CX)
67 };
68
69 static const tInstLibEntry<tMethod> s_f_array[] = {
70 /*
71 Note: all entries of cNOPEntryCPU s_n_array must have corresponding
72 in the same order in tInstLibEntry<tMethod> s_f_array, and these entries must
73 be the first elements of s_f_array.
74 */
75 tInstLibEntry<tMethod>("nop-A", &cHardwareGX::Inst_Nop, (nInstFlag::DEFAULT | nInstFlag::NOP), "No-operation instruction; modifies other instructions"),
76 tInstLibEntry<tMethod>("nop-B", &cHardwareGX::Inst_Nop, (nInstFlag::DEFAULT | nInstFlag::NOP), "No-operation instruction; modifies other instructions"),
77 tInstLibEntry<tMethod>("nop-C", &cHardwareGX::Inst_Nop, (nInstFlag::DEFAULT | nInstFlag::NOP), "No-operation instruction; modifies other instructions"),
78
79 tInstLibEntry<tMethod>("nop-X", &cHardwareGX::Inst_Nop, 0, "True no-operation instruction: does nothing"),
80 tInstLibEntry<tMethod>("if-equ-0", &cHardwareGX::Inst_If0, 0, "Execute next instruction if ?BX?==0, else skip it"),
81 tInstLibEntry<tMethod>("if-not-0", &cHardwareGX::Inst_IfNot0, 0, "Execute next instruction if ?BX?!=0, else skip it"),
82 tInstLibEntry<tMethod>("if-n-equ", &cHardwareGX::Inst_IfNEqu, nInstFlag::DEFAULT, "Execute next instruction if ?BX?!=?CX?, else skip it"),
83 tInstLibEntry<tMethod>("if-equ", &cHardwareGX::Inst_IfEqu, 0, "Execute next instruction if ?BX?==?CX?, else skip it"),
84 tInstLibEntry<tMethod>("if-grt-0", &cHardwareGX::Inst_IfGr0),
85 tInstLibEntry<tMethod>("if-grt", &cHardwareGX::Inst_IfGr),
86 tInstLibEntry<tMethod>("if->=-0", &cHardwareGX::Inst_IfGrEqu0),
87 tInstLibEntry<tMethod>("if->=", &cHardwareGX::Inst_IfGrEqu),
88 tInstLibEntry<tMethod>("if-les-0", &cHardwareGX::Inst_IfLess0),
89 tInstLibEntry<tMethod>("if-less", &cHardwareGX::Inst_IfLess, nInstFlag::DEFAULT, "Execute next instruction if ?BX? < ?CX?, else skip it"),
90 tInstLibEntry<tMethod>("if-<=-0", &cHardwareGX::Inst_IfLsEqu0),
91 tInstLibEntry<tMethod>("if-<=", &cHardwareGX::Inst_IfLsEqu),
92 tInstLibEntry<tMethod>("if-A!=B", &cHardwareGX::Inst_IfANotEqB),
93 tInstLibEntry<tMethod>("if-B!=C", &cHardwareGX::Inst_IfBNotEqC),
94 tInstLibEntry<tMethod>("if-A!=C", &cHardwareGX::Inst_IfANotEqC),
95 tInstLibEntry<tMethod>("if-bit-1", &cHardwareGX::Inst_IfBit1),
96
97 tInstLibEntry<tMethod>("call", &cHardwareGX::Inst_Call),
98 tInstLibEntry<tMethod>("return", &cHardwareGX::Inst_Return),
99
100 tInstLibEntry<tMethod>("throw", &cHardwareGX::Inst_Throw),
101 tInstLibEntry<tMethod>("throwif=0", &cHardwareGX::Inst_ThrowIf0),
102 tInstLibEntry<tMethod>("throwif!=0", &cHardwareGX::Inst_ThrowIfNot0),
103 tInstLibEntry<tMethod>("catch", &cHardwareGX::Inst_Catch),
104
105 tInstLibEntry<tMethod>("goto", &cHardwareGX::Inst_Goto),
106 tInstLibEntry<tMethod>("goto-if=0", &cHardwareGX::Inst_GotoIf0),
107 tInstLibEntry<tMethod>("goto-if!=0", &cHardwareGX::Inst_GotoIfNot0),
108 tInstLibEntry<tMethod>("label", &cHardwareGX::Inst_Label),
109
110 tInstLibEntry<tMethod>("pop", &cHardwareGX::Inst_Pop, nInstFlag::DEFAULT, "Remove top number from stack and place into ?BX?"),
111 tInstLibEntry<tMethod>("push", &cHardwareGX::Inst_Push, nInstFlag::DEFAULT, "Copy number from ?BX? and place it into the stack"),
112 tInstLibEntry<tMethod>("swap-stk", &cHardwareGX::Inst_SwitchStack, nInstFlag::DEFAULT, "Toggle which stack is currently being used"),
113 tInstLibEntry<tMethod>("flip-stk", &cHardwareGX::Inst_FlipStack),
114 tInstLibEntry<tMethod>("swap", &cHardwareGX::Inst_Swap, nInstFlag::DEFAULT, "Swap the contents of ?BX? with ?CX?"),
115 tInstLibEntry<tMethod>("swap-AB", &cHardwareGX::Inst_SwapAB),
116 tInstLibEntry<tMethod>("swap-BC", &cHardwareGX::Inst_SwapBC),
117 tInstLibEntry<tMethod>("swap-AC", &cHardwareGX::Inst_SwapAC),
118 tInstLibEntry<tMethod>("copy-reg", &cHardwareGX::Inst_CopyReg),
119 tInstLibEntry<tMethod>("set_A=B", &cHardwareGX::Inst_CopyRegAB),
120 tInstLibEntry<tMethod>("set_A=C", &cHardwareGX::Inst_CopyRegAC),
121 tInstLibEntry<tMethod>("set_B=A", &cHardwareGX::Inst_CopyRegBA),
122 tInstLibEntry<tMethod>("set_B=C", &cHardwareGX::Inst_CopyRegBC),
123 tInstLibEntry<tMethod>("set_C=A", &cHardwareGX::Inst_CopyRegCA),
124 tInstLibEntry<tMethod>("set_C=B", &cHardwareGX::Inst_CopyRegCB),
125 tInstLibEntry<tMethod>("reset", &cHardwareGX::Inst_Reset),
126
127 tInstLibEntry<tMethod>("pop-A", &cHardwareGX::Inst_PopA),
128 tInstLibEntry<tMethod>("pop-B", &cHardwareGX::Inst_PopB),
129 tInstLibEntry<tMethod>("pop-C", &cHardwareGX::Inst_PopC),
130 tInstLibEntry<tMethod>("push-A", &cHardwareGX::Inst_PushA),
131 tInstLibEntry<tMethod>("push-B", &cHardwareGX::Inst_PushB),
132 tInstLibEntry<tMethod>("push-C", &cHardwareGX::Inst_PushC),
133
134 tInstLibEntry<tMethod>("shift-r", &cHardwareGX::Inst_ShiftR, nInstFlag::DEFAULT, "Shift bits in ?BX? right by one (divide by two)"),
135 tInstLibEntry<tMethod>("shift-l", &cHardwareGX::Inst_ShiftL, nInstFlag::DEFAULT, "Shift bits in ?BX? left by one (multiply by two)"),
136 tInstLibEntry<tMethod>("bit-1", &cHardwareGX::Inst_Bit1),
137 tInstLibEntry<tMethod>("set-num", &cHardwareGX::Inst_SetNum),
138 tInstLibEntry<tMethod>("val-grey", &cHardwareGX::Inst_ValGrey),
139 tInstLibEntry<tMethod>("val-dir", &cHardwareGX::Inst_ValDir),
140 tInstLibEntry<tMethod>("val-add-p", &cHardwareGX::Inst_ValAddP),
141 tInstLibEntry<tMethod>("val-fib", &cHardwareGX::Inst_ValFib),
142 tInstLibEntry<tMethod>("val-poly-c", &cHardwareGX::Inst_ValPolyC),
143 tInstLibEntry<tMethod>("inc", &cHardwareGX::Inst_Inc, nInstFlag::DEFAULT, "Increment ?BX? by one"),
144 tInstLibEntry<tMethod>("dec", &cHardwareGX::Inst_Dec, nInstFlag::DEFAULT, "Decrement ?BX? by one"),
145 tInstLibEntry<tMethod>("zero", &cHardwareGX::Inst_Zero, 0, "Set ?BX? to zero"),
146 tInstLibEntry<tMethod>("neg", &cHardwareGX::Inst_Neg),
147 tInstLibEntry<tMethod>("square", &cHardwareGX::Inst_Square),
148 tInstLibEntry<tMethod>("sqrt", &cHardwareGX::Inst_Sqrt),
149 tInstLibEntry<tMethod>("not", &cHardwareGX::Inst_Not),
150
151 tInstLibEntry<tMethod>("add", &cHardwareGX::Inst_Add, nInstFlag::DEFAULT, "Add BX to CX and place the result in ?BX?"),
152 tInstLibEntry<tMethod>("sub", &cHardwareGX::Inst_Sub, nInstFlag::DEFAULT, "Subtract CX from BX and place the result in ?BX?"),
153 tInstLibEntry<tMethod>("mult", &cHardwareGX::Inst_Mult, 0, "Multiple BX by CX and place the result in ?BX?"),
154 tInstLibEntry<tMethod>("div", &cHardwareGX::Inst_Div, 0, "Divide BX by CX and place the result in ?BX?"),
155 tInstLibEntry<tMethod>("mod", &cHardwareGX::Inst_Mod),
156 tInstLibEntry<tMethod>("nand", &cHardwareGX::Inst_Nand, nInstFlag::DEFAULT, "Nand BX by CX and place the result in ?BX?"),
157 tInstLibEntry<tMethod>("nor", &cHardwareGX::Inst_Nor),
158 tInstLibEntry<tMethod>("and", &cHardwareGX::Inst_And),
159 tInstLibEntry<tMethod>("order", &cHardwareGX::Inst_Order),
160 tInstLibEntry<tMethod>("xor", &cHardwareGX::Inst_Xor),
161
162 tInstLibEntry<tMethod>("copy", &cHardwareGX::Inst_Copy),
163 tInstLibEntry<tMethod>("read", &cHardwareGX::Inst_ReadInst),
164 tInstLibEntry<tMethod>("write", &cHardwareGX::Inst_WriteInst),
165 tInstLibEntry<tMethod>("stk-read", &cHardwareGX::Inst_StackReadInst),
166 tInstLibEntry<tMethod>("stk-writ", &cHardwareGX::Inst_StackWriteInst),
167
168 tInstLibEntry<tMethod>("compare", &cHardwareGX::Inst_Compare),
169 tInstLibEntry<tMethod>("if-n-cpy", &cHardwareGX::Inst_IfNCpy),
170 tInstLibEntry<tMethod>("allocate", &cHardwareGX::Inst_Allocate),
171 tInstLibEntry<tMethod>("c-alloc", &cHardwareGX::Inst_CAlloc),
172 tInstLibEntry<tMethod>("search-f", &cHardwareGX::Inst_SearchF),
173 tInstLibEntry<tMethod>("search-b", &cHardwareGX::Inst_SearchB),
174 tInstLibEntry<tMethod>("mem-size", &cHardwareGX::Inst_MemSize),
175
176 tInstLibEntry<tMethod>("get", &cHardwareGX::Inst_TaskGet),
177 tInstLibEntry<tMethod>("get-2", &cHardwareGX::Inst_TaskGet2),
178 tInstLibEntry<tMethod>("stk-get", &cHardwareGX::Inst_TaskStackGet),
179 tInstLibEntry<tMethod>("stk-load", &cHardwareGX::Inst_TaskStackLoad),
180 tInstLibEntry<tMethod>("put", &cHardwareGX::Inst_TaskPut),
181 tInstLibEntry<tMethod>("put-reset", &cHardwareGX::Inst_TaskPutResetInputs),
182 tInstLibEntry<tMethod>("IO", &cHardwareGX::Inst_TaskIO, nInstFlag::DEFAULT, "Output ?BX?, and input new number back into ?BX?"),
183 tInstLibEntry<tMethod>("IO-decay", &cHardwareGX::Inst_TaskIO_DecayBonus),
184 tInstLibEntry<tMethod>("IO-Feedback", &cHardwareGX::Inst_TaskIO_Feedback, 0, "Output ?BX?, and input new number back into ?BX?, and push 1,0, or -1 onto stack1 if merit increased, stayed the same, or decreased"),
185 tInstLibEntry<tMethod>("match-strings", &cHardwareGX::Inst_MatchStrings),
186 tInstLibEntry<tMethod>("sell", &cHardwareGX::Inst_Sell),
187 tInstLibEntry<tMethod>("buy", &cHardwareGX::Inst_Buy),
188 tInstLibEntry<tMethod>("send", &cHardwareGX::Inst_Send),
189 tInstLibEntry<tMethod>("receive", &cHardwareGX::Inst_Receive),
190 tInstLibEntry<tMethod>("sense", &cHardwareGX::Inst_SenseLog2),
191 tInstLibEntry<tMethod>("sense-unit", &cHardwareGX::Inst_SenseUnit),
192 tInstLibEntry<tMethod>("sense-m100", &cHardwareGX::Inst_SenseMult100),
193
194 tInstLibEntry<tMethod>("donate-rnd", &cHardwareGX::Inst_DonateRandom),
195 tInstLibEntry<tMethod>("donate-edt", &cHardwareGX::Inst_DonateEditDist),
196 tInstLibEntry<tMethod>("donate-gbg", &cHardwareGX::Inst_DonateGreenBeardGene),
197 tInstLibEntry<tMethod>("donate-tgb", &cHardwareGX::Inst_DonateTrueGreenBeard),
198 tInstLibEntry<tMethod>("donate-threshgb", &cHardwareGX::Inst_DonateThreshGreenBeard),
199 tInstLibEntry<tMethod>("donate-quantagb", &cHardwareGX::Inst_DonateQuantaThreshGreenBeard),
200 tInstLibEntry<tMethod>("donate-NUL", &cHardwareGX::Inst_DonateNULL),
201
202 tInstLibEntry<tMethod>("rotate-l", &cHardwareGX::Inst_RotateL),
203 tInstLibEntry<tMethod>("rotate-r", &cHardwareGX::Inst_RotateR),
204
205 tInstLibEntry<tMethod>("set-cmut", &cHardwareGX::Inst_SetCopyMut),
206 tInstLibEntry<tMethod>("mod-cmut", &cHardwareGX::Inst_ModCopyMut),
207
208 // Energy instruction
209 tInstLibEntry<tMethod>("recover", &cHardwareGX::Inst_ZeroEnergyUsed),
210
211 // Head-based instructions
212 tInstLibEntry<tMethod>("h-alloc", &cHardwareGX::Inst_MaxAlloc, nInstFlag::DEFAULT, "Allocate maximum allowed space"),
213 tInstLibEntry<tMethod>("h-divide", &cHardwareGX::Inst_HeadDivide, nInstFlag::DEFAULT, "Divide code between read and write heads."),
214 tInstLibEntry<tMethod>("h-read", &cHardwareGX::Inst_HeadRead),
215 tInstLibEntry<tMethod>("h-write", &cHardwareGX::Inst_HeadWrite),
216 tInstLibEntry<tMethod>("h-copy", &cHardwareGX::Inst_HeadCopy, nInstFlag::DEFAULT, "Copy from read-head to write-head; advance both"),
217 tInstLibEntry<tMethod>("h-search", &cHardwareGX::Inst_HeadSearch, nInstFlag::DEFAULT, "Find complement template and make with flow head"),
218 tInstLibEntry<tMethod>("h-push", &cHardwareGX::Inst_HeadPush),
219 tInstLibEntry<tMethod>("h-pop", &cHardwareGX::Inst_HeadPop),
220 tInstLibEntry<tMethod>("adv-head", &cHardwareGX::Inst_AdvanceHead),
221 tInstLibEntry<tMethod>("mov-head", &cHardwareGX::Inst_MoveHead, nInstFlag::DEFAULT, "Move head ?IP? to the flow head"),
222 tInstLibEntry<tMethod>("jmp-head", &cHardwareGX::Inst_JumpHead, nInstFlag::DEFAULT, "Move head ?IP? by amount in CX register; CX = old pos."),
223 tInstLibEntry<tMethod>("get-head", &cHardwareGX::Inst_GetHead, nInstFlag::DEFAULT, "Copy the position of the ?IP? head into CX"),
224 tInstLibEntry<tMethod>("if-label", &cHardwareGX::Inst_IfLabel, nInstFlag::DEFAULT, "Execute next if we copied complement of attached label"),
225 tInstLibEntry<tMethod>("if-label2", &cHardwareGX::Inst_IfLabel2, 0, "If copied label compl., exec next inst; else SKIP W/NOPS"),
226 tInstLibEntry<tMethod>("set-flow", &cHardwareGX::Inst_SetFlow, nInstFlag::DEFAULT, "Set flow-head to position in ?CX?"),
227
228 tInstLibEntry<tMethod>("h-copy2", &cHardwareGX::Inst_HeadCopy2),
229 tInstLibEntry<tMethod>("h-copy3", &cHardwareGX::Inst_HeadCopy3),
230 tInstLibEntry<tMethod>("h-copy4", &cHardwareGX::Inst_HeadCopy4),
231 tInstLibEntry<tMethod>("h-copy5", &cHardwareGX::Inst_HeadCopy5),
232 tInstLibEntry<tMethod>("h-copy6", &cHardwareGX::Inst_HeadCopy6),
233 tInstLibEntry<tMethod>("h-copy7", &cHardwareGX::Inst_HeadCopy7),
234 tInstLibEntry<tMethod>("h-copy8", &cHardwareGX::Inst_HeadCopy8),
235 tInstLibEntry<tMethod>("h-copy9", &cHardwareGX::Inst_HeadCopy9),
236 tInstLibEntry<tMethod>("h-copy10", &cHardwareGX::Inst_HeadCopy10),
237
238 tInstLibEntry<tMethod>("repro", &cHardwareGX::Inst_Repro),
239
240 tInstLibEntry<tMethod>("spawn-deme", &cHardwareGX::Inst_SpawnDeme),
241
242 // Suicide
243 tInstLibEntry<tMethod>("kazi", &cHardwareGX::Inst_Kazi),
244 tInstLibEntry<tMethod>("kazi5", &cHardwareGX::Inst_Kazi5),
245 tInstLibEntry<tMethod>("die", &cHardwareGX::Inst_Die),
246
247 // Placebo instructions
248 tInstLibEntry<tMethod>("skip", &cHardwareGX::Inst_Skip),
249
250 // Gene Expression Instructions
251 tInstLibEntry<tMethod>("p-alloc", &cHardwareGX::Inst_NewExecutableProgramid),
252 tInstLibEntry<tMethod>("g-alloc", &cHardwareGX::Inst_NewGenomeProgramid),
253 tInstLibEntry<tMethod>("p-copy", &cHardwareGX::Inst_ProgramidCopy),
254 tInstLibEntry<tMethod>("p-divide", &cHardwareGX::Inst_ProgramidDivide),
255
256 tInstLibEntry<tMethod>("site", &cHardwareGX::Inst_Site),
257 tInstLibEntry<tMethod>("bind", &cHardwareGX::Inst_Bind),
258 tInstLibEntry<tMethod>("bind2", &cHardwareGX::Inst_Bind2),
259 tInstLibEntry<tMethod>("if-bind", &cHardwareGX::Inst_IfBind),
260 tInstLibEntry<tMethod>("if-bind2", &cHardwareGX::Inst_IfBind2),
261 tInstLibEntry<tMethod>("num-sites", &cHardwareGX::Inst_NumSites),
262
263 tInstLibEntry<tMethod>("i-alloc", &cHardwareGX::Inst_ProgramidImplicitAllocate),
264 tInstLibEntry<tMethod>("i-divide", &cHardwareGX::Inst_ProgramidImplicitDivide),
265
266 tInstLibEntry<tMethod>("promoter", &cHardwareGX::Inst_Promoter),
267 tInstLibEntry<tMethod>("terminator", &cHardwareGX::Inst_Terminator),
268 tInstLibEntry<tMethod>("h-repress", &cHardwareGX::Inst_HeadRepress),
269 tInstLibEntry<tMethod>("h-activate", &cHardwareGX::Inst_HeadActivate),
270 tInstLibEntry<tMethod>("end", &cHardwareGX::Inst_EndProgramidExecution),
271
272 // These are dummy instructions used for making mutiple programids
273 // look like one genome (for purposes of passing to offspring and printing).
274 // They need to be included for the full GX model but should have ZERO probabilities!!!
275 tInstLibEntry<tMethod>("PROGRAMID", &cHardwareGX::Inst_Nop),
276 tInstLibEntry<tMethod>("READABLE", &cHardwareGX::Inst_Nop),
277 tInstLibEntry<tMethod>("BINDABLE", &cHardwareGX::Inst_Nop),
278 tInstLibEntry<tMethod>("EXECUTABLE", &cHardwareGX::Inst_Nop),
279
280 // Must always be the last instruction
281 tInstLibEntry<tMethod>("NULL", &cHardwareGX::Inst_Nop, 0, "True no-operation instruction: does nothing"),
282 };
283
284 const int n_size = sizeof(s_n_array)/sizeof(cNOPEntryCPU);
285
286 static cString n_names[n_size];
287 static int nop_mods[n_size];
288 for (int i = 0; i < n_size && i < NUM_REGISTERS; i++) {
289 n_names[i] = s_n_array[i].name;
290 nop_mods[i] = s_n_array[i].nop_mod;
291 }
292
293 const int f_size = sizeof(s_f_array)/sizeof(tInstLibEntry<tMethod>);
294 static tMethod functions[f_size];
295 for (int i = 0; i < f_size; i++) functions[i] = s_f_array[i].GetFunction();
296
297 const int def = 0;
298 const int null_inst = f_size - 1;
299
300 return new tInstLib<tMethod>(f_size, s_f_array, n_names, nop_mods, functions, def, null_inst);
301 }
302
303
304 /*! Construct a cHardwareGX instance from the passed-in cOrganism. This amounts to
305 creating an initial cProgramid from in_organism's genome.
306 */
cHardwareGX(cAvidaContext & ctx,cWorld * world,cOrganism * in_organism,cInstSet * in_inst_set)307 cHardwareGX::cHardwareGX(cAvidaContext& ctx, cWorld* world, cOrganism* in_organism, cInstSet* in_inst_set)
308 : cHardwareBase(world, in_organism, in_inst_set)
309 {
310 m_last_unique_id_assigned = 0;
311 m_functions = s_inst_slib->GetFunctions();
312 Reset(ctx); // Setup the rest of the hardware...also creates initial programid(s) from genome
313 }
314
315
316 /*! Destructor; delete all programids. */
~cHardwareGX()317 cHardwareGX::~cHardwareGX()
318 {
319 std::for_each(m_programids.begin(), m_programids.end(), delete_functor());
320 }
321
322
323 /*! Reset this cHardwareGX to a known state.
324 Removes all the current cProgramids, and creates new cProgramids from the germ.
325 */
internalReset()326 void cHardwareGX::internalReset()
327 {
328 // Clear the current list of programids.
329 std::for_each(m_programids.begin(), m_programids.end(), delete_functor());
330 m_programids.clear();
331
332 // Using the "full" gene expression setup. All programid creation is explicit.
333 if (m_world->GetConfig().IMPLICIT_GENE_EXPRESSION.Get() == 0)
334 {
335
336 // And add any programids specified by the "genome."
337 Sequence genome = m_organism->GetGenome().GetSequence();
338
339 // These specify the range of instructions that will be used to create a new
340 // programid. The range of instructions used to create a programid is:
341 // [begin, end), that is, the instruction pointed to by end is *not* copied.
342 // Find the first instance of a PROGRAMID instruction.
343 int begin = genome.FindInst(GetInstSet().GetInst("PROGRAMID"));
344 while (true) {
345 // Find the boundary of this programid.
346 int i = genome.FindInst(GetInstSet().GetInst("PROGRAMID"), begin + 1);
347 if (i == -1) {
348 if (begin != -1) AddProgramid(new cProgramid(genome.Crop(begin, genome.GetSize()), this));
349 break;
350 }
351 AddProgramid(new cProgramid(genome.Crop(begin, i), this));
352 begin = i;
353 }
354
355 assert(m_programids.size()>0);
356
357 // Sanity, oh, where is my sanity?
358 bool has_executable=false;
359 bool has_bindable=false;
360 for(programid_list::iterator i=m_programids.begin(); i!=m_programids.end(); ++i) {
361 has_executable = has_executable || (*i)->GetExecutable();
362 has_bindable = has_bindable || (*i)->GetBindable();
363 }
364 assert(has_bindable && has_executable);
365 }
366 else // Implicit RNAP GX Model. Executable programids created from genome.
367 {
368 m_recycle_state = 0.0;
369 // Optimization -- don't actually need a programid for the genome in the double implicit model.
370 // In the implicit model, we create one genome programid as the first memory space
371 programid_ptr p = new cProgramid(m_organism->GetGenome().GetSequence(), this);
372 p->SetReadable(true);
373 AddProgramid(p);
374
375 // Initialize the promoter state and rates
376 m_promoter_update_head.Reset(this,0);
377 m_promoter_update_head.Set(0);
378 m_promoter_default_rates.ResizeClear( m_organism->GetGenome().GetSize() );
379 m_promoter_rates.ResizeClear( m_organism->GetGenome().GetSize() ); // Initialized in UpdatePromoterRates()
380 m_promoter_states.ResizeClear( m_organism->GetGenome().GetSize() );
381 m_promoter_occupied_sites.ResizeClear( m_organism->GetGenome().GetSize() );
382
383 cInstruction promoter_inst = GetInstSet().GetInst(cStringUtil::Stringf("promoter"));
384 do {
385 m_promoter_default_rates[m_promoter_update_head.GetPosition()] =
386 (m_promoter_update_head.GetInst() == promoter_inst) ? 1 : m_world->GetConfig().IMPLICIT_BG_PROMOTER_RATE.Get();
387 m_promoter_states[m_promoter_update_head.GetPosition()] = 0.0;
388 m_promoter_occupied_sites[m_promoter_update_head.GetPosition()] = 0;
389 m_promoter_update_head++;
390 } while (m_promoter_update_head.GetPosition() != 0);
391
392 AdjustPromoterRates();
393
394 // \todo implement different initial conditions for created executable programids
395 m_promoter_update_head.Set(m_organism->GetGenome().GetSize() - 1); //So that ++ moves it to position zero
396 ProcessImplicitGeneExpression(1);
397 }
398
399 m_current = m_programids.back();
400 m_mal_active = false;
401 m_executedmatchstrings = false;
402
403 }
404
405
internalResetOnFailedDivide()406 void cHardwareGX::internalResetOnFailedDivide()
407 {
408 internalReset();
409 }
410
411 /*! In cHardwareGX, SingleProcess is something of a misnomer. Each time this method
412 is called, each cProgramid executes a single instruction.
413 */
SingleProcess(cAvidaContext & ctx,bool speculative)414 bool cHardwareGX::SingleProcess(cAvidaContext& ctx, bool speculative)
415 {
416 if (speculative) return false;
417
418 cPhenotype& phenotype = m_organism->GetPhenotype();
419
420 // Turn over programids if using the implicit model
421 if ( m_world->GetConfig().IMPLICIT_GENE_EXPRESSION.Get() )
422 {
423 m_recycle_state += (double)m_world->GetConfig().IMPLICIT_TURNOVER_RATE.Get();
424 int num_programids = m_programids.size();
425 while (m_recycle_state >= 1.0)
426 {
427 if (m_programids.size() > (unsigned int)m_world->GetConfig().MAX_PROGRAMIDS.Get())
428 {
429 RemoveProgramid(1);
430 }
431 else
432 {
433 num_programids++;
434 }
435 m_recycle_state -= 1.0;
436 }
437 ProcessImplicitGeneExpression(num_programids);
438 }
439
440 m_organism->SetRunning(true);
441 m_just_divided = false;
442 phenotype.IncTimeUsed();
443
444 // A temporary list of the current programids in this organism.
445 programid_list runnable(m_programids.begin(), m_programids.end());
446
447 // Execute one instruction for each programid.
448 for(programid_list::iterator i=runnable.begin(); i!=runnable.end(); ++i) {
449 // Currently executing programid.
450 m_current = *i;
451
452 // Print the status of this CPU at each step...
453 if (m_tracer != NULL) m_tracer->TraceHardware(ctx, *this);
454
455 if(m_current->GetExecute()) {
456 // In case the IP is modified by this instruction, make sure that it wraps
457 // around to the beginning of the genome.
458 IP().Adjust();
459
460 // Find the instruction to be executed.
461 const cInstruction& cur_inst = IP().GetInst();
462
463 m_advance_ip = true;
464 m_reset_inputs = false;
465 m_reset_heads = false;
466 SingleProcess_ExecuteInst(ctx, cur_inst);
467
468 // Break out if we just divided b/c the number of programids
469 // will have changed and it won't be obvious how to continue
470 // @JEB this organism may also have been replaced by its child,
471 // so we will not be able to continue safely!
472 if (m_just_divided) break;
473
474 if (m_advance_ip == true) {
475 IP().Advance();
476 }
477
478 // Update this programid stat
479 m_current->IncCPUCyclesUsed();
480 }
481 }
482 m_current = 0;
483
484 // Now kill old programids.
485 unsigned int on_p = 0;
486 while(on_p < m_programids.size()) {
487 if( ((m_world->GetConfig().MAX_PROGRAMID_AGE.Get() > 0) && (m_programids[on_p]->GetCPUCyclesUsed() > m_world->GetConfig().MAX_PROGRAMID_AGE.Get()) )
488 || m_programids[on_p]->m_marked_for_death) {
489 RemoveProgramid(on_p);
490 } else {
491 on_p++;
492 }
493 }
494
495 m_current = m_programids.back(); // m_current must always point to a programid!
496
497 // Update phenotype. Note the difference between these cpu cycles and the per-programid ones...
498 phenotype.IncCPUCyclesUsed();
499
500 // Kill creatures who have reached their max num of instructions executed.
501 const int max_executed = m_organism->GetMaxExecuted();
502 if((max_executed > 0 && phenotype.GetTimeUsed() >= max_executed)
503 || phenotype.GetToDie() == true) {
504 m_organism->Die(ctx);
505 }
506
507 // Kill organisms that have no active programids.
508 if(m_programids.size() == 0) {
509 m_organism->Die(ctx);
510 }
511
512 m_organism->SetRunning(false);
513 CheckImplicitRepro(ctx);
514
515 return true;
516 }
517
518 /*! This method executes one instruction for one programid. */
SingleProcess_ExecuteInst(cAvidaContext & ctx,const cInstruction & cur_inst)519 bool cHardwareGX::SingleProcess_ExecuteInst(cAvidaContext& ctx, const cInstruction& cur_inst)
520 {
521 // Copy the instruction in case of execution errors.
522 cInstruction actual_inst = cur_inst;
523
524 #ifdef EXECUTION_ERRORS
525 // If there is an execution error, execute a random instruction.
526 if (m_organism->TestExeErr()) actual_inst = m_inst_set->GetRandomInst(ctx);
527 #endif /* EXECUTION_ERRORS */
528
529 // Get the index for the instruction.
530 int inst_idx = m_inst_set->GetLibFunctionIndex(actual_inst);
531
532 // Mark the instruction as executed
533 IP().SetFlagExecuted();
534
535 // instruction execution count incremeneted
536 m_organism->GetPhenotype().IncCurInstCount(actual_inst.GetOp());
537
538 // And execute it.
539 const bool exec_success = (this->*(m_functions[inst_idx]))(ctx);
540
541 // decremenet if the instruction was not executed successfully
542 if (exec_success == false) {
543 m_organism->GetPhenotype().DecCurInstCount(actual_inst.GetOp());
544 }
545
546 return exec_success;
547 }
548
549
ProcessBonusInst(cAvidaContext & ctx,const cInstruction & inst)550 void cHardwareGX::ProcessBonusInst(cAvidaContext& ctx, const cInstruction& inst)
551 {
552 // Mark this organism as running...
553 bool prev_run_state = m_organism->IsRunning();
554 m_organism->SetRunning(true);
555
556 if (m_tracer != NULL) m_tracer->TraceHardware(ctx, *this, true);
557
558 SingleProcess_ExecuteInst(ctx, inst);
559
560 m_organism->SetRunning(prev_run_state);
561 }
562
563
564 /*! \todo Revisit.
565 */
PrintStatus(ostream & fp)566 void cHardwareGX::PrintStatus(ostream& fp)
567 {
568 //\todo clean up references
569
570 // Extra information about this programid
571 fp << "MemSpace:" << m_current->GetID() << " Programid ID:" << m_current->m_unique_id;
572 if (m_current->GetExecute()) fp << " EXECUTING";
573 if (m_current->GetExecutable()) fp << " EXECUTABLE";
574 if (m_current->GetBindable()) fp << " BINDABLE";
575 if (m_current->GetReadable()) fp << " READABLE";
576 fp << endl;
577
578 fp << m_organism->GetPhenotype().GetCPUCyclesUsed() << " ";
579 fp << "IP:" << IP().GetPosition() << " ";
580
581 for (int i = 0; i < NUM_REGISTERS; i++) {
582 fp << static_cast<char>('A' + i) << "X:" << GetRegister(i) << " ";
583 fp << setbase(16) << "[0x" << GetRegister(i) << "] " << setbase(10);
584 }
585 fp << endl;
586
587 fp << " R-Head:" << GetHead(nHardware::HEAD_READ).GetMemSpace() << ":" << GetHead(nHardware::HEAD_READ).GetPosition() << " "
588 << "W-Head:" << GetHead(nHardware::HEAD_WRITE).GetMemSpace() << ":" << GetHead(nHardware::HEAD_WRITE).GetPosition() << " "
589 << "F-Head:" << GetHead(nHardware::HEAD_FLOW).GetMemSpace() << ":" << GetHead(nHardware::HEAD_FLOW).GetPosition() << " "
590 << "RL:" << GetReadLabel().AsString() << " "
591 << endl;
592
593 // This will have to be revisited soon.
594 // int number_of_stacks = GetNumStacks();
595 // for (int stack_id = 0; stack_id < number_of_stacks; stack_id++) {
596 // fp << ((m_threads[m_cur_thread].cur_stack == stack_id) ? '*' : ' ') << " Stack "
597 // << stack_id << ":" << setbase(16) << setfill('0');
598 // for (int i = 0; i < nHardware::STACK_SIZE; i++)
599 // fp << " Ox" << setw(8) << GetStack(i, stack_id, 0);
600 // fp << setfill(' ') << setbase(10) << endl;
601 // }
602
603 fp << " Mem (" << GetMemory().GetSize() << "):"
604 << " " << GetMemory().AsString()
605 << endl;
606 fp.flush();
607 }
608
609 /////////////////////////////////////////////////////////////////////////
610 // Method: cHardwareGX::FindLabel(direction)
611 //
612 // Search in 'direction' (+ or - 1) from the instruction pointer for the
613 // compliment of the label in 'next_label' and return a pointer to the
614 // results. If direction is 0, search from the beginning of the genome.
615 //
616 /////////////////////////////////////////////////////////////////////////
617
FindLabel(int direction)618 cHeadCPU cHardwareGX::FindLabel(int direction)
619 {
620 cHeadCPU & inst_ptr = IP();
621
622 // Start up a search head at the position of the instruction pointer.
623 cHeadCPU search_head(inst_ptr);
624 cCodeLabel & search_label = GetLabel();
625
626 // Make sure the label is of size > 0.
627
628 if (search_label.GetSize() == 0) {
629 return inst_ptr;
630 }
631
632 // Call special functions depending on if jump is forwards or backwards.
633 int found_pos = 0;
634 if( direction < 0 ) {
635 found_pos = FindLabel_Backward(search_label, inst_ptr.GetMemory(),
636 inst_ptr.GetPosition() - search_label.GetSize());
637 }
638
639 // Jump forward.
640 else if (direction > 0) {
641 found_pos = FindLabel_Forward(search_label, inst_ptr.GetMemory(),
642 inst_ptr.GetPosition());
643 }
644
645 // Jump forward from the very beginning.
646 else {
647 found_pos = FindLabel_Forward(search_label, inst_ptr.GetMemory(), 0);
648 }
649
650 // Return the last line of the found label, if it was found.
651 if (found_pos >= 0) search_head.Set(found_pos - 1);
652
653 // Return the found position (still at start point if not found).
654 return search_head;
655 }
656
657
658 // Search forwards for search_label from _after_ position pos in the
659 // memory. Return the first line _after_ the the found label. It is okay
660 // to find search label's match inside another label.
661
FindLabel_Forward(const cCodeLabel & search_label,const Sequence & search_genome,int pos)662 int cHardwareGX::FindLabel_Forward(const cCodeLabel & search_label,
663 const Sequence & search_genome, int pos)
664 {
665 assert (pos < search_genome.GetSize() && pos >= 0);
666
667 int search_start = pos;
668 int label_size = search_label.GetSize();
669 bool found_label = false;
670
671 // Move off the template we are on.
672 pos += label_size;
673
674 // Search until we find the complement or exit the memory.
675 while (pos < search_genome.GetSize()) {
676
677 // If we are within a label, rewind to the beginning of it and see if
678 // it has the proper sub-label that we're looking for.
679
680 if (m_inst_set->IsNop(search_genome[pos])) {
681 // Find the start and end of the label we're in the middle of.
682
683 int start_pos = pos;
684 int end_pos = pos + 1;
685 while (start_pos > search_start &&
686 m_inst_set->IsNop( search_genome[start_pos - 1] )) {
687 start_pos--;
688 }
689 while (end_pos < search_genome.GetSize() &&
690 m_inst_set->IsNop( search_genome[end_pos] )) {
691 end_pos++;
692 }
693 int test_size = end_pos - start_pos;
694
695 // See if this label has the proper sub-label within it.
696 int max_offset = test_size - label_size + 1;
697 int offset = start_pos;
698 for (offset = start_pos; offset < start_pos + max_offset; offset++) {
699
700 // Test the number of matches for this offset.
701 int matches;
702 for (matches = 0; matches < label_size; matches++) {
703 if (search_label[matches] !=
704 m_inst_set->GetNopMod( search_genome[offset + matches] )) {
705 break;
706 }
707 }
708
709 // If we have found it, break out of this loop!
710 if (matches == label_size) {
711 found_label = true;
712 break;
713 }
714 }
715
716 // If we've found the complement label, set the position to the end of
717 // the label we found it in, and break out.
718
719 if (found_label == true) {
720 // pos = end_pos;
721 pos = label_size + offset;
722 break;
723 }
724
725 // We haven't found it; jump pos to just after the current label being
726 // checked.
727 pos = end_pos;
728 }
729
730 // Jump up a block to the next possible point to find a label,
731 pos += label_size;
732 }
733
734 // If the label was not found return a -1.
735 if (found_label == false) pos = -1;
736
737 return pos;
738 }
739
740 // Search backwards for search_label from _before_ position pos in the
741 // memory. Return the first line _after_ the the found label. It is okay
742 // to find search label's match inside another label.
743
FindLabel_Backward(const cCodeLabel & search_label,const Sequence & search_genome,int pos)744 int cHardwareGX::FindLabel_Backward(const cCodeLabel & search_label,
745 const Sequence & search_genome, int pos)
746 {
747 assert (pos < search_genome.GetSize());
748
749 int search_start = pos;
750 int label_size = search_label.GetSize();
751 bool found_label = false;
752
753 // Move off the template we are on.
754 pos -= label_size;
755
756 // Search until we find the complement or exit the memory.
757 while (pos >= 0) {
758 // If we are within a label, rewind to the beginning of it and see if
759 // it has the proper sub-label that we're looking for.
760
761 if (m_inst_set->IsNop( search_genome[pos] )) {
762 // Find the start and end of the label we're in the middle of.
763
764 int start_pos = pos;
765 int end_pos = pos + 1;
766 while (start_pos > 0 && m_inst_set->IsNop(search_genome[start_pos - 1])) {
767 start_pos--;
768 }
769 while (end_pos < search_start &&
770 m_inst_set->IsNop(search_genome[end_pos])) {
771 end_pos++;
772 }
773 int test_size = end_pos - start_pos;
774
775 // See if this label has the proper sub-label within it.
776 int max_offset = test_size - label_size + 1;
777 for (int offset = start_pos; offset < start_pos + max_offset; offset++) {
778
779 // Test the number of matches for this offset.
780 int matches;
781 for (matches = 0; matches < label_size; matches++) {
782 if (search_label[matches] !=
783 m_inst_set->GetNopMod(search_genome[offset + matches])) {
784 break;
785 }
786 }
787
788 // If we have found it, break out of this loop!
789 if (matches == label_size) {
790 found_label = true;
791 break;
792 }
793 }
794
795 // If we've found the complement label, set the position to the end of
796 // the label we found it in, and break out.
797
798 if (found_label == true) {
799 pos = end_pos;
800 break;
801 }
802
803 // We haven't found it; jump pos to just before the current label
804 // being checked.
805 pos = start_pos - 1;
806 }
807
808 // Jump up a block to the next possible point to find a label,
809 pos -= label_size;
810 }
811
812 // If the label was not found return a -1.
813 if (found_label == false) pos = -1;
814
815 return pos;
816 }
817
818 // Search for 'in_label' anywhere in the hardware.
FindLabel(const cCodeLabel & in_label,int direction)819 cHeadCPU cHardwareGX::FindLabel(const cCodeLabel & in_label, int direction)
820 {
821 assert (in_label.GetSize() > 0);
822
823 // IDEALY:
824 // Keep making jumps (in the proper direction) equal to the label
825 // length. If we are inside of a label, check its size, and see if
826 // any of the sub-labels match properly.
827 // FOR NOW:
828 // Get something which works, no matter how inefficient!!!
829
830 cHeadCPU temp_head(this);
831
832 while (temp_head.InMemory()) {
833 // IDEALY: Analyze the label we are in; see if the one we are looking
834 // for could be a sub-label of it. Skip past it if not.
835
836 int i;
837 for (i = 0; i < in_label.GetSize(); i++) {
838 if (!m_inst_set->IsNop(temp_head.GetInst()) ||
839 in_label[i] != m_inst_set->GetNopMod(temp_head.GetInst())) {
840 break;
841 }
842 }
843 if (i == GetLabel().GetSize()) {
844 temp_head.AbsJump(i - 1);
845 return temp_head;
846 }
847
848 temp_head.AbsJump(direction); // IDEALY: MAKE LARGER JUMPS
849 }
850
851 temp_head.AbsSet(-1);
852 return temp_head;
853 }
854
855
856
857
858
ReadInst(const int in_inst)859 void cHardwareGX::ReadInst(const int in_inst)
860 {
861 if(m_inst_set->IsNop(cInstruction(in_inst))) {
862 GetReadLabel().AddNop(in_inst);
863 } else {
864 GetReadLabel().Clear();
865 }
866 }
867
868
AdjustHeads()869 void cHardwareGX::AdjustHeads()
870 {
871 // for (int i = 0; i < GetNumThreads(); i++) {
872 // for (int j = 0; j < NUM_HEADS; j++) {
873 // m_threads[i].heads[j].Adjust();
874 // }
875 // }
876 }
877
878
879
880 // This function looks at the current position in the info of a creature,
881 // and sets the next_label to be the sequence of nops which follows. The
882 // instruction pointer is left on the last line of the label found.
883
ReadLabel(int max_size)884 void cHardwareGX::ReadLabel(int max_size)
885 {
886 int count = 0;
887 cHeadCPU * inst_ptr = &( IP() );
888
889 GetLabel().Clear();
890
891 while (m_inst_set->IsNop(inst_ptr->GetNextInst()) &&
892 (count < max_size)) {
893 count++;
894 inst_ptr->Advance();
895 GetLabel().AddNop(m_inst_set->GetNopMod(inst_ptr->GetInst()));
896
897 // If this is the first line of the template, mark it executed.
898 if (GetLabel().GetSize() <= m_world->GetConfig().MAX_LABEL_EXE_SIZE.Get()) {
899 inst_ptr->SetFlagExecuted();
900 }
901 }
902 }
903
904
905 //bool cHardwareGX::ForkThread()
906 //{
907 // const int num_threads = GetNumThreads();
908 // if (num_threads == m_world->GetConfig().MAX_CPU_THREADS.Get()) return false;
909 //
910 // // Make room for the new thread.
911 // m_threads.Resize(num_threads + 1);
912 //
913 // // Initialize the new thread to the same values as the current one.
914 // m_threads[num_threads] = m_threads[m_cur_thread];
915 //
916 // // Find the first free bit in m_thread_id_chart to determine the new
917 // // thread id.
918 // int new_id = 0;
919 // while ( (m_thread_id_chart >> new_id) & 1 == 1) new_id++;
920 // m_threads[num_threads].SetID(new_id);
921 // m_thread_id_chart |= (1 << new_id);
922 //
923 // return true;
924 //}
925 //
926 //
927 //bool cHardwareGX::KillThread()
928 //{
929 // // Make sure that there is always at least one thread...
930 // if (GetNumThreads() == 1) return false;
931 //
932 // // Note the current thread and set the current back one.
933 // const int kill_thread = m_cur_thread;
934 // ThreadPrev();
935 //
936 // // Turn off this bit in the m_thread_id_chart...
937 // m_thread_id_chart ^= 1 << m_threads[kill_thread].GetID();
938 //
939 // // Copy the last thread into the kill position
940 // const int last_thread = GetNumThreads() - 1;
941 // if (last_thread != kill_thread) {
942 // m_threads[kill_thread] = m_threads[last_thread];
943 // }
944 //
945 // // Kill the thread!
946 // m_threads.Resize(GetNumThreads() - 1);
947 //
948 // if (m_cur_thread > kill_thread) m_cur_thread--;
949 //
950 // return true;
951 //}
952
953 ////////////////////////////
954 // Instruction Helpers...
955 ////////////////////////////
956
FindModifiedRegister(int default_register)957 inline int cHardwareGX::FindModifiedRegister(int default_register)
958 {
959 assert(default_register < NUM_REGISTERS); // Reg ID too high.
960
961 if (m_inst_set->IsNop(IP().GetNextInst())) {
962 IP().Advance();
963 default_register = m_inst_set->GetNopMod(IP().GetInst());
964 IP().SetFlagExecuted();
965 }
966 return default_register;
967 }
968
FindModifiedNextRegister(int default_register)969 inline int cHardwareGX::FindModifiedNextRegister(int default_register)
970 {
971 assert(default_register < NUM_REGISTERS); // Reg ID too high.
972
973 if (m_inst_set->IsNop(IP().GetNextInst())) {
974 IP().Advance();
975 default_register = m_inst_set->GetNopMod(IP().GetInst());
976 IP().SetFlagExecuted();
977 } else {
978 default_register = (default_register + 1) % NUM_REGISTERS;
979 }
980 return default_register;
981 }
982
FindModifiedPreviousRegister(int default_register)983 inline int cHardwareGX::FindModifiedPreviousRegister(int default_register)
984 {
985 assert(default_register < NUM_REGISTERS); // Reg ID too high.
986
987 if (m_inst_set->IsNop(IP().GetNextInst())) {
988 IP().Advance();
989 default_register = m_inst_set->GetNopMod(IP().GetInst());
990 IP().SetFlagExecuted();
991 } else {
992 default_register = (default_register + NUM_REGISTERS - 1) % NUM_REGISTERS;
993 }
994 return default_register;
995 }
996
997
FindModifiedHead(int default_head)998 inline int cHardwareGX::FindModifiedHead(int default_head)
999 {
1000 assert(default_head < NUM_HEADS); // Head ID too high.
1001
1002 if (m_inst_set->IsNop(IP().GetNextInst())) {
1003 IP().Advance();
1004 default_head = m_inst_set->GetNopMod(IP().GetInst());
1005 IP().SetFlagExecuted();
1006 }
1007 return default_head;
1008 }
1009
1010
FindNextRegister(int base_reg)1011 inline int cHardwareGX::FindNextRegister(int base_reg)
1012 {
1013 return (base_reg + 1) % NUM_REGISTERS;
1014 }
1015
1016
Allocate_Necro(const int new_size)1017 bool cHardwareGX::Allocate_Necro(const int new_size)
1018 {
1019 GetMemory().ResizeOld(new_size);
1020 return true;
1021 }
1022
Allocate_Random(cAvidaContext & ctx,const int old_size,const int new_size)1023 bool cHardwareGX::Allocate_Random(cAvidaContext& ctx, const int old_size, const int new_size)
1024 {
1025 GetMemory().Resize(new_size);
1026
1027 for (int i = old_size; i < new_size; i++) {
1028 GetMemory()[i] = m_inst_set->GetRandomInst(ctx);
1029 }
1030 return true;
1031 }
1032
Allocate_Default(const int new_size)1033 bool cHardwareGX::Allocate_Default(const int new_size)
1034 {
1035 GetMemory().Resize(new_size);
1036
1037 // New space already defaults to default instruction...
1038
1039 return true;
1040 }
1041
Allocate_Main(cAvidaContext & ctx,const int allocated_size)1042 bool cHardwareGX::Allocate_Main(cAvidaContext& ctx, const int allocated_size)
1043 {
1044 // must do divide before second allocate & must allocate positive amount...
1045 if (m_world->GetConfig().REQUIRE_ALLOCATE.Get() && m_mal_active == true) {
1046 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR, "Allocate already active");
1047 return false;
1048 }
1049 if (allocated_size < 1) {
1050 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
1051 cStringUtil::Stringf("Allocate of %d too small", allocated_size));
1052 return false;
1053 }
1054
1055 const int old_size = GetMemory().GetSize();
1056 const int new_size = old_size + allocated_size;
1057
1058 // Make sure that the new size is in range.
1059 if (new_size > MAX_GENOME_LENGTH || new_size < MIN_GENOME_LENGTH) {
1060 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
1061 cStringUtil::Stringf("Invalid post-allocate size (%d)",
1062 new_size));
1063 return false;
1064 }
1065
1066 const int max_alloc_size = (int) (old_size * m_world->GetConfig().OFFSPRING_SIZE_RANGE.Get());
1067 if (allocated_size > max_alloc_size) {
1068 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
1069 cStringUtil::Stringf("Allocate too large (%d > %d)",
1070 allocated_size, max_alloc_size));
1071 return false;
1072 }
1073
1074 const int max_old_size =
1075 (int) (allocated_size * m_world->GetConfig().OFFSPRING_SIZE_RANGE.Get());
1076 if (old_size > max_old_size) {
1077 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
1078 cStringUtil::Stringf("Allocate too small (%d > %d)",
1079 old_size, max_old_size));
1080 return false;
1081 }
1082
1083 switch (m_world->GetConfig().ALLOC_METHOD.Get()) {
1084 case ALLOC_METHOD_NECRO:
1085 // Only break if this succeeds -- otherwise just do random.
1086 if (Allocate_Necro(new_size) == true) break;
1087 case ALLOC_METHOD_RANDOM:
1088 Allocate_Random(ctx, old_size, new_size);
1089 break;
1090 case ALLOC_METHOD_DEFAULT:
1091 Allocate_Default(new_size);
1092 break;
1093 }
1094
1095 m_mal_active = true;
1096
1097 return true;
1098 }
1099
calcExecutedSize(const int parent_size)1100 int cHardwareGX::calcExecutedSize(const int parent_size)
1101 {
1102 /* @JEB - not really relevant to GX
1103 int executed_size = 0;
1104 const cCPUMemory& memory = GetMemory();
1105 for (int i = 0; i < parent_size; i++) {
1106 if (memory.FlagExecuted(i)) executed_size++;
1107 }
1108 return executed_size;
1109 */
1110 return parent_size;
1111 }
1112
calcCopiedSize(const int parent_size,const int child_size)1113 int cHardwareGX::calcCopiedSize(const int parent_size, const int child_size)
1114 {
1115 return parent_size;
1116
1117 //@JEB - this is used in a division test. Not clear how to translate to GX for now.
1118
1119 //int copied_size = 0;
1120 //const cCPUMemory& memory = m_programids[GetHead(nHardware::HEAD_WRITE).GetMemSpace()]->GetMemory();
1121 //for (int i = 0; i < memory.GetSize(); i++) {
1122 // if (memory.FlagCopied(i)) copied_size++;
1123 //}
1124 //return copied_size;
1125 }
1126
1127
1128 /*! Divide works a little differently in cHardwareGX than in other CPUs. First,
1129 the cProgramid directly builds its offspring (rather than attaching its genome), so
1130 we don't need to do any genome splitting. This also enables the offspring to be
1131 active during replication. Second, we have to divvy up the other cProgramid objects
1132 between the parent and offspring. We also have to be careful to make sure that
1133 we have two genomes!
1134 */
Divide_Main(cAvidaContext & ctx)1135 bool cHardwareGX::Divide_Main(cAvidaContext& ctx)
1136 {
1137 return true;
1138 }
1139
1140
1141 //////////////////////////
1142 // And the instructions...
1143 //////////////////////////
1144
Inst_If0(cAvidaContext & ctx)1145 bool cHardwareGX::Inst_If0(cAvidaContext& ctx) // Execute next if ?bx? ==0.
1146 {
1147 const int reg_used = FindModifiedRegister(REG_BX);
1148 if (GetRegister(reg_used) != 0) IP().Advance();
1149 return true;
1150 }
1151
Inst_IfNot0(cAvidaContext & ctx)1152 bool cHardwareGX::Inst_IfNot0(cAvidaContext& ctx) // Execute next if ?bx? != 0.
1153 {
1154 const int reg_used = FindModifiedRegister(REG_BX);
1155 if (GetRegister(reg_used) == 0) IP().Advance();
1156 return true;
1157 }
1158
Inst_IfEqu(cAvidaContext & ctx)1159 bool cHardwareGX::Inst_IfEqu(cAvidaContext& ctx) // Execute next if bx == ?cx?
1160 {
1161 const int op1 = FindModifiedRegister(REG_BX);
1162 const int op2 = FindNextRegister(op1);
1163 if (GetRegister(op1) != GetRegister(op2)) IP().Advance();
1164 return true;
1165 }
1166
Inst_IfNEqu(cAvidaContext & ctx)1167 bool cHardwareGX::Inst_IfNEqu(cAvidaContext& ctx) // Execute next if bx != ?cx?
1168 {
1169 const int op1 = FindModifiedRegister(REG_BX);
1170 const int op2 = FindNextRegister(op1);
1171 if (GetRegister(op1) == GetRegister(op2)) IP().Advance();
1172 return true;
1173 }
1174
Inst_IfGr0(cAvidaContext & ctx)1175 bool cHardwareGX::Inst_IfGr0(cAvidaContext& ctx) // Execute next if ?bx? ! < 0.
1176 {
1177 const int reg_used = FindModifiedRegister(REG_BX);
1178 if (GetRegister(reg_used) <= 0) IP().Advance();
1179 return true;
1180 }
1181
Inst_IfGr(cAvidaContext & ctx)1182 bool cHardwareGX::Inst_IfGr(cAvidaContext& ctx) // Execute next if bx > ?cx?
1183 {
1184 const int op1 = FindModifiedRegister(REG_BX);
1185 const int op2 = FindNextRegister(op1);
1186 if (GetRegister(op1) <= GetRegister(op2)) IP().Advance();
1187 return true;
1188 }
1189
Inst_IfGrEqu0(cAvidaContext & ctx)1190 bool cHardwareGX::Inst_IfGrEqu0(cAvidaContext& ctx) // Execute next if ?bx? != 0.
1191 {
1192 const int reg_used = FindModifiedRegister(REG_BX);
1193 if (GetRegister(reg_used) < 0) IP().Advance();
1194 return true;
1195 }
1196
Inst_IfGrEqu(cAvidaContext & ctx)1197 bool cHardwareGX::Inst_IfGrEqu(cAvidaContext& ctx) // Execute next if bx > ?cx?
1198 {
1199 const int op1 = FindModifiedRegister(REG_BX);
1200 const int op2 = FindNextRegister(op1);
1201 if (GetRegister(op1) < GetRegister(op2)) IP().Advance();
1202 return true;
1203 }
1204
Inst_IfLess0(cAvidaContext & ctx)1205 bool cHardwareGX::Inst_IfLess0(cAvidaContext& ctx) // Execute next if ?bx? != 0.
1206 {
1207 const int reg_used = FindModifiedRegister(REG_BX);
1208 if (GetRegister(reg_used) >= 0) IP().Advance();
1209 return true;
1210 }
1211
Inst_IfLess(cAvidaContext & ctx)1212 bool cHardwareGX::Inst_IfLess(cAvidaContext& ctx) // Execute next if ?bx? < ?cx?
1213 {
1214 const int op1 = FindModifiedRegister(REG_BX);
1215 const int op2 = FindNextRegister(op1);
1216 if (GetRegister(op1) >= GetRegister(op2)) IP().Advance();
1217 return true;
1218 }
1219
Inst_IfLsEqu0(cAvidaContext & ctx)1220 bool cHardwareGX::Inst_IfLsEqu0(cAvidaContext& ctx) // Execute next if ?bx? != 0.
1221 {
1222 const int reg_used = FindModifiedRegister(REG_BX);
1223 if (GetRegister(reg_used) > 0) IP().Advance();
1224 return true;
1225 }
1226
Inst_IfLsEqu(cAvidaContext & ctx)1227 bool cHardwareGX::Inst_IfLsEqu(cAvidaContext& ctx) // Execute next if bx > ?cx?
1228 {
1229 const int op1 = FindModifiedRegister(REG_BX);
1230 const int op2 = FindNextRegister(op1);
1231 if (GetRegister(op1) > GetRegister(op2)) IP().Advance();
1232 return true;
1233 }
1234
Inst_IfBit1(cAvidaContext & ctx)1235 bool cHardwareGX::Inst_IfBit1(cAvidaContext& ctx)
1236 {
1237 const int reg_used = FindModifiedRegister(REG_BX);
1238 if ((GetRegister(reg_used) & 1) == 0) IP().Advance();
1239 return true;
1240 }
1241
Inst_IfANotEqB(cAvidaContext & ctx)1242 bool cHardwareGX::Inst_IfANotEqB(cAvidaContext& ctx) // Execute next if AX != BX
1243 {
1244 if (GetRegister(REG_AX) == GetRegister(REG_BX) ) IP().Advance();
1245 return true;
1246 }
1247
Inst_IfBNotEqC(cAvidaContext & ctx)1248 bool cHardwareGX::Inst_IfBNotEqC(cAvidaContext& ctx) // Execute next if BX != CX
1249 {
1250 if (GetRegister(REG_BX) == GetRegister(REG_CX) ) IP().Advance();
1251 return true;
1252 }
1253
Inst_IfANotEqC(cAvidaContext & ctx)1254 bool cHardwareGX::Inst_IfANotEqC(cAvidaContext& ctx) // Execute next if AX != BX
1255 {
1256 if (GetRegister(REG_AX) == GetRegister(REG_CX) ) IP().Advance();
1257 return true;
1258 }
1259
1260
Inst_Call(cAvidaContext & ctx)1261 bool cHardwareGX::Inst_Call(cAvidaContext& ctx)
1262 {
1263 // Put the starting location onto the stack
1264 const int location = IP().GetPosition();
1265 StackPush(location);
1266
1267 // Jump to the compliment label (or by the ammount in the bx register)
1268 ReadLabel();
1269 GetLabel().Rotate(1, NUM_NOPS);
1270
1271 if (GetLabel().GetSize() == 0) {
1272 IP().Jump(GetRegister(REG_BX));
1273 return true;
1274 }
1275
1276 const cHeadCPU jump_location(FindLabel(1));
1277 if (jump_location.GetPosition() != -1) {
1278 IP().Set(jump_location);
1279 return true;
1280 }
1281
1282 // If complement label was not found; record an error.
1283 m_organism->Fault(FAULT_LOC_JUMP, FAULT_TYPE_ERROR,
1284 "call: no complement label");
1285 return false;
1286 }
1287
Inst_Return(cAvidaContext & ctx)1288 bool cHardwareGX::Inst_Return(cAvidaContext& ctx)
1289 {
1290 IP().Set(StackPop());
1291 return true;
1292 }
1293
Inst_Throw(cAvidaContext & ctx)1294 bool cHardwareGX::Inst_Throw(cAvidaContext& ctx)
1295 {
1296 // Only initialize this once to save some time...
1297 static cInstruction catch_inst = GetInstSet().GetInst(cStringUtil::Stringf("catch"));
1298
1299 //Look for the label directly (no complement)
1300 ReadLabel();
1301
1302 cHeadCPU search_head(IP());
1303 int start_pos = search_head.GetPosition();
1304 search_head++;
1305
1306 while (start_pos != search_head.GetPosition())
1307 {
1308 // If we find a catch instruction, compare the NOPs following it
1309 if (search_head.GetInst() == catch_inst)
1310 {
1311 int catch_pos = search_head.GetPosition();
1312 search_head++;
1313
1314 // Continue to examine the label after the catch
1315 // (1) It ends (=> use the catch!)
1316 // (2) It becomes longer than the throw label (=> use the catch!)
1317 // (3) We find a NOP that doesnt match the throw (=> DON'T use the catch...)
1318
1319 bool match = true;
1320 int size_matched = 0;
1321 while ( match && m_inst_set->IsNop(search_head.GetInst()) && (size_matched < GetLabel().GetSize()) )
1322 {
1323 if ( GetLabel()[size_matched] != m_inst_set->GetNopMod( search_head.GetInst()) ) match = false;
1324 search_head++;
1325 size_matched++;
1326 }
1327
1328 // We found a matching catch instruction
1329 if (match)
1330 {
1331 IP().Set(catch_pos);
1332 m_advance_ip = false; // Don't automatically move the IP
1333 // so we mark the catch as executed.
1334 return true;
1335 }
1336
1337 //If we advanced past NOPs during testing, retreat
1338 if ( !m_inst_set->IsNop(search_head.GetInst()) ) search_head--;
1339 }
1340 search_head.Advance();
1341 }
1342
1343 return false;
1344 }
1345
1346
Inst_ThrowIfNot0(cAvidaContext & ctx)1347 bool cHardwareGX::Inst_ThrowIfNot0(cAvidaContext& ctx)
1348 {
1349 if (GetRegister(REG_BX) == 0) return false;
1350 return Inst_Throw(ctx);
1351 }
1352
Inst_ThrowIf0(cAvidaContext & ctx)1353 bool cHardwareGX::Inst_ThrowIf0(cAvidaContext& ctx)
1354 {
1355 if (GetRegister(REG_BX) != 0) return false;
1356 return Inst_Throw(ctx);
1357 }
1358
Inst_Goto(cAvidaContext & ctx)1359 bool cHardwareGX::Inst_Goto(cAvidaContext& ctx)
1360 {
1361 // Only initialize this once to save some time...
1362 static cInstruction label_inst = GetInstSet().GetInst(cStringUtil::Stringf("label"));
1363
1364 //Look for an EXACT label match after a 'label' instruction
1365 ReadLabel();
1366
1367 cHeadCPU search_head(IP());
1368 int start_pos = search_head.GetPosition();
1369 search_head++;
1370
1371 while (start_pos != search_head.GetPosition())
1372 {
1373 if (search_head.GetInst() == label_inst)
1374 {
1375 int label_pos = search_head.GetPosition();
1376 search_head++;
1377 int size_matched = 0;
1378 while ( size_matched < GetLabel().GetSize() )
1379 {
1380 if ( !m_inst_set->IsNop(search_head.GetInst()) ) break;
1381 if ( GetLabel()[size_matched] != m_inst_set->GetNopMod( search_head.GetInst()) ) break;
1382 if ( !m_inst_set->IsNop(search_head.GetInst()) ) break;
1383
1384 size_matched++;
1385 search_head++;
1386 }
1387
1388 // We found a matching 'label' instruction only if the next
1389 // instruction (at the search head now) is also not a NOP
1390 if ( (size_matched == GetLabel().GetSize()) && !m_inst_set->IsNop(search_head.GetInst()) )
1391 {
1392 IP().Set(label_pos);
1393 m_advance_ip = false; // Don't automatically move the IP
1394 // so we mark the catch as executed.
1395 return true;
1396 }
1397
1398 //If we advanced past NOPs during testing, retreat
1399 if ( !m_inst_set->IsNop(search_head.GetInst()) ) search_head--;
1400 }
1401 search_head++;
1402 }
1403
1404 return false;
1405 }
1406
1407
Inst_GotoIfNot0(cAvidaContext & ctx)1408 bool cHardwareGX::Inst_GotoIfNot0(cAvidaContext& ctx)
1409 {
1410 if (GetRegister(REG_BX) == 0) return false;
1411 return Inst_Goto(ctx);
1412 }
1413
Inst_GotoIf0(cAvidaContext & ctx)1414 bool cHardwareGX::Inst_GotoIf0(cAvidaContext& ctx)
1415 {
1416 if (GetRegister(REG_BX) != 0) return false;
1417 return Inst_Goto(ctx);
1418 }
1419
1420
Inst_Pop(cAvidaContext & ctx)1421 bool cHardwareGX::Inst_Pop(cAvidaContext& ctx)
1422 {
1423 const int reg_used = FindModifiedRegister(REG_BX);
1424 GetRegister(reg_used) = StackPop();
1425 return true;
1426 }
1427
Inst_Push(cAvidaContext & ctx)1428 bool cHardwareGX::Inst_Push(cAvidaContext& ctx)
1429 {
1430 const int reg_used = FindModifiedRegister(REG_BX);
1431 StackPush(GetRegister(reg_used));
1432 return true;
1433 }
1434
Inst_HeadPop(cAvidaContext & ctx)1435 bool cHardwareGX::Inst_HeadPop(cAvidaContext& ctx)
1436 {
1437 const int head_used = FindModifiedHead(nHardware::HEAD_IP);
1438 GetHead(head_used).Set(StackPop());
1439 return true;
1440 }
1441
Inst_HeadPush(cAvidaContext & ctx)1442 bool cHardwareGX::Inst_HeadPush(cAvidaContext& ctx)
1443 {
1444 const int head_used = FindModifiedHead(nHardware::HEAD_IP);
1445 StackPush(GetHead(head_used).GetPosition());
1446 if (head_used == nHardware::HEAD_IP) {
1447 GetHead(head_used).Set(GetHead(nHardware::HEAD_FLOW));
1448 m_advance_ip = false;
1449 }
1450 return true;
1451 }
1452
1453
Inst_PopA(cAvidaContext & ctx)1454 bool cHardwareGX::Inst_PopA(cAvidaContext& ctx) { GetRegister(REG_AX) = StackPop(); return true;}
Inst_PopB(cAvidaContext & ctx)1455 bool cHardwareGX::Inst_PopB(cAvidaContext& ctx) { GetRegister(REG_BX) = StackPop(); return true;}
Inst_PopC(cAvidaContext & ctx)1456 bool cHardwareGX::Inst_PopC(cAvidaContext& ctx) { GetRegister(REG_CX) = StackPop(); return true;}
1457
Inst_PushA(cAvidaContext & ctx)1458 bool cHardwareGX::Inst_PushA(cAvidaContext& ctx) { StackPush(GetRegister(REG_AX)); return true;}
Inst_PushB(cAvidaContext & ctx)1459 bool cHardwareGX::Inst_PushB(cAvidaContext& ctx) { StackPush(GetRegister(REG_BX)); return true;}
Inst_PushC(cAvidaContext & ctx)1460 bool cHardwareGX::Inst_PushC(cAvidaContext& ctx) { StackPush(GetRegister(REG_CX)); return true;}
1461
Inst_SwitchStack(cAvidaContext & ctx)1462 bool cHardwareGX::Inst_SwitchStack(cAvidaContext& ctx) { SwitchStack(); return true;}
Inst_FlipStack(cAvidaContext & ctx)1463 bool cHardwareGX::Inst_FlipStack(cAvidaContext& ctx) { StackFlip(); return true;}
1464
Inst_Swap(cAvidaContext & ctx)1465 bool cHardwareGX::Inst_Swap(cAvidaContext& ctx)
1466 {
1467 const int op1 = FindModifiedRegister(REG_BX);
1468 const int op2 = FindNextRegister(op1);
1469 Swap(GetRegister(op1), GetRegister(op2));
1470 return true;
1471 }
1472
Inst_SwapAB(cAvidaContext & ctx)1473 bool cHardwareGX::Inst_SwapAB(cAvidaContext& ctx)\
1474 {
1475 Swap(GetRegister(REG_AX), GetRegister(REG_BX)); return true;
1476 }
Inst_SwapBC(cAvidaContext & ctx)1477 bool cHardwareGX::Inst_SwapBC(cAvidaContext& ctx)
1478 {
1479 Swap(GetRegister(REG_BX), GetRegister(REG_CX)); return true;
1480 }
Inst_SwapAC(cAvidaContext & ctx)1481 bool cHardwareGX::Inst_SwapAC(cAvidaContext& ctx)
1482 {
1483 Swap(GetRegister(REG_AX), GetRegister(REG_CX)); return true;
1484 }
1485
Inst_CopyReg(cAvidaContext & ctx)1486 bool cHardwareGX::Inst_CopyReg(cAvidaContext& ctx)
1487 {
1488 const int src = FindModifiedRegister(REG_BX);
1489 const int dst = FindNextRegister(src);
1490 GetRegister(dst) = GetRegister(src);
1491 return true;
1492 }
1493
Inst_CopyRegAB(cAvidaContext & ctx)1494 bool cHardwareGX::Inst_CopyRegAB(cAvidaContext& ctx)
1495 {
1496 GetRegister(REG_AX) = GetRegister(REG_BX); return true;
1497 }
Inst_CopyRegAC(cAvidaContext & ctx)1498 bool cHardwareGX::Inst_CopyRegAC(cAvidaContext& ctx)
1499 {
1500 GetRegister(REG_AX) = GetRegister(REG_CX); return true;
1501 }
Inst_CopyRegBA(cAvidaContext & ctx)1502 bool cHardwareGX::Inst_CopyRegBA(cAvidaContext& ctx)
1503 {
1504 GetRegister(REG_BX) = GetRegister(REG_AX); return true;
1505 }
Inst_CopyRegBC(cAvidaContext & ctx)1506 bool cHardwareGX::Inst_CopyRegBC(cAvidaContext& ctx)
1507 {
1508 GetRegister(REG_BX) = GetRegister(REG_CX); return true;
1509 }
Inst_CopyRegCA(cAvidaContext & ctx)1510 bool cHardwareGX::Inst_CopyRegCA(cAvidaContext& ctx)
1511 {
1512 GetRegister(REG_CX) = GetRegister(REG_AX); return true;
1513 }
Inst_CopyRegCB(cAvidaContext & ctx)1514 bool cHardwareGX::Inst_CopyRegCB(cAvidaContext& ctx)
1515 {
1516 GetRegister(REG_CX) = GetRegister(REG_BX); return true;
1517 }
1518
Inst_Reset(cAvidaContext & ctx)1519 bool cHardwareGX::Inst_Reset(cAvidaContext& ctx)
1520 {
1521 GetRegister(REG_AX) = 0;
1522 GetRegister(REG_BX) = 0;
1523 GetRegister(REG_CX) = 0;
1524 StackClear();
1525 return true;
1526 }
1527
Inst_ShiftR(cAvidaContext & ctx)1528 bool cHardwareGX::Inst_ShiftR(cAvidaContext& ctx)
1529 {
1530 const int reg_used = FindModifiedRegister(REG_BX);
1531 GetRegister(reg_used) >>= 1;
1532 return true;
1533 }
1534
Inst_ShiftL(cAvidaContext & ctx)1535 bool cHardwareGX::Inst_ShiftL(cAvidaContext& ctx)
1536 {
1537 const int reg_used = FindModifiedRegister(REG_BX);
1538 GetRegister(reg_used) <<= 1;
1539 return true;
1540 }
1541
Inst_Bit1(cAvidaContext & ctx)1542 bool cHardwareGX::Inst_Bit1(cAvidaContext& ctx)
1543 {
1544 const int reg_used = FindModifiedRegister(REG_BX);
1545 GetRegister(reg_used) |= 1;
1546 return true;
1547 }
1548
Inst_SetNum(cAvidaContext & ctx)1549 bool cHardwareGX::Inst_SetNum(cAvidaContext& ctx)
1550 {
1551 ReadLabel();
1552 GetRegister(REG_BX) = GetLabel().AsInt(NUM_NOPS);
1553 return true;
1554 }
1555
Inst_ValGrey(cAvidaContext & ctx)1556 bool cHardwareGX::Inst_ValGrey(cAvidaContext& ctx) {
1557 ReadLabel();
1558 GetRegister(REG_BX) = GetLabel().AsIntGreyCode(NUM_NOPS);
1559 return true;
1560 }
1561
Inst_ValDir(cAvidaContext & ctx)1562 bool cHardwareGX::Inst_ValDir(cAvidaContext& ctx) {
1563 ReadLabel();
1564 GetRegister(REG_BX) = GetLabel().AsIntDirect(NUM_NOPS);
1565 return true;
1566 }
1567
Inst_ValAddP(cAvidaContext & ctx)1568 bool cHardwareGX::Inst_ValAddP(cAvidaContext& ctx) {
1569 ReadLabel();
1570 GetRegister(REG_BX) = GetLabel().AsIntAdditivePolynomial(NUM_NOPS);
1571 return true;
1572 }
1573
Inst_ValFib(cAvidaContext & ctx)1574 bool cHardwareGX::Inst_ValFib(cAvidaContext& ctx) {
1575 ReadLabel();
1576 GetRegister(REG_BX) = GetLabel().AsIntFib(NUM_NOPS);
1577 return true;
1578 }
1579
Inst_ValPolyC(cAvidaContext & ctx)1580 bool cHardwareGX::Inst_ValPolyC(cAvidaContext& ctx) {
1581 ReadLabel();
1582 GetRegister(REG_BX) = GetLabel().AsIntPolynomialCoefficent(NUM_NOPS);
1583 return true;
1584 }
1585
Inst_Inc(cAvidaContext & ctx)1586 bool cHardwareGX::Inst_Inc(cAvidaContext& ctx)
1587 {
1588 const int reg_used = FindModifiedRegister(REG_BX);
1589 GetRegister(reg_used) += 1;
1590 return true;
1591 }
1592
Inst_Dec(cAvidaContext & ctx)1593 bool cHardwareGX::Inst_Dec(cAvidaContext& ctx)
1594 {
1595 const int reg_used = FindModifiedRegister(REG_BX);
1596 GetRegister(reg_used) -= 1;
1597 return true;
1598 }
1599
Inst_Zero(cAvidaContext & ctx)1600 bool cHardwareGX::Inst_Zero(cAvidaContext& ctx)
1601 {
1602 const int reg_used = FindModifiedRegister(REG_BX);
1603 GetRegister(reg_used) = 0;
1604 return true;
1605 }
1606
Inst_Neg(cAvidaContext & ctx)1607 bool cHardwareGX::Inst_Neg(cAvidaContext& ctx)
1608 {
1609 const int src = FindModifiedRegister(REG_BX);
1610 const int dst = src;
1611 GetRegister(dst) = -GetRegister(src);
1612 return true;
1613 }
1614
Inst_Square(cAvidaContext & ctx)1615 bool cHardwareGX::Inst_Square(cAvidaContext& ctx)
1616 {
1617 const int src = FindModifiedRegister(REG_BX);
1618 const int dst = src;
1619 GetRegister(dst) = GetRegister(src) * GetRegister(src);
1620 return true;
1621 }
1622
Inst_Sqrt(cAvidaContext & ctx)1623 bool cHardwareGX::Inst_Sqrt(cAvidaContext& ctx)
1624 {
1625 const int src = FindModifiedRegister(REG_BX);
1626 const int dst = src;
1627 const int value = GetRegister(src);
1628 if (value > 1) GetRegister(dst) = static_cast<int>(sqrt(static_cast<double>(value)));
1629 else if (value < 0) {
1630 m_organism->Fault(FAULT_LOC_MATH, FAULT_TYPE_ERROR, "sqrt: value is negative");
1631 return false;
1632 }
1633 return true;
1634 }
1635
Inst_Log(cAvidaContext & ctx)1636 bool cHardwareGX::Inst_Log(cAvidaContext& ctx)
1637 {
1638 const int src = FindModifiedRegister(REG_BX);
1639 const int dst = src;
1640 const int value = GetRegister(src);
1641 if (value >= 1) GetRegister(dst) = static_cast<int>(log(static_cast<double>(value)));
1642 else if (value < 0) {
1643 m_organism->Fault(FAULT_LOC_MATH, FAULT_TYPE_ERROR, "log: value is negative");
1644 return false;
1645 }
1646 return true;
1647 }
1648
Inst_Log10(cAvidaContext & ctx)1649 bool cHardwareGX::Inst_Log10(cAvidaContext& ctx)
1650 {
1651 const int src = FindModifiedRegister(REG_BX);
1652 const int dst = src;
1653 const int value = GetRegister(src);
1654 if (value >= 1) GetRegister(dst) = static_cast<int>(log10(static_cast<double>(value)));
1655 else if (value < 0) {
1656 m_organism->Fault(FAULT_LOC_MATH, FAULT_TYPE_ERROR, "log10: value is negative");
1657 return false;
1658 }
1659 return true;
1660 }
1661
Inst_Add(cAvidaContext & ctx)1662 bool cHardwareGX::Inst_Add(cAvidaContext& ctx)
1663 {
1664 const int dst = FindModifiedRegister(REG_BX);
1665 const int op1 = REG_BX;
1666 const int op2 = REG_CX;
1667 GetRegister(dst) = GetRegister(op1) + GetRegister(op2);
1668 return true;
1669 }
1670
Inst_Sub(cAvidaContext & ctx)1671 bool cHardwareGX::Inst_Sub(cAvidaContext& ctx)
1672 {
1673 const int dst = FindModifiedRegister(REG_BX);
1674 const int op1 = REG_BX;
1675 const int op2 = REG_CX;
1676 GetRegister(dst) = GetRegister(op1) - GetRegister(op2);
1677 return true;
1678 }
1679
Inst_Mult(cAvidaContext & ctx)1680 bool cHardwareGX::Inst_Mult(cAvidaContext& ctx)
1681 {
1682 const int dst = FindModifiedRegister(REG_BX);
1683 const int op1 = REG_BX;
1684 const int op2 = REG_CX;
1685 GetRegister(dst) = GetRegister(op1) * GetRegister(op2);
1686 return true;
1687 }
1688
Inst_Div(cAvidaContext & ctx)1689 bool cHardwareGX::Inst_Div(cAvidaContext& ctx)
1690 {
1691 const int dst = FindModifiedRegister(REG_BX);
1692 const int op1 = REG_BX;
1693 const int op2 = REG_CX;
1694 if (GetRegister(op2) != 0) {
1695 if (0-INT_MAX > GetRegister(op1) && GetRegister(op2) == -1)
1696 m_organism->Fault(FAULT_LOC_MATH, FAULT_TYPE_ERROR, "div: Float exception");
1697 else
1698 GetRegister(dst) = GetRegister(op1) / GetRegister(op2);
1699 } else {
1700 m_organism->Fault(FAULT_LOC_MATH, FAULT_TYPE_ERROR, "div: dividing by 0");
1701 return false;
1702 }
1703 return true;
1704 }
1705
Inst_Mod(cAvidaContext & ctx)1706 bool cHardwareGX::Inst_Mod(cAvidaContext& ctx)
1707 {
1708 const int dst = FindModifiedRegister(REG_BX);
1709 const int op1 = REG_BX;
1710 const int op2 = REG_CX;
1711 if (GetRegister(op2) != 0) {
1712 GetRegister(dst) = GetRegister(op1) % GetRegister(op2);
1713 } else {
1714 m_organism->Fault(FAULT_LOC_MATH, FAULT_TYPE_ERROR, "mod: modding by 0");
1715 return false;
1716 }
1717 return true;
1718 }
1719
1720
Inst_Nand(cAvidaContext & ctx)1721 bool cHardwareGX::Inst_Nand(cAvidaContext& ctx)
1722 {
1723 const int dst = FindModifiedRegister(REG_BX);
1724 const int op1 = REG_BX;
1725 const int op2 = REG_CX;
1726 GetRegister(dst) = ~(GetRegister(op1) & GetRegister(op2));
1727 return true;
1728 }
1729
Inst_Nor(cAvidaContext & ctx)1730 bool cHardwareGX::Inst_Nor(cAvidaContext& ctx)
1731 {
1732 const int dst = FindModifiedRegister(REG_BX);
1733 const int op1 = REG_BX;
1734 const int op2 = REG_CX;
1735 GetRegister(dst) = ~(GetRegister(op1) | GetRegister(op2));
1736 return true;
1737 }
1738
Inst_And(cAvidaContext & ctx)1739 bool cHardwareGX::Inst_And(cAvidaContext& ctx)
1740 {
1741 const int dst = FindModifiedRegister(REG_BX);
1742 const int op1 = REG_BX;
1743 const int op2 = REG_CX;
1744 GetRegister(dst) = (GetRegister(op1) & GetRegister(op2));
1745 return true;
1746 }
1747
Inst_Not(cAvidaContext & ctx)1748 bool cHardwareGX::Inst_Not(cAvidaContext& ctx)
1749 {
1750 const int src = FindModifiedRegister(REG_BX);
1751 const int dst = src;
1752 GetRegister(dst) = ~(GetRegister(src));
1753 return true;
1754 }
1755
Inst_Order(cAvidaContext & ctx)1756 bool cHardwareGX::Inst_Order(cAvidaContext& ctx)
1757 {
1758 const int op1 = REG_BX;
1759 const int op2 = REG_CX;
1760 if (GetRegister(op1) > GetRegister(op2)) {
1761 Swap(GetRegister(op1), GetRegister(op2));
1762 }
1763 return true;
1764 }
1765
Inst_Xor(cAvidaContext & ctx)1766 bool cHardwareGX::Inst_Xor(cAvidaContext& ctx)
1767 {
1768 const int dst = FindModifiedRegister(REG_BX);
1769 const int op1 = REG_BX;
1770 const int op2 = REG_CX;
1771 GetRegister(dst) = GetRegister(op1) ^ GetRegister(op2);
1772 return true;
1773 }
1774
Inst_Copy(cAvidaContext & ctx)1775 bool cHardwareGX::Inst_Copy(cAvidaContext& ctx)
1776 {
1777 const int op1 = REG_BX;
1778 const int op2 = REG_AX;
1779
1780 const cHeadCPU from(this, GetRegister(op1));
1781 cHeadCPU to(this, GetRegister(op2) + GetRegister(op1));
1782
1783 if (m_organism->TestCopyMut(ctx)) {
1784 to.SetInst(m_inst_set->GetRandomInst(ctx));
1785 to.SetFlagMutated(); // Mark this instruction as mutated...
1786 to.SetFlagCopyMut(); // Mark this instruction as copy mut...
1787 } else {
1788 to.SetInst(from.GetInst());
1789 to.ClearFlagMutated(); // UnMark
1790 to.ClearFlagCopyMut(); // UnMark
1791 }
1792
1793 to.SetFlagCopied(); // Set the copied flag.
1794 return true;
1795 }
1796
Inst_ReadInst(cAvidaContext & ctx)1797 bool cHardwareGX::Inst_ReadInst(cAvidaContext& ctx)
1798 {
1799 const int dst = FindModifiedRegister(REG_CX);
1800 const int src = REG_BX;
1801
1802 const cHeadCPU from(this, GetRegister(src));
1803
1804 // Dis-allowing mutations on read, for the moment (write only...)
1805 // @CAO This allows perfect error-correction...
1806 GetRegister(dst) = from.GetInst().GetOp();
1807 return true;
1808 }
1809
Inst_WriteInst(cAvidaContext & ctx)1810 bool cHardwareGX::Inst_WriteInst(cAvidaContext& ctx)
1811 {
1812 const int src = FindModifiedRegister(REG_CX);
1813 const int op1 = REG_BX;
1814 const int op2 = REG_AX;
1815
1816 cHeadCPU to(this, GetRegister(op2) + GetRegister(op1));
1817 const int value = Mod(GetRegister(src), m_inst_set->GetSize());
1818
1819 // Change value on a mutation...
1820 if (m_organism->TestCopyMut(ctx)) {
1821 to.SetInst(m_inst_set->GetRandomInst(ctx));
1822 to.SetFlagMutated(); // Mark this instruction as mutated...
1823 to.SetFlagCopyMut(); // Mark this instruction as copy mut...
1824 } else {
1825 to.SetInst(cInstruction(value));
1826 to.ClearFlagMutated(); // UnMark
1827 to.ClearFlagCopyMut(); // UnMark
1828 }
1829
1830 to.SetFlagCopied(); // Set the copied flag.
1831 return true;
1832 }
1833
Inst_StackReadInst(cAvidaContext & ctx)1834 bool cHardwareGX::Inst_StackReadInst(cAvidaContext& ctx)
1835 {
1836 const int reg_used = FindModifiedRegister(REG_CX);
1837 cHeadCPU from(this, GetRegister(reg_used));
1838 StackPush(from.GetInst().GetOp());
1839 return true;
1840 }
1841
Inst_StackWriteInst(cAvidaContext & ctx)1842 bool cHardwareGX::Inst_StackWriteInst(cAvidaContext& ctx)
1843 {
1844 const int dst = FindModifiedRegister(REG_BX);
1845 const int op1 = REG_AX;
1846 cHeadCPU to(this, GetRegister(op1) + GetRegister(dst));
1847 const int value = Mod(StackPop(), m_inst_set->GetSize());
1848
1849 // Change value on a mutation...
1850 if (m_organism->TestCopyMut(ctx)) {
1851 to.SetInst(m_inst_set->GetRandomInst(ctx));
1852 to.SetFlagMutated(); // Mark this instruction as mutated...
1853 to.SetFlagCopyMut(); // Mark this instruction as copy mut...
1854 } else {
1855 to.SetInst(cInstruction(value));
1856 to.ClearFlagMutated(); // UnMark
1857 to.ClearFlagCopyMut(); // UnMark
1858 }
1859
1860 to.SetFlagCopied(); // Set the copied flag.
1861 return true;
1862 }
1863
Inst_Compare(cAvidaContext & ctx)1864 bool cHardwareGX::Inst_Compare(cAvidaContext& ctx)
1865 {
1866 const int dst = FindModifiedRegister(REG_CX);
1867 const int op1 = REG_BX;
1868 const int op2 = REG_AX;
1869
1870 cHeadCPU from(this, GetRegister(op1));
1871 cHeadCPU to(this, GetRegister(op2) + GetRegister(op1));
1872
1873 // Compare is dangerous -- it can cause mutations!
1874 if (m_organism->TestCopyMut(ctx)) {
1875 to.SetInst(m_inst_set->GetRandomInst(ctx));
1876 to.SetFlagMutated(); // Mark this instruction as mutated...
1877 to.SetFlagCopyMut(); // Mark this instruction as copy mut...
1878 }
1879
1880 GetRegister(dst) = from.GetInst().GetOp() - to.GetInst().GetOp();
1881
1882 return true;
1883 }
1884
Inst_IfNCpy(cAvidaContext & ctx)1885 bool cHardwareGX::Inst_IfNCpy(cAvidaContext& ctx)
1886 {
1887 const int op1 = REG_BX;
1888 const int op2 = REG_AX;
1889
1890 const cHeadCPU from(this, GetRegister(op1));
1891 const cHeadCPU to(this, GetRegister(op2) + GetRegister(op1));
1892
1893 // Allow for errors in this test...
1894 if (m_organism->TestCopyMut(ctx)) {
1895 if (from.GetInst() != to.GetInst()) IP().Advance();
1896 } else {
1897 if (from.GetInst() == to.GetInst()) IP().Advance();
1898 }
1899 return true;
1900 }
1901
Inst_Allocate(cAvidaContext & ctx)1902 bool cHardwareGX::Inst_Allocate(cAvidaContext& ctx) // Allocate bx more space...
1903 {
1904 const int src = REG_BX;
1905 const int dst = REG_AX;
1906 const int size = GetMemory().GetSize();
1907 if (Allocate_Main(ctx, GetRegister(src))) {
1908 GetRegister(dst) = size;
1909 return true;
1910 } else return false;
1911 }
1912
1913
Inst_CAlloc(cAvidaContext & ctx)1914 bool cHardwareGX::Inst_CAlloc(cAvidaContext& ctx)
1915 {
1916 return Allocate_Main(ctx, GetMemory().GetSize());
1917 }
1918
Inst_MaxAlloc(cAvidaContext & ctx)1919 bool cHardwareGX::Inst_MaxAlloc(cAvidaContext& ctx) // Allocate maximal more
1920 {
1921 const int dst = REG_AX;
1922 const int cur_size = GetMemory().GetSize();
1923 const int alloc_size = Min((int) (m_world->GetConfig().OFFSPRING_SIZE_RANGE.Get() * cur_size),
1924 MAX_GENOME_LENGTH - cur_size);
1925 if (Allocate_Main(ctx, alloc_size)) {
1926 GetRegister(dst) = cur_size;
1927 return true;
1928 } else return false;
1929 }
1930
Inst_Repro(cAvidaContext & ctx)1931 bool cHardwareGX::Inst_Repro(cAvidaContext& ctx)
1932 {
1933 // Setup child
1934 Sequence& child_genome = m_organism->OffspringGenome().GetSequence();
1935 child_genome = m_organism->GetGenome().GetSequence();
1936 m_organism->OffspringGenome().SetHardwareType(GetType());
1937 m_organism->OffspringGenome().SetInstSet(m_inst_set->GetInstSetName());
1938 m_organism->GetPhenotype().SetLinesCopied(m_organism->GetGenome().GetSize());
1939
1940 Divide_DoMutations(ctx);
1941
1942 bool viable = Divide_CheckViable(ctx, m_organism->GetGenome().GetSize(), child_genome.GetSize());
1943 //if the offspring is not viable (due to splippage mutations), then what do we do?
1944 if (viable == false) return false;
1945
1946 // Many tests will require us to run the offspring through a test CPU;
1947 // this is, for example, to see if mutations need to be reverted or if
1948 // lineages need to be updated.
1949 Divide_TestFitnessMeasures(ctx);
1950
1951 // reset first time instruction costs
1952 for (int i = 0; i < m_inst_ft_cost.GetSize(); i++) {
1953 m_inst_ft_cost[i] = m_inst_set->GetFTCost(cInstruction(i));
1954 }
1955
1956 if (m_world->GetConfig().DIVIDE_METHOD.Get() == DIVIDE_METHOD_SPLIT) {
1957 m_advance_ip = false;
1958 }
1959
1960 // Activate the child
1961 bool parent_alive = m_organism->ActivateDivide(ctx);
1962
1963 // Do more work if the parent lives through the birth of the offspring
1964 if (parent_alive) {
1965 if (m_world->GetConfig().DIVIDE_METHOD.Get() == DIVIDE_METHOD_SPLIT) Reset(ctx);
1966 }
1967
1968 m_just_divided = true;
1969 return true;
1970 }
1971
Inst_SpawnDeme(cAvidaContext & ctx)1972 bool cHardwareGX::Inst_SpawnDeme(cAvidaContext& ctx)
1973 {
1974 m_organism->SpawnDeme(ctx);
1975 return true;
1976 }
1977
Inst_Kazi(cAvidaContext & ctx)1978 bool cHardwareGX::Inst_Kazi(cAvidaContext& ctx)
1979 {
1980 const int reg_used = FindModifiedRegister(REG_AX);
1981 double percentProb = ((double) (GetRegister(reg_used) % 100)) / 100.0;
1982 if ( ctx.GetRandom().P(percentProb) ) m_organism->Kaboom(0, ctx);
1983 return true;
1984 }
1985
Inst_Kazi5(cAvidaContext & ctx)1986 bool cHardwareGX::Inst_Kazi5(cAvidaContext& ctx)
1987 {
1988 const int reg_used = FindModifiedRegister(REG_AX);
1989 double percentProb = ((double) (GetRegister(reg_used) % 100)) / 100.0;
1990 if ( ctx.GetRandom().P(percentProb) ) m_organism->Kaboom(5, ctx);
1991 return true;
1992 }
1993
Inst_Die(cAvidaContext & ctx)1994 bool cHardwareGX::Inst_Die(cAvidaContext& ctx)
1995 {
1996 m_organism->Die(ctx);
1997 return true;
1998 }
1999
2000
2001
Inst_TaskGet(cAvidaContext & ctx)2002 bool cHardwareGX::Inst_TaskGet(cAvidaContext& ctx)
2003 {
2004 const int reg_used = FindModifiedRegister(REG_CX);
2005 const int value = m_organism->GetNextInput(m_current->m_input_pointer);
2006 GetRegister(reg_used) = value;
2007 m_organism->DoInput(m_current->m_input_buf, m_current->m_output_buf, value); // Check for tasks completed.
2008 return true;
2009 }
2010
2011
2012 // @JEB - this instruction does more than two "gets" together
2013 // it also (1) resets the inputs and (2) resets an organisms task counts
Inst_TaskGet2(cAvidaContext & ctx)2014 bool cHardwareGX::Inst_TaskGet2(cAvidaContext& ctx)
2015 {
2016 // Randomize the inputs so they can't save numbers
2017 m_organism->GetOrgInterface().ResetInputs(ctx); // Now re-randomize the inputs this organism sees
2018 m_current->m_input_buf.Clear();
2019 //m_organism->ClearInput(); // Also clear their input buffers, or they can still claim
2020 // rewards for numbers no longer in their environment!
2021
2022 const int reg_used_1 = FindModifiedRegister(REG_BX);
2023 const int reg_used_2 = FindNextRegister(reg_used_1);
2024
2025 const int value1 = m_organism->GetNextInput(m_current->m_input_pointer);
2026 GetRegister(reg_used_1) = value1;
2027 m_organism->DoInput(m_current->m_input_buf, m_current->m_output_buf, value1);
2028
2029 const int value2 = m_organism->GetNextInput(m_current->m_input_pointer);
2030 GetRegister(reg_used_2) = value2;
2031 m_organism->DoInput(m_current->m_input_buf, m_current->m_output_buf, value2);
2032
2033 // Clear the task number
2034 m_organism->GetPhenotype().ClearEffTaskCount();
2035
2036 return true;
2037 }
2038
Inst_TaskStackGet(cAvidaContext & ctx)2039 bool cHardwareGX::Inst_TaskStackGet(cAvidaContext& ctx)
2040 {
2041 const int value = m_organism->GetNextInput(m_current->m_input_pointer);
2042 StackPush(value);
2043 m_organism->DoInput(m_current->m_input_buf, m_current->m_output_buf, value);
2044 return true;
2045 }
2046
Inst_TaskStackLoad(cAvidaContext & ctx)2047 bool cHardwareGX::Inst_TaskStackLoad(cAvidaContext& ctx)
2048 {
2049 // @DMB - TODO: this should look at the input_size...
2050 for (int i = 0; i < 3; i++)
2051 StackPush( m_organism->GetNextInput(m_current->m_input_pointer) );
2052 return true;
2053 }
2054
Inst_TaskPut(cAvidaContext & ctx)2055 bool cHardwareGX::Inst_TaskPut(cAvidaContext& ctx)
2056 {
2057 const int reg_used = FindModifiedRegister(REG_BX);
2058 const int value = GetRegister(reg_used);
2059 GetRegister(reg_used) = 0;
2060 m_organism->DoOutput(ctx, value);
2061 return true;
2062 }
2063
Inst_TaskPutResetInputs(cAvidaContext & ctx)2064 bool cHardwareGX::Inst_TaskPutResetInputs(cAvidaContext& ctx)
2065 {
2066 bool return_value = Inst_TaskPut(ctx); // Do a normal put
2067 m_organism->GetOrgInterface().ResetInputs(ctx); // Now re-randomize the inputs this organism sees
2068 m_current->m_input_buf.Clear(); // Also clear their input buffers, or they can still claim
2069 // rewards for numbers no longer in their environment!
2070 return return_value;
2071 }
2072
Inst_TaskIO(cAvidaContext & ctx)2073 bool cHardwareGX::Inst_TaskIO(cAvidaContext& ctx)
2074 {
2075 const int reg_used = FindModifiedRegister(REG_BX);
2076
2077 // Do the "put" component
2078 const int value_out = GetRegister(reg_used);
2079 m_organism->DoOutput(ctx, m_current->m_input_buf, m_current->m_output_buf, value_out); // Check for tasks completed.
2080
2081 // Do the "get" component
2082 const int value_in = m_organism->GetNextInput(m_current->m_input_pointer);
2083 GetRegister(reg_used) = value_in;
2084 m_organism->DoInput(m_current->m_input_buf, m_current->m_output_buf, value_in); // Check for tasks completed.
2085 return true;
2086 }
2087
Inst_TaskIO_DecayBonus(cAvidaContext & ctx)2088 bool cHardwareGX::Inst_TaskIO_DecayBonus(cAvidaContext& ctx)
2089 {
2090 (void) Inst_TaskIO(ctx);
2091 m_organism->GetPhenotype().SetCurBonus(m_organism->GetPhenotype().GetCurBonus() * 0.99);
2092 return true;
2093 }
2094
Inst_TaskIO_Feedback(cAvidaContext & ctx)2095 bool cHardwareGX::Inst_TaskIO_Feedback(cAvidaContext& ctx)
2096 {
2097 const int reg_used = FindModifiedRegister(REG_BX);
2098
2099 //check cur_bonus before the output
2100 double preOutputBonus = m_organism->GetPhenotype().GetCurBonus();
2101
2102 // Do the "put" component
2103 const int value_out = GetRegister(reg_used);
2104 m_organism->DoOutput(ctx, m_current->m_input_buf, m_current->m_output_buf, value_out); // Check for tasks completed.
2105
2106 //check cur_merit after the output
2107 double postOutputBonus = m_organism->GetPhenotype().GetCurBonus();
2108
2109 //push the effect of the IO on merit (+,0,-) to the active stack
2110 if (preOutputBonus > postOutputBonus){
2111 StackPush(-1);
2112 }
2113 else if (preOutputBonus == postOutputBonus){
2114 StackPush(0);
2115 }
2116 else if (preOutputBonus < postOutputBonus){
2117 StackPush(1);
2118 }
2119 else {
2120 assert(0);
2121 //Bollocks. There was an error.
2122 }
2123
2124 // Do the "get" component
2125 const int value_in = m_organism->GetNextInput(m_current->m_input_pointer);
2126 GetRegister(reg_used) = value_in;
2127 m_organism->DoInput(m_current->m_input_buf, m_current->m_output_buf, value_in); // Check for tasks completed.
2128 return true;
2129 }
2130
Inst_MatchStrings(cAvidaContext & ctx)2131 bool cHardwareGX::Inst_MatchStrings(cAvidaContext& ctx)
2132 {
2133 if (m_executedmatchstrings)
2134 return false;
2135 m_organism->DoOutput(ctx, 357913941);
2136 m_executedmatchstrings = true;
2137 return true;
2138 }
2139
Inst_Sell(cAvidaContext & ctx)2140 bool cHardwareGX::Inst_Sell(cAvidaContext& ctx)
2141 {
2142 int search_label = GetLabel().AsInt(3) % MARKET_SIZE;
2143 int send_value = GetRegister(REG_BX);
2144 int sell_price = m_world->GetConfig().SELL_PRICE.Get();
2145 m_organism->SellValue(send_value, search_label, sell_price);
2146 return true;
2147 }
2148
Inst_Buy(cAvidaContext & ctx)2149 bool cHardwareGX::Inst_Buy(cAvidaContext& ctx)
2150 {
2151 int search_label = GetLabel().AsInt(3) % MARKET_SIZE;
2152 int buy_price = m_world->GetConfig().BUY_PRICE.Get();
2153 GetRegister(REG_BX) = m_organism->BuyValue(search_label, buy_price);
2154 return true;
2155 }
2156
Inst_Send(cAvidaContext & ctx)2157 bool cHardwareGX::Inst_Send(cAvidaContext& ctx)
2158 {
2159 const int reg_used = FindModifiedRegister(REG_BX);
2160 m_organism->SendValue(GetRegister(reg_used));
2161 GetRegister(reg_used) = 0;
2162 return true;
2163 }
2164
Inst_Receive(cAvidaContext & ctx)2165 bool cHardwareGX::Inst_Receive(cAvidaContext& ctx)
2166 {
2167 const int reg_used = FindModifiedRegister(REG_BX);
2168 GetRegister(reg_used) = m_organism->ReceiveValue();
2169 return true;
2170 }
2171
Inst_SenseLog2(cAvidaContext & ctx)2172 bool cHardwareGX::Inst_SenseLog2(cAvidaContext& ctx)
2173 {
2174 return DoSense(ctx, 0, 2);
2175 }
2176
Inst_SenseUnit(cAvidaContext & ctx)2177 bool cHardwareGX::Inst_SenseUnit(cAvidaContext& ctx)
2178 {
2179 return DoSense(ctx, 1, 1);
2180 }
2181
Inst_SenseMult100(cAvidaContext & ctx)2182 bool cHardwareGX::Inst_SenseMult100(cAvidaContext& ctx)
2183 {
2184 return DoSense(ctx, 1, 100);
2185 }
2186
DoSense(cAvidaContext & ctx,int conversion_method,double base)2187 bool cHardwareGX::DoSense(cAvidaContext& ctx, int conversion_method, double base)
2188 {
2189 // Returns the log2 amount of a resource or resources
2190 // specified by modifying NOPs into register BX
2191 const tArray<double> res_count = m_organism->GetOrgInterface().GetResources(ctx) +
2192 m_organism->GetOrgInterface().GetDemeResources(m_organism->GetOrgInterface().GetDemeID(), ctx);
2193
2194 // Arbitrarily set to BX since the conditionals use this directly.
2195 int reg_to_set = REG_BX;
2196
2197 // There are no resources, return
2198 if (res_count.GetSize() == 0) return false;
2199
2200 // Only recalculate logs if these values have changed
2201 static int last_num_resources = 0;
2202 static int max_label_length = 0;
2203 int num_nops = GetInstSet().GetNumNops();
2204
2205 if ((last_num_resources != res_count.GetSize()))
2206 {
2207 max_label_length = (int) ceil(log((double)res_count.GetSize())/log((double)num_nops));
2208 last_num_resources = res_count.GetSize();
2209 }
2210
2211 // Convert modifying NOPs to the index of the resource.
2212 // If there are fewer than the number of NOPs required
2213 // to uniquely specify a resource, then add together
2214 // a subset of resources (motivation: regulation can evolve
2215 // to be more specific if there is an advantage)
2216
2217 // Find the maximum number of NOPs needed to specify this number of resources
2218 // Note: It's a bit wasteful to recalculate this every time and organisms will
2219 // definitely be confused if the number of resources changes during a run
2220 // because their mapping to resources will be disrupted
2221
2222 // Attempt to read a label with this maximum length
2223 cHardwareGX::ReadLabel(max_label_length);
2224
2225 // Find the length of the label that we actually obtained (max is max_reg_needed)
2226 int real_label_length = GetLabel().GetSize();
2227
2228 // Start and end labels to define the start and end indices of
2229 // resources that we need to add together
2230 cCodeLabel start_label = cCodeLabel(GetLabel());
2231 cCodeLabel end_label = cCodeLabel(GetLabel());
2232
2233 for (int i = 0; i < max_label_length - real_label_length; i++)
2234 {
2235 start_label.AddNop(0);
2236 end_label.AddNop(num_nops-1);
2237 }
2238
2239 int start_index = start_label.AsInt(num_nops);
2240 int end_index = end_label.AsInt(num_nops);
2241
2242 // If the label refers to ONLY resources that
2243 // do not exist, then the operation fails
2244 if (start_index >= res_count.GetSize()) return false;
2245
2246 // Otherwise sum all valid resources that it might refer to
2247 // (this will only be ONE if the label was of the maximum length).
2248 int resource_result = 0;
2249 for (int i = start_index; i <= end_index; i++)
2250 {
2251 // if it's a valid resource
2252 if (i < res_count.GetSize())
2253 {
2254 if (conversion_method == 0) // Log2
2255 {
2256 // (alternately you could assign min_int for zero resources, but
2257 // that would cause wierdness when adding sense values together)
2258 if (res_count[i] > 0) resource_result += (int)(log(res_count[i])/log(base));
2259 }
2260 else if (conversion_method == 1) // Addition of multiplied resource amount
2261 {
2262 int add_amount = (int) (res_count[i] * base);
2263 // Do some range checking to make sure we don't overflow
2264 resource_result = (INT_MAX - resource_result <= add_amount) ? INT_MAX : resource_result + add_amount;
2265 }
2266 }
2267 }
2268
2269 //Dump this value into an arbitrary register: BX
2270 GetRegister(reg_to_set) = resource_result;
2271
2272 //We have to convert this to a different index that includes all degenerate labels possible: shortest to longest
2273 int sensed_index = 0;
2274 int on = 1;
2275 for (int i = 0; i < real_label_length; i++)
2276 {
2277 sensed_index += on;
2278 on *= num_nops;
2279 }
2280 sensed_index+= GetLabel().AsInt(num_nops);
2281 m_organism->GetPhenotype().IncSenseCount(sensed_index);
2282
2283 return true;
2284
2285 // Note that we are converting <double> resources to <int> register values
2286 }
2287
DoDonate(cOrganism * to_org)2288 void cHardwareGX::DoDonate(cOrganism* to_org)
2289 {
2290 assert(to_org != NULL);
2291
2292 const double merit_given = m_world->GetConfig().MERIT_GIVEN.Get();
2293 const double merit_received = m_world->GetConfig().MERIT_RECEIVED.Get();
2294
2295 double cur_merit = m_organism->GetPhenotype().GetMerit().GetDouble();
2296 cur_merit -= merit_given;
2297 if(cur_merit < 0) cur_merit=0;
2298
2299 // Plug the current merit back into this organism and notify the scheduler.
2300 m_organism->UpdateMerit(cur_merit);
2301
2302 // Update the merit of the organism being donated to...
2303 double other_merit = to_org->GetPhenotype().GetMerit().GetDouble();
2304 other_merit += merit_received;
2305 to_org->UpdateMerit(other_merit);
2306 }
2307
Inst_DonateRandom(cAvidaContext & ctx)2308 bool cHardwareGX::Inst_DonateRandom(cAvidaContext& ctx)
2309 {
2310
2311 if (m_organism->GetPhenotype().GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2312 return false;
2313 }
2314
2315 m_organism->GetPhenotype().IncDonates();
2316 m_organism->GetPhenotype().SetIsDonorRand();
2317
2318 // Turn to a random neighbor, get it, and turn back...
2319 int neighbor_id = ctx.GetRandom().GetInt(m_organism->GetNeighborhoodSize());
2320 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(1);
2321 cOrganism * neighbor = m_organism->GetNeighbor();
2322 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(-1);
2323
2324 // Donate only if we have found a neighbor.
2325 if (neighbor != NULL) {
2326 DoDonate(neighbor);
2327 neighbor->GetPhenotype().SetIsReceiverRand();
2328 }
2329
2330 return true;
2331 }
2332
2333
2334
Inst_DonateEditDist(cAvidaContext & ctx)2335 bool cHardwareGX::Inst_DonateEditDist(cAvidaContext& ctx)
2336 {
2337 if (m_organism->GetPhenotype().GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2338 return false;
2339 }
2340
2341 m_organism->GetPhenotype().IncDonates();
2342 m_organism->GetPhenotype().SetIsDonorEdit();
2343
2344 // Find the target as the first Kin found in the neighborhood.
2345 const int num_neighbors = m_organism->GetNeighborhoodSize();
2346
2347 // Turn to face a random neighbor
2348 int neighbor_id = ctx.GetRandom().GetInt(num_neighbors);
2349 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(1);
2350 cOrganism* neighbor = m_organism->GetNeighbor();
2351
2352 // If there is no max edit distance, take the random neighbor we're facing.
2353 const int max_dist = m_world->GetConfig().MAX_DONATE_EDIT_DIST.Get();
2354 if (max_dist != -1) {
2355 int max_id = neighbor_id + num_neighbors;
2356 bool found = false;
2357 while (neighbor_id < max_id) {
2358 neighbor = m_organism->GetNeighbor();
2359 int edit_dist = max_dist + 1;
2360 if (neighbor != NULL) {
2361 edit_dist = Sequence::FindEditDistance(m_organism->GetGenome().GetSequence(),
2362 neighbor->GetGenome().GetSequence());
2363 }
2364 if (edit_dist <= max_dist) {
2365 found = true;
2366 break;
2367 }
2368 m_organism->Rotate(1);
2369 neighbor_id++;
2370 }
2371 if (found == false) neighbor = NULL;
2372 }
2373
2374 // Put the facing back where it was.
2375 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(-1);
2376
2377 // Donate only if we have found a close enough relative...
2378 if (neighbor != NULL){
2379 DoDonate(neighbor);
2380 neighbor->GetPhenotype().SetIsReceiverEdit();
2381 }
2382 return true;
2383 }
2384
Inst_DonateGreenBeardGene(cAvidaContext & ctx)2385 bool cHardwareGX::Inst_DonateGreenBeardGene(cAvidaContext& ctx)
2386 {
2387 //this donates to organisms that have this instruction anywhere
2388 //in their genome (see Dawkins 1976, The Selfish Gene, for
2389 //the history of the theory and the name 'green beard'
2390 cPhenotype & phenotype = m_organism->GetPhenotype();
2391
2392 if (m_organism->GetPhenotype().GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2393 return false;
2394 }
2395
2396 phenotype.IncDonates();
2397 phenotype.SetIsDonorGbg();
2398
2399 // Find the target as the first match found in the neighborhood.
2400
2401 //get the neighborhood size
2402 const int num_neighbors = m_organism->GetNeighborhoodSize();
2403
2404 // Turn to face a random neighbor
2405 int neighbor_id = ctx.GetRandom().GetInt(num_neighbors);
2406 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(1);
2407 cOrganism * neighbor = m_organism->GetNeighbor();
2408
2409 int max_id = neighbor_id + num_neighbors;
2410
2411 //we have not found a match yet
2412 bool found = false;
2413
2414 // rotate through orgs in neighborhood
2415 while (neighbor_id < max_id) {
2416 neighbor = m_organism->GetNeighbor();
2417
2418 //if neighbor exists, do they have the green beard gene?
2419 if (neighbor != NULL) {
2420 const Sequence & neighbor_genome = neighbor->GetGenome().GetSequence();
2421
2422 // for each instruction in the genome...
2423 for(int i=0;i<neighbor_genome.GetSize();i++){
2424
2425 // ...see if it is donate-gbg
2426 if (neighbor_genome[i] == IP().GetInst()) {
2427 found = true;
2428 break;
2429 }
2430
2431 }
2432 }
2433
2434 // stop searching through the neighbors if we already found one
2435 if (found == true) break;
2436
2437 m_organism->Rotate(1);
2438 neighbor_id++;
2439 }
2440
2441 if (found == false) neighbor = NULL;
2442
2443 // Put the facing back where it was.
2444 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(-1);
2445
2446 // Donate only if we have found a close enough relative...
2447 if (neighbor != NULL) {
2448 DoDonate(neighbor);
2449 neighbor->GetPhenotype().SetIsReceiverGbg();
2450 }
2451
2452 return true;
2453
2454 }
2455
Inst_DonateTrueGreenBeard(cAvidaContext & ctx)2456 bool cHardwareGX::Inst_DonateTrueGreenBeard(cAvidaContext& ctx)
2457 {
2458 //this donates to organisms that have this instruction anywhere
2459 //in their genome AND their parents excuted it
2460 //(see Dawkins 1976, The Selfish Gene, for
2461 //the history of the theory and the name 'green beard'
2462 // cout << "i am about to donate to a green beard" << endl;
2463 cPhenotype & phenotype = m_organism->GetPhenotype();
2464
2465 if (m_organism->GetPhenotype().GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2466 return false;
2467 }
2468
2469 phenotype.IncDonates();
2470 phenotype.SetIsDonorTrueGb();
2471
2472 // Find the target as the first match found in the neighborhood.
2473
2474 //get the neighborhood size
2475 const int num_neighbors = m_organism->GetNeighborhoodSize();
2476
2477 // Turn to face a random neighbor
2478 int neighbor_id = ctx.GetRandom().GetInt(num_neighbors);
2479 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(1);
2480 cOrganism * neighbor = m_organism->GetNeighbor();
2481
2482 int max_id = neighbor_id + num_neighbors;
2483
2484 //we have not found a match yet
2485 bool found = false;
2486
2487 // rotate through orgs in neighborhood
2488 while (neighbor_id < max_id) {
2489 neighbor = m_organism->GetNeighbor();
2490 //if neighbor exists, AND if their parent attempted to donate,
2491 if (neighbor != NULL && neighbor->GetPhenotype().IsDonorTrueGbLast()) {
2492 const Sequence& neighbor_genome = neighbor->GetGenome().GetSequence();
2493
2494 // for each instruction in the genome...
2495 for(int i=0;i<neighbor_genome.GetSize();i++){
2496
2497 // ...see if it is donate-tgb, if so, we found a target
2498 if (neighbor_genome[i] == IP().GetInst()) {
2499 found = true;
2500 break;
2501 }
2502
2503 }
2504 }
2505
2506 // stop searching through the neighbors if we already found one
2507 if (found == true) break;
2508
2509 m_organism->Rotate(1);
2510 neighbor_id++;
2511 }
2512
2513 if (found == false) neighbor = NULL;
2514
2515 // Put the facing back where it was.
2516 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(-1);
2517
2518 // Donate only if we have found a close enough relative...
2519 if (neighbor != NULL) {
2520 DoDonate(neighbor);
2521 neighbor->GetPhenotype().SetIsReceiverTrueGb();
2522 }
2523
2524
2525 return true;
2526
2527 }
2528
Inst_DonateThreshGreenBeard(cAvidaContext & ctx)2529 bool cHardwareGX::Inst_DonateThreshGreenBeard(cAvidaContext& ctx)
2530 {
2531 //this donates to organisms that have this instruction anywhere
2532 //in their genome AND their parents excuted it >=THRESHOLD number of times
2533 //(see Dawkins 1976, The Selfish Gene, for
2534 //the history of the theory and the name 'green beard'
2535 // cout << "i am about to donate to a green beard" << endl;
2536 cPhenotype & phenotype = m_organism->GetPhenotype();
2537
2538 if (m_organism->GetPhenotype().GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2539 return false;
2540 }
2541
2542
2543 phenotype.IncDonates();
2544 phenotype.SetIsDonorThreshGb();
2545 phenotype.IncNumThreshGbDonations();
2546
2547 // Find the target as the first match found in the neighborhood.
2548
2549 //get the neighborhood size
2550 const int num_neighbors = m_organism->GetNeighborhoodSize();
2551
2552 // Turn to face a random neighbor
2553 int neighbor_id = ctx.GetRandom().GetInt(num_neighbors);
2554 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(1);
2555 cOrganism * neighbor = m_organism->GetNeighbor();
2556
2557 int max_id = neighbor_id + num_neighbors;
2558
2559 //we have not found a match yet
2560 bool found = false;
2561
2562 // rotate through orgs in neighborhood
2563 while (neighbor_id < max_id) {
2564 neighbor = m_organism->GetNeighbor();
2565 //if neighbor exists, AND if their parent attempted to donate >= threshhold,
2566 if (neighbor != NULL && neighbor->GetPhenotype().GetNumThreshGbDonationsLast()>= m_world->GetConfig().MIN_GB_DONATE_THRESHOLD.Get() ) {
2567 const Sequence & neighbor_genome = neighbor->GetGenome().GetSequence();
2568
2569 // for each instruction in the genome...
2570 for(int i=0;i<neighbor_genome.GetSize();i++){
2571
2572 // ...see if it is donate-threshgb, if so, we found a target
2573 if (neighbor_genome[i] == IP().GetInst()) {
2574 found = true;
2575 break;
2576 }
2577
2578 }
2579 }
2580
2581 // stop searching through the neighbors if we already found one
2582 if (found == true) break;
2583
2584 m_organism->Rotate(1);
2585 neighbor_id++;
2586 }
2587
2588 if (found == false) neighbor = NULL;
2589
2590 // Put the facing back where it was.
2591 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(-1);
2592
2593 // Donate only if we have found a close enough relative...
2594 if (neighbor != NULL) {
2595 DoDonate(neighbor);
2596 neighbor->GetPhenotype().SetIsReceiverThreshGb();
2597 // cout << "************ neighbor->GetPhenotype().GetNumThreshGbDonationsLast() is " << neighbor->GetPhenotype().GetNumThreshGbDonationsLast() << endl;
2598
2599 }
2600
2601 return true;
2602
2603 }
2604
2605
Inst_DonateQuantaThreshGreenBeard(cAvidaContext & ctx)2606 bool cHardwareGX::Inst_DonateQuantaThreshGreenBeard(cAvidaContext& ctx)
2607 {
2608 // this donates to organisms that have this instruction anywhere in their
2609 // genome AND their parents excuted it more than a THRESHOLD number of times
2610 // where that threshold depend on the number of times the individual's
2611 // parents attempted to donate using this instruction. The threshold levels
2612 // are multiples of the quanta value set in avida.cfg, and the highest level
2613 // that the donor qualifies for is the one used.
2614
2615 // (see Dawkins 1976, The Selfish Gene, for the history of the theory and
2616 // the name 'green beard'
2617 // cout << "i am about to donate to a green beard" << endl;
2618 cPhenotype & phenotype = m_organism->GetPhenotype();
2619
2620 if (phenotype.GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2621 return false;
2622 }
2623
2624 phenotype.IncDonates();
2625 phenotype.SetIsDonorQuantaThreshGb();
2626 phenotype.IncNumQuantaThreshGbDonations();
2627 //cout << endl << "quanta_threshgb attempt.. " ;
2628
2629
2630 // Find the target as the first match found in the neighborhood.
2631
2632 //get the neighborhood size
2633 const int num_neighbors = m_organism->GetNeighborhoodSize();
2634
2635 // Turn to face a random neighbor
2636 int neighbor_id = ctx.GetRandom().GetInt(num_neighbors);
2637 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(1);
2638 cOrganism * neighbor = m_organism->GetNeighbor();
2639
2640 int max_id = neighbor_id + num_neighbors;
2641
2642 //we have not found a match yet
2643 bool found = false;
2644
2645 // Get the quanta (step size) between threshold levels.
2646 const int donate_quanta = m_world->GetConfig().DONATE_THRESH_QUANTA.Get();
2647
2648 // Calculate what quanta level we should be at for this individual. We do a
2649 // math trick to make sure its the next lowest event multiple of donate_quanta.
2650 const int quanta_donate_thresh =
2651 (phenotype.GetNumQuantaThreshGbDonationsLast() / donate_quanta) * donate_quanta;
2652 //cout << " phenotype.GetNumQuantaThreshGbDonationsLast() is " << phenotype.GetNumQuantaThreshGbDonationsLast();
2653 //cout << " quanta thresh= " << quanta_donate_thresh;
2654 // rotate through orgs in neighborhood
2655 while (neighbor_id < max_id) {
2656 neighbor = m_organism->GetNeighbor();
2657 //if neighbor exists, AND if their parent attempted to donate >= threshhold,
2658 if (neighbor != NULL &&
2659 neighbor->GetPhenotype().GetNumQuantaThreshGbDonationsLast() >= quanta_donate_thresh) {
2660
2661 const Sequence & neighbor_genome = neighbor->GetGenome().GetSequence();
2662
2663 // for each instruction in the genome...
2664 for(int i=0;i<neighbor_genome.GetSize();i++){
2665
2666 // ...see if it is donate-quantagb, if so, we found a target
2667 if (neighbor_genome[i] == IP().GetInst()) {
2668 found = true;
2669 break;
2670 }
2671
2672 }
2673 }
2674
2675 // stop searching through the neighbors if we already found one
2676 if (found == true) break;
2677
2678 m_organism->Rotate(1);
2679 neighbor_id++;
2680 }
2681
2682 if (found == false) neighbor = NULL;
2683
2684 // Put the facing back where it was.
2685 for (int i = 0; i < neighbor_id; i++) m_organism->Rotate(-1);
2686
2687 // Donate only if we have found a close enough relative...
2688 if (neighbor != NULL) {
2689 DoDonate(neighbor);
2690 neighbor->GetPhenotype().SetIsReceiverQuantaThreshGb();
2691 //cout << " ************ neighbor->GetPhenotype().GetNumQuantaThreshGbDonationsLast() is " << neighbor->GetPhenotype().GetNumQuantaThreshGbDonationsLast();
2692
2693 }
2694
2695 return true;
2696
2697 }
2698
2699
Inst_DonateNULL(cAvidaContext & ctx)2700 bool cHardwareGX::Inst_DonateNULL(cAvidaContext& ctx)
2701 {
2702 if (m_organism->GetPhenotype().GetCurNumDonates() > m_world->GetConfig().MAX_DONATES.Get()) {
2703 return false;
2704 }
2705
2706 m_organism->GetPhenotype().IncDonates();
2707 m_organism->GetPhenotype().SetIsDonorNull();
2708
2709 // This is a fake donate command that causes the organism to lose merit,
2710 // but no one else to gain any.
2711
2712 const double merit_given = m_world->GetConfig().MERIT_GIVEN.Get();
2713 double cur_merit = m_organism->GetPhenotype().GetMerit().GetDouble();
2714 cur_merit -= merit_given;
2715
2716 // Plug the current merit back into this organism and notify the scheduler.
2717 m_organism->UpdateMerit(cur_merit);
2718
2719 return true;
2720 }
2721
2722
Inst_SearchF(cAvidaContext & ctx)2723 bool cHardwareGX::Inst_SearchF(cAvidaContext& ctx)
2724 {
2725 ReadLabel();
2726 GetLabel().Rotate(1, NUM_NOPS);
2727 const int search_size = FindLabel(1).GetPosition() - IP().GetPosition();
2728 GetRegister(REG_BX) = search_size;
2729 GetRegister(REG_CX) = GetLabel().GetSize();
2730 return true;
2731 }
2732
Inst_SearchB(cAvidaContext & ctx)2733 bool cHardwareGX::Inst_SearchB(cAvidaContext& ctx)
2734 {
2735 ReadLabel();
2736 GetLabel().Rotate(1, NUM_NOPS);
2737 const int search_size = IP().GetPosition() - FindLabel(-1).GetPosition();
2738 GetRegister(REG_BX) = search_size;
2739 GetRegister(REG_CX) = GetLabel().GetSize();
2740 return true;
2741 }
2742
Inst_MemSize(cAvidaContext & ctx)2743 bool cHardwareGX::Inst_MemSize(cAvidaContext& ctx)
2744 {
2745 GetRegister(FindModifiedRegister(REG_BX)) = GetMemory().GetSize();
2746 return true;
2747 }
2748
2749
Inst_RotateL(cAvidaContext & ctx)2750 bool cHardwareGX::Inst_RotateL(cAvidaContext& ctx)
2751 {
2752 const int num_neighbors = m_organism->GetNeighborhoodSize();
2753
2754 // If this organism has no neighbors, ignore rotate.
2755 if (num_neighbors == 0) return false;
2756
2757 ReadLabel();
2758
2759 // Always rotate at least once.
2760 m_organism->Rotate(-1);
2761
2762 // If there is no label, then the one rotation was all we want.
2763 if (!GetLabel().GetSize()) return true;
2764
2765 // Rotate until a complement label is found (or all have been checked).
2766 GetLabel().Rotate(1, NUM_NOPS);
2767 for (int i = 1; i < num_neighbors; i++) {
2768 cOrganism* neighbor = m_organism->GetNeighbor();
2769
2770 if (neighbor != NULL && neighbor->GetHardware().FindLabelFull(GetLabel()).InMemory()) return true;
2771
2772 // Otherwise keep rotating...
2773 m_organism->Rotate(-1);
2774 }
2775 return true;
2776 }
2777
Inst_RotateR(cAvidaContext & ctx)2778 bool cHardwareGX::Inst_RotateR(cAvidaContext& ctx)
2779 {
2780 const int num_neighbors = m_organism->GetNeighborhoodSize();
2781
2782 // If this m_organism has no neighbors, ignore rotate.
2783 if (num_neighbors == 0) return false;
2784
2785 ReadLabel();
2786
2787 // Always rotate at least once.
2788 m_organism->Rotate(1);
2789
2790 // If there is no label, then the one rotation was all we want.
2791 if (!GetLabel().GetSize()) return true;
2792
2793 // Rotate until a complement label is found (or all have been checked).
2794 GetLabel().Rotate(1, NUM_NOPS);
2795 for (int i = 1; i < num_neighbors; i++) {
2796 cOrganism* neighbor = m_organism->GetNeighbor();
2797
2798 if (neighbor != NULL && neighbor->GetHardware().FindLabelFull(GetLabel()).InMemory()) return true;
2799
2800 // Otherwise keep rotating...
2801 m_organism->Rotate(1);
2802 }
2803 return true;
2804 }
2805
Inst_SetCopyMut(cAvidaContext & ctx)2806 bool cHardwareGX::Inst_SetCopyMut(cAvidaContext& ctx)
2807 {
2808 const int reg_used = FindModifiedRegister(REG_BX);
2809 const int new_mut_rate = Max(GetRegister(reg_used), 1 );
2810 m_organism->SetCopyMutProb(static_cast<double>(new_mut_rate) / 10000.0);
2811 return true;
2812 }
2813
Inst_ModCopyMut(cAvidaContext & ctx)2814 bool cHardwareGX::Inst_ModCopyMut(cAvidaContext& ctx)
2815 {
2816 const int reg_used = FindModifiedRegister(REG_BX);
2817 const double new_mut_rate = m_organism->GetCopyMutProb() + static_cast<double>(GetRegister(reg_used)) / 10000.0;
2818 if (new_mut_rate > 0.0) m_organism->SetCopyMutProb(new_mut_rate);
2819 return true;
2820 }
2821
2822 // Energy use
2823
Inst_ZeroEnergyUsed(cAvidaContext & ctx)2824 bool cHardwareGX::Inst_ZeroEnergyUsed(cAvidaContext& ctx)
2825 {
2826 // Typically, this instruction should be triggered by a REACTION
2827 m_organism->GetPhenotype().SetTimeUsed(0);
2828 return true;
2829 }
2830
2831
2832 // Head-based instructions
2833
2834 //bool cHardwareGX::Inst_SetHead(cAvidaContext& ctx)
2835 //{
2836 // const int head_used = FindModifiedHead(nHardware::HEAD_IP);
2837 // m_threads[m_cur_thread].cur_head = static_cast<unsigned char>(head_used);
2838 // return true;
2839 //}
2840
Inst_AdvanceHead(cAvidaContext & ctx)2841 bool cHardwareGX::Inst_AdvanceHead(cAvidaContext& ctx)
2842 {
2843 const int head_used = FindModifiedHead(nHardware::HEAD_WRITE);
2844 GetHead(head_used).Advance();
2845 return true;
2846 }
2847
Inst_MoveHead(cAvidaContext & ctx)2848 bool cHardwareGX::Inst_MoveHead(cAvidaContext& ctx)
2849 {
2850 const int head_used = FindModifiedHead(nHardware::HEAD_IP);
2851 const int target = nHardware::HEAD_FLOW;
2852 GetHead(head_used).Set(GetHead(target));
2853 if (head_used == nHardware::HEAD_IP) m_advance_ip = false;
2854 return true;
2855 }
2856
Inst_JumpHead(cAvidaContext & ctx)2857 bool cHardwareGX::Inst_JumpHead(cAvidaContext& ctx)
2858 {
2859 const int head_used = FindModifiedHead(nHardware::HEAD_IP);
2860 GetHead(head_used).Jump(GetRegister(REG_CX) );
2861 // JEB - probably shouldn't advance inst ptr after jumping here?
2862 // Any negative number jumps to the beginning of the genome (pos 0)
2863 // and then we immediately advance past that first instruction.
2864 return true;
2865 }
2866
Inst_GetHead(cAvidaContext & ctx)2867 bool cHardwareGX::Inst_GetHead(cAvidaContext& ctx)
2868 {
2869 const int head_used = FindModifiedHead(nHardware::HEAD_IP);
2870 GetRegister(REG_CX) = GetHead(head_used).GetPosition();
2871 return true;
2872 }
2873
Inst_IfLabel(cAvidaContext & ctx)2874 bool cHardwareGX::Inst_IfLabel(cAvidaContext& ctx)
2875 {
2876 ReadLabel();
2877 GetLabel().Rotate(1, NUM_NOPS);
2878 if (GetLabel() != GetReadLabel()) IP().Advance();
2879 return true;
2880 }
2881
2882 // This is a variation on IfLabel that will skip the next command if the "if"
2883 // is false, but it will also skip all nops following that command.
Inst_IfLabel2(cAvidaContext & ctx)2884 bool cHardwareGX::Inst_IfLabel2(cAvidaContext& ctx)
2885 {
2886 ReadLabel();
2887 GetLabel().Rotate(1, NUM_NOPS);
2888 if (GetLabel() != GetReadLabel()) {
2889 IP().Advance();
2890 if (m_inst_set->IsNop( IP().GetNextInst() )) IP().Advance();
2891 }
2892 return true;
2893 }
2894
2895
Inst_HeadDivide(cAvidaContext & ctx)2896 bool cHardwareGX::Inst_HeadDivide(cAvidaContext& ctx)
2897 {
2898 return Divide_Main(ctx);
2899 }
2900
2901
Inst_HeadRead(cAvidaContext & ctx)2902 bool cHardwareGX::Inst_HeadRead(cAvidaContext& ctx)
2903 {
2904 const int dst = REG_BX;
2905
2906 const int head_id = FindModifiedHead(nHardware::HEAD_READ);
2907 GetHead(head_id).Adjust();
2908
2909 // <--- GX addition
2910 if ( !m_programids[GetHead(head_id).GetMemSpace()]->GetReadable() ) return false;
2911 // --->
2912
2913 // Mutations only occur on the read, for the moment.
2914 int read_inst = 0;
2915 if (m_organism->TestCopyMut(ctx)) {
2916 read_inst = m_inst_set->GetRandomInst(ctx).GetOp();
2917 } else {
2918 read_inst = GetHead(head_id).GetInst().GetOp();
2919 }
2920 GetRegister(dst) = read_inst;
2921 ReadInst(read_inst);
2922
2923 GetHead(head_id).Advance();
2924 return true;
2925 }
2926
Inst_HeadWrite(cAvidaContext & ctx)2927 bool cHardwareGX::Inst_HeadWrite(cAvidaContext& ctx)
2928 {
2929 const int src = REG_BX;
2930 const int head_id = FindModifiedHead(nHardware::HEAD_WRITE);
2931 cHeadCPU& active_head = GetHead(head_id);
2932
2933 // <--- GX addition
2934 if ( !m_programids[GetHead(head_id).GetMemSpace()]->GetReadable() ) return false;
2935 // --->
2936
2937 active_head.Adjust();
2938
2939 int value = GetRegister(src);
2940 if (value < 0 || value >= m_inst_set->GetSize()) value = 0;
2941
2942 active_head.SetInst(cInstruction(value));
2943 active_head.SetFlagCopied();
2944
2945 // Advance the head after write...
2946 active_head++;
2947 return true;
2948 }
2949
Inst_HeadCopy(cAvidaContext & ctx)2950 bool cHardwareGX::Inst_HeadCopy(cAvidaContext& ctx)
2951 {
2952 // For the moment, this cannot be nop-modified.
2953 cHeadCPU& read_head = GetHead(nHardware::HEAD_READ);
2954 cHeadCPU& write_head = GetHead(nHardware::HEAD_WRITE);
2955
2956 read_head.Adjust();
2957 write_head.Adjust();
2958
2959 // Do mutations.
2960 cInstruction read_inst = read_head.GetInst();
2961 ReadInst(read_inst.GetOp());
2962 if (m_organism->TestCopyMut(ctx)) {
2963 read_inst = m_inst_set->GetRandomInst(ctx);
2964 write_head.SetFlagMutated();
2965 write_head.SetFlagCopyMut();
2966 }
2967
2968 write_head.SetInst(read_inst);
2969 write_head.SetFlagCopied(); // Set the copied flag...
2970
2971 read_head.Advance();
2972 write_head.Advance();
2973 return true;
2974 }
2975
HeadCopy_ErrorCorrect(cAvidaContext & ctx,double reduction)2976 bool cHardwareGX::HeadCopy_ErrorCorrect(cAvidaContext& ctx, double reduction)
2977 {
2978 // For the moment, this cannot be nop-modified.
2979 cHeadCPU & read_head = GetHead(nHardware::HEAD_READ);
2980 cHeadCPU & write_head = GetHead(nHardware::HEAD_WRITE);
2981
2982 read_head.Adjust();
2983 write_head.Adjust();
2984
2985 // Do mutations.
2986 cInstruction read_inst = read_head.GetInst();
2987 ReadInst(read_inst.GetOp());
2988 if ( ctx.GetRandom().P(m_organism->GetCopyMutProb() / reduction) ) {
2989 read_inst = m_inst_set->GetRandomInst(ctx);
2990 write_head.SetFlagMutated();
2991 write_head.SetFlagCopyMut();
2992 }
2993
2994 write_head.SetInst(read_inst);
2995 write_head.SetFlagCopied(); // Set the copied flag...
2996
2997 read_head.Advance();
2998 write_head.Advance();
2999 return true;
3000 }
3001
Inst_HeadCopy2(cAvidaContext & ctx)3002 bool cHardwareGX::Inst_HeadCopy2(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 2); }
Inst_HeadCopy3(cAvidaContext & ctx)3003 bool cHardwareGX::Inst_HeadCopy3(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 3); }
Inst_HeadCopy4(cAvidaContext & ctx)3004 bool cHardwareGX::Inst_HeadCopy4(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 4); }
Inst_HeadCopy5(cAvidaContext & ctx)3005 bool cHardwareGX::Inst_HeadCopy5(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 5); }
Inst_HeadCopy6(cAvidaContext & ctx)3006 bool cHardwareGX::Inst_HeadCopy6(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 6); }
Inst_HeadCopy7(cAvidaContext & ctx)3007 bool cHardwareGX::Inst_HeadCopy7(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 7); }
Inst_HeadCopy8(cAvidaContext & ctx)3008 bool cHardwareGX::Inst_HeadCopy8(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 8); }
Inst_HeadCopy9(cAvidaContext & ctx)3009 bool cHardwareGX::Inst_HeadCopy9(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 9); }
Inst_HeadCopy10(cAvidaContext & ctx)3010 bool cHardwareGX::Inst_HeadCopy10(cAvidaContext& ctx) { return HeadCopy_ErrorCorrect(ctx, 10); }
3011
Inst_HeadSearch(cAvidaContext & ctx)3012 bool cHardwareGX::Inst_HeadSearch(cAvidaContext& ctx)
3013 {
3014 ReadLabel();
3015 GetLabel().Rotate(1, NUM_NOPS);
3016 cHeadCPU found_pos = FindLabel(0);
3017 const int search_size = found_pos.GetPosition() - IP().GetPosition();
3018 GetRegister(REG_BX) = search_size;
3019 GetRegister(REG_CX) = GetLabel().GetSize();
3020 GetHead(nHardware::HEAD_FLOW).Set(found_pos);
3021 GetHead(nHardware::HEAD_FLOW).Advance();
3022 return true;
3023 }
3024
Inst_SetFlow(cAvidaContext & ctx)3025 bool cHardwareGX::Inst_SetFlow(cAvidaContext& ctx)
3026 {
3027 const int reg_used = FindModifiedRegister(REG_CX);
3028 GetHead(nHardware::HEAD_FLOW).Set(GetRegister(reg_used));
3029 return true;
3030 }
3031
3032 //// Placebo insts ////
Inst_Skip(cAvidaContext & ctx)3033 bool cHardwareGX::Inst_Skip(cAvidaContext& ctx)
3034 {
3035 IP().Advance();
3036 return true;
3037 }
3038
3039 /*! This instruction allocates a new programid with a zero length genome
3040 and moves the write head of the current programid to it.
3041 */
Inst_NewProgramid(cAvidaContext & ctx,bool executable,bool bindable,bool readable)3042 bool cHardwareGX::Inst_NewProgramid(cAvidaContext& ctx, bool executable, bool bindable, bool readable)
3043 {
3044 m_reset_inputs = true;
3045
3046 // Do some maintenance on the programid where the write head was previously.
3047 // (1) Adjust the number of heads on it; this could make it executable!
3048 // (2) \todo Delete if it has no instructions or is below a certain size?
3049 int write_head_contacted = GetHead(nHardware::HEAD_WRITE).GetMemSpace();
3050 m_programids[write_head_contacted]->RemoveContactingHead(GetHead(nHardware::HEAD_WRITE));
3051 GetHead(nHardware::HEAD_WRITE).Set(0, m_current->m_id); // Immediately set the write head back to itself
3052
3053 // If we've reached a programid limit, then deal with that
3054 if ( (int)m_programids.size() >= m_world->GetConfig().MAX_PROGRAMIDS.Get() ) {
3055
3056 //Decide on a programid to destroy, currently highest number of cpu cycles executed
3057 //\todo more methods of choosing..
3058
3059 if (PROGRAMID_REPLACEMENT_METHOD == 0) {
3060 // Don't replace (they can still die of old age!)
3061 return false;
3062 } else if(PROGRAMID_REPLACEMENT_METHOD == 1) {
3063 // Replace oldest programid
3064 int destroy_index = -1;
3065 int max_cpu_cycles_used = -1;
3066 for (unsigned int i=0; i<m_programids.size(); i++) {
3067 if (m_programids[i]->GetCPUCyclesUsed() > max_cpu_cycles_used) {
3068 max_cpu_cycles_used = m_programids[i]->GetCPUCyclesUsed();
3069 destroy_index = i;
3070 }
3071 }
3072 assert(destroy_index>=0);
3073 RemoveProgramid(destroy_index);
3074
3075 // Also replace anything that its WRITE head contacted (unless itself)...
3076 // to prevent accumulating partially copied programids
3077 if(write_head_contacted != destroy_index) {
3078 RemoveProgramid(write_head_contacted);
3079 }
3080 }
3081 }
3082
3083 // Create the new programid and add it to the list
3084 Sequence new_genome(1);
3085 programid_ptr new_programid = new cProgramid(new_genome, this);
3086 new_programid->m_executable = executable;
3087 new_programid->m_bindable = bindable;
3088 new_programid->m_readable = readable;
3089 AddProgramid(new_programid);
3090
3091 // Set the write head to the newly allocated programid
3092 new_programid->AddContactingHead(GetHead(nHardware::HEAD_WRITE));
3093 GetHead(nHardware::HEAD_WRITE).Set(0, new_programid->GetID());
3094
3095 return true;
3096 }
3097
Inst_Site(cAvidaContext & ctx)3098 bool cHardwareGX::Inst_Site(cAvidaContext& ctx)
3099 {
3100 // Do nothing except move past the label
3101 ReadLabel();
3102 return true;
3103 }
3104
3105 /*! This instruction reads the trailing label and tries to match it against other
3106 programids in the CPU. Note: labels that follow a match instruction in the
3107 programid to be matched against are *ignored*. Note that match should be a
3108 'free' instruction, that is, it should not cost anything to execute.
3109
3110 \todo Determine if Inst_Match should wait for a match, as opposed to letting the
3111 cProgramid continue. This seems like a good idea...
3112
3113 \todo Have to determine the correct placement of the heads on Bind. If we're not
3114 careful, we'll allow two RNAPs to bind to each other and start copying each other's
3115 genome. Probably a bad idea. @JEB Well, at least those selfish solutions would die.
3116 Temp Soln @JEB -- added a flag to programids so that only certain programids are bindable
3117 */
Inst_Bind(cAvidaContext & ctx)3118 bool cHardwareGX::Inst_Bind(cAvidaContext& ctx)
3119 {
3120 m_reset_inputs = true;
3121
3122 // Get the label that we're trying to match.
3123 // Do this first to advance IP past it.
3124 ReadLabel();
3125
3126 // Binding fails if we are already bound!
3127 cHeadProgramid& read = GetHead(nHardware::HEAD_READ);
3128 if(read.GetMemSpace() != m_current->GetID()) return false;
3129 cHeadProgramid& write = GetHead(nHardware::HEAD_WRITE);
3130
3131 // Now, select another programid to match against.
3132 // Go through all *other* programids looking for matches
3133 std::vector<cMatchSite> all_matches;
3134 for(programid_list::iterator i=m_programids.begin(); i!=m_programids.end(); ++i) {
3135 // Don't bind to ourself, or to whatever programid our write head is attached to.
3136 if((*i != m_current) && ((*i)->GetID() != write.GetMemSpace())) {
3137 std::vector<cMatchSite> matches = (*i)->Sites(GetLabel());
3138 all_matches.insert(all_matches.end(), matches.begin(), matches.end());
3139 }
3140 }
3141
3142 // The instruction failed if there were no matches
3143 if(all_matches.size() == 0) return false;
3144
3145 // Otherwise set the read head to a random match
3146 unsigned int c = ctx.GetRandom().GetUInt(all_matches.size());
3147
3148 // Ok, it matched. Bind the current programid's read head to the matched site.
3149 m_current->Bind(nHardware::HEAD_READ, all_matches[c]);
3150
3151 // And we're done.
3152 return true;
3153 }
3154
3155
3156 /*! This instruction attempts to locate two programids that have the same site.
3157 If two such programids are found, BX is set to 2, otherwise BX is set to 0.
3158
3159 This instruction is well-suited for finding two genomes. For example, the following
3160 instruction sequence may be used to locate two genomes with an origin of replication,
3161 and if found, trigger a cell division:
3162 bind2
3163 nop-B
3164 nop-C
3165 if-not-0
3166 p-divide
3167 */
Inst_Bind2(cAvidaContext & ctx)3168 bool cHardwareGX::Inst_Bind2(cAvidaContext& ctx)
3169 {
3170 m_reset_inputs = true;
3171
3172 // Get the label we're searching for.
3173 ReadLabel();
3174
3175 // Search for matches to this label.
3176 std::vector<cMatchSite> bindable;
3177 for(programid_list::iterator i=m_programids.begin(); i!=m_programids.end(); ++i) {
3178 if(*i != m_current) {
3179 std::vector<cMatchSite> matches = (*i)->Sites(GetLabel());
3180 // Now, we only want one match from each programid; we'll take a random one.
3181 if(matches.size()>0) {
3182 bindable.push_back(matches[ctx.GetRandom().GetInt(matches.size())]);
3183 }
3184 }
3185 }
3186
3187 // Select two of the matches at random.
3188 if(bindable.size()>=2) {
3189 int first = ctx.GetRandom().GetInt(bindable.size());
3190 int second = ctx.GetRandom().GetInt(bindable.size());
3191 while(first == second) { second = ctx.GetRandom().GetUInt(bindable.size()); }
3192 assert(bindable[first].m_programid->GetID() != bindable[second].m_programid->GetID());
3193 assert(bindable[first].m_programid->GetBindable());
3194 assert(bindable[second].m_programid->GetBindable());
3195 assert(bindable[first].m_programid->GetReadable());
3196 assert(bindable[second].m_programid->GetReadable());
3197
3198 // If the caller is already bound to other programids, detach.
3199 m_current->Detach();
3200
3201 // And attach this programid's read and write heads to the indexed organisms.
3202 // It *is* possible that the caller could do "bad things" now.
3203 m_current->Bind(nHardware::HEAD_READ, bindable[first]);
3204 m_current->Bind(nHardware::HEAD_WRITE, bindable[second]);
3205
3206 // Finally, set BX to indicate that bind2 worked, and return.
3207 GetRegister(REG_BX) = 2;
3208 return true;
3209 }
3210
3211 // Bind2 didn't work.
3212 GetRegister(REG_BX) = 0;
3213 return false;
3214 }
3215
3216
Inst_IfBind(cAvidaContext & ctx)3217 bool cHardwareGX::Inst_IfBind(cAvidaContext& ctx)
3218 {
3219 // Normal Bind
3220 bool ret = Inst_Bind(ctx);
3221
3222 //Skip the next instruction if binding was not successful
3223 if (!ret) IP().Advance();
3224
3225 return ret;
3226 }
3227
3228
Inst_IfBind2(cAvidaContext & ctx)3229 bool cHardwareGX::Inst_IfBind2(cAvidaContext& ctx)
3230 {
3231 // Normal Bind2
3232 bool ret = Inst_Bind2(ctx);
3233
3234 //Skip the next instruction if binding was not successful
3235 if (!ret) IP().Advance();
3236
3237 return ret;
3238 }
3239
3240
3241 /*! This instruction puts the total number of binding sites found in BX
3242 Currently it is used to keep track of whether genome division has completed.*/
Inst_NumSites(cAvidaContext & ctx)3243 bool cHardwareGX::Inst_NumSites(cAvidaContext& ctx)
3244 {
3245 // Get the label that we're trying to match.
3246 ReadLabel();
3247
3248 // Go through all *other* programids counting matches
3249 int num_sites = 0;
3250 for(programid_list::iterator i=m_programids.begin(); i!=m_programids.end(); ++i) {
3251 if (*i != m_current) {
3252 num_sites += (*i)->Sites(GetLabel()).size();
3253 }
3254 }
3255
3256 GetRegister(REG_BX) = num_sites;
3257 return true;
3258 }
3259
3260
3261 /*! This instruction is like h-copy, except:
3262 (1) It does nothing if the read and write head are not on OTHER programids
3263 (2) It dissociates the read head if it encounters the complement of the label that was used in the
3264 match instruction that put the read head on its current target
3265 */
Inst_ProgramidCopy(cAvidaContext & ctx)3266 bool cHardwareGX::Inst_ProgramidCopy(cAvidaContext& ctx)
3267 {
3268 m_reset_inputs = true;
3269
3270 cHeadProgramid& write = GetHead(nHardware::HEAD_WRITE);
3271 cHeadProgramid& read = GetHead(nHardware::HEAD_READ);
3272 read.Adjust(); // Strange things can happen (like we're reading from a programid that was being written).
3273 write.Adjust(); // Always adjust if the memory spaces themselves are accessed.
3274
3275 // Don't copy if this programid's write or read head is on itself
3276 if(read.GetMemSpace() == m_current->GetID()) return false;
3277 if(write.GetMemSpace() == m_current->GetID()) return false;
3278
3279 // Don't copy if the source is not readable
3280 if(!m_programids[read.GetMemSpace()]->GetReadable()) return false;
3281
3282 // If a copy is mutated in after a bind2, then the read head could be at the
3283 // end of a genome. The copy fails, and we should probably break the bind, too.
3284 if(read.GetPosition() >= read.GetMemory().GetSize()) {
3285 m_current->Detach();
3286 return false;
3287 }
3288
3289 // Keep track of whether the last non-NOP we copied was a site
3290 if(GetInstSet().IsNop(read.GetInst())) {
3291 m_current->m_copying_label.AddNop(GetInstSet().GetNopMod(read.GetInst()));
3292 } else {
3293 m_current->m_copying_site = (read.GetInst() == GetInstSet().GetInst("site"));
3294 m_current->m_copying_label.Clear();
3295 }
3296
3297 // Allocate space for one additional instruction if the write head is at the end
3298 if(write.GetMemory().GetSize() == write.GetPosition() + 1) {
3299 write.GetMemory().Resize(write.GetMemory().GetSize() + 1);
3300 }
3301
3302 // \todo The timing of deletion, change, insertion mutation checks matters
3303 // I'm not sure this is right @JEB
3304
3305 bool ret = true;
3306 // Normal h-copy, unless a deletion occured
3307 if (!m_organism->TestDivideDel(ctx)) {
3308 // Normal h-copy
3309 ret = Inst_HeadCopy(ctx);
3310 }
3311
3312 // Divide Insertion
3313 if (m_organism->TestDivideIns(ctx)) {
3314 write.GetMemory().Insert(write.GetPosition(), GetInstSet().GetRandomInst(ctx));
3315 // Advance the write head;
3316 write++;
3317 }
3318
3319 // "Jump" Mutations
3320
3321 if ( ctx.GetRandom().P(0.0000) )
3322 {
3323 // Set the read head to a random position
3324 int new_read_pos = ctx.GetRandom().GetInt(read.GetMemory().GetSize());
3325 read.Set(new_read_pos, read.GetMemSpace());
3326
3327 // Reset any start/end labels that we might have been copying.
3328 m_current->m_copying_site = false;
3329 m_current->m_copying_label.Clear();
3330 }
3331
3332
3333 // Peek at the next inst to see if it is a NOP
3334 // If it isn't, then we can compare the label and possibly fall off
3335 if(m_current->m_copying_site) {
3336 if((!GetInstSet().IsNop(read.GetInst()) && (m_current->m_terminator_label == m_current->m_copying_label))) {
3337 // Move read head off of old target and back to itself
3338 read.GetProgramid()->RemoveContactingHead(read);
3339 read.Set(0, m_current->m_id);
3340
3341 //Shrink the programid we were on by one inst
3342 if (write.GetMemory().GetSize()>1)
3343 {
3344 write.GetMemory().Resize( write.GetMemory().GetSize() - 1 );
3345 }
3346
3347 // \to do, we would apply insertion/deletion on divide instructions here
3348 // if we want them to happen for each new programid that is produced
3349 // // Handle Divide Mutations...
3350
3351 // This would be equivalent to
3352 // Divide_DoMutations(ctx, mut_multiplier);
3353 // But that operates on m_child_genome, currently
3354
3355 return true;
3356 }
3357 }
3358
3359 // Check our processivity - read and write heads fall off with some probability
3360 // What kind of programid are we writing?
3361 double terminate_p = 1 - ((m_programids[read.GetMemSpace()]->GetReadable()) ? READABLE_COPY_PROCESSIVITY : EXECUTABLE_COPY_PROCESSIVITY);
3362 if ( ctx.GetRandom().P(terminate_p) ) m_reset_heads = true;
3363
3364 return ret;
3365 }
3366
3367 /*! This instruction creates a new organism by randomly dividing the programids with
3368 the daughter cell. Currently we convert the daughter genomes back into one list with pseudo-instructions
3369 telling us where new programids start and some of their properties. This is pretty inefficient.
3370 */
Inst_ProgramidDivide(cAvidaContext & ctx)3371 bool cHardwareGX::Inst_ProgramidDivide(cAvidaContext& ctx)
3372 {
3373 // Even if unsuccessful, we reset task inputs
3374 m_reset_inputs = true;
3375
3376 //This stuff is usually set by Divide_CheckViable, leaving it zero causes problems
3377 m_organism->GetPhenotype().SetLinesExecuted(1);
3378 m_organism->GetPhenotype().SetLinesCopied(1);
3379
3380 // Let's make sure that things seem sane.
3381 cHeadProgramid& read = GetHead(nHardware::HEAD_READ); // The parent.
3382 cHeadProgramid& write = GetHead(nHardware::HEAD_WRITE); // The offspring.
3383
3384 // If either of these heads are on m_current, this instruction fails.
3385 if(read.GetMemSpace() == m_current->GetID()) return false;
3386 if(write.GetMemSpace() == m_current->GetID()) return false;
3387
3388 // It should never be the case that the read and write heads are on the same programid.
3389 assert(read.GetMemSpace() != write.GetMemSpace());
3390 // Actually, it can happen with bind2 @JEB
3391
3392 // If the read and write heads are on the same programid, then fail
3393 if (read.GetMemSpace() == write.GetMemSpace()) return false;
3394
3395 // If we're not bound to two bindable programids, this instruction fails.
3396 if(!m_programids[read.GetMemSpace()]->GetBindable()) return false;
3397 if(!m_programids[write.GetMemSpace()]->GetBindable()) return false;
3398
3399 // Now, let's keep track of two different lists of programids, one for the parent,
3400 // and one for the offspring.
3401 programid_list parent;
3402 programid_list offspring;
3403 // We're also going to do all our work on a temporary list, so that we can fail
3404 // without affecting the state of the caller.
3405 programid_list all(m_programids);
3406 // This is a list of fragments, to be deleted once we've passed the viability check.
3407 programid_list fragments;
3408
3409 // The currently executing programid called the divide instruction. The caller
3410 // is (hopefully) a DNA polymerase, therefore it knows which genome fragments go where.
3411 parent.push_back(m_programids[read.GetMemSpace()]); // The parent's genome.
3412 all[read.GetMemSpace()] = 0;
3413 offspring.push_back(m_programids[write.GetMemSpace()]); // The offspring's genome.
3414 //offspring.back()->SetBindable(true);
3415 all[write.GetMemSpace()] = 0;
3416
3417 // Locate and remove all incomplete genomes, identified by programids that have
3418 // write heads on them.
3419 for(programid_list::iterator i=all.begin(); i!=all.end(); ++i) {
3420 // Does this programid currently have it's write head somewhere?
3421 if((*i != 0) && ((*i)->GetHead(nHardware::HEAD_WRITE).GetMemSpace() != (*i)->GetID())) {
3422 // Yes - It is likely an incomplete genome fragment, so don't
3423 // allow it to propagate.
3424 fragments.push_back(all[(*i)->GetHead(nHardware::HEAD_WRITE).GetMemSpace()]);
3425 all[(*i)->GetHead(nHardware::HEAD_WRITE).GetMemSpace()] = 0;
3426 }
3427 }
3428
3429 // Divvy up the programids.
3430 for(programid_list::iterator i=all.begin(); i!=all.end(); ++i) {
3431 if(*i != 0) {
3432 if(ctx.GetRandom().GetUInt(2) == 0) {
3433 // Offspring!
3434 offspring.push_back(*i);
3435 } else {
3436 // Parent!
3437 parent.push_back(*i);
3438 }
3439 }
3440 }
3441
3442 ///// Failure conditions (custom divide_check_viable)
3443 // It is possible that the divide kills the child and the parent
3444 // Each must have genomic programids of some minimum length
3445 // and an executable programid (otherwise it is inviable)
3446 // For now we leave a zombie mother to die of old age
3447 // but do not permit creating an inviable daughter
3448
3449 // Conditions for successful replication
3450 int num_daughter_programids = 0;
3451 int daughter_genome_length = 0;
3452 bool daughter_has_executable = false;
3453 bool daughter_has_bindable = false;
3454
3455 // Calculate these conditions for offspring.
3456 for(programid_list::iterator i=offspring.begin(); i!=offspring.end(); ++i) {
3457 ++num_daughter_programids;
3458 if((*i)->GetReadable()) {
3459 daughter_genome_length += (*i)->GetMemory().GetSize();
3460 }
3461 daughter_has_executable = daughter_has_executable || (*i)->GetExecutable();
3462 daughter_has_bindable = daughter_has_bindable || (*i)->GetBindable();
3463 }
3464 assert(daughter_has_bindable); // We know this should be there...
3465
3466 int num_mother_programids = 0;
3467 int mother_genome_length = 0;
3468 bool mother_has_executable = false;
3469 bool mother_has_bindable = false;
3470
3471 // Calculate these conditions for parent.
3472 for(programid_list::iterator i=parent.begin(); i!=parent.end(); ++i) {
3473 ++num_mother_programids;
3474 if((*i)->GetReadable()) {
3475 mother_genome_length += (*i)->GetMemory().GetSize();
3476 }
3477 mother_has_executable = mother_has_executable || (*i)->GetExecutable();
3478 mother_has_bindable = mother_has_bindable || (*i)->GetBindable();
3479 }
3480 assert(mother_has_bindable); // We know this should be there...
3481
3482 // And check them. Note that if this check fails, we have *not* modified the
3483 // state of the calling programid.
3484 if((num_daughter_programids == 0)
3485 || (!daughter_has_executable)
3486 || (daughter_genome_length < 50)) {
3487 // \todo link to original genome length
3488 return false;
3489 }
3490 if((num_mother_programids == 0)
3491 || (!mother_has_executable)
3492 || (mother_genome_length < 50)) {
3493 // \todo link to original genome length
3494 return false;
3495 }
3496
3497 // Ok, we're good to go. We have to create the offspring's genome and delete the
3498 // offspring's programids from m_programids.
3499 Sequence& child_genome = m_organism->OffspringGenome().GetSequence();
3500 child_genome.Resize(1);
3501 m_organism->OffspringGenome().SetHardwareType(GetType());
3502 m_organism->OffspringGenome().SetInstSet(m_inst_set->GetInstSetName());
3503 if (m_world->GetVerbosity() >= VERBOSE_DETAILS) std::cout << "-=OFFSPRING=-" << endl;
3504 for(programid_list::iterator i=offspring.begin(); i!=offspring.end(); ++i) {
3505 (*i)->AppendLinearGenome(child_genome);
3506 if (m_world->GetVerbosity() >= VERBOSE_DETAILS) (*i)->PrintGenome(std::cout);
3507 delete *i;
3508 *i = 0;
3509 }
3510
3511 // Now clean up the parent.
3512 m_programids.clear();
3513 if (m_world->GetVerbosity() >= VERBOSE_DETAILS) std::cout << "-=PARENT=-" << endl;
3514 for(programid_list::iterator i=parent.begin(); i!=parent.end(); ++i) {
3515 AddProgramid(*i);
3516 if (m_world->GetVerbosity() >= VERBOSE_DETAILS) (*i)->PrintGenome(std::cout);
3517 }
3518
3519 // And delete the fragments.
3520 for(programid_list::iterator i=fragments.begin(); i!=fragments.end(); ++i) {
3521 delete *i;
3522 }
3523
3524 // Activate the child
3525 m_organism->ActivateDivide(ctx);
3526
3527 // Mother viability checks could go here.
3528 m_just_divided = true;
3529 return true;
3530 }
3531
3532 /* Implicit RNAP Model
3533 Allocate a new genome programid and place the write head on it.
3534 */
Inst_ProgramidImplicitAllocate(cAvidaContext & ctx)3535 bool cHardwareGX::Inst_ProgramidImplicitAllocate(cAvidaContext& ctx)
3536 {
3537 const int dst = REG_BX;
3538 const int cur_size = m_programids[0]->GetMemory().GetSize();
3539 const int old_size = cur_size;
3540 const int allocated_size = Min((int) (m_world->GetConfig().OFFSPRING_SIZE_RANGE.Get() * cur_size), MAX_GENOME_LENGTH);
3541
3542 // Modified Allocate_Main()
3543 // must do divide before second allocate & must allocate positive amount...
3544 if (m_world->GetConfig().REQUIRE_ALLOCATE.Get() && m_mal_active == true) {
3545 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR, "Allocate already active");
3546 return false;
3547 }
3548 if (allocated_size < 1) {
3549 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
3550 cStringUtil::Stringf("Allocate of %d too small", allocated_size));
3551 return false;
3552 }
3553
3554
3555 const int max_alloc_size = (int) (old_size * m_world->GetConfig().OFFSPRING_SIZE_RANGE.Get());
3556 if (allocated_size > max_alloc_size) {
3557 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
3558 cStringUtil::Stringf("Allocate too large (%d > %d)",
3559 allocated_size, max_alloc_size));
3560 return false;
3561 }
3562
3563 const int max_old_size =
3564 (int) (allocated_size * m_world->GetConfig().OFFSPRING_SIZE_RANGE.Get());
3565 if (old_size > max_old_size) {
3566 m_organism->Fault(FAULT_LOC_ALLOC, FAULT_TYPE_ERROR,
3567 cStringUtil::Stringf("Allocate too small (%d > %d)",
3568 old_size, max_old_size));
3569 return false;
3570 }
3571
3572 Sequence new_genome(allocated_size);
3573 programid_ptr new_programid = new cProgramid(new_genome, this);
3574 new_programid->SetBindable(true);
3575 new_programid->SetReadable(true);
3576 AddProgramid(new_programid);
3577
3578 /* switch (m_world->GetConfig().ALLOC_METHOD.Get()) {
3579 case ALLOC_METHOD_NECRO:
3580 // Only break if this succeeds -- otherwise just do random.
3581 if (Allocate_Necro(new_size) == true) break;
3582 case ALLOC_METHOD_RANDOM:
3583 Allocate_Random(ctx, old_size, new_size);
3584 break;
3585 case ALLOC_METHOD_DEFAULT:
3586 Allocate_Default(new_size);
3587 break;
3588 } */
3589 m_mal_active = true;
3590 //--> End modified Main_Alloc
3591
3592 // Set the write head to the newly allocated programid
3593 GetHead(nHardware::HEAD_WRITE).Set(0, new_programid->GetID());
3594 // Set the read head to the first programid (=current genome)
3595 GetHead(nHardware::HEAD_READ).Set(0, 0);
3596 GetRegister(dst) = cur_size;
3597
3598 return true;
3599 }
3600
3601 /* Implicit RNAP Model
3602 Create a new organism with a genome based on the current position
3603 of th write head.
3604 */
Inst_ProgramidImplicitDivide(cAvidaContext & ctx)3605 bool cHardwareGX::Inst_ProgramidImplicitDivide(cAvidaContext& ctx)
3606 {
3607 cHeadProgramid write_head = GetHead(nHardware::HEAD_WRITE);
3608 int child_end = GetHead(nHardware::HEAD_WRITE).GetPosition();
3609
3610 // Make sure this divide will produce a viable offspring.
3611 const bool viable = Divide_CheckViable(ctx, m_organism->GetGenome().GetSequence().GetSize(), child_end);
3612 if (viable == false) return false;
3613
3614 // Since the divide will now succeed, set up the information to be sent
3615 // to the new organism
3616 Sequence& child_genome = m_organism->OffspringGenome().GetSequence();
3617 child_genome = m_programids[write_head.GetMemSpace()]->GetMemory();
3618 child_genome = child_genome.Crop(0, child_end);
3619 m_organism->OffspringGenome().SetHardwareType(GetType());
3620 m_organism->OffspringGenome().SetInstSet(m_inst_set->GetInstSetName());
3621
3622
3623 // Handle Divide Mutations...
3624 Divide_DoMutations(ctx);
3625
3626 // Many tests will require us to run the offspring through a test CPU;
3627 // this is, for example, to see if mutations need to be reverted or if
3628 // lineages need to be updated.
3629 Divide_TestFitnessMeasures(ctx);
3630
3631 // reset first time instruction costs
3632 for (int i = 0; i < m_inst_ft_cost.GetSize(); i++) {
3633 m_inst_ft_cost[i] = m_inst_set->GetFTCost(cInstruction(i));
3634 }
3635
3636 m_mal_active = false;
3637 if (m_world->GetConfig().DIVIDE_METHOD.Get() == DIVIDE_METHOD_SPLIT) {
3638 m_advance_ip = false;
3639 }
3640
3641 // Activate the child
3642 bool parent_alive = m_organism->ActivateDivide(ctx);
3643
3644 // Do more work if the parent lives through the birth of the offspring
3645 if (parent_alive) {
3646 if (m_world->GetConfig().DIVIDE_METHOD.Get() == DIVIDE_METHOD_SPLIT) Reset(ctx);
3647 }
3648
3649 m_just_divided = true;
3650 return true;
3651 }
3652
Inst_EndProgramidExecution(cAvidaContext & ctx)3653 bool cHardwareGX::Inst_EndProgramidExecution(cAvidaContext& ctx)
3654 {
3655 m_current->m_marked_for_death = true; //Mark us for death
3656 return true;
3657 }
3658
3659 /* Full Implict Model*/
3660
Inst_Promoter(cAvidaContext & ctx)3661 bool cHardwareGX::Inst_Promoter(cAvidaContext& ctx)
3662 {
3663 // Promoters don't do anything themselves
3664 return true;
3665 }
3666
Inst_Terminator(cAvidaContext & ctx)3667 bool cHardwareGX::Inst_Terminator(cAvidaContext& ctx)
3668 {
3669 // Terminators don't do anything themselves
3670 return true;
3671 }
3672
3673 /*
3674 Place the write head of this programid on the next available match
3675 in the genome. Upgrade to promoter rate at the final position of the label.
3676 */
Inst_HeadActivate(cAvidaContext & ctx)3677 bool cHardwareGX::Inst_HeadActivate(cAvidaContext& ctx)
3678 {
3679 // Remove current regulation first
3680 m_current->RemoveRegulation();
3681
3682 // Find next best match.
3683 ReadLabel();
3684 GetLabel().Rotate(1, NUM_NOPS);
3685
3686 int match_pos = FindRegulatoryMatch( GetLabel() );
3687 if (match_pos == -1) return false;
3688
3689 GetHead(nHardware::HEAD_WRITE).Set(match_pos, 0); // Used to track regulation
3690 GetHead(nHardware::HEAD_FLOW).Set(match_pos+GetLabel().GetSize(), 0); // Used to track regulation
3691
3692 int regulatory_footprint = 5; // Positions ahead and behind label that it covers
3693 match_pos = (match_pos - regulatory_footprint + m_programids[0]->GetMemory().GetSize() ) % m_programids[0]->GetMemory().GetSize();
3694
3695 cHeadProgramid h(this);
3696 h.Set(match_pos, 0);
3697 for (int i=0; i < 2*regulatory_footprint + GetLabel().GetSize(); i++)
3698 {
3699 m_promoter_occupied_sites[h.GetPosition()] = -1; // Repress overlap
3700 // Except where we create a new promoter that starts expression right after the matched label
3701 if (i == regulatory_footprint + GetLabel().GetSize() - 1)
3702 {
3703 m_promoter_occupied_sites[h.GetPosition()] = +1;
3704 }
3705
3706 h++;
3707 }
3708
3709 AdjustPromoterRates();
3710
3711 if (m_world->GetVerbosity() >= VERBOSE_DETAILS)
3712 {
3713 cout << "Activate: position " << match_pos << " length " << 2*regulatory_footprint + GetLabel().GetSize() << endl;
3714 for (int i=0; i < m_programids[0]->GetMemory().GetSize(); i++)
3715 {
3716 cout << i << " " << m_promoter_rates[i] << " " << m_promoter_default_rates[i] << " " << m_promoter_occupied_sites[i] << endl;
3717 }
3718 }
3719 return true;
3720 }
3721
3722 /*
3723 Place the write head of this programid on the next available match
3724 in the genome. Downgrade promoter rates to background for 5 instructions on each side.
3725 */
Inst_HeadRepress(cAvidaContext & ctx)3726 bool cHardwareGX::Inst_HeadRepress(cAvidaContext& ctx)
3727 {
3728 // Remove current regulation first
3729 m_current->RemoveRegulation();
3730
3731 // Find next best match.
3732 ReadLabel();
3733 GetLabel().Rotate(1, NUM_NOPS);
3734
3735 int match_pos = FindRegulatoryMatch( GetLabel() );
3736 if (match_pos == -1) return false;
3737
3738 GetHead(nHardware::HEAD_WRITE).Set(match_pos, 0); // Used to track regulation
3739 GetHead(nHardware::HEAD_FLOW).Set(match_pos+GetLabel().GetSize(), 0); // Used to track regulation
3740
3741 int regulatory_footprint = 5; // Positions ahead and behind label that it covers
3742 match_pos = (match_pos - regulatory_footprint + m_programids[0]->GetMemory().GetSize() ) % m_programids[0]->GetMemory().GetSize();
3743
3744 cHeadProgramid h(this);
3745 h.Set(match_pos, 0);
3746 for (int i=0; i < 2*regulatory_footprint + GetLabel().GetSize(); i++)
3747 {
3748 m_promoter_occupied_sites[h.GetPosition()] = -1;
3749 h++;
3750 }
3751
3752 AdjustPromoterRates();
3753
3754 if (m_world->GetVerbosity() >= VERBOSE_DETAILS)
3755 {
3756 cout << "Repress: position " << match_pos << " length " << 2*regulatory_footprint + GetLabel().GetSize() << endl;
3757 for (int i=0; i < m_programids[0]->GetMemory().GetSize(); i++)
3758 {
3759 cout << i << " " << m_promoter_rates[i] << " " <<m_promoter_default_rates[i] << " " << m_promoter_occupied_sites[i] << endl;
3760 }
3761 }
3762
3763 return true;
3764 }
3765
3766 //! Adds a new programid to the current cHardwareGX.
AddProgramid(programid_ptr programid)3767 void cHardwareGX::AddProgramid(programid_ptr programid)
3768 {
3769 programid->m_id = m_programids.size();
3770 programid->ResetHeads();
3771 // programid->ResetCPUCyclesUsed();
3772 programid->m_contacting_heads = 0;
3773 m_programids.push_back(programid);
3774 }
3775
3776
RemoveProgramid(unsigned int remove_index)3777 void cHardwareGX::RemoveProgramid(unsigned int remove_index)
3778 {
3779 assert(remove_index<m_programids.size());
3780
3781 programid_ptr save=m_current;
3782 m_current = m_programids[remove_index];
3783
3784 // Remove regulation if in implicit GX mode
3785 if (m_world->GetConfig().IMPLICIT_GENE_EXPRESSION.Get() == 1) m_current->RemoveRegulation();
3786
3787 if (m_world->GetVerbosity() >= VERBOSE_DETAILS)
3788 {
3789 cout << "Programid removed. Start position = " << m_current->GetHead(nHardware::HEAD_READ).GetPosition();
3790 cout << " length = " << m_current->m_memory.GetSize() << endl;
3791 }
3792
3793 unsigned int write_head_contacted = (unsigned int)GetHead(nHardware::HEAD_WRITE).GetMemSpace();
3794
3795 // First update the contacting head count for any cProgramids the heads
3796 // of the programid to be removed might have been on
3797
3798 // The contacting head count is not used in the implicit model (and will be incorrect)
3799
3800 if (!m_world->GetConfig().IMPLICIT_GENE_EXPRESSION.Get()) m_current->Detach();
3801
3802
3803 // m_programids[GetHead(nHardware::HEAD_READ).GetMemSpace()]->RemoveContactingHead(GetHead(nHardware::HEAD_READ));
3804 // m_programids[GetHead(nHardware::HEAD_WRITE).GetMemSpace()]->RemoveContactingHead(GetHead(nHardware::HEAD_WRITE));
3805
3806 // Update the programid list
3807 delete *(m_programids.begin()+remove_index);
3808 m_programids.erase(m_programids.begin()+remove_index);
3809 // Add adjust all the programid ids from the removed programid to the end.
3810 for(programid_list::iterator i=m_programids.begin()+remove_index; i!=m_programids.end(); ++i) {
3811 --(*i)->m_id;
3812 }
3813
3814 // We also need to make sure heads are on the correct memory
3815 // spaces, since that indexing changes with the programid list
3816 for(unsigned int i=0; i< m_programids.size(); i++) {
3817 programid_ptr p = m_programids[i];
3818 for(int j=0; j<NUM_HEADS; j++) {
3819 // We removed the thing they were writing from or reading to
3820 // For now, just put the write head back on themselves (inactivating further copies)
3821 // Might want to also reset the read head..
3822 if (p->m_heads[j].GetMemSpace() == (int)remove_index) {
3823 p->m_heads[j].Set(0, p->m_id);
3824 } else if (p->m_heads[j].GetMemSpace() > (int)remove_index) {
3825 p->m_heads[j].Set(p->m_heads[j].GetPosition(), p->m_heads[j].GetMemSpace() - 1);
3826 }
3827 }
3828 }
3829
3830 // Finally, also delete whatever programid our write head contacted (if not ourself!)
3831 // DON'T DO THIS in implicit mode, sine the write head means something different (regulation)
3832 if (m_world->GetConfig().IMPLICIT_GENE_EXPRESSION.Get() == 0)
3833 {
3834 if(write_head_contacted != remove_index) {
3835 RemoveProgramid( (write_head_contacted > remove_index) ? write_head_contacted - 1 : write_head_contacted);
3836 }
3837 }
3838 m_current = save;
3839 }
3840
3841
ProcessImplicitGeneExpression(int in_limit)3842 void cHardwareGX::ProcessImplicitGeneExpression(int in_limit)
3843 {
3844 // If organism has no active promoters, catch us before we enter an infinite loop...
3845 if (m_promoter_sum == 0.0) return;
3846
3847 static cInstruction terminator_inst = GetInstSet().GetInst(cStringUtil::Stringf("terminator"));
3848
3849 if (in_limit == -1 ) in_limit = m_world->GetConfig().MAX_PROGRAMIDS.Get();
3850 if (in_limit > m_world->GetConfig().MAX_PROGRAMIDS.Get()) in_limit = m_world->GetConfig().MAX_PROGRAMIDS.Get();
3851
3852 // Create executable programids up to the limit
3853 //const int genome_size = m_programids[m_promoter_update_head.GetMemSpace()]->GetMemory().GetSize();
3854 //const int inc = Min(genome_size, m_world->GetConfig().IMPLICIT_MAX_PROGRAMID_LENGTH.Get());
3855
3856 while ( m_programids.size() < (unsigned int)in_limit )
3857 {
3858 // Update promoter states according to rates until one fires
3859
3860 do {
3861 // This way goes straight through the genome
3862 m_promoter_update_head++;
3863
3864 // This way goes interspersed through the genome -- Add as a config option @JEB
3865 /*
3866 int new_pos = m_promoter_update_head.GetPosition();
3867 new_pos += inc;
3868 if ( new_pos >= genome_size )
3869 {
3870 new_pos++;
3871 new_pos %= inc;
3872 }
3873 */
3874
3875 m_promoter_states[m_promoter_update_head.GetPosition()] += m_promoter_rates[m_promoter_update_head.GetPosition()];
3876 if ( (m_world->GetVerbosity() >= VERBOSE_DETAILS) && (m_promoter_states[m_promoter_update_head.GetPosition()] > 0) )
3877 {
3878 cout << "Promoter position " << m_promoter_update_head.GetPosition() << " value " << m_promoter_states[m_promoter_update_head.GetPosition()] << endl;
3879 }
3880 } while (m_promoter_states[m_promoter_update_head.GetPosition()] < 1.0);
3881
3882 m_promoter_states[m_promoter_update_head.GetPosition()] -= 1.0;
3883
3884 // Create new programid
3885 Sequence new_genome(m_world->GetConfig().IMPLICIT_MAX_PROGRAMID_LENGTH.Get());
3886 programid_ptr new_programid = new cProgramid(new_genome, this);
3887 new_programid->SetExecutable(true);
3888 AddProgramid(new_programid);
3889
3890 cHeadProgramid read_head(m_promoter_update_head);
3891 read_head++; //Don't copy the promoter instruction itself (we start after that position)
3892 cHeadProgramid write_head(this, 0, new_programid->GetID());
3893 int copied = 0;
3894 cInstruction inst;
3895 do {
3896 inst = read_head.GetInst();
3897 if (inst == terminator_inst) break; // Early termination
3898 write_head.SetInst(inst);
3899 write_head++;
3900 read_head++;
3901 copied++;
3902 } while (write_head.GetPosition() != 0);
3903
3904 // Set the read head of this programid to where it began so that we can keep track in trace files.
3905 new_programid->GetHead(nHardware::HEAD_READ).Set(m_promoter_update_head.GetPosition(),0);
3906 // Adjust length downward if prematurely terminated...
3907 if (inst == terminator_inst)
3908 {
3909 new_programid->m_memory.ResizeOld( Max(1, copied) );
3910 // If the length was zero, then we are left with a single NOP.
3911 }
3912
3913 if (m_world->GetVerbosity() >= VERBOSE_DETAILS)
3914 {
3915 cout << "New programid created. Start position = " << new_programid->GetHead(nHardware::HEAD_READ).GetPosition();
3916 cout << " length = " << new_programid->m_memory.GetSize() << endl;
3917 }
3918
3919 m_promoter_update_head++; //move on to the next position
3920 }
3921 }
3922
3923
AdjustPromoterRates()3924 void cHardwareGX::AdjustPromoterRates()
3925 {
3926 cHeadProgramid h(this);
3927 h.Set(0,0);
3928
3929 m_promoter_sum = 0.0;
3930
3931 do {
3932 switch (m_promoter_occupied_sites[h.GetPosition()])
3933 {
3934 case +1: // Activated
3935 m_promoter_rates[h.GetPosition()] = 1;
3936 break;
3937 case 0:
3938 m_promoter_rates[h.GetPosition()] = m_promoter_default_rates[h.GetPosition()];
3939 break;
3940 case -1: // Repressed
3941 m_promoter_rates[h.GetPosition()] = m_world->GetConfig().IMPLICIT_BG_PROMOTER_RATE.Get();
3942 break;
3943 default: //Error
3944 assert(1);
3945 }
3946
3947 m_promoter_sum += m_promoter_rates[h.GetPosition()];
3948 h++;
3949
3950 } while (h.GetPosition() != 0);
3951
3952 }
3953
FindRegulatoryMatch(const cCodeLabel & label)3954 int cHardwareGX::FindRegulatoryMatch(const cCodeLabel& label)
3955 {
3956 // Examine number of sites matched by overlaying input label and this space in genome
3957 // Skip a site if it is already occupied by a regulator.
3958 cHeadProgramid h;
3959 h.Reset(this,0);
3960
3961 int best_site = -1; // No match found
3962 int best_site_matched_positions = 0;
3963 do {
3964 int matched_positions = 0;
3965 cHeadProgramid m(h);
3966 for (int i=0; i<label.GetSize(); i++)
3967 {
3968 // Don't allow a site that overlaps current regulation
3969 if (m_promoter_occupied_sites[m.GetPosition()] != 0)
3970 {
3971 matched_positions = 0;
3972 break;
3973 }
3974
3975 if ((m_inst_set->IsNop(m.GetInst())) && (label[i] == m_inst_set->GetNopMod( m.GetInst() )))
3976 {
3977 matched_positions++;
3978 }
3979 m++;
3980 }
3981
3982 // Keep new bests
3983 if (matched_positions > best_site_matched_positions)
3984 {
3985 best_site = h.GetPosition();
3986 best_site_matched_positions = matched_positions;
3987 // If we find an exact match, we can bail early
3988 if (best_site_matched_positions == label.GetSize()) return best_site;
3989 }
3990
3991 h++;
3992 } while (h.GetPosition() != 0);
3993
3994 return best_site;
3995 }
3996
3997 /*! Construct this cProgramid, and initialize hardware resources.
3998 */
cProgramid(const Sequence & genome,cHardwareGX * hardware)3999 cHardwareGX::cProgramid::cProgramid(const Sequence& genome, cHardwareGX* hardware)
4000 : m_gx_hardware(hardware)
4001 , m_unique_id(hardware->m_last_unique_id_assigned++)
4002 , m_executable(false)
4003 , m_bindable(false)
4004 , m_readable(false)
4005 , m_marked_for_death(false)
4006 , m_cpu_cycles_used(0)
4007 , m_copying_site(false)
4008 , m_memory(genome)
4009 , m_input_buf(m_gx_hardware->m_world->GetEnvironment().GetInputSize())
4010 , m_output_buf(m_gx_hardware->m_world->GetEnvironment().GetOutputSize())
4011 {
4012 assert(m_gx_hardware!=0);
4013 for(int i=0; i<NUM_HEADS; ++i) {
4014 m_heads[i].Reset(hardware);
4015 }
4016
4017 if (!m_gx_hardware->m_world->GetConfig().IMPLICIT_GENE_EXPRESSION.Get())
4018 {
4019 // Check what flags should be set on this programid.
4020 for(int i=0; i<m_memory.GetSize();) {
4021 if(m_memory[i]==GetInst("PROGRAMID")) {
4022 m_memory.Remove(i);
4023 continue;
4024 }
4025 if(m_memory[i]==GetInst("EXECUTABLE")) {
4026 m_memory.Remove(i);
4027 m_executable=true;
4028 continue;
4029 }
4030 if(m_memory[i]==GetInst("BINDABLE")) {
4031 m_memory.Remove(i);
4032 m_bindable=true;
4033 continue;
4034 }
4035 if(m_memory[i]==GetInst("READABLE")) {
4036 m_memory.Remove(i);
4037 m_readable=true;
4038 continue;
4039 }
4040 ++i;
4041 }
4042 }
4043
4044 Reset();
4045 }
4046
ResetHeads()4047 void cHardwareGX::cProgramid::ResetHeads()
4048 {
4049 for(int i=0; i<NUM_HEADS; ++i) {
4050 m_heads[i].SetProgramid(this);
4051 m_heads[i].Reset(m_gx_hardware, m_id);
4052 }
4053 }
4054
Reset()4055 void cHardwareGX::cProgramid::Reset()
4056 {
4057 for (int i = 0; i < NUM_REGISTERS; i++) m_regs[i] = 0;
4058 ResetHeads();
4059
4060 m_stack.Clear();
4061 m_read_label.Clear();
4062 m_next_label.Clear();
4063 }
4064
4065 /*! Append this programid's genome to the passed in genome. Include the tags
4066 that specify what this programid is capable of.
4067 */
AppendLinearGenome(Sequence & genome)4068 void cHardwareGX::cProgramid::AppendLinearGenome(Sequence& genome) {
4069 genome.Append(GetInst("PROGRAMID"));
4070 if(GetExecutable()) { genome.Append(GetInst("EXECUTABLE")); }
4071 if(GetBindable()) { genome.Append(GetInst("BINDABLE")); }
4072 if(GetReadable()) { genome.Append(GetInst("READABLE")); }
4073 genome.Append(m_memory);
4074 }
4075
4076
PrintGenome(std::ostream & out)4077 void cHardwareGX::cProgramid::PrintGenome(std::ostream& out) {
4078 out << "Programid ID: " << m_id << " UID:" << m_unique_id << endl;
4079 if(GetExecutable()) out << " EXECUTABLE";
4080 if(GetBindable()) out << " BINDABLE";
4081 if(GetReadable()) out << " READABLE";
4082 out << endl;
4083 out << " Mem (" << GetMemory().GetSize() << "):" << " " << GetMemory().AsString() << endl;
4084 out.flush();
4085 }
4086
4087
4088 /*! This method attempts to match this cProgramid with the passed-in label. If the
4089 match is succesful, it returns true and a cMatchSite object that may be used to bind
4090 to the programid. If the match is unsuccessful, it return false and the cMatchSite
4091 object points to null.
4092
4093 A "successful" match is one where this cProgramid has a series of NOPs that are similar
4094 to the the passed-in label. A number of configuration options (will eventually)
4095 control how precisely the NOPs must be related (e.g., exact, all-but-one, etc.).
4096 */
Sites(const cCodeLabel & label)4097 std::vector<cHardwareGX::cMatchSite> cHardwareGX::cProgramid::Sites(const cCodeLabel& label)
4098 {
4099 std::vector<cHardwareGX::cMatchSite> matches;
4100 if(!m_bindable) return matches;
4101
4102 cInstruction site_inst = m_gx_hardware->GetInstSet().GetInst("site");
4103
4104 // Create a new search head at the beginning of our memory space
4105 // \to do doesn't properly find wrap-around matches overlapping the origin of the memory
4106
4107 //Find the first non-NOP and start there (this allows ups to wrap around correctly)
4108 cHeadCPU search_head(m_gx_hardware, 0, m_id);
4109 int first_non_nop = -1;
4110 do {
4111 if ( !m_gx_hardware->m_inst_set->IsNop(search_head.GetInst()) )
4112 {
4113 first_non_nop = search_head.GetPosition();
4114 break;
4115 }
4116 search_head++;
4117 } while (search_head.GetPosition() != 0);
4118
4119 // This genome is all NOPs...
4120 if (first_non_nop == -1) return matches;
4121
4122 //keep track of the first time we find a non-NOP instruction (finish when we reach it a second time)
4123 int site_pos = -1;
4124 cCodeLabel site_label;
4125
4126 // Start at this instruction
4127 search_head.Set(first_non_nop, m_id);
4128 do {
4129
4130 if (search_head.GetInst() == site_inst)
4131 {
4132 site_pos = search_head.GetPosition();
4133 site_label.Clear();
4134 }
4135 else if ( m_gx_hardware->m_inst_set->IsNop(search_head.GetInst()) && (site_pos != -1) )
4136 {
4137 // Add NOPs to the current label
4138 site_label.AddNop( m_gx_hardware->m_inst_set->GetNopMod( search_head.GetInst() ) );
4139 }
4140 else // Any other non-NOP instruction means to stop looking for terminator matches
4141 {
4142 site_pos = -1;
4143 }
4144
4145 // Is the next inst a NOP?
4146 // If not, then check our current label for termination
4147 if (site_pos != -1)
4148 {
4149 int old_pos = search_head.GetPosition();
4150 search_head++;
4151 if ( !m_gx_hardware->m_inst_set->IsNop( search_head.GetInst()) )
4152 {
4153 if ( site_label == label )
4154 {
4155 cMatchSite match;
4156 match.m_programid = this;
4157 match.m_site = site_pos; // We return is exactly on the site
4158 match.m_label = site_label;
4159 matches.push_back(match);
4160 }
4161 }
4162
4163 // Grrr. Heads don't wrap backwards properly!~! Can't just search_head--; No idea why.
4164 search_head.Set(old_pos, m_id);
4165 }
4166
4167 search_head++;
4168 } while ( search_head.GetPosition() != first_non_nop ); // back at the beginning
4169
4170 return matches;
4171 }
4172
4173
4174 /*! Bind attaches parts of this cProgramid to the cProgramid specified in the
4175 passed-in cMatchSite. Currently, we only support binding the read head to a
4176 location in the genome of the other cProgramid, but this will be extended later.
4177 */
Bind(nHardware::tHeads head,cMatchSite & site)4178 void cHardwareGX::cProgramid::Bind(nHardware::tHeads head, cMatchSite& site) {
4179 // We set the terminator site label to the complement of the one that was bound.
4180 m_terminator_label = site.m_label;
4181 m_terminator_label.Rotate(1, NUM_NOPS);
4182
4183 // Set the head.
4184 if(GetHead(head).GetMemSpace() != GetID()) {
4185 // Head is somewhere else; remove it
4186 m_gx_hardware->m_programids[GetHead(head).GetMemSpace()]->RemoveContactingHead(GetHead(head));
4187 }
4188
4189 // Now attach it to the passed-in match site
4190 m_gx_hardware->m_programids[site.m_programid->GetID()]->AddContactingHead(GetHead(head));
4191 GetHead(head).Set(site.m_site, site.m_programid->GetID());
4192 }
4193
4194
4195 /*! This method detaches the caller's heads from programids that it is connected
4196 to. It also updates the head contact counts.
4197 */
Detach()4198 void cHardwareGX::cProgramid::Detach() {
4199 int head = GetHead(nHardware::HEAD_WRITE).GetMemSpace();
4200 m_gx_hardware->m_programids[head]->RemoveContactingHead(GetHead(nHardware::HEAD_WRITE));
4201 GetHead(nHardware::HEAD_WRITE).Set(0, GetID());
4202
4203 head = GetHead(nHardware::HEAD_READ).GetMemSpace();
4204 m_gx_hardware->m_programids[head]->RemoveContactingHead(GetHead(nHardware::HEAD_READ));
4205 GetHead(nHardware::HEAD_READ).Set(0, GetID());
4206 }
4207
4208 /* End current regulation in implicit GX mode
4209 */
RemoveRegulation()4210 void cHardwareGX::cProgramid::RemoveRegulation()
4211 {
4212 if (GetHead(nHardware::HEAD_WRITE).GetMemSpace() != 0) return;
4213 int _pos = GetHead(nHardware::HEAD_WRITE).GetPosition();
4214
4215 // Slow but reliable to cycling way of calculating size
4216 cHeadProgramid t(GetHead(nHardware::HEAD_WRITE));
4217 int _label_size = 0;
4218 while (t.GetPosition() != GetHead(nHardware::HEAD_FLOW).GetPosition())
4219 {
4220 _label_size++;
4221 t++;
4222 }
4223
4224 //Reset heads
4225 GetHead(nHardware::HEAD_WRITE).Set(0, m_id);
4226 GetHead(nHardware::HEAD_FLOW).Set(0, m_id);
4227
4228 int regulatory_footprint = 5; // Positions ahead and behind label that it covers
4229 int match_pos = (_pos - regulatory_footprint + m_gx_hardware->m_programids[0]->GetMemory().GetSize() ) % m_gx_hardware->m_programids[0]->GetMemory().GetSize();
4230
4231 cHeadProgramid h(m_gx_hardware);
4232 h.Set(match_pos, 0);
4233 for (int i=0; i < 2*regulatory_footprint + _label_size; i++)
4234 {
4235 m_gx_hardware->m_promoter_occupied_sites[h.GetPosition()] = 0; //Zero regulation
4236 h++;
4237 }
4238
4239 m_gx_hardware->AdjustPromoterRates();
4240 }
4241