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:
|
case 0b0000011:
|
||||||
// LB/LH/LW/LBU/LHU
|
// 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")
|
panic("todo")
|
||||||
|
|
||||||
case 0b0100011:
|
case 0b0100011:
|
||||||
|
@ -4,8 +4,11 @@ package riscvemu
|
|||||||
// code is run.
|
// code is run.
|
||||||
type EEI interface {
|
type EEI interface {
|
||||||
ReadByte(addr uint32) (byte, error)
|
ReadByte(addr uint32) (byte, error)
|
||||||
|
Read16(addr uint32) (uint16, error)
|
||||||
Read32(addr uint32) (uint32, error)
|
Read32(addr uint32) (uint32, error)
|
||||||
|
|
||||||
WriteByte(addr uint32, value byte) error
|
WriteByte(addr uint32, value byte) error
|
||||||
|
Write16(addr uint32, value uint16) error
|
||||||
Write32(addr, value uint32) error
|
Write32(addr, value uint32) error
|
||||||
|
|
||||||
Syscall()
|
Syscall()
|
@ -20,6 +20,15 @@ func (ve *VirtualEEI) ReadByte(addr uint32) (byte, error) {
|
|||||||
return page[addr&0b111111111111], nil
|
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) {
|
func (ve *VirtualEEI) Read32(addr uint32) (uint32, error) {
|
||||||
// n.b. will panic on overflow
|
// n.b. will panic on overflow
|
||||||
// RISC-V allows 32-bit load/stores to be implemented as either little-endian
|
// 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
|
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 {
|
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]
|
page, ok := ve.memPages[addr>>12]
|
||||||
inPageAddr := addr & 0b111111111111
|
inPageAddr := addr & 0b111111111111
|
||||||
@ -89,3 +115,5 @@ func (ve *VirtualEEI) Syscall() {
|
|||||||
func (ve *VirtualEEI) Trap() (bool, error) {
|
func (ve *VirtualEEI) Trap() (bool, error) {
|
||||||
panic("trap")
|
panic("trap")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ EEI = &VirtualEEI{} // interface assertion
|
||||||
|
Loading…
Reference in New Issue
Block a user