\ Gforth primitives \ Copyright (C) 1995,1996,1997,1998 Free Software Foundation, Inc. \ This file is part of Gforth. \ Gforth is free software; you can redistribute it and/or \ modify it under the terms of the GNU General Public License \ as published by the Free Software Foundation; either version 2 \ of the License, or (at your option) any later version. \ This program is distributed in the hope that it will be useful, \ but WITHOUT ANY WARRANTY; without even the implied warranty of \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \ GNU General Public License for more details. \ You should have received a copy of the GNU General Public License \ along with this program; if not, write to the Free Software \ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. \ WARNING: This file is processed by m4. Make sure your identifiers \ don't collide with m4's (e.g. by undefining them). \ \ \ \ This file contains primitive specifications in the following format: \ \ forth name stack effect category [pronunciation] \ [""glossary entry""] \ C code \ [: \ Forth code] \ \ prims2x is pedantic about tabs vs. blanks. The fields of the first \ line of a primitive are separated by tabs, the stack items in a \ stack effect by blanks. \ \ Both pronounciation and stack items (in the stack effect) must \ conform to the C name syntax or the C compiler will complain. \ \ \ These specifications are automatically translated into C-code for the \ interpreter and into some other files. I hope that your C compiler has \ decent optimization, otherwise the automatically generated code will \ be somewhat slow. The Forth version of the code is included for manual \ compilers, so they will need to compile only the important words. \ \ Note that stack pointer adjustment is performed according to stack \ effect by automatically generated code and NEXT is automatically \ appended to the C code. Also, you can use the names in the stack \ effect in the C code. Stack access is automatic. One exception: if \ your code does not fall through, the results are not stored into the \ stack. Use different names on both sides of the '--', if you change a \ value (some stores to the stack are optimized away). \ \ \ \ The stack variables have the following types: \ \ name matches type \ f.* Bool \ c.* Char \ [nw].* Cell \ u.* UCell \ d.* DCell \ ud.* UDCell \ r.* Float \ a_.* Cell * \ c_.* Char * \ f_.* Float * \ df_.* DFloat * \ sf_.* SFloat * \ xt.* XT \ wid.* WID \ f83name.* F83Name * \ \ \ \ In addition the following names can be used: \ ip the instruction pointer \ sp the data stack pointer \ rp the parameter stack pointer \ lp the locals stack pointer \ NEXT executes NEXT \ cfa \ NEXT1 executes NEXT1 \ FLAG(x) makes a Forth flag from a C flag \ \ \ \ Percentages in comments are from Koopmans book: average/maximum use \ (taken from four, not very representative benchmarks) \ \ \ \ To do: \ \ throw execute, cfa and NEXT1 out? \ macroize *ip, ip++, *ip++ (pipelining)? \ these m4 macros would collide with identifiers undefine(`index') undefine(`shift') noop -- gforth ; : ; lit -- w gforth w = (Cell)NEXT_INST; INC_IP(1); : r> dup @ swap cell+ >r ; execute xt -- core ""Perform the semantics represented by the execution token, @i{xt}."" ip=IP; IF_TOS(TOS = sp[0]); EXEC(xt); perform a_addr -- gforth ""Equivalent to @code{@ execute}."" /* and pfe */ ip=IP; IF_TOS(TOS = sp[0]); EXEC(*(Xt *)a_addr); : @ execute ; \fhas? skipbranchprims 0= [IF] \+glocals branch-lp+!# -- gforth branch_lp_plus_store_number /* this will probably not be used */ branch_adjust_lp: lp += (Cell)(IP[1]); goto branch; \+ branch -- gforth branch: SET_IP((Xt *)(((Cell)IP)+(Cell)NEXT_INST)); : r> dup @ + >r ; \ condbranch(forthname,restline,code,forthcode) \ this is non-syntactical: code must open a brace that is closed by the macro define(condbranch, $1 $2 $3 SET_IP((Xt *)(((Cell)IP)+(Cell)NEXT_INST)); NEXT; } else INC_IP(1); $4 \+glocals $1-lp+!# $2_lp_plus_store_number $3 goto branch_adjust_lp; } else INC_IP(2); \+ ) condbranch(?branch,f -- f83 question_branch, if (f==0) { IF_TOS(TOS = sp[0]); ,: 0= dup \ !f !f r> dup @ \ !f !f IP branchoffset rot and + \ !f IP|IP+branchoffset swap 0= cell and + \ IP'' >r ;) \ we don't need an lp_plus_store version of the ?dup-stuff, because it \ is only used in if's (yet) \+xconds ?dup-?branch f -- f new question_dupe_question_branch ""The run-time procedure compiled by @code{?DUP-IF}."" if (f==0) { sp++; IF_TOS(TOS = sp[0]); SET_IP((Xt *)(((Cell)IP)+(Cell)NEXT_INST)); NEXT; } else INC_IP(1); ?dup-0=-?branch f -- new question_dupe_zero_equals_question_branch ""The run-time procedure compiled by @code{?DUP-0=-IF}."" /* the approach taken here of declaring the word as having the stack effect ( f -- ) and correcting for it in the branch-taken case costs a few cycles in that case, but is easy to convert to a CONDBRANCH invocation */ if (f!=0) { sp--; SET_IP((Xt *)(((Cell)IP)+(Cell)NEXT_INST)); NEXT; } else INC_IP(1); \+ \f[THEN] \fhas? skiploopprims 0= [IF] condbranch((next),-- cmFORTH paren_next, if ((*rp)--) { ,: r> r> dup 1- >r IF dup @ + >r ELSE cell+ >r THEN ;) condbranch((loop),-- gforth paren_loop, Cell index = *rp+1; Cell limit = rp[1]; if (index != limit) { *rp = index; ,: r> r> 1+ r> 2dup = IF >r 1- >r cell+ >r ELSE >r >r dup @ + >r THEN ;) condbranch((+loop),n -- gforth paren_plus_loop, /* !! check this thoroughly */ Cell index = *rp; /* sign bit manipulation and test: (x^y)<0 is equivalent to (x<0) != (y<0) */ /* dependent upon two's complement arithmetic */ Cell olddiff = index-rp[1]; if ((olddiff^(olddiff+n))>=0 /* the limit is not crossed */ || (olddiff^n)>=0 /* it is a wrap-around effect */) { #ifdef i386 *rp += n; #else *rp = index + n; #endif IF_TOS(TOS = sp[0]); ,: r> swap r> r> 2dup - >r 2 pick r@ + r@ xor 0< 0= 3 pick r> xor 0< 0= or IF >r + >r dup @ + >r ELSE >r >r drop cell+ >r THEN ;) \+xconds condbranch((-loop),u -- gforth paren_minus_loop, /* !! check this thoroughly */ Cell index = *rp; UCell olddiff = index-rp[1]; if (olddiff>u) { #ifdef i386 *rp -= u; #else *rp = index - u; #endif IF_TOS(TOS = sp[0]); ,) condbranch((s+loop),n -- gforth paren_symmetric_plus_loop, ""The run-time procedure compiled by S+LOOP. It loops until the index crosses the boundary between limit and limit-sign(n). I.e. a symmetric version of (+LOOP)."" /* !! check this thoroughly */ Cell index = *rp; Cell diff = index-rp[1]; Cell newdiff = diff+n; if (n<0) { diff = -diff; newdiff = -newdiff; } if (diff>=0 || newdiff<0) { #ifdef i386 *rp += n; #else *rp = index + n; #endif IF_TOS(TOS = sp[0]); ,) \+ unloop -- core rp += 2; : r> rdrop rdrop >r ; (for) ncount -- cmFORTH paren_for /* or (for) = >r -- collides with unloop! */ *--rp = 0; *--rp = ncount; : r> swap 0 >r >r >r ; (do) nlimit nstart -- gforth paren_do /* or do it in high-level? 0.09/0.23% */ *--rp = nlimit; *--rp = nstart; : r> swap rot >r >r >r ; (?do) nlimit nstart -- gforth paren_question_do *--rp = nlimit; *--rp = nstart; if (nstart == nlimit) { IF_TOS(TOS = sp[0]); goto branch; } else { INC_IP(1); } : 2dup = IF r> swap rot >r >r dup @ + >r ELSE r> swap rot >r >r cell+ >r THEN ; \ --> CORE-EXT \+xconds (+do) nlimit nstart -- gforth paren_plus_do *--rp = nlimit; *--rp = nstart; if (nstart >= nlimit) { IF_TOS(TOS = sp[0]); goto branch; } else { INC_IP(1); } : swap 2dup r> swap >r swap >r >= IF dup @ + ELSE cell+ THEN >r ; (u+do) ulimit ustart -- gforth paren_u_plus_do *--rp = ulimit; *--rp = ustart; if (ustart >= ulimit) { IF_TOS(TOS = sp[0]); goto branch; } else { INC_IP(1); } : swap 2dup r> swap >r swap >r u>= IF dup @ + ELSE cell+ THEN >r ; (-do) nlimit nstart -- gforth paren_minus_do *--rp = nlimit; *--rp = nstart; if (nstart <= nlimit) { IF_TOS(TOS = sp[0]); goto branch; } else { INC_IP(1); } : swap 2dup r> swap >r swap >r <= IF dup @ + ELSE cell+ THEN >r ; (u-do) ulimit ustart -- gforth paren_u_minus_do *--rp = ulimit; *--rp = ustart; if (ustart <= ulimit) { IF_TOS(TOS = sp[0]); goto branch; } else { INC_IP(1); } : swap 2dup r> swap >r swap >r u<= IF dup @ + ELSE cell+ THEN >r ; \+ \ don't make any assumptions where the return stack is!! \ implement this in machine code if it should run quickly! i -- n core n = *rp; : \ rp@ cell+ @ ; r> r> tuck >r >r ; i' -- w gforth i_tick ""loop end value"" w = rp[1]; : \ rp@ cell+ cell+ @ ; r> r> r> dup itmp ! >r >r >r itmp @ ; variable itmp j -- n core n = rp[2]; : \ rp@ cell+ cell+ cell+ @ ; r> r> r> r> dup itmp ! >r >r >r >r itmp @ ; [IFUNDEF] itmp variable itmp [THEN] k -- n gforth n = rp[4]; : \ rp@ [ 5 cells ] Literal + @ ; r> r> r> r> r> r> dup itmp ! >r >r >r >r >r >r itmp @ ; [IFUNDEF] itmp variable itmp [THEN] \f[THEN] \ digit is high-level: 0/0% move c_from c_to ucount -- core ""Copy the contents of @i{ucount} address units at @i{c-from} to @i{c-to}. @code{move} works correctly even if the two areas overlap."" memmove(c_to,c_from,ucount); /* make an Ifdef for bsd and others? */ : >r 2dup u< IF r> cmove> ELSE r> cmove THEN ; cmove c_from c_to u -- string ""Copy the contents of @i{ucount} characters from data space at @i{c-from} to @i{c-to}. The copy proceeds @code{char}-by-@code{char} from low address to high address; i.e., for overlapping areas it is safe if @i{c-to}=<@i{c-from}."" while (u-- > 0) *c_to++ = *c_from++; : bounds ?DO dup c@ I c! 1+ LOOP drop ; cmove> c_from c_to u -- string c_move_up ""Copy the contents of @i{ucount} characters from data space at @i{c-from} to @i{c-to}. The copy proceeds @code{char}-by-@code{char} from high address to low address; i.e., for overlapping areas it is safe if @i{c-to}>=@i{c-from}."" while (u-- > 0) c_to[u] = c_from[u]; : dup 0= IF drop 2drop exit THEN rot over + -rot bounds swap 1- DO 1- dup c@ I c! -1 +LOOP drop ; fill c_addr u c -- core "" If @i{u}>0, store character @i{c} in each of @i{u} consecutive @code{char} addresses in memory, starting at address @i{c-addr}."" memset(c_addr,c,u); : -rot bounds ?DO dup I c! LOOP drop ; compare c_addr1 u1 c_addr2 u2 -- n string ""Compare two strings lexicographically. If they are equal, @i{n} is 0; if the first string is smaller, @i{n} is -1; if the first string is larger, @i{n} is 1. Currently this is based on the machine's character comparison. In the future, this may change to consider the current locale and its collation order."" #ifdef MEMCMP_AS_SUBROUTINE n = gforth_memcmp(c_addr1, c_addr2, u10) n = 1; : rot 2dup - >r min swap -text dup IF rdrop ELSE drop r@ 0> IF rdrop -1 ELSE r> 1 and THEN THEN ; -text c_addr1 u c_addr2 -- n new dash_text #ifdef MEMCMP_AS_SUBROUTINE n = gforth_memcmp(c_addr1, c_addr2, u); #else n = memcmp(c_addr1, c_addr2, u); #endif if (n<0) n = -1; else if (n>0) n = 1; : swap bounds ?DO dup c@ I c@ = WHILE 1+ LOOP drop 0 ELSE c@ I c@ - unloop THEN -text-flag ; : -text-flag ( n -- -1/0/1 ) dup 0< IF drop -1 ELSE 0> 1 and THEN ; toupper c1 -- c2 gforth ""If @i{c1} is a lower-case character (in the current locale), @i{c2} is the equivalent upper-case character. All other characters are unchanged."" c2 = toupper(c1); : dup [char] a - [ char z char a - 1 + ] Literal u< bl and - ; capscomp c_addr1 u c_addr2 -- n new n = memcasecmp(c_addr1, c_addr2, u); /* !! use something that works in all locales */ if (n<0) n = -1; else if (n>0) n = 1; : swap bounds ?DO dup c@ I c@ <> IF dup c@ toupper I c@ toupper = ELSE true THEN WHILE 1+ LOOP drop 0 ELSE c@ toupper I c@ toupper - unloop THEN -text-flag ; -trailing c_addr u1 -- c_addr u2 string dash_trailing ""Adjust the string specified by @i{c-addr, u1} to remove all trailing spaces. @i{u2} is the length of the modified string."" u2 = u1; while (u2>0 && c_addr[u2-1] == ' ') u2--; : BEGIN 1- 2dup + c@ bl = WHILE dup 0= UNTIL ELSE 1+ THEN ; /string c_addr1 u1 n -- c_addr2 u2 string slash_string ""Adjust the string specified by @i{c-addr1, u1} to remove @i{n} characters from the start of the string."" c_addr2 = c_addr1+n; u2 = u1-n; : tuck - >r + r> dup 0< IF - 0 THEN ; + n1 n2 -- n core plus n = n1+n2; \ PFE-0.9.14 has it differently, but the next release will have it as follows under+ n1 n2 n3 -- n n2 gforth under_plus ""add @i{n3} to @i{n1} (giving @i{n})"" n = n1+n3; : rot + swap ; - n1 n2 -- n core minus n = n1-n2; : negate + ; negate n1 -- n2 core /* use minus as alias */ n2 = -n1; : invert 1+ ; 1+ n1 -- n2 core one_plus n2 = n1+1; : 1 + ; 1- n1 -- n2 core one_minus n2 = n1-1; : 1 - ; max n1 n2 -- n core if (n1 IF swap THEN drop ; abs n1 -- n2 core if (n1<0) n2 = -n1; else n2 = n1; : dup 0< IF negate THEN ; * n1 n2 -- n core star n = n1*n2; : um* drop ; / n1 n2 -- n core slash n = n1/n2; : /mod nip ; mod n1 n2 -- n core n = n1%n2; : /mod drop ; /mod n1 n2 -- n3 n4 core slash_mod n4 = n1/n2; n3 = n1%n2; /* !! is this correct? look into C standard! */ : >r s>d r> fm/mod ; 2* n1 -- n2 core two_star n2 = 2*n1; : dup + ; 2/ n1 -- n2 core two_slash /* !! is this still correct? */ n2 = n1>>1; : dup MINI and IF 1 ELSE 0 THEN [ bits/byte cell * 1- ] literal 0 DO 2* swap dup 2* >r MINI and IF 1 ELSE 0 THEN or r> swap LOOP nip ; fm/mod d1 n1 -- n2 n3 core f_m_slash_mod ""Floored division: @i{d1} = @i{n3}*@i{n1}+@i{n2}, @i{n1}>@i{n2}>=0 or 0>=@i{n2}>@i{n1}."" #ifdef BUGGY_LONG_LONG DCell r = fmdiv(d1,n1); n2=r.hi; n3=r.lo; #else /* assumes that the processor uses either floored or symmetric division */ n3 = d1/n1; n2 = d1%n1; /* note that this 1%-3>0 is optimized by the compiler */ if (1%-3>0 && (d1<0) != (n1<0) && n2!=0) { n3--; n2+=n1; } #endif : dup >r dup 0< IF negate >r dnegate r> THEN over 0< IF tuck + swap THEN um/mod r> 0< IF swap negate swap THEN ; sm/rem d1 n1 -- n2 n3 core s_m_slash_rem ""Symmetric division: @i{d1} = @i{n3}*@i{n1}+@i{n2}, sign(@i{n2})=sign(@i{d1}) or 0."" #ifdef BUGGY_LONG_LONG DCell r = smdiv(d1,n1); n2=r.hi; n3=r.lo; #else /* assumes that the processor uses either floored or symmetric division */ n3 = d1/n1; n2 = d1%n1; /* note that this 1%-3<0 is optimized by the compiler */ if (1%-3<0 && (d1<0) != (n1<0) && n2!=0) { n3++; n2-=n1; } #endif : over >r dup >r abs -rot dabs rot um/mod r> r@ xor 0< IF negate THEN r> 0< IF swap negate swap THEN ; m* n1 n2 -- d core m_star #ifdef BUGGY_LONG_LONG d = mmul(n1,n2); #else d = (DCell)n1 * (DCell)n2; #endif : 2dup 0< and >r 2dup swap 0< and >r um* r> - r> - ; um* u1 u2 -- ud core u_m_star /* use u* as alias */ #ifdef BUGGY_LONG_LONG ud = ummul(u1,u2); #else ud = (UDCell)u1 * (UDCell)u2; #endif : >r >r 0 0 r> r> [ 8 cells ] literal 0 DO over >r dup >r 0< and d2*+ drop r> 2* r> swap LOOP 2drop ; : d2*+ ( ud n -- ud+n c ) over MINI and >r >r 2dup d+ swap r> + swap r> ; um/mod ud u1 -- u2 u3 core u_m_slash_mod ""ud=u3*u1+u2, u1>u2>=0"" #ifdef BUGGY_LONG_LONG UDCell r = umdiv(ud,u1); u2=r.hi; u3=r.lo; #else u3 = ud/u1; u2 = ud%u1; #endif : 0 swap [ 8 cells 1 + ] literal 0 ?DO /modstep LOOP drop swap 1 rshift or swap ; : /modstep ( ud c R: u -- ud-?u c R: u ) >r over r@ u< 0= or IF r@ - 1 ELSE 0 THEN d2*+ r> ; : d2*+ ( ud n -- ud+n c ) over MINI and >r >r 2dup d+ swap r> + swap r> ; m+ d1 n -- d2 double m_plus #ifdef BUGGY_LONG_LONG d2.lo = d1.lo+n; d2.hi = d1.hi - (n<0) + (d2.lod d+ ; d+ d1 d2 -- d double d_plus #ifdef BUGGY_LONG_LONG d.lo = d1.lo+d2.lo; d.hi = d1.hi + d2.hi + (d.lor tuck + swap over u> r> swap - ; d- d1 d2 -- d double d_minus #ifdef BUGGY_LONG_LONG d.lo = d1.lo - d2.lo; d.hi = d1.hi-d2.hi-(d1.lo>(CELL_BITS-1)); #else d2 = 2*d1; #endif : 2dup d+ ; d2/ d1 -- d2 double d_two_slash #ifdef BUGGY_LONG_LONG d2.hi = d1.hi>>1; d2.lo= (d1.lo>>1) | (d1.hi<<(CELL_BITS-1)); #else d2 = d1>>1; #endif : dup 1 and >r 2/ swap 2/ [ 1 8 cells 1- lshift 1- ] Literal and r> IF [ 1 8 cells 1- lshift ] Literal + THEN swap ; and w1 w2 -- w core w = w1&w2; or w1 w2 -- w core w = w1|w2; : invert swap invert and invert ; xor w1 w2 -- w core w = w1^w2; invert w1 -- w2 core w2 = ~w1; : MAXU xor ; rshift u1 n -- u2 core u2 = u1>>n; : 0 ?DO 2/ MAXI and LOOP ; lshift u1 n -- u2 core u2 = u1< $2 -- f $7 $3different f = FLAG($4!=$5); : [ char $1x char 0 = [IF] ] IF true ELSE false THEN [ [ELSE] ] xor 0<> [ [THEN] ] ; $1< $2 -- f $8 $3less f = FLAG($4<$5); : [ char $1x char 0 = [IF] ] MINI and 0<> [ [ELSE] char $1x char u = [IF] ] 2dup xor 0< IF nip ELSE - THEN 0< [ [ELSE] ] MINI xor >r MINI xor r> u< [ [THEN] [THEN] ] ; $1> $2 -- f $9 $3greater f = FLAG($4>$5); : [ char $1x char 0 = [IF] ] negate [ [ELSE] ] swap [ [THEN] ] $1< ; $1<= $2 -- f gforth $3less_or_equal f = FLAG($4<=$5); : $1> 0= ; $1>= $2 -- f gforth $3greater_or_equal f = FLAG($4>=$5); : [ char $1x char 0 = [IF] ] negate [ [ELSE] ] swap [ [THEN] ] $1<= ; ) comparisons(0, n, zero_, n, 0, core, core-ext, core, core-ext) comparisons(, n1 n2, , n1, n2, core, core-ext, core, core) comparisons(u, u1 u2, u_, u1, u2, gforth, gforth, core, core-ext) \ dcomparisons(prefix, args, prefix, arg1, arg2, wordsets...) define(dcomparisons, $1= $2 -- f $6 $3equals #ifdef BUGGY_LONG_LONG f = FLAG($4.lo==$5.lo && $4.hi==$5.hi); #else f = FLAG($4==$5); #endif $1<> $2 -- f $7 $3different #ifdef BUGGY_LONG_LONG f = FLAG($4.lo!=$5.lo || $4.hi!=$5.hi); #else f = FLAG($4!=$5); #endif $1< $2 -- f $8 $3less #ifdef BUGGY_LONG_LONG f = FLAG($4.hi==$5.hi ? $4.lo<$5.lo : $4.hi<$5.hi); #else f = FLAG($4<$5); #endif $1> $2 -- f $9 $3greater #ifdef BUGGY_LONG_LONG f = FLAG($4.hi==$5.hi ? $4.lo>$5.lo : $4.hi>$5.hi); #else f = FLAG($4>$5); #endif $1<= $2 -- f gforth $3less_or_equal #ifdef BUGGY_LONG_LONG f = FLAG($4.hi==$5.hi ? $4.lo<=$5.lo : $4.hi<=$5.hi); #else f = FLAG($4<=$5); #endif $1>= $2 -- f gforth $3greater_or_equal #ifdef BUGGY_LONG_LONG f = FLAG($4.hi==$5.hi ? $4.lo>=$5.lo : $4.hi>=$5.hi); #else f = FLAG($4>=$5); #endif ) \+dcomps dcomparisons(d, d1 d2, d_, d1, d2, double, gforth, double, gforth) dcomparisons(d0, d, d_zero_, d, DZERO, double, gforth, double, gforth) dcomparisons(du, ud1 ud2, d_u_, ud1, ud2, gforth, gforth, double-ext, gforth) \+ within u1 u2 u3 -- f core-ext ""u2=r - r> u< ; sp@ -- a_addr gforth sp_fetch a_addr = sp+1; sp! a_addr -- gforth sp_store sp = a_addr; /* works with and without TOS caching */ rp@ -- a_addr gforth rp_fetch a_addr = rp; rp! a_addr -- gforth rp_store rp = a_addr; \+floating fp@ -- f_addr gforth fp_fetch f_addr = fp; fp! f_addr -- gforth fp_store fp = f_addr; \+ ;s -- gforth semis ""The primitive compiled by @code{EXIT}."" SET_IP((Xt *)(*rp++)); >r w -- core to_r *--rp = w; : (>r) ; : (>r) rp@ cell+ @ rp@ ! rp@ cell+ ! ; r> -- w core r_from w = *rp++; : rp@ cell+ @ rp@ @ rp@ cell+ ! (rdrop) rp@ ! ; Create (rdrop) ' ;s A, rdrop -- gforth rp++; : r> r> drop >r ; 2>r w1 w2 -- core-ext two_to_r *--rp = w1; *--rp = w2; : swap r> swap >r swap >r >r ; 2r> -- w1 w2 core-ext two_r_from w2 = *rp++; w1 = *rp++; : r> r> swap r> swap >r swap ; 2r@ -- w1 w2 core-ext two_r_fetch w2 = rp[0]; w1 = rp[1]; : i' j ; 2rdrop -- gforth two_r_drop rp+=2; : r> r> drop r> drop >r ; over w1 w2 -- w1 w2 w1 core : sp@ cell+ @ ; drop w -- core : IF THEN ; swap w1 w2 -- w2 w1 core : >r (swap) ! r> (swap) @ ; Variable (swap) dup w -- w w core : sp@ @ ; rot w1 w2 w3 -- w2 w3 w1 core rote : [ defined? (swap) [IF] ] (swap) ! (rot) ! >r (rot) @ (swap) @ r> ; Variable (rot) [ELSE] ] >r swap r> swap ; [THEN] -rot w1 w2 w3 -- w3 w1 w2 gforth not_rote : rot rot ; nip w1 w2 -- w2 core-ext : swap drop ; tuck w1 w2 -- w2 w1 w2 core-ext : swap over ; ?dup w -- w core question_dupe if (w!=0) { IF_TOS(*sp-- = w;) #ifndef USE_TOS *--sp = w; #endif } : dup IF dup THEN ; pick u -- w core-ext w = sp[u+1]; : 1+ cells sp@ + @ ; 2drop w1 w2 -- core two_drop : drop drop ; 2dup w1 w2 -- w1 w2 w1 w2 core two_dupe : over over ; 2over w1 w2 w3 w4 -- w1 w2 w3 w4 w1 w2 core two_over : 3 pick 3 pick ; 2swap w1 w2 w3 w4 -- w3 w4 w1 w2 core two_swap : rot >r rot r> ; 2rot w1 w2 w3 w4 w5 w6 -- w3 w4 w5 w6 w1 w2 double-ext two_rote : >r >r 2swap r> r> 2swap ; 2nip w1 w2 w3 w4 -- w3 w4 gforth two_nip : 2swap 2drop ; 2tuck w1 w2 w3 w4 -- w3 w4 w1 w2 w3 w4 gforth two_tuck : 2swap 2over ; \ toggle is high-level: 0.11/0.42% @ a_addr -- w core fetch "" Read from the cell at address @i{a-addr}, and return its contents, @i{w}."" w = *a_addr; ! w a_addr -- core store "" Write the value @i{w} to the cell at address @i{a-addr}."" *a_addr = w; +! n a_addr -- core plus_store "" Add @i{n} to the value stored in the cell at address @i{a-addr}."" *a_addr += n; : tuck @ + swap ! ; c@ c_addr -- c core c_fetch "" Read from the char at address @i{c-addr}, and return its contents, @i{c}."" c = *c_addr; : [ bigendian [IF] ] [ cell>bit 4 = [IF] ] dup [ 0 cell - ] Literal and @ swap 1 and IF $FF and ELSE 8>> THEN ; [ [ELSE] ] dup [ cell 1- ] literal and tuck - @ swap [ cell 1- ] literal xor 0 ?DO 8>> LOOP $FF and [ [THEN] ] [ [ELSE] ] [ cell>bit 4 = [IF] ] dup [ 0 cell - ] Literal and @ swap 1 and IF 8>> ELSE $FF and THEN [ [ELSE] ] dup [ cell 1- ] literal and tuck - @ swap 0 ?DO 8>> LOOP 255 and [ [THEN] ] [ [THEN] ] ; : 8>> 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ ; c! c c_addr -- core c_store "" Write the value @i{c} to the char at address @i{c-addr}."" *c_addr = c; : [ bigendian [IF] ] [ cell>bit 4 = [IF] ] tuck 1 and IF $FF and ELSE 8<< THEN >r dup -2 and @ over 1 and cells masks + @ and r> or swap -2 and ! ; Create masks $00FF , $FF00 , [ELSE] ] dup [ cell 1- ] literal and dup [ cell 1- ] literal xor >r - dup @ $FF r@ 0 ?DO 8<< LOOP invert and rot $FF and r> 0 ?DO 8<< LOOP or swap ! ; [THEN] [ELSE] ] [ cell>bit 4 = [IF] ] tuck 1 and IF 8<< ELSE $FF and THEN >r dup -2 and @ over 1 and cells masks + @ and r> or swap -2 and ! ; Create masks $FF00 , $00FF , [ELSE] ] dup [ cell 1- ] literal and dup >r - dup @ $FF r@ 0 ?DO 8<< LOOP invert and rot $FF and r> 0 ?DO 8<< LOOP or swap ! ; [THEN] [THEN] : 8<< 2* 2* 2* 2* 2* 2* 2* 2* ; 2! w1 w2 a_addr -- core two_store "" Write the value @i{w1, w2} to the double at address @i{a-addr}."" a_addr[0] = w2; a_addr[1] = w1; : tuck ! cell+ ! ; 2@ a_addr -- w1 w2 core two_fetch "" Read from the double at address @i{a-addr}, and return its contents, @i{w1, w2}."" w2 = a_addr[0]; w1 = a_addr[1]; : dup cell+ @ swap @ ; cell+ a_addr1 -- a_addr2 core cell_plus "" Increment @i{a-addr1} by the number of address units corresponding to the size of one cell, to give @i{a-addr2}."" a_addr2 = a_addr1+1; : cell + ; cells n1 -- n2 core "" @i{n2} is the number of address units corresponding to @i{n1} cells."" n2 = n1 * sizeof(Cell); : [ cell 2/ dup [IF] ] 2* [ [THEN] 2/ dup [IF] ] 2* [ [THEN] 2/ dup [IF] ] 2* [ [THEN] 2/ dup [IF] ] 2* [ [THEN] drop ] ; char+ c_addr1 -- c_addr2 core char_plus "" Increment @i{c-addr1} by the number of address units corresponding to the size of one char, to give @i{c-addr2}."" c_addr2 = c_addr1 + 1; : 1+ ; (chars) n1 -- n2 gforth paren_chars n2 = n1 * sizeof(Char); : ; count c_addr1 -- c_addr2 u core "" If @i{c-add1} is the address of a counted string return the length of the string, @i{u}, and the address of its first character, @i{c-addr2}."" u = *c_addr1; c_addr2 = c_addr1+1; : dup 1+ swap c@ ; (f83find) c_addr u f83name1 -- f83name2 new paren_f83find for (; f83name1 != NULL; f83name1 = (struct F83Name *)(f83name1->next)) if ((UCell)F83NAME_COUNT(f83name1)==u && memcasecmp(c_addr, f83name1->name, u)== 0 /* or inline? */) break; f83name2=f83name1; : BEGIN dup WHILE (find-samelen) dup WHILE >r 2dup r@ cell+ char+ capscomp 0= IF 2drop r> EXIT THEN r> @ REPEAT THEN nip nip ; : (find-samelen) ( u f83name1 -- u f83name2/0 ) BEGIN 2dup cell+ c@ $1F and <> WHILE @ dup 0= UNTIL THEN ; \+hash (hashfind) c_addr u a_addr -- f83name2 new paren_hashfind struct F83Name *f83name1; f83name2=NULL; while(a_addr != NULL) { f83name1=(struct F83Name *)(a_addr[1]); a_addr=(Cell *)(a_addr[0]); if ((UCell)F83NAME_COUNT(f83name1)==u && memcasecmp(c_addr, f83name1->name, u)== 0 /* or inline? */) { f83name2=f83name1; break; } } : BEGIN dup WHILE 2@ >r >r dup r@ cell+ c@ $1F and = IF 2dup r@ cell+ char+ capscomp 0= IF 2drop r> rdrop EXIT THEN THEN rdrop r> REPEAT nip nip ; (tablefind) c_addr u a_addr -- f83name2 new paren_tablefind ""A case-sensitive variant of @code{(hashfind)}"" struct F83Name *f83name1; f83name2=NULL; while(a_addr != NULL) { f83name1=(struct F83Name *)(a_addr[1]); a_addr=(Cell *)(a_addr[0]); if ((UCell)F83NAME_COUNT(f83name1)==u && #ifdef MEMCMP_AS_SUBROUTINE gforth_memcmp(c_addr, f83name1->name, u)== 0 /* or inline? */) #else memcmp(c_addr, f83name1->name, u)== 0 /* or inline? */) #endif { f83name2=f83name1; break; } } : BEGIN dup WHILE 2@ >r >r dup r@ cell+ c@ $1F and = IF 2dup r@ cell+ char+ -text 0= IF 2drop r> rdrop EXIT THEN THEN rdrop r> REPEAT nip nip ; (hashkey) c_addr u1 -- u2 gforth paren_hashkey u2=0; while(u1--) u2+=(Cell)toupper(*c_addr++); : 0 -rot bounds ?DO I c@ toupper + LOOP ; (hashkey1) c_addr u ubits -- ukey gforth paren_hashkey1 ""ukey is the hash key for the string c_addr u fitting in ubits bits"" /* this hash function rotates the key at every step by rot bits within ubits bits and xors it with the character. This function does ok in the chi-sqare-test. Rot should be <=7 (preferably <=5) for ASCII strings (larger if ubits is large), and should share no divisors with ubits. */ unsigned rot = ((char []){5,0,1,2,3,4,5,5,5,5,3,5,5,5,5,7,5,5,5,5,7,5,5,5,5,6,5,5,5,5,7,5,5})[ubits]; Char *cp = c_addr; for (ukey=0; cp>(ubits-rot))) ^ toupper(*cp)) & ((1<r tuck - 2swap r> 0 2swap bounds ?DO dup 4 pick lshift swap 3 pick rshift or I c@ toupper xor over and LOOP nip nip nip ; Create rot-values 5 c, 0 c, 1 c, 2 c, 3 c, 4 c, 5 c, 5 c, 5 c, 5 c, 3 c, 5 c, 5 c, 5 c, 5 c, 7 c, 5 c, 5 c, 5 c, 5 c, 7 c, 5 c, 5 c, 5 c, 5 c, 6 c, 5 c, 5 c, 5 c, 5 c, 7 c, 5 c, 5 c, \+ (parse-white) c_addr1 u1 -- c_addr2 u2 gforth paren_parse_white /* use !isgraph instead of isspace? */ Char *endp = c_addr1+u1; while (c_addr1 WHILE 1 /string REPEAT THEN nip - ; aligned c_addr -- a_addr core "" @i{a-addr} is the first aligned address greater than or equal to @i{c-addr}."" a_addr = (Cell *)((((Cell)c_addr)+(sizeof(Cell)-1))&(-sizeof(Cell))); : [ cell 1- ] Literal + [ -1 cells ] Literal and ; faligned c_addr -- f_addr float f_aligned "" @i{f-addr} is the first float-aligned address greater than or equal to @i{c-addr}."" f_addr = (Float *)((((Cell)c_addr)+(sizeof(Float)-1))&(-sizeof(Float))); : [ 1 floats 1- ] Literal + [ -1 floats ] Literal and ; >body xt -- a_addr core to_body a_addr = PFA(xt); : 2 cells + ; \ threading stuff is currently only interesting if we have a compiler \fhas? standardthreading has? compiler and [IF] >code-address xt -- c_addr gforth to_code_address ""@i{c-addr} is the code address of the word @i{xt}."" /* !! This behaves installation-dependently for DOES-words */ c_addr = (Address)CODE_ADDRESS(xt); : @ ; >does-code xt -- a_addr gforth to_does_code ""If @i{xt} is the execution token of a defining-word-defined word, @i{a-addr} is the start of the Forth code after the @code{DOES>}; Otherwise @i{a-addr} is 0."" a_addr = (Cell *)DOES_CODE(xt); : cell+ @ ; code-address! c_addr xt -- gforth code_address_store ""Create a code field with code address @i{c-addr} at @i{xt}."" MAKE_CF(xt, c_addr); CACHE_FLUSH(xt,(size_t)PFA(0)); : ! ; does-code! a_addr xt -- gforth does_code_store ""Create a code field at @i{xt} for a defining-word-defined word; @i{a-addr} is the start of the Forth code after @code{DOES>}."" MAKE_DOES_CF(xt, a_addr); CACHE_FLUSH(xt,(size_t)PFA(0)); : dodoes: over ! cell+ ! ; does-handler! a_addr -- gforth does_handler_store ""Create a @code{DOES>}-handler at address @i{a-addr}. Usually, @i{a-addr} points just behind a @code{DOES>}."" MAKE_DOES_HANDLER(a_addr); CACHE_FLUSH((caddr_t)a_addr,DOES_HANDLER_SIZE); : drop ; /does-handler -- n gforth slash_does_handler ""The size of a @code{DOES>}-handler (includes possible padding)."" /* !! a constant or environmental query might be better */ n = DOES_HANDLER_SIZE; : 2 cells ; threading-method -- n gforth threading_method ""0 if the engine is direct threaded. Note that this may change during the lifetime of an image."" #if defined(DOUBLY_INDIRECT) n=2; #else # if defined(DIRECT_THREADED) n=0; # else n=1; # endif #endif : 1 ; \f[THEN] key-file wfileid -- n gforth paren_key_file #ifdef HAS_FILE fflush(stdout); n = key((FILE*)wfileid); #else n = key(stdin); #endif key?-file wfileid -- n facility key_q_file #ifdef HAS_FILE fflush(stdout); n = key_query((FILE*)wfileid); #else n = key_query(stdin); #endif \+os stdin -- wfileid gforth wfileid = (Cell)stdin; stdout -- wfileid gforth wfileid = (Cell)stdout; stderr -- wfileid gforth wfileid = (Cell)stderr; form -- urows ucols gforth ""The number of lines and columns in the terminal. These numbers may change with the window size."" /* we could block SIGWINCH here to get a consistent size, but I don't think this is necessary or always beneficial */ urows=rows; ucols=cols; flush-icache c_addr u -- gforth flush_icache ""Make sure that the instruction cache of the processor (if there is one) does not contain stale data at @i{c-addr} and @i{u} bytes afterwards. @code{END-CODE} performs a @code{flush-icache} automatically. Caveat: @code{flush-icache} might not work on your installation; this is usually the case if direct threading is not supported on your machine (take a look at your @file{machine.h}) and your machine has a separate instruction cache. In such cases, @code{flush-icache} does nothing instead of flushing the instruction cache."" FLUSH_ICACHE(c_addr,u); (bye) n -- gforth paren_bye return (Label *)n; (system) c_addr u -- wretval wior gforth peren_system #ifndef MSDOS int old_tp=terminal_prepped; deprep_terminal(); #endif wretval=system(cstr(c_addr,u,1)); /* ~ expansion on first part of string? */ wior = IOR(wretval==-1 || (wretval==127 && errno != 0)); #ifndef MSDOS if (old_tp) prep_terminal(); #endif getenv c_addr1 u1 -- c_addr2 u2 gforth ""The string @i{c-addr1 u1} specifies an environment variable. The string @i{c-addr2 u2} is the host operating system's expansion of that environment variable. If the environment variable does not exist, @i{c-addr2 u2} specifies a string 0 characters in length."" c_addr2 = getenv(cstr(c_addr1,u1,1)); u2 = (c_addr2 == NULL ? 0 : strlen(c_addr2)); open-pipe c_addr u ntype -- wfileid wior gforth open_pipe wfileid=(Cell)popen(cstr(c_addr,u,1),fileattr[ntype]); /* ~ expansion of 1st arg? */ wior = IOR(wfileid==0); /* !! the man page says that errno is not set reliably */ close-pipe wfileid -- wretval wior gforth close_pipe wretval = pclose((FILE *)wfileid); wior = IOR(wretval==-1); time&date -- nsec nmin nhour nday nmonth nyear facility-ext time_and_date struct timeval time1; struct timezone zone1; struct tm *ltime; gettimeofday(&time1,&zone1); ltime=localtime((time_t *)&time1.tv_sec); nyear =ltime->tm_year+1900; nmonth=ltime->tm_mon+1; nday =ltime->tm_mday; nhour =ltime->tm_hour; nmin =ltime->tm_min; nsec =ltime->tm_sec; ms n -- facility-ext struct timeval timeout; timeout.tv_sec=n/1000; timeout.tv_usec=1000*(n%1000); (void)select(0,0,0,0,&timeout); allocate u -- a_addr wior memory ""Allocate @i{u} address units of contiguous data space. The initial contents of the data space is undefined. If the allocation is successful, @i{a-addr} is the start address of the allocated region and @i{wior} is 0. If the allocation fails, @i{a-addr} is undefined and @i{wior} is an implementation-defined I/O result code."" a_addr = (Cell *)malloc(u?u:1); wior = IOR(a_addr==NULL); free a_addr -- wior memory ""Return the region of data space starting at @i{a-addr} to the system. The regon must originally have been obtained using @code{allocate} or @code{resize}. If the operational is successful, @i{wior} is 0. If the operation fails, @i{wior} is an implementation-defined I/O result code."" free(a_addr); wior = 0; resize a_addr1 u -- a_addr2 wior memory ""Change the size of the allocated area at @i{a-addr1} to @i{u} address units, possibly moving the contents to a different area. @i{a-addr2} is the address of the resulting area. If the operational is successful, @i{wior} is 0. If the operation fails, @i{wior} is an implementation-defined I/O result code. If @i{a-addr1} is 0, Gforth's (but not the Standard) @code{resize} @code{allocate}s @i{u} address units."" /* the following check is not necessary on most OSs, but it is needed on SunOS 4.1.2. */ if (a_addr1==NULL) a_addr2 = (Cell *)malloc(u); else a_addr2 = (Cell *)realloc(a_addr1, u); wior = IOR(a_addr2==NULL); /* !! Define a return code */ strerror n -- c_addr u gforth c_addr = strerror(n); u = strlen(c_addr); strsignal n -- c_addr u gforth c_addr = strsignal(n); u = strlen(c_addr); call-c w -- gforth call_c ""Call the C function pointed to by @i{w}. The C function has to access the stack itself. The stack pointers are exported in the global variables @code{SP} and @code{FP}."" /* This is a first attempt at support for calls to C. This may change in the future */ IF_FTOS(fp[0]=FTOS); FP=fp; SP=sp; ((void (*)())w)(); sp=SP; fp=FP; IF_TOS(TOS=sp[0]); IF_FTOS(FTOS=fp[0]); \+ \+file close-file wfileid -- wior file close_file wior = IOR(fclose((FILE *)wfileid)==EOF); open-file c_addr u ntype -- wfileid wior file open_file wfileid = (Cell)fopen(tilde_cstr(c_addr, u, 1), fileattr[ntype]); #if defined(GO32) && defined(MSDOS) if(wfileid && !(ntype & 1)) setbuf((FILE*)wfileid, NULL); #endif wior = IOR(wfileid == 0); create-file c_addr u ntype -- wfileid wior file create_file Cell fd; fd = open(tilde_cstr(c_addr, u, 1), O_CREAT|O_TRUNC|ufileattr[ntype], 0666); if (fd != -1) { wfileid = (Cell)fdopen(fd, fileattr[ntype]); #if defined(GO32) && defined(MSDOS) if(wfileid && !(ntype & 1)) setbuf((FILE*)wfileid, NULL); #endif wior = IOR(wfileid == 0); } else { wfileid = 0; wior = IOR(1); } delete-file c_addr u -- wior file delete_file wior = IOR(unlink(tilde_cstr(c_addr, u, 1))==-1); rename-file c_addr1 u1 c_addr2 u2 -- wior file-ext rename_file ""Rename file @i{c_addr1 u1} to new name @i{c_addr2 u2}"" char *s1=tilde_cstr(c_addr2, u2, 1); wior = IOR(rename(tilde_cstr(c_addr1, u1, 0), s1)==-1); file-position wfileid -- ud wior file file_position /* !! use tell and lseek? */ ud = LONG2UD(ftell((FILE *)wfileid)); wior = IOR(UD2LONG(ud)==-1); reposition-file ud wfileid -- wior file reposition_file wior = IOR(fseek((FILE *)wfileid, UD2LONG(ud), SEEK_SET)==-1); file-size wfileid -- ud wior file file_size struct stat buf; wior = IOR(fstat(fileno((FILE *)wfileid), &buf)==-1); ud = LONG2UD(buf.st_size); resize-file ud wfileid -- wior file resize_file wior = IOR(ftruncate(fileno((FILE *)wfileid), UD2LONG(ud))==-1); read-file c_addr u1 wfileid -- u2 wior file read_file /* !! fread does not guarantee enough */ u2 = fread(c_addr, sizeof(Char), u1, (FILE *)wfileid); wior = FILEIO(u20) && (c_addr[u2-1]==NEWLINE)); } else { wior=0; u2=0; } \+ \+file write-file c_addr u1 wfileid -- wior file write_file /* !! fwrite does not guarantee enough */ { UCell u2 = fwrite(c_addr, sizeof(Char), u1, (FILE *)wfileid); wior = FILEIO(u2f d -- r float d_to_f #ifdef BUGGY_LONG_LONG extern double ldexp(double x, int exp); r = ldexp((Float)d.hi,CELL_BITS) + (Float)d.lo; #else r = d; #endif f>d r -- d float f_to_d #ifdef BUGGY_LONG_LONG d.hi = ldexp(r,-(int)(CELL_BITS)) - (r<0); d.lo = r-ldexp((Float)d.hi,CELL_BITS); #else d = r; #endif f! r f_addr -- float f_store "" Store the floating-point value @i{r} to address @i{f-addr}."" *f_addr = r; f@ f_addr -- r float f_fetch "" Fetch floating-point value @i{r} from address @i{f-addr}."" r = *f_addr; df@ df_addr -- r float-ext d_f_fetch "" Fetch the double-precision IEEE floating-point value @i{r} from the address @i{df-addr}."" #ifdef IEEE_FP r = *df_addr; #else !! df@ #endif df! r df_addr -- float-ext d_f_store "" Store the double-precision IEEE floating-point value @i{r} to the address @i{df-addr}."" #ifdef IEEE_FP *df_addr = r; #else !! df! #endif sf@ sf_addr -- r float-ext s_f_fetch "" Fetch the single-precision IEEE floating-point value @i{r} from the address @i{sf-addr}."" #ifdef IEEE_FP r = *sf_addr; #else !! sf@ #endif sf! r sf_addr -- float-ext s_f_store "" Store the single-precision IEEE floating-point value @i{r} to the address @i{sf-addr}."" #ifdef IEEE_FP *sf_addr = r; #else !! sf! #endif f+ r1 r2 -- r3 float f_plus r3 = r1+r2; f- r1 r2 -- r3 float f_minus r3 = r1-r2; f* r1 r2 -- r3 float f_star r3 = r1*r2; f/ r1 r2 -- r3 float f_slash r3 = r1/r2; f** r1 r2 -- r3 float-ext f_star_star ""@i{r3} is @i{r1} raised to the @i{r2}th power."" r3 = pow(r1,r2); fnegate r1 -- r2 float r2 = - r1; fdrop r -- float fdup r -- r r float fswap r1 r2 -- r2 r1 float fover r1 r2 -- r1 r2 r1 float frot r1 r2 r3 -- r2 r3 r1 float fnip r1 r2 -- r2 gforth ftuck r1 r2 -- r2 r1 r2 gforth float+ f_addr1 -- f_addr2 float float_plus "" Increment @i{f-addr1} by the number of address units corresponding to the size of one floating-point number, to give @i{f-addr2}."" f_addr2 = f_addr1+1; floats n1 -- n2 float ""@i{n2} is the number of address units corresponding to @i{n1} floating-point numbers."" n2 = n1*sizeof(Float); floor r1 -- r2 float ""Round towards the next smaller integral value, i.e., round toward negative infinity."" /* !! unclear wording */ r2 = floor(r1); fround r1 -- r2 float ""Round to the nearest integral value."" /* !! unclear wording */ #ifdef HAVE_RINT r2 = rint(r1); #else r2 = floor(r1+0.5); /* !! This is not quite true to the rounding rules given in the standard */ #endif fmax r1 r2 -- r3 float if (r1float c_addr u -- flag float to_float ""Attempt to convert the character string @i{c-addr u} to internal floating-point representation. If the string represents a valid floating-point number @i{r} is placed on the floating-point stack and @i{flag} is true. Otherwise, @i{flag} is false. A string of blanks is a special case and represents the flotaing-point number 0."" /* real signature: c_addr u -- r t / f */ Float r; char *number=cstr(c_addr, u, 1); char *endconv; while(isspace((unsigned)(number[--u])) && u>0); switch(number[u]) { case 'd': case 'D': case 'e': case 'E': break; default : u++; break; } number[u]='\0'; r=strtod(number,&endconv); if((flag=FLAG(!(Cell)*endconv))) { IF_FTOS(fp[0] = FTOS); fp += -1; FTOS = r; } else if(*endconv=='d' || *endconv=='D') { *endconv='E'; r=strtod(number,&endconv); if((flag=FLAG(!(Cell)*endconv))) { IF_FTOS(fp[0] = FTOS); fp += -1; FTOS = r; } } fabs r1 -- r2 float-ext r2 = fabs(r1); facos r1 -- r2 float-ext r2 = acos(r1); fasin r1 -- r2 float-ext r2 = asin(r1); fatan r1 -- r2 float-ext r2 = atan(r1); fatan2 r1 r2 -- r3 float-ext ""@i{r1/r2}=tan(@i{r3}). ANS Forth does not require, but probably intends this to be the inverse of @code{fsincos}. In gforth it is."" r3 = atan2(r1,r2); fcos r1 -- r2 float-ext r2 = cos(r1); fexp r1 -- r2 float-ext r2 = exp(r1); fexpm1 r1 -- r2 float-ext ""@i{r2}=@i{e}**@i{r1}@minus{}1"" #ifdef HAVE_EXPM1 extern double #ifdef NeXT const #endif expm1(double); r2 = expm1(r1); #else r2 = exp(r1)-1.; #endif fln r1 -- r2 float-ext r2 = log(r1); flnp1 r1 -- r2 float-ext ""@i{r2}=ln(@i{r1}+1)"" #ifdef HAVE_LOG1P extern double #ifdef NeXT const #endif log1p(double); r2 = log1p(r1); #else r2 = log(r1+1.); #endif flog r1 -- r2 float-ext ""The decimal logarithm."" r2 = log10(r1); falog r1 -- r2 float-ext ""@i{r2}=10**@i{r1}"" extern double pow10(double); r2 = pow10(r1); fsin r1 -- r2 float-ext r2 = sin(r1); fsincos r1 -- r2 r3 float-ext ""@i{r2}=sin(@i{r1}), @i{r3}=cos(@i{r1})"" r2 = sin(r1); r3 = cos(r1); fsqrt r1 -- r2 float-ext r2 = sqrt(r1); ftan r1 -- r2 float-ext r2 = tan(r1); : fsincos f/ ; fsinh r1 -- r2 float-ext r2 = sinh(r1); : fexpm1 fdup fdup 1. d>f f+ f/ f+ f2/ ; fcosh r1 -- r2 float-ext r2 = cosh(r1); : fexp fdup 1/f f+ f2/ ; ftanh r1 -- r2 float-ext r2 = tanh(r1); : f2* fexpm1 fdup 2. d>f f+ f/ ; fasinh r1 -- r2 float-ext r2 = asinh(r1); : fdup fdup f* 1. d>f f+ fsqrt f/ fatanh ; facosh r1 -- r2 float-ext r2 = acosh(r1); : fdup fdup f* 1. d>f f- fsqrt f+ fln ; fatanh r1 -- r2 float-ext r2 = atanh(r1); : fdup f0< >r fabs 1. d>f fover f- f/ f2* flnp1 f2/ r> IF fnegate THEN ; sfloats n1 -- n2 float-ext s_floats ""@i{n2} is the number of address units corresponding to @i{n1} single-precision IEEE floating-point numbers."" n2 = n1*sizeof(SFloat); dfloats n1 -- n2 float-ext d_floats ""@i{n2} is the number of address units corresponding to @i{n1} double-precision IEEE floating-point numbers."" n2 = n1*sizeof(DFloat); sfaligned c_addr -- sf_addr float-ext s_f_aligned "" @i{sf-addr} is the first single-float-aligned address greater than or equal to @i{c-addr}."" sf_addr = (SFloat *)((((Cell)c_addr)+(sizeof(SFloat)-1))&(-sizeof(SFloat))); : [ 1 sfloats 1- ] Literal + [ -1 sfloats ] Literal and ; dfaligned c_addr -- df_addr float-ext d_f_aligned "" @i{df-addr} is the first double-float-aligned address greater than or equal to @i{c-addr}."" df_addr = (DFloat *)((((Cell)c_addr)+(sizeof(DFloat)-1))&(-sizeof(DFloat))); : [ 1 dfloats 1- ] Literal + [ -1 dfloats ] Literal and ; \ The following words access machine/OS/installation-dependent \ Gforth internals \ !! how about environmental queries DIRECT-THREADED, \ INDIRECT-THREADED, TOS-CACHED, FTOS-CACHED, CODEFIELD-DOES */ \ local variable implementation primitives \+ \+glocals @local# -- w gforth fetch_local_number w = *(Cell *)(lp+(Cell)NEXT_INST); INC_IP(1); @local0 -- w new fetch_local_zero w = *(Cell *)(lp+0*sizeof(Cell)); @local1 -- w new fetch_local_four w = *(Cell *)(lp+1*sizeof(Cell)); @local2 -- w new fetch_local_eight w = *(Cell *)(lp+2*sizeof(Cell)); @local3 -- w new fetch_local_twelve w = *(Cell *)(lp+3*sizeof(Cell)); \+floating f@local# -- r gforth f_fetch_local_number r = *(Float *)(lp+(Cell)NEXT_INST); INC_IP(1); f@local0 -- r new f_fetch_local_zero r = *(Float *)(lp+0*sizeof(Float)); f@local1 -- r new f_fetch_local_eight r = *(Float *)(lp+1*sizeof(Float)); \+ laddr# -- c_addr gforth laddr_number /* this can also be used to implement lp@ */ c_addr = (Char *)(lp+(Cell)NEXT_INST); INC_IP(1); lp+!# -- gforth lp_plus_store_number ""used with negative immediate values it allocates memory on the local stack, a positive immediate argument drops memory from the local stack"" lp += (Cell)NEXT_INST; INC_IP(1); lp- -- new minus_four_lp_plus_store lp += -sizeof(Cell); lp+ -- new eight_lp_plus_store lp += sizeof(Float); lp+2 -- new sixteen_lp_plus_store lp += 2*sizeof(Float); lp! c_addr -- gforth lp_store lp = (Address)c_addr; >l w -- gforth to_l lp -= sizeof(Cell); *(Cell *)lp = w; \+floating f>l r -- gforth f_to_l lp -= sizeof(Float); *(Float *)lp = r; fpick u -- r gforth r = fp[u+1]; /* +1, because update of fp happens before this fragment */ : floats fp@ + f@ ; \+ \+ \+OS define(`uploop', `pushdef(`$1', `$2')_uploop(`$1', `$2', `$3', `$4', `$5')`'popdef(`$1')') define(`_uploop', `ifelse($1, `$3', `$5', `$4`'define(`$1', incr($1))_uploop(`$1', `$2', `$3', `$4', `$5')')') \ argflist(argnum): Forth argument list define(argflist, `ifelse($1, 0, `', `uploop(`_i', 1, $1, `format(`u%d ', _i)', `format(`u%d ', _i)')')') \ argdlist(argnum): declare C's arguments define(argdlist, `ifelse($1, 0, `', `uploop(`_i', 1, $1, `Cell, ', `Cell')')') \ argclist(argnum): pass C's arguments define(argclist, `ifelse($1, 0, `', `uploop(`_i', 1, $1, `format(`u%d, ', _i)', `format(`u%d', _i)')')') \ icall(argnum) define(icall, `icall$1 argflist($1)u -- uret gforth uret = (SYSCALL(Cell(*)(argdlist($1)))u)(argclist($1)); ') define(fcall, `fcall$1 argflist($1)u -- rret gforth rret = (SYSCALL(Float(*)(argdlist($1)))u)(argclist($1)); ') open-lib c_addr1 u1 -- u2 gforth open_lib #if defined(HAVE_LIBDL) || defined(HAVE_DLOPEN) #ifndef RTLD_GLOBAL #define RTLD_GLOBAL 0 #endif u2=(UCell) dlopen(cstr(c_addr1, u1, 1), RTLD_GLOBAL | RTLD_LAZY); #else # ifdef _WIN32 u2 = (Cell) GetModuleHandle(cstr(c_addr1, u1, 1)); # else #warning Define open-lib! u2 = 0; # endif #endif lib-sym c_addr1 u1 u2 -- u3 gforth lib_sym #if defined(HAVE_LIBDL) || defined(HAVE_DLOPEN) u3 = (UCell) dlsym((void*)u2,cstr(c_addr1, u1, 1)); #else # ifdef _WIN32 u3 = (Cell) GetProcAddress((HMODULE)u2, cstr(c_addr1, u1, 1)); # else #warning Define lib-sym! u3 = 0; # endif #endif uploop(i, 0, 7, `icall(i)') icall(20) uploop(i, 0, 7, `fcall(i)') fcall(20) \+ up! a_addr -- gforth up_store UP=up=(char *)a_addr; : up ! ; Variable UP wcall u -- gforth IF_FTOS(fp[0]=FTOS); FP=fp; sp=(SYSCALL(Cell(*)(Cell *, void *))u)(sp, &FP); fp=FP; IF_TOS(TOS=sp[0];) IF_FTOS(FTOS=fp[0]);