• 0

# Not a Not?

## Question

I don't remember there not being a Not instruction in 6502. I must not have even needed it or it would have made an impression on me.

Still it's easy enough to construct one if necessary:

Not n = 255 - n, for an 8 bit number. Learned that  from Brainf***.

## Recommended Posts

• 0
5 minutes ago, dmc6502 said:

I don't remember there not being a Not instruction in 6502. I must not have even needed it or it would have made an impression on me.

Still it's easy enough to construct one if necessary:

Not n = 255 - n, for an 8 bit number. Learned that  from Brainf***.

There's other ways too. The easiest is

LDA # n

EOR #255

##### Share on other sites

• 0

Yeah, NOT x = x XOR 1. 0 => 1, 1 => 0.

##### Share on other sites

• 0
23 hours ago, dmc6502 said:

I don't remember there not being a Not instruction in 6502. I must not have even needed it or it would have made an impression on me.

Still it's easy enough to construct one if necessary:

Not n = 255 - n, for an 8 bit number. Learned that  from Brainf***.

By contrast, from all the times I have scribbled Forth primitives for different versions of 6502 threading models, EOR #\$FF is tattooed into my memory.

##### Share on other sites

• 0

Hm... 1 = true. 1 EOR \$FF = \$FE, which is also true.

##### Share on other sites

• 0
2 hours ago, iljitsch said:

Hm... 1 = true. 1 EOR \$FF = \$FE, which is also true.

That's confusing a bitwise NOT for a logical NOT.

Logical NOTs, even today, are primarily concerned with equivalency against zero, for the purposes of branching logic. Not that far back into the history of programming, C actually explicitly defined 'TRUE' as '1' and 'FALSE' as '0', making any other value either indeterminate or requiring a comparison by equivalency (e.g. 'if(2 == TRUE)' would evaluate to false and the program would not enter that if block). And I believe there have been a few languages which defined "true" as '0' and "false" as -1.

Even today, the x86 instruction set does not specify a "logical NOT", and determining the logical value of an arbitrary number would typically be implemented by clearing the eax register ('xor eax, eax' to set its bits to 0) and then comparing it against the arbitrary number. If you wanted to assign the result, X86 implements the instructions 'sete' and 'setne' which set a value to 1 or 0 based on the status of the ZF (zero flag) bit on the CPU.

The 6502 would need to jump through some additional hoops, unfortunately, because it lacks equivalent operations to SETE and SETNE. Most likely, I would imagine someone would lda, clc, adc #\$ff, lda #0, rol to booleanize a value. But maybe someone knows a faster way.

##### Share on other sites

• 0
3 hours ago, iljitsch said:

Hm... 1 = true. 1 EOR \$FF = \$FE, which is also true.

No, \$FE is not True. Neither is 1, unless you defined a variable or constant named "True" to mean 1.

In that case, \$FE is not equal to 1, and so it is not "also true."

Then you say something like

Quote

but... if I write 'IF \$FE THEN PRINT "TRUE" ', will print TRUE.

That's because the IF statement in nearly all programming languages does not test for Trueness. It tests for non-zero. And non-zero and true are not the same thing.

So this statement is simply nonsense.

Edited by TomXP411
##### Share on other sites

• 0
10 minutes ago, StephenHorn said:

And I believe there have been a few languages which defined "true" as '0' and "false" as -1.

Yes.

BASIC actually returns a -1 for any logical test, so an expression like 2 > 1 would return -1. And since NOT -1 returns 0, BASIC can be said to define True as -1 and False as 0.

Of course, the symbols TRUE and FALSE do not exist in BASIC 2, but if they did, they would be those two values.

And this is actually mathematically correct: many languages have a specific Boolean type, which stores a value as 1 bit. This means that the "True" value is always -1, when mapped to an integer or floating point number. While it seems strange written on paper, it's actually due to Two's Complement representation, which causes a number to be negative when the high bit is set to 1.

Quote

That's confusing a bitwise NOT for a logical NOT.

Also true, although it's more correct to say that the person who originally made this statement, back in the 60s, knew very well what he was saying, and he was trolling his audience.

Edited by TomXP411
##### Share on other sites

• 0

It's been a while since I wrote any 6502 assembly, but this was quite a formative experience for me so I'm going to assume I remember correctly that it's basically "zero" or "not zero". And in C it's also that way: "zero" evaluates to "false" and "not zero" to "true".

Now it's possible to argue that the results of an IF in a programming language and boolean true/false are different things, but then we're encroaching on the area of theology...

In any event, there is no easy way flip a value that we don't know except that it evaluates to true so that it's now false.

##### Share on other sites

• 0
1 hour ago, iljitsch said:

In any event, there is no easy way flip a value that we don't know except that it evaluates to true so that it's now false.

I would submit:

Quote

; This assumes the A register has been loaded with the value to logically invert.
; Clobbers the Z, C, and N flags.
logical_not:
cmp #1 ; sets carry flag if A >= 1, otherwise carry is cleared
lda #0
rol ; rotate carry bit into A
eor #1 ; flip the bit
rts

This is 8 bytes and 14 cycles, and each call is 3 bytes and 6 cycles. But if you made it a macro, it would only be 7 bytes and 8 cycles per inlining, so definitely a candidate there, particularly since the cost of the lda/sta pair you might bracket it with could easily exceed the runtime of the actual operation.

Of course, if you only wanted to "booleanize" the value, for whatever reason (about to store it into a packed field?), then you don't need the eor #1 at the end, which shaves 2 bytes and 2 cycles off of the operation.

##### Share on other sites

• 0
On 5/28/2021 at 10:14 PM, iljitsch said:

Hm... 1 = true. 1 EOR \$FF = \$FE, which is also true.

If you use Pascal booleans, than it's "LDA N : EOR #1 : AND #1" or "LDA N : DEC : AND #1". If you use all-bits-set true, then for bytes, it's just "EOR #\$FF". And if you have an 1-8 item bit vector, then it's still "EOR #\$FF", as that inverts each bit in the vector, but given that it's a bit vector, the zero flag only tests whether ALL items are false or ANY items are true.

##### Share on other sites

• 0

If one is looking to set or reset a single bit in a zero page address, you can use the SMB or RMB commands. Testing such a single bit for a one or zero can be done with the BBS or BBR commands. So if you want to NOT a single bit you can EOR# n where n is \$01, \$02, \$04, \$08, \$10, \$20, \$40 or \$80, and you can then test for 0 with BBR or test for 1 with BBS.

##### Share on other sites

• 0
On 6/14/2021 at 9:37 AM, Ed Minchau said:

If one is looking to set or reset a single bit in a zero page address, you can use the SMB or RMB commands. Testing such a single bit for a one or zero can be done with the BBS or BBR commands. So if you want to NOT a single bit you can EOR# n where n is \$01, \$02, \$04, \$08, \$10, \$20, \$40 or \$80, and you can then test for 0 with BBR or test for 1 with BBS.

Note that these "Rockwell" instructions are NOT in the "65c02 emulation mode" of the 65816. They were also not in the 65c02's from sources other than Rockwell or WDC, but that doesn't mean much since only the WDC chip is still in active production.

The BBR and BBS save three bytes and two or three clock cycles over:
TSTBIT5:  LDA zp : BIT #\$20 : BEQ target

... though of course that second pattern is a lot more flexible, since it can use any address mode for the data to test and can test for a set of bits being all zero vs one or more nonzero:

TSTLOBITS:  LDA (stack),Y : BIT #\$0F : BEQ target

By contrast (h/t @Ed Minchau), SMB and RMB save 2 cycles  and 2 bytes over TSB/TRB ... and while there is not the same flexibility in addressing, they work on I/O address registers anywhere in memory with the "TSB a" and "TRB a" addressing.

SETBIT5: LDA #20 : TSB zp

Edited by BruceMcF
##### Share on other sites

• 0
17 hours ago, BruceMcF said:

Note that these "Rockwell" instructions are NOT in the "65c02 emulation mode" of the 65816. They were also not in the 65c02's from sources other than Rockwell or WDC, but that doesn't mean much since only the WDC chip is still in active production.

Handily, these commands do work in the x16 emulator as of IIRC Rev 37 and will work on the actual hardware.

##### Share on other sites

• 0
18 hours ago, BruceMcF said:

The SMB and RMB save three bytes and two or three clock cycles over:
TSTBIT5:  LDA zp : BIT #\$20 : BEQ target

SMB and RMB either set or reset a bit, respectively.  BBR branches if the bit is reset (0) and BBS branches if the bit is set (1).

##### Share on other sites

• 0
13 hours ago, Ed Minchau said:

Handily, these commands do work in the x16 emulator as of IIRC Rev 37 and will work on the actual hardware.

Unless someone hacks their machine to use a 65816 ... which is TEMPTING ... or wants to run the code on a 65816 expansion card.

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.