; ; driver.s ; ; The text-only interface to Sledgehammer (TMA) ; ; © 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. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis GET libs:stream ;----- External dependencies ------------------------------------------------ GET quartz:string GET sh.brkpt GET sh.armEmul GET sh.asm GET sh.diss GET sh.hammer ;----- Main code ------------------------------------------------------------ AREA |Hammer$$Code|,CODE,READONLY ; --- driver --- ; ; On entry: R0 == pointer to a register block ; ; On exit: Doesn't ; ; Use: Text only interactive debugger interface EXPORT driver driver ROUT BL hammer_getStack ;Get our own pretty stack MOV R3,R0 ;Preserve block pointer MOV R10,R13 ;Remember stack base address MOV R14,PC ;Get PC with flags TST R14,#3 ;Are we in user mode? LDRNE R13,[R3,#13*4] ;No -- use his stack pointer driver__next LDR R0,[R3,#15*4] ;Get the 'PC' BIC R0,R0,#&FC000003 ;Clear flags BL diss_address ;Set this as the diss address BL brkpt_translate ;Translate the address LDR R0,[R0,#0] ;Load the instruction BL diss_disassemble ;Disassemble the instruction SWI XOS_NewLine ;Print a newline SWI XOS_Write0 ;Print it out nicely SWI XOS_NewLine ;Print a newline 10driver SWI XOS_WriteS ;Print out the following DCB 10,"Sledgehammer [stwx rad bke nzcv *oh]",0 MOV R0,#229 ;Set escape action MOV R1,#1 ;Just generate key code MOV R2,#0 ;Set the state SWI XOS_Byte ;Do it 15driver SWI XOS_ReadC ;Read a character CMP R0,#27 ;Was it escape? MOVEQ R0,#'t' ;Yes -- make it continue CMP R0,#32 ;How about space MOVEQ R0,#'s' ;Yeap -- make it step ORR R0,R0,#&20 ;Make it lower case ADR R2,driver__table ;Point to the table 20driver LDR R4,[R2],#8 ;Load the byte CMP R4,#0 ;Was there one? BEQ %15driver ;Nope -- keep trying CMP R4,R0 ;Was this what he typed? BNE %20driver ;No -- keep on trying SWI XOS_WriteI+32 ;Print a space SWI XOS_WriteC ;Print out the character SWI XOS_NewLine ;Let's be pretty about this SWI XOS_NewLine SUB PC,R2,#4 ;Do the instruction driver__table DCD 's' B driver__step DCD 't' B driver__cont DCD 'w' B driver__soft DCD 'x' SWI OS_BreakPt DCD 'r' B driver__regDump DCD 'a' B driver__next;driver__alter DCD 'd' B driver__diss DCD 'b' B driver__break DCD 'k' B driver__killBrk DCD 'e' B driver__killAll DCD 'n' B driver__toggleN DCD 'z' B driver__toggleZ DCD 'c' B driver__toggleC DCD 'v' B driver__toggleV DCD '*' B driver__oscli DCD 'o' B driver__next;driver__options DCD 'h' B driver__help DCD 0 ; --- Single Step --- driver__step MOV R0,#229 ;Set escape action MOV R2,#0 ;Set the state to what it was SWI XOS_Byte ;Do it MOV R13,R10 ;Use our private stack MOV R0,R3 ;Point to register block BL armEmul ;Emulate an instruction MOV R14,PC ;Get PC with flags TST R14,#3 ;Are we in user mode? LDRNE R13,[R3,#13*4] ;No -- use his stack pointer B driver__next ;Do the next one ; --- Continue --- driver__cont MOV R0,#229 ;Set escape action MOV R2,#0 ;Set the state to what it was SWI XOS_Byte ;Do it LDR R0,[R3,#15*4] ;Get the current PC BIC R0,R0,#&FC000003 ;Get rid of flags BL brkpt_exist ;Is there a breakpoint here? LDMCCIA R3,{R0-PC}^ ;No -- continue his execution MOV R13,R10 ;Use our private stack MOV R0,R3 ;Point to register block BL armEmul ;Emulate an instruction LDMIA R3,{R0-PC}^ ;Continue pleasently ; --- Soft emulation --- driver__soft ROUT MOV R0,#229 ;Set escape action MOV R2,#0 ;Set the state to what it was SWI XOS_Byte ;Do it MOV R13,R10 ;Yes -- Use our private stack 00driver__soft MOV R0,R3 ;...point to register block BL armEmul ;...emulate an instruction LDR R0,[R3,#15*4] ;Get the current PC BIC R0,R0,#&FC000003 ;Get rid of flags BL brkpt_exist ;Is there a breakpoint here? BCC %00driver__soft ;No -- keep looping MOV R14,PC ;Get PC with flags TST R14,#3 ;Are we in user mode? LDRNE R13,[R3,#13*4] ;No -- use his stack pointer B driver__next ;And tell user about it LTORG ; --- Help display --- driver__help ADR R0,driver__helpText ;Point to my help text SWI XOS_PrettyPrint ;Display it on the screen B driver__next ;Rejoin the main loop driver__helpText DCB "Commands available:",13 DCB 13 DCB "[S]ingle step (also SPACE)",13 DCB "con[T]inue (also ESCAPE)",13 DCB "[W]hisper mode",13 DCB "e[X]it application",13 DCB "[R]egister list",13 DCB "[A]lter register value",13 DCB "[D]isassemble around current PC",13 DCB "[B]reakpoint set",13 DCB "[K]ill breakpoint",13 DCB "toggle [N,Z,C,V] flag",13 DCB "[E]xterminate all breakpoints",13 DCB "[*] commands",13 DCB "[O]ptions",13 DCB "[H]elp",13 DCB 0 ; --- Set a breakpoint --- driver__break BL driver__readAddr ;Read the address he typed BVS driver__error ;If he spasmed, return BLCS brkpt_set ;Try to set the breakpoint BVS driver__error ;If he spasmed, return B driver__next ;And rejoin the main loop ; --- Remove a breakpoint --- driver__killBrk BL driver__readAddr ;Read the address he typed BVS driver__error ;If he spasmed, return BLCS brkpt_remove ;Try to set the breakpoint BVS driver__error ;If he spasmed, return B driver__next ;And rejoin the main loop ; --- Remove all breakpoints --- driver__killAll ADR R0,driver__kaMsg ;Point to message string BL hammer_confirm ;Get confirmation on this BLCS brkpt_remAll ;If OK, remove all of them B driver__next ;And rejoin the main loop driver__kaMsg DCB "Remove all breakpoints?",0 driver__error ADD R0,R0,#4 ;Point to the error SWI XOS_Write0 ;Display the message SWI XOS_NewLine ;Move to new line B driver__next ;And rejoin the main loop ; --- * commands --- driver__oscli ROUT STMFD R13!,{R0-R5} ;Save some registers BL str_buffer ;Get me a nice buffer MOV R5,R1 ;Look after the buffer 00driver__oscli SWI XOS_WriteS ;Write a prompt string DCB "Sledgehammer *",0 MOV R0,R5 ;Point to it nicely MOV R1,#256 ;Give the buffer size MOV R2,#32 ;Allow all printable chars MOV R3,#255 SWI XOS_ReadLine ;Read a command line BVS %10driver__oscli ;Failed -- report error LDRB R1,[R5,#0] ;Load the first character CMP R1,#13 ;Is the string empty? BEQ %20driver__oscli ;Yes -- return then MOV R0,R5 ;Point to the buffer SWI XOS_CLI ;Do the command nicely BVS %10driver__oscli ;Failed -- report error B %00driver__oscli ;Go round for some more 10driver__oscli ADD R0,R0,#4 ;Point to error message SWI XOS_Write0 ;Display it B %00driver__oscli ;Go round for some more 20driver__oscli LDMFD R13!,{R0-R5} ;Unstack registers B driver__next ;And rejoin the main loop ; --- Toggle flags --- driver__toggleN MOV R4,#N_flag ;Get the flag bit MOV R5,#'N' ;And the name B driver__toggle ;Do the toggle op driver__toggleZ MOV R4,#Z_flag ;Get the flag bit MOV R5,#'Z' ;And the name B driver__toggle ;Do the toggle op driver__toggleC MOV R4,#C_flag ;Get the flag bit MOV R5,#'C' ;And the name B driver__toggle ;Do the toggle op driver__toggleV MOV R4,#V_flag ;Get the flag bit MOV R5,#'V' ;And the name B driver__toggle ;Do the toggle op driver__toggle LDR R14,[R3,#15*4] ;Load current flags EOR R14,R14,R4 ;Toggle the flag STR R14,[R3,#15*4] ;Save them back again TST R14,R4 ;Is it on or off now? MOV R0,R5 ;Get the flag's name SWI XOS_WriteC ;Display it SWI XOS_WriteS ;Display some text DCB " flag now ",0 ADREQ R0,driver__flagOff ;Point to the right string ADRNE R0,driver__flagOn ;Whichever one that is SWI XOS_Write0 ;And display it SWI XOS_NewLine ;Follow with a newline B driver__next ;And rejoin main loop driver__flagOff DCB "off.",0 driver__flagOn DCB "on.",0 ; --- Disassemble context --- driver__diss ROUT LDR R4,[R3,#15*4] ;Load current program count BIC R4,R4,#&FC000003 ;Clear PSR flags BL driver__readAddr ;Get an address to display BVS driver__error ;If he goofed, report error MOVCC R0,R4 ;If none, use the PC SUB R5,R0,#40 ;Do 10 instrs each side MOV R6,#21 ;That's 21 instructions total MOV R0,R5 ;Get the disassembly base BL diss_address ;Set disassembly up nicely 00driver__diss MOV R0,R5 ;Get the disassembly address BL brkpt_exist ;Is there a breakpoint there? SWICS XOS_WriteI+"*" ;Yes -- put a splodge there SWICC XOS_WriteI+" " ;Otherwise leave a gap CMP R0,R4 ;Is this the current one? SWIEQ XOS_WriteI+">" ;Yes -- mark it somehow SWINE XOS_WriteI+" " ;Otherwise leave a space SWI XOS_WriteI+" " ;Gap before disassembly BL brkpt_translate ;Translate the address LDR R0,[R0,#0] ;Load the instruction BL diss_disassemble ;Disassemble it SWI XOS_Write0 ;Display the result SWI XOS_NewLine ;And move down a line SUBS R6,R6,#1 ;Decrement the counter ADDGE R5,R5,#4 ;If more to go, bump address BGE %00driver__diss ;And go round again B driver__next ;Rejoin the main loop LTORG ; --- Register dump --- driver__regDump ROUT STMFD R13!,{R0-R12} ;Save some registers MOV R0,#229 ;Set escape action MOV R2,#0 ;Set the state to what it was SWI XOS_Byte ;Do it ; --- Start the main display loop --- MOV R12,R3 ;Keep pointer to dump block ADR R11,driver__regNames ;Point to register name tbl MOV R10,#0 ;Which register we're on ; --- Display a register --- 00 ADD R0,R11,R10,LSL #2 ;Point to the string SWI OS_Write0 ;Display register name SWI OS_WriteS ;Display immediate string DCB " == &",0 ;A separater string LDR R0,[R12,R10,LSL #2] ;Load the register value BL driver__writeHex ;Display register value SWI OS_WriteS ;Display more immediate DCB " ",0 ;Just some spaces ADD R10,R10,#1 ;Increment register count TST R10,#3 ;Now a multiple of 4? SWIEQ OS_NewLine ;Yes -- new line then CMP R10,#16 ;Finished all registers? BLT %00driver__regDump ;No -- do some more then ; --- Now display R14 and R15 with PSR bits testually --- SWI OS_NewLine ;Another newline SWI OS_WriteS ;Display immediate stuff DCB "R14 == &",0 LDR R9,[R12,#14*4] ;Load the R14 value BL driver__psr ;Display all the PSR bits SWI OS_NewLine ;Another newline SWI OS_WriteS ;Display immediate stuff DCB " PC == &",0 LDR R9,[R12,#15*4] ;Load the PC value BL driver__psr ;Display all the PSR bits SWI OS_NewLine ;Another newline SWI OS_NewLine ;And one more for luck LDMFD R13!,{R0-R12} ;Get register back B driver__next ;Get the next command LTORG driver__regNames DCB " R0",0 DCB " R1",0 DCB " R2",0 DCB " R3",0 DCB " R4",0 DCB " R5",0 DCB " R6",0 DCB " R7",0 DCB " R8",0 DCB " R9",0 DCB "R10",0 DCB "R11",0 DCB "R12",0 DCB "R13",0 DCB "R14",0 DCB " PC",0 ; --- driver__psr --- ; ; On entry: R9 == value to display ; ; On exit: R0-R12 corrupted, maybe ; ; Use: Displays R9 with PSR bits stripped away, and then with ; all the PSR bits described too. driver__psr ROUT STMFD R13!,{R14} ;Save the link register BIC R0,R9,#&FC000003 ;Get the PC bits only BL driver__writeHex ;Display them in hex SWI OS_WriteS ;Some more spaces DCB ", flags == ",0 MOV R0,#'N' ;First do the `N' flag TST R9,#N_flag ;Is it set ORREQ R0,R0,#&20 ;No -- force to lower case SWI OS_WriteC ;Display the character MOV R0,#'Z' ;First do the `N' flag TST R9,#Z_flag ;Is it set ORREQ R0,R0,#&20 ;No -- force to lower case SWI OS_WriteC ;Display the character MOV R0,#'C' ;First do the `N' flag TST R9,#C_flag ;Is it set ORREQ R0,R0,#&20 ;No -- force to lower case SWI OS_WriteC ;Display the character MOV R0,#'V' ;First do the `N' flag TST R9,#V_flag ;Is it set ORREQ R0,R0,#&20 ;No -- force to lower case SWI OS_WriteC ;Display the character SWI OS_WriteS ;Yet more stuff DCB ", mode == ",0 ADR R0,driver__modes ;Point to the mode strings AND R14,R9,#3 ;Get the mode bits ADD R0,R0,R14,LSL #2 ;Point to the correct string SWI OS_Write0 ;Display the mode setting TST R9,#IRQ_disable ;Is the IRQ bit on or off? ADREQ R0,driver__irqOn ;If off, point to message SWIEQ OS_Write0 ;And display the string TST R9,#FIQ_disable ;Is the FIQ bit on or off? ADREQ R0,driver__fiqOn ;If off, point to message SWIEQ OS_Write0 ;And display the string LDMFD R13!,{PC}^ ;Return to caller LTORG driver__modes DCB "USR",0 DCB "FIQ",0 DCB "IRQ",0 DCB "SVC",0 driver__irqOn DCB ", IRQ",0 driver__fiqOn DCB ", FIQ",0 driver__writeHex ROUT STMFD R13!,{R0-R2,R14} SUB R13,R13,#16 MOV R1,R13 MOV R2,#16 SWI OS_ConvertHex8 SWI OS_Write0 ADD R13,R13,#16 LDMFD R13!,{R0-R2,PC}^ LTORG ; --- driver__readAddr --- ; ; On entry: -- ; ; On exit: Error, or CC for no entry, or R0 == address ; ; Use: Interprets an address as typed by the user. driver__readAddr ROUT BIC R14,R14,#V_flag ;Assume all is well STMFD R13!,{R1-R5,R8,R14} ;Save some registers SWI XOS_WriteS ;Display the prompt DCB "([] [+|- ]) |
:",0 SUB R13,R13,#20 ;Make a small buffer MOV R5,R3 ;Look after register block MOV R0,R13 ;Point to my buffer MOV R1,#20 ;Give it the buffer size MOV R2,#32 ;Minimum value to allow MOV R3,#255 ;Allow all printable chars SWI XOS_ReadLine ;Read the line then BVS %90driver__readAddr ;If he messed up, report err MOV R3,R5 ;Restore register block ptr MOV R8,R13 ;Point to the string MOV R5,#0 ;Initial address offset MOV R4,#0 ;Clear some flags BL asm_register ;Try to read a register name LDRVC R5,[R3,R0,LSL #2] ;If OK get register value BICVC R5,R5,#&FC000003 ;And clear possible PSR flags ORRVC R4,R4,#3 ;We have reg/We read sthing 00 LDRB R14,[R8],#1 ;Load a byte from the string CMP R14,#' ' ;Is it a space BEQ %00driver__readAddr ;Yes -- go for another CMP R14,#32 ;Is this the string end? BLO %10driver__readAddr ;Yes -- weird things happened CMP R14,#'-' ;Is it a '-'? ORREQ R4,R4,#4 ;Yes -- subtract offset then CMPNE R14,#'+' ;Or a '+'? ORREQ R4,R4,#1 ;This is something typed BNE %05driver__readAddr ;No -- skip TST R4,#2 ;Do we have a base reg? LDREQ R5,[R3,#15*4] ;No -- use the PC then BICEQ R5,R5,#&FC000003 ;And clear possible PSR flags MOV R1,R8 ;Point to the string MOV R0,#10 ;Expect a decimal number SWI XOS_ReadUnsigned ;Try to understand the value BVS %90driver__readAddr ;If failed, report error TST R4,#4 ;Was it a subtract? SUBNE R5,R5,R2 ;Yes -- subtract offset ADDEQ R5,R5,R2 ;Otherwise add it B %10driver__readAddr ;And branch ahead 05 TST R4,#2 ;Was there a register? BNE %10driver__readAddr ;Yes -- almost done then SUB R1,R8,#1 ;Point to the string MOV R0,#16 ;Expect a hex number SWI XOS_ReadUnsigned ;Try to understand the value BVS %90driver__readAddr ;If failed, report error MOV R5,R2 ;Get the address he typed ORR R4,R4,#1 ;He typed something 10 TST R4,#1 ;Was there anything at all? BEQ %80driver__readAddr ;No -- return C clear then BIC R0,R5,#3 ;Truncate to word boundary ADD R13,R13,#20 ;Restore the stack LDMFD R13!,{R1-R5,R8,R14} ;Unstack registers ORRS PC,R14,#C_flag ;And return with C proudly on ; --- Lazy user typed nothing --- 80 ADD R13,R13,#20 ;Restore the stack LDMFD R13!,{R1-R5,R8,R14} ;Unstack registers BICS PC,R14,#C_flag ;And return with C sadly off ; --- Stupid user caused an error --- 90 ADD R13,R13,#20 ;Restore the stack LDMFD R13!,{R1-R5,R8,R14} ;Unstack registers ORRS PC,R14,#V_flag ;So set V on exit LTORG ;----- That's all, folks ---------------------------------------------------- END