Bochs has many macros with inscrutable names. One might even go as far as to say that Bochs is macro infested. Some of them are gross speed hacks, to cover up the slow speed that C++ causes. Others paper over differences between the simulated PC configurations. Many of the macros exhibit the same problem as C++ does: too much stuff happens behind the programmer's back. More explicitness would be a big win.
C++ methods have an invisible parameter called the this pointer - otherwise the method wouldn't know which object to operate on. In many cases in Bochs, there will only ever be one object - so this flexibility is unnecessary. There is a hack that can be enabled by #defining BX_USE_CPU_SMF to 1 in config.h that makes most methods static, which means they have a "special relationship" with the class they are declared in but apart from that are normal C functions with no hidden parameters. Of course they still need access to the internals of an object, so the single object of their class has a globally visible name that these functions use. It is all hidden with macros.
Declaration of a class, from iodev/pic.h:
... #if BX_USE_PIC_SMF # define BX_PIC_SMF static # define BX_PIC_THIS thePic-> #else # define BX_PIC_SMF # define BX_PIC_THIS this-> #endif ... class bx_pic_c : public bx_pic_stub_c { public: bx_pic_c(void); ~bx_pic_c(void); ... BX_PIC_SMF void service_master_pic(void); BX_PIC_SMF void service_slave_pic(void); BX_PIC_SMF void clear_highest_interrupt(bx_pic_t *pic); };
And iodev/pic.cc:
... #define LOG_THIS thePic-> ... bx_pic_c *thePic = NULL; ... void bx_pic_c::service_master_pic(void) { Bit8u unmasked_requests; int irq; Bit8u isr, max_irq; Bit8u highest_priority = BX_PIC_THIS s.master_pic.lowest_priority + 1; if(highest_priority > 7) highest_priority = 0; if (BX_PIC_THIS s.master_pic.INT) { /* last interrupt still not acknowleged */ return; } if (BX_PIC_THIS s.master_pic.special_mask) { /* all priorities may be enabled. check all IRR bits except ones * which have corresponding ISR bits set */ max_irq = highest_priority; } else { /* normal mode */ /* Find the highest priority IRQ that is enabled due to current ISR */ isr = BX_PIC_THIS s.master_pic.isr; ... } ...
Ugly, isn't it? If we use static methods, methods prefixed with BX_PIC_SMF are declared static and references to fields inside the object, which are prefixed with BX_PIC_THIS, will use the globally visible object, thePic->. If we don't use static methods, BX_PIC_SMF evaluates to nothing and BX_PIC_THIS becomes this->. Making it evaluate to nothing would be a lot cleaner, but then the scoping rules would change slightly between the two Bochs configurations, which would be a load of bugs just waiting to happen. Some classes use BX_SMF, others have their own version of the macro, like BX_PIC_SMF above.
The CPU class is a special case of the above: if Bochs is simulating a uni- processor machine then there is obviously only one bx_cpu_c object and the static methods trick can be used. If, on the other hand, Bochs is simulating an smp machine then we can't use the trick. The same seems to be true for memory: for some reason, we have a memory object for each CPU object. This might become relevant for NUMA machines, but they are not all that common -- and even the existing IA-32 NUMA machines bend over backwards to hide that fact: it should only be visible in slightly worse timing for non-local memory and non-local peripherals. Other than that, the memory map and device map presented to each CPU will be identical.
In a UP configuration, the CPU object is declared as bx_cpu. In an SMP configuration it will be an array of pointers to CPU objects (bx_cpu_array[]). For memory that would be bx_mem and bx_mem_array[], respectively. Each CPU object contains a pointer to its associated memory object. Access of a CPU object often goes through the BX_CPU(x) macro, which either ignores the parameter and evaluates to &bx_cpu, or evaluates to bx_cpu_array [n], so the result will always be a pointer. The same goes for BX_MEM(x). If static methods are used then BX_CPU_THIS_PTR evaluates to BX_CPU(0)->. Ugly, isn't it?