Program memory space available

vansav8r

New Member
I was wondering, I have a program running on an 18-M2 chip, it is 250 lines of code and about 5.5Kb of disc space when stored. Is there any way of monitoring or displaying the percentage of available program memory remaining or used when writing the program?
I'm always expecting the next line of code to result in a FULL statement.
Thanks Jim
 

Buzby

Senior Member
The 'Syntax Check' button tells you how many bytes used, and the total available.

The size on disk has no relation to the size of the compiled code.
 

techElder

Well-known member
Jim, I just used syntax check on a program with about 350 lines on screen. Syntax Check showed "1915 out of 2048 bytes".

The "Display Pre-processor Output" option is checked on my workspace. It shows close to "real" code and for this program produced about 1000 lines.
 

techElder

Well-known member
BTW, Jim, if you'll post your code, we'll likely be able to squeeze some out of it. :D

It would be appropriate if you read this "Read Me First!" forum post before you practice posting code. It contains the following suggestion.

If you add any PICAXE BASIC program code to your post, please enclose it within
Code:
tags e.g.
[code]
high 1
pause 1000
low 1
[/code]
 

lbenson

Senior Member
Note from Manual 2 on "RUN slot":

"The 14M2/18M2/20M2 have two completely separate internal program slots. The 08M2 only supports slot 0."

So if you run out of space on your 18M2, you can use an whole new 2048-byte slot with PE6 (some jumping through hoops required).
 

AllyCat

Senior Member
Hi,

The size on disk has no relation to the size of the compiled code.
Nor even the size on the screen. It depends very much on programming style and the commands used.

It should be possible to fit 500 - 1000 lines of well-structured program code into a single M2 slot. But use too many PWMDUTY, BINTOHEX, SERTXD, READINTERNALTEMP and SWAPs and you might not even get to 100 lines. Those 5 commands alone can use around 150 bytes, even before you start using MACROs. ;)

Cheers, Alan.
 

hippy

Technical Support
Staff member
"Gone in Five Lines" ...

Code:
#Picaxe 20X2
#Define x1  SerTxd("XXXXXXXXXXXXXXXX")
#Define x8  x1 x1 x1 x1 x1 x1 x1 x1
#Define x64 x8 x8 x8 x8 x8 x8 x8 x8
x64 x64 x8 x1 x1 x1 x1 x1
 

Buzby

Senior Member
There's 4 bytes left !.

Come on hippy, you should be able to fit a few super-duper bit twiddling algorithms in there.

Cheers,

Buzby ( + fine malt )
 

darb1972

Senior Member
"Gone in Five Lines" ...

Code:
#Picaxe 20X2
#Define x1  SerTxd("XXXXXXXXXXXXXXXX")
#Define x8  x1 x1 x1 x1 x1 x1 x1 x1
#Define x64 x8 x8 x8 x8 x8 x8 x8 x8
x64 x64 x8 x1 x1 x1 x1 x1
Ok, I'm a code "dummy". I'll bite. Can you please explain how that's even possible?
 

hippy

Technical Support
Staff member
Can you please explain how that's even possible?
The first #define creates an alias with name 'x1' which whenever used will mean it is replaced by the SERTXD. A program with a single 'x1' will therefore be the same as a program with a single SERTXD.

The second #define creates an 'x8' alias which is replaced by eight 'x1' names, which in turn are replaced by SERTXD. A program with a single 'x8' will therefore be the same as a program with eight 'x1' or eight SERTXD.

Likewise the third #define. A program with a single 'x64' will be the same as a program with eight 'x8', sixty four 'x1', or sixty four SERTXD.

One way to think of it is 'x1' is a box with an Easter egg in it. An 'x8' is a box with eight 'x1' boxes in it, an 'x64' a box with eight 'x8' boxes in it. If you order one 'x64' box you will have 64 Easter Eggs when you have finally unpacked everything.

The final line just creates a number of box orders, bringing you as many Easer eggs as you can hold in your pantry.

Technically there should be colons included to separate out SERTXD commands in the full expanded program but, in this case, we rely on the pre-processor and compiler sorting things out when they are not included.
 

darb1972

Senior Member
Hello Hippy

Ok, I think I understand all of that. Thanks for the explanation. What I still don't understand is how the five lines use up all of the program space? Is it all of the "unpacking" of those (dark chocolate?) Easter Eggs??? In other words, does the DEFINE statement appear very compact to the programmer, but the complier then recognises this statement in its full unpacked layout??? Is that what uses up all of the memory?
 

darb1972

Senior Member
I've just reread your reply (for about the third time) and I think the penny has now dropped. I see what you are doing.

I gather then that the SERTXD command is extremely memory hungry (classed as "frungry") and this is where the program memory is used. Correct? Allan also listed this command along with others suggesting they use a considerable amount of memory. I gather SERTXD uses a fair amount of space because of the process/steps used to get data out to the terminal screen. Correct???
 

inglewoodpete

Senior Member
I gather then that the SERTXD command is extremely memory hungry (classed as "frungry") and this is where the program memory is used. Correct?
Perhaps not "extremely" memory hungry. SerTxd("XXXXXXXXXXXXXXXX") takes about 30 bytes. The killer is when you replicate it 141 times: 141 x 30 = 4230 bytes
 

AllyCat

Senior Member
Hi,

I listed those 5 commands (in #7) because they could "typically" use 30 or more bytes in a "simple" program line and thus overflow an M2 program in less than 100 lines. Actually SWAP uses only around 15 bytes (which is surprising for such a simple function) and a SERTXD("X") uses 5 or 6 bytes. But as Pete and hippy have described, SERTXD can easily use 30 or more bytes.

But the key part of my post was "even before you start using MACROs". The SYMBOL command as used by hippy defines a "one line Macro" and it is that which can be very memory hungry. Hippy showed an extreme example by "nesting" them to use up the memory very rapidly. Note that this is a PE6 feature and AFAIK it's not possible to do the same with PE5. ;)

There is a more general point about the SERTXD (and SEROUT, etc.) commands: that "Print Formatting" often can be quite memory hungry. A list of Menu options, etc., will obviously use more than one byte for each character sent, but also the display of signed or "decimal" (fractional) numbers can need quite complex routines to handle the "nasty" details. For example, to overcome the automatic "leading zero suppression" and ensure that 1.05 (i.e.an integer divided by 100) is presented as "1.05" and not "1.5" (which is where the greedy BINTOHEX comes in).

Cheers, Alan.
 

techElder

Well-known member
...The SYMBOL command as used by hippy defines a "one line Macro" and it is that which can be very memory hungry. Hippy showed an extreme example by "nesting" them to use up the memory very rapidly.....
Not to get into a peeing match with Alan :D, Hippy's code was a nest of substitutions.

I am still not convinced that MACROs are useful (in general) with such limited memory. Creating a macro that is more than a simple substitution with parameter passing is extremely memory hungry if one designs the program to use the macro more than a few times. Remember that everything in the block is placed into the program every time the macro is referenced. We all agree that Hippy's substitution code above clearly demonstrates that.

I am convinced that next generation PICAXE processors will have more memory available, and that will make some coding techniques more palatable, albeit memory hogging.
 

hippy

Technical Support
Staff member
I gather then that the SERTXD command is extremely memory hungry
Not excessively but it can become so as the data output increases. Each extra character will usually use at least one more byte so a single SERTXD outputting a 60 character line for display can use over 100 bytes. Twenty such lines will almost fill an M2 memory.

Memory hungriness is mostly where the amount of memory used exceeds what might be expected from what is written as the command. "Return" can be expected to be about 1 byte, "High C.0" can be expected to be 2 bytes, "PulsOut B.0,10" can be expected to be 3 so there can be an expectation that "PwmDuty C.5,127" would be 3 and "BinToAscii w10,b1,b2,b3,b4,b5" might be about 7 but much more can be used than may be expected.

This is usually because things like BINTOASCII are effectively 'hidden macro commands' rather than native PICAXE commands, eg -

Code:
#Macro Hidden_BinToAscii(wvar,n1,n2,n3,n4,n5)
  n1 = wvar / 10000      + "0"
  n2 = wvar / 1000 // 10 + "0"
  n3 = wvar / 100  // 10 + "0"
  n4 = wvar / 10   // 10 + "0"
  n5 = wvar        // 10 + "0"
#EndMacro
With that in mind, that BINTOASCII uses 40 or so bytes becomes more easier to understand.

Likewise PWMDUTY can also be a 'hidden macro' which has to do a lot more than it may be expected on some chips; around 30 bytes to that in total.
 

BESQUEUT

Senior Member
I am still not convinced that MACROs are useful (in general) with such limited memory. Creating a macro that is more than a simple substitution with parameter passing is extremely memory hungry if one designs the program to use the macro more than a few times..
IMHO as long as it fit in program memory, macros are very usefull for readability and speed.
As long as it fit in memory, there is no reason to optimize code size.

But, your are true : as soon as it will not fit (even with multiple slots...), removing macros is a starting point for reducing code size, even if a subroutine is slower than a macro...
 

hippy

Technical Support
Staff member
I am still not convinced that MACROs are useful (in general) with such limited memory.
As BESQUEUT notes macros are not so much about saving memory as making programs shorter, easier to create, easier to debug, more readable, more understandable -

Code:
OutputVal:
  tmp = val & $01 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $02 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $04 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $08 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $10 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $20 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $40 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  tmp = val & $80 : If tmp = 0 Then : Low SDAT : Else High SDAT : End If : PulsOut SCLK, 1
  Return
Code:
#Macro OutValBit(value,bitMask)
  tmp = value & bitMask
  If tmp = 0 Then
    Low  SDAT
  Else
    High SDAT
  End If
  PulsOut SCLK, 1
#EndMacro 

OutputVal:
  OutValBit(val,$01)
  OutValBit(val,$02)
  OutValBit(val,$04)
  OutValBit(val,$08)
  OutValBit(val,$10)
  OutValBit(val,$20)
  OutValBit(val,$40)
  OutValBit(val,$80)
  Return
Exactly the same but likely to make fewer errors in writing the second and easier to understand. It might also lead to saving memory if one needs that ...

Code:
#Macro OutValBit(value,bitMask)
  tmp = value & bitMask
  Gosub Do_OutValBit
#EndMacro 

OutputVal:
  OutValBit(val,$01)
  OutValBit(val,$02)
  OutValBit(val,$04)
  OutValBit(val,$08)
  OutValBit(val,$10)
  OutValBit(val,$20)
  OutValBit(val,$40)
  OutValBit(val,$80)
  Return

Do_OutValBit:
  If tmp = 0 Then
    Low  SDAT
  Else
    High SDAT
  End If
  PulsOut SCLK, 1
  Return
Of course the original code could have been altered to work this way but the beauty of macros is that the 'OutputVal:' routine itself doesn't change no matter if written to creates inline code for speed or uses GOSUB to save memory.

And from that one can have a program which supports both, optimised for speed or optimised for memory use -

Code:
#Define OPTIMISE_FOR_SPEED

#Macro OutValBit_Fast(value,bitMask)
  tmp = value & bitMask
  If tmp = 0 Then
    Low  SDAT
  Else
    High SDAT
  End If
  PulsOut SCLK, 1
#EndMacro 

#Macro OutValBit_Slow(value,bitMask)
  tmp = value & bitMask
  Gosub Do_OutValBit
#EndMacro 

#IfDef OPTIMISED_FOR_SPEED
  #Define OutValBit(value,bitMask) OutValBit_Fast(value,bitMask)
#Else
  #Define OutValBit(value,bitMask) OutValBit_Slow(value,bitMask)
#EndIf

OutputVal:
  OutValBit(val,$01)
  OutValBit(val,$02)
  OutValBit(val,$04)
  OutValBit(val,$08)
  OutValBit(val,$10)
  OutValBit(val,$20)
  OutValBit(val,$40)
  OutValBit(val,$80)
  Return

#IfNDef OPTIMISED_FOR_SPEED

Do_OutValBit:
  If tmp = 0 Then
    Low  SDAT
  Else
    High SDAT
  End If
  PulsOut SCLK, 1
  Return

#EndIf
And if we wanted to optimise further, use direct outpt pin assignment rather than an IF, "outpinSDAT = value & bitMask", it would mean only change in the macro and/or GOSUB and we wouldd make that gain without having to alter the "OuputVal:" routine itself.
 

techElder

Well-known member
All valid points and why I've created extensive files with macros.

But the main point was that macros will be more useful with more available RAM.

What we really want is a subroutine call that includes parameter passing like a macro with local variables. I've done that with macros, but I had to reserve variables for those parameters.
 

darb1972

Senior Member
Hi all

This has been a very interesting thread. I wasn't the OP but I couldn't resist the urge to ask questions. I've learnt quite a bit (no pun intended) thus far. Thanks to all for the input.

This might seem like a dumb question/suggestion, but if (at times) a lack of available program memory space is a problem, couldn't one employ the use of an external EEPROM for additional space or does the read/write process slow things down too much??? Could an EEPROM just store Macros?

This is aside from Tex talking about additional RAM variables.

I hope I didn't just cause the average IQ to plummet to new lows. Most of this is way above my PICAXE pay grade. ��
 

hippy

Technical Support
Staff member
This might seem like a dumb question/suggestion, but if (at times) a lack of available program memory space is a problem, couldn't one employ the use of an external EEPROM for additional space or does the read/write process slow things down too much???
The X2 devices support external memory slots to do exactly that.

One could store data which commands any PICAXE what to do in EEPROM but the PICAXE would have to retrieve the data, determine what it means, then act on that.

Exactly what the PICAXE firmware does with internal memory and program code; it's entirely possible but in most cases it would be too slow to be useful as you suggest.

Could an EEPROM just store Macros?
Macro uses are expanded at compilation time, not at run time, so the answer is "no" in the sense I think you mean.
 

Buzby

Senior Member
Going back to hippy's 5-line filler, with pre-processor output switched on, you get a file like this :
PE6 Preprocessor 4.PNG

A strange thing is, if you run the original '5 line' code, it works as expected, and prints lots and lots of X's.
But if you try to run the pre-processor output, which you would expect to be valid, it crashes the pre-processor.
PE6 Preprocessor 3.PNG

Cheers,

Buzby
 

hippy

Technical Support
Staff member
But if you try to run the pre-processor output, which you would expect to be valid, it crashes the pre-processor.
I think that may be an issue with line length. The pre-processor seems to be having a problem with lines which are over 1000 or so characters wide.

Even though there is nothing to be expanded when the already expanded code is compiled, it still has to go through the pre-processor. That can be disabled in the Options -> Compiler settings.
 
Top