NES 2.0 Mapper 547

From Nesdev wiki
Jump to: navigation, search

NES 2.0 Mapper 547 denotes games for the Konami Q-Tai adapter, based on the Konami VRC5 ASIC. Its UNIF board name is KONAMI-QTAI. It is known to be used for the following games:

  • NHK 学園 - Space School: 算数 4年 (上)
  • NHK 学園 - Space School: 算数 4年 (下)
  • NHK 学園 - Space School: 算数 5年 (上)
  • NHK 学園 - Space School: 算数 5年 (下)
  • NHK 学園 - Space School: 算数 6年 (上)
  • NHK 学園 - Space School: 算数 6年 (下)
  • 出光 Space College: 危険物のやさしい物理と化学

The Konami Q-Tai adapter is inserted into the console's cartridge slot. It provides 128 KiB of internal PRG-ROM, 256 KiB of Kanji CHR pattern data, 8 KiB of non-battery-backed WRAM, and 8 KiB of CHR-RAM. It cannot be used alone, as the internal PRG-ROM contains no reset vector. Instead, custom 40-pin cartridges must be inserted. Each 40-pin game cartridge contains an additional 512 KiB of PRG-ROM plus 8 KiB of additional WRAM, which is battery-backed for all known games.

UNIF-format files contain the internal PRG- and CHR-ROM chips' data in PRG0 and CHR0 chunks, and the 40-pin cartridges' PRG-ROM in a PRG1 chunk. NES-2.0-format files first contain the 128 KiB internal PRG-ROM, then the external 512 KiB PRG-ROM, for a total of 640 KiB of PRG-ROM, plus 256 KiB of Kanji CHR-ROM. PRG-RAM size must be 8 KiB battery-backed and 8 KiB non-battery backed; CHR-RAM size must be 8 KiB.

The Q-Tai main adapter also has 2 KiB of "shadow" nametable memory (named QTRAM) that allows for automatic CHR bank switching, somewhat similarly to the MMC5's EXRAM.

Banks

  • CPU $6000-$6FFF: 4 KiB switchable PRG-RAM bank
  • CPU $7000-$7FFF: 4 KiB switchable PRG-RAM bank
  • CPU $8000-$9FFF: 8 KiB switchable PRG-ROM bank
  • CPU $A000-$BFFF: 8 KiB switchable PRG-ROM bank
  • CPU $C000-$DFFF: 8 KiB switchable PRG-ROM bank
  • CPU $E000-$FFFF: 8 KiB fixed PRG-ROM bank, always last bank of external cartridge
  • PPU $0000-$0FFF: 4 KiB switchable CHR-RAM bank
  • PPU $1000-$1FFF: 4 KiB fixed CHR-RAM bank #1

FCEUX' source code mentions that the PPU $1000-$1FFF bank can be switched to map to the Kanji CHR-ROM, but the details are not known, and this feature seems to be unused by all known games.

Registers

WRAM Bank Select ($D000/$D100, write)

Mask: probably $FF00

$D000: Select 4 KiB PRG-RAM bank at CPU $6000-$6FFF
$D100: Select 4 KiB PRG-RAM bank at CPU $7000-$7FFF

D~7654 3210
  ---------
  .... C..B
       |  +- PRG A12
       +---- Chip select
             0: External cartridge's 8 KiB (battery-backed)
             1: Internal 8 KiB (not battery-backed)
             

PRG-ROM Bank Select ($D200/$D300/$D400, write)

Mask: probably $FF00

$D200: Select 8 KiB PRG-ROM bank at CPU $8000-$9FFF
$D300: Select 8 KiB PRG-ROM bank at CPU $A000-$BFFF
$D400: Select 8 KiB PRG-ROM bank at CPU $C000-$DFFF

D~7654 3210
  ---------
  .CBB BBBB
   |++-++++- PRG A13-A18
   +-------- Chip select
             0: Internal PRG-ROM (128 KiB)
             1: External PRG-ROM (512 KiB)
             

CHR-RAM Bank Select ($D500, write)

Mask: probably $FF00

D~7654 3210
  ---------
  .... ...B
          +- CHR A12 for PPU $0000-$0FFF

This CHR-RAM bank is only relevant for sprite data fetches, as the bank for background tiles is determined differently.

IRQ Latch Write ($D600/$D700, write)

Mask: $FF00

$D600: Set 16-bit IRQ latch LSB
$D700: Set 16-bit IRQ latch MSB

IRQ Acknowledge ($D800, write)

Mask: probably $FF00

Writing to this register acknowledges a pending IRQ and moves the 'A' bit to the 'E' bit.

IRQ Control ($D900, write)

Mask: probably $FF00

D~7654 3210
  ---------
  .... ..EA
         |+- IRQ Enable after acknowledgement
         +-- IRQ Enable (1=enabled)

Any write to this register will acknowledge a pending IRQ. If 'E' is set, the 16-bit counter is loaded with the 16-bit latch value. If enabled, the IRQ counter is incremented on every M2 cycle until it wraps from $FFFF to $0000, which generates an IRQ and reloads the counter with the latch. There seems to be no pseudo-scanline mode.

Nametable Control ($DA00, write)

Mask: probably $FF00

D~7654 3210
  ---------
  .... ..MQ
         |+- 0: Writes to PPU $2000-$2FFF go to CIRAM
         |   1: Writes to PPU $2000-$2FFF go to QTRAM
         +-- 0: Vertical mirroring
             1: Horizontal mirroring

Character Translation Input ($DB00/$DC00/$DD00, write)

Mask: probably $FF00 

$DB00: Set character tile position and attribute
D~7654 3210
  ---------
  .... .APP
        |++- Position within 16x16 tile
        |     0: Top left
        |     1: Top right
        |     2: Bottom left
        |     3: Bottom right
        +--- Attribute
              0: normal
              1: alternate

$DC00: Set 7-bit JIS X 0208 column
$DD00: Set 7-bit JIS X 0208 row

After writing to these three registers, the correct CIRAM tile number and QTRAM banking byte may be read from $DC00 and $DD00.

Character Translation Output ($DC00/$DD00, read)

Mask: probably $FF00 (ROM data filled with $FF from $D400-DFFF)

$DC00: Read translated tile number to be written to CIRAM
$DD00: Read translated bank byte to be written to QTRAM

CHR Banking Operation

Any time the PPU fetches graphical data for the background, it will first fetch a nametable and attribute byte, then two pattern table bytes based on the tile number from the nametable byte. The Konami VRC5 ASIC intercepts this process: Whenever the NES PPU fetches a background nametable byte from CIRAM, the VRC5 fetches an additional byte from QTRAM that indicates whether the next CHR pattern data that will be fetched should come from CHR-ROM or CHR-RAM and from which 4 KiB bank.

Games will first write a tile number to CIRAM with $DA00 bit 0 clear, then write a bank byte to QTRAM by writing to the same PPU address with $DA00 bit 0 set. For normal game graphics, the tile number will point to a normal graphical pattern that was previously written to CHR-RAM, with the QTRAM bank byte only determining whether to use the left ($0xxx) or right ($1xxx) nametable; PPU A12 is ignored during background CHR pattern fetches. This allows using 512 tiles for background graphics.

To display 16x16 Kanji characters from CHR-ROM, the game first writes the character's double-byte 7-bit JIS X 0208 code to $DC00/$DD00 as well as the attribute and position of the current 8x8 tile within the 16x16 glyph. It then reads the correct CIRAM tile number and QTRAM bank byte from $DC00 and $DD00, which are then written to CIRAM and QTRAM using the same $DA00 bit 0 procedure.

Format of the QTRAM bank byte:

D~7654 3210
  ---------
  RCBB BBBb
  ||++-++++- CHR A12..A17 if C=1, only b used if C=0
  |+-------- Chip select: 0=CHR-RAM, 1=CHR-ROM
  +--------- If CHR-ROM is selected, 
             0: Force CHR pattern data to $00 if CHR A3=1 (i.e. pattern data is colors 0 and 1)
             1: Force CHR pattern data to $FF if CHR A3=1 (i.e. pattern data is colors 2 and 3)

Sprite CHR pattern fetches work normally, PPU A12 applies.

Character Translation Operation

The JIS X 0208 code map is arranged into 94 rows ($21-$7E) with 94 columns per row ($21-$7E), with unassigned code points in-between, in particular rows $29-$30. Even without those unassigned code points, 94 columns per row means that the code map is not contiguous.

The hardware translates double-byte 7-bit JIS X 0208 codes into a 4 KiB bank and a tile number into the Kanji ROM. While the actual conversion table, and presumably operations, used by the hardware are replicated by FCEUX according to source code comments, the following description explains the idea behind it and produces identical results for valid JIS code points. The translation involves two steps: remapping the non-contiguous JIS X 0208 code map to a continuous code map, and remapping duplicate and unused "pages" (each consisting of 256 code points).

To form a contiguous code map, assume

  • 96 columns per row, divided into three column-thirds consisting of 32 columns each;
  • 96 rows, divided into six row-thirds consisting of 16 rows each.

This 9,216-element contiguous code map is built in the order 32 columns within a column-third, 16 rows within a row-third, three column thirds, six row-thirds:

// "row" and "col" are the first and second 7-bit JIS X 0208 code byte, respectively, each minus the $21 offset.
code = (col %32)		// First, go through 32 columns of a column-third.
      +(row %16)*32 		// Then, through 16 rows of a row-third.
      +(col /32)*32*16		// Then, through three column-thirds.
      +(row /16)*32*16*3 	// Finally, through six row-thirds.
;

The actual Kanji ROM does not contain every character; in particular, it only contains the Level 1 kanji (up to row 47), and skips the empty rows $29-$30. Therefore, a second step of the translation requires remapping from the full contiguous 9,216-element table to a 4,096-element table. Given the contiguous code map created previously, this remapping can be done on a page (256-code) level:

static const uint8_t pageTable[0x24] ={
	0x0,0x0,0x2,0x2,0x1,0x1,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF, // JIS X 0208 rows $20-$4F. $20 is not a valid row number.
	0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,0xD,0xD  // JIS X 0208 rows $50-$7F. $7F is not a valid row number.
};
glyph =(code &0xFF) | (pageTable[code >>8] <<8);

Since the Kanji ROM contains 16x16 glyphs, there are four tiles per gylph:

tile =glyph *4;
ciramByte =(tile &0xFF) | (reg[0xDB00] &3);
qtramByte =(tile >>8);

// Indicate CHR-ROM
qtramByte |=0x40;

// Indicate alternate attribute
if (reg[0xDB00] &0x04) qtramByte |=0x80;

See also