C++ 在多个类上定义的面向对象跳转表
我正在将NMOS6502模拟器重构为多个类。我想知道是否有一种“面向对象”的方法来定义函数跳转表。基本上,我已经定义了单独的指令类来对相关的cpu操作组进行分类,比如“CStackInstHandler”或“CarithMetricInstHandler”,它们将引用cpu对象。每个指令类都派生自一个抽象指令类。每个派生指令类都有一组函数,这些函数将使用cpu对象的公共接口来更改cpu状态,例如:C++ 在多个类上定义的面向对象跳转表,c++,6502,C++,6502,我正在将NMOS6502模拟器重构为多个类。我想知道是否有一种“面向对象”的方法来定义函数跳转表。基本上,我已经定义了单独的指令类来对相关的cpu操作组进行分类,比如“CStackInstHandler”或“CarithMetricInstHandler”,它们将引用cpu对象。每个指令类都派生自一个抽象指令类。每个派生指令类都有一组函数,这些函数将使用cpu对象的公共接口来更改cpu状态,例如: uint8_t opcode = _memory->readMem(_cpu->get
uint8_t opcode = _memory->readMem(_cpu->getProgramCounter());
AInstructionHandler* _handler = _cpu->getInstHandler(opcode);
_handler->setCpu(&cpu);
_handler->setMemory(&memory);
_handler->execute(opcode);
问题在于,在运行时,需要使用操作码确定指令处理程序以及为该处理程序定义的适当成员函数
所以我们有-操作码从内存中读取,cpu使用一个表将操作码映射到指令处理程序类型,然后指令处理程序使用相同的操作码来选择正确的函数。每条指令覆盖一个“执行”功能,例如:
void CBranchInstHandler::execute() {
switch(_opcode) {
case 0x90:
this->BCC();
break;
case 0xb0:
this->BCS();
break;
case 0xf0:
this->BEQ();
break;
case 0x30:
this->BMI();
break;
case 0xd0:
this->BNE();
break;
case 0x10:
this->BPL();
break;
default:
break;
}
}
我最后有两个查找,其中一个是多余的。一个用于选择处理程序,另一个用于选择处理程序函数。我觉得这是完成这项任务的错误方法,但我不确定是否有一种替代方法不只是将其转移到非成员函数组中
我想知道是否有人了解这个问题。它基本上可以归结为想要将一个类重构成更小的比特(cpu类和指令成员函数重构为cpu类和指令类),但是所有的组件都是如此相互关联,以至于我不得不重复我自己。引入冗余
一个非面向对象的解决方案就是让这些指令成为接受cpu引用的非成员函数。然后,定义一个函数跳转表,通过操作码查找和索引指令并执行
这对于对象来说似乎并不实际。我可以让所有的说明都是静态的,但这似乎没有抓住重点
任何关于甚至是无关紧要的问题的见解或信息都会非常有用
谢谢。您可以使用指向类成员函数/方法的指针:
void (CBranchHandlerBase::*)();
用于存储指向给定\u操作码
应调用的方法的指针
map<uint8_t, void (CBranchHandlerBase::*)()> handlers;
handlers[0x90] = &BCC;
handlers[0xb0] = &BCS;
...
请注意,execute是在基类中定义的(它不必是虚拟的,因为每个处理程序都具有与execute方法相同的功能)
编辑:映射实际上可以被大小为
2^(8*sizeof(uint8_t))的向量或数组替换。
出于效率原因您可以使用指向类成员函数/方法的指针:
void (CBranchHandlerBase::*)();
用于存储指向给定\u操作码
应调用的方法的指针
map<uint8_t, void (CBranchHandlerBase::*)()> handlers;
handlers[0x90] = &BCC;
handlers[0xb0] = &BCS;
...
请注意,execute是在基类中定义的(它不必是虚拟的,因为每个处理程序都具有与execute方法相同的功能)
编辑:映射实际上可以被大小为
2^(8*sizeof(uint8_t))的向量或数组替换。
出于效率原因您可以使用指向类成员函数/方法的指针:
void (CBranchHandlerBase::*)();
用于存储指向给定\u操作码
应调用的方法的指针
map<uint8_t, void (CBranchHandlerBase::*)()> handlers;
handlers[0x90] = &BCC;
handlers[0xb0] = &BCS;
...
请注意,execute是在基类中定义的(它不必是虚拟的,因为每个处理程序都具有与execute方法相同的功能)
编辑:映射实际上可以被大小为
2^(8*sizeof(uint8_t))的向量或数组替换。
出于效率原因您可以使用指向类成员函数/方法的指针:
void (CBranchHandlerBase::*)();
用于存储指向给定\u操作码
应调用的方法的指针
map<uint8_t, void (CBranchHandlerBase::*)()> handlers;
handlers[0x90] = &BCC;
handlers[0xb0] = &BCS;
...
请注意,execute是在基类中定义的(它不必是虚拟的,因为每个处理程序都具有与execute方法相同的功能)
编辑:映射实际上可以由大小为
2^(8*sizeof(uint8_t))的向量或数组替换
出于效率原因如果我理解的话,您所做的是为每种类型的指令(分支、算术、加载、存储等)创建一个类然后在这些指令中,您正在为各个指令编写成员函数——c.f.您有一个“CBranchInstrHandler”,它处理“进位分支”、“零位分支”等
完全面向对象的方法是将子类化扩展到单个指令
class CBranchInstrHandler { virtual void execute() = 0; };
class CBranchOnCarryClear : public CBranchInstrHandler {
void execute() override {
...;
}
};
class CBranchOnCarrySet : public CBranchInstrHandler {
void execute() override {
...;
}
};
现在,您可以一次查找指令,但需要所有这些指令的一对一映射
switch (opCode) {
case 0x90: return .. CBranchOnCarryClear .. ;
case 0xB0: return .. CBranchOnCarrySet .. ;
}
省略号的存在是因为我不确定如何获得指向CBranchInstrHandler的指针;我猜它们是静态的,并且您并不是new
如果它们是无数据的,则可以按值将其作为函数对象返回:
struct Base { virtual void execute() { /* noop */ } };
struct Derived { void execute(override) { ... } };
Base getHandler(opcode_t opcode) {
if (opcode == 1) { return Derived(); }
}
但我怀疑您可能想要获取参数并存储状态,在这种情况下,这里按值返回可能会导致切片
当然,如果您使用的是C++11,则可以使用lambdas:
switch (opCode) {
case 0x90: return [this] () {
... implement BCC execute here ...
};
case 0xB0: return [this] () {
... implement BCS execute here ...
}
}
如果我理解的话,您所做的是为每种类型的指令(分支、算术、加载、存储等)创建一个类,然后在这些类中为单个指令编写成员函数——c.f.您有一个“CBranchInstrHandler”,它处理“携带分支”、“零分支”等 完全面向对象的方法是将子类化扩展到单个指令
class CBranchInstrHandler { virtual void execute() = 0; };
class CBranchOnCarryClear : public CBranchInstrHandler {
void execute() override {
...;
}
};
class CBranchOnCarrySet : public CBranchInstrHandler {
void execute() override {
...;
}
};
现在,您可以一次查找指令,但需要所有这些指令的一对一映射
switch (opCode) {
case 0x90: return .. CBranchOnCarryClear .. ;
case 0xB0: return .. CBranchOnCarrySet .. ;
}
省略号的存在是因为我不确定如何获得指向CBranchInstrHandler的指针;我猜它们是静态的,并且您并不是new
如果它们是无数据的,则可以按值将其作为函数对象返回:
struct Base { virtual void execute() { /* noop */ } };
struct Derived { void execute(override) { ... } };
Base getHandler(opcode_t opcode) {
if (opcode == 1) { return Derived(); }
}
但我怀疑你可能