0 users browsing Emulation. | 1 guest | 5 bots  
    Main » Emulation » Emulator code design without libco
    Pages: 1
    Posted on 19-11-11, 21:05
    Post: #1 of 2
    Since: 11-11-19

    Last post: 1835 days
    Last view: 1819 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-11, 21:56

    Post: #210 of 456
    Since: 10-29-18

    Last post: 44 days
    Last view: 1 day
    Posted by segarally
    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.

    You could also try posting a thread on /r/EmuDev/.

    My current setup: Super Famicom ("2/1/3" SNS-CPU-1CHIP-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10
    Posted on 19-11-12, 17:06

    Post: #15 of 17
    Since: 10-29-18

    Last post: 1802 days
    Last view: 1374 days
    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.
    Posted on 19-11-12, 20:37
    Post: #2 of 2
    Since: 11-11-19

    Last post: 1835 days
    Last view: 1819 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.
    Posted on 19-11-13, 07:29
    Post: #68 of 77
    Since: 10-31-18

    Last post: 1189 days
    Last view: 1116 days
    I'm not experienced in emulation, but what happens if the CPU writes to a register during the middle of the GPU rendering a dot?
    Posted on 19-11-14, 08:16

    Post: #211 of 456
    Since: 10-29-18

    Last post: 44 days
    Last view: 1 day
    Depends on the hardware, the register, and the timing. For example on the SNES writing to VRAM during active display has no effect on the VRAM content, whereas writes to the brightness register do take effect immediately on most SNES revisions.

    My current setup: Super Famicom ("2/1/3" SNS-CPU-1CHIP-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10
    Pages: 1
      Main » Emulation » Emulator code design without libco
      This does not actually go there and I regret nothing.