Tuesday | 30 APR 2024
[ previous ]
[ next ]

Adding a Mode to ScarletDME

Title:
Date: 2023-11-04
Tags:  scarletdme

Certain BASIC functionality is optional in ScarletDME and these are toggled by using the $MODE statement followed by a mode. Some examples of this are:

CASE.SENSITIVE - Enables case sensitivity
OPTIONAL.FINAL.END - Suppress warning messages if there is no END at the end

I want to add the TIME.MS option which will let the TIME() function get the milliseconds fraction as well as the seconds. This is something that is in a later version of qm but isn't in ScarletDME.

I already have the op_timems function that returns the correct time in seconds and milliseconds.

I have also already added that opcode following the steps in my previous post about adding a function:

Adding a Function to ScarletDME

In this post I'll focus on just the MODE specific stuff.

I need to first add the TIME.MS mode to the BASIC compiler, GPL.BP/BCOMP, and then I also need to check that mode and switch between op_time and op_timems based on the mode.

Adding a Mode

Adding the mode looks to be straightforward. The modes are all defined in the same place and so I can add my mode here as well.

* Values of mode (Bit positions)
...
$define M.NO.CASE.INVERT       20
$define M.OPTIONAL.THEN.ELSE   21; *rdm
$define M.TIME.MS 22
** End New Modes

Now we also need to add it to the list of mode names:

...
mode.names := @VM:"NO.CASE.INVERT"
mode.names := @VM:"OPTIONAL.THEN.ELSE"
mode.names := @VM:"TIME.MS"

This sets up the skeleton work so that we can test for the TIME.MS mode.

Switching on the Mode

Now that we have the mode, we can use the same check used elsewhere to check for the TIME.MS mode.

An example of this can be found around line 3940.

if not(bittest(mode, M.CASE.SENSITIVE)) then define.token = upcase(define.token)

This test is for the case sensitivity but the one I'm doing for TIME.MS will be similar.

This area is where the tokens are looped over and checked to see if the are an intrinsic and if they should trigger an intrinsic function. I'm going to add a check here for M.TIME.MS and then based on that change the intrinsic.

This is very similar to what the M.CASE.SENSITIVE mode is doing.

            if bittest(mode, M.CASE.SENSITIVE) then x.symbol.name = upcase(symbol.name)
            else x.symbol.name = symbol.name
            
            if bittest(mode, M.TIME.MS) then 
                locate 'TIME' in intrinsics<1> by 'AL' setting i then
                    intrinsic.opcodes<i> = OP.TIMEMS
                end
            end else
                locate 'TIME' in intrinsics<1> by 'AL' setting i then
                    intrinsic.opcodes<i> = OP.TIME
                end
            end

Now I haven't figured out why trying to compile the qmsys in the ScarletDME working folder doesn't update the gcat properly. Luckily I do have a work around.

I copy the modified BCOMP to my system's qmsys and then compile it with the internal account.

cp qmysys/GPL.BP/BCOMP /usr/qmsys/GPL.BP

Now we can compile BCOMP making sure to use the qm Internal account.

cd /usr/qmsys/
bin/qm -Internal 
> BASIC GPL.BP BCOMP

Once compiled, I then copy the gcat/$BCOMP to my working directory so that I can push it to github.

cp /usr/qmsys/gcat/\$BCOMP ~/bp/ScarletDME/qmsys/gcat

Testing the MODE

We can make sure that everything works by trying some test routines:

PRINT TIME()
END

This will print just the seconds after midnight.

$MODE TIME.MS
PRINT TIME()
END

We should now see the seconds with a millisecond fractional part!

We can also update the $BASIC.OPTIONS with our new mode:

X
MODE TIME.MS

This way any programs compiled in the future will use the millisecond version instead of the regular TIME function.

With that we have a working version of ScarletDME with a brand new mode!