From 9837729656edc91682c204ccd8f30bdb69e2c54f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Wed, 5 Feb 2025 20:32:36 +0100 Subject: [PATCH] Do some stuff --- assembler/assembler.c | 579 +++++++++++++++++++++++++++--------------- assembler/assembler.h | 2 +- cpu/core.c | 306 +++++++++++----------- cpu/core.h | 61 +++-- cpu/memory.c | 52 +++- cpu/memory.h | 24 +- main.c | 2 +- 7 files changed, 630 insertions(+), 396 deletions(-) diff --git a/assembler/assembler.c b/assembler/assembler.c index aaf1d3c..ff260ac 100644 --- a/assembler/assembler.c +++ b/assembler/assembler.c @@ -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); } \ No newline at end of file diff --git a/assembler/assembler.h b/assembler/assembler.h index 350f934..4214ab3 100644 --- a/assembler/assembler.h +++ b/assembler/assembler.h @@ -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 diff --git a/cpu/core.c b/cpu/core.c index 5298890..fa318bf 100644 --- a/cpu/core.c +++ b/cpu/core.c @@ -9,7 +9,6 @@ // Initialize CPU void init_cpu(CPU *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, @@ -17,21 +16,25 @@ void init_cpu(CPU *cpu) { static inline void set_flags(CPU *cpu, int32_t result) { cpu->flags = 0; if (result == 0) - cpu->flags |= 0x01; // Zero flag + cpu->flags |= CPU_FLAG_ZERO; // Zero flag 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) { - if (cpu->mode < EverySecond) { + if (!(cpu->mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR))) { return; } 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 reg1, reg2, imm, temp; + uint8_t reg1, reg2, imm, temp, temp2; uint32_t newPC, addrTemp; int32_t cmpResult; @@ -42,7 +45,7 @@ void step(CPU *cpu) { case BRK: //Pause CPU (for breakpoints) - cpu->mode = Paused; + cpu->mode |= CPU_MODE_PAUSED; break; case INC_RN: @@ -50,11 +53,10 @@ void step(CPU *cpu) { reg1 = read_reg_number(cpu); temp = read_reg(cpu, reg1); write_reg(cpu, reg1, temp + 1); - cpu->regs[reg1]++; case INC_ADDR: //Increment address - addrTemp = read_address(cpu); + addrTemp = read_address_argument(cpu); temp = read_mem(cpu, addrTemp); write_mem(cpu, addrTemp, temp + 1); @@ -63,11 +65,10 @@ void step(CPU *cpu) { reg1 = read_reg_number(cpu); temp = read_reg(cpu, reg1); write_reg(cpu, reg1, temp - 1); - cpu->regs[reg1]++; case DEC_ADDR: //Decrement address - addrTemp = read_address(cpu); + addrTemp = read_address_argument(cpu); temp = read_mem(cpu, addrTemp); write_mem(cpu, addrTemp, temp - 1); @@ -88,211 +89,254 @@ void step(CPU *cpu) { case MOV_RN_ADDR: // Store register to memory reg1 = read_reg_number(cpu); - addrTemp = read_address(cpu); + addrTemp = read_address_argument(cpu); temp = read_reg(cpu, reg1); write_mem(cpu, addrTemp, temp); break; case MOV_ADDR_RN: // Load memory to register - addrTemp = read_address(cpu); + addrTemp = read_address_argument(cpu); reg1 = read_reg_number(cpu); - temp = read - write_mem(cpu, addrTemp, cpu->regs[reg1]); + temp = read_mem(cpu, addrTemp); + write_reg(cpu, reg1, temp); break; case SWAP: { // Swap contents of two registers. reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - temp = cpu->regs[reg1]; - cpu->regs[reg1] = cpu->regs[reg2]; - cpu->regs[reg2] = temp; + temp = read_reg(cpu, reg1); + temp2 = read_reg(cpu, reg2); + write_reg(cpu, reg1, temp2); + write_reg(cpu, reg2, temp); break; } case SWAPN: { // Swap the nibbles of a register. reg1 = read_reg_number(cpu); - uint8_t val = (uint8_t) cpu->regs[reg1]; - cpu->regs[reg1] = ((val & 0x0F) << 4) | ((val & 0xF0) >> 4); + temp = read_reg(cpu, reg1); + cpu->regs[reg1] = ((temp & 0x0F) << 4) | ((temp & 0xF0) >> 4); + write_reg(cpu, reg1, temp); break; } case ADD_RN_RM: reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - cpu->regs[reg1] += cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp += read_reg(cpu, reg2); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case ADD_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] += imm; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp += imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case SUB_RN_RM: reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - cpu->regs[reg1] -= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp -= read_reg(cpu, reg2); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case SUB_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] -= imm; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp -= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case MUL_RN_RM: reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - cpu->regs[reg1] *= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp *= read_reg(cpu, reg2); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case MUL_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] *= imm; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp *= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case DIV_RN_RM: reg1 = 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"); - cpu->mode = Error; + cpu->mode |= CPU_MODE_ERROR; return; } - cpu->regs[reg1] /= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp /= temp2; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case DIV_RN_IMM: reg1 = read_reg_number(cpu); + temp = read_reg(cpu, reg1); imm = read_mem(cpu, cpu->pc++); if (imm == 0) { printf("Error: Division by zero!\n"); - cpu->mode = Error; + cpu->mode |= CPU_MODE_ERROR; return; } - cpu->regs[reg1] /= imm; - set_flags(cpu, cpu->regs[reg1]); + temp /= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case MOD_RN_RM: reg1 = 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"); - cpu->mode = Error; + cpu->mode |= CPU_MODE_ERROR; return; } - cpu->regs[reg1] %= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp %= temp2; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case MOD_RN_IMM: reg1 = read_reg_number(cpu); + temp = read_reg(cpu, reg1); imm = read_mem(cpu, cpu->pc++); if (imm == 0) { printf("Error: Modulo by zero!\n"); - cpu->mode = Error; + cpu->mode |= CPU_MODE_ERROR; return; } - cpu->regs[reg1] %= imm; - set_flags(cpu, cpu->regs[reg1]); + temp %= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case NEG_RN: reg1 = read_reg_number(cpu); - cpu->regs[reg1] = -((int32_t) cpu->regs[reg1]); - set_flags(cpu, cpu->regs[reg1]); + temp = -read_reg(cpu, reg1); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case AND_RN_RM: reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - cpu->regs[reg1] &= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp &= read_reg(cpu, reg2); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case AND_RN_IMM: reg1 = read_reg_number(cpu); + temp = read_reg(cpu, reg1); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] &= imm; - set_flags(cpu, cpu->regs[reg1]); + temp &= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case OR_RN_RM: reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - cpu->regs[reg1] |= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp |= read_reg(cpu, reg2); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case OR_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] |= imm; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp |= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case XOR_RN_RM: reg1 = read_reg_number(cpu); reg2 = read_reg_number(cpu); - cpu->regs[reg1] ^= cpu->regs[reg2]; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp ^= read_reg(cpu, reg2); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case XOR_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] ^= imm; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp ^= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case NOT_RN: reg1 = read_reg_number(cpu); - cpu->regs[reg1] = ~cpu->regs[reg1]; - set_flags(cpu, cpu->regs[reg1]); + temp = ~read_reg(cpu, reg1); + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case SHL_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] <<= imm; - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp <<= imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case SHR_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); - cpu->regs[reg1] >>= imm; // Logical right shift (assuming unsigned value) - set_flags(cpu, cpu->regs[reg1]); + temp = read_reg(cpu, reg1); + temp >>= imm; // Logical right shift (assuming unsigned value) + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case SAR_RN_IMM: reg1 = read_reg_number(cpu); imm = read_mem(cpu, cpu->pc++); + temp = read_reg(cpu, reg1); // Arithmetic right shift; cast to signed before shifting. - cpu->regs[reg1] = ((int32_t) cpu->regs[reg1]) >> imm; - set_flags(cpu, cpu->regs[reg1]); + temp = ((int32_t) temp) >> imm; + write_reg(cpu, reg1, temp); + set_flags(cpu, temp); break; case JMP: - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; + newPC = read_address_argument(cpu); cpu->pc = newPC; break; case JMP_REL: - imm = (int32_t) read_mem(cpu, cpu->pc++); + imm = read_mem(cpu, cpu->pc++) & 0xFF; cpu->pc += imm; break; @@ -300,31 +344,34 @@ void step(CPU *cpu) { // Compare two registers: set flags (Zero, Negative) reg1 = 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); break; } - case JE_BIT_RN: { + case JMP_BIT_SET_RN: { // Jump if bit in register set 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) { reg1 = REG_COUNT - 1; } - uint8_t bit = read_mem(cpu, cpu->pc++); if (bit > 7) { bit = 7; } - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (cpu->regs[reg1] & (1 << bit)) + if (temp & (1 << bit)) cpu->pc = newPC; break; } - case JE_BIT_ADDR: { + case JMP_BIT_SET_ADDR: { // 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; } @@ -332,9 +379,8 @@ void step(CPU *cpu) { if (bit > 7) { bit = 7; } - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (cpu->memory[addrTemp] & (1 << bit)) + newPC = read_address_argument(cpu); + if (read_mem(cpu, addrTemp) & (1 << bit)) cpu->pc = newPC; break; } @@ -342,134 +388,98 @@ void step(CPU *cpu) { case JE: { // Jump if equal (Zero flag set) - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (cpu->flags & 0x01) + newPC = read_address_argument(cpu); + if (cpu->flags & CPU_FLAG_ZERO) cpu->pc = newPC; break; } - case JNE_BIT_RN: { + case JMP_BIT_CLEAR_RN: { // Jump if bit in register set reg1 = read_reg_number(cpu); - if (reg1 >= REG_COUNT) { - reg1 = REG_COUNT - 1; - } + temp = read_reg(cpu, reg1); uint8_t bit = read_mem(cpu, cpu->pc++); if (bit > 7) { bit = 7; } - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (!(cpu->regs[reg1] & (1 << bit))) + newPC = read_address_argument(cpu); + if (!(temp & (1 << bit))) cpu->pc = newPC; break; } - case JNE_BIT_ADDR: { + case JMP_BIT_CLEAR_ADDR: { // Jump if bit in register set - addrTemp = read_mem32(cpu, cpu->pc); - if (addrTemp >= MEM_SIZE) { - addrTemp = MEM_SIZE - 1; - } + addrTemp = read_address_argument(cpu); uint8_t bit = read_mem(cpu, cpu->pc++); if (bit > 7) { bit = 7; } - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (!(cpu->memory[addrTemp] & (1 << bit))) + newPC = read_address_argument(cpu); + if (!(read_mem(cpu, addrTemp) & (1 << bit))) cpu->pc = newPC; break; } case JNE: { // Jump if not equal (Zero flag clear) - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (!(cpu->flags & 0x01)) + newPC = read_address_argument(cpu); + if (!(cpu->flags & CPU_FLAG_ZERO)) { cpu->pc = newPC; + } break; } case JG: { // Jump if greater: not negative and not zero. - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (((cpu->flags & 0x02) == 0) && ((cpu->flags & 0x01) == 0)) + newPC = read_address_argument(cpu); + if (!(cpu->flags & CPU_FLAG_NEGATIVE) && !(cpu->flags & CPU_FLAG_ZERO)) { cpu->pc = newPC; + } break; } case JL: { // Jump if less: Negative flag set. - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if (cpu->flags & 0x02) + newPC = read_address_argument(cpu); + if (cpu->flags & CPU_FLAG_NEGATIVE) { cpu->pc = newPC; + } break; } case JGE: { // Jump if greater or equal: Zero flag set or negative clear. - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if ((cpu->flags & 0x01) || ((cpu->flags & 0x02) == 0)) + newPC = read_address_argument(cpu); + if ((cpu->flags & CPU_FLAG_ZERO) || !(cpu->flags & CPU_FLAG_NEGATIVE)) { cpu->pc = newPC; + } break; } case JLE: { // Jump if less or equal: Negative flag set or Zero flag set. - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - if ((cpu->flags & 0x02) || (cpu->flags & 0x01)) + newPC = read_address_argument(cpu); + if ((cpu->flags & CPU_FLAG_NEGATIVE) || (cpu->flags & CPU_FLAG_ZERO)) { cpu->pc = newPC; + } break; } case CALL: { - // Push the current PC onto the stack, then jump to the address. - newPC = read_mem32(cpu, cpu->pc); - cpu->pc += 4; - // Push return address (current PC) onto the stack. - write_mem32(cpu, cpu->sp, cpu->pc); - cpu->sp -= 4; + newPC = read_address_argument(cpu); + write_stack(cpu); cpu->pc = newPC; break; } case RET: // For RET, assume that the return address was stored on the stack. - cpu->pc = read_mem(cpu, ++cpu->sp); - 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); + read_stack(cpu); break; default: printf("Unknown opcode: %d\n", opcode); - cpu->mode = Error; + cpu->mode |= CPU_MODE_ERROR; } } \ No newline at end of file diff --git a/cpu/core.h b/cpu/core.h index 3d9d9bc..4163a2b 100644 --- a/cpu/core.h +++ b/cpu/core.h @@ -8,27 +8,31 @@ #include #include "stdio.h" -enum Mode { - Paused, - Done, - Error, - EverySecond, - EveryFrame, - MaxSpeed -}; - #define MEM_SIZE 65535 // Register count (register names R0 to R7) #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 typedef struct { uint8_t regs[REG_COUNT]; uint8_t memory[MEM_SIZE]; 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 - enum Mode mode; + uint8_t mode; } CPU; void step(CPU *cpu); @@ -45,12 +49,6 @@ typedef enum { 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_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]) @@ -98,15 +96,22 @@ typedef enum { 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) 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_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) @@ -116,17 +121,9 @@ typedef enum { 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) - - 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]) + RET, // RET - Return from subroutine (pop PC, flags and registers from stack) } Opcode; #endif //RISCB_CORE_H diff --git a/cpu/memory.c b/cpu/memory.c index 95ad8f7..6a11c60 100644 --- a/cpu/memory.c +++ b/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); } -uint32_t read_address(CPU *cpu) { - uint32_t out = read_mem16(cpu, cpu->pc) | (read_mem16(cpu, cpu->pc + 2) << 16); +uint32_t read_address_argument(CPU *cpu) { + uint32_t out = read_mem32(cpu, cpu->pc); cpu->pc += 4; if (out >= MEM_SIZE) { out = MEM_SIZE - 1; @@ -46,6 +46,54 @@ uint32_t read_address(CPU *cpu) { 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 out = read_mem(cpu, cpu->pc++); if (out >= REG_COUNT) { diff --git a/cpu/memory.h b/cpu/memory.h index f01838e..3b5c340 100644 --- a/cpu/memory.h +++ b/cpu/memory.h @@ -8,25 +8,29 @@ #include #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 diff --git a/main.c b/main.c index f779d65..ae23a5e 100644 --- a/main.c +++ b/main.c @@ -126,7 +126,7 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) uint8_t *program; int program_size; - completePass(programString, &cpu); + completePass(programString, &cpu, true); step(&cpu);