Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 15:38 19 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : MMB4L: MMBasic for Linux alpha release

     Page 10 of 11    
Author Message
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3008
Posted: 11:42pm 23 Jan 2022
Copy link to clipboard 
Print this post

Here's an update on the benchmark for the Pi-Z2W relative to the Picromite port on the Pi-ZW based on the benchmarks from here.
                 BM1     BM2    BM3      BM4     BM5     BM6     BM7     BM8
ABC 800 single   0.9     1.8    6.0      5.9     6.3    11.6    19.6     2.9
ABC 800 double   1.2     2.2    10.0    10.6    11.0    17.8    26.4    14.4
IBM PC           1.5     5.2    12.1    12.6    13.6    23.5    37.4     3.5
Apple III        1.7     7.2    13.5    14.5    16.0    27.0    42.5     7.5
VIC-20           1.4     8.3    15.5    17.1    18.3    27.2    42.7     9.9
ZX81 "fastmode"  4.5     6.9    16.4    15.8    18.6    49.7    68.5    22.9

Maximite         0.016   0.144   0.196   0.205   0.354   0.512   0.721   0.310

Maximite w. #s   0.016   0.131   0.193   0.194   0.245   0.393   0.582   0.241
Maximite w/o #s  0.016   0.111   0.173   0.173   0.192   0.336   0.525   0.220
MicroMite 40MHz  0.028   0.18    0.285   0.289   0.644   0.892   1.346   0.376
MicroMite 48MHz  0.023   0.15    0.237   0.24    0.536   0.744   1.121   0.313
MM+ MX470 120MHz 0.013   0.082   0.135   0.135   0.265   0.380   0.597   0.174
MMX 198MHz       0.006   0.045   0.07    0.08    0.141   0.201   0.287   0.126
Picromite ZeroW  0.014   0.058   0.093   0.102   0.184   0.298   0.354   0.127
>MMB4L Pi-Z2W    0.011   0.030   0.034   0.035   0.066   0.114   0.163   0.064<***
Armmite H7       0.003   0.023   0.038   0.042   0.067   0.098   0.146   0.065
ArmMite F4       0.011   0.079   0.14    0.149   0.249   0.354   0.528   0.257
MMBASIC DOS      0.0002  0.0017  0.0028  0.0028  0.0073  0.0092  0.0117  0.0058
CMM2             0.00176 0.0118  0.01903 0.01728 0.03611 0.05062 0.07582 0.02851
PicoMite         0.0166  0.1096  0.1800  0.1840  0.3325  0.4715  0.6688  0.3193
MMB4L i7 2.9GHz  0.0000  0.0020  0.0040  0.0040  0.0080  0.0130  0.0190  0.0070

So an appreciable speed boost from the Pi-ZW
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8566
Posted: 10:03am 24 Jan 2022
Copy link to clipboard 
Print this post

Figures for MMBasic for Windows on I7-8086 (look it up   )
I had to increase the loop counts to get readings

                BM1     BM2    BM3      BM4     BM5     BM6     BM7     BM8
ABC 800 single   0.9     1.8    6.0      5.9     6.3    11.6    19.6     2.9
ABC 800 double   1.2     2.2    10.0    10.6    11.0    17.8    26.4    14.4
IBM PC           1.5     5.2    12.1    12.6    13.6    23.5    37.4     3.5
Apple III        1.7     7.2    13.5    14.5    16.0    27.0    42.5     7.5
VIC-20           1.4     8.3    15.5    17.1    18.3    27.2    42.7     9.9
ZX81 "fastmode"  4.5     6.9    16.4    15.8    18.6    49.7    68.5    22.9

Maximite         0.016   0.144   0.196   0.205   0.354   0.512   0.721   0.310

Maximite w. #s   0.016   0.131   0.193   0.194   0.245   0.393   0.582   0.241
Maximite w/o #s  0.016   0.111   0.173   0.173   0.192   0.336   0.525   0.220
MicroMite 40MHz  0.028   0.18    0.285   0.289   0.644   0.892   1.346   0.376
MicroMite 48MHz  0.023   0.15    0.237   0.24    0.536   0.744   1.121   0.313
MM+ MX470 120MHz 0.013   0.082   0.135   0.135   0.265   0.380   0.597   0.174
MMX 198MHz       0.006   0.045   0.07    0.08    0.141   0.201   0.287   0.126
Picromite ZeroW  0.014   0.058   0.093   0.102   0.184   0.298   0.354   0.127
>MMB4L Pi-Z2W    0.011   0.030   0.034   0.035   0.066   0.114   0.163   0.064<***
Armmite H7       0.003   0.023   0.038   0.042   0.067   0.098   0.146   0.065
ArmMite F4       0.011   0.079   0.14    0.149   0.249   0.354   0.528   0.257
MMBASIC DOS      0.0002  0.0017  0.0028  0.0028  0.0073  0.0092  0.0117  0.0058
CMM2             0.00176 0.0118  0.01903 0.01728 0.03611 0.05062 0.07582 0.02851
PicoMite         0.0166  0.1096  0.1800  0.1840  0.3325  0.4715  0.6688  0.3193
MMB4L i7 2.9GHz  0.0000  0.0020  0.0040  0.0040  0.0080  0.0130  0.0190  0.0070
MMB4W I7 4-5GHz  0.00005 0.00032 0.00069 0.00046 0.00062 0.00096 0.0016  0.0008

Edited 2022-01-24 20:17 by matherp
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 10:26am 24 Jan 2022
Copy link to clipboard 
Print this post

It really does matter what problems you are benchmarking. SAAINT running on Pi3/MMB4L is noticeably slower than it is running on the CMM2. I put this down to the fact that SAAINT has lots of long identifiers and a high-level of decomposition into small subs and functions. The CMM2 can out-perform on this due to the variable lookup and sub/fun call optimisations that Peter implemented and which I haven't yet ported over to MMB4L.

Best wishes,

Tom
Edited 2022-01-24 20:46 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
mobluse

Newbie

Joined: 10/02/2013
Location: Sweden
Posts: 24
Posted: 10:51pm 27 Jan 2022
Copy link to clipboard 
Print this post

DIV is considered to be a keyword of MMBasic according to the syntax coloring that comes with MMB4L, but AFAIK DIV is not a keyword in any BASIC.

RANDOMIZE, RND, and LET are keywords of MMBasic, but not according to the syntax coloring that comes with MMB4L.

The operators e.g. (), ^, *, /, +, -, = , <>, >=, <= have the same color as identifiers now. This also goes for the separator :.

.001 is not syntax colored as a number, but .5 is.
Edited 2022-01-28 09:33 by mobluse
Maker Pi Pico Rev1.2, DuinoMite-Mini, Raspberry Pi 0-4, iCE40HX8K, Arduino Uno, VM111, STK500, ZX81
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 5705
Posted: 07:31am 28 Jan 2022
Copy link to clipboard 
Print this post

DIV is common in Pascal / Delphi languages. It returns the quotient of a division. I'm not sure if it's in any of the MMBasic versions. I thought I'd seen it somewhere else - yeah. C++.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 12:17pm 28 Jan 2022
Copy link to clipboard 
Print this post

Hi @mobluse,

Thanks for reporting these issues.

  mobluse said  DIV is considered to be a keyword of MMBasic according to the syntax coloring that comes with MMB4L, but AFAIK DIV is not a keyword in any BASIC. RANDOMIZE, RND, and LET are keywords of MMBasic, but not according to the syntax coloring that comes with MMB4L.

The nano syntax file for MMB4L "mmbasic.syntax.nanorc" is a hack-job that started from a syntax for X11-Basic which I guess must have a DIV keyword. It could do with a proper clean-up and if anyone want to send me an updated copy I would be very grateful and give due credit, otherwise I'll be updating it piecemeal as and when.


  mobluse said  The operators e.g. (), ^, *, /, +, -, = , <>, >=, <= have the same color as identifiers now. This also goes for the separator :.

Behaviour is as intended. The CMM2 editor which this syntax file seeks to mimic also uses white for these identifiers. Users who want otherwise can edit the mmbasic.syntax.nanorc file as they wish.


  mobluse said  .001 is not syntax colored as a number, but .5 is.

I guess that's something for me, or someone else, to look at fixing in the syntax file.

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 12:38pm 28 Jan 2022
Copy link to clipboard 
Print this post

It's probably only fair to say I'm taking a break from MMB4L at the moment:

   - Issues with the day-job.
   - Need to clean my palette with some more recreational programming.
   - Waiting to see where Peter's "MMB4W" is going and whether anyone is going to
     decide they want to port than to Linux.
   - Just a tinge of disappointment/jealousy at the performance difference vs. MMB4W
       - Peter is MMB4W an immediate decendant of MMBasic for DOS or does it already
         include optimisations from your other MMBasics ?
       - Just to eliminate an obvious oops moment, has anyone actually tried timing
         MMB4W with a physical stop-watch? I know that initially the timing code in
         MMB4L wasn't counting correctly.
       - Even after I pull in Peter's enhancements (from the PicoMite source) I
         suspect MMB4L would be a little slower as Peter and I (and Geoff) prioritise
         different things in our source-code.
       - I'm not aware of any significant performance differences between MSVC and GCC
         compiled code (I can also try Clang/LLVM) but use of different optimisation
         flags is very possible.

OK, just wrote 11 lines on performance differences, that suggests it might be more than a "tinge"   .

I haven't given up though, just taking a break.

Best wishes,

Tom
Edited 2022-01-28 22:39 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2285
Posted: 01:13pm 28 Jan 2022
Copy link to clipboard 
Print this post

hi tom,
   fair enough to take a breather, but you shouldn't be discouraged by the 'performance' numbers that have been floating round. in the real world these are (a) often wrong, and (b) usually irrelevant.

your MMB4L is shaping up to be a compact and versatile package that has low overhead and meshes well with the 'linux ethos'. when you get back into it, just focus on simplicity and don't get sidetracked by the lure of creating a 'swiss army knife' that can do absolutely everything. file I/O, 3-wire serial I/O, linux text console access - these are the things that matter, and can be readily achieved.


cheers,
rob   :-)
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8566
Posted: 01:17pm 28 Jan 2022
Copy link to clipboard 
Print this post

  Quote  Peter is MMB4W an immediate decendant of MMBasic for DOS or does it already
        include optimisations from your other MMBasics ?
      - Just to eliminate an obvious oops moment, has anyone actually tried timing


No: It is a port of the "open source" PicoMite and therefore has most of the CMM2 enhancements but with much bigger hash tables and therefore much less collision risk
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 01:26pm 28 Jan 2022
Copy link to clipboard 
Print this post

  matherp said  No: It is a port of the "open source" PicoMite and therefore has most of the CMM2 enhancements but with much bigger hash tables and therefore much less collision risk


Thanks for taking the time to reply Peter, that clears up that issue.

Given some of the examples people are running (very few variables) I doubt that hash-table variable lookup (MMB4W) vs. linear lookup (MMB4L currently) is the source in performance difference.

Anyway when I get the time I need to see if a "diff" between the core files that makeup MMBasic for DOS vs. the PicoMite is clean enough to tell me anything.

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 02:14pm 28 Jan 2022
Copy link to clipboard 
Print this post

  robert.rozee said  hi tom,
   fair enough to take a breather, but you shouldn't be discouraged by the 'performance' numbers that have been floating round. in the real world these are (a) often wrong, and (b) usually irrelevant.

your MMB4L is shaping up to be a compact and versatile package that has low overhead and meshes well with the 'linux ethos'. when you get back into it, just focus on simplicity and don't get sidetracked by the lure of creating a 'swiss army knife' that can do absolutely everything. file I/O, 3-wire serial I/O, linux text console access - these are the things that matter, and can be readily achieved.


Thanks for the words of encouragement Rob. MMB4L alpha 3 actually already does all the things you've mentioned, though the 3-wire serial is much slower than it should be and the automatic flow-control (RTSCTS and XONXOFF) is buggered.

Personally I am already using MMB4L "in anger" for developing my CMM2 console applications (specifically SAAINT at the moment) and also as part of my tool-chain for PicoMite development; which I do on Linux and then transpile and transfer the files via my MMB4L powered serial comms program (which is why if nothing else the performance will be getting fixed.)

Anyway more to come I'm sure, I was just warning interested parties not to hold their breath.

Best wishes,

Tom
Edited 2022-01-29 00:43 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 5705
Posted: 02:33pm 28 Jan 2022
Copy link to clipboard 
Print this post

The fact that you can already use it for "proper" stuff is *very* encouraging for those of us keeping an eye on this project. :)  I can't blame you in the least, having a rest. It's been pretty intensive.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
Tinine
Guru

Joined: 30/03/2016
Location: United Kingdom
Posts: 1646
Posted: 04:47pm 28 Jan 2022
Copy link to clipboard 
Print this post

  Mixtel90 said  The fact that you can already use it for "proper" stuff is *very* encouraging for those of us keeping an eye on this project. :)  I can't blame you in the least, having a rest. It's been pretty intensive.


Ditto!

 
 
mobluse

Newbie

Joined: 10/02/2013
Location: Sweden
Posts: 24
Posted: 01:24am 29 Jan 2022
Copy link to clipboard 
Print this post

  Quote  DIV is common in Pascal / Delphi languages. It returns the quotient of a division. I'm not sure if it's in any of the MMBasic versions.

DIV is integer division in Pascal, but GW-BASIC and MMBasic have \ to do the same job.
> ?17\6
2
> ?17/6
2.833333333

Maker Pi Pico Rev1.2, DuinoMite-Mini, Raspberry Pi 0-4, iCE40HX8K, Arduino Uno, VM111, STK500, ZX81
 
toml_12953
Guru

Joined: 13/02/2015
Location: United States
Posts: 324
Posted: 03:38am 29 Jan 2022
Copy link to clipboard 
Print this post

  matherp said  Figures for MMBasic for Windows on I7-8086 (look it up   )
I had to increase the loop counts to get readings



Did you normalize the results? I know you probably did but there are many who don't know enough about benchmarks to do it.

Example: If you increased the loops from 100 to 10000, you have to divide the result by 100 to get a comparable reading.
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 12:59pm 14 May 2022
Copy link to clipboard 
Print this post

Just a post for Peter's amusement, assuming he even reads my ramblings.

As a result of updating the installation and tooling, the Android/Termux version now segfaults during global initialisation, before even hitting main() ... bloody Linux ... given the nature of the problem I suspect it's a compiler issue so hopefully I can wait it out; it doesn't hurt the main Linux builds.

What makes me especially sore is that I did the upgrade because the Android/Termux version was showing a bug with the interaction between errors and interrupts that I haven't seen on any other platform. Specifically it was failing to keep a reliable count of the subroutine nesting level (LocalIndex in the source code) and I assumed there was a possibility this was a compiler issue. Immediately following the upgrade and discovering that it now didn't work at all I had the revelation that it *might* be a programming *error* in all (?) MMBasic versions.

In MMBasic.c#ExecuteProgram() I *think* the automatic variable SaveLocalIndex should be declared volatile otherwise the specification of setjmp/longjmp allows for the possibility that it may not necessarily have the expected value after the longjmp occurs and the LocalIndex is restored.

... or I may be wrong, I may never know unless the Termux maintainers find and fix the new issue.

Best wishes,

Tom
Edited 2022-05-15 00:04 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3830
Posted: 02:06pm 15 May 2022
Copy link to clipboard 
Print this post

Alpha 4 now released, and open-source, many thanks to Geoff and Peter for allowing this to be possible.

   README
   ChangeLog
   https://github.com/thwill1000/mmb4l

In all honesty there is nothing terribly spectacular here, just some steady progress on the things that I need MMB4L for. Serial comms hasn't been fixed since alpha 3 and there are still no hi-res graphics, sound or GPIO support.

ChangeLog
---------

Version 2022.01.00-a4 - 15-May-2022:
 - MMB4L is now open-source and licensed under a modified 4-clause BSD license
   similar to that for the PicoMite firmware and MMBasic for Windows. See the
   LICENSE file for details. My thanks to Geoff and Peter for allowing this to
   be possible.
 - Added * command which may be used at the MMBasic prompt (but not within
   programs) as a short-cut for RUN, e.g.
       *foo --bar "wombat"
   is translated to:
       RUN "foo", --bar "wombat"
 - Added ! command which may be used at the MMBasic prompt (but not within
   programs) as a short-cut for SYSTEM, e.g.
       !cp foo bar
   is translated to:
       SYSTEM "cp foo bar"
   An exception is !cd, e.g.
       !cd foo
   is translated to:
       CHDIR "foo"
   This is required because "!cd foo" would otherwise appear to do nothing
   due to the fact that each SYSTEM command is run in the context of a separate
   forked process instead of the MMBasic process itself.
 - Added SYSTEM sub-commands for getting and setting environment variables:
       - SYSTEM GETENV name$, {value$ | value%()}
       - SYSTEM SETENV name$, {value$ | value%()}
   This supplements MM.INFO$(ENVVAR) because it allows values > 255 characters
   to be read into a LONGSTRING.
 - Added option persistence to file "~/.mmbasic/mmbasic.options" for all
   options except for BASE, BREAK, CASE, CODEPAGE, CONSOLE, EXPLICIT and
   RESOLUTION:
       - only options that differ from their defaults are saved to this file.
 - Added OPTION EDITOR <editor> command to change the EDITor.
   Supported editors are:
       - ATOM
       - CODE     (synonym for VSCODE)
       - DEFAULT  (synonym for NANO)
       - GEANY
       - GEDIT
       - LEAFPAD
       - NANO
       - SUBLIME
       - VI
       - VIM
       - VSCODE
       - XED      (untested because I couldn't get a version for Ubuntu 21.)
       - or a string specifying a command to run, e.g.
             OPTION EDITOR "code -g ${file}:${line}"
         ${file} is a placeholder for the filename to edit, the filename will
                 be automatically enclosed in double-quoted.
         ${line} is a placeholder for the line number to set the initial
                 cursor position to.
 - Added OPTION F<num> string$ sub-commands to define the strings that will be
   generated when pressing function keys at the MMBasic prompt:
       - all of F1-F12 can be redefined.
       - beware that some function keys may be captured by the Linux window
         manager and never passed on to MMBasic.
 - Added OPTION SEARCH PATH <path> command as per CMM2.
 - Added OPTION {SAVE | LOAD} <filename> command.
 - Added OPTION RESET {ALL | <option>} command.
 - Added PEEK(DATAPTR) function and POKE DATAPTR ptr% command for getting
   and setting the DATA read pointer.
 - Added READ {RESTORE | SAVE} command for pushing and popping the DATA read
   position to an internal stack as per PicoMite.
 - Changed OPTION LIST to only show options that differ from their defaults:
        - use OPTION LIST ALL to list all options.
 - Changed OPTION <option> <value> command so that for all options with
   enumerated values the value can now be supplied as a string, e.g.
       OPTION CODEPAGE NONE
       OPTION CODEPAGE "NONE"
       DIM none$ = "NONE"
       OPTION CODEPAGE none$
 - Changed RESTORE command to support targets given as string or number
   expressions as per PicoMite.
 - Changed SYSTEM execute command to have a 3rd optional argument to capture
   the exit status of the executed command, e.g.
       SYSTEM command$, out$, exit_status%
       SYSTEM command$, out%(), exit_status%
   The SYSTEM command now only reports an error if it cannot run the command.
   If the command runs but fails then no error is reported and the failure
   code will be captured in exit_status%.
 - Changed LOAD and RUN so that if a filename without a [.bas|.BAS|.Bas]
   extension is not provided then one is automatically appended:
       - unlike other MMBasic ports MMB4L has a case-sensitive file-system
         and expects MMBasic program files to have one of three possible
         extensions: .bas, .BAS, or .Bas.
       - if no extension is specified then MMB4L will check for the existence
         of a file with each extension in order, if no such file is found then
         it assumes the .bas extension.
 - Changed assignment of values to MM.ERRNO, they are now:
          0      : no error.
      1  ..  255 : standard error numbers (errno) for C library calls.
         256     : unclassified MMBasic error.
     257 .. 1023 : other standard MMBasic errors.
        1024+    : recommended range for program specific errors.
 - Removed LS as an alias of FILES:
       - LS was a non-standard aberration inherited from the CMM2.
       - use !ls instead to execute the 'ls' Linux shell command with all its
         power and with no requirement to enquote its arguments.
 - Fixed bug where could not open non-existing file for RANDOM access.
 - Fixed bug where when one program tried to RUN another it would resolve the
   path to the new program from the current program directory instead of from
   the current working directory.
 - Fixed bugs in interaction between ON ERROR and interrupts.
   The normal thread of execution and the execution of interrupts now maintain
   independent error states.
 - Fixed bug where MM.ERRMSG$ would contain trailing newline character after
   an uncaught ERROR.
 - Fixed more issues with "mmbasic.syntax.nanorc".


Best wishes,

Tom
Edited 2022-05-16 00:14 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
toml_12953
Guru

Joined: 13/02/2015
Location: United States
Posts: 324
Posted: 07:33pm 15 May 2022
Copy link to clipboard 
Print this post

  matherp said  Figures for MMBasic for Windows on I7-8086 (look it up   )
I had to increase the loop counts to get readings



Did you normalize the figures after running to make a valid comparison? In other words, if you had to increase the loop count by 1000 times (e.g. increase it from the original 100 to 100000), you have to divide the results by 1000 in order to compare your results to the others.
 
Rickard5

Guru

Joined: 31/03/2022
Location: United States
Posts: 328
Posted: 11:28pm 15 May 2022
Copy link to clipboard 
Print this post

Mr Thwill
WILL There ever be a port of MMBasic for the Specky or the BBC Micro? Please!
I turned the volume on the monitor to max and could hear sound. Thanks Stanleyella
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2285
Posted: 02:43am 16 May 2022
Copy link to clipboard 
Print this post

  Rickard5 said  Mr Thwill
WILL There ever be a port of MMBasic for the Specky or the BBC Micro? Please!


the minimum requirements for MMbasic are a PIC32 MX170 processor. this has:
*       256k flash (ROM)
*       64k RAM
*       40MHz processor
these far exceed the hardware of the ZX spectrum or BBC micro. the best you could do would be to attach an MX170 chip (in a 28-pin DIP package) to one of the above computers and just use the ZX/BBC as a terminal.


cheers,
rob   :-)
 
     Page 10 of 11    
Print this page
© JAQ Software 2024