Issues with accessing a structure array

Coding and general discussion relating to the compiler

Moderators: David Barker, Jerry Messina

Post Reply
bradsprojects
Posts: 28
Joined: Thu Nov 29, 2012 12:29 am
Location: Australia

Issues with accessing a structure array

Post by bradsprojects » Sat Jun 01, 2013 9:32 pm

Hi everyone, hopefully someone can lend a hand with an issue I am having.

I am writing a little program that displays different color bouncing balls on a 32x32 pixel RGB LED matrix. I wanted to use a structure array for the balls. The code to bounce the balls around the screen works just fine as long as I copy and paste each section of code and individually write in the number of the ball in the structure array.

To start off, heres the contents of the structure:

Code: Select all

Structure Ball
    Color As Byte
    X As Byte
    Y As Byte
    XDelay As Byte
    XDelay2 As Byte
    YDelay As Byte
    YDelay2 As Byte
    GoingUp As Boolean
    GoingLeft As Boolean
    MaxJumpHeight As Byte
    CurrentJumpHeight As Byte
End Structure
I then created an array of these so I can get multiple balls:

Code: Select all

Dim Multiball(4) As Ball
And then I have a sub routine to bounce the balls around the screen (within a 32 pixel wide area):

Code: Select all

Sub BounceTheBalls()
    for Index = 0 to bound(multiball)
        ' First move the balls left / right
        If Multiball(Index).XDelay <> 0 Then
            Dec(Multiball(Index).XDelay)
        Else
            Multiball(Index).XDelay = Multiball(Index).XDelay2
            If Multiball(Index).GoingLeft = false Then
                Inc(Multiball(Index).X)
                If Multiball(Index).X = 31 Then
                    Multiball(Index).GoingLeft = true
                EndIf
            Else
                Dec(Multiball(Index).X)
                If Multiball(Index).X = 0 Then
                    Multiball(Index).GoingLeft = false
                EndIf
            EndIf
        EndIf
       ' then move the balls up / down
        If Multiball(Index).YDelay <> 0 Then
            Dec(Multiball(Index).YDelay)
        Else
            If Multiball(Index).GoingUp = false Then                          ' if the ball is going down
                If Multiball(Index).YDelay2 > 0 Then                           ' only take one away if we are still greater than 0 (0 is the fastest we can get because it's the lowest delay)
                    Dec(Multiball(Index).YDelay2)
                EndIf
                Multiball(Index).YDelay = Multiball(Index).YDelay2                                               ' now make the ball move down 1 space
                Inc(Multiball(Index).Y)
            Else                                                    ' if the ball is not going down, then it must be going up - so we need to slow the ball down                 
                Inc(Multiball(Index).YDelay2)
                Multiball(Index).YDelay = Multiball(Index).YDelay2
                Dec(Multiball(Index).Y)
                Inc(Multiball(Index).CurrentJumpHeight)
            EndIf
            If Multiball(Index).Y = 31 And Multiball(Index).GoingUp = false Then
                Multiball(Index).YDelay = 0
                Multiball(Index).YDelay2 = 0
                Multiball(Index).GoingUp = true
                Multiball(Index).CurrentJumpHeight = 0
            End If
            If Multiball(Index).GoingUp = true And Multiball(Index).CurrentJumpHeight >= Multiball(Index).MaxJumpHeight Then
                Multiball(Index).GoingUp = false
            EndIf
        EndIf
    next   
End Sub
In the above code, it has a for next loop using the variable Index (which has been declared previously as a byte), which should cycle through each ball in multiball and move it accordingly. Unfortunately with this code, the balls never move - they just appear in their starting locations (which are pre-set) and that's it - they just don't move anywhere.

However if I use exactly the same code - but replace the 'Index' in multiball(Index) with a number such as 0, 1, 2 or 3, the code works perfectly and that particular ball will then bounce around the screen. If I copy and paste this code four times (one time for each ball) and then replace the 'Index' with an actual number, all balls then bounce around the screen.

So I guess the big issue here is that as soon as I try and use a variable to access the array, it doesn't work. But if I use an actual integer - it does work.

Any help or suggestions would be much appreciated!

bitfogav
Registered User
Registered User
Posts: 169
Joined: Sat Oct 09, 2010 1:39 pm
Location: United Kingdom

Post by bitfogav » Sun Jun 02, 2013 9:56 am

Ive tested your code with a simple LCD output on one of my test boards and Index gets initialized correctly within the For Loop:

the code I used to test:

Code: Select all

// Device and Clock
Device = 18F452
Clock = 20
                                
// LCD optional settings 
#option LCD_DATA = PORTC.4         
#option LCD_RS = PORTE.0          
#option LCD_EN = PORTE.1
 
// Modules
Include "LCD.bas"          
Include "Convert.bas"

Structure Ball
    Color As Byte
    X As Byte
    Y As Byte
    XDelay As Byte
    XDelay2 As Byte
    YDelay As Byte
    YDelay2 As Byte
    GoingUp As Boolean
    GoingLeft As Boolean
    MaxJumpHeight As Byte
    CurrentJumpHeight As Byte
End Structure

Dim Multiball(4) As Ball
Dim Index As Byte

Sub BounceTheBalls()
    For Index = 0 To Bound(Multiball)
    
      ' debug code added to output Index value on LCD
      LCD.WriteAt(1,1,"Index = " + Convert.dectostr(Index))
      DelayMS(2000)
      
      
        ' First move the balls left / right
        If Multiball(Index).XDelay <> 0 Then
            Dec(Multiball(Index).XDelay)
        Else
            Multiball(Index).XDelay = Multiball(Index).XDelay2
            If Multiball(Index).GoingLeft = false Then
                Inc(Multiball(Index).X)
                If Multiball(Index).X = 31 Then
                    Multiball(Index).GoingLeft = true
                EndIf
            Else
                Dec(Multiball(Index).X)
                If Multiball(Index).X = 0 Then
                    Multiball(Index).GoingLeft = false
                EndIf
            EndIf
        EndIf
       ' then move the balls up / down
        If Multiball(Index).YDelay <> 0 Then
            Dec(Multiball(Index).YDelay)
        Else
            If Multiball(Index).GoingUp = false Then                          ' if the ball is going down
                If Multiball(Index).YDelay2 > 0 Then                           ' only take one away if we are still greater than 0 (0 is the fastest we can get because it's the lowest delay)
                    Dec(Multiball(Index).YDelay2)
                EndIf
                Multiball(Index).YDelay = Multiball(Index).YDelay2                                               ' now make the ball move down 1 space
                Inc(Multiball(Index).Y)
            Else                                                    ' if the ball is not going down, then it must be going up - so we need to slow the ball down                 
                Inc(Multiball(Index).YDelay2)
                Multiball(Index).YDelay = Multiball(Index).YDelay2
                Dec(Multiball(Index).Y)
                Inc(Multiball(Index).CurrentJumpHeight)
            EndIf
            If Multiball(Index).Y = 31 And Multiball(Index).GoingUp = false Then
                Multiball(Index).YDelay = 0
                Multiball(Index).YDelay2 = 0
                Multiball(Index).GoingUp = true
                Multiball(Index).CurrentJumpHeight = 0
            End If
            If Multiball(Index).GoingUp = true And Multiball(Index).CurrentJumpHeight >= Multiball(Index).MaxJumpHeight Then
                Multiball(Index).GoingUp = false
            EndIf
        EndIf
    Next   
End Sub

DelayMS(200)                                 
ADCON1 = $07                 

TRISA = %111111       
TRISB = %11111111   
TRISC = %00000000            
TRISD = %00000000   
TRISE = %000       
LCD.Cls            

Multiball(0).XDelay = 100
Multiball(1).XDelay = 100
Multiball(2).XDelay = 100
Multiball(3).XDelay = 100

// Main Program...
While true                        
   BounceTheBalls()
Wend
The output to a LCD is:
Index = 0
Index = 1
Index = 2
Index = 3

Brad are you using the latest version of the compiler 2.2.1.7?.

bitfogav
Registered User
Registered User
Posts: 169
Joined: Sat Oct 09, 2010 1:39 pm
Location: United Kingdom

Post by bitfogav » Sun Jun 02, 2013 10:14 am

Actually I think ive found the issue, theres a problem with how Swordfish deals with the command DEC() and INC() with reference to calling a Structure in your case with a variable?.
If you replace all the lines of code for example:

Code: Select all

Dec(Multiball(Index).XDelay)
replace to:

Code: Select all

Multiball(Index).XDelay = Multiball(Index).XDelay -1
Then I find the output of XDelay is correct.

bradsprojects
Posts: 28
Joined: Thu Nov 29, 2012 12:29 am
Location: Australia

Post by bradsprojects » Sun Jun 02, 2013 11:01 am

Thanks for taking the time to run through it Gav - much appreciated :)

I'll give your suggestions a try and report back.

Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Sun Jun 02, 2013 11:33 am

Yeah, there's definitely something wrong with the code produced by Inc() and Dec(). Looks like something for David to check out.

Another way to 'fix' this is to use an optimization I usually use when accessing arrays and structures, and that's to make a copy of the array member and use that for all the computations

Code: Select all

Sub BounceTheBalls2()
    dim mball as ball       // copy of current structure

    for Index = 0 to bound(multiball)
        mball = Multiball(Index)            // copy array member

        ' First move the balls left / right
        If mball.XDelay <> 0 Then
            Dec(mball.XDelay)
        Else
            mball.XDelay = mball.XDelay2
            If mball.GoingLeft = false Then
                Inc(mball.X)
                If mball.X = 31 Then
                    mball.GoingLeft = true
                EndIf
            Else
                Dec(mball.X)
                If mball.X = 0 Then
                    mball.GoingLeft = false
                EndIf
            EndIf
        EndIf
       ' then move the balls up / down
        If mball.YDelay <> 0 Then
            Dec(mball.YDelay)
        Else
            If mball.GoingUp = false Then                          ' if the ball is going down
                If mball.YDelay2 > 0 Then                           ' only take one away if we are still greater than 0 (0 is the fastest we can get because it's the lowest delay)
                    Dec(mball.YDelay2)
                EndIf
                mball.YDelay = mball.YDelay2                                               ' now make the ball move down 1 space
                Inc(mball.Y)
            Else                                                    ' if the ball is not going down, then it must be going up - so we need to slow the ball down
                Inc(mball.YDelay2)
                mball.YDelay = mball.YDelay2
                Dec(mball.Y)
                Inc(mball.CurrentJumpHeight)
            EndIf
            If mball.Y = 31 And mball.GoingUp = false Then
                mball.YDelay = 0
                mball.YDelay2 = 0
                mball.GoingUp = true
                mball.CurrentJumpHeight = 0
            End If
            If mball.GoingUp = true And mball.CurrentJumpHeight >= mball.MaxJumpHeight Then
                mball.GoingUp = false
            EndIf
        EndIf

        Multiball(Index) = mball        // update array
    next
End Sub
Operations like 'Multiball(Index).Xdelay' can be costly in terms of code size and speed. It takes quite a bit of code to figure out where that variable is actually located.

This version is less than 1/4 the size of the original, and even with the structure copying it runs almost twice as fast.

bradsprojects
Posts: 28
Joined: Thu Nov 29, 2012 12:29 am
Location: Australia

Post by bradsprojects » Sun Jun 02, 2013 12:50 pm

Thanks for the replies guys.

Gav - your suggestion has fixed the problem - the balls now bounce around the screen just nicely - I replaced all of the DEC() and INC() lines of code just as you said.

I will have to post a little video clip of it in action when I get a few spare minutes. The cool thing is that you just need to type in whatever number you want in the array declaration and you will end up with that many number of bouncing balls on the display.

Jerry - thankyou very much for your suggestion and I don't know why I didn't think of doing it before. I have had to use your method when dealing with ORing some graphic data together. If I try and OR some data from a constant array so that it will get included in the output graphic data array, it doesn't work (properly).

However, if I store that constant array data in a temp location as you have done, and then OR it with the output graphic data, it works fine.

Once again, thankyou very much for the help and I'll be sure to post a video clip of what you guys have helped me to achieve.

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Post by David Barker » Mon Jun 03, 2013 2:39 pm

Thanks guys for checking the problem out and getting a workaround - I will look into the inc() and dec() problem in the next week or so...

Post Reply