Scrolling edge case woes

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Scrolling edge case woes

Post by JoeGtake2 »

I'm having the most difficult time weeding out stray edge case nonsense with this L-R scrolling engine, so I thought I'd post up here rather than bash my head against the same thing over and over again for another night to get your guys' thoughts.

Obviously, you guys know that I'm a bit wrangled on method simply due to mutli-purpose, malleable nature of this engine. So bear with me.

Background collisions and column loads are working great. I have vertical mirroring, so I load current nametable at $2000, then the left half of the right nametable to the second nametable, right half of the left nametable to the second nametable. The midpoint of the second nametable is the seam. All good. I've got background scrolling working perfectly - attributes, collisions, nametables all acting just like they're supposed to - variable pads on each side of the scroll, autoscroll bits...a few particular "it must be this way" things I'd eventually like to address, but for right now, I'm having a hell of a time with objects, with something I felt would be fairly simple.

There is a columnTracker variable. It can be 0-31. This represents the camera-left column. Thus, columnTracker+24 is the seam. Cool. As the camera scrolls it updates the seam, and checks the screen info to see if objects should be loaded. This is fine.

Objects have a variable called Object_status. Bit 2 of Object_status determines if they are in the camera view or not (1 = active but out of view, 0 = in view). This is where all things break down.

This should be a fairly simple thing, but effectively, here's the logic I am trying to handle:

Code: Select all

;; If inside the camera window, jump to "show this object"
;; If outside the camera window, jump to "hide this object"
   LDA Object_x,x
   LSR
   LSR
   LSR
   LSR 
   STA temp ;; now, we know which one of 16 columns.
   LDA Object_scroll,x
   AND #%00000001
   ASL
   ASL
   ASL
   ASL
   ORA temp
   STA thisObjectColumnPosition
   ;;;;;;;; the scroll value acts as the high x byte.  This puts it in place in byte 4, giving 
   ;;;;;;;; the object its column evaluation
;;;;;;;============
;;;;;; For if the scroll is 0-15, this is fairly straight forward.
;;;;;; it would look like this:
    LDA columnTracker
    CMP thisObjectColumnPosition
    BCC outsideOfCamera
;;;;;; still inside, check right edge.
    LDA columnTracker
    CLC
    ADC #$10
    AND #%00011111
    CMP thisObjectColumnPosition
    BCC outsideOfCamera
;;;;;; Passed the test, that means this is inside
;;;;;; Camera area.
    JMP doInsideCamera
outsideOfCamera:
    LDA Object_status,x
    ORA #%00000100
    STA Object_status,x
    JMP done

doInsideCamera:
    LDA Object_status,x
    AND #%11111011
    STA Object_status,x

done:


Hopefully that makes sense. The problem is, everything I try that seems to work out logically to check to see when columnTracker is in the second nametable ends up yielding bugs (things turning on when they shouldn't/off when they shouldn't).

Was hoping for some thoughts, advice, etc. What do you guys think the best method would be for determining in/out of camera here?

Thanks!
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: Scrolling edge case woes

Post by Oziphantom »

Welcome to scrollers ;) They sound so simple but can be finicky.

So with out knowing exactly what the values represent and what numbers you need, my no 1 guess is you are being tripped up by the 6502 asynchronous branch. its < OR >= not < or > this gives you edge cases ;) I'm basing this on the code above;)

So lets tidy the code

Code: Select all

LSR Object_scroll,x ; get 9th bit into C
LDA Object_x,x ; get lower 8 bits
ROR A
LSR A 
LSR A
LSR A ; 9bit shift 4 times 
STA thisObjectColumnPosition ; upper 5 bits
CMP columnTracker ; if thisObjectColumnPosition < columnTracker then outside
BCC outOfCamera 
    SBC #$10 ; carry is already set
    AND #%00011111 ; mask of lower 5 bits
    CMP columnTracker ; if thisObjectColumnPosition-$10 >= columnTracker then outside
    BCS outOfCamera
        ;inside
        LDA Object_status,x
        AND #%11111011
        JMP done
outOfCamera
LDA Object_status,x
ORA #%00000100
done
STA Object_status,x
; if thisObjectColumnPosition < columnTracker then outside
; if thisObjectColumnPosition-$10 >= columnTracker then outside
So is this the right set of equations is going to be the edge case
; if thisObjectColumnPosition <= columnTracker then outside
; if thisObjectColumnPosition-$10 >= columnTracker then outside
or
; if thisObjectColumnPosition < columnTracker then outside
; if thisObjectColumnPosition-$10 > columnTracker then outside
or
; if thisObjectColumnPosition < columnTracker then outside
; if thisObjectColumnPosition-$10 > columnTracker then outside
Without knowing exactly how much you are drawing, and what number columnTracker is, i.e the first visible column or the first invisible column on the left side. It is hard to know which set you need.

To stop your self from going made and having something that "mostly" looks right, I really really recommend this https://github.com/martinpiper/BDD6502 as you can make a test case with the data you need to be true, and then you can test the code above to make sure it is true and remains true.

The other big question is, does this "loop", ie you have a case where the left edge starts at 15 goes to 16 then loops back to 0 while on the screen, because in that case you need code that detects said wrap and you need code to handle said wrap.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Scrolling edge case woes

Post by JoeGtake2 »

Thanks for the code clean up for sure. I'm never very efficient with ASM. Something to work on. Haha.

And yeah, The ORIGINAL method I was using was some 16 bit math. it was honestly easier, but I think was causing issues on *the wrap*.

Something like:

Code: Select all

;;;; CHECK AGAINST CAMERA LEFT
  LDA Object_x_hi,x
  SEC
  SBC xScroll_lo
  LDA Object_x_hi_hi,x ;; handles the screen object is in.
  SBC xSctoll_hi
  BCS isInCameraView_1
  JMP isOutOfCameraView

isInCameraView_1:
;;;; CHECK AGAINST CAMERA RIGHT
   LDA xScroll_lo
   CLC
   ADC #$10
   SEC
   SBC Object_x_hi,x
   LDA xScroll_hi
   SBC Object_x_hi_hi,x
   BCC isInCameraView
   JMP isOutOfCameraView


That was my first instinct. To do 16 bit math on it, using the "screen number" as the high byte, essentially. It was cleaner and I thought pretty logically sound. Though because of continuing issues, I tried swapping over to having it keep with the column updates.




****** SOLVED, FOR NOW *******

In case anyone stumbles upon this -

I did a little extra checking to make sure when a new object was loaded to the seam, it was loading with the correct "high position bit" (that it was inside the right nametable, if you want to think about it that way). Then, it was much easier and cleaner:

Code: Select all


   LDA Object_hi_hi,x
   CMP currentNametable
   BNE isInDifferentNametable
   ;; is in same nametable
   LDA Object_x_hi,x
   CMP xScroll
   BCC isOutOfCameraView
   JMP isInCameraView

isInDifferentNametable:
   CMP rightNametable ;;; this is nametable+1
   BNE isOutOfCameraView
   
   LDA Object_x_hi,x
   CMP xScroll
   BCC isInCameraView
   JMP isOutOfCameraView


Have to allow for width of objects and whatnot or it shows up on the other side of the screen for a few pixels, but other than that, now this seems to be working fine. :-)

Most times just talking it out here helps a lot. Rubber duck debugging FTW.
Post Reply