|
Forum Index : Microcontroller and PC projects : Ideas for useful commands and functions?
| Author | Message | ||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10572 |
The ArmmiteH7 and the MMX have lots of flash memory available but are limited by the number of commands and functions supported by MMBasic (127 of each). Geoff has quite rightly pointed out that changing this is a big job and risks de-stabilising the code. I've worked out a way of getting round the limitation by allowing the user to select the commands and functions they want available, where necessary substituting them for existing ones. The Armmite H7 currently has 126 commands and 126 functions pre-defined out of a maximimum of 127. The attached screenshot shows how I have engineered the code to allow extra and different functions and commands to be used. ![]() The LOAD FUNCTION and LOAD COMMAND statements can be used either at the command line or in the MM.STARTUP subroutine. They cannot be used in a program as the RUN command tokenises the functions and commands so they must all be pre-establised. The function names to be replaced must include the "$" where appropriate and the opening bracket where it is shown in LIST FUNCTIONS. The command is insensitive to case and AUTORUN works as expected as long as command and function usage is defined in MM.STARTUP. Do Print test$, test2$ Test Test2 Pause 500 Loop Sub mm.startup Load function "test$" Load function "test2$" As "string$(" Load command "test" Load command "test2" As "sprite" End Sub I think it is very unlikely that a user couldn't find unused commands or functions to replace in any specific program so I think this approach gets round the current limitation without compromising stability of the core MMBasic code. Thoughts on the approach and all ideas for new functionality (e.g. complex number support) appreciated. |
||||
| robert.rozee Guru Joined: 31/12/2012 Location: New ZealandPosts: 2464 |
i hate to say it, but it does sound awfully complicated - and a good way for everyone to end up running incompatible versions of mmbasic! plus, what happens to a (tokenised) program sitting in flash when a command is swapped out? personally, i'd far rather see folks put effort into introducing shift-tokens to allow extending the number tokens available. while there is some effort involved in implementing and testing, it places mmbasic on a far more secure footing moving forwards, and i suspect may untimately simplify the interpreter. given the availability of the mmbasic source and ease of building the win32 version of mmbasic, why don't a few of the forum members get together and tackle the project? the win32 version is an ideal framework to work with, as it shares a common code base, and developing for a desktop platform makes bugs much easier to track down. the results could then be passed back to geoff to roll into the release versions for PIC32, arm, etc - i am reasonably confident that any assistance with the programming effort would be appreciated. just my opinion, mind you. cheers, rob :-) |
||||
| lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3470 |
Re debugging, MM.LineNo (so a print line of debugging info could say where it was); also: And RUN (delay nnn, [start at nnn, [run for nnn] ] ). RUN without parameters would function as it does now. RUN(100) would continue at the next line without clearing variables with a delay of 100 ms between lines. RUN(0) would continue at the next line with no delay. RUN(0,0,10) would continue at the next line with no delay for 10 lines. RUN(0,55) would continue at line 55. The third parameter would require counting lines from the restart point. Lexically it could completely replace both RUN and CONTINUE. Paul in NY PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
CircuitGizmos![]() Guru Joined: 08/09/2011 Location: United StatesPosts: 1427 |
Is CPU a single token? Or is CPU SLEEP a token and CPU RESTART another token? If CPU is a single token and then the remainder of the command is parsed, then adding CPU RALPH wouldn't consume another token, right? If so, can the code parser be modified to see MM. as a single token, and parse VER DEVICE$ ERRNO etc as continuations of the command? Edit: My point being that would free up a few tokens, if that is how it works. My opinion is that functions, cfunctions, and the library are great at extending the language. Micromites and Maximites! - Beginning Maximite |
||||
| seco61 Senior Member Joined: 15/06/2011 Location: AustraliaPosts: 205 |
Hi Peter. Another option that comes to mind. Could the additional functions/commands be implemented in a way similar to cfunctions/csubs (but inbuilt)? Whilst this would not tokenise them, could your implementation effectively pre-populate the relevant tables with the entries? I hope this makes sense... Also, with any of the current commands/functions that are the same (ie BLIT/SPRITE, SPC$/SPACE$), could they be tokinised to the one token associated with the preferred name and the manual updated to state that this will be the case and the LIST command will use the preferred name. This will maintain backward compatibility... Regards Gerard (vk3cg/vk3grs) |
||||
| seco61 Senior Member Joined: 15/06/2011 Location: AustraliaPosts: 205 |
Hi Peter. As mentioned in the string allocation thread, a suggestion for some mechanism to use a substitute name for an expression that would be substituted in as a statement is interpreted (a sort of basic (pun not intended ) macro facility).DEFINE name1=st(0) DEFINE name2=st(0+i) Similar to using CONST but evaluated as each statement uses it (ie when it is interpreted it replaces the name with the expression, like a "C" #define macro). Though of course it could be used for any substitution, not just array elements... Regards Gerard (vk3cg/vk3grs) |
||||
| JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 4133 |
DEFINE name1=st(0) DEFINE name2=st(0+i) I suppose a Function might overall be "good enough" FUNCTION Name1$ Name1$ = st$(0) END FUNCTION or some such. John |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10572 |
This can't work as described, other than as John says by using a function. The only way it could be done is as part of the load process so that the expansions are done at the same time as the program is tokenised. This would though mean that the EDIT command would see the "expanded" version. This would be OK if you always do development on a computer using MMEDIT or similar but if you set up "defines" when using the EDIT command they would always be seen as expanded the next time into the editor. |
||||
| Paul_L Guru Joined: 03/03/2016 Location: United StatesPosts: 769 |
) macro facility).DEFINE name1=st(0) DEFINE name2=st(0+i) Similar to using CONST but evaluated as each statement uses it (ie when it is interpreted it replaces the name with the expression, like a "C" #define macro). Hi Gerard, I think you're looking for the kind of "macro expansion" provided in dBase, Clipper and FoxPro. In those languages placing an expression within parenthesis or following an "&" indicated to the interpreter that the expression should be "expanded" or calculated separately as if it was submitted from the command line. I believe that MMbasic can do something similar using the EVAL() command. Paul in NY |
||||
| seco61 Senior Member Joined: 15/06/2011 Location: AustraliaPosts: 205 |
Hi All. Again, thank you for the thoughtful replies. I always do my development on an external editor, so do not use the EDIT command. So in my case the result would be what I want and expect , but as you mention it may confuse issues for those who use EDIT and also attempted to use this facility. If it were to happen it would need to be well documented that this is the case.Functions are an option, I will investigate to see the overall affect on code size and speed. Whilst EVAL will certainly perform runtime evaluation (and I do use this very useful facility at times), it would not help with the program readability problem described earlier in the string allocation thread (combining multiple string (or integer/float) variables into arrays to save RAM). Regards Gerard (vk3cg/vk3grs) |
||||
| JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 4133 |
Maybe switch to a CPU with more RAM. John |
||||
| robert.rozee Guru Joined: 31/12/2012 Location: New ZealandPosts: 2464 |
i can think of a few mechanisms to allow packing multiple strings into a smaller RAM footprint, ranging from the simple to the convoluted. they are all based upon the observation that the following piece of code respects the length specified - even if a full 256 bytes are allocated behind the scenes: Dim A$ length 20 For I=1 To 30 A$=A$+"*" If I>15 Then Print I, A$ Next I > > RUN 16 **************** 17 ***************** 18 ****************** 19 ******************* 20 ******************** [5] A$=A$+"*" Error: String too long > so all we really need to do is provide a mechanism for NOT allocating any memory to hold the contents of A$, while instead allowing the address of A$'s data to be set to a pre-allocated block of memory. this can be accommodated by introducing just one new keyword: ABSOLUTE. DIM DB$(5) length 20 ; allocate space for 6 strings DIM A$ length 20 ABSOLUTE DB$(0) DIM B$ length 20 ABSOLUTE DB$(1) DIM C$ length 20 ABSOLUTE DB$(2) DIM D$ length 20 ABSOLUTE DB$(3) DIM E$ length 20 ABSOLUTE DB$(4) DIM F$ length 20 ABSOLUTE DB$(5) the lengths should all match, otherwith things could overlap. or ABSOLUTE could pick up the string length from DB$. and it also becomes possible to do some interesting things if DB is instead made, for instance, an array of integers. it all depends on how tightly one wishes to tie down the syntax and error checking. geoff: does this idea have any merit? it is essentially borrowed from the syntax used by Delphi/Turbo Pascal. cheers, rob :-) |
||||
| CaptainBoing Guru Joined: 07/09/2016 Location: United KingdomPosts: 2171 |
Disagree with most of this I'm afraid... doubly-so in light of the almost full up statement/function token table. Ways to save RAM have been shown. OK, it isn't neat but the need to use them will be really rare I bet. I have one fairly complex system with an uptime of 4500 hours... here is its memory report: 2019-05-17 12:59:15 System UpTime 07-11-2018 16:37:17 + 4580 Hours 2019-05-17 12:59:15 MMBasic 5.02 (Geoff Graham) Flash: 30K (49%) Program (1302 lines) 1K ( 1%) 9 Saved Variables (139 bytes) 29K (50%) Free RAM: 14K (27%) 81 Variables 10K (18%) General 26K (55%) Free 2019-05-17 12:59:15 Hardware: Micromite MkII, M590E, ESP8266, CH340G This is a busy system that exposes an HTTP API, internet access and Text messaging... and still 55% free ram. Just go to a bigger 'mite if you need more RAM. Don't risk changing the inner working to address a rare requirement - seriously, how many times has 50K not been enough? Remember these are microcontrollers... If you need greater capability, think of using an ST variant or MMX or Pi - recent changes here have completely removed the maximum program size, limited now only by the capacity of your SD Card and a filing system ready to go means no need for VAR SAVE etc. |
||||
| Geoffg Guru Joined: 06/06/2011 Location: AustraliaPosts: 3308 |
That would work, although a simpler option would be to let the interpreter handle the issue by packing multiple smaller strings into one page of RAM (256 bytes). This would make string handling more complex within the interpreter but at least it would be transparent to the user (other than the LENGTH parameter). However, is this really necessary? Generally programs have very few strings and the Micromites have far more RAM than is needed. For example, on the MM+ you can define something like 250 strings, 250 floats and 250 integers and still have some free RAM left over - that would be a mega program. If you could pack multiple string variables into a single page of RAM you could have thousands of variables (and an unimaginably complex program). I understand that Gerard was running out of RAM because he had a lot of GUI elements and arrays but probably the space used by non arrayed string variables would not be that significant in the larger scheme of things. EDIT: I completely agree with CaptainBoing Geoff Geoff Graham - http://geoffg.net |
||||
| seco61 Senior Member Joined: 15/06/2011 Location: AustraliaPosts: 205 |
After digesting how memory is allocated, I have been able to revise the program to continue working on the old MM2+. I also combined quite a few strings into a string array and managed to maintain readability using the following type of construct: const can20=0,can30=1,can40=2,can41=3 etc dim string bms(20) length 8 bms(can20)=expression This particular program has over 400 variables, 3000 lines of code and uses 3 serial ports to communicate with a CAN bus, a RS485 bus as well as an ESP8266. It manages a solar and battery system with 9 inverters, a 16 cell 100AH LiPO4 battery bank and its BMS and solar regulators attached to 120 solar panels. The information is uploaded to the cloud and is accessed by an Android App I wrote that shows current state and also allows updating some of the operating parameters. The code can run on both the MM2+ as well as the H7 - but as it was originally developed for the MM2+ and is in situ I did not want to have to build a new case to hold the larger H7 board. And of course it is only after converting to the GUI based controls that I had the memory issue... Regards Gerard (vk3cg/vk3grs) |
||||
| robert.rozee Guru Joined: 31/12/2012 Location: New ZealandPosts: 2464 |
probably not. one can create 788 simple numeric variables (ie, float or integer) before running out of memory. or 150 or so simple strings. i wrote a program to test the number of numeric variables a while back when there was some discussion about how the variable table worked, and was pleasantly surprised. plus, one can always use the oft forgotten ERASE command to recover the memory used by variables that are no longer required. provided OPTION EXPLICIT is not used, the (simple) variables will then just be recreated next time they are used. i have used ERASE to good effect in the past when wanting to repeat the same block of code twice, unaltered, in the one program to repeat a speed test that DIM'ed an array. cheers, rob :-) |
||||
| JohnS Guru Joined: 18/11/2011 Location: United KingdomPosts: 4133 |
As I recall (rather dimly) some BASICs (*) used FIELD to create named sub-strings of a larger string, which might be another work-around. It worked, but I never liked it. (*) such as DEC's BASIC-PLUS / BASIC-PLUS-2 John |
||||
| sawasdee01 Newbie Joined: 23/12/2016 Location: United KingdomPosts: 33 |
I would like to preface this post with the admission that I know nothing about the 'inner' memory model for MMX or the ST variant of Micromite - so, if there is some confusion about the role of Flash or Ram in this respect.... apologies. I have had two instances where I had to take steps to reduce the memory footprint of the program that I was developing for the MMX+ and I think that I will shortly be encountering the same issue with my STM32L432 based smartwatch:- ![]() In both previous and the now likely third instance, the GUI components take up a very considerable percentage of both the 'lines of code' aspect of memory and also the number of variables used. Additionally, I tend to use bitmap objects and various fonts to bring the end result into the realms of what is expected. Despite using some tricks to limit memory size, such as chopping unwanted characters out of the fonts that I have created, it is not at all unusual for me to burn up 10k bytes + in fonts and bitmap images. A quick look at the front screen of the watch and you will see; the Battery use bitmap (there are 5 battery images, one for each 20% of battery utilisation), the flashing heart bitmap, the super sized time font (numbers only) and the GUI buttons along the bottom. Yes, I could do some trickery to use just one Battery image and then dynamically draw inner battery gauge bar - but that would increase lines of code and possibly reduce inner loop state machine responsiveness. Additionally, on the MM+, I tend to build the entire graphical environment when the application first starts and then just use the PAGE command to switch to the relevant screen, as required. My guess is that fewer graphical objects could be used if I destroyed and then re-built each screen, as necessary - but, once again, this would be at the cost of more lines of code and inner loop responsiveness. I have never considered strings to be a problem whatsoever. I tend to use the LENGTH attribute quite a bit and I also keep most string variables as being LOCAL, where possible. I admit to being quite lazy with global string constants and tend to define them using something like DIM Fred$ = "Help!". I sometimes wonder whether a better approach might me to declare them as type CONST and then assign to a LOCAL string variable inside a subroutine (?). I would be interested to understand what works best for the memory model. My guess is that defining global string constants as type CONST is going to be much easier on RAM - but this assumes that FLASH CONST isn't dumped into RAM during startup. Finally, on to the question at hand - what to do with the extra, unused flash. While it is way above my pay grade to suggest solutions - here are some poorly thought through wish list items:- 1. Some way of pushing all of those bitmaps, additional fonts (and maybe, even C functions) up into the 'outer space' on-board MCU flash - ie without using an external flash chip. There might even be a way of doing this without extending the MM command set - eg a pre-processor / MMEDIT / Utility sort of thing. 2. Easy one - a new chapter in the existing Micromite Manuals entitled 'How to save and manage Memory'. On first glance, this looks to be a small topic - but once past the simple stuff, like setting up MMEDIT correctly, so that it chops off comments, defining multiple variables on the same line, using the LENGTH keyword, etc - there is a mountain of stuff that is much more nuanced. For instance, does DIM Fred% = 10 use less memory that DIM No_Of_Freds_Fingers% = 10? Additionally, do both of these variables execute at the same speed ? 3. I really like the LIBRARY command in Micromite Basic and I wonder whether it could be extended with something that looks a little bit like the FROZEN utility in micropython - so that the LIBRARY could be (optionally) sent up into the 'outer space' on-board MCU flash. Interestingly, if combined with a command to disable LIBRARY LIST, this approach might go a long way towards providing the language extensibility mentioned by Peter - as it would allow new commands to be added to this 'invisible, out of core flash' area so that they behaved as though they were part of the normal MM Basic offering. This approach might also be necessary to support future heavily integrated CPU + network products - eg Nordics Nrf52840 ARM Cpu with Bluetooth 5.0 BLE 1Mb Flash, 256k Ram. One could imagine a Nordic Softdevice (the core Bluetooth Stack and API) living up in this secured Flash area, along with a MMBASIC wrapper to make the Bluetooth API available to MMBasic programmers - how cool would that be? 4. Does anyone remember the BBC Basic 'CHAIN' command? 5. Final one and a bit of a cheat, as this is probably only loosely related to the 'what do we do with the spare flash' question - I really like the GUI commands and integrated touch screen support of MM+. If there is any way that this could be implemented on the STM32L series, it would be fantastic. I have got my touch screen driver working as an ST Basic interrupt routine and it works - but it just isn't the same as the 'real deal'. Best wishes to all. Sawasdee |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |