Castlevania II (Simon's Quest) - Multilingual enhancement

Started by Bisqwit, December 19, 2012, 01:38:36 PM

Previous topic - Next topic

hackbar

#1120
I've found a few bugs, all in saves. This is with a Powerpak on a real NES, with cv2eng-2_11_1-ntsc-stm1mwfmFrUp4.

2. On a cold boot, the load screen has no cursor visable.
3. After loading a game, some of the tiles flicker.
4. On the save screen, the music stutters when saving.
5. When opening the loading screen, there's often some garbage, and the screen is sometimes offset vertically for a frame or two.

storall

cv2eng-2_11_1-ntsc-stm1mwfmFrup4-ips  (FCEUX, Mesen)
cv2eng-2_11_1-ntsc-stm10mwfmFrup4-ips  (Mesen)


Figured out why they have in-game save crash problem. I have iNES 2.0 headers and they report "PRG NVRAM: 0B" or no save ram after patching.

I guess Mesen is pickier about the flag with MMC4.

Bisqwit

Quote from: hackbar on July 30, 2020, 04:39:57 PMI've found a few bugs, all in saves. This is with a Powerpak on a real NES, with cv2eng-2_11_1-ntsc-stm1mwfmFrUp4.

1. On a cold boot, from the start menu, go to to the load game screen and hit select. The game crashes, showing a half-screen of credits for the sram system.
2. On a cold boot, the load screen has no cursor visable.
3. After loading a game, some of the tiles flicker.
4. On the save screen, the music stutters when saving.
5. When opening the loading screen, there's often some garbage, and the screen is sometimes offset vertically for a frame or two.
6. from the save menu in game, if you hit select it will act as hitting the "confirm" choice, ie saving the game, even though the cursor isn't on that choice. I would expect select to go back to the game, or do nothing.

These problems were previously reported to me by someone through e-mail. Because I was not able to reproduce the reported symptoms, I disregarded it as "already fixed (probably)". Thank you for telling me the patch name. The mapper number seems to be a factor.

hackbar

Quote from: Bisqwit on July 31, 2020, 08:59:13 AM
These problems were previously reported to me by someone through e-mail. Because I was not able to reproduce the reported symptoms, I disregarded it as "already fixed (probably)". Thank you for telling me the patch name. The mapper number seems to be a factor.

That was me. It looks like the select bug is fixed now, but I've uploaded a video of the others. Lemme know if it's not not clear or if you need more.

https://youtu.be/5jMmlTFtEoE

storall

After some play, a thought. Have a way to record and show 100% completion progress? Got all endings, easter eggs, day 99, misc.

To reward player, animate title logo when pressing START with the blood effect from FDS version. Or change the title screen itself.


Quote from: https://en.wikipedia.org/wiki/Castlevania_II:_Simon%27s_Quest
A former Castlevania producer, Koji Igarashi, revealed that all the NPCs in the Japanese version were deliberate liars.

I can understand why NA was endearingly cryptic from this quote. But the Japan script didn't seem to mess with you as much.


And knockback on stairs. Wonder why they did it for CV2 only?

Bisqwit

#1125
Quote from: storall on August 02, 2020, 08:19:24 AMAfter some play, a thought. Have a way to record and show 100% completion progress? Got all endings, easter eggs, day 99, misc.

To reward player, animate title logo when pressing START with the blood effect from FDS version. Or change the title screen itself.

I have been thinking of an achievements system actually. I have several in mind. But I like this idea.
Spoiler
$ grep Achievement dialog_ext/dialog_ext.s
        ; TODO: Activate Achievement: BY_WILLPOWER_ALONE
         ; TODO: Activate Achievement: UNSUCCESSFUL_DATE
         ; TODO: Activate Achievement: UNWELCOME_VISITOR
        ; TODO: Activate Achievement: CUISINE_EXPRESS
        ; TODO: Activate Achievement: NO_STONE_UNTURNED
[close]

Quote from: storall on August 02, 2020, 08:19:24 AMAnd knockback on stairs. Wonder why they did it for CV2 only?

I don't think it was particularly designed that way. They (probably) just rewrote the handling code from scratch and didn't particularly pay attention to that detail. (I haven't done code comparisons between these two games, so I am just guessing.) In CV3 they either reused code from CV1, or they did pay attention to that detail because of playtesting/player feedback.
This is something that I might considering making a vending machine option, although I have not researched how to do that.

storall

#1126
When you like playing a game, achievements are plus+. I can likely decipher 3 of those but the other 2 hmm.. interesting.


It's the challenge ones that would drive me up-the-wall.
Spoiler

Don't hurry, rush! - Finish the game in 3 (?) days (no password)
You only get one. - Finish without dying
No curse here. - Finish without getting hit
Trust the family. - Don't upgrade your whip
It's my money! - Spend no more than x hearts
Heart farmer. - Gain 750 (?) hearts in 1 night
[close]

August 02, 2020, 11:36:39 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

Have a decent start on the stairs knockback.


SimonDamageKnockBack

lda ObjectCurrentActionType
cmp #$09
beq +++
cmp #$0a
beq +++

$D36F  AD 20 04:    lda ObjectFacingLeft
$D372  F0 06:       beq + ; $D37A
$D374  A0 FF:       ldy #$FF
$D376  A9 00:       lda #$00
$D378  F0 04:       beq ++ ; $D37E

+ $D37A  A0 01:       ldy #$01
$D37C  A9 00:       lda #$00
++ $D37E  20 6A D3:    jsr ItemMenuPauseScreen_CursorMovement_Horizontal_ByY
$D381  A9 FD:       lda #$FD
$D383  A0 80:       ldy #$80
$D385  20 62 D3:    jsr Object0_SetYVelocity16bit_from_AY
$D388  A9 1F:       lda #$1F
$D38A  20 87 DD:    jsr SetObjectIndexToAutomaticSpriteDataTable_to_A_for_Simon
$D38D  20 CA DC:    jsr DeleteSimonWhipObject
$D390  A9 06:       lda #$06
$D392  8D D8 03:    sta ObjectCurrentActionType
+++ $D395  A9 20:       lda #$20
$D397  8D B0 04:    sta ObjectAIvar6
$D39A  20 D3 D3:    jsr FindIfThereIsAFloatingPlatform
$D39D  90 07:       bcc + ; $D3A6
$D39F  A9 00:       lda #$00
$D3A1  9D 7A 04:    sta Ending_PrimaryActionIndex,x
$D3A4  85 41:       sta Unknown41
+ $D3A6  A9 5F:       lda #$5F
$D3A8  20 18 C1:    jsr AnyBankPlayTracks
$D3AB  60:          rts


If we're on stairs (action 09-0a), skips the x-y velocity changes + hurt frames. Simon will still blink like normal and fall down when dead, with ability to whip while alive.

Although I'm short 3+4 bytes for inline after code squashing (*).
(*) = I optimized it a bit but still short afterwards.


And curiously just noticed you can hold whip button on stairs and repeatedly fire away (vanilla also). But not on normal ground.

Bisqwit

Quote from: storall on August 02, 2020, 11:22:29 AMSimonDamageKnockBack

lda ObjectCurrentActionType
cmp #$09
beq +++
cmp #$0a
beq +++

<...>


Although I'm short 3+4 bytes for inline after code squashing.

Yeah, I was able to save 6 bytes on the function by just rewriting it a little. Your hack requires 3+4+4 = 11 bytes. But that's irrelevant since I can just relocate code on whim.         D36F                ldy #1
        D371                lda ObjectFacingLeft
        D373                beq ++
        D375                lda #0
        D377                ldy #$FF
        D379                nop,nop,nop,nop,nop
...
        $D3A8  20 18 C1:    jsr AnyBankPlayTracks ; could save 1 byte by tailcalling
        $D3AB  60:          rts

storall

#1128
Forgot to add action 0A (+4 bytes), but I still wonder if there's a shorter way..


Also encountered 2 more bugs which I suspect are original but didn't have rewind ready and will have to reproduce again to confirm.
- After using stake on orb, I had no items but cursor somehow got stuck on a blank slot
- Got hit trying to use whirlwind and became stuck in next screen.

August 03, 2020, 12:53:33 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

cv2eng-2_11_1-ntsc-stm1mwfmFrup4-ips
cv2eng-2_11_1-ntsc-stm10mwfmFrup4-ips

Went to Berkeley Mansion. Walked into some sideways spikes and died. Simon's body disappeared.

Original: Simon dies but floats up a bit. Dead body is visible.

August 03, 2020, 01:00:29 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

cv2eng-2_11_1-ntsc-stm1mwfmFrup4-ips
cv2eng-2_11_1-ntsc-stm10mwfmFrup4-ips

New game.
Buy white crystal.
Buy oak stake.
Select oak stake.
Use oak stake.
Open menu. Press left. Visible sticky cursor on dead slot.


Original: Does not happen. No visible cursor anywhere.

August 03, 2020, 09:09:27 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

Deborah Cliff tornado glitch is well known on youtube. Actually has easy solution.


SimonDamageKnockBack:
lda ObjectCurrentActionType
cmp #$0b
beq +

..

+ rts


Which looking at the action list, strairs knockback could be a simple cmp #$08, bcc

$81DA  47 8A:       .word (SimonExecuteCurrentAction8_BeginStairsClimb) ;8A47 (CA47) ()
$81DC  D9 8A:       .word (SimonExecuteCurrentAction9_StairsStand) ;8AD9 (CAD9) ()
$81DE  52 8A:       .word (SimonExecuteCurrentAction10_StairsClimb_Automatic) ;8A52 (CA52) ()
$81E0  26 82:       .word (SimonExecuteCurrentAction11_Nothing ; $8226 -> rts) ;8226 (C226) ()


Although I did play with the stairs knockback some more and it has new 1 glitch: Simon can change direction while on the stairs. It looks really odd when he reverses left / right.

August 03, 2020, 09:32:54 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

New idea for stairs knockback. It flips the direction right before running knockback routine.


$8844  A0 00:       ldy #$00
$8846  BD 48 03:    lda ObjectScreenXCoord,x
$8849  CD 48 03:    cmp ObjectScreenXCoord
$884C  90 01:       bcc + ; $884F
$884E  C8:          iny
+ $884F  8C 20 04:    sty ObjectFacingLeft
$8852  4C 6F D3:    jmp SimonDamageKnockBack


Bank 01 has lots of space. So we can do this check and avoid the new problem.


lda ObjectCurrentActionType
cmp #$08
bcs rts

($8844 code)


And for Deborah Cliff, I guess fix is still a bit short after optimization?
Which I just found out that nuking stairs knockback with 08-0b also fixes Deborah Cliff.


And while I'm at it
- I wonder why everyone complains about the Dracula sprite. There's so many artists around here, someone should have the talent to redraw the Dark Lord to his manual book or series equivalent.

- The palette fader is a technical achievement. One of the best highlights of translation no matter how many times I've seen it. I'd add a menu cursor hack to advance time by 1 hour just to keep watching the effect everywhere.

- Is ferryman not stopping at other shore genuinely a bug? The danger of jumping off and missing is entertaining; I've mistimed it for several deaths. That'd make a good endgame statistic: how many water deaths.

- I'm going to add my own hack to flip the ferryman sprite when he reverses direction. It's goofy watching him shuttle back-and-forth always facing the wrong way, and I'd consider that a graphics bug. Even Simon looks the correct way after a screen switch.
  edit: Which took more code than I thought. Had to flip 2 sprites + swap their x-coords. But I like the result. And if you stand on the edge of the boat, he'll still drop you off on the shore and leave away.


And I triggered a new bug.? When on the moving boat, I got hit by a fishman. I somehow talked to either the fish or the ferryman and he mentioned about not being able to fish anymore..?

August 03, 2020, 11:58:44 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

Have a possible solution to the exit room yscroll problem. Since it did happen to me while playing.


++ lda !CurrentYScrollingPositionPixels_Mod240 ; round yscroll
bpl +
inc !Current_WhatUnknown57

+ $845E  A5 30:       lda CurrentLevelMapType
$8460  C9 05:       cmp #$05
$8462  D0 12:       bne + ; $8476


This code triggers when we exit the room.
- $56 = CurrentYScrollingPositionPixels_Mod240Frac.
- $57 = CurrentYScrollingPositionPixels_Mod240.

Usually the values are nice whole #s when we exit the room (0.0, 1.0, 2.0). If we're near the top, we round down. When we're at the bottom, we round up. For the lake scroll glitch, I get $00,$CE when exiting. Round up to $01,$00 and we're no longer stuck ($02,$00 = next room).

Should I be worried though about another problematic map?

Bisqwit

#1129
Quote from: storall on August 03, 2020, 08:38:44 AMAlso encountered 2 more bugs which I suspect are original but didn't have rewind ready and will have to reproduce again to confirm.
- After using stake on orb, I had no items but cursor somehow got stuck on a blank slot

Just press either left or right, it will move to the first item on the same row where the stake was, or up to move to the bodyparts row. This is slightly less glitchy than in the original game. I will have to do further tests to see if I accidentally introduced new misbehavior when I fixed the negative cursor coordinate bug.
EDIT: Confirmed with different explanation: If you press left (no other direction) after using a stake and you have no items on that row, cursor lands on empty column. Fixed in next version.

Quote from: storall on August 03, 2020, 08:38:44 AM- Got hit trying to use whirlwind and became stuck in next screen.

A bug that exists in the original game. There are many variants of it.
Won't fix. It has potential to be used in speedruns, no potential to be used in casual runs, and I don't like touching stuff like that. Same goes for the three-block jump: If you know, you know.
The most I would do is to pretend to fix it, in the IOCCC spirit: Apply the smallest possible plaster on it.

Quote from: storall on August 03, 2020, 08:38:44 AMWent to Berkeley Mansion. Walked into some sideways spikes and died. Simon's body disappeared.

Original: Simon dies but floats up a bit. Dead body is visible.

Got a video clip?
EDIT: Confirmed. This is because Simon gets damaged every frame but does not die immediately. The first damage activates flashing, and when Simon dies, flashing stops and he remains in invisible state. Planned fix: Reset the flashing counter when Simon dies. My patch already has a special condition like that, ensuring that Simon is not invisible when Dracula dies, but needs another so-it-seems. A fix has been written for the next release.

Quote from: storall on August 03, 2020, 08:38:44 AMNew game.
Buy white crystal.
Buy oak stake.
Select oak stake.
Use oak stake.
Open menu. Press left. Visible sticky cursor on dead slot.

See first quote.

Quote from: storall on August 03, 2020, 08:38:44 AMIs ferryman not stopping at other shore genuinely a bug? The danger of jumping off and missing is entertaining; I've mistimed it for several deaths. That'd make a good endgame statistic: how many water deaths.

Arguable whether it's a bug or a design feature. I'm going to compromise and say it's a design compromise.

Quote from: storall on August 03, 2020, 08:38:44 AMAnd I triggered a new bug.? When on the moving boat, I got hit by a fishman. I somehow talked to either the fish or the ferryman and he mentioned about not being able to fish anymore..?

Not a bug. You can talk to ferryman en route as long as you are not standing.

August 04, 2020, 06:59:52 AM - (Auto Merged - Double Posts are not allowed before 7 days.)

Quote from: hackbar on July 30, 2020, 04:39:57 PM1. On a cold boot, from the start menu, go to to the load game screen and hit select. The game crashes, showing a half-screen of credits for the sram system.

2. On a cold boot, the load screen has no cursor visable.

I was not able to confirm either of these on FCEUX 2.2.2, Nestopia UE 1.50, nor Nintendulator 0.985. I used same patch settings as you did. I suspect it is a problem with your cartridge.

Quote from: hackbar on July 30, 2020, 04:39:57 PM3. After loading a game, some of the tiles flicker.

I suspect something in this report itself was lost in translation.

Quote from: hackbar on July 30, 2020, 04:39:57 PM4. On the save screen, the music stutters when saving.

EWONTFIX.


Quote from: hackbar on July 30, 2020, 04:39:57 PM5. When opening the loading screen, there's often some garbage, and the screen is sometimes offset vertically for a frame or two.

EWONTFIX.
However, when you exit the save screen, sometimes the screen is not properly restored. I have not yet discovered what causes it.

storall

Quote
Not a bug. You can talk to ferryman en route as long as you are not standing.

Got it to work if I'm in knockback mode or jumping. Neat because I didn't expect him to multiple conversations.

Although now I wonder about the townspeople. There should be more of them complaining about the last 7 years of pain, and being hostile to Simon like the manual suggests (for allegedly creating the curse).


And the endings.

Spoiler

Quote
WILL BRING US FULL- BLOWN FLOWERS WITH HAPPINESS IN NEXT SPRING

The part I like about that is it seems poetic (and sets you up for the gut-puncher). The rewrite is good too.


I'm going to rearrange the sequences for my own build though.


And a question. Did Dracula gain enough strength in 7 years to revive anyway in the fast ending??
[close]

Bisqwit

Version 2.11.2 has been released.
https://bisqwit.iki.fi/cv2fin/

Bugfixes:
— Exiting the SRAM / map screens will no longer immediately invoke dialog, unless you keep holding B, and the screen won't be misaligned even if you do
— Fix invisibility when death by spikes
— Using a stake with no remaining items no longer lets you land cursor on nonexisting item
— Password screen no longer appears to be broken, although I did not explicitly address this problem

Other:
— Internal changes to use some common code in SRAM & map, reducing amount of copypaste
— Changed internal representation of clue names

Bug observed:
— On laborsome mappers (mainly MMC1) screen may shake when rendering dialog box

ifightdragons

Will you be adding new versions of the patch on RHDN going forward, or just keep linking in the forum?

Thanks for the updates =)

Special

Why does it need to be on RHDN? It's a hassle having to deal with this sites "Queue" system and by the time it gets approved there's going to be another update anyways, his site's "vending machine" is awesome anyways :P

Bisqwit

#1134
Quote from: ifightdragons on August 04, 2020, 11:36:53 AMWill you be adding new versions of the patch on RHDN going forward, or just keep linking in the forum?
Anyone can submit updates to the RHDN entry.
It's so much hassle for me to update it and it cannot be automated, it needs moderator approval, and it can only have one particular version (as opposed to the entire vending machine selection), so I only do it when I am feeling particularly inspired to do it.

POLL TIME

Should I add the stay-on-stairs feature, where instead of getting knocked back from stairs when you take damage, you are stunned for about ¼ seconds (and invulnerable for 1 seconds as per normal)? I managed to get it working without problems as far as I can see.
My tools for NES recording are very rusty (especially compared to my TASVideos days) so I was only able to make this low-quality soundless video right now. (Tried to make a GIF, but I was unable to make a lossless video for the input for my tools.) https://bisqwit.iki.fi/cv2fin/dev/stairs-undrop-test.mp4

Masked Dedede

#1135
Hey, I found these graphical bugs when exiting the Save menu.
Patch version: cv2eng-2_11_2-ntsc-stm10mwfFrup4.ips.





Also, the stay-on-stairs feature seems like a pretty good addition to the hack. :)

Oh. :|
Bottom Text

Bisqwit

#1136
Quote from: Masked Dedede on August 04, 2020, 01:36:15 PMHey, I found these graphical bugs when exiting the Save menu.
Quote from: Bisqwit on August 04, 2020, 05:17:06 AMHowever, when you exit the save screen, sometimes the screen is not properly restored. I have not yet discovered what causes it.

UPDATE:

The problem seems to be traced to this code.

First, here is the data format.

; PPU send queue format:
;    $00
;            End of send queue. Queue is cleared.
;    $01 BEint16bitWord data... $FF
;            $2000 is configured for 1-byte increments.
;            Word is put in $2006.
;            Data is put to $2007.
;            To include literal $FF in data, put $FF and any byte >= $05.
;  ( $02 BEint16bitWord data... $FF )
;            Never actually used in Simon's Quest
;            $2000 is configured for 32-byte increments.
;            Word is put in $2006.
;            Data is put to $2007.
;            To include literal $FF in data, put $FF and any byte >= $05.
;    $03 BEint16bitWord Count Byte
;            $2000 is configured for 1-byte increments.
;            Word is put in $2006.
;            Byte is put in $2007, Count times (>= 1, 0 is interpreted as 256).
;    $04 BEint16bitWord BitNo Bits
;            $2000 is configured for 1-byte increments.
;            Temp = PPUmemory[Word]
;            Temp &= BitMask
;            Temp |= Bits
;            PPUmemory[Word] = Temp
;            Where BitMask = from table $FC,$F3,$CF,$3F,$F0,$0F according to BitNo.
;    >=$05 data... $FF
;            Data is put to $2007, no address prefix.


Secondly, here is the original code. The bug does not seem to occur with it. I have commented out instructions that I have verified that they do not influence the result.;
; ORIGINAL CODE:
;NMI_ProcessPPUsendQueue
ldy #0
;sty $08
m4:
;lda $08
;cmp #$3F
;bne :+
;sta $2006
;lda #0
;sta $2006
;sta $2006
;sta $2006
;:
m3: ldx PPUsendQueue,y
beq was_0
; NonZero
;lda PPUdesiredRegister2000
;and #$18
;ora _data_1CB65_indexed-1,x
;sta $2000
iny
cpx #$04
beq was_4
;lda $2002
lda PPUsendQueue,y
;sta $08
sta $2006
iny
lda PPUsendQueue,y
sta $2006
iny
cpx #$03
beq was_3
bne was_12
was_0:
;lda #0
stx PPUsendQueue
stx PPUsendQueueHead
;lda PPUdesiredRegister2000
;sta $2000
rts

m2: lda #$FF
m1: sta $2007
was_12: lda PPUsendQueue,y
iny
cmp #$FF
bne m1
lda PPUsendQueue,y
cmp #$5
bcs m2 ; Was $FF >=$05 -> send literal $FF
bcc m4

was_3:
ldx PPUsendQueue,y
iny
lda PPUsendQueue,y
iny
: sta $2007
dex
bne :-
beq m4

was_4:
;lda $2002
lda PPUsendQueue+0,y
;sta $08
sta $2006
lda PPUsendQueue+1,y
sta $2006
lda $2007
lda $2007
ldx PPUsendQueue+2,y
and $FFFF,x ; Address of _data_1CC1E_indexed / NMI_ProcessPPUsendQueue_data will be patched by insert.php
ora PPUsendQueue+3,y
sta $00
;lda $2002
lda PPUsendQueue+0,y
sta $2006
iny
lda PPUsendQueue,y
sta $2006
lda $00
sta $2007
iny
iny
iny
jmp m3


And here's my optimized code, which has higher throughput (more PPU data transferred per CPU cycle), but sometimes causes that bug to manifest.begin:
ldy #0
beq loop
;;;barrier

was_3:
; Carry is set.
; Unrolling by factor 8 (saves 35 cycles per 8 bytes; 70 cycles per 16 bytes):
; Actual cost: 4.625 cycles per byte + setup.
; Setup cost:  ...

lda PPUsendQueue,y ; Count
adc #7-1 ; Because C is already set
ror ; If adc overflowed, ror brings the overflowed bit back into the MSB
lsr
lsr
tax ; Loop count: (count+7)/8

; Offset = (8 - count)%8 * 3
lda #8
sec
sbc PPUsendQueue,y ; Count
and #7
sta $09
asl     ; Clears C, because A is in 0..7 range
adc $09 ; Does not set C, because inputs are (0..7) + (0..15), never >= 128
adc #<was_3_loop ; For this one we don't care whether it sets C or not
sta $08

lda #>was_3_loop
sta $09

iny
lda PPUsendQueue,y ; Byte to send
iny
jmp ($08)
;;;barrier

  was_3_loop:
sta $2007 ;0
sta $2007 ;7
sta $2007 ;6
sta $2007 ;5
sta $2007 ;4
sta $2007 ;3
sta $2007 ;2
sta $2007 ;1
dex
bne was_3_loop
;beq loop
  TableWrapCheck was_3_loop, (3*8), "8 * sta $2007 array should not wrap pages"

loop:
ldx PPUsendQueue,y
;bne loop_with_x
beq was_0
  was_1_FF_nonzero:
cpx #5
bcs was_1_store
;;;bcc loop_with_x
  loop_with_x:
; Load address
iny
lda PPUsendQueue,y
sta $2006
iny
lda PPUsendQueue,y
sta $2006
iny
; Determine the case
cpx #3
bcc was_12
;;;bcs was_34
;was_34:
beq was_3
;;;bne was_4
;was_4:
; Carry is set.
;ldx PPUsendQueue-2,y
;stx $2006
;ldx PPUsendQueue-1,y
;stx $2006
lda $2007
lda $2007
ldx PPUsendQueue-2,y
stx $2006
ldx PPUsendQueue-1,y
stx $2006
ldx PPUsendQueue,y
and $FFFF,x ; Address of _data_1CC1E_indexed will be patched by insert.php
iny
ora PPUsendQueue,y
sta $2007
iny
bcs loop
;;;barrier

  was_1_store:
sta $2007
;;;jmp was_1
was_12:
; cpx #2
; bcc was_1
; ;;;bcs was_2
;  ;was_2:
; ; Configure PPU for 32-byte increments
; lda PPUdesiredRegister2000
; ora #4
; sta $2000
; Apparently, mode 2 is never used in Simon's Quest.
; Then proceed to case 1
was_1:
; Unroll by factor 3 (saves 2 cycles per 3 bytes; 10 cycles per 16 bytes):
; Actual cost: 14.333 cycles per byte + setup.
  .if 1
; Part 1
lda PPUsendQueue,y
iny
cmp #$FF
beq was_1_FF
sta $2007
  .endif
  .if 1
; Part 2
lda PPUsendQueue,y
iny
cmp #$FF
beq was_1_FF
sta $2007
  .endif
  .if 1
; Part 3
lda PPUsendQueue,y
iny
cmp #$FF
beq was_1_FF
sta $2007
  .endif
  .if 1
; Part 4
lda PPUsendQueue,y
iny
cmp #$FF
beq was_1_FF
sta $2007
  .endif
  .if 0
; Part 5
lda PPUsendQueue,y
iny
cmp #$FF
beq was_1_FF
sta $2007
  .endif
; Part 6
lda PPUsendQueue,y
iny
cmp #$FF
bne was_1_store
;;;beq was_1_FF
  was_1_FF:
ldx PPUsendQueue,y
bne was_1_FF_nonzero
;beq was_1_and_0
  ;was_1_and_0: ; arrived here with Z=1, X=0
;beq was_0

was_0: ; Arrived here with X=0
stx PPUsendQueue
stx PPUsendQueueHead
;.byte "C0D" ; jmp $C0D2 - NMI_UpdatePPUregisters_Simplify + 20
lda PPUdesiredRegister2000
sta $2000
rts
;;;barrier


Can anyone see where my optimized code goes wrong?

Spoiler
It turned out to be those two last lines before RTS.
[close]

August 04, 2020, 04:58:58 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

New version released: 2.11.3

Bugfixes:

— No longer trash on screen after SRAM/map exit

Features:

— Stay on stairs when taking damage (hidden feature! Until I get enough results on the poll, it can be enabled only by using developer mode to edit the vending machine and change StairsFeatures value from 1 to 2)

Internal changes:

— Size optimizations through trampoline — saving 1 byte here, 4 bytes there. To make more room for prologue music. Here's the Trampoline source code by the way.
.code
Mapper1reg_reg3    = $1C
SwitchBank_NewPage = $C183

; Switch to given bank, JSR given address, and then return back to the previous bank.
; Registers fed to the routine are A,X,Y.
; At return, registers are:
;                        A = clobbered
;                        X,Y = preserved
; Clobbers memory variables $00-$03.
; Use as:
;    jsr Trampoline
;    .word <address of routine to call>
;    .byte <bank number to use>
;   continues here:
;
; If you need a faster version, you can do this instead:
;    jsr Visit_Trampoline
;    continues here:
;    ...
;    jsr Trampoline_Jmp
;    .word, .byte
;    <barrier>
;

Trampoline:
clc ; 1 byte,  2/0 cycles
.byte $B0 ; bcs, harmless ; 1 byte,  2/0 cycles
; skips the "sec"
Trampoline_Jmp:
sec ; 1 byte,  0/2 cycles
sta $02  ; Save A to $02        ; 2 bytes, 3/3 cycles
sty $03  ; Save Y to $03        ; 2 bytes, 3/3 cycles

; Retrieve the return address from stack (save to $00) and modify it by +3
; Note: Manipulating stack directly with "$101,x" is not faster,
; and is more than 5 bytes longer. Tested it.
; Also, adding the @skip feature to a direct stack manipulation
; version would be quite difficult.

pla ; 1 byte,  4/4 cycles
sta $00 ; 2 bytes, 3/3 cycles
; Total:                        10 bytes, 17/15 cycles

pla ; 1 byte,  4/4 cycles
sta $01 ; 2 bytes, 3/3 cycles

bcs @skip_returning ; 2 bytes, 2/3 cycles

lda $00 ; 2 bytes, 3/0 cycles
adc #3 ; 2 bytes, 2/0 cycles
tay ; 1 byte,  2/0 cycles

lda $01 ; 2 bytes, 3/0 cycles
adc #0 ; 2 bytes, 2/0 cycles
pha ; 1 byte,  3/0 cycles

tya ; 1 byte,  2/0 cycles
pha ; 1 byte,  3/0 cycles
@skip_returning:
; Total:                        17 bytes, 29/10 cycles

@continue:
; Load three bytes (#3, #2, #1). Push #3 and #2, keep #1 in A.
lda Mapper1reg_reg3 ; Save old Mapper1_reg3
.if 1
; OPTION 1: FASTER BUT LONGER CODE
ldy #3          ; 2 bytes, 2 cycles
pha             ; 1 byte,  3 cycles
lda ($00),y    ; 2 bytes, 5 cycles (+1 if wrap)
pha            ; 1 byte,  3 cycles
  dey           ; 1 byte,  2 cycles
  lda ($00),y   ; 2 bytes, 5 cycles (+1 if wrap)
  pha           ; 1 byte,  3 cycles
   dey          ; 1 byte,  2 cycles
   lda ($00),y  ; 2 bytes, 5 cycles (+1 if wrap)
   ; Total:      13 bytes, 30 cycles
.else
; OPTION 2: SHORTER BUT SLOWER CODE
ldy #3          ; 2 bytes, 2 cycles
@l: pha             ; 1 byte,  3 * 3 = 9 cycles

   lda ($00),y  ; 2 bytes, 3 * 5 = 15 cycles (+3 if wrap)
   dey          ; 1 byte,  3 * 2 = 6 cycles

   bne @l       ; 2 bytes,  3*3 - 1 = 8 cycles
   ; Total:       8 bytes, 40 cycles
.endif

   sta $00
  pla
  sta $01
pla
jsr SwitchBank_NewPage

; Load A and Y
lda $02
ldy $03
jsr $C5D6 ; jmp ($00) is already found in Castlevania 2 ROM...
; Predeceded by ldy $02 and ldx $03, so those are not useful

; The JSR $C5D6 pushes the return address of the following
; instruction on stack.
pla
jmp SwitchBank_NewPage

storall

I'd vote yes for stairs, but would it make it easier for speedrunners?

That is some amazing code sorcery!

Bisqwit

Quote from: storall on August 04, 2020, 05:30:38 PMI'd vote yes for stairs, but would it make it easier for speedrunners?
Actually it only makes it easier for casual players. This change makes it more difficult for speedrunners, who actually depend on the damage knockback taking them to the top of the stairs.

storall

Quote
Actually it only makes it easier for casual players. This change makes it more difficult for speedrunners, who actually depend on the damage knockback taking them to the top of the stairs.

Cute trick!


Question about trampoline: would BRK be more efficient?