shithub: zelda3

Download patch

ref: ad9e25c0879389dac842edb5d2e705483880b7dc
parent: ed9661ac979b27fc4e7c5b522ed81583807c63db
author: Snesrev <snesrev@protonmail.com>
date: Sun Sep 18 19:20:33 EDT 2022

Display up to 240 lines instead of 224

--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,5 @@
 /*.exe
 /*.out
 /snes/*.o
-/msu/alttp_msu-*.pcm
\ No newline at end of file
+/msu/alttp_msu-*.pcm
+/tmp/
--- a/attract.c
+++ b/attract.c
@@ -9,7 +9,7 @@
 #include "attract.h"
 #include "sprite_main.h"
 
-const uint16 kMapMode_Zooms1[224] = {
+const uint16 kMapMode_Zooms1[240] = {
   375, 374, 373, 373, 372, 371, 371, 370, 369, 369, 368, 367, 367, 366, 365, 365,
   364, 363, 363, 361, 361, 360, 359, 359, 358, 357, 357, 356, 355, 355, 354, 354,
   353, 352, 352, 351, 351, 350, 349, 349, 348, 348, 347, 346, 346, 345, 345, 344,
@@ -24,8 +24,9 @@
   281, 280, 280, 279, 279, 279, 278, 278, 278, 277, 277, 276, 276, 276, 275, 275,
   275, 274, 274, 273, 273, 273, 272, 272, 272, 271, 271, 271, 270, 270, 269, 269,
   269, 268, 268, 268, 267, 267, 267, 266, 266, 266, 265, 265, 265, 264, 264, 264,
+  263, 263, 262, 262, 262, 261, 261, 261, 260, 260, 260, 259, 259, 259, 258, 258,
 };
-const uint16 kMapMode_Zooms2[224] = {
+const uint16 kMapMode_Zooms2[240] = {
   136, 136, 135, 135, 135, 135, 135, 134, 134, 134, 133, 133, 133, 133, 132, 132,
   132, 132, 132, 131, 131, 131, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129,
   128, 128, 128, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 125, 125, 125,
@@ -40,6 +41,7 @@
   102, 102, 102, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100,
   100,  99,  99,  99,  99,  99,  99,  99,  99,  98,  98,  98,  98,  98,  97,  97,
   97,  97,  97,  97,  97,  97,  97,  96,  96,  96,  96,  96,  96,  96,  96,  96,
+  95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94,
 };
 static const uint8 kAttract_Legendgraphics_0[157+1] = {
   0x61, 0x65, 0x40, 0x28,    0, 0x35, 0x61, 0x85, 0x40, 0x28, 0x10, 0x35, 0x61, 0xa5,    0, 0x29,
@@ -994,7 +996,7 @@
 }
 
 void Attract_ControlMapZoom() {  // 8cf783
-  for (int i = 223; i >= 0; i--)
+  for (int i = 240 - 1; i >= 0; i--)
     hdma_table_dynamic[i] = kMapMode_Zooms1[i] * timer_for_mode7_zoom >> 8;
 }
 
--- a/attract.h
+++ b/attract.h
@@ -5,8 +5,8 @@
 } AttractOamInfo;
 
 
-extern const uint16 kMapMode_Zooms1[224];
-extern const uint16 kMapMode_Zooms2[224];
+extern const uint16 kMapMode_Zooms1[240];
+extern const uint16 kMapMode_Zooms2[240];
 void Attract_DrawSpriteSet2(const AttractOamInfo *p, int n);
 void Attract_ZeldaPrison_Case0();
 void Attract_ZeldaPrison_Case1();
--- a/config.c
+++ b/config.c
@@ -245,13 +245,17 @@
       return true;
     } else if (StringEqualsNoCase(key, "ExtendedAspectRatio")) {
       const char* s;
+      int h = 224;
+      // todo: make it not depend on the order
       while ((s = NextDelim(&value, ',')) != NULL) {
-        if (strcmp(s, "16:9") == 0)
-          g_config.extended_aspect_ratio = (224 * 16 / 9 - 256) / 2;
+        if (strcmp(s, "extend_y") == 0)
+          h = 240, g_config.extend_y = true;
+        else if (strcmp(s, "16:9") == 0)
+          g_config.extended_aspect_ratio = (h * 16 / 9 - 256) / 2;
         else if (strcmp(s, "16:10") == 0)
-          g_config.extended_aspect_ratio = (224 * 16 / 10 - 256) / 2;
+          g_config.extended_aspect_ratio = (h * 16 / 10 - 256) / 2;
         else if (strcmp(s, "18:9") == 0)
-          g_config.extended_aspect_ratio = (224 * 18 / 9 - 256) / 2;
+          g_config.extended_aspect_ratio = (h * 18 / 9 - 256) / 2;
         else if (strcmp(s, "4:3") == 0)
           g_config.extended_aspect_ratio = 0;
         else if (strcmp(s, "unchanged_sprites") == 0)
--- a/config.h
+++ b/config.h
@@ -43,7 +43,7 @@
   uint16 audio_samples;
   bool autosave;
   uint8 extended_aspect_ratio;
-  bool extended_aspect_ratio_nospr;
+  bool extend_y, extended_aspect_ratio_nospr;
   bool display_perf_title;
   bool enable_msu;
   bool item_switch_lr;
--- a/load_gfx.c
+++ b/load_gfx.c
@@ -1251,7 +1251,7 @@
         return;
       zelda_snes_dummy_write(HDMAEN, 0);
       HDMAEN_copy = 0;
-      for (int i = 0; i < 32 * 7; i++)
+      for (int i = 0; i < 240; i++)
         hdma_table_dynamic[i] = 0x778;
       HDMAEN_copy = 0xc0;
     }
@@ -1471,8 +1471,7 @@
   uint16 r6 = r14 * 2;
   if (r6 < 224)
     r6 = 224;
-  uint16 r10 = r6 - r14;
-  uint16 r4 = r14 - r10;
+  uint16 r4 = r14 * 2 - r6;
   for(;;) {
     uint16 r8 = 0xff;
     if (r6 < spotlight_y_upper) {
@@ -1481,17 +1480,20 @@
         spotlight_var4--;
       r8 = IrisSpotlight_CalculateCircleValue(t);
     }
-    if (r4 < 0xe0)
-      hdma_table[r4] = r8;
-    if (r6 < 0xe0)
-      hdma_table[r6] = r8;
+    if (r4 < 240)
+      hdma_table_dynamic[r4] = r8;
+    if (r6 < 240)
+      hdma_table_dynamic[r6] = r8;
     if (r4 == r14)
       break;
     r4++, r6--;
   }
 
-  memcpy(hdma_table_dynamic, hdma_table, 224  * sizeof(uint16));
+  for (int i = 224; i < 240; i++)
+    hdma_table_dynamic[i] = 0;
 
+  memcpy(hdma_table_unused, hdma_table_dynamic, 224  * sizeof(uint16));
+
   spotlight_var1 += kSpotlight_delta_size[spotlight_var2 >> 1];
 
   if (spotlight_var1 != kSpotlight_goal[spotlight_var2 >> 1])
@@ -1518,7 +1520,7 @@
 }
 
 void IrisSpotlight_ResetTable() {  // 80f427
-  for (int i = 0; i < 224; i++)
+  for (int i = 0; i < 240; i++)
     hdma_table_dynamic[i] = 0xff00;
 }
 
@@ -1566,7 +1568,7 @@
         a = 0xff;
       else
         a = r12;
-      if (r4 < 224)
+      if (r4 < 240)
         hdma_table_dynamic[r4] = (a != 0xffff) ? a : 0xff;
     }
     if (r6 >= spotlight_y_upper) {
@@ -1576,7 +1578,7 @@
         word_7E0678--;
       a = r12;
     }
-    if (r6 < 224)
+    if (r6 < 240)
       hdma_table_dynamic[r6] = (a != 0xffff) ? a : 0xff;
   } while (r6--, r10 != r4++);
 }
@@ -1603,7 +1605,7 @@
       uint16 a = r4;
       do {
         a *= 2;
-      } while (a >= 448);
+      } while (a >= 480);
       hdma_table_dynamic[a >> 1] = r12 == 0xffff ? 0xff : r12;
     }
   } while (++r4 < 225);
--- a/main.c
+++ b/main.c
@@ -43,7 +43,6 @@
 
 
 enum {
-  kRenderHeight = 224 * 2,
   kDefaultFullscreen = 0,
   kDefaultWindowScale = 2,
   kMaxWindowScale = 10,
@@ -66,8 +65,7 @@
 static int g_curr_fps;
 static int g_ppu_render_flags = 0;
 static bool g_run_without_emu = false;
-static int g_snes_width;
-static const int g_snes_height = kRenderHeight;
+static int g_snes_width, g_snes_height;
 
 void NORETURN Die(const char *error) {
   fprintf(stderr, "Error: %s\n", error);
@@ -182,6 +180,7 @@
   ZeldaInitialize();
   g_zenv.ppu->extraLeftRight = UintMin(g_config.extended_aspect_ratio, kPpuExtraLeftRight);
   g_snes_width = 2 * (g_config.extended_aspect_ratio * 2 + 256);
+  g_snes_height = (g_config.extend_y ? 240 : 224) * 2;
 
 
   // Delay actually setting those features in ram until any snapshots finish playing.
@@ -196,7 +195,9 @@
     g_wanted_zelda_features = f;
   }
 
-  g_ppu_render_flags = g_config.new_renderer * kPpuRenderFlags_NewRenderer | g_config.enhanced_mode7 * kPpuRenderFlags_4x4Mode7;
+  g_ppu_render_flags = g_config.new_renderer * kPpuRenderFlags_NewRenderer |
+                       g_config.enhanced_mode7 * kPpuRenderFlags_4x4Mode7 |
+                       g_config.extend_y * kPpuRenderFlags_Height240;
   msu_enabled = g_config.enable_msu;
 
   if (g_config.fullscreen == 1)
--- a/messaging.c
+++ b/messaging.c
@@ -534,7 +534,7 @@
   TMW_copy = TM_copy;
   TSW_copy = TS_copy;
   HDMAEN_copy = 0x80;
-  memset(hdma_table_dynamic, 0, 0x1e0);
+  memset(hdma_table_dynamic, 0, 240 * sizeof(uint16));
 }
 
 void DesertPrayer_InitializeIrisHDMA() {  // 87ea06
@@ -577,11 +577,11 @@
     uint8 t6 = (r0 < 256) ? r0 : (r0 < 512) ? 255 : 0;
     uint8 t7 = (r2 < 256) ? r2 : 255;
     uint16 r6 = t7 << 8 | t6;
-    if (k < 224)
+    if (k < 240)
      hdma_table_dynamic[k] = (r6 == 0xffff) ? 0xff : r6;
     if (sign16(spotlight_y_lower) || (r4 >= spotlight_y_lower && r4 < spotlight_y_upper)) {
       k = BYTE(spotlight_var4) - 2 + r14;
-      if (k < 224)
+      if (k < 240)
         hdma_table_dynamic[k] = (r6 == 0xffff) ? 0xff : r6;
       spotlight_var4++;
     }
--- a/misc.c
+++ b/misc.c
@@ -260,7 +260,7 @@
 
 static void KillAghanim_Func5() {
   HdmaSetup(0, 0xf2fb, 0x41, 0, (uint8)WH0, 0);
-  for (int i = 0; i < 224; i++)
+  for (int i = 0; i < 240; i++)
     hdma_table_dynamic[i] = 0xff00;
   palette_filter_countdown = 0;
   darkening_or_lightening_screen = 0;
--- a/overworld.c
+++ b/overworld.c
@@ -344,7 +344,7 @@
   HdmaSetup(0xF2FB, 0xF2FB, 0x42, (uint8)BG1HOFS, (uint8)BG2HOFS, 0);
 
   uint16 v = BG2HOFS_copy2;
-  for (int i = 0; i < 32 * 7; i++)
+  for (int i = 0; i < 240; i++)
     hdma_table_dynamic[i] = v;
   HDMAEN_copy = 0xc0;
 }
@@ -354,11 +354,12 @@
   if (frame_counter & 1)
     return;
 
-  int x = 0x1a0 / 2, y = 0x1b0 / 2;
+  int y = 240 - 8;
   do {
-    hdma_table_dynamic[y] = hdma_table_dynamic[y + 2] = hdma_table_dynamic[y + 4] = hdma_table_dynamic[y + 6] = hdma_table_dynamic[x];
-    x -= 8, y -= 8;
+    hdma_table_dynamic[y] = hdma_table_dynamic[y + 2] = hdma_table_dynamic[y + 4] = hdma_table_dynamic[y + 6] = hdma_table_dynamic[y - 8];
+    y -= 8;
   } while (y != 0);
+
   int i = mirror_vars.var0 >> 1;
   int t = mirror_vars.var6 + mirror_vars.var3[i];
   if (!sign16(t - mirror_vars.var1[i] ^ mirror_vars.var1[i])) {
@@ -389,10 +390,10 @@
   MirrorWarp_RunAnimationSubmodules();
   if (frame_counter & 1)
     return;
-  int x = 0x1a0 / 2, y = 0x1b0 / 2;
+  int y = 240 - 8;
   do {
-    hdma_table_dynamic[y] = hdma_table_dynamic[y + 2] = hdma_table_dynamic[y + 4] = hdma_table_dynamic[y + 6] = hdma_table_dynamic[x];
-    x -= 8, y -= 8;
+    hdma_table_dynamic[y] = hdma_table_dynamic[y + 2] = hdma_table_dynamic[y + 4] = hdma_table_dynamic[y + 6] = hdma_table_dynamic[y - 8];
+    y -= 8;
   } while (y != 0);
 
   uint16 t = hdma_table_dynamic[0xc0] | hdma_table_dynamic[0xc8] | hdma_table_dynamic[0xd0] | hdma_table_dynamic[0xd8];
--- a/snes/ppu.c
+++ b/snes/ppu.c
@@ -41,6 +41,7 @@
   ppu->extraLeftCur = 0;
   ppu->extraRightCur = 0;
   ppu->extraLeftRight = kPpuExtraLeftRight;
+  ppu->extraBottomCur = 0;
   ppu->vramPointer = 0;
   ppu->vramIncrementOnHigh = false;
   ppu->vramIncrement = 1;
@@ -188,6 +189,15 @@
     memset(&ppu->objBuffer.prio, 0x05, sizeof(ppu->objBuffer.prio));
     ppu->lineHasSprites = !ppu->forcedBlank && ppu_evaluateSprites(ppu, line - 1);
 
+    // outside of visible range?
+    if (line >= 225 + ppu->extraBottomCur) {
+      uint8 *dst = &ppu->renderBuffer[(line - 1) * 2 * ppu->renderPitch];
+      size_t n = sizeof(uint32) * 2 * (256 + ppu->extraLeftRight * 2);
+      memset(dst, 0, n);
+      memset(dst + ppu->renderPitch, 0, n);
+      return;
+    }
+
     // actual line
     if (ppu->renderFlags & kPpuRenderFlags_NewRenderer) {
       PpuDrawWholeLine(ppu, line);
@@ -705,9 +715,10 @@
   ppu->mode7PerspectiveHigh = 1.0f / high;
 }
 
-void PpuSetExtraSideSpace(Ppu *ppu, int left, int right) {
+void PpuSetExtraSideSpace(Ppu *ppu, int left, int right, int bottom) {
   ppu->extraLeftCur = UintMin(left, ppu->extraLeftRight);
   ppu->extraRightCur = UintMin(right, ppu->extraLeftRight);
+  ppu->extraBottomCur = UintMin(bottom, 16);
 }
 
 static FORCEINLINE float FloatInterpolate(float x, float xmin, float xmax, float ymin, float ymax) {
--- a/snes/ppu.h
+++ b/snes/ppu.h
@@ -50,6 +50,8 @@
   kPpuRenderFlags_NewRenderer = 1,
   // Render mode7 upsampled by 4x4
   kPpuRenderFlags_4x4Mode7 = 2,
+  // Use 240 height instead of 224
+  kPpuRenderFlags_Height240 = 4,
 };
 
 
@@ -60,7 +62,7 @@
   uint8_t renderFlags;
   uint32_t renderPitch;
   uint8_t *renderBuffer;
-  uint8_t extraLeftCur, extraRightCur, extraLeftRight;
+  uint8_t extraLeftCur, extraRightCur, extraLeftRight, extraBottomCur;
   float mode7PerspectiveLow, mode7PerspectiveHigh;
 
   Snes* snes;
@@ -169,6 +171,6 @@
 bool PpuBeginDrawing(Ppu *ppu, uint8_t *buffer, size_t pitch, uint32_t render_flags);
 
 void PpuSetMode7PerspectiveCorrection(Ppu *ppu, int low, int high);
-void PpuSetExtraSideSpace(Ppu *ppu, int left, int right);
+void PpuSetExtraSideSpace(Ppu *ppu, int left, int right, int bottom);
 
 #endif
--- a/variables.h
+++ b/variables.h
@@ -804,7 +804,6 @@
 #define bird_travel_y_lo ((uint8*)(g_ram+0x1AD0))
 #define bird_travel_y_hi ((uint8*)(g_ram+0x1AE0))
 #define birdtravel_var1 ((uint8*)(g_ram+0x1AF0))
-#define hdma_table_dynamic ((uint16*)(g_ram+0x1B00))
 #define text_msgbox_topleft_copy (*(uint16*)(g_ram+0x1CD0))
 #define text_msgbox_topleft (*(uint16*)(g_ram+0x1CD2))
 #define text_render_state (*(uint8*)(g_ram+0x1CD4))
@@ -1180,7 +1179,7 @@
 #define overworld_music ((uint8*)(g_ram+0x15B00))
 #define freeRam ((uint8*)(g_ram+0x15BA0))
 #define enemy_damage_data ((uint8*)(g_ram+0x16000))
-#define hdma_table ((uint16*)(g_ram+0x17000))
+#define hdma_table_unused ((uint16*)(g_ram+0x17000))
 #define kTextDialoguePointers ((uint8*)(g_ram+0x171C0))
 #define word_7F8000 (*(uint16*)(g_ram+0x18000))
 #define word_7F9B52 (*(uint16*)(g_ram+0x19B52))
--- a/zelda3.ini
+++ b/zelda3.ini
@@ -6,9 +6,9 @@
 
 # Extended aspect ratio, either 16:9, 16:10, or 18:9. 4:3 means normal aspect ratio.
 # Add ", unchanged_sprites" to avoid changing sprite spawn/die behavior. Without this
-# replays will be incompatible
+# replays will be incompatible. Add "extend_y, " right before the aspect radio specifier
+# to display 240 lines instead of 224.
 ExtendedAspectRatio = 4:3
-
 
 [Graphics]
 # Fullscreen mode (0=windowed, 1=desktop fullscreen, 2=fullscreen w/mode change)
--- a/zelda_cpu_infra.c
+++ b/zelda_cpu_infra.c
@@ -78,6 +78,7 @@
 
 static bool g_fail;
 
+// b is mine, a is theirs
 static void VerifySnapshotsEq(Snapshot *b, Snapshot *a, Snapshot *prev) {
   memcpy(b->ram, a->ram, 16);
   b->ram[0xfa1] = a->ram[0xfa1];
@@ -107,6 +108,10 @@
 
   memcpy(&b->ram[0x1f0d], &a->ram[0x1f0d], 0x3f - 0xd);
   memcpy(b->ram + 0x138, a->ram + 0x138, 256 - 0x38); // copy the stack over
+
+  memcpy(a->ram + 0x1DBA0, b->ram + 0x1DBA0, 240 * 2);  // hdma_table
+  memcpy(b->ram + 0x1B00, b->ram + 0x1DBA0, 224 * 2);  // hdma_table (partial)
+
 
   if (memcmp(b->ram, a->ram, 0x20000)) {
     fprintf(stderr, "@%d: Memory compare failed (mine != theirs, prev):\n", frame_counter);
--- a/zelda_rtl.c
+++ b/zelda_rtl.c
@@ -162,23 +162,30 @@
 }
 
 void ConfigurePpuSideSpace() {
-  // Let PPU impl know about the maximum allowed extra space on the sides
-  int extra_right = 0, extra_left = 0;
+  // Let PPU impl know about the maximum allowed extra space on the sides and bottom
+  int extra_right = 0, extra_left = 0, extra_bottom = 0;
 //  printf("main %d, sub %d  (%d, %d, %d)\n", main_module_index, submodule_index, BG2HOFS_copy2, room_bounds_x.v[2 | (quadrant_fullsize_x >> 1)], quadrant_fullsize_x >> 1);
   int mod = main_module_index;
   if (mod == 14)
     mod = saved_module_for_menu;
   if (mod == 9) {
+    // outdoors
     extra_left = BG2HOFS_copy2 - ow_scroll_vars0.xstart;
     extra_right = ow_scroll_vars0.xend - BG2HOFS_copy2;
+    extra_bottom = ow_scroll_vars0.yend - BG2VOFS_copy2;
   } else if (mod == 7) {
+    // indoors
     int qm = quadrant_fullsize_x >> 1;
     extra_left = IntMax(BG2HOFS_copy2 - room_bounds_x.v[qm], 0);
     extra_right = IntMax(room_bounds_x.v[qm + 2] - BG2HOFS_copy2, 0);
+
+    int qy = quadrant_fullsize_y >> 1;
+    extra_bottom = IntMax(room_bounds_y.v[qy + 2] - BG2VOFS_copy2, 0);
   } else if (mod == 20) {
     extra_left = kPpuExtraLeftRight, extra_right = kPpuExtraLeftRight;
+    extra_bottom = 16;
   }
-  PpuSetExtraSideSpace(g_zenv.ppu, extra_left, extra_right);
+  PpuSetExtraSideSpace(g_zenv.ppu, extra_left, extra_right, extra_bottom);
 }
 
 bool ZeldaDrawPpuFrame(uint8 *pixel_buffer, size_t pitch, uint32 render_flags) {
@@ -206,7 +213,9 @@
   if (g_zenv.ppu->extraLeftRight != 0)
     ConfigurePpuSideSpace();
 
-  for (int i = 0; i < 225; i++) {
+  int height = render_flags & kPpuRenderFlags_Height240 ? 240 : 224;
+
+  for (int i = 0; i <= height; i++) {
     if (i == 128 && irq_flag) {
       zelda_ppu_write(BG3HOFS, selectfile_var8);
       zelda_ppu_write(BG3HOFS, selectfile_var8 >> 8);
--- a/zelda_rtl.h
+++ b/zelda_rtl.h
@@ -207,6 +207,10 @@
 #define R18 (*(uint16*)(g_ram+0xca))
 #define R20 (*(uint16*)(g_ram+0xcc))
 
+// Relocated the hdma table so it can fit 240 rows
+#define hdma_table_dynamic_orig_pos ((uint16*)(g_ram+0x1B00))
+#define hdma_table_dynamic ((uint16*)(g_ram+0x1DBA0))
+
 void zelda_apu_write(uint32_t adr, uint8_t val);
 void zelda_apu_write_word(uint32_t adr, uint16_t val);
 uint8_t zelda_read_apui00();