Now we are ready to
look at some binary specific operations, better known as Boolean operations.
These operations, like addition and subtraction, are limited to the accumulator
and perform the desired Boolean operation with a value in memory. The 6502 supports
3 Boolean operations, AND, ORA, and EOR representing logical and, logical or,
and logical exclusive or. These operations work on the bits within the byte
performing the operation between the two bits in the same position within the
byte.
AND requires that
both the bits be 1 for the resulting bit to be 1 making the resulting bit 0 if
either or both of the bits are 0. OR just requires one of the bits to be 1
number. Exclusive OR requires that one and only one of the bits be one to
result in one otherwise it results in 0. The obligatory logic table is below.
First Bit
|
Second Bit
|
AND
|
OR aka ORA
|
XOR aka EOR
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
1
|
1
|
1
|
0
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
0
|
The biggest use of
the Boolean logic operations is for manipulating bits within a byte. Remember
that the Atari 2600 only had 128 bytes of RAM and a 4K cartridge. This meant
that every bit of memory was important. Boolean instructions let you compact bits
of data together into a single byte potentially saving memory at the cost of
slightly more complicated code.
The AND command is
good for masking out bits. You simply form a byte with the bits that you want
to use set and any other bits in the byte will become zero. If you want to make
a bit 0 you can create a byte that has every bit but the bit you want to be 0
set and then AND this byte with the value being changed.
The ORA command is
handy for setting bits. Any 1 bits that you OR the accumulator with become set.
The EOR command is
the odd one but is probably the most useful of the three once you start to
understand how it works. It is essentially a toggle. This means that any bit
that you set to 1 will be switched to the opposite that it was while leaving
the other bits alone. One really neat trick that can be done with exclusive or
is to switch two variables as follows:
LDA first ;
first/a = 11110000 second=00001111
EOR second ; first =
11110000 second=00001111 acc = 11111111
STA first ;
first = 11111111 second = 00001111 acc=11111111
EOR second ; first
11111111 second 00001111 acc=11110000
STA second ; first
11111111 second 11110000 acc=11110000
EOR first ;
first 11111111 second 11110000 acc = 00001111
STA first ;
first 00001111 second 11110000
BRK
first: .BYTE
%11110000
second: .BYTE
001111
This works but there
are many more efficient ways of swapping 2 variables. Still, working through
the above program with different values for first and second will really
let you get the hang of the exclusive or command.
AND – logical AND accumulator with
memory
Address Mode
|
Decimal OPCode
|
Hexadecimal OpCode
|
Size
|
Cycles
|
#Immediate
|
41
|
$29
|
2
|
2
|
Zero Page
|
37
|
$25
|
2
|
3
|
Zero Page,X
|
53
|
$35
|
2
|
4
|
Absolute
|
45
|
$2D
|
3
|
4
|
Absolute,X
|
61
|
$3D
|
3
|
4-5
|
Absolute,Y
|
57
|
$39
|
3
|
4-5
|
(Indirect, X)
|
33
|
$21
|
2
|
6
|
(Indirect),Y
|
49
|
$31
|
2
|
5(?)
|
Flags affected: NZ
Cycle Notes: Indirect and indexed modes take extra
cycle if page boundaries crossed
Usage: Performs logical AND with accumulator
storing results to accumulator. Bits that match stay 1 while all other
combinations are 0. This allows you to selectively turn off bits.
Test Code:
; AND Immediate and
Zero Page
LDA
#$F0
AND
#$33 ; $30 result
TAX
AND
200 ; 30 and 03 = 0 to test Z
BRK
.ORG 200
.BYTE 3
;
expect X=$30, A =0, Z=1, N=0
; AND Absolute and
Zero Page,X
LDA
$F0
AND
512 ; $90 result
TAY
LDX
#11
AND
189,X ; 90 and 88 = 8 to test Z
BRK
.ORG 200
.BYTE 88
.ORG 512
.BYTE $99
;
expect Y=$90, A =128, Z=0, N=1
; AND Absolute,X and
(Indirect,X)
LDA
$0F
LDX
#11
AND
(189,X) ; $09 result
TAY
AND
502,X ; 9 and 88 = 8
BRK
.ORG 200
.WORD 512
.ORG 512
.BYTE $99 $88
;
expect Y=$9, A =8, Z=0, N=0
; AND Absolute,Y
and (Indirect,Y)
LDA
$0F
LDY
#11
AND
(200),Y ; $09 result
TAX
AND
502,Y ; 9 and 88 = 8
BRK
.ORG 200
.WORD 501
.ORG 512
.BYTE $99 $88
;
expect Y=$9, A =8, Z=0, N=0
Implementation:
// #immediate
state.acc = setNumberFlags(state.acc
and mem.read(state.ip+1))
// zero page
state.acc = setNumberFlags(state.acc
and mem.read(mem.read(state.ip+1)))
// zero page,X
state.acc = setNumberFlags(state.acc
and mem.read(mem.read(state.ip+1) + state.x))
//absolute
state.acc = setNumberFlags(state.acc
and mem.read(findAbsoluteAddress(state.ip)))
//absolute,X
state.acc = setNumberFlags(state.acc
and loadByteFromAddress(findAbsoluteAddress(state.ip),state.x,true,false))
//absolute,Y
state.acc = setNumberFlags(state.acc
and loadByteFromAddress(findAbsoluteAddress(state.ip),state.y,true,false))
// (indirect, X)
state.acc = setNumberFlags(state.acc
and mem.read(findAbsoluteAddress(((mem.read(state.ip+1)+state.x) and 255)-1)))
// (indirect),Y
state.acc = setNumberFlags(state.acc
and mem.read(findAbsoluteAddress(mem.read(state.ip+1) -1) + state.y))
EOR – logical Exclusive-OR
accumulator with memory
Address Mode
|
Decimal OPCode
|
Hexadecimal OpCode
|
Size
|
Cycles
|
#Immediate
|
73
|
$49
|
2
|
2
|
Zero Page
|
69
|
$45
|
2
|
3
|
Zero Page,X
|
85
|
$55
|
2
|
4
|
Absolute
|
77
|
$4D
|
3
|
4
|
Absolute,X
|
93
|
$5D
|
3
|
4-5
|
Absolute,Y
|
89
|
$59
|
3
|
4-5
|
(Indirect, X)
|
65
|
$41
|
2
|
6
|
(Indirect),Y
|
81
|
$51
|
2
|
5-6
|
Flags affected: NZ
Cycle Notes: Indirect and indexed modes take extra
cycle if page boundaries crossed
Usage: Performs logical XOR with accumulator
storing results to accumulator. Bits that match become 0 while non-matching
combinations become 1. This allows you to toggle bits.
Test Code:
; EOR Immediate and
Zero Page
LDA
#$F0
EOR
#$33 ; $C3 result
TAX
EOR
200 ; C3 xor C3 = 0 to test Z
BRK
.ORG 200
.BYTE $C3
;
expect X=$C3, A =0, Z=1, N=0
; EOR Absolute and
Zero Page,X
LDA
$F0
EOR
512 ; $FF result
TAY
LDX
#11
EOR
189,X ; FF and 88 = 77
BRK
.ORG 200
.BYTE 88
.ORG 512
.BYTE $0F
;
expect Y=$FF, A =$77, Z=0, N=0
; EOR Absolute,X and
(Indirect,X)
LDA
$0F
LDX
#11
EOR
(189,X) ; $96 result
TAY
EOR
502,X ; 9 and 77 = E1
BRK
.ORG 200
.WORD 512
.ORG 512
.BYTE $99 $77
;
expect Y=$96, A =$E1, Z=0, N=1
; EOR Absolute,Y
and (Indirect,Y)
LDA
$0F
LDY
#11
EOR
(200),Y ; $96 result
TAX
EOR
502,Y ; 9 and 88 = 1E
BRK
.ORG 200
.WORD 501
.ORG 512
.BYTE $99 $88
;
expect Y=$96, A =$1E, Z=0, N=0
Implementation:
// #immediate
state.acc =
setNumberFlags(state.acc xor mem.read(state.ip+1))
// zero page
state.acc =
setNumberFlags(state.acc xor mem.read(mem.read(state.ip+1)))
// zero page,X
state.acc =
setNumberFlags(state.acc xor mem.read(mem.read(state.ip+1) + state.x))
//absolute
state.acc =
setNumberFlags(state.acc xor mem.read(findAbsoluteAddress(state.ip)))
//absolute,X
state.acc =
setNumberFlags(state.acc xor
loadByteFromAddress(findAbsoluteAddress(state.ip),state.x,true,false))
//absolute,Y
state.acc =
setNumberFlags(state.acc xor
loadByteFromAddress(findAbsoluteAddress(state.ip),state.y,true,false))
// (indirect, X)
state.acc =
setNumberFlags(state.acc xor
mem.read(findAbsoluteAddress(((mem.read(state.ip+1)+state.x) and 255)-1)))
// (indirect),Y
state.acc =
setNumberFlags(state.acc xor
loadByteFromAddress(findAbsoluteAddress(mem.read(state.ip+1) -1),
state.y,true,false))
ORA – OR Accumulator with memory
Address Mode
|
Decimal OPCode
|
Hexadecimal OpCode
|
Size
|
Cycles
|
#Immediate
|
9
|
$09
|
2
|
2
|
Zero Page
|
5
|
$05
|
2
|
3
|
Zero Page,X
|
21
|
$15
|
2
|
4
|
Absolute
|
13
|
$0D
|
3
|
4
|
Absolute,X
|
29
|
$1D
|
3
|
4-5
|
Absolute,Y
|
25
|
$19
|
3
|
4-5
|
(Indirect, X)
|
1
|
$01
|
2
|
6
|
(Indirect),Y
|
17
|
$11
|
2
|
5-6
|
Flags affected: NZ
Cycle Notes: Indirect and indexed modes take extra
cycle if page boundaries crossed
Usage: Performs logical XOR with accumulator
storing results to accumulator. Bits that match become 0 while non-matching
combinations become 1. This allows you to toggle bits.
Test Code:
; ORA Immediate and
Zero Page
LDA
#$F0
ORA
#$33 ; $30 result
TAX
LDA
#0
ORA
200 ; 0 and 0 = 0 to test Z
BRK
.ORG 200
.BYTE 0
;
expect X=$F3, A =0, Z=1, N=0
; ORA Absolute and
Zero Page,X
LDA
$F0
ORA
512 ; $F9 result
TAY
LDX
#11
ORA
189,X ; F9 and 88 = F9
BRK
.ORG 200
.BYTE 88
.ORG 512
.BYTE $99
;
expect Y=$F9, A =$F9, Z=0, N=1
; ORA Absolute,X and
(Indirect,X)
LDA
$0F
LDX
#11
ORA
(189,X) ; $9F result
TAY
ORA
502,X ; 9F and 77 = FF
BRK
.ORG 200
.WORD 512
.ORG 512
.BYTE $99 $77
;
expect Y=$9F, A =$FF, Z=0, N=0
; ORA Absolute,Y
and (Indirect,Y)
LDA
$0F
LDY
#11
ORA
(200),Y ; $1F result
TAX
ORA
502,Y ; 1F and 22 = 3F
BRK
.ORG 200
.WORD 501
.ORG 512
.BYTE $11 $22
;
expect X=$1F, A =3F, Z=0, N=0
Implementation:
// #immediate
state.acc =
setNumberFlags(state.acc or mem.read(state.ip+1))
// zero page
state.acc =
setNumberFlags(state.acc or mem.read(mem.read(state.ip+1)))
// zero page,X
state.acc =
setNumberFlags(state.acc or mem.read(mem.read(state.ip+1) + state.x))
//absolute
state.acc =
setNumberFlags(state.acc or mem.read(findAbsoluteAddress(state.ip)))
//absolute,X
state.acc =
setNumberFlags(state.acc or
loadByteFromAddress(findAbsoluteAddress(state.ip),state.x,true,false))
//absolute,Y
state.acc =
setNumberFlags(state.acc or
loadByteFromAddress(findAbsoluteAddress(state.ip),state.y,true,false))
// (indirect, X)
state.acc =
setNumberFlags(state.acc or
mem.read(findAbsoluteAddress(((mem.read(state.ip+1)+state.x) and 255)-1)))
// (indirect),Y
state.acc =
setNumberFlags(state.acc or
loadByteFromAddress(findAbsoluteAddress(mem.read(state.ip+1) -1),
state.y,true,false))