RPGDXThe center of Indie-RPG gaming
Not logged in. [log in] [register]
 
 
Post new topic Reply to topic Goto page Previous  1, 2, 3  Next 
View previous topic - View next topic  
Author Message
janus
Mage


Joined: 29 Jun 2002
Posts: 464
Location: Issaquah, WA

PostPosted: Tue Jul 29, 2003 5:12 am    Post subject: [quote]

Ah, I see.

When I was first working on my language I wrote the first scripts using a hex editor, so my method isn't really that different from yours. I just didn't understand the purpose in creating an assembler if you intend to make a more useful language later. I went straight from bytecodes to a C-style language, but it was pretty difficult, so maybe your method is better.
Back to top  
valderman
Mage


Joined: 29 Aug 2002
Posts: 334
Location: Gothenburg, Sweden

PostPosted: Tue Jul 29, 2003 12:39 pm    Post subject: [quote]

Quote:
Bytecode isn't the be-all-end-all of script translation. You can perfectly well execute a script from its parse-tree representation. Sure, this is a translation of the script into a "machine-friendly" form, but it isn't bytecode (in the normative sense). Due to Moore's Law, computer architecture is increasing in processing power every 18 months or so. Low-ended machines are not what they used to be. It seems rather silly to me to create VMs for programs to increase the speed of those programs under these conditions. Especially when you consider the amount of computer cycles your program is going to be wasting sitting there waiting for user input, or for vertical retrace on the monitor to blit image data.
You're right, bytecode isn't eh only machine-friendly approach, and your trees sound like a good option (got any link on where to learn more about them? I'm curious.), however, I disagree that making the scripting as fast as possible is silly. No non-turn based game willl just be sitting there waiting for input, there's always things going on behind the scenes, and the faster those things are carried out, the more machines it will run on.

Quote:
When I was first working on my language I wrote the first scripts using a hex editor, so my method isn't really that different from yours. I just didn't understand the purpose in creating an assembler if you intend to make a more useful language later. I went straight from bytecodes to a C-style language, but it was pretty difficult, so maybe your method is better.
My reason for creating an assembler is simply that I'm currently workng on the VM part of the scripting. Writing the scripts with a hexeditor, manually calculating jump addresses and such, is quite a pain in the ass, so in order to test it properly, I more or less had to create a simple assembler.
Of course, if I were to work simultaneously on the language and the VM, I would have to debug both whenever something went wrong, which is not a good idea. I'd rather have the VM in a more or less bug-free state before creating the compiler.
_________________
http://www.weeaboo.se
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Tue Jul 29, 2003 6:34 pm    Post subject: [quote]

This tutorial series is awesome. My language is going to be very 'BASIC' - I don't know how basic yet though.

What defines a BASIC language anyway? I'll have for/while and if/else. If all your variables are global, then isn't a gosub/returrn the same as a 'user defined' function call? Hmmm... I need peek and poke!
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Thu Jul 31, 2003 12:54 pm    Post subject: [quote]

Well, I just ran some timed test and for a simple program the "decision tree" version of my java vm ran MUCH faster than one big switch statement; guess this means Java doesn't optimize to a jump table. I'll try it again with a larger more complex program that uses more of the various opcodes, but I think the results will be the same.
Back to top  
valderman
Mage


Joined: 29 Aug 2002
Posts: 334
Location: Gothenburg, Sweden

PostPosted: Thu Jul 31, 2003 4:07 pm    Post subject: [quote]

How many opcodes are you using? If your compiler optimizes huge switches into a jump table (which it really should be doing), it probably made the decision that you don't have enough opcodes for a jump table to be benificial.
For reference, these are the opcodes I'm using right now (those evil #define tricks are there just to make the executing function smaller):
Code:
#define OPCODE(op, func, data) case op: func(data); break;

#define OP_TABLE(data)\
   OPCODE(op_crmov, _crmov, data)\
   OPCODE(op_ssmov, _ssmov, data)\
   OPCODE(op_srmov, _srmov, data)\
   OPCODE(op_csmov, _csmov, data)\
   OPCODE(op_rsmov, _rsmov, data)\
   OPCODE(op_sremove, _sremove, data)\
   OPCODE(op_cpush, _cpush, data)\
   OPCODE(op_rpush, _rpush, data)\
   OPCODE(op_spush, _spush, data)\
   OPCODE(op_rpop, _rpop, data)\
   OPCODE(op_spop, _spop, data)\
   OPCODE(op_rinc, _rinc, data)\
   OPCODE(op_rdec, _rdec, data)\
   OPCODE(op_sinc, _sinc, data)\
   OPCODE(op_sdec, _sdec, data)\
   OPCODE(op_ssadd, _ssadd, data)\
   OPCODE(op_sssub, _sssub, data)\
   OPCODE(op_ssmul, _ssmul, data)\
   OPCODE(op_ssdiv, _ssdiv, data)\
   OPCODE(op_ssmod, _ssmod, data)\
   OPCODE(op_sradd, _sradd, data)\
   OPCODE(op_srsub, _srsub, data)\
   OPCODE(op_srmul, _srmul, data)\
   OPCODE(op_srdiv, _srdiv, data)\
   OPCODE(op_srmod, _srmod, data)\
   OPCODE(op_rssub, _rssub, data)\
   OPCODE(op_rsdiv, _rsdiv, data)\
   OPCODE(op_rsmod, _rsmod, data)\
   OPCODE(op_sscmp, _sscmp, data)\
   OPCODE(op_srcmp, _srcmp, data)\
   OPCODE(op_cscmp, _cscmp, data)\
   OPCODE(op_crcmp, _crcmp, data)\
   OPCODE(op_cjmp, _cjmp, data)\
   OPCODE(op_cje, _cje, data)\
   OPCODE(op_cjne, _cjne, data)\
   OPCODE(op_cja, _cja, data)\
   OPCODE(op_cjae, _cjae, data)\
   OPCODE(op_cjb, _cjb, data)\
   OPCODE(op_cjbe, _cjbe, data)\
   OPCODE(op_cjz, _cjz, data)\
   OPCODE(op_cjnz, _cjnz, data)\
   OPCODE(op_rjmp, _rjmp, data)\
   OPCODE(op_rje, _rje, data)\
   OPCODE(op_rjne, _rjne, data)\
   OPCODE(op_rja, _rja, data)\
   OPCODE(op_rjae, _rjae, data)\
   OPCODE(op_rjb, _rjb, data)\
   OPCODE(op_rjbe, _rjbe, data)\
   OPCODE(op_rjnz, _rjnz, data)\
   OPCODE(op_trm, _trm, data)\
   OPCODE(op_runscript, _runscript, data)\
   OPCODE(op_call, _call, data)\
   OPCODE(op_ret, _ret, data)\
   OPCODE(op_extcall, _extcall, data)

// Executing loop
for(;m_dwPc < m_dwCodeSize;m_dwPc++)
{
   i = m_vInstr[m_dwPc];
   switch(i->cOpcode)
   {
      OP_TABLE(i->pData)
   };
}

I don't know at what point the compiler decides a jump table is beneficial, but with these (54 of them), it seems to be enough.
_________________
http://www.weeaboo.se
Back to top  
Rainer Deyke
Demon Hunter


Joined: 05 Jun 2002
Posts: 672

PostPosted: Thu Jul 31, 2003 4:44 pm    Post subject: [quote]

If you're just going to dispatch to functions, why use a switch statement at all?

Code:

typedef void (*opcode_function)(int);  // Substitute correct argument type for 'int'
opcode_function opcode_table[] = {
  _crmov,
  _ssmov,
  // ...
};

// Executing loop
for(;m_dwPc < m_dwCodeSize;m_dwPc++)
{
   i = m_vInstr[m_dwPc];
   opcode_table[i->cOpcode](i->pData);
}
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Thu Jul 31, 2003 5:13 pm    Post subject: [quote]

Hmm, I just though of something; I'm not calling functions in my switch statements, I'm executing the code right there (each opcode is at most 3 lines). I wonder if that makes a difference.... Right now I have 25 opcodes, but when I add the interface to the game engine it will be around 40 (probably).
Code:

if(memspace[pc]<8)  {
            switch (memspace[pc])  {
               case 1:  //bra
                  pc=memspace[++pc];
                  break;
               case 2:  //beq
                  if(memspace[memspace[++pc]] == memspace[++pc]) {
                     pc=memspace[++pc];
                     pc--;
                  } else pc++;
                  break;
               case 3:  //bne
                  if(memspace[memspace[++pc]] != memspace[++pc]) {
                     pc=memspace[++pc];
                     pc--;
                  } else pc++;
                  break;
               case 4:  //bgt
                  if(memspace[memspace[++pc]] > memspace[++pc]) {
                     pc=memspace[++pc];
                     pc--;
                  } else pc++;
                  break;
               case 5:  //blt
                  if(memspace[memspace[++pc]] < memspace[++pc]) {
                     pc=memspace[++pc];
                     pc--;
                  } else pc++;
                  break;
               case 6:  //bge
                  if(memspace[memspace[++pc]] >= memspace[++pc]) {
                     pc=memspace[++pc];
                     pc--;
                  } else pc++;
                  break;
               case 7:  //ble
                  if(memspace[memspace[++pc]] <= memspace[++pc]) {
                     pc=memspace[++pc];
                     pc--;
                  } else pc++;
                  break;
            }
         }
         else if(memspace[pc]<17)  {
             etc....


Edit: is that Hungarian notation in your variable names? What does m_ mean?
Back to top  
valderman
Mage


Joined: 29 Aug 2002
Posts: 334
Location: Gothenburg, Sweden

PostPosted: Thu Jul 31, 2003 5:47 pm    Post subject: [quote]

Rainer Deyke wrote:
If you're just going to dispatch to functions, why use a switch statement at all?

Code:

typedef void (*opcode_function)(int);  // Substitute correct argument type for 'int'
opcode_function opcode_table[] = {
  _crmov,
  _ssmov,
  // ...
};

// Executing loop
for(;m_dwPc < m_dwCodeSize;m_dwPc++)
{
   i = m_vInstr[m_dwPc];
   opcode_table[i->cOpcode](i->pData);
}

Because the functions are inlined. There is no way a compiler will be able to inline an array of function pointers, or even a single function pointer. I thought about using an array of function pointers, but the switch proved to be faster.

EDIT: For some reason, I like hungarian notation, least for class members, but the "m_" isn't hungarian notation, it's simply a prefix to tell that this is a member variable.

If you call functions from the switch statement, make sure they are inlined, or it will add a crapload of overhead. They won't make it faster, but if they're inlined, they won't make it slower either.
I'm calling functions so the switch won't get too messy, and it helps a bit with debugging too.
_________________
http://www.weeaboo.se
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Fri Aug 01, 2003 9:34 pm    Post subject: [quote]

So it should be the same thing since my actual code is inlined?

Well, the vm is complete enough, I can know start to work on the compilator!! (hand coding opcodes does suck) Hmm, and I still need a rudimentary engine to hook it into.....
Back to top  
valderman
Mage


Joined: 29 Aug 2002
Posts: 334
Location: Gothenburg, Sweden

PostPosted: Fri Aug 01, 2003 10:13 pm    Post subject: [quote]

When I started designing my scripting engine, the main goal was for it to be totally independent of any engines, all script<-->engine communication should be handled by DLLs. Perhaps this is something for you to consider, so it will be easier to reuse the scripting engine in future projects?

Oh, and if you stumble upon any good resources on text parsing, I'd be happy to know about them.
_________________
http://www.weeaboo.se
Back to top  
LeoDraco
Demon Hunter


Joined: 24 Jun 2003
Posts: 584
Location: Riverside, South Cali

PostPosted: Sat Aug 02, 2003 12:10 am    Post subject: [quote]

Valderman wrote:
Oh, and if you stumble upon any good resources on text parsing, I'd be happy to know about them.


As I have mentioned before, the GNU tool Bison (which is a free version of YACC; the humor there is amusing.) is a fairly good parser-generator. Text parsing itself isn't terribly difficult, it's just tedious to code. Either way, if you want to roll your own parser, you'll want to use either a LL(k), a LR(k), or a LALR lookup table. While you could probably google some information on any of this, you could look at this compiler manual put out by one of my professors.
_________________
"...LeoDraco is a pompus git..." -- Mandrake
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Sat Aug 02, 2003 4:51 am    Post subject: [quote]

Valderman wrote:
When I started designing my scripting engine, the main goal was for it to be totally independent of any engines, all script<-->engine communication should be handled by DLLs. Perhaps this is something for you to consider, so it will be easier to reuse the scripting engine in future projects?

Oh, and if you stumble upon any good resources on text parsing, I'd be happy to know about them.


Well, right now I'm doing this in Java so including dll support is alot more work than I want to go thru (although exposing all the game engine functions with JNI would be a way to use Java itself for the scripting bit). Right now, I'm just going to include the vm class and give it a reference to the main 'gamemanager' class and update the vm every loop.

Sooo, I looked at the Flex link LeoDraco posted earlier, there was almost no information on that page, guess I'll have another look.
Back to top  
LeoDraco
Demon Hunter


Joined: 24 Jun 2003
Posts: 584
Location: Riverside, South Cali

PostPosted: Sat Aug 02, 2003 5:40 am    Post subject: [quote]

BigManJones wrote:
Well, right now I'm doing this in Java so including dll support is alot more work than I want to go thru (although exposing all the game engine functions with JNI would be a way to use Java itself for the scripting bit). Right now, I'm just going to include the vm class and give it a reference to the main 'gamemanager' class and update the vm every loop.


I was going to mention that a good OOD would be a good alternative to going the DLL approach, for those cases where DLLs were not a good option.

Quote:
Sooo, I looked at the Flex link LeoDraco posted earlier, there was almost no information on that page, guess I'll have another look.


Flex itself just does scanning of whatever stream is passed to it. Parsing of the tokens returned by a lexer function would either have to be done manually, or via a parser generated by some utility (such as Bison or YACC). However, to my knowledge, Bison and Flex only generate pasers/scanners in C/C++ code, so the standard GNU utlities might not be a viable option for a Java environment. However, I'm sure you could google a search on Java and Bison to find information. There are probably Java modules that can generate parser and lexer Java classes, which you could also search for. For example, I found CUP by doing a search for "java parser generator".
_________________
"...LeoDraco is a pompus git..." -- Mandrake
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Sat Aug 02, 2003 2:34 pm    Post subject: [quote]

Thanks!
Back to top  
BigManJones
Scholar


Joined: 22 Mar 2003
Posts: 196

PostPosted: Fri Aug 08, 2003 11:07 pm    Post subject: [quote]

I just realized something; I don't have to use Java to write my compiler, I can use any language, all I need is the byte code.... /me dumbass
Back to top  
Post new topic Reply to topic Page 2 of 3 All times are GMT
Goto page Previous  1, 2, 3  Next 



Display posts from previous:   
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum