Right to left language?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Right to left language?
by on (#59773)
How can I make a NES ROM to support right to left language?
I don't have any problem for translating fixed text on the screen but texts which gradually appear from left to right, are really hard to translate.
Captain Tsubasa Arabic translation is a good example for this matter.
I guess I have to do it in assembly 6502 but I don't have any idea how to do it.
I appreciate anyone who can help me.

Also some other nice people guide me but I really need some more help :

http://www.romhacking.net/forum/index.php/topic,10389.0.html

by on (#59775)
Hello FARID.

Your question is quite weird. I mean, the way the text is displayed on the screen has no incidence on the way it is stored in the ROM.

Did you find the address of the text? You should use relative search tools to begin. And if it doesn't work, maybe you should look at debug tools such those which FCEUXD SP features...

by on (#59776)
Are you talking about making your own ROM or modding an existing game?

by on (#59777)
He says he wants to translate Captain Tsubasa into Arabic.

by on (#59784)
There are three ways to print from right to left:
  1. Store entire lines of text in visual order. This might work for something like Super Mario Bros.
  2. Store text in logical order and copy it to VRAM backwards, starting from the last character in a string and using a 'dex/bpl' loop.
  3. If your game prints text to the screen a character at a time, like in many cut scenes, print it at descending VRAM addresses on each row.

This would suffice for Hebrew, but Arabic has the complication of connected glyphs.

by on (#59793)
The VRAM address normally auto-increments, so you definitely will have to change the code in the game, possibly in several locations. You won't be able to make it the same size or smaller as the original, so you probably will have to find or make some free space in the ROM (in the same bank, in RAM, anywhere possible).

You could start by finding the text data that you want to print reversed in ROM - you need to convert that to the address space as the NES CPU sees it. Basically, remove 16 byte iNES header, and divide it into bank size and locate the address within that bank. Once you have the address you can use a debugger to set a breakpoint when that address is read. If the debugger stopped on an LDA instruction, then you've probably found the first part of it.

At that point it probably will not write it to $2007 - instead it may do an STA $xxxx,y somewhere into RAM (as a buffer). Now you could put a breakpoint on the address of the buffer, and find out where it's reading that followed by an STA $2007. Then scroll backwards to the previous code, you should see it write to $2006 twice, to set the VRAM address. And right there you may have found the code that you need to change. If not, put a breakpoint on whatever RAM location it read the VRAM address from, I doubt you would have to trace it back any further than that, but you never know until you try it.

You'll have to replace the code that sets the VRAM address in that particular case, and hopefully that's all. Just make sure you've found the right case, where it's for text only and not any other kind of PPU data.

by on (#59794)
Let me explain more :

I want to translate a nes game (Kunio Kun no Nekketsu Soccer League.nes) from Japanese to Persian. As I said, I can translate every text in it, by using FCEUX and Tile Layer Pro. I made some progress in it :

Image

Image

Image

Image

Image


Even I can translate text which appear gradually from left to right. But guess what! Persian language is written from right to left so my text appears in a reverse order and it is really unpleasant!


On the other hand I am not the first person who want to find a way for this matter, I mean about ten years ago someone (must be a skillful one!) solved this problem, and he translated Captain Tsubasa from Japanese to Arabic :

Text is appearing gradually from left to right :

Image

Text is appearing gradually from right to left :

Image

But unfortunately I couldn't find him to ask my question!

To be honest, I didn't read the rules of this forum, am I permitted to link to a nes game or not? Also I have it's IPS patch.

by on (#59795)
Memblers wrote:
The VRAM address normally auto-increments, so you definitely will have to change the code in the game, possibly in several locations. You won't be able to make it the same size or smaller as the original, so you probably will have to find or make some free space in the ROM (in the same bank, in RAM, anywhere possible).

You could start by finding the text data that you want to print reversed in ROM - you need to convert that to the address space as the NES CPU sees it. Basically, remove 16 byte iNES header, and divide it into bank size and locate the address within that bank. Once you have the address you can use a debugger to set a breakpoint when that address is read. If the debugger stopped on an LDA instruction, then you've probably found the first part of it.

At that point it probably will not write it to $2007 - instead it may do an STA $xxxx,y somewhere into RAM (as a buffer). Now you could put a breakpoint on the address of the buffer, and find out where it's reading that followed by an STA $2007. Then scroll backwards to the previous code, you should see it write to $2006 twice, to set the VRAM address. And right there you may have found the code that you need to change. If not, put a breakpoint on whatever RAM location it read the VRAM address from, I doubt you would have to trace it back any further than that, but you never know until you try it.

You'll have to replace the code that sets the VRAM address in that particular case, and hopefully that's all. Just make sure you've found the right case, where it's for text only and not any other kind of PPU data.


Thanks man, you really deserve to be an admin!

Can you look at the following link and make a tutorial with an example for your explanations? Use any nes game you want and please don't forget to add some pictures. I am not in a hurry so you can write it any time that you are free.

http://www.romhacking.net/forum/index.php/topic,10389.msg156524.html#msg156524

by on (#59797)
Note: I'm typing this as I go, so apologies for total lack of clarity.

I'll do Dragon Warrior 1 as en example. Every game does things differently!

The first message the king tells you is stored at address B13E in NES memory.
Set a data breakpoint for there.



After stepping through some code, we find something relevant:

01:B653:AE 0D 65 LDX $650D
01:B656:9D 14 65 STA $6514,X @ $6514
01:B659:EE 0D 65 INC $650D

It's loading an index for the text buffer, storing the text character it read, and increments the pointer.

Then later on:

01:B5C1:AE B4 64 LDX $64B4
01:B5C4:BD 14 65 LDA $6514,X @ $6514 = #$50
01:B5C7:EE B4 64 INC $64B4

It's reading from that buffer, and incrementing some other pointer. Maybe there's two parts so it can output longer strings like character names...

01:B5EB:AE 0D 65 LDX $650D = #$00
01:B5EE:BD 14 65 LDA $6514,X @ $6514 = #$50
01:B5F1:EE 0D 65 INC $650D = #$00

Later on it does some more stuff involving reading the text from the buffer...

01:BA01:9D 7C 65 STA $657C,X @ $657C = #$5F

Now it's storing the character into yet another buffer...

03:C6AC:9D 00 03 STA $0300,X @ $0302 = #$44

This one looks like it might be the one where it goes into some kind of buffer for PPU display?

Here we go, finally some meat:

03:FE98:BD 00 03 LDA $0300,X @ $0300 = #$22
03:FE9B:8D 06 20 STA $2006 = #$00
03:FE9E:BD 01 03 LDA $0301,X @ $0301 = #$65
03:FEA1:8D 06 20 STA $2006 = #$00
03:FEA4:BD 02 03 LDA $0302,X @ $0302 = #$50
03:FEA7:8D 07 20 STA $2007 = #$00
03:FEAA:E8 INX
03:FEAB:E8 INX
03:FEAC:E8 INX

This code that runs inside vblank takes a character PPU address, then writes the character.

So it looks like in this case you want to find where it's messing with bytes 300 and 301, then change the code so it decreases the address instead of increase the address, then you also want it to reset to the right margin instead of the left margin.





======================
EXPLANATION OF WHAT I'M DOING
======================





Basically, you follow your text character through its journey through the NES game's code.

Here, I'll demonstrate:

03:FD2F:B1 30 LDA ($30),Y @ $B13E = #$50

It loads the first text character here...

03:FD31:85 37 STA $0037 = #$02

Then stores it into address $37. Now we need to watch $37. Set a data breakpoint for reads and writes to $37.

03:FD33:68 PLA

Next instruction is PLA. It pops A off the stack. We no longer need to watch register A at this point since it no longer contains our text character.

Now we wait for the game to access $37

03:FD37:A5 37 LDA $0037 = #$50

Now step for a while and see what it does with register A...

01:B64E:A2 01 LDX #$01
01:B650:8E 0A 65 STX $650A = #$01
01:B653:AE 0D 65 LDX $650D = #$00
01:B656:9D 14 65 STA $6514,X @ $6514 = #$50
01:B659:EE 0D 65 INC $650D = #$00

It stores A into a buffer starting at 6514. Set a breakpoint for that now. Keep stepping and watching register A, but now we also need to watch 6514.

Eventually we reach
01:B679:A9 02 LDA #$02
and now we can stop watching A. Run until it accesses 6514 or 37

03:FD1C:85 37 STA $0037 = #$50
It just replaced 37 with something else, we no longer care about 37. Stop watching that.

01:B5BC:A9 00 LDA #$00
01:B5BE:8D B4 64 STA $64B4 = #$00
01:B5C1:AE B4 64 LDX $64B4 = #$00
01:B5C4:BD 14 65 LDA $6514,X @ $6514 = #$50
01:B5C7:EE B4 64 INC $64B4 = #$00

Now it's reading 6514 for some reason. Watch where A goes...

We later see it loop through to there again and discard A, so let's keep running ahead (watching 6514)

01:B5E6:A2 00 LDX #$00
01:B5E8:8E 0D 65 STX $650D = #$00
01:B5EB:AE 0D 65 LDX $650D = #$00
01:B5EE:BD 14 65 LDA $6514,X @ $6514 = #$50
01:B5F1:EE 0D 65 INC $650D = #$00

We see it read 6514 here. Let's keep watching where A goes...

01:B5F8:48 PHA

Now it pushed A onto the stack. Keep running until we hit PLA, (the Step Out feature of a debugger helps here, you skip the unnecessary code and keep reaching your breakpoints) Looks like this particular stack push we don't really need to watch though.

01:B9C7:48 PHA

Another PHA...

01:B9C8:A5 D2 LDA $00D2 = #$00

Now A is gone, keep running it until it hits the PLA and gets the character back...

01:B9D8:68 PLA

Now A is the text character again...

01:B9F5:48 PHA

another pha...

01:B9FC:68 PLA

the corresponding PLA...

01:BA01:9D 7C 65 STA $657C,X @ $657C = #$5F
01:BA04:85 08 STA $0008 = #$5F

Now it's storing it into a buffer, and 08. Watch 657C and 08.


03:C69C:A6 04 LDX $0004 = #$00
03:C69E:A5 0B LDA $000B = #$22
03:C6A0:9D 00 03 STA $0300,X @ $0300
03:C6A3:E8 INX
03:C6A4:A5 0A LDA $000A = #$65
03:C6A6:9D 00 03 STA $0300,X @ $0301
03:C6A9:E8 INX
03:C6AA:A5 08 LDA $0008 = #$50
03:C6AC:9D 00 03 STA $0300,X @ $0302 = #$44
03:C6AF:E8 INX
03:C6B0:E6 03 INC $0003 = #$00
03:C6B2:86 04 STX $0004 = #$00

This code reads 08, and stores it into 302. For this game, 302 is the text data it will write to the screen during vblank, and 300 is the PPU address it writes it to.

Looks like we may need to watch 0A and 0B, since it has to do with the PPU address, they get copied to 301 and 300.


03:FE98:BD 00 03 LDA $0300,X @ $0300 = #$22
03:FE9B:8D 06 20 STA $2006 = #$00
03:FE9E:BD 01 03 LDA $0301,X @ $0301 = #$65
03:FEA1:8D 06 20 STA $2006 = #$00
03:FEA4:BD 02 03 LDA $0302,X @ $0302 = #$50
03:FEA7:8D 07 20 STA $2007 = #$00
03:FEAA:E8 INX
03:FEAB:E8 INX
03:FEAC:E8 INX
03:FEAD:E4 04 CPX $0004 = #$03
03:FEAF:D0 E7 BNE $FE98

So yeah, here's that VBLANK code again. this code runs during vblank and draws the character to the screen.



=============
SO NOW WHAT?
=============

Find out how the game calculates what PPU address to write text to. Then hack it so it goes right to left instead of left to right.
You need to change an increment somewhere into a decrement.
Then change the code for when it resets to the left margin to instead set to the right margin.

Obviously every game does things differently. Dragon Warrior wasn't too bad to follow and see where the text character goes once its been read.

by on (#60796)
Ok I made some progress. Here is my report :

Finding the correct breakpoint address was really difficult but after reading KingMike's DTE document and some trial and error I could find it.

I want to change these two lines which are appearing from left to right.

Image


By setting a breakpoint for B18F Debugger stops at here :

Image

By stepping over, the following code comes up :

00:AFA6:B1 32 LDA ($32),Y @ $B18F = #$EA
00:AFA8:D0 0A BNE $AFB4
00:AFB4:10 07 BPL $AFBD
00:AFB6:C9 F0 CMP #$F0
00:AFB8:90 1C BCC $AFD6
00:AFD6:85 2A STA $002A = #$00
00:AFD8:A9 FF LDA #$FF
00:AFDA:85 29 STA $0029 = #$FF
00:AFDC:A5 2A LDA $002A = #$EA
00:AFDE:8D B9 06 STA $06B9 = #$FF
00:AFE1:A5 29 LDA $0029 = #$FF
00:AFE3:8D 96 06 STA $0696 = #$FF
00:AFE6:4C 23 B0 JMP $B023
00:B023:A9 01 LDA #$01
00:B025:8D 95 06 STA $0695 = #$1B
00:B028:8D B8 06 STA $06B8 = #$1B
00:B02B:AD 08 06 LDA $0608 = #$02
00:B02E:8D 94 06 STA $0694 = #$42
00:B031:18 CLC
00:B032:69 20 ADC #$20
00:B034:8D B7 06 STA $06B7 = #$62
00:B037:08 PHP
00:B038:AD 07 06 LDA $0607 = #$23
00:B03B:8D 93 06 STA $0693 = #$FF
00:B03E:28 PLP
00:B03F:69 00 ADC #$00
00:B041:8D B6 06 STA $06B6 = #$FF
00:B044:EE 08 06 INC $0608 = #$02
00:B047:D0 03 BNE $B04C
00:B04C:AD B9 06 LDA $06B9 = #$EA
00:B04F:C9 FF CMP #$FF
00:B051:F0 05 BEQ $B058
00:B053:A9 35 LDA #$35
00:B055:20 02 C0 JSR $C002
00:B058:60 RTS
07:C323:4C 68 C3 JMP $C368
07:C368:68 PLA
07:C369:20 D3 EE JSR $EED3
07:C36C:60 RTS
05:A2E0:AD 05 06 LDA $0605 = #$80
05:A2E3:30 03 BMI $A2E8
05:A2E8:A2 00 LDX #$00
05:A2EA:20 94 91 JSR $9194
05:A2ED:20 65 C0 JSR $C065
05:A2F0:20 14 80 JSR $8014
05:A2F3:20 5C C0 JSR $C05C
05:A2F6:60 RTS
05:80C4:4C 7A 80 JMP $807A
05:807A:60 RTS
07:C362:68 PLA
07:C363:85 F7 STA $00F7 = #$80
07:C365:68 PLA
07:C366:A5 F6 LDA $00F6 = #$0C
07:C368:68 PLA
07:C369:20 D3 EE JSR $EED3
07:C36C:60 RTS
07:C4BA:4C C3 C4 JMP $C4C3
07:C4C3:A5 51 LDA $0051 = #$00
07:C4C5:C5 51 CMP $0051 = #$00
07:C4C7:F0 FC BEQ $C4C5
07:C4C5:C5 51 CMP $0051 = #$00
07:C4C7:F0 FC BEQ $C4C5
07:C4C5:C5 51 CMP $0051 = #$00
07:C4C7:F0 FC BEQ $C4C5

And finally it loops at C4C5 and C4C7! Without printing any character!

Anyway as you said I changed

EE 08 06 INC $0608
To
CE 08 06 DEC $0608

And
69 20 ADC #$20
To
69 19 ADC #$19

Now the text (throughout the game) appears from right to left.
Also I can adjust it's alignment by changing the operand of ADC.

But as you can see in the picture just two characters of the first line print and the rest become hidden!

Image

The second line is ok.

Now what can I do about it?

by on (#60825)
Changing: 69 20 ADC #$20 To 69 19 ADC #$19 is wrong. The #$20 allows for the " to be placed directly above the letters.

Using your code above, you know that the PPU Address is being set by $607 and $608. You need to adjust the starting PPU Address for the text, so set a breakpoint for a write to $607 and $608 then you'll get:

00:B0AC:AD 18 B1 LDA $B118 = #$02
00:B0AF:8D 08 06 STA $0608 = #$46
00:B0B2:AD 19 B1 LDA $B119 = #$23
00:B0B5:8D 07 06 STA $0607 = #$23

Changing $B118 (x3128) to #$1B gets the results that you are desiring.

by on (#60861)
jstout wrote:
Changing: 69 20 ADC #$20 To 69 19 ADC #$19 is wrong. The #$20 allows for the " to be placed directly above the letters.

Using your code above, you know that the PPU Address is being set by $607 and $608. You need to adjust the starting PPU Address for the text, so set a breakpoint for a write to $607 and $608 then you'll get:

00:B0AC:AD 18 B1 LDA $B118 = #$02
00:B0AF:8D 08 06 STA $0608 = #$46
00:B0B2:AD 19 B1 LDA $B119 = #$23
00:B0B5:8D 07 06 STA $0607 = #$23

Changing $B118 (x3128) to #$1B gets the results that you are desiring.


Thank you very very ... much! :D It really worked!

I set a breakpoint for a read to $B118 :

00:B0AC:AD 18 B1 LDA $B118 = #$02
00:B0AF:8D 08 06 STA $0608 = #$29
00:B0B2:AD 19 B1 LDA $B119 = #$23
00:B0B5:8D 07 06 STA $0607 = #$23

:?: Why the second line is different from yours?
Even if I set a write breakpoint to $0608 it returns :

00:B0AF:8D 08 06 STA $0608 = #$29

Anyway as you said I changed :

AD 18 B1 LDA $B118
To
A9 1B LDA #$1B
EA NOP

But nothing special happened! Later debugger stopped at :

00:AF1F:AD 18 B1 LDA $B118 = #$02

And I changed it to

00:AF1F:A9 1B LDA #$1B
00:AF21:EA NOP

Now the text appears from right to left :

Image

And now some real questions : :oops:

:?: Can you tell me how did you realize that, I must set a write breakpoint to $0607 and change B118?

:?: What do you mean by (x3128) in your last sentence?

:?: In some places after appearing text, some characters are left on the screen! How can I solve it?

Image

Image

by on (#60862)
Characters aren't mirrored, just the order is reversed.