; ; brkpt.s ; ; Breakpoint handling (MDW) ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sledgehammer debugger. ; ; Sledgehammer is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Sledgehammer is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Sledgehammer. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Things not to forget ------------------------------------------------- ; ; When removing a break point, ensure instruction is still our branch ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis GET libs:stream ;----- External dependencies ------------------------------------------------ GET sh.armEmul GET sh.driver GET sh.hammer ;----- Main code ------------------------------------------------------------ AREA |Hammer$$Code|,CODE,READONLY ; --- brkpt_set --- ; ; On entry: R0 == address to set breakpoint ; ; On exit: May return error ; ; Use: Sets a breakpoint at the given location, giving it the ; current handle. The address is assumed to be word-aligned, ; and writable. EXPORT brkpt_set brkpt_set ROUT STMFD R13!,{R0-R5,R14} ;Save some registers MOV R5,R0 ;Look after the address LDR R12,brkpt__wSpace ;Find my workspace address ; --- Check for an exisiting break point --- BL brkpt__findBlock ;Try to find it ADRCS R0,brkpt__exists ;Already one there? BCS %90brkpt_set ;Yes -- return with error ; --- Allocate the breakpoint block --- MOV R0,#6 ;Allocate from the RMA MOV R3,#brkpt__size ;Get the size of the block SWI XOS_Module ;Allocate the block nicely BVS %90brkpt_set ;If it failed, return error ; --- Fill in the breakpoint block --- LDR R0,brkpt__current ;Load the current handle ADR R14,brkpt__table ;Point to the main table LDR R1,[R14,R0,LSL #2] ;Load the correct list head STR R1,[R2,#brkpt__next] ;Store in the breakpoint blk STR R2,[R14,R0,LSL #2] ;And fill in the head again STR R5,[R2,#brkpt__address] ;Save the address there too LDR R1,[R5,#0] ;Load the old contents STR R1,[R2,#brkpt__old] ;Store this away for later ; --- Build the breakpoint entry code --- ADR R14,brkpt__entry ;Point to the basic entry LDMIA R14,{R0,R1} ;Load them into registers ADR R3,brkpt__handler ;Point to the main handler ADD R14,R2,#brkpt__code+16 ;Find the branch instruction SUB R3,R3,R14 ;Subtract the offset MOV R3,R3,LSR #2 ;Shift right two places BIC R3,R3,#&FF000000 ;Clear the opcode byte ORR R3,R3,#&EA000000 ;Make it a branch instruction ADD R14,R2,#brkpt__code ;Point to the actual code STMIA R14,{R0,R1,R3} ;Build the code nicely ; --- Now insert the breakpoint --- SUB R0,R2,R5 ;Find offset from code to blk ADD R0,R0,#brkpt__code-8 ;Find the code, handle pipe MOV R0,R0,LSR #2 ;Shift right two places BIC R0,R0,#&FF000000 ;Clear the opcode byte ORR R0,R0,#&EA000000 ;Make it a branch instruction STR R0,[R5,#0] ;Store it away nicely LDMFD R13!,{R0-R5,R14} ;Unstack registers BICS PC,R14,#V_flag ;And return no errors brkpt__entry STR R14,{PC}-brkpt__code+brkpt__R14 ADR R14,{PC}-brkpt__code-4 ; --- It went amiss --- 90brkpt_set ADD R13,R13,#4 ;Don't restore R0 on exit LDMFD R13!,{R1-R5,R14} ;Unstack registers ORRS PC,R14,#V_flag ;And return the error brkpt__exists DCD 1 DCB "There is already a breakpoint at this address",0 LTORG ; --- brkpt__setViaStar --- ; ; On entry: R0 == Pointer to command tail ; R1 == number of parameters ; ; On exit: -- ; ; Use: Sets a break point at the desired address. This call ; is made via a * command brkpt__setViaStar ROUT STMFD R13!,{R0-R2,R14} ;Save some registers MOV R1,R0 ;Point to the string MOV R0,#16 ;Default base to read SWI XOS_ReadUnsigned ;Read the address BVS %90brkpt__setViaStar ;Barf on error MOV R0,R2 ;Put address in R0 BL brkpt_set ;Set the breakpoint LDMVCFD R13!,{R0-R2,PC}^ ;Return if no error 90 ADD R13,R13,#4 ;Skip over R0 LDMFD R13!,{R1-R2,R14} ;Get the registers back ORRS PC,R14,#V_flag ;And return with error LTORG brkpt__setHelp DCB "*SB sets up a Sledgehammer " DCB "breakpoint at the given address.",13 brkpt__setSyn DCB "Syntax: *SB
",0 ; --- brkpt__handler --- ; ; On entry: R14 == pointer to the breakpint block ; ; On exit: -- ; ; Use: Called by a breakpoint block, to stop execution brkpt__handler STR R12,brkpt__tmp ;Save R12 -- we need it LDR R12,brkpt__wSpace ;Load my workspace address ADR R12,brkpt__regs ;Find the register save block STMIA R12,{R0-R11} ;Save most of his registers STR R13,[R12,#13*4] ;Also save his R13 LDR R0,[R14,#brkpt__R14] ;Find the R14 the bp saved STR R0,[R12,#14*4] ;Write it to the reg block LDR R0,brkpt__tmp ;Get the R12 I saved earlier STR R0,[R12,#12*4] ;Write that to the reg block MOV R0,PC ;Get the PC value AND R0,R0,#&FC000003 ;Extract the flags LDR R1,[R14,#brkpt__address] ;Find the bp's address ORR R0,R0,R1 ;Mix 'em to find his PC STR R0,[R12,#15*4] ;Store as his R15 MOV R0,R12 B driver brkpt__tmp DCD 4 ; --- brkpt__hbHandler --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Stops execution after a SWI Sledgehammer_BreakPoint. brkpt__hbHandler ROUT STR R12,brkpt__tmp ;Save R12 -- we need it LDR R12,brkpt__wSpace ;Load my workspace address ADR R12,brkpt__regs ;Find the register save block STMIA R12,{R0-R11} ;Save most of his registers STR R13,[R12,#13*4] ;Also save his R13 STR R14,[R12,#14*4] ;And his R14 LDR R0,brkpt__tmp ;Get the R12 I saved earlier STR R0,[R12,#12*4] ;Write that to the reg block MOV R0,R12 B driver ROUT ; --- brkpt_hardBreak --- ; ; On entry: [R13+4] == return address to stomp on ; ; On exit: -- ; ; Use: Sets an immediate break specified by a SWI call. EXPORT brkpt_hardBreak brkpt_hardBreak ROUT MOV R11,R13 ;Keep stack pointer save LDR R12,brkpt__wSpace ;Find my workspace address LDR R10,[R11,#4] ;Load the return address ADR R12,brkpt__regs ;Point to the regs buffer STR R10,[R12,#15*4] ;And as the PC value AND R10,R10,#&FC000003 ;Get his processor flags ADR R12,brkpt__hbHandler ;Point to handler routine ORR R10,R10,R12 ;Mix them up nicely STR R10,[R11,#4] ;Save over return address MOVS PC,R14 ;And return normally LTORG ; --- brkpt__findBlock --- ; ; On entry: R0 == address of breakpoint ; ; On exit: CS and R0 == address of breakpoint block ; R1 == address of previous next pointer ; CC and R1 corrupted otherwise ; ; Use: A little obvious, I feel! brkpt__findBlock ROUT STMFD R13!,{R2-R5,R12} ;Stack some registers LDR R12,brkpt__wSpace ;Locate my workspace MOV R5,#brkpt__handles ;A counter thing ADR R4,brkpt__table ;Point to the table MOV R3,R0 ;Remember this value ; --- The main loop --- 00 LDR R0,[R4],#4 ;Load the list head SUB R1,R4,#4 ;Get address of previous next CMP R0,#0 ;Is this the end? BEQ %20brkpt__findBlock ;Yes -- skip onwards then 10 LDR R2,[R0,#brkpt__address] ;And find its address CMP R2,R3 ;Are these values the same LDMEQFD R13!,{R2-R5,R12} ;Load back my registers ORREQS PC,R14,#C_flag ;Return with C set ADD R1,R0,#brkpt__next ;This is now prev next addr LDR R0,[R0,#brkpt__next] ;Load the next pointer CMP R0,#0 ;Have we finished yet? BNE %10brkpt__findBlock ;No -- keep looking 20 SUBS R5,R5,#1 ;Decrement the counter BGT %00brkpt__findBlock ;If more to do, loop MOV R0,R3 ;Put R0 back again LDMFD R13!,{R2-R5,R12} ;Load back my registers BICS PC,R14,#C_flag ;Return with C clear LTORG ; --- brkpt_translate --- ; ; On entry: R0 == address to translate ; ; On exit: R0 == the address we are really interested in ; ; Use: Returns the address given, as if there was no breakpoint ; there. EXPORT brkpt_translate brkpt_translate ROUT STMFD R13!,{R1,R14} ;Stack registers BL brkpt__findBlock ;Find the block ADDCS R0,R0,#brkpt__old ;Found it -- add happily LDMFD R13!,{R1,PC}^ ;Return to caller LTORG ; --- brkpt_remove --- ; ; On entry: R0 == address of breakpoint ; ; On exit: -- ; ; Use: Removes the break point (if any) at the given address EXPORT brkpt_remove brkpt_remove ROUT STMFD R13!,{R0-R2,R14} ;Stack registers BL brkpt__findBlock ;Find the block BCC %99brkpt_remove ;Found it -- add happily LDR R14,[R0,#brkpt__next] ;Load the next pointer STR R14,[R1,#0] ;Store over previous addr LDR R14,[R0,#brkpt__old] ;Get the old value out LDR R2,[R0,#brkpt__address] ;And find its address STR R14,[R2,#0] ;Store the value back MOV R2,R0 ;Point to block in R2 MOV R0,#7 ;Free the block SWI XOS_Module ;Yes siree bob matey 99brkpt_remove LDMFD R13!,{R0-R2,PC}^ ;Return to caller LTORG ; --- brkpt_remAll --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Removes all the breakpoints currently installed. EXPORT brkpt_remAll brkpt_remAll ROUT STMFD R13!,{R0-R5,R12,R14} ;Save some registers LDR R12,brkpt__wSpace ;Find my workspace address MOV R5,#brkpt__handles ;A counter thing ADR R4,brkpt__table ;Point to the table ; --- The main loop --- 00brkpt_remAll LDR R2,[R4],#4 ;Load the list head CMP R2,#0 ;Is this the end? BEQ %20brkpt_remAll ;Yes -- skip onwards then 05brkpt_remAll LDR R14,[R2,#brkpt__old] ;Get the old value out LDR R0,[R2,#brkpt__address] ;And find its address STR R14,[R0,#0] ;Store the value back LDR R3,[R2,#brkpt__next] ;Load the next pointer MOV R0,#7 ;Free the RMA block SWI XOS_Module ;Yes, really do it MOVS R2,R3 ;Move on to the next one BNE %05brkpt_remAll ;If more to do, loop 20brkpt_remAll MOV R14,#0 ;Zero the breakpoint list hdr STR R14,[R4,#-4] ;Store over the list head SUBS R5,R5,#1 ;Decrement the counter BGT %00brkpt_remAll ;If more to do, loop LDMFD R13!,{R0-R5,R12,PC}^ ;Return to caller LTORG ; --- brkpt__remViaStar --- ; ; On entry: R0 == Pointer to command tail ; R1 == number of parameters ; ; On exit: -- ; ; Use: Removes the break point at the given address. This call ; is made via a * command brkpt__remViaStar ROUT CMP R1,#0 ;Has he ommitted the args? BEQ %50brkpt__remViaStar ;Yes -- maybe we get 'em all LDRB R2,[R0,#0] ;Get the first character CMP R2,#'Y' ;Is it a 'Y'? CMPNE R2,#'y' ;Check both cases BEQ brkpt_remAll ;Yes -- remove all then STMFD R13!,{R0-R2,R14} ;Save some registers MOV R1,R0 ;Point to the string MOV R0,#16 ;Default base to read SWI XOS_ReadUnsigned ;Read the address BVS %90brkpt__remViaStar ;Barf on error MOV R0,R2 ;Put address in R0 BL brkpt_remove ;Set the breakpoint LDMVCFD R13!,{R0-R2,PC}^ ;Return if no error 90 ADD R13,R13,#4 ;Skip over R0 LDMFD R13!,{R1-R2,R14} ;Get the registers back ORRS PC,R14,#V_flag ;And return with error ; --- Maybe remove all breakpoints --- 50 STMFD R13!,{R0,R14} ;Save some registers ADR R0,brkpt__rmConf ;Point to confirm message BL hammer_confirm ;Are we sure about this? BLCS brkpt_remAll ;Yes -- do it then LDMFD R13!,{R0,PC}^ ;Return to caller brkpt__rmConf DCB "Remove all breakpoints?",0 LTORG brkpt__remHelp DCB "*RB removes any Sledgehammer " DCB "breakpoint set at that address. If the address " DCB "is omitted, all breakpoints are removed.",13 brkpt__remSyn DCB "Syntax: *RB [
]",0 ; --- brkpt_exist --- ; ; On entry: R0 == address to test for ; ; On exit: CC if there is no breakpoint here ; CS otherwise ; ; Use: Informs the user if there is a breakpint at the address ; passed. EXPORT brkpt_exist brkpt_exist ROUT STMFD R13!,{R0,R1,R14} ;Stack some registers BL brkpt__findBlock ;Find the block thingy LDMFD R13!,{R0,R1,PC} ;Return all happy ; --- brkpt__init --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Initialises the breakpoint manager. brkpt__init ROUT STMFD R13!,{R0-R9,R14} ;Save a load of registers MOV R0,#bpFlag__inited MOV R1,#0 MOV R2,#0 MOV R3,#0 MOV R4,#0 MOV R5,#0 MOV R6,#0 MOV R7,#0 MOV R8,#0 MOV R9,#0 STMIA R12,{R0-R9} ;Fill the workspace LDMFD R13!,{R0-R9,PC}^ ;Return to caller LTORG ; --- brkpt__die --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Tidies away all installed breakpoints. brkpt__die ROUT B brkpt_remAll ;Remove all the breakpoints LTORG brkpt__wSpace DCD 0 ;----- Constants ------------------------------------------------------------ brkpt__handles EQU 8 ;----- Data structures ------------------------------------------------------ ^ 0 brkpt__next # 4 ;Link to next breakpoint brkpt__address # 4 ;Where the breakpoint `is' brkpt__old # 4 ;The previous value brkpt__R14 # 4 ;User's R14 value on entry brkpt__code # 12 ;The breakpoint entry code brkpt__size # 0 ;Size of a breakpoint block ;----- Workspace ------------------------------------------------------------ ^ 0,R12 brkpt__wStart # 0 brkpt__flags # 4 ;Various flags brkpt__current # 4 ;Current breakpoint handle brkpt__table # brkpt__handles*4 ;The links for the bp lists brkpt__regs # 16*4 ;Register block for brkpt brkpt__wSize EQU {VAR}-brkpt__wStart bpFlag__inited EQU (1<<0) AREA |Quartz$$Table|,CODE,READONLY DCD brkpt__wSize DCD brkpt__wSpace DCD brkpt__init DCD brkpt__die AREA |Quartz$$Commands|,CODE,READONLY DCB "SB",0 DCD brkpt__setViaStar DCD &00010001 DCD brkpt__setSyn DCD brkpt__setHelp DCB "RB",0 DCD brkpt__remViaStar DCD &00010000 DCD brkpt__remSyn DCD brkpt__remHelp ;----- That's all, folks ---------------------------------------------------- END