shithub: zelda3

ref: ec08202f3a7d95c4db1e384a51d12244fae9c23b
dir: /nmi.c/

View raw version
#include "nmi.h"
#include "zelda_rtl.h"
#include "variables.h"
#include "messaging.h"
#include "snes/snes_regs.h"
#include "snes/ppu.h"
#include "assets.h"

static const uint8 kNmiVramAddrs[] = {
  0, 0, 4, 8, 12, 8, 12, 0, 4, 0, 8, 4, 12, 4, 12, 0,
  8, 16, 20, 24, 28, 24, 28, 16, 20, 16, 24, 20, 28, 20, 28, 16,
  24, 96, 104,
};
static PlayerHandlerFunc *const kNmiSubroutines[25] = {
  &NMI_UploadTilemap_doNothing,
  &NMI_UploadTilemap,
  &NMI_UploadBG3Text,
  &NMI_UpdateOWScroll,
  &NMI_UpdateSubscreenOverlay,
  &NMI_UpdateBG1Wall,
  &NMI_TileMapNothing,
  &NMI_UpdateLoadLightWorldMap,
  &NMI_UpdateBG2Left,
  &NMI_UpdateBGChar3and4,
  &NMI_UpdateBGChar5and6,
  &NMI_UpdateBGCharHalf,
  &NMI_UploadSubscreenOverlayLatter,
  &NMI_UploadSubscreenOverlayFormer,
  &NMI_UpdateBGChar0,
  &NMI_UpdateBGChar1,
  &NMI_UpdateBGChar2,
  &NMI_UpdateBGChar3,
  &NMI_UpdateObjChar0,
  &NMI_UpdateObjChar2,
  &NMI_UpdateObjChar3,
  &NMI_UploadDarkWorldMap,
  &NMI_UploadGameOverText,
  &NMI_UpdatePegTiles,
  &NMI_UpdateStarTiles,
};
void NMI_UploadSubscreenOverlayFormer() {
  NMI_HandleArbitraryTileMap(&g_ram[0x12000], 0, 0x40);
}

void NMI_UploadSubscreenOverlayLatter() {
  NMI_HandleArbitraryTileMap(&g_ram[0x13000], 0x40, 0x80);
}

static void CopyToVram(uint32 dstv, const uint8 *src, int len) {
  memcpy(&g_zenv.vram[dstv], src, len);
}

static void CopyToVramVertical(uint32 dstv, const uint8 *src, int len) {
  assert(!(len & 1));
  uint16 *dst = &g_zenv.vram[dstv];
  for (int i = 0, i_end = len >> 1; i < i_end; i++, dst += 32, src += 2)
    *dst = WORD(*src);
}

static void CopyToVramLow(const uint8 *src, uint32 addr, int num) {
  uint16 *dst = &g_zenv.vram[addr];
  for (int i = 0; i < num; i++)
    dst[i] = (dst[i] & ~0xff) | src[i];
}

void WritePpuRegisters() {
  zelda_ppu_write(W12SEL, W12SEL_copy);
  zelda_ppu_write(W34SEL, W34SEL_copy);
  zelda_ppu_write(WOBJSEL, WOBJSEL_copy);
  zelda_ppu_write(CGWSEL, CGWSEL_copy);
  zelda_ppu_write(CGADSUB, CGADSUB_copy);
  zelda_ppu_write(COLDATA, COLDATA_copy0);
  zelda_ppu_write(COLDATA, COLDATA_copy1);
  zelda_ppu_write(COLDATA, COLDATA_copy2);
  zelda_ppu_write(TM, TM_copy);
  zelda_ppu_write(TS, TS_copy);
  zelda_ppu_write(TMW, TMW_copy);
  zelda_ppu_write(TSW, TSW_copy);
  zelda_ppu_write(BG1HOFS, BG1HOFS_copy);
  zelda_ppu_write(BG1HOFS, BG1HOFS_copy >> 8);
  zelda_ppu_write(BG1VOFS, BG1VOFS_copy);
  zelda_ppu_write(BG1VOFS, BG1VOFS_copy >> 8);
  zelda_ppu_write(BG2HOFS, BG2HOFS_copy);
  zelda_ppu_write(BG2HOFS, BG2HOFS_copy >> 8);
  zelda_ppu_write(BG2VOFS, BG2VOFS_copy);
  zelda_ppu_write(BG2VOFS, BG2VOFS_copy >> 8);
  zelda_ppu_write(BG3HOFS, BG3HOFS_copy2);
  zelda_ppu_write(BG3HOFS, BG3HOFS_copy2 >> 8);
  zelda_ppu_write(BG3VOFS, BG3VOFS_copy2);
  zelda_ppu_write(BG3VOFS, BG3VOFS_copy2 >> 8);
  zelda_ppu_write(INIDISP, INIDISP_copy);
  zelda_ppu_write(MOSAIC, MOSAIC_copy);
  zelda_ppu_write(BGMODE, BGMODE_copy);
  if ((BGMODE_copy & 7) == 7) {
    zelda_ppu_write(M7B, 0);
    zelda_ppu_write(M7B, 0);
    zelda_ppu_write(M7C, 0);
    zelda_ppu_write(M7C, 0);
    zelda_ppu_write(M7X, M7X_copy);
    zelda_ppu_write(M7X, M7X_copy >> 8);
    zelda_ppu_write(M7Y, M7Y_copy);
    zelda_ppu_write(M7Y, M7Y_copy >> 8);
  }
  zelda_ppu_write(BG12NBA, 0x22);
  zelda_ppu_write(BG34NBA, 7);
}

void Interrupt_NMI(uint16 joypad_input) {  // 8080c9
  if (music_control == 0) {
    if (zelda_apu_read(APUI00) == last_music_control)
      zelda_apu_write(APUI00, 0);
  // Zelda causes unwanted music change when going in a portal. last_music_control doesn't hold the 
  // song but the last applied effect
  } else if (music_control != (enhanced_features0 & kFeatures0_MiscBugFixes ? music_unk1 : last_music_control)) {
    last_music_control = music_control;
    ZeldaPlayMsuAudioTrack();
    if (music_control < 0xf2)
      music_unk1 = music_control;
    music_control = 0;
  }

  if (sound_effect_ambient == 0) {
    if (zelda_apu_read(APUI01) == sound_effect_ambient_last)
      zelda_apu_write(APUI01, 0);
  } else {
    sound_effect_ambient_last = sound_effect_ambient;
    zelda_apu_write(APUI01, sound_effect_ambient);
    sound_effect_ambient = 0;
  }
  zelda_apu_write(APUI02, sound_effect_1);
  zelda_apu_write(APUI03, sound_effect_2);
  sound_effect_1 = 0;
  sound_effect_2 = 0;

  if (!nmi_boolean) {
    nmi_boolean = true;
    NMI_DoUpdates();
    NMI_ReadJoypads(joypad_input);
  }

  if (is_nmi_thread_active) {
    NMI_UpdateIRQGFX();
    thread_other_stack = (thread_other_stack != 0x1f31) ? 0x1f31 : 0x1f2;
  }
  WritePpuRegisters();
}

void NMI_ReadJoypads(uint16 joypad_input) {  // 8083d1
  uint16 both = joypad_input;
  uint16 reversed = 0;
  for (int i = 0; i < 16; i++, both >>= 1)
    reversed = reversed * 2 + (both & 1);
  uint8 r0 = reversed;
  uint8 r1 = reversed >> 8;

  joypad1L_last = r0;
  filtered_joypad_L = (r0 ^ joypad1L_last2) & r0;
  joypad1L_last2 = r0;

  joypad1H_last = r1;
  filtered_joypad_H = (r1 ^ joypad1H_last2) & r1;
  joypad1H_last2 = r1;
}

void NMI_DoUpdates() {  // 8089e0
  if (!nmi_disable_core_updates) {
    memcpy(&g_zenv.vram[0x4100], &kLinkGraphics[dma_source_addr_0 - 0x8000], 0x40);
    memcpy(&g_zenv.vram[0x4120], &kLinkGraphics[dma_source_addr_1 - 0x8000], 0x40);
    memcpy(&g_zenv.vram[0x4140], &kLinkGraphics[dma_source_addr_2 - 0x8000], 0x20);

    memcpy(&g_zenv.vram[0x4000], &kLinkGraphics[dma_source_addr_3 - 0x8000], 0x40);
    memcpy(&g_zenv.vram[0x4020], &kLinkGraphics[dma_source_addr_4 - 0x8000], 0x40);
    memcpy(&g_zenv.vram[0x4040], &kLinkGraphics[dma_source_addr_5 - 0x8000], 0x20);

    memcpy(&g_zenv.vram[0x4050], &g_ram[dma_source_addr_6], 0x40);
    memcpy(&g_zenv.vram[0x4070], &g_ram[dma_source_addr_7], 0x40);
    memcpy(&g_zenv.vram[0x4090], &g_ram[dma_source_addr_8], 0x40);
    memcpy(&g_zenv.vram[0x40b0], &g_ram[dma_source_addr_9], 0x20);
    memcpy(&g_zenv.vram[0x40c0], &g_ram[dma_source_addr_10], 0x40);
    memcpy(&g_zenv.vram[0x4150], &g_ram[dma_source_addr_11], 0x40);
    memcpy(&g_zenv.vram[0x4170], &g_ram[dma_source_addr_12], 0x40);
    memcpy(&g_zenv.vram[0x4190], &g_ram[dma_source_addr_13], 0x40);
    memcpy(&g_zenv.vram[0x41b0], &g_ram[dma_source_addr_14], 0x20);
    memcpy(&g_zenv.vram[0x41c0], &g_ram[dma_source_addr_15], 0x40);
    memcpy(&g_zenv.vram[0x4200], &g_ram[dma_source_addr_16], 0x40);
    memcpy(&g_zenv.vram[0x4220], &g_ram[dma_source_addr_17], 0x40);
    memcpy(&g_zenv.vram[0x4240], &g_ram[0xbd40], 0x40);
    memcpy(&g_zenv.vram[0x4300], &g_ram[dma_source_addr_18], 0x40);
    memcpy(&g_zenv.vram[0x4320], &g_ram[dma_source_addr_19], 0x40);
    memcpy(&g_zenv.vram[0x4340], &g_ram[0xbd80], 0x40);

    if (BYTE(flag_travel_bird)) {
      memcpy(&g_zenv.vram[0x40e0], &g_ram[dma_source_addr_20], 0x40);
      memcpy(&g_zenv.vram[0x41e0], &g_ram[dma_source_addr_21], 0x40);
    }

    memcpy(&g_zenv.vram[animated_tile_vram_addr], &g_ram[animated_tile_data_src], 0x400);
  }

  if (flag_update_hud_in_nmi) {
    memcpy(&g_zenv.vram[word_7E0219], hud_tile_indices_buffer, 165 * sizeof(uint16));
  }

  if (flag_update_cgram_in_nmi) {
    memcpy(g_zenv.ppu->cgram, main_palette_buffer, 0x200);
  }

  flag_update_hud_in_nmi = 0;
  flag_update_cgram_in_nmi = 0;

  memcpy(g_zenv.ppu->oam, &g_ram[0x800], 0x220);

  if (nmi_load_bg_from_vram) {
    const uint8 *p;
    switch (nmi_load_bg_from_vram) {
    case 1: p = g_ram + 0x1002; break;
    case 2: p = g_ram + 0x1000; break;
    case 3: p = kBgTilemap_0; break;
    case 4: p = g_ram + 0x21b; break;
    case 5: p = kBgTilemap_1; break;
    case 6: p = kBgTilemap_2; break;
    case 7: p = kBgTilemap_3; break;
    case 8: p = kBgTilemap_4; break;
    case 9: p = kBgTilemap_5; break;
    default: assert(0);
    }
    HandleStripes14(p);
    if (nmi_load_bg_from_vram == 1)
      vram_upload_offset = 0;
    nmi_load_bg_from_vram = 0;
  }

  if (nmi_update_tilemap_dst) {
    memcpy(&g_zenv.vram[nmi_update_tilemap_dst * 256], &g_ram[0x10000 + nmi_update_tilemap_src], 0x200);
    nmi_update_tilemap_dst = 0;
  }

  if (nmi_copy_packets_flag) {
    uint8 *p = (uint8 *)uvram.data;
    do {
      int dst = WORD(p[0]);
      int vmain = p[2];
      int len = p[3];
      p += 4;
      if (vmain == 0x80) {
        // plain copy
        memcpy(&g_zenv.vram[dst], p, len);
      } else if (vmain == 0x81) {
        // copy with other increment
        assert((len & 1) == 0);
        uint16 *dp = &g_zenv.vram[dst];
        for (int i = 0; i < len; i += 2, dp += 32)
          *dp = WORD(p[i]);
      } else {
        assert(0);
      }
      p += len;
    } while (WORD(p[0]) != 0xffff);
    nmi_copy_packets_flag = 0;
    nmi_disable_core_updates = 0;
  }

  int idx = nmi_subroutine_index;
  nmi_subroutine_index = 0;
  kNmiSubroutines[idx]();
}

void NMI_UploadTilemap() {  // 808cb0
  memcpy(&g_zenv.vram[kNmiVramAddrs[BYTE(nmi_load_target_addr)] << 8], &g_ram[0x1000], 0x800);

  *(uint16 *)&g_ram[0x1000] = 0;
  nmi_disable_core_updates = 0;
}

void NMI_UploadTilemap_doNothing() {  // 808ce3
}

void NMI_UploadBG3Text() {  // 808ce4
  memcpy(&g_zenv.vram[0x7c00], &g_ram[0x10000], 0x7e0);
  nmi_disable_core_updates = 0;
}

void NMI_UpdateOWScroll() {  // 808d13
  uint8 *src = (uint8 *)uvram.data, *src_org = src;
  int f = WORD(src[0]);
  int step = (f & 0x8000) ? 32 : 1;
  int len = f & 0x3fff;
  src += 2;
  do {
    uint16 *dst = &g_zenv.vram[WORD(src[0])];
    src += 2;
    for (int i = 0, i_end = len >> 1; i < i_end; i++, dst += step, src += 2)
      *dst = WORD(*src);
  } while (!(src[1] & 0x80));
  nmi_disable_core_updates = 0;
}

void NMI_UpdateSubscreenOverlay() {  // 808d62
  NMI_HandleArbitraryTileMap(&g_ram[0x12000], 0, 0x80);
}

void NMI_HandleArbitraryTileMap(const uint8 *src, int i, int i_end) {  // 808dae
  uint16 *r10 = &word_7F4000;
  do {
    memcpy(&g_zenv.vram[r10[i >> 1]], src, 0x80);
    src += 0x80;
  } while ((i += 2) != i_end);
  nmi_disable_core_updates = 0;
}

void NMI_UpdateBG1Wall() {  // 808e09
  // Secret Wall Right
  CopyToVramVertical(nmi_load_target_addr, &g_ram[0xc880], 0x40);
  CopyToVramVertical(nmi_load_target_addr + 0x800, &g_ram[0xc8c0], 0x40);
}

void NMI_TileMapNothing() {  // 808e4b
}

void NMI_UpdateLoadLightWorldMap() {  // 808e54
  static const uint16 kLightWorldTileMapDsts[4] = { 0, 0x20, 0x1000, 0x1020 };
  const uint8 *src = GetLightOverworldTilemap();
  for (int j = 0; j != 4; j++) {
    int t = kLightWorldTileMapDsts[j];
    for (int i = 0x20; i; i--) {
      CopyToVramLow(src, t, 0x20);
      src += 32;
      t += 0x80;
    }
  }
}

void NMI_UpdateBG2Left() {  // 808ea9
  CopyToVram(0, &g_ram[0x10000], 0x800);
  CopyToVram(0x800, &g_ram[0x10800], 0x800);
}

void NMI_UpdateBGChar3and4() {  // 808ee7
  memcpy(&g_zenv.vram[0x2c00], &g_ram[0x10000], 0x1000);
  nmi_disable_core_updates = 0;
}

void NMI_UpdateBGChar5and6() {  // 808f16
  memcpy(&g_zenv.vram[0x3400], &g_ram[0x11000], 0x1000);
  nmi_disable_core_updates = 0;
}

void NMI_UpdateBGCharHalf() {  // 808f45
  memcpy(&g_zenv.vram[BYTE(nmi_load_target_addr) * 256], &g_ram[0x11000], 0x400);
}

void NMI_UpdateBGChar0() {  // 808f72
  NMI_RunTileMapUpdateDMA(0x2000);
}

void NMI_UpdateBGChar1() {  // 808f79
  NMI_RunTileMapUpdateDMA(0x2800);
}

void NMI_UpdateBGChar2() {  // 808f80
  NMI_RunTileMapUpdateDMA(0x3000);
}

void NMI_UpdateBGChar3() {  // 808f87
  NMI_RunTileMapUpdateDMA(0x3800);
}

void NMI_UpdateObjChar0() {  // 808f8e
  CopyToVram(0x4400, &g_ram[0x10000], 0x800);
  nmi_disable_core_updates = 0;
}

void NMI_UpdateObjChar2() {  // 808fbd
  NMI_RunTileMapUpdateDMA(0x5000);
}

void NMI_UpdateObjChar3() {  // 808fc4
  NMI_RunTileMapUpdateDMA(0x5800);
}

void NMI_RunTileMapUpdateDMA(int dst) {  // 808fc9
  CopyToVram(dst, &g_ram[0x10000], 0x1000);
  nmi_disable_core_updates = 0;
}

void NMI_UploadDarkWorldMap() {  // 808ff3
  static const uint16 kLightWorldTileMapSrcs[4] = { 0, 0x20, 0x1000, 0x1020 };
  const uint8 *src = g_ram + 0x1000;
  int t = 0x810;
  for (int i = 0x20; i; i--) {
    CopyToVramLow(src, t, 0x20);
    src += 32;
    t += 0x80;
  }
}

void NMI_UploadGameOverText() {  // 809038
  CopyToVram(0x7800, &g_ram[0x2000], 0x800);
  CopyToVram(0x7d00, &g_ram[0x3400], 0x600);
}

void NMI_UpdatePegTiles() {  // 80908b
  CopyToVram(0x3d00, &g_ram[0x10000], 0x100);
}

void NMI_UpdateStarTiles() {  // 8090b7
  CopyToVram(0x3ed0, &g_ram[0x10000], 0x40);
}

void HandleStripes14(const uint8 *p) {  // 8092a1
  while (!(p[0] & 0x80)) {
    uint16 vmem_addr = swap16(WORD(p[0]));
    uint8 vram_incr_amount = (p[2] & 0x80) >> 7;
    uint8 is_memset = p[2] & 0x40;  // Cpu BUS Address Step  (0=Increment, 2=Decrement, 1/3=Fixed) (DMA only)
    int len = (swap16(WORD(p[2])) & 0x3fff) + 1;
    p += 4;

    if (vram_incr_amount == 0) {
      uint16 *dst = &g_zenv.vram[vmem_addr];
      if (is_memset) {
        uint16 v = p[0] | p[1] << 8;
        len = (len + 1) >> 1;
        for (int i = 0; i < len; i++)
          dst[i] = v;
        p += 2;
      } else {
        memcpy(dst, p, len);
        p += len;
      }
    } else {
      // increment vram by 32 instead of 1
      uint16 *dst = &g_zenv.vram[vmem_addr];
      if (is_memset) {
        uint16 v = p[0] | p[1] << 8;
        len = (len + 1) >> 1;
        for (int i = 0; i < len; i++, dst += 32)
          *dst = v;
        p += 2;
      } else {
        assert((len & 1) == 0);
        len >>= 1;
        for (int i = 0; i < len; i++, dst += 32, p += 2)
          WORD(*dst) = WORD(*p);
      }
    }
  }
}

void NMI_UpdateIRQGFX() {  // 809347
  if (nmi_flag_update_polyhedral) {
    memcpy(&g_zenv.vram[0x5800], &g_ram[0xe800], 0x800);
    nmi_flag_update_polyhedral = 0;
  }
}