File:  [gforth] / gforth / prim
Revision 1.29: download - view: text, annotated - select for diffs
Thu May 6 21:33:32 1999 UTC (24 years, 10 months ago) by crook
Branches: MAIN
CVS tags: HEAD
Major re-write of manual sections concerning text interpreter and
defining words. Much fine-tuning of other sections. The manual is
``nearly finished'' -- at least, all the major pieces of work that
I envisaged for the first mods (which were only going to take a
couple of weeks...). The manual has grown from 127 pages to 192
which is good news in terms of content but bad news in terms of the
time it takes to print out on my HP550C DeskJet.

Other changes are just tweaks to glossary entries.

\ 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 ;

\+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);

\+

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]

\ digit is high-level: 0/0%

move	c_from c_to ucount --		core
"" If @i{ucount}>0, copy the contents of @i{ucount} address units
at @i{c-from} to @i{c-to}. @code{move} chooses its copy direction
to avoid problems when @i{c-from}, @i{c-to} 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
"" If @i{u}>0, 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.""
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
"" If @i{u}>0, 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.""
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.""
n = memcmp(c_addr1, c_addr2, u1<u2 ? u1 : u2);
if (n==0)
  n = u1-u2;
if (n<0)
  n = -1;
else if (n>0)
  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;
:
 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<n2)
  n = n2;
else
  n = n1;
:
 2dup < IF swap THEN drop ;

min	n1 n2 -- n	core
if (n1<n2)
  n = n1;
else
  n = n2;
:
 2dup > 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
#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.lo<d1.lo);
#else
d2 = d1+n;
#endif
:
 s>d 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.lo<d1.lo);
#else
d = d1+d2;
#endif
:
 rot + >r 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<d2.lo);
#else
d = d1-d2;
#endif
:
 dnegate d+ ;

dnegate	d1 -- d2		double
/* use dminus as alias */
#ifdef BUGGY_LONG_LONG
d2 = dnegate(d1);
#else
d2 = -d1;
#endif
:
 invert swap negate tuck 0= - ;

d2*	d1 -- d2		double		d_two_star
#ifdef BUGGY_LONG_LONG
d2.lo = d1.lo<<1;
d2.hi = (d1.hi<<1) | (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<<n;
:
    0 ?DO 2* LOOP ;

\ comparisons(prefix, args, prefix, arg1, arg2, wordsets...)
define(comparisons,
$1=	$2 -- f		$6	$3equals
f = FLAG($4==$5);
:
    [ char $1x char 0 = [IF]
	] IF false ELSE true THEN [
    [ELSE]
	] xor 0= [
    [THEN] ] ;

$1<>	$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
f = FLAG(u1-u2 < u3-u2);
:
 over - >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 &&
       memcmp(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+ -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<c_addr+u; cp++)
    ukey = ((((ukey<<rot) | (ukey>>(ubits-rot))) 
	     ^ toupper(*cp))
	    & ((1<<ubits)-1));
:
 dup rot-values + c@ over 1 swap lshift 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<endp && isspace(*c_addr1))
  c_addr1++;
if (c_addr1<endp) {
  for (c_addr2 = c_addr1; c_addr1<endp && !isspace(*c_addr1); c_addr1++)
    ;
  u2 = c_addr1-c_addr2;
}
else {
  c_addr2 = c_addr1;
  u2 = 0;
}
:
 BEGIN  dup  WHILE  over c@ bl <=  WHILE  1 /string
 REPEAT  THEN  2dup
 BEGIN  dup  WHILE  over c@ bl >   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 + ;

\+standardthreading

>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 ;

\+

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(u2<u1 && ferror((FILE *)wfileid));
/* !! is the value of ferror errno-compatible? */
if (wior)
  clearerr((FILE *)wfileid);

read-line	c_addr u1 wfileid -- u2 flag wior	file	read_line
/*
Cell c;
flag=-1;
for(u2=0; u2<u1; u2++)
{
   *c_addr++ = (Char)(c = getc((FILE *)wfileid));
   if(c=='\n') break;
   if(c==EOF)
     {
	flag=FLAG(u2!=0);
	break;
     }
}
wior=FILEIO(ferror((FILE *)wfileid));
*/
if ((flag=FLAG(!feof((FILE *)wfileid) &&
	       fgets(c_addr,u1+1,(FILE *)wfileid) != NULL))) {
  wior=FILEIO(ferror((FILE *)wfileid)); /* !! ior? */
  if (wior)
    clearerr((FILE *)wfileid);
  u2 = strlen(c_addr);
  u2-=((u2>0) && (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(u2<u1 && ferror((FILE *)wfileid));
  if (wior)
    clearerr((FILE *)wfileid);
}

\+

emit-file	c wfileid -- wior	gforth	emit_file
#ifdef HAS_FILE
wior = FILEIO(putc(c, (FILE *)wfileid)==EOF);
if (wior)
  clearerr((FILE *)wfileid);
#else
putc(c, stdout);
#endif

\+file

flush-file	wfileid -- wior		file-ext	flush_file
wior = IOR(fflush((FILE *) wfileid)==EOF);

file-status	c_addr u -- ntype wior	file-ext	file_status
char *filename=tilde_cstr(c_addr, u, 1);
if (access (filename, F_OK) != 0) {
  ntype=0;
  wior=IOR(1);
}
else if (access (filename, R_OK | W_OK) == 0) {
  ntype=2; /* r/w */
  wior=0;
}
else if (access (filename, R_OK) == 0) {
  ntype=0; /* r/o */
  wior=0;
}
else if (access (filename, W_OK) == 0) {
  ntype=4; /* w/o */
  wior=0;
}
else {
  ntype=1; /* well, we cannot access the file, but better deliver a legal
	    access mode (r/o bin), so we get a decent error later upon open. */
  wior=0;
}

\+
\+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
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 (r1<r2)
  r3 = r2;
else
  r3 = r1;

fmin		r1 r2 -- r3	float
if (r1<r2)
  r3 = r1;
else
  r3 = r2;

represent		r c_addr u -- n f1 f2	float
char *sig;
int flag;
int decpt;
sig=ecvt(r, u, &decpt, &flag);
n=(r==0 ? 1 : decpt);
f1=FLAG(flag!=0);
f2=FLAG(isdigit((unsigned)(sig[0]))!=0);
memmove(c_addr,sig,u);

>float	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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>