This proposal for an NES mapper combines the runtime flexibility of CHR RAM with some of the tile animation capability of CHR ROM, while still using fewer state bits than (say) an MMC3. Certain parts are inspired by CPROM and the Famicom Disk System.
The pattern table is divided into 32 rows, each 256 bytes in size: $0000-$00FF, $0100-$01FF, ..., $1F00-$1FFF. The 32 KiB CHR RAM (a 62256 SRAM) is divided into 128 such rows. Most of the time, PPU $0000-$1FFF is mapped directly into CHR RAM $0000-$1FFF. There are two "windows" that override this direct mapping: a variable-size window of 1 to 32 rows and a fixed-size window of 1 row. PPU addresses within either window are mapped to another row elsewhere in the CHR RAM.
The fixed-size window maps one row at a time. It is useful for switching the player character's sprite cel. The variable-size window maps several rows starting to some place elsewhere in the CHR RAM. It is useful for animating parts of the background in a loop, like Super Mario Bros. 2, Super Mario Bros. 3, or Kirby's Adventure.
Ports 0-4 control the CHR bank windows. Port 5 controls nametable mirroring. Ports 6, 8, 10, 12, and 14 control PRG bankswitching. Port 9 controls control scanline counter IRQs. Ports 7, 13, and 15 are reserved.
The pattern table is divided into 32 rows, each 256 bytes in size: $0000-$00FF, $0100-$01FF, ..., $1F00-$1FFF. Most of this space is directly mapped into $0000-$1FFF of the CHR RAM.
To disable this window, set the value of port 2 equal to the value of port 0.
A second window, 16 tiles in size, is useful for bankswitching a player's sprite cels.
This window takes priority over the variable-size window if they overlap. To disable it, set the value of ports 3 and 4 equal to the value of port 0.
The four MMC1 nametable mirroring modes and a four-screen mode are available.
When combined with sprite 0 or the optional IRQ feature, this allows four-screen mirroring and a status bar at the same time, as the playfield is in CHR RAM and the status bar is in CIRAM.
The PRG side of the mapper is mostly independent of the rest, and several sub-mappers are possible with different capabilities to fit different CPLDs.
The most full-featured version of this mapper, similar to MMC5 or FME-7, has five switchable banks:
Each of these registers has the format RBBB BBBB, where B controls PRG A19-A13 (8192 byte bank number) for this region and R chooses between the two PRG chip selects: 0 for PRG ROM or 1 for PRG RAM. Loss of M2 oscillation, detected with a circuit like this one, causes register $0E to revert to a value of $7F, mapping the last ROM bank in the cart into $E000-$FFFF.
Several cost-reduced versions of the PRG logic are possible.
One simpler mapper is comparable to UNROM:
In this case, $08 would have the format RBBB BBBx, where x is assumed 0 for $08. $C000-$FFFF would be fixed to PRG ROM in the last two banks of the cart.
Another is comparable to MMC3:
$C000-$FFFF would be fixed to $7E and $7F, putting PRG ROM in the last bank of the cart.
At the start of each scanline, the PPU freezes for a few cycles, and PPU A13 stays high for at least three consecutive cycles of PPU /RD. The mapper detects this and subtracts 1 from the value in $09 unless the value is $F0-$FF. While the value is 0, /IRQ is pulled low.
Programming tip: Reading from the nametables or palette during vertical or forced blanking will cause counts unless you write $FF to port $09.
Some implementations may count M2 cycles (1.8 MHz) instead of PPU /RD cycles (2.7 MHz) to save a pin. Cost-reduced versions may lack IRQ logic entirely.
Internally, the CHR RAM address is computed using two 5-bit adders and a multiplexer:
ppu_hi = PPU A12..A8 rel_rowno = ppu_hi - reg_0 if PPU A13 is true: CHR A14, A13, A12 = 111 CHR A11, A9, A8 = ppu_hi 3, 1, 0 CHR A10 = from mirroring logic elif ppu_hi == reg_3: CHR A14..A8 = reg_4 elif rel_rowno <= reg_1: CHR A14..A8 = reg_2 + rel_rowno else: CHR A14, A13 = 0 CHR A12..A8 = ppu_hi
CPLDs typically require one macrocell for each bit of state, plus a few more for intermediate results.
Total: 86 bits, or 61-63 bits for versions with reduced PRG bank capability
If this were produced on an ASIC, the package would be roughly the same size as an MMC3.