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?
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.
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".
\ 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!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.