Do some stuff
This commit is contained in:
@@ -56,12 +56,33 @@ int parseRegister(const char *token) {
|
|||||||
|
|
||||||
// Parse an immediate value (supports decimal and 0x... hexadecimal)
|
// Parse an immediate value (supports decimal and 0x... hexadecimal)
|
||||||
uint8_t parseImmediate(const char *token) {
|
uint8_t parseImmediate(const char *token) {
|
||||||
int value;
|
int16_t value = 0; // Temporary variable as signed int16_t
|
||||||
if (strlen(token) > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X'))
|
|
||||||
sscanf(token, "%x", &value);
|
// Check if the value starts with '0x' or '0X' for hexadecimal
|
||||||
else
|
if (strlen(token) > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X')) {
|
||||||
sscanf(token, "%d", &value);
|
// Handle hexadecimal: Check for signed hex (e.g. -0x1F4, +0x1F4)
|
||||||
return (uint8_t) value;
|
if (token[2] == '+' || token[2] == '-') {
|
||||||
|
sscanf(token, "%hx", &value); // Hexadecimal signed (same as unsigned in sscanf)
|
||||||
|
if (token[2] == '-') value = -value; // Adjust sign if negative
|
||||||
|
} else {
|
||||||
|
// Hexadecimal unsigned value
|
||||||
|
sscanf(token, "%hx", &value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if the value has a signed prefix (+ or -) for decimal
|
||||||
|
if (token[0] == '+' || token[0] == '-') {
|
||||||
|
sscanf(token, "%hd", &value); // Signed decimal
|
||||||
|
} else {
|
||||||
|
// Unsigned decimal value
|
||||||
|
unsigned int unsigned_value;
|
||||||
|
sscanf(token, "%u", &unsigned_value);
|
||||||
|
value = (int16_t) unsigned_value; // Cast unsigned to signed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert signed 16-bit value to unsigned 8-bit value
|
||||||
|
// Ensure the value fits within the range of uint8_t (0 to 255)
|
||||||
|
return (uint8_t) (value & 0xFF); // Mask with 0xFF to discard upper bits
|
||||||
}
|
}
|
||||||
|
|
||||||
void toUpperCase(char *string) {
|
void toUpperCase(char *string) {
|
||||||
@@ -79,10 +100,10 @@ void toUpperCase(char *string) {
|
|||||||
//
|
//
|
||||||
int getOpcode(char *mnemonic) {
|
int getOpcode(char *mnemonic) {
|
||||||
toUpperCase(mnemonic);
|
toUpperCase(mnemonic);
|
||||||
if (strcmp(mnemonic, "BRK") == 0)
|
if (strcmp(mnemonic, "NOP") == 0)
|
||||||
return BRK;
|
|
||||||
else if (strcmp(mnemonic, "NOP") == 0)
|
|
||||||
return NOP;
|
return NOP;
|
||||||
|
else if (strcmp(mnemonic, "BRK") == 0)
|
||||||
|
return BRK;
|
||||||
else if (strcmp(mnemonic, "MOV") == 0)
|
else if (strcmp(mnemonic, "MOV") == 0)
|
||||||
return -2; // Special case: we must decide between MOV_RN_IMM, MOV_RN_RM, MOV_RN_ADDR, MOV_ADDR_RN
|
return -2; // Special case: we must decide between MOV_RN_IMM, MOV_RN_RM, MOV_RN_ADDR, MOV_ADDR_RN
|
||||||
else if (strcmp(mnemonic, "SWAP") == 0)
|
else if (strcmp(mnemonic, "SWAP") == 0)
|
||||||
@@ -116,13 +137,21 @@ int getOpcode(char *mnemonic) {
|
|||||||
else if (strcmp(mnemonic, "SAR") == 0)
|
else if (strcmp(mnemonic, "SAR") == 0)
|
||||||
return SAR_RN_IMM;
|
return SAR_RN_IMM;
|
||||||
else if (strcmp(mnemonic, "JMP") == 0)
|
else if (strcmp(mnemonic, "JMP") == 0)
|
||||||
return JMP;
|
return -11; //Special: if + or - present choose JMP_REL, otherwise JMP
|
||||||
|
else if (strcmp(mnemonic, "INC") == 0)
|
||||||
|
return -12; //Special: decide between INC_RN and INC_ADDR
|
||||||
|
else if (strcmp(mnemonic, "DEC") == 0)
|
||||||
|
return -13; //Special: decide between DEC_RN and DEC_ADDR
|
||||||
else if (strcmp(mnemonic, "CMP") == 0)
|
else if (strcmp(mnemonic, "CMP") == 0)
|
||||||
return CMP;
|
return CMP;
|
||||||
else if (strcmp(mnemonic, "JE") == 0)
|
else if (strcmp(mnemonic, "JE") == 0)
|
||||||
return JE;
|
return JE;
|
||||||
else if (strcmp(mnemonic, "JNE") == 0)
|
else if (strcmp(mnemonic, "JNE") == 0)
|
||||||
return JNE;
|
return JNE;
|
||||||
|
else if (strcmp(mnemonic, "JMPBS") == 0)
|
||||||
|
return -14; //Special: decide between JMP_BIT_SET_RN and JMP_BIT_SET_ADDR
|
||||||
|
else if (strcmp(mnemonic, "JMPBC") == 0)
|
||||||
|
return -15; //Special: decide between JMP_BIT_CLEAR_RN and JMP_BIT_CLEAR_ADDR
|
||||||
else if (strcmp(mnemonic, "JG") == 0)
|
else if (strcmp(mnemonic, "JG") == 0)
|
||||||
return JG;
|
return JG;
|
||||||
else if (strcmp(mnemonic, "JL") == 0)
|
else if (strcmp(mnemonic, "JL") == 0)
|
||||||
@@ -135,14 +164,6 @@ int getOpcode(char *mnemonic) {
|
|||||||
return CALL;
|
return CALL;
|
||||||
else if (strcmp(mnemonic, "RET") == 0)
|
else if (strcmp(mnemonic, "RET") == 0)
|
||||||
return RET;
|
return RET;
|
||||||
else if (strcmp(mnemonic, "PUSH") == 0)
|
|
||||||
return PUSH;
|
|
||||||
else if (strcmp(mnemonic, "POP") == 0)
|
|
||||||
return POP;
|
|
||||||
else if (strcmp(mnemonic, "PUSHF") == 0)
|
|
||||||
return PUSHF;
|
|
||||||
else if (strcmp(mnemonic, "POPF") == 0)
|
|
||||||
return POPF;
|
|
||||||
else {
|
else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -172,7 +193,7 @@ int resolveMOV(const char *dest, const char *src) {
|
|||||||
|
|
||||||
int resolveALU(int baseOpcode, const char *src) {
|
int resolveALU(int baseOpcode, const char *src) {
|
||||||
// baseOpcode is one of our special negative values for ADD, SUB, etc.
|
// baseOpcode is one of our special negative values for ADD, SUB, etc.
|
||||||
if (src[0] == 'R' || src[0] == 'r')
|
if (src[0] == 'R' || src[0] == 'r') {
|
||||||
switch (baseOpcode) {
|
switch (baseOpcode) {
|
||||||
case -3:
|
case -3:
|
||||||
return ADD_RN_RM;
|
return ADD_RN_RM;
|
||||||
@@ -190,10 +211,25 @@ int resolveALU(int baseOpcode, const char *src) {
|
|||||||
return OR_RN_RM;
|
return OR_RN_RM;
|
||||||
case -10:
|
case -10:
|
||||||
return XOR_RN_RM;
|
return XOR_RN_RM;
|
||||||
|
case -12:
|
||||||
|
return INC_RN;
|
||||||
|
case -13:
|
||||||
|
return DEC_RN;
|
||||||
|
case -14:
|
||||||
|
return JMP_BIT_SET_RN;
|
||||||
|
case -15:
|
||||||
|
return JMP_BIT_CLEAR_RN;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else
|
} else if (src[0] == '+' || src[0] == '-') {
|
||||||
|
switch (baseOpcode) {
|
||||||
|
case -11:
|
||||||
|
return JMP_REL;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
switch (baseOpcode) {
|
switch (baseOpcode) {
|
||||||
case -3:
|
case -3:
|
||||||
return ADD_RN_IMM;
|
return ADD_RN_IMM;
|
||||||
@@ -211,10 +247,21 @@ int resolveALU(int baseOpcode, const char *src) {
|
|||||||
return OR_RN_IMM;
|
return OR_RN_IMM;
|
||||||
case -10:
|
case -10:
|
||||||
return XOR_RN_IMM;
|
return XOR_RN_IMM;
|
||||||
|
case -11:
|
||||||
|
return JMP;
|
||||||
|
case -12:
|
||||||
|
return INC_ADDR;
|
||||||
|
case -13:
|
||||||
|
return DEC_ADDR;
|
||||||
|
case -14:
|
||||||
|
return JMP_BIT_SET_ADDR;
|
||||||
|
case -15:
|
||||||
|
return JMP_BIT_CLEAR_ADDR;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reads a single line from the source string.
|
// Reads a single line from the source string.
|
||||||
const char *readLine(const char *source, char *buffer, size_t maxLen) {
|
const char *readLine(const char *source, char *buffer, size_t maxLen) {
|
||||||
@@ -237,19 +284,19 @@ int firstPass(const char *source) {
|
|||||||
const char *ptr = source;
|
const char *ptr = source;
|
||||||
|
|
||||||
while (*ptr) {
|
while (*ptr) {
|
||||||
// Read a line from the source string
|
|
||||||
ptr = readLine(ptr, line, sizeof(line));
|
ptr = readLine(ptr, line, sizeof(line));
|
||||||
trim(line);
|
trim(line);
|
||||||
|
|
||||||
|
// Skip blank lines or comments.
|
||||||
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
|
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
|
||||||
continue; // Skip empty or comment lines
|
continue;
|
||||||
|
|
||||||
|
// Process labels.
|
||||||
char *colon = strchr(line, ':');
|
char *colon = strchr(line, ':');
|
||||||
if (colon != NULL) {
|
if (colon != NULL) {
|
||||||
*colon = '\0';
|
*colon = '\0';
|
||||||
trim(line);
|
trim(line);
|
||||||
addLabel(line, addr);
|
addLabel(line, addr);
|
||||||
|
|
||||||
char *rest = colon + 1;
|
char *rest = colon + 1;
|
||||||
trim(rest);
|
trim(rest);
|
||||||
if (strlen(rest) == 0)
|
if (strlen(rest) == 0)
|
||||||
@@ -257,44 +304,125 @@ int firstPass(const char *source) {
|
|||||||
strcpy(line, rest);
|
strcpy(line, rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For simplicity, we assume each instruction (with its operands) takes a fixed number of bytes.
|
// Parse the mnemonic and operands.
|
||||||
// Here we calculate the number of bytes by looking at the opcode mnemonic.
|
char mnemonic[32], operand1[64], operand2[64];
|
||||||
// (A more robust approach would have a table for instruction sizes.)
|
operand1[0] = '\0';
|
||||||
char mnemonic[32];
|
operand2[0] = '\0';
|
||||||
sscanf(line, "%31s", mnemonic);
|
sscanf(line, "%31s %63[^,], %63s", mnemonic, operand1, operand2);
|
||||||
|
|
||||||
int opcode = getOpcode(mnemonic);
|
// Use the mapper to get a base opcode.
|
||||||
if (opcode == -2) {
|
int baseOpcode = getOpcode(mnemonic);
|
||||||
// MOV: two operands separated by comma
|
if (baseOpcode == -1) {
|
||||||
// e.g. MOV R1, 42
|
printf("Unknown instruction: %s\n", mnemonic);
|
||||||
// We add 3 bytes: opcode, operand1, operand2.
|
continue;
|
||||||
addr += 3;
|
|
||||||
} else if (opcode == -3 || opcode == -4 || opcode == -5 || opcode == -6 ||
|
|
||||||
opcode == -7 || opcode == -8 || opcode == -9 || opcode == -10) {
|
|
||||||
// ALU instructions with two operands: 3 bytes.
|
|
||||||
addr += 3;
|
|
||||||
} else if (opcode == NEG_RN || opcode == SWAPN || opcode == NOT_RN) {
|
|
||||||
// One operand: 2 bytes.
|
|
||||||
addr += 2;
|
|
||||||
} else if (opcode == SWAP || opcode == CMP) {
|
|
||||||
// Two operands: 3 bytes.
|
|
||||||
addr += 3;
|
|
||||||
} else if (opcode == SHL_RN_IMM || opcode == SHR_RN_IMM ||
|
|
||||||
opcode == SAR_RN_IMM) {
|
|
||||||
addr += 3;
|
|
||||||
} else if (opcode == JMP || opcode == JE || opcode == JNE ||
|
|
||||||
opcode == JG || opcode == JL || opcode == JGE || opcode == JLE ||
|
|
||||||
opcode == CALL) {
|
|
||||||
// Jump or call: 2 bytes (opcode and one byte address/immediate).
|
|
||||||
addr += 2;
|
|
||||||
} else if (opcode == RET || opcode == PUSHF || opcode == POPF) {
|
|
||||||
addr += 1;
|
|
||||||
} else if (opcode == PUSH || opcode == POP) {
|
|
||||||
addr += 2;
|
|
||||||
} else {
|
|
||||||
// For other instructions, we assume 3 bytes.
|
|
||||||
addr += 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int size = 0; // Instruction size in bytes.
|
||||||
|
if (baseOpcode == -2) {
|
||||||
|
// MOV instruction requires further resolution.
|
||||||
|
int resolvedOpcode = resolveMOV(operand1, operand2);
|
||||||
|
if (resolvedOpcode == MOV_RN_IMM || resolvedOpcode == MOV_RN_RM) {
|
||||||
|
size = 3; // opcode (1) + reg (1) + immediate or register (1)
|
||||||
|
} else if (resolvedOpcode == MOV_RN_ADDR || resolvedOpcode == MOV_ADDR_RN) {
|
||||||
|
size = 6; // opcode (1) + one operand as register (1) and one 32-bit address (4) [+ padding if needed]
|
||||||
|
} else {
|
||||||
|
size = 3; // fallback
|
||||||
|
}
|
||||||
|
} else if (baseOpcode < 0) {
|
||||||
|
// Ambiguous instructions that use resolveALU.
|
||||||
|
// For JMP and jump-bit instructions, the jump target is in operand1.
|
||||||
|
if (baseOpcode == -11) {
|
||||||
|
// JMP: if operand1 starts with '+' or '-', it's relative.
|
||||||
|
if (operand1[0] == '+' || operand1[0] == '-') {
|
||||||
|
// resolve as JMP_REL.
|
||||||
|
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||||
|
size = 2; // opcode (1) + 1-byte relative offset (1)
|
||||||
|
} else {
|
||||||
|
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||||
|
size = 5; // opcode (1) + 32-bit absolute address (4)
|
||||||
|
}
|
||||||
|
} else if (baseOpcode == -14 || baseOpcode == -15) {
|
||||||
|
// JMPBS or JMPBC (jump if bit set/clear)
|
||||||
|
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||||
|
if (operand1[0] == 'R' || operand1[0] == 'r')
|
||||||
|
size = 7; // opcode (1) + register (1) + bit (1) + 32-bit jump address (4)
|
||||||
|
else
|
||||||
|
size = 10; // opcode (1) + 32-bit memory address (4) + bit (1) + 32-bit jump address (4)
|
||||||
|
} else {
|
||||||
|
// For arithmetic ALU instructions and INC/DEC,
|
||||||
|
// use operand2 to resolve.
|
||||||
|
int resolvedOpcode = resolveALU(baseOpcode, operand2);
|
||||||
|
switch (resolvedOpcode) {
|
||||||
|
case ADD_RN_RM:
|
||||||
|
case SUB_RN_RM:
|
||||||
|
case MUL_RN_RM:
|
||||||
|
case DIV_RN_RM:
|
||||||
|
case MOD_RN_RM:
|
||||||
|
case AND_RN_RM:
|
||||||
|
case OR_RN_RM:
|
||||||
|
case XOR_RN_RM:
|
||||||
|
case ADD_RN_IMM:
|
||||||
|
case SUB_RN_IMM:
|
||||||
|
case MUL_RN_IMM:
|
||||||
|
case DIV_RN_IMM:
|
||||||
|
case MOD_RN_IMM:
|
||||||
|
case AND_RN_IMM:
|
||||||
|
case OR_RN_IMM:
|
||||||
|
case XOR_RN_IMM:
|
||||||
|
size = 3; // opcode (1) + register (1) + reg/immediate (1)
|
||||||
|
break;
|
||||||
|
case INC_RN:
|
||||||
|
case DEC_RN:
|
||||||
|
size = 2; // opcode (1) + register (1)
|
||||||
|
break;
|
||||||
|
case INC_ADDR:
|
||||||
|
case DEC_ADDR:
|
||||||
|
size = 5; // opcode (1) + 32-bit address (4)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
size = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-ambiguous instructions that have positive opcodes.
|
||||||
|
// Use the mapping value (baseOpcode) directly.
|
||||||
|
switch (baseOpcode) {
|
||||||
|
case NOP:
|
||||||
|
case BRK:
|
||||||
|
size = 1;
|
||||||
|
break;
|
||||||
|
case SWAP:
|
||||||
|
case CMP:
|
||||||
|
size = 3;
|
||||||
|
break;
|
||||||
|
case SWAPN:
|
||||||
|
case NEG_RN:
|
||||||
|
case NOT_RN:
|
||||||
|
size = 2;
|
||||||
|
break;
|
||||||
|
case SHL_RN_IMM:
|
||||||
|
case SHR_RN_IMM:
|
||||||
|
case SAR_RN_IMM:
|
||||||
|
size = 3;
|
||||||
|
break;
|
||||||
|
case JE:
|
||||||
|
case JNE:
|
||||||
|
case JG:
|
||||||
|
case JL:
|
||||||
|
case JGE:
|
||||||
|
case JLE:
|
||||||
|
case CALL:
|
||||||
|
size = 5; // opcode (1) + 32-bit address (4)\n break;
|
||||||
|
case RET:
|
||||||
|
size = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
size = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addr += size;
|
||||||
}
|
}
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
@@ -315,100 +443,86 @@ int secondPass(const char *source, uint8_t *code) {
|
|||||||
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
|
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Process labels: replace colon with a space.
|
||||||
char *colon = strchr(line, ':');
|
char *colon = strchr(line, ':');
|
||||||
if (colon != NULL) {
|
if (colon != NULL) {
|
||||||
*colon = ' ';
|
*colon = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(line) == 0)
|
if (strlen(line) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
char *token = strtok(line, " ,");
|
// Parse the mnemonic and operands.
|
||||||
if (!token)
|
char mnemonic[32], operand1[64], operand2[64];
|
||||||
continue;
|
operand1[0] = '\0';
|
||||||
|
operand2[0] = '\0';
|
||||||
|
sscanf(line, "%31s %63[^,], %63s", mnemonic, operand1, operand2);
|
||||||
|
|
||||||
char mnemonic[32];
|
// Use the mapper to get the base opcode.
|
||||||
strncpy(mnemonic, token, sizeof(mnemonic));
|
int baseOpcode = getOpcode(mnemonic);
|
||||||
int opcode = getOpcode(mnemonic);
|
if (baseOpcode == -1) {
|
||||||
code[addr++] = opcode;
|
fprintf(stderr, "Unknown instruction: %s\n", mnemonic);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle instructions that need operand disambiguation.
|
// --- MOV Instruction ---
|
||||||
if (strcmp(mnemonic, "MOV") == 0) {
|
if (baseOpcode == -2) { // MOV is ambiguous.
|
||||||
// Get first operand.
|
|
||||||
char *dest = strtok(NULL, " ,");
|
char *dest = strtok(NULL, " ,");
|
||||||
char *src = strtok(NULL, " ,");
|
char *src = strtok(NULL, " ,");
|
||||||
if (!dest || !src) {
|
if (!dest || !src) {
|
||||||
fprintf(stderr, "Error: MOV requires two operands.\n");
|
fprintf(stderr, "Error: MOV requires two operands.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int opcode2 = resolveMOV(dest, src);
|
int resolvedOpcode = resolveMOV(dest, src);
|
||||||
code[addr++] = opcode2;
|
code[addr++] = resolvedOpcode;
|
||||||
// For the MOV instructions we decide that:
|
if (resolvedOpcode == MOV_RN_IMM) {
|
||||||
// - For MOV_RN_IMM: operand bytes: [register, immediate]
|
|
||||||
// - For MOV_RN_RM: operand bytes: [dest register, src register]
|
|
||||||
// - For MOV_RN_ADDR: operand bytes: [dest register, address]
|
|
||||||
// - For MOV_ADDR_RN: operand bytes: [address, register]
|
|
||||||
if (opcode2 == MOV_RN_IMM) {
|
|
||||||
int reg = parseRegister(dest);
|
int reg = parseRegister(dest);
|
||||||
uint8_t imm = parseImmediate(src);
|
uint8_t imm = parseImmediate(src);
|
||||||
code[addr++] = reg;
|
code[addr++] = reg;
|
||||||
code[addr++] = imm;
|
code[addr++] = imm;
|
||||||
} else if (opcode2 == MOV_RN_RM) {
|
} else if (resolvedOpcode == MOV_RN_RM) {
|
||||||
int regDest = parseRegister(dest);
|
int regDest = parseRegister(dest);
|
||||||
int regSrc = parseRegister(src);
|
int regSrc = parseRegister(src);
|
||||||
code[addr++] = regDest;
|
code[addr++] = regDest;
|
||||||
code[addr++] = regSrc;
|
code[addr++] = regSrc;
|
||||||
} else if (opcode2 == MOV_RN_ADDR) {
|
} else if (resolvedOpcode == MOV_RN_ADDR) {
|
||||||
// src is memory reference like "[123]"
|
int reg = parseRegister(dest);
|
||||||
int regDest = parseRegister(dest);
|
// Remove brackets from src, assuming format "[address]"
|
||||||
// Remove the brackets.
|
|
||||||
char addrStr[32];
|
char addrStr[32];
|
||||||
strncpy(addrStr, src + 1, strlen(src) - 2);
|
strncpy(addrStr, src + 1, strlen(src) - 2);
|
||||||
addrStr[strlen(src) - 2] = '\0';
|
addrStr[strlen(src) - 2] = '\0';
|
||||||
uint8_t memAddr = parseImmediate(addrStr);
|
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
|
||||||
code[addr++] = regDest;
|
code[addr++] = reg;
|
||||||
code[addr++] = memAddr;
|
code[addr++] = (memAddr >> 24) & 0xFF;
|
||||||
} else if (opcode2 == MOV_ADDR_RN) {
|
code[addr++] = (memAddr >> 16) & 0xFF;
|
||||||
// dest is a memory reference, src is a register.
|
code[addr++] = (memAddr >> 8) & 0xFF;
|
||||||
// Remove brackets from dest.
|
code[addr++] = memAddr & 0xFF;
|
||||||
|
} else if (resolvedOpcode == MOV_ADDR_RN) {
|
||||||
|
// dest is memory reference.
|
||||||
char addrStr[32];
|
char addrStr[32];
|
||||||
strncpy(addrStr, dest + 1, strlen(dest) - 2);
|
strncpy(addrStr, dest + 1, strlen(dest) - 2);
|
||||||
addrStr[strlen(dest) - 2] = '\0';
|
addrStr[strlen(dest) - 2] = '\0';
|
||||||
uint8_t memAddr = parseImmediate(addrStr);
|
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
|
||||||
int regSrc = parseRegister(src);
|
int reg = parseRegister(src);
|
||||||
code[addr++] = memAddr;
|
code[addr++] = (memAddr >> 24) & 0xFF;
|
||||||
code[addr++] = regSrc;
|
code[addr++] = (memAddr >> 16) & 0xFF;
|
||||||
|
code[addr++] = (memAddr >> 8) & 0xFF;
|
||||||
|
code[addr++] = memAddr & 0xFF;
|
||||||
|
code[addr++] = reg;
|
||||||
}
|
}
|
||||||
} else if (strcmp(mnemonic, "ADD") == 0 ||
|
}
|
||||||
strcmp(mnemonic, "SUB") == 0 ||
|
// --- ALU Instructions (Arithmetic, INC/DEC, etc.) ---
|
||||||
strcmp(mnemonic, "MUL") == 0 ||
|
else if (baseOpcode < 0 && baseOpcode != -2 && baseOpcode != -11 && baseOpcode != -14 && baseOpcode != -15) {
|
||||||
strcmp(mnemonic, "DIV") == 0 ||
|
// For arithmetic and INC/DEC instructions, use operand2.
|
||||||
strcmp(mnemonic, "MOD") == 0 ||
|
|
||||||
strcmp(mnemonic, "AND") == 0 ||
|
|
||||||
strcmp(mnemonic, "OR") == 0 ||
|
|
||||||
strcmp(mnemonic, "XOR") == 0) {
|
|
||||||
// ALU instructions with two operands.
|
|
||||||
char *dest = strtok(NULL, " ,");
|
char *dest = strtok(NULL, " ,");
|
||||||
char *src = strtok(NULL, " ,");
|
char *src = strtok(NULL, " ,");
|
||||||
if (!dest || !src) {
|
if (!dest || !src) {
|
||||||
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int baseOpcode;
|
int resolvedOpcode = resolveALU(baseOpcode, src);
|
||||||
if (strcmp(mnemonic, "ADD") == 0) baseOpcode = -3;
|
code[addr++] = resolvedOpcode;
|
||||||
else if (strcmp(mnemonic, "SUB") == 0) baseOpcode = -4;
|
|
||||||
else if (strcmp(mnemonic, "MUL") == 0) baseOpcode = -5;
|
|
||||||
else if (strcmp(mnemonic, "DIV") == 0) baseOpcode = -6;
|
|
||||||
else if (strcmp(mnemonic, "MOD") == 0) baseOpcode = -7;
|
|
||||||
else if (strcmp(mnemonic, "AND") == 0) baseOpcode = -8;
|
|
||||||
else if (strcmp(mnemonic, "OR") == 0) baseOpcode = -9;
|
|
||||||
else if (strcmp(mnemonic, "XOR") == 0) baseOpcode = -10;
|
|
||||||
else baseOpcode = -1;
|
|
||||||
int opcode3 = resolveALU(baseOpcode, src);
|
|
||||||
code[addr++] = opcode3;
|
|
||||||
int regDest = parseRegister(dest);
|
int regDest = parseRegister(dest);
|
||||||
code[addr++] = regDest;
|
code[addr++] = regDest;
|
||||||
// For a register source, encode the register; for an immediate, encode the value.
|
|
||||||
if (src[0] == 'R' || src[0] == 'r') {
|
if (src[0] == 'R' || src[0] == 'r') {
|
||||||
int regSrc = parseRegister(src);
|
int regSrc = parseRegister(src);
|
||||||
code[addr++] = regSrc;
|
code[addr++] = regSrc;
|
||||||
@@ -416,96 +530,158 @@ int secondPass(const char *source, uint8_t *code) {
|
|||||||
uint8_t imm = parseImmediate(src);
|
uint8_t imm = parseImmediate(src);
|
||||||
code[addr++] = imm;
|
code[addr++] = imm;
|
||||||
}
|
}
|
||||||
} else if (strcmp(mnemonic, "NEG") == 0 ||
|
}
|
||||||
strcmp(mnemonic, "SWAPN") == 0 ||
|
// --- Jump Instructions ---
|
||||||
strcmp(mnemonic, "NOT") == 0) {
|
else if (baseOpcode == -11) { // JMP (ambiguous)
|
||||||
// One operand instructions.
|
// For JMP, the operand is the jump target.
|
||||||
char *op = strtok(NULL, " ,");
|
char *operand = strtok(NULL, " ,");
|
||||||
if (!op) {
|
if (!operand) {
|
||||||
fprintf(stderr, "Error: %s requires one operand.\n", mnemonic);
|
fprintf(stderr, "Error: JMP requires an operand.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int opcode4 = getOpcode(mnemonic);
|
int resolvedOpcode = resolveALU(baseOpcode, operand);
|
||||||
code[addr++] = opcode4;
|
code[addr++] = resolvedOpcode;
|
||||||
int reg = parseRegister(op);
|
if (operand[0] == '+' || operand[0] == '-') {
|
||||||
|
// Relative jump: 1-byte offset.
|
||||||
|
uint8_t offset = parseImmediate(operand);
|
||||||
|
code[addr++] = offset;
|
||||||
|
} else {
|
||||||
|
// Absolute jump: 32-bit address.
|
||||||
|
uint32_t jumpAddr = (uint32_t) lookupLabel(operand);
|
||||||
|
code[addr++] = (jumpAddr >> 24) & 0xFF;
|
||||||
|
code[addr++] = (jumpAddr >> 16) & 0xFF;
|
||||||
|
code[addr++] = (jumpAddr >> 8) & 0xFF;
|
||||||
|
code[addr++] = jumpAddr & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- Jump Bit Set/Clear Instructions ---
|
||||||
|
else if (baseOpcode == -14 || baseOpcode == -15) {
|
||||||
|
// For JMPBS (jump if bit set) or JMPBC (jump if bit clear), the operand specifies the register/memory
|
||||||
|
// from which to test the bit, followed by the bit value and the jump target.
|
||||||
|
char *srcOperand = strtok(NULL, " ,"); // register or memory reference
|
||||||
|
char *bitToken = strtok(NULL, " ,");
|
||||||
|
char *target = strtok(NULL, " ,");
|
||||||
|
if (!srcOperand || !bitToken || !target) {
|
||||||
|
fprintf(stderr, "Error: %s requires three operands.\n", mnemonic);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
int resolvedOpcode = resolveALU(baseOpcode, srcOperand);
|
||||||
|
code[addr++] = resolvedOpcode;
|
||||||
|
// Encode the source operand.
|
||||||
|
if (srcOperand[0] == 'R' || srcOperand[0] == 'r') {
|
||||||
|
int reg = parseRegister(srcOperand);
|
||||||
code[addr++] = reg;
|
code[addr++] = reg;
|
||||||
} else if (strcmp(mnemonic, "SWAP") == 0 || strcmp(mnemonic, "CMP") == 0) {
|
} else {
|
||||||
// Two operand instructions: both registers.
|
// Memory reference: encode 32-bit address.
|
||||||
|
char addrStr[32];
|
||||||
|
strncpy(addrStr, srcOperand + 1, strlen(srcOperand) - 2);
|
||||||
|
addrStr[strlen(srcOperand) - 2] = '\\0';
|
||||||
|
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
|
||||||
|
code[addr++] = (memAddr >> 24) & 0xFF;
|
||||||
|
code[addr++] = (memAddr >> 16) & 0xFF;
|
||||||
|
code[addr++] = (memAddr >> 8) & 0xFF;
|
||||||
|
code[addr++] = memAddr & 0xFF;
|
||||||
|
}
|
||||||
|
// Encode the bit number (assumed to be a one-byte immediate).
|
||||||
|
uint8_t bitVal = parseImmediate(bitToken);
|
||||||
|
code[addr++] = bitVal;
|
||||||
|
// Encode the jump target as a 32-bit address.
|
||||||
|
uint32_t jumpAddr = (uint32_t) lookupLabel(target);
|
||||||
|
code[addr++] = (jumpAddr >> 24) & 0xFF;
|
||||||
|
code[addr++] = (jumpAddr >> 16) & 0xFF;
|
||||||
|
code[addr++] = (jumpAddr >> 8) & 0xFF;
|
||||||
|
code[addr++] = jumpAddr & 0xFF;
|
||||||
|
}
|
||||||
|
// --- Other Instructions (CMP, SWAP, NEG, NOT, SHL, SHR, SAR, JE, JNE, JG, JL, JGE, JLE, CALL, RET) ---
|
||||||
|
else if (baseOpcode > 0) {
|
||||||
|
// For instructions that are not ambiguous, simply encode the opcode and its operands.
|
||||||
|
switch (baseOpcode) {
|
||||||
|
case CMP:
|
||||||
|
case SWAP: { // Two register operands.
|
||||||
char *op1 = strtok(NULL, " ,");
|
char *op1 = strtok(NULL, " ,");
|
||||||
char *op2 = strtok(NULL, " ,");
|
char *op2 = strtok(NULL, " ,");
|
||||||
if (!op1 || !op2) {
|
if (!op1 || !op2) {
|
||||||
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int opcode5 = getOpcode(mnemonic);
|
code[addr++] = baseOpcode;
|
||||||
code[addr++] = opcode5;
|
|
||||||
int r1 = parseRegister(op1);
|
int r1 = parseRegister(op1);
|
||||||
int r2 = parseRegister(op2);
|
int r2 = parseRegister(op2);
|
||||||
code[addr++] = r1;
|
code[addr++] = r1;
|
||||||
code[addr++] = r2;
|
code[addr++] = r2;
|
||||||
} else if (strcmp(mnemonic, "SHL") == 0 ||
|
}
|
||||||
strcmp(mnemonic, "SHR") == 0 ||
|
break;
|
||||||
strcmp(mnemonic, "SAR") == 0 ||
|
case SWAPN:
|
||||||
strcmp(mnemonic, "SHRS") == 0) {
|
case NEG_RN:
|
||||||
// Shift instructions: one register operand and one immediate.
|
case NOT_RN: { // Single register operand.
|
||||||
|
char *op = strtok(NULL, " ,");
|
||||||
|
if (!op) {
|
||||||
|
fprintf(stderr, "Error: %s requires one operand.\n", mnemonic);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
code[addr++] = baseOpcode;
|
||||||
|
int reg = parseRegister(op);
|
||||||
|
code[addr++] = reg;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHL_RN_IMM:
|
||||||
|
case SHR_RN_IMM:
|
||||||
|
case SAR_RN_IMM: { // Shift: register and immediate operand.
|
||||||
char *regToken = strtok(NULL, " ,");
|
char *regToken = strtok(NULL, " ,");
|
||||||
char *immToken = strtok(NULL, " ,");
|
char *immToken = strtok(NULL, " ,");
|
||||||
if (!regToken || !immToken) {
|
if (!regToken || !immToken) {
|
||||||
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int opcode6 = getOpcode(mnemonic);
|
code[addr++] = baseOpcode;
|
||||||
code[addr++] = opcode6;
|
|
||||||
int reg = parseRegister(regToken);
|
int reg = parseRegister(regToken);
|
||||||
code[addr++] = reg;
|
code[addr++] = reg;
|
||||||
uint8_t imm = parseImmediate(immToken);
|
uint8_t imm = parseImmediate(immToken);
|
||||||
code[addr++] = imm;
|
code[addr++] = imm;
|
||||||
} else if (strcmp(mnemonic, "JMP") == 0 ||
|
}
|
||||||
strcmp(mnemonic, "JE") == 0 ||
|
break;
|
||||||
strcmp(mnemonic, "JNE") == 0 ||
|
case JE:
|
||||||
strcmp(mnemonic, "JG") == 0 ||
|
case JNE:
|
||||||
strcmp(mnemonic, "JL") == 0 ||
|
case JG:
|
||||||
strcmp(mnemonic, "JGE") == 0 ||
|
case JL:
|
||||||
strcmp(mnemonic, "JLE") == 0 ||
|
case JGE:
|
||||||
strcmp(mnemonic, "CALL") == 0) {
|
case JLE:
|
||||||
// Jump instructions: one operand which may be a label or an immediate address.
|
case CALL: {
|
||||||
|
// One operand: jump target (label or immediate 32-bit address).
|
||||||
char *operand = strtok(NULL, " ,");
|
char *operand = strtok(NULL, " ,");
|
||||||
if (!operand) {
|
if (!operand) {
|
||||||
fprintf(stderr, "Error: %s requires an operand.\n", mnemonic);
|
fprintf(stderr, "Error: %s requires an operand.\n", mnemonic);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int opcode7 = getOpcode(mnemonic);
|
code[addr++] = baseOpcode;
|
||||||
code[addr++] = opcode7;
|
|
||||||
// If the operand is not a number, assume it is a label.
|
|
||||||
if (!isdigit(operand[0])) {
|
if (!isdigit(operand[0])) {
|
||||||
int labelAddr = lookupLabel(operand);
|
int labelAddr = lookupLabel(operand);
|
||||||
if (labelAddr < 0) {
|
if (labelAddr < 0) {
|
||||||
fprintf(stderr, "Error: undefined label '%s'\n", operand);
|
fprintf(stderr, "Error: undefined label '%s'\n", operand);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
code[addr++] = (uint8_t) labelAddr;
|
code[addr++] = (labelAddr >> 24) & 0xFF;
|
||||||
|
code[addr++] = (labelAddr >> 16) & 0xFF;
|
||||||
|
code[addr++] = (labelAddr >> 8) & 0xFF;
|
||||||
|
code[addr++] = labelAddr & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t imm = parseImmediate(operand);
|
uint32_t immAddr = (uint32_t) strtoul(operand, NULL, 0);
|
||||||
code[addr++] = imm;
|
code[addr++] = (immAddr >> 24) & 0xFF;
|
||||||
|
code[addr++] = (immAddr >> 16) & 0xFF;
|
||||||
|
code[addr++] = (immAddr >> 8) & 0xFF;
|
||||||
|
code[addr++] = immAddr & 0xFF;
|
||||||
}
|
}
|
||||||
} else if (strcmp(mnemonic, "RET") == 0 ||
|
}
|
||||||
strcmp(mnemonic, "PUSHF") == 0 ||
|
break;
|
||||||
strcmp(mnemonic, "POPF") == 0) {
|
case RET:
|
||||||
// Instructions with no operand.
|
case BRK:
|
||||||
int opcode8 = getOpcode(mnemonic);
|
case NOP:
|
||||||
code[addr++] = opcode8;
|
code[addr++] = baseOpcode;
|
||||||
} else if (strcmp(mnemonic, "PUSH") == 0 ||
|
break;
|
||||||
strcmp(mnemonic, "POP") == 0) {
|
default:
|
||||||
// One operand (a register)
|
fprintf(stderr, "Error: Unhandled opcode %d\n", baseOpcode);
|
||||||
char *regToken = strtok(NULL, " ,");
|
|
||||||
if (!regToken) {
|
|
||||||
fprintf(stderr, "Error: %s requires a register operand.\n", mnemonic);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
int opcode9 = getOpcode(mnemonic);
|
|
||||||
code[addr++] = opcode9;
|
|
||||||
int reg = parseRegister(regToken);
|
|
||||||
code[addr++] = reg;
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error: Unknown instruction '%s'\n", mnemonic);
|
fprintf(stderr, "Error: Unknown instruction '%s'\n", mnemonic);
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -514,12 +690,11 @@ int secondPass(const char *source, uint8_t *code) {
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void completePass(const char *input, CPU *cpu) {
|
void completePass(const char *input, CPU *cpu, bool erase) {
|
||||||
// First pass: determine label addresses.
|
// First pass: determine label addresses.
|
||||||
firstPass(input);
|
firstPass(input);
|
||||||
|
if (erase) {
|
||||||
memset(cpu->memory, 0, MEM_SIZE);
|
memset(cpu->memory, 0, MEM_SIZE);
|
||||||
|
}
|
||||||
// Second pass: generate machine code.
|
|
||||||
secondPass(input, cpu->memory);
|
secondPass(input, cpu->memory);
|
||||||
}
|
}
|
@@ -81,7 +81,7 @@ int firstPass(const char *source);
|
|||||||
//
|
//
|
||||||
int secondPass(const char *source, uint8_t *code);
|
int secondPass(const char *source, uint8_t *code);
|
||||||
|
|
||||||
void completePass(const char *input, CPU *cpu);
|
void completePass(const char *input, CPU *cpu, bool erase);
|
||||||
|
|
||||||
|
|
||||||
#endif //RISCB_ASSEMBLER_H
|
#endif //RISCB_ASSEMBLER_H
|
||||||
|
306
cpu/core.c
306
cpu/core.c
@@ -9,7 +9,6 @@
|
|||||||
// Initialize CPU
|
// Initialize CPU
|
||||||
void init_cpu(CPU *cpu) {
|
void init_cpu(CPU *cpu) {
|
||||||
memset(cpu, 0, sizeof(CPU));
|
memset(cpu, 0, sizeof(CPU));
|
||||||
cpu->sp = MEM_SIZE - 1; // Stack grows downward
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
|
// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
|
||||||
@@ -17,21 +16,25 @@ void init_cpu(CPU *cpu) {
|
|||||||
static inline void set_flags(CPU *cpu, int32_t result) {
|
static inline void set_flags(CPU *cpu, int32_t result) {
|
||||||
cpu->flags = 0;
|
cpu->flags = 0;
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
cpu->flags |= 0x01; // Zero flag
|
cpu->flags |= CPU_FLAG_ZERO; // Zero flag
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
cpu->flags |= 0x02; // Negative flag
|
cpu->flags |= CPU_FLAG_NEGATIVE; // Negative flag
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a program (a byte array) on the given CPU.
|
// Execute one cycle
|
||||||
void step(CPU *cpu) {
|
void step(CPU *cpu) {
|
||||||
if (cpu->mode < EverySecond) {
|
if (!(cpu->mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cpu->pc >= MEM_SIZE) {
|
if (cpu->pc >= MEM_SIZE) {
|
||||||
cpu->mode = Done; //terminate
|
if (cpu->mode | CPU_MODE_LOOP) {
|
||||||
|
cpu->pc = 0;
|
||||||
|
} else {
|
||||||
|
cpu->mode |= CPU_MODE_HALTED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uint8_t opcode = read_mem(cpu, cpu->pc++);
|
uint8_t opcode = read_mem(cpu, cpu->pc++);
|
||||||
uint8_t reg1, reg2, imm, temp;
|
uint8_t reg1, reg2, imm, temp, temp2;
|
||||||
uint32_t newPC, addrTemp;
|
uint32_t newPC, addrTemp;
|
||||||
int32_t cmpResult;
|
int32_t cmpResult;
|
||||||
|
|
||||||
@@ -42,7 +45,7 @@ void step(CPU *cpu) {
|
|||||||
|
|
||||||
case BRK:
|
case BRK:
|
||||||
//Pause CPU (for breakpoints)
|
//Pause CPU (for breakpoints)
|
||||||
cpu->mode = Paused;
|
cpu->mode |= CPU_MODE_PAUSED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INC_RN:
|
case INC_RN:
|
||||||
@@ -50,11 +53,10 @@ void step(CPU *cpu) {
|
|||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
temp = read_reg(cpu, reg1);
|
temp = read_reg(cpu, reg1);
|
||||||
write_reg(cpu, reg1, temp + 1);
|
write_reg(cpu, reg1, temp + 1);
|
||||||
cpu->regs[reg1]++;
|
|
||||||
|
|
||||||
case INC_ADDR:
|
case INC_ADDR:
|
||||||
//Increment address
|
//Increment address
|
||||||
addrTemp = read_address(cpu);
|
addrTemp = read_address_argument(cpu);
|
||||||
temp = read_mem(cpu, addrTemp);
|
temp = read_mem(cpu, addrTemp);
|
||||||
write_mem(cpu, addrTemp, temp + 1);
|
write_mem(cpu, addrTemp, temp + 1);
|
||||||
|
|
||||||
@@ -63,11 +65,10 @@ void step(CPU *cpu) {
|
|||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
temp = read_reg(cpu, reg1);
|
temp = read_reg(cpu, reg1);
|
||||||
write_reg(cpu, reg1, temp - 1);
|
write_reg(cpu, reg1, temp - 1);
|
||||||
cpu->regs[reg1]++;
|
|
||||||
|
|
||||||
case DEC_ADDR:
|
case DEC_ADDR:
|
||||||
//Decrement address
|
//Decrement address
|
||||||
addrTemp = read_address(cpu);
|
addrTemp = read_address_argument(cpu);
|
||||||
temp = read_mem(cpu, addrTemp);
|
temp = read_mem(cpu, addrTemp);
|
||||||
write_mem(cpu, addrTemp, temp - 1);
|
write_mem(cpu, addrTemp, temp - 1);
|
||||||
|
|
||||||
@@ -88,211 +89,254 @@ void step(CPU *cpu) {
|
|||||||
case MOV_RN_ADDR:
|
case MOV_RN_ADDR:
|
||||||
// Store register to memory
|
// Store register to memory
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
addrTemp = read_address(cpu);
|
addrTemp = read_address_argument(cpu);
|
||||||
temp = read_reg(cpu, reg1);
|
temp = read_reg(cpu, reg1);
|
||||||
write_mem(cpu, addrTemp, temp);
|
write_mem(cpu, addrTemp, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOV_ADDR_RN:
|
case MOV_ADDR_RN:
|
||||||
// Load memory to register
|
// Load memory to register
|
||||||
addrTemp = read_address(cpu);
|
addrTemp = read_address_argument(cpu);
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
temp = read
|
temp = read_mem(cpu, addrTemp);
|
||||||
write_mem(cpu, addrTemp, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SWAP: {
|
case SWAP: {
|
||||||
// Swap contents of two registers.
|
// Swap contents of two registers.
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
temp = cpu->regs[reg1];
|
temp = read_reg(cpu, reg1);
|
||||||
cpu->regs[reg1] = cpu->regs[reg2];
|
temp2 = read_reg(cpu, reg2);
|
||||||
cpu->regs[reg2] = temp;
|
write_reg(cpu, reg1, temp2);
|
||||||
|
write_reg(cpu, reg2, temp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SWAPN: {
|
case SWAPN: {
|
||||||
// Swap the nibbles of a register.
|
// Swap the nibbles of a register.
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
uint8_t val = (uint8_t) cpu->regs[reg1];
|
temp = read_reg(cpu, reg1);
|
||||||
cpu->regs[reg1] = ((val & 0x0F) << 4) | ((val & 0xF0) >> 4);
|
cpu->regs[reg1] = ((temp & 0x0F) << 4) | ((temp & 0xF0) >> 4);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ADD_RN_RM:
|
case ADD_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] += cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp += read_reg(cpu, reg2);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ADD_RN_IMM:
|
case ADD_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] += imm;
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp += imm;
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SUB_RN_RM:
|
case SUB_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] -= cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp -= read_reg(cpu, reg2);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SUB_RN_IMM:
|
case SUB_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] -= imm;
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp -= imm;
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MUL_RN_RM:
|
case MUL_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] *= cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp *= read_reg(cpu, reg2);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MUL_RN_IMM:
|
case MUL_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] *= imm;
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp *= imm;
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DIV_RN_RM:
|
case DIV_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
if (cpu->regs[reg2] == 0) {
|
temp = read_reg(cpu, reg1);
|
||||||
|
temp2 = read_reg(cpu, reg2);
|
||||||
|
if (temp2 == 0) {
|
||||||
printf("Error: Division by zero!\n");
|
printf("Error: Division by zero!\n");
|
||||||
cpu->mode = Error;
|
cpu->mode |= CPU_MODE_ERROR;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cpu->regs[reg1] /= cpu->regs[reg2];
|
temp /= temp2;
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DIV_RN_IMM:
|
case DIV_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
if (imm == 0) {
|
if (imm == 0) {
|
||||||
printf("Error: Division by zero!\n");
|
printf("Error: Division by zero!\n");
|
||||||
cpu->mode = Error;
|
cpu->mode |= CPU_MODE_ERROR;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cpu->regs[reg1] /= imm;
|
temp /= imm;
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOD_RN_RM:
|
case MOD_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
if (cpu->regs[reg2] == 0) {
|
temp = read_reg(cpu, reg1);
|
||||||
|
temp2 = read_reg(cpu, reg2);
|
||||||
|
if (temp2 == 0) {
|
||||||
printf("Error: Modulo by zero!\n");
|
printf("Error: Modulo by zero!\n");
|
||||||
cpu->mode = Error;
|
cpu->mode |= CPU_MODE_ERROR;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cpu->regs[reg1] %= cpu->regs[reg2];
|
temp %= temp2;
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOD_RN_IMM:
|
case MOD_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
if (imm == 0) {
|
if (imm == 0) {
|
||||||
printf("Error: Modulo by zero!\n");
|
printf("Error: Modulo by zero!\n");
|
||||||
cpu->mode = Error;
|
cpu->mode |= CPU_MODE_ERROR;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cpu->regs[reg1] %= imm;
|
temp %= imm;
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NEG_RN:
|
case NEG_RN:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] = -((int32_t) cpu->regs[reg1]);
|
temp = -read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AND_RN_RM:
|
case AND_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] &= cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp &= read_reg(cpu, reg2);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AND_RN_IMM:
|
case AND_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] &= imm;
|
temp &= imm;
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OR_RN_RM:
|
case OR_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] |= cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp |= read_reg(cpu, reg2);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OR_RN_IMM:
|
case OR_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] |= imm;
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp |= imm;
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XOR_RN_RM:
|
case XOR_RN_RM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] ^= cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp ^= read_reg(cpu, reg2);
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XOR_RN_IMM:
|
case XOR_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] ^= imm;
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp ^= imm;
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NOT_RN:
|
case NOT_RN:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
cpu->regs[reg1] = ~cpu->regs[reg1];
|
temp = ~read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHL_RN_IMM:
|
case SHL_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] <<= imm;
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp <<= imm;
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHR_RN_IMM:
|
case SHR_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
cpu->regs[reg1] >>= imm; // Logical right shift (assuming unsigned value)
|
temp = read_reg(cpu, reg1);
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
temp >>= imm; // Logical right shift (assuming unsigned value)
|
||||||
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SAR_RN_IMM:
|
case SAR_RN_IMM:
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
imm = read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++);
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
// Arithmetic right shift; cast to signed before shifting.
|
// Arithmetic right shift; cast to signed before shifting.
|
||||||
cpu->regs[reg1] = ((int32_t) cpu->regs[reg1]) >> imm;
|
temp = ((int32_t) temp) >> imm;
|
||||||
set_flags(cpu, cpu->regs[reg1]);
|
write_reg(cpu, reg1, temp);
|
||||||
|
set_flags(cpu, temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JMP:
|
case JMP:
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JMP_REL:
|
case JMP_REL:
|
||||||
imm = (int32_t) read_mem(cpu, cpu->pc++);
|
imm = read_mem(cpu, cpu->pc++) & 0xFF;
|
||||||
cpu->pc += imm;
|
cpu->pc += imm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -300,31 +344,34 @@ void step(CPU *cpu) {
|
|||||||
// Compare two registers: set flags (Zero, Negative)
|
// Compare two registers: set flags (Zero, Negative)
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
reg2 = read_reg_number(cpu);
|
reg2 = read_reg_number(cpu);
|
||||||
cmpResult = (int32_t) cpu->regs[reg1] - (int32_t) cpu->regs[reg2];
|
temp = read_reg(cpu, reg1);
|
||||||
|
temp2 = read_reg(cpu, reg2);
|
||||||
|
cmpResult = (int32_t) temp - (int32_t) temp2;
|
||||||
set_flags(cpu, cmpResult);
|
set_flags(cpu, cmpResult);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JE_BIT_RN: {
|
case JMP_BIT_SET_RN: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
|
newPC = read_address_argument(cpu);
|
||||||
|
|
||||||
|
temp = read_reg(cpu, reg1);
|
||||||
if (reg1 >= REG_COUNT) {
|
if (reg1 >= REG_COUNT) {
|
||||||
reg1 = REG_COUNT - 1;
|
reg1 = REG_COUNT - 1;
|
||||||
}
|
}
|
||||||
uint8_t bit = read_mem(cpu, cpu->pc++);
|
|
||||||
if (bit > 7) {
|
if (bit > 7) {
|
||||||
bit = 7;
|
bit = 7;
|
||||||
}
|
}
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
if (temp & (1 << bit))
|
||||||
cpu->pc += 4;
|
|
||||||
if (cpu->regs[reg1] & (1 << bit))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JE_BIT_ADDR: {
|
case JMP_BIT_SET_ADDR: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
addrTemp = read_mem32(cpu, cpu->pc);
|
addrTemp = read_address_argument(cpu);
|
||||||
if (addrTemp >= MEM_SIZE) {
|
if (addrTemp >= MEM_SIZE) {
|
||||||
addrTemp = MEM_SIZE - 1;
|
addrTemp = MEM_SIZE - 1;
|
||||||
}
|
}
|
||||||
@@ -332,9 +379,8 @@ void step(CPU *cpu) {
|
|||||||
if (bit > 7) {
|
if (bit > 7) {
|
||||||
bit = 7;
|
bit = 7;
|
||||||
}
|
}
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (read_mem(cpu, addrTemp) & (1 << bit))
|
||||||
if (cpu->memory[addrTemp] & (1 << bit))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -342,134 +388,98 @@ void step(CPU *cpu) {
|
|||||||
|
|
||||||
case JE: {
|
case JE: {
|
||||||
// Jump if equal (Zero flag set)
|
// Jump if equal (Zero flag set)
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (cpu->flags & CPU_FLAG_ZERO)
|
||||||
if (cpu->flags & 0x01)
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JNE_BIT_RN: {
|
case JMP_BIT_CLEAR_RN: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
reg1 = read_reg_number(cpu);
|
reg1 = read_reg_number(cpu);
|
||||||
if (reg1 >= REG_COUNT) {
|
temp = read_reg(cpu, reg1);
|
||||||
reg1 = REG_COUNT - 1;
|
|
||||||
}
|
|
||||||
uint8_t bit = read_mem(cpu, cpu->pc++);
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
if (bit > 7) {
|
if (bit > 7) {
|
||||||
bit = 7;
|
bit = 7;
|
||||||
}
|
}
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (!(temp & (1 << bit)))
|
||||||
if (!(cpu->regs[reg1] & (1 << bit)))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JNE_BIT_ADDR: {
|
case JMP_BIT_CLEAR_ADDR: {
|
||||||
// Jump if bit in register set
|
// Jump if bit in register set
|
||||||
addrTemp = read_mem32(cpu, cpu->pc);
|
addrTemp = read_address_argument(cpu);
|
||||||
if (addrTemp >= MEM_SIZE) {
|
|
||||||
addrTemp = MEM_SIZE - 1;
|
|
||||||
}
|
|
||||||
uint8_t bit = read_mem(cpu, cpu->pc++);
|
uint8_t bit = read_mem(cpu, cpu->pc++);
|
||||||
if (bit > 7) {
|
if (bit > 7) {
|
||||||
bit = 7;
|
bit = 7;
|
||||||
}
|
}
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (!(read_mem(cpu, addrTemp) & (1 << bit)))
|
||||||
if (!(cpu->memory[addrTemp] & (1 << bit)))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JNE: {
|
case JNE: {
|
||||||
// Jump if not equal (Zero flag clear)
|
// Jump if not equal (Zero flag clear)
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (!(cpu->flags & CPU_FLAG_ZERO)) {
|
||||||
if (!(cpu->flags & 0x01))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JG: {
|
case JG: {
|
||||||
// Jump if greater: not negative and not zero.
|
// Jump if greater: not negative and not zero.
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (!(cpu->flags & CPU_FLAG_NEGATIVE) && !(cpu->flags & CPU_FLAG_ZERO)) {
|
||||||
if (((cpu->flags & 0x02) == 0) && ((cpu->flags & 0x01) == 0))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JL: {
|
case JL: {
|
||||||
// Jump if less: Negative flag set.
|
// Jump if less: Negative flag set.
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if (cpu->flags & CPU_FLAG_NEGATIVE) {
|
||||||
if (cpu->flags & 0x02)
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JGE: {
|
case JGE: {
|
||||||
// Jump if greater or equal: Zero flag set or negative clear.
|
// Jump if greater or equal: Zero flag set or negative clear.
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if ((cpu->flags & CPU_FLAG_ZERO) || !(cpu->flags & CPU_FLAG_NEGATIVE)) {
|
||||||
if ((cpu->flags & 0x01) || ((cpu->flags & 0x02) == 0))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JLE: {
|
case JLE: {
|
||||||
// Jump if less or equal: Negative flag set or Zero flag set.
|
// Jump if less or equal: Negative flag set or Zero flag set.
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
newPC = read_address_argument(cpu);
|
||||||
cpu->pc += 4;
|
if ((cpu->flags & CPU_FLAG_NEGATIVE) || (cpu->flags & CPU_FLAG_ZERO)) {
|
||||||
if ((cpu->flags & 0x02) || (cpu->flags & 0x01))
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CALL: {
|
case CALL: {
|
||||||
// Push the current PC onto the stack, then jump to the address.
|
newPC = read_address_argument(cpu);
|
||||||
newPC = read_mem32(cpu, cpu->pc);
|
write_stack(cpu);
|
||||||
cpu->pc += 4;
|
|
||||||
// Push return address (current PC) onto the stack.
|
|
||||||
write_mem32(cpu, cpu->sp, cpu->pc);
|
|
||||||
cpu->sp -= 4;
|
|
||||||
cpu->pc = newPC;
|
cpu->pc = newPC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RET:
|
case RET:
|
||||||
// For RET, assume that the return address was stored on the stack.
|
// For RET, assume that the return address was stored on the stack.
|
||||||
cpu->pc = read_mem(cpu, ++cpu->sp);
|
read_stack(cpu);
|
||||||
break;
|
|
||||||
|
|
||||||
case PUSH: {
|
|
||||||
// Push register value onto the stack.
|
|
||||||
reg1 = read_reg_number(cpu);
|
|
||||||
write_mem(cpu, cpu->sp--, (uint8_t) cpu->regs[reg1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case POP: {
|
|
||||||
// Pop a value from the stack into a register.
|
|
||||||
reg1 = read_reg_number(cpu);
|
|
||||||
cpu->regs[reg1] = read_mem(cpu, ++cpu->sp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PUSHF:
|
|
||||||
// Push the flags register.
|
|
||||||
write_mem(cpu, cpu->sp--, cpu->flags);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case POPF:
|
|
||||||
// Pop into the flags register.
|
|
||||||
cpu->flags = read_mem(cpu, ++cpu->sp);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("Unknown opcode: %d\n", opcode);
|
printf("Unknown opcode: %d\n", opcode);
|
||||||
cpu->mode = Error;
|
cpu->mode |= CPU_MODE_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
61
cpu/core.h
61
cpu/core.h
@@ -8,27 +8,31 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "stdio.h"
|
#include "stdio.h"
|
||||||
|
|
||||||
enum Mode {
|
|
||||||
Paused,
|
|
||||||
Done,
|
|
||||||
Error,
|
|
||||||
EverySecond,
|
|
||||||
EveryFrame,
|
|
||||||
MaxSpeed
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MEM_SIZE 65535
|
#define MEM_SIZE 65535
|
||||||
// Register count (register names R0 to R7)
|
// Register count (register names R0 to R7)
|
||||||
#define REG_COUNT 32
|
#define REG_COUNT 32
|
||||||
|
#define STACK_SIZE 255
|
||||||
|
|
||||||
|
#define CPU_FLAG_ZERO (1 << 0)
|
||||||
|
#define CPU_FLAG_NEGATIVE (1 << 1)
|
||||||
|
|
||||||
|
#define CPU_MODE_PAUSED (1 << 0)
|
||||||
|
#define CPU_MODE_HALTED (1 << 1)
|
||||||
|
#define CPU_MODE_ERROR (1 << 2)
|
||||||
|
#define CPU_MODE_LOOP (1 << 3)
|
||||||
|
#define CPU_MODE_STEP (1 << 4)
|
||||||
|
#define CPU_MODE_SECOND (1 << 5)
|
||||||
|
|
||||||
|
|
||||||
// CPU state
|
// CPU state
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t regs[REG_COUNT];
|
uint8_t regs[REG_COUNT];
|
||||||
uint8_t memory[MEM_SIZE];
|
uint8_t memory[MEM_SIZE];
|
||||||
uint32_t pc; // Program counter
|
uint32_t pc; // Program counter
|
||||||
uint32_t sp; // Stack pointer
|
uint32_t stack[STACK_SIZE]; // Stack pointer
|
||||||
|
uint32_t stack_ptr; // Stack pointer
|
||||||
uint8_t flags; // Status flags
|
uint8_t flags; // Status flags
|
||||||
enum Mode mode;
|
uint8_t mode;
|
||||||
} CPU;
|
} CPU;
|
||||||
|
|
||||||
void step(CPU *cpu);
|
void step(CPU *cpu);
|
||||||
@@ -45,12 +49,6 @@ typedef enum {
|
|||||||
|
|
||||||
BRK, // BRK - Pause CPU (halts execution until resumed)
|
BRK, // BRK - Pause CPU (halts execution until resumed)
|
||||||
|
|
||||||
INC_RN, //INC Rn - Increment register
|
|
||||||
INC_ADDR, //INC [Addr] - Increment address
|
|
||||||
|
|
||||||
DEC_RN, //DEC Rn - Increment register
|
|
||||||
DEC_ADDR, //DEC [Addr] - Increment address
|
|
||||||
|
|
||||||
MOV_RN_IMM, // MOV Rn, Imm - Move immediate to register (Rn = Imm)
|
MOV_RN_IMM, // MOV Rn, Imm - Move immediate to register (Rn = Imm)
|
||||||
MOV_RN_RM, // MOV Rn, Rm - Move value from one register to another (Rn = Rm)
|
MOV_RN_RM, // MOV Rn, Rm - Move value from one register to another (Rn = Rm)
|
||||||
MOV_RN_ADDR, // MOV Rn, [Addr] - Load value from memory address into register (Rn = [Addr])
|
MOV_RN_ADDR, // MOV Rn, [Addr] - Load value from memory address into register (Rn = [Addr])
|
||||||
@@ -98,15 +96,22 @@ typedef enum {
|
|||||||
|
|
||||||
JMP_REL, // JMP Offset - relative jump (offset is signed)
|
JMP_REL, // JMP Offset - relative jump (offset is signed)
|
||||||
|
|
||||||
|
INC_RN, //INC Rn - Increment register
|
||||||
|
INC_ADDR, //INC [Addr] - Increment address
|
||||||
|
|
||||||
|
DEC_RN, //DEC Rn - Increment register
|
||||||
|
DEC_ADDR, //DEC [Addr] - Increment address
|
||||||
|
|
||||||
CMP, // CMP Rn, Rm - Compare two registers (sets flags based on Rn - Rm)
|
CMP, // CMP Rn, Rm - Compare two registers (sets flags based on Rn - Rm)
|
||||||
|
|
||||||
JE, // JE Addr - Jump if equal (if zero flag set, PC = Addr)
|
JE, // JE Addr - Jump if equal (if zero flag set, PC = Addr)
|
||||||
JE_BIT_RN, // jump relative if a given bit in a register is set
|
|
||||||
JE_BIT_ADDR, // jump relative if a given bit in memory is set
|
|
||||||
|
|
||||||
JNE, // JNE Addr - Jump if not equal (if zero flag not set, PC = Addr)
|
JNE, // JNE Addr - Jump if not equal (if zero flag not set, PC = Addr)
|
||||||
JNE_BIT_RN, // jump relative if a given bit in a register is not set
|
|
||||||
JNE_BIT_ADDR, // jump relative if a given bit in memory is not set
|
JMP_BIT_SET_RN, // jump relative if a given bit in a register is set
|
||||||
|
JMP_BIT_SET_ADDR, // jump relative if a given bit in memory is set
|
||||||
|
|
||||||
|
JMP_BIT_CLEAR_RN, // jump relative if a given bit in a register is not set
|
||||||
|
JMP_BIT_CLEAR_ADDR, // jump relative if a given bit in memory is not set
|
||||||
|
|
||||||
JG, // JG Addr - Jump if greater (if greater flag set, PC = Addr)
|
JG, // JG Addr - Jump if greater (if greater flag set, PC = Addr)
|
||||||
|
|
||||||
@@ -116,17 +121,9 @@ typedef enum {
|
|||||||
|
|
||||||
JLE, // JLE Addr - Jump if less or equal (if less or zero flag set, PC = Addr)
|
JLE, // JLE Addr - Jump if less or equal (if less or zero flag set, PC = Addr)
|
||||||
|
|
||||||
CALL, // CALL Addr - Call subroutine (push PC to stack, PC = Addr)
|
CALL, // CALL Addr - Call subroutine (push PC, flags and registers to stack, PC = Addr)
|
||||||
|
|
||||||
RET, // RET - Return from subroutine (pop PC from stack)
|
RET, // RET - Return from subroutine (pop PC, flags and registers from stack)
|
||||||
|
|
||||||
PUSH, // PUSH Rn - Push register onto stack (stack[top] = Rn)
|
|
||||||
|
|
||||||
POP, // POP Rn - Pop value from stack into register (Rn = stack[top])
|
|
||||||
|
|
||||||
PUSHF, // PUSHF - Push flags onto stack (stack[top] = flags)
|
|
||||||
|
|
||||||
POPF // POPF - Pop flags from stack (flags = stack[top])
|
|
||||||
} Opcode;
|
} Opcode;
|
||||||
|
|
||||||
#endif //RISCB_CORE_H
|
#endif //RISCB_CORE_H
|
||||||
|
52
cpu/memory.c
52
cpu/memory.c
@@ -37,8 +37,8 @@ uint32_t read_mem32(CPU *cpu, uint32_t addr) {
|
|||||||
return read_mem16(cpu, addr) | (read_mem16(cpu, addr + 2) << 16);
|
return read_mem16(cpu, addr) | (read_mem16(cpu, addr + 2) << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t read_address(CPU *cpu) {
|
uint32_t read_address_argument(CPU *cpu) {
|
||||||
uint32_t out = read_mem16(cpu, cpu->pc) | (read_mem16(cpu, cpu->pc + 2) << 16);
|
uint32_t out = read_mem32(cpu, cpu->pc);
|
||||||
cpu->pc += 4;
|
cpu->pc += 4;
|
||||||
if (out >= MEM_SIZE) {
|
if (out >= MEM_SIZE) {
|
||||||
out = MEM_SIZE - 1;
|
out = MEM_SIZE - 1;
|
||||||
@@ -46,6 +46,54 @@ uint32_t read_address(CPU *cpu) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Push a 32-bit program counter (PC) onto the stack
|
||||||
|
void write_stack(CPU *cpu) {
|
||||||
|
if (cpu->pc >= MEM_SIZE) {
|
||||||
|
return; // Invalid memory address
|
||||||
|
}
|
||||||
|
if (cpu->stack_ptr >= STACK_SIZE - 1) {
|
||||||
|
return; // Stack overflow protection
|
||||||
|
}
|
||||||
|
cpu->stack[++cpu->stack_ptr] = cpu->pc;
|
||||||
|
for (uint32_t reg = 0; reg < REG_COUNT; reg += 4) {
|
||||||
|
uint32_t packed = cpu->regs[reg] |
|
||||||
|
((reg + 1 < REG_COUNT) ? (cpu->regs[reg + 1] << 8) : 0) |
|
||||||
|
((reg + 2 < REG_COUNT) ? (cpu->regs[reg + 2] << 16) : 0) |
|
||||||
|
((reg + 3 < REG_COUNT) ? (cpu->regs[reg + 3] << 24) : 0);
|
||||||
|
cpu->stack[++cpu->stack_ptr] = packed;
|
||||||
|
}
|
||||||
|
cpu->stack[++cpu->stack_ptr] = cpu->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop a 32-bit program counter (PC) and registers from the stack
|
||||||
|
void read_stack(CPU *cpu) {
|
||||||
|
if (cpu->stack_ptr == 0) {
|
||||||
|
return; // Stack underflow protection
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore flags
|
||||||
|
cpu->flags = cpu->stack[cpu->stack_ptr--];
|
||||||
|
|
||||||
|
// Restore registers
|
||||||
|
for (int32_t reg = REG_COUNT - 1; reg >= 0; reg -= 4) {
|
||||||
|
if (cpu->stack_ptr == 0) {
|
||||||
|
return; // Stack underflow protection
|
||||||
|
}
|
||||||
|
uint32_t packed = cpu->stack[cpu->stack_ptr--];
|
||||||
|
cpu->regs[reg] = (packed & 0xFF);
|
||||||
|
if (reg - 1 >= 0) cpu->regs[reg - 1] = ((packed >> 8) & 0xFF);
|
||||||
|
if (reg - 2 >= 0) cpu->regs[reg - 2] = ((packed >> 16) & 0xFF);
|
||||||
|
if (reg - 3 >= 0) cpu->regs[reg - 3] = ((packed >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore PC
|
||||||
|
if (cpu->stack_ptr == 0) {
|
||||||
|
return; // Stack underflow protection
|
||||||
|
}
|
||||||
|
cpu->pc = cpu->stack[cpu->stack_ptr--];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t read_reg_number(CPU *cpu) {
|
uint8_t read_reg_number(CPU *cpu) {
|
||||||
uint8_t out = read_mem(cpu, cpu->pc++);
|
uint8_t out = read_mem(cpu, cpu->pc++);
|
||||||
if (out >= REG_COUNT) {
|
if (out >= REG_COUNT) {
|
||||||
|
24
cpu/memory.h
24
cpu/memory.h
@@ -8,25 +8,29 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../cpu/core.h"
|
#include "../cpu/core.h"
|
||||||
|
|
||||||
uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value);
|
inline uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value);
|
||||||
|
|
||||||
uint8_t write_mem16(CPU *cpu, uint32_t addr, uint16_t value);
|
inline uint8_t write_mem16(CPU *cpu, uint32_t addr, uint16_t value);
|
||||||
|
|
||||||
uint32_t read_mem32(CPU *cpu, uint32_t addr);
|
inline uint32_t read_mem32(CPU *cpu, uint32_t addr);
|
||||||
|
|
||||||
uint32_t read_address(CPU *cpu);
|
inline uint32_t read_address_argument(CPU *cpu);
|
||||||
|
|
||||||
uint8_t read_reg_number(CPU *cpu);
|
void read_stack(CPU *cpu);
|
||||||
|
|
||||||
uint8_t read_reg(CPU *cpu, uint8_t number);
|
void write_stack(CPU *cpu);
|
||||||
|
|
||||||
uint8_t write_reg(CPU *cpu, uint8_t number, uint8_t value);
|
inline uint8_t read_reg_number(CPU *cpu);
|
||||||
|
|
||||||
uint16_t read_mem16(CPU *cpu, uint32_t addr);
|
inline uint8_t read_reg(CPU *cpu, uint8_t number);
|
||||||
|
|
||||||
uint8_t read_mem(CPU *cpu, uint32_t addr);
|
inline uint8_t write_reg(CPU *cpu, uint8_t number, uint8_t value);
|
||||||
|
|
||||||
uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value);
|
inline uint16_t read_mem16(CPU *cpu, uint32_t addr);
|
||||||
|
|
||||||
|
inline uint8_t read_mem(CPU *cpu, uint32_t addr);
|
||||||
|
|
||||||
|
inline uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value);
|
||||||
|
|
||||||
|
|
||||||
#endif //RISCB_MEMORY_H
|
#endif //RISCB_MEMORY_H
|
||||||
|
Reference in New Issue
Block a user