Klassenaufteilung



  • Hallo,

    ich steh gerade etwas auf dem Schlauch. Bin dabei mir eine kleine Simulation für einen Prozessor zu schreiben den ich von frühers her noch gut kenne. Einfach so aus Spaß heraus. Das ganze sieht bis jetzt so aus. Ich habe das ganze mit dem Qt-Creator angefangen, da das Signal- Slot-Konzept sehr gut zu gebrauchen ist.

    #ifndef Z180CPU_H
    #define Z180CPU_H
    // --------------------------------------------------------------------------------
    #include <QtGlobal>
    #include <QObject>
    // --------------------------------------------------------------------------------
    class Z180CPU : public QObject {
            Q_OBJECT
        private:    // Attributes
            // Data-Types for internal Registers
            // Data-Types for first Register-Set
            quint8  m_regA;     // Accumulator
            quint8  m_regF;     // Flag-Register
            quint8  m_regB;     // general purpose Register B
            quint8  m_regC;     // general purpose Register C
            quint8  m_regD;     // general purpose Register D
            quint8  m_regE;     // general purpose Register E
            quint8  m_regH;     // general purpose Register H
            quint8  m_regL;     // general purpose Register L
    
            // Data-Types for second Register-Set
            quint8  m_regA_;    // Accumulator
            quint8  m_regF_;    // Flag-Register
            quint8  m_regB_;    // general purpose Register B
            quint8  m_regC_;    // general purpose Register C
            quint8  m_regD_;    // general purpose Register D
            quint8  m_regE_;    // general purpose Register E
            quint8  m_regH_;    // general purpose Register H
            quint8  m_regL_;    // general purpose Register L
    
            // Data-Types for special Register-Set
            quint8  m_regI;     // Interrupt-Vector Register
            quint8  m_regR;     // Refresh-Counter
            quint16 m_regIX;    // Indexregister IX
            quint16 m_regIY;    // Indexregister IY
            quint16 m_regSP;    // Stack-Pointer
            quint16 m_regPC;    // Programm-Counter
    
            // Function-Array of Core-OpCodes
            typedef int(Z180CPU::*coreOpCodes)();
            coreOpCodes m_OPcore[256];
    
        public:     // Attributes
            static const quint8 Flag_S = 0x080;
            static const quint8 Flag_Z = 0x040;
            static const quint8 Flag_H = 0x010;
            static const quint8 Flag_PV = 0x004;
            static const quint8 Flag_N = 0x002;
            static const quint8 Flag_C = 0x001;
    
        public:     // Contructor & Destructor
            Z180CPU();
            ~Z180CPU();
    
        protected:  // Event Methods
    
        public:     // Methods
    
        private:    // Methods
            quint8 memRead(const quint16 &addr);
            void memWrite(const quint16 &addr, const quint8 &data);
            quint8 ioRead(const quint8 &addr);
            void ioWrite(const quint8 &addr, const quint8 &data);
            void createCoreOpCodeArray();
    
            // Methods for Core CPU-Commands
            int op_NOP();       // OP-Code 0x00 : NOP
            int op_LDBCnn();    // OP-Code 0x01 : LD BC,nn
            int op_LDiBCA();    // OP-Code 0x02 : LD (BC),A
            int op_INCBC();     // OP-Code 0x03 : INC BC
            int op_INCB();      // OP-Code 0x04 : INC B
    
        signals:
            void cpuDataChange();
            void cpuMemRead(const quint32 &addr, quint8 &data);
            void cpuMemWrite(const quint32 &addr, const quint8 &data);
            void cpuIoRead(const quint16 &addr, quint8 &data);
            void cpuIoWrite(const quint16 &addr, const quint8 &data);
    
        public slots:
            void reset();       // Reset the CPU
            void singleStep();  // execute one command on the actual Memory-Adress
    
        private slots:
    
    };
    // --------------------------------------------------------------------------------
    #endif // Z180CPU_H
    
    #include "z180cpu.h"
    // --------------------------------------------------------------------------------
    Z180CPU::Z180CPU() {
        createCoreOpCodeArray();
        //reset();    // Reset CPU
    }
    
    // --------------------------------------------------------------------------------
    Z180CPU::~Z180CPU() {
    
    }
    
    // --------------------------------------------------------------------------------
    quint8 Z180CPU::memRead(const quint16 &addr) {
        quint8 iValue;
        emit cpuMemRead(addr, iValue);
        return iValue;
    }
    
    // --------------------------------------------------------------------------------
    void Z180CPU::memWrite(const quint16 &addr, const quint8 &data) {
        emit cpuMemWrite(addr, data);
    }
    
    // --------------------------------------------------------------------------------
    quint8 Z180CPU::ioRead(const quint8 &addr) {
        quint8 iValue;
        emit cpuIoRead((m_regA << 8) + addr, iValue);
        return iValue;
    }
    
    // --------------------------------------------------------------------------------
    void Z180CPU::ioWrite(const quint8 &addr, const quint8 &data) {
        emit cpuIoWrite((m_regA << 8) + addr, data);
    }
    
    // --------------------------------------------------------------------------------
    void Z180CPU::createCoreOpCodeArray() {
        m_OPcore[0x00] = &Z180CPU::op_NOP;      // OP-Code 0x00 : NOP
        m_OPcore[0x01] = &Z180CPU::op_LDBCnn;   // OP-Code 0x01 : LD BC,nn
        m_OPcore[0x02] = &Z180CPU::op_LDiBCA;   // OP-Code 0x02 : LD (BC),A
        m_OPcore[0x03] = &Z180CPU::op_INCBC;    // OP-Code 0x03 : INC BC
        m_OPcore[0x04] = &Z180CPU::op_INCB;     // OP-Code 0x04 : INC B
    }
    
    // --------------------------------------------------------------------------------
    int Z180CPU::op_NOP() {         // OP-Code 0x00 : NOP
        m_regPC++;
        return 4;
    }
    
    // --------------------------------------------------------------------------------
    int Z180CPU::op_LDBCnn() {      // OP-Code 0x01 : LD BC,nn
        m_regC = memRead(++m_regPC);
        m_regB = memRead(++m_regPC);
        m_regPC++;
        return 10;
    }
    
    // --------------------------------------------------------------------------------
    int Z180CPU::op_LDiBCA() {      // OP-Code 0x02 : LD (BC),A
        memWrite((m_regB << 8) + m_regC, m_regA);
        m_regPC++;
        return 7;
    }
    
    // --------------------------------------------------------------------------------
    int Z180CPU::op_INCBC() {       // OP-Code 0x03 : INC BC
        m_regC++;
        if(m_regC == 0) {
            m_regB++;
        }
        m_regPC++;
        return 6;
    }
    
    // --------------------------------------------------------------------------------
    int Z180CPU::op_INCB() {        // OP-Code 0x04 : INC B
        (m_regB == 0x7F) ? (m_regF |= Flag_PV) : (m_regF &= ~Flag_PV);
        ((m_regB & 0x0F) + 1 > 0x0F) ? (m_regF |= Flag_H) : (m_regF &= ~Flag_H);
        m_regB++;
        (m_regB & 0x80) ? (m_regF |= Flag_S) : (m_regF &= ~Flag_S);
        (m_regB == 0x00) ? (m_regF |= Flag_Z) : (m_regF &= ~Flag_Z);
        m_regF &= ~Flag_N;
        m_regPC++;
        return 4;
    }
    
    // --------------------------------------------------------------------------------
    void Z180CPU::reset() {
        m_regPC = 0x0000;
        m_regI = 0x00;
        m_regR = 0x00;
        emit cpuDataChange();
    }
    
    // --------------------------------------------------------------------------------
    void Z180CPU::singleStep() {
        (this->*this->m_OPcore[memRead(m_regPC)])();
        emit cpuDataChange();
    }
    
    // --------------------------------------------------------------------------------
    

    Ich möchte nun die Deklarationen und die Implementierungen der OP-Codes in jeweils eigene Dateien auslagern. Und zwar sozusagen
    als "Grundbefehlssatz" und dann jeweils noch für die erweiterten "Präfixbefehle". Das wären insgesamt 8 Gruppen mit zusammen ca.
    800 OP-Codes. Diese alle in der dargestellten Klasse unterzubringen wäre wohl doch etwas unübersichtlich.
    Jede Gruppe muß dabei auf die Register- und Flag-Datetypen zugreifen können. Auch die Methoden memRead, memWrite, ioRead und ioWrite
    müssen für alle Gruppen verfügbar sein.

    Wäre schön wenn mir da jemand auf die Sprünge helfen kann.

    Grüße Netzschleicher



  • Bei sowas ist vielleicht doch eher Performance und nicht Übersichtlichkeit ausschlaggebend?
    Wozu brauchst du hier Signals und Slots?

    Ansonsten könnte man das wohl irgendwie in Blöcke aufteilen und denen die CPU als Parameter reingeben?

    class ALU
    {
    public:
      ALU(Z180CPU * cpu);
    
      void executeOpCode(unsigned int op);
    };
    


  • Die Performance des ganzen hat absolut keine Priorität. Mal ganz abgesehen davon, das der zu simulierende Prozessor aus den 80er Jahren stammt und damals kaum mehr als 6MHz Taktfrequenz hatte. 🙂

    Das Signal- Slot-Konzept habe ich deshalb gewählt, weil ich damit wie ich meine, die Speicher- und IO-Zugriffe sehr gut abbilden kann.

    Gerade das Aufteilen in Blöcke und vor allem ohne groß irgendwie Konstruktor Parameter übergeben zu müssen möchte ich vermeiden.
    Ich habe auch schon an Namespaces gedacht, dieses aber noch nicht weiters ausgeführt.



  • Dann musst schon genauer erklären, was du dir vorstellst. Wenn du keine "Blöcke" (also Klassen?) willst, dann kannst doch einfach Funktionen schreiben, die dann in anderen Dateien implementiert werden. Oder schreib einfach ein Beispiel, was du dir vorstellst, vielleicht denk ich immer noch in eine komplett falsche Richtung.



  • Da hab ich mich wohl etwas unverständlich ausgedrückt. Natürlich will ich das ganze in Blöcken und damit in mehreren Dateien haben. Also praktisch Z180CPU.H & Z180CPU.CPP für die eigentliche Abbildung der CPU. Dort sollen eben die Methoden und Signal und Slots für die Befehlsausführung und Speicherzugriffe sein. Diese CPU hat auch eine kleine Memory-Management-Unit. Diese soll dort auch untergebracht werden. Und dann eben für die einzelnen OP-Code Ebenen jeweils.
    Also z.B. Z180ASM_CORE.H & Z180ASM_CORE.CPP , Z180ASM_PrefDD.H & Z180ASM_PrefDD.CPP, Z180ASM_PrefFD.H & Z180ASM_PrefFD.CPP.
    Pro OP-Code Modul dachte ich an ein eigenes OP-Code Array. Und da die Präfixe alle über die Z180ASM_CORE aufgerufen werden sollen, müsste das dann dort auch wieder zusammengefügt werden. 🙂



  • Du brauchst ja eigentlich kein "Array", sondern du hast einen Bereich von Opcodes, sagen wir Modul A ist für Opcodes 20-30 zuständig?
    Aber das kannst ja auch so machen, wenn du willst.



  • In dem Array kann ich eben bequem die verschiedenen OP-Code Methoden als Funktions-Pointer ablegen und drauf zugreifen.
    Das mit den verschiedenen OP-Code Bereichen ist schon richtig. Das Modul Z180ASM_Core wäre dann für die Code 0x00 bis 0xFF zuständig. Das Modul Z180ASM_PrefDD dann für alle OP-Codes wo mit 0xDD beginnen. Dahinter kommen dann wieder weitere OP-Codes von 0x00 bis 0xFF. Das Modul Z180ASM_CORE muß dann bei erkanntem OP-Code 0xDD eben an das Modul Z180ASM_PrefDD weiterleiten. Die Systematik dahinter ist kein Problem. Lediglich die Aufteilung in verschiedene Dateien um das ganze übersichtlicher zu halten. Wie eingangs schon erwähnt ergeben sich aus den Möglichen CORE- und Präfix-Komibnationen ca. 800 Methoden mit den Simulierten Assembler-Befehlen des Prozessors.


Anmelden zum Antworten