diff --git a/cpu.go b/cpu.go index 98f0473..70cf04d 100644 --- a/cpu.go +++ b/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 }