BBC BASIC for SDL 2.0 v0.18a released

discuss PC<>Acorn file transfer issues & the use of FDC, XFER, Omniflop/disk etc.
User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Tue Aug 01, 2017 9:56 am

I have updated BBCSDL, the cross-platform version of BBC BASIC for Windows, Linux, Mac-OS X, Raspberry Pi and Android. A major new feature is full support for 3D graphics programming on Android. Full details of the new release may be found at the forum.

Richard.

User avatar
bakoulis
Posts: 255
Joined: Wed Feb 08, 2012 9:45 pm
Location: Athens, Greece
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by bakoulis » Tue Aug 01, 2017 1:07 pm

Thank you Richard for the update. Very nice work.
A pure 64-bit Linux version will be very welcome.
:D
2xElectron, 3xBBC B, BBC Master.
2xAcorn A310, A420/1, 2xA3000, 2xA3010, A3020, A4000, A5000.
2xRISC PC, Acorn Pocket Book, Acorn Pocket Book II.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Tue Aug 01, 2017 4:21 pm

bakoulis wrote:A pure 64-bit Linux version will be very welcome.
A 64-bit BBC BASIC raises a number of thorny issues, as I'm sure you're aware - not least that BBC BASIC's integer variables are 32-bits! There was a recent long thread on the subject at the BB4W Discussion Group, but without any firm conclusion other than that however one might tackle it, compatibility with existing programs could be a serious issue.

It irritates me that there seems to be a tacit assumption on the part of Apple (and others) that creating a 64-bit version of an app is just a case of re-compiling it with a few tweaks. Admittedly in many cases that may well be true, but if the application in question is a programming language - with its own set of data types - transitioning to a 64-bit address space may well be problematical.

Acorn were far-sighted in making BBC BASIC a 32-bit language from the start, more than 35 years ago. That has served us well to this day, but with Apple threatening to withdraw support for 32-bit Mac OS apps next year, and with some Linux users opting not to install 32-bit support (for 'strange' reasons, it seems to me), there is going to be a crunch at some point. I'd be very interested if you, or anybody else at this forum, have any contributions to make on the subject.

Richard.

User avatar
BigEd
Posts: 1983
Joined: Sun Jan 24, 2010 10:24 am
Location: West
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by BigEd » Tue Aug 01, 2017 4:23 pm

The idea of a 64-bit build of a 32-bit Basic is... consistent, I think? Using 32-bit ints and limiting Basic's memory arena to 4Gbyte.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Tue Aug 01, 2017 8:34 pm

BigEd wrote:limiting Basic's memory arena to 4Gbyte.
Limiting BBC BASIC's 'arena' to 4Gbytes is all very well, but as soon as you need to interact with the OS that scheme doesn't work. Most problematic in that respect is the SYS statement, used for calling API functions, which may need to be able to pass 64-bit addresses (pointers) as parameters and receive them back as returned values.

For example, consider the common situation when you need to pass a memory block (e.g. a structure) as a parameter to an API function. What you need to send is the 'true' memory address of that block; if BASIC is internally using 32-bit offsets within a 4 Gbyte 'arena' those values are not suitable. However you attempt to tackle this, it is going to be incompatible with existing programs.

Even something as simple as receiving from the OS a handle to a window, or a sprite, or some other resource and then passing that handle back to the OS in a subsequent API function cannot be achieved in a way that is compatible with existing programs, because they are almost certainly going to be expecting the handle to be a 32-bit value, not 64.

As I said, there was a long thread at the BB4W discussion group on this issue. There's no perfect solution and each possible approach has its own pros and cons. Preserving full compatibility with all existing programs is impossible, but it would be desirable to have a scheme which would allow programs to be written that would run on both 32-bit and 64-bit versions of BBC BASIC without alteration.

Richard.

User avatar
BigEd
Posts: 1983
Joined: Sun Jan 24, 2010 10:24 am
Location: West
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by BigEd » Tue Aug 01, 2017 8:57 pm

Ah yes, good point about interoperation with the 64 bit OS.

(Can you share a link to that long discussion? I found this one.)

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Tue Aug 01, 2017 9:17 pm

BigEd wrote:Can you share a link to that long discussion?
https://groups.io/g/bb4w/message/21739
I found this one
Wow, a lot has happened since then (not least that I eventually did introduce 64-bit integers, using a %% suffix). The thread is a good illustration of how I have cycled between being optimistic and pessimistic about BBC BASIC; it was obviously a pessimistic period!

Richard.

User avatar
BigEd
Posts: 1983
Joined: Sun Jan 24, 2010 10:24 am
Location: West
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by BigEd » Tue Aug 01, 2017 9:47 pm

Thanks for the link - I'll have a good read of that discussion.

User avatar
BigEd
Posts: 1983
Joined: Sun Jan 24, 2010 10:24 am
Location: West
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by BigEd » Wed Aug 02, 2017 8:48 pm

That was an interesting read. I'm not sure I have anything to add! Evidently any kind of way to tackle the future will be some kind of compromise. The conservative tactic is to make a 32-bit Basic which works; the forward-looking tactic is to make a 64-bit Basic with the ability to reach more address space.

The idea of passing parameters in two places seems like a clever workaround.

Sorry to hear about your declining faculties - I do hope you still enjoy whatever you can still do, and I hope you do release some source code some day for future adventurers.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Thu Aug 03, 2017 9:02 am

BigEd wrote:The idea of passing parameters in two places seems like a clever workaround.
It quite literally came to me in the middle of the night. Persuading a C compiler to do it was challenging, and relies on a non-standard GCC extension (nested functions). I found it necessary to disable optimisation for that function and I'd be interested if any GCC experts can see a way in which that could be avoided.

Richard.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Mon Aug 07, 2017 11:27 am

One of the new features of BBCSDL v0.18a is full support for 3D (OpenGL) graphics on the Raspberry Pi. Here's a video showing some of the supplied 3D example programs running on that platform.

Richard.

Coeus
Posts: 900
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Coeus » Fri Aug 03, 2018 11:45 am

Richard Russell wrote:
Tue Aug 01, 2017 9:56 am
...Full details of the new release may be found at the forum.
Unfortunately that forum seems to be down at the moment, at least for me. I get connection timeout.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 11:58 am

Coeus wrote:
Fri Aug 03, 2018 11:45 am
Unfortunately that forum seems to be down at the moment, at least for me. I get connection timeout.
Conforums closed down months ago! It's not just the old BBC BASIC forum that's gone, they all have: Liberty BASIC, Just BASIC, you name it!

You need only follow the links from the BBC BASIC web site to find the forum's new home: https://www.bbcbasic.co.uk/forum/

Richard.

Phlamethrower
Posts: 72
Joined: Fri Nov 24, 2017 1:35 pm
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Phlamethrower » Fri Aug 03, 2018 1:23 pm

Richard Russell wrote:
Thu Aug 03, 2017 9:02 am
BigEd wrote:The idea of passing parameters in two places seems like a clever workaround.
It quite literally came to me in the middle of the night. Persuading a C compiler to do it was challenging, and relies on a non-standard GCC extension (nested functions). I found it necessary to disable optimisation for that function and I'd be interested if any GCC experts can see a way in which that could be avoided.

Richard.
Are you still using that code? I can see that it would work in some situations, but I'd be surprised if it works for anything other than the most trivial cases (e.g. all parameters are either integers or floats).

The ABI/PCS define how function call parameters (and return values) get mapped to the hardware registers. For a C-like language this mapping is typically dependent on the order of the parameters and the size and type of each parameter. This has implications for both soft vs. hard float ABIs and 32bit vs. 64bit OS's/CPU modes, since they'll all use different ABIs.

I'm fairly certain that for all standard ARM hardfloat ABIs, the basic algorithm is to take the function parameter list and split it into three lists: One for integer-type parameters (which will be mapped to the ARM integer registers), one for float-type parameters (which will get mapped to the floating point registers), and one "overflow" list which will be pushed onto the stack (used when there are more parameters than can fit in the available registers). So if there was a C function "void foo(int a,double b,int c)", 'a' would get passed in R0 and 'b' would get passed in D0, and 'c' in R1.

To be able to cope with that, SYS would need to know the data types that the called function is using - which I'm guessing is something that it currently doesn't have any knowledge of, otherwise all the discussions of how to upgrade SYS to run on 64bit OS's would be moot. E.g. Python has a CFFI package that allows calling arbitrary functions using the "native" ABI (typically C libraries, or libraries which can be compiled to expose functions in a C-like manner) - but before you can call a function, you're required to provide its signature so that the package can work out how to map the arguments & result between Python-land and ABI-land. Extending BASIC to support that kind of functionality would seem like a sensible approach to me. E.g. to borrow CFFI's approach where you provide the signature of the function in C's syntax:

Code: Select all

DEF SYS "void foo(int a,double b,int c)"
SYS "foo",1,PI,2
Attempting to call a function which hasn't been DEFined will cause an error. Passing invalid arguments (e.g. providing a string where a numberic type is required) will cause an error. (Maybe these two could be optional, so that platforms where the current setup works can continue to run existing programs)

There will be some complexity with variadic arguments; in some situations BASIC might be able to guess at the intended types, but for the general case the programmer will probably have to be explicit, perhaps by specifying the types directly in the SYS call:

Code: Select all

SYS "printf(char*,int,char*)","Number %d is %s",5,"odd"
BASIC could come with a built-in list of DEFs to allow any common functions to continue to be used without any problems.

(Note that I'm not saying that this will magically fix all of the 64bit problems, but it will be a good step in the right direction, and will shield BASIC against problems with differing ABIs on 32bit platforms)

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 2:16 pm

Phlamethrower wrote:
Fri Aug 03, 2018 1:23 pm
Are you still using that code?
Yes, but not on the only 64-bit platform I currently support (iOS) because that uses Clang rather than GCC and it's one of the few GCC extensions not implemented by Clang.
I can see that it would work in some situations, but I'd be surprised if it works for anything other than the most trivial cases (e.g. all parameters are either integers or floats).
Bearing in mind that 'integers' includes 'pointers' (they are passed the same way in every ABI I know of) that covers every kind of parameter BBC BASIC needs to pass!
To be able to cope with that, SYS would need to know the data types that the called function is using, which I'm guessing is something that it currently doesn't have any knowledge of, otherwise all the discussions of how to upgrade SYS to run on 64bit OS's would be moot.
The issue is not that the parameter types are unknown (if that were the case a solution would be impossible) but that they are known only at run-time. Assembly language code can pass the parameters as required no problem, but there is no 'legitimate' way of achieving that in C as far as I am aware.
Extending BASIC to support that kind of functionality would seem like a sensible approach to me.
I don't see how it would help. The parameter types are still known only at run time (unless you are proposing that BBC BASIC is compiled rather than interpreted!) so exactly the same issue arises: there's no syntax in C for specifying parameter types at run time, they need to be known at compile time.

If there is a nicer way of solving the problem than the method I am currently using I would certainly like to know.

Richard.

Coeus
Posts: 900
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Coeus » Fri Aug 03, 2018 3:09 pm

Richard Russell wrote:
Fri Aug 03, 2018 2:16 pm
Bearing in mind that 'integers' includes 'pointers' (they are passed the same way in every ABI I know of) that covers every kind of parameter BBC BASIC needs to pass!
Except for floating point on at least the ARM hard-float ABI.
Richard Russell wrote:
Fri Aug 03, 2018 2:16 pm
The issue is not that the parameter types are unknown (if that were the case a solution would be impossible) but that they are known only at run-time. Assembly language code can pass the parameters as required no problem, but there is no 'legitimate' way of achieving that in C as far as I am aware.
So by compile time do you mean when the BBC BASIC interpreter is compiled? If so, then yes, to take up Phlamethrower's suggestion then I think you would need to resort to a small piece of assembler to make the call out to the external library function concerned. That's a nuisance given that you need a variant of that not just for each processor type but for each variant of the ABI on that processor type, but still doable, surely?
Richard Russell wrote:
Fri Aug 03, 2018 2:16 pm
I don't see how it would help.
So am I right in assuming that currently SYS works by putting every passed parameter into a 32bit integer and hoping that the called library function is happy to receive one, either because it is to be used as a 32 bit integer, because it is to be used as an address and they happen to be 32 bit too, or the ABI happens to also pass floats in the same way as integers? Don't most functions implemented in C that expect floating point arguments expect double-precision arguments, i.e. 64bit, not 32?

This reminds me of the days of K&R C. Programs we wrote on Unix workstations, and which worked reliably there, would sometimes crash on the PC. The reason was two-fold. K&R C assumed that every function that wasn't declared returned an integer and, on our Unix machines integers and pointers were both 32bit so if a function has not been declared but actually returned an address it was still just one CPU register wide and returned by exactly the same convention, so by good luck it worked. On the PC, usually, an inetger was 16bit so half the pointer was lost and a crash soon ensued.

You can really see the history of C in this - the laid-back approach to declaring functions came from word-based languages where no-one was too worried about whether a machine word represented an integer or a pointer until you actually tried to use it, but then PC C compilers challenged that. It seems this is at the heart of the challenge of BBC BASIC in a 64bit environment in that previous a 32 bit "word" was almost universal, now it no longer is.

What solved the issue for C was ANSI C with prototypes for functions (from C++) and compiler warnings for functions that were not declared.

So the DEF SYS proposal may not not be key to providing a neat and easy answer to the implementation of how to pass different types of variables differently according to different ABIs (that may just be leg work), it is probably still a step forward.
Last edited by Coeus on Fri Aug 03, 2018 3:12 pm, edited 1 time in total.

Soruk
Posts: 105
Joined: Mon Jul 09, 2018 10:31 am
Location: Basingstoke, Hampshire
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Soruk » Fri Aug 03, 2018 3:52 pm

Richard Russell wrote:
Fri Aug 03, 2018 2:16 pm
I don't see how it would help. The parameter types are still known only at run time (unless you are proposing that BBC BASIC is compiled rather than interpreted!) so exactly the same issue arises: there's no syntax in C for specifying parameter types at run time, they need to be known at compile time.

If there is a nicer way of solving the problem than the method I am currently using I would certainly like to know.
I believe that's where va_arg comes in. After all, printf() is able to handle multiple parameter types. Hit the man page for stdarg(3), and there's an example of its use towards the bottom of the page.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 3:55 pm

Coeus wrote:
Fri Aug 03, 2018 3:09 pm
Except for floating point on at least the ARM hard-float ABI.
Floats are one of the two parameter types that I referred to as being required by the SYS statement (integers/pointers and floats). Or have I misunderstood?
Richard Russell wrote:
Fri Aug 03, 2018 2:16 pm
So by compile time do you mean when the BBC BASIC interpreter is compiled?
Yes. BBC BASIC is interpreted; the interpreter is compiled (apart from 32-bit x86 which is written in assembler).
That's a nuisance given that you need a variant of that not just for each processor type but for each variant of the ABI on that processor type, but still doable, surely?
Not by me! If you or somebody else would like to volunteer to write it I would be very happy indeed, but with probably at least 8 versions of the code being required (x86/ARM, 32/64-bits, hard/soft floats, Windows/Linux ABI) it's well beyond my capabilities.
So am I right in assuming that currently SYS works by putting every passed parameter into a 32bit integer and hoping that the called library function is happy to receive one...
That's how it works in 32-bit Windows, because that's exactly how the ABI is defined. But sadly not on any other platform.
Don't most functions implemented in C that expect floating point arguments expect double-precision arguments, i.e. 64bit, not 32?
That's too much of a generalisation. GDIplus usually expects 32-bit floats, as does DirectX. OpenGLES uses 32-bit floats but OpenGL often has both 'float' and 'double' variants of functions. SDL 2.0 expects doubles. So there's no standardisation and it's up to the BBC BASIC programmer to know what the API he is calling expects and code appropriately. At the ABI level (apart from the special case of 32-bit Windows where everything is passed on the stack) floats of all flavours are passed in the floating-point registers, so you get oddities like a 32-bit float being passed in the LS half of a 64-bit floating-point register.
So the DEF SYS proposal may not not be key to providing a neat and easy answer to the implementation of how to pass different types of variables differently according to different ABIs (that may just be leg work), it is probably still a step forward.
The DEF SYS suggestion is one way of flagging which parameter is which type. But that's moot because of course 64-bit BBCSDL (for iOS) already exists and was released quite some time ago. I'm not going to be changing the method I use now, which works nicely for all values except zero (because float zero and integer zero cannot be distinguished). I use the kludge of passing 'minus zero' in order to force it to be a float!

Richard.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 4:11 pm

Soruk wrote:
Fri Aug 03, 2018 3:52 pm
I believe that's where va_arg comes in. After all, printf() is able to handle multiple parameter types.
I don't see how that helps. Varargs is surely addressing the issue of how the called routine can deal at run-time with receiving different numbers and/or types of parameters, not the issue (relevant to BBC BASIC) of how the calling code can pass different numbers and/or types of parameters at run time. Or have I misunderstood?

Richard.

Coeus
Posts: 900
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Coeus » Fri Aug 03, 2018 4:45 pm

Soruk wrote:
Fri Aug 03, 2018 3:52 pm
I believe that's where va_arg comes in. After all, printf() is able to handle multiple parameter types. Hit the man page for stdarg(3), and there's an example of its use towards the bottom of the page.
Conceptually, by including the ... in the parameter list you tell the compiler that any number of arguments of any type could follow then the receiving function has the job of stepping through them using the macros provided. The macros provided by stdarg.h were clearly defined on the assumption that the arguments were passed on the stack and that therefore va_start would be getting the stack pointer/frame pointer depending on convention, va_arg would be de-referencing that as a pointer and advancing in the correct direction, i.e. the stack direction on the machine concerned etc.

It is hard to see how those macros could be implemented to instead step through a series of CPU registers so I rather suspect as soon as you include ... in a function declaration the compiler reverts to passing the arguments on the stack even if the usual convention is to use registers. If that is indeed the case then it means it is not a useful way to call non-stdarg functions that expect their arguments in registers. I'll try a small test case and see.

Coeus
Posts: 900
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Coeus » Fri Aug 03, 2018 5:11 pm

Coeus wrote:
Fri Aug 03, 2018 4:45 pm
It is hard to see how those macros could be implemented to instead step through a series of CPU registers so I rather suspect as soon as you include ... in a function declaration the compiler reverts to passing the arguments on the stack even if the usual convention is to use registers. If that is indeed the case then it means it is not a useful way to call non-stdarg functions that expect their arguments in registers. I'll try a small test case and see.
Using gcc on Linux as a test case, it seems there is nothing different about calling a variable argument list function, so writing for example:

Code: Select all

extern void called(const char *fmt, ...);

int main(int argc, char **argv)
{
    called("df", 56, 2.1);
}
one can then either implement called as:

Code: Select all

#include <stdarg.h>
#include <stdio.h>

void called(const char *fmt, ...)
{
    va_list ap;
    int ch;

    va_start(ap, fmt);
    while ((ch = *fmt++)) {
        switch(ch) {
        case 'd':
            printf("%d\n", va_arg(ap, int));
            break;
        case 'f':
            printf("%g\n", va_arg(ap, double));
            break;
        }
    }
}
or as

Code: Select all

#include <stdio.h>

void called(const char *fmt, int i, double f)
{
    printf("fmt=%s, i=%d, f=%g\n", fmt, i, f);
}
and both give the expected results with the compiler some extra complexity into the version that can receive any args vs. the version with fixed args so one can call normal ABI functions with a function prototype declared (...).

Phlamethrower
Posts: 72
Joined: Fri Nov 24, 2017 1:35 pm
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Phlamethrower » Fri Aug 03, 2018 6:00 pm

Richard Russell wrote:
Fri Aug 03, 2018 2:16 pm
Phlamethrower wrote:
Fri Aug 03, 2018 1:23 pm
I can see that it would work in some situations, but I'd be surprised if it works for anything other than the most trivial cases (e.g. all parameters are either integers or floats).
Bearing in mind that 'integers' includes 'pointers' (they are passed the same way in every ABI I know of) that covers every kind of parameter BBC BASIC needs to pass!
Maybe I wasn't clear enough; I meant that it would work if all the parameters are to be passed via integer CPU registers, and that it would work if all the parameters are to be passed via FP CPU registers. But it won't work if you have a mix of ints and floats because you're duplicating the full set of arguments in both the int and float registers.
The issue is not that the parameter types are unknown (if that were the case a solution would be impossible) but that they are known only at run-time. Assembly language code can pass the parameters as required no problem, but there is no 'legitimate' way of achieving that in C as far as I am aware.
If you know what ABI you're compiling BASIC for, then you can probably abuse the mechanics of the ABI to get the desired effect without requiring any assembler. E.g.:

Code: Select all

typedef void (*arm_hardfloat_func)(int a0,int a1,int a2,int a3,float f0,float f1,float f2,float f3,int s0,int s1,int s2,int s3);

void sys(arm_hardfloat_func *func,variable_t *args[8])
{
  int int_args[4];
  int float_args[4];
  int stack_args[4];
  int next_int=0;
  int next_float=0;
  int next_stack=0;
  for(int i=0;i<8;i++)
  {
    if (should_argument_be_int(i)) // Replace with desired logic
    {
      if (next_int == 4)
      {
        stack_args[next_stack++] = variable_as_int(args[i]);
      }
      else
      {
        int_args[next_int++] = variable_as_int(args[i]);
      }
    }
    else
    {
      if(next_float == 4)
      {
        float value = variable_as_float(args[i]);
        stack_args[next_stack++] = *((int *)&value);
      }
      else
      {
        float_args[next_float++] = variable_as_float(args[i]);
      }
    }
  }
  func(int_args[0],int_args[1],int_args[2],int_args[3],float_args[0],float_args[1],float_args[2],float_args[3],stack_args[0],stack_args[1],stack_args[2],stack_args[3]);
}
That example won't quite work as-is, but it shows the general concept: Define a function signature which accepts the maximum number of parameters in registers, and then create a routine which will dynamically map SYS arguments to those parameters.

Or if C is a bit slow for the task you could have an assembler routine which does the same thing (or if the host OS supports it, a code generator that generates optimised code for each function signature that's used).

Either way, it should only require you to write one or two functions per ABI that you want to support.

Coeus
Posts: 900
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Coeus » Fri Aug 03, 2018 7:16 pm

BTW, which version of the Raspberry Pi is the Raspberry Pi version of BBC SDL targetted at? If I try running it on my Pi 2 B+ I get "illegal instruction" in main()

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 9:55 pm

Phlamethrower wrote:
Fri Aug 03, 2018 6:00 pm
But it won't work if you have a mix of ints and floats because you're duplicating the full set of arguments in both the int and float registers.
It will in the case of the Windows ABI (which is what that code is targetting). The way things work in Windows is fundamentally different from the way they work in Linux. Suppose you have a function that receives four parameters: (int1, float1, float2, int2) and let's assume the registers in which integers are passed are called i1, i2, i3, i4 and those in which floats are passed are called f1, f2, f3 and f4.

Now in Linux and related OSes:

int1 is passed in i1
float1 is passed in f1
float2 is passed in f2
int2 is passed in i2

But in Windows this is what happens:

int1 is passed in i1
float1 is passed in f2
float2 is passed in f3
int2 is passed in i4

Registers f1, i2, i3 and f4 are unused, which is what allows me to duplicate the parameters. If you are in doubt, this is formally documented here where it states "Floating-point and double-precision arguments are passed in XMM0 – XMM3 (up to 4) with the integer slot (RCX, RDX, R8, and R9) that would normally be used for that cardinal slot being ignored (see example) and vice versa".
If you know what ABI you're compiling BASIC for, then you can probably abuse the mechanics of the ABI to get the desired effect without requiring any assembler.
That's what I do in iOS (which is convenient because since it's using Clang I can't use the GCC nested function extension anyway) but I don't think any similar method is possible in Windows.
Either way, it should only require you to write one or two functions per ABI that you want to support.
If you can think of a way of solving the problem entirely in C for the Windows ABI, other than the nested function kludge, I would be very interested!

Richard.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 10:14 pm

Coeus wrote:
Fri Aug 03, 2018 7:16 pm
BTW, which version of the Raspberry Pi is the Raspberry Pi version of BBC SDL targetted at? If I try running it on my Pi 2 B+ I get "illegal instruction" in main()
It works best on an RPi 3 but should run OK (if rather slowly) on an RPi 2. I would not have expected you to receive an 'illegal instruction' error on an RPi 2 since it supports the full ARMv7 instruction set. What OS version are you running? I've only tried it on Raspbian Jessie and Raspbian Stretch.

Richard.

Soruk
Posts: 105
Joined: Mon Jul 09, 2018 10:31 am
Location: Basingstoke, Hampshire
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Soruk » Fri Aug 03, 2018 10:27 pm

Richard Russell wrote:
Fri Aug 03, 2018 10:14 pm
Coeus wrote:
Fri Aug 03, 2018 7:16 pm
BTW, which version of the Raspberry Pi is the Raspberry Pi version of BBC SDL targetted at? If I try running it on my Pi 2 B+ I get "illegal instruction" in main()
It works best on an RPi 3 but should run OK (if rather slowly) on an RPi 2. I would not have expected you to receive an 'illegal instruction' error on an RPi 2 since it supports the full ARMv7 instruction set. What OS version are you running? I've only tried it on Raspbian Jessie and Raspbian Stretch.
The code executes on my RPi2 running Wheezy. ("Run" would be a stretch, more like a leisurely walk) I did have to manually compile and install SDL2 for it.
Last edited by Soruk on Fri Aug 03, 2018 10:28 pm, edited 1 time in total.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Fri Aug 03, 2018 10:44 pm

Soruk wrote:
Fri Aug 03, 2018 10:27 pm
("Run" would be a stretch, more like a leisurely walk)
Well quite, and unless the 'experimental' VC4 graphics driver is enabled it slows to a snail's pace. But I remain pleasantly surprised at just how well it runs on a RPi 3, considering the cheapness of that machine. Even David Williams' prizewinning Forces of Darkness game - one of the most complex and ambitious vdeo games to be coded in pure BBC BASIC - runs acceptably well as you can see from this YouTube video.

Phlamethrower
Posts: 72
Joined: Fri Nov 24, 2017 1:35 pm
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Phlamethrower » Fri Aug 03, 2018 10:51 pm

Richard Russell wrote:
Fri Aug 03, 2018 9:55 pm
Phlamethrower wrote:
Fri Aug 03, 2018 6:00 pm
But it won't work if you have a mix of ints and floats because you're duplicating the full set of arguments in both the int and float registers.
It will in the case of the Windows ABI (which is what that code is targetting). The way things work in Windows is fundamentally different from the way they work in Linux. Suppose you have a function that receives four parameters: (int1, float1, float2, int2) and let's assume the registers in which integers are passed are called i1, i2, i3, i4 and those in which floats are passed are called f1, f2, f3 and f4.
I was talking about this message, which suggested that the exact same trick was going to be used on ARM targets with a VFP hardfloat ABI.

https://groups.io/g/bb4w/message/21788
Either way, it should only require you to write one or two functions per ABI that you want to support.
If you can think of a way of solving the problem entirely in C for the Windows ABI, other than the nested function kludge, I would be very interested!
Yeah, that Windows ABI is pretty weird.

There are a few options I can think of:

1. The "cleanest" approach might be to brute-force it. Only four arguments are passed in registers, and for each argument you've only got two choices (int or float register), so that's 16 possible function signatures (ignoring any variations due to return values). So a switch statement could easily be used to select between the different signatures.

2. Abuse unprototyped functions (if your compiler doesn't throw a fit): https://msdn.microsoft.com/en-us/library/6yy8aw4d.aspx

Code: Select all

int apicall_ (int (*APIfunc) (), int *parm)
{
 double *p = (double *) parm;
 return APIFunc(p[0], p[1], p[2], p[3], p[4], p[5]) ;
}
Because everything gets passed in as a double, the compiler (if it's following MS's rules) should populate both the int and float CPU regs.

3. You might be able to do something similar to option 2, but using variadic arguments (less likely to make the compiler explode), as again, floating point values will be passed in both integer and float registers: https://msdn.microsoft.com/en-us/library/dd2wa36c.aspx

Code: Select all

int apicall_ (int (*APIfunc) (int, ...), int *parm)
{
 double *p = (double *) parm;
 if (first_arg_is_int)
 {
  return APIFunc(parm[0], p[1], p[2], p[3], p[4], p[5]) ;
 }
 else
 {
  int (*APIFunc2)(double,...) = APIFunc;
  return APIFunc2(p[0], p[1], p[2], p[3], p[4], p[5]) ;
 }
}
Although an extra step or two may be necessary to avoid the compiler complaining about the cast between the two different function pointer types.

User avatar
Richard Russell
Posts: 368
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: BBC BASIC for SDL 2.0 v0.18a released

Post by Richard Russell » Sat Aug 04, 2018 9:38 am

Phlamethrower wrote:
Fri Aug 03, 2018 10:51 pm
I was talking about this message, which suggested that the exact same trick was going to be used on ARM targets with a VFP hardfloat ABI.
Yes, but for a different reason. You've got to remember that there are two quite separate issues here which are easily confused. One is the Windows ABI complication, and the other is the fact that (traditionally) BBC BASIC's SYS statement has no way of telling whether a parameter is a float or an integer - they are all just 'numbers'.

It's the latter which has led me to use the 'nested function' technique in the ARM hard-float case, since by passing all the parameters as both integers and floats it no longer matters that the SYS statement can't tell the difference. As you rightly say this second case will only work if all the parameters are integers or all floats, but that's a common case and it's far better than the alternative, which is not being able to pass floats at all!

To clarify the context we need to consider a bit of history. Until the iOS edition of BBCSDL forced my hand, all my versions of BBC BASIC (for Windows and for SDL 2.0) have been 32-bit builds and therefore there has not been an issue with SYS - 32-bit platforms generally pass all parameters on the stack and everything works beautifully. Until, that is, the Raspberry Pi with its hard-float ABI came along (Android uses the FPU but still passes floats in the stack)! I needed some kind of workaround to get that working so that it could join the other 32-bit editions in being substantially 100% compatible with each other. The nested function kludge doesn't achieve that when there's a mix of integer and float parameters but that's sufficiently rare that I can live with it, and it's the best I can do.

With 64-bit editions (so far only iOS, but it's the way the world is going - Mac OS soon) all this gets blown out of the water. Now it really is impossible to make SYS work if it has no way of knowing which parameters are integers and which floats, so I've had to bite the bullet and implement such a system. It's far from perfect, but BBC BASIC simply wasn't designed with this issue in mind. And it's at least acceptable to introduce an incompatibility in the SYS statement because 64-bit BBC BASIC is necessarily incompatible with 32-bit BBC BASIC in other ways. Once SYS can tell which parameter is which type the problem largely goes away, but it still needs a workaround for Windows.
The "cleanest" approach might be to brute-force it.
Maybe. I don't really see what the objection is to the nested-function kludge though. What I'm doing is syntactically perfectly legal (the compiler doesn't complain) but of course there's nothing contractual to say it will actually do what I want it to do (in practice it does if optimisation is switched off). It means using GCC, but on Windows - which is the only OS affected - that isn't a problem. I have control over the build platform and I can check that it continues to work after, say, an update to GCC. The only objection I can see is that it offends good coding practice, but I fear that can be said of a lot of my code (I'm not a skilled C programmer and BBC BASIC has been translated from assembler to C in the crudest fashion).
Because everything gets passed in as a double, the compiler (if it's following MS's rules) should populate both the int and float CPU regs.
Why? Doubles get passed only in the MMX registers, surely? The ABI mandates creating 'shadow' stack space for the registers, but not transferring doubles in two different ways, AIUI.

Richard.


Post Reply