; TRO1.0 set breakpoints at Output points and trace them
; Firmware : S45iv4
; Author: fcotrina
; Created: 16.01.2005
; Based in patch DAR=Dump_All_Registers_v2 by RizaPN
; With some ideas taken from lalo.lerry and RizaPN
; Big thanks to Redkin and NCTN and all patchers out there
;
; This patch is related to TRI
; This patch is useful for patch programmers. If you
; find some code in ROM and want to know who calls it, just
; change any instruction "rets" with "trap #4Ah", that is:
; 0xYYYYYY: DB00 9B94
; After patching the ROM with new traps, start the phone. If it
; crashes, undo the trap, as this is not a traceable routine.
; Then, using CGSN patch, set the debug level in 0040:0802
; at+cgsn*40,0802,0000 not debug anything
; at+cgsn*40,0802,FFFF not debug anything
; at+cgsn*40,0802,7777 call procedure 0100700h before anything else
; at+cgsn*40,0802,6666 call procedure 0100600h after debugging
; at+cgsn*40,0802,1111 print the routine that is hitted and its caller
; Any other value will print the routine that is hitted and 12 entries in stack.
; for example, at+cgsn*40,0802,2222 will do that. This is what I normaly use.
; When I say "print", I mean that the information it output to the first
; serial port COM1 . Use Hyperterminal or similar to see the data.
;
; For example, apply this patch:
; 0x53B940: DB00 9B94
; then, at+cgsn*40,0802,2222
; and when you start the game RaceAce , it shows:
; 00F3B940<00FA61C8-0800.BF7E.00FC.BB34.00FC.B50A.00FC.FED2.00F4.64F2. 00CC.E6C6.
; The first value is the routine that was intercepted. Almost sure, this is the
; place where you have set your breakpoint: 0xF3B940
; The second value, after the '<', is the routine that called it. Let's call it S1
; In this case, FA61C8 is "calls 0E2FFFAh" (a.k.a. callR5R4) with r5=03E9 and r4=21C8,
; what converts to 03E9:21C8=0FA61C8h
; Later, after the '-', it dumps the stack:
; Usually, the first value is the flags register PSW , 0800 in this case
; Then, it usually follows the routine S2 that called S1, But remember that S1 might
; store information in the stack, so this word can be anything else.
; in this case, it is BF7E.00FC , that means 0FCBF7Eh
; Following, the routine S3 that called S2, with same warning as above: 0FCBB34h
; If nothing messes up with the stack, it is possible to back-trace 2+5 routines.
;
; limitations:
; -the patch sets up a new handler for trap #4Ah. This is done
; quite early in the mobile initialization, but may not early enough.
; That means that not all "rets" can be substituted, because
; the trap might had not been set yet.
; -too many breakpoints might print too much data, and crash the phone
; -only work with "rets" . Instruction "ret" can not be traced because
; CSP is not stored, and it is tricky to know it.
; -if another trap is being processed, the breakpoint might not be processsed.
; This depends on the priority of the processed trap and the processor mode
; -if to terminal is connected, the patch tries to send the information
; -I know there are routines to output characters to serial port. I did not
; use them because they use new processes/multitasking, and this modifies
; the values and timings
; -this patch might confuse aplications like SiemensDebugger. Set debug_level
; to 0000 before using SiemensDebugger, ex: at+cgsn*40,0802,0000

base 0A00000h
#include C166.inc

org 0CEE650h ; this is the end of the original routine that sets any trap handler
jmps set_my_trap_4A
; originaly:
; 2EE650: FC00 ; pop DPP0
; 2EE642: DB00 ; rets

org 0FEF100h ; just some freee space in ROM
;******************************************
; Set the new handler for trap #4A
set_my_trap_4A:
mov r1, #s(my_trap_4A)
movb rh1, rl1
movb rl1, #0FAh ; code for "JMPS"
mov r12, #0128h ; trap 4A jumps to 4A*4=128h. This is in C166 manual
mov [r12], r1 ; set the segment to jump to
mov r1, #o(my_trap_4A)
add r12, #2
mov [r12], r1 ; set the offset to jump to.
; in other words: put in 0000:0128 the intruction "jmps my_trap_4A"
pop DPP0 ; this was in the original code
rets
;******************************************

; this is where "trap #4Ah" will jump to
my_trap_4A:
mov [-r0], r6
mov [-r0], r5
mov [-r0], r4
mov [-r0], r3

extp #40h, #1h ; read debug_level
mov r4, 0802h
cmp r4, #7777h ; if 7777h, call subrutine
jmpr cc_NZ, continue_process ; if not, continue
calls 0100700h
continue_process:
pop r6 ; a trap always sets PSW-CSP-IP. As I come from
pop r3 ; a "rets" substituted by "trap #4Ah" , therefore
; ; IP is the last data in stack, and CSP is the previous
callr output_r3 ; output CSP
mov r3, r6 ; and then IP. To make it better, show the address that
sub r3, #02h ; called the trap, not the address where I should return to
callr output_r3
movb rl4, #'<' ; print a separator
callr output_rl4_char
pop r6 ; get PSW from stack. Can not be modified
pop r5 ; stack holds IP of the address that called
pop r3 ; stack holds CSP
push r6 ; insert PSW again, because we'll return with reti
push r3 ; insert CSP again. Remember that reti extracts PSW-CSP-IP
push r5 ; insert IP again. Now we could return
callr output_r3 ; print who called, that is, where we come from. First CSP
mov r3, r5 ; and later IP. As this is a "calls ..." instruction, takes 4 bytes
sub r3, #04h
callr output_r3

; after printing the current address and the calling address, dump some stack
extp #40h, #1h
mov r5, 0802h ; but only if debug_level tells us so.
cmp r5, #1111h ; if #1111h , short listing.
jmpr cc_Z, check_post_routine
; long listing
movb rl4, #'-' ; initial separator
callr output_rl4_char
mov r5, SP
add r5, #04h ; the first 4 bytes have already been printed
mov r6, #0Ch ; dump 12 words
another_stack:
mov r3, [r5+]
callr output_r3
movb rl4, #'.' ; separator in between stack words
callr output_rl4_char
sub r6, #01h
cmp r6, #0h
jmpr cc_NZ, another_stack

; output a line_feed and see if need to call a post-process-routine
check_post_routine:
movb rl4, #0Dh
callr output_rl4_char
movb rl4, #0Ah
callr output_rl4_char
extp #40h, #1h
mov r5, 0802h ; read debug_level
cmp r5, #6666h ; if 6666h, call routine at 100600
jmpr cc_NZ, end_my_trap_4A
calls 0100600h
end_my_trap_4A:
mov r3, [r0+]
mov r4, [r0+]
mov r5, [r0+]
mov r6, [r0+]
reti ; this pops IP, CSP, PSW, and then rets

;**************
; if r3 is a word with value #1234h, outputs 1, 2, 3, 4
; this is not a trivial as it looks, because of little indian
output_r3:
mov r4,r3
shr r4, #0Ch ; get left-most nibble: 1
callr output_rl4_numeric
mov r4,r3
shr r4, #08h ; get second-left-most nibble: 2
callr output_rl4_numeric
mov r4,r3
shr r4, #04h ; get second-right-most nibble: 3
callr output_rl4_numeric
mov r4,r3 ; get right-most nibble: 4
callr output_rl4_numeric
ret ; it is a relative routine.

; ****************
output_rl4_char:
mov [-r0], r5
jmpr cc_UC, numeric

output_rl4_numeric: ; outputs a number. Basically, adds '0' to it
mov [-r0], r5
and r4, #000Fh
add r4, #30h
cmp r4, #3Ah
jmpr cc_ULT, numeric ; although if it is in range 0Ah to 0Fh, needs
add r4, #07h ; to add 'A'-'0'
numeric:
extp #40h, #1h
mov r5, 0802h ; read debug_level
cmp r5, #0h ; if 0 of FFFF, do not output anything
jmpr cc_Z, exit_output_rl4
cmp r5, #0FFFFh
jmpr cc_Z, exit_output_rl4

movb S0TBUF, rl4
mov r4, r5
bclr S0TIR ; send to COM1 serial port
not_sent_yet:
jb S0TIR, exit_output_rl4 ; if sent, exit
sub r4, #01h ; if not sent, try again, as many times as debug_level
jmpr cc_NZ, not_sent_yet
exit_output_rl4:
mov r5, [r0+]
ret

end

; I am not releasing the binary code because I assume that
; you need (and you know how) to compile yourself