diff --git a/virtualEei.go b/virtualEei.go index 0a7c2b2..c61185e 100644 --- a/virtualEei.go +++ b/virtualEei.go @@ -6,27 +6,35 @@ import ( ) type VirtualEEI struct { - memPages map[uint32][4096]byte + memPages map[uint32]*[4096]byte } func NewVirtualEEI() VirtualEEI { return VirtualEEI{ - memPages: make(map[uint32][4096]byte), + memPages: make(map[uint32]*[4096]byte), } } func (ve *VirtualEEI) ReadByte(addr uint32) (byte, error) { - page, _ := ve.memPages[addr>>12] + page, ok := ve.memPages[addr>>12] + if !ok { + return 0, 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] + page, ok := ve.memPages[addr>>12] + if !ok { + return 0, nil + } + inPageAddr := addr & 0b111111111111 - return (uint16(page[inPageAddr]) << 8) | uint16(page[inPageAddr+1]), nil + return (uint16(page[inPageAddr+1]) << 8) | uint16(page[inPageAddr]), nil } func (ve *VirtualEEI) Read32(addr uint32) (uint32, error) { @@ -34,57 +42,61 @@ func (ve *VirtualEEI) Read32(addr uint32) (uint32, error) { // RISC-V allows 32-bit load/stores to be implemented as either little-endian // or big-endian. This is the little-endian version - page, _ := ve.memPages[addr>>12] + page, ok := ve.memPages[addr>>12] + if !ok { + return 0, nil + } + inPageAddr := addr & 0b111111111111 - return (uint32(page[inPageAddr]) << 24) | (uint32(page[inPageAddr+1]) << 16) | (uint32(page[inPageAddr+2]) << 8) | uint32(page[inPageAddr+3]), nil + return (uint32(page[inPageAddr+3]) << 24) | (uint32(page[inPageAddr+2]) << 16) | (uint32(page[inPageAddr+1]) << 8) | uint32(page[inPageAddr]), nil } func (ve *VirtualEEI) WriteByte(addr uint32, value byte) error { page, ok := ve.memPages[addr>>12] - - // Slices are reference-types, this will write-through into ve.memPages - page[addr&0b111111111111] = value - if !ok { + page = new([4096]byte) ve.memPages[addr>>12] = page } + // The pointer-to-page-array is a reference type, this will write-through into ve.memPages + page[addr&0b111111111111] = value 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 { + page = new([4096]byte) ve.memPages[addr>>12] = page } + inPageAddr := addr & 0b111111111111 + + // The pointer-to-page-array is a reference type, this will write-through into ve.memPages + // n.b. will panic on split page boundary + page[inPageAddr+1] = byte((value >> 8) & 0b11111111) + page[inPageAddr] = byte(value & 0b11111111) + return nil } func (ve *VirtualEEI) Write32(addr, value uint32) 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 >> 24) & 0b11111111) - page[inPageAddr+1] = byte((value >> 16) & 0b11111111) - page[inPageAddr+2] = byte((value >> 8) & 0b11111111) - page[inPageAddr+3] = byte(value & 0b11111111) - if !ok { + page = new([4096]byte) ve.memPages[addr>>12] = page } + inPageAddr := addr & 0b111111111111 + + // The pointer-to-page-array is a reference type, this will write-through into ve.memPages + // n.b. will panic on split page boundary + page[inPageAddr+3] = byte((value >> 24) & 0b11111111) + page[inPageAddr+2] = byte((value >> 16) & 0b11111111) + page[inPageAddr+1] = byte((value >> 8) & 0b11111111) + page[inPageAddr] = byte(value & 0b11111111) return nil } diff --git a/virtualEei_test.go b/virtualEei_test.go new file mode 100644 index 0000000..a4550a5 --- /dev/null +++ b/virtualEei_test.go @@ -0,0 +1,57 @@ +package riscvemu + +import ( + "testing" +) + +func TestEndianness(t *testing.T) { + e := NewVirtualEEI() + + err := e.Write32(0, 0x12345678) + if err != nil { + t.Fatal(err) + } + + rw, err := e.Read32(0) + if err != nil { + t.Fatal(err) + } + if rw != 0x12345678 { + t.Errorf("got %x expected 0x12345678", rw) + } + + // In little-endian, the 32-bit value 0x12345678 is written in order with its + // least-significant bytes first (i.e. in a hex editor: 78563412) + + rb, err := e.ReadByte(0) + if err != nil { + t.Fatal(err) + } + if rb != 0x78 { + t.Errorf("got %x expected 0x78", rb) + } + + rb, err = e.ReadByte(1) + if err != nil { + t.Fatal(err) + } + if rb != 0x56 { + t.Errorf("got %x expected 0x56", rb) + } + + rb, err = e.ReadByte(2) + if err != nil { + t.Fatal(err) + } + if rb != 0x34 { + t.Errorf("got %x expected 0x34", rb) + } + + rb, err = e.ReadByte(3) + if err != nil { + t.Fatal(err) + } + if rb != 0x12 { + t.Errorf("got %x expected 0x12", rb) + } +}