Primordial Ooze, on Thu May 14, 2009 12:15 PM, said:
What exactly do you mean by "crossing a page boundry", i don't understand what that means or how exactly i managed to "cross a page boundry"?
As Dan said, a "page" is 256 bytes. The 2600 uses a 6507 processor, which is basically a 6502 but with fewer pins, and therefore some reduced capabilities-- namely, it can address only 8k of nemory instead of 64k, plus it doesn't have any interrupts (although you can use the BRK opcode to force an interrupt). Anyway, the 6502-- and hence also the 6507-- uses two bytes to identify each memory address, a "lo byte" and a "hi byte." The hi byte is the page number.
In your code, when you had remarked out the line of code that set the y velocity, the PositionSprite loop didn't cross a page boundary. But when you unremarked that line of code to put it back in the program, the PositionSprite loop got pushed up in memory by 2 bytes, because that's how many bytes were used by the line of code to store the y velocity.
When I looked at the assembly listing for your code (after putting that line back into the program), I saw this:
399 f8fe PositionSpriteLoop
400 f8fe
401 f8fe e9 0f sbc #15
402 f900
403 f900 b0 fc bcs PositionSpriteLoop
404 f902
405 f902 49 07 eor #7
The first column is just the line numbers in the assembly listing, so they don't mean anything important. But the second column shows the address where each instruction ends up being placed by the assembler when your code is assembled. The PositionSpriteLoop label (which doesn't actually take up any space) is at address $f8fe, which is also where the first line of code in that loop is stored (the "sbc #15" instruction). The next line ("bcs PositionSpriteLoop") is stored at address $f900. And the line after that ("eor #7") is stored at addressed $f902. So that means the first line-- along with the loop's label-- is stored on page $f8 (the first two hex digits of the address, which is the hi byte), and the branch instruction is stored on page $f9, which is a different page.
When you do a branch, the branch can go forward 127 bytes in the code, or it can go backward 128 (i.e., -128) bytes in the code. A distance of 0 bytes would be the byte that immediately follows the branch instruction-- in this case, "eor #7" that starts as address $f902. You want to branch backward to "PositionSpriteLoop," which is a distance of -4 bytes. Now, the -4 is of no importance to this discussion, but I'm mentioning it to help explain the assembly listing. If you express -4 as an unsigned byte value, you get 252 (i.e., 256 - 4 = 252), and the hex version of 252 is $fc. So that's why the assembled machine code for the "bcs PositionSpriteLoop" is "b0 fc"-- the "b0" represents the "bcs" opcode, and the "fc" means "-4," or branch backwards 4 bytes if the carry flag is set.
Each machine language instruction takes a specific number of machine cycles to execute.
A branch instruction takes 2 cycles to execute if the branch is *not* taken-- i.e., if the program just falls through to the next instruction after the branch, because the condition for the branch was false. ("Was the carry flag set? No? Okay, then just fall through.")
But if the branch *is* taken, then the branch instruction takes 3 machine cycles to execute. On the other hand, "sbc #15" takes only 2 cycles to execute, because it's an immediate mode instruction. So as long as the program keeps looping back, the loop takes a total of 5 cycles-- 2 for the "sbc" and 3 more to branch back to the beginning of the loop. This is equal to 15 color clocks or "horizontal pixel positions" on the scan line, because the processor is running at a third of the speed of the color subcarrier frequency (or "color clock" rate)-- that is, during 1 machine cycle, the scanning beam(s) move forward 3 color clocks or "pixel positions" on the line. So the loop takes 5 cycles, which is 15 color clocks (5 times 3). That's why the loop does a "sbc #15"-- because each time the loop is repeated, the RGB scanning beams move 15 positions.
*But*, if the branch instruction crosses a page boundary-- that is, if the instruction that immediately follows the branch instruction is on one page, but the target of the branch is on a different page-- then the branch instructions takes 4 cycles instead of the usual 3 cycles. This is what happened when you added back in the line that stores the y velocity. As I stated a few paragraphs up, the instruction that immediately follows the branch is at address $f902, which is on page $f9. But the target of the branch-- the "PositionSpriteLoop" label-- is at address $f8fe, which is on page $f8. So the Atari has to go from page $f9 to page $f8 when the branch is taken-- i.e., it has to cross a page boundary-- so the branch takes 4 cycles instead of 3 cycles, which means the entire loop lasts for 6 cycles instead of 5 cycles. That's 18 color clocks instead of 15 color clocks, so the "sbc #15" gets out of whack from the x position counter as the ball is moving from left to right, or from right to left. That's why the ball seems to move smoothly, then jerk forward a bit further than it should, then move smoothly, then jerk forward a bit further than it should, over and over again as the ball moves horizontally. The difference between the x position counter and the ball's actual horizontal position gets larger as the ball moves further to the right, so that by the time the ball's x position counter reaches the value you've set for the right boundary, the ball has already moved off the right edge of the screen and wrapped around to the left side, such that the ball doesn't bounce back when it should.
If you move the loop so it no longer crosses a page boundary-- using any of the four solutions that I suggested-- the loop will go back to taking only 5 cycles to execute, and the ball will move smoothly again, and will also bounce back when you expect it to.
I know that's a lot to try to absorb if you're just learning this stuff, but page-crossings are something you need to learn to watch out for if your program starts to act flakey, because they can throw off the timing of your loops, and precise timing can be very critical on the 2600, especially when it comes to making changes in the middle of a scan line, such as resetting the horizontal position of a sprite.
Michael