; ; armEmul.s ; ; ARM emulation (MDW/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. ;----- Don't forget these things -------------------------------------------- ; ; SWP ; Trans pin on LDR/STR ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis GET libs:stream ;----- External dependencies ------------------------------------------------ GET sh.brkpt GET sh.diss ;----- Main code ------------------------------------------------------------ AREA |Hammer$$Code|,CODE,READONLY ; --- ae_addr --- ; ; On entry: -- ; ; On exit: R0 == address of armEmul routine ; ; Use: Returns the address of the arm emulation code, for speedy ; calling. EXPORT ae_addr ae_addr ADR R0,armEmul MOVS PC,R14 ; --- armEmul --- ; ; On entry: R0 == pointer to register block ; ; On exit: Flags corrupted ; May return error ; ; Use: Emulates a given ARM instruction. EXPORT armEmul armEmul ROUT STMFD R13!,{R0-R12,R14} ;Save some registers MOV R1,R0 ;Keep the register block ptr LDR R2,[R1,#15*4] ;Load current PC value BIC R0,R2,#&FC000003 ;Clear the PSR bits BL ae__translateAddr ;Translate the address LDR R0,[R0,#0] ;Load the instruction ; --- Debugging stuff --- [ {FALSE} IMPORT stream_regDump STMFD R13!,{R0-R2} SWI Stream_WriteS = "armEmul entered with instruction:",13,10,0 BIC R0,R2,#&FC000003 BL diss_address LDR R0,[R13,#0] BL diss_disassemble SWI Stream_Write0 SWI Stream_NewLine MOV R0,R1 BL stream_regDump LDMIA R13!,{R0-R2} RDUMP ] ; --- Make sure we need to execute the instruction --- MOV R3,R0,LSR #28 ;Get the condition bits ADR R14,ae__condTbl ;Point to the cunning table AND R4,R3,#&E ;Get rid of the bottom bit LDR R4,[R14,R4,LSL #1] ;Load the entry we want MOV R6,#&FF ;A vaguely useful value TST R3,#1 ;Is this a complement BEQ %10armEmul ;No -- handle it then ; --- We can work directly from the table --- ANDS R14,R6,R4 ;Get the bottom byte AND R5,R14,R2,LSR #28 ;Get the bits we want CMP R5,R14,LSR #4 ;Does it match? BEQ %20armEmul ;Yes -- we actually execute ANDS R14,R6,R4,LSR #8 ;Get the next byte BEQ ae__next ;Nothing there -- advance PC AND R5,R14,R2,LSR #28 ;Get the bits we want CMP R5,R14,LSR #4 ;Does it match? BEQ %20armEmul ;Yes -- we actually execute ANDS R14,R6,R4,LSR #16 ;Get the next byte BEQ ae__next ;Nothing there -- advance PC AND R5,R14,R2,LSR #28 ;Get the bits we want CMP R5,R14,LSR #4 ;Does it match? BEQ %20armEmul ;Yes -- we actually execute B ae__next ;Nothing there -- advance PC ; --- Handle complementary conditions :-) --- 10armEmul ANDS R14,R6,R4 ;Get the bottom byte AND R5,R14,R2,LSR #28 ;Get the bits we want CMP R5,R14,LSR #4 ;Does it match? BEQ ae__next ;Yes -- don't do it then ANDS R14,R6,R4,LSR #8 ;Get the next byte BEQ %20armEmul ;Yes -- then execute it AND R5,R14,R2,LSR #28 ;Get the bits we want CMP R5,R14,LSR #4 ;Does it match? BEQ ae__next ;Yes -- don't do it then ANDS R14,R6,R4,LSR #16 ;Get the next byte BEQ %20armEmul ;Yes -- then execute it AND R5,R14,R2,LSR #28 ;Get the bits we want CMP R5,R14,LSR #4 ;Does it match? BEQ ae__next ;Yes -- don't do it then B %20armEmul ;Got all the way -- do it ; --- The condition table --- ; ; Format of each byte: ; ; Bits Meaning ; 0-3 AND mask for processor flags ; 4-7 Value to match for condition true ae__condTbl DCD &00000004 ;NE (!Z) DCD &00000002 ;CC (!C) DCD &00000008 ;PL (!N) DCD &00000001 ;VC (!V) DCD &00000244 ;LS (!C | Z) DCD &00001989 ;LT (N & !V) | (!N & V) DCD &00441989 ;LE (N&!V) | (!N&V) | Z DCD &00000010 ;NV -- ; --- Dispatch an instruction --- 20armEmul AND R14,R0,#&0C000000 ;Get the opcode field ADD PC,PC,R14,LSR #24 ;Just dispatch on that then DCB "TMA!" B ae__type00 B ae__type01 B ae__type10 B ae__type11 ; --- Normal return point --- ae__next AND R3,R2,#&FC000003 ;Look after PSR bits ADD R2,R2,#4 ;Bump PC on a bit BIC R2,R2,#&FC000003 ;Kill off PSR bits ORR R2,R2,R3 ;Bolt on the old PSR ae__return LDR R3,[R1,#15*4] ;Get the old PC AND R3,R3,#&3 ;Get just the mode flags AND R4,R2,#&3 ;Get the new mode flags CMP R4,R3 ;Has the mode changed? BEQ %90armEmul ;No -- jump ahead ; --- The processor mode has changed --- CMP R4,#2 ;Are we now in IRQ? CMPNE R4,#1 ;Or FIQ? ADREQ R0,ae__invMode ;Yes -- point to error LDMEQFD R13!,{R0-R12,R14} ;...load registers back ORREQS PC,R14,#V_flag ;...and return with error MOV R6,R13 ;Remember current R13 value ADD R5,R1,#13*4 ;Point to saved R13 LDMIA R5,{R13,R14} ;Load correct registers CMP R4,#0 ;Are we entering user mode? SWINE XOS_EnterOS ;No -- must be entering SVC TEQEQP PC,#0 ;Yes -- enter it then MOV R0,R0 ;A no-op -- keep ARM happy STMIA R5,{R13,R14} ;Store new register values MOV R13,R6 ;Restore my stack pointer LDR R14,[R13,#13*4] ;Load the link register BIC R14,R14,#3 ;Clear the current mode ORR R14,R14,R4 ;Enter the new mode STR R14,[R13,#13*4] ;Store back modified R14 ; --- Return as normal --- 90 STR R2,[R1,#15*4] ;Save the new PC back LDMFD R13!,{R0-R12,R14} ;Get registers back BICS PC,R14,#V_flag ;Return without error LTORG ae__invMode DCD 1 DCB "Sledgehammer can only operate in " DCB "User or Supervisor mode",0 ; --- ae__type00 --- ; ; On entry: R0 == instruction to execute ; R1 == pointer to register block ; ; On exit: R3-R12 mangled beyond redemption ; ; Use: Emulates a type 0 ARM instruction. ae__type00 ROUT AND R3,R0,#&0FC00000 ;Get the op code AND R4,R0,#&000000F0 ;We need these bits too ; --- See if it is a multiply instruction --- CMP R3,#0 ;Multiply instruction? CMPEQ R4,#&90 ;Double check BEQ ae__multiply ;Yes -- deal with it then ; --- Is it a SWP instruction --- CMP R3,#&01000000 ;Is this bit set? CMPNE R3,#&01400000 ;Or maybe this bit too? CMPEQ R4,#&90 ;Double check BEQ ae__swp ;Yes -- deal with it ; --- Is it an undefined operation? --- AND R3,R3,#&03000000 ;Get the correct bits AND R4,R4,#&00000090 ;Are these bits set too? CMP R3,#&01000000 ;Is opcode 0001? CMPEQ R4,#&00000090 ;And are these bits set? BNE ae__aluOp ;No -- deal with alu Op then DCD &E6000090 ;It's undefined, you know LTORG ; --- ae__type01 --- ; ; On entry: R0 == instruction to execute ; R1 == pointer to register block ; ; On exit: R3-R12 mangled beyond redemption ; ; Use: Emulates a type 1 ARM instruction. ae__type01 ROUT ; --- See if it's undefined --- TST R0,#(1<<25) ;Is bit 25 set? TSTNE R0,#(1<<4) ;And bit 4? BEQ ae__sDataTrans ;No -- data transfer then DCD &E6000090 ;It's undefined, you know LTORG ; --- ae__type10 --- ; ; On entry: R0 == instruction to execute ; R1 == pointer to register block ; ; On exit: R3-R12 mangled beyond redemption ; ; Use: Emulates a type 2 ARM instruction. ae__type10 ROUT TST R0,#(1<<25) ;Is this bit set? BNE ae__branch ;Yes -- it's a branch B ae__mTransfer ;Must be LDM/STM then LTORG ; --- ae__type11 --- ; ; On entry: R0 == instruction to execute ; R1 == pointer to register block ; ; On exit: R3-R12 mangled beyond redemption ; ; Use: Emulates a type 3 ARM instruction. ae__type11 ROUT TST R0,#(1<<25) ;Is this bit clear? BEQ ae__coDataTran ;Yes -- do co proc data trans AND R8,R0,#&0F000000 ;Get top nibble of opcode CMP R8,#&0F000000 ;Is it a SWI instruction? BEQ ae__swi ;Yes -- deal with it TST R0,#(1<<4) ;Is it a coregister transfer? BNE ae__coRegTran ;Yes -- deal with it B ae__coDataOp ;Do a co-proc data op LTORG ; --- ae__multiply --- ; ; On entry: R0 == instruction to do ; R1 == pointer to register block ; R2 == current PC value ; ; On exit: R2 updated ; R3-R12 trashed totally ; ; Use: Emulates a MUL/MLA instruction ae__multiply ROUT MOV R3,#&F ;A useful value AND R8,R0,R3 ;Get Rm AND R5,R3,R0,LSR #8 ;Get Rs AND R6,R3,R0,LSR #12 ;Get Rn too AND R7,R3,R0,LSR #16 ;Finally, get Rd CMP R8,#15 ;Is Rm really the PC? LDR R4,[R1,R8,LSL #2] ;Load my values ADDEQ R4,R4,#12 ;Yes -- bump it along CMP R5,#15 ;Is Rs really the PC? LDR R5,[R1,R5,LSL #2] ADDEQ R5,R5,#8 ;Yes -- bump it up BICEQ R5,R5,#&FC000003 ;And clear the PSR flags (!) CMP R6,#15 ;Is Rn really the PC? LDR R6,[R1,R6,LSL #2] ;Yes -- load accumulate ADDEQ R6,R6,#8 ;Yes -- bump it up CMP R7,R8 ;Is Rd == Rm? BEQ %10ae__multiply ;Yes -- then handle weirdly TST R0,#(1<<21) ;Is it an MLA? MLANE R8,R4,R5,R6 ;...and do the MLA MULEQ R8,R4,R5 ;No -- Do the multiply B %20ae__multiply ;And set up the results ; --- Berk put Rd == Rm -- emulate faithfully --- 10ae__multiply TST R0,#(1<<21) ;Is it an MLA? DCD &10246594 ;MLANE R4,R4,R5,R6 DCD &00040594 ;MULEQ R4,R4,R5 MOV R8,R4 ;And put result in nice reg ; --- Now write the results back --- 20ae__multiply CMP R7,#15 ;Is destination the PC? STRNE R8,[R1,R7,LSL #2] ;No -- then store the result BIC R2,R2,#Z_flag + N_flag ;Clear the flags we modify CMP R8,#0 ;Is the result zero? ORREQ R2,R2,#Z_flag ;Yes -- set the Z bit ORRMI R2,R2,#N_flag ;Yes -- set the N bit B ae__next ;And get the next instruction LTORG ; --- ae__branch --- ; ; On entry: R0 == instruction to do ; R1 == pointer to register block ; R2 == current PC value ; ; On exit: R3-R12 trashed totally ; ; Use: Emulates a branch instruction ae__branch ROUT ; --- First try to do the link business --- TST R0,#(1<<24) ;Is the L bit set? ADDNE R3,R2,#4 ;Bump on PC thing (overflow!) STRNE R3,[R1,#14*4] ;Save it in the reg block ; --- Now branch --- BIC R3,R0,#&FF000000 ;Clear unwanted bits AND R4,R2,#&FC000003 ;Look after PSR bits ADD R2,R2,R3,LSL #2 ;Bump PC on a bit ADD R2,R2,#8 ;And allow for `pipeline' BIC R2,R2,#&FC000003 ;Kill off PSR bits ORR R2,R2,R4 ;Bolt on the old PSR B ae__return ;And that's it for branch LTORG ; --- ae__aluOp --- ; ; On entry: R0 == instruction to do ; R1 == pointer to register block ; R2 == current PC value ; ; On exit: R2 updated ; R3-R12 trashed totally ; ; Use: Emulates a general ALU op instruction ae__aluOp ROUT ; --- Start building the instruction --- AND R3,R0,#&03F00000 ;Get out: ;Immediate flag ;Opcode number ;S bit (FWIW) ORR R3,R3,#&E0000000 ;Always execute this instr MOV R11,#0 ;Some interesting flags ; --- Work out the correct PC bump amount --- MOV R12,#8 ;By default it's 8 TST R0,#(1<<4) ;Is the reg-shift bit on? MOVNE R12,#12 ;Yes -- then actually it's 12 TST R0,#(1<<25) ;Unless the op's immediate MOVNE R12,#8 ;When it's actually 8 anyway ; --- Load the register values out --- MOV R14,#&F ;Get a register mask AND R4,R14,R0,LSR #12 ;Get Rd's number AND R10,R0,#&01800000 ;Get the top two opcode bis CMP R10,#&01000000 ;Is it a comparison instr? ORRNE R3,R3,#8<<12 ;No -- put in fake Rd value BNE %05ae__aluOp ;And branch Ahead ORR R11,R11,#1 ;Yes -- then remember this CMP R4,#15 ;Is a TEQP or the like? BNE %05ae__aluOp ;No -- branch ahead ORR R3,R3,#8<<12 ;Put in fake Rd value BIC R3,R3,#&01800000 ;Yes -- make a real ALU op ORR R11,R11,#2 ;Remember we did this AND R10,R0,#&00600000 ;Get the bottom two bits CMP R10,#&00600000 ;Is it now an RSB? EOREQ R3,R3,#&00E00000 ;Yes -- make it an ADD 05ae__aluOp AND R5,R14,R0,LSR #16 ;Get Rn's number too CMP R5,#15 ;Is Rn the PC? LDR R5,[R1,R5,LSL #2] ;Load the register value ADDEQ R5,R5,R12 ;Yes -- then bump the value BICEQ R5,R5,#&FC000003 ;And clear the PSR bits ORR R3,R3,#5<<16 ;Put in our fake Rd value ; --- Now deal with Op2 --- TST R0,#(1<<25) ;Is the operand immediate? ORRNE R8,R14,#&FF0 ;Build the number &FFF ANDNE R8,R0,R8 ;Get bottom three nibbles ORRNE R3,R3,R8 ;And bung 'em in my fake BNE %10ae__aluOp ;Yes -- deal with that then ; --- Handle Op2 registers --- AND R6,R14,R0 ;Get Rm's number out CMP R6,#15 ;Is Rm the PC? LDR R6,[R1,R6,LSL #2] ;Load the register value ADDEQ R6,R6,R12 ;Yes -- then bump the value ORR R3,R3,#6<<0 ;Put our fake Rm value in ; --- Is the shift immediate or reg-done? --- TST R0,#(1<<4) ;Check the reg-shift bit ANDEQ R7,R0,#&FF0 ;No -- get the gubbins out ORREQ R3,R3,R7 ;And put it in our fake instr BEQ %10ae__aluOp ;And skip register mangling AND R7,R14,R0,LSR #8 ;Get Rs's number out CMP R7,#15 ;Is Rs the PC? LDR R7,[R1,R7,LSL #2] ;Load the register value ADDEQ R7,R7,#8 ;Rs always bumped by 8 BICEQ R7,R7,#&FC000003 ;And PSR bits are stripped ORR R3,R3,#7<<8 ;Put that in there nicely AND R8,R0,#&0F0 ;Get the shift type out ORR R3,R3,R8 ;And put it in our fake instr ; --- Do the instruction --- 10ae__aluOp LDR R14,ae__retInstr ;Get the return instruction STMFD R13!,{R3,R14} ;Save them on the stack MOV R14,PC ;Get my current status AND R14,R14,#&0C000003 ;Get the special flags AND R10,R2,#&F0000000 ;Get all of his flags ORR R10,R10,R14 ;Mix them with my status MOV R14,PC ;Set up the return address ORRS PC,R13,R10 ;And call the instruction ADD R13,R13,#8 ;Restore the stack MOV R12,PC ;Look after processor status ; --- Now stash the result away --- TST R11,#1 ;Was it a compare instr? TSTNE R11,#2 ;Is Rd the program counter? BNE %50ae__aluOp ;Yes -- P type comparison CMP R4,#15 ;Was destination the PC? BEQ %40ae__aluOp ;Yes -- this is special then TST R11,#1 ;Was it a compare instr? STREQ R8,[R1,R4,LSL#2] ;Store the result away TST R0,#(1<<20) ;Is the `S' bit set? ANDNE R12,R12,#&F0000000 ;Get the status flags BICNE R2,R2,#&F0000000 ;Clear the current ones ORRNE R2,R2,R12 ;Set the flag appropriately B ae__next ;And branch to next instr ; --- The destination was PC --- 40ae__aluOp EOR R14,R2,R8 ;Get bits that WILL change TST R0,#(1<<20) ;Is the `S' bit set? BICEQ R14,R14,#&FC000003 ;No -- don't update PSR TST R2,#3 ;Are we in user mode? BICEQ R14,R14,#&0C000003 ;Yes -- update top 4 bits EOR R2,R2,R14 ;Munge bits we really want B ae__return ;Return happily!@? ; --- There was a `P' suffix thingy --- 50ae__aluOp EOR R14,R2,R8 ;Get bits that WILL change AND R14,R14,#&FC000003 ;Clear the PC bits TST R2,#3 ;Are we in user mode? BICEQ R14,R14,#&0C000003 ;Yes -- update top 4 bits EOR R2,R2,R14 ;Munge bits we really want B ae__next ;Return happily!@? ae__retInstr MOV PC,R14 ;Return, leave status alone LTORG ; --- ae__swp --- ; ; On entry: R0 == instruction to do ; R1 == pointer to register block ; R2 == current PC value ; ; On exit: R2 updated ; R3-R12 trashed totally ; ; Use: Emulates a SWP instruction ae__swp ROUT B ae__next ;Ignore it LTORG ; --- ae__sDataTrans --- ; ; On entry: R0 == instruction to do ; R1 == pointer to register block ; R2 == current PC value ; ; On exit: R2 updated ; R3-R12 trashed totally ; ; Use: Emulates LDR/STR instructions ae__sDataTrans ROUT ; --- Load the register values out --- MOV R14,#&F ;Get a register mask MOV R11,#0 ;Some flags and things AND R4,R14,R0,LSR #12 ;Get Rd's number AND R5,R14,R0,LSR #16 ;Get Rn's number too CMP R5,#15 ;Is Rn the PC? LDR R6,[R1,R5,LSL #2] ;Load the register value ADDEQ R6,R6,#8 ;Yes -- then bump the value BICEQ R6,R6,#&FC000003 ;And clear the PSR bits CMP R4,R5 ;Is Rd==Rn? ORREQ R11,R11,#4 ;Yes -- remember this ; --- Now deal with Op2 --- TST R0,#(1<<25) ;Is the operand immediate? ORREQ R8,R14,#&FF0 ;Build the number &FFF ANDEQ R8,R0,R8 ;Get bottom three nibbles BEQ %10ae__sDataTrans ;And skip register mangling AND R7,R14,R0 ;Get the offset register CMP R7,#15 ;Is it the PC? LDR R7,[R1,R7,LSL #2] ;Load the register value ADDEQ R7,R7,#8 ;Yes -- then bump the value AND R8,R0,#&FF0 ;Get the constant shift bit ORR R8,R8,#&E1000000 ;Do the shift by... ORR R8,R8,#&00A00000 ;building MOV R8,R7,shift ORR R8,R8,#&00008000 ORR R8,R8,#&00000007 LDR R14,ae__retInstr ;Get the return instruction STMFD R13!,{R8,R14} ;Save them on the stack AND R14,PC,#&0C000003 ;Get the special flags AND R10,R2,#&F0000000 ;Get all of his flags ORR R10,R10,R14 ;Mix them with my status MOV R14,PC ;Set up the return address ORRS PC,R13,R10 ;Execute the code nicely ADD R13,R13,#8 ;Recover the stack I used ; --- R8 contains the offset -- get the address --- 10 MOV R3,R0 ;Preserve R0 for a bit TST R3,#(1<<23) ;Is the op an addition? RSBEQ R8,R8,#0 ;No -- then make it negative TST R3,#(1<<24) ;Is the op pre-indexed? ADDNE R7,R6,R8 ;Yes -- form the address MOVEQ R7,R6 ;Otherwise just use base BIC R0,R7,#3 ;Word align the address AND R7,R7,#3 ;Get the non-word-alignedness BL ae__translateAddr ;Translate the address ORR R7,R7,R0 ;Get the translated address MOV R0,R3 ;Put instruction back in R0 ; --- Now work out whether it's a load or store --- TST R0,#(1<<20) ;Is it a load then? BEQ %20ae__sDataTrans ;No -- then do a store TST R0,#(1<<22) ;Does he only want a byte? LDREQ R7,[R7,#0] ;No -- load a word value LDRNEB R7,[R7,#0] ;Yes -- load a byte value ; --- Stuff the loaded value into a `register' --- CMP R4,#15 ;Is destination the PC? ORREQ R11,R11,#1 ;Yes -- say we've updated it BICEQ R7,R7,#&FC000003 ;Clear the PSR bits ANDEQ R2,R2,#&FC000003 ;Keep his PSR bits ORREQ R2,R2,R7 ;And mix 'em together STRNE R7,[R1,R4,LSL #2] ;Otherwise store the value B %50ae__sDataTrans ;Tidy everything up nicely ; --- Handle a store operation --- 20 CMP R4,#15 ;Is the register the PC? LDR R4,[R1,R4,LSL #2] ;Load the correct value out ADDEQ R4,R4,#12 ;Bump the value along a bit TST R0,#(1<<22) ;Does he only want a byte? STREQ R4,[R7,#0] ;No -- store the whole word STRNEB R4,[R7,#0] ;Yes -- just store the byte ; --- Finish off -- do writeback maybe --- 50 MVN R14,R0 ;Toggle all the bits TST R14,#(1<<24) ;Is the postindexed bit on? TSTEQ R0,#(1<<21) ;Or is the writeback bit on? BEQ %60ae__sDataTrans ;No writeback -- skip on TST R11,#4 ;Is Rd==Rn? BNE %60ae__sDataTrans ;Yes -- don't corrupt Rd ; --- Perform the writeback --- ADD R7,R6,R8 ;Get the resulting address CMP R5,#15 ;Is Rn the program counter ORREQ R11,R11,#1 ;Yes -- say we've updated it BICEQ R7,R7,#&FC000003 ;Clear the PSR bits ANDEQ R2,R2,#&FC000003 ;Keep his PSR bits ORREQ R2,R2,R7 ;And mix 'em together STRNE R7,[R1,R5,LSL #2] ;Otherwise store the value ; --- Finally return to the main thing --- 60 TST R11,#1 ;Did we modify the PC? BNE ae__return ;Yes -- don't advance PC BEQ ae__next ;Otherwise get next instr LTORG GBLA count ; --- ae__mTransfer --- ; ; On entry: R0 == instruction to do ; R1 == pointer to register block ; R2 == current PC value ; ; On exit: R3-R12 trashed totally ; ; Use: Emulates an LDM/STM instruction ae__mTransfer ROUT ; --- Load the register values out --- MOV R14,#&F ;Get a register mask MOV R11,#0 ;Some flags and things AND R4,R14,R0,LSR #16 ;Get Rn's number CMP R4,#15 ;Is Rn the PC? LDR R5,[R1,R4,LSL #2] ;Load the register value ADDEQ R5,R5,#8 ;Yes -- then bump the value ; --- We need to know how may to transfer --- MOV R7,#0 ;The count so far count SETA 0 WHILE count<=15 TST R0,#1<