Questions on CLC/SEC and general NMI 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

Post Reply
mr_cannoli
Posts: 4
Joined: Mon Apr 27, 2020 5:30 pm
Location: Pacific Northwest

Questions on CLC/SEC and general NMI logic

Post by mr_cannoli » Mon Apr 27, 2020 5:51 pm

Hi there!
First time poster here, hopefully I'm following this forums formatting rules so feel free to let me know otherwise.

I'm following the Nerdy Nights tutorials and just finished putting my first sprite on the screen. I gave myself the challenge to see if I can
move the sprite back and forth across the screen, and was surprised to see how difficult it was. I have something that now works
as I intended (shown below) but had a few questions on it.

1. I initially did not use CLC and SEC instructions before add and subtract and the movement of the sprite across the screen was rather broken. The sprite would only move right and start back at 00 sometimes... rather than bouncing off the right side of the screen. If I shrunk the boundaries of the x direction from the screens max of 0x00-0xF9, to say 01 and F8 I found that the code would work. I'm confused to why Adding CLC and SEC worked in the first place, as since I'm only adding and subtracting between 00 and F9, I thought there shouldn't be an overflow causing the carry bit to screw up any additions. Why does adding these instructions fix this behaviour?
2. Adding CLC and SEC slowed down the movement of the sprite considerably. Why is this? Is it because the VBLANK is already ready again and it's just waiting for NMI label to return?
3. For what I'm trying to do, is this proper style? Would it be possible to do this outside the NMI loop?

Code: Select all

; removed boiler plate above
;attempt to make the sprite move left and right across the screens width
LDA #$01 ; initial movment direction (to the right)  
STA $0066 ; random address to store direction vector

Forever:
  JMP Forever     ;jump back to Forever, infinite loop
  
NMI:
  LDA #$00
  STA $2003  ; set the low byte (00) of the RAM address
  LDA #$02
  STA $4014  ; set the high byte (02) of the RAM address, start the transfer
  LDA $0066  ; Direction vector for movement 
  CMP #$01
  BEQ MoveRight
  JMP MoveLeft  
  
MoveRight:
 LDA $0203
 CLC 
 ADC #$01 ;calculate the movement 
 STA $0203
 CMP #$F9
 BEQ RtoL 
 RTI
 
MoveLeft:
 LDA $0203
 SEC 
 SBC #$01;
 STA $0203
 CMP #$00
 BEQ LtoR
 RTI  

;following two labels are to switch the direction
;variable that the sprite moves in 
RtoL:
 LDA #$00
 STA $0066
 RTI ; return from NMI interrupt
 
LtoR:
 LDA #$01
 STA $0066
 RTI  ; return from NMi interrupt 
 ; removed boiler plate below
cheers
Last edited by mr_cannoli on Mon Apr 27, 2020 6:31 pm, edited 3 times in total.

User avatar
never-obsolete
Posts: 377
Joined: Wed Sep 07, 2005 9:55 am
Location: Phoenix, AZ

Re: Questions on CLC/SEC and general NMI logic

Post by never-obsolete » Mon Apr 27, 2020 6:01 pm

ADC with carry set will add value+1. The same goes for SBC with carry clear. If you don't know the state of the carry flag beforehand, you should set or clear it depending on the operation you are performing.

Without the CLC/SEC, you were probably moving by 2 each frame instead of 1 as you intended.
. That's just like, your opinion, man .

mr_cannoli
Posts: 4
Joined: Mon Apr 27, 2020 5:30 pm
Location: Pacific Northwest

Re: Questions on CLC/SEC and general NMI logic

Post by mr_cannoli » Mon Apr 27, 2020 6:16 pm

never-obsolete wrote:
Mon Apr 27, 2020 6:01 pm
ADC with carry set will add value+1. The same goes for SBC with carry clear. If you don't know the state of the carry flag beforehand, you should set or clear it depending on the operation you are performing.

Without the CLC/SEC, you were probably moving by 2 each frame instead of 1 as you intended.
This is definitely it and explains the behaviour for the sprite to appear again from the left. Since the sprites initial position was at 0x80, it would have skipped over the collision at 0xF9 and overflowed back the left side of the screen after a few frames. Thank you!

I take it this means that once the carry bit is set from an operation, that it will remain set until it is cleared? Interesting.

User avatar
never-obsolete
Posts: 377
Joined: Wed Sep 07, 2005 9:55 am
Location: Phoenix, AZ

Re: Questions on CLC/SEC and general NMI logic

Post by never-obsolete » Mon Apr 27, 2020 6:26 pm

It will stay until you perform an operation that changes it. This page shows the 6502 instruction set and what flags each one alters.
Last edited by never-obsolete on Mon Apr 27, 2020 6:27 pm, edited 1 time in total.
. That's just like, your opinion, man .

Fiskbit
Posts: 117
Joined: Sat Nov 18, 2017 9:15 pm

Re: Questions on CLC/SEC and general NMI logic

Post by Fiskbit » Mon Apr 27, 2020 6:26 pm

Flags like carry (C), negative (N), overflow (V), and zero (Z) are all bits in P, the flags register. They keep their values until an instruction changes them. An instruction will generally only affect a subset of the flags. Z and N are the most commonly changed, while C is usually changed just from comparisons, addition/subtraction, shifting, and CLC/SEC.

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

Re: Questions on CLC/SEC and general NMI logic

Post by Garth » Mon Apr 27, 2020 7:26 pm

The carry flag is always drawn into the ADC and SBC instructions. If you already know that a previous instruction left it the way you want it, then you can omit the CLC or SEC; but usually an addition or subtraction will require initializing it. (If it's a multi-byte addition or subtraction, only do it before operating on the first, ie, low, byte, not higher bytes.)

I see your ISR is using the accumulator without saving and restoring it. That's a sure way to mess up the background program! The interrupt sequence pushes the processor-status register P, and the RTI pull it back off the stack and restores it; but the accumulator does not experience the saving and restoring automatically. You have to do it in your ISR. Do a PHA at the beginning, and a PLA at the end.

Note that all the arithmetic and logic instructions have a built-in, implied CMP#0 included in them; so for example your CMP #$00 after the SBC #$01 is redundant, a wasted instruction. For the LDA #0 or LDA #1 followed by STA, if you already know the value in the memory location is the other one, you can just increment it or decrement it. It will take the same amount of time to execute, but save a couple of bytes. If you had the CMOS 65c02 and you don't need to keep the value after that, you could test against 01 by just doing a DEA (DEcrement Accumulator). If it's 1, it will turn into a 0 and set the Z flag. It takes the same amount of time as CMP# but saves a byte. Same thing testing for $FF by using INA.
http://WilsonMinesCo.com/ lots of 6502 resources

User avatar
Quietust
Posts: 1555
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Questions on CLC/SEC and general NMI logic

Post by Quietust » Mon Apr 27, 2020 8:22 pm

Garth wrote:
Mon Apr 27, 2020 7:26 pm
I see your ISR is using the accumulator without saving and restoring it. That's a sure way to mess up the background program! The interrupt sequence pushes the processor-status register P, and the RTI pull it back off the stack and restores it; but the accumulator does not experience the saving and restoring automatically. You have to do it in your ISR. Do a PHA at the beginning, and a PLA at the end.
Your sample program does very little during NMI, but if you start expanding it, most of the common NMI tasks will require at least one index register, if not both. As such, you'll want to preserve X and Y as well, typically by doing "PHA; TXA; PHA; TYA; PHA" at the beginning, then "PLA; TAY; PLA; TAX; PLA; RTI" at the end (and also have everything branch to a single instance of RTI rather than sharing it as you did in MoveRight/MoveLeft/LtoR/RtoL).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

Post Reply