hBasic > hManual > System

hBasic Manual
System changes

Interrupt Scope Fixes Fix scope during an interrupt
Fix for volkeys at start of run Fix volkey status at start of run (ack:aFox)
DEVICE fixes Fix crash on Android-10+
Sensors fix Fix for Sensors with less than 3 parameters
GR.Modify List fix Fix Gr.modify "list"
GR.Open statusbar fix Fix Gr.Open statusbar
Faster BYTE.WRITE.BUFFER


Array Pointers Allow array pointers for assignment
Array Functions Functions returning an array
TEXT/BYTE.OPEN
change behaviour on failure
FILE Absolute paths
SAF Infrastructure
Internal support for File.SAF commands
Shortcut Support StandAlone APKs can recieve shortcut data
FTP Server Embed an ftp server to allow access into scoped storage


Command Pre-Loading
Load up commands before Run
Control Stacks Modified
Combine control stacks and local stacks for  Functions
Directives
used to configure the intepreter
Bundle Expressions
Accept bundle.key expressions ( ** On Hold   --- awaiting bug fix --- ** )
MyPhoneNumber Removed
Acceleration
Hardware Acceleration changes.

Interrupt Scope Fixes

By 'scope' we mean the list/s that Basic! searches through when trying to find a variable.
There are mainly two types of scopes which a basic program is running in.

1. The main scope i.e
the main program or
the main block of an interrupt e.g OnBackKey
or
2. A local scope i.e
inside a function

An interrupt could happen in the middle of a function.
In 01.91,  Basic would detect this and change the scope (from local) to the 'global' list which starts at the main program list. If the variable is not found, it would keep searching through the list of the first function that was called, then the next function, and so on until it reached the end of the last called function (the function that was interrupted).
This normally works fine if the variable exists in the main list. Many uses of Interrupts rely on finding a variable in a convenient 'global' area such as the main list.

There are two problems with this design;
a) If the variable does not exist in the main list. Then it might (un-intentionally) be found in one of the other lists. This can lead to errors which could be difficult to trace.
and
b) If a function is called inside an interrupt's main block,

e.g
OnBackKey:
myfunc ()
Back.resume

basic still thinks it's inside an interrupt (which it is) and uses the 'global' scope. This means any local variable inside the function would be searched starting from 'main'. Only if the variable does not exist in main or any other preceeding function call, would the correct list be finally searched.

In 01.91, Functions called by an interrupt block were getting wrong scope.
This is clearly undesirable, and is a side-effect of an interrupt handling system not intended to call functions.

The following fixes were done.

Fixed scope during an interrupt.

a) Variable searches inside the main-block or an interrupt-main-block will search only the main list and nothing but the main list.

else
b) Variable searches inside a function-block (no matter when they are called) will only search the function's local variable list.

All that is needed is a flag to indicate which scope (a or b) the code is running in. This flag is implemented in this fix and is updated for each interrupt or function call and restored upon interrupt resumes and function exits.

Fix scope for new variables created during an interrupt

Make sure new variables in main-block and interrupt-main-block are always created in main scope.
Make sure new variables in function-block are always created in local scope.

This is a similar fix using the new scope flag for newly created variables.



Exceptions

The only exception to these fixes is code run inside an ONERROR block.
ONERROR is treated differently than other interrupts and it's behaviour (regarding scope) has been left alone. That is, it's scope is the scope that was interrupted. e,g If an error occurred in a function, then the scope of the ONERROR block is that of the function. The rationale behind being that it is sometimes useful to return to the error point with a GOTO, in which a flag or variable (in the same scope) is neccessary to transfer meaning.

Fix for volkeys at start of run

When VOLKEYS.OFF is run in the editor, the next program that is run does not reset a system flag, so volume keys stay blocked.

Fix was to reset that flag at start of a run.

Faster BYTE.WRITE.BUFFER

BYTE.WRITE.BUFFER <file_table_nexp>, <buffer_sexp>

(Basic 01.91 writes this output one low byte at a time, each time ignoring the high byte ( each character is two bytes).) which is rather slow.

This enhancement converts the string to a byte array and writes the whole array at once.

The conversion (from a string to a byte array)  assumes this;
from UTF-16 (i.e a Basic/Java string)
to ISO-8859-1 (by default -intentionally to save lower byte after translation).
It is essentially a two-byte to one-byte conversion.

If there's anything in the high byte of the source (i.e >255) then a question mark glyph (3F) is used to mark unknown data because it can't find anything to map to.


Bundle Expressions

** Bundle Expressions have been removed due to a bug. Below is for reference only **

The interpreter will now accept bundle expressions of the forms;

Syntax

<nvar>.< key> simple key
or

<nvar>."<key>" quoted key

e.g
mybundle.key1
mybundle."Key 1"

The first form has no quotes surrounding the key and conforms to rules simiar to variable names;
Will be converted to lowercase,
Cannot have spaces
Must begin with a letter, but can have numbers.
The only allowable special characters are : "_"  or "@" or '#"

The second form has quotes and the key can be any string similar to bundle.put,
Is case sensitive,
Is fully compatible with bundle.put

Expression Rules

  - A bundle.key will evaluate to a value similar to: bundle.get
        e.g
            bundle.create mybundle
            bundle.put mybundle, "key1", 99            

            bundle.get mybundle, "key1", n    % old way
            n = mybundle.key1        % new way

    - A bundle.key can also be the destination of an assignment
        e.g
            bundle.create mybundle
            
            bundle.put mybundle, "key1", 98   % old way
            mybundle.key1 = 99      % new way

    - A bundle.key value can either be a number or a string
        e.g
            bundle.create mybundle
            mybundle.key1 = 99
            mybundle.key2 = "red"

            n = mybundle.key1        % value at key1 must exist and is a number
            s$= mybundle.key2        % value at key2 must exist and is a string

      If the index or key does not exist, you will get an error.

    - A bundle.key assignment can either be a number or a string
        and can also change the value type.

            bundle.create mybundle

            mybundle.key1 = n        % value is a number
            mybundle.key1 = "wow"        % value is no longer a number

    - A bundle.key will auto-create the key if the key is not found.

            bundle.create mybundle

            mybundle.newkey3 = "first value"


    - A bundle.key will NOT auto-create a new bundle if the index is not found.
        The bundle (i.e it's index) MUST exist either by creating the bundle with
        bundle.create or bundle.put. Otherwise an error occurs.
        (note: this is different to the normal bundle.put behaviour)

        e.g

            bundle.create mybundle        % mandatory

            mybundle.key2 = "red"        % assignment auto-creates key2

            print mybundle.key2        % red
            print your_bundle.key2        % ERROR


    - A numeric bundle.key can be used with pre/post inc/dec operators similar to
        variables.
        e.g
            bundle.create mybundle

            mybundle.key1 = 99
            n = mybundle.key1++        % n=99
            a = mybundle.key1        % a=100

        These operators will update the bundle value pointed to by bundle.key,
        in which the value must be numeric.

    - A bundle.key can have embedded keys i.e bundle indexes inside bundle keys (bundles within bundles). This is of the form

bundle.key1.key2.key3..

bundle is an <nvar> and it's value is the bundle index of key1.
key1's value is the bundle index of key2.
key2's value is the bundle index of key3.

key3's value will be the value of the whole expression and any update will be to the value at key3.
Only the last key (key3 in this case) can be auto-created (if it doesn't exist).

Note that all keys must be literal, they cannot be expressions.

        e.g
      bundle.create location
        location.area = "Westminster"
        location.city = "London"


        bundle.create contact
        contact.name = "John"
        contact.phone = 55556666
        contact.assigned = location

        bundle.create work
        work.sales = contact
        !work.dog = humpty

        ? work.sales.name;
        ? " will cover ";
        ? work.sales.assigned.area        % embedded keys

        ! John will cover Westminster


Known quirks
If both the index and key are bad, you might get 2 errors reported.


Array Pointers

Assignments

e.g  a[] = b[]    or    LET a[] = b[]
e.g  a$[] = b$[]

a[] = b[]

Makes an exact copy of b[] to a[] including the size and dimensions
Background references, i.e passed by referenced variables, are preserved for a[].

   
hBasic uses the term  "array pointer" (e.g a[] )as representing the whole array. An array pointer has no indices within the brackets. It is a reference to an array as opposed to just one element.

(This syntax is inline with traditional Basic 01.91 which already uses the syntax for
some commands and functions.)
   

Evaluation (place holder for future development).

Evaluation of a[] always returns 0        ( might be changed in the future )
Evaluation of a$[] always returns ""        ( might be changed in the future )
Evaluation of non-existent a[] leads to an error.

e.g PRINT a[]$, b[]
will print ",  0.0"

Array Functions

Functions can now return an Array (pointer) by adding "[]"

Array function names cannot be the same as existing non-array functions.

        e.g

        fn.def func_a$(n)[]           % define an Array Function

            dim b$[3]
            b$[2] = "nice"

            if n=1 then fn.rtn        % returns a useless default array
            if n=99 then fn.rtn b$[]  % return specified array

        fn.end                        % returns default array

        ! fn.def func_a$()            % (error) Function previously defined at:
       
        a$[] = func_a$(99)[]          % Array function assignment (deep copy)

        ! print func_a$(99)[]         % Error> Array functions cannot be evaluated :
        print a$[2]                   % only normal arrays can be evaluated with an index


TEXT/BYTE.OPEN change on failure

Before, if TEXT.OPEN or BYTE.OPEN fails to open a file, an unusable filetable entry was still created.
This no longer happens for v2.80+, there is no new filetable entry upon failure.


Shortcut and File-Share Support

First off, there is a difference between a shortcut and a fileshare.

A shortcut is a widget on the home screen that calls the app with a string of data.
Note that shortcut creation requires the app to be run once beforehand.

A file-share is a request (normally by a file-manger) to hBasic with a filename.
A) Legacy Basic only has shortcut support for a filename <something>.bas which is passed to the Standard app (i.e with editor) for execution. This is the same for hBasic (Standard).
B) hBasic also has shortcut support for StandAlone (i.e without editor) only if you had compiled the app as StandAlone with included the supporting patch H0290.

For (B), a shortcut can be created on the Android home screen. It can launch the app and send a string of data.
This feature will entertain any shortcut data string being sent by a shortcut launch and put the string into an undocumented function called COMMAND$().

Shortcuts to Standard APKs (non-standalone) will not be affected since they are always assumed to be <something>.bas.

Note that COMMAND$() is also used for a file-share data string (e.g from a file-manager) , the difference between a shortcut and a file-share is that a file-share will prepend a relative path to a file-name. Shortcut data strings are not prepended with anything, the data string is what the user typed in and is clean. The string can be any text and can have spaces, e.g "Dark Theme".

Shortcut fixes since v4.49.

Standard (i.e with editor)
Shortcuts (and Standard fileshares) with a new program request were being ignored if there was an old instance of the interpreter still hanging around.
From now on, the interpreter will let any new program take over, even if there's a old one still running.
File-Share fixes since v4.49.

Standard (i.e with editor)
Because of scoped storage, newer file managers are sending
a uri instead of a regular path to hBasic.
hBasic will now attempt to match these to it's source directory.
If it can't be found, then you will get an error comment in the Editor.
Standalone (apk)
E.g ..a file manager sending a file to a Standalone apk (fileshare);
If the file is within scoped storage, the apk will receive a relative
path from /data.
If the file is outside of scoped storage, the apk will receive a
full path (as best that hBasic can extract a path) starting with a
slash "/".
This does not affect shortcuts to a Standalone apk, which will always send the shortcut data cleanly.
DEVICE Fixes

Ack: dkal - forum member.
Permissions Fix 1 - Android 10+

DEVICE command would crash on devices with Android-10+ because Google decided not to allow access to 'Non-resettable device identifiers' which can potentially be tracked or used for ads. So they are banned for normal apps.

The two keys which did this was 'DeviceID' and 'SIM SN

They both require READ_PRIVILEGED_PHONE_STATE or hasCarrierPrivileges, of which
normal apps/users are unlikely to get.

To fix the crash ->

If the device is < Android Q
DeviceID is supposed to return the IMEI or MEID.
SIM SN is supposed to return the card serial number.

If the device is >= Android Q
DeviceID will return the Android_ID.
(ANDROID_ID is per device (not card) and is generated on device set-up.
It is not 100% reliable and could change on a factory reset or after an OTA update + app re-install).

SIM SN will return "Not available".

Permissions Fix 2 - Android 11+

DEVICE command would crash on devices with Android-11+.

Fix was to add READ_PHONE_NUMBERS to manifest.

Permissions Fix 3 - No-Grant-Run-Time-Error

Run-Time Error is triggered If permissions are not granted by user. A condition that does not merit a program crash.

Fix was to return either an empty string or an invalid bundle number (-1).

Sensors Fix

Ack: dkal,daveacre - forum member.
SENSORS command would crash on devices with Android-10+ if (internal) parameters were less than 3.
The fix code is from aFox from a github issue.
Gr.Modify List Fix

Ack: afox - forum.
Gr.Modify Group "list" would treat the list as a polygon list of coordinates instead of a list of objects.
Fixed and tidied up code.
Gr.Open StatusBar Fix

Ack: dkal - forum.
Status bar would not show in graphics mode, even if flagged with GR.OPEN for Android 11+ devices.
Upgraded and used new API.

The status bar flag has been superceded with Decors Flag since v4.13.
FTP Server

Ack: dkal, @MoStueck
Android-11's scoped storage prevents outside access to source, data ..etc.

So an ftp server (hFTP server)  was embedded.

The new built-in ftp server (hFTP) allows any ftp-client (or plugin) to access the base
    ( /Android/data/com.rfo.hbasic ) for management of your source, data & databases files).
   
    To start the server;
    Editor menu > Start FTP server.

A pop-up will confirm the ip-address of the server together with the command port.

<server-ip>:<port-number>            (enter this info for your ftp-client.)


    To stop the server
    Editor menu > Stop FTP server.

    To change the ftp command port number
    Editor menu > Preferences > FTP server port,
        you can change the ftp command port.

    On your ftp client,
        default cmd port : 1025  2345    (since version 3.04)
        user Name     : hbasic
        password     : hbasic
        mode         : passive or active (passive recommended)
        type         : unix
        auth         : plain text

    You can also control the server programmatically with FTPS commands.

    Where are you scoped ?
    The root of the server starts at ( /Android/data/com.rfo.hbasic ).
This is known as the base_drive  i.e the parent of the base_directory [rfo-basic].

Typically the contents of this base are [rfo-basic] and [files].
You are recommended to put files inside [rfo-basic/source], [rfo-basic/data] or [rfo-basic/databases]
even though you will have write-access to anywhere below the base_drive.

    Server Exiting
    The server will close if;
    a) You tell it to stop via the Editor menu.
    or
    b) hBasic is killed via the android recents list
    or
    c) android removes it in the background.

    If hBasic is 'Exit'd, either via Editor menu or Basic command, the server is still running.
    You will have to re-enter the hBasic app in order to stop it.
    It will be killed if android decides to remove hBasic from memory.

MyPhoneNumber was removed.

Due to the same functionality within DEVICE command.

DEVICE b
Bundle.contain b,"PhoneNumber" , exists
IF exists THEN
    BUNDLE.GET b, "PhoneNumber", p$
    PRINT p$
ELSE
    PRINT "Not Available"
ENDIF

Command Pre-Loading

Legacy Basic remembers commands per line as a form of tokenisation to circumvent future command searches. This happens only after the first execution.

hBasic adds Pre-Loading so that all lines have commands pre-loaded before a Run.
This ensures that all commands start off without needing a search and with equal footing.

Control Stacks Modified

In legacy Basic, there were 6 seperate control stacks for loops, gosubs, if-blocks and functions.
hBasic is the first to combine 5 of them into one block control stack (BCS).
The remaining function stack is still separate but each called function gets to have it's own local BCS.

Where before, you had to make sure inner loops were exited before exiting outer loops, a central BCS can cancel all inner loops/blocks automatically.

The Rules are;

1. You can execute;
                W_R.break, D_U.break, F_N.break,
                W_R.continue, D_U.continue, F_N.continue
                RETURN
..inside of other loop types, GOSUBs and IF blocks.
  Any other different type in-between will be cleaned out.
  The nearest same type inner block will be matched.
   1.a You can execute;
                REPEAT, UNTIL, NEXT,   
..inside of other loop types, GOSUBs and IF blocks.
   Any other different type in-between will be cleaned out.
   All same type inner blocks will also be cleaned out. (loop bottoms can cancel same types).
Loop bottoms must begin a line by itself, they cannot be part of THEN statements.

2. You cannot stagger an ELSE, ELSEIF or ENDIF to cancel a loop or gosub.

e.g you cannot do stuff like this

    IF a THEN
        DO
            ELSE      % Error - Misplaced ELSE
            PRINT "impossible"
        UNTIL ++a > 3
    ENDIF

3. You can safely use FN.RTN in the middle of  a loop, a gosub, or an IF block
    only if they started inside the function.

4. You can only use <x>.BREAK. , <x>.CONTINUE,
                    REPEAT, UNTIL, NEXT,
                or  RETURN
    if their headers also began inside the function

e.g
    FN.DEF myfunc()
        NEXT        % error: NEXT without FOR
    FN.END

    otherwise you will get an error.

5. No support for Multiple Bottoms.

You can only have one loop bottom per loop
e.g  you cannot do this;

    FOR a=1 TO 5
        IF a<3 THEN GOTO SKIP1
        IF a>3 THEN GOTO SKIP2
        b++
    NEXT                % true-bottom (only one)
    END

    SKIP1:
    NEXT                % false-bottom1
                        % Error> NEXT without FOR
    SKIP2:
    IF a<5 THEN NEXT    % false-bottom2 (and illegal)
                        % Error> NEXT without FOR
    ? "found 5"
    END

Instead, to make the above work, you must use <loop>.CONTINUE
which has the same effect;

FOR a=1 TO 5

        IF a<3 THEN GOTO SKIP1
        IF a>3 THEN GOTO SKIP2
        b++
    NEXT                % true-bottom (only one)
    END

    SKIP1:
    F_N.CONTINUE                % no error
    SKIP2:
    IF a<5 THEN F_N.CONTINUE    % no error

    ? "found 5"
    END

FILE -  Absolute Paths

Originally, paths for FILE commands were designed for relative paths. These paths are relative to the data directory at
      <BasePath>/App_Path/data

e.g <BasePath>/rfo-basic/data
e.g /storage/emulated/0/Android/data/com.rfo.hbasic/rfo-basic/data.
This means to get to the root directory you need a path like "../../../../../../../.."
 and this is just to get to the root.

Normally you would not need to navigate too far away from data or even rfo-basic. Also, since Android 11's, scoped storage, you probably wouldn't have access to anywhere before the BasePath.

But for rooted phones or special places where you do have access, e.g to get to internal private storage, or native library areas, using relative paths to these places can be cumbersome.

hBasic now allows Absolute Paths for these FILE operations. To specify an absolute path, just begin it with a slash '/'.

e.g
debug.on
FILE.DIR "/storage/emulated/0/DCIM/Camera" , a$[]
debug.dump.array a$[]
FILE.DIR "/sdcard/Download" , a$[]
debug.dump.array a$[]

Acceleration
Editor GPU Acceleration.

The Editor and the rest of the Standard app now uses GPU acceleration by default. This cannot be turned off.

Graphics Hardware overlay layer.

The setting in the Editor > Preferences labeled "Graphic acceleration"
has always (in legacy Basic) referred to Hardware overlay layer (not GPU acceleration as above).
It further boosts performance for the graphics screen (only).

This preference setting is now ON by default.
It can be turned off either in Preferences or programmatically with the new command
GR.SETACC. It uses less power than direct GPU rendering at the expense of causing lag in some animations. But in most cases it is faster.
Directives @@
Directives are used to configure the intepreter.

These directives should be put the before the start of a program on their own.
They all start with "@@".

@@HIDE_ACTIONBAR
This hides the console Action Bar at startup.

The console actionbar default title is "Program Output".
You may want to avoid it's display when your program starts up.

The actionbar will remain hidden until the next CONSOLE.TITLE.

Warning:
    If you choose not to restore the actionbar after  (with CONSOLE.TITLE),
    then the user will not see the console title and cannot interact with the menu.
    Also, MENU.SHOW will be ignored if there is no actionbar.

    E.g
@@HIDE_ACTIONBAR        % prevent flash of console title

GR.OPEN "white",1,1     % graphics now in front

CONSOLE.TITLE "My Title" % restore actionbar in background

GR.COLOR "black" : GR.TEXT.SIZE 40
GR.TEXT.DRAW o, 50,100,"Tap BackKey to quit"
GR.RENDER

DO : pause 100 : UNTIL 0
OnBackKey :
print "Done"
END            % console now in front with title

If you do not use this directive, the actionbar will  be shown by default with the default title "Program Output".

Deprecated Directives
The following directives are no longer in use;
@@PRELOAD,  @@LOCALSTACKS

Note: Prior to v4.13, the directive prefixes used to be a single"@". The change to "@@" is allow through variables that start with a single "@".




Leading Cloud Surveillance, Recording and Storage service; IP camera live viewing

Leading Enterprise Cloud IT Service; cloud file server, FTP Hosting, Online Storage, Backup and Sharing

Powered by FirstCloudIT.com, a division of DriveHQ, the leading Cloud IT and Cloud Surveillance Service provider since 2003.