bsnes/higan/processor/arm/instructions-thumb.cpp

373 lines
9.5 KiB
C++
Raw Normal View History

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;
}