The V flag

bbc micro/electron/atom/risc os coding queries and routines
Post Reply
julie_m
Posts: 227
Joined: Wed Jul 24, 2019 9:53 pm
Location: Derby, UK
Contact:

The V flag

Post by julie_m » Sat Oct 05, 2019 8:38 pm

The poor little V flag, there in bit 6 of the processor status byte, is often ignored. It is short for "oVerflow", but what does that even mean?

To put it in the simplest terms possible, V means that the N flag is wrong -- an addition has gone outside the range 0 to 127, or a subtraction has gone outside the range -128 to -1, and so caused a false change of sign.

This happens whenever an ADC instruction with a positive addend already in the accumulator and a positive augend specified in the instruction produces a negative-looking sum (when it's actually between 128 and 255); or an SBC instruction with a negative minuend already in the accumulator and a positive subtrahend being taken away from it produces a positive-looking difference (actually between -256 and -129). Subsequent arithmetic operations may push it back into range, of course, and the way twos-complement arithmetic works will mean the answer is then right (they may just as easily make it wronger and wronger; even eventually going past -256 or +255, where things begin to look right again until we exceed -384 or +383 .....) but right now, the important thing we need to know is: if V is set following an addition or subtraction, N is wrong. V=1 and N=1 together indicate a positive number; V=1 and N=0 indicate a negative number. If V=0 then we do not have to worry, of course: N is correct.

(Part of my day job involves testing edge cases. And the V flag is a nice example of an edge case test, right there in the 6502 hardware.)

For all but the most significant byte of a multi-byte quantity, V is as meaningless as N (which is set from the high bit of the last byte seen, but is only the sign bit when it's the very highest bit, i.e., in the highest byte.)

In the case of higher-or-lower comparisons between signed numbers, this is important. CMP performs an unsigned comparison without borrow, and sets C if the subtrahend is smaller than or equal to the minuend and Z if it is equal. That's fine for unsigned numbers. But -128 (which looks like 128) appears larger than 127, and -1 (which looks like 255) appears larger than 1. In fact, all simple comparisons between negative and positive numbers fail this way.

If we add 128 to the negative numbers -128 to -1 (giving a range 0 to 127 instead of -128 to -1) and subtract 128 from the positive numbers 0 to 127 (giving a range -128 to -1, which looks like 128 to 255, instead of 0 to 127) the two ends of the range effectively swap places, putting the whole number line into the right-looking order for the CMP instruction. And that operation is basically just toggling bit 7 of each of the minuend (comparand?) and subtrahend (comparator?), which can be done with EOR #&80.

For multi-byte signed numbers, we can't really use CMP because it doesn't allow for importing a borrow. So instead we will have to do a multi-byte subtraction, starting with SEC and then LDA / SBC pairs. And this is where we are likely to run afoul of sign bit issues.

Here's an example from BCP, of course:

Code: Select all

\ Test whether the point is off to the left (bottom) of the box
\ Y=0 for left, Y=2 for bottom
.testLB
SEC
LDA (ptb),Y \ point base
SBC (bxb),Y \ box base
INY
LDA (ptb),Y
SBC (bxb),Y
\ now if V is set, N will be wrong
BVC testLB1
EOR #&80 \ flip bit 7
.testLB1
\...
If the subtraction of the high byte has produced a false change of sign, V will be set. So immediately after the subtraction we test V and branch forward if it was clear; skipping over an EOR instruction to toggle the sign bit.

Anyway, now you know what V does. Well, almost. Because there is another instruction, besides ADC and SBC, which can affect it: BIT. BIT is limited to absolute addressing (though it has "long" and "short" forms) and performs an AND operation between the contents of the accumulator and the contents of the address supplied in the operand; the result is discarded, but the Z flag is set if all bits of the result were zero. It also sets N and V to bits 7 and 6 respectively of the address contents before the AND -- this enables you to test a flag in either bit 6 or bit 7 of a byte without wasting cycles and without disturbing the contents of the accumulator.

You can test the V flag and conditionally branch with BVC and BVS; and you can clear it with CLV. (If you ever need some code to be able to run independently of its position in memory, CLV followed by BVC will simulate an unconditional relative branch without affecting N, Z or C.) There's no SEV instruction; but if you ever need to set the V flag, you can do it with a BIT instruction pointing to an address which you know contains a value with bit 6 set -- an RTS instruction in your code is as good as anything. Other instructions that can affect V are PLP, RTI and there is also an input pin on the processor to allow it to be set by hardware.

And that's about it for V.

User avatar
0xC0DE
Posts: 669
Joined: Tue Mar 19, 2019 7:52 pm
Location: The Netherlands
Contact:

Re: The V flag

Post by 0xC0DE » Sun Oct 06, 2019 10:07 am

While getting all 6502 instructions (including undocumented) 100% correct in my Acorn Electron emulator, this page was invaluable to me for understanding all the ways that the V-flag is affected, especially in Decimal Mode.
0xC0DE
"I program my home computer / Beam myself into the future"
:arrow: Follow me on Twitter
:arrow: Visit my YouTube channel featuring my demos for Acorn Electron and BBC Micro

User avatar
jgharston
Posts: 4081
Joined: Thu Sep 24, 2009 12:22 pm
Location: Whitby/Sheffield
Contact:

Re: The V flag

Post by jgharston » Sun Oct 06, 2019 11:47 am

If you arrange your code appropriately, you can synthesise a SEV from the bytes of code itself, eg:

Code: Select all

  BIT rts    ; V set from b6 of RTS opcode (also clears M)
  .rts
  RTS        ; opcode is %01100000

  LDA &FE08  ; where absaddr>=&C000
  .bit
  BIT bit-1  ; V set from b14 of &FE08 (also sets M)

  LDA &FD    ; where zpaddr>=&C0
  .bit
  BIT bit-1  ; V set from b6 of &FD (also sets M)
and a quite useful one:

Code: Select all

  BIT jmp    ; V set from b6 of JMP () opcode (also clears M)
  .jmp
  JMP (KEYV) ; opcode is %01101100
See also.

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.32
(C) Copyright J.G.Harston 1989,2005-2020
>_

Post Reply

Return to “programming”