mirror of https://github.com/bsnes-emu/bsnes.git
373 lines
9.5 KiB
C++
373 lines
9.5 KiB
C++
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;
|
|
}
|