--- gforth/prim 1997/12/14 01:15:19 1.7 +++ gforth/prim 2009/05/12 16:54:58 1.242 @@ -1,12 +1,12 @@ \ Gforth primitives -\ Copyright (C) 1995,1996 Free Software Foundation, Inc. +\ Copyright (C) 1995,1996,1997,1998,2000,2003,2004,2005,2006,2007,2008 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 +\ as published by the Free Software Foundation, either version 3 \ of the License, or (at your option) any later version. \ This program is distributed in the hope that it will be useful, @@ -15,8 +15,7 @@ \ 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. +\ along with this program. If not, see http://www.gnu.org/licenses/. \ WARNING: This file is processed by m4. Make sure your identifiers @@ -26,19 +25,19 @@ \ \ This file contains primitive specifications in the following format: \ -\ forth name stack effect category [pronunciation] +\ 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. +\ Note: Fields in brackets are optional. Word specifications have to +\ be separated by at least one empty line \ \ Both pronounciation and stack items (in the stack effect) must -\ conform to the C name syntax or the C compiler will complain. -\ +\ conform to the C identifier syntax or the C compiler will complain. +\ If you don't have a pronounciation field, the Forth name is used, +\ and has to conform to the C identifier syntax. \ \ These specifications are automatically translated into C-code for the \ interpreter and into some other files. I hope that your C compiler has @@ -53,15 +52,18 @@ \ 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). -\ -\ +\ +\ For superinstructions the syntax is: +\ +\ forth-name [/ c-name] = forth-name forth-name ... +\ \ \ The stack variables have the following types: \ \ name matches type \ f.* Bool \ c.* Char -\ [nw].* Cell +\ [nw].* Cell \ u.* UCell \ d.* DCell \ ud.* UDCell @@ -72,8 +74,44 @@ \ df_.* DFloat * \ sf_.* SFloat * \ xt.* XT -\ wid.* WID \ f83name.* F83Name * + +\E stack data-stack sp Cell +\E stack fp-stack fp Float +\E stack return-stack rp Cell +\E +\E get-current prefixes set-current +\E +\E s" Bool" single data-stack type-prefix f +\E s" Char" single data-stack type-prefix c +\E s" Cell" single data-stack type-prefix n +\E s" Cell" single data-stack type-prefix w +\E s" UCell" single data-stack type-prefix u +\E s" DCell" double data-stack type-prefix d +\E s" UDCell" double data-stack type-prefix ud +\E s" Float" single fp-stack type-prefix r +\E s" Cell *" single data-stack type-prefix a_ +\E s" Char *" single data-stack type-prefix c_ +\E s" Float *" single data-stack type-prefix f_ +\E s" DFloat *" single data-stack type-prefix df_ +\E s" SFloat *" single data-stack type-prefix sf_ +\E s" Xt" single data-stack type-prefix xt +\E s" struct F83Name *" single data-stack type-prefix f83name +\E s" struct Longname *" single data-stack type-prefix longname +\E +\E data-stack stack-prefix S: +\E fp-stack stack-prefix F: +\E return-stack stack-prefix R: +\E inst-stream stack-prefix # +\E +\E set-current +\E store-optimization on +\E ' noop tail-nextp2 ! \ now INST_TAIL just stores, but does not jump +\E +\E `include-skipped-insts' on \ static superinsts include cells for components +\E \ useful for dynamic programming and +\E \ superinsts across entry points + \ \ \ @@ -99,474 +137,634 @@ \ throw execute, cfa and NEXT1 out? \ macroize *ip, ip++, *ip++ (pipelining)? +\ Stack caching setup + +ifdef(`STACK_CACHE_FILE', `include(STACK_CACHE_FILE)', `include(cache0.vmg)') + \ these m4 macros would collide with identifiers undefine(`index') undefine(`shift') +undefine(`symbols') -noop -- gforth -; +\F 0 [if] + +\ run-time routines for non-primitives. They are defined as +\ primitives, because that simplifies things. + +(docol) ( -- R:a_retaddr ) gforth-internal paren_docol +""run-time routine for colon definitions"" +#ifdef NO_IP +a_retaddr = next_code; +INST_TAIL; +goto **(Label *)PFA(CFA); +#else /* !defined(NO_IP) */ +a_retaddr = (Cell *)IP; +SET_IP((Xt *)PFA(CFA)); +#endif /* !defined(NO_IP) */ + +(docon) ( -- w ) gforth-internal paren_docon +""run-time routine for constants"" +w = *(Cell *)PFA(CFA); +#ifdef NO_IP +INST_TAIL; +goto *next_code; +#endif /* defined(NO_IP) */ + +(dovar) ( -- a_body ) gforth-internal paren_dovar +""run-time routine for variables and CREATEd words"" +a_body = PFA(CFA); +#ifdef NO_IP +INST_TAIL; +goto *next_code; +#endif /* defined(NO_IP) */ + +(douser) ( -- a_user ) gforth-internal paren_douser +""run-time routine for constants"" +a_user = (Cell *)(up+*(Cell *)PFA(CFA)); +#ifdef NO_IP +INST_TAIL; +goto *next_code; +#endif /* defined(NO_IP) */ + +(dodefer) ( -- ) gforth-internal paren_dodefer +""run-time routine for deferred words"" +#ifndef NO_IP +ip=IP; /* undo any ip updating that may have been performed by NEXT_P0 */ +#endif /* !defined(NO_IP) */ +SUPER_END; /* !! probably unnecessary and may lead to measurement errors */ +VM_JUMP(EXEC1(*(Xt *)PFA(CFA))); + +(dofield) ( n1 -- n2 ) gforth-internal paren_field +""run-time routine for fields"" +n2 = n1 + *(Cell *)PFA(CFA); +#ifdef NO_IP +INST_TAIL; +goto *next_code; +#endif /* defined(NO_IP) */ + +(dovalue) ( -- w ) gforth-internal paren_doval +""run-time routine for constants"" +w = *(Cell *)PFA(CFA); +#ifdef NO_IP +INST_TAIL; +goto *next_code; +#endif /* defined(NO_IP) */ + +(dodoes) ( -- a_body R:a_retaddr ) gforth-internal paren_dodoes +""run-time routine for @code{does>}-defined words"" +#ifdef NO_IP +a_retaddr = next_code; +a_body = PFA(CFA); +INST_TAIL; +#ifdef DEBUG +fprintf(stderr, "dodoes to %x, push %x\n", a_retaddr, a_body); +#endif +goto **(Label *)DOES_CODE1(CFA); +#else /* !defined(NO_IP) */ +a_retaddr = (Cell *)IP; +a_body = PFA(CFA); +#ifdef DEBUG +fprintf(stderr, "dodoes to %x, push %x\n", a_retaddr, a_body); +#endif +SET_IP(DOES_CODE1(CFA)); +#endif /* !defined(NO_IP) */ + +(does-handler) ( -- ) gforth-internal paren_does_handler +""just a slot to have an encoding for the DOESJUMP, +which is no longer used anyway (!! eliminate this)"" + +\F [endif] + +\g control + +noop ( -- ) gforth : ; -lit -- w gforth -w = (Cell)NEXT_INST; -INC_IP(1); -: - r> dup @ swap cell+ >r ; +call ( #a_callee -- R:a_retaddr ) new +""Call callee (a variant of docol with inline argument)."" +#ifdef NO_IP +assert(0); +INST_TAIL; +JUMP(a_callee); +#else +#ifdef DEBUG + { + CFA_TO_NAME((((Cell *)a_callee)-2)); + fprintf(stderr,"%08lx: call %08lx %.*s\n",(Cell)ip,(Cell)a_callee, + len,name); + } +#endif +a_retaddr = (Cell *)IP; +SET_IP((Xt *)a_callee); +#endif -execute xt -- core +execute ( xt -- ) core +""Perform the semantics represented by the execution token, @i{xt}."" +#ifdef DEBUG +fprintf(stderr, "execute %08x\n", xt); +#endif +#ifndef NO_IP ip=IP; -IF_TOS(TOS = sp[0]); -EXEC(xt); +#endif +SUPER_END; +VM_JUMP(EXEC1(xt)); -perform a_addr -- gforth -""equivalent to @code{@ execute}"" +perform ( a_addr -- ) gforth +""@code{@@ execute}."" /* and pfe */ +#ifndef NO_IP ip=IP; -IF_TOS(TOS = sp[0]); -EXEC(*(Xt *)a_addr); +#endif +SUPER_END; +VM_JUMP(EXEC1(*(Xt *)a_addr)); : @ execute ; -\+has? glocals [IF] +;s ( R:w -- ) gforth semis +""The primitive compiled by @code{EXIT}."" +#ifdef NO_IP +INST_TAIL; +goto *(void *)w; +#else +SET_IP((Xt *)w); +#endif + +unloop ( R:w1 R:w2 -- ) core +/* !! alias for 2rdrop */ +: + r> rdrop rdrop >r ; -branch-lp+!# -- gforth branch_lp_plus_store_number +lit-perform ( #a_addr -- ) new lit_perform +#ifndef NO_IP +ip=IP; +#endif +SUPER_END; +VM_JUMP(EXEC1(*(Xt *)a_addr)); + +does-exec ( #a_cfa -- R:nest a_pfa ) new does_exec +#ifdef NO_IP +/* compiled to LIT CALL by compile_prim */ +assert(0); +#else +a_pfa = PFA(a_cfa); +nest = (Cell)IP; +#ifdef DEBUG + { + CFA_TO_NAME(a_cfa); + fprintf(stderr,"%08lx: does %08lx %.*s\n", + (Cell)ip,(Cell)a_cfa,len,name); + } +#endif +SET_IP(DOES_CODE1(a_cfa)); +#endif + +\+glocals + +branch-lp+!# ( #a_target #nlocals -- ) gforth branch_lp_plus_store_number /* this will probably not be used */ -branch_adjust_lp: -lp += (Cell)(IP[1]); -goto branch; +lp += nlocals; +#ifdef NO_IP +INST_TAIL; +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +#endif -\+[THEN] +\+ -branch -- gforth -branch: -ip = (Xt *)(((Cell)IP)+(Cell)NEXT_INST); -NEXT_P0; +branch ( #a_target -- ) gforth +#ifdef NO_IP +INST_TAIL; +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +#endif : - r> dup @ + >r ; + r> @ >r ; -\ condbranch(forthname,restline,code,forthcode) +\ condbranch(forthname,stackeffect,restline,code1,code2,forthcode) \ this is non-syntactical: code must open a brace that is closed by the macro define(condbranch, -$1 $2 -$3 ip = (Xt *)(((Cell)IP)+(Cell)NEXT_INST); - NEXT_P0; - NEXT; +$1 ( `#'a_target $2 ) $3 +$4 #ifdef NO_IP +INST_TAIL; +#endif +$5 #ifdef NO_IP +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +ifelse(condbranch_opt,`1',`INST_TAIL; NEXT_P2;',`/* condbranch_opt=0 */') +#endif } -else - INC_IP(1); -$4 +ifelse(condbranch_opt,`1',`SUPER_CONTINUE;',`/* condbranch_opt=0 */') +$6 -\+has? glocals [IF] +\+glocals -$1-lp+!# $2_lp_plus_store_number -$3 goto branch_adjust_lp; +$1-lp+!`#' ( `#'a_target `#'nlocals $2 ) $3_lp_plus_store_number +$4 #ifdef NO_IP +INST_TAIL; +#endif +$5 lp += nlocals; +#ifdef NO_IP +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +ifelse(condbranch_opt,`1',`INST_TAIL; NEXT_P2;',`/* condbranch_opt=0 */') +#endif } -else - INC_IP(2); +ifelse(condbranch_opt,`1',`SUPER_CONTINUE;',`/* condbranch_opt=0 */') -\+[THEN] +\+ ) -condbranch(?branch,f -- f83 question_branch, -if (f==0) { - IF_TOS(TOS = sp[0]); +condbranch(?branch,f --,f83 question_branch, +,if (f==0) { ,: - 0= dup \ !f !f - r> dup @ \ !f !f IP branchoffset - rot and + \ !f IP|IP+branchoffset - swap 0= cell and + \ IP'' + 0= dup 0= \ !f f + r> tuck cell+ \ !f branchoffset f IP+ + and -rot @ and or \ f&IP+|!f&branch >r ;) \ we don't need an lp_plus_store version of the ?dup-stuff, because it \ is only used in if's (yet) -\+has? xconds [IF] +\+xconds -?dup-?branch f -- f new question_dupe_question_branch +?dup-?branch ( #a_target f -- S:... ) new question_dupe_question_branch ""The run-time procedure compiled by @code{?DUP-IF}."" if (f==0) { - sp++; - IF_TOS(TOS = sp[0]); - ip = (Xt *)(((Cell)IP)+(Cell)NEXT_INST); - NEXT_P0; - NEXT; +#ifdef NO_IP +INST_TAIL; +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +#endif +} else { +sp--; +sp[0]=f; } -else - INC_IP(1); -?dup-0=-?branch f -- new question_dupe_zero_equals_question_branch +?dup-0=-?branch ( #a_target f -- S:... ) 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--; - ip = (Xt *)(((Cell)IP)+(Cell)NEXT_INST); - NEXT_P0; - NEXT; + sp[0]=f; +#ifdef NO_IP + JUMP(a_target); +#else + SET_IP((Xt *)a_target); +#endif } -else - INC_IP(1); -\+[THEN] +\+ +\fhas? skiploopprims 0= [IF] -condbranch((next),-- cmFORTH paren_next, -if ((*rp)--) { +condbranch((next),R:n1 -- R:n2,cmFORTH paren_next, +n2=n1-1; +,if (n1) { ,: r> r> dup 1- >r - IF dup @ + >r ELSE cell+ >r THEN ;) + IF @ >r ELSE cell+ >r THEN ;) -condbranch((loop),-- gforth paren_loop, -Cell index = *rp+1; -Cell limit = rp[1]; -if (index != limit) { - *rp = index; +condbranch((loop),R:nlimit R:n1 -- R:nlimit R:n2,gforth paren_loop, +n2=n1+1; +,if (n2 != nlimit) { ,: r> r> 1+ r> 2dup = IF >r 1- >r cell+ >r - ELSE >r >r dup @ + >r THEN ;) + ELSE >r >r @ >r THEN ;) -condbranch((+loop),n -- gforth paren_plus_loop, +condbranch((+loop),n R:nlimit R:n1 -- R:nlimit R:n2,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]); +Cell olddiff = n1-nlimit; +n2=n1+n; +,if (((olddiff^(olddiff+n)) /* the limit is not crossed */ + &(olddiff^n)) /* OR it is a wrap-around effect */ + >=0) { /* & is used to avoid having two branches for gforth-native */ ,: 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 + IF >r + >r @ >r ELSE >r >r drop cell+ >r THEN ;) -\+has? xconds [IF] +\+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((-loop),u R:nlimit R:n1 -- R:nlimit R:n2,gforth paren_minus_loop, +UCell olddiff = n1-nlimit; +n2=n1-u; +,if (olddiff>u) { ,) -condbranch((s+loop),n -- gforth paren_symmetric_plus_loop, +condbranch((s+loop),n R:nlimit R:n1 -- R:nlimit R:n2,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 diff = n1-nlimit; 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]); +n2=n1+n; +,if (((~diff)|newdiff)<0) { /* use | to avoid two branches for gforth-native */ ,) -\+[THEN] - -unloop -- core -rp += 2; -: - r> rdrop rdrop >r ; +\+ -(for) ncount -- cmFORTH paren_for +(for) ( ncount -- R:nlimit R:ncount ) cmFORTH paren_for /* or (for) = >r -- collides with unloop! */ -*--rp = 0; -*--rp = ncount; +nlimit=0; : 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; +(do) ( nlimit nstart -- R:nlimit R:nstart ) gforth paren_do : r> swap rot >r >r >r ; -(?do) nlimit nstart -- gforth paren_question_do -*--rp = nlimit; -*--rp = nstart; +(?do) ( #a_target nlimit nstart -- R:nlimit R:nstart ) gforth paren_question_do +#ifdef NO_IP + INST_TAIL; +#endif if (nstart == nlimit) { - IF_TOS(TOS = sp[0]); - goto branch; - } -else { - INC_IP(1); +#ifdef NO_IP + JUMP(a_target); +#else + SET_IP((Xt *)a_target); +#endif } : 2dup = IF r> swap rot >r >r - dup @ + >r + @ >r ELSE r> swap rot >r >r cell+ >r THEN ; \ --> CORE-EXT -\+has? xconds [IF] +\+xconds -(+do) nlimit nstart -- gforth paren_plus_do -*--rp = nlimit; -*--rp = nstart; +(+do) ( #a_target nlimit nstart -- R:nlimit R:nstart ) gforth paren_plus_do +#ifdef NO_IP + INST_TAIL; +#endif if (nstart >= nlimit) { - IF_TOS(TOS = sp[0]); - goto branch; - } -else { - INC_IP(1); +#ifdef NO_IP + JUMP(a_target); +#else + SET_IP((Xt *)a_target); +#endif } : 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; +(u+do) ( #a_target ulimit ustart -- R:ulimit R:ustart ) gforth paren_u_plus_do +#ifdef NO_IP + INST_TAIL; +#endif if (ustart >= ulimit) { - IF_TOS(TOS = sp[0]); - goto branch; - } -else { - INC_IP(1); +#ifdef NO_IP +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +#endif } : 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; +(-do) ( #a_target nlimit nstart -- R:nlimit R:nstart ) gforth paren_minus_do +#ifdef NO_IP + INST_TAIL; +#endif if (nstart <= nlimit) { - IF_TOS(TOS = sp[0]); - goto branch; - } -else { - INC_IP(1); +#ifdef NO_IP +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +#endif } : 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; +(u-do) ( #a_target ulimit ustart -- R:ulimit R:ustart ) gforth paren_u_minus_do +#ifdef NO_IP + INST_TAIL; +#endif if (ustart <= ulimit) { - IF_TOS(TOS = sp[0]); - goto branch; - } -else { - INC_IP(1); +#ifdef NO_IP +JUMP(a_target); +#else +SET_IP((Xt *)a_target); +#endif } : swap 2dup r> swap >r swap >r u<= IF - dup @ + + @ ELSE cell+ THEN >r ; -\+[THEN] +\+ \ 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; +i ( R:n -- R:n n ) core : \ rp@ cell+ @ ; r> r> tuck >r >r ; -i' -- w gforth i_tick -""loop end value"" -w = rp[1]; +i' ( R:w R:w2 -- R:w R:w2 w ) gforth i_tick : \ rp@ cell+ cell+ @ ; r> r> r> dup itmp ! >r >r >r itmp @ ; variable itmp -j -- n core -n = rp[2]; +j ( R:w R:w1 R:w2 -- w R:w R:w1 R:w2 ) core : \ 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]; +k ( R:w R:w1 R:w2 R:w3 R:w4 -- w R:w R:w1 R:w2 R:w3 R:w4 ) gforth : \ 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 +\g strings + +move ( c_from c_to ucount -- ) core +""Copy the contents of @i{ucount} aus at @i{c-from} to +@i{c-to}. @code{move} works correctly even if the two areas overlap."" +/* !! note that the standard specifies addr, not c-addr */ 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 -while (u-- > 0) - *c_to++ = *c_from++; +cmove ( c_from c_to u -- ) string c_move +""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}."" +cmove(c_from,c_to,u); : bounds ?DO dup c@ I c! 1+ LOOP drop ; -cmove> c_from c_to u -- string c_move_up -while (u-- > 0) - c_to[u] = c_from[u]; +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}."" +cmove_up(c_from,c_to,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 +fill ( c_addr u c -- ) core +""Store @i{c} in @i{u} chars starting at @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 the strings lexicographically. If they are equal, n is 0; if -the first string is smaller, n is -1; if the first string is larger, n +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 considering the current +comparison. In the future, this may change to consider the current locale and its collation order."" -n = 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 -n = memcmp(c_addr1, c_addr2, u); -if (n<0) - n = -1; -else if (n>0) - n = 1; +/* close ' to keep fontify happy */ +n = compare(c_addr1, u1, c_addr2, u2); : + rot 2dup swap - >r min swap -text dup + IF rdrop ELSE drop r> sgn THEN ; +: -text ( c_addr1 u c_addr2 -- n ) 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 + ELSE c@ I c@ - unloop THEN sgn ; +: sgn ( n -- -1/0/1 ) + dup 0= IF EXIT THEN 0< 2* 1+ ; + +\ -text is only used by replaced primitives now; move it elsewhere +\ -text ( c_addr1 u c_addr2 -- n ) new dash_text +\ n = memcmp(c_addr1, c_addr2, u); +\ 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 sgn ; +\ : sgn ( n -- -1/0/1 ) +\ dup 0= IF EXIT THEN 0< 2* 1+ ; + +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 -u2 = u1; -while (u2>0 && c_addr[u2-1] == ' ') - u2--; -: - BEGIN 1- 2dup + c@ bl = WHILE - dup 0= UNTIL ELSE 1+ THEN ; +capscompare ( c_addr1 u1 c_addr2 u2 -- n ) gforth +""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."" +/* close ' to keep fontify happy */ +n = capscompare(c_addr1, u1, c_addr2, u2); -/string c_addr1 u1 n -- c_addr2 u2 string slash_string +/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 +\g arith + +lit ( #w -- w ) gforth +: + r> dup @ swap cell+ >r ; + ++ ( n1 n2 -- n ) core plus n = n1+n2; +\ lit+ / lit_plus = lit + + +lit+ ( n1 #n2 -- n ) new lit_plus +#ifdef DEBUG +fprintf(stderr, "lit+ %08x\n", n2); +#endif +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 @var{n3} to @var{n1} (giving @var{n})"" +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 +- ( n1 n2 -- n ) core minus n = n1-n2; : negate + ; -negate n1 -- n2 core +negate ( n1 -- n2 ) core /* use minus as alias */ n2 = -n1; : invert 1+ ; -1+ n1 -- n2 core one_plus +1+ ( n1 -- n2 ) core one_plus n2 = n1+1; : 1 + ; -1- n1 -- n2 core one_minus +1- ( n1 -- n2 ) core one_minus n2 = n1-1; : 1 - ; -max n1 n2 -- n core +max ( n1 n2 -- n ) core if (n1 IF swap THEN drop ; -abs n1 -- n2 core -if (n1<0) - n2 = -n1; +abs ( n -- u ) core +if (n<0) + u = -n; else - n2 = n1; + u = n; : dup 0< IF negate THEN ; -* n1 n2 -- n core star +* ( n1 n2 -- n ) core star n = n1*n2; : um* drop ; -/ n1 n2 -- n core slash +/ ( n1 n2 -- n ) core slash n = n1/n2; +if (CHECK_DIVISION_SW && n2 == 0) + throw(BALL_DIVZERO); +if (CHECK_DIVISION_SW && n2 == -1 && n1 == CELL_MIN) + throw(BALL_RESULTRANGE); +if (FLOORED_DIV && ((n1^n2) < 0) && (n1%n2 != 0)) + n--; : /mod nip ; -mod n1 n2 -- n core +mod ( n1 n2 -- n ) core n = n1%n2; +if (CHECK_DIVISION_SW && n2 == 0) + throw(BALL_DIVZERO); +if (CHECK_DIVISION_SW && n2 == -1 && n1 == CELL_MIN) + throw(BALL_RESULTRANGE); +if(FLOORED_DIV && ((n1^n2) < 0) && n!=0) n += n2; : /mod drop ; -/mod n1 n2 -- n3 n4 core slash_mod +/mod ( n1 n2 -- n3 n4 ) core slash_mod n4 = n1/n2; n3 = n1%n2; /* !! is this correct? look into C standard! */ +if (CHECK_DIVISION_SW && n2 == 0) + throw(BALL_DIVZERO); +if (CHECK_DIVISION_SW && n2 == -1 && n1 == CELL_MIN) + throw(BALL_RESULTRANGE); +if (FLOORED_DIV && ((n1^n2) < 0) && n3!=0) { + n4--; + n3+=n2; +} : >r s>d r> fm/mod ; -2* n1 -- n2 core two_star +*/mod ( n1 n2 n3 -- n4 n5 ) core star_slash_mod +""n1*n2=n3*n5+n4, with the intermediate result (n1*n2) being double."" +#ifdef BUGGY_LL_MUL +DCell d = mmul(n1,n2); +#else +DCell d = (DCell)n1 * (DCell)n2; +#endif +#ifdef ASM_SM_SLASH_REM +ASM_SM_SLASH_REM(DLO(d), DHI(d), n3, n4, n5); +if (FLOORED_DIV && ((DHI(d)^n3)<0) && n4!=0) { + if (CHECK_DIVISION && n5 == CELL_MIN) + throw(BALL_RESULTRANGE); + n5--; + n4+=n3; +} +#else +DCell r = FLOORED_DIV ? fmdiv(d,n3) : smdiv(d,n3); +n4=DHI(r); +n5=DLO(r); +#endif +: + >r m* r> fm/mod ; + +*/ ( n1 n2 n3 -- n4 ) core star_slash +""n4=(n1*n2)/n3, with the intermediate result being double."" +#ifdef BUGGY_LL_MUL +DCell d = mmul(n1,n2); +#else +DCell d = (DCell)n1 * (DCell)n2; +#endif +#ifdef ASM_SM_SLASH_REM +Cell remainder; +ASM_SM_SLASH_REM(DLO(d), DHI(d), n3, remainder, n4); +if (FLOORED_DIV && ((DHI(d)^n3)<0) && remainder!=0) { + if (CHECK_DIVISION && n4 == CELL_MIN) + throw(BALL_RESULTRANGE); + n4--; +} +#else +DCell r = FLOORED_DIV ? fmdiv(d,n3) : smdiv(d,n3); +n4=DLO(r); +#endif +: + */mod nip ; + +2* ( n1 -- n2 ) core two_star +""Shift left by 1; also works on unsigned numbers"" n2 = 2*n1; : dup + ; -2/ n1 -- n2 core two_slash -/* !! is this still correct? */ +2/ ( n1 -- n2 ) core two_slash +""Arithmetic shift right by 1. For signed numbers this is a floored +division by 2 (note that @code{/} not necessarily floors)."" n2 = n1>>1; : dup MINI and IF 1 ELSE 0 THEN - [ bits/byte cell * 1- ] literal + [ bits/char 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: d1 = n3*n1+n2, n1>n2>=0 or 0>=n2>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) { +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 ASM_SM_SLASH_REM +ASM_SM_SLASH_REM(DLO(d1), DHI(d1), n1, n2, n3); +if (((DHI(d1)^n1)<0) && n2!=0) { + if (CHECK_DIVISION && n3 == CELL_MIN) + throw(BALL_RESULTRANGE); n3--; n2+=n1; } -#endif +#else /* !defined(ASM_SM_SLASH_REM) */ +DCell r = fmdiv(d1,n1); +n2=DHI(r); +n3=DLO(r); +#endif /* !defined(ASM_SM_SLASH_REM) */ : 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: d1 = n3*n1+n2, sign(n2)=sign(d1) or 0"" -#ifdef BUGGY_LONG_LONG +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 ASM_SM_SLASH_REM +ASM_SM_SLASH_REM(DLO(d1), DHI(d1), n1, n2, n3); +#else /* !defined(ASM_SM_SLASH_REM) */ 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 +n2=DHI(r); +n3=DLO(r); +#endif /* !defined(ASM_SM_SLASH_REM) */ : 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 +m* ( n1 n2 -- d ) core m_star +#ifdef BUGGY_LL_MUL d = mmul(n1,n2); #else d = (DCell)n1 * (DCell)n2; @@ -681,32 +937,31 @@ d = (DCell)n1 * (DCell)n2; 2dup swap 0< and >r um* r> - r> - ; -um* u1 u2 -- ud core u_m_star +um* ( u1 u2 -- ud ) core u_m_star /* use u* as alias */ -#ifdef BUGGY_LONG_LONG +#ifdef BUGGY_LL_MUL ud = ummul(u1,u2); #else ud = (UDCell)u1 * (UDCell)u2; #endif : - >r >r 0 0 r> r> [ 8 cells ] literal 0 + 0 -rot dup [ 8 cells ] literal - DO - over >r dup >r 0< and d2*+ drop - r> 2* r> swap - LOOP 2drop ; + dup 0< I' and d2*+ drop + LOOP ; : 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 -#ifdef BUGGY_LONG_LONG +um/mod ( ud u1 -- u2 u3 ) core u_m_slash_mod +""ud=u3*u1+u2, u1>u2>=0"" +#ifdef ASM_UM_SLASH_MOD +ASM_UM_SLASH_MOD(DLO(ud), DHI(ud), u1, u2, u3); +#else /* !defined(ASM_UM_SLASH_MOD) */ UDCell r = umdiv(ud,u1); -u2=r.hi; -u3=r.lo; -#else -u3 = ud/u1; -u2 = ud%u1; -#endif +u2=DHI(r); +u3=DLO(r); +#endif /* !defined(ASM_UM_SLASH_MOD) */ : 0 swap [ 8 cells 1 + ] literal 0 ?DO /modstep @@ -717,39 +972,39 @@ u2 = ud%u1; 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 +d2* ( d1 -- d2 ) double d_two_star +""Shift left by 1; also works on unsigned numbers"" +d2 = DLSHIFT(d1,1); : 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)); +d2/ ( d1 -- d2 ) double d_two_slash +""Arithmetic shift right by 1. For signed numbers this is a floored +division by 2."" +#ifdef BUGGY_LL_SHIFT +DHI_IS(d2, DHI(d1)>>1); +DLO_IS(d2, (DLO(d1)>>1) | (DHI(d1)<<(CELL_BITS-1))); #else d2 = d1>>1; #endif @@ -778,35 +1031,46 @@ d2 = d1>>1; 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 +and ( w1 w2 -- w ) core w = w1&w2; -or w1 w2 -- w core +or ( w1 w2 -- w ) core w = w1|w2; : invert swap invert and invert ; -xor w1 w2 -- w core +xor ( w1 w2 -- w ) core x_or w = w1^w2; -invert w1 -- w2 core +invert ( w1 -- w2 ) core w2 = ~w1; : MAXU xor ; -rshift u1 n -- u2 core - u2 = u1>>n; +rshift ( u1 n -- u2 ) core r_shift +""Logical shift right by @i{n} bits."" +#ifdef BROKEN_SHIFT + u2 = rshift(u1, n); +#else + u2 = u1 >> n; +#endif : 0 ?DO 2/ MAXI and LOOP ; -lshift u1 n -- u2 core - u2 = u1< $2 -- f $7 $3different +$1<> ( $2 -- f ) $7 $3not_equals f = FLAG($4!=$5); : [ char $1x char 0 = [IF] @@ -824,7 +1088,7 @@ f = FLAG($4!=$5); ] xor 0<> [ [THEN] ] ; -$1< $2 -- f $8 $3less +$1< ( $2 -- f ) $8 $3less_than f = FLAG($4<$5); : [ char $1x char 0 = [IF] @@ -836,18 +1100,18 @@ f = FLAG($4<$5); [THEN] [THEN] ] ; -$1> $2 -- f $9 $3greater +$1> ( $2 -- f ) $9 $3greater_than f = FLAG($4>$5); : [ char $1x char 0 = [IF] ] negate [ [ELSE] ] swap [ [THEN] ] $1< ; -$1<= $2 -- f gforth $3less_or_equal +$1<= ( $2 -- f ) gforth $3less_or_equal f = FLAG($4<=$5); : $1> 0= ; -$1>= $2 -- f gforth $3greater_or_equal +$1>= ( $2 -- f ) gforth $3greater_or_equal f = FLAG($4>=$5); : [ char $1x char 0 = [IF] ] negate [ [ELSE] ] swap [ [THEN] ] @@ -861,43 +1125,43 @@ comparisons(u, u1 u2, u_, u1, u2, gforth \ dcomparisons(prefix, args, prefix, arg1, arg2, wordsets...) define(dcomparisons, -$1= $2 -- f $6 $3equals -#ifdef BUGGY_LONG_LONG +$1= ( $2 -- f ) $6 $3equals +#ifdef BUGGY_LL_CMP 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 +$1<> ( $2 -- f ) $7 $3not_equals +#ifdef BUGGY_LL_CMP 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 +$1< ( $2 -- f ) $8 $3less_than +#ifdef BUGGY_LL_CMP 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 +$1> ( $2 -- f ) $9 $3greater_than +#ifdef BUGGY_LL_CMP 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 +$1<= ( $2 -- f ) gforth $3less_or_equal +#ifdef BUGGY_LL_CMP 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 +$1>= ( $2 -- f ) gforth $3greater_or_equal +#ifdef BUGGY_LL_CMP f = FLAG($4.hi==$5.hi ? $4.lo>=$5.lo : $4.hi>=$5.hi); #else f = FLAG($4>=$5); @@ -905,104 +1169,107 @@ f = FLAG($4>=$5); ) -\+has? dcomps [IF] +\+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) -\+[THEN] +\+ -within u1 u2 u3 -- f core-ext +within ( u1 u2 u3 -- f ) core-ext +""u2=r - r> u< ; -sp@ -- a_addr gforth spat -a_addr = sp+1; +\g stack + +useraddr ( #u -- a_addr ) new +a_addr = (Cell *)(up+u); -sp! a_addr -- gforth spstore +up! ( a_addr -- ) gforth up_store +gforth_UP=up=(Address)a_addr; +: + up ! ; +Variable UP + +sp@ ( S:... -- a_addr ) gforth sp_fetch +a_addr = sp; + +sp! ( a_addr -- S:... ) gforth sp_store sp = a_addr; -/* works with and without TOS caching */ -rp@ -- a_addr gforth rpat +rp@ ( -- a_addr ) gforth rp_fetch a_addr = rp; -rp! a_addr -- gforth rpstore +rp! ( a_addr -- ) gforth rp_store rp = a_addr; -\+has? floating [IF] +\+floating -fp@ -- f_addr gforth fp_fetch +fp@ ( f:... -- f_addr ) gforth fp_fetch f_addr = fp; -fp! f_addr -- gforth fp_store +fp! ( f_addr -- f:... ) gforth fp_store fp = f_addr; -\+[THEN] +\+ -;s -- gforth semis -ip = (Xt *)(*rp++); -NEXT_P0; - ->r w -- core to_r -*--rp = w; +>r ( w -- R:w ) core to_r : (>r) ; : (>r) rp@ cell+ @ rp@ ! rp@ cell+ ! ; -r> -- w core r_from -w = *rp++; +r> ( R:w -- w ) core r_from : rp@ cell+ @ rp@ @ rp@ cell+ ! (rdrop) rp@ ! ; Create (rdrop) ' ;s A, -rdrop -- gforth -rp++; +rdrop ( R:w -- ) gforth : r> r> drop >r ; -2>r w1 w2 -- core-ext two_to_r -*--rp = w1; -*--rp = w2; +2>r ( d -- R:d ) core-ext two_to_r : swap r> swap >r swap >r >r ; -2r> -- w1 w2 core-ext two_r_from -w2 = *rp++; -w1 = *rp++; +2r> ( R:d -- d ) core-ext two_r_from : r> r> swap r> swap >r swap ; -2r@ -- w1 w2 core-ext two_r_fetch -w2 = rp[0]; -w1 = rp[1]; +2r@ ( R:d -- R:d d ) core-ext two_r_fetch : i' j ; -2rdrop -- gforth two_r_drop -rp+=2; +2rdrop ( R:d -- ) gforth two_r_drop : r> r> drop r> drop >r ; -over w1 w2 -- w1 w2 w1 core +over ( w1 w2 -- w1 w2 w1 ) core : sp@ cell+ @ ; -drop w -- core +drop ( w -- ) core : IF THEN ; -swap w1 w2 -- w2 w1 core +swap ( w1 w2 -- w2 w1 ) core : >r (swap) ! r> (swap) @ ; Variable (swap) -dup w -- w w core +dup ( w -- w w ) core dupe : sp@ @ ; -rot w1 w2 w3 -- w2 w3 w1 core rote +rot ( w1 w2 w3 -- w2 w3 w1 ) core rote : [ defined? (swap) [IF] ] (swap) ! (rot) ! >r (rot) @ (swap) @ r> ; @@ -1011,75 +1278,86 @@ Variable (rot) >r swap r> swap ; [THEN] --rot w1 w2 w3 -- w3 w1 w2 gforth not_rote +-rot ( w1 w2 w3 -- w3 w1 w2 ) gforth not_rote : rot rot ; -nip w1 w2 -- w2 core-ext +nip ( w1 w2 -- w2 ) core-ext : swap drop ; -tuck w1 w2 -- w2 w1 w2 core-ext +tuck ( w1 w2 -- w2 w1 w2 ) core-ext : swap over ; -?dup w -- w core question_dupe +?dup ( w -- S:... w ) core question_dupe +""Actually the stack effect is: @code{( w -- 0 | w w )}. It performs a +@code{dup} if w is nonzero."" 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]; +pick ( S:... u -- S:... w ) core-ext +""Actually the stack effect is @code{ x0 ... xu u -- x0 ... xu x0 }."" +w = sp[u]; : 1+ cells sp@ + @ ; -2drop w1 w2 -- core two_drop +2drop ( w1 w2 -- ) core two_drop : drop drop ; -2dup w1 w2 -- w1 w2 w1 w2 core two_dupe +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 +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 +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 +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 +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 +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 +\g memory + +@ ( a_addr -- w ) core fetch +""@i{w} is the cell stored at @i{a_addr}."" w = *a_addr; -! w a_addr -- core store +\ lit@ / lit_fetch = lit @ + +lit@ ( #a_addr -- w ) new lit_fetch +w = *a_addr; + +! ( w a_addr -- ) core store +""Store @i{w} into the cell at @i{a-addr}."" *a_addr = w; -+! n a_addr -- core plus_store ++! ( n a_addr -- ) core plus_store +""Add @i{n} to the cell at @i{a-addr}."" *a_addr += n; : tuck @ + swap ! ; -c@ c_addr -- c core cfetch +c@ ( c_addr -- c ) core c_fetch +""@i{c} is the char stored at @i{c_addr}."" c = *c_addr; : [ bigendian [IF] ] @@ -1104,7 +1382,8 @@ c = *c_addr; ; : 8>> 2/ 2/ 2/ 2/ 2/ 2/ 2/ 2/ ; -c! c c_addr -- core cstore +c! ( c c_addr -- ) core c_store +""Store @i{c} into the char at @i{c-addr}."" *c_addr = c; : [ bigendian [IF] ] @@ -1133,24 +1412,29 @@ c! c c_addr -- core cstore [THEN] : 8<< 2* 2* 2* 2* 2* 2* 2* 2* ; -2! w1 w2 a_addr -- core two_store +2! ( w1 w2 a_addr -- ) core two_store +""Store @i{w2} into the cell at @i{c-addr} and @i{w1} into the next cell."" a_addr[0] = w2; a_addr[1] = w1; : tuck ! cell+ ! ; -2@ a_addr -- w1 w2 core two_fetch +2@ ( a_addr -- w1 w2 ) core two_fetch +""@i{w2} is the content of the cell stored at @i{a-addr}, @i{w1} is +the content of the next cell."" w2 = a_addr[0]; w1 = a_addr[1]; : dup cell+ @ swap @ ; -cell+ a_addr1 -- a_addr2 core cell_plus +cell+ ( a_addr1 -- a_addr2 ) core cell_plus +""@code{1 cells +}"" a_addr2 = a_addr1+1; : cell + ; -cells n1 -- n2 core +cells ( n1 -- n2 ) core +"" @i{n2} is the number of address units of @i{n1} cells."" n2 = n1 * sizeof(Cell); : [ cell @@ -1160,28 +1444,40 @@ n2 = n1 * sizeof(Cell); 2/ dup [IF] ] 2* [ [THEN] drop ] ; -char+ c_addr1 -- c_addr2 core care_plus +char+ ( c_addr1 -- c_addr2 ) core char_plus +""@code{1 chars +}."" c_addr2 = c_addr1 + 1; : 1+ ; -(chars) n1 -- n2 gforth paren_cares +(chars) ( n1 -- n2 ) gforth paren_chars n2 = n1 * sizeof(Char); : ; -count c_addr1 -- c_addr2 u core +count ( c_addr1 -- c_addr2 u ) core +""@i{c-addr2} is the first character and @i{u} the length of the +counted string at @i{c-addr1}."" 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 = f83name1->next) +\g compiler + +\+f83headerstring + +(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; +#ifdef DEBUG +fprintf(stderr, "F83find "); +fwrite(c_addr, u, 1, stderr); +fprintf(stderr, " found %08x\n", f83name2); +#endif : BEGIN dup WHILE (find-samelen) dup WHILE >r 2dup r@ cell+ char+ capscomp 0= @@ -1190,75 +1486,68 @@ f83name2=f83name1; REPEAT THEN nip nip ; : (find-samelen) ( u f83name1 -- u f83name2/0 ) BEGIN 2dup cell+ c@ $1F and <> WHILE @ dup 0= UNTIL THEN ; +: capscomp ( c_addr1 u c_addr2 -- n ) + 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 sgn ; +: sgn ( n -- -1/0/1 ) + dup 0= IF EXIT THEN 0< 2* 1+ ; -\+has? hash [IF] +\- -(hashfind) c_addr u a_addr -- f83name2 new paren_hashfind -F83Name *f83name1; -f83name2=NULL; -while(a_addr != NULL) -{ - f83name1=(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; - } -} +(listlfind) ( c_addr u longname1 -- longname2 ) new paren_listlfind +longname2=listlfind(c_addr, u, longname1); +: + BEGIN dup WHILE (findl-samelen) dup WHILE + >r 2dup r@ cell+ cell+ capscomp 0= + IF 2drop r> EXIT THEN + r> @ + REPEAT THEN nip nip ; +: (findl-samelen) ( u longname1 -- u longname2/0 ) + BEGIN 2dup cell+ @ lcount-mask and <> WHILE @ dup 0= UNTIL THEN ; +: capscomp ( c_addr1 u c_addr2 -- n ) + 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 sgn ; +: sgn ( n -- -1/0/1 ) + dup 0= IF EXIT THEN 0< 2* 1+ ; + +\+hash + +(hashlfind) ( c_addr u a_addr -- longname2 ) new paren_hashlfind +longname2 = hashlfind(c_addr, u, a_addr); : BEGIN dup WHILE - 2@ >r >r dup r@ cell+ c@ $1F and = - IF 2dup r@ cell+ char+ capscomp 0= + 2@ >r >r dup r@ cell+ @ lcount-mask and = + IF 2dup r@ cell+ cell+ capscomp 0= IF 2drop r> rdrop EXIT THEN THEN rdrop r> REPEAT nip nip ; -(tablefind) c_addr u a_addr -- f83name2 new paren_tablefind +(tablelfind) ( c_addr u a_addr -- longname2 ) new paren_tablelfind ""A case-sensitive variant of @code{(hashfind)}"" -F83Name *f83name1; -f83name2=NULL; -while(a_addr != NULL) -{ - f83name1=(F83Name *)(a_addr[1]); - a_addr=(Cell *)(a_addr[0]); - if ((UCell)F83NAME_COUNT(f83name1)==u && - memcmp(c_addr, f83name1->name, u)== 0 /* or inline? */) - { - f83name2=f83name1; - break; - } -} +longname2 = tablelfind(c_addr, u, a_addr); : BEGIN dup WHILE - 2@ >r >r dup r@ cell+ c@ $1F and = - IF 2dup r@ cell+ char+ -text 0= + 2@ >r >r dup r@ cell+ @ lcount-mask and = + IF 2dup r@ cell+ cell+ -text 0= IF 2drop r> rdrop EXIT THEN THEN rdrop r> REPEAT nip nip ; +: -text ( c_addr1 u c_addr2 -- n ) + swap bounds + ?DO dup c@ I c@ = WHILE 1+ LOOP drop 0 + ELSE c@ I c@ - unloop THEN sgn ; +: sgn ( n -- -1/0/1 ) + dup 0= IF EXIT THEN 0< 2* 1+ ; -(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 +(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 @@ -1272,89 +1561,35 @@ Create rot-values 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, -\+[THEN] +\+ -(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 +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 +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 + ; - ->code-address xt -- c_addr gforth to_code_address -""c_addr is the code address of the word xt"" -/* !! This behaves installation-dependently for DOES-words */ -c_addr = (Address)CODE_ADDRESS(xt); -: - @ ; - ->does-code xt -- a_addr gforth to_does_code -""If xt ist the execution token of a defining-word-defined word, -a_addr is the start of the Forth code after the DOES>; -Otherwise a_addr is 0."" -a_addr = (Cell *)DOES_CODE(xt); -: - cell+ @ ; - -code-address! c_addr xt -- gforth code_address_store -""Creates a code field with code address c_addr at xt"" -MAKE_CF(xt, c_addr); -CACHE_FLUSH(xt,PFA(0)); -: - ! ; - -does-code! a_addr xt -- gforth does_code_store -""creates a code field at xt for a defining-word-defined word; a_addr -is the start of the Forth code after DOES>"" -MAKE_DOES_CF(xt, a_addr); -CACHE_FLUSH(xt,PFA(0)); -: - dodoes: over ! cell+ ! ; - -does-handler! a_addr -- gforth does_handler_store -""creates a DOES>-handler at address a_addr. a_addr usually points -just behind a DOES>."" -MAKE_DOES_HANDLER(a_addr); -CACHE_FLUSH(a_addr,DOES_HANDLER_SIZE); -: - drop ; - -/does-handler -- n gforth slash_does_handler -""the size of a 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 +\ threading stuff is currently only interesting if we have a compiler +\fhas? standardthreading has? compiler and [IF] +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) @@ -1369,34 +1604,73 @@ n=1; : 1 ; -\+has? os [IF] +\f[THEN] -(key) -- n gforth paren_key +\g hostos + +key-file ( wfileid -- c ) gforth paren_key_file +""Read one character @i{c} from @i{wfileid}. This word disables +buffering for @i{wfileid}. If you want to read characters from a +terminal in non-canonical (raw) mode, you have to put the terminal in +non-canonical mode yourself (using the C interface); the exception is +@code{stdin}: Gforth automatically puts it into non-canonical mode."" +#ifdef HAS_FILE fflush(stdout); -/* !! noecho */ -n = key(); +c = key((FILE*)wfileid); +#else +c = key(stdin); +#endif -key? -- n facility key_q +key?-file ( wfileid -- f ) gforth key_q_file +""@i{f} is true if at least one character can be read from @i{wfileid} +without blocking. If you also want to use @code{read-file} or +@code{read-line} on the file, you have to call @code{key?-file} or +@code{key-file} first (these two words disable buffering)."" +#ifdef HAS_FILE fflush(stdout); -n = key_query; +f = key_query((FILE*)wfileid); +#else +f = key_query(stdin); +#endif -stdout -- wfileid gforth +stdin ( -- wfileid ) gforth +""The standard input file of the Gforth process."" +wfileid = (Cell)stdin; + +stdout ( -- wfileid ) gforth +""The standard output file of the Gforth process."" wfileid = (Cell)stdout; -stderr -- wfileid gforth +stderr ( -- wfileid ) gforth +""The standard error output file of the Gforth process."" wfileid = (Cell)stderr; -form -- urows ucols gforth -""The number of lines and columns in the terminal. These numbers may change -with the window size."" +\+os + +form ( -- urows ucols ) gforth +""The number of lines and columns in the terminal. These numbers may +change with the window size. Note that it depends on the OS whether +this reflects the actual size and changes with the window size +(currently only on Unix-like OSs). On other OSs you just get a +default, and can tell Gforth the terminal size by setting the +environment variables @code{COLUMNS} and @code{LINES} before starting +Gforth."" /* 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 +wcwidth ( u -- n ) gforth +""The number of fixed-width characters per unicode character u"" +#ifdef HAVE_WCWIDTH +n = wcwidth(u); +#else +n = 1; +#endif + +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 @var{c_addr} and @var{u} bytes +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 @@ -1404,37 +1678,51 @@ supported on your machine (take a look a 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); +FLUSH_ICACHE((caddr_t)c_addr,u); -(bye) n -- gforth paren_bye +(bye) ( n -- ) gforth paren_bye +SUPER_END; return (Label *)n; -(system) c_addr u -- wretval wior gforth peren_system -int old_tp=terminal_prepped; -deprep_terminal(); -wretval=system(cstr(c_addr,u,1)); /* ~ expansion on first part of string? */ +(system) ( c_addr u -- wretval wior ) gforth paren_system +wretval = gforth_system(c_addr, u); wior = IOR(wretval==-1 || (wretval==127 && errno != 0)); -if (old_tp) - prep_terminal(); -getenv c_addr1 u1 -- c_addr2 u2 gforth -c_addr2 = getenv(cstr(c_addr1,u1,1)); -u2 = (c_addr2 == NULL ? 0 : strlen(c_addr2)); +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."" +/* close ' to keep fontify happy */ +c_addr2 = (Char *)getenv(cstr(c_addr1,u1,1)); +u2 = (c_addr2 == NULL ? 0 : strlen((char *)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? */ +open-pipe ( c_addr u wfam -- wfileid wior ) gforth open_pipe +fflush(stdout); +wfileid=(Cell)popen(cstr(c_addr,u,1),pfileattr[wfam]); /* ~ 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 +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 +time&date ( -- nsec nmin nhour nday nmonth nyear ) facility-ext time_and_date +""Report the current time of day. Seconds, minutes and hours are numbered from 0. +Months are numbered from 1."" +#if 1 +time_t now; +struct tm *ltime; +time(&now); +ltime=localtime(&now); +#else struct timeval time1; struct timezone zone1; struct tm *ltime; gettimeofday(&time1,&zone1); +/* !! Single Unix specification: + If tzp is not a null pointer, the behaviour is unspecified. */ ltime=localtime((time_t *)&time1.tv_sec); +#endif nyear =ltime->tm_year+1900; nmonth=ltime->tm_mon+1; nday =ltime->tm_mday; @@ -1442,102 +1730,110 @@ 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 +ms ( u -- ) facility-ext +""Wait at least @i{n} milli-second."" +gforth_ms(u); + +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 a non-zero I/O result code."" a_addr = (Cell *)malloc(u?u:1); wior = IOR(a_addr==NULL); -free a_addr -- wior memory +free ( a_addr -- wior ) memory +""Return the region of data space starting at @i{a-addr} to the system. +The region 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 a non-zero 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} +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 -@code{a_addr1} is 0, Gforth's (but not the standard) @code{resize} -@code{allocate}s @i{u} address units."" +area. @i{a-addr2} is the address of the resulting area. +If the operation is successful, @i{wior} is 0. +If the operation fails, @i{wior} is a non-zero +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. */ +/* close ' to keep fontify happy */ 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); +strerror ( n -- c_addr u ) gforth +c_addr = (Char *)strerror(n); +u = strlen((char *)c_addr); + +strsignal ( n -- c_addr u ) gforth +c_addr = (Char *)strsignal(n); +u = strlen((char *)c_addr); -call-c w -- gforth call_c +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}."" +variables @code{gforth_SP} and @code{gforth_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; +IF_fpTOS(fp[0]=fpTOS); +gforth_FP=fp; +gforth_SP=sp; +gforth_RP=rp; +gforth_LP=lp; +#ifdef HAS_LINKBACK ((void (*)())w)(); -sp=SP; -fp=FP; -IF_TOS(TOS=sp[0]); -IF_FTOS(FTOS=fp[0]); +#else +((void (*)(void *))w)(gforth_pointers); +#endif +sp=gforth_SP; +fp=gforth_FP; +rp=gforth_RP; +lp=gforth_LP; +IF_fpTOS(fpTOS=fp[0]); -\+[THEN] ( has? os ) has? file [IF] +\+ +\+file -close-file wfileid -- wior file close_file +close-file ( wfileid -- wior ) file close_file wior = IOR(fclose((FILE *)wfileid)==EOF); -open-file c_addr u ntype -- w2 wior file open_file -w2 = (Cell)fopen(tilde_cstr(c_addr, u, 1), fileattr[ntype]); -wior = IOR(w2 == 0); - -create-file c_addr u ntype -- w2 wior file create_file -Cell fd; -fd = open(tilde_cstr(c_addr, u, 1), O_CREAT|O_TRUNC|ufileattr[ntype], 0666); -if (fd != -1) { - w2 = (Cell)fdopen(fd, fileattr[ntype]); - wior = IOR(w2 == 0); -} else { - w2 = 0; - wior = IOR(1); -} +open-file ( c_addr u wfam -- wfileid wior ) file open_file +wfileid = opencreate_file(tilde_cstr(c_addr,u,1), wfam, 0, &wior); + +create-file ( c_addr u wfam -- wfileid wior ) file create_file +wfileid = opencreate_file(tilde_cstr(c_addr,u,1), wfam, O_CREAT|O_TRUNC, &wior); -delete-file c_addr u -- wior file delete_file +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 -char *s1=tilde_cstr(c_addr2, u2, 1); -wior = IOR(rename(tilde_cstr(c_addr1, u1, 0), s1)==-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}"" +wior = rename_file(c_addr1, u1, c_addr2, u2); -file-position wfileid -- ud wior file file_position +file-position ( wfileid -- ud wior ) file file_position /* !! use tell and lseek? */ -ud = LONG2UD(ftell((FILE *)wfileid)); -wior = IOR(UD2LONG(ud)==-1); +ud = OFF2UD(ftello((FILE *)wfileid)); +wior = IOR(UD2OFF(ud)==-1); -reposition-file ud wfileid -- wior file reposition_file -wior = IOR(fseek((FILE *)wfileid, UD2LONG(ud), SEEK_SET)==-1); +reposition-file ( ud wfileid -- wior ) file reposition_file +wior = IOR(fseeko((FILE *)wfileid, UD2OFF(ud), SEEK_SET)==-1); -file-size wfileid -- ud wior file file_size -#include +file-size ( wfileid -- ud wior ) file file_size struct stat buf; wior = IOR(fstat(fileno((FILE *)wfileid), &buf)==-1); -ud = LONG2UD(buf.st_size); +ud = OFF2UD(buf.st_size); -resize-file ud wfileid -- wior file resize_file -wior = IOR(ftruncate(fileno((FILE *)wfileid), UD2LONG(ud))==-1); +resize-file ( ud wfileid -- wior ) file resize_file +wior = IOR(ftruncate(fileno((FILE *)wfileid), UD2OFF(ud))==-1); -read-file c_addr u1 wfileid -- u2 wior file read_file +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; -} +(read-line) ( c_addr u1 wfileid -- u2 flag u3 wior ) file paren_read_line +struct Cellquad r = read_line(c_addr, u1, wfileid); +u2 = r.n1; +flag = r.n2; +u3 = r.n3; +wior = r.n4; -\+[THEN] has? file [IF] -1 [ELSE] has? os [THEN] [IF] +\+ -write-file c_addr u1 wfileid -- wior file write_file +write-file ( c_addr u1 wfileid -- wior ) file write_file /* !! fwrite does not guarantee enough */ +#ifdef HAS_FILE { UCell u2 = fwrite(c_addr, sizeof(Char), u1, (FILE *)wfileid); wior = FILEIO(u20. +If the attempt succeeds, store file name to the buffer at @i{c-addr} +and return @i{ior}=0, @i{flag}=true and @i{u2} equal to the size of the file name. +If the length of the file name is greater than @i{u1}, +store first @i{u1} characters from file name into the buffer and +indicate "name too long" with @i{ior}, @i{flag}=true, and @i{u2}=@i{u1}."" +struct dirent * dent; +dent = readdir((DIR *)wdirid); +wior = 0; +flag = -1; +if(dent == NULL) { + u2 = 0; + flag = 0; +} else { + u2 = strlen((char *)dent->d_name); + if(u2 > u1) { + u2 = u1; + wior = -512-ENAMETOOLONG; + } + memmove(c_addr, dent->d_name, u2); +} + +close-dir ( wdirid -- wior ) gforth close_dir +""Close the directory specified by @i{dir-id}."" +wior = IOR(closedir((DIR *)wdirid)); + +filename-match ( c_addr1 u1 c_addr2 u2 -- flag ) gforth match_file +char * string = cstr(c_addr1, u1, 1); +char * pattern = cstr(c_addr2, u2, 0); +flag = FLAG(!fnmatch(pattern, string, 0)); + +set-dir ( c_addr u -- wior ) gforth set_dir +""Change the current directory to @i{c-addr, u}. +Return an error if this is not possible"" +wior = IOR(chdir(tilde_cstr(c_addr, u, 1))); + +get-dir ( c_addr1 u1 -- c_addr2 u2 ) gforth get_dir +""Store the current directory in the buffer specified by @i{c-addr1, u1}. +If the buffer size is not sufficient, return 0 0"" +c_addr2 = (Char *)getcwd((char *)c_addr1, u1); +if(c_addr2 != NULL) { + u2 = strlen((char *)c_addr2); +} else { + u2 = 0; } -\+[THEN] ( has? file ) has? floating [IF] +=mkdir ( c_addr u wmode -- wior ) gforth equals_mkdir +""Create directory @i{c-addr u} with mode @i{wmode}."" +wior = IOR(mkdir(tilde_cstr(c_addr,u,1),wmode)); + +\+ + +newline ( -- c_addr u ) gforth +""String containing the newline sequence of the host OS"" +char newline[] = { +#if DIRSEP=='/' +/* Unix */ +'\n' +#else +/* DOS, Win, OS/2 */ +'\r','\n' +#endif +}; +c_addr=(Char *)newline; +u=sizeof(newline); +: + "newline count ; +Create "newline e? crlf [IF] 2 c, $0D c, [ELSE] 1 c, [THEN] $0A c, + +\+os + +utime ( -- dtime ) gforth +""Report the current time in microseconds since some epoch."" +struct timeval time1; +gettimeofday(&time1,NULL); +dtime = timeval2us(&time1); + +cputime ( -- duser dsystem ) gforth +""duser and dsystem are the respective user- and system-level CPU +times used since the start of the Forth system (excluding child +processes), in microseconds (the granularity may be much larger, +however). On platforms without the getrusage call, it reports elapsed +time (since some epoch) for duser and 0 for dsystem."" +#ifdef HAVE_GETRUSAGE +struct rusage usage; +getrusage(RUSAGE_SELF, &usage); +duser = timeval2us(&usage.ru_utime); +dsystem = timeval2us(&usage.ru_stime); +#else +struct timeval time1; +gettimeofday(&time1,NULL); +duser = timeval2us(&time1); +dsystem = DZERO; +#endif + +\+ + +\+floating + +\g floating comparisons(f, r1 r2, f_, r1, r2, gforth, gforth, float, gforth) comparisons(f0, r, f_zero_, r, 0., float, gforth, float, gforth) -d>f d -- r float d_to_f -#ifdef BUGGY_LONG_LONG +s>f ( n -- r ) float s_to_f +r = n; + +d>f ( d -- r ) float d_to_f +#ifdef BUGGY_LL_D2F extern double ldexp(double x, int exp); -r = ldexp((Float)d.hi,CELL_BITS) + (Float)d.lo; +if (DHI(d)<0) { +#ifdef BUGGY_LL_ADD + DCell d2=dnegate(d); +#else + DCell d2=-d; +#endif + r = -(ldexp((Float)DHI(d2),CELL_BITS) + (Float)DLO(d2)); +} else + r = ldexp((Float)DHI(d),CELL_BITS) + (Float)DLO(d); #else r = d; #endif -f>d r -- d float f_to_d -#ifdef BUGGY_LONG_LONG -d.hi = ldexp(r,-CELL_BITS) - (r<0); -d.lo = r-ldexp((Float)d.hi,CELL_BITS); -#else -d = r; -#endif +f>d ( r -- d ) float f_to_d +extern DCell double2ll(Float r); +d = double2ll(r); + +f>s ( r -- n ) float f_to_s +n = (Cell)r; -f! r f_addr -- float f_store +f! ( r f_addr -- ) float f_store +""Store @i{r} into the float at address @i{f-addr}."" *f_addr = r; -f@ f_addr -- r float f_fetch +f@ ( f_addr -- r ) float f_fetch +""@i{r} is the float at address @i{f-addr}."" r = *f_addr; -df@ df_addr -- r float-ext d_f_fetch +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 +df! ( r df_addr -- ) float-ext d_f_store +""Store @i{r} as double-precision IEEE floating-point value to the +address @i{df-addr}."" #ifdef IEEE_FP *df_addr = r; #else !! df! #endif -sf@ sf_addr -- r float-ext s_f_fetch +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 +sf! ( r sf_addr -- ) float-ext s_f_store +""Store @i{r} as single-precision IEEE floating-point value to the +address @i{sf-addr}."" #ifdef IEEE_FP *sf_addr = r; #else !! sf! #endif -f+ r1 r2 -- r3 float f_plus +f+ ( r1 r2 -- r3 ) float f_plus r3 = r1+r2; -f- r1 r2 -- r3 float f_minus +f- ( r1 r2 -- r3 ) float f_minus r3 = r1-r2; -f* r1 r2 -- r3 float f_star +f* ( r1 r2 -- r3 ) float f_star r3 = r1*r2; -f/ r1 r2 -- r3 float f_slash +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"" +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 +fm* ( r1 n -- r2 ) gforth fm_star +r2 = r1*n; + +fm/ ( r1 n -- r2 ) gforth fm_slash +r2 = r1/n; + +fm*/ ( r1 n1 n2 -- r2 ) gforth fm_star_slash +r2 = (r1*n1)/n2; + +f**2 ( r1 -- r2 ) gforth fm_square +r2 = r1*r1; + +fnegate ( r1 -- r2 ) float f_negate r2 = - r1; -fdrop r -- float +fdrop ( r -- ) float f_drop -fdup r -- r r float +fdup ( r -- r r ) float f_dupe -fswap r1 r2 -- r2 r1 float +fswap ( r1 r2 -- r2 r1 ) float f_swap -fover r1 r2 -- r1 r2 r1 float +fover ( r1 r2 -- r1 r2 r1 ) float f_over -frot r1 r2 r3 -- r2 r3 r1 float +frot ( r1 r2 r3 -- r2 r3 r1 ) float f_rote -fnip r1 r2 -- r2 gforth +fnip ( r1 r2 -- r2 ) gforth f_nip -ftuck r1 r2 -- r2 r1 r2 gforth +ftuck ( r1 r2 -- r2 r1 r2 ) gforth f_tuck -float+ f_addr1 -- f_addr2 float float_plus +float+ ( f_addr1 -- f_addr2 ) float float_plus +""@code{1 floats +}."" f_addr2 = f_addr1+1; -floats n1 -- n2 float +floats ( n1 -- n2 ) float +""@i{n2} is the number of address units of @i{n1} floats."" n2 = n1*sizeof(Float); -floor r1 -- r2 float -""round towards the next smaller integral value, i.e., round toward negative infinity"" +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 +fround ( r1 -- r2 ) float f_round +""Round to the nearest integral value."" 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 +fmax ( r1 r2 -- r3 ) float f_max if (r1u) /* happens in glibc-2.1.3 if 999.. is rounded up */ + siglen=u; +if (!f2) /* workaround Cygwin trailing 0s for Inf and Nan */ + for (; sig[siglen-1]=='0'; siglen--); + ; +memcpy(c_addr,sig,siglen); +memset(c_addr+siglen,f2?'0':' ',u-siglen); ->float c_addr u -- flag float to_float -/* real signature: c_addr u -- r t / f */ +>float ( c_addr u -- f:... flag ) float to_float +""Actual stack effect: ( c_addr u -- r t | f ). 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 floating-point number 0."" Float r; -char *number=cstr(c_addr, u, 1); -char *endconv; -while(isspace(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; - } +flag = to_float(c_addr, u, &r); +if (flag) { + fp--; + fp[0]=r; } -fabs r1 -- r2 float-ext +fabs ( r1 -- r2 ) float-ext f_abs r2 = fabs(r1); -facos r1 -- r2 float-ext +facos ( r1 -- r2 ) float-ext f_a_cos r2 = acos(r1); -fasin r1 -- r2 float-ext +fasin ( r1 -- r2 ) float-ext f_a_sine r2 = asin(r1); -fatan r1 -- r2 float-ext +fatan ( r1 -- r2 ) float-ext f_a_tan r2 = atan(r1); -fatan2 r1 r2 -- r3 float-ext -""@i{r1/r2}=tan@i{r3}. The standard does not require, but probably +fatan2 ( r1 r2 -- r3 ) float-ext f_a_tan_two +""@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 +fcos ( r1 -- r2 ) float-ext f_cos r2 = cos(r1); -fexp r1 -- r2 float-ext +fexp ( r1 -- r2 ) float-ext f_e_x_p r2 = exp(r1); -fexpm1 r1 -- r2 float-ext +fexpm1 ( r1 -- r2 ) float-ext f_e_x_p_m_one ""@i{r2}=@i{e}**@i{r1}@minus{}1"" #ifdef HAVE_EXPM1 extern double @@ -1820,10 +2212,10 @@ r2 = expm1(r1); r2 = exp(r1)-1.; #endif -fln r1 -- r2 float-ext +fln ( r1 -- r2 ) float-ext f_l_n r2 = log(r1); -flnp1 r1 -- r2 float-ext +flnp1 ( r1 -- r2 ) float-ext f_l_n_p_one ""@i{r2}=ln(@i{r1}+1)"" #ifdef HAVE_LOG1P extern double @@ -1836,201 +2228,200 @@ r2 = log1p(r1); r2 = log(r1+1.); #endif -flog r1 -- r2 float-ext -""the decimal logarithm"" +flog ( r1 -- r2 ) float-ext f_log +""The decimal logarithm."" r2 = log10(r1); -falog r1 -- r2 float-ext +falog ( r1 -- r2 ) float-ext f_a_log ""@i{r2}=10**@i{r1}"" extern double pow10(double); r2 = pow10(r1); -fsin r1 -- r2 float-ext +fsin ( r1 -- r2 ) float-ext f_sine r2 = sin(r1); -fsincos r1 -- r2 r3 float-ext +fsincos ( r1 -- r2 r3 ) float-ext f_sine_cos ""@i{r2}=sin(@i{r1}), @i{r3}=cos(@i{r1})"" r2 = sin(r1); r3 = cos(r1); -fsqrt r1 -- r2 float-ext +fsqrt ( r1 -- r2 ) float-ext f_square_root r2 = sqrt(r1); -ftan r1 -- r2 float-ext +ftan ( r1 -- r2 ) float-ext f_tan r2 = tan(r1); : fsincos f/ ; -fsinh r1 -- r2 float-ext +fsinh ( r1 -- r2 ) float-ext f_cinch r2 = sinh(r1); : fexpm1 fdup fdup 1. d>f f+ f/ f+ f2/ ; -fcosh r1 -- r2 float-ext +fcosh ( r1 -- r2 ) float-ext f_cosh r2 = cosh(r1); : fexp fdup 1/f f+ f2/ ; -ftanh r1 -- r2 float-ext +ftanh ( r1 -- r2 ) float-ext f_tan_h r2 = tanh(r1); : f2* fexpm1 fdup 2. d>f f+ f/ ; -fasinh r1 -- r2 float-ext +fasinh ( r1 -- r2 ) float-ext f_a_cinch r2 = asinh(r1); : fdup fdup f* 1. d>f f+ fsqrt f/ fatanh ; -facosh r1 -- r2 float-ext +facosh ( r1 -- r2 ) float-ext f_a_cosh r2 = acosh(r1); : fdup fdup f* 1. d>f f- fsqrt f+ fln ; -fatanh r1 -- r2 float-ext +fatanh ( r1 -- r2 ) float-ext f_a_tan_h 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 +sfloats ( n1 -- n2 ) float-ext s_floats +""@i{n2} is the number of address units of @i{n1} +single-precision IEEE floating-point numbers."" n2 = n1*sizeof(SFloat); -dfloats n1 -- n2 float-ext d_floats +dfloats ( n1 -- n2 ) float-ext d_floats +""@i{n2} is the number of address units of @i{n1} +double-precision IEEE floating-point numbers."" n2 = n1*sizeof(DFloat); -sfaligned c_addr -- sf_addr float-ext s_f_aligned +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 +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 ; +v* ( f_addr1 nstride1 f_addr2 nstride2 ucount -- r ) gforth v_star +""dot-product: r=v1*v2. The first element of v1 is at f_addr1, the +next at f_addr1+nstride1 and so on (similar for v2). Both vectors have +ucount elements."" +r = v_star(f_addr1, nstride1, f_addr2, nstride2, ucount); +: + >r swap 2swap swap 0e r> 0 ?DO + dup f@ over + 2swap dup f@ f* f+ over + 2swap + LOOP 2drop 2drop ; + +faxpy ( ra f_x nstridex f_y nstridey ucount -- ) gforth +""vy=ra*vx+vy"" +faxpy(ra, f_x, nstridex, f_y, nstridey, ucount); +: + >r swap 2swap swap r> 0 ?DO + fdup dup f@ f* over + 2swap dup f@ f+ dup f! over + 2swap + LOOP 2drop 2drop fdrop ; + +\+ + \ 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 -\+[THEN] ( has? floats ) has? glocals [IF] -@local# -- w gforth fetch_local_number -w = *(Cell *)(lp+(Cell)NEXT_INST); -INC_IP(1); +\+glocals + +\g locals + +@local# ( #noffset -- w ) gforth fetch_local_number +w = *(Cell *)(lp+noffset); -@local0 -- w new fetch_local_zero -w = *(Cell *)(lp+0*sizeof(Cell)); +@local0 ( -- w ) new fetch_local_zero +w = ((Cell *)lp)[0]; -@local1 -- w new fetch_local_four -w = *(Cell *)(lp+1*sizeof(Cell)); +@local1 ( -- w ) new fetch_local_four +w = ((Cell *)lp)[1]; -@local2 -- w new fetch_local_eight -w = *(Cell *)(lp+2*sizeof(Cell)); +@local2 ( -- w ) new fetch_local_eight +w = ((Cell *)lp)[2]; -@local3 -- w new fetch_local_twelve -w = *(Cell *)(lp+3*sizeof(Cell)); +@local3 ( -- w ) new fetch_local_twelve +w = ((Cell *)lp)[3]; -\+has? floating [IF] +\+floating -f@local# -- r gforth f_fetch_local_number -r = *(Float *)(lp+(Cell)NEXT_INST); -INC_IP(1); +f@local# ( #noffset -- r ) gforth f_fetch_local_number +r = *(Float *)(lp+noffset); -f@local0 -- r new f_fetch_local_zero -r = *(Float *)(lp+0*sizeof(Float)); +f@local0 ( -- r ) new f_fetch_local_zero +r = ((Float *)lp)[0]; -f@local1 -- r new f_fetch_local_eight -r = *(Float *)(lp+1*sizeof(Float)); +f@local1 ( -- r ) new f_fetch_local_eight +r = ((Float *)lp)[1]; -\+[THEN] +\+ -laddr# -- c_addr gforth laddr_number +laddr# ( #noffset -- c_addr ) gforth laddr_number /* this can also be used to implement lp@ */ -c_addr = (Char *)(lp+(Cell)NEXT_INST); -INC_IP(1); +c_addr = (Char *)(lp+noffset); -lp+!# -- gforth lp_plus_store_number +lp+!# ( #noffset -- ) 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 += noffset; -lp- -- new minus_four_lp_plus_store +lp- ( -- ) new minus_four_lp_plus_store lp += -sizeof(Cell); -lp+ -- new eight_lp_plus_store +lp+ ( -- ) new eight_lp_plus_store lp += sizeof(Float); -lp+2 -- new sixteen_lp_plus_store +lp+2 ( -- ) new sixteen_lp_plus_store lp += 2*sizeof(Float); -lp! c_addr -- gforth lp_store +lp! ( c_addr -- ) gforth lp_store lp = (Address)c_addr; ->l w -- gforth to_l +>l ( w -- ) gforth to_l lp -= sizeof(Cell); *(Cell *)lp = w; -\+has? floating [IF] +\+floating -f>l r -- gforth f_to_l +f>l ( r -- ) gforth f_to_l lp -= sizeof(Float); *(Float *)lp = r; -\+[THEN] [THEN] \ has? glocals +fpick ( f:... u -- f:... r ) gforth +""Actually the stack effect is @code{ r0 ... ru u -- r0 ... ru r0 }."" +r = fp[u]; +: + floats fp@ + f@ ; -\+has? OS [IF] +\+ +\+ -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 = ((Cell(*)(argdlist($1)))u)(argclist($1)); - -') -define(fcall, -`fcall$1 argflist($1)u -- rret gforth -rret = ((Float(*)(argdlist($1)))u)(argclist($1)); - -') - - -open-lib c_addr1 u1 -- u2 gforth open_lib -#if defined(HAVE_LIBDL) || defined(HAVE_DLOPEN) -u2=(UCell) dlopen(cstr(c_addr1, u1, 1), RTLD_GLOBAL | RTLD_LAZY); -#else -# ifdef HAVE_LIBKERNEL32 -u2 = (Cell) GetModuleHandle(cstr(c_addr1, u1, 1)); -# else -#warning Define open-lib! -u2 = 0; -# endif -#endif +\+OS -lib-sym c_addr1 u1 u2 -- u3 gforth lib_sym -#if defined(HAVE_LIBDL) || defined(HAVE_DLOPEN) +\g syslib + +open-lib ( c_addr1 u1 -- u2 ) gforth open_lib +u2 = gforth_dlopen(c_addr1, u1); + +lib-sym ( c_addr1 u1 u2 -- u3 ) gforth lib_sym +#ifdef HAVE_LIBLTDL +u3 = (UCell) lt_dlsym((lt_dlhandle)u2, cstr(c_addr1, u1, 1)); +#elif defined(HAVE_LIBDL) || defined(HAVE_DLOPEN) u3 = (UCell) dlsym((void*)u2,cstr(c_addr1, u1, 1)); #else -# ifdef HAVE_LIBKERNEL32 +# ifdef _WIN32 u3 = (Cell) GetProcAddress((HMODULE)u2, cstr(c_addr1, u1, 1)); # else #warning Define lib-sym! @@ -2038,16 +2429,95 @@ u3 = 0; # endif #endif -uploop(i, 0, 7, `icall(i)') -icall(20) -uploop(i, 0, 7, `fcall(i)') -fcall(20) +wcall ( ... u -- ... ) gforth +gforth_FP=fp; +sp=(Cell*)(SYSCALL(Cell*(*)(Cell *, void *))u)(sp, &gforth_FP); +fp=gforth_FP; + +uw@ ( c_addr -- u ) gforth u_w_fetch +""@i{u} is the zero-extended 16-bit value stored at @i{c_addr}."" +u = *(UWyde*)(c_addr); + +sw@ ( c_addr -- n ) gforth s_w_fetch +""@i{n} is the sign-extended 16-bit value stored at @i{c_addr}."" +n = *(Wyde*)(c_addr); + +w! ( w c_addr -- ) gforth w_store +""Store the bottom 16 bits of @i{w} at @i{c_addr}."" +*(Wyde*)(c_addr) = w; + +ul@ ( c_addr -- u ) gforth u_l_fetch +""@i{u} is the zero-extended 32-bit value stored at @i{c_addr}."" +u = *(UTetrabyte*)(c_addr); + +sl@ ( c_addr -- n ) gforth s_l_fetch +""@i{n} is the sign-extended 32-bit value stored at @i{c_addr}."" +n = *(Tetrabyte*)(c_addr); + +l! ( w c_addr -- ) gforth l_store +""Store the bottom 32 bits of @i{w} at @i{c_addr}."" +*(Tetrabyte*)(c_addr) = w; + +lib-error ( -- c_addr u ) gforth lib_error +""Error message for last failed @code{open-lib} or @code{lib-sym}."" +#ifdef HAVE_LIBLTDL +c_addr = (Char *)lt_dlerror(); +u = (c_addr == NULL) ? 0 : strlen((char *)c_addr); +#else +c_addr = "libltdl is not configured"; +u = strlen(c_addr); +#endif -\+[THEN] \ has? OS +\+ +\g peephole -up! a_addr -- gforth up_store -UP=up=(char *)a_addr; -: - up ! ; -Variable UP +\+peephole + +compile-prim1 ( a_prim -- ) gforth compile_prim1 +""compile prim (incl. immargs) at @var{a_prim}"" +compile_prim1(a_prim); + +finish-code ( ... -- ... ) gforth finish_code +""Perform delayed steps in code generation (branch resolution, I-cache +flushing)."" +/* The ... above are a workaround for a bug in gcc-2.95, which fails + to save spTOS (gforth-fast --enable-force-reg) */ +finish_code(); + +forget-dyncode ( c_code -- f ) gforth-internal forget_dyncode +f = forget_dyncode(c_code); + +decompile-prim ( a_code -- a_prim ) gforth-internal decompile_prim +""a_prim is the code address of the primitive that has been +compile_prim1ed to a_code"" +a_prim = (Cell *)decompile_code((Label)a_code); + +\ set-next-code and call2 do not appear in images and can be +\ renumbered arbitrarily + +set-next-code ( #w -- ) gforth set_next_code +#ifdef NO_IP +next_code = (Label)w; +#endif + +call2 ( #a_callee #a_ret_addr -- R:a_ret_addr ) gforth +/* call with explicit return address */ +#ifdef NO_IP +INST_TAIL; +JUMP(a_callee); +#else +assert(0); +#endif + +tag-offsets ( -- a_addr ) gforth tag_offsets +extern Cell groups[32]; +a_addr = groups; + +\+ + +\g static_super + +ifdef(`STACK_CACHE_FILE', +`include(peeprules.vmg)') +\g end