auto ARM::thumb_opcode(uint4 opcode, uint4 d, uint4 m) { switch(opcode) { case 0: r(d) = bit(r(d) & r(m)); break; //AND case 1: r(d) = bit(r(d) ^ r(m)); break; //EOR case 2: r(d) = bit(lsl(r(d), r(m))); break; //LSL case 3: r(d) = bit(lsr(r(d), r(m))); break; //LSR case 4: r(d) = bit(asr(r(d), r(m))); break; //ASR case 5: r(d) = add(r(d), r(m), cpsr().c); break; //ADC case 6: r(d) = sub(r(d), r(m), cpsr().c); break; //SBC case 7: r(d) = bit(ror(r(d), r(m))); break; //ROR case 8: bit(r(d) & r(m)); break; //TST case 9: r(d) = sub(0, r(m), 1); break; //NEG case 10: sub(r(d), r(m), 1); break; //CMP case 11: add(r(d), r(m), 0); break; //CMN case 12: r(d) = bit(r(d) | r(m)); break; //ORR case 13: r(d) = mul(0, r(m), r(d)); break; //MUL case 14: r(d) = bit(r(d) & ~r(m)); break; //BIC case 15: r(d) = bit(~r(m)); break; //MVN } } //(add,sub) rd,rn,rm //0001 10om mmnn nddd //o = opcode //m = rm //n = rn //d = rd auto ARM::thumb_op_adjust_register() { uint1 opcode = instruction() >> 9; uint3 m = instruction() >> 6; uint3 n = instruction() >> 3; uint3 d = instruction() >> 0; switch(opcode) { case 0: r(d) = add(r(n), r(m), 0); break; case 1: r(d) = sub(r(n), r(m), 1); break; } } //(add,sub) rd,rn,#immediate //0001 11oi iinn nddd //o = opcode //i = immediate //n = rn //d = rd auto ARM::thumb_op_adjust_immediate() { uint1 opcode = instruction() >> 9; uint3 immediate = instruction() >> 6; uint3 n = instruction() >> 3; uint3 d = instruction() >> 0; switch(opcode) { case 0: r(d) = add(r(n), immediate, 0); break; case 1: r(d) = sub(r(n), immediate, 1); break; } } //(lsl,lsr,asr) rd,rm,#immediate //000o oiii iimm mddd //o = opcode //i = immediate //m = rm //d = rd auto ARM::thumb_op_shift_immediate() { uint2 opcode = instruction() >> 11; uint5 immediate = instruction() >> 6; uint3 m = instruction() >> 3; uint3 d = instruction() >> 0; switch(opcode) { case 0: r(d) = bit(lsl(r(m), immediate)); break; case 1: r(d) = bit(lsr(r(m), immediate == 0 ? 32u : (unsigned)immediate)); break; case 2: r(d) = bit(asr(r(m), immediate == 0 ? 32u : (unsigned)immediate)); break; } } //(mov,cmp,add,sub) (rd,rn),#immediate //001o orrr iiii iiii //o = opcode //r = (rd,rn) //i = immediate auto ARM::thumb_op_immediate() { uint2 opcode = instruction() >> 11; uint3 d = instruction() >> 8; uint8 immediate = instruction(); switch(opcode) { case 0: r(d) = bit( immediate ); break; case 1: sub(r(d), immediate, 1); break; case 2: r(d) = add(r(d), immediate, 0); break; case 3: r(d) = sub(r(d), immediate, 1); break; } } //{opcode} rd,rm //0100 00oo oomm mddd //o = opcode //m = rm //d = rd auto ARM::thumb_op_alu() { uint4 opcode = instruction() >> 6; uint3 m = instruction() >> 3; uint3 d = instruction(); thumb_opcode(opcode, d, m); } //bx rm //0100 0111 0mmm m--- //m = rm auto ARM::thumb_op_branch_exchange() { uint4 m = instruction() >> 3; cpsr().t = r(m) & 1; r(15) = r(m); } //{opcode} rd,rm //0100 01oo DMmm mddd //o = opcode //M:m = rm //D:d = rd auto ARM::thumb_op_alu_hi() { uint2 opcode = instruction() >> 8; uint4 m = instruction() >> 3; uint3 dl = instruction(); uint1 dh = instruction() >> 7; uint4 d = (dh << 3) + (dl << 0); switch(opcode) { case 0: r(d) = r(d) + r(m); break; //ADD (does not modify flags) case 1: sub(r(d), r(m), 1); break; //SUB case 2: r(d) = r(m); break; //MOV (does not modify flags) } } //ldr rd,[pc,#+/-offset] //0100 1ddd oooo oooo //d = rd //o = offset auto ARM::thumb_op_load_literal() { uint3 d = instruction() >> 8; uint8 displacement = instruction(); unsigned rm = (r(15) & ~3) + displacement * 4; r(d) = load(Word | Nonsequential, rm); } //(ld(r,s),str){b,h} rd,[rn,rm] //0101 ooom mmnn nddd //o = opcode //m = rm //n = rn //d = rd auto ARM::thumb_op_move_register_offset() { uint3 opcode = instruction() >> 9; uint3 m = instruction() >> 6; uint3 n = instruction() >> 3; uint3 d = instruction() >> 0; switch(opcode) { case 0: store(Word | Nonsequential, r(n) + r(m), r(d)); break; //STR case 1: store(Half | Nonsequential, r(n) + r(m), r(d)); break; //STRH case 2: store(Byte | Nonsequential, r(n) + r(m), r(d)); break; //STRB case 3: r(d) = load(Byte | Nonsequential | Signed, r(n) + r(m)); break; //LDSB case 4: r(d) = load(Word | Nonsequential, r(n) + r(m)); break; //LDR case 5: r(d) = load(Half | Nonsequential, r(n) + r(m)); break; //LDRH case 6: r(d) = load(Byte | Nonsequential, r(n) + r(m)); break; //LDRB case 7: r(d) = load(Half | Nonsequential | Signed, r(n) + r(m)); break; //LDSH } } //(ldr,str) rd,[rn,#offset] //0110 looo oonn nddd //l = load //o = offset //n = rn //d = rd auto ARM::thumb_op_move_word_immediate() { uint1 l = instruction() >> 11; uint5 offset = instruction() >> 6; uint3 n = instruction() >> 3; uint3 d = instruction() >> 0; if(l == 1) r(d) = load(Word | Nonsequential, r(n) + offset * 4); if(l == 0) store(Word | Nonsequential, r(n) + offset * 4, r(d)); } //(ldr,str)b rd,[rn,#offset] //0111 looo oonn nddd //l = load //o = offset //n = rn //d = rd auto ARM::thumb_op_move_byte_immediate() { uint1 l = instruction() >> 11; uint5 offset = instruction() >> 6; uint3 n = instruction() >> 3; uint3 d = instruction() >> 0; if(l == 1) r(d) = load(Byte | Nonsequential, r(n) + offset); if(l == 0) store(Byte | Nonsequential, r(n) + offset, r(d)); } //(ldr,str)h rd,[rn,#offset] //1000 looo oonn nddd //l = load //o = offset //n = rn //d = rd auto ARM::thumb_op_move_half_immediate() { uint1 l = instruction() >> 11; uint5 offset = instruction() >> 6; uint3 n = instruction() >> 3; uint3 d = instruction() >> 0; if(l == 1) r(d) = load(Half | Nonsequential, r(n) + offset * 2); if(l == 0) store(Half | Nonsequential, r(n) + offset * 2, r(d)); } //(ldr,str) rd,[sp,#immediate] //1001 oddd iiii iiii //l = load //d = rd //i = immediate auto ARM::thumb_op_move_stack() { uint1 l = instruction() >> 11; uint3 d = instruction() >> 8; uint8 immediate = instruction(); if(l == 1) r(d) = load(Word | Nonsequential, r(13) + immediate * 4); if(l == 0) store(Word | Nonsequential, r(13) + immediate * 4, r(d)); } //add rd,{pc,sp},#immediate //1010 sddd iiii iiii //s = sp (0 = pc) //d = rd //i = immediate auto ARM::thumb_op_add_register_hi() { uint1 sp = instruction() >> 11; uint3 d = instruction() >> 8; uint8 immediate = instruction(); if(sp == 0) r(d) = (r(15) & ~2) + immediate * 4; if(sp == 1) r(d) = r(13) + immediate * 4; } //(add,sub) sp,#immediate //1011 0000 oiii iiii //o = opcode //i = immediate auto ARM::thumb_op_adjust_stack() { uint1 opcode = instruction() >> 7; uint7 immediate = instruction(); if(opcode == 0) r(13) += immediate * 4; if(opcode == 1) r(13) -= immediate * 4; } //push {r...{,lr}} //pop {r...{,pc}} //1011 o10r llll llll //o = opcode (0 = push, 1 = pop) //r = push lr -or- pop pc //l = register list auto ARM::thumb_op_stack_multiple() { uint1 l = instruction() >> 11; uint1 branch = instruction() >> 8; uint8 list = instruction(); uint32 sp = 0; if(l == 1) sp = r(13); if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4; unsigned sequential = Nonsequential; for(unsigned m = 0; m < 8; m++) { if(list & 1 << m) { if(l == 1) r(m) = read(Word | sequential, sp); //POP if(l == 0) write(Word | sequential, sp, r(m)); //PUSH sp += 4; sequential = Sequential; } } if(branch) { //note: ARMv5+ POP sets cpsr().t if(l == 1) r(15) = read(Word | Nonsequential, sp); //POP if(l == 0) write(Word | Nonsequential, sp, r(14)); //PUSH sp += 4; } if(l == 1) { idle(); r(13) += (bit::count(list) + branch) * 4; } else { pipeline.nonsequential = true; r(13) -= (bit::count(list) + branch) * 4; } } //(ldmia,stmia) rn!,{r...} //1100 lnnn llll llll //l = load (0 = save) //n = rn //l = register list auto ARM::thumb_op_move_multiple() { uint1 l = instruction() >> 11; uint3 n = instruction() >> 8; uint8 list = instruction(); uint32 rn = r(n); //rn may be in register list; so we must cache it for(unsigned m = 0; m < 8; m++) { if(list & 1 << m) { if(l == 1) r(m) = read(Word | Nonsequential, rn); //LDMIA if(l == 0) write(Word | Nonsequential, rn, r(m)); //STMIA rn += 4; } } if(l == 0 || (list & 1 << n) == 0) r(n) = rn; //update rn on save or when not in register list if(l == 1) idle(); } //swi #immediate //1101 1111 iiii iiii //i = immediate auto ARM::thumb_op_software_interrupt() { uint8 immediate = instruction(); vector(0x00000008, Processor::Mode::SVC); } //b{condition} //1101 cccc dddd dddd //c = condition //d = displacement auto ARM::thumb_op_branch_conditional() { uint4 flagcondition = instruction() >> 8; int8 displacement = instruction(); if(condition(flagcondition) == false) return; r(15) = r(15) + displacement * 2; } //b address //1110 0ooo oooo oooo //o = offset auto ARM::thumb_op_branch_short() { int11 displacement = instruction(); r(15) += displacement * 2; } //bl address //1111 0ooo oooo oooo //o = offset auto ARM::thumb_op_branch_long_prefix() { int11 offsethi = instruction(); r(14) = r(15) + ((offsethi * 2) << 11); } //bl address //1111 1ooo oooo oooo //o = offset auto ARM::thumb_op_branch_long_suffix() { uint11 offsetlo = instruction(); r(15) = r(14) + (offsetlo * 2); r(14) = pipeline.decode.address | 1; }