Action 53 mapper/Reference implementations

From NESdev Wiki
Jump to navigationJump to search

These reference implementations of the PRG ROM bank formula in the Action 53 mapper (mapper 28) may be used to verify emulator or hardware implementations of this mapper.


#!/usr/bin/env python3
from __future__ import print_function

bank_size_masks = [(2<<i)-1 for i in range(4)]  # 0x01, 0x03, 0x07, 0x0F

def calc_prg_bank(address, bank_mode, outer_bank, current_bank):
    """Calculate the 16K bank on A20-A14 for a given address.

address -- current address being accessed by the CPU,
    in range 0x8000-0xFFFF
bank_mode -- last value written to $80
outer_bank -- last value written to $81
current_bank -- last value written to $01

    cpu_a14 = (address >> 14) & 0x01
    outer_bank = outer_bank << 1
    bank_mode >>= 2  # discard mirroring bits
    if (bank_mode ^ cpu_a14) & 0x03 == 0x02:  # in UNROM fixed bank?
        bank_mode = 0  # if so, treat as NROM
    if (bank_mode & 0x02) == 0:  # in 32K bank mode?
        current_bank = (current_bank << 1) | cpu_a14
    bank_size_mask = bank_size_masks[(bank_mode >> 2) & 3]
    return (current_bank & bank_size_mask) | (outer_bank & ~bank_size_mask)

def test_with_bank_mode_size(bank_mode, outer_bank):
    print("mode $%02x, outer bank $%02x" % (bank_mode, outer_bank))
    out80 = [calc_prg_bank(0x8000, bank_mode, outer_bank, current_bank)
         for current_bank in range(16)]
    print("$8000 banks:", " ".join("%02x" % i for i in out80))
    outC0 = [calc_prg_bank(0xC000, bank_mode, outer_bank, current_bank)
             for current_bank in range(16)]
    print("$C000 banks:", " ".join("%02x" % i for i in outC0))

for outer_bank in (0x00, 0x3C, 0x3F):
    test_with_bank_mode_size(0x28, outer_bank)
for outer_bank in (0x00, 0x03, 0x3F):
    test_with_bank_mode_size(0x2C, outer_bank)
for bank_mode in (0x00, 0x08, 0x0C, 0x10, 0x18, 0x1C,
                  0x20, 0x28, 0x2C, 0x30, 0x38, 0x3C):
    test_with_bank_mode_size(bank_mode, 0x2A)

6502 assembly

This routine appears in the test ROMs "test28" and "Holy Mapperel".

; Determines what 16 KiB PRG ROM bank ought to be mapped into a given
; CPU address with a given set of $80, $81, $00 values.
; For use in a test ROM that verifies mapper 28.
; @param A $80 value
; @param X $01 value
; @param Y $81 value
; @param C CPU A14 value: clear for $8000-$BFFF, set for $C000-$FFFF
; @return PRG bank number in A
.proc calc_prg_bank
bank_mode = 0
outer_bank = 1
current_bank = 2
  stx current_bank
  sty outer_bank
  rol outer_bank
  lsr a  ; discard mirroring bits
  lsr a
  sta bank_mode

  ; If the mode is UxROM (10 = mapper 180, 11 = mapper 2), and bit 0
  ; of the mode matches CPU A14, then the read is within the fixed
  ; bank.  For such reads, the mapper acts in 32K (NROM) mode.
  and #$02
  beq not_unrom
  lda outer_bank
  eor bank_mode
  ; If bit 0 of the eor result is false, there is a match, so
  ; fall through to the not-UNROM code.
  and #$01
  bne have_current_bank
  sta bank_mode

  ; In 32K switched modes (NROM, CNROM, BNROM, AOROM),
  ; shift CPU A14 into the current bank
  lda outer_bank
  lsr a
  rol current_bank
  lda bank_mode
  lsr a
  lsr a
  and #$03
  lda current_bank
  eor outer_bank
  and bank_size_masks,x
  eor outer_bank

.segment "RODATA"
bank_size_masks: .byt $01, $03, $07, $0F