Binary manipulations in FORTH

published: 21 December 2024 / updated 21 December 2024

Lire cette page en français

 

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:

  b7b6b5b4b3b2b1b0
231 11100111

How do you know if bit b2 is 0 or 1?

Reminder of the operation of the AND operator:

C1C2AND
000
010
100
111

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:

  b7b6b5b4b3b2b1b0
231 11100111
%00000100 00000100
AND 00000100

Analysis of the result for the test on a single bit:

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:

  b7b6b5b4b3b2b1b0
231 11100111
%11111011 11111011
AND 11100011

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:

  b7b6b5b4b3b2b1b0
231 11100111
%00001000 00001000
OR 11101111

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.