There's not been anything interesting to "blog" about on the Xanadu translations for a while, but SamIAm finally reported a problem that needed to be fixed.
The problem was in Xanadu 2 when you get a message box that prompts you to make a choice ... the highlighting was totally messed up.
Figuring out what was going on showed that Falcom are doing something really quite cunning to get those options dialogs to work.
Here's what was going wrong ...
The game wasn't crashing, so it obviously wasn't anything going
too horribly wrong ... but WTF???
This is the piece of script that SamIAm has translated into English ...
.scriptA6BA:
_enable_8x12_font()
_set_pen_then_call_then_eol( orange, .scriptB4BE )
_disable_8x12_font()
[Lord Areios, I can send you to anywhere from the Prologue to Chapter Three.]
_wait_for_keypress_then_clear()
[What will you do?]*
_choose_option_then_clear( $01, $01 )
[ Continue]
_next_option()
[ Never mind]
_end_of_options()
_conditional_jump( $c9, $f0, $02, $9c63, .scriptA8BB )
_conditional_jump( $c9, $f0, $01, $9c63, .scriptA72C )Well, that looks simple, and there's nothing obviously wrong with it, so it was time to actually take a look at the code that process those options boxes.
That's something that I'd been hoping that I wouldn't ever need to do, because I just figured out that the code was doing some smart stuff behind-the-scenes to remember where each string in the options was, and to just redraw each string when the highlight changes.
Once I found the code (which just took setting a VRAM-write breakpoint in Mednafen), it looked a bit simpler that I expected.
But it was quickly obvious that is was using some hard-coded coordinates, and that it was yet-another thing that my shift to using a VWF had broken.
Changing the coordinates from the original 4-pixel increments to 1-pixel increments resulted in ...
Definitely better ... but WTF is going on with all of those little dots?
Looking deeper into the code, it turns out that Falcom aren't redrawing the text at all to change the highlighted option, they are just doing a bunch of simple writes to VRAM.
Now the entire message box is made up of a bunch of 32x64 pixel sprites, and they call these routines to change 2 sprite-lines at-a-time of the whole 224x14-pixel block of a text line.
Y = vram-addr-lo
A = vram-addr-hi
set_hilite_2lines: tya
clc
adc #$10
sta self_modify+1
sei
st0 #VDC_MAWR
sty video_data_l
sta video_data_h
st0 #VDC_MARR
self_modify: st1 #$nn
sta video_data_h
cli
st0 #VDC_DATA
cla
tsb video_data_l
tsb video_data_h
tsb video_data_l
tsb video_data_h
rts
Y = vram-addr-lo
A = vram-addr-hi
set_normal_2lines: lda $3e
sei
st0 #VDC_MAWR
sty video_data_l
sta video_data_h
cli
st0 #VDC_DATA
st1 #$00
st2 #$00
st1 #$00
st2 #$00
tya
clc
adc #$10
tay
rtsLooking at that left me totally shocked ... I couldn't see how-on-Earth that code could change the text from cyan to white, and then change it back again?
So I had to seriously think about what that code was doing.
When highlighting an option, it's doing a
really neat trick with the VRAM and the TSB instruction to copy the 2nd bitplane of the sprite data into the 1st bitplane of the sprite data.
Now saying that is one thing ... but it makes no real sense until you actually look at the colors that everything is drawn in ...
Color: 6 -> 7 (0110 -> 0111) Effect: ------ Usage: text cyan
Color: 7 -> 7 (0111 -> 0111) Effect: hilite Usage: text white
Color: 8 -> 8 (1000 -> 1000) Effect: hilite Usage: background pattern 1
Color: 9 -> 9 (1001 -> 1000) Effect: -LOST- Usage: box border lite color
Color: A -> B (1010 -> 1011) Effect: ------ Usage: background pattern 2
Color: B -> B (1011 -> 1011) Effect: hilite Usage: background pattern 2
Color: C -> C (1100 -> 1100) Effect: hilite Usage: background pattern 3
Color: D -> D (1101 -> 1100) Effect: -LOST- Usage: box border dark color
Color: E -> F (1110 -> 1111) Effect: ------ Usage: background pattern 4
Color: F -> F (1111 -> 1111) Effect: hilite Usage: background pattern 4When you look at it that way, you can see that copying bit-1 to bit-0 changes the text from cyan to white, and it changes the background colors, too ... but since some of the background colors are duplicated in the palette, you don't actually see the background change on the screen.
When setting an option back to normal, it's just setting the 1st bitplane of the sprite data to zero.
Huh??? How can that work if it's so different to what it does to set the highlight in the first place???
Once again, it makes no real sense until you actually look at the colors that everything is drawn in ...
Color: 6 -> 6 (0110 -> 0110) Effect: normal Usage: text cyan
Color: 7 -> 6 (0111 -> 0110) Effect: ------ Usage: text white
Color: 8 -> 8 (1000 -> 1000) Effect: normal Usage: background pattern 1
Color: 9 -> 8 (1001 -> 1000) Effect: -LOST- Usage: box border lite color
Color: A -> A (1010 -> 1010) Effect: normal Usage: background pattern 2
Color: B -> A (1011 -> 1010) Effect: ------ Usage: background pattern 2
Color: C -> C (1100 -> 1100) Effect: normal Usage: background pattern 3
Color: D -> C (1101 -> 1100) Effect: -LOST- Usage: box border dark color
Color: E -> E (1110 -> 1110) Effect: normal Usage: background pattern 4
Color: F -> E (1111 -> 1110) Effect: ------ Usage: background pattern 4When you look at it that way, you can see that clearing bit-0 changes the text from white to cyan, and it changes the background colors, too ... but, again, since some of the background colors are duplicated in the palette, you don't actually see the background change on the screen.
Now that's really, really cunning!
A clever organization of the color palette and the colors that are used in the message box allows Falcom to change the highlight in the options dialog without caring what the text actually says.
But wait ... why does that mean that those dots appear?
Well, that's because we changed the background colors to make them darker so that the text stands out more.
But I didn't realize that those "duplicate" colors needed to be changed, too, so I left them at their original colors, and that's what those dots are. :oops:
Once I figured that out, I just set the "duplicate" colors in the palette to our new darker colors, and ...
The problem is fixed, but there's one more question ... if you look at the two color tables, then you can see that changing to hilite or a normal both change the colors that the border of the message box is drawn in.
So the question is, why don't they?
The screen is 256-pixels wide, and the message box is 248-pixels wide, and the code that's shown above changes the background in 16-pixel wide strips. That should
guarantee that the right-hand-edge of the message box is overwritten, just like the left-hand-edge was in SamIAm's original report of the bug.
But in reality, the last screenshot shows that it doesn't get destroyed.
Well, it turns out that Falcom must have had the same problem, and that they came up with another cunning solution.
They shifted the entire message box graphics over by a few pixels in VRAM, and so the right-hand-edge of the visible message box is actually at the left-hand-edge of a mostly-blank and off-screen sprite.
That means that the highlighting code doesn't actually overwrite that sprite at all ... just everything right up to the edge of it.
Once more, that's a really cunning solution to the problem.
Those Falcom guys really deserve a few beers, I wonder if they celebrate Cinco De Mayo?
Anyway, here's one last screenshot of the everything working properly ...