D-Bug & Automation Forum
D-Bug & Automation Forum >> Coding >> Sauron's homework! (assignment #1 - bouncy)
https://d-bug.mooo.com/dbugforums/cgi-bin/yabb2/YaBB.pl?num=1304238634

Message started by ggn on 01.05.11 at 08:30:33

Title: Sauron's homework! (assignment #1 - bouncy)
Post by ggn on 01.05.11 at 08:30:33
Assignment #1 - bouncy thing!

So, let's say you want to control a bouncy ball. Here are its characteristics:

  • This ball moves diagonally in 1 pixel increments
  • Once it hits an axis, its direction changes (for example if it goes down and right and hits the bottom border, it'll go up and right)
  • The top and bottom border coords are (0,0) and (320,240)
  • [edit]The ball moves on its own, no user interraction is required.[/edit]
  • [edit]The ball initially starts at the middle of the screen and moves down-left.[/edit]



Task #1 - Provide code to control the ball
Task #2 (optional) - Assume that the borders aren't fixed and can vary. Change Task #1's code to reflect this change
Task #3 (optional) - Modify the Downfall source so it'll show that ball

That is all. Have fun, and again, everyone that wants to have a shot at this is very welcome to, not just Sauron :)

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by Sauron on 02.05.11 at 23:09:42
Here's a partial, probably wrong answer, completely in pseudo-pseudo code:

define playfield (define borders of screen)
set up ball object
position ball object in center of screen
read joypad
  test direction of joypad press
    if pressed left, move ball left + down
    if pressed right, move ball right + down
compare position of ball to borders
  if ball = bottom border, move up + (left or right)
  if ball = top border, move down + (left or right)
  if ball = right border, move left + (up or down)
  if ball = left border, move right + (up or down)

I'll try to work out some actual code for this, provided I'm on the right track here.

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by ggn on 03.05.11 at 17:09:23
Hmm, it seems like the rules I made were a little vague. How about that for starters we throw interaction out of the window?

What I ideally wanted to see would be code to move a ball around and change direction when it hits corners. I know that this sounds boring, but I don't want you to worry about too much parameters, i.e. setting up the screen and creating objects - you can do this afterwards. (it's task #3 in the list anyway :))

So, I altered the first post a bit, hope I'm more specific now, read it again and happy coding :)

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by Sauron on 04.05.11 at 02:54:01
The interaction wasn't really a problem so much, that was probably the easier part of this. What I'm running into right now is translating the game logic into 68k asm. Looking at the Downfall source, this is the relevant bit to testing the borders of the screen:

[code]
cmp.l      #240,player_y                        ; are we at the bottom of the screen?
bgt            gameover                        ; GAME OVER PLAYER ONE
cmp.l      #17,player_y                        ; are we at the top of the screen?
blt            gameover                              ; GAME OVER PLAYER ONE
                       
moveq      #0,d7                                    ; pad movement flag (0=no movement)

move.l      #-1,falling                              ; man falling flag (-1 = falling)[/code]

So obviously what I need to change is the branch to gameover, for one, since hitting the top or bottom border will necessitate a change in direction of the player object (the ball). What I need to add in is this:

- Movement code for the ball
- testing left and right borders
- changing direction of ball when any borders are hit

I'm running into a mental block, and having to find the appropriate instructions on a 68k cheat sheet will always make me hate life.  ;D

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by Sauron on 04.05.11 at 05:08:37
Ok, I have an answer, even though I'm expecting problems with it. I'll just post what I have and let you guys correct me.

[code]ball_x            dc.l      160            ; ball x position
ball_y            dc.l      120            ; ball y position
x_direction      dc.l      1            ; vertical direction of ball
y_direction      dc.l      1            ; horizontal direction of ball
topborder      dc.l      0            ; sets top border
bottomborder      dc.l      240            ; sets bottom border
leftborder      dc.l      0            ; sets left border
rightborder      dc.l      320            ; sets right border


start:

     move.l      ball_x,d0            ; x coordinate for ball
     move.l      ball_y,d1            ; y coordinate for ball

     sub.l      x_direction,d0            ; moves ball left by one pixel
     add.l      y_direction,d1            ; moves ball down by one pixel


moveball:

     move.l      d0,ball_x            ; updates ball_x variable
     move.l      d1,ball_y            ; updates ball_y variable

     cmp.l      leftborder,ball_x      ; compares left border to x position
     beq      .move_right            ; branches if equal to left border

     cmp.l      rightborder,ball_x      ; compares right border to x position
     beq      .move_left            ; branches if equal to right border

     cmp.l      topborder,ball_y      ; compares top border to y position
     beq      .move_down            ; branches if equal to top border

     cmp.l      bottomborder,ball_y      ; compares bottom border to y position
     beq      .move_up            ; branches if equal to bottom border

     rts                        ; returns to moveball

.move_right:

     add.l      x_direction,d0            ; then starts moving the ball right
     rts                        ; returns to moveball

.move_left:

     sub.l      x_direction,d0            ; then starts moving the ball left
     rts                        ; returns to moveball

.move_down:

     add.l      y_direction,d1            ; then moves the ball down
     rts                        ; returns to moveball

.move_up:

     sub.l      y_direction,d1            ; then moves the ball up
     rts                        ; returns to moveball[/code]

I think my biggest problem is with syntax (remembering the 68k instruction set, more specifically), but we'll see.

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by Sauron on 04.05.11 at 05:32:14
CJ helped me a bit with some corrections in IRC, so here's the udpated code.

[code]ball_x            dc.l      160            ; ball x position
ball_y            dc.l      120            ; ball y position
x_direction      dc.l      1            ; vertical direction of ball
y_direction      dc.l      1            ; horizontal direction of ball
topborder      dc.l      0            ; sets top border
bottomborder      dc.l      240            ; sets bottom border
leftborder      dc.l      0            ; sets left border
rightborder      dc.l      320            ; sets right border


start:

     move.l      ball_x,d0            ; x coordinate for ball
     move.l      ball_y,d1            ; y coordinate for ball

     sub.l      x_direction,d0            ; moves ball left by one pixel
     add.l      y_direction,d1            ; moves ball down by one pixel


moveball:

     move.l      d0,ball_x            ; updates ball_x variable
     move.l      d1,ball_y            ; updates ball_y variable

     cmp.l      leftborder,ball_x      ; compares left border to x position
     beq      .move_right            ; branches if equal to left border

     cmp.l      rightborder,ball_x      ; compares right border to x position
     beq      .move_left            ; branches if equal to right border

     cmp.l      topborder,ball_y      ; compares top border to y position
     beq      .move_down            ; branches if equal to top border

     cmp.l      bottomborder,ball_y      ; compares bottom border to y position
     beq      .move_up            ; branches if equal to bottom border

     rts                        ; returns to moveball

.move_right:

     move.l      #1,x_direction            ; then starts moving the ball right
     rts                        ; returns to moveball

.move_left:

     move.l      #-1,x_direction            ; then starts moving the ball left
     rts                        ; returns to moveball

.move_down:

     move.l      #1,y_direction            ; then moves the ball down
     rts                        ; returns to moveball

.move_up:

     move.l      #-1,ydirection            ; then moves the ball up
     rts                        ; returns to moveball[/code]

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by ggn on 07.05.11 at 16:49:35
Well, it took long for someone to reply here :).

Of course it's all very good and fine. There is one small potential bug there, that is if the ball touches 2 borders at once it'll fail to flip both directions. In any case, I'll just add some variations to this to make some other points.

First in line is, how to make it run faster. Now, of course it's a bit moot to speed up such a small routine, but it's mostly to give some ideas.

So here's a faster version:

[code]ball_x            dc.l      160            ; ball x position
ball_y            dc.l      120            ; ball y position
x_direction      dc.l      1            ; vertical direction of ball
y_direction      dc.l      1            ; horizontal direction of ball
topborder      dc.l      0            ; sets top border
bottomborder      dc.l      240            ; sets bottom border
leftborder      dc.l      0            ; sets left border
rightborder      dc.l      320            ; sets right border


start:

     movem.l ball_x(pc),d0-d3      ;load ball_x in d0, ball_y in d1, x_direction in d2 and y_direction in d3

     sub.l      d2,d0            ; moves ball left by one pixel
     add.l      d3,d1            ; moves ball down by one pixel


moveball:

     move.l      d0,ball_x            ; updates ball_x variable
     move.l      d1,ball_y            ; updates ball_y variable

     cmp.l      leftborder,d0      ; compares left border to x position
     bne.s      .check_right      ; branches if not equal to left border
     moveq      #1,d2            ; then starts moving the ball right
     bra.s      .check_top      ; now go check for top/bottom

.check_right:
     cmp.l      rightborder,d0      ; compares right border to x position
     bne.s      .check_top      ; branches if not equal to right border
     moveq      #-1,d2            ; then starts moving the ball left

.check_top:
     cmp.l      topborder,d1      ; compares top border to y position
     bne.s      .check_bottom      ; branches if not equal to top border
     moveq      #1,d3            ; then moves the ball down
     bra.s      .check_done

.check_bottom:
     cmp.l      bottomborder,d1      ; compares bottom border to y position
     beq.s      .move_up      ; branches if equal to bottom border
     moveq      #-1,d3            ; then moves the ball up

.check_done:
     movem.l d0-d3,ball_x
     rts                  ;all done, thank you, drive thru![/code]

A bit different, isn't it? :) Now, let me comment my changes a bit.

  • I changed all move.l statements to moveq - it is one of the fastest cpu instructions on the 68000. It has a small drawback though, that being that you can only use it if you want to load values from -128 to 127, so use with care!
  • Also obvious, I changed all branches to their short sizes (for example beq.s instead of beq.w). Again, these work only if the branched code is less or equal to 128 bytes from the instruction executed. Usually I make all my branches short and let the assembler whine if any passes the limit :)
  • Generally, I tend to use registers as much as possible, as it's much faster than accessing memory all the time. Since we will be using x_direction and y_direction more than once in the code, I load them too and save them after all processing is finished. On the other hand, the border values are only used once, so I let them be.
  • The 68000 has a special instruction for loading multiple registers to and from memory, called movem. You need to care that what you load is sequentially in memory. Fortunately Sauron had all lined up for a movem:)
  • Finally, I restructured the code a bit, so it'll be a bit tighter, and also a bit easier to check, as you have all the relevant code close by and your eyes don't have to search for branches all the time. Dunno, I just think it's nice, it's not a necessity :)

Title: Re: Sauron's homework! (assignment #1 - bouncy)
Post by ggn on 07.05.11 at 17:16:47
Right, while I'm on the subject, I thought I'd make a quick point about abstraction as well. It may be more advanced for a simple first exercise, so you might want to skip reading it and come back later when we've done more assignments :).

So, say we want to control 10 bouncy balls like this. How would we do it with the code we have so far? Well, one solution would be to copy/paste the code 10 times and change labels, and it would work. But... what about if we found a bug? We'd then have to change it to 10 points in the code! And then maybe we'll need a change later on. 10 changes again! Etc, etc. Soon it becomes ultra tedious to say the least!

What could we do then? Well...


Code (]

the_balls:
     rept 10
     dc.l      160            ; ball x position
     dc.l      120            ; ball y position
     dc.l      1            ; vertical direction of ball
     dc.l      1            ; horizontal direction of ball
     endr

topborder      dc.l      0            ; sets top border
bottomborder      dc.l      240            ; sets bottom border
leftborder      dc.l      0            ; sets left border
rightborder      dc.l      320            ; sets right border

mainloop
     move.l      #the_balls,a0
     bsr.s      bouncy
     bra.s      mainloop

bouncy:

     movem.l (a0),d0-d3      ;load ball_x in d0, ball_y in d1, x_direction in d2 and y_direction in d3

     sub.l      d2,d0            ; moves ball left by one pixel
     add.l      d3,d1            ; moves ball down by one pixel


moveball:

     move.l      d0,ball_x            ; updates ball_x variable
     move.l      d1,ball_y            ; updates ball_y variable

     cmp.l      leftborder,d0      ; compares left border to x position
     bne.s      .check_right      ; branches if not equal to left border
     moveq      #1,d2            ; then starts moving the ball right
     bra.s      .check_top      ; now go check for top/bottom

.check_right:
     cmp.l      rightborder,d0      ; compares right border to x position
     bne.s      .check_top      ; branches if not equal to right border
     moveq      #-1,d2            ; then starts moving the ball left

.check_top:
     cmp.l      topborder,d1      ; compares top border to y position
     bne.s      .check_bottom      ; branches if not equal to top border
     moveq      #1,d3            ; then moves the ball down
     bra.s      .check_done

.check_bottom:
     cmp.l      bottomborder,d1      ; compares bottom border to y position
     beq.s      .move_up      ; branches if equal to bottom border
     moveq      #-1,d3            ; then moves the ball up

.check_done:
     movem.l d0-d3,(a0)
     rts                  ;all done, thank you, drive thru![/code):




Eh what?

Firstly, I've defined 10 balls. They're set up at the top. rept and endr define a block to be repeated as many times as we want. Saves some typing :). As a consequence, each ball's values are directly after the previous one. So what, you'll ask. We still have to reference them somehow, right?

What I basically did there was to use pointers. Registers a0 to a7, as we all know, are called address registers. In addition to just holding numbers, they have some extra functionality. For example, if an address register holds a valid address, then we can access the memory in that address by the use of parentheses. So, if a0 is loaded with the value $10000, then executing

[code]move.l (a0),d0


will fetch the longword that a0 points to and load it in a0. Cool huh? :) Of course there are limits to what one can do, but most of the 68000 instruction set is very flexible and we can use pointer logic in lots of places.

So, how does this help in our case? Simple. To control the first ball, I just load the address of the first element of the first ball, its ball_x to be precise, to a0. I then call the subroutine bouncy. Assuming that a0 contains the address of the ball's structure we want to check, I just go ahead and read all variables with the movem, but instead of a fixed address I use the one stored in a0!


Code (]      movem.l (a0),d0-d3      ;load ball_x in d0, ball_y in d1, x_direction in d2 and y_direction in d3[/code):


After all processing is done, I simply write it back to the address of a0:

[code]      movem.l d0-d3,(a0)


So, can you guess how to process the 2nd bacll? Quite simply, we can load the address of the 2nd ball's structure. We can calculate that quite easily; since each ball uses 4 longwords to store its data, the 2nd ball is located at 4bytes*4longwords=16 bytes after the first one. So,

[code]      move.l      #the_balls+4*4,a0[/code]

will point to our 2nd ball! (yes, arithmetic expressions are allowed as long as they're not too complex)



And that's it really, as you see turning an ordinary routine to a more generic one isn't as difficult as you might first think :)

D-Bug & Automation Forum » Powered by YaBB 2.6.0!
YaBB Forum Software © 2000-2022. All Rights Reserved.