Posted on 19-11-11, 21:05 in Emulator code design without libco
Post: #1 of 2
Since: 11-11-19

Last post: 1838 days
Last view: 1822 days
Hello Everybody.

My name is Mickael an i live in france (sorry for my english).
I'm french fan of sega and emulation world.

I'm interesting particullary on code design.
Higan and bsnes are a reference for me ;-).
In my free time (i don't have much) i try to create a genesis emulator too.

Theses days i'm trying to rework the general structure of my emulator to improve code design.

I'm asking myself about using libco from byuu.

After some search i found a solution to have the same advantage that libco but without using a library thread.
Perhaps something is wrong in the way i think and i told myself it could be good to share this solution in this forum to take advices.
Perhaps for example the way to synchronize device don't work in very every case but i can't have all configuration in mind yet to figure if it's a good solution or no.

I wrote below an example of code synchronizing a cpu, wich can be executed at instruction level, and a gpu wich can be executed at slot level.
The basic idea is that every device have a device manager wich receive event during execution of the device.
Exactly like libco, the cpu device can send an event to his manager notify him that a memory read will start.



class CPU {

private :

//internal clock
uint _clock;

//DeviceConroller
CPUController* _controller;

//internal bus reader
u16 readBus(u32 addr) {

_clock = _clock + 2;
_controller->CPUEvent(DEVICEA::EVENT_BUS_READED);
_clock = _clock + 4; //Improve could be to add wait potential extra cycles due to device latency
}

public :

// exec loop
void exec(){

clock_instruction = execInstruction();
_clock = _clock + clock_instruction;

_controller->CPU(CPU::INSTRUCTION_ENDED);
}

uint getClock(){
return _clock;
}
}



The same thing for the GPU, wich can notify his manager that a new slot will start (i don't write example here).

The class charged to synchronize all devices have to implement all DeviceManager interface from each component.
When the object's class receive an event, the class will look if exist an other device with his clock in late.
If exist this device will be executed until the next event this device will send.

For example in our case when the memory read event will be notified by cpu, the class will ask the gpu to exec all slot until the clock reach the actual cpu clock.
To avoid the case the gpu ask the cpu to sync, the class know that the cpu is waiting sync so it will ignore it.


class DevicesManager :: CPUController, GPUController {

private :

Device[] _devices;
uint[] _devicesState;

uint _CPUID = 0;
uint _GPUID = 1;

public :

void runDevices() {

CPU->exec();
}

void CPUEvent(uint clock, u8 event) {

if(event == CPU::EVENT_READ_BUS || CPU::INSTRUCTION_ENDED) {
uint[] devices = {_GPUID};
sync(clock, _GPUID);
}
}

void GPUEvent(uint clock, u8 event) {

if(event == GPU::SLOT_ENDED){
uint[] devices = {_CPUID};
sync(clock, _CPUID);
}
}

void sync(clock, uint waitingDevice, uint[] deviceToSync) {

_devicesState[waitingDevice] = WAITING;

for(uint = 0; i < nbDevices; i++) {

if(_devicesState[deviceID] != WAITING) {

if(device->getClock() < clock)
device->exec(clock);
}

_devicesState[waitingDevice] = RUNNING;
}
};



The code syntax is perhaps wrong, the code need perhpas to be improved and completed to be consistent enough to manage a system like the megadrive but i wrote it only to show the principles.

If people have experience emulating system complexity at the level of megadrive and have advice about this design and way to synchronize the device i'm very interested.
My aim is have a design sufficient enough to emulate the megadrive.








Posted on 19-11-12, 20:37 in Emulator code design without libco
Post: #2 of 2
Since: 11-11-19

Last post: 1838 days
Last view: 1822 days
Posted by Sintendo
What you're showing us here looks like a very basic scheduler, in which you run the CPU ahead and let the GPU play catch-up. Whether this particular scheme will suffice for the Megadrive I can't say; I imagine the answer depends on how the different devices are capable of interacting with each other, and what level of accuracy you're aiming for with your emulator.

However, a scheduler doesn't have a whole lot to do with whether you use cooperative threads (e.g. libco) or not. The true advantage of using cooperative threads, is that you can suspend execution at any point without requiring additional logic. This maps beautifully to cycle-level emulation, where you can suspend in the middle of an instruction. State machines are an alternative, but can be very cumbersome to write. byuu wrote a nice article on this topic, if you haven't read it yet.


I'm agree with you Sintendo, but the interest of my design is to suspend an instruction (for just before a slot of the vdp) to be sure that cpu is in sync before rendering the dot :-)
The code is simple and does not recquire any libco thread.
    Main » segarally » List of posts
    Get an ad blocker.