BX-24 stack problem? RAM usage?

Started by don_kinzer October 24, 2003
Greetings,

I'm working on an application that uses a BX-24 and I've been seeing
some strange problems that I suspect are caused by stack overflow.
I've been reducing String usage and I've also changed the string size
from 64 to 24 and those two changes have helped me get much further
than I was last weekend. If I change the string size back to 64,
parts that have been working cease to work - it crashes and resets.

I observed something today that I really don't understand. I have a
subroutine that is 3 levels down - Main calls sub1 which calls sub2.
In sub2 there's fragment that looks like this:

If (0 = 1) Then
Call Foo()
Call Bar("abcdef")
Call Bar("0123456789")
End If

Clearly, the body of the if-statement will never be executed. When
the program executes through sub2, it always crashes (restarts from
the top). Oddly, if I comment out the two calls to Bar() everything
works find. If I restore the two Bar() calls it again crashes.
These results are repeatable time after time with the same user
inputs in all trials. There are other calls to Bar() that get do
exercised in either case.

Obviously, the parameter to Bar() is passed by value so I would
expect it to use RAM whenever called. It appears, however, that some
RAM is used even for calls that are never executed, perhaps related
to the usage of the string constant.

Anybody have any ideas as to what is going on?

I have optimization turned on, by the way. Clearly, it doesn't
remove inaccessible code though. :(

Don in Portland, OR



From: don_kinzer <>

> [...]
> I observed something today that I really don't understand.
> I have a subroutine that is 3 levels down - Main calls
> sub1 which calls sub2. In sub2 there's fragment that looks
> like this:
>
> If (0 = 1) Then
> Call Foo()
> Call Bar("abcdef")
> Call Bar("0123456789")
> End If
>
> Clearly, the body of the if-statement will never be
> executed. When the program executes through sub2, it
> always crashes (restarts from the top). Oddly, if I
> comment out the two calls to Bar() everything works
> find. If I restore the two Bar() calls it again
> crashes. [...]

It's difficult to say without seeing the program in detail, but
I'd guess that in both cases there is a stack overflow.

If the stack overflows, the behavior of a program is totally
unpredictable, and it's possible that a small, innocuous change
could cause a big change in behavior, including the appearance of
normal operation.

In other words, if a program isn't working because of stack
overflow, it's possible that a small change could cause the
program to suddenly start working (or appear to start working),
even though the RAM is trashed.

-- Frank Manning
-- NetMedia, Inc.



--- In , "Frank Manning" <fmanning@n...> wrote:

> In other words, if a program isn't working because of stack
> overflow, it's possible that a small change could cause the
> program to suddenly start working (or appear to start working),
> even though the RAM is trashed.
>
> -- Frank Manning
> -- NetMedia, Inc.

A more specific question:
Does the appearance of a string constant in a subroutine or function
invocation have an impact on the amount of stack space that said
routine uses even if that particular line of code is never executed?

Example:

' fragment with inaccessible string reference
If (0 = 1) Then
Call foo("abcd")
End If

versus

' fragment with commented out string reference
If (0 = 1) Then
'Call foo("abcd")
End If

All other things being the same, would the stack usage of the
containing routine be any different between the two cases?

Second question:
There is a reference (in discussion related to tasks) about seeding
the stack area and analyzing after a run to determine the stack
usage. Is it true that ram-based variables are allocated from 0
upward and the stack grows from the other end downward? Is there an
accesible register that reflects the current stack pointer? My
recollection is that every time I read Register.SPH or Register.SPL
the values are the same, even in different parts of the program that,
I believe, have different nesting level.

Don in Portland, OR


From: don_kinzer <>

> Frank Manning <fmanning@n...> wrote:
>
>> In other words, if a program isn't working because of
>> stack overflow, it's possible that a small change
>> could cause the program to suddenly start working
>> (or appear to start working), even though the RAM is
>> trashed.
>
> A more specific question:
> Does the appearance of a string constant in a
> subroutine or function invocation have an impact
> on the amount of stack space that said routine
> uses even if that particular line of code is never
> executed?
>
> Example:
>
> ' fragment with inaccessible string reference
> If (0 = 1) Then
> Call foo("abcd")
> End If
>
> versus
>
> ' fragment with commented out string reference
> If (0 = 1) Then
> 'Call foo("abcd")
> End If
>
> All other things being the same, would the stack
> usage of the containing routine be any different
> between the two cases?

Possibly. The uncommented case may have an additional 2-byte
temporary variable that is required for passing strings by value.
The variable would take up stack space.

But if the stack has overflowed, it is anybody's guess what the
stack usage is.

> Is it true that ram-based variables are allocated from 0
> upward and the stack grows from the other end downward?

No, variables and stacks are allocated in order of increasing
addresses, and stacks grow upwards.

> Is there an accesible register that reflects the current
> stack pointer? My recollection is that every time I
> read Register.SPH or Register.SPL the values are the
> same, even in different parts of the program that,
> I believe, have different nesting level.

The stack pointer is a 16 bit unsigned integer at address 26. Code
to access the stack pointer looks something like this:

Private SP As New UnsignedInteger
Private Const StackPointerAddress As Integer = 26
Public Sub DisplayStackPointer()
Call BlockMove(2, StackPointerAddress, MemAddressU(SP))
Debug.Print "SP = "; CStr(SP)
End Sub

Register.SPH and SPL refer to an internal stack that is used only
by the operating system.

-- Frank Manning
-- NetMedia, Inc.



--- In , "Frank Manning" <fmanning@n...> wrote:

> The stack pointer is a 16 bit unsigned integer at address 26. Code
> to access the stack pointer looks something like this:
>
> Private SP As New UnsignedInteger
> Private Const StackPointerAddress As Integer = 26
> Public Sub DisplayStackPointer()
> Call BlockMove(2, StackPointerAddress, MemAddressU(SP))
> Debug.Print "SP = "; CStr(SP)
> End Sub
>

Thanks for the information.

I added code to my application to print (via Debug.Print) the SP
value immediately upon entry to Main(). The value displayed is 296.
The .mpp file shows the following RAM allocation information:

RAM available: 401 bytes
RAM allocated: 72 bytes
Main stack size: 329 bytes

My app reports that, at startup, the SP is 296. I created a separate
application which consists of nothing more than a Main() with the SP
retrieval code (total RAM allocated: 6 bytes) and it reported an SP
of 230. From this information, I made the assumption that the user
RAM area might be from 200 through 600. Is this correct?

On the basis of that assumption, I then added code to initialize the
RAM from SP+1 through 599 with the value &H5a. Then, at various
times while exercising my app, I invoke a sub that scans memory
downward from 599 until it finds a location that is not &H5a.
Presumably, this will represent the high water mark of the stack.

On one occasion, after I believe that I had caused the app to consume
the maximum stack space, the reported value of the high water mark
was 522. If my assumption about the address range of user RAM is
correct then I have about 75 bytes of headroom on the stack.

The program is now working as expected through all of the tests that
I have conducted so far. To get to this point, I have eliminated all
String usage (except for the small amount introduced by the debugging
code mentioned above). All of my string data resides in EEPROM and
is accessed via a ByteVectorData variable.

Don in Portland, OR


From: don_kinzer <>

> I added code to my application to print (via Debug.Print)
> the SP value immediately upon entry to Main(). The value
> displayed is 296. The .mpp file shows the following RAM
> allocation information:
>
> RAM available: 401 bytes
> RAM allocated: 72 bytes
> Main stack size: 329 bytes
>
> My app reports that, at startup, the SP is 296. I created a
> separate application which consists of nothing more than a
> Main() with the SP retrieval code (total RAM allocated: 6
> bytes) and it reported an SP of 230. From this information,
> I made the assumption that the user RAM area might be from
> 200 through 600. Is this correct?

It's 207 to 607 (BX-24 Hardware Reference). For the first program,
the main stack of 329 bytes would be addresses 279 to 607.

Of this memory, the first 15 bytes (279 to 293) are taken up by
the task frame for the main program.

The code you need to read the stack pointer itself adds a small
amount to that, so a reading of 296 sounds reasonable.

> On the basis of that assumption, I then added code to
> initialize the RAM from SP+1 through 599 with the value
> &H5a.

OK, but you need to be a little careful here, and leave some room
for the code that is actually writing to RAM. Starting at SP+1
would make me a bit nervous. That code uses some of the stack, so
you need to make sure it doesn't step on itself.

> Then, at various times while exercising my app, I
> invoke a sub that scans memory downward from 599 until it
> finds a location that is not &H5a. Presumably, this will
> represent the high water mark of the stack. [...]

True, unless the stack happens to push &H5a on the stack. It's
probably a good idea to try the program with more than one test
value.

-- Frank Manning
-- NetMedia, Inc.



Thanks again for your help.

Don