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();