/* * grissom.cc -- Grissom Virtual Machine -- like genetic2005 but generalized for wider words * # Copyright (c) 2006 Henry Strickland -- in the domain # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # (* http://www.opensource.org/licenses/mit-license.php *) * */ #include "li.h" namespace Grissom { using namespace Li; using Li::uint; using Li::word; // Configure these #define NUM_GLOBAL 4 #define NUM_STACK 300 #define NUM_CODE 128 #define NUM_OPNUM 11 #define BRANCH_POINT_SPACING 16 /* sparse points doesnt work unless this divides NUM_CODE */ #define HEX_DIGITS_PER_OPCODE 2 // Shortcuts class GrissomMachine : public Machine { protected: virtual ~GrissomMachine(); virtual void eval_virtual( ); public: GrissomMachine(Link p, Link c ); int global [ NUM_GLOBAL ]; // global vars word stack [ NUM_STACK ]; // subroutine stack word code [ NUM_CODE ]; // read-only program code int accum; // accumulator int outptr; // into output buffer word sp; // subr stack ptr, indexes into stack word pc; // program counter, indexes into code void step(); word branch_target(word oparg) { return (oparg*BRANCH_POINT_SPACING) % NUM_CODE; } void setCodeFromString( string program ); }; GrissomMachine::~GrissomMachine() {} GrissomMachine::GrissomMachine( Link p, Link c ) : Machine(p,c) , accum(0) , outptr(0) , sp(0) , pc(0) { memset( global, 0, sizeof global ); memset( stack, 0, sizeof stack ); memset( code, 0, sizeof code ); setCodeFromString( m_creature->sourceCode ); } void GrissomMachine::step() { assert( pc < NUM_CODE ); assert( sp < NUM_STACK ); word opcode= code [ pc ]; if (trace) printf(": sp=%d(A=%d)[pc=%d] code=%02x ", +sp, accum, +pc, +opcode); word opnum = opcode % NUM_OPNUM; word oparg = opcode / NUM_OPNUM; word reg = oparg % NUM_GLOBAL; pc = (pc+1) % NUM_CODE; switch ( opnum ) { case 0: { // NOP, PUTNUM, SPECIAL oparg= oparg % 2; switch (oparg) { case 0: if (trace) printf("NOP\n"); // NOP break; case 1: // PUTNUM if (trace) printf("PUTNUM\n"); this->putnum(accum); break; } } break; case 1: { // DBP N*OMAX // decrement accum and branch if positive if (trace) printf("DBP %d", branch_target(oparg) ); -- accum; if ( accum<=0 ) { pc= branch_target(oparg); } } break; case 2: { // CALL N*OMAX if (trace) printf("CALL %d", branch_target(oparg)); stack[sp]= pc; sp= (sp+1) % NUM_STACK; pc= branch_target(oparg); } break; case 3: { // RETURN - if (trace) printf("RTN"); sp= (sp+NUM_STACK-1) % NUM_STACK; pc= stack[sp]; } break; case 4: { // LOAD CONSTANT int num= (opnum&15)-8; // in range -8 .. 7 if (trace) printf("LDC %d", num); accum = num; } break; case 5: { // INCR/DECR reg oparg= (oparg / NUM_GLOBAL) & 1; // after removing reg from oparg, take a bit if (trace) printf("%s %d", (oparg?"DECR":"INCR"), reg); if ( oparg ) { --global[reg]; } else { ++global[reg]; } } break; case 6: { // STO reg if (trace) printf("STO %d", reg); global[reg] = accum; } break; case 7: { // RCL reg if (trace) printf("RCL %d", reg); accum = global[reg]; } break; case 8: { // ADD reg if (trace) printf("ADD %d", reg); accum += global[reg]; } break; case 9: { // SUB reg if (trace) printf("SUB %d", reg); accum -= global[reg]; } break; case 10: { // MUL reg if (trace) printf("MUL %d", reg); accum *= global[reg]; } break; } if (trace) printf("\n"); if (trace) fflush(stdout); } void GrissomMachine::eval_virtual() { setCodeFromString( m_creature->sourceCode ); word max_steps= m_params->limitEvals; uint i; for ( i=0; i< max_steps; ++i ) { if (trace) printf("#%u:", i ); step(); } if (trace) printf("\neval DONE(%u)\n", i ); fflush(stdout); } // for backwards compatibilty, where program is 256 bytes void GrissomMachine::setCodeFromString( string program ) { // Incomplete trailing digits will not be used, // nor will digits beyond NUM_CODE const char* s= program.c_str(); word slen= program.size(); word n= min( slen/HEX_DIGITS_PER_OPCODE, (word)NUM_CODE ); for (word i=0; icode[i]= n; } } TEST(setCodeFromString) { Language* lang= Language::Find("gr"); Link params= lang->defaultParams(); Link prog= new Creature("45AbF19",lang); Link g= new GrissomMachine( params, prog ); AssertEq( 0x45u, g->code[0] ); AssertEq( 0xABu, g->code[1] ); AssertEq( 0xF1u, g->code[2] ); AssertEq( 0u, g->code[3] ); // trailing 9 should not be used } class GrissomLanguage : public Language { private: // private singleton, only used via Find() GrissomLanguage() : Language("gr") {} static GrissomLanguage Singleton; public: virtual Link defaultParams( ); virtual Link createMachine( Link p, Link c ); } GrissomLanguage::Singleton; Link GrissomLanguage::defaultParams( ) { Link p= Language::defaultParams(); p->creatureAlphabet = "0123456789abcdef"; p->creatureInitialLength = NUM_CODE * 2; return p; } Link GrissomLanguage::createMachine( Link p, Link c ) { return new GrissomMachine( p, c ); } } //Li //END