Here comes another Castlevania TAS, on the same day. Did I see this coming? I didn't. :P
  • Emulator Used: Bizhawk 2.2.2
  • Genre: Metroidvania
  • Takes damage and dies to save time, MASSIVELY
On Aug 27, 2018, Burb discovered a wrong warp in FDS Castlevania 2 that allows the player to skip to the final boss from early game and made a testrun that beats the game in 05:59.824. This run uses the inputs from that run for the first 90 seconds. Hence I made this movie.

Version Choice

This run is done on the very first release of Castlevania 2, on Famicom Disk System. This run is made possible by two of its exclusive glitches. Another important differences is that the height of Simon's jump while whipping is slightly lower than how it is on the NES releases; because of this, the wrong warp and certain damage boost featured in the NES run cannot be done on the FDS version.

Jump Animation Cancellation

If you use your whip during a jump and the animation of the whip ends exactly when you land, 1 frame is skipped by canceling the landing animation. However, due to how easy it is to create lags in this game, this trick isn't used too often in this run since it only saves time when using the whip doesn't generate any extra lags at all.

Day-night Transition Skip

Remember the infamous staple of this game? You can skip it on FDS version by entering the next screen on the exact same frame the in-game clock reaches 18:00 or 6:00. This glitched is used around 2:57 in this movie to skip the first and only day-night transition. However, this essentially created a bottleneck since I would need to wait for the clock to reach 18:00 before triggering this screen transition. Therefore, lag-reduction is the top priority before this point since there is time to spare.

Loading Sequence Death Warp

Reducing HP to 0 during a loading sequence triggers a wrong warp, as seen around 3:13 in this run. I tried to trigger a day-night transition skip and a death warp at the same time, but this seems to be impossible. Another option was to make the day-night transition happen right after the loading sequence, but it turned out to be slower than skipping the day-night transition. I can't just let the day-night transition happen while Simon is in the monochrome glitch town either becausethat would cause the universe to explode.

Subterranean Death Warp

If you exit a town after doing a floor drop without waiting for the camera to catch up, you will enter the void beneath the next map and die. Usually, you will simply respawn in the town, but for unknown reasons, you can warp to glitched maps from certain places on FDS version. FYI, the monochrome glitch town takes you to a different place than the normal Town of Ondol does, and the combination of these two death warps is therefore necessary for getting into the final castle.
A friendly reminder: If you sit through the credits and go back to the save file selection screen, you will notice that all save files are corrupted and you can't even delete them. In other words, the game becomes practically unplayable at this point. Therefore, it's probably not a good idea for you to perform this glitch on actual FDS hardware! :D

Special Thanks

  • Burb: for discovering this wrong warp route that allows skipping the majority of the game and jump animation cancellation for minor optimization.
  • Producks & Koh1fds: for posting information about the death warp on the forum. Koh1fds was actually pretty close to finding this specific route to the final boss.
  • zggzdydp: for the run on NES version.
  • YOU, for reading the submission text and watching the movie.

Memory: Judging
Memory: Changing branch name to warp glitch per guidelines.
Memory: Delaying to implement improvements per this post.
Memory: Updating with 311 frame improvement and resetting to judging underway.
Memory: With the updated file, this seems fairly optimized.
feos has confirmed here that the ending does play as normal despite the corruption of saves. We don't have any rules against leaving the game in an unplayable state after beating it, just that the ending does indeed play.
This movie got a very positive rating, however given how much gameplay is cut from this branch and the high enough ratings for the existing movie, I believe this deserves a separate branch altogether.
Accepting to Moons as a new branch.
fsvgm777: Processing.


Site Admin, Skilled player (1254)
Joined: 4/17/2010
Posts: 11475
Location: Lake Char­gogg­a­gogg­man­chaugg­a­gogg­chau­bun­a­gung­a­maugg
Okay so I'm comparing to regular startup. CV2J stores 3 save files at address $07D9, 13 bytes per file, up to $07FF. First byte denotes active file (#$00 = empty, #$FF = valid). Then 5 name letters (from #$D3 = ',' to #$F4 = 'Z'). Then some stats of your playthrough, reportedly these: items acquired, character level, time taken in game in days. At frame 12730 all these save files get filled with garbage. Code samples This code checks which save files are not empty. Even if there is data in them, as long as their Active byte is 0, they are considered empty. When viewing in FDSExplorer, bank is #6, ID 16, DRQ_OPEN.
Language: asm6502

$6785: AD D9 07 LDA $07D9 ; check save 1 $6788: F0 09 BEQ $6793 ; skip if 0 $678A: A9 00 LDA #$00 $678C: A2 25 LDX #$25 $678E: A0 CF LDY #$CF $6790: 20 E3 6A JSR $6AE3 $6793: AD E6 07 LDA $07E6 ; check save 2 $6796: F0 09 BEQ $67A1 ; skip if 0 $6798: A9 01 LDA #$01 $679A: A2 26 LDX #$26 $679C: A0 0F LDY #$0F $679E: 20 E3 6A JSR $6AE3 $67A1: AD F3 07 LDA $07F3 ; check save 3 $67A4: F0 09 BEQ $67AF ; skip if 0 $67A6: A9 02 LDA #$02 $67A8: A2 26 LDX #$26 $67AA: A0 4F LDY #$4F $67AC: 20 E3 6A JSR $6AE3 $67AF: AD 91 01 LDA $0191 $67B2: 8D 90 01 STA $0190 $67B5: 4C A8 66 JMP $66A8
Then, in the same bank, this code compares each Active byte to 0 again.
Language: asm6502

$66DF: A2 00 LDX #$00 ; init $66E1: 86 93 STX $93 $66E3: 86 16 STX $16 $66E5: 86 17 STX $17 $66E7: BD D9 07 LDA $07D9,X ; check each file using X reg as offset $66EA: F0 04 BEQ $66F0 $66EC: E6 17 INC $17 ; occupied slot count $66EE: D0 02 BNE $66F2 $66F0: E6 16 INC $16 ; empty slot count $66F2: E6 93 INC $93 $66F4: A5 93 LDA $93 $66F6: 20 73 66 JSR $6673 $66F9: AA TAX $66FA: E0 27 CPX #$27 $66FC: 90 E9 BCC $66E7 $66FE: 60 RTS
Same bank, finally Active byte is compared to #$FF.
Language: asm6502

$66A8: 20 DF 66 JSR $66DF ; do the above subroutine $66AB: AD 90 01 LDA $0190 ; current save index $66AE: C9 03 CMP #$03 ; OoB? $66B0: B0 11 BCS $66C3 ; stop $66B2: 20 70 66 JSR $6670 $66B5: A8 TAY ; copy index to Y reg $66B6: B9 D9 07 LDA $07D9,Y ; load save Active flag $66B9: C9 FF CMP #$FF ; #$FF = -1 = valid $66BB: F0 21 BEQ $66DE $66BD: EE 90 01 INC $0190 ; increment current save index $66C0: 4C AB 66 JMP $66AB
Observations Writing 00 to Active flag of any save makes the game completely ignore its data when save selection screen shows. Writing #$FF there makes it possible to erase or load the save. Having anything else, which this movie does, makes the game unable to acknowledge or disregard the saves. If I write #$FF, each state loads just fine, and then after switching the disk side, the game loads with whatever stats your save data contained. Save 1 Stats data: 00000100000800 Save 2 Stats data: C797949AC79794 Save 3 Stats data: CE929392937E7F Pressing pause for slot 3 freezes the game. This movie The game finds no empty saves, and no valid ones, so it doesn't allow you to load them, and sets the cursor to Erase. But when you select that, it can't find any saves to erase and freezes. Setting any of these to #$00 or #$FF allows you to either load/erase or create a save: $07D9 $07E6 $07F3 So the conclusion is, both RAM and save data are corrupted. It is possible to poke a single byte to make everything work, even though the results may be corrupted as well. But the ending occurs the proper way.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Alyosha
He/Him
Editor, Emulator Coder, Expert player (3821)
Joined: 11/30/2014
Posts: 2829
Location: US
Out of curiousity, what happens when you reset the console? $0790 is just ordinary RAM, so unless it's written back to disc somewhere it looks like the corruption should clear up upon reset. Pretty interesting either way, yes from me.
Editor, Active player (297)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Alyosha wrote:
Out of curiousity, what happens when you reset the console?
On the NES version, this happens. (Annotations added)
Language: asm6502

_Reset $FFD0 D8: cld $FFD1 78: sei $FFD2 EE FF FF: inc _data_1FFFF $FFD5 4C 00 C0: jmp Main_ProgramBegin Main_ProgramBegin $C000 AD 02 20: lda $2002 $C003 10 FB: bpl Main_ProgramBegin - $C005 AD 02 20: lda $2002 $C008 10 FB: bpl - ; $C005 $C00A A2 FF: ldx #$FF $C00C 9A: txs $C00D 20 D8 C0: jsr ResetPPUtoKnownState $C010 20 9D C0: jsr ResetAPUtoKnownState $C013 E8: inx ; Sets X = 0 $C014 8A: txa $C015 85 00: sta TempPtr00_lo ; Pointer initialized as $0000 $C017 85 01: sta TempPtr00_hi ; Pointer itself is at $00—$01 $C019 A0 FE: ldy #$FE - $C01B 88: dey $C01C 91 00: sta (TempPtr00_lo),y ; Zero-initialises $00—$FD $C01E D0 FB: bne - ; $C01B $C020 E6 01: inc TempPtr00_hi ; Pointer initialized as $0100 $C022 A0 00: ldy #$00 $C024 A2 08: ldx #$08 - $C026 91 00: sta (TempPtr00_lo),y ; Zero-initializes $100—$7FF $C028 C8: iny $C029 D0 FB: bne - ; $C026 $C02B E6 01: inc TempPtr00_hi $C02D E4 01: cpx TempPtr00_hi $C02F D0 F5: bne - ; $C026 ;… ResetPPUtoKnownState $C0D8 A9 00: lda #$00 $C0DA 8D 06 20: sta $2006 $C0DD 8D 06 20: sta $2006 $C0E0 8D 00 20: sta $2000 $C0E3 8D 01 20: sta $2001 $C0E6 60: rts ResetAPUtoKnownState $C09D A9 0F: lda #$0F $C09F 8D 15 40: sta $4015 $C0A2 A9 C0: lda #$C0 $C0A4 8D 17 40: sta $4017 $C0A7 60: rts
In other words, it unconditionally zero-initializes all RAM except $00FE and $00FF. And sets $01 to #$08, but it will of course do many more memory writes after that. The RAM addresses $FE and $FF store the cached/planned values of registers $2001 and $2000 respectively. During the reset process, they will be initialized to #$1E and #$A8 respectively, before reading. In other words, all RAM addresses will be initialized to a known value before they are read the first time. The FDS version does something similar:
Language: asm6502

00781B A2 FF ldx #$FF 00781D E8 inx 00781E A9 01 lda #$01 007820 85 01 sta $01 007822 8A txa 007823 86 00 stx $00 007825 A0 04 ldy #$04 007827 91 00 sta ($00),y ; Zero-initialize $104—$1FF 007829 C8 iny 00782A D0 FB bne $007827 <Lbl_0077F0+55> 00782C E6 01 inc $01 00782E A6 01 ldx $01 007830 E0 08 cpx #$08 ; Zero-initialize $200-$7FF 007832 D0 F3 bne $007827 <Lbl_0077F0+55> 007834 A0 00 ldy #$00 007836 98 tya 007837 99 00 00 sta $0000,y ; Zero-initialize $00—$F8 00783A C8 iny 00783B C0 F9 cpy #$F9 00783D 90 F8 bcc $007837 <Lbl_0077F0+71> ; RAM init done, launch the game 00783F A9 00 lda #$00 007841 8D 00 07 sta $0700 007844 20 AD 78 jsr $78AD 007847 20 96 BC jsr $BC96 ; Main loop, permute random seed 00784A A5 2E lda $2E 00784C 18 clc 00784D 65 1A adc $1A 00784F 85 2E sta $2E 007851 4C 4A 78 jmp $784A
This is from near the beginning of the DRQ_MAIN module, and according to my understand this is invoked at the reset, when the FDS BIOS passes control to it. Variables $F9—$FF are required by the FDS BIOS if I remember correctly (same purpose as in the USA version; the USA version did a straightforward reimplementation of the same stuff the BIOS does, for ease of porting), and $100—$103 are used by the game for purposes that I do not remember off-hand. Specific to FDS, it is preceded by some code that seems to be able to skip the RAM reset though.
Language: asm6502

.fds_file $05,$01,'DRQ_MAIN', $77F0, 26640, $00 ;Ends at $E000 Lbl_0077F0: 0077F0 A9 C0 lda #$C0 0077F2 8D 00 01 sta $0100 0077F5 A9 80 lda #$80 0077F7 8D 01 01 sta $0101 0077FA A9 00 lda #$00 0077FC 8D 03 01 sta $0103 0077FF AD 02 20 lda $2002 007802 10 FB bpl $0077FF <Lbl_0077F0+15> 007804 AD 02 20 lda $2002 007807 10 FB bpl $007804 <Lbl_0077F0+20> 007809 20 E3 78 jsr $78E3 00780C A9 00 lda #$00 00780E 20 46 BC jsr $BC46 ; What does this subroutine do? If it returns nonzero, instead of RAM reset, a full FDS BIOS reset is performed. 007811 F0 08 beq $00781B <Lbl_0077F0+43> 007813 A9 00 lda #$00 ; Skip 007815 8D 03 01 sta $0103 007818 4C 24 EE jmp $EE24 ; Jumps to FDS BIOS reset routine
Alyosha
He/Him
Editor, Emulator Coder, Expert player (3821)
Joined: 11/30/2014
Posts: 2829
Location: US
So when does the game actually copy the save files back to disc? If it never happens because they are corrupted then I guess this is only a temporary corruption, which would be a nice coincidence. What happens if you hit reset in the emulator after the game returns to the title screen, is the corruption still there? I admit I haven't looked at the SRAM code in FDS in quite a long time so I'm not familiar exactly with what it's doing.
Burb
He/Him
Player (60)
Joined: 8/28/2018
Posts: 5
Alyosha wrote:
So when does the game actually copy the save files back to disc? If it never happens because they are corrupted then I guess this is only a temporary corruption, which would be a nice coincidence. What happens if you hit reset in the emulator after the game returns to the title screen, is the corruption still there? I admit I haven't looked at the SRAM code in FDS in quite a long time so I'm not familiar exactly with what it's doing.
The game saves after the credits are fully played out. After that, it saves the data to disk and returns to the title screen. So if you reset before that, it's fine.
Mitjitsu
He/Him
Banned User
Joined: 4/24/2006
Posts: 2997
It was like you were in the games inner code. Yes vote.
Post subject: Movie published
TASVideoAgent
They/Them
Moderator
Joined: 8/3/2004
Posts: 15576
Location: 127.0.0.1
This movie has been published. The posts before this message apply to the submission, and posts after this message apply to the published movie. ---- [3795] FDS Castlevania II: Simon's Quest "warp glitch" by Fortranm & Burb in 05:35.71