Why do so many games write to $400D?

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

Post Reply
TheouAegis
Posts: 4
Joined: Tue May 17, 2022 1:54 pm

Why do so many games write to $400D?

Post by TheouAegis »

My understanding is $400D doesn't do anything, unless perhaps a developer possibly reworks it to function for some other purpose. However, a large number of NES games actually wrote to $400D and I am curious why. Below is a list of some NES games, by devoloper, that wrote non-zero values to $400D. I am inclined to just chalk it up to in-house proprietary software. If this was ever addressed anywhere already, I'd appreciate a point in the right direction. It was the RARE games specifically that got my attention, because they were explicitly writing to $400D in the audio routines. I noticed Konami seemed to use this same code

Code: Select all

 LDA #$30
 STA SQ1_VOL,Y @ NOISE_VOL
 JSR {
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     RTS
 }
 LDA #$7F
 STA SQ1_SWEEP,Y @ $400D
And comparing Battletoads to R.C. Pro-Am II exhibited shared code between the two games. But the question still remains, why even bother with the $400D writes if they don't do anything? I can't just leave it at "proprietary software" and walk away from it, though. That may be the case for most games, but the value written to $400D is inconsistent between games, even within developers, meaning there was some degree of deliberateness.

Coupled with the games which actually vary $400D writes mid-game (sometimes within the span of one sound), there was more going on here. If we presume these rogue bytes are handled similarly to pulse sweeps, then someone could code an emulator extension which would interpret noise sweeps so we could hear how the sound designers possibly intended the games to sound.

(Note: A lot of games set $400D to a persistent value, but why is it even set at all?)

RARE (usually #08 or #43)
  • A Nightmare On Elm Street
  • Anticipation
  • Arch Rivals
  • Battletoads (1,2)
  • Beetlejuice
  • Cabal
  • Captain Skyhawk
  • Cobra Triangle
  • Danny Sullivan's Indy Heat
  • Digger T. Rock: Legend of the Lost City
  • High Speed / PinBot
  • Ivan Stewart's Super Off-Road
  • Jeopardy
  • John Elway's Quarterback
  • Jordan vs. Bird: One-on-One
  • Marble Madness
  • N.A.R.C.
  • Pirates!
  • R.C. Pro-Am (1,2)
  • Seasame Street 123
  • Snake, Rattle 'n' Roll
  • Solar Jetman
  • Super Glove Ball
  • Time Lord
  • Wizards & Warriors (1,2,3)
  • WWF Wrestlemania
Nintendo
  • Kid Icarus
  • Metroid
  • Wild Gunman
Konami (usually #30)
  • Ai Senshi Nicol
  • Armana no Kiseki
  • Bio Miracle Bokutte Upa
  • Blades of Steel
  • Castlevania (1,2)
  • Contra
  • Cosmic Wars
  • Dragon Scroll
  • Esper Dream
  • Falsion
  • Getsu Fuuma Den
  • Goonies (1,2)
  • Gradius
  • Gyruss
  • Jackal (almost feels like debugging)
  • King Kong 2
  • King of the Beach
  • Meikyuu Jiin Debaba
  • Metal Gear (1,2)
  • Mission: Impossible
  • Q-Bert
  • Wai Wai World
Sunsoft
  • Batman (1,2)
  • Blaster Master
  • Dodge Danpei (1,2)
  • Freedom Force
  • Gremlins 2
  • Journey To Silius
  • Mr. Gimmick
  • Platoon
  • Uncle Fester's Quest
  • Xenophobe
Hudson
  • Adventure Island (2,3)
  • Bonk's Adventure
  • Mendel Palace
  • Star Force
  • Xexyz
Capcom
  • Duck Tales 2
  • Yo Noid!
Tecmo (usually #08)
  • Fire 'n' Ice
  • Ninja Gaiden (1,2,3)
  • Radia Senki
  • Solomon's Key
  • Tecmo Baseball
  • Tecmo Bowl
  • Tecmo Cup Soccer
  • Tecmo NBA Basketball
  • Tecmo Super Bowl
  • Tecmo World Wrestling
Jaleco
  • Bio Senshi Dan
  • Ginga Denshou: Galaxy Odyssey
  • Metal Mech
Data East
  • Bad Dudes
  • Boulder Dash
  • Bump 'n' Jump
  • Captain America & The Avengers
  • Captain Silver
  • Heavy Barrel
  • Karate Champ
  • Karnov
  • Rampage
  • Werewolf
SNK
  • Ikari Warriors (1,2)
  • Mechanized Attack
Seta-Taxan
  • 8 Eyes
Sculptured Software
  • Daydreamin' Davey
  • Eliminator Boat Duel
  • Monopoly
  • Stanley: The Search For Dr. Livingston
  • Star Wars: The Empire Strikes Back
  • T & C Surf Designs (1,2)
Taito
  • Kiki Kaikai
  • Operation Wolf
  • Power Blade (1,2)
  • Tetrastar
Compile
  • Zanac
Imagineering
  • Flight of the Intruder
Irem
  • Hammerin' Harry
  • Holy Diver
  • Metal Storm
Activision
  • Ghostbusters II
  • Super Pitfall
Advance Communication Company
  • Dr. Jekyll & Mr. Hyde
Atlus
  • Gotcha
  • Karate Kid
Broderbund Software
  • The Guardian Legend
Culture Brain
  • Flying Dragon
  • Flying Warriors
Game Studio
  • Wizardry
HiScore Mediawork
  • Zombie Hunter
Pony Canyon
  • Phantom Fighter
Softie Inc.
  • Harlem Globetrotters
Source
  • Matchbox Motor City
Tengen
  • Klax
  • Rampart
Titus Software Corp.
  • Blues Brothers
UPL
  • Ninjakun
Vic Tokai
  • Chester Field
  • Clash at Demonhead
  • Golgo 13
  • Kid Kool
Virgin Games
  • Robin Hood: Prince of Thieves
VAP
  • Kick Challenger: Air Foot
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Why do so many games write to $400D?

Post by dougeff »

My notes say that 400d doesn't do anything.

My guess is that since all the other addresses 4000-400f are used, and nothing bad happens of you write to 400d, it was simply easier to program code that wrote to every value than it would be to skip over 1 address.

Also, the code you are showing is an example of a "patch" where they removed some code by replacing it with EA (NOP, no operation).
nesdoug.com -- blog/tutorial on programming for the NES
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: Why do so many games write to $400D?

Post by Drag »

The four APU channels are similar enough that the music engine can reuse the same code to drive all four registers of all four of them, even if it doesn't make sense in certain circumstances. The only exceptions you need to make are for register $4008 (triangle length counter instead of a volume control register), and for register $400E (noise channel pitch, works differently from the other channels). In all other cases, you can just reuse the same code and it won't hurt anything, even if you're writing to unused bits or addresses.

Why write to the sweep registers if you don't use them? It's because the sweep units mute the channel when you try to set the channel's pitch too low, because the sweep unit is trying to prevent itself from wrapping the pitch around while sweeping (even while disabled). To get around this, you set the sweep unit to "up" instead of "down" (write $08), and then you can reach the lowest pitches. If the music engine likes to write some kind of constant to this register, that's probably what it's trying to do (even if the sweep unit doesn't actually exist for the channel).
TheouAegis
Posts: 4
Joined: Tue May 17, 2022 1:54 pm

Re: Why do so many games write to $400D?

Post by TheouAegis »

That little code snippet was just to show that it was copy-pasted into a lot of their games, which lent credence to my suspicion that it was carry-over from some in-house sound editing software. I pretty much have to agree with your assumption on the "there's no harm so leave it in" side of things, especially for the ones where the write is static, as was typically the case in Konami's games. Looking at other games, like Nightmare On Elm Street, $400D typically has the same value as $4001 and $4005, so that is probably most often the case. When I compared Uncle Fester's Quest to some other game (I forgot which) today, they seem to wrote "sound data" when a channel has sound, but explicitly wrote #00 when the note stopped. It made it look like they explicitly wrote #88 whenever the note played, but in actuality the source of the "#88" never changed after the bootup write.

I think Jackal, PinBot, and High Speed (among possibly a few others) are writing SFX identifiers to the "noise sweep", now that I look at them a bit more. Jackal uses #01 for drums, #02 for jeep getting hit, #03 for bullet shooting, #05 for grenade or jeep exploding, and #06 for turrets exploding. PinBot and High Speed use #02 for the plunger, I think #06 for chutes, and #08 for bumpers (just as an example).

It raises a couple questions for me, though.
  1. The "there's no harm so leave it in" argument seems to flounder when the "noise sweep" value intentionally fluctuates. When punching an enemy in A Nightmare On Elm Street, the "noise sweep" changes to #9F, then reverts back to #43 after the sound plays.

    R.C. Pro-Am II sets the "noise sweep" to #40 on the title screen, #44 on the race standings screen, and #43 for other song screens (arguably an auto-write), but #00 for splashing in water. And again, this looks intentional to me. When the bomber plane appears, it changes to #08. When the nitros are used, it changes to #28. Unlike PinBot and Jackal, these don't strike me as debugging identifiers.

    There may be other "deliberate" ones, but I'm tired of looking at this code now.
  2. The way I read it on the wiki, only bit 3 exists outside the influence of bit 7 for the pulse sweep. Why write anything other #08 if bit 7 is clear? Many Konami games used that code snippet to write #7F to all four "sweep" registers. What significance would #7F have over #08 for the pulse channels? A Nightmare On Elm Street wrote #43. It wasn't even XORing the value with #80, it was deliberate writes of #43 vs. #83. How is #43 any different from #00 for a pulse sweep?

    I know it seems like I'm deviating from the $400D issue, but it's as much a concern about the values as it is about the register itself.
Drag wrote: Sat May 27, 2023 1:08 pm The four APU channels are similar enough that the music engine can reuse the same code to drive all four registers of all four of them, even if it doesn't make sense in certain circumstances. The only exceptions you need to make are for register $4008 (triangle length counter instead of a volume control register), and for register $400E (noise channel pitch, works differently from the other channels). In all other cases, you can just reuse the same code and it won't hurt anything, even if you're writing to unused bits or addresses.

Why write to the sweep registers if you don't use them? It's because the sweep units mute the channel when you try to set the channel's pitch too low, because the sweep unit is trying to prevent itself from wrapping the pitch around while sweeping (even while disabled). To get around this, you set the sweep unit to "up" instead of "down" (write $08), and then you can reach the lowest pitches. If the music engine likes to write some kind of constant to this register, that's probably what it's trying to do (even if the sweep unit doesn't actually exist for the channel).
I noticed that about the Triangle length counter in a lot of the games I looked at, so I was thinking if they go through that much effort to make an exception for the triangle registers, why not make exceptions for noise too? Anyway, what's your idea on my question #2 here?
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: Why do so many games write to $400D?

Post by Drag »

TheouAegis wrote: Sat May 27, 2023 2:30 pm 1. The "there's no harm so leave it in" argument seems to flounder when the "noise sweep" value intentionally fluctuates. When punching an enemy in A Nightmare On Elm Street, the "noise sweep" changes to #9F, then reverts back to #43 after the sound plays.

R.C. Pro-Am II sets the "noise sweep" to #40 on the title screen, #44 on the race standings screen, and #43 for other song screens (arguably an auto-write), but #00 for splashing in water. And again, this looks intentional to me. When the bomber plane appears, it changes to #08. When the nitros are used, it changes to #28. Unlike PinBot and Jackal, these don't strike me as debugging identifiers.
Many of those values leave bit 7 cleared, so even if there were a sweep unit on the noise channel, those values would produce no sweep. That points to these writes being artifacts and/or garbage data rather than developer-intended values.

It's like if some kind of leftover data were getting written to the register because a code routine that normally calculates the sweep unit byte is getting skipped over when it's the noise channel. There might be patterns with this leftover data, like it coincidentally being the sound effect ID, or maybe the byte for the next sound effect over, but there's no larger conspiracy here; developers were aware that writes to $400D did nothing and you couldn't read them back like RAM. :P
2. The way I read it on the wiki, only bit 3 exists outside the influence of bit 7 for the pulse sweep. Why write anything other #08 if bit 7 is clear?
Developer preference. As long as bit 7 is clear and bit 3 is set, the other bits can be whatever you want and it'll still have the same effect of mitigating the "can't play low pitches" side effect. Some people liked all 1's, some people liked all 0's.

For example, to "disable" a sprite on the NES, you must set its Y coordinate to any value between F0 and FF. Some people set it to FF, some set it to F0, I've seen F8, some people set all of the sprite's properties to FF, some people set only the Y coordinate, etc.
A Nightmare On Elm Street wrote #43. It wasn't even XORing the value with #80, it was deliberate writes of #43 vs. #83. How is #43 any different from #00 for a pulse sweep?
The only thing that's different is, by having bits 2..0 set to "3", it hypothetically allows you to access slightly more of the side-effect-muted lower pitches (but not as much as doing it the "correct" way). Why 3 and not 7? No idea. Why didn't they do the "correct" thing and set bit 3? No idea. Why do they set bits 6..4 to anything? No idea.

It's worth mentioning that, for whatever reason, Rare needed to reverse engineer the NES in lieu of Nintendo's own documentation (last I heard anyway), so it's possible that they weren't aware of any "best practices", like writing 08 or 7F to the sweep units.
if they go through that much effort to make an exception for the triangle registers, why not make exceptions for noise too?
They do; the triangle needs $4008 treated differently from $4000/4004/400C, and the noise channel needs $400E treated differently from $4002/4006/400A. The special treatment for $4008 means your key-on, key-off, and instrument-change logic is different. The special treatment for $400E means your pitch calculation is different.

Depending on how the sound engine works, if writing to the sweep unit is part of key-on/off and instrument changes, then there's a higher likelihood of the special triangle channel logic to skip those entirely, since we're already writing new logic there anyway.
The logic for the noise channel can reuse the key-on/off and instrument change logic from the square channels and operate perfectly fine, hence the higher likelihood to see writes to $400D but not to $4009.
The special logic for calculating pitch on the noise channel would be in a different part of the sound engine's code, and writes to $400D are harmless, so we'd be going out of our way to write special logic to skip $400D (and/or to do extra stuff like stripping out the unused duty-cycle bits for $400C) in this case. It's purely up to the developer if they want to do this or not, but in general, if making the effort isn't strictly necessary then it's often a lower priority.
TheouAegis
Posts: 4
Joined: Tue May 17, 2022 1:54 pm

Re: Why do so many games write to $400D?

Post by TheouAegis »

:beer: :? Okay, that makes sense. I was thinking about #7F just being #FF^#80, but all those other weirdos like #07 or #43 made me question it.

Part of me was also hoping someone would come in and be like, "If they had enabled the sweep here and noise sweeps were functional, this bit would have done such-and-such, and those bits would have done such-and-such-else." Then someone could come along and make an emulator that plays back the appropriate noise sweeps. :)
creaothceann
Posts: 611
Joined: Mon Jan 23, 2006 7:47 am
Location: Germany
Contact:

Re: Why do so many games write to $400D?

Post by creaothceann »

Maybe you can find some of the people involved online (e.g. twitter, youtube) and ask them.
My current setup:
Super Famicom ("2/1/3" SNS-CPU-GPM-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10
Post Reply