Now that we know how to manipulate ASCII codes in poke, we may wonder how can we combine them to conform words or, more generally, strings of ASCII characters.
GNU poke has support for native ASCII string values. The characters
conforming the string are written between "
and "
characters, like in:
(poke) "word" "word"
Note, and this is important, that string values are as atomic as
integer values: they are not really composite values. The fact that
"word"
contains an r
at position 3 is like the fact that
the value 123
contains a digit 2
at position 2.
Like in character literals, poke strings support several escape sequences that help to denote non-printed characters, such as new lines and horizontal tabs. See String Literals.
Let’s start with a fresh memory buffer IOS *scratch*
:
(poke) .mem scratch The current IOS is now `*scratch*'. (poke) dump :size 48#B 76543210 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789ABCDEF 00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
If we wanted to, somehow, store the word word
in this IO space,
encoded in ASCII, we could proceed as:
(poke) char @ 0x12#B = 'w' (poke) char @ 0x13#B = 'o' (poke) char @ 0x14#B = 'r' (poke) char @ 0x15#B = 'd' (poke) dump :size 48#B 76543210 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789ABCDEF 00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010: 0000 776f 7264 0000 0000 0000 0000 0000 ..word.......... 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
This worked. The ASCII part of the dump
output, which
interprets the bytes as ASCII, clearly shows the word word
at
the offset where we poked the character values. However, we can do
better: string values can be mapped themselves.
String values use the type specifier string
. As any other kind
of value in poke, they can be mapped from an IO space:
(poke) string @ 0x12#B "word"
Clearly that is the string resulting from the concatenation of the character values that we poked before.
The question now is: how did poke know that the last character of the
string was the d
at offset 0x15#B? The fact the character code
0 (also known as the NULL character) at offset 0x16#B is
non-printable, doesn’t imply it is not part of the ASCII character
set. Clearly, we have to pick an ASCII code and reserve it to mark
the end of strings. Like the C programming language, and countless
formats and systems do, poke uses the NULL character to delimit
strings.
Now consider this:
(poke) "word"'length 4UL (poke) "word"'size 40UL#b
Using the length
and size
attributes, poke tells us that
the length of the string "word"
is 4 characters, but the size
of the string value is 40 bits, or 5 bytes. Why this discrepancy?
The size
value attribute tells how much storage space a given
value required once mapped to an IO space, and in the case of strings
it should count the space occupied by the terminating NULL character.
Poking string values on the IO space is as straightforward as poking integers:
(poke) string @ 0x22#B = "WORD" (poke) dump :size 48#B 76543210 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789ABCDEF 00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000010: 0000 776f 7264 0000 0000 0000 0000 0000 ..word.......... 00000020: 0000 574f 5244 0000 0000 0000 0000 0000 ..WORD..........
Strings can be concatenated using the string-concatenation +
operators:
(poke) "foo" + "bar" "foobar"
The string resulting from the operation above has length 6 characters,
and size 7 bytes. The terminating NULL character of "foo"
is
lost in the operation. This is easily seen:
(poke) "foo"'size + "bar"'size 0x40UL#b (poke) ("foo" + "bar")'size 0x38UL#b
The string concatenation operator requires two strings. Therefore, if we wanted to append a character to a string, we would get an error:
(poke) "Putin" + 'a' <stdin>:1:1: error: invalid operands in expression "Putin" + 'a'; ^~~~~~~~~~~~~
It is possible to transform a character value (i.e. a byte value) into a string composed of that character using a cast:
(poke) 'a' as string "a"
Using that cast, we can now append:
(poke) "Putin" + 'a' as string "Putina"