From 4654a5bc72a3361a15eb8dd19340677eaf32bedb Mon Sep 17 00:00:00 2001 From: mappu Date: Wed, 28 Dec 2022 14:27:12 +1300 Subject: [PATCH] cpu: implement LB/LH/LW/LBU/LHU --- cpu.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++ world.go => eei.go | 3 ++ virtualEei.go | 30 +++++++++++++++++++- 3 files changed, 103 insertions(+), 1 deletion(-) rename world.go => eei.go (79%) diff --git a/cpu.go b/cpu.go index 1695baf..9d201c5 100644 --- a/cpu.go +++ b/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: diff --git a/world.go b/eei.go similarity index 79% rename from world.go rename to eei.go index 31aa592..c5fba85 100644 --- a/world.go +++ b/eei.go @@ -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() diff --git a/virtualEei.go b/virtualEei.go index 01f0f34..05e2633 100644 --- a/virtualEei.go +++ b/virtualEei.go @@ -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