Status flags: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Do we need a merge?)
m (Adjusts the anchors so they don't skip the section heading.)
 
(24 intermediate revisions by 9 users not shown)
Line 1: Line 1:
The 6502 has 6 status flags: carry, decimal mode, interrupt disable, negative, overflow, and zero. Each of these can either be set or clear. The processor stores the current state of each at all times. There are 2^6 = 64 total states these flags can be in.
The '''flags''' register, also called '''processor status''' or just '''P''', is one of the six architectural registers on the 6502 family CPU. It is composed of six one-bit registers. Instructions modify one or more bits and leave others unchanged.


During certain events, a copy of the flags is written to a byte in memory. There are 8 bits in a byte, and when writing to memory, all 8 bits must be written. Since there are only 6 status flags, the unused 2 bits must be given values.
== Flags ==


There are four events that cause the flags to be written to memory: NMI and IRQ vectoring, and the BRK and PHP instructions. Rather than set the unused bits in the copy in memory to the same values in all cases, the 6502 sets one of them based on whether the event was interrupt vectoring or an executed instruction.
Instructions that save or restore the flags map them to bits in the architectural 'P' register as follows:


The flags are copied to memory in this arrangement:
7  bit  0
---- ----
NV1B DIZC
|||| ||||
|||| |||+- [[#C: Carry|Carry]]
|||| ||+-- [[#Z: Zero|Zero]]
|||| |+--- [[#I: Interrupt Disable|Interrupt Disable]]
|||| +---- [[#D: Decimal|Decimal]]
|||+------ (No CPU effect; see: [[#The B flag|the B flag]])
||+------- (No CPU effect; always pushed as 1)
|+-------- [[#V: Overflow|Overflow]]
+--------- [[#N: Negative|Negative]]


Bit 7: Negative<br>
* The PHP (Push Processor Status) and PLP (Pull Processor Status) instructions can be used to retrieve or set this register directly via the stack.
Bit 6: Overflow<br>
* [[Interrupts]] (NMI and IRQ/BRK) implicitly push the status register to the stack.
Bit 5: Always set<br>
* Interrupts returning with RTI will implicitly pull the saved status register from the stack.
Bit 4: Clear if interrupt vectoring, set if BRK or PHP<br>
* The two bits with no CPU effect are ignored when pulling flags from the stack; there are no corresponding registers for them in the CPU.
Bit 3: Decimal mode<br>
* When P is displayed as a single 8-bit register by debuggers, there is no convention for what values to use for bits 5 and 4 and their values should not be considered meaningful.
Bit 2: Interrupt disable<br>
Bit 1: Zero<br>
Bit 0: Carry<br>


Note bits 4 and 5. This means that the following will put $30 into A, not 0:
{{Anchor|C}}
=== C: Carry ===
----
* After ADC, this is the carry result of the addition.
* After SBC or CMP, both of which do subtraction, this flag will be set if no borrow was the result, or alternatively a "greater than or equal" result.
* After a shift instruction (ASL, LSR, ROL, ROR), this contains the bit that was shifted out.
* Increment and decrement instructions do not affect the carry flag.
* Can be set or cleared directly with SEC or CLC.


LDA #$00
{{Anchor|Z}}
PHA      ; pushes $00 on stack
=== Z: Zero ===
PLP      ; clears all 6 status flags
----
PHP      ; pushes $30 on stack
* After most instructions that have a value result, this flag will either be set or cleared based on whether or not that value is equal to zero.
PLA      ; pops the $30 off the stack


Since bit 4 is set if the cause was a BRK instruction, and clear if it was an IRQ, this allows code to determine the cause of its IRQ handler being invoked, since BRK and IRQ both use the vector at $FFFE:
{{Anchor|I}}
=== I: Interrupt Disable ===
----
* When set, IRQ [[interrupts]] are inhibited. NMI, BRK, and reset are not affected.
* Can be set or cleared directly with SEI or CLI.
* Automatically set by the CPU after pushing flags to the stack when any interrupt is triggered (NMI, IRQ/BRK, or reset). Restored to its previous state from the stack when leaving an interrupt handler with RTI.
* If an IRQ is pending when this flag is cleared (i.e. the [[IRQ|/IRQ]] line is low), an interrupt will be triggered immediately. However, the effect of toggling this flag is delayed 1 instruction when caused by SEI, CLI, or PLP.


irq:
{{Anchor|D}}
        STA temp
=== D: Decimal ===
        PLA        ; get saved flags off stack
----
        PHA        ; save again
* On the NES, decimal mode is disabled and so this flag has no effect. However, it still exists and can be observed and modified, as normal.
        AND #$10  ; check bit 4
* On the original 6502, this flag causes some arithmetic instructions to use [[wikipedia:Binary-coded_decimal|binary-coded decimal]] representation to make base 10 calculations easier.
        BNE caused_by_brk
* Can be set or cleared directly with SED or CLD.
caused_by_irq:
        ...
caused_by_brk:
        ....


The above is the only way to determine the cause of the irq handler invocation. One might think that the following would work, if one thought that there was a status flag that told when it was a BRK instruction that invoked the IRQ handler:
{{Anchor|V}}
=== V: Overflow ===
----
* ADC and SBC will set this flag if the signed result would be invalid<ref>[http://www.6502.org/tutorials/vflag.html Article: The Overflow (V) Flag Explained]</ref>, necessary for making signed comparisons<ref>[http://www.6502.org/tutorials/compare_beyond.html#5 Article: Beyond 8-bit Unsigned Comparisons], Signed Comparisons</ref>.
* BIT will load bit 6 of the addressed value directly into the V flag.
* Can be cleared directly with CLV. There is no corresponding set instruction, and the NES CPU does not expose the 6502's Set Overflow (SO) pin.


; wrong way to determine cause of IRQ handler invocation
{{Anchor|N}}
irq:
=== N: Negative ===
        STA temp
----
        PHP        ; save current flags
* After most instructions that have a value result, this flag will contain bit 7 of that result.
        PLA        ; get saved flags off stack
* BIT will load bit 7 of the addressed value directly into the N flag.
        AND #$10  ; check bit 4
        BNE caused_by_brk
caused_by_irq:
        ...
caused_by_brk:
        ....


This fails because there are only 6 status flags, none of which tell the reason the IRQ handler was invoked. This misunderstanding exists because many descriptions of the 6502 incorrectly state that the processor has 7 status flags, the 7th one being the break flag.
{{Anchor|B}}
=== The B flag ===
----
While there are only six flags in the processor status register within the CPU, the value pushed to the stack contains additional state in bit 4 called the B flag that can be useful to software. The value of B depends on what caused the flags to be pushed. Note that this flag does not represent a register that can hold a value, but rather a transient signal in the CPU controlling whether it was processing an interrupt when the flags were pushed. B is 0 when pushed by interrupts ([[NMI]] and [[IRQ]]) and 1 when pushed by instructions (BRK and PHP).


There is no break flag; there are only 6 status flags. As covered above, there are 8 bits in a byte, so when saving a ''copy'' of the status flags in memory, these unused two bits must be set to something. On the 6502, bit 5 of the copy is always set, and bit 4 is set BRK or PHP, clear if an interrupt. When restoring the flags from memory, bits 4 and 5 are ignored, since again, there are only 6 status flags, and thus the extra 2 bits of the byte must be ignored.
{| class="tabular"
|-
! Cause || B flag
|-
| [[NMI]] || 0
|-
| [[IRQ]] || 0
|-
| BRK || 1
|-
| PHP || 1
|}


== See also ==
Because IRQ and BRK use the same IRQ vector, testing the state of the B flag pushed by the interrupt to the stack is the only way for an IRQ handler to distinguish between them. The B flag also allows software to identify whether [[CPU_interrupts#Interrupt_hijacking|interrupt hijacking]] has occurred, where an NMI overrides the BRK instruction, but the B flag is still set by the BRK. However, testing this bit from the stack is fairly slow, which is one reason why BRK wasn't used as a syscall mechanism. Instead, it was more often used to trigger a patching mechanism that hung off the IRQ vector: a single byte in programmable ROM would be forced to 0, and the IRQ handler would pick something to do instead based on the program counter.
*[[CPU status flag behavior]]
 
Some debugging tools, such as [[Visual6502wiki/JssimUserHelp|Visual6502]], display the B flag as bit 4 of P as a matter of convenience.
The user can see it turn off at the start of an interrupt and back on after the CPU reads the vector.
 
== External links ==
*[http://forums.nesdev.org/viewtopic.php?p=64224#p64224 koitsu's explanation]
 
=== References ===
<references/>

Latest revision as of 22:36, 24 October 2024

The flags register, also called processor status or just P, is one of the six architectural registers on the 6502 family CPU. It is composed of six one-bit registers. Instructions modify one or more bits and leave others unchanged.

Flags

Instructions that save or restore the flags map them to bits in the architectural 'P' register as follows:

7  bit  0
---- ----
NV1B DIZC
|||| ||||
|||| |||+- Carry
|||| ||+-- Zero
|||| |+--- Interrupt Disable
|||| +---- Decimal
|||+------ (No CPU effect; see: the B flag)
||+------- (No CPU effect; always pushed as 1)
|+-------- Overflow
+--------- Negative
  • The PHP (Push Processor Status) and PLP (Pull Processor Status) instructions can be used to retrieve or set this register directly via the stack.
  • Interrupts (NMI and IRQ/BRK) implicitly push the status register to the stack.
  • Interrupts returning with RTI will implicitly pull the saved status register from the stack.
  • The two bits with no CPU effect are ignored when pulling flags from the stack; there are no corresponding registers for them in the CPU.
  • When P is displayed as a single 8-bit register by debuggers, there is no convention for what values to use for bits 5 and 4 and their values should not be considered meaningful.

C: Carry


  • After ADC, this is the carry result of the addition.
  • After SBC or CMP, both of which do subtraction, this flag will be set if no borrow was the result, or alternatively a "greater than or equal" result.
  • After a shift instruction (ASL, LSR, ROL, ROR), this contains the bit that was shifted out.
  • Increment and decrement instructions do not affect the carry flag.
  • Can be set or cleared directly with SEC or CLC.

Z: Zero


  • After most instructions that have a value result, this flag will either be set or cleared based on whether or not that value is equal to zero.

I: Interrupt Disable


  • When set, IRQ interrupts are inhibited. NMI, BRK, and reset are not affected.
  • Can be set or cleared directly with SEI or CLI.
  • Automatically set by the CPU after pushing flags to the stack when any interrupt is triggered (NMI, IRQ/BRK, or reset). Restored to its previous state from the stack when leaving an interrupt handler with RTI.
  • If an IRQ is pending when this flag is cleared (i.e. the /IRQ line is low), an interrupt will be triggered immediately. However, the effect of toggling this flag is delayed 1 instruction when caused by SEI, CLI, or PLP.

D: Decimal


  • On the NES, decimal mode is disabled and so this flag has no effect. However, it still exists and can be observed and modified, as normal.
  • On the original 6502, this flag causes some arithmetic instructions to use binary-coded decimal representation to make base 10 calculations easier.
  • Can be set or cleared directly with SED or CLD.

V: Overflow


  • ADC and SBC will set this flag if the signed result would be invalid[1], necessary for making signed comparisons[2].
  • BIT will load bit 6 of the addressed value directly into the V flag.
  • Can be cleared directly with CLV. There is no corresponding set instruction, and the NES CPU does not expose the 6502's Set Overflow (SO) pin.

N: Negative


  • After most instructions that have a value result, this flag will contain bit 7 of that result.
  • BIT will load bit 7 of the addressed value directly into the N flag.

The B flag


While there are only six flags in the processor status register within the CPU, the value pushed to the stack contains additional state in bit 4 called the B flag that can be useful to software. The value of B depends on what caused the flags to be pushed. Note that this flag does not represent a register that can hold a value, but rather a transient signal in the CPU controlling whether it was processing an interrupt when the flags were pushed. B is 0 when pushed by interrupts (NMI and IRQ) and 1 when pushed by instructions (BRK and PHP).

Cause B flag
NMI 0
IRQ 0
BRK 1
PHP 1

Because IRQ and BRK use the same IRQ vector, testing the state of the B flag pushed by the interrupt to the stack is the only way for an IRQ handler to distinguish between them. The B flag also allows software to identify whether interrupt hijacking has occurred, where an NMI overrides the BRK instruction, but the B flag is still set by the BRK. However, testing this bit from the stack is fairly slow, which is one reason why BRK wasn't used as a syscall mechanism. Instead, it was more often used to trigger a patching mechanism that hung off the IRQ vector: a single byte in programmable ROM would be forced to 0, and the IRQ handler would pick something to do instead based on the program counter.

Some debugging tools, such as Visual6502, display the B flag as bit 4 of P as a matter of convenience. The user can see it turn off at the start of an interrupt and back on after the CPU reads the vector.

External links

References