CMP Instruction

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Post Reply
andrewem
Posts: 17
Joined: 21 May 2004
Location: Burlington, ON

CMP Instruction

Post by andrewem »

Hi,

I've been reading up on the CMP instruction (CMP and SBC are the only two that are giving me any grief) and I found some really good information on this web site, and I was doing well until a question poped into my head.

It says on tutorials/compare_beyond.html that "both [CMP and SBC] affect the N, Z, and C flags in exactly the same way".

Given this information I took the source code for my emulated SBC instruction and poped into my CMP instruction (ignoring the carry, not modifying the registers, and ignoring the BCD handling) and the results are NOT the same. Obviously something is wrong.

However, if I take the table (showing how the flags are set) listed on tutorials/compare_instructions.html and implement that in my CMP instruction and not do any subtracting etc. the instruction works perfect.

Am I missing something?

Any clarification on these two instructions would make me so happy... :?

Thanks,

Andrew :?
User avatar
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Post by BitWise »

Computers compare numbers by subtracting one value from the other and looking at result. Thats why CMP and SBC set the flags the same way.

For example if you subtract to equal values the result will be zero and would set the 6502's zero flag. If the values are unequal the result would be non zero. If you subtract a big unsigned value from a small one you generate a 'borrow' which clears the carry flag. Subtracting a small unsigned value from a larger (or equal sized) one does not generate a 'borrow' so the carry would be set.

(I won't complicate this discussion with signed values and overflow -- Look them up when you understand unsigned comparison)

There are a couple of key differences between CMP/CPX/CPY and SBC to remember:
  • 1. CMP/CPX/CPY is not affected by the value of the carry flag at point of execution, SBC is. (CMP/CPX/CPY acts as if there is an implied SEC before the subtraction).
  • 2. CMP/CPX/CPY does not keep the result of the subtraction, it is used only to set the flags then it is discarded, SBC does.
What the document is saying is that settings of the flags after a code sequence like this...

Code: Select all

        SEC
        LDA VAL1
        SBC VAL2
... will be exactly the same as after ...

Code: Select all

        LDA VAL1
        CMP VAL2
... but the value of accumlator is different (i.e. in the first case A = VAL1-VAL2 and in the second A = VAL1)
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
User avatar
dclxvi
Posts: 362
Joined: 11 Mar 2004

Re: CMP Instruction

Post by dclxvi »

andrewem wrote:
It says on tutorials/compare_beyond.html that "both [CMP and SBC] affect the N, Z, and C flags in exactly the same way".
Not quite. It says that CMP and the sequence SEC SBC affect the N, Z, and C flags in the same way. The D flag is assumed to be zero, so really the SBC sequence is CLD SEC SBC. Anyway, you don't have to take my word for it. Here is a program that demonstrates that it is true. Conveniently, it stops at the first counterexample. It takes about 4 seconds (assuming it passes) at 1 MHz. If it doesn't work on the emulator, then the emulator has a bug.

Code: Select all

; Compare N, Z, and C flag results of CMP and SBC
;
; Returns with ERROR = 0 if the test passes, ERROR = 1 if the test fails
;
; Four additional memory locations are used: ERROR, N1, N2, and SNZC
; These may be located anywhere convenient in RAM
;
TEST  CLD       ; just in case
      LDA #1    ; store 1 in ERROR until the test passes
      STA ERROR
      LDA #0    ; initialize N1 and N2
      STA N1
      STA N2
TEST1 LDA N1

      CMP N2    ; compare N2 to N1

      PHP       ; push CMP flags

      SEC       ; subtract N2 from N1
      SBC N2

      PHP       ; store SEC SBC flags in SNZC
      PLA
      STA SNZC
      PLA       ; pull CMP flags
      EOR SNZC
      AND #$83  ; mask N, z, and C flags
      BNE TEST2
      INC N2
      BNE TEST1
      INC N1
      BNE TEST1
      LDA #0    ; the test passes, so store 0 in ERROR
      STA ERROR
TEST2 RTS
I really didn't intend for the tutorial to be an introduction to the CMP instruction. It's more of a compilation of different comparison techniques and optimization tips. The title is "Beyond 8-bit unsigned comparisons" and one section is named "Review of compare instructions', so a familarity with the CMP instruction was assumed.
andrewem wrote:
Given this information I took the source code for my emulated SBC instruction and poped into my CMP instruction (ignoring the carry, not modifying the registers, and ignoring the BCD handling) and the results are NOT the same. Obviously something is wrong.
I can only guess where the problem might be, but when you "ignore" BCD handling, you must subtract as though the D flag is ZERO, and when you "ignore" the carry you must subtract as though it were ONE.

Also, CMP does not affect the V flag, unlike SBC.
Mats
Posts: 111
Joined: 24 Aug 2003

Post by Mats »

Well, I run the code of "dclxvi" and got as result "ERROR=0" confirming (as expected) that:


"CMP and SBC affect the N, Z, and C flags in exactly the same way"

as expected.
Post Reply