Description:
|
Return a single byte value in the range 0 to 255 from machine address a in memory, or return a
sequence containing i consecutive byte values starting at address a.
There are in fact nine further variations of the peek function:
peek1s peek2s peek4s peek8s
peek1u peek2u peek4u peek8u
peekNS
which from left to right retrieve bytes, words, dwords, and qwords,
with the first row obtaining signed values and the second unsigned.
The peekNS routine takes three parameters: (a or {a,i}), size, and signed, eg peek4u(a)==peekNS(a,4,false).
The peek routine is actually an alias of peek1u, and when passed a
parameter of {a,i} returns a string, whereas when given the same
parameter all the others, including peek1s, return a
dword-sequence.
|
Comments:
|
The peek8s/u routines are not intended for use on 32-bit; for more details see the Technicalia dropdown below.
The 32-bit values returned by peek4s/u() may be too large for the 32-bit phix
integer type (31-bits), so you should use atom
variables, likewise for 64-bit values returned by peek8s() on 64-bit phix.
For similar reasons all variables that hold an address should also be declared as atoms.
When passed an atom parameter, peek(1|2)(s|u) (and peek4(s|u) on 64-bit) are
however guaranteed to return an integer, should that help any.
Historically the peek({a,i}) form was faster than reading one byte at a time in a loop, however
recent optimisations mean that is no longer necessarily (but will more often than not be) true.
Specifically, when the overhead of subscripting to extract the individual elements of a
sequence result that might otherwise not exist is taken into account,
then individual peeks in a loop is likely to be faster. Of course if you are retreiving a
string/sequence, and keeping a copy of it
anyway, then whether or not you examine the individual characters/elements the peek({a,i}) form
will almost certainly be faster.
Remember that peek always takes just one argument, which in the second form is
actually a 2-element sequence (except for peekNS, which takes 3 parameters).
|
Technicalia
|
While somewhat flakey versions of peek8s and peek8u now exist in 32-bit phix
they are not formally supported; any code that is required to run on both 32
and 64 bit should stick to using peek4s/u and below. Atoms in 64-bit phix use
80-bit floats that have a maximum precision of 63 bits+sign, perfect for peek8s.
However, atoms in 32-bit phix use 64-bit floats that have a maximum precision
of 53 bits, hence loss of data is avoided by making these routines return a
two-element pair of unsigned dword-sized blocks when necessary, as the
following example (which assumes little endian) shows. When peek8s or peek8u
is used in a 32-bit application (not recommended and not officially supported),
it checks for precision loss:
poke(addr,{0,0,0,0,0,0,0,#80,1,0,0,0,0,0,0,#80})
result = peek8u({addr,2})
On 64-bit result gets set to {#8000000000000000,#8000000000000001}
On 32-bit result gets set to {#8000000000000000,{#80000000,#00000001}}.
Internally, of course, result[1] is an atom value that happens to fit quite comfortably in 53 bits, whereas
attempting to do the same for result[2] would simply throw away the trailing 1, along with 10 other bits.
In hex, that translates as two-and-half nibbles, or hex characters, roughly speaking the trailing #03FF, so
very loosly speaking this split occurs when any quadword does not end #x000, or start with #000x or #FFFx.
(Internally it uses a simple and straightforward "is the reconstructed qword binary identical?" test. In
decimal, the largest integer on 32 bit is 9,007,199,254,740,992 as per floats are not exact.)
Note that when two dwords are returned, they are always unsigned, even for peek8s, and are in the correct
order for sprintf("#%08x%08x",result[2]) but the wrong order for poke4(addr+8,result[2]) ,
whereas poke8(addr,result[1]) together with poke4(addr+8,reverse(result[2])) would
make for the perfect bitwise copy, and obviously it would all be far easier if you just did it all using
peek4s/u and poke4s/u, as I recommended in the first place. (And likewise, on 32 bit, if you try and shift/
add the results from peek4s/u together to form a quadword, you will get exactly the same precision loss that
all this was trying to avoid.)
Naturally poke8 would go hopelessly wrong should you pass it a pair of dwords obtained from peek8s/u, that is,
when under 32-bit, and obviously poke8(addr,result) would fail over the nested subsequence. Also note that the
32-bit version of printf() is not designed to cope well with negative integer/hex values less than -#80000000,
(arguably a bug, but hex values of more than 32 bits on a 32 bit system, one has to ask is it worth fixing?).
In truth, there are two reasons why peek8s/u exist at all on 32-bit: peek8u is used (lightly and in a
non-critical manner) when cross-compiling, ie when a 32-bit phix is asked, via a format directive, to create a
64-bit executable, and secondly so that code such as
if machine_bits()=32 then
r = peek4u(k)
else -- machine_bits()=64
r = peek8u(k)
end if
-- r = peekNS(machine_word(),0) -- one-line equivalent to the above
compiles cleanly rather than complains that peek8u does not exist, even though you are not going to call it
because you understand and accept that it can be a bit flakey on 32-bit. Also note that the 32-bit version
of dir() uses a peek8 on linux on the assumption you will not have many files >8192TB lying about.
Since 64-bit phix uses 80-bit floats that have the required 64 bits of precision, peek8s/u does not need to do
any of this, not that your code would suffer much from a few sequence() checks on the result.
Note that the last two parameters to the peekNS routine must be integers: a size of 2.001 should (for performance reasons)
trigger a run-time error, as it is not one of the integers (1|2|4|8), in contrast sign is just tested for non-zero rather
than formally validated as being (0|1).
|