cpu: implement JAL/JALR
This commit is contained in:
parent
77a155fd79
commit
236337c219
46
cpu.go
46
cpu.go
@ -53,11 +53,46 @@ func (c *CPUState) Step() error {
|
||||
|
||||
case 0b01101111:
|
||||
// JAL
|
||||
panic("todo")
|
||||
// The jump and link (JAL) instruction uses the J-type format, where the J-immediate encodes a
|
||||
// signed offset in multiples of 2 bytes. The offset is sign-extended and added to the address of the
|
||||
// jump instruction to form the jump target address. Jumps can therefore target a ±1 MiB range.
|
||||
// JAL stores the address of the instruction that follows the JAL (pc+4) into register rd. The standard
|
||||
// software calling convention uses x1 as the return address register and x5 as an alternate link register.
|
||||
// Plain unconditional jumps (assembler pseudoinstruction J) are encoded as a JAL with rd=x0.
|
||||
|
||||
var imm uint32 = (opcode >> 12) & 0b11111111111111111111
|
||||
if imm&0b10000000000000000000 != 0 {
|
||||
imm |= 0b11111111111100000000000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
// 2^20 has ~1MiB unsigned range, but it's in multiples of two bytes
|
||||
offset := int32(imm) * 2
|
||||
|
||||
c.Registers[opcode_rd(opcode)] = c.Pc + 4
|
||||
c.Pc = uint32(int32(c.Pc) + offset)
|
||||
return nil // Don't fallthrough, or else we increment Pc again
|
||||
|
||||
case 0b1100111:
|
||||
// JALR
|
||||
panic("todo")
|
||||
funct3 := (opcode >> 12) & 0b111
|
||||
if funct3 != 0b000 {
|
||||
return ErrInvalidOpcode{}
|
||||
}
|
||||
|
||||
// The indirect jump instruction JALR (jump and link register) uses the I-type encoding. The target
|
||||
// address is obtained by adding the sign-extended 12-bit I-immediate to the register rs1, then setting
|
||||
// the least-significant bit of the result to zero. The address of the instruction following the jump
|
||||
// (pc+4) is written to register rd. Register x0 can be used as the destination if the result is not
|
||||
// required.
|
||||
|
||||
var imm uint32 = (opcode >> 20) & 0b111111111111
|
||||
if imm&0b100000000000 != 0 {
|
||||
imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
c.Registers[opcode_rd(opcode)] = c.Pc + 4
|
||||
c.Pc = (c.Registers[opcode_rs1(opcode)] + imm) & 0b11111111111111111111111111111110
|
||||
return nil // Don't fallthrough, or else we increment Pc again
|
||||
|
||||
case 0b1100011:
|
||||
// BEQ/BNE/BLT/BGE/BLTU/BGEU
|
||||
@ -82,10 +117,13 @@ func (c *CPUState) Step() error {
|
||||
|
||||
switch funct3 {
|
||||
case 0b000:
|
||||
// ADDI
|
||||
// ADDI/NOP
|
||||
// ADDI adds the sign-extended 12-bit immediate to register rs1. Arithmetic overflow is ignored and
|
||||
// the result is simply the low XLEN bits of the result. ADDI rd, rs1, 0 is used to implement the MV
|
||||
// rd, rs1 assembler pseudoinstruction.
|
||||
//
|
||||
// The NOP instruction does not change any architecturally visible state, except for advancing the
|
||||
// pc and incrementing any applicable performance counters. NOP is encoded as ADDI x0, x0, 0.
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) + imm
|
||||
|
||||
case 0b010:
|
||||
@ -274,7 +312,7 @@ func (c *CPUState) Step() error {
|
||||
}
|
||||
|
||||
// Step program-counter forward
|
||||
c.Pc++
|
||||
c.Pc += 4
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user