Binary manipulations in FORTH
published: 21 December 2024 / updated 21 December 2024
article: 02 mai 2020 / mis à jour 04 mai 2020
Test and manipulate bits in a byte
In FORTH language, there is no word or operator allowing to manipulate specifically bits in a byte. However, the operators OR, AND and NOT, as well as XOR, allow to know and specifically modify a bit in a single precision integer.
In the rest of our article, we will pay special attention to carrying out only manipulations binary at the byte level, ie a group of eight bits. But most FORTH words working on integers of 2 bytes (FlashForth) or 4 bytes (gForth) remain usable beyond of the 8 byte bits.
In the rest of the article, any hexadecimal value will be preceded by "$", any binary value will be preceded by "%", example:
$24 constant DDRB \ Direction Data Register PORT B $25 constant PORTB \ data in PORT B %11111111 DDRB c!
This syntax is specific to FlashForth.
Test a bit in a byte
Take as an example a memory byte containing the value 231 which is represented in binary as follows:
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | |
---|---|---|---|---|---|---|---|---|
231 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
How do you know if bit b2 is 0 or 1?
Reminder of the operation of the AND operator:
C1 | C2 | AND |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
In FORTH, the word and
performs this logical operation bit by bit on
an integer value. So, by taking a mask value, we can perform
an AND on the content of our byte:
%11100111 \ value 231 to test %00000100 \ value 4 for binary mask and . \ display 4
The value % 00000100
(4 in decimal) is our binary mask.
To test a single bit in our byte, the value of the bit mask will ALWAYS be
a power of 2: 2EXP0 = 1,2EXP1 = 2, 2EXP2,4, 2EXP3 = 8 ... The exponent of 2 corresponds to the rank
of the bit to be tested in the byte. For example, bit b6 has rand 6. So we will test it with
the mask 2EXP6 = 128, i.e. %01000000
.
You understand better why it is easier to use a mask in its form binary rather than decimal or hexadecimal.
Here is the result of the execution of and
on our byte:
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | |
---|---|---|---|---|---|---|---|---|
231 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
%00000100 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
AND | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Analysis of the result for the test on a single bit:
- if the result is not zero, the bit is at 1
- if the result is zero, the bit is at 0
FlashForth's mtst word
In the FlashForth version, there is the word mtst
which retrieves the
content of an address and allows to perform a test on a specific bit of
this byte:
37 constant PORTB \ Port B Data Register
36 constant DDRB \ Port B Data Direction Register
35 constant PINB \ Port B Input Pins
%00000000 DDRB c!
%01000000 PINB mtst \ test bit B6 in PORTB
Here, %01000000 PINB mtst
retrieves the contents of the register pointed to by PINB
and performs a test on bit b6 of this port.
Set a bit to 0 in a byte
Take our value 231 in a byte. We want to zero bit b2, just this one without changing the other bits. We could do this:
231 %11111011 and . \ display 227
Here is the result of the execution of %11111011 and
on our byte:
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | |
---|---|---|---|---|---|---|---|---|
231 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
%11111011 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
AND | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
If we look at the binary mask value %11111011
we note that this value corresponds
bit by bit when inverting %00000100
. It turns out that the operator XOR
is perfectly suited to perform this bit-by-bit inversion:
2 base ! %00000100 \ binary mask for b2 %11111111 xor . \ display 111110111 decimal
Here is the complete FORTH sequence to zero bit b2 in 231:
231 %00000100 \ binary mask for b2 %11111111 xor \ invert bits for binary mask and
FlashForth's mclr word
In the FlashForth version, there is the word mclr
which sets one or more to zero.
bits in the content of an address:
37 constant PORTB \ Port B Data Register
36 constant DDRB \ Port B Data Direction Register
%11111111 DDRB c!
%01000000 PORTB mclr \ set bit B6 to 0 in PORTB
Here, %01000000 PORTB mclr
sets bit b6 of the contents of the register pointed to by PORTB
to zero.
Set a bit to 1 in a byte
Take our value 231 in a byte. We want to set bit b3 to one, just this one without changing the other bits. We could do this:
231 %00001000 or . \ display 239
Here is the result of the execution of %000001000 or
on our byte:
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | |
---|---|---|---|---|---|---|---|---|
231 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
%00001000 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
OR | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
FlashForth's mset word
In the FlashForth version, there is the word mset
which sets 1 one or more
bits in the content of an address:
37 constant PORTB \ Port B Data Register
36 constant DDRB \ Port B Data Direction Register
%11111111 DDRB c!
%01000000 PORTB mset \ set bit B6 to 1 in PORTB
Here, %01000000 PORTB mset
sets bit b6 of the contents of the register pointed to by PORTB
to 1.