cpu: implement BEQ/BNE/BLT/BGE/BLTU/BGEU
This commit is contained in:
parent
236337c219
commit
c46b1e515d
75
cpu.go
75
cpu.go
@ -20,7 +20,7 @@ func NewCPU(w World) CPUState {
|
||||
|
||||
func opcode_rd(opcode uint32) uint32 { return (opcode >> 7) & 0b11111 }
|
||||
func opcode_rs1(opcode uint32) uint32 { return (opcode >> 15) & 0b11111 } // 2^5 is 32 for 32 registers
|
||||
func opcode_rs2(opcode uint32) uint32 { return (opcode >> 19) & 0b11111 }
|
||||
func opcode_rs2(opcode uint32) uint32 { return (opcode >> 20) & 0b11111 }
|
||||
|
||||
func (c *CPUState) ReadRegister(r uint32) uint32 {
|
||||
if r == 0 {
|
||||
@ -61,9 +61,9 @@ func (c *CPUState) Step() error {
|
||||
// Plain unconditional jumps (assembler pseudoinstruction J) are encoded as a JAL with rd=x0.
|
||||
|
||||
var imm uint32 = (opcode >> 12) & 0b11111111111111111111
|
||||
if imm&0b10000000000000000000 != 0 {
|
||||
if imm&0b10000000000000000000 != 0 { // 20th position
|
||||
imm |= 0b11111111111100000000000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
} // FIXME the encoding seems weirder than this??
|
||||
|
||||
// 2^20 has ~1MiB unsigned range, but it's in multiples of two bytes
|
||||
offset := int32(imm) * 2
|
||||
@ -86,7 +86,7 @@ func (c *CPUState) Step() error {
|
||||
// required.
|
||||
|
||||
var imm uint32 = (opcode >> 20) & 0b111111111111
|
||||
if imm&0b100000000000 != 0 {
|
||||
if imm&0b100000000000 != 0 { // 12th position
|
||||
imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
@ -96,7 +96,70 @@ func (c *CPUState) Step() error {
|
||||
|
||||
case 0b1100011:
|
||||
// BEQ/BNE/BLT/BGE/BLTU/BGEU
|
||||
panic("todo")
|
||||
// All branch instructions use the B-type instruction format. The 12-bit B-immediate encodes signed
|
||||
// offsets in multiples of 2 bytes. The offset is sign-extended and added to the address of the branch
|
||||
// instruction to give the target address. The conditional branch range is ±4 KiB.
|
||||
|
||||
var imm uint32 = (opcode>>6)&0b11111 | (((opcode >> 25) & 0b11111) << 5) // FIXME the encoding seems weirder than this??
|
||||
if imm&0b100000000000 != 0 { // 12th position
|
||||
imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
var offset int32 = int32(imm) * 2
|
||||
|
||||
// Branch instructions compare two registers. BEQ and BNE take the branch if registers rs1 and rs2
|
||||
// are equal or unequal respectively. BLT and BLTU take the branch if rs1 is less than rs2, using
|
||||
// signed and unsigned comparison respectively. BGE and BGEU take the branch if rs1 is greater
|
||||
// than or equal to rs2, using signed and unsigned comparison respectively. Note, BGT, BGTU,
|
||||
// BLE, and BLEU can be synthesized by reversing the operands to BLT, BLTU, BGE, and BGEU,
|
||||
// respectively.
|
||||
funct3 := (opcode >> 12) & 0b111
|
||||
switch funct3 {
|
||||
case 0b000:
|
||||
// BEQ
|
||||
if c.ReadRegister(opcode_rs1(opcode)) == c.ReadRegister(opcode_rs2(opcode)) {
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough to regular Pc advance
|
||||
}
|
||||
|
||||
case 0b001:
|
||||
// BNE
|
||||
if c.ReadRegister(opcode_rs1(opcode)) != c.ReadRegister(opcode_rs2(opcode)) {
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough to regular Pc advance
|
||||
}
|
||||
|
||||
case 0b100:
|
||||
// BLT
|
||||
if int32(c.ReadRegister(opcode_rs1(opcode))) < int32(c.ReadRegister(opcode_rs2(opcode))) {
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough to regular Pc advance
|
||||
}
|
||||
|
||||
case 0b101:
|
||||
// BGE
|
||||
if int32(c.ReadRegister(opcode_rs1(opcode))) >= int32(c.ReadRegister(opcode_rs2(opcode))) {
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough to regular Pc advance
|
||||
}
|
||||
|
||||
case 0b110:
|
||||
// BLTU
|
||||
if c.ReadRegister(opcode_rs1(opcode)) < c.ReadRegister(opcode_rs2(opcode)) {
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough to regular Pc advance
|
||||
}
|
||||
|
||||
case 0b111:
|
||||
// BGEU
|
||||
if c.ReadRegister(opcode_rs1(opcode)) >= c.ReadRegister(opcode_rs2(opcode)) {
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough to regular Pc advance
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrInvalidOpcode{}
|
||||
}
|
||||
|
||||
case 0b0000011:
|
||||
// LB/LH/LW/LBU/LHU
|
||||
@ -111,7 +174,7 @@ func (c *CPUState) Step() error {
|
||||
funct3 := (opcode >> 12) & 0b111
|
||||
|
||||
var imm uint32 = (opcode >> 20) & 0b111111111111
|
||||
if imm&0b100000000000 != 0 {
|
||||
if imm&0b100000000000 != 0 { // 12th position
|
||||
imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user