CPU power up state: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
No edit summary
m (Fix typo for "initial")
 
(12 intermediate revisions by 5 users not shown)
Line 1: Line 1:
The following results are from a US (NTSC) NES, original front-loading design, RP2A03G CPU chip, NES-CPU-07 main board revision, manufactured in 1988. The memory values are probably slightly different for each individual NES console. Please note that you should NOT rely on the state of any registers after Power-UP and especially not the stack register and RAM ($0000-$07FF).
Initial tests on the power-up/reset state of the CPU/APU and RAM contents were done using an NTSC front-loading NES from 1988 with a RP2A03G CPU on the NES-CPU-07 board revision.


==At power-up==
Countless bugs in [[Game bugs|commercial]] and [[Program compatibility|homebrew]] games exist because of a reliance on the initial system state. An NES programmer should not rely on the state of CPU/APU registers and RAM contents not guaranteed at power-up/reset.
:P = $34<ref>The [[Emulator tests|golden log of nestest]] differs from this in the [[CPU status flag behavior|irrelevant bits 5 and 4 of P]]</ref> (IRQ disabled)<ref>IRQ was first asserted about 1/60 second after power-up, by APU.</ref>
:A, X, Y = 0
:S = $FD<ref name="reset-stack-push">This is due to RESET actually pushing PC and P onto the stack, even though they aren't actually used. See [https://www.youtube.com/watch?v=fWqBmmPQP40&t=41m45s 27c3: Reverse Engineering the MOS 6502 CPU (en)] from 41:45 onward for details</ref>
:$4017 = $00 (frame irq enabled)
:$4015 = $00 (all channels disabled)
:$4000-$400F = $00 (not sure about $4010-$4013)
:All 15 bits of noise channel LFSR = $0000<ref>[https://forums.nesdev.org/viewtopic.php?p=172797#p172797 Noise channel init log]</ref>. The first time the LFSR is clocked from the all-0s state, it will shift in a 1.


:Internal memory ($0000-$07FF) has unreliable startup state. Some machines may have consistent RAM contents at power-on, but others do not.
== CPU ==
:* Emulators often implement a consistent RAM startup state (e.g. all $00 or $FF, or a particular pattern), and flash carts like the [[PowerPak]] may partially or fully initialize RAM before starting a program, so an NES programmer must be careful not to rely on the startup contents of RAM.
{| class="wikitable"
|+ Initial [[CPU registers| CPU Register]] Values
! Register
! At Power
! After Reset
|-
| A, X, Y || $00 || unchanged
|-
| PC || ($FFFC) || ($FFFC)
|-
| S<ref name="reset-stack-push">RESET uses the logic shared with NMI, IRQ, and BRK that would push PC and P. However, like [[Visual6502wiki/6502 BRK and B bit#masking of the stack writes during RESET|some but not all 6502s]], the 2A03 prohibits writes during reset. [https://forums.nesdev.org/viewtopic.php?p=184247#p184247 This test] relies on open bus being precharged by these reads. See [https://www.youtube.com/watch?v=fWqBmmPQP40&t=41m45s 27c3: Reverse Engineering the MOS 6502 CPU (en)] from 41:45 onward for details</ref> || $00 - 3 = $FD || S -= 3
|-
| [[Status flags#C: Carry|C]] || 0 || unchanged
|-
| [[Status flags#Z: Zero|Z]] || 0 || unchanged
|-
| [[Status flags#I: Interrupt Disable|I]] || 1 || 1
|-
| [[Status flags#D: Decimal|D]] || 0 || unchanged
|-
| [[Status flags#V: Overflow|V]] || 0 || unchanged
|-
| [[Status flags#N: Negative|N]] || 0 || unchanged
|}


==After reset==
== APU ==
:A, X, Y were not affected
{| class="wikitable"
:S was decremented by 3 (but nothing was written to the stack)<ref name="reset-stack-push" />
|+ Initial [[APU]] Register Values
:The I (IRQ disable) flag was set to true (status ORed with $04)
! Register
:The internal memory was unchanged
! At Power
:APU mode in $4017 was unchanged
! After Reset
:APU was silenced ($4015 = 0)
|-
| [[APU Pulse|Pulses]] ($4000-$4007) || $00 || unchanged?
|-
| [[APU Triangle|Triangle]] ($4008-$400B) || $00 || unchanged?
|-
| [[APU Triangle|Triangle]] phase || ? || 0 (output = 15)
|-
| [[APU Noise|Noise]] ($400C-$400F) || $00 || unchanged?
|-
| [[APU Noise|Noise]] 15-bit LFSR || $0000 (first clock shifts in a 1)<ref>[https://forums.nesdev.org/viewtopic.php?p=172797#p172797 Noise channel init log]</ref> || unchanged?
|-
| [[APU DMC|DMC]] flags and rate ($4010)<ref name="ebd">[https://forums.nesdev.org/viewtopic.php?t=18278 Eliminator Boat Duel]</ref> || $00 || unchanged
|-
| [[APU DMC|DMC]] direct load ($4011)<ref name="ebd" /> || $00 || [$4011] &= 1
|-
| [[APU DMC|DMC]] sample address ($4012)<ref name="ebd" /> || $00 || unchanged
|-
| [[APU DMC|DMC]] sample length ($4013)<ref name="ebd" /> || $00 || unchanged
|-
| [[APU DMC|DMC]] LFSR || $00? (revision-dependent?) || ? (revision-dependent?)
|-
| [[APU#Status ($4015) | Status]] ($4015) || $00 (all channels disabled) || $00 (all channels disabled)
|-
| [[APU Frame Counter|Frame Counter]] ($4017) || $00 (frame IRQ enabled) || unchanged
|-
| [[APU Frame Counter|Frame Counter]] LFSR<ref name="$4017 lfsr">[https://forums.nesdev.org/viewtopic.php?p=214939#p214939 2A03letterless is missing transistor to set frame counter LFSR on reset]</ref> || $7FFF || revision-dependent
|}
 
=== Revision-dependent Register Values ===
{| class="wikitable"
|+ 2A03 letterless
! Register
! At Power
! After Reset
|-
| [[APU DMC|DMC]] LFSR || $00? || ?
|-
| [[APU Frame Counter|Frame Counter]] LFSR<ref name="$4017 lfsr" /> || $7FFF || unchanged
|}
 
{| class="wikitable"
|+ 2A03E, 2A03G, 2A07, various clones
! Register
! At Power
! After Reset
|-
| [[APU DMC|DMC]] LFSR || $00? || ?
|-
| [[APU Frame Counter|Frame Counter]] LFSR<ref name="$4017 lfsr" /> || $7FFF || $7FFF
|}
 
== RAM contents ==
Internal RAM ($0000-$07FF) and cartridge RAM (usually $6000–$7FFF, depends on mapper) have an unreliable state on power-up and is unchanged after a reset. Some machines may have consistent RAM contents at power-up, but others may not. Emulators often implement a consistent RAM startup state (e.g. all $00 or $FF, or a particular pattern), and [[Flashcart | flashcarts]] may partially or fully initialize RAM before starting a program.
 
Battery-backed save RAM and other types of SRAM/NVRAM have an unreliable state on the first power-up and is generally unchanged after subsequent resets and power-ups. However, there is an added chance of data corruption due to loss of power or other external factors (bugs, cheats, etc). Emulators and flashcarts may initialize save files with a consistent state (much like other sections of RAM) and persist this data without corruption after closing or reloading a game.
 
Because of these factors, an NES programmer must be careful not to blindly trust the initial contents of RAM.
 
== Best practices ==
*Configure the emulator so it provides a random system state and random RAM contents on power-up.
**[https://www.mesen.ca/ Mesen] provides a set of such emulation options recommended for developers, along with a debugger setting to break execution on all reads from uninitialized RAM.
*Refer to the [[Init code|init code]] article when setting up the reset handler. The sample implementation is a good point to start from.
**If you are using an [[Audio drivers|audio driver]], make sure to call its initialization routine in the reset handler before playing any sound.
*If some RAM state is intended to persist across resets, ensure that the checks used to do so are robust against random initial RAM contents. (e.g. unique multi-byte signatures, checksum calculations, etc)
*Validate any data read from potentially unreliable sources before using it. For example, the stats of an RPG character could be checked against valid ranges when loading them from a save.


== See also ==
== See also ==
*[[PPU power up state]]
*[[PPU power up state]]
== Notes ==
 
== References ==
<references />

Latest revision as of 03:52, 3 June 2024

Initial tests on the power-up/reset state of the CPU/APU and RAM contents were done using an NTSC front-loading NES from 1988 with a RP2A03G CPU on the NES-CPU-07 board revision.

Countless bugs in commercial and homebrew games exist because of a reliance on the initial system state. An NES programmer should not rely on the state of CPU/APU registers and RAM contents not guaranteed at power-up/reset.

CPU

Initial CPU Register Values
Register At Power After Reset
A, X, Y $00 unchanged
PC ($FFFC) ($FFFC)
S[1] $00 - 3 = $FD S -= 3
C 0 unchanged
Z 0 unchanged
I 1 1
D 0 unchanged
V 0 unchanged
N 0 unchanged

APU

Initial APU Register Values
Register At Power After Reset
Pulses ($4000-$4007) $00 unchanged?
Triangle ($4008-$400B) $00 unchanged?
Triangle phase ? 0 (output = 15)
Noise ($400C-$400F) $00 unchanged?
Noise 15-bit LFSR $0000 (first clock shifts in a 1)[2] unchanged?
DMC flags and rate ($4010)[3] $00 unchanged
DMC direct load ($4011)[3] $00 [$4011] &= 1
DMC sample address ($4012)[3] $00 unchanged
DMC sample length ($4013)[3] $00 unchanged
DMC LFSR $00? (revision-dependent?) ? (revision-dependent?)
Status ($4015) $00 (all channels disabled) $00 (all channels disabled)
Frame Counter ($4017) $00 (frame IRQ enabled) unchanged
Frame Counter LFSR[4] $7FFF revision-dependent

Revision-dependent Register Values

2A03 letterless
Register At Power After Reset
DMC LFSR $00? ?
Frame Counter LFSR[4] $7FFF unchanged
2A03E, 2A03G, 2A07, various clones
Register At Power After Reset
DMC LFSR $00? ?
Frame Counter LFSR[4] $7FFF $7FFF

RAM contents

Internal RAM ($0000-$07FF) and cartridge RAM (usually $6000–$7FFF, depends on mapper) have an unreliable state on power-up and is unchanged after a reset. Some machines may have consistent RAM contents at power-up, but others may not. Emulators often implement a consistent RAM startup state (e.g. all $00 or $FF, or a particular pattern), and flashcarts may partially or fully initialize RAM before starting a program.

Battery-backed save RAM and other types of SRAM/NVRAM have an unreliable state on the first power-up and is generally unchanged after subsequent resets and power-ups. However, there is an added chance of data corruption due to loss of power or other external factors (bugs, cheats, etc). Emulators and flashcarts may initialize save files with a consistent state (much like other sections of RAM) and persist this data without corruption after closing or reloading a game.

Because of these factors, an NES programmer must be careful not to blindly trust the initial contents of RAM.

Best practices

  • Configure the emulator so it provides a random system state and random RAM contents on power-up.
    • Mesen provides a set of such emulation options recommended for developers, along with a debugger setting to break execution on all reads from uninitialized RAM.
  • Refer to the init code article when setting up the reset handler. The sample implementation is a good point to start from.
    • If you are using an audio driver, make sure to call its initialization routine in the reset handler before playing any sound.
  • If some RAM state is intended to persist across resets, ensure that the checks used to do so are robust against random initial RAM contents. (e.g. unique multi-byte signatures, checksum calculations, etc)
  • Validate any data read from potentially unreliable sources before using it. For example, the stats of an RPG character could be checked against valid ranges when loading them from a save.

See also

References

  1. RESET uses the logic shared with NMI, IRQ, and BRK that would push PC and P. However, like some but not all 6502s, the 2A03 prohibits writes during reset. This test relies on open bus being precharged by these reads. See 27c3: Reverse Engineering the MOS 6502 CPU (en) from 41:45 onward for details
  2. Noise channel init log
  3. 3.0 3.1 3.2 3.3 Eliminator Boat Duel
  4. 4.0 4.1 4.2 2A03letterless is missing transistor to set frame counter LFSR on reset