Help with NMI and Game Logic

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

lidnariq
Posts: 9510
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Help with NMI and Game Logic

BioMechanical Dude wrote:there's no other way to transfer values between the Accumulator, X and Y registers (...or is there?)
TAX, TAY, TXA, TYA. Arguably TXS and TSX.
and that you can't do any mathematical functions on the X and Y registers.
Mostly true, but INX, DEX, INY, DEY. And AXS...

Also, if you have enough spare ROM for various lookup tables, LDX \$ZZZZ,y and LDY \$ZZZZ,x

tokumaru
Posts: 11770
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Help with NMI and Game Logic

BioMechanical Dude wrote:But from what I can tell, the Rotate functions push either the leftmost or rightmost bit into the carry flag. (Is that right?)
The difference between shifting and rotating is that shifts insert 0 into the value, while rotations insert whatever is in the carry, but both will push either the leftmost or rightmost bit into the carry.
Does that mean that, if I have bit 7 set to one and I do a ROL, the carry flag will be equal to one and all of the bits will be set to zero?
No, this happens when you ASL. ROL will result in the carry being set and the accumulator containing either \$00 or \$01, depending on whether the carry was 0 or 1 before the ROL.
What happens if I do another ROL?
The carry (which is set) will be inserted into the value from the right and 0 will be put into the carry, so the value will be either \$01 or \$03, depending on the state of the carry before the first ROL.
Also, given that I've seen the BEQ and BNQ functions being used without the CMP function, does this mean that BEQ compares the Carry flag to 0?
There's no BNQ instruction, you probably mean BNE. These two instructions will branch or not based on the Z (zero) flag, not the carry (BCC and BCS will use the carry). CMP is just one of the many instructions that affect the Z flag, but loading, adding, subtracting, shifting, etc. will also affect it (along with other flags). A decent 6502 reference document will tell you exactly which flags are affected by which instructions.
Well, originally, the code was much simpler, but then I found out that there's no other way to transfer values between the Accumulator, X and Y registers (...or is there?), and that you can't do any mathematical functions on the X and Y registers.
In this particular case, it isn't a matter of what can or can't be done with the X and Y register, but whether they are needed at all. As you can see now, this particular task can be performed using only the accumulator. Don't worry too much about this for now, you'll eventually get the hang of how to do stuff in less steps and using less resources.

BioMechanical Dude
Formerly AlienX
Posts: 137
Joined: Fri Apr 18, 2014 7:41 am
Location: Bulgaria

Re: Help with NMI and Game Logic

OK, got the controller check code fixed, so moving around works now.

However I reached another problem. I have to ask, does ASM6 have a set certain number of lines or cycles before a branch becomes out of range? When I try to add code for horizontal and vertical movement, I can only get to moving in 3 directions (not counting the additional ones, when you press two buttons at the same time) Unfortunately I deleted the code, but this is what it kinda looked like:

Code: Select all

``````NOTE: I'm typing this from memory, so there might be some errors, unrelated to the problem itself.

Left:
LDA Buttons
AND #%00000010
BEQ Right
LDX #\$00
Loop:
LDA \$0203, X
SEC
SBC #\$01
STA \$0203, X
TXA
CLC
TAX
CPX #\$10
BNE Loop

Right:
...

CheckDone:
Execute the rest of the code``````
As I previously mentioned, I can only go up to three checks for three directions. Anything more gives me a Branch Not in Range error, even though the branch I'm trying to get to is right next to the code that branches to it. I tried compressing movement in all directions into one or two loops, but it didn't work. I get the same result. I don't remember any of this happening with NESASM3, so is the problem in ASM6? What is it exactly and is there a more compact way to have code for movement in all directions?

Thanks!
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.

Sogona
Posts: 186
Joined: Thu Jul 23, 2015 7:54 pm
Location: USA
Contact:

Re: Help with NMI and Game Logic

You can gan go 129 bytes forward and 126 bytes backward. It should be -127 - 128 but for whatever reason the people who designed the 6502 fucked up with the microcode. I believe this is also responsible why the address pushed onto the stack when jumping to a subroutine is off by 1.

tokumaru
Posts: 11770
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Help with NMI and Game Logic

BioMechanical Dude wrote:However I reached another problem. I have to ask, does ASM6 have a set certain number of lines or cycles before a branch becomes out of range?
"Branch Not in Range" is not an ASM6 limitation, but a 6502 limitation. A branch's offset is specified by an 8-bit signed value, whose range is -128 to 127, so that's how far you can go. The generic solution is to check for the opposite condition and branch over a JMP, so this:

Code: Select all

``````	lda Variable
beq IsZero
;(lots of code here)
IsZero:``````
...becomes this:

Code: Select all

``````	lda Variable
bne IsNotZero
jmp IsZero
IsNotZero:
;(lots of code here)
IsZero:``````
But you should generally try to design your code in a way that avoids distant branches in the first place.
Anything more gives me a Branch Not in Range error, even though the branch I'm trying to get to is right next to the code that branches to it.
A branch to a nearby location (i.e. less than 128 bytes away) should never result in an error, but since you don't have the original code anymore, it will be hard for anyone to say anything conclusive about this problem.
Sogona wrote:the people who designed the 6502 fucked up with the microcode.
I don't think they fucked up anything, it's just that the offset is added to the program counter AFTER the branch instruction (which takes 2 bytes) is read, so you can only go 126 bytes back instead of 128.

lidnariq
Posts: 9510
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Help with NMI and Game Logic

What? The branch is relative to the value of the PC after the branch instruction has been loaded. Why should it rewind the PC to calculate the relative offset?
At the very least, the 8051, PIC, and Z80 all do that too.

Sogona
Posts: 186
Joined: Thu Jul 23, 2015 7:54 pm
Location: USA
Contact:

Re: Help with NMI and Game Logic

Yeah that does make more sense, never really thought much of why it would be like thatm

BioMechanical Dude
Formerly AlienX
Posts: 137
Joined: Fri Apr 18, 2014 7:41 am
Location: Bulgaria

Re: Help with NMI and Game Logic

tokumaru wrote:since you don't have the original code anymore, it will be hard for anyone to say anything conclusive about this problem.
Well the example I've given is a copy of the code (it's just that I had to write it from scratch). I've written only the check for the Left button, but the code is pretty much the same(with some minor differences regarding ADC, SBC and the addresses) for all other checks. After it's done checking for the Left button, it goes to the Right, from there it goes to Up, that one goes to Down and finally it gets to CheckDone. There's no way to shorten it(Aside from changing ADC and SBC to INC and DEC, but if I wanted to use higher values, that wouldn't have worked.
tokumaru wrote:But you should generally try to design your code in a way that avoids distant branches in the first place.
I'll try(I guess the JMP function might help, unless it also has a limit), but considering how a simple sprite movement loop is too big for the 6502 to branch, this feels next to impossible.

*EDIT: I'm still trying to figure out a way to compress all this code into one loop, that could use variables to determine the direction of movement. Unfortunately, the loops still get quite large. My two main problems: I have no choice but to have different code for adding and subtraction (it's not like in a high-level language, where I could just set the "speed" to a negative number); There's no way to make two variables, that would represent an address and then load that address into the Accumulator. I suppose I could use X to change the low byte of an address, when writing to it, but even that can get complicated.
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.

tokumaru
Posts: 11770
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Help with NMI and Game Logic

BioMechanical Dude wrote:it's not like in a high-level language, where I could just set the "speed" to a negative number
Yes you can. Several games do this.

Another thing you can do to make the code less spaghetti-like is to put the individual movements into subroutines, then you can branch around as much as you like, since the code would be just JSRs.

Garth
Posts: 184
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: Help with NMI and Game Logic

Sogona wrote:You can gan go 129 bytes forward and 126 bytes backward. It should be -127 - 128 but for whatever reason the people who designed the 6502 fucked up with the microcode. I believe this is also responsible why the address pushed onto the stack when jumping to a subroutine is off by 1.
The 6502 does not use microcode, but rather a program logic array. It does a lot of small-scale pipelining, and I suspect what's happening here is that the program counter is already advanced to the first byte of the next instruction when the branch calculation is being done, so a branch with a distance of 0 is a do-nothing instruction. Since the branch-taken scenario takes one more cycle than the branch-not-taken scenario, this may actually be useful for timing, using a flag to determine whether to take two or three clocks. (Be aware that a branch will take four clocks if it crosses a page boundary; but most branches won't cross a page boundary.)

The pipelining is not deep, but does allow the 6502 to make better use of clock cycles than its contemporaries. Take ADC # for example. There are five distinct steps, but it only takes two cycles, finishing up while the next op code is being fetched. An interrupt's operation is different from that of JSR in more ways than just pushing the status and setting the interrupt-disable flag (plus clearing the D flag on the 65c02); and I'm sure the pipelining worked out better for performance and silicon real estate the way the designers did it.
BioMechanical Dude wrote:

Code: Select all

``````NOTE: I'm typing this from memory, so there might be some errors, unrelated to the problem itself.

Left:
LDA Buttons
AND #%00000010
BEQ Right
LDX #\$00
Loop:
LDA \$0203, X
SEC
SBC #\$01
STA \$0203, X
TXA
CLC
TAX
CPX #\$10
BNE Loop

Right:
...

CheckDone:
Execute the rest of the code``````
Since you're trying to shorten the code so the branches reach better, take the four lines starting with LDA \$0203, X and replace them with the one instruction DEC \$0203, X. You'll save six bytes, and it will take 6 cycles to execute instead of 12. The only reason not to do this is if you need it in decimal, since increment and decrement instructions are always in hex, regardless of the state of the D flag. The next four lines can be replaced with four INX's. It will run the same speed, but save a byte. You apparently have a table starting at \$203. If there's no need to go up in the indexing, ie, if it would work just as well to start at the top and go down instead, that will remove the need for the CPX #\$10, because coming down, you can take advantage of the automatic, implied compare-to-0 in DEX before the BNE loop. If you really do need to go in ascending order, you can start with a different value of X (make it \$F0) and a different base (start with \$113) such than INX will get you up to 0 instead of down to zero. Now it becomes:

Code: Select all

``````Left:
LDA Buttons
AND #%00000010
BEQ Right
LDX #\$F0
Loop:
DEC \$0113, X
INX
INX
INX
INX
BNE Loop

Right:
...
``````
I'm not familiar with your hardware; but if you can put whatever the "1" in your AND# instruction represents into bit 6 or 7, then you can use BIT Buttons and you can get rid of the AND# instruction too. Regardless of what was in the accumulator, bit 7 of whatever is read by BIT goes into the N flag, and bit 6 goes into the V flag, and you can branch on those with BPL, BMI, BVC, or BVS. Now the accumulator goes undisturbed for the entire routine.
http://WilsonMinesCo.com/ lots of 6502 resources

tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: Help with NMI and Game Logic

http://www.pcedev.net/blog/files/Otaku_ ... _0_1_4.png
^ While this is HuC6280, it's 65x based. You can use the diagram at the top/right of the page for quick logic of ROR, ROL, ASL, LSR.

(and here's the other page of it, though I doubt it'll be of much use: http://www.pcedev.net/blog/files/Otaku_ ... v1_0_3.png)

Someone should make an NES cribsheet like I did with the PCE. Make it four pages though (one page for PPU characteristics, and common mapper specs - and one page for audio).
__________________________
http://pcedev.wordpress.com