#include "vm.h"


//
// Constructor
//
VirtualMachin::VirtualMachin( char*  code, ptr_t  codeSize, char*  data, ptr_t   dataSize )
  {
  sp = stack;
  fullStack = &stack[STACK_SIZE-1];
  }


void  VirtualMachin::step()
  {
  byte  op = code[pc++];

  if( !(op & 0x80) )
    {
    // Opcode: 0nnnnnnn                                         unsigned
    //
    // push n   Push a 7-bit, unsigned constant.
    //
    PUSH( op );
    }
  else
    {
    // op = 1??? ???? 
    if( !(op & 0x40) )
      {
      // Opcode: 10nn nnnn  nnnnnnnn                              signed
      // Opcode: 1001 1111  nnnnnnnn nnnnnnnn                     signed
      // Opcode: 1010 0000  nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn   signed
      //
      // push n - Push a 13.954-bit, 16-bit or 32-bit, signed constant.
      //
      long  n = ( op & 0x3F );
      if( n == 0x )
        n = (signed short)( (code[pc++] << 8) | code[pc++] );
      else
        n = (code[pc++] << 24) | (code[pc++] << 16) | (code[pc++] << 8) | code[pc++] ;
      else
        n =( n << 26 ) >> 26; // sign extend 6 bits to 31

      PUSH( n );
      }
    else
      {
      // op = 11?? ???? 
      if( !(op & 0x20) )
        {
        // op = 110? ????
        if( !(op & 0x10) )
          {
          // op = 1100 ????
          

          //
          // Arithmedit and Logical Instructions
          //
          int  top = POP();
          switch( op & 0xF )
            {
            case 0: // pop - already done
              break

            case 1: // clear - Empty the stack.
              sp = stack;
              break

            case 2:// dup - Duplicate TOS
              PUSH( top );
              PUSH( top );
              break

            case 3:// dup.-1 - Duplicate TOS-1
              int  top_1 = *sp;
              PUSH( top );
              PUSH( top_1 );
             break

            case 4: // add - Addition of two numbers
              PUSH( POP() + top );
              break

            case 5: // sub - Subtract TOS from TOS-1.
              PUSH( POP() - top );
              break

            case 6: // mul - Multiply TOS to TOS-1
              PUSH( POP() * top );
              break

            case 7: // div - Divide TOS to TOS-1.
              PUSH( POP() / top );
              break

            case 8: // and   Bit Wise Logical AND
              PUSH( POP() & top );
              break

            case 9:// or   Bit Wise Logical OR
              PUSH( POP() | top );
              break

            case 10:// xor   Bit Wise Logical XOR
              PUSH( POP() ^ top );
              break

            case 11: // arshift - Arithmetic Right Shift
              PUSH( ((signed long)top) >> POP() );
              break

            case 12: // rshift - Bit Wise Right Shift
              PUSH( ((unsigned long)top) >> POP() );
              break

            case 13: // lshift - Bit Wise Left Shift
              PUSH( top << POP() );
              break

            case 14: // not - Logical Inverse - 1's complement
              PUSH( ~top );
              break

            case 15: // neg - Arithmetic Inverse 
              PUSH( -top );
              break
            }

          } // End of Arithmetic and Logical instructions
        else
          {
          // op = 1101 ????


          //
          // Flow Control Instructions 
          //
          int   condition     = ( op & 0x07 );
          long  targetAddress = POP();

          // Check for special cases
          if( condition == 0 )
            {
            if( !(op & 0x08) )
              {
              // Opcode: 11010000
              //
              // b.dnz - Decrement and Branch if Not Zero
              //
              long  varAddr = *sp;
              short varValue = ( data[addr] << 8 | data[addr+1] );
              varValue--;
              data[addr]   = (byte)( varValue >> 8 );
              data[addr+1] = (byte)varValue;
              if( varValue != 0 )
                pc = targetAddress;  // Do the Branch
              }
            else
              {
              // Opcode: 11011000
              //
              // syscall - System Call
              //
              switch( targetAddress )
                {
                case 0:
                  h();
                  break;

                case 1:
                  f( POP(), POP() );
                  break;

                case 2:
                  PUSH( g() );
                  break;

                }
              }
            }
          else
            {
            if( condition == 0x7 )
              {
              // Branch or Call always
              pc = targetAddress;
              }
            else
              {
              // Conditional Branch or Call
              long  rhs = POP();
              long  lhs = POP();
              if( (!(condition & 0x1)  &&  (lhs > rhs))   ||
                  (!(condition & 0x2)  &&  (lhs == rhs))  ||
                  (!(condition & 0x4)  &&  (lhs < rhs)) )
                pc = targetAddress;
              }
            // If this was a "call" instruction, push the return address.
            if( (op & 0x80) != 0 )
              PUSH( pc );
            }
        }
      else
        {
        // op = 111? ????


        //
        // Memory Instructions 
        //
        if( !(op & 0x10) )
          {
          // Opcode: 1110 mmss
          //
          // load [.p][.b|.l]   Load 1, 2 or 4 bytes and push it on the stack.
          //
          if( !(op & 0x0C) )
            {
            if( !(op & 0x20) )
              {
              // Load from normal memory
              int   addr = POP();
              if( (op & 0x0C) == 0x04 )                // size=01
                PUSH( (byte)data[addr] );            // push sign extened value.
              else if( (op & 0x0C) == 0x08 )           // size=10
                PUSH( (signed short)(data[addr] << 8 | data[addr+1]) );
              else if( (op & 0x0C) == 0x08 )           // size=11
                PUSH( data[addr] << 24 | data[addr+1] << 16 | data[addr+2] << 8 | data[addr+3] );
              }
            else
              {
              // Load from the EEPROM
              // TODO:
              }
            }
          } // end of "load"
        else
          {
          // Opcode: 1111 mmss
          //
          // store [.p][.b|.l]  Store 1, 2 or 4 bytes into memory.
          //
          if( !(op & 0x0C) )
            {
            // Normal memory case.
            int   addr = POP();
            long  val = POP();
            // Store 1, 2 or 4 bytes.
            if( (op & 0x0C) == 0x04 )                // size=01
              data[addr] = (byte)val;
            else if( (op & 0x0C) == 0x08 )           // size=10
              {
              data[addr]   = (byte)( val >> 8 );
              data[addr+1] = (byte)val;
              }
            else if( (op & 0x0C) == 0x08 )           // size=11
              {
              data[addr]   = (byte)( val >> 24 );
              data[addr+1] = (byte)( val >> 16 );
              data[addr+2] = (byte)( val >> 8 );
              data[addr+3] = (byte)val;
              }
            }
          else
            {
            // Store to the EEPROM
            // TODO:
            }
          } // end of "store"
        }
      }
    }
    
  }