Strange select case evaluation on number 44

Discuss about beta versions of mikroBasic
compiler.
Post Reply
Author
Message
rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

Strange select case evaluation on number 44

#1 Post by rackley » 21 Mar 2006 05:45

Background: I have 15 different menu items that are each a struct. Within the struct there is a numerical upper bound and lower bound for that item, even though all items are stored as integers for flexibility.

So for example, a boolean item would have a lower bound of 0 and an upper bound of 1. Other items have lower bounds of 0 and uppers of 300. Others have -200 to +200, etc.

But for items with a defined upper boundry of over 255 something odd happens. Most of my items have an upper bound of 300 and whenever they increment or decrement to 44 a select case statement incorrectly evaluates it as an upper bound case.

It compares the upper bound value stored in the structure to the "working value" to see if it's an upper bound case. The WorkingVal and UpperBound are both defined as integers. Here is the output: (note the UpperBoundText: 0 is expected/normal since it's undefined)

Code: Select all

Upperbound Case
Upperbound val:    300 Working val:     44
UpperBoundText:      0
Here is the code behind it:

Code: Select all

' Generic item handler
    Select Case SubMenu.WorkingVal
        Case menuItems[MainMenu.Cursor].LowerBound
            ' See if there is a lower bound text identified
            SerOutText("Lowerbound Case ")
            If menuItems[MainMenu.Cursor].LowerBoundText = integer(0) Then
                ' No text defined
                DrawVal = TRUE
            Else
                ' Lower limit text defined, so draw it
                Lcd_PrintPtr(LCDLine3,menuItems[MainMenu.Cursor].LowerBoundText)
                DrawVal = FALSE
            End If

        Case menuItems[MainMenu.Cursor].UpperBound
            ' See if there is an upper bound text identified
            SerOutTextCRLF("Upperbound Case ")
            SerOutText("Upperbound val: ")
            SerOutInt(menuItems[MainMenu.Cursor].UpperBound)
            SerOutText("Working val: ")
            SerOutInt(SubMenu.WorkingVal)
            SerOutCRLF()
            SerOutText("UpperBoundText: ")
            SerOutInt(menuItems[MainMenu.Cursor].UpperBoundText)
            SerOutCRLF()
            
            If menuItems[MainMenu.Cursor].UpperBoundText = integer(0) Then
                ' No text defined
                DrawVal = TRUE
            Else
                ' Upperlimit text defined, so draw it
                Lcd_PrintPtr(LCDLine3,menuItems[MainMenu.Cursor].UpperBoundText)
                DrawVal = FALSE
            End If
            
        Case Else
            ' We're in a non-upper and non-lower bound case
            DrawVal = TRUE
     End Select
The select case statement also correctly identifies the REAL lower and upper bound numbers.

There is an obvious explanation here. The binary representation of 300 is:

0000 0001 0010 1100

And the binary representation of 44 is, you guessed it,

0010 1100

So somehow this is being evaluated as byte values and the upper byte is being cut off, and I don't think I'm inadvertently doing it.


Which bring up another issue.. I ordinarily would never have noticed this because if UpperBoundText = 0, as it clearly is based on the serial output, it should just draw the value like any other number and I would be unaware it even entered this state. However, that IF statement is evaluating to FALSE, causing it to print the contents of the flash address 0 to the LCD. I have also tried

Code: Select all

If menuItems[MainMenu.Cursor].UpperBoundText = 0 Then
and it produces the same incorrect evaluation.

Edit: UpperBoundText is a longint and is a pointer to a constant string in ROM, that's why it evaluates it as a number.

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#2 Post by rackley » 21 Mar 2006 06:44

I've tried a few variations on the statement just massaging the variables around a little bit and they all have the same result.

Variations tried:

Code: Select all

Dim testingInt as Integer

testingInt = SubMenu.WorkingVal

    Select Case testingInt 
        Case menuItems[MainMenu.Cursor].LowerBound

        Case menuItems[MainMenu.Cursor].UpperBound
            
        Case Else
            
     End Select

Code: Select all

Dim testingInt as Integer

testingInt = menuItems[MainMenu.Cursor].UpperBound

    Select Case SubMenu.WorkingVal
        Case menuItems[MainMenu.Cursor].LowerBound

        Case testingInt
            
        Case Else
            
     End Select

Code: Select all

Dim testingInt as Integer

testingInt = integer(300)

    Select Case SubMenu.WorkingVal
        Case menuItems[MainMenu.Cursor].LowerBound

        Case testingInt
            
        Case Else
            
     End Select
I don't know if this info helps or not. I'd put together a sample project but it's very late here I won't have time to put it together and verify the results on-chip until tomorrow night.

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#3 Post by jpc » 21 Mar 2006 10:12

Hi ray ,

are you really sure the case-identification is correct ? If ever SubMenu.Workingval already would be clipped to byte-size the same would apply there already. What is the struct-definition of SubMenu ? How is it initialised ? The fact that inside the case , while accessing MenuItems[MainMenu.Cursor].UpperBound gives the right result does not exclude the error to be already made before.
If you feel it usefull , give me some more code concerning the declaration and initialisation of these structures.

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#4 Post by rackley » 21 Mar 2006 16:42

Good point, the case where WorkingVal is initalized with a clean integer is the only case I didn't test last night. I'll test it tonight and see what the results are.

Note: If you just want the specifics just look at the code. Lots of verbage included just in case :-)

In any case, WorkingVal is populated in the following block. It looks to see if the user just entered the submenu and all related initialization work needs to be done, or if it's simply being redrawn with a new value (i.e. user change the value up or down.) I had it setting a default value of 32767 so if I saw that on the keypad I knew something wasn't getting initialized right.

The reason for WorkingVal is so that users can modify the value they see on the keypad and still have the option to exit the submenu without saving to eeprom or changing the REAL value in memory. The increment/decrement is in another routine that handles button presses. When the user clicks the up or down buttons it will increment or decrement WorkingVal without affecting the actual user preference value. This way the user can exit the menu without saving their changes.

Code: Select all

If SubMenu.Redraw = FALSE Then
       SubMenu.Redraw = TRUE
       LCD_PrintLine(LCDLine1,"   Update Value:    ")
       ' Print variable name
       Lcd_PrintPtr(LCDLine2, menuItems[MainMenu.Cursor].LCDText)
       btmLftCurrent = btmExit
       btmRtCurrent = btmSave
       DrawBtmLine(btmLftCurrent, btmRtCurrent)
       SubMenu.WorkingVal = 32767

       ' Set our working value to our actual value
       SubMenu.WorkingVal = menuItems[MainMenu.Cursor].Value^
    End If
In turn, the menuItem struct is declared as:

Code: Select all

structure menuItem
    Dim DefaultValue as Integer    ' Integer denoting default value for item
    Dim LowerBound as Integer      ' Integer denoting lower bound for item
    Dim UpperBound as Integer      ' Integer denoting upper bound for item
    Dim LowerBoundText as longint  ' Address of lower bound string in ROM
    Dim UpperBoundText as longint  ' Address of upper bound string in ROM
    Dim PositiveSuffix as longint  ' Address of + string in ROM
    Dim NegativeSuffix as longint  ' Address of - string in ROM
    Dim LCDText as longint         ' Address of string in ROM
    Dim Value as ^Integer          ' Pointer to user preference value
end structure
Value in turn points back to the User Preferences structure, which is a long list of variables that are all of Integer type. The reason the value is also made accessable in menuItem is so that each menu item can be tied back to it's user preference value, which is the value used directly by the code logic. This menuItem[x].Value pointer is initialized when the array of menuItem structures is initialized. All this just so that in my code to handle the menu item, I don't need a long Select Case with a Case for every single user preference value. Good lord it's giving me a headache just thinking about it! :-D

Anyhow, here is how the menu items are initialized. First, they are all gone over and given defaults for most of the variables and then the specifics are filled in for each one.

Code: Select all

sub procedure InitMenus(Dim ByRef menuItems as menuItem[numMenuItems],
                        Dim ByRef prefs as UserPrefsStruct)
    Dim i as byte

    ' Init to defaults
    for i=0 to numMenuItems-1
        menuItems[i].LowerBound = integer(0)
        menuItems[i].UpperBound = integer(300)
        menuItems[i].LowerBoundText = integer(0)
        menuItems[i].UpperBoundText = integer(0)
        menuItems[i].PositiveSuffix = @txtSec
        menuItems[i].NegativeSuffix = @txtSec
    next i

    i=0
    'txtVegTurnOnCTemp
    menuItems[i].LCDText = @txtVegTurnOnCTemp
    menuItems[i].PositiveSuffix = @txtDegF
    menuItems[i].Value = @prefs.VegOnCTemp

    i=1
    'txtVegTurnOffCTemp
    menuItems[i].LCDText = @txtVegTurnOffCTemp
    menuItems[i].PositiveSuffix = @txtDegF
    menuItems[i].Value = @prefs.VegOffCTemp

    i=2
    'txtMinVOTemp
    menuItems[i].LCDText = @txtMinVOTemp
    menuItems[i].PositiveSuffix = @txtDegF
    menuItems[i].Value = @prefs.VegVOTempMin

Etc...
end sub

jpc
Posts: 1986
Joined: 22 Apr 2005 17:40
Location: France 87

#5 Post by jpc » 21 Mar 2006 18:02

this does not show how SubMenu.WorkingVal is initialised before you enter the case , it behaves as if it was declared as byte . What exactly does your struct SubMenu look like ? Is it global ? Possibly declared in a separate module ?

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#6 Post by rackley » 21 Mar 2006 19:05

Code: Select all

structure SubMenuStruct
    Dim DrawNow as Byte
    Dim Redraw As Byte
    Dim WorkingVal As Integer
end structure
It is declared in the SubMenu.pbas file. It is instantialized in my program's main: module and passed by reference to all subroutines that need it due to the visibility constraints of making it global.

Here is the complete function for drawing the submenu:

Code: Select all

sub procedure DrawSubMenu(Dim ByRef menuItems as menuItem[numMenuItems],
                          Dim ByRef MainMenu as MainMenuStruct,
                          Dim ByRef SubMenu as SubMenuStruct,
                          Dim ByRef btmLftCurrent, btmRtCurrent as Byte)
                          
    Dim tmpByteTxt as String[3]
    Dim tmpIntTxt as String[6]
    Dim tmpStr as String[20]
    dim txtLine3 as String[20]
    Dim testingInt as Integer
    Dim i as Byte
    Dim ii as Byte
    dim j as Byte
    Dim LengthOfVal as Byte
    Dim LengthOfSuffix as Byte
    Dim ValStartingChar as Byte
    Dim startingPos as Byte
    Dim DrawVal as Byte       ' Do we just draw the value, or draw text instead?
    
    i=0
    ii=0
    j=0
    ValStartingChar=0
    LengthOfSuffix=0
    

    If SubMenu.Redraw = FALSE Then
       SubMenu.Redraw = TRUE
       LCD_PrintLine(LCDLine1,"   Update Value:    ")
       ' Print variable name
       Lcd_PrintPtr(LCDLine2, menuItems[MainMenu.Cursor].LCDText)
       btmLftCurrent = btmExit
       btmRtCurrent = btmSave
       DrawBtmLine(btmLftCurrent, btmRtCurrent)
       SubMenu.WorkingVal = 32767

       ' Set our working value to our actual value
       SubMenu.WorkingVal = menuItems[MainMenu.Cursor].Value^
    End If

    ' Start out with blank line
    txtLine3 = "                    "

    testingInt = integer(300)
    ' Generic item handler
    Select Case SubMenu.WorkingVal
        Case menuItems[MainMenu.Cursor].LowerBound
            ' See if there is a lower bound text identified
            SerOutText("Lowerbound Case ")
            If menuItems[MainMenu.Cursor].LowerBoundText = 0 Then
                ' No text defined
                DrawVal = TRUE
            Else
                ' Lower limit text defined, so draw it
                Lcd_PrintPtr(LCDLine3,menuItems[MainMenu.Cursor].LowerBoundText)
                DrawVal = FALSE
            End If

        Case testingInt
            ' See if there is a lower bound text identified
            SerOutTextCRLF("Upperbound Case ")
            SerOutText("Upperbound val: ")
            SerOutInt(menuItems[MainMenu.Cursor].UpperBound)
            SerOutText("Working val: ")
            SerOutInt(SubMenu.WorkingVal)
            SerOutCRLF()
            SerOutText("UpperBoundText: ")
            SerOutInt(menuItems[MainMenu.Cursor].UpperBoundText)
            SerOutCRLF()
            
            If menuItems[MainMenu.Cursor].UpperBoundText = 0 Then
                ' No text defined
                DrawVal = TRUE
            Else
                ' Lower limit text defined, so draw it
                Lcd_PrintPtr(LCDLine3,menuItems[MainMenu.Cursor].UpperBoundText)
                DrawVal = FALSE
            End If
            
        Case Else
            ' We're in a non-upper and non-lower bound case
            DrawVal = TRUE
     End Select
     
     If DrawVal = TRUE Then
         ' First get our value
         IntToStr(SubMenu.WorkingVal,tmpIntTxt)

         ' Find the char where the value actually starts
         ValStartingChar = 0
         while tmpIntTxt[ValStartingChar]=" "
             ValStartingChar=ValStartingChar+1
         wend
         
         LengthOfVal = 6 - ValStartingChar

         If SubMenu.WorkingVal >= 0 Then
             ptr2str(menuItems[MainMenu.Cursor].PositiveSuffix,tmpStr)
         Else
             ptr2str(menuItems[MainMenu.Cursor].NegativeSuffix,tmpStr)
         End If
         
         ' Find length of suffix
         while tmpStr[LengthOfSuffix] <> 0
             LengthOfSuffix=LengthOfSuffix+1
         wend

         ' Compute total length, get half, center on line and print
         startingPos = 10-((LengthOfSuffix + LengthOfVal)/2)

         ' Copy value to line
         ' i is the starting char that actually contains the number in tmpIntTxt
         j=0
         i=ValStartingChar
         while i<=5
             txtLine3[startingPos+j] = tmpIntTxt[i]
             i=i+1
             j=j+1
         wend

         ' Copy suffix to line
         i=0
         while tmpStr[i] <> 0
             txtLine3[startingPos+LengthOfVal+i] = tmpStr[i]
             i=i+1
         wend

         ' Fill the rest of the line with spaces
         i = startingPos + LengthOfVal + LengthOfSuffix
         while i < 20
             txtLine3[i] = " "
             i=i+1
         wend

         LCD_PrintLine(LCDLine3,txtLine3)
     End If
end sub
It all works great execpt for the Select Case evaluation and also the If menuItems[MainMenu.Cursor].UpperBoundText = 0 evaluation.

I don't see why it's only evaluating the lower byte of the integer when doing the Select Case, especially when outputting the value shows the correct, full integer value.

Likewise, when menuItems[MainMenu.Cursor].UpperBoundText is explicitly set to zero during structure initialization, and verified to BE zero at this point in the code, why is the If/Then statement saying 0 = 0 is false?

It almost seems as if it's two separate issues.

rackley
Posts: 550
Joined: 08 Aug 2005 17:47
Location: Livonia, MI
Contact:

#7 Post by rackley » 22 Mar 2006 02:50

Here is a clear cut test case:

Code: Select all

sub procedure DrawSubMenu(Dim ByRef menuItems as menuItem[numMenuItems],
                          Dim ByRef MainMenu as MainMenuStruct,
                          Dim ByRef SubMenu as SubMenuStruct,
                          Dim ByRef btmLftCurrent, btmRtCurrent as Byte)
                          
    Dim tmpByteTxt as String[3]
    Dim tmpIntTxt as String[6]
    Dim tmpStr as String[20]
    dim txtLine3 as String[20]
    Dim testingInt1 as Integer
    Dim testingInt2 as Integer
    Dim i as Byte
    Dim ii as Byte
    dim j as Byte
    Dim LengthOfVal as Byte
    Dim LengthOfSuffix as Byte
    Dim ValStartingChar as Byte
    Dim startingPos as Byte
    Dim DrawVal as Byte       ' Do we just draw the value, or draw text instead?
    
    i=0
    ii=0
    j=0
    ValStartingChar=0
    LengthOfSuffix=0
    

    If SubMenu.Redraw = FALSE Then
       SubMenu.Redraw = TRUE
       LCD_PrintLine(LCDLine1,"   Update Value:    ")
       ' Print variable name
       Lcd_PrintPtr(LCDLine2, menuItems[MainMenu.Cursor].LCDText)
       btmLftCurrent = btmExit
       btmRtCurrent = btmSave
       DrawBtmLine(btmLftCurrent, btmRtCurrent)
       SubMenu.WorkingVal = 32767

       ' Set our working value to our actual value
       SubMenu.WorkingVal = menuItems[MainMenu.Cursor].Value^
    End If

    testingInt1 = integer(300)
    testingInt2 = integer(44)
    ' Generic item handler
    Select Case testingInt1 ' SubMenu.WorkingVal
        Case menuItems[MainMenu.Cursor].LowerBound
            ' See if there is a lower bound text identified
            SerOutText("Lowerbound Case ")
            If menuItems[MainMenu.Cursor].LowerBoundText = 0 Then
                ' No text defined
                DrawVal = TRUE
            Else
                ' Lower limit text defined, so draw it
                Lcd_PrintPtr(LCDLine3,menuItems[MainMenu.Cursor].LowerBoundText)
                DrawVal = FALSE
            End If

        Case testingInt2
            ' See if there is a lower bound text identified
            SerOutTextCRLF("Upperbound Case ")
            SerOutText("Upperbound val: ")
            SerOutInt(menuItems[MainMenu.Cursor].UpperBound)
            SerOutText("Working val: ")
            SerOutInt(SubMenu.WorkingVal)
            SerOutCRLF()
            SerOutText("UpperBoundText: ")
            SerOutInt(menuItems[MainMenu.Cursor].UpperBoundText)
            SerOutCRLF()
            
            If menuItems[MainMenu.Cursor].UpperBoundText = 0 Then
                ' No text defined
                DrawVal = TRUE
            Else
                ' Lower limit text defined, so draw it
                Lcd_PrintPtr(LCDLine3,menuItems[MainMenu.Cursor].UpperBoundText)
                DrawVal = FALSE
            End If
            
        Case Else
            ' We're in a non-upper and non-lower bound case
            DrawVal = TRUE
     End Select
     
Serial output while adjusting the value up and down. Note that the variables in the Select Case do NOT change. The Select Case is set to 300 and the Case is set to 44, yet it always evaluates 44 as true.

Code: Select all

Upperbound Case
Upperbound val:    300 Working val:    163
UpperBoundText:      0
Upperbound Case
Upperbound val:    300 Working val:    164
UpperBoundText:      0
Upperbound Case
Upperbound val:    300 Working val:    163
UpperBoundText:      0
Upperbound Case
Upperbound val:    300 Working val:    162
UpperBoundText:      0
I have found this also appears for other values. For example, if I set the value to 256 and the lower bound is set to 0, it evaluates it as a lower bound case.

Post Reply

Return to “mikroBasic Beta testing”