; ; diss.s ; ; Superior Disassembly of ARM instructions (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. ;----- Things to do --------------------------------------------------------- ; ; Disassemble Floating point instructions ; Deal with labels ; Recognise XRel's ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis GET libs:stream ;----- External dependencies ------------------------------------------------ GET quartz:quartz GET quartz:string ;----- Main code ------------------------------------------------------------ AREA |Hammer$$Code|,CODE,READONLY ; --- diss_setOptions --- ; ; On entry: R0 == new options field ; ; On exit: -- ; ; Use: Sets up the new display options for the disassembly EXPORT diss_setOptions diss_setOptions ROUT STMFD R13!,{R12} ;Stack registers LDR R12,diss__wSpace ;Locate my workspace STR R0,ws__options ;Save the new options LDMFD R13!,{R12} ;Reclaim stack MOVS PC,R14 ; --- diss_address --- ; ; On entry: R0 == address to start disassembly ; ; On exit: -- ; ; Use: Initialises the disassembly to start at the given address EXPORT diss_address diss_address ROUT STMFD R13!,{R12,R14} ;Save some registers LDR R12,diss__wSpace ;Find my workspace address LDR R14,ws__flags ;Load the flags word AND R14,R14,#wFlag__inited ;Clear most of the flags STR R14,ws__flags ;Save the flags back again STR R0,ws__nextAddr ;Store as next address LDMFD R13!,{R12,PC}^ ;Return to caller LTORG ; --- diss_disassemble --- ; ; On entry: R0 == the instruction itself ; ; On exit: R0 == pointer to buffer containing disassembly of ; instruction ; ; Use: Disassembles the instruction given, and places ; the resulting string in the returned buffer. EXPORT diss_disassemble diss_disassemble ROUT STMFD R13!,{R1-R12,R14} ;Stack lots of registers LDR R12,diss__wSpace ;Locate my workspace LDR R9,ws__nextAddr ;Get the address to use MOV R8,R0 ;And the contents in R8 ; --- We don't have any comments yet --- MOV R2,#0 ;A nice NULL byte STR R2,ws__comment ;Zero the comment ADR R2,ws__comment ;Point to the comment STR R2,ws__commentEnd ;Start comment from here ; --- Sort out the flags --- LDR R14,ws__flags ;Load the flags TST R14,#wFlag__newAdr ;Was it a new ADR? BICNE R14,R14,#wFlag__newAdr ;Yes -- clear the flag BICEQ R14,R14,#wFlag__wasAdr ;Otherwise clear was ADR flag STR R14,ws__flags ;Save the flags back again ; --- First do the address --- MOV R0,R9 ;Put the address in R0 ADR R7,ws__buffer ;Point to string buffer ADR R1,ws__buffer ;Point to string buffer MOV R2,#16 ;The buffer size SWI OS_ConvertHex8 ;Convert the address MOV R3,#' ' ;A nice space STRB R3,[R1],#1 ;Put it in the buffer ; --- Display the label --- MOV R4,#20 ;Enter 20 spaces 00 STRB R3,[R1],#1 ;Store the space SUBS R4,R4,#1 ;Decrement the counter BNE %00diss_disassemble ;Keep on looping MOV R3,#' ' ;A nice space STRB R3,[R1],#1 ;Put it in the buffer ; --- Display the word --- MOV R0,R8 ;Load the word into R0 MOV R2,#16 ;The buffer size SWI OS_ConvertHex8 ;Convert the address MOV R3,#' ' ;A nice space STRB R3,[R1],#1 ;Put it in the buffer ; --- Display the ASCII field --- MOV R0,R8 ;Get the instruction BL diss__showChar ;Show a character MOV R0,R0,LSR#8 ;Get nextww byte BL diss__showChar ;Show a character MOV R0,R0,LSR#8 ;Get next byte BL diss__showChar ;Show a character MOV R0,R0,LSR#8 ;Get next byte BL diss__showChar ;Show a character MOV R3,#' ' ;A nice space STRB R3,[R1],#1 ;Put it in the buffer ; --- Disassemble the instruction --- AND R0,R8,#&0F000000 ;Get just the opcode MOV R0,R0,LSR#26 ;Get top two bits of opcode MOV R14,PC ;Set up return address ADD PC,PC,R0,LSL#2 ;Branch to correct routine B %90diss_disassemble ;Branch ahead B diss__type00 B diss__type01 B diss__type10 B diss__type11 ; --- Return to caller --- 90 LDRB R14,ws__comment ;Is there a comment? CMP R14,#0 ;Look and see BEQ %95diss_disassemble ;No -- jump ahead MOV R3,#68 ;Set up the tab BL diss__tab ;And do it MOV R0,R1 ;Point to the buffer end ADR R1,ws__comment ;Point at the comment BL str_cpy ;Copy it over 95 ADD R9,R9,#4 ;Set up the next address STR R9,ws__nextAddr ;And store it away ADR R0,ws__buffer ;Point to the buffer MOV R2,#0 ;A nice NULL value STRB R2,[R1] ;Terminate the string LDMFD R13!,{R1-R12,PC}^ ;Return to caller LTORG ; --- diss__showChar --- ; ; On entry: R0 == character to display ; R1 == address to insert character ; ; On exit: -- ; ; Use: Inserts the character in the bottom 8 bits of R0 into ; the buffer pointed to by R1 diss__showChar ROUT STMFD R13!,{R2-R3} ;Stack some registers AND R2,R0,#&FF ;Get LSB CMP R2,#127 ;Is it the delete char? CMPNE R2,#31 ;..or a control character MOVLE R2,#'.' ;Yes -- make it a dot STRB R2,[R1],#1 ;Store char in the buffer LDMFD R13!,{R2-R3} ;Get registers back MOVS PC,R14 ;Return to caller ; --- diss__addComment --- ; ; On entry: R0 == pointer to comment to add ; ; On exit: R0 corrupted ; ; Use: Adds the given comment to the current one diss__addComment ROUT STMFD R13!,{R1,R14} ;Stack some registers MOV R1,R0 ;Copying from here LDR R0,ws__commentEnd ;Point to the comment end ADR R14,ws__comment ;Get the beginning CMP R14,R0 ;Are they the same? MOVEQ R14,#';' ;Yes -- get a semicolon STREQB R14,[R0],#1 ;And put it at beginning BL str_cpy ;Copy the string over STR R0,ws__commentEnd ;This is now comment end LDMFD R13!,{R1,PC}^ ;Return to caller ; --- diss__typeXX --- ; ; On entry: R1 == buffer position to write to ; R8 == the instruction ; R9 == address from which instruction came ; ; On exit: R0,R2-R6,R10-R12 possibly corrupted ; ; Use: Disassemble instructions with an opcode prefix of XX. diss__type00 ROUT AND R5,R8,#&0FC00000 ;Get the op code AND R6,R8,#&000000F0 ;We need these bits too ; --- See if it is a multiply instruction --- CMP R5,#0 ;Multiply instruction? CMPEQ R6,#&90 ;Double check BEQ diss__multiply ;Yes -- deal with it then ; --- Is it a SWP instruction --- CMP R5,#&01000000 ;Is this bit set? CMPNE R5,#&01400000 ;Or maybe this bit too? CMPEQ R6,#&90 ;Double check BEQ diss__swp ;Yes -- deal with it ; --- Is it an undefined operation? --- AND R0,R5,#&03000000 ;Get the correct bits AND R3,R6,#&00000090 ;Are these bits set too? CMP R0,#&01000000 ;Is opcode 0001? CMPEQ R3,#&00000090 ;And are these bits set? BEQ diss__undefined ;Yes -- deal with it then ; --- It must be a data processing operation then --- B diss__aluOp ;Dissassemble an ALU op LTORG diss__type01 ROUT ; --- See if it's undefined --- TST R8,#(1<<25) ;Is bit 25 set? TSTNE R8,#(1<<4) ;And bit 4? BNE diss__undefined ;Yes -- it's undefined then ; --- So it must be a data transfer --- B diss__sTransfer ;Deal with it LTORG diss__type10 ROUT ; --- Test to see if it's a branch --- TST R8,#(1<<25) ;Is this bit set? BNE diss__branch ;Yes -- it's a branch ; --- It must be a multiple load then --- B diss__mTransfer ;Disassemble it LTORG diss__type11 ROUT TST R8,#(1<<25) ;Is this bit clear? BEQ diss__coDataTran ;Yes -- do co proc data trans AND R0,R8,#&0F000000 ;Get top nibble of opcode CMP R0,#&0F000000 ;Is it a SWI instruction? BEQ diss__swi ;Yes -- deal with it TST R8,#(1<<4) ;Is it a coregister transfer? BNE diss__coRegTran ;Yes -- deal with it B diss__coDataOp ;Do a co-proc data op LTORG diss__undefined ROUT MOVS R3,R14 ;Remember return address MOVS R0,R1 ;Copy to here ADR R1,diss__undef ;Point to the message BL str_cpy ;Copy the string across MOVS R1,R0 ;Make R1 point to buffer end MOVS PC,R3 ;Return to caller diss__undef DCB "Undefined instruction",0 LTORG ; --- diss__multiply --- ; ; On entry R1 == position in buffer to write dissassembly ; R7 == address of buffer for dissassembled line ; R8 == instruction to dissassemble ; R9 == location in memory of instruction ; ; On exit: R0,R2,R3,R4 corrupted ; ; Use: Dissassembles a multiply instruction diss__multiply ROUT STMFD R13!,{R14} ;Stack the link register ; --- Set up the opcode in the buffer --- TST R8,#(1<<21) ;Is it an MLA? LDRNE R14,diss__mla ;Yes -- load that name LDREQ R14,diss__mul ;No -- load MUL word then STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in TST R8,#(1<<20) ;Write back condition code? MOVNE R14,#'S' ;Yes -- stick an `S' on it STRNEB R14,[R1],#1 ;And write it on the end MOV R3,#52 ;Tab to here please BL diss__tab ;Do a 'tab' character ; --- Write out the registers --- MOV R2,#',' ;A comma, for niceness MOV R3,R8,LSR #16 ;Get Rd AND R3,R3,#&F ;Get the register CMP R3,#&F ;Is it PC? ADREQ R0,diss__mulErr1 ;Yes -- point to comment BLEQ diss__addComment ;And add the comment BL diss__reg ;Write out the register name STRB R2,[R1],#1 ;Separate Rd from the rest MOV R4,R8 ;Get Rm next AND R4,R4,#&F ;Get the register CMP R4,R3 ;Is Rd == Rm? ADREQ R0,diss__mulErr2 ;Yes -- point to comment BLEQ diss__addComment ;And add the comment MOV R3,R4 ;Display this register BL diss__reg ;Write out the register name STRB R2,[R1],#1 ;Separate Rm from next one MOV R3,R8,LSR #8 ;Get Rs BL diss__reg ;Write out the register name TST R8,#(1<<21) ;Is this an MLA instruction STRNEB R2,[R1],#1 ;Separate Rs from next one MOVNE R3,R8,LSR #12 ;Get Rn BLNE diss__reg ;And write that on the end LDMFD R13!,{PC}^ ;Return to caller diss__mla DCB "MLA",0 diss__mul DCB "MUL",0 diss__mulErr1 DCB "! Rd=PC ",0 diss__mulErr2 DCB "! Rd=Rm ",0 LTORG ; --- diss__swp --- ; ; On entry R1 == position in buffer to write dissassembly ; R7 == address of buffer for dissassembled line ; R8 == instruction to dissassemble ; R9 == location in memory of instruction ; ; On exit: R0,R2,R3,R4 corrupted ; ; Use: Dissassembles an SWP operation instruction diss__swp ROUT STMFD R13!,{R14} ;Stack the link register ; --- Set up the opcode in the buffer --- LDR R14,diss__swpName ;Get the SWP word STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in TST R8,#(1<<22) ;Single byte transfer? MOVNE R14,#'B' ;Yes -- stick an `B' on it STRNEB R14,[R1],#1 ;And write it on the end MOV R3,#52 ;Tab to here please BL diss__tab ;Do a 'tab' character ; --- Write out the registers --- MOV R2,#',' ;A comma, for niceness MOV R3,R8,LSR #12 ;Get Rd BL diss__reg ;Write out the register name STRB R2,[R1],#1 ;Separate Rd from the rest MOV R4,R8 ;Get Rm next MOV R3,R4 ;Display this register BL diss__reg ;Write out the register name STRB R2,[R1],#1 ;Separate Rm from next one MOV R2,#'[' ;Get a '[' STRB R2,[R1],#1 ;Put it in the string MOV R3,R8,LSR #16 ;Get Rn BL diss__reg ;Write out the register name MOV R2,#']' ;Get a ']' STRB R2,[R1],#1 ;Put it in the string ADR R0,diss__swpComment ;Point to the comment BL diss__addComment ;And add it nicley LDMFD R13!,{PC}^ ;Return to caller diss__swpName DCB "SWP",0 diss__swpComment DCB "Not available on ARM 2",0 ; --- diss__aluOp --- ; ; On entry: R1 == position in buffer to write dissassembly ; R7 == address of buffer for dissassembled line ; R8 == instruction to dissassemble ; R9 == location in memory of instruction ; ; On exit: R0,R2-R6,R10 corrupted ; ; Use: Dissassembles an ALU operation instruction diss__aluOp ROUT STMFD R13!,{R14} ;Save some register(s) ; --- Display the opcode nicely --- MOV R2,R8,LSR #21 ;Get the opcode AND R2,R2,#15 ;Mask of unwanted bits ADR R3,diss__aluTable ;Point to my table ADD R3,R3,R2,LSL #3 ;Point to the entry I want LDMIA R3,{R3,R5} ;Load text opcode and flags ; --- Check for ADR instructions --- TST R5,#dFlag__adrable ;Is it at all possible? TSTNE R8,#(1<<25) ;Is it also an immediate op? BEQ %05diss__aluOp ;No -- skip onwards then TSTNE R8,#(1<<20) ;Is he writing back flags? BNE %05diss__aluOp ;Yes -- it's not an ADR then MOV R14,R8,LSR #16 ;Get the middle register AND R14,R14,#15 ;Mask off unwanted bits CMP R14,#15 ;Is it the PC? BEQ %70diss__aluOp ;Yes -- then handle it ; --- Just do it the old fashioned way (oo-er) --- 05diss__aluOp STR R3,[R1],#3 ;Store the opcode BL diss__cond ;Add on the condition code TST R8,#(1<<20) ;Do we need to write an S? TSTNE R5,#dFlag__nonCmp ;Would it be superfluous? MOVNE R14,#'S' ;All OK, get an `S' char STRNEB R14,[R1],#1 ;And store it away BNE %10diss__aluOp ;And don't mess with CMPs ; --- Handle the `P' suffix on CMP instructions --- TST R5,#dFlag__nonCmp ;Is this a CMP-type instr? BNE %10diss__aluOp ;No -- don't bother with P MOV R3,R8,LSR #12 ;Otherwise get dest reg AND R3,R3,#15 ;Mask off other bits CMP R3,#15 ;Is it a `P' instruction? MOVEQ R14,#'P' ;Yes -- get a `P' char STREQB R14,[R1],#1 ;And store it away 10diss__aluOp MOV R3,#52 ;Get the tab position BL diss__tab ;And move the position on ; --- Output the operands --- MOV R2,#',' ;Get our trusty comma TST R5,#dFlag__nonCmp ;Is there a destination reg? MOVNE R3,R8,LSR #12 ;Yes -- get the register sym BLNE diss__reg ;And display it nicely STRNEB R2,[R1],#1 ;And separate it off nicely TST R5,#dFlag__noMiddle ;Is there a middle operand? MOVEQ R3,R8,LSR #16 ;Yes -- get the register BLEQ diss__reg ;And display it nicely STREQB R2,[R1],#1 ;And separate it off nicely ; --- Oh no -- it's the last operand --- TST R8,#(1<<25) ;Is it an immediate op? BNE %50diss__aluOp ;Yes -- AAARRRGHHHH MOV R3,R8 ;Get the Op2 register BL diss__reg ;Display it nicely ; --- It's fun with shifts time, boys and girls --- TST R8,#(1<<4) ;Is it a register shift BLEQ diss__shift ;No -- do a constant shift BEQ %90diss__aluOp ;...and return MOV R3,#',' ;Get a comma STRB R3,[R1],#1 ;Store it away then MOV R3,R8,LSR #5 ;Get the shift type AND R3,R3,#3 ;Kill off excess bits ADRL R4,diss__shifts ;Point to the shifts table LDR R4,[R4,R3,LSL #2] ;Load the shift name STRB R4,[R1],#1 ;Store the first byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the second byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the third byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the last byte (space) MOV R4,R4,LSR #8 ;Shift it down one byte MOV R3,R8,LSR #8 ;Get the shift register BL diss__reg ;Display the register name B %90diss__aluOp ;And return to caller ; --- Now deal with immediate constants --- 50diss__aluOp AND R2,R8,#&F00 ;Get the shift size MOV R2,R2,LSR #7 ;Make it an even rotation AND R0,R8,#&FF ;Get the actual immediate MOV R0,R0,ROR R2 ;And work out the real value ; --- Add a comment if we need to --- SUB R14,R0,#32 ;Greater or equal to this? CMP R14,#255 ;Or lower than this? BHS %53diss__aluOp ;No -- jump ahead CMP R0,#127 ;Is it ASCII 127? BEQ %53diss__aluOp ;Yes -- jump ahead MOV R2,R0 ;Put the character in R2 MOV R3,R1 ;Preserve buffer pointer MOV R4,R0 ;Preserve the number too SUB R13,R13,#12 ;Get a small block MOV R1,R13 ;Point to it nicely ADR R0,diss__chMess ;Point to the skeleton BL str_subst ;Substitute the string BL diss__addComment ;Add the comment ADD R13,R13,#12 ;Get my stack back again MOV R0,R4 ;Restore the constant MOV R1,R3 ;And restore buffer pointer ; --- Handle possible ADRLs --- 53diss__aluOp TST R5,#dFlag__adrable ;Is it a possible LDRNE R14,ws__flags ;Load the jolly old flags TSTNE R14,#wFlag__wasAdr ;Was the last one an ADR? BEQ %55diss__aluOp ;No -- skip ahead then MOV R2,R8,LSR #16 ;Get Rn out AND R2,R2,#15 ;Clear off excess bits LDRB R3,ws__lastReg ;Get the last register CMP R2,R3 ;Do they match nicely? LDREQB R2,ws__lastCond ;Get the previous condition MOVEQ R3,R8,LSR #28 ;Get the current condition CMPEQ R2,R3 ;Do they match too? BNE %55diss__aluOp ;No -- skip ahead then ; --- It's an ADRL -- it's official --- STMFD R13!,{R1} ;Save the old buffer pointer MOV R10,R0 ;Keep the offset value safe BL str_buffer ;Get a buffer thingy MOV R6,R1 ;Keep this buffer ptr LDR R14,diss__adr ;Load the opcode text STR R14,[R1],#3 ;Save it in the buffer BL diss__cond ;Attach the condition code MOV R14,#'L' ;Say this is a long ADR STRB R14,[R1],#1 ;Tack the L on the end MOV R14,#' ' ;Put a space in there too STRB R14,[R1],#1 ;Put that in nicely MOV R3,R8,LSR #12 ;Get Rd BL diss__reg ;Insert the register name STRB R3,ws__lastReg ;Save the new register val MOV R14,#',' ;The magic comma again STRB R14,[R1],#1 ;Put that in nicely LDR R0,ws__lastAddr ;Load the previous address TST R8,#(1<<23) ;Is it an ADD instruction? ADDNE R0,R0,R10 ;Yes -- add offset to old SUBEQ R0,R0,R10 ;Otherwise subtract it STR R0,ws__lastAddr ;Store this new address BL diss__address ;Insert the address MOV R0,R6 ;Point to the buffer LDMFD R13!,{R1} ;Restore my old buffer BL diss__addComment ;Insert the comment string MOV R0,R10 ;Get the immediate value back LDR R14,ws__flags ;Load the old flags word ORR R14,R14,#wFlag__newAdr ;This is another ADR thing STR R14,ws__flags ;Save the flags back again ; --- Display the mystic hash sign --- 55diss__aluOp MOV R14,#'#' ;Get a hash sign STRB R14,[R1],#1 ;Add it to the string ; --- Now work out how we want to display it --- LDR R14,ws__options ;Load the options word TST R5,#dFlag__bitwise ;Is this a bitwise operation? MOVEQ R14,R14,LSR #1 ;No -- then shift options TST R14,#dOpt__bitHex ;Now, do we display in hex? BNE %60diss__aluOp ;Yes -- do that then ; --- Display the constant in decimal --- MOV R2,#50 ;Say my buffer is big SWI XOS_ConvertInteger4 ;Display the number B %90diss__aluOp ;Now we can go home ; --- Display it in hexadecimal --- 60diss__aluOp BL diss__displayHex ;Print the hex value B %90diss__aluOp ;Return to caller ; --- Display an ADR pseudo instruction --- 70diss__aluOp LDR R0,diss__adr ;Load the characters `ADR' STR R0,[R1],#3 ;Save them in the buffer BL diss__cond ;Add on a condition code MOV R3,#52 ;Get the tab position BL diss__tab ;Tab up to the right pos ; --- Set up the flags to say this was an ADR --- LDR R14,ws__flags ;Load the flags word ORR R14,R14,#wFlag__wasAdr+wFlag__newAdr STR R14,ws__flags ;Save the flags back again MOV R14,R8,LSR #28 ;Get the condition code STRB R14,ws__lastCond ;Save that for later nicely ; --- Display the destination register --- MOV R3,R8,LSR #12 ;Get the register number AND R3,R3,#15 ;Kill off the excess bits STRB R3,ws__lastReg ;Store this register away BL diss__reg ;And display it nicely MOV R2,#',' ;Get our trusty comma STRB R2,[R1],#1 ;And separate it off nicely AND R2,R8,#&F00 ;Get the shift size MOV R2,R2,LSR #7 ;Make it an even rotation AND R0,R8,#&FF ;Get the actual immediate MOV R0,R0,ROR R2 ;And work out the real value ADD R14,R9,#8 ;Move PC on a little bit TST R8,#(1<<23) ;Is it an ADD instruction? ADDNE R0,R14,R0 ;Yes -- add offset to PC SUBEQ R0,R14,R0 ;Otherwise subtract it STR R0,ws__lastAddr ;Save this address away BL diss__address ;Display the address 90diss__aluOp LDMFD R13!,{PC}^ ;Return to caller dFlag__bitwise EQU (1<<0) dFlag__adrable EQU (1<<1) dFlag__multiply EQU (1<<2) dFlag__noMiddle EQU (1<<3) dFlag__nonCmp EQU (1<<4) diss__adr DCB "ADR",0 diss__aluTable DCB "AND",0 DCD dFlag__bitwise+dFlag__nonCmp DCB "EOR",0 DCD dFlag__bitwise+dFlag__nonCmp DCB "SUB",0 DCD dFlag__adrable+dFlag__nonCmp DCB "RSB",0 DCD dFlag__multiply+dFlag__nonCmp DCB "ADD",0 DCD dFlag__multiply+dFlag__adrable+dFlag__nonCmp DCB "ADC",0 DCD dFlag__nonCmp DCB "SBC",0 DCD dFlag__nonCmp DCB "RSC",0 DCD dFlag__nonCmp DCB "TST",0 DCD dFlag__bitwise DCB "TEQ",0 DCD dFlag__bitwise DCB "CMP",0 DCD 0 DCB "CMN",0 DCD 0 DCB "ORR",0 DCD dFlag__bitwise+dFlag__nonCmp DCB "MOV",0 DCD dFlag__noMiddle+dFlag__multiply+dFlag__nonCmp DCB "BIC",0 DCD dFlag__bitwise+dFlag__nonCmp DCB "MVN",0 DCD dFlag__noMiddle+dFlag__nonCmp diss__chMess DCB "='%c0'",0 ALIGN LTORG diss__shifts DCB "LSL " DCB "LSR " DCB "ASR " DCB "ROR " diss__rrx DCB " RRX" ; --- diss__shift --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R5 == Flags word of the instruction ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: R3,R4 corrupted ; ; Use: Disassemble a constant controlled shift diss__shift ROUT STMFD R13!,{R14} ;Stack the link register AND R3,R8,#&FF0 ;Get the shift type and size CMP R3,#&000 ;Is it an LSL #0? LDMEQFD R13!,{PC}^ ;Yes -- nothing to do then CMP R3,#&060 ;Is it an RRX? BNE %10diss__shift ;No -- do other things then LDR R4,diss__rrx ;Get the RRX string STRB R4,[R1],#1 ;Store the first byte (comma) MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the second byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the third byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the last byte MOV R4,R4,LSR #8 ;Shift it down one byte LDMEQFD R13!,{PC}^ ;And return to caller ; --- Do ordinary things now --- 10diss__shift MOV R14,#' ' ;Put a comma in STRB R14,[R1],#1 ;Store that in the buffer MOV R14,R8,LSR #5 ;Get the shift type AND R14,R14,#3 ;Kill off excess bits ADR R4,diss__shifts ;Point to the shifts table LDR R4,[R4,R14,LSL #2] ;Load the shift name MOV R0,R3,LSR #7 ;Get the shift amount value CMP R0,#0 ;Is it zero? MOVEQ R0,#32 ;Then pretend it's 32 ; --- Add in the multipy comment --- TST R5,#dFlag__multiply ;Does it apply to the inst? BEQ %50diss__shift ;No -- jump ahead CMP R14,#0 ;Is it an LSL? BNE %50diss__shift ;No -- jump ahead MOV R6,R8 ;Get Rm AND R6,R6,#&F ;Clear unwanted bits MOV R3,R8,LSR#16 ;Get Rn AND R3,R3,#&F ;Clear unwanted bits TST R5,#dFlag__noMiddle ;Does it have a middle reg? BNE %12diss__shift ;No -- jump folowing test CMP R3,R6 ;And is Rm <> Rn? BNE %50diss__shift ;If so -- jump ahead TST R5,#dFlag__noMiddle ;Does it have a middle reg? 12 MOV R5,#1 ;We start with 1 MOV R5,R5,LSL R0 ;Work out the multiply BNE %15diss__shift ;No -- jump ahead a bit TST R8,#(1<<23) ;Is it a RSB? ADDNE R5,R5,#1 ;No -- increment multiply SUBEQ R5,R5,#1 ;Yes -- decrement multiply 15 MOV R11,R0 ;Preserve R0 MOV R3,R8,LSR#12 ;Get Rd MOV R10,R1 ;Preserve the buffer pointer BL str_buffer ;Get a buffer STMFD R13!,{R1} ;Remember this position BL diss__reg ;Display the destination MOV R3,#'=' ;Get the equal sign STRB R3,[R1],#1 ;Store it in the buffer MOV R3,R6 ;Get the operand register BL diss__reg ;Display that MOV R3,#'*' ;Get the '*' sign STRB R3,[R1],#1 ;Store it in the buffer MOV R0,R5 ;Put 'multiply by' in R0 SWI OS_ConvertInteger4 ;Convert to a string LDMFD R13!,{R0} ;Get comment pointer BL diss__addComment ;Add the comment MOV R0,R11 ;Restore R0 value MOV R1,R10 ;Get buffer pos back ; --- Display the shift --- 50 STRB R4,[R1],#1 ;Store the first byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the second byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the third byte MOV R4,R4,LSR #8 ;Shift it down one byte STRB R4,[R1],#1 ;Store the last byte (space) MOV R4,R4,LSR #8 ;Shift it down one byte MOV R14,#'#' ;Put a hash sign in STRB R14,[R1],#1 ;Store it away MOV R2,#50 ;Say my buffer's massive SWI XOS_ConvertInteger1 ;Write it out in decimal LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss__displayHex --- ; ; On entry: R0 == hex number to display ; R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: R0,R2,R3 corrupted ; ; Use: Displays a hex number without leading NULL bytes diss__displayHex ROUT STMFD R13!,{R14} ;Stack the link register MOV R2,#'&' ;Tell user it's in hex STRB R2,[R1],#1 ;Save it in the buffer MOV R2,#8 ;No nastiness done yet TST R0,#&FF000000 ;Is the top byte set? MOVEQ R0,R0,LSL #8 ;No -- shift it up then SUBEQ R2,R2,#2 ;Two digits lost here TST R0,#&FF000000 ;Is the top byte set? MOVEQ R0,R0,LSL #8 ;No -- shift it up then SUBEQ R2,R2,#2 ;Two digits lost here TST R0,#&FF000000 ;Is the top byte set? MOVEQ R0,R0,LSL #8 ;No -- shift it up then SUBEQ R2,R2,#2 ;Two digits lost here ADR R3,diss__hexDigits ;Point to my hex table 10 MOV R14,R0,LSR #28 ;Get the top nibble LDRB R14,[R3,R14] ;Get the correct hex digit STRB R14,[R1],#1 ;Store it in the buffer MOV R0,R0,LSL #4 ;Shift up by a nibble SUBS R2,R2,#1 ;One less digit to go BGT %10diss__displayHex ;If I've done it, stop LDMFD R13!,{PC}^ ;Return to caller diss__hexDigits DCB "0123456789ABCDEF" LTORG ; --- diss__sTransfer --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassemble LDR/STR instructions, putting the result into ; the buffer. diss__sTransfer ROUT STMFD R13!,{R14} ;Stack the link register ; --- Display the instruction --- TST R8,#(1<<20) ;Is it an LDR? LDRNE R14,diss__ldr ;Yes -- load that name LDREQ R14,diss__str ;No -- load STR word then STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in TST R8,#(1<<22) ;Is this byte load? MOVNE R3,#'B' ;Yes -- get a 'B' character STRNEB R3,[R1],#1 ;...and store it in buffer ; --- Display the 'T' suffix --- TST R8,#(1<<21) ;Is the W bit set? BEQ %00diss__sTransfer ;No -- jump ahead a bit TST R8,#(1<<24) ;How about the P bit? MOVEQ R3,#'T' ;No -- get a 'B' character STREQB R3,[R1],#1 ;...and store it in buffer ; --- Now the destination register --- 00 MOV R3,#52 ;Tab to this position BL diss__tab ;Do the tabbing MOV R3,R8,LSR#12 ;Get Rd in bottom nibble BL diss__reg ;Display the register MOV R3,#',' ;Get a comma STRB R3,[R1],#1 ;...and store it in buffer TST R8,#(1<<24) ;Are we pre-indexed? BNE diss__preIndexed ;Yes -- deal with it then BEQ diss__postIndexed ;No -- must be post-indexed diss__ldr DCB "LDR",0 diss__str DCB "STR",0 LTORG ; --- diss__preIndexed --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; Return address is on the stack ; ; On exit: Lots corrupted ; ; Use: Disassembles the pre-indexed part of a data transfer op. diss__preIndexed ROUT ; --- Deal with the base register --- MOV R3,R8,LSR#16 ;Get the base register AND R3,R3,#&F ;Mask off unwanted bits CMP R3,#&F ;Is it the PC? TSTEQ R8,#(1<<25) ;And is data immediate? TSTEQ R8,#(1<<21) ;And are we not using ! BEQ diss__pcRel ;Yes -- it's PC relative MOV R2,#'[' ;Get an open bracket STRB R2,[R1],#1 ;Store it nicely BL diss__reg ;Display the base register MOV R5,#&FF ;Prepare the bit mask ORR R5,R5,#&F00 ;Finish it off AND R0,R8,R5 ;Get the offset MOV R2,#',' ;Get an comma STRB R2,[R1],#1 ;Store it in the buffer TST R8,#(1<<25) ;Is data immediate? MOVEQ R2,#'#' ;Yes -- get an hash STREQB R2,[R1],#1 ;...and store it TST R8,#(1<<23) ;Is data negative? MOVEQ R2,#'-' ;Yes -- get a minus sugn STREQB R2,[R1],#1 ;...and store that TST R8,#(1<<25) ;Is data immediate? MOV R5,#0 ;The flags word BEQ %10diss__preIndexed ;Yes -- deal with it MOV R3,R8 ;Get the register BL diss__reg ;Display the register BL diss__shift ;Display the shift B %20diss__preIndexed ;and jump ahead ; --- Now work out how we want to display constant --- 10 LDR R14,ws__options ;Load the options word TST R14,#dOpt__tranHex ;Now, do we display in hex? BLNE diss__displayHex ;Yes -- do it then MOVEQ R2,#50 ;No -- say my buffer is big SWIEQ XOS_ConvertInteger4 ;...display the number ; --- Finish off the instruction --- 20 MOV R2,#']' ;Yes -- get an close bracket STRB R2,[R1],#1 ;...and store it TST R8,#(1<<21) ;Are we writing back? MOVNE R2,#'!' ;Yes -- get the pling STRNEB R2,[R1],#1 ;...and store it LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss__postIndexed --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; Return address is on the stack ; ; On exit: Lots corrupted ; ; Use: Disassembles the post-indexed part of a data transfer op. diss__postIndexed ROUT ; --- Deal with the base register --- MOV R3,R8,LSR#16 ;Get the base register MOV R2,#'[' ;Get an open bracket STRB R2,[R1],#1 ;Store it nicely BL diss__reg ;Display the base register MOV R2,#']' ;Get an close bracket STRB R2,[R1],#1 ;Store it nicely ; --- Is there any post index to display --- MOV R5,#&FF ;Prepare the bit mask ORR R5,R5,#&F00 ;Finish it off AND R0,R8,R5 ;Get the offset MOV R2,#',' ;Get an comma STRB R2,[R1],#1 ;Store it in the buffer TST R8,#(1<<25) ;Is data immediate? MOVEQ R2,#'#' ;Yes -- get an hash STREQB R2,[R1],#1 ;...and store it TST R8,#(1<<23) ;Is data negative? MOVEQ R2,#'-' ;Yes -- get a minus sugn STREQB R2,[R1],#1 ;...and store that TST R8,#(1<<25) ;Is data immediate? BEQ %10diss__postIndexed ;Yes -- deal with it MOV R3,R8 ;Get the register BL diss__reg ;Display the register MOV R5,#0 ;The flags word BL diss__shift ;Display the shift LDMFD R13!,{PC}^ ;Return to caller ; --- Now work out how we want to display constant --- 10 LDR R14,ws__options ;Load the options word TST R14,#dOpt__tranHex ;Now, do we display in hex? BLNE diss__displayHex ;Yes -- do it then MOVEQ R2,#50 ;No -- say my buffer is big SWIEQ XOS_ConvertInteger4 ;...display the number LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss__pcRel --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; Return address is on the stack ; ; On exit: Lots corrupted ; ; Use: Disassembles PC relative data transfer instructions diss__pcRel ROUT ADD R2,R9,#8 ;Allow for the pipeline MOV R0,#&FF ;Prepare the bit mask ORR R0,R0,#&F00 ;Finish it off AND R0,R8,R0 ;Get the offset TST R8,#(1<<23) ;Is offset negative? SUBEQ R0,R2,R0 ;Yes -- do a subtraction ADDNE R0,R2,R0 ;No -- do an addition BIC R0,R0,#&FC000000 ;Clear silly bits BL diss__address ;Display the address LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss__mTransfer --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassembles LDM/STM instruction diss__mTransfer ROUT STMFD R13!,{R14} ;Stack the link TST R8,#(1<<20) ;Is it a load? LDRNE R14,diss__ldm ;Yes -- load that name LDREQ R14,diss__stm ;No -- load STM word then STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in MOV R5,R8,LSR#16 ;Get the base register AND R5,R5,#&F ;Clear unwanted bits LDR R0,ws__options ;Get the options TST R0,#dOpt__r13Stack ;Do we use alternative type BEQ %10diss__mTransfer ;No -- jump ahead CMP R5,#13 ;Are we using R13? BNE %10diss__mTransfer ;No -- jump ahead TST R8,#(1<<21) ;Is there write back? BEQ %10diss__mTransfer ;No -- jump ahead ; --- Work out stack type (FD etc.) --- TST R8,#(1<<20) ;Yes -- Are we storing? BNE %05diss__mTransfer ;No -- do load seperatley TST R8,#(1<<24) ;Are we full? MOVNE R0,#'F' ;Yes -- use 'F' MOVEQ R0,#'E' ;No -- use 'E' STRB R0,[R1],#1 ;Store the character TST R8,#(1<<23) ;Are we incrementing? MOVNE R0,#'A' ;Yes -- use 'A' MOVEQ R0,#'D' ;No -- use 'D' STRB R0,[R1],#1 ;Store the character B %20diss__mTransfer ;Jump ahead 05 TST R8,#(1<<24) ;Are we full? MOVNE R0,#'E' ;Yes -- use 'E' MOVEQ R0,#'F' ;No -- use 'F' STRB R0,[R1],#1 ;Store the character TST R8,#(1<<23) ;Are we incrementing? MOVNE R0,#'D' ;Yes -- use 'D' MOVEQ R0,#'A' ;No -- use 'A' STRB R0,[R1],#1 ;Store the character B %20diss__mTransfer ;Jump ahead ; --- Word out stack type normally --- 10 TST R8,#(1<<23) ;Are we incrementing? MOVNE R0,#'I' ;Yes -- use 'I' MOVEQ R0,#'D' ;No -- use 'D' STRB R0,[R1],#1 ;Store the character TST R8,#(1<<24) ;Are we full? MOVNE R0,#'B' ;Yes -- use 'B' MOVEQ R0,#'A' ;No -- use 'A' STRB R0,[R1],#1 ;Store the character B %20diss__mTransfer ;Jump ahead ; --- Continue the disassembly --- 20 MOV R3,#52 ;Tab to here BL diss__tab ;Do the tab MOV R3,R5 ;Put the base regster in R3 BL diss__reg ;And display it TST R8,#(1<<21) ;Do we want write back? MOVNE R0,#'!' ;Yes -- get the '!' STRNEB R0,[R1],#1 ;...store the character MOV R0,#',' ;Get a comma STRB R0,[R1],#1 ;Store it MOV R0,#'{' ;Get the '{' STRB R0,[R1],#1 ;And store that too ; --- Now do the register list --- MOV R6,#1 ;The flags so far MOV R4,#0 ;The current register MOV R5,#0 ;Number in a row so far 30 MOV R2,#1 ;A nice 1 value MOV R2,R2,LSL R4 ;Set up the bit mask ANDS R14,R8,R2 ;Is the register in the list? BNE %50diss__mTransfer ;Yes -- jump ahead 35 TST R6,#4 ;Are we in a sequence? BEQ %40diss__mTransfer ;No -- jump a bit CMP R5,#2 ;Have there been >2 regs? MOVGT R14,#'-' ;Yes -- get the dash MOVLE R14,#',' ;No -- get a comma then STRB R14,[R1],#1 ;Store it in the buffer SUB R3,R4,#1 ;Put previous register in R3 BL diss__reg ;And display it 40 BIC R6,R6,#&6 ;Clear some flags MOV R5,#0 ;None in a row now B %70diss__mTransfer ;And jump to end of loop ; --- The register is in the list --- 50 ADD R5,R5,#1 ;Increment reg count TST R6,#2 ;Is this the second? BNE %55diss__mTransfer ;Yes -- jump ahead a bit TST R6,#4 ;Are we in a sequence? BNE %70diss__mTransfer ;Yes -- jump to loop end TST R6,#1 ;Is this the *very* first? MOVEQ R14,#',' ;No -- get a comma then STREQB R14,[R1],#1 ;...store it in the buffer MOVEQ R6,#2 ;...just set 'first' bit now BEQ %60diss__mTransfer ;...and jump ahead 55 TST R6,#2 ;Was last one first one? MOVNE R6,#4 ;Yes -- now in sequence BNE %70diss__mTransfer ;...and jump to loop end 60 MOV R3,R4 ;Put register in R3 BL diss__reg ;Display the register MOV R6,#2 ;That was the first 70 ADD R4,R4,#1 ;Increment current register CMP R4,#15 ;Have we finished? BLE %30diss__mTransfer ;No -- keep on looping TST R6,#4 ;Are we still in a sequence? MOVNE R4,#16 ;Yes -- say were on R16 BNE %35diss__mTransfer ;And finish off nicely ; --- Finish off the instruction --- MOV R0,#'}' ;Get the closing bracket STRB R0,[R1],#1 ;Store it TST R8,#(1<<22) ;Is there a '^'? MOVNE R0,#'^' ;Yes -- get the character STRNEB R0,[R1],#1 ;And store that too LDMFD R13!,{PC}^ ;Return to caller diss__ldm DCB "LDM",0 diss__stm DCB "STM",0 LTORG ; --- diss__branch --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassembles branch instructions diss__branch STMFD R13!,{R14} ;Stack the link MOV R3,#'B' ;It's a B instruction STRB R3,[R1],#1 ;So store it away TST R8,#(1<<24) ;Is it BL? MOVNE R3,#'L' ;Yes -- get the 'L' STRNEB R3,[R1],#1 ;...and store it in buffer BL diss__cond ;Put in the condition MOV R3,#52 ;Tab to here BL diss__tab ;Do the tab BIC R0,R8,#&FF000000 ;Get the offset ADD R0,R9,R0,LSL#2 ;Offset correctld ADD R0,R0,#8 ;Take pipeline into account BIC R0,R0,#&FC000000 ;Make sure its not silly BL diss__address ;Print the address LDMFD R13!,{PC}^ ;Return to caller ; --- diss__coDataOp --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassembles co-processor data operations diss__coDataOp ROUT STMFD R13!,{R14} ;Stack the link LDR R14,diss__cdp ;Load the mnemonic STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in 10 MOV R3,#52 ;Set up the tab position BL diss__tab ;And do the tab MOV R3,#'C' ;Get the 'C' STRB R3,[R1],#1 ;And store it in buffer MOV R3,#'P' ;Get the 'P' STRB R3,[R1],#1 ;And store it in buffer MOV R0,R8,LSR#8 ;Get cp# AND R0,R0,#&F ;Clear unwanted bits MOV R2,#50 ;Say that buffer is big SWI OS_ConvertInteger1 ;Translate the number MOV R4,#',' ;Get the ',' STRB R4,[R1],#1 ;And store it in buffer MOV R0,R8,LSR#20 ;Get cp instruction AND R0,R0,#&F ;Clear unwanted bits MOV R2,#50 ;Say that buffer is big SWI OS_ConvertInteger1 ;Translate the number STRB R4,[R1],#1 ;Store comma in buffer MOV R3,R8,LSR#12 ;Get destination register BL diss__coReg ;Display the register STRB R4,[R1],#1 ;Store comma in buffer MOV R3,R8,LSR#16 ;Get CRn BL diss__coReg ;Display it STRB R4,[R1],#1 ;Store comma in buffer MOV R3,R8 ;Get CRm BL diss__coReg ;Display it MOV R0,R8,LSR#5 ;Get optional constant ANDS R0,R0,#&7 ;Clear unwanted bits STRNEB R4,[R1],#1 ;Store comma in buffer MOVNE R2,#50 ;...say that buffer is big SWINE OS_ConvertInteger1 ;...translate the number LDMFD R13!,{PC}^ ;Return to caller diss__cdp DCB "CDP",0 LTORG ; --- diss__coDataTran --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassembles co-processor data transfers diss__coDataTran ROUT STMFD R13!,{R14} ;Stack the link TST R8,#(1<<20) ;Is it a load? LDRNE R14,diss__ldc ;Yes -- load that name LDREQ R14,diss__stc ;No -- load STC word then STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in TST R8,#(1<<22) ;Is it long transfer? MOVNE R3,#'L' ;Yes -- get the 'L' STRNEB R3,[R1],#1 ;...and store it in buffer [ 1<>1 ;T on co-processor trandfers TST R8,#(1<<21) ;Is the W bit set? BEQ %10diss__coDataTran ;No -- jump ahead a bit TST R8,#(1<<24) ;How about the P bit? MOVEQ R3,#'T' ;No -- get a 'T' character STREQB R3,[R1],#1 ;...and store it in buffer ] 10 MOV R3,#52 ;Set up the tab position BL diss__tab ;And do the tab MOV R3,#'C' ;Get the 'C' STRB R3,[R1],#1 ;And store it in buffer MOV R3,#'P' ;Get the 'P' STRB R3,[R1],#1 ;And store it in buffer MOV R0,R8,LSR#8 ;Get cp# AND R0,R0,#&F ;Clear unwanted bits MOV R2,#50 ;Say that buffer is big SWI OS_ConvertInteger1 ;Translate the number MOV R3,#',' ;Get the ',' STRB R3,[R1],#1 ;And store it in buffer MOV R3,#'C' ;Get the 'C' STRB R3,[R1],#1 ;And store that in the buffer MOV R0,R8,LSR#12 ;Get cp# AND R0,R0,#&F ;Clear unwanted bits MOV R2,#50 ;Say that buffer is big SWI OS_ConvertInteger1 ;Translate the number MOV R3,#',' ;Get the ',' STRB R3,[R1],#1 ;And store it in buffer ; --- Sabotage into and LDR/STR form --- BIC R0,R8,#&000000FF ;Clear bottom bits BIC R0,R0,#&00000F00 ;Those ones too AND R2,R8,#&FF ;Get the offset MOV R2,R2,LSL#2 ;Shift it up properley ORR R0,R0,R2 ;Merge the two words MOV R8,R0 ;Put new instruction in R8 TST R8,#(1<<24) ;Are we pre-indexing? BNE diss__preIndexed ;Yes -- deal with it BEQ diss__postIndexed ;No -- do a post index diss__ldc DCB "LDC",0 diss__stc DCB "STC",0 LTORG ; --- diss__coRegTran --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassembles co-processor register transfers diss__coRegTran ROUT STMFD R13!,{R14} ;Stack the link TST R8,#(1<<20) ;Is it a co->arc LDRNE R14,diss__mcr ;Yes -- load that name LDREQ R14,diss__mrc ;No -- load MRC word then STR R14,[R1],#3 ;Store the opcode in buffer BL diss__cond ;Put the condition code in 10 MOV R3,#52 ;Set up the tab position BL diss__tab ;And do the tab MOV R3,#'C' ;Get the 'C' STRB R3,[R1],#1 ;And store it in buffer MOV R3,#'P' ;Get the 'P' STRB R3,[R1],#1 ;And store it in buffer MOV R0,R8,LSR#8 ;Get cp# AND R0,R0,#&F ;Clear unwanted bits MOV R2,#50 ;Say that buffer is big SWI OS_ConvertInteger1 ;Translate the number MOV R4,#',' ;Get the ',' STRB R4,[R1],#1 ;And store it in buffer MOV R0,R8,LSR#21 ;Get cp instruction AND R0,R0,#&7 ;Clear unwanted bits MOV R2,#50 ;Say that buffer is big SWI OS_ConvertInteger1 ;Translate the number STRB R4,[R1],#1 ;Store comma in buffer MOV R3,R8,LSR#12 ;Get arc register BL diss__reg ;Display the register STRB R4,[R1],#1 ;Store comma in buffer MOV R3,R8,LSR#16 ;Get CRn BL diss__coReg ;Display it STRB R4,[R1],#1 ;Store comma in buffer MOV R3,R8 ;Get CRm BL diss__coReg ;Display it MOV R0,R8,LSR#5 ;Get optional constant ANDS R0,R0,#&7 ;Clear unwanted bits STRNEB R4,[R1],#1 ;Store comma in buffer MOVNE R2,#50 ;...say that buffer is big SWINE OS_ConvertInteger1 ;...translate the number LDMFD R13!,{PC}^ ;Return to caller diss__mcr DCB "MCR",0 diss__mrc DCB "MRC",0 LTORG ; --- diss__swi --- ; ; On entry: R1 == pointer to buffer to disassemble into ; R7 == pointer to the start of the buffer ; R8 == the instruction ; R9 == location in memory from which instruction came ; ; On exit: Lots corrupted ; ; Use: Disassembles swi instructions diss__swi ROUT STMFD R13!,{R14} ;Stack the link LDR R0,diss__swiCode ;Load the opcode STR R0,[R1],#3 ;Store it in the buffer BL diss__cond ;Put in the condition MOV R3,#52 ;Set up the tab position BL diss__tab ;Do the tabbing MOV R4,R1 ;Look after the buffer pos BL str_buffer ;Get a buffer to use MOV R6,R1 ;Don't lose this either! BIC R0,R8,#&FF000000 ;Get the swi number MOV R5,R0 ;Look after this too MOV R2,#256 ;The buffer size SWI XOS_SWINumberToString ;Get the SWI name ; --- Now try to convert back again --- BIC R2,R5,#(1<<17) ;Clear the X bit SUB R2,R2,#&100 ;Do a range check between SUBS R2,R2,#&200 ;...if its OS_WriteI BLO %10diss__swi ;It is -- return SWI XOS_SWINumberFromString ;Convert back again BVC %10diss__swi ;All OK, jump ahead MOV R1,R6 ;...point to the number MOV R2,#'&' ;Get the '&' character STRB R2,[R1],#1 ;Store it in the buffer MOV R0,R5 ;Put number in R0 MOV R2,#50 ;Say buffer is big SWI XOS_ConvertHex8 ;And convert the number MOV R1,R6 ;Point to the number ; --- Now put the string in the buffer --- 10diss__swi MOV R0,R4 ;Write to here BL str_cpy ;Copy the string over MOV R1,R0 ;Make R1 point to buffer end LDMFD R13!,{PC}^ ;Return to caller diss__swiCode DCB "SWI",0 LTORG ; --- diss__cond --- ; ; On entry: R1 == position in buffer to put string ; R8 == the instruction ; ; On exit: -- ; ; Use: Inserts the condition code into the buffer diss__cond ROUT MOV R3,R8,LSR#28 ;Get the condition code ADR R4,diss__condTable ;Point to the table ADD R4,R4,R3,LSL#2 ;Point to the string LDRB R2,[R4],#1 ;Get a byte CMP R2,#0 ;Am I there yet STRNEB R2,[R1],#1 ;Store the byte LDRNEB R2,[R4],#1 ;Get a byte CMPNE R2,#0 ;Am I there yet STRNEB R2,[R1],#1 ;Store the byte MOVS PC,R14 ;Return to caller diss__condTable DCB "EQ",0,0 DCB "NE",0,0 DCB "CS",0,0 DCB "CC",0,0 DCB "MI",0,0 DCB "PL",0,0 DCB "VS",0,0 DCB "VC",0,0 DCB "HI",0,0 DCB "LS",0,0 DCB "GE",0,0 DCB "LT",0,0 DCB "GT",0,0 DCB "LE",0,0 DCB 0,0,0,0 DCB "NV",0,0 ; --- diss__tab --- ; ; On entry: R1 == Current position within buffer ; R3 == tab position required ; R7 == start of the buffer ; ; On exit: R1 updated appropriately ; ; Use: Inserts as many spaces as in nessesary to get to the ; right position in the buffer. diss__tab ROUT ADD R4,R7,R3 ;Word out the offset MOV R2,#' ' ;Get a space character 00diss__tab STRB R2,[R1],#1 ;Store the sapce character CMP R1,R4 ;Have we finished yet? BLT %00diss__tab ;No -- keep looping MOVS PC,R14 ;Return LTORG ; --- diss__reg --- ; ; On entry: R1 == position in buffer to write register name ; R3 == register number to write (in bottom 4 bits) ; ; On exit: R1 updated ; ; Use: Writes a register name to the output buffer diss__reg ROUT STMFD R13!,{R14} ;Save some registers ADR R14,ws__names ;Find the register names AND R3,R3,#15 ;Hack off unwanted bits MOV R0,R1 ;Point to the output buffer LDR R1,[R14,R3,LSL #2] ;Find the name I want BL str_cpy ;Copy the string out MOV R1,R0 ;Point to string terminator LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss__coReg --- ; ; On entry: R1 == position in buffer to write register name ; R3 == register number to write (in bottom 4 bits) ; ; On exit: R1 updated ; ; Use: Writes a co-processor register name to the output buffer diss__coReg ROUT STMFD R13!,{R14} ;Save some registers AND R0,R3,#15 ;Hack off unwanted bits MOV R14,#'C' ;Get the prefix STRB R14,[R1],#1 ;Store it MOV R2,#50 ;Say buffer is big SWI OS_ConvertInteger1 ;Write number into buffer LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss__address --- ; ; On entry: R0 == an address to display ; R1 == pointer to where to store it ; ; On exit: R1 updated ; ; Use: Displays an address, looking it up in the symbol table ; and trying to turn it into a label if possible. ; ; (26-Aug-1994 Symbol table not implemented) diss__address ROUT STMFD R13!,{R14} ;Stash the link away MOV R14,#'&' ;The address is in hex STRB R14,[R1],#1 ;Store it in the buffer MOV R2,#50 ;Please fondle my buffer (?) SWI XOS_ConvertHex8 ;Convert to nice address LDMFD R13!,{PC}^ ;Return to caller LTORG ; --- diss_regTable --- ; ; On entry: -- ; ; On exit: R0 == pointer to register table ; ; Use: Returns a pointer to the register name table, so that the ; assembler can get its grubby paws on it. EXPORT diss_regTable diss_regTable ROUT LDR R0,diss__wSpace ;Find the workspace address ADD R0,R0,#:INDEX: ws__names MOVS PC,R14 ;Return to caller LTORG ; --- diss_init --- ; ; On entry: R12 == pointer to workspace ; ; On exit: -- ; ; Use: Initialises the disassembler segment nicely. EXPORT diss_init diss_init ROUT STMFD R13!,{R0-R3,R14} ;Stack what we need ; --- If we are already initialised, return --- LDR R0,ws__flags ;Load my flags word TST R0,#wFlag__inited ;Are we initialised? BNE %99diss_init ;Yes -- return ORR R0,R0,#wFlag__inited ;We are initialised now STR R0,ws__flags ;Store this fact ; --- Set up the default options --- MOV R0,#0 ;The default options STR R0,ws__options ;Store them here ; --- Set up the default register names --- BL quartz_base ;Find the module base ADR R1,ws__names ;Point to the name table ADR R2,diss__defNames ;Point to default name table MOV R3,#16 ;Copy over this many 10diss_init LDR R14,[R2],#4 ;Get a name pointer ADD R14,R14,R0 ;Relocate the name pointer STR R14,[R1],#4 ;Copy it over SUBS R3,R3,#1 ;Decrement the counter BNE %10diss_init ;More to go -- do them 99diss_init LDMFD R13!,{R0-R3,PC}^ ;Return to caller LTORG diss__wSpace DCD 0 diss__defNames DCD dName__R0,dName__R1,dName__R2,dName__R3 DCD dName__R4,dName__R5,dName__R6,dName__R7 DCD dName__R8,dName__R9,dName__R10,dName__R11 DCD dName__R12,dName__R13,dName__R14,dName__R15 dName__R0 DCB "R0",0 dName__R1 DCB "R1",0 dName__R2 DCB "R2",0 dName__R3 DCB "R3",0 dName__R4 DCB "R4",0 dName__R5 DCB "R5",0 dName__R6 DCB "R6",0 dName__R7 DCB "R7",0 dName__R8 DCB "R8",0 dName__R9 DCB "R9",0 dName__R10 DCB "R10",0 dName__R11 DCB "R11",0 dName__R12 DCB "R12",0 dName__R13 DCB "R13",0 dName__R14 DCB "R14",0 dName__R15 DCB "PC",0 ;----- Workspace ------------------------------------------------------------ ^ 0,R12 ws__start # 0 ws__flags # 4 ;The main flags word ws__options # 4 ;Disassembler options ws__commentEnd # 4 ;End of the current comment ws__lastReg # 1 ;Register in ADR / LDR ws__lastCond # 3 ;Last condition code thing ws__lastAddr # 4 ;Address from ADR / LDR ws__nextAddr # 4 ;Address of next instruction ws__names # 16*4 ;Table of register names ws__buffer # 256 ;Somewhere to put the string ws__comment # 80 ;Somewhere to put comments ws__size EQU {VAR}-ws__start ;The workspace size wFlag__inited EQU (1<<0) ;We are initialised wFlag__wasAdr EQU (1<<1) ;Last instruction was ADR wFlag__newAdr EQU (1<<2) ;This instruction is ADR dOpt__bitHex EQU (1<<0) ;Display bitwise in hex dOpt__arthHex EQU (1<<1) ;Display arithmetic in hex dOpt__tranHex EQU (1<<2) ;Display data transfer in hex dOpt__r13Stack EQU (1<<3) ;Use FD etc. if R13 is base AREA |Quartz$$Table|,CODE,READONLY DCD ws__size DCD diss__wSpace DCD diss_init DCD 0 ;----- That's all, folks ---------------------------------------------------- END