cpu: implement BEQ/BNE/BLT/BGE/BLTU/BGEU

This commit is contained in:
mappu 2022-12-28 13:23:30 +13:00
parent 236337c219
commit c46b1e515d

75
cpu.go
View File

@ -20,7 +20,7 @@ func NewCPU(w World) CPUState {
func opcode_rd(opcode uint32) uint32 { return (opcode >> 7) & 0b11111 } 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_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 { func (c *CPUState) ReadRegister(r uint32) uint32 {
if r == 0 { 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. // Plain unconditional jumps (assembler pseudoinstruction J) are encoded as a JAL with rd=x0.
var imm uint32 = (opcode >> 12) & 0b11111111111111111111 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 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 // 2^20 has ~1MiB unsigned range, but it's in multiples of two bytes
offset := int32(imm) * 2 offset := int32(imm) * 2
@ -86,7 +86,7 @@ func (c *CPUState) Step() error {
// required. // required.
var imm uint32 = (opcode >> 20) & 0b111111111111 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 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: case 0b1100011:
// BEQ/BNE/BLT/BGE/BLTU/BGEU // 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: case 0b0000011:
// LB/LH/LW/LBU/LHU // LB/LH/LW/LBU/LHU
@ -111,7 +174,7 @@ func (c *CPUState) Step() error {
funct3 := (opcode >> 12) & 0b111 funct3 := (opcode >> 12) & 0b111
var imm uint32 = (opcode >> 20) & 0b111111111111 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 imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
} }