I'll not give pseudocode for any of these. AND INVERT OR XOR are all bitwise operators, acting on every bit of the cell.
\ x1 u -- x2 ; shift x1 left u places, filling with zero
\ x1 u -- x2 ; shift x1 right u places, filling with zero
0< 0= 0<> < > all act as you would expect, returning a flag with all bits set for TRUE, or zero for FALSE.
U< is similiar but for unsigned numbers (see below)
(n1 n2 -- n3) POP PSP TO W TOS W < IF W TO TOS THEN NEXT
(n1 n2 -- n3) POP PSP TO W TOS W > IF W TO TOS THEN NEXT
\ n1 n2 n3 -- f ; true if n2 <= n1 < n3
OVER - >R - R> U< ;
WITHIN also works for unsigned numbers, so if n2 > n3 then n1 n2 n3 WITHIN is true if n1 > n2
The form of signed number representation usually used is called two's complement, and that is the only form I will deal with. Negative numbers are represented by inverting all the bits of the positive equivalent and adding one. The same operation repeated returns the original number. Hence:
: NEGATE INVERT 1+ ;
The largest possible number (all bits set), when seen as a signed number, is -1. It can easily be seen that adding 1 to -1 gives zero. HALFMAX (only high bit set), which we saw when discussing the runtime looping words, is the most negative number, obtained by adding one to the largest postive signed number. This circular arrangement of numbers is seen in the behaviour of words like WITHIN above
Since a TRUE flag is also -1, arithmetic can be done on that too. Forth words do not care where their arguments come from! Conditionals such as IF will regard any number of bits set as a true flag, but to make it 'canonical' use:
0<> ( x -- 0 | -1 ) TOS IF -1 TO TOS THEN NEXT
: 0< HALFMAX AND 0<> ;
A high-level U< can be written:
: U< \ ni n2 -- f
2DUP XOR 0< IF \ signs differ?
DROP 0< \ if n1 < 0 then n2 > 0
ELSE < THEN ;