OSDev.org
https://forum.osdev.org/

Writing a Interpreter
https://forum.osdev.org/viewtopic.php?f=13&t=3236
Page 1 of 1

Author:  beyondsociety [ Sun May 11, 2003 10:41 pm ]
Post subject:  Writing a Interpreter

I am in the process of beginning to write a compiler and someone on this forum suggested creating a byte code interpreter to help with constructing the compiler.

How would I go about writing a simple interpreter?

Author:  df [ Mon May 12, 2003 1:18 am ]
Post subject:  Re:Writing a Interpreter

a nice big switch statement? :)

you need to decide if your going to output pcode or real cpu code.

a register based system is the easiest to write an interp for.

when i get home from work I'll post some info on my interp I have.

Author:  AGI1122 [ Mon May 12, 2003 1:57 am ]
Post subject:  Re:Writing a Interpreter

Sarien? Or are you guys talking about something completly different?

Author:  Perica [ Mon May 12, 2003 2:54 am ]
Post subject:  Re:Writing a Interpreter

..

Author:  Beyond Infinity lazy [ Mon May 12, 2003 4:22 am ]
Post subject:  Re:Writing a Interpreter

Nay, Perica, they are talking about compilers and Interpreters of hll's. This can be easily induced from beyondsocietys inquiry.

Author:  df [ Mon May 12, 2003 10:59 am ]
Post subject:  Re:Writing a Interpreter

below is an old core to one of my interpreters.
it was a register based machine.
basically it used a 32bit number

top 8 bits were opcode, next 8 were register 1, then next8 were register 2. the remaing were some status bits.

all opcodes ran on register to register with the exception of the load/store opcode.

it ran 'compiled' scripts in a 64kb data block (all code/text etc must sit inside 64kb. that was my restriction, since this version didnt have a VM doing memory interfacing in it, which my current one has).


anyway, gives you an idea of what code my compiler output. (I cut some commands out, since my message was too long)

Code:
void run_opcode(vContext *vCPU)
{
   UINT32   op;
   UINT32   r1, r2, r3, r4;

   op = (UINT32)( ((UINT8*)vCPU->ptrMem)[ vCPU->cpu.reg[ REG_IP ] ] );

   switch(GET_OPCODE(op))
   {
      case op_NULL:
         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
         break;

      case op_ADD:
         r1 = vCPU->cpu.reg[ GET_REGA(op) ];
         r2 = vCPU->cpu.reg[ GET_REGB(op) ];
         vCPU->cpu.reg[ GET_REGA(op) ] = r1 + r2;
         
         if( r1 > vCPU->cpu.reg[ GET_REGA(op) ])
            vCPU->cpu.reg[ REG_FLAGS ] |= FLAG_OVERFLOW;
         else
            vCPU->cpu.reg[ REG_FLAGS ] &= ~FLAG_OVERFLOW;

         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
         break;

      case op_SUB:
         r1 = vCPU->cpu.reg[ GET_REGA(op) ];
         r2 = vCPU->cpu.reg[ GET_REGB(op) ];
         vCPU->cpu.reg[ GET_REGA(op) ] = r1 - r2;
         
         if( r1 < vCPU->cpu.reg[ GET_REGA(op) ])
            vCPU->cpu.reg[ REG_FLAGS ] |= FLAG_OVERFLOW;
         else
            vCPU->cpu.reg[ REG_FLAGS ] &= ~FLAG_OVERFLOW;

         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
         break;

      case op_MUL:
         r1 = vCPU->cpu.reg[ GET_REGA(op) ];
         r2 = vCPU->cpu.reg[ GET_REGB(op) ];
         vCPU->cpu.reg[ GET_REGA(op) ] = r1 * r2;
         /* compute if overflow */
         break;

      case op_DIV:
         r1 = vCPU->cpu.reg[ GET_REGA(op) ];
         r2 = vCPU->cpu.reg[ GET_REGB(op) ];
         vCPU->cpu.reg[ GET_REGA(op) ] = r1 / r2;
         vCPU->cpu.reg[ GET_REGB(op) ] = r1 % r2;
         /* compute if overflow */

         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
         break;

      case op_CMP:
         if( vCPU->cpu.reg[ GET_REGA(op) ] == vCPU->cpu.reg[ GET_REGB(op) ] )
            vCPU->cpu.reg[ REG_FLAGS ] |= FLAG_EQUAL;
         else
            vCPU->cpu.reg[ REG_FLAGS ] &= ~FLAG_EQUAL;

         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
         break;

      case op_MOV:
         // dont change reg0!
         if( GET_REGA(op) == 0)
            break;

         r1 = vCPU->cpu.reg[ GET_REGA(op) ]; // dest
         r2 = vCPU->cpu.reg[ GET_REGB(op) ]; // source

         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);

         // source
         switch( GET_MOD1(op) )
         {
            // mov r1, r2
            case 0:
               r4 = r2;
               break;

            // mov r1, [r4]
            case MOD_MEM:
               r4 = ( ((UINT32*)vCPU->ptrMem)[ r2 ] );
               break;
            
            // mov r1, 0xDEADBEEF
            case MOD_NUM:
               r4 = ( ((UINT32*)vCPU->ptrMem)[ vCPU->cpu.reg[ REG_IP ] ] );
               vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
               break;

            // mov r1, [0xDEADBEEF]
            case MOD_MEM+MOD_NUM:
               r4 = ( ((UINT32*)vCPU->ptrMem)[ vCPU->cpu.reg[ REG_IP ] ] );
               vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);

               r4 = ( ((UINT32*)vCPU->ptrMem)[ r4] );
               break;
         }

         // move r4 into { r1|[r1]|[xx] }


         // destination
         switch( GET_MOD1(op) )
         {
            // mov r1, r4
            case 0:
               vCPU->cpu.reg[ r1 ] = r4;
               break;

            // mov [r1], r4
            case MOD_MEM:
               r3 = ( ((UINT32*)vCPU->ptrMem)[ r1 ] );
               ((UINT32*)vCPU->ptrMem)[ r3 ] = r4;
               break;
            
            // mov 0xDEADBEEF, r4
            // illegal!
            case MOD_NUM:
               //r3 = ( ((UINT32*)vCPU->ptrMem)[ vCPU->cpu.reg[ REG_IP ] ] );
               //vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
               // signal illegal!
               break;

            // mov [0xDEADBEEF], r4
            case MOD_MEM+MOD_NUM:
               r3 = ( ((UINT32*)vCPU->ptrMem)[ vCPU->cpu.reg[ REG_IP ] ] );
               vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);

               ((UINT32*)vCPU->ptrMem)[ r3 ] = r4;
               break;
         }
         break;
         
      case op_JE:
         r1 = vCPU->cpu.reg[ GET_REGA(op) ];
         vCPU->cpu.reg[ REG_IP ] += sizeof(UINT32);
         
         if( (vCPU->cpu.reg[ REG_FLAGS ]&=FLAG_EQUAL)==FLAG_EQUAL )
             vCPU->cpu.reg[ REG_IP ] = r1;         
         break;
   }
}

Author:  beyondsociety [ Mon May 12, 2003 1:39 pm ]
Post subject:  Re:Writing a Interpreter

df: what cpu is this snipe of code for? just wondering because it looks familar.

Also, I would like to take a look at all the code for this intepreter you wrote to get a idea of what a intepreter consists of.

Whats the difference between pcode and real cpu code?

Author:  df [ Mon May 12, 2003 3:02 pm ]
Post subject:  Re:Writing a Interpreter

well that code is just a virtual cpu i made up. 16 registers. very simple stuff. since its all register to register operands, implementation is really basic.

i guess there isnt a great deal of difference from pcode to real cpu in a lot of ways, the original 'pcode' was for a pascal compiler back in the early 80's.

you could have code in your pcode for list->next, list->prev, or encode really complex stuff into an operand, etc.

i dont know if there is any hard/fast rules for pcode. VB4 and < used to compile to PCODE.

these kinds of 'cpu's are really simple to construct.

Author:  beyondsociety [ Tue May 13, 2003 1:20 am ]
Post subject:  Re:Writing a Interpreter

Do you have an opcode list or instruction set for this made up virtual pc?

Author:  df [ Tue May 13, 2003 11:20 am ]
Post subject:  Re:Writing a Interpreter

yeah I have an opcode list.

Code:
/*

opcodes

00 - null
01 - add
02 - sub
03 - mul
04 - div
05 - cmp
06 - mov
07 - je
08 - xor
09 - and
10 - or
11 - not
12 - neg

16 regs

'' all reg, reg

push =
mov   [r13],r1
sub r13, 4

pop =
add r13, 4
mov r1,[r13]


xxxxxxxx 00zq1111 00zq2222 bbbbbbbb

x = opcode   
1 = reg1
2 = reg2
z = numeric flag
    0 - no num
    1 - 32bit num follows
q = memory flag
   0 - reg
   1 - memory
b = undefined.   

reg0 is always ZERO
reg13 is SIP
reg14 is flags
reg15 is IP

*/

Author:  beyondsociety [ Wed May 14, 2003 11:36 am ]
Post subject:  Re:Writing a Interpreter

How does the code you posted lookup the opcodes?

Do you have to put a list of opcodes into a buffer or table and load it first before I run the get_Opcode function?

Author:  df [ Wed May 14, 2003 1:31 pm ]
Post subject:  Re:Writing a Interpreter

what??

i load my 'binary' into memory. setup my cpu registers...
and run it?

i dont get your question. i know opcode 1 is add.. so I run the add function when I get to it...

i allocate say 64kb

Code:
char *x;

x=malloc(1024*64);
read_into_file(x);

// ok, buffer x contains my code..

opcode = x[ register[instruction_pointer] ];

switch(opcode)
{
}


maybe i'm reading your question wrong, I dunno.

Author:  df [ Thu May 15, 2003 3:30 pm ]
Post subject:  Re:Writing a Interpreter

I uploaded all the code for that interpreter I had in my examples above.

There is probably lots of bugs in it, but its fairly simple.
unrar it into a directory and run virt.exe it will load and run x1.bin (it being a small test file).

[ftp=ftp://ftp.mega-tokyo.com/pub/my_stuff/temp/int.rar]ftp://ftp.mega-tokyo.com/pub/my_stuff/temp/int.rar[/ftp]

its designed to work, regardless of the endianness of its host cpu.

I compiled it under VC6, but there is nothing fancy, so should compile under much of any 32bit compiler...

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/