Let's go back to $$external programming for a last common side; the parameters passing.
Despite the fact that most interesting feature with externals is the share of BASIC compiled libraries, the most attractive use is the ability to turn easily a compiled BASIC PROCedure/FUNCtion to System Keyword, resident and re-rentrant code.
This naturally implies passing back and forth some parameters between the caller and the external. QLIB does support this parameters passing, at least with v3.36 and the right RUNTIMEs, but in a very limited, undocumented enough and obscur (badly) way - sorry.
As stated in the manual, "When called from interpreter ... only scalar parameters can be passed...". This is not really a big lack but if you try to pass an array as parameter, you will be notified by a "Not Implemented" error message while your system is probably already crashed. Remember, any QLIB run time error occuring within an external job drives directly to crash.
Also stated in the manual,"If the value of a parameter is altered by external ...then the corresponding variable in the caller program is also altered...". In fact, all passed parameters to external are copied back when job ends, altered or not. If an UNSET variable is passed to an external, on return, variable is SET anyway but this is not really dramatic either.
More impotant, Passing parameters to/from external is entirely carried out by QLIB RUNTIMEs, Embeded or Not. Thus, the RUNTIME used (version, mods, patches, ...) can have a certain influence on the way this is done when they are invoked on a target system. Better to have integrated a working RUNTIME in the external binary file, but it costs about 11 Ko whereas the external tokenized program can have a size of 2-3 Ko (large external extensions are not recommended).
I have no idea how RUNTIMEs handle parameters passing and I haven't managed to obtain any real help or valid information about this. I didn't want to hack RUNTIMEs either but I learned some useful rules which allowed me to realize some improvements in my work on this area.
Main things I have learned :
- - paramaters are passed by value and not by reference
- paramaters are not "casted" and still typed like originals from the caller, regardless of arguments type in the external job (pretty QDOS compliance)
- Missed parameters are not created or passed neither by the caller nor by the RUNTIMEs or external job (missed parameters are created in S*BASIC)
- Strangely enough, no entries are created in the Name Table of the external job for the passed parameters or their copies despite the fact External job uses its own independent table.
- - Any string parameter has to be passed as quoted expression or a set string variable ended by $. This is boring for name parameters passing (e.g. filename)
- All usual assembler staff used to manipulate parameters can't work (finding name of parameter per example) as no entries exist in the Name Table for these parameters.
- The actual number of passed parameters (which becomes here variable) can't be known at external job starting.
- All parameters flags are lost for good (e.g. # or separators , ; \ ! TO)
- Furthermore, the TK2 PARNAM$() and PARSTR$() FUNCtions are not working here and they crash system if used. This is because TK2 lies on entries in the Return Table of S*BASIC which are not created by QDOS when calling machine-code or assimilated $$external code.
The main precaution to avoid this is always "test existence and type of any parameter" before using in any affectation or expression in the external. TK2 PARUSE() and PARTYP() are very helpfully for this and still working within compiled externals. Also keep in mind that UNSET variables are now handled differently on QDOS and SMSQ/E systems and that QLIB compiled programs are more QDOS method compliant (BANG).
If you have reached this point of this long article (which may be not in the Forum spirit), you may find useful the attached archive containing my now released special toolkit PARAMEX I have written in assembler to deal with parameters passing for externals. It allows to fectch easily name or string parameters, to know how many actual parameters was passed and to find any hash or separator flags used along parameters. This is a "MINIMUM TO HAVE" for external programming.
The method I used is to access the Name Table of the caller, where all passed parameters are awaiting until the (happy) end of the executed external job. It was a very tenious but exciting task.
Also included some readme text and a BASIC external program you can use it as example to test what happens on your system about parameters passing.
As promised. Alain