News: 11 March 2016 - Forum Rules

Author Topic: The Minucce Yard  (Read 13612 times)

minucce

  • Jr. Member
  • **
  • Posts: 96
    • View Profile
Re: The Minucce Yard
« Reply #100 on: November 23, 2021, 06:51:55 pm »
I think it looks correct! :)


I'll explain the revised code:

Code: [Select]
save_hearts:
lda.w $066F // Load Hearts address
and.b #$0F // Mask higher nibble (only consider current health, not max amount of hearts)
cmp.b #$02 // Compare result to #$02 (3 hearts)
bcs not_three // Branch if equal or greater than 3 hearts

lda.w $066F // Load Hearts address
and.b #$F0 // Mask lower nibble (to avoid the case of heart value $01 loading as $03) (keep maximum life)
ora.b #$02 // Bitwise OR to #$02 (Org. ORA #$02) (minimum 3 life)
sta.w $066F // Store in address $066F (Hearts)

not_three:
rts


1) dead code removal
(I think you see this so I'll explicitly explain it anyways)

Code: [Select]
lda.w $66f
and.b #$ff
ora.b #$00
sta.w $66f

ex.
- A = $80  ($66f)  (Z,N flags can change in Processor!)
- $80 & $FF = $80  (value of A does not change but it will change the (P)rocessor flags!)
- $80 | $00 = $80  (value of A does not change but it also changes P flags!)
- $66f = $80  (no memory change, no flags change either)

In short, (poof!) it. Which leaves rts.


2) Your comments are excellent, easy for any novice to read and follow. Or for yourself in the future. ;)


3) Here's an (unnecessary) flag trick. Saves no space in this example but for learning.

Code: [Select]
lda $66f
and #$0f
cmp #$02

lda $66f
bcs .exit

and #$f0
ora #$02
sta $66f

.exit:
rts

- cmp.b #$02 sets (c)arry flag
- lda.w does not affect cf (value remains same)
- allows opcode re-ordering


4) (Optional) Bit trick

Code: [Select]
lda $66f
and #$0f
lsr
bne .exit

lda $66f
and #$f0
ora #$02
sta $66f

.exit:
rts

I'll let you decipher this one. Maybe it's buggy but I want to know if you can solve the intent of (lsr).

If you don't see it, write down some examples and trace the values.
(and #$0f limits us to 16 possible starting incoming)


5) ?? Unknown optimization trick I don't see ??


EDIT:
If space was really desperate, you could do this:

Code: [Select]
ldx $66f
txa

and #$0f
lsr
bne .exit

txa
and #$f0
ora #$02
sta $66f

.exit:
rts

but it hugely depends on x or y not being used atm.
« Last Edit: November 23, 2021, 07:08:47 pm by minucce »

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1719
    • View Profile
Re: The Minucce Yard
« Reply #101 on: November 24, 2021, 11:46:42 am »
Oh interesting!
I did think that maybe one of the repeated routines could be saved up, but it didn't occur me that simply not storing back the value into $066F wasn't really needed. That helped a lot!

Also, neat thing about using the LSR there! If I'm not wrong, having a branch check directly after an LDA or anything like that, checks directly against $01 (BEQ directly checks against $01, while BNE branches if the result is NOT $01, right?).

From what I understand out of it, the LSR moves the bits of the lower nibble to the right, and with the BNE there, it checks if you have any value $01 or above after the shift right in order to branch to the RTS instead, so this effectively takes care of anything above $02. For the BNE to not branch, the result should be $00 after the LSR.
$01 should only occur if you have $03 or $02 as values, but in the case of $01 and $00, you get $00 in the check, so it doesn't branch and continues to the ORA #$02. Is this correct?


If so, then that's a nice trick to save some extra space, in the dire need of a couple of bytes. Having the knowledge of the Branches always checking against $01 as well by default without doing a CMP can also save a lot of bytes when optimizing code.

On a side note, I stumbled upon something rather odd.
If you start your save file and then move one screen above or in any direction, and save in that other screen, the cursor in the File Select defaults to the 3rd save instead. It seems like after saving, RAM address $16 is incremented.

Funny thing is it only seems to occur when you move from the starting screen, if you save at the starting screen itself, it defaults to the 1st save slot when returning to the File Select screen.
It's a weird little thing, I took the liberty of debugging it and it seems like this is the code that's doing that:

Code: [Select]
02:A2F9: 20 5D A3  JSR $A35D
>02:A2FC: E6 16     INC $16 = #$00
 02:A2FE: A5 16     LDA $16 = #$00

Not sure what this might be doing precisely, but it's such a weird thing to stumble upon haha.

Thanks for all the help to create the Save Hearts code, Minucce. It certainly opened up the view of some opcodes I didn't fully understand before, and this would help to debug better in the future, and to understand better some routines and opcodes I could use as well!

minucce

  • Jr. Member
  • **
  • Posts: 96
    • View Profile
Re: The Minucce Yard
« Reply #102 on: November 24, 2021, 02:17:30 pm »
Long explanation of LDA + BNE - BEQ

EDIT: Keeps eating my code.

Quote
Code: [Select]
LDA #$00 ==> sets Z flag (zero)
BNE no_this_wont_jump
BEQ yes_jump_right_now


Code: [Select]
LDA #$01-FF ==> clears Z flag  (not zero)
BEQ no_this_wont_jump
BNE yes_jump_right_now


Code: [Select]
LDA #80-FF ==> sets N flag  (negative)
BPL no_this_wont_jump
BMI yes_jump_right_now


Code: [Select]
LDA #00-7F ==> clears N flag  (not negative, positive)
BMI no_this_wont_jump
BPL yes_jump_right_now


Code: [Select]
SEC ==> sets C flag  (carry)
LDA #$00-FF ==> no C flag change
BCC no_this_wont_jump_ever__impossible__dead_c0de
BCS yes_jump_right_now


Code: [Select]
CLC ==> clears C flag  (no carry)
LDA #$00-FF ==> no C flag change
BCS no_this_wont_jump_ever__impossible__dead_c0de
BCC yes_jump_right_now


Since others are reading, re-verification of your LSR analysis.

Bit long so bracket tagged.

Quote
Code: [Select]
LDA #$00
LSR  ==> $00 / 2 = $00
BNE not_happening
BEQ yes_we_are_zero


Code: [Select]
LDA #$01
LSR  ==> $01 / 2 = $00
BNE not_happening
BEQ yes_we_are_zero


Code: [Select]
LDA #$02
LSR  ==> $02 / 2 = $01
BEQ not_happening
BNE yes_we_are_non_zero


Code: [Select]
LDA #$03
LSR  ==> $03 / 2 = $01
BEQ not_happening
BNE yes_we_are_non_zero


Code: [Select]
LDA #$04
LSR  ==> $04 / 2 = $02
BEQ not_happening
BNE yes_we_are_non_zero



Right! Only for 00-01 we fall-through to the ORA #$02.

Nothing more to add. :)

----

Quote
If you start your save file and then move one screen above or in any direction, and save in that other screen, the cursor in the File Select defaults to the 3rd save instead. It seems like after saving, RAM address $16 is incremented.

Funny thing is it only seems to occur when you move from the starting screen, if you save at the starting screen itself, it defaults to the 1st save slot when returning to the File Select screen.
It's a weird little thing, I took the liberty of debugging it and it seems like this is the code that's doing that:

Mmm. Have to check my notes. $16 could be a "submode selecter" -- aka what menu phase we're in. It can only get so much done in 1 VBlank so it split itself into mulitple parts.

----

Quote
Thanks for all the help to create the Save Hearts code, Minucce. It certainly opened up the view of some opcodes I didn't fully understand before, and this would help to debug better in the future, and to understand better some routines and opcodes I could use as well!

Curiously other hackers have learned a lot from reading this exercise also. Bonus!

November 24, 2021, 02:58:29 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
Keep tapping $16 writes. Final one says:

Code: [Select]
02:A573: A5 54     LDA $54 = #$02
 02:A575: C9 03     CMP #$03
 02:A577: 90 02     BCC $A57B
 02:A579: A9 00     LDA #$00

 02:A57B: 85 16     STA $16 = #$02
>02:A57D: A9 00     LDA #$00


which turns out to be my code (.. uh-oh!)


Code: [Select]
%org($a573, $02)

main_menu_init_done:
lda $54
cmp #$03
bcc .write

lda #$00 ; default save slot 0


.write:
sta $16


$16 is used as our menu cursor (save 1-3, copy, erase).
(watch hex editor as cursor moves around)


Put a breakpoint on a573.
- Enter copy menu. Quit. $54 = $03 ==> $16 = $00.
- Enter erase menu. Quit. $54 = $04 ==> $16 = $00.
- Copy from 2 to 1. $54 = $00 ==> $16 = $00.
- Copy from 2 to 3. $54 = $02 ==> $16 = $02.

So $54 appears to be a temp var I found and was using. It remembers the last person we selected.

This seems to break on game over.


You have a few quick options:
- Always default to save 1 (makes sense)
- Fix game over to reset to save 1 (or current player). More asm work.
- ..?
- Debug $54 as turns out it's the guilty one (I first thought $16 too)
« Last Edit: November 24, 2021, 03:01:54 pm by minucce »

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1719
    • View Profile
Re: The Minucce Yard
« Reply #103 on: November 24, 2021, 04:02:23 pm »
Oh whoops :laugh:
I didn't check if that STA $16 was part of your Menu tweaks code haha

From what I'm seeing, making it default to the first save slot is what would make the most sense, right?
In that case, then perhaps simply forcing it to store #$00 at $16 on menu init should be good enough.

Code: [Select]
org $A573 // 0x0A583
main_menu_init_done:
lda.b #$00 // Default save slot 0
sta.b $16

Having that should do it, right?
Although I'm not sure if the LDA $54 and the CMP #$03 is needed there for anything else.
If it's not, then I think leaving it like this should be good enough.

Let me know what do you think.

minucce

  • Jr. Member
  • **
  • Posts: 96
    • View Profile
Re: The Minucce Yard
« Reply #104 on: November 24, 2021, 07:01:15 pm »
Quote
I didn't check if that STA $16 was part of your Menu tweaks code haha

I was wondering where it came from and dug around. Surprised me too.


You're fix looks good to me! Discarding $54 is safe because code flow shows us:

Code: [Select]
LDA $54 = #$02
CMP #$03
BCC path_2

path_1:
LDA #$00

path_2:
STA $16 = #$02

path_2a:
LDA #$00
sta $13

lda #$ff
sta $526

inc $11
rts

We always hit path_2. And path_2a says to clobber A = #$00, which removes any evidence we loaded $54 (00-FF).

A smart person can object that the (C)arry flag is still in play. Rest of code shows it is not used (follow code after rts).

You're done!

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1719
    • View Profile
Re: The Minucce Yard
« Reply #105 on: November 24, 2021, 08:13:39 pm »
Neat! Then it's ready to ship :D
Thanks!

A smart person can object that the (C)arry flag is still in play. Rest of code shows it is not used (follow code after rts).

In that case, would you recommend adding a CLC to clear the carry flag?
And if so, where would it be ideal to put it?

minucce

  • Jr. Member
  • **
  • Posts: 96
    • View Profile
Re: The Minucce Yard
« Reply #106 on: November 24, 2021, 09:06:04 pm »
Quote
In that case, would you recommend adding a CLC to clear the carry flag?

I personally checked it. The Carry flag (c=0 or c=1) gets erased later on so does no damage to us. Original main menu routine itself does not rely on (c)arry flag (0 or 1) so that's a double + to ignore it.


And I had no use for (not)carry beyond that simple cmp. No need to worry this time.
(people like me can preserve it for awhile or use it as (rts exit value) so it is something to be mindful of, but not this case)


EDIT:
If it was important, you'd either have to know the correct value (clc, sec) and set it towards the end (latest possible moment). You don't want some other opcode mucking it up too early (Z80 is very good at doing this to you; xor - or - and are not friendly to carry, unlike the 6502).

Or php / plp.
Or regenerate it from whatever (original old code).


Side-note:
A "ld" on Z80 does not affects Z,C flags. Which requires a harmless "or a" to check if it's zero.
(a | a = a)


Honestly I forgot if it's (not) true for the other chips, but that's why you use a debugger to write up quick opcode tests to see what happens.

Give me a week and likely I'll forget these opcode quirks.
« Last Edit: November 24, 2021, 09:17:28 pm by minucce »

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1719
    • View Profile
Re: The Minucce Yard
« Reply #107 on: November 25, 2021, 11:26:18 am »
Ah I see.
Then indeed the carry flag is only kept but isn't used at all during its routine, and it effectively gets erased without being used.

It's nice to know!

Thanks for all the detailed explanations, and also thanks yet again for all the wonderful help that you've given to some of the projects I had in preparation for years.
Thanks to all that, all of the points I wanted to cover for both Zelda 1 and Zelda 2 are now a reality, and I can happily say both projects are complete and finished!
If nothing arises, I will push the updates to the Romhacking pages in the upcoming days.

The most sincere and honest thanks for your help!