Saturday, August 18, 2018

Boolean Arithmetic


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.

ANDlogical 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))



EORlogical 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))



ORAOR 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))