Path: utzoo!attcan!uunet!samsung!gem.mps.ohio-state.edu!tut.cis.ohio-state.edu!ucbvax!SUN.COM!wmb From: wmb@SUN.COM Newsgroups: comp.lang.forth Subject: Re: Forth Implementation (long) Message-ID: <8911201436.AA01775@jade.berkeley.edu> Date: 18 Nov 89 04:33:37 GMT Sender: daemon@ucbvax.BERKELEY.EDU Reply-To: Forth Interest Group International List Organization: The Internet Lines: 134 One purpose of a Forth written is C is to have Forth on machines upon which you otherwise couldn't get Forth. Another purpose is to be able to link a Forth interpreter into an application which is written in C. Another purpose is because some people just *want* a Forth written in C, for whatever reason. Another purpose is to get a Forth system "up and running" quickly on a new machine which already has a C compiler. It took me about 5 minutes to bring up C Forth 83 on a Sun 386i, without looking at any kind of manual whatsoever. I sell both assembly-language Forth and C Forth products. I recommend the assembly language versions where they are applicable. Given the choice, I personally prefer to use the assembly language version. I don't always have choice. Given the choice between C Forth and no Forth, I will choose C Forth. C is the "assembly language" of a Forth written in C . Given that, here is how you could write "D+" in C. Yes, you have to know some C to understand it or to have written it. You also have to understand binary arithmetic. I don't buy the argument that you can write assembly language without knowing assembly language. Learning just enough to get by is still learning, and even that must build upon a base of knowledge about other assembly languages and computer architecture in general (registers, condition codes, addressing, etc). Anyway, this is the straightforward version of D+ ; a slightly-optimized version appears later. (By the way, has anybody else noticed that the word "D+" sounds sort of like "deep lust"? (No, I don't hang out in adult video stores)). /* * Carry calculation (assumes 2's complement arithmetic): * (a) If both operands are positive, carry = 0. * (b) Else, if both operands are negative, carry = 1. * (c) Else it must be true that exactly one operand is negative, * so carry = 1 iff the sum of the operands is non-negative. * * This calculation takes advantage of the "short-circuit evaluation" * semantics of && and || , both for correctness and for efficiency. * It is likely that both operands are positive, so the expression will * be resolved by the &&, thus skipping the rest of the calculation. */ #define CARRY(a,b,r) ( ( (a|b) < 0 ) && (( (a&b) < 0 ) || ( r >= 0 )) ) int ah, al, bh, bl, rh, rl; /* arguments a,b result r */ bh = *sp++; bl = *sp++; /* Pop arguments */ ah = *sp++; al = *sp++; rl = al + bl; rh = ah + bh + CARRY(al,bl,rl); /* Calculate result */ *sp++ = rl; *sp++ = rh; /* Push result */ Discussion: This is not as fast as the equivalent machine code, but it's probably within a factor of roughly 2. It is portable to any 2's complement machine. Presumably, this implementation is readily-understandable by anybody who knows C . If "int" is replaced by "long", this definition should even provide 64-bit arithmetic on a 32-bit Forth running on a 16-bit machine, such as a PC. (Doing that in assembly language on a PC is likely to be quite an exercise, considering the shortage of registers). Implementation of D- and/or DNEGATE is left to the reader as an exercise. Mysterious "XFORTH" unmasked: Dr. Wavrik has been kind enough not to mention C Forth 83 by name while he points out its deficiencies. In the interest of calling a spade a spade, the mysterious "XFORTH" is my C Forth 83 product. I admit the deficiencies (UM* is indeed implemeted incorrectly. I thank Dr. Wavrik for pointing that out. The lack of D+ is corrected by this message). (To add to the possible confusion, Mikael Patel has a Forth in C which is actually named XForth. I believe that the XFORTH to which Dr. Wavrik is referring is C Forth 83). Optimized version: With that in mind, here is the optimized version of D+ for C Forth 83. These optimizations are based on knowledge of how the stack is implemented in C Forth 83 (with the top of stack "cached" in a register variable) and knowledge of some predeclared register variables. It's basically the same code with the arguments are declared slightly differently and some unnecessary "pushes" and "pops" eliminated. case DPLUS: { normal ah, bh; #define al scr #define bl tos #define rh tos #define rl *sp /* * Carry calculation (assumes 2's complement arithmetic): * (a) If both operands are positive, carry = 0. * (b) Else, if both operands are negative, carry = 1. * (c) Else it must be true that exactly one operand is negative, * so carry = 1 iff the sum of the operands is non-negative. * * This calculation takes advantage of the "short-circuit evaluation" * semantics of && and || , both for correctness and for efficiency. * It is likely that both operands are positive, so the expression will * be resolved by the &&, thus skipping the rest of the calculation. */ #define CARRY(a,b,r) ( ((a | b) < 0) && (((a & b) < 0) || (r >= 0)) ) bh = tos; bl = *sp++; ah = *sp++; al = *sp; rl = al + bl; rh = ah + bh + CARRY(al, bl, rl); #undef al #undef bh #undef rh #undef rl #undef CARRY } next;