Adventures in DTE Encoding: Episode 5 (Inbou no Wakusei: Shancara)

Started by BlackPaladin, July 28, 2023, 09:22:59 AM

Previous topic - Next topic

BlackPaladin

I'm trying one more time for to ask some help with some NES hacking.  I'm hoping this will be successful... *cross fingers*  All I ask is to please be patient with me.  I'm still green on this level of ROM hacking.

This is regarding a potential project I recently had my eye on... "Inbou no Wakusei: Shancara" for the NES.  Currently, I managed to dump about 132kb of text from the game which could be sent to a potential translator.  But this is regarding DTE.  Yes, I did this before with Last Armageddon.  I want to see if I can implement it here like I did with that game.  Therefore, I did the following...

Following KingMike's document on the subject (Thanks again for writing that document) I set up a breakpoint to find the "sweet spot", which I did.  That was located in 0x103B9-0x103BC (A0 00 B1 66).  In its place, I "broke the ROM" and copied A0 00 B1 66 to 0x1DEFE with a 60 right after.  (I typed 20 EE DE EA where 0x103E9-0x103EC is) and ran the ROM which was successful.  Then, I modified the DTE code I used in Last Armageddon to work with Shancara.  The new code was applied at 0x1DEFE and the DTE table was posted below at 0x1DF50-0x1DFC3

I had planned the DTE code to start at 3A and end at 73.  I had planned so that 3A would display "11", 3B would display "12", etc. (for texting purposes).  When I tested the code in FCEUX, this is what I got...



The "21" in that image was where I typed 3A in that area, so it should display "11" in that space.  So not what I had hoped...

Then, I tested it in Mesen2, and this is what I got...



This was worse than I thought.

I'm trying to figure out what I'm doing wrong, and I'm having a hard time finding out why.  This is the modified code I pasted...



Here it is in hexadecimal form...

AD F0 61 C9 00 D0 1F A0 00 B1 66 C9 3A 10 03 B1
66 60 B1 66 C9 73 10 F7 EE F0 61 B1 66 38 E9 B0
0A AA BD 40 DF 60 CE F0 61 C6 96 A5 66 C9 FF D0
02 C6 67 A0 00 B1 66 38 E9 3A 0A AA BD 42 DF 60

Can someone advise me in what I can do to fix this?  Thank you for your time.

BlackPaladin



I did a little more tinkering with the code, and it looks as if it appears to be half-working... I think.  The first digit displays normally, but the second digit is constantly stuck at displaying the number "1".  This is all well and good when I'm using the hex number 3A to display 11, but when I use the hex number 63 to display "56" in the DTE table (for sample), it only displays "51".  Furthermore, when I use the hex number 66 to display the number "59 in the DTE table (another example), it displays "61".  (It should be displaying "59", but second digit is displaying only the number 1 while the first digit displays one digit ahead.)

Here's the code in hex form.

AD F0 61 C9 00 D0 1F A0 00 B1 66 C9 39 10 03
B1 66 60 B1 66 C9 74 10 F7 EE F0 61 B1 66 38
E9 39 0A AA BD 40 DF 60 CE F0 61 C6 96 A5 66
C9 FF D0 02 C6 67 A0 00 B1 66 38 E9 3A 0A AA
BD 41 DF 60

I could really use some help with fixing this.  Can someone help me out here?  Again, thank you for your time.

BlackPaladin



It looks like I may have landed a victory here.  Some more tweaking and finding out that I forgot to change a couple of bytes to reflect that the DTE code was working with Shancara instead of Last Armageddon.  The above image is what I typed up, and when I tested it out on FCEUX, I get this result...



The "74" you see on the screenshot is supposed to represent the same number I typed in the DTE table for test purposes.  The hex number "73" would display "74" on-screen.  So I consider this a victory.

HOWEVER, I did hit one problem.  I tested my code out on Mesen2, and this is what I got...



Not something I had hoped.

Now, I may need some assistance in getting the ROM to work on Mesen2 as well as FCEUX... assuming this is possible. 

Maybe, I need to pick out a different RAM byte for the code.  I used $61F0, which was another artifact from the LA DTE code.

(For the record, the DTE code was based on the same code used in Last Armageddon, so when this projects gets off the ground, KingMike, FCAndChill, Pennywise, and Pluvius will be credited for it.)

Oh yeah, and here's the latest code in hex format...

AD F0 61 C9 00 D0 1F A0 00 B1 66 C9 39 10 03 B1
66 60 B1 66 C9 74 10 F7 EE F0 61 B1 66 38 E9 3A
0A AA BD 40 DF 60 CE F0 61 C6 66 A5 66 C9 FF D0
02 C6 67 A0 00 B1 66 38 E9 3A 0A AA BD 41 DF 60


BlackPaladin

Quote from: Cyneprepou4uk on July 29, 2023, 03:00:35 AMTry enabling SRAM in header.

I checked the NES header editor in Mesen2, and from what I see, Save RAM (I assume this is SRAM) is enabled at 8kb.


Cyneprepou4uk


BlackPaladin

Quote from: Cyneprepou4uk on July 29, 2023, 06:08:49 AMMy next guess is that 61F0 is not initialized with 00.

Looks like you're right.  I'll have to find another RAM byte to use for the DTE to work.  I did try several others so far.  The first byte in the DTE table appears normally, but the second byte in the DTE table would either repeat constantly or not appear at all.

Now I know what to do.  The search for an available RAM byte continues...

Thank you, Cyneprepou4uk!  :)

BlackPaladin



Unless some unexpected surprise comes up down the road, I may have finally finished with DTE code in Inbou no Wakusei: Shancara for the NES.  Turns out that it DID need a different RAM byte instead of $61F0 (which was used in Last Armageddon).  Instead, I used $184, and the DTE stuff is displaying normally on both FCEUX AND Mesen2!

Here's what the code itself looks like...



If KingMike, Pennywise, FCandChill, and/or Pluvius is reading this, I want to thank you for helping me with the DTE code in the first place.  Furthermore, a really big thank you to Cyneprepou4uk (sorry if I spelled your name wrong) for helping me debug the code after I modified it to work on Shancara!

Cyneprepou4uk

I don't recommend checking N flag for higher/lower comparsion. For example, if you load byte FF and compare it with 74, the result will be FF - 74 = 8B, which is a negative byte (N = 1). So even though FF is higher than 74, your check will fail.

You should check C flag instead, which is set if >= and cleared if <.

KingMike

Note that, as noted in the tutorial, CMP #$00 was an instruction used only for code clarification purposes and is useless to include in finalized code. The Z flag (for Zero/Equals) will already be set accordingly to whether A is zero after an LDA instruction. Though I believe you'll still need to explicitly check when you want to know if X or Y are zero.
"My watch says 30 chickens" Google, 2018

Pennywise

You really should study the NES hardware to get a better grasp on what to do otherwise you'll end up making poor choices that could cause bugs.

https://www.nesdev.org/wiki/Sample_RAM_map

$0186 isn't a good choice simply because that area of RAM is used for other things and not for storing variables.

BlackPaladin

Quote from: Pennywise on July 30, 2023, 05:09:21 PMYou really should study the NES hardware to get a better grasp on what to do otherwise you'll end up making poor choices that could cause bugs.

https://www.nesdev.org/wiki/Sample_RAM_map

$0186 isn't a good choice simply because that area of RAM is used for other things and not for storing variables.


I see... so using $0184 isn't a good idea to use for the DTE code...

From what link you sent here, it looks like I should use another RAM address that's meant for storing variables...

After reading that article, does this mean what's recommended is using a RAM address between $400 and $7FF that's appears unused (i.e.: "00") or using a RAM address completely outside of the map itself (i.e.: using a seemingly unused RAM address "00" between $800 and $FFFF)?

Sorry, this is a bit confusing.  :(

For what it's worth, I did use $0400 instead of $0184, and the DTE code appears to be running fine as well.  I hope that's a good choice instead of bad one.

Quote from: KingMike on July 30, 2023, 02:59:17 PMNote that, as noted in the tutorial, CMP #$00 was an instruction used only for code clarification purposes and is useless to include in finalized code. The Z flag (for Zero/Equals) will already be set accordingly to whether A is zero after an LDA instruction. Though I believe you'll still need to explicitly check when you want to know if X or Y are zero.

Oh, I see... so this means "CMP #$00" can be omitted in the final version of the DTE code?  I can remove that in my code and it will run normally?

Cyneprepou4uk

I disagree with Pennywise. An address from stack is more likely to be unused than any other, unless the game uses stack for other things besides its intended purpose (which is quite noticeable).

In any case, Mesen has a RAM usage counter and can permanently highlight reads (which in this case is much more useful than writes highlighting), this can help you make up your mind.



I've got a 100% safe solution for a free RAM address, which can work on other MMC3 games as well. You can still use your 61F0.

First, use reset vector (FFFC-FFFD) to easily insert your own code at the boot and then jump to old reset vector. You need to enable writes to 6000-7FFF, and also clear 61F0 (just in case).



https://www.nesdev.org/wiki/MMC3#PRG_RAM_protect_($A001-$BFFF,_odd)

Second, you need to delete original writes to A001 in case they disable writes to 6000-7FFF later. Your game has one disabling writes right here.



Usually games have a single write to A001 or none at all, but your game has more. Search for 8D 01 A0/8E 01 A0/8C 01 A0 (STA/STX/STY) and replace all of them (except your own, of course) with EA EA EA, you won't need any of them anymore even if some of them enable writes because now you'll have them enabled from the start.

Another way, instead of searching and deleting all writes to A001, you can write LDA 80 + STA A001 right before your LDA 61F0 at the very beginning of your DTE subroutine.



BlackPaladin

After following the advice shown here, I did some more tweaking with the code, and after testing out it, the DTE routine worked even with $61F0 used as a RAM byte as well as with CMP = $00 removed.  Here's the latest version of the DTE code involved...



KingMike and Pennywise and ESPECIALLY Cyneprepou4uk, I want to thank you all very much for helping me out with this issue.  This wouldn't have been possible without your assistance.  ;_;

With this, I think trying to make Inbou no Wakusei Shancara a translation project may just be possible... just need to look for a translator who'll help out, of course.

I may sound like a broken record, but again, thank you very much for helping me!

BlackPaladin



While the DTE did work, I encountered a bug.  This happens when you reset when it's halfway displaying a DTE value.  (Lack of a better example, the image there is supposed to display a 71 where the "f" is as the code uses the hex value "70" to display "71" in the DTE table, currently.)  If you reset while it's halfway running an "f" will show up when it starts re-running the DTE routine.

Does this require a different RAM byte used in the routine?  Is there something I forgot to add in the routine itself?

For reference, this is the most recent version of the DTE routine currently...


Cyneprepou4uk


BlackPaladin

Quote from: Cyneprepou4uk on August 01, 2023, 10:31:15 PMWhy don't you debug it to see what's wrong with it?


Will all due respect, it sounds like you're saying "Figure it out."  Fair enough.

Anyway, I remember something that abw mentioned when it came to coding DTE back when I was working on Last Armageddon.  I remember he mentioned someting about "initialize the DTE flag at the start of the text display routine",  When it came to Last Armageddon, this wasn't an issue.  However, this is the case with Shancara.

So, this means I have to look for the game's text routine.  If I can initialize the DTE flag there, the bug won't be a problem anymore.  This where I break into the debugger and hope for the best.

First off, while running the unedited version of the game, I set up a PPU write breakpoint at $22A7 with a "$16" (That's the Japanese hiragana letter that supposed to be pronounced "shi".) conditional and reset.  The game snapped here...

  01738F: B9 00 06  LDA $0600,Y @ $0635 = #$00
  017392: C8        INY
  017393: 8D 06 20  STA PPU_ADDRESS = #$A7
  017396: 8E 06 20  STX PPU_ADDRESS = #$A7
  017399: B9 00 06  LDA $0600,Y @ $0635 = #$00
  01739C: C8        INY
> 01739D: 84 18    STY $18 = #$31
  01739F: 8D 07 20  STA PPU_DATA = #$00
  0173A2: 60        RTS

While reading on a document on RHDN's database about looking up text routines in NES roms, it mentioned that in the next step is to apply a CPU write breakpoint.  I tried with $35 (nothing happened), $635 (nothing happened), $600 (nothing happened), and $18 (snapped at $1739D). 

And here's where I got lost.   I'm not sure where to go from here.

Cyneprepou4uk

I'm saying that it's much faster to do a little debugging instead of keep on guessing. Just set an Execute breakpoint at the start of your subroutine, and press Step Into button to go through the code when it triggers.

If by DTE flag you mean 61F0, then it should be already initialized at reset if you did everything correctly. In fact, when I said to clear 61F0 at reset just in case, this is exactly the case I was thinking of (when you reset the game in the middle of printing text, which could leave a nonzero flag).

Currently I don't see any possible bugs in your code besides relying on BPL instructions for comparsion, although it seems to not affect anything at the moment if the code works as intended. So I don't have any more guesses myself.

By the way, if you're looking for "start of the text display routine", you could go with where the game loads pointers for text (writes to 0066 and 0067).

BlackPaladin

...and this is the part where I eat some humble pie.  I realized my mistake in the DTE routine.  I didn't add the first instructions you had mentioned firsthand.  The one where you suggested inserting at 0x1FFF0...

LDA #$80
STA $A001
LDA #$00
STA #61F0
JMP $E000

...followed with "E0 FF" to 0x2000C-0x2000D.  After inserting that into the Shancara NES ROM and testing it out by resetting when half-way displaying a DTE value, no errors occur.  So it looks like the DTE routine is running flawlessly.   I misunderstood that just adding "LDA #$80 / STA $A001" at that start of the DTE routine was all that was needed for it to work flawlessly when that wasn't the case.  I admit my error here.

If I came across as condescending in my last reply, I humbly apologize.  Thank you for being patient with an idiot like me.

KingMike

Do you know where the text pointer is? Finding the point in the code where the pointer is read to being the text print process is a good time to reset the DTE flag.
"My watch says 30 chickens" Google, 2018