cpu: implement LB/LH/LW/LBU/LHU

This commit is contained in:
mappu 2022-12-28 14:27:12 +13:00
parent 49b78a4b75
commit 4654a5bc72
3 changed files with 103 additions and 1 deletions

71
cpu.go
View File

@ -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:

View File

@ -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()

View File

@ -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