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);
}