I am thinking to inlcude some simple BASIC drivers into a C application of me
to allow users to adapt drivers for their own purposes easily (in the moment
I using a selfwritten very simple and limited language for this). So it would
be nice if the C applciation can load the drivers permanently and simply call
a central BASIC function of the driver. The BASIC program could access the
C-apllication and its central functions by an according library.
One possibility would be making usage of system() function. However this
would swap out the main applicatiion every time and a call from the BASIC
drivers to the main application is not easy.
The second possibilty would be to start the BASIC drivers as a child task and
communicate by messages (data exchange is small). This looks the best
solution to me.
However I still want to know whether it is possible in general to load the
BASIC driver by the C application permanently and simply to call it directly
(without swapping etc.). Has anyone any hints into this direction for me?
Many thanks in advance
Thomas Milius
> I am thinking to inlcude some simple BASIC drivers into a C application of
> me to allow users to adapt drivers for their own purposes easily (in the
> moment I using a selfwritten very simple and limited language for this). So
> it would be nice if the C applciation can load the drivers permanently and
> simply call a central BASIC function of the driver. The BASIC program could
> access the C-apllication and its central functions by an according library.
This isn't really an answer to your question, but I would highly recommend
Lua as an embedded scripting language for these sorts of situations. It's
very powerful and easy to extend.
And well-supported on RISC OS.
See http://www.wra1th.plus.com/lua/ and http://www.lua.org/
--
Matthew Phillips
Dundee
> > I am thinking to inlcude some simple BASIC drivers into a C
> > application of me to allow users to adapt drivers for their own
> > purposes easily (in the moment I using a selfwritten very simple
> > and limited language for this). So it would be nice if the C
> > applciation can load the drivers permanently and simply call a
> > central BASIC function of the driver. The BASIC program could
> > access the C-apllication and its central functions by an according
> > library.
>
> This isn't really an answer to your question, but I would highly
> recommend Lua as an embedded scripting language for these sorts of
> situations. It's very powerful and easy to extend.
>
> And well-supported on RISC OS.
+1.
B.
No. It would be better to launch the BASIC code as a Wimp task and
communicate with it via messages.
> I am thinking to inlcude some simple BASIC drivers into a C application of me
> to allow users to adapt drivers for their own purposes easily (in the moment
> I using a selfwritten very simple and limited language for this). So it would
> be nice if the C applciation can load the drivers permanently and simply call
> a central BASIC function of the driver. The BASIC program could access the
> C-apllication and its central functions by an according library.
Sounds nasty. Matthew's idea of a scrip language with proper C
interoperability would be a lot better.
> The second possibilty would be to start the BASIC drivers as a child task and
> communicate by messages (data exchange is small). This looks the best
> solution to me.
As my first thought above.
> However I still want to know whether it is possible in general to load the
> BASIC driver by the C application permanently and simply to call it directly
> (without swapping etc.). Has anyone any hints into this direction for me?
I'm sure it could be done with much nastiness, but there is really no
need for that these days.
---druck
> Is there a way to call BBC-BASIC programs from C inside an
> application and to return afterwards without loading the BASIC
> programs everytime?
>
Have you tried the shareware program '!BBC_C32' - it might do what you
want.
(BBC BASIC to Acorn ANSI C Translator)
http://www.martinkd.freeuk.com/
--
Colin Ferris Cornwall UK
The obvious answer, it seems to me, is to have this BASIC 'driver' loaded as
a library by a BASIC application which runs in the background. The C
application sends Wimp messages to it and it sends them back as required.
You could design the API of the driver library to be nice and simple so that
users can modify it as required, but the application code and communications
to the C program would be handled by your own BASIC program that the user
doesn't touch.
Steve
--
Steve Revill @ Home
Note: All opinions expressed herein are my own.
> In message <12f660a1...@thomas-milius.t-online.de>
> on 27 Sep 2009 Thomas Milius wrote:
>
> > I am thinking to include some simple BASIC drivers into a C application of
> > me to allow users to adapt drivers for their own purposes easily (in the
> > moment I using a selfwritten very simple and limited language for this). So
> > it would be nice if the C application can load the drivers permanently and
> > simply call a central BASIC function of the driver. The BASIC program could
> > access the C-application and its central functions by an according library.
>
> This isn't really an answer to your question, but I would highly recommend
> Lua as an embedded scripting language for these sorts of situations. It's
> very powerful and easy to extend.
>
> And well-supported on RISC OS.
>
> See http://www.wra1th.plus.com/lua/ and http://www.lua.org/
Perhaps it is worth spelling out why so many replies have been dubious
about having applications written in C trying to use Basic programs.
It is possible for a C program to call other programs, using the system()
call. The manual for Acorn ANSI C Release 4 has a section about this on
page 374, which explains the difficulties:
"Applications in RISC OS are loaded at a fixed address specified by
the application image. Normally this is the base of application
workspace, 0x8000. While executing, applications are free to use store
between the base and end of application workspace. .... "
To cut a long story short, system() must first copy the calling C
program to the top end of application workspace before calling BASIC,
and then when the BASIC program stops, has to copy it back again. I think
the word "nastiness" was justifiably used in one response. The point is
that BASIC and the C runtime system are both "applications", and hence
competitors for application workspace. Now Lua is a library of C functions,
and it was written primarily for incorporating into applications written
in C. When so incorporated, the application can call functions written in
Lua which are stored in external files. The mechanism for this does not
involve the contortions that system() has to resort to. In other words,
Lua and C are not in competition, whereas Basic and C are.
I hope this explanation, though oversimplified, is useful.
--
Gavin Wraith (ga...@wra1th.plus.com)
Home page: http://www.wra1th.plus.com/
It's possible to call *Basic with some hex parameters telling it the
location of the program it's going to run. However I think the idea that
the BASIC workspace lives between &8000 and &8F00 is fixed; while you can
set PAGE to be somewhere else and run your program from there, it'll still
trample over &8000-&8F00. I'm not clear if the hex parameters even mean
that the program is run from there, or if it's copied down to &8F00.
Don't forget that BASIC is a module so there's no actual reason why it must
use application workspace for a program, but since it does I don't think
there's any easy way around this.
Theo
> The obvious answer, it seems to me, is to have this BASIC 'driver'
> loaded as a library by a BASIC application which runs in the
> background. The C application sends Wimp messages to it and it sends
> them back as required.
An approach somewhat similar to this is employed by LIRC, the IRC
client. "Scripts" a pre-processed by LIRC to replace certain
IRC-specific keywords with function calls, and the whole lot is linked
with a BASIC library with a stub, which turns the script into a WIMP
process that LIRC communicates with via WIMP messages.
It's a bit ugly, but it does work. The other problem is latency; you
have to call Wimp_Poll to get your answer back.
A language in a library, such as Lua, seems like a much more elegant
approach, and may even end up being higher performance.
B.
I agree with all but the ugly assertion - I think doing it as a separate
Wimp task is _exactly_ how this sort of thing should be done under RISC OS
if you want to have BASIC and C programs interact with each other.
Looking into a more native C solution, as Rob suggests, would seem to be a
more efficient way to do this. However, I can see why you'd want to use
BASIC as the language for your 'drivers' given that most RISC OS users who
are capable of programming would be capable of modifying the BASIC program -
as opposed to having to learn Lua (irrespective of how easy that may be).
Ta,
> Looking into a more native C solution, as Rob suggests, would seem to be a
> more efficient way to do this. However, I can see why you'd want to use
> BASIC as the language for your 'drivers' given that most RISC OS users who
> are capable of programming would be capable of modifying the BASIC program -
> as opposed to having to learn Lua (irrespective of how easy that may be).
>
Actually, as a result of my survey, I think I can state that "most RISC
OS users are capable of modifying BASIC". Again, I'll have a full
analysis at completion.
> I agree with all but the ugly assertion - I think doing it as a
> separate Wimp task is _exactly_ how this sort of thing should be done
> under RISC OS if you want to have BASIC and C programs interact with
> each other.
Ugly in the preprocessing and fudging sense. Multi-process
arrangements using IPC are almost universally pleasing, indeed.
B.
Ah, I see. :)
> > I am thinking to inlcude some simple BASIC drivers into a C
> > application of me to allow users to adapt drivers for their own
> > purposes easily (in the moment I using a selfwritten very simple and
> > limited language for this). So it would be nice if the C applciation
> > can load the drivers permanently and simply call a central BASIC
> > function of the driver. The BASIC program could access the
> > C-apllication and its central functions by an according library.
> This isn't really an answer to your question, but I would highly
> recommend Lua as an embedded scripting language for these sorts of
> situations. It's very powerful and easy to extend.
I have the same problem as Thomas since I am also developing a program
which will get scripting support. Scripting is also done in lua because it
was the easiest way. The disadvantage is that most people know how to
program BASIC but not lua.
So today(!) I worked on a script which is written in lua and translates
BBC BASIC into lua code automatically. This now works quite well for a
subset of commands (IF,WHILE,CASE OF,REPEAT), more commands to be added
soon.
Get in touch with me, Thomas, if you would like to go this route.
Michael
--
Please replace nospam by m.gerbracht when replying by e-mail
> So today(!) I worked on a script which is written in lua and
> translates BBC BASIC into lua code automatically. This now works
> quite well for a subset of commands (IF,WHILE,CASE OF,REPEAT), more
> commands to be added soon.
This sounds like an interesting project (ie, something that lets you
execute BASIC-style syntax from inside Lua.)
Fancy sharing?
B.
Many many thanks for all the answers and hints (to Lua). My idea is indeed
based on the fact that most RISC OS Users can modify/program BBC BASIC. I
read the manuals before and it seems that I understood it right that system()
copies the application elsewhere.
I my case the driver activity has to take place very seldom (I want to update
my printer tool PrtInfo), so a child task would be the right choice (even
because of stability in case that a driver has a program bug). However I
thought it would be a good point of time to look for general possible
concepts. A similar but from speed different task eg. is to extend a DTP- or
a spreadsheet program by an own language like VBA for EXCEL or Word. However
I dislike the concept in which VBA is implemented for it is so much
taskswapping. For such tasks Lua seems to be a good approach and perhaps
with Michaels Lua BASIC Emulator a good concept for classical RISC OS Users.
I assume that this with BASIC could work too for the manual allows some
modifications like PAGE. However it would be a bad assembler/BASIC hack until
this feature isn't included into BASIC at start as option directly (Something
like: Load a BASIC/Assembler dummy and start. Note the addresses and
initialization elsewhere. Return to application and load BASIC programs with
setting up the program and workspace (with data noted before) and at the end
emulate a CALL-Return with some modified stack parameters to call the load
routines and at the end of BASIC call an assembler part again to clean up).
But as Druck said:
> I'm sure it could be done with much nastiness, but there is really no
> need for that these days.
Many thanks for the help.
Thomas Milius
> A similar but from speed different task eg. is to extend a DTP- or
> a spreadsheet program by an own language like VBA for EXCEL or Word.
> However I dislike the concept in which VBA is implemented for it is
> so much taskswapping.
One of us is confused, and it may be me: VBA is implemented in the way
Lua is; as a library. A program calling a function written in VBA
requires no task swapping at all; unlike the BASIC Task with WIMP
messages approach that was suggested.
B.
> To cut a long story short, system() must first copy the calling C
> program to the top end of application workspace before calling BASIC,
> and then when the BASIC program stops, has to copy it back again.
Copy, or merely remap?
Dave
Depending on how good it works it will certainly be interesting. It could be
used for scripting but it could also be useful for people who developed some
programs in BASIC and who would like to switch to lua. They could just change
their libraries quickly. I guess that the lua code will not be perfect, and
you probably still need some knowledge about lua but it will do the hard work
for you even if it does not produce 100% working programs.
At the moment the program relies on basic tokens. So you need a basic file as
input and it will output a lua file. For this process tokens are nice because
you do not need to look whether a command is used like a command or inside a
remark or part of a string, e.g. "PRINT("REMEMBER")" will not change into
"PRINT("--EMBER") or so ;-)
> Fancy sharing?
With you or other developers I can share it although it is at a very early
stage and I would not have said something if this topic did not came out
today.
In general I have not decided whether I want to make it an open source
project or distribute it commercially (or probably better as shareware). This
depends mainly on three factors:
1. How good will it work? Will it be possible to convert WIMP programs?
2. You may know that I am working on luafox, a program for data analysis. How
well will it sell? I plan to release a Windows/MacOS version of the program
and need to finance the gab till it is ready.
3. Is there somebody else who wants to contribute to this project? Then it is
more liekly to become open source.
Anyway, as I said, I have no problem sharing it with developers anyway.
Michael
> Copy, or merely remap?
Copy. This is why a C program should never use system() to execute a
simple *command that does not start an application. The overhead is
tremendous.
Martin
--
---------------------------------------------------------------------
Martin Wuerthner MW Software http://www.mw-software.com/
ArtWorks 2 -- Designing stunning graphics has never been easier
spam...@mw-software.com [replace "spamtrap" by "info" to reply]
> At the moment the program
> relies on basic tokens. So you need a basic file as input and it will
> output a lua file. For this process tokens are nice because you do
> not need to look whether a command is used like a command or inside a
> remark or part of a string, e.g. "PRINT("REMEMBER")" will not change
> into "PRINT("--EMBER") or so ;-)
It sounds like a lexer would be useful here. Pretty easy to do, too,
if you ignore irregular hideousnesses that are permissible in normal
BASIC. (Such as IF foo ELSE bar ENDIF).
> In general I have not decided whether I want to make it an open source
> project or distribute it commercially (or probably better as
> shareware). This depends mainly on three factors:
I suspect such a gizmo's prospects as a commercial offering are
somewhat limited; especially given that most competent Lua programmers
would be able to create something similar reasonably easily.
> 1. How good will it work? Will it be possible to convert WIMP
> programs?
I doubt this strongly; Lua is a very very different beast from BBC
Basic. To do this, you would need a whole compiler. And that will not
be a small or trivial task. Plus, why would you want to? Most
programs converted in such a way would end up slower. Only programs
written to take advantage of Lua will be faster. (Data structures
being implemented in C and immutable strings spring to mind here.)
> 3. Is there somebody else who wants to contribute to
> this project? Then it is more liekly to become open source.
I'd certainly be interested. Writing an implementation of BASIC for
Lua has been on my to do list for quite some time. While I didn't
intend for it to be any way compatible with BBC Basic, it'd certainly
be something BBC Basic programmers would feel more at home with.
And the sooner we can move people on to an elegant language the better,
and such a thing can only help :)
B.
> > At the moment the program
> > relies on basic tokens. So you need a basic file as input and it will
> > output a lua file. For this process tokens are nice because you do
> > not need to look whether a command is used like a command or inside a
> > remark or part of a string, e.g. "PRINT("REMEMBER")" will not change
> > into "PRINT("--EMBER") or so ;-)
> It sounds like a lexer would be useful here. Pretty easy to do, too,
> if you ignore irregular hideousnesses that are permissible in normal
> BASIC. (Such as IF foo ELSE bar ENDIF).
Yes, I agree that it is not nice that BBC Basic allows this. Anyway my
goal is to take this into account because people have used this syntax. So
currently I am doing two different thinks:
1. Global replacement
Some expressions can be replaced globaly, e.g. "REM" -> "--" or
"ENDIF" -> "end -- if"
2. Special code for structures
Basically I put every line of the Basic Program into a table and then
look e.g. for the IF token in this line and check whether there is a
THEN token or not etc.
So I can e.g. handle:
IF b = TRUE THEN PRINT("true") ELSE PRINT("false")
IF c = TRUE PRINT("true") ELSE PRINT("false")
IF c = TRUE ELSE PRINT("false")
IF b = TRUE THEN
PRINT("true")
ELSE
PRINT("false")
ENDIF
I do not have so much experience with lexers so I am not sure whether
there is a better way of doing this.
3. Lexer (I did not start this)
I guess it is pretty easy to use a lexer for many keywords such as
those taking one argument only (ABS,CHR$,...) or more than one argument
like MID$. (Although here it might be difficult to handle
MID$(...) = string)
> > In general I have not decided whether I want to make it an open source
> > project or distribute it commercially (or probably better as
> > shareware). This depends mainly on three factors:
> I suspect such a gizmo's prospects as a commercial offering are
> somewhat limited; especially given that most competent Lua programmers
> would be able to create something similar reasonably easily.
Yes, if at all it might be a 10 Pound shareware or so. But maybe it is
better to keep it open source since it is not a big project and more
people may become interested to use it.
> > 1. How good will it work? Will it be possible to convert WIMP
> > programs?
> I doubt this strongly; Lua is a very very different beast from BBC
> Basic. To do this, you would need a whole compiler. And that will not
> be a small or trivial task.
No, this is not my intention. And I also do not want to support all
commands, especially those like GOTO or GOSUB.
> Plus, why would you want to? Most programs converted in such a way
> would end up slower. Only programs written to take advantage of Lua
> will be faster. (Data structures being implemented in C and immutable
> strings spring to mind here.)
The first idea was to be able to use BBC Basic as scripting language. Then
I though it could also be a useful tool for people who want to start
programming lua and have a BBC Basic background.
> > 3. Is there somebody else who wants to contribute to
> > this project? Then it is more liekly to become open source.
> I'd certainly be interested. Writing an implementation of BASIC for
> Lua has been on my to do list for quite some time. While I didn't
> intend for it to be any way compatible with BBC Basic, it'd certainly
> be something BBC Basic programmers would feel more at home with.
Ok, I will send you the files after sending this news posting.
> And the sooner we can move people on to an elegant language the better,
> and such a thing can only help :)
Yes, this is also what I would like to see and everybody who understands
BBC Basic will be able to learn lua quickly (even when not understanding
all of it at the beginning).
I am not sure about todays implementation but some years ago I had to do some
bigger things in EXCEL and VBA at my job. The result was however extremly
disappointing. Compared to classical EXCEL 4 makros the same program was a
factor 10 slower (nearly 1:1 translation). A collegue and I found that
permanently VBA-Files were generated inside the TEMP directory and deleted
after a short time. Functions written in VBA made EXCEL shhets very slow.
Makros without access to EXCEL cells worked fast but with often access to
EXCEL cells (ony reading) it worked very slow. All hints we had lead us to
the (perhaps wrong) conclusion that there was a context switch and something
had to be swapped in. I must say I am not expecting this from an integrated
language like EXCEL 4 makros are. VBA was very inefficient and changed to
much slower with some ACCESS versions until unusebility.
Beneath running VBA as a seperate task has also advantages: It doesn't play a
role whether you are taking some EXCEL cells and generating a mail through
Outlook when you are a separate task. Of course both actions are slow. I
think you won't find a speed up for the part eg. running in EXCEL with often
EXCEL access compared to another part of the same program which often
accesses ACCESS or WORD.
MS entirely program interface is not really fast. Indeed I never lost the
impression that it has been made in a primitve and slow way. Its adavantage
is simply that so much applications are supporting it. Eg the first DDE (?
Data Exchange protocol) had a certain similarity like sending messages
between RISC OS applications. The whole VBA interaction with the applications
AFAIR in early versions based on it (which explains why it was so slow).
As said I never got a detailed specification about the internals so I don't
know sure but from experience I must say that I dislike VBA even in point of
speed very much. And so I don't think that it is difficulty for an embedded
language like Lua to obtain better results of more than a factor ten.
Regards
Thomas Milius
Oh, that's just because it's a slow abomination :)
> collegue and I found that permanently VBA-Files were generated inside
> the TEMP directory and deleted after a short time. Functions written
> in VBA made EXCEL shhets very slow. Makros without access to EXCEL
> cells worked fast but with often access to EXCEL cells (ony reading)
> it worked very slow. All hints we had lead us to the (perhaps wrong)
> conclusion that there was a context switch and something had to be
> swapped in. I must say I am not expecting this from an integrated
> language like EXCEL 4 makros are. VBA was very inefficient and
> changed to much slower with some ACCESS versions until unusebility.
One of the issues beyond its general hideous performance back then (it
now has a JIT) is the marshaling of data in and out of Excel.
> MS entirely program interface is not really fast. Indeed I never lost
> the impression that it has been made in a primitve and slow way. Its
> adavantage is simply that so much applications are supporting it. Eg
> the first DDE (? Data Exchange protocol) had a certain similarity
> like sending messages between RISC OS applications. The whole VBA
> interaction with the applications AFAIR in early versions based on it
> (which explains why it was so slow).
DDE's basically a plain-text messaging system, often requiring each end
to parse the strings sent. Windows's entire model is based on sending
binary messages to processes though; just like RISC OS. These days you
use COM, and cry about object reference counting instead.
Although VBA could talk via DDE, it was built into applications.
Visual Basic proper was outside, and would absolutely need to use DDE
back then to manipulate Excel or similar.
> As said I never got a detailed specification about the internals so I
> don't know sure but from experience I must say that I dislike VBA
> even in point of speed very much. And so I don't think that it is
> difficulty for an embedded language like Lua to obtain better results
> of more than a factor ten.
Oh, certainly. Lua is easily one of the fastest portable scripting
languages out there.
B.
Apologies for a response to such an outdated message, but a friend
just mentioned this in conversation and, after explaining how you do
this to him, I thought it might be useful to at least reply to the OP.
As you (and, I see, others) suggest, the BASIC and C applications do
have to exist within the same space and using system() to invoke the
child is unreasonable. It would also have the obvious disadvantage of
having no state for the BASIC application as it would require the re-
loading of the application in each instance of the calls. I have a
solution, which I will detail in a moment, but want to first echo the
earlier comments that using a language which is supported within C
would be more sensible than attempting this. Lua has been suggested,
Javascript would be reasonable, Python is another possibility, and
there are other scriptable languages that might be used in place. If
you're actually looking for BASIC support, why not use Brandy - I
cannot speak for its safety in terminal cases (whether it leaks or
allows return calls to be performed safely when it's not being used
standalone) but it would allow you to meet your original requirement
of calling BASIC drivers 'underneath', but without the problems of
fighting over the applications space.
Anyhow, here's the 'how-to' that I came up with - remember that this
is off the top of my head and certain details may be sketchy, but the
general principle of how you would do this should hold.
All AIF applications have a base which can be used to position the
executable. I cannot remember off the top of my head exactly how you
do it, but there's a way to specify the base address for execution of
the executable, which is normally &8000. A brief google tells me that
you may be able to just specify -Base <blah> together with -AIF -
relocatable, although it may be necessary to write the relocater
yourself as a AIF_RELOC code area. That should be relatively trivial
after you look at the basic relocation code present in the executable.
I would suggest (off the top of my head) that you relocate to 128K and
that's where your C application will run, leaving &8000 -> 128K
untouched. This is where the BASIC program (singular - you mention
'BASIC drivers', plural, which implies that you want more complexity -
this can be done but involved a little more evil which I shall come to
later) will reside. Once running, and having determined the BASIC
program needs to be run, you load the BASIC program yourself (you
could let BASIC load the code itself, but it'll be easier on the
debugging if you load it yourself) to &8000 and then call some new,
exciting assembler to initialise the BASIC program - effectively this
will perform the linking function for the process.
You need to:
* Preserve the register context for the application you're running.
This shouldn't be too hard - it's just a 17 word block that you're
going to hold on to.
You may also wish to preserve the FP state, although that's really
a decision based on whether you intend on using floating point with
BASIC64. In some respects it would be easier to do so because then you
can pass floating point values directly to your BASIC inferior without
worrying about the difference in formats.
* Exchange the current environment for a custom environment which
will return to us.
Essentually you need to provide all the environment handlers for
escape, aborts, events, exit, etc, which will trap a 'bad' (or normal)
exit from BASIC in the case where things go wrong.
As you're doing this you stash the current state (the C-state) so
that you can restore it later.
Remember that this has to be done in a safe manner so that if you
are exiting (consider someone pressing alt-break whilst you're moving
through this preservation) you can still terminate nicely. This makes
things far more fun.
* As well as the environment handlers you want to set the
application limit to be 128K (the base of your application).
This is important because it will indicate to BASIC that it cannot
extend itself through the use of END = END + blah (or any similar
method) as the application limit and the size of the application space
are not the same.
* Stash, at 128K (because we know that the start of the program will
not be needed any more - there are other ways of doing this, including
passing the context as an argument, but using the base of the
application is a lot simpler on the coding of the BASIC) a pointer to
whatever context you require to get back the above context information
that you've stored for later.
* Call OS_CLI "BASIC @00008000,<end of basic program> <address of
return routine>" (I think this is the syntax - look it up in the BASIC
manual or the PRM - it's the BASIC in-memory loading syntax"
This invokes the BASIC program and the <address of return routine>
that was pass as a paramater is a piece of code (which will be defined
in a moment) which restores the environment for us so that our C code
can continue. This call should be immediately followed by an OS_Exit
call - because if the BASIC call doesn't work then we need to fail
fatally.
* The environment handlers will be quite common. Events will do
nothing; Exit, Aborts, Errors, Escape all need to result in a
restoration of the environment and register context (similar to how
we'll do below) and the reporting of an error to the calling routine.
The difference to a 'natural' exit from the BASIC (through the <return
routine>) would be that this MUST be flagged as a failure and not re-
enter the BASIC. The BASIC environment has terminated and the only way
to restart it is to load the program again.
The BASIC program has a skeleton introduction which does a very simple
process:
1. We read the CLI (using OS_GetEnv or similar) and pull off the
argument from the list, this is the 'polling address'. Store this as
Cpoll% or something similar.
2. We invoke any 'initialisation' routines that we need to, for
example setting up global variables and the like.
3. We execute a simple loop something like this:
REPEAT
msg% = USR(Cpoll%)
IF msg% <> 0 THEN PROCinvoke(msg%)
UNTIL msg% = 0
<call any finalisation code>
END
This is the code that will do the message passing for the C code;
there's no back-channel to call arbitrary C programs in this design
but that could be implemented by using a secondary exit sequence
similar to the one that I'll define in a moment.
DEFPROCinvoke(msg%) can be defined in any number of ways. What I'd
probably suggest would be that the first word was the function to call
and the following parameters were parameters to be handled. You might
want to do thing differently, but a simple implementation might just
be:
A% = EVAL("FN" + $(!msg%) + "(msg%)")
ENDPROC
(or you might pass the parameter in the message - that's a decision
for you and you said that your data exchange is quite small).
Notice that I've used A% as the return from the function, this has the
handy side effect that it's passed in to the <return function> in R0
where we could happily store it in our return to the C caller. That's
an implementation detail - you could just as easily pass all your
parameters through the message block.
* The <return code> which we exit through restores the context so
that we return safely back to the C code. It shares a lot in common
with the failing exit handlers and might be implemented in subroutines
as such.
* First thing we do is pick up the value at 128K (the base of the
program) that we stashed our context information in so that we can do
a restore of the environment and the like. Remember we're going to be
switching stacks so this is going to be fun, too.
* If we're using R0 as a return from the called function then we can
store this in some context location.
* Store all the other registers for the BASIC environment in a
context block (17 registers) so that we can return back here.
* Restore, swapping, the C environment back, putting the BASIC
environment in the context blocks so that we can restore it later when
we return.
* Restore the C register context from that we stashed previously,
which now moves our registers out.
(this sequence may need to be jiggled about so that R10 (sl) and
R11 (fp) are valid whilst the C context is restored so that any
failure during that process still invokes the C environment and can
find where we are. R13 (sp) may also need to be valid for the C
context but I'm not sure off the top of my head - I'd err on the side
of making it reasonable though, if only for making your own code seem
sane!)
* Return to the caller with the R0 value that we stashed away (if
relevant).
The variation of the above for the exit, abort, error, escape handlers
would be that a different exit path or return could be used - for
example you might invoke a 'throw' operation if you were actually
linking against C++ so that you got a fatal error, or a longjmp if you
so wished. The easiest thing, however, would be to set up the code to
return (say) -1 in the case of fatal failures.
Ok, that's got your BASIC program initialised and waiting to be
called. Now, to call it you invoke a function which does a similar
thing to the first entry sequence, except that we're not setting up
the blocks for the first time, but instead we're restoring them. So
instead of just setting up our BASIC environment handlers, etc, we
instead restore the ones that we swapped out on the return from BASIC.
Same goes for the registers. We still need to keep the state sensible
so that we cannot be terminated in the middle of the swap, and keep
R10, R11, R13 vaguely sensible in the case of failure. Testing this is
fun, but aside from code inspection, a good dollop of try-it-and-check
applies - stick invalid instructions in your code at the places you're
unsure of the behaviour, or an OS_Exit, or OS_GenerateError or MOV pc,
#0 or whatever your favourite manner of termination will be.
Instead of invoking the OS_CLI command you need to merely MOV pc,lr
after the register environment has been restored, having set up R0 to
contain the message block that you wish BASIC to process - containing
functions or other things you wish it to do.
You will also need to (well, strictly 'should have' - it's not
absolutely necessary, but if the BASIC code is expected to do any
clean up then you will probably need to do this) register an 'atexit'
function (or ensure that one is always called) in order to finalise
the BASIC library and close it down properly, passing message block
pointer of 0 to it. On return from that call BASIC will have hit the
END and thus called OS_Exit can returned through the exit handler
rather than the return, so you would either 'catch' the error, or
handle it as normal at that point.
That's the general way that you could do it. Essentially you have a
BASIC inferior which polls for things to do, and (from its point of
view) blocks until it is given work. The C code does nothing whilst
the BASIC is running and doesn't have any particular knowledge of the
BASIC program's state except for what returns through the inferiors
message block return or its return code. The memory partitioning is
fixed at compile time (could be varied with a little thought, but it's
easier to fix it - come on, we're doing so much evil here, let's try
to make our lives a smidge easier). Preemption systems running from
within the C environment should (if they are written correctly) be
unaffected by the use of BASIC and thus BASIC could be preempted away
from - the BASIC interaction should be treated as a thread-unsafe
library and protected by mutexes to prevent multiple entries into the
BASIC code. The BASIC interpreter is not threadsafe (and the above
description of how to enter and return from the entry point isn't
designed to be used in that manner).
Should the BASIC program terminate because of a problem, the handlers
that we set up should handle the problem. It may be necessary to
ensure that the error is not printed but instead thrown to the parent,
with:
ON ERROR ERROR EXT ERR, REPORT$
in the BASIC program's initialisation - this ensures that the error is
reported back through OS_GenerateError so that there's something
useful to go on. BASIC's own handling of aborts and escape will mean
that these should be reported back as a error event so you probably
shouldn't see either of those environment handlers called. Which
doesn't mean that they shouldn't be there or that they shouldn't be
tested, but just that they'll probably not be seen in general use.
There's limits about how the code works - BASIC cannot extend its
heap, so using LIBRARY to pull in other libraries may not work. I have
a feeling that OVERLAY is even more complex and may not work at all -
or may use the top end of memory above the application area, in which
case you're going to have fun.
If BASIC does increase the size of the application area (say as part
of a heap implementation) this should still be safe to an extent. The
C code won't trample over the extended space, but it may extend the
space further if it needs more room - in which case it will be a
matter of the BASIC heap library recognising that the application
space limit is no longer what it expected and not just assuming it has
complete control over it. (I'm dubious about this paragraph and if in
doubt I'd recommend not doing anything like this - I seem to remember
that the C library is careful about clients changing the application
space size without its knowledge, but I could be thinking of the
application-in-module-space case, rather than the plain old
application - oh, that's another way you could do the C code, rather
than placing yourself higher in the application space, implement as an
application-in-module, with all your workspace in the module area, but
that's a little more complex than I care to get into at this time in
the morning).
Essentually you're just swapping in and out the environment in a
similar manner to that of system, but without the memory copy, but
with an environment that you wish to restore rather than just toss on
completion.
How to develop this sort of thing ? Very slowly, testing at each
point, preferable on a second instance of RISC OS so that you can
reboot it when you make a complete pigs ear of it - which you
undoubtedly will the first few shots at it. I'd recommend ignoring
what you're actually trying to do initially and have a rudimentary
stub C program which merely initialises a BASIC library, calls it with
a message block containing values and then shuts it down and exits,
together with a BASIC program which can start up and print out the
parameters it's been given and returns. You could easily set up some
unit tests based on this and various failure cases by just having a
number of BASIC programs with syntax errors at various points, or
calls to bad routines. It's unreasonable to assume that the C
application could continue if faced with being overwritten but many
case should be able to be shown to work.
Returning to your original request which stated 'drivers' plural, how
can this be made to work within the above enviroment. Well, you cannot
run BASIC anywhere other than &8000 without changing the module;
that's invariant and hence why the above solution was proposed.
However, there's no reason why you can't swap them in and out.
Essentially the context you create could easily be allocated space in
the application space of the C code in order to copy the BASIC
workspace there so that another environment can be paged in. Obviously
this might be costly so remembering which context was paged in at a
given time would be reasonable to prevent a page out and in of a
context which didn't need to be done. Another, slightly faster,
alternative would be to create a dynamic area of sufficient size for
the context you're interested in and use OS_SetMemMapEntries to update
the application space pages and the dynamic area pages to be swapped.
This is somewhat fun and I'm relatively certain that it's safe to do
on application space and the pages are correctly moved around.
Performance will be relatively awful using this method prior to Kernel
9.21 (which improved the handling of multiple consecutive page swaps
and I /think/ included the OS_SetMemMapEntries - I might be wrong and
it only affected OS_DynamicArea resizing), I think, but probably
better than copying marginally. I'm also relatively certain that you
should not use application space pages as part of a dynamic area page
grow operation, so I wouldn't try using that - it might work for non-
lazy task swapping, but I've got a feeling that with lazy task
swapping it'll cause you a world of pain very quickly.
As an alternative to the above you /could/ use OS_AMBControl to
perform your switch between the C code and inferior, using a small
section of RMA as context and trampoline to switch between them.
However, the WindowManager would be unaware of the switch and would
fail on certain operations (mostly relating to Alt-break). However you
would be limited in such a case in that your memory spaces would not
be paged in concurrently, so you could have to use the RMA context (or
Dynamic area) as your sole means of communication. Because of the
method of preemption and the above comment about the Window Manager,
it would not be possible to use the OS_AMBControl solution within a
TaskWindow. Also using OS_AMBControl in this manner will lose track of
the memory in the TaskManager and, on fatal termination of the
application, would lose track of it entirely - the memory would not be
recoverable without manual intervention, or a reboot.
Which is, of course, ALL COMPLETELY INSANE.
Whilst it's possible, that doesn't mean that you should do it.
Agreed. The alternative, as I suggested, is to implement the BASIC bit as a
simple Wimp task and use Wimp message passing between the C task and it.
That way, the Wimp ends up doing (pretty much) all of the work you described.
> <big snip>
How good to hear from you. I could never have worked out how it could be
implemented, but your split memory method had crossed my mind. However,
I thought you might run the BASIC program as the primary calling the C
code, which would drop back into BASIC when a driver was needed.
Communication would be via a memory block. Could that work?
> There's limits about how the code works - BASIC cannot extend its
> heap, so using LIBRARY to pull in other libraries may not work. I have
> a feeling that OVERLAY is even more complex and may not work at all -
> or may use the top end of memory above the application area, in which
> case you're going to have fun.
Just a reassurance that OVERLAY uses a fixed block of the heap. If each
driver is in an overlay library I think this could be quite neat and
tidy and also extendible.
> <another big snip>
> How to develop this sort of thing ? Very slowly, testing at each
> point, preferable on a second instance of RISC OS so that you can
> reboot it when you make a complete pigs ear of it - which you
> undoubtedly will the first few shots at it. I'd recommend ignoring
> what you're actually trying to do initially and have a rudimentary
> stub C program which merely initialises a BASIC library, calls it with
> a message block containing values and then shuts it down and exits,
> together with a BASIC program which can start up and print out the
> parameters it's been given and returns.
I am not sure whether it has already been suggested, but the simplest
method I thought of was to run a separate BASIC application and
communicate by messages.
It might be worth thinking about running the BASIC program outside the
application space by changing PAGE - look at early versions of RISC OS
that run Alarm etc from rom. You then only require a small block for the
BASIC environment at &8000. I have run a BASIC program from a dynamic
area like this. My Basil shared library system uses the module area,
but I have experimented satisfactorily with dynamic areas, too.
--
; ,', * Basalt * - gives RO 3.10+ versions of BASIC V new and alternative
;,' keywords, dynamic memory for arrays and blocks, new variable types.
;', Legal, fast and simple to use. Freeware - version 0.98� - 19 Aug 03
,; ',, Steve Drain, Kappa : http://www.kappa.me.uk/basalt.htm
Stimulated by this insane discussion I have looked back to something I
played with in 1998. Then I produced a method to call BASIC code in a
module as either a *command or a SWI. This was not as pure as that
implies, because the code was run as a BASIC application much as it
would be from a Library file. It was never completed, but it was
certainly feasible.
One thing I did find, though, was that BASIC's ARGP, which is central to
its use of its workspace, it too firmly hard-coded to make running BASIC
anywhere other than at &8000 impossible. Nevertheless, you can play
around with the program and user memory locations so that an application
/can/ be made to run in a 8k (?4k) wimpslot.
A 4k wimpslot; everything else in a dynamic area. So far, the only
problem is to remove the dynamic area at the end of the program.
I'm afraid I don't have much to contribute, but just to say it's great to
see you around again :-)
> Which is, of course, ALL COMPLETELY INSANE.
> Whilst it's possible, that doesn't mean that you should do it.
But it's always useful to know what's insane, so we can avoid it but do
almost-but-just-enough-not-to-be-insane things. Before breakfast,
preferably :)
Theo