Action 53 mapper/Reference implementations

From Nesdev wiki
Jump to: navigation, 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 Diver Batman".

; 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