Skip to content
Snippets Groups Projects
manager.h 4.21 KiB
Newer Older
#pragma once
#include <common/memory.h>
#include <cpu/iop/dma.h>
Geo Ster's avatar
Geo Ster committed
#include <fmt/core.h>
#include <memory>
Geo Ster's avatar
Geo Ster committed
#include <vector>
Geo Ster's avatar
Geo Ster committed
#include <fstream>

namespace ee {
    class EmotionEngine;
}

namespace iop {
    class IOProcessor;
}

enum Component
{
    EE = 0x0,
    IOP = 0x1
};

/* This class act as the "motherboard" of sorts */
class ComponentManager {
public:
    ComponentManager();
Geo Ster's avatar
Geo Ster committed
    ~ComponentManager();
Geo Ster's avatar
Geo Ster committed
    void tick();
    /* Memory operations */
    template <typename T>
Geo Ster's avatar
Geo Ster committed
    T read(uint32_t addr, Component comp);
    
    template <typename T>
Geo Ster's avatar
Geo Ster committed
    void write(uint32_t addr, T data, Component comp);
protected:
    void read_bios();

public:
    /* Components */
Geo Ster's avatar
Geo Ster committed
    std::unique_ptr<ee::EmotionEngine> ee;
    std::unique_ptr<iop::IOProcessor> iop;
    std::unique_ptr<iop::DMAController> iop_dma;
    
    /* Memory - Registers */
Geo Ster's avatar
Geo Ster committed
    uint8_t* ram, * iop_ram, * bios;

    uint32_t MCH_RICM = 0, MCH_DRD = 0;
    uint8_t rdram_sdevid = 0;
Geo Ster's avatar
Geo Ster committed

    /* Utilities */
    const char* component_name[2] = { "EE", "IOP" };
    std::atomic_bool stop_thread = false;
    std::ofstream console;
};

/* Instanciate the templates here so we can limit their types below */
template <typename T>
T ComponentManager::read(uint32_t addr, Component comp)
{
    uint32_t vaddr = addr & KUSEG_MASKS[addr >> 29];

    /* Check if it's any memory ranges we care */
    if (ee::RAM.contains(vaddr))
        return *(T*)&ram[ee::RAM.offset(vaddr)];
    else if (ee::BIOS.contains(vaddr))
        return *(T*)&bios[ee::BIOS.offset(vaddr)];
    else if (iop::RAM.contains(vaddr))
        return *(T*)&iop_ram[iop::RAM.offset(vaddr)];
    else if (iop::DMA1.contains(vaddr) || iop::DMA2.contains(vaddr))
        return iop_dma->read(vaddr);
Geo Ster's avatar
Geo Ster committed
    else /* Otherwise handle specific address diferently. */
    {
        switch (vaddr)
        {
        case 0x1000f130:
        case 0x1000f430:
            return 0;
        case 0x1000f440:
        {
            uint8_t SOP = (MCH_RICM >> 6) & 0xF;
            uint8_t SA = (MCH_RICM >> 16) & 0xFFF;
            if (!SOP)
            {
                switch (SA)
                {
                case 0x21:
                    if (rdram_sdevid < 2)
                    {
                        rdram_sdevid++;
                        return 0x1F;
                    }
                    return 0;
                case 0x23:
                    return 0x0D0D;
                case 0x24:
                    return 0x0090;
                case 0x40:
                    return MCH_RICM & 0x1F;
                }
            }
            return 0;
        }
        case 0x1f801450: /* Unknown */
        case 0xfffe0130: /* Cache control */
            return 0;
Geo Ster's avatar
Geo Ster committed
        default:
            fmt::print("[{}] {:d}bit read from unknown address {:#x}\n", component_name[comp], sizeof(T) * 8, addr);
            return 0;
        }
    }
}

/* Instanciate the templates here so we can limit their types below */
template <typename T>
void ComponentManager::write(uint32_t addr, T data, Component comp)
{
    uint32_t vaddr = addr & KUSEG_MASKS[addr >> 29];

    /* Check if it's any memory ranges we care */
    if (ee::RAM.contains(vaddr))
        *(T*)&ram[ee::RAM.offset(vaddr)] = data;
    else if (iop::RAM.contains(vaddr))
        *(T*)&iop_ram[iop::RAM.offset(vaddr)] = data;
    else if (iop::DMA1.contains(vaddr) || iop::DMA2.contains(vaddr))
        iop_dma->write(vaddr, data);
Geo Ster's avatar
Geo Ster committed
    else
    {
        switch (vaddr)
        {
            /* Record any console output */
        case 0x1000f180:
        {
            console << (char)data;
            console.flush();
            break;
        }
        case 0x1000f430:
        {
            uint8_t SA = (data >> 16) & 0xFFF;
            uint8_t SBC = (data >> 6) & 0xF;

            if (SA == 0x21 && SBC == 0x1 && ((MCH_DRD >> 7) & 1) == 0)
                rdram_sdevid = 0;

            MCH_RICM = data & ~0x80000000;
            break;
        }
        case 0x1000f440:
        {
            MCH_DRD = data;
            break;
        }
        case 0x1f801450:
        case 0xfffe0130: /* Cache control */
            return;
Geo Ster's avatar
Geo Ster committed
        default:
            fmt::print("[{}] {:d}bit write {:#x} to unknown address {:#x}\n", component_name[comp], sizeof(T) * 8, data, addr);
            break;
        };
    }
}