03 August 2023

File I/O using Get and Put

This time I want to put the focus on the Get and Put commands that were first introduced in GFA-BASIC 32. These are high-performance commands to save and load the contents of a variable in a binary format. The syntax for these commands:

Get #n, [index%], variable
Put #n, [index%], variable

The index% parameter specifies the record number, but it is only required for a Random mode file. A Random mode file needs a Len value when opened, which is the last parameter in the Open command:

Open pathname [For mode] [Access access] [share] [Commit] [Based 0/1] As [#]filenumber [Len=reclength]

When the index% parameter is used, the file pointer is positioned at index% * Len. For all other files (mode is Output, Input, Binary, Update, or Append) the index% parameter should be omitted and:

Get reads from the current file position.
Put writes the variable at the current position.

Depending on the Based setting in the Open command, which is 1 by default, the file position is actually calculated as (index% - Based) * Len. If Len is omitted the record length is set to 1. Consequently, when index% is used with a Len setting of 1, the index% value is multiplied by 1. Therefor, it is important to omit the parameter in non-Random files, so most often these commands are used like this:

Put # 1, , a$
Get # 1, , a$

The binary format
The contents of the variable is not saved as a string representation of the value like Print # and Write # do. The contents of the variable is saved in the same format as it is stored in memory. For instance, a Float variable is stored in 4 bytes and Put only copies those 4 bytes to the opened file. Get reads the 4 bytes and moves them directly into the Float variable. Saving a the floating-point as a 4 byte binary prevents the rounding otherwise necessary for writing a string representation using Write # or Print #. (BTW Print # isn't a suitable file I/O command unless you are saving a string.)

If the variable being written is a numeric Variant, Put writes 2 bytes identifying the variable type of the Variant and then writes the binary representation of the variable. So, a Variant holding a Float is saved in 6 bytes: first 2 bytes identifying the Variant as basSingle (4) and followed by 4 bytes containing the data.
If the Variant holds a string, Put writes 2 bytes to identify the type (basVString) , then 2 bytes specifying the length of the string, followed by the string data.

A variable-length string is saved likewise, only it omits the 2 bytes that specify the datatype. Put writes the length in 2 bytes, directly followed by the string data. The disadvantage is the limitation of 65535 characters, because that's the maximum value that can be stored in 2 bytes (Card).

File mode
Since Put and Get are binary I/O commands you might think they are restricted to Random mode or Binary mode files, but that isn't true. Binary I/O can happen with files opened in all modes, including Output, Input, Update, and Append.These modes do not differ much; opening a file with a certain mode is more of a reminder to the developer than that it defines an I/O operation. It is the file I/O command that defines the input-output format. Print # always writes a string, and Out # always write a binary value, whatever the mode of the opened file. Making a file Random allows Get and Put to read/write to a record directly with out setting the file pointer, otherwise it is the same as Update.

The Get and Put commands are fast binary I/O commands and are an interesting addition to the file I/O commands from previous versions of GFA-BASIC.

29 May 2023

Sleep doesn't wait?

Once in a while you might have created a quick and dirty program that repeatedly prints some value from inside the Sleep message loop. However, there is something funny about that. Shouldn't Sleep wait until there is a message in the queue? So, how can you print something repeatedly? How can Sleep return from its wait state?

The issue
The next small program shows the issue. It opens a window and enters the message loop while printing a dot each time. (I wanted to print the number of the message (_Mess) that is retrieved from the queue, but that isn't possible. The _Mess variable is set in the window procedure and is not part of the Sleep (GetEvent or PeekEvent) command. In addition, GB32 filters the message and not all message numbers are stored in _Mess. So, instead, we print a dot each time the loop is executed.)

OpenW 1
PrintScroll = True
PrintWrap = True
  Print ".";
Until Me Is Nothing

According to the documentation Sleep should 'sleep' until there is a message available. However, the Print command is executed frequently at a pretty high speed printing dots in the window. This shouldn't happen, should it?

Now, lets copy the code above into the IDE, save it and execute the code as a stand-alone EXE. For this choose the 'Launch EXE' button on the toolbar:


Now we see something completely different: in the stand-alone EXE the Print command is not executed repeatedly; now Sleep does wait.

Difference between IDE and standalone EXE
When a program is run from inside the IDE the Sleep command doesn't seem to wait, because it returns repeatedly. However, the clue here is the word 'seem', because Sleep does actually wait (as does GetEvent), but it receives WM_TIMER messages at a pretty high speed. These WM_TIMER messages are missing in the stand-alone EXE. So, obviously, the IDE must be responsible for the WM_TIMER messages and indeed it is.

The IDE is to blame
The IDE uses several timers for different purposes. Well known timers are Gfa_Minute and Gfa_Second, that fire every minute or second resp. These two timers are disabled before a program is run (F5), so these are not the cause of the issue. However, there are two more IDE timers that are used to update UI elements like the toolbar buttons, for instance. Every 200ms the clipboard is checked to see if there is text available. If so, the toolbar's Paste button is enabled. Another timer is used to update the sidebar, this one fires every 100ms.

As it happens, these timers are not disabled before a program is run from within the IDE.These timers keep firing their WM_TIMER messages to the thread's queue, which are read by our Sleep in the program's message loop. Our program is executed in the same thread as the IDE and thus takes over the retrieving and dispatching of messages. While running a program, the IDE's message loop isn't executed any longer and all the messages for the IDE are obtained by the program's message loop. After quitting the program the IDE returns to its main message loop and takes over again.

Be aware that the number of times Sleep and GetEvent return from their wait state depends on the timer resolution of the IDE's timers that are still active while running the program.

04 January 2023

The danger of And and Or

The previous blogpost discussed the difference between And vs &&, and Or vs || in conditional statements. It showed why && and || should be used instead of And and Or. The post also contained an example where it is legitimate to use the And operator:

Local Int x = %111      ' binary notation of 7
If x And %11 > 0        ' test if lower 2 bits are set
  ' do something

However, this condition is true for the wrong reasons. The condition is also true when only bit one is set. To prevent this and only check for the two lower bits we could change it into:

If x And %11 == 3       ' test if lower 2 bits are set

This seem to work ok, but the condition is still true for the wrong reason and that's gone haunt you. Let's try something different, let's test if bit 1 (value is 2) is set using the same method:

If x And %11 == 2       ' test if bit 1 is set
  Message "Condition is True"
  Message "Condition is False"

When we run this code, the program displays:

Screenshot 2023-01-04 071908

As you can see, the statement does not recognize that bit 1 is set (binary %10 == 2), despite the fact that the bit is 1.

So, what is going on here? Can't we trust And anymore? Maybe it is a bug? It is not a bug; it works like this is in any language and to understand this behavior we need to look at the operator precedence table. The helpfile contains the table under the topic name Operator Hierarchy:

( ) parenthesis
+ - ~ ! unary plus, unary minus, bitwise NOT, logical NOT
$ & explicit string addition
^ the power of
* / multiply, divide (floating-point)
\ Div Mul integer division, integer multiplication
% Mod Fmod integer and the floating point modulo
+ - Add Sub addition (the string addition, too) and subtraction
<< >> Shl Shr Rol Ror all shift and rotate operators (also: Shl%, Rol|, Sar8, etc.)
%& bitwise And
%| the bitwise Or
= == < > <= >= != all comparisons (also: NEAR ...)
And bitwise And
Or bitwise Or
Xor Imp Eqv bitwise exclusive Or, implication and equivalence
&& logical And
|| logical Or
^^ logical exclusive Or
Not bitwise complement

When an expression is evaluated the rules of operator precedence are applied. For instance, multiplication and division go before addition and subtraction. The table shows this by placing the * and / operators before (or above) + and -. Now look at the And operator; it comes after the comparison operators. This means that if an expression contains a comparison operator, it is evaluated (computed/calculated) before the And operator is applied. In our case, the part %11 == 2 is evaluated before And is applied. Since %11 = 3, 3 is not equal to 2 (results in False) and x And 0 is always 0, the result is 0 (False). These are the steps involved in the evaluation:

(Step 1) If x And %11 == 2 
(Step 2) If x And False
(Step 3) If x And 0
(Step 4) If 0

Can you now see why the original example form above results in True?

If x And %11 == 3       ' True for the wrong reason

The problem can be solved by using either parenthesis or %&:

If (x And %11) == 2     ' test if bit 1 is set
If x %& %11 == 2        ' GB way

In all other languages than GB the first solution is used. However GB defines an additional 'And' operator with a higher precedence that comes before the comparison operators: the %& operator. See the table for its location.

The Or operator suffers from the same problem, so GB defines an or-operator with higher precedence, the %| operator.

Is there a moral to this story? Try to avoid the use of And (&) and Or (|), unless - for instance - when translating C/C++ code to GB. (C/C++ does not support %& and %|.) Otherwise you might prefer the use of %& and %|.