我正在一起研究处理器的大致ISS,我想知道是否有一种更有效的方法来执行我的工作(理想情况下不求助于不安全的库)。
(简化)我通过以下方式表示内存:

1
type DataMem []uint8

注册人:

1
type Register uint16

内存需要以字节大小为单位,而处理器则以更大的单位工作,如上所述。 这意味着存储我们要做的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func (m *Machine) ExecuteST (ins Instruction) {
  // Store to memory
  // Data to store specified in one register
  // Address to store to specified by another register
  target_address := m.RegBank[ins.Dst]
  src_data := m.RegBank[ins.Src]

  ///////// Start of annoying section////////
  var target_0 uint8
  var target_1 uint8
  target_0 =  src_data & 0x0f
  target_1 = (src_data & 0xf0)>>8
  m.data_mem[target_address  ] = target_0
  m.data_mem[target_address+1] = target_1
  /////////// End of annoying section /////////
  m.logger.Printf("ST To Address %x, Data %x\
",target_address,src_data)
}

对于各种不同的数据类型和传输,依此类推。

对我来说,令人烦恼的是,我知道将在其上运行go代码的处理器将具有单个加载指令,该指令可以在一次传输中完成所有上述操作。 我希望一个好的c编译器可以为我优化它,但是如果不破坏不安全的库并直接使用指针,我认为我无法解决这个问题吗?

我愿意征求建议,包括建立数据存储器模型的更好方法。

  • 我不知道有什么更好的方法来做您的工作,但是您的口罩是错误的。 第一个应该是0xff,另一个应该是0xff00
  • 糟糕,谢谢! ID从我正在运行的代码中重新编写了代码以简化问题-好像我错误地/太快地简化了!

由于Go具有类型安全性,因此在不使用不安全软件包的情况下,您可以做很多不同的事情。

最简单的解决方案,也许也是性能最高的解决方案,是对目标地址进行转换:

1
2
3
// convert the target address to *uint16
srcData = uint16(0xeeee)
*(*uint16)(unsafe.Pointer(&dataMem[targetAddress])) = srcData

类似地,另一个选择是用指向同一内存区域的[]uint16切片遮盖[]uint8切片。这看起来更加毛茸茸,您必须检查偏移量并自行对齐。

1
2
dataMem16 := (*(*[1<<30 - 1]uint16)(unsafe.Pointer(&dataMem[0])))[:len(dataMem)/2 : len(dataMem)/2]
dataMem16[targetAddress/2] = srcData

另一个尝试的方法是使用内置的copy移动字节。由于复制是由编译器在汇编中实现的,因此它可以执行您想要的操作(尽管我没有检查转换的汇编实际产生的内容,因此可能会很麻烦)

1
copy(dataMem[targetAddress:], (*(*[2]uint8)(unsafe.Pointer(&srcData)))[:])

或作为@OneOfOne显示的内联函数:

1
2
3
4
5
func regToMem(reg uint16) *[2]uint8 {
    return (*[2]uint8)(unsafe.Pointer(&reg))
}

copy(mem[targetAddress:], regToMem(0xffff)[:])

https://play.golang.org/p/0c1UywVuzj

如果性能至关重要,并且您要使用特定于体系结构的指令,则"正确"的方法是直接在汇编中实现您想要的功能,但是从第一个解决方案生成的代码可能很难克服。

  • 该死的,别再让Jim忍不住回答我的问题了! 任何人play.golang.org/p/C8jo2Jk59R
  • @OneOfOne:谢谢,寻找第一种情况要好一些
  • 非常感谢这个TBH,我更喜欢切片阴影,因为它应该可以工作而不会在移动顶部添加额外操作的任何风险-复制可能会添加其他内容。 但是,这是增加技巧的有用工具。
  • @ChrisHopkins:我可能错过了最简单的解决方案,将目标地址转换为uint16而不是复制2个字节。 它与转换整个片段基本上相同,但是我认为它的错误倾向少一些,并且更容易推理。 我只使用最后一个选项,您必须在该内存区域内进行大量随机访问。

由于我关心的是模拟的效率,因此另一个选择是声明数据存储器的不同方式,例如

1
type DataMem []uint32

给定地址的提供方法提取字节

1
2
func (dm *DataMem) StoreB (address int) uint8 {...}
func (dm *DataMem) Store16 (address int) uint16 {...}

鉴于它是在PC上运行的,因此那里会有一个数据缓存,因此,获取太大的数据字不会在现代处理器上花费任何费用。

我需要一个新的基本规则,当您遇到棘手的问题时,请查看数据结构和您的平台;-)