cpu: implement LB/LH/LW/LBU/LHU
This commit is contained in:
parent
49b78a4b75
commit
4654a5bc72
71
cpu.go
71
cpu.go
@ -163,6 +163,77 @@ func (c *CPUState) Step() error {
|
||||
|
||||
case 0b0000011:
|
||||
// LB/LH/LW/LBU/LHU
|
||||
// The effective address is obtained by adding register rs1
|
||||
// to the sign-extended 12-bit offset. Loads copy a value from memory to register rd.
|
||||
|
||||
var imm uint32 = (opcode >> 20) & 0b111111111111
|
||||
if imm&0b100000000000 != 0 { // 12th position
|
||||
imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
memAddr := imm + c.Registers[opcode_rs1(opcode)]
|
||||
|
||||
// The LW instruction loads a 32-bit value from memory into rd. LH loads a 16-bit value from memory,
|
||||
// then sign-extends to 32-bits before storing in rd. LHU loads a 16-bit value from memory but then
|
||||
// zero extends to 32-bits before storing in rd. LB and LBU are defined analogously for 8-bit values.
|
||||
|
||||
funct3 := (opcode >> 12) & 0b111
|
||||
switch funct3 {
|
||||
case 0b000:
|
||||
// LB
|
||||
memVal, err := c.w.ReadByte(memAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storeVal := uint32(memVal)
|
||||
if storeVal&0b10000000 != 0 { // sign extend / 8th position
|
||||
storeVal |= 0b11111111111111111111111100000000
|
||||
}
|
||||
c.Registers[opcode_rd(opcode)] = storeVal
|
||||
|
||||
case 0b001:
|
||||
// LH
|
||||
memVal, err := c.w.Read16(memAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storeVal := uint32(memVal)
|
||||
if storeVal&0b1000000000000000 != 0 { // sign extend / 16th position
|
||||
storeVal |= 0b11111111111111110000000000000000
|
||||
}
|
||||
c.Registers[opcode_rd(opcode)] = storeVal
|
||||
|
||||
case 0b010:
|
||||
// LW
|
||||
memVal, err := c.w.Read32(memAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Registers[opcode_rd(opcode)] = memVal
|
||||
|
||||
case 0b100:
|
||||
// LBU
|
||||
memVal, err := c.w.ReadByte(memAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Registers[opcode_rd(opcode)] = uint32(memVal)
|
||||
|
||||
case 0b101:
|
||||
// LHU
|
||||
memVal, err := c.w.Read16(memAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Registers[opcode_rd(opcode)] = uint32(memVal)
|
||||
|
||||
default:
|
||||
return ErrInvalidOpcode{}
|
||||
}
|
||||
panic("todo")
|
||||
|
||||
case 0b0100011:
|
||||
|
@ -4,8 +4,11 @@ package riscvemu
|
||||
// code is run.
|
||||
type EEI interface {
|
||||
ReadByte(addr uint32) (byte, error)
|
||||
Read16(addr uint32) (uint16, error)
|
||||
Read32(addr uint32) (uint32, error)
|
||||
|
||||
WriteByte(addr uint32, value byte) error
|
||||
Write16(addr uint32, value uint16) error
|
||||
Write32(addr, value uint32) error
|
||||
|
||||
Syscall()
|
@ -20,6 +20,15 @@ func (ve *VirtualEEI) ReadByte(addr uint32) (byte, error) {
|
||||
return page[addr&0b111111111111], nil
|
||||
}
|
||||
|
||||
func (ve *VirtualEEI) Read16(addr uint32) (uint16, error) {
|
||||
// n.b. will panic on overflow
|
||||
|
||||
page, _ := ve.memPages[addr>>12]
|
||||
inPageAddr := addr & 0b111111111111
|
||||
|
||||
return (uint16(page[inPageAddr]) << 8) | uint16(page[inPageAddr+1]), nil
|
||||
}
|
||||
|
||||
func (ve *VirtualEEI) Read32(addr uint32) (uint32, error) {
|
||||
// n.b. will panic on overflow
|
||||
// RISC-V allows 32-bit load/stores to be implemented as either little-endian
|
||||
@ -44,8 +53,25 @@ func (ve *VirtualEEI) WriteByte(addr uint32, value byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ve *VirtualEEI) Write16(addr uint32, value uint16) error {
|
||||
// n.b. will panic on split page boundary
|
||||
|
||||
page, ok := ve.memPages[addr>>12]
|
||||
inPageAddr := addr & 0b111111111111
|
||||
|
||||
// Slices are reference-types, this will write-through into ve.memPages
|
||||
page[inPageAddr] = byte((value >> 8) & 0b11111111)
|
||||
page[inPageAddr+1] = byte(value & 0b11111111)
|
||||
|
||||
if !ok {
|
||||
ve.memPages[addr>>12] = page
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ve *VirtualEEI) Write32(addr, value uint32) error {
|
||||
// n.b. will panic on overflow
|
||||
// n.b. will panic on split page boundary
|
||||
|
||||
page, ok := ve.memPages[addr>>12]
|
||||
inPageAddr := addr & 0b111111111111
|
||||
@ -89,3 +115,5 @@ func (ve *VirtualEEI) Syscall() {
|
||||
func (ve *VirtualEEI) Trap() (bool, error) {
|
||||
panic("trap")
|
||||
}
|
||||
|
||||
var _ EEI = &VirtualEEI{} // interface assertion
|
||||
|
Loading…
Reference in New Issue
Block a user