使用大型数组时,最好能够为每个数字调整一定数量的字节。大多数情况下,我希望快速例程将这种调整后的多字节数读取到堆栈上的单个字节,反之,将单个字节存储在为特定字节数调整的数组中。在64位系统中,除了一个字节(c@c!)和八个字节(@!)之外,还需要其他单个数字数组。
那么如何实现
cs@ ( ad b -- n )
cs! ( n ad b -- )
其中b是字节数。cs这个词!似乎可以作为工作
: cs! ( n ad b -- ) >r sp@ cell+ swap r> cmove drop ;
但是cs@怎么样?如何在没有sp@或类似单词的纯ANS Forth中做到这一点?
Forth200*x*委员会已经花了相当长的时间来开发一个适合的内存访问字集。到目前为止,由于它的尺寸,我们还没有将其纳入标准。
兼容的方法是使用C@
和按位操作。为了在内存中使用与Forth系统相同的字节顺序,需要检测字节序并编译某些定义的合适版本。
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>
;
为了获得更高的性能,最好使用特定于实现的特性(包括W@
、T@
、Q@
、SP@
等),甚至使用内联Forth汇编程序。
请注意,通过DO
循环直接定义通常性能较差(取决于优化器;SP Forth/4.21中为10%)
: 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
;
由于减少了循环索引和+LOOP
语义,我们不能在第二种情况下使用?DO
:当索引越过"循环极限减去一和循环极限之间的边界"时,它会离开圆圈。
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 .
当然,HERE可以是任何一个单元缓冲区。
感谢ruvim向前解析过程!