It has more to do with FF3us than SNES games meaning the SNES is not limited in this regard. Simply put you need to look at bank $C0. There is a RAM value for position on line ($BF). Instead of starting at #$04 (beginning X position) and checking for ((position += character width) > #$E0 (max X position)) to go to the next line, you start at #$E0 and decrease each time you draw a character, checking for ((#$E0 - position) >= #$04), where position is increased each time you draw (position += current character width).
It's not well documented but by studying the code in bank $C0 it is doable. There would be special stuff like actor names to take care of, check $C08067 for special opcodes cases. I made a text centering opcode and got a good idea of the system. Here is my code to center text:
Code:
;------------------------------------------------------------------------
; Centring Control Character code (OP#$17)
;------------------------------------------------------------------------
CChar:
LDA $BF ; Load current line position
CMP #$04 ; Beginning of line
BNE brn_exitB ; Branch if we are not at the start of a line
STZ $3F ; Current word length: 0
sub_loop:
LDY $00 ; Current dialogue character: pointer + 0
LDA [$C9],Y ; Load dialogue character
BPL notDTE ; Branch if not DTE
AND #$7F ; Isolate DTE ID
ASL A ; Multiply by 2
TAX ; Index it
LDA $CF ; Load text buffer byte
CMP #$80 ; Check if buffer is empty
BEQ .sub_loopB ; branch if buffer is empty
LDA #$80 ;
STA $CF ; Set buffer as not empty
BRA secondDTE ; Verify second DTE character
.sub_loopB
LDA $C0DFA0,X ; Load DTE character 1
TAY ; Transfer A to X
LDA [$1A],Y ; Load width for variable font cell
CLC ; Prepare addition (no carry)
ADC $3F ; Add font cell width to sentence width
STA $3F ; Save as sentence width
secondDTE:
LDA $C0DFA1,X ; Load DTE character 2
TAY ; Save character index
LDA [$1A],Y ; Load width for variable font cell
CLC ; Prepare addition (no carry)
ADC $3F ; Add font cell width to sentence width
STA $3F ; Save as sentence width
BRA checkEol ; Check end of line an increment pointer if necessary
notDTE:
LDY $00 ; Clear Index
LDA [$C9],Y ; Load dialogue character
CMP #$01 ; Is it new line character?
BEQ calcMiddle ; branch if so (calculate center)
CMP #$11 ; Is it end parameter character?
BEQ calcMiddle ; branch if so (calculate center)
CMP #$12 ; Is it end parameter character?
BEQ calcMiddle ; branch if so (calculate center)
CMP #$13 ; Is it end of page character?
BEQ calcMiddle ; branch if so (calculate center)
CMP #$20 ; Is the character a character code?
BCC incDialog ; Branch if so (increment dialogue pointer and loop)
TAY ; Save character index
LDA [$1A],Y ; Load width for variable font cell
CLC ; Prepare addition (no carry)
ADC $3F ; Add font cell width to sentence width
STA $3F ; Save as sentence width
checkEol:
CMP #$E0 ; Compare to line max width
BCS brn_exitB ; we have ended the line, there's nothing to center...
incDialog:
INC $C9 ; Increment dialogue
BNE sub_loop
INC $CA
BNE sub_loop
INC $CB
BRA sub_loop
brn_exitB:
RTS
calcMiddle:
LDA #$E0 ; Load line width
SBC $3F ; Subtract sentence width
LSR A ; Divide by 2
STA $BF ; Store as position in line
TAY
STY $4204 ; Whatever is left, store as to be divided
LDA #$10
STA $4206 ; Divide Y by 16
NOP
NOP
NOP
NOP
NOP
NOP
NOP
LDA $4214 ; Load the division result
STA $4202 ; Store as a multiplier
LDA #$20
STA $4203 ; Multiply previous result by 32
NOP
NOP
NOP
LDA $4216 ; Get the multiplication result
ADC $C1
STA $C1
BRA brn_exitB