Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 14:36 20 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : MMBasic implementation details

Author Message
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3831
Posted: 11:49pm 29 Nov 2019
Copy link to clipboard 
Print this post

Hi folks,

I don't suppose anyone knows of a document describing how (the algorithms for) MMBasic performs variable and procedure lookup, handles local variable memory, handles string memory, etc?

I'm pushing the CMM quite hard with my ZMachine implementation and these sort of details would prove useful in optimising for size and speed ... or at least they would for a "real" retro computer, they might be irrelevant for the CMM.

Two specific things I'm wondering about are:

Does declaring a Local (or procedure/function parameter) X introduce a global variable X? It does on the BBC micro.

When a new longer string is assigned to X$ is the previous space recovered? On the BBC micro it is lost ... well strictly speaking there is a little leeway, whenever X$ is resized it gets a few more characters than it needs to allow for limited growth.

I have sent an access request for the source code and probably insufficient time has passed for Geoff to act on it, but there is no guarantee I will be able to understand it when it arrives ;-)

Regards, Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5884
Posted: 01:19am 30 Nov 2019
Copy link to clipboard 
Print this post

To answer your two questions.

A LOCAL variable is discarded when the SUB/FUNCTION ends. LOCAL variables are not global.

You need to be careful with any variable passed to SUBs. It is easy to accidentally change its value and that would be reflected in the global variable.
A handy feature but can surprise some.

Strings are 255 bytes long. Even if you assign a shorter length, the interpreter still assigns the full 255 bytes (plus one for the length).

Once declared, the same 256 bytes are reused every time you reassign to that variable.

With arrays, you can add the LENGTH parameter which limits the maximum size of each element and can save a lot of memory.

This is often used when size matters.

Jim
VK7JH
MMedit   MMBasic Help
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1094
Posted: 01:26am 30 Nov 2019
Copy link to clipboard 
Print this post

Tom,

Regarding your two specific questions, Geoff or Peter will be able to answer your querys in detail but my understanding is as follows:-

  Quote  Does declaring a Local (or procedure/function parameter) X introduce a global variable X? It does on the BBC micro.


Using the LOCAL statement inside a sub or function creates a variable that is only available within the routine and the memory is recovered on exit from the routine.
Repeated calls to the sub or function won't incrementally use more memory as the memory is recovered on each exit.
See the 4.5C Language Manual pages 18 and 19

  Quote  When a new longer string is assigned to X$ is the previous space recovered? On the BBC micro it is lost ... well strictly speaking there is a little leeway, whenever X$ is resized it gets a few more characters than it needs to allow for limited growth.


Strings are assigned 256 bytes regardless of the length of the actual content - adding new content to a string eg. a$ = a$ + "more data" does not use any more memory. Integers and floats both use 8 bytes - assigning a new value does not use more memory.

If you decide to try to get "into" the source, the book "Programming 32-bit Microcontrollers in C" by Lucia Di Jasio is an excellent intro to the PIC32 family and programming in C.

Also, not sure if it is still in print but "Software Interpreters for Microcomputers" by Thomas C. McIntire although targeted at 8 bit micros and assembly language, it is still a usefull explanation of how interpreters work.

Doug.
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
ceptimus
Senior Member

Joined: 05/07/2019
Location: United Kingdom
Posts: 130
Posted: 04:55am 30 Nov 2019
Copy link to clipboard 
Print this post

  panky said   Integers and floats both use 8 bytes - assigning a new value does not use more memory.


I think only four bytes per integer or float on the 32-bit platforms like the Maximite.  I'm referring to the memory that holds the actual values here, not the overhead for storing the variable's name etc.

A technique I use to store big arrays of bytes is to peek and poke the memory assigned to an array of numbers.  If you DIM A(10), then with the default option base 0 you've allocated 11 array elements (0 to 10) which occupy 44 contiguous bytes of memory on the CMM. You can access this "44 element byte array" by b=PEEK(VAR A(0), n) and POKE VAR A(0),n,b where n is the "array index" (0 to 43 in this example) and b is the byte value to store or retrieve.  If you use this technique then be aware that there is no bounds checking, so in this example it would be up to you to guarantee that n would not exceed 43.

On platforms which use 64-bit integers and floats, such as the Armmites and DOS version, then you have eight bytes per array element allocated, but if you want your MMBasic code to be portable to all 'mite platforms and memory is not too tight, you can just pretend there are only four - and then the peeked/poked values will only occupy the memory of the first half of the number array.
.
Edited 2019-11-30 15:03 by ceptimus
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3165
Posted: 07:30am 30 Nov 2019
Copy link to clipboard 
Print this post

  thwill said  I don't suppose anyone knows of a document describing how (the algorithms for) MMBasic performs variable and procedure lookup, handles local variable memory, handles string memory, etc?

Sorry, there is not.  Probably looking at the source would be the best bet but that is not easy to decipher.

  thwill said  Does declaring a Local (or procedure/function parameter) X introduce a global variable X? It does on the BBC micro.

There is one variable table which includes the variable's name, value (if float or integer) or pointer to a block of RAM (if string or array) and various attributes.

The interpreter does a linear search of this table when it needs to lookup a variable.  This sounds inefficient but due to the way that it is implemented it is quite fast for typical programs.  Local variables are also stored in this table but they are then removed when the subroutine/function exits.

Some time ago I did extensive profiling of the interpreter while running typical programs and, as a result, added optimisations for the slow spots.  This included things like pre scanning the code to locate all subroutine/functions, caching for...next loops, etc.  There is not a lot of effective optimisations left that can be done as MMBasic is mostly limited by being an interpreter and needing to step through the code.

  thwill said  When a new longer string is assigned to X$ is the previous space recovered? On the BBC micro it is lost ... well strictly speaking there is a little leeway, whenever X$ is resized it gets a few more characters than it needs to allow for limited growth.

As others have pointed out, the RAM allocated to a string is fixed at 255 bytes.  Any changes to a string are copied back to the same RAM allocated to the string in the first place.

This is very efficient (and fast) and most times there are only a few string variables and plenty of free RAM so it works well.  In a microcontroller it is just not possible to have dynamic string allocations along with garbage collection and all the other complications.

  thwill said  I have sent an access request for the source code and probably insufficient time has passed for Geoff to act on it, but there is no guarantee I will be able to understand it when it arrives ;-)

I have been having problems with the source requesting/sending mechanism om mmbasic.com so if you (or anyone else) does not receive a response within 24 hours please email me at projects@geoffg.net and I will send it directly to you.

Geoff
Geoff Graham - http://geoffg.net
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1985
Posted: 10:57am 30 Nov 2019
Copy link to clipboard 
Print this post

  thwill said  
Does declaring a Local (or procedure/function parameter) X introduce a global variable X? It does on the BBC micro.


didn't know that (never played with Acorn machines) - so not local at all. That's horrible. I bet it caused more trouble than it helped.

  TassyJim said  
You need to be careful with any variable passed to SUBs. It is easy to accidentally change its value and that would be reflected in the global variable.
A handy feature but can surprise some.


I got badly bitten by that one a while back. Kept me up for few hours - but I learnt.

For reference, anyone who knows their VB, Variables passed to Subs/Functions in MMBasic is equivalent to

Sub MySub(ByRef x) so the pointer to the variable is passed and not the value (ByVal x)

you still get x to play with but if you tweak it inside your Sub, it will destroy the original value. If you are going to have to chop it around a bit, you need to make a Local copy. This is intentional which is why you have a choice.

As Jim pointed out, all non-array string variables are created equal, this speeds things up quite a bit and removes the need for a garbage collect. I do Poke/Peek directly into strings for speed in some repetitive things. For instance

x=Peek(Var a$,n+1)

is faster than

x=Asc(Mid$(a$,n,1))

Likewise I might have a loop building up a string;

For n=1 to 255:Poke Var a$,n,v:Next ' put the chars in the memory space
Poke Var a$,0,255 ' set the string length

is faster than

For n=1 to 255:a$=a$+Chr$(v):Next ' same but less cryptic.


Only by a fraction but if you are whizzing round a fast loop (i.e. with no delays) then these tricks can shave a few milliseconds off the whole shebang.

Like most grey-beards here, I come from an assembler background and I still think in those terms especially on the subject of memory... I sometimes spend too much time trying to be clever and my advice is to stick to the language whenever you can, only start mucking about like this when it becomes absolutely critical to try and make things faster - in a few months you might look back at stuff you wrote and ask yourself what it all means.  
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1985
Posted: 11:07am 30 Nov 2019
Copy link to clipboard 
Print this post

  Geoffg said  
The interpreter does a linear search of this table when it needs to lookup a variable.  

Quick question Geoff...
I determined for my self a while back that long variable names will slow a program so keep them where they belong, but is there an advantage over low-in-the-alphabet names? e.g. is  "A" found in the table quicker than "Z" or are they added to the table in the order they get assigned?

cheers
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3165
Posted: 11:56am 30 Nov 2019
Copy link to clipboard 
Print this post

The variable table is built in the order that variables are declared or discovered and it is searched in that order.  The starting letter is not significant but the search will take longer if all your variables have the same starting sequence (ie, MyVariable1, MyVariable5, etc) as the interpreter would have to check many more characters before finding the difference between the variable names.

As you have alluded to... programmers should not obsess with optimising their code for speed.  An interpreter is inherently slow anyway and if you need to optimise your BASIC program it is an indication that it is time to learn C and write a CFunction.

MMBasic + CFunctions is the perfect solution as you have the ease of use of an interpreter with the speed of compiled C functions.  BUT development of CFunctions is much harder (there is no such thing as a free lunch ).

Geoff
Geoff Graham - http://geoffg.net
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3831
Posted: 03:06pm 30 Nov 2019
Copy link to clipboard 
Print this post

Thanks everone,

Lots of useful insights and things to follow up on - though I do have a few more grey hairs than I have been credited for ;-)

  "panky" said  If you decide to try to get "into" the source, the book "Programming 32-bit Microcontrollers in C" by Lucia Di Jasio is an excellent intro to the PIC32 family and programming in C.
Also, not sure if it is still in print but "Software Interpreters for Microcomputers" by Thomas C. McIntire although targeted at 8 bit micros and assembly language, it is still a usefull explanation of how interpreters work.


Thanks Doug, I will add them to my reading list. The latter is out of print and most of the copies for sale seem to be hiding in the US, hopefully AbeBooks will provide one for me.

  "TassyJim" said  Strings are 255 bytes long. Even if you assign a shorter length, the interpreter still assigns the full 255 bytes (plus one for the length).
Once declared, the same 256 bytes are reused every time you reassign to that variable.
With arrays, you can add the LENGTH parameter which limits the maximum size of each element and can save a lot of memory. This is often used when size matters.


OK, so if I'm being parsimonious about memory there are circumstances where I might want to use a 1 element String array rather than a simple String variable?

  "ceptimus" said  A technique I use to store big arrays of bytes is to peek and poke the memory assigned to an array of numbers ...


I'm using exactly this technique for the Z-machine memory, though it is more like 10,000 elements rather than 10.

  "Geoffg - the man himself" said  There is one variable table which includes the variable's name, value (if float or integer) or pointer to a block of RAM (if string or array) and various attributes. The interpreter does a linear search of this table when it needs to lookup a variable.  This sounds inefficient but due to the way that it is implemented it is quite fast for typical programs. Local variables are also stored in this table but they are then removed when the subroutine/function exits.


Does this mean that internally Local variables have a prefix so they don't clash with the globals (and locals from lower down the stack) when you do a lookup?

For info (and apologies if I'm teaching anyone to suck eggs) my understanding of the BBC micro BASIC interpreter (sorry to harp on, it's just the only one I have any technical familiarity with) is:

1. It maintains separate linked list per initial variable starting letter so as to be more speed efficient than searching a single monolithic table. In addition integer variables @% and A%-Z% are special and have fixed locations in memory so don't require the variable table to be traversed at all.

2. When a Local statement declaring a variable ABC is encountered the following occurs:
a) If global ABC does not exist then create it with a default value, i.e. 0, 0.0 or ""
b) Push the value of global ABC onto a stack
c) Set global ABC to the default value
d) ABC can now be used as "local"
e) When exiting the function/procedure restore the previous value for ABC by popping it off the stack.

  "CaptainBoing" said  ... didn't know that (never played with Acorn machines) - so not local at all. That's horrible. I bet it caused more trouble than it helped.


As described above it seems to work. My understanding is the only time it was a problem was when an Error was thrown in which case the stack wasn't unrolled and the original values of the global variables restored. I believe the BBC had the "most powerful" BASIC of the 8-bit era, helped by the fact that its 6502 and memory ran twice as fast as anyone elses.

Speaking of problems when an Error is thrown, I'm having an issue with MMBasic reporting "Error: Variable already Local" if I try to call one of my diagnostic functions in command mode after an Error has been thrown ... I'm guessing MMBasic doesn't unwind the variables after an Error either? Is there anything I can do about this?

  "Geoffg" said  As you have alluded to... programmers should not obsess with optimising their code for speed.  An interpreter is inherently slow anyway and if you need to optimise your BASIC program it is an indication that it is time to learn C and write a CFunction.


I can program in C, but my understanding is that CFunctions are not an option with the Colour Maximite and MMBasic 4.5C, am I wrong?

Regards,

Tom
Edited 2019-12-01 01:53 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 05:47pm 30 Nov 2019
Copy link to clipboard 
Print this post

  thwill said  

As described above it seems to work. My understanding is the only time it was a problem was when an Error was thrown in which case the stack wasn't unrolled and the original values of the global variables restored. I believe the BBC had the "most powerful" BASIC of the 8-bit era, helped by the fact that its 6502 and memory ran twice as fast as anyone elses.



Hi

As I remember, Acorn Atom use an signetics 2650 proc and not an 6502 or z80 (https://en.wikipedia.org/wiki/Signetics_2650). The "big" diff between 2650 and 6502 is an indirect and indexed adressing mode memory. For that it is more speedly progs.
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3831
Posted: 06:00pm 30 Nov 2019
Copy link to clipboard 
Print this post

  goc30 said  
As I remember, Acorn Atom use an signetics 2650 proc and not an 6502 or z80 (https://en.wikipedia.org/wiki/Signetics_2650). The "big" diff between 2650 and 6502 is an indirect and indexed adressing mode memory. For that it is more speedly progs.


I'm 99% certain you are misremembering, the Acorn Atom appears to have been 6502 based like all (?) the early Acorn machines.

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5884
Posted: 08:02pm 30 Nov 2019
Copy link to clipboard 
Print this post

  Quote  OK, so if I'm being parsimonious about memory there are circumstances where I might want to use a 1 element String array rather than a simple String variable?

The overheads in pointing to an array make it unlikely to help and I think the memory is allocated in 256 byte chunks anyway.

I make extensive use of MID$ to extract short strings from longer ones to save space and keep like elements together as in a list from other languages.

  Quote  weekdays$ = "Sunday   Monday   Tuesday  WednesdayThursday Friday   Saturday "

FOR n = 0 TO 6
PRINT "Today is ";MID$(weekdays$,n*9+1,9)
NEXT n


Jim
VK7JH
MMedit   MMBasic Help
 
ceptimus
Senior Member

Joined: 05/07/2019
Location: United Kingdom
Posts: 130
Posted: 09:14pm 30 Nov 2019
Copy link to clipboard 
Print this post

BBC Basic used multiple linked lists for variable lookup. One list for each starting letter.  That was one reason why it was faster than many other Basics in its day. Also it had the special fast integer variables A% to Z% (plus @% I think) that didn't need any lookup at all.
.
Edited 2019-12-01 07:17 by ceptimus
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3831
Posted: 09:23pm 30 Nov 2019
Copy link to clipboard 
Print this post

  ceptimus said  BBC Basic used multiple linked lists for variable lookup ...


There seems to be an echo in here ;-)
Edited 2019-12-01 07:24 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 12:51am 01 Dec 2019
Copy link to clipboard 
Print this post

  thwill said  

I'm 99% certain you are misremembering, the Acorn Atom appears to have been 6502 based like all (?) the early Acorn machines.

Tom


I am sure of what I say. At the time (1982) I was taking care of a computer club, and we had bought the acorn kit, and it was me who had mounted it. It seems that Acorn had later developed a "light" version based on 6502 (and after that a "Risc" version-1987), hence your confusion
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3831
Posted: 07:39am 01 Dec 2019
Copy link to clipboard 
Print this post

  goc30 said  I am sure of what I say. At the time (1982) I was taking care of a computer club, and we had bought the acorn kit, and it was me who had mounted it. It seems that Acorn had later developed a "light" version based on 6502 (and after that a "Risc" version-1987), hence your confusion


If you say so, though the internet does not seem to support you regarding the Atom.

The "Risc version" you refer to is probably the 32-bit Archimedes and it's descendants which followed the 8-bit BBC Master (ignoring copros for the Beeb they didn't release a 16-bit computer.)

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 425
Posted: 06:27pm 01 Dec 2019
Copy link to clipboard 
Print this post

Hi thwill

In the 80's (82 to 85) there was in Paris a "world" center ( ) of computer science. On the ground floor we had the personal computers, the French manufacturing but also Pet, TRS80, Apple, and .. Acorn, and on the 1st floor there were, among other things, 2 LISP machines. The building also hosted a small association of some members, called "Medecins sans Frontieres". The center was open 24 hours a day. When we passed after midnight, there were only crazy dev's and other novice hackers, and on the ground floor, the only computer turned on and used was the Acorn. This is to say the interest for this microcomputer
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3831
Posted: 08:20pm 01 Dec 2019
Copy link to clipboard 
Print this post

  goc30 said  Hi thwill, in the 80's (82 to 85) there was in Paris a "world" center ...


Cool ... though we appear to have strayed far off topic ;-)

Tom
Edited 2019-12-02 19:22 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024