2
votes

When using large arrays it would be nice to be able to adjust the array for a certain number of bytes per number. Mostly I want fast routines to read such adjusted multi byte numbers to singles on the stack and conversely to store singles in the array adjusted for a certain number of bytes. In a 64 bit system there is a need for other single number arrays than one byte (c@ c!) and eight bytes (@ !).

So how to implement

cs@ ( ad b -- n )
cs! ( n ad b -- )

where b is the number of bytes. The word cs! seems to work as

: cs! ( n ad b -- )  >r sp@ cell+ swap r> cmove drop ;

but how about cs@ and how to do it in pure ANS Forth without sp@ or similar words?

3
Note that CS stands for Control-Flow Stack operations, likecs-roll etc, see The optional Programming-Tools word set. So usage cs suffix for other semantics may confuse.ruvim
Yes, it felt familiar but I didn't thought about that. Better names are perhaps mb@ and mb!Lehs
Must the expression addr @ give the same result as addr 8 mb@ in case of cell size is 64 bits? I mean that cmove may lead to different result in such case. Also note that in general case byte order may differ in memory and on stack.ruvim
No, that's not necessary. It just have to work for 2-7 bytes, no matter how. Speed is the interesting thing.Lehs
@ruvim: 2-7 bytes in 64 bit systems and 2-3 bytes in 32 bit systems would be nice.Lehs

3 Answers

2
votes

The Forth200*x* committee has put quite some time into developing a Memory Access wordset that would suite. We have not included it into the standard thus far due to its size.

1
votes

The compatible way is to use C@ and bitwise operations. To use the same byte order in memory as Forth system there is need to detect endianness and compile the suitable versions of the certain definitions.

\ These definitions use little-endian format in memory.
\ Assumption: char size and address unit size equal to 1 octet.

: MB! ( x addr u -- )
  ROT >R  OVER +  SWAP
  BEGIN  2DUP U>  WHILE  R> DUP 8 RSHIFT >R OVER C! 1+ REPEAT
  2DROP RDROP
;
: MB@ ( addr u -- x )
  0 >R  OVER +
  BEGIN  2DUP U<  WHILE  1- DUP C@ R> 8 LSHIFT OR >R  REPEAT
  2DROP R>
;

For higher performance it could be better to use implementation specific features (including W@, T@, Q@, SP@, etc) or even inline Forth-assembler.

Note that a straightforward definition via DO loop usually has worse performance (depends on optimizer; 10% in SP-Forth/4.21). The code for reference:

: MB! ( x addr u -- )
  OVER + SWAP ?DO DUP I C! 8 RSHIFT LOOP DROP
;
: MB@ ( addr u -- x )
  DUP 0= IF NIP EXIT THEN
  0 -ROT
  1- OVER + DO 8 LSHIFT I C@ OR -1 +LOOP
;

We can't use ?DO in the second case because of decreasing the loop index and +LOOP semantics: it leaves circle when the index crosses "the boundary between the loop limit minus one and the loop limit".

1
votes
\ little-endian (eg. pc, android)
: mb! ( n ad i -- )  2>r here ! here 2r> cmove ;
: mb@ ( ad i -- n )  here 0 over ! swap cmove here @ ;

\ big-endian (eg. mac)
: mb! ( n ad i -- )  2>r here ! here cell + r@ - 2r> cmove ;
: mb@ ( ad i -- n )  here 0 over ! cell + over - swap cmove here @ ;

\ little-endian test
1 here ! here c@ negate .

Of course HERE could be any one cell buffer.

Thanks ruvim for parsing the process forward!