【发布时间】:2019-12-29 19:44:35
【问题描述】:
出于我制作 Z80 计算机系统的电子爱好,我正在构建 Z80 在线仿真器。这个想法是将物理 Z80 芯片从电路中移除,并将仿真器插入其插槽并精确仿真 Z80。此外,模拟器将实现调试和诊断支持——但这不是问题所在。现在的想法是,这个在线仿真器将在 PSoC5 模块内运行,并通过 USB 与 PC 通信。
I have currently setup a ginormous state machine in code (C) 每时钟边沿变化(正/负边沿)都提前 - 每个时钟周期两次。我称之为时钟滴答声。
问题是这个状态机代码变得庞大而复杂。
我为每条 Z80 指令生成了结构,其中包含有关每个处理周期调用哪些函数的详细信息。 (一条 Z80 指令可能需要多达 6 个处理(机器)周期,每个处理周期至少需要 3 个(通常是 4 个或更多)时钟周期。
这是一个更复杂的指令示例,需要 4 个机器周期才能完成。奇怪的名称用于对每条指令的属性进行编码并生成唯一的名称。在每个机器周期内,都会多次调用相应的 OnClock_Xxxx 函数 - 对于该机器周期内的每个时钟滴答声。
// ADD IY, SP - ADDIY_SP_FD2 - FD, 39
const InstructionInfo instructionInfoADDIY_SP_FD2 =
{
4,
0,
{
{ 4, OnClock_OF },
{ 4, OnClock_ADDIY_o_FD2_OF },
{ 4, OnClock_ADDIY_o_FD2_OP },
{ 3, OnClock_ADDIY_o_FD2_OP },
{ 0, nullptr },
{ 0, nullptr },
},
{
{ Type_RegistersSP16, {3} },
{ Type_None, {0} },
}
};
对这些指令信息结构的引用存储在表中,以便在解码期间快速查找。
我有一个包含 Z80 状态的全局结构,如时钟周期计数、寄存器和指令处理期间使用的状态 - 如操作数等。所有代码都在此全局状态下运行。
为了与主机交互(a unit test 或PSoC5 micro-controller),我设置了一个简单的接口来控制 Z80 的引脚,请求输入(读取数据总线)或输出(激活 MEMREQ)。
在代码I have used a dirty C-trick 中实现状态机,其中涉及跳入和跳出 switch 语句,隐藏在宏后面。 这使得代码像普通(但异步)代码一样可读。
Here's an example 这个异步状态机代码对于获取和解码操作码的逻辑的外观:
Async_Function(FetchDecode)
{
AssertClock(M1, T1, Level_PosEdge, 1);
setRefresh(Inactive);
setAddressPC();
setM1(Active);
Async_Yield();
_state.Clock.TL++;
AssertClock(M1, T1, Level_NegEdge, 2);
setMemReq(Active);
setRd(Active);
Async_Yield();
NextTCycle();
AssertClock(M1, T2, Level_PosEdge, 3);
// time for some book keeping
if (_state.Instruction.InstructionAddress == 0)
_state.Instruction.InstructionAddress = _state.Registers.PC - 1;
Async_Yield();
_state.Clock.TL++;
AssertClock(M1, T2, Level_NegEdge, 4);
Async_Yield();
NextTCycle();
AssertClock(M1, T3, Level_PosEdge, 5);
_state.Instruction.Data = getDataBus();
setRd(Inactive);
setMemReq(Inactive);
setM1(Inactive);
setAddressIR();
setRefresh(Active);
Async_Yield();
_state.Clock.TL++;
AssertClock(M1, T3, Level_NegEdge, 6);
setMemReq(Active);
Decode();
Async_Yield();
}
Async_End
Async_Yield() 退出函数,下一次调用函数将在那里恢复执行。
好的,现在问题来了: 我很难让状态机表现得恰到好处,这让我质疑我对这个问题的推理方式。因为处理更复杂的指令涉及状态机中的更多状态,我发现很难对代码进行推理——这是一种符号/气味。
是否有任何明显的算法和/或模式可用于编写这种类型的时钟周期精确仿真器?
【问题讨论】:
-
可能最适合codereview.stackexchange.com 或软件工程。
-
我不知道 codereview,但我检查了软件工程——这意味着更多的 ALM 和部署类型主题。 meta.stackoverflow.com/questions/254570/…
-
那么可能会进行逆向计算。尽管我很喜欢逆向计算和旧 CPU,但我不确定你会得到关于 SO 的答案。例如,“是否有任何明显的算法和/或模式可用于编写这种类型的时钟周期精确仿真器?” 看起来很像推荐。您可能需要重新表述这一点,并研究您的问题外观。
-
请参阅What's the proper implementation for hardware emulation? 它拥有一个链接到我的 Z80 iset,每个 MC 计时和 MC 类型或多或少是你现在做的硬编码。它是一个格式化的 TXT 文件,可以直接加载到您的模拟器中……大大简化了 CPU 内核……它包含高达 4Byte(包括)的所有操作码并通过 ZEXALL 100%。因此,在您的情况下,我只会对每种 MC 类型的时序进行编码(只是其中的几个)...... t 从 MC 时序转变为单独的时钟周期
标签: c emulation state-machine z80