I have wrote a riscv64 emulator but I have an issue on offset calculation for branch instructions (especially for bge).
For me , the formula to calculate offset to add to pc when condition is meet is : PC = PC+IMM; Immediate is extract from instruction : there is my C code :
void BNE(unsigned long instr) {
unsigned long IMM1 = instr >>7 & 0b11111;
unsigned long IMM2 = instr >> 25 & 0b1111111;
unsigned long first = IMM1 & 0b1; // 11 eme bits IMM\[11\]
unsigned long second = IMM1 >> 1 & 0b1111; // IMM\[4:1\]
unsigned long third = IMM2 & 0b111111; //imm\[10:5\]
unsigned long fourth = IMM2 >> 6 & 0b1; // IMM\[12\]
// after extract assemble :
unsigned long imm = second | third << 5 | first <<7 | fourth << 9; // \<- I think here is the problem
}
When the program get this simply program code :
// I have simplified the code :
00000000000100e8 \<init\>:
10130: fd843783 ld a5,-40(s0)
1018c: fae7d2e3 bge a5,a4,10130 \<init+0x48\> # note that the condition is always met on my code
I got : 0x7a2.
The pc adress is : 0x1018c
When I add 0x1018c to 0x7a2 , I got :0x1092E There is an issue but I don't know where. I think the extract on imm variable have some issue.
I have tried to modify the imm variable bits shift and mask. But I don't have any luck.
I have try this :
unsigned long imm = second | third << 4 | first << 5 | fourth << 6; // output wrong data
unsigned long imm = second | third << 6 | first << 11 | fourth << 12; // output the same data as imm
unsigned long imm = second | third << 5 | first << 7 | fourth << 9; // actual version
unsigned long imm = second | third << 6 | first << 7 | fourth <<8; // output the same data as imm