@org $80E4B00 @thumb ; This is the first part of a hack to make battle text work right. ; ; This routine should be linked to from 80840D2. ; ; This first part takes the place of the place in the game's code where it loads the current ; character right before doing control code checks. Later on in the code, the game will load ; the character again if it wasn't a control code. This later part will be handled by ; battle_vwf2.asm. We're doing things this way so that control codes like [WAIT] will work ; as intended. ; ; Basically, this part of the hack will prep a special block of RAM for battle_vwf2.asm ; and then perform the code we overwrote. ; ;-------------------------------------------------------------------------------------------- ; ; We're going to need to use a few bytes of RAM, starting from 0x2014300 ; ; 2014300 byte DO NOT USE! ; 2014301 byte init_flag -- If this is 0, we know we need to initialize everything ; 2014302 hword current letter/character/control code ; 2014303 ... ; 2014304 word current x and y coordinates ; 2014305 ... ; 2014306 ... ; 2014307 ... ; 2014308 word base address of the current string ; 2014309 ... ; 201430A ... ; 201430B ... ; 201430C hword current position in the string ; 201430D ... ; 201430E hword total length of the current string ; 201430F ... ; 2014310 hword current line # we're on ; 2014311 ... ; 2014312 byte newline_encountered flag -- 0 if no, non-zero if yes ; ;-------------------------------------------------------------------------------------------- ; Coming into this, the registers are as follows: ; ; r0 = current address (so when this is first called, it's the start of the string) ; r1 = current position in the current string ; r2 = ??? ; r3 = ??? ; r4 = ??? ; r5 = ??? ; r6 = ??? (seems to be pretty important though) ; r7 = current string/tile counter (this is the key to making our hack run long enough) ; r8 = not sure, but (r8 + 6) is where the total string length is stored ; r9 = ??? ; r10 = ??? ; r11 = ??? ; r12 = ??? ; ;=========================================================================================== ;=========================================================================================== ; This is some routine initialization stuff push {r5,LR} ; We're going to use r5, so we need to keep it in ; r13 is the SP, r14 is LR ldr r5,[sp,#0x08] ; Load r5 with our former LR value? mov r14,r5 ; Move the former LR value back into LR ldr r5,[SP,#0x04] ; Grab the LR value for THIS function str r5,[SP,#0x08] ; Store it over the previous one pop {r5} ; Get back r5 add SP,#0x04 ; Get the un-needed value off the stack lsl r1,r1,#1 ; this is code we clobbered while linking here ldr r0,[r0] add r0,r0,r1 push {r6} push {r5} ldr r6,=0x2014300 ; Load r6 with the base address of our custom RAM block ldr r5,=0x8D1CE78 ; load r5 with the address of the font width table cmp r1,#0 ; If this is the start of the string, initialize stuff bne main_code ; otherwise jump to the main code here ldrb r1,[r6,#1] ; This was the first character, but let's check init_flag just to be safe cmp r1,#0 ; If init_flag == 0, then that means we need to initialize stuff beq initialize b main_code ; Otherwise jump to the main code ;-------------------------------------------------------------------------------------------- ; This initializes our custom RAM block initialize: str r0,[r6,#0x8] ; store the base address of the string in the RAM block mov r0,#0x0 strh r0,[r6,#0xC] ; starting position = 0 strh r0,[r6,#0x10] ; current line = 0 strb r0,[r6,#0x12] ; newline_encountered flag = FALSE mov r0,#1 strb r0,[r6,#1] ; init_flag = 1, this makes the game not re-initialize everything mov r0,#12 strh r0,[r6,#0x4] ; start x = 12 mov r0,#6 strh r0,[r6,#0x6] ; start y = 6 mov r0,r8 ldrb r0,[r0,#0x6] ; [r8 + 6] has the total string length strb r0,[r6,#0xE] ; store total length in our RAM block bl word_wrap_setup ; add any newlines if text gets too long in the current string ;-------------------------------------------------------------------------------------------- ; This is the meat of the code -- this is why we're doing this hack main_code: ldr r0,[r6,#0x8] ; load the base address of the string ldrh r1,[r6,#0xC] ; load the current position in the string lsl r1,r1,#1 ; multiply it by 2, since there are two bytes per letter add r0,r0,r1 ; get the address for the current letter ldrh r0,[r0] ; load the current character strh r0,[r6,#0x2] ; store the current character in our RAM block ldr r1,=0xFF02 cmp r0,r1 ; see if this is a [WAIT] code (0xFF02) beq set_newline sub r1,#0x1 cmp r0,r1 ; see if this is a [BREAK] code (0xFF01) bne end_code ;-------------------------------------------------------------------------------------------- ; If we're executing this code, we've encountered a code that signifies a line break set_newline: ldrh r0,[r6,#0x10] ; load current line # add r0,r0,#0x1 strh r0,[r6,#0x10] ; line_num++ mov r0,#0x1 strb r0,[r6,#0x12] ; newline_encountered flag = TRUE ldrh r0,[r6,#0x0C] ; load current position add r0,r0,#0x1 ; we need to increment it manually if we're on a control code it seems strh r0,[r6,#0x0C] ; ;-------------------------------------------------------------------------------------------- ; This is the end area of the code, mostly preparing to leave and doing lines we clobbered end_code: ldrh r1,[r6,#0x2] ; load the current character in r1, the game expects this ldr r0,=0xFF30 ; load r0 with FF30, which the game expects to be here pop {r5} pop {r6} pop {PC} ; time to leave! ;=========================================================================================== ;=========================================================================================== ; This is the automatic word wrap routine, we call it once, when everything is getting inited ; ; The register layout during most of this is as follows: ; ; r0: scratch register ; r1: curr_char_address, points to the current character in the string ; r2: start_address, this points to the beginning of the string ; r3: end_address, this points to the final character of the string ; r4: curr_width, we use this to total up widths until it exceeds a certain amount (208) ; r5: contains the address of the font width table ; r6: starts with our RAM block's address, but I also need it for scratch usage too ; r7: contains the address of the last "space-ish" character, so we'll know where to put ; a newline if need be ;-------------------------------------------------------------------------------------------- ; We're just initializing everything here. Lots of registers being used for ease of coding word_wrap_setup: push {r2} push {r3} push {r4} push {r7} ldr r2,[r6,#0x8] ; load r2 with the start address of the string ldrh r3,[r6,#0xE] ; load r3 with the length of the string lsl r3,r3,#1 ; multiply the length by two, since two bytes per letter add r3,r2,r3 ; r3 now has the end address of the string push {r6} mov r1,r2 ; r1 is the current character's address mov r4,#0 ; r4 is curr_width mov r7,r2 ; r7 is last_space, the spot where the last space was ;-------------------------------------------------------------------------------------------- ; Now we do the meat of the auto word wrap stuff word_wrap_loop: ldr r6,=0xFF01 cmp r1,r3 bge word_wrap_end ; jump to the end if we're past the end of the string ldrh r0,[r1] ; load the current character cmp r0,#0x40 ; is the current character a space? beq space_found cmp r0,r6 ; is the current character a [BREAK]? beq newline_found add r6,#1 cmp r0,r6 ; is the current character a [WAIT]? beq newline_found mov r6,r0 lsr r0,r0,#0x8 ; clear the first two bytes of the current character cmp r0,#0xFF ; if r0 == 0xFF, this is a control code, so skip the width adding junk beq no_wrap_needed mov r0,r6 b main_wrap_code ;-------------------------------------------------------------------------------------------- ; We found a space or a space-like character, so reset some values space_found: mov r7,r1 ; last_space = curr_char_address b main_wrap_code newline_found: mov r4,#0 ; this was a [WAIT] of [BREAK], so reset the width mov r7,r1 ; last_space = curr_char_address ;-------------------------------------------------------------------------------------------- ; Here is the real meat of the auto word wrap routine main_wrap_code: ldrb r0,[r5,r0] ; get the width of the current character add r4,r4,r0 ; curr_width += width of current character cmp r4,#208 blt no_wrap_needed ; if curr_width < 208, go to no_wrap_needed to update the width and such mov r4,#0 ; if we're executing this, then width >= 208, so do curr_width = 0 now mov r1,r7 ; curr_char_address = last_space_address; we're gonna recheck earlier stuff ldr r0,=0xFF01 ; replace the last space-ish character with a newline code strh r0,[r7] ; barring any incredibly crazy text, this should almost never overwrite ; existing [BREAK]s or [WAIT]s ;mov r7,r6 ; last_space = start_address, this "de-initializes" it until the next space ;-------------------------------------------------------------------------------------------- ; Get ready for the next loop iteration no_wrap_needed: add r1,#2 ; curr_char_address += 2 b word_wrap_loop ; do the next loop iteration ;-------------------------------------------------------------------------------------------- ; Let's get out of here! word_wrap_end: pop {r6} pop {r7} pop {r4} pop {r3} pop {r2} bx LR