Jump to content
IGNORED

Different ways to handle movement


Recommended Posts

Okay, so I finally finished Andrew Davie's tutorials (a good 16 hours of on and off reading spread out over 3 days...) and I'm going through Kirk Israel's tutorials again.

 

I'm noticing that Andrew recommends that motion be handled in grabbing a sprite's position, then adding to/subtracting from it and then sending the new position to the Atari to draw it there instead through some complex mathematical equation that I didn't exactly wrap my head around well. I mean, I get that the bare minimum we can change positions at is 15 color clocks (5 cycles for the smallest amount of code at 3 color clocks per cycle) and I get that from there, we can fine tune motion, but the exacts of the how went right over my head.

 

Kirk seems to have something much more simple for people new to 6502 Assembly Language like me, but only for objects affected by player input. I also have an odd feeling that it isn't exactly optimized or streamlined.

 

In fact, Andrew never touched on player input.

 

Just in general, what are some of the better ways to handle movement both for objects moved by the player and objects not moved by the player?

 

Also, does anyone know where I can gain knowledge on how to handle NPC AI and their movement?

Edited by johnon
Link to comment
Share on other sites

Try this. Somewhere in the middle of your screen draw a dot. Do this with the GRP0 register. Specifically:

 

 

- wait 80 or so scanlines with a loop.

- load GRP0 with a value that you choose.

- do a WSYNC

- clear GRP0 by storing a 0 to it.

- draw the 80 or so remaining scanlines with another loop.

 

 

The end result is you should have a dot somewhere in the middle of the screen. But there are some things we ignored!

 

1. We didn't set a color for GRP0, and if it's the same color as the background we won't see it. So write some code to update the COLUP0 register.

2. We didn't tell the Atari how many copies to make. It could be anything right now. So look at the Stella's programmer guide for values to load into NUSIZ0.

3. We didn't position the dot on the screen. So right now it could be anywhere. We need to use RESP0 to reset the position, and then fine tune it with HMP0.

 

 

 

So here is a famous routine for repositioning. It is very elegant in that it can be used to reposition anything (ball, missiles, players), and doesn't take many bytes of rom space:

 

HorizPositioning
   sec
   sta    WSYNC
;---------------------------------------
.divideBy15:
   sbc    #15                   ;2
   bcs    .divideBy15           ;2³
   eor    #7                    ;2
   asl                          ;2
   asl                          ;2
   asl                          ;2
   asl                          ;2
   sta    HMP0,X                ;4
   sta    RESP0,X               ;4
   rts                          ;6

 

Place this code somewhere outside all of your main code. It is a subroutine, which of course is a bit of code that you jump to, and then return from. You'll see RTS at the bottom of it. That means return from subroutine. To jump to it you'll use "JSR HorizPositioning" where JSR means jump to subroutine. Okay, now what you need to do:

 

 

- load the accumulator LDA with a value (0 - 255). This is the horizontal position you want (the value is your choice).

- load X (LDX) with 0. X is only used in this case to choose between missiles, balls, players.

- JSR HorizPositioning

- finally do a WSYNC followed by a HMOVE.

 

 

 

Finally if you can get this working you are on your way! Post any problems you have. Common sources of error (these are just examples):

 

- confusing LDA #$80 with LDA $80. One means load a value, the other means load from a ram location. Very different results indeed.

- forgeting that X and A have been modified during the routine. In other words if X was previously holding your scanline count, then it will no longer as it was loaded with 0.

- putting JSR all the way to left on your source code. You need indentation! This is an instruction, not a label.

- putting the subroutine in your main loop. Put it within the 4k romsize, but outside all other code.

- crossing a page boundary (more on that later).

 

 

 

This is a lot to chew, but you can do it. Just work your way through it slowly, and ask questions if you don't understand. Once it works try changing the values that you load for horizontal positioning, and observe the results.

  • Like 1
Link to comment
Share on other sites

- confusing LDA #$80 with LDA $80. One means load a value, the other means load from a ram location. Very different results indeed.

Ironically, I would have gone along my jolly way never knowing the difference and then came back here wondering what was going wrong, had you not mentioned it. Details, details. :D

 

- forgeting that X and A have been modified during the routine. In other words if X was previously holding your scanline count, then it will no longer as it was loaded with 0.

So, in other words, I have to re-modify it after the routine by loading whatever value is needed again and then continue on?

 

- putting JSR all the way to left on your source code. You need indentation! This is an instruction, not a label.

 

- putting the subroutine in your main loop. Put it within the 4k romsize, but outside all other code.

So pretty much anywhere that's outside of the main loop, correct?

 

- crossing a page boundary (more on that later).

Right. Adds an extra cycle and throws everything off. Then I'd have to make up for it by subtracting one somewhere, correct?

 

This is a lot to chew, but you can do it. Just work your way through it slowly, and ask questions if you don't understand. Once it works try changing the values that you load for horizontal positioning, and observe the results.

Currently, a lot of my not understanding comes from not understanding how binary/hexadecimal math works, how to put everything together to make it work, not knowing how many cycles each op takes, etc. I'm working on it, though. Thanks for all the help you have been. =]

Link to comment
Share on other sites

So, in other words, I have to re-modify it after the routine by loading whatever value is needed again and then continue on?

 

So pretty much anywhere that's outside of the main loop, correct?

Correct on both accounts.

 

Right. Adds an extra cycle and throws everything off. Then I'd have to make up for it by subtracting one somewhere, correct?

You want to avoid it altogether. In the HorizPositioning subroutine this is mission cricital because that routine is entirely cycle specific.

 

So put it within a page by placing an ORG statement before it (ie ORG $FC00 etc...), or "align 256". Align 256 takes the next 256 bytes of code and puts it within a page. It's more flexibile then ORG, but I still prefer ORG myself.

 

Currently, a lot of my not understanding comes from not understanding how binary/hexadecimal math works, how to put everything together to make it work, not knowing how many cycles each op takes, etc.

There should be lots of places on the web to learn HEX and binary. Probably easiest just to look on youtube for some short videos. For 6502 instructions and cycles here is a good website:

 

http://www.6502.org/tutorials/6502opcodes.html

Link to comment
Share on other sites

So, in other words, I have to re-modify it after the routine by loading whatever value is needed again and then continue on?

 

So pretty much anywhere that's outside of the main loop, correct?

correct on both accounts

Alright, progress! ^^

 

Right. Adds an extra cycle and throws everything off. Then I'd have to make up for it by subtracting one somewhere, correct?

You want to avoid it altogether. In the HorizPositioning subroutine this is mission cricital because that routine is entirely cycle specific.

 

So put it within a page by placing an ORG statement before it (ie ORG $FC00 etc...), or "align 256". Align 256 takes the next 256 bytes of code and puts it within a page. It's more flexibile then ORG, but I still prefer ORG myself.

Alright, I'll remember that as I'm toying with it. ^^

 

I can understand how being specific would mean that I don't want to jump pages. So, in other words, either fit it all on the zero page or handle horizontal motion on a second page, correct?

 

Currently, a lot of my not understanding comes from not understanding how binary/hexadecimal math works, how to put everything together to make it work, not knowing how many cycles each op takes, etc.

There should be lots of places on the web to learn HEX and binary. Probably easiest just to look on youtube for some short videos. For 6502 instructions and cycles here is a good website:

 

http://www.6502.org/tutorials/6502opcodes.html

Duly noted. I've bookmarked it for reference. I'd have it open, but I already have like 8 tabs open dealing with Atari programming alone currently. xD

 

Also, as I asked in another thread, are there any tutorials/guides/walkthroughs that delve into more complex code without the need to learn from other people's code?

Link to comment
Share on other sites

I can understand how being specific would mean that I don't want to jump pages. So, in other words, either fit it all on the zero page or handle horizontal motion on a second page, correct?
Zero page actually refers to an area of ram.

 

In truth you'll worry mostly about page boundarys in your kernal, or positioning routines. Other code shouldn't cause you too much concern.

 

Also, as I asked in another thread, are there any tutorials/guides/walkthroughs that delve into more complex code without the need to learn from other people's code?

None that I'm aware of. You'll either have to search specific questions, or look at some source code and disassemblies that have been done. The Stella Mailing list archives are also a treasure trove.

Link to comment
Share on other sites

I can understand how being specific would mean that I don't want to jump pages. So, in other words, either fit it all on the zero page or handle horizontal motion on a second page, correct?
Zero page actually refers to an area of ram.

 

In truth you'll worry mostly about page boundarys in your kernal, or positioning routines. Other code shouldn't cause you too much concern.

Alright, I'm sure I'll come to terms with that knowledge once I've got more experience. For now, I sit here and smile and nod (such a teenager move, right? xD).

 

EDIT: Apologies for the sarcasm. I understood. :D

 

Also, as I asked in another thread, are there any tutorials/guides/walkthroughs that delve into more complex code without the need to learn from other people's code?

None that I'm aware of. You'll either have to search specific questions, or look at some source code and disassemblies that have been done. The Stella Mailing list archives are also a treasure trove.

Alright, looks like I know where I'll be spending a lot of my time. I only ask because I really get lost when looking at disassemblies of commercial games. Even the Donkey Kong disassembly confused me a bit.

Edited by johnon
Link to comment
Share on other sites

Once you get that dot going okay on the screen, then we can look at writing code to make it move by itself, say left to right.

 

After that we can try making it move by reading the joystick. And if that goes well we can try adding 2 dots, and make them interact with each other. By interact I mean if they touch what happens? Do they bounce away form each other? etc...

 

This will start giving you an idea of how the system works. Afterwards you can add the fancy graphics, score, music, etc... But first you must learn the basics of drawing, control, and interactions.

Link to comment
Share on other sites

Okay, so I officially started messing with this code today. what does each X value represent in the subroutine? You said that it is used to choose player, missile, ball, etc, but which one is what value?

Link to comment
Share on other sites

In the Stella Programmers Guide there is a nice summary table of the registers. We can see that they are stacked as such:

 

10 RESP0

11 RESP1

12 RESM0

13 RESM1

14 RESBL

 

20 HMP0

21 HMP1

22 HMM0

23 HMM1

24 HMBL

 

 

We are using HMP0,X and RESP0,X. This addressing mode will let us choose between these registers with different values of X.

 

 

X=0 Player 0

X=1 Player 1

X=2 Missile 0

X=3 Missile 1

X=4 Ball

 

 

Pretty easy, eh? If you want to learn more about what address modes there are, then read chapter 4 of this book:

 

http://www.atariarchives.org/mlb/

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...