Do some stuff

This commit is contained in:
2025-02-05 20:32:36 +01:00
parent 2daec68484
commit 9837729656
7 changed files with 630 additions and 396 deletions

View File

@@ -56,12 +56,33 @@ int parseRegister(const char *token) {
// Parse an immediate value (supports decimal and 0x... hexadecimal)
uint8_t parseImmediate(const char *token) {
int value;
if (strlen(token) > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X'))
sscanf(token, "%x", &value);
else
sscanf(token, "%d", &value);
return (uint8_t) value;
int16_t value = 0; // Temporary variable as signed int16_t
// Check if the value starts with '0x' or '0X' for hexadecimal
if (strlen(token) > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X')) {
// Handle hexadecimal: Check for signed hex (e.g. -0x1F4, +0x1F4)
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) {
@@ -79,10 +100,10 @@ void toUpperCase(char *string) {
//
int getOpcode(char *mnemonic) {
toUpperCase(mnemonic);
if (strcmp(mnemonic, "BRK") == 0)
return BRK;
else if (strcmp(mnemonic, "NOP") == 0)
if (strcmp(mnemonic, "NOP") == 0)
return NOP;
else if (strcmp(mnemonic, "BRK") == 0)
return BRK;
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
else if (strcmp(mnemonic, "SWAP") == 0)
@@ -116,13 +137,21 @@ int getOpcode(char *mnemonic) {
else if (strcmp(mnemonic, "SAR") == 0)
return SAR_RN_IMM;
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)
return CMP;
else if (strcmp(mnemonic, "JE") == 0)
return JE;
else if (strcmp(mnemonic, "JNE") == 0)
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)
return JG;
else if (strcmp(mnemonic, "JL") == 0)
@@ -135,14 +164,6 @@ int getOpcode(char *mnemonic) {
return CALL;
else if (strcmp(mnemonic, "RET") == 0)
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 {
return -1;
}
@@ -172,7 +193,7 @@ int resolveMOV(const char *dest, const char *src) {
int resolveALU(int baseOpcode, const char *src) {
// 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) {
case -3:
return ADD_RN_RM;
@@ -190,10 +211,25 @@ int resolveALU(int baseOpcode, const char *src) {
return OR_RN_RM;
case -10:
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:
return -1;
}
else
} else if (src[0] == '+' || src[0] == '-') {
switch (baseOpcode) {
case -11:
return JMP_REL;
default:
return -1;
}
} else {
switch (baseOpcode) {
case -3:
return ADD_RN_IMM;
@@ -211,9 +247,20 @@ int resolveALU(int baseOpcode, const char *src) {
return OR_RN_IMM;
case -10:
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:
return -1;
}
}
}
// Reads a single line from the source string.
@@ -237,19 +284,19 @@ int firstPass(const char *source) {
const char *ptr = source;
while (*ptr) {
// Read a line from the source string
ptr = readLine(ptr, line, sizeof(line));
trim(line);
// Skip blank lines or comments.
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
continue; // Skip empty or comment lines
continue;
// Process labels.
char *colon = strchr(line, ':');
if (colon != NULL) {
*colon = '\0';
trim(line);
addLabel(line, addr);
char *rest = colon + 1;
trim(rest);
if (strlen(rest) == 0)
@@ -257,44 +304,125 @@ int firstPass(const char *source) {
strcpy(line, rest);
}
// For simplicity, we assume each instruction (with its operands) takes a fixed number of bytes.
// Here we calculate the number of bytes by looking at the opcode mnemonic.
// (A more robust approach would have a table for instruction sizes.)
char mnemonic[32];
sscanf(line, "%31s", mnemonic);
// Parse the mnemonic and operands.
char mnemonic[32], operand1[64], operand2[64];
operand1[0] = '\0';
operand2[0] = '\0';
sscanf(line, "%31s %63[^,], %63s", mnemonic, operand1, operand2);
int opcode = getOpcode(mnemonic);
if (opcode == -2) {
// MOV: two operands separated by comma
// e.g. MOV R1, 42
// We add 3 bytes: opcode, operand1, operand2.
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;
// Use the mapper to get a base opcode.
int baseOpcode = getOpcode(mnemonic);
if (baseOpcode == -1) {
printf("Unknown instruction: %s\n", mnemonic);
continue;
}
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;
}
@@ -315,100 +443,86 @@ int secondPass(const char *source, uint8_t *code) {
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
continue;
// Process labels: replace colon with a space.
char *colon = strchr(line, ':');
if (colon != NULL) {
*colon = ' ';
}
if (strlen(line) == 0)
continue;
char *token = strtok(line, " ,");
if (!token)
continue;
// Parse the mnemonic and operands.
char mnemonic[32], operand1[64], operand2[64];
operand1[0] = '\0';
operand2[0] = '\0';
sscanf(line, "%31s %63[^,], %63s", mnemonic, operand1, operand2);
char mnemonic[32];
strncpy(mnemonic, token, sizeof(mnemonic));
int opcode = getOpcode(mnemonic);
code[addr++] = opcode;
// Use the mapper to get the base opcode.
int baseOpcode = getOpcode(mnemonic);
if (baseOpcode == -1) {
fprintf(stderr, "Unknown instruction: %s\n", mnemonic);
exit(1);
}
// Handle instructions that need operand disambiguation.
if (strcmp(mnemonic, "MOV") == 0) {
// Get first operand.
// --- MOV Instruction ---
if (baseOpcode == -2) { // MOV is ambiguous.
char *dest = strtok(NULL, " ,");
char *src = strtok(NULL, " ,");
if (!dest || !src) {
fprintf(stderr, "Error: MOV requires two operands.\n");
exit(1);
}
int opcode2 = resolveMOV(dest, src);
code[addr++] = opcode2;
// For the MOV instructions we decide that:
// - 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 resolvedOpcode = resolveMOV(dest, src);
code[addr++] = resolvedOpcode;
if (resolvedOpcode == MOV_RN_IMM) {
int reg = parseRegister(dest);
uint8_t imm = parseImmediate(src);
code[addr++] = reg;
code[addr++] = imm;
} else if (opcode2 == MOV_RN_RM) {
} else if (resolvedOpcode == MOV_RN_RM) {
int regDest = parseRegister(dest);
int regSrc = parseRegister(src);
code[addr++] = regDest;
code[addr++] = regSrc;
} else if (opcode2 == MOV_RN_ADDR) {
// src is memory reference like "[123]"
int regDest = parseRegister(dest);
// Remove the brackets.
} else if (resolvedOpcode == MOV_RN_ADDR) {
int reg = parseRegister(dest);
// Remove brackets from src, assuming format "[address]"
char addrStr[32];
strncpy(addrStr, src + 1, strlen(src) - 2);
addrStr[strlen(src) - 2] = '\0';
uint8_t memAddr = parseImmediate(addrStr);
code[addr++] = regDest;
code[addr++] = memAddr;
} else if (opcode2 == MOV_ADDR_RN) {
// dest is a memory reference, src is a register.
// Remove brackets from dest.
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
code[addr++] = reg;
code[addr++] = (memAddr >> 24) & 0xFF;
code[addr++] = (memAddr >> 16) & 0xFF;
code[addr++] = (memAddr >> 8) & 0xFF;
code[addr++] = memAddr & 0xFF;
} else if (resolvedOpcode == MOV_ADDR_RN) {
// dest is memory reference.
char addrStr[32];
strncpy(addrStr, dest + 1, strlen(dest) - 2);
addrStr[strlen(dest) - 2] = '\0';
uint8_t memAddr = parseImmediate(addrStr);
int regSrc = parseRegister(src);
code[addr++] = memAddr;
code[addr++] = regSrc;
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
int reg = parseRegister(src);
code[addr++] = (memAddr >> 24) & 0xFF;
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 ||
strcmp(mnemonic, "MUL") == 0 ||
strcmp(mnemonic, "DIV") == 0 ||
strcmp(mnemonic, "MOD") == 0 ||
strcmp(mnemonic, "AND") == 0 ||
strcmp(mnemonic, "OR") == 0 ||
strcmp(mnemonic, "XOR") == 0) {
// ALU instructions with two operands.
}
// --- ALU Instructions (Arithmetic, INC/DEC, etc.) ---
else if (baseOpcode < 0 && baseOpcode != -2 && baseOpcode != -11 && baseOpcode != -14 && baseOpcode != -15) {
// For arithmetic and INC/DEC instructions, use operand2.
char *dest = strtok(NULL, " ,");
char *src = strtok(NULL, " ,");
if (!dest || !src) {
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
exit(1);
}
int baseOpcode;
if (strcmp(mnemonic, "ADD") == 0) baseOpcode = -3;
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 resolvedOpcode = resolveALU(baseOpcode, src);
code[addr++] = resolvedOpcode;
int regDest = parseRegister(dest);
code[addr++] = regDest;
// For a register source, encode the register; for an immediate, encode the value.
if (src[0] == 'R' || src[0] == 'r') {
int regSrc = parseRegister(src);
code[addr++] = regSrc;
@@ -416,96 +530,158 @@ int secondPass(const char *source, uint8_t *code) {
uint8_t imm = parseImmediate(src);
code[addr++] = imm;
}
} else if (strcmp(mnemonic, "NEG") == 0 ||
strcmp(mnemonic, "SWAPN") == 0 ||
strcmp(mnemonic, "NOT") == 0) {
// One operand instructions.
char *op = strtok(NULL, " ,");
if (!op) {
fprintf(stderr, "Error: %s requires one operand.\n", mnemonic);
exit(1);
}
int opcode4 = getOpcode(mnemonic);
code[addr++] = opcode4;
int reg = parseRegister(op);
code[addr++] = reg;
} else if (strcmp(mnemonic, "SWAP") == 0 || strcmp(mnemonic, "CMP") == 0) {
// Two operand instructions: both registers.
char *op1 = strtok(NULL, " ,");
char *op2 = strtok(NULL, " ,");
if (!op1 || !op2) {
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
exit(1);
}
int opcode5 = getOpcode(mnemonic);
code[addr++] = opcode5;
int r1 = parseRegister(op1);
int r2 = parseRegister(op2);
code[addr++] = r1;
code[addr++] = r2;
} else if (strcmp(mnemonic, "SHL") == 0 ||
strcmp(mnemonic, "SHR") == 0 ||
strcmp(mnemonic, "SAR") == 0 ||
strcmp(mnemonic, "SHRS") == 0) {
// Shift instructions: one register operand and one immediate.
char *regToken = strtok(NULL, " ,");
char *immToken = strtok(NULL, " ,");
if (!regToken || !immToken) {
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
exit(1);
}
int opcode6 = getOpcode(mnemonic);
code[addr++] = opcode6;
int reg = parseRegister(regToken);
code[addr++] = reg;
uint8_t imm = parseImmediate(immToken);
code[addr++] = imm;
} else if (strcmp(mnemonic, "JMP") == 0 ||
strcmp(mnemonic, "JE") == 0 ||
strcmp(mnemonic, "JNE") == 0 ||
strcmp(mnemonic, "JG") == 0 ||
strcmp(mnemonic, "JL") == 0 ||
strcmp(mnemonic, "JGE") == 0 ||
strcmp(mnemonic, "JLE") == 0 ||
strcmp(mnemonic, "CALL") == 0) {
// Jump instructions: one operand which may be a label or an immediate address.
}
// --- Jump Instructions ---
else if (baseOpcode == -11) { // JMP (ambiguous)
// For JMP, the operand is the jump target.
char *operand = strtok(NULL, " ,");
if (!operand) {
fprintf(stderr, "Error: %s requires an operand.\n", mnemonic);
fprintf(stderr, "Error: JMP requires an operand.\n");
exit(1);
}
int opcode7 = getOpcode(mnemonic);
code[addr++] = opcode7;
// If the operand is not a number, assume it is a label.
if (!isdigit(operand[0])) {
int labelAddr = lookupLabel(operand);
if (labelAddr < 0) {
fprintf(stderr, "Error: undefined label '%s'\n", operand);
exit(1);
}
code[addr++] = (uint8_t) labelAddr;
int resolvedOpcode = resolveALU(baseOpcode, operand);
code[addr++] = resolvedOpcode;
if (operand[0] == '+' || operand[0] == '-') {
// Relative jump: 1-byte offset.
uint8_t offset = parseImmediate(operand);
code[addr++] = offset;
} else {
uint8_t imm = parseImmediate(operand);
code[addr++] = imm;
// 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;
}
} else if (strcmp(mnemonic, "RET") == 0 ||
strcmp(mnemonic, "PUSHF") == 0 ||
strcmp(mnemonic, "POPF") == 0) {
// Instructions with no operand.
int opcode8 = getOpcode(mnemonic);
code[addr++] = opcode8;
} else if (strcmp(mnemonic, "PUSH") == 0 ||
strcmp(mnemonic, "POP") == 0) {
// One operand (a register)
char *regToken = strtok(NULL, " ,");
if (!regToken) {
fprintf(stderr, "Error: %s requires a register operand.\n", mnemonic);
}
// --- 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 opcode9 = getOpcode(mnemonic);
code[addr++] = opcode9;
int reg = parseRegister(regToken);
code[addr++] = reg;
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;
} else {
// 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 *op2 = strtok(NULL, " ,");
if (!op1 || !op2) {
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
exit(1);
}
code[addr++] = baseOpcode;
int r1 = parseRegister(op1);
int r2 = parseRegister(op2);
code[addr++] = r1;
code[addr++] = r2;
}
break;
case SWAPN:
case NEG_RN:
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 *immToken = strtok(NULL, " ,");
if (!regToken || !immToken) {
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
exit(1);
}
code[addr++] = baseOpcode;
int reg = parseRegister(regToken);
code[addr++] = reg;
uint8_t imm = parseImmediate(immToken);
code[addr++] = imm;
}
break;
case JE:
case JNE:
case JG:
case JL:
case JGE:
case JLE:
case CALL: {
// One operand: jump target (label or immediate 32-bit address).
char *operand = strtok(NULL, " ,");
if (!operand) {
fprintf(stderr, "Error: %s requires an operand.\n", mnemonic);
exit(1);
}
code[addr++] = baseOpcode;
if (!isdigit(operand[0])) {
int labelAddr = lookupLabel(operand);
if (labelAddr < 0) {
fprintf(stderr, "Error: undefined label '%s'\n", operand);
exit(1);
}
code[addr++] = (labelAddr >> 24) & 0xFF;
code[addr++] = (labelAddr >> 16) & 0xFF;
code[addr++] = (labelAddr >> 8) & 0xFF;
code[addr++] = labelAddr & 0xFF;
} else {
uint32_t immAddr = (uint32_t) strtoul(operand, NULL, 0);
code[addr++] = (immAddr >> 24) & 0xFF;
code[addr++] = (immAddr >> 16) & 0xFF;
code[addr++] = (immAddr >> 8) & 0xFF;
code[addr++] = immAddr & 0xFF;
}
}
break;
case RET:
case BRK:
case NOP:
code[addr++] = baseOpcode;
break;
default:
fprintf(stderr, "Error: Unhandled opcode %d\n", baseOpcode);
exit(1);
}
} else {
fprintf(stderr, "Error: Unknown instruction '%s'\n", mnemonic);
exit(1);
@@ -514,12 +690,11 @@ int secondPass(const char *source, uint8_t *code) {
return addr;
}
void completePass(const char *input, CPU *cpu) {
void completePass(const char *input, CPU *cpu, bool erase) {
// First pass: determine label addresses.
firstPass(input);
memset(cpu->memory, 0, MEM_SIZE);
// Second pass: generate machine code.
if (erase) {
memset(cpu->memory, 0, MEM_SIZE);
}
secondPass(input, cpu->memory);
}

View File

@@ -81,7 +81,7 @@ int firstPass(const char *source);
//
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