Matrix Brandy V1.22.15 released

I have released version 1.22.15 of Matrix Brandy. Changes include:

- System: For SDL builds, move ESCAPE polling to its own thread.
- System: Added CTRL+PrtSc to force immediate exit in SDL build. Unfortunately the Pause/Break key doesn't register in SDL. This functionality can be disabled with build-time option BRANDY_NOBREAKONCTRLPRTSC.
- System: Added some fixes to handle issues with the sdl12-compat library.
- System: Implemented an internal *./*CAT handler, loosely styled on BBC Micro ADFS output.
- System: Exit code is now ERR MOD 256 (or 255 if that would result in 0), or 0 if exited with no error. A program can still set its own exit code as a parameter to QUIT.
- System: Fixed a regression in the way floats were handled by SYS.
- System: Corrected a flawed usage of fcntl().
- MOS: Implemented OSBYTE 210
- BASIC: Extended ATN to take two parameters in parentheses, this calls atan2().

Downloads are available from the Matrix Brandy website, also from git.

Comments

  • Soruk wrote: »
    - BASIC: Extended ATN to take two parameters in parentheses, this calls atan2().
    It interests me that you thought this deserved an extension to the core language; I've never found the BASIC implementation (FNatan2) to have any significant shortcomings. Obviously a built-in function will be slightly faster, but I wouldn't have expected that to be sufficient to justify such a drastic step.

    Realistically I cannot follow suit. Although I probably could add the functionality to my coded-in-C versions (but only at the cost of slowing down the existing ATN function) I no longer have the assembly language skills to add it to BBC BASIC for Windows or the 32-bit x86 editions of BBC BASIC for SDL 2.0.
  • That is of course, fair enough. My overriding thing on adding this was doing it in such a way that any existing code would not break, hence, not adding a new function ATN2 (as that is in itself a valid way of saying ATN(2). The check is, if no parentheses seen, assume single parameter. If parentheses seen, then check if second parameter supplied, and only then call atan2().

    While, as you well know, I am aiming to keep close to RISC OS BBC BASIC VI compatibility, it isn't bidirectional, else I wouldn't have been able to implement things like DIM HIMEM either.
  • Soruk wrote: »
    My overriding thing on adding this was doing it in such a way that any existing code would not break
    I appreciate that it's a 'compatible' change (apart from speed: since ATN doesn't currently take a parenthesised expression it's quite an overhead to test for one); my query was why change it at all? I can't see what is wrong with the existing FNatan2() function, which I use all over the place and have never given a second thought.

    I have spent decades explaining to people the 'philosophy' behind BBC BASIC, as established in the 1980s, which was to incorporate as a standard feature of the language only capabilities that either cannot be implemented as a user-defined FN/PROC or that doing so would involve an unacceptable speed penalty or that the feature is so commonly needed that including it can be justified on those grounds.

    If none of those tests is passed the feature should be implemented as a FN/PROC, possibly in a library. It's this longstanding philosophy which has kept BBC BASIC both compact and stable, not bloated with features that hardly anybody ever uses (and bloating can impact speed because of instruction cache efficiency). It's one of BBC BASIC's Unique Selling Points over many other BASIC interpreters.

    I realise that you don't claim that Matrix Brandy is BBC BASIC, and there's no way that you can be expected to be constrained by decades-old decisions and philosophy, but I do wonder where it might end if you feel able to add any extensions at all so long as they don't impact compatibility.
  • I don't think the overheads are that great, what I'm doing is basically this:
    IF ?exec_pointer = ASC("(") THEN
        new_code_path with additional check for one or 2 parameters
        behave appropriately
    ELSE
        existing code path, one parameter only
    ENDIF
    

    The atan2() call extension was a user request, from the guys reverse engineering Elite.
  • Soruk wrote: »
    I don't think the overheads are that great
    It's all a matter of degree. My interpreters are slow already, so I don't like to make changes which add even a small overhead, at least not to a mainstream mathematical function like ATN. I don't worry so much with statements that are unlikely to be used in time-critical code.
    The atan2() call extension was a user request, from the guys reverse engineering Elite.
    Did you point out to them that there's a standard user-defined function for it? In my experience the performance of that function has been entirely acceptable, even in things like high-speed games. For example in aliens.bbc, which hopefully you're familiar with as one of the graphics demos supplied with BBC BASIC for SDL 2.0, FNatan2() is used in the calculation of how the alien turns when approaching an edge.

    I know that BBC BASIC isn't standardised, which opens the door to extensions being made to one version but not to another. However when I've been tempted to make an extension (most recently using SYS as a function) I've always consulted on it and made attempts to get it implemented more widely.
  • There may be a standard library function for it in BBCSDL, however (with the exception of GPIO) I don't ship any of your libraries with Matrix Brandy, and even in the case of gpiolib, that was simply because it's relatively complicated to program for so I used yours as an API definition/template and reworked it using Matrix Brandy internals to do the heavy lifting.
  • Soruk wrote: »
    There may be a standard library function for it in BBCSDL
    I wouldn't expect it to be in a library, at just two lines of code that would be overkill; I just include it inline when needed.

    My point is simply that as it's trivially possible to implement atan2() as a user-defined function, why not do it that way? That's what user-defined functions are for!

    It's also perfectly possible (certainly in my BASICs and I assume in Matrix Brandy too) to call the C-library function using SYS (here for a 64-bit platform):
          x# = -1.23
          y# =  2.34
          SYS "atan2", y#, x# TO a#
          PRINT a#
    

    I feel very strongly that extending the language should only be considered in the most exceptional circumstances, when every other avenue has been exhausted. I can't see how this qualifies.
  • Over in the Bubble Universe thread I looked into this for implementing sincos(), creating a new Brandy_sincos call as I can't return floats from a SYS call, but can make it use a memory block OSWORD-style, however the overheads of setting up the SYS environment were so much worse than just calling SIN and COS separately in BASIC I backed that change out as it proved to be completely pointless. Although that might also point to some optimisation needed within the way SYS works.

    Also, since you use 80-bit floats you can safely store a 64-bit int in one without losing accuracy, I don't have that luxury. Though, not sure how you work around this on ARM as it's limited to 64-bit floats. I did try altering Matrix Brandy to use 80 bit floats, but it went horribly wrong, as there must be some places in the code where the float size is hard-wired at 64 bits, probably in the stack code amongst others.
  • Soruk wrote: »
    the overheads of setting up the SYS environment were so much worse than just calling SIN and COS separately in BASIC I backed that change out as it proved to be completely pointless.
    One has to expect that an interpreted language will be relatively slow, sometimes too slow, and not worry about it. After all my BASICs are much slower than Brandy, but I don't obsess about that; they are still (I hope) useful. Extending the language, almost on an application-by-application basis, to work around specific performance bottlenecks seems completely wrong to me.
    I can't return floats from a SYS call
    Presumably that would be easy to implement, in my BASICs it's done in the most obvious and straightforward way: there are two versions of SYS, one which returns an integer and one which returns a float, and the appropriate one is called depending on the type of the variable specified after the TO. It doesn't rely at all on being able to store an integer 'in' a float.
    Though, not sure how you work around this on ARM as it's limited to 64-bit floats.
    No workaround is necessary, because in my BASICs 'unsuffixed' variables are variants, not floats. Even on ARM platforms, when floats are only 8 bytes, 10-bytes of memory are allocated for a variant so it can hold a 64-bit integer with no loss of precision.

    You can sometimes see a side-effect though. For example you might expect this code to generate a 'Number too big' error, but it doesn't on an ARM platform:
    a%% = -2^63-1
    
    It doesn't raise an error because -2^63-1 won't fit in an integer so gets automatically promoted to a float, but since a 64-bit float doesn't have the precision to contain this value precisely it gets 'silently' rounded to -2^63, and that rounded value will store in a 64-bit integer variable!

    A small price to pay for the overall benefit of variants.