Essentially, I'm learning how emulators work. I've got the core down, but I wanted to know if I'm actually doing this the right way, or if I should be doing it differently.

#include <stdio.h>

// Opcode + Argument Retriever
#define GetOpCodeControl1(x) (x>>14)
#define GetOpCodeControl2(x) ((x>>12)&0x3)
#define GetOpCode0Arg(x) (x&0x0FFF)
#define GetOpCode1Arg(x) ((x&0x0FF0)>>4)
#define GetOpCode2Arg(x) ((x&0x0F00)>>8)
#define GetArg1A(x) (x&0x000F)
#define GetArg2A(x) ((x&0x00F0)>>4)
#define GetArg2B(x) (x&0x000F)
#define GetArg3A(x) (x&0x3FFF)

// Limits 
#define REGISTERS_AVAILABLE 0xF+1
#define RAM_AVAILABLE 0xFFFF+1
#define SIZE_OF_STACK 0xFFFF+1
#define DEVICES_AVAILABLE 0xFFFF+1
#define FUNC_LIMIT 0xFF

// The registers
#define $pc     0x0 // Program Counter      What instruction are we on
#define $ram    0x1 // Ram Fetch            The result of RAM fetch
#define $e      0x2 // Overflow/Error if != 0
#define $l      0x3 // Load
#define $j      0x4 // Jump
#define $r      0x5 // Result
#define $d0     0x6 // Device 0             The address of the device to access
#define $d1     0x7 // Device 1             The command being sent to device (if applicable)
#define $p      0x8 // Stack Pop            Value from popped stack
#define $t0     0x9 // Temporary Registers  Expect these to change at any given time
#define $t1     0xA
#define $s0     0xB // General Registers    These will never be changed by the CPU itself
#define $s1     0xC
#define $s2     0xD
#define $s3     0xE
#define $s4     0xF

int main(void) {

    // Program Control
    int running = 1;

    // CPU control
    int halted = 0;
    int IP = 0x0;
    int lastOp = 0x0;
    int stackPos = 0x0;
    int funcCount = 0x0;
    int _0 = 0x0;
    int _1 = 0x1;

    // Memory Elements
    int registers[REGISTERS_AVAILABLE];
    int ram[RAM_AVAILABLE];
    int stack[SIZE_OF_STACK];

    // Example coderam
    ram[0x00] = 0x00F0; // FUNC
    ram[0x01] = 0xC003; // LOAD 0x3
    ram[0x02] = 0x803B; // MOV $l, $s0
    ram[0x03] = 0xC006; // LOAD 0x6
    ram[0x04] = 0x803C; // MOV $l, $s1
    ram[0x05] = 0x90BC; // ADD $s0, $s1
    ram[0x06] = 0x803D; // MOV $r, $s2
    ram[0x07] = 0x91BC; // SUB $s0, $s1
    ram[0x08] = 0x803E; // MOV $r, $s3
    ram[0x09] = 0x92BC; // MUL $s0, $s1
    ram[0x0a] = 0x809F; // MOV $t0, $s4
    ram[0x0b] = 0x98BC; // AND $s0, $s1
    ram[0x0c] = 0x8039; // MOV $r, $t0
    ram[0x0d] = 0x99BC; // OR $s0, $s1
    ram[0x0e] = 0x803A; // MOV $r, $t1
    ram[0x0f] = 0xA0DE; // EQU $s2, $s3
    ram[0x10] = 0x803B; // MOV $r, $s0
    ram[0x11] = 0xA4E9; // EOL $s3, $t0
    ram[0x12] = 0x803C; // MOV $r, $s1
    ram[0x13] = 0xA3FA; // GRE $s4, $t1
    ram[0x14] = 0x803D; // MOV $r, $s2
    ram[0x15] = 0x0000; // NOP
    ram[0x16] = 0x000F; // RET

    // Emulation start
    int varArgs;
    int varType;
    int varOp;
    int varA;
    int varB;

    while (running) {
        while (!halted) {
            lastOp = ram[IP];

            varArgs = GetOpCodeControl1(lastOp);
            varType = GetOpCodeControl2(lastOp);

            switch(varArgs) {
                case 0x0:
                    // 0 args required
                    varOp = GetOpCode0Arg(lastOp);
                    switch (varType) {
                        case 0x0:
                            // BASIC | I/O | JUMP
                            switch (varOp) {
                                case 0x000:
                                    // NOP
                                    break;
                                case 0xFFF:
                                    // HALT
                                    halted = 1;
                                    break;
                                case 0x0F0:
                                    // FUNC
                                    if (funcCount != FUNC_LIMIT) {
                                        funcCount += 1;
                                        for (int x=0;x<=0xF;x++) {
                                            stack[stackPos] = registers[x];
                                            stackPos += 1;
                                        }
                                    }
                                    else {
                                        registers[$e] = 0x00F0;
                                        halted = 1;
                                    }
                                    break;
                                case 0x00F:
                                    // RET
                                    funcCount -= 1;
                                    for (int x=0;x<=0xF;x++) {
                                        registers[x] = stack[stackPos];
                                        stackPos -= 1;
                                    }
                                    break;
                                case 0x001:
                                    // READ
                                    /* IMPLEMENT LATER */
                                    // registers[$r] = devices[registers[$d0]].read()
                                    break;
                                case 0x002:
                                    // WRITE
                                    /* IMPLEMENT LATER */
                                    // devices[registers[$d0]].write(registers[$d1])
                                    break;
                                case 0x200:
                                    IP = registers[$j];
                                    registers[$pc] = IP;
                                    break;
                            }
                            break;
                        case 0x1:
                            // Arith/logic
                            break;
                        case 0x2:
                            // Compare
                            break;
                        case 0x3:
                            // Stack
                            switch (varOp) {
                                case 0x800:
                                    // POP
                                    registers[$p] = stack[stackPos];
                                    stackPos -= 1;
                                    break;
                                case 0xC00:
                                    // FETCH
                                    registers[$ram] = ram[registers[$d0]];
                            }
                            break;
                    }
                    break;
                case 0x1:
                    // 1 arg required
                    varOp = GetOpCode1Arg(lastOp);
                    varA = GetArg1A(lastOp);
                    switch (varType) {
                        case 0x0:
                            // BASIC | I/O | JUMP
                            break;
                        case 0x1:
                            // Arith/logic
                            switch (varOp) {
                                case 0x00:
                                    // NOT
                                    registers[$r] = ~varA & 0xFFFF;
                                    break;
                                case 0x01:
                                    // NEG
                                    registers[$r] = (~varA & 0xFFFF)+1;
                                    break;
                                case 0x02:
                                    // SHL
                                    registers[$r] = (varA << 1)&0xFFFF;
                                    break;
                                case 0x06:
                                    // SHR
                                    registers[$r] = (varA >> 1);
                                    break;
                                case 0x0A:
                                    // ROL
                                    //
                                    //
                                    break;
                                case 0x0E:
                                    // ROR
                                    //
                                    //
                                    break;
                            }
                            break;
                        case 0x2:
                            // Compare
                            break;
                        case 0x3:
                            // Stack
                            switch (varOp) {
                                case 0x08:
                                    // PUSH
                                    stack[stackPos] = registers[varA];
                                    stackPos += 1;
                                    break;
                                case 0x0B:
                                    // PUT
                                    ram[registers[$d0]] = registers[varA];
                                    break;
                            }
                            break;
                    }
                    break;
                case 0x2:
                    // 2 args required
                    varOp = GetOpCode2Arg(lastOp);
                    varA = GetArg2A(lastOp);
                    varB = GetArg2B(lastOp);
                    switch (varType) {
                        case 0x0:
                            // BASIC | I/O | JUMP
                            switch (varOp) {
                                case 0x0:
                                    // MOV
                                    registers[varB] = registers[varA];
                                    break;
                            }
                            break;
                        case 0x1:
                            // Arith/logic
                            switch (varOp) {
                                case 0x0:
                                    // ADD
                                    registers[$r] = registers[varA] + registers[varB];
                                    break;
                                case 0x1:
                                    // SUB
                                    registers[$r] = registers[varA] - registers[varB];
                                    break;
                                case 0x2:
                                    // MUL
                                    int t;
                                    t = registers[varA] * registers[varB];
                                    registers[$t0] = t & 0x00FF;
                                    registers[$t1] = t & 0xFF00;
                                    break;
                                case 0x3:
                                    // DIV
                                    //
                                    //
                                    break;
                                case 0x8:
                                    // AND
                                    registers[$r] = registers[varA] & registers[varB];
                                    break;
                                case 0x9:
                                    // OR
                                    registers[$r] = registers[varA] | registers[varB];
                                    break;
                                case 0xA:
                                    // XOR
                                    registers[$r] = registers[varA] ^ registers[varB];
                                    break;
                            }
                            break;
                        case 0x2:
                            // Compare
                            switch (varOp) {
                                case 0x0:
                                    // EQU
                                    if (registers[varA] == registers[varB])
                                        registers[$r] = 1;
                                    else
                                        registers[$r] = 0;
                                    break;
                                case 0x1:
                                    // NOE
                                    if (registers[varA] == registers[varB])
                                        registers[$r] = 0;
                                    else
                                        registers[$r] = 1;
                                    break;
                                case 0x2:
                                    // LES
                                    if (registers[varA] < registers[varB])
                                        registers[$r] = 1;
                                    else
                                        registers[$r] = 0;
                                    break;
                                case 0x3:
                                    // GRE
                                    if (registers[varA] > registers[varB])
                                        registers[$r] = 1;
                                    else
                                        registers[$r] = 0;
                                    break;
                                case 0x4:
                                    // EOL
                                    if (registers[varA] <= registers[varB])
                                        registers[$r] = 1;
                                    else
                                        registers[$r] = 0;
                                    break;
                                case 0x5:
                                    // EOG
                                    if (registers[varA] >= registers[varB])
                                        registers[$r] = 1;
                                    else
                                        registers[$r] = 0;
                                    break;
                            }
                            break;
                        case 0x3:
                            // Stack
                            break;
                    }
                    break;
                case 0x3:
                    // immediate load of value
                    registers[$l] = GetArg3A(lastOp);
                    break;
            }
            IP += 1;
            registers[$pc] = IP;
            if (IP > RAM_AVAILABLE) {
                registers[$e] = 0xFFFF;
               halted = 1;
            }

        }
        running = 0;
    }


    return (0);
}

Recommended Answers

All 2 Replies

I can't help but think it could be done better without all the macros and one giant main function.

Btw, are you using C or C++ for this? I think classes could be used to your advantage.

I'm using C++.
I'm planning on expanding to throwing this into classes, kinda like (psuedocode):

class BaseDevice {
    int isinitialized();
    int id(); // Get ID of device
    void update(); // update device if initialized
}
BaseDevice[0xFFFF];

Then I can just add multiple devices and interface with them. I'm actually trying to learn how computers work, and build my own CPU/computer from scratch. albeit, not too complex, of course!

Macros were used in this little section because it allowed easy access to change them. I was focused primarily on if the actual logic could use any changes.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.