Saturday, July 21, 2018

6502 Comparison operations


A comparison is simply a subtraction that doesn’t save the results but sets the status flags. It doesn’t set the overflow flag however so implementation is a bit harder than it should be. In anticipation of this instruction I wrote the subtract function to return the result instead of saving it so I could just use the subtraction function. Sadly I noticed the missing flag just when I was getting ready to implement this function so ended up writing a new version. This is probably for the best as the subtraction routine would add a lot of inefficiency to the process so this will work a lot faster.

    private fun performCompare(first:Int, second:Int) {
        val sub = first - second
        setNumberFlags(sub)
        adjustFlag(CARRY_FLAG, (state.flags and NEGATIVE_FLAG) == 0 )
    }

The one reason why you might want to use the proper subtraction method would be in the case of binary coded decimal (BCD) numbers, but when you think about it for a minute it becomes obvious that the flag results would be the same for BCD numbers the only thing that would be different would be the result of the subtraction and as that is discarded anyway this much faster function is ideal for our needs.

The CMP instruction compares the contents of the accumulator with the indicated memory by subtracting the memory from the accumulator while leaving the contents of the accumulator alone. This means that if the accumulator is equal to the memory the zero flag will be set. In cases where the accumulator is greater than the memory the carry flag will be set while the negative flag will be clear. In cases where the accumulator is less than the memory then the carry flag will be clear while the negative flag will be set.

CMP ValueToCompare
BMI lessThan
BEQ equals
; code for greater than here

There are also versions of compare for the x register and the y register, which are CPX and CPY. These work the same as CMP but using their respective register instead of the accumulator. This is good as it allows more control over looping, for instance, here is a routine for copying 20 bytes from Source to Dest.

LDX #0
CopyLoop:
LDA Source,X
STA Dest,X
CPX #20
BMI CopyLoop


CMPCoMPare accumulator with memory
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
#Immediate
201
$C9
2
2
Zero Page
197
$C5
2
3
Zero Page,X
213
$D5
2
4
Absolute
205
$CD
3
4
Absolute,X
221
$DD
3
4-5
Absolute,Y
217
$D9
3
4-5
(Indirect, X)
193
$C1
2
6
(Indirect),Y
209
$D1
2
5-6
Flags affected: CNZ
Cycle Notes: Indirect and indexed modes take extra cycle if page boundaries crossed
Usage: Compare accumulator with memory setting flags as if memory was subtracted from accumulator. Z set if equals. C set if accumulator greater than or equal to memory. N set if accumulator is less than memory.
Test Code:
; CMP Immediate
  CLD
  LDA #0
  TAX
loop: INX
  CLC
  ADC #2
  CMP #20
  BNE loop
  BRK
  ; expect x=10,a=20,z=1
 
 
; CMP Zero Page
  CLD
  LDA #0
  TAX
loop: INX
  CLC
  ADC #2
  CMP 200
  BMI loop
  BRK
.ORG 200
.BYTE 20
  ; expect x=10,a=20,z=1

 
; CMP Zero Page,X
  CLD
  LDA #0
  TAY
  LDX #10
loop: INY
  CLC
  ADC #2
  CMP 190,X
  BCC loop
  BRK
.ORG 200
.BYTE 20
  ; expect x=10,a=20,z=1


; CMP Absolute
  CLD
  LDA #0
  TAX
loop: INX
  CLC
  ADC #2
  CMP 512
  BMI loop
  BRK
.ORG 512
.BYTE 20
  ; expect x=10,a=20,z=1

 
; CMP Absolute,X find 4 index
  LDA #4
  LDX #255
loop: INX
  CMP 512,X
  BNE loop
  BRK
.ORG 512
.BYTE 1 2 3 4
  ; expect A=4, X=3, Z=1, N=0, C=1


; CMP Absolute,Y
  LDA #5
  LDY #255
loop: INY
  CMP 512,Y
  BNE loop
  BRK
.ORG 512
.BYTE 1 2 3 4 5
  ; expect A=5, X=4, Z=1, N=0, C=1


; CMP (Indirect,X)
  LDA #69
  LDX #2
  LDY #0
  CMP (200,X)
  BEQ done
  LDY 512
done: BRK 
.ORG 200
.WORD 0 512
.ORG 512
.BYTE 42
  ; Expect A=69,Y=42
 
; CMP (Indirect),Y
  LDA #3
  LDY #255
loop: INY
  CMP (200),Y
  BNE loop
  BRK
.ORG 200
.WORD 512
.ORG 512
.BYTE 1 2 3 4 5
  ; expect A=3, X=2, Z=1, N=0, C=1

Implementation:
// #immediate
performCompare(state.acc, mem.read(state.ip+1))
// zero page
performCompare(state.acc, mem.read(mem.read(state.ip+1)))
// zero page,X
performCompare(state.acc, mem.read(mem.read(state.ip+1) + state.x))
//absolute
performCompare(state.acc, mem.read(findAbsoluteAddress(state.ip)))
//absolute,X
performCompare(state.acc, mem.read(findAbsoluteAddress(state.ip)+state.x))
//absolute,Y
performCompare(state.acc, mem.read(findAbsoluteAddress(state.ip)+state.y))
// (indirect, X)
performCompare(state.acc, mem.read(findAbsoluteAddress(((mem.read(state.ip+1)+state.x) and 255)-1)))
// (indirect),Y
performCompare(state.acc, mem.read(findAbsoluteAddress(mem.read(state.ip+1) -1) + state.y))


 CPX   Compare Memory and Index X
CPXComPare X register with memory
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
#Immediate
224
$E0
2
2
Zero Page
228
$E4
2
3
Absolute
236
$EC
3
4
Flags affected: CNZ
Usage: Compare X Register with memory setting flags as if memory was subtracted from X. Z set if equals. C set if X greater than or equal to memory. N set if X is less than memory.
Test Code:
; CPX Immediate - compute 3x20 the hard way
  CLD
  LDA #0
  TAX
loop: INX
  CLC
  ADC #3
  CPX #20
  BNE loop
  BRK
  ; expect x=20,a=60,z=1
 
 
; CPX Zero Page - compute 5x5 the hard way
  CLD
  LDA #0
  TAX
loop: INX
  CLC
  ADC #5
  CPX 200
  BCC loop
  BRK
.ORG 200
.BYTE 5
  ; expect x=5,a=25,c=1

 
; CPX Absolute - Compute 6x7 the hard way
  CLD
  LDA #0
  TAX
loop: INX
  CLC
  ADC #7
  CPX 520
  BMI loop
  BRK
.ORG 520
.BYTE 6
  ; expect x=6,a=42,n=0

Implementation:
// #immediate
performCompare(state.x, mem.read(state.ip+1))
// zero page
performCompare(state.x, mem.read(mem.read(state.ip+1)))
//absolute
performCompare(state.x, mem.read(findAbsoluteAddress(state.ip)))




CPYComPare Y register with memory
Address Mode
Decimal OPCode
Hexadecimal OpCode
Size
Cycles
#Immediate
192
$C0
2
2
Zero Page
196
$C4
2
3
Absolute
204
$CC
3
4
Flags affected: CNZ
Usage: Compare Y Register with memory setting flags as if memory was subtracted from Y. Z set if equals. C set if Y greater than or equal to memory. N set if Y is less than memory.
Test Code:
; CPY Immediate - compute 3x20 the hard way
  CLD
  LDA #0
  TAY
loop: INY
  CLC
  ADC #3
  CPY #20
  BNE loop
  BRK
  ; expect y=20,a=60,z=1
 
 
; CPY Zero Page - compute 5x5 the hard way
  CLD
  LDA #0
  TAY
loop: INY
  CLC
  ADC #5
  CPY 200
  BCC loop
  BRK
.ORG 200
.BYTE 5
  ; expect Y=5,a=25,c=1

 
; CPY Absolute - Compute 6x7 the hard way
  CLD
  LDA #0
  TAY
loop: INY
  CLC
  ADC #7
  CPY 520
  BMI loop
  BRK
.ORG 520
.BYTE 6
  ; expect y=6,a=42,n=0

Implementation:
// #immediate
performCompare(state.y, mem.read(state.ip+1))
// zero page
performCompare(state.y, mem.read(mem.read(state.ip+1)))
//absolute
performCompare(state.y, mem.read(findAbsoluteAddress(state.ip)))


No comments:

Post a Comment