Bus conflict

From NESdev Wiki
Revision as of 20:58, 12 February 2023 by Jeffythedragonslayer (talk | contribs) (→‎Emulating bus conflicts: g -> t)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

A bus conflict occurs when two logic devices output different values on the same bus line. When two signals are asserted at the same voltage, the one with less impedance generally wins. In the NES, this generally happens when a program writes to a mapper whose registers overlap ROM but the ROM does not shut off its output, causing a potential conflict on the PRG data bus. Most ASIC based mappers include logic to disable the ROM's output enable during writes, putting the ROM's outputs into a high-impedance state and preventing the bus conflict. But many mappers, especially discrete logic mappers, do not.

Programming around bus conflicts

If you are using a mapper with bus conflicts, make sure that all devices on the bus are asserting the same value by writing to a ROM location that already contains the value that you are writing. For instance, to switch to bank 5 in UNROM or UOROM, write a 5 to a ROM location that already contains a 5.

One common way to do this is to perform an immediate load and then store over the opcode:

  ldy #5
  sty @loadInstruction+1

To switch to a bank based on the value of a variable, put it in an indexed register and then perform an absolute indexed store:

  lda curMapBank
  sta bankBytes,x
; ...
  .byt $00, $01, $02, $03, $04, $05, $06, $07

Emulating bus conflicts

Most emulators have assumed that the CPU "wins" all bus conflicts, that is, that the mapper circuitry sees the signals from the CPU more strongly than the signals from the PRG ROM and acts solely on the CPU. Quite a few early programs in iNES format were developed without taking bus conflicts in account, and do not work correctly when run on an NES Game Pak that has been modified to take rewritable memory. In general, the authors of these programs did not know at the time that bus conflicts existed. These programs can, however, be made to run by adding ROM-disabling circuitry like that of ANROM or the positive chip enable of the PRG ROM chips used with AOROM. The following classes of iNES files will often contain bugs causing bus conflicts:

  • Old homebrew ROMs. Old documents did not mention the possibility of bus conflicts.
  • Mapper hacks. Because the MMC1 was poorly understood, and the code to operate an MMC1 is generally larger than that for a discrete logic mapper without bus conflicts, the early English translations of the Famicom game Final Fantasy II changed it from MMC1 to a variant of mapper 2.
  • Buggy homebrew or hack. Code or tables to avoid bus conflicts may have been written incorrectly, or a JMP instruction may have sent the program counter into nowhere.

It has been confirmed through testing that both the CPU and the mask ROMs used in the NES era drive a 0 more strongly than a 1, as one would expect based on the logic's implementation. This implies that an emulator should use the bitwise AND of the value from the CPU and the value from the ROM. However, programmers must not rely on this undefined behavior. Emulators should log a warning whenever a bus conflict occurs. This will help find bugs 1. in the emulator's handling of PRG ROM bank switching and 2. in future homebrew ROMs tested on the emulator.

See also