Cursor movement problems.

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

Post Reply
Count SymphoniC
Very interested
Posts: 149
Joined: Sat Nov 17, 2012 3:58 am

Cursor movement problems.

Post by Count SymphoniC » Fri Sep 26, 2014 2:24 am

I have an issue that's been infuriating. This is code I have for determining whether the user has pressed up or down on the gamepad (this subroutine is being called from the main loop), and doing the appropriate maths to move the cursor on screen vertically up or down. The problem is, no matter how I rewrite this code, I can only get one direction working. I think there's something wrong with the branching here and I suspect that, if user has pressed up, it will branch to the @subtracty and go to rts, but I think that rts is taking us back up to the point where it branched to subtracty, and going to the addition, cancelling out my subtraction math. I'm not sure how to approach this! I thought using jmp to get out of this and return to the main loop would be kind of 'hacky', so I'm trying to avoid doing it. This is my last question here for a while, I've been going a good pace at figuring stuff out and I've come this far. Just this snag is really annoying.

Code: Select all

MOVECURSORY:
;d0 how many pixels to move (8 pixels for one row) = 0x08
;d1 sprite ID, = 0x00
;d2 sprite 2 ID, = 0x01
;d3 0x00 for up, 0x01 for down

    mulu.w               #0x8, d1
    swap                  d1                ;d1 0x00000000
    mulu.w               #0x8, d2
    swap                  d2                ;d2 0x00080000

    add.l                    #VDPWRITESPRITE, d1              ;0x60000003
    add.l                    #VDPWRITESPRITE, d2              ;0x60080003
    move.l                  d1, CURSORATTRIBUTERAM     ;This is for the VDP control port during vblank
    move.l                  d2, CURSORATTRIBUTERAM2     ;This is for the VDP control port during vblank

    move.l                #0x00FFFFB2, a2                     ;Ready ram address for y position
    move.w               (a2), d4                         ; move old y value to d4

    cmp.b                   #0x01, d3
    bne                      @SUBTRACTY
    add.w                   d0, d4          ;add old y value and 8 pixels to get new y value
    move.w                d4, 0x00FFFFB2
    move.w                 d4, CURSORPOSITIONRAM       ;This is for VDP  Data port
    jmp                       @END

    @SUBTRACTY:
    sub.w                   d0, d4           ;sub old y value and 8 pixels to get new y value
    move.w                d4, 0x00FFFFB2
    move.w                 d4, CURSORPOSITIONRAM       ;This is for VDP  Data port

    @END:

    rts
The goal is to be able to either add, or subtract 8 pixels of y position to or from the old y value to create the new y value, dpad up or down being the deciding factor on whether or not we add or subtract. I've isolated parts of the code and tested them and the addition makes both cursor sprites move down 8 pixels at a time, and subtraction makes both sprites move up 8 pixels at a time, but no matter how I write this branching code... I can't get it to work with both!!!


EDIT: Yeah using jsr to jump back into the main loop is bad news here. Eventually, my ram is filled with junk stack data from the jsr being used too much and this results in the rom crashing. I was hoping to keep my y positioning all in one subroutine without having to separate it out. Is there any way this can be achieved, am I missing something important?

EDIT:

Code: Select all

cmp.b                   #0x01, d3
    bne                      SUBTRACTY
    cmp.b                   #0x01, d3
    beq                       ADDY
    rts
ADDY:
    add.w                   d0, d4        ;add old y value and 8 pixels to get new y value
    move.w                d4, 0x00FFFFB2
    move.w                 d4, CURSORPOSITIONRAM       ;This is for VDP  Data port
    rts

SUBTRACTY:
    sub.w                   d0, d4         ;add old y value and 8 pixels to get new y value
    move.w                d4, 0x00FFFFB2
    move.w                 d4, CURSORPOSITIONRAM      ;This is for VDP  Data port
    rts


This is logical to me, but still does not work! Does this have to do with the stack?

BigEvilCorporation
Very interested
Posts: 209
Joined: Sat Sep 08, 2012 10:41 am
Contact:

Post by BigEvilCorporation » Fri Sep 26, 2014 8:27 am

Branches are not subroutines, they don't push the return address and cannot be jumped back to with rts - you need to jsr (Jump to SubRoutine) if you want to return to that location with rts (ReTurn from Subroutine)

Unfortunately this means changing your code a little, since you'll need to do the conditional checks to drop down a few lines THEN call jsr. Something like:

Code: Select all

   cmp #SomeValue, d0
   bne @NotEqual
   jmp RoutineToRunIfEqual
   bra @EndIf
@NotEqual:
   jmp RoutineToRunIfNotEqual
@EndIf:
If you're brave, you could do away with the label and explicitly make it branch to the known 'notequal' jump instruction. It'll make the code neater but open yourself up to some confusion if you want to add more code before doing the jump. I used the Sonic 1 disassembly for reference, it does a lot of "if(condition) { DoSomething(); } else { DoSomethingElse(); }" style checks.

EDIT: If you're following my blog's example code be aware of dragons, I have made this mistake myself on more than one occasion and may not have cleaned them all up.

EDIT EDIT: I've just read the first block you posted, and that seems ok to me. How are you jumping to that subroutine in the first place? Show the code relevant code from the main loop.

There must be a mismatched pair of jsr/rts somewhere. I take it you're not using the stack for anything yourself yet?

EDIT EDIT EDIT: I should have more than one coffee before I start reading forums, both your samples make logical sense. Again, did you get to that subroutine using jsr in the first place?
A blog of my Megadrive programming adventures: http://www.bigevilcorporation.co.uk

Count SymphoniC
Very interested
Posts: 149
Joined: Sat Nov 17, 2012 3:58 am

Post by Count SymphoniC » Fri Sep 26, 2014 12:49 pm

I solved it, I missed one tiny important detail. You have to go to the main loop to see it, and it sure was hard to notice. I was using the same data register that was being used for gamepad checking (d0). I moved it to d7 and everything works so beautifully. Although I was already being mindful of keeping important data in important registers intact, I apparently wasn't being vigilant enough. Anyways, thanks. You forced me to take a (3rd/4th?) look at the main loop and then it suddenly dawned on me what happened.

Well now I can start working on giving this cursor a timing mechanism to keep it from moving too fast, then write code to restrict it's movement to the boundaries of the onscreen rows of pattern numbers it's supposed to edit. Then I can tie it in with data array I already have done, and make it so that it can edit that data array, and then tie in the array values with the graphics of the cursor, it is after all a font based cursor! It's used for editing numbers, options and musical notes. Making a GUI is kinda fun. I like it when things go a little smoother of course.

BigEvilCorporation
Very interested
Posts: 209
Joined: Sat Sep 08, 2012 10:41 am
Contact:

Post by BigEvilCorporation » Fri Sep 26, 2014 1:54 pm

Excellent!

Look into calling conventions (passing params via regs or stacks, how to back up regs before jumping, where return values are stored, etc) and design one to suit your coding style, then stick to it. Some macros help - BACKUP_REGS, RESTORE_REGS, that kind of thing.

At the moment I use regs for everything, but have a comment chunk at the top of each function describing what param goes where, and where to get the return value from, so I know which regs are going to be stamped on before I use it. Some small documentation goes a long way.
A blog of my Megadrive programming adventures: http://www.bigevilcorporation.co.uk

Count SymphoniC
Very interested
Posts: 149
Joined: Sat Nov 17, 2012 3:58 am

Post by Count SymphoniC » Fri Sep 26, 2014 4:48 pm

Yeah, I should definitely start doing that. I already have a function for cleaning all the registers in one sweep. A couple of other forum members here explained pushing and popping the stack too, I just need to be careful I don't corrupt what's on my ram when doing that! I found that if you do enough jmp's or jsr's (it really only takes one) without using rts in a loop everything in the ram will eventually be overwritten and corrupted and the program will crash.

Thanks again for your help and your blog. It really helped get past the confusing stress stage of orientation to both 68k and the genesis. I guess I'm doing things the hard way, learning a new language on a console. But I see no reason to quit doing that now. Not when I've come this far.

BigEvilCorporation
Very interested
Posts: 209
Joined: Sat Sep 08, 2012 10:41 am
Contact:

Post by BigEvilCorporation » Mon Sep 29, 2014 8:47 am

I'm glad you're finding it useful! Just make sure to proof read everything and don't take anything I wrote as gospel, it's more of a diary than a tutorial and there are a lot of mistakes.
A blog of my Megadrive programming adventures: http://www.bigevilcorporation.co.uk

Count SymphoniC
Very interested
Posts: 149
Joined: Sat Nov 17, 2012 3:58 am

Post by Count SymphoniC » Mon Sep 29, 2014 1:56 pm

BigEvilCorporation wrote:I'm glad you're finding it useful! Just make sure to proof read everything and don't take anything I wrote as gospel, it's more of a diary than a tutorial and there are a lot of mistakes.
Without a doubt. I'm using the startup code you made directly in the program, although I could probably figure out my own at this point, but I've found some things that needed to be fixed in the startup code, with the VDP registers to be a little more specific. And don't worry, my code has it's dragons too, I had to fix two major bugs last night that were very hard to track down. Anyways, I understand using the blog as a journal, it helps everything sink in better and helps keep thoughts organized. Not to mention you always have a reference to go back to (one that you can understand).

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Mon Sep 29, 2014 5:34 pm

BigEvilCorporation wrote:Excellent!

Look into calling conventions (passing params via regs or stacks, how to back up regs before jumping, where return values are stored, etc) and design one to suit your coding style, then stick to it. Some macros help - BACKUP_REGS, RESTORE_REGS, that kind of thing.

At the moment I use regs for everything, but have a comment chunk at the top of each function describing what param goes where, and where to get the return value from, so I know which regs are going to be stamped on before I use it. Some small documentation goes a long way.
The ABI for M68K gcc is pretty simple. Your function needs to save and restore d2-d7 and a2-a6 if they are used by the function. Nothing else needs saving. ALL parameters are passed on the stack as longwords. When your function is entered, and before you push anything on the stack, SP+0 = return address, SP+4 = leftmost arg, SP+8 = next arg, and so on. You also cannot count on smaller args being sign-extended to a long... they may or may not be sign or zero extended; the extra bytes of the long on the stack may even be garbage, depending on the revision of the compiler. So if you pass a byte arg to the function, it takes a long in space, but only the LSB itself is actually valid. Your function returns any result up to 32 bits in d0 (any 68k compiler), and any result up to 64 bits in d0-d1 (only on newer versions of gcc).

The stack arg size is only an issue in assembly where type is wholly the responsibility of the programmer. In C, as long as you don't use conflicting types for the arg, it's not an issue.

Post Reply