diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e84cff..d07cd6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,10 @@ add_executable(RISCB main.c cpu/core.c cpu/core.h util/texteditor.c - util/texteditor.h) # Ensure the target is defined before linking + util/texteditor.h + util/hexdump.c + util/hexdump.h + util/cpustatusui.c + util/cpustatusui.h) # Ensure the target is defined before linking target_link_libraries(RISCB SDL2 SDL2_ttf m) diff --git a/assembler/assembler.c b/assembler/assembler.c index f6d0158..79b69ab 100644 --- a/assembler/assembler.c +++ b/assembler/assembler.c @@ -32,10 +32,10 @@ int lookupLabel(const char *name) { } // Add a label to the table -void addLabel(const char *name, int address) { +int addLabel(const char *name, int address) { if (labelCount >= MAX_LABELS) { fprintf(stderr, "Too many labels!\n"); - exit(1); + return 1; } strncpy(labels[labelCount].name, name, sizeof(labels[labelCount].name)); labels[labelCount].address = address; @@ -90,6 +90,16 @@ void toUpperCase(char *string) { if (*string > 0x60 && *string < 0x7b) { (*string) -= 0x20; } + string++; + } +} + +void toLowerCase(char *string) { + while (*string) { + if (*string >= 'A' && *string <= 'Z') { + (*string) += 0x20; + } + string++; } } @@ -104,6 +114,8 @@ int getOpcode(char *mnemonic) { return NOP; else if (strcmp(mnemonic, "BRK") == 0) return BRK; + else if (strcmp(mnemonic, "HLT") == 0) + return HLT; 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) @@ -281,6 +293,7 @@ const char *readLine(const char *source, char *buffer, size_t maxLen) { int firstPass(const char *source) { char line[MAX_LINE_LENGTH]; int addr = 0; + labelCount = 0; const char *ptr = source; while (*ptr) { @@ -305,10 +318,12 @@ int firstPass(const char *source) { } // Parse the mnemonic and operands. - char mnemonic[32], operand1[64], operand2[64]; + char mnemonic[32], operand1[64], operand2[64], operand3[64]; operand1[0] = '\0'; operand2[0] = '\0'; - sscanf(line, "%31s %63[^,], %63s", mnemonic, operand1, operand2); + int tokenCount = sscanf(line, "%31s %63[^, ] %63[^, ] %63s", + mnemonic, operand1, operand2, operand3); + // Use the mapper to get a base opcode. int baseOpcode = getOpcode(mnemonic); @@ -390,6 +405,7 @@ int firstPass(const char *source) { switch (baseOpcode) { case NOP: case BRK: + case HLT: size = 1; break; case SWAP: @@ -439,57 +455,57 @@ int secondPass(const char *source, uint8_t *code) { while (*ptr) { ptr = readLine(ptr, line, sizeof(line)); trim(line); - if (line[0] == '\0' || line[0] == ';' || line[0] == '#') continue; - // Process labels: replace colon with a space. + // Remove any label definitions (up to the colon). char *colon = strchr(line, ':'); if (colon != NULL) { - *colon = ' '; + *colon = ' '; // Replace the colon so the rest of the line can be parsed. + continue; } if (strlen(line) == 0) 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); + // Parse mnemonic and up to three operands. + char mnemonic[32], operand1[64], operand2[64], operand3[64]; + mnemonic[0] = operand1[0] = operand2[0] = operand3[0] = '\0'; + int tokenCount = sscanf(line, "%31s %63[^, ] %63[^, ] %63s", + mnemonic, operand1, operand2, operand3); - // Use the mapper to get the base opcode. + // (Optionally, you might trim each operand individually here.) + + // Map the mnemonic to a base opcode. int baseOpcode = getOpcode(mnemonic); if (baseOpcode == -1) { fprintf(stderr, "Unknown instruction: %s\n", mnemonic); - exit(1); + return 1; } - // --- MOV Instruction --- - if (baseOpcode == -2) { // MOV is ambiguous. - char *dest = strtok(NULL, " ,"); - char *src = strtok(NULL, " ,"); - if (!dest || !src) { + // --- MOV Instruction (baseOpcode == -2) --- + if (baseOpcode == -2) { + if (strlen(operand1) == 0 || strlen(operand2) == 0) { fprintf(stderr, "Error: MOV requires two operands.\n"); - exit(1); + return 1; } - int resolvedOpcode = resolveMOV(dest, src); + int resolvedOpcode = resolveMOV(operand1, operand2); code[addr++] = resolvedOpcode; if (resolvedOpcode == MOV_RN_IMM) { - int reg = parseRegister(dest); - uint8_t imm = parseImmediate(src); + int reg = parseRegister(operand1); + uint8_t imm = parseImmediate(operand2); code[addr++] = reg; code[addr++] = imm; } else if (resolvedOpcode == MOV_RN_RM) { - int regDest = parseRegister(dest); - int regSrc = parseRegister(src); + int regDest = parseRegister(operand1); + int regSrc = parseRegister(operand2); code[addr++] = regDest; code[addr++] = regSrc; } else if (resolvedOpcode == MOV_RN_ADDR) { - int reg = parseRegister(dest); - // Remove brackets from src, assuming format "[address]" + int reg = parseRegister(operand1); + // Assume source is written as "[address]": remove the brackets. char addrStr[32]; - strncpy(addrStr, src + 1, strlen(src) - 2); - addrStr[strlen(src) - 2] = '\0'; + strncpy(addrStr, operand2 + 1, strlen(operand2) - 2); + addrStr[strlen(operand2) - 2] = '\0'; uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0); code[addr++] = reg; code[addr++] = (memAddr >> 24) & 0xFF; @@ -497,12 +513,12 @@ int secondPass(const char *source, uint8_t *code) { code[addr++] = (memAddr >> 8) & 0xFF; code[addr++] = memAddr & 0xFF; } else if (resolvedOpcode == MOV_ADDR_RN) { - // dest is memory reference. + // Destination is memory (written as "[address]"). char addrStr[32]; - strncpy(addrStr, dest + 1, strlen(dest) - 2); - addrStr[strlen(dest) - 2] = '\0'; + strncpy(addrStr, operand1 + 1, strlen(operand1) - 2); + addrStr[strlen(operand1) - 2] = '\0'; uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0); - int reg = parseRegister(src); + int reg = parseRegister(operand2); code[addr++] = (memAddr >> 24) & 0xFF; code[addr++] = (memAddr >> 16) & 0xFF; code[addr++] = (memAddr >> 8) & 0xFF; @@ -510,136 +526,145 @@ int secondPass(const char *source, uint8_t *code) { code[addr++] = reg; } } - // --- 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); + // --- INC and DEC (baseOpcode == -12 or -13) --- + // These instructions require only a single operand. + else if (baseOpcode == -12 || baseOpcode == -13) { + if (strlen(operand1) == 0) { + fprintf(stderr, "Error: %s requires one operand.\n", mnemonic); + return 1; } - int resolvedOpcode = resolveALU(baseOpcode, src); + int resolvedOpcode = resolveALU(baseOpcode, operand1); code[addr++] = resolvedOpcode; - int regDest = parseRegister(dest); + if (operand1[0] == 'R' || operand1[0] == 'r') { + int reg = parseRegister(operand1); + code[addr++] = reg; + } else { + // Assume memory reference written as "[address]". + char addrStr[32]; + strncpy(addrStr, operand1 + 1, strlen(operand1) - 2); + addrStr[strlen(operand1) - 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; + } + } + // --- Other Ambiguous ALU Instructions (ADD, SUB, MUL, etc.) --- + // These require two operands (destination and source). + else if (baseOpcode < 0 && baseOpcode != -2 && baseOpcode != -11 && + baseOpcode != -14 && baseOpcode != -15 && baseOpcode != -12 && baseOpcode != -13) { + if (strlen(operand1) == 0 || strlen(operand2) == 0) { + fprintf(stderr, "Error: %s requires two operands.\n", mnemonic); + return 1; + } + int resolvedOpcode = resolveALU(baseOpcode, operand2); + code[addr++] = resolvedOpcode; + int regDest = parseRegister(operand1); code[addr++] = regDest; - if (src[0] == 'R' || src[0] == 'r') { - int regSrc = parseRegister(src); + if (operand2[0] == 'R' || operand2[0] == 'r') { + int regSrc = parseRegister(operand2); code[addr++] = regSrc; } else { - uint8_t imm = parseImmediate(src); + uint8_t imm = parseImmediate(operand2); code[addr++] = imm; } } - // --- 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: JMP requires an operand.\n"); - exit(1); + // --- JMP Instruction (baseOpcode == -11) --- + else if (baseOpcode == -11) { + if (strlen(operand1) == 0) { + fprintf(stderr, "Error: JMP requires one operand.\n"); + return 1; } - int resolvedOpcode = resolveALU(baseOpcode, operand); + int resolvedOpcode = resolveALU(baseOpcode, operand1); code[addr++] = resolvedOpcode; - if (operand[0] == '+' || operand[0] == '-') { - // Relative jump: 1-byte offset. - uint8_t offset = parseImmediate(operand); + if (operand1[0] == '+' || operand1[0] == '-') { + // Relative jump: one-byte offset. + uint8_t offset = parseImmediate(operand1); code[addr++] = offset; } else { - // Absolute jump: 32-bit address. - uint32_t jumpAddr = (uint32_t) lookupLabel(operand); + // Absolute jump: use label lookup for 32-bit address. + uint32_t jumpAddr = (uint32_t) lookupLabel(operand1); code[addr++] = (jumpAddr >> 24) & 0xFF; code[addr++] = (jumpAddr >> 16) & 0xFF; code[addr++] = (jumpAddr >> 8) & 0xFF; code[addr++] = jumpAddr & 0xFF; } } - // --- Jump Bit Set/Clear Instructions --- + // --- Jump Bit Set/Clear Instructions (JMPBS, JMPBC) --- 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) { + if (strlen(operand1) == 0 || strlen(operand2) == 0 || strlen(operand3) == 0) { fprintf(stderr, "Error: %s requires three operands.\n", mnemonic); - exit(1); + return 1; } - int resolvedOpcode = resolveALU(baseOpcode, srcOperand); + int resolvedOpcode = resolveALU(baseOpcode, operand1); code[addr++] = resolvedOpcode; - // Encode the source operand. - if (srcOperand[0] == 'R' || srcOperand[0] == 'r') { - int reg = parseRegister(srcOperand); + // Encode the source operand (register or memory). + if (operand1[0] == 'R' || operand1[0] == 'r') { + int reg = parseRegister(operand1); 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'; + strncpy(addrStr, operand1 + 1, strlen(operand1) - 2); + addrStr[strlen(operand1) - 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); + // Encode the bit number (a one-byte immediate). + uint8_t bitVal = parseImmediate(operand2); code[addr++] = bitVal; - // Encode the jump target as a 32-bit address. - uint32_t jumpAddr = (uint32_t) lookupLabel(target); + // Encode the jump target (label -> 32-bit address). + uint32_t jumpAddr = (uint32_t) lookupLabel(operand3); 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) --- + // --- Non-ambiguous Instructions --- 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) { + case SWAP: { + if (strlen(operand1) == 0 || strlen(operand2) == 0) { fprintf(stderr, "Error: %s requires two operands.\n", mnemonic); - exit(1); + return 1; } code[addr++] = baseOpcode; - int r1 = parseRegister(op1); - int r2 = parseRegister(op2); + int r1 = parseRegister(operand1); + int r2 = parseRegister(operand2); code[addr++] = r1; code[addr++] = r2; - } break; + } case SWAPN: case NEG_RN: - case NOT_RN: { // Single register operand. - char *op = strtok(NULL, " ,"); - if (!op) { + case NOT_RN: { + if (strlen(operand1) == 0) { fprintf(stderr, "Error: %s requires one operand.\n", mnemonic); - exit(1); + return 1; } code[addr++] = baseOpcode; - int reg = parseRegister(op); + int reg = parseRegister(operand1); 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) { + case SAR_RN_IMM: { + if (strlen(operand1) == 0 || strlen(operand2) == 0) { fprintf(stderr, "Error: %s requires two operands.\n", mnemonic); - exit(1); + return 1; } code[addr++] = baseOpcode; - int reg = parseRegister(regToken); + int reg = parseRegister(operand1); code[addr++] = reg; - uint8_t imm = parseImmediate(immToken); + uint8_t imm = parseImmediate(operand2); code[addr++] = imm; - } break; + } case JE: case JNE: case JG: @@ -647,54 +672,58 @@ int secondPass(const char *source, uint8_t *code) { 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); + if (strlen(operand1) == 0) { + fprintf(stderr, "Error: %s requires one operand.\n", mnemonic); + return 1; } code[addr++] = baseOpcode; - if (!isdigit(operand[0])) { - int labelAddr = lookupLabel(operand); + // If the operand isn’t purely numeric, treat it as a label. + if (!isdigit(operand1[0])) { + int labelAddr = lookupLabel(operand1); if (labelAddr < 0) { - fprintf(stderr, "Error: undefined label '%s'\n", operand); - exit(1); + fprintf(stderr, "Error: undefined label '%s'\n", operand1); + return 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); + uint32_t immAddr = (uint32_t) strtoul(operand1, 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: + case HLT: + case NOP: { code[addr++] = baseOpcode; break; - default: + } + default: { fprintf(stderr, "Error: Unhandled opcode %d\n", baseOpcode); - exit(1); + return 1; + } } } else { fprintf(stderr, "Error: Unknown instruction '%s'\n", mnemonic); - exit(1); + return 1; } } + return addr; } + void completePass(const char *input, CPU *cpu, bool erase) { // First pass: determine label addresses. - firstPass(input); if (erase) { - memset(cpu->memory, 0, MEM_SIZE); + init_cpu(cpu); } + firstPass(input); secondPass(input, cpu->memory); } \ No newline at end of file diff --git a/assembler/assembler.h b/assembler/assembler.h index 4214ab3..d2c79a4 100644 --- a/assembler/assembler.h +++ b/assembler/assembler.h @@ -35,11 +35,15 @@ extern int labelCount; // void trim(char *s); +void toUpperCase(char *string); + +void toLowerCase(char *string); + // Look up a label by name; returns -1 if not found. int lookupLabel(const char *name); // Add a label to the table -void addLabel(const char *name, int address); +int addLabel(const char *name, int address); // // Parse a register string (e.g., "R0", "R1", etc.) and return it's number. diff --git a/cpu/core.c b/cpu/core.c index fa318bf..713d87b 100644 --- a/cpu/core.c +++ b/cpu/core.c @@ -9,6 +9,7 @@ // Initialize CPU void init_cpu(CPU *cpu) { memset(cpu, 0, sizeof(CPU)); + cpu->mode = CPU_MODE_HALTED; } // Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag, @@ -23,7 +24,7 @@ static inline void set_flags(CPU *cpu, int32_t result) { // Execute one cycle void step(CPU *cpu) { - if (!(cpu->mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR))) { + if (cpu->mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR)) { return; } if (cpu->pc >= MEM_SIZE) { @@ -48,29 +49,38 @@ void step(CPU *cpu) { cpu->mode |= CPU_MODE_PAUSED; break; + case HLT: + //Pause CPU (for breakpoints) + cpu->mode |= CPU_MODE_HALTED; + break; + case INC_RN: //Increment register reg1 = read_reg_number(cpu); temp = read_reg(cpu, reg1); write_reg(cpu, reg1, temp + 1); + break; case INC_ADDR: //Increment address addrTemp = read_address_argument(cpu); temp = read_mem(cpu, addrTemp); write_mem(cpu, addrTemp, temp + 1); + break; case DEC_RN: //Decrement register reg1 = read_reg_number(cpu); temp = read_reg(cpu, reg1); write_reg(cpu, reg1, temp - 1); + break; case DEC_ADDR: //Decrement address addrTemp = read_address_argument(cpu); temp = read_mem(cpu, addrTemp); write_mem(cpu, addrTemp, temp - 1); + break; case MOV_RN_IMM: //Load from immediate to register @@ -351,7 +361,7 @@ void step(CPU *cpu) { break; } - case JMP_BIT_SET_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++); @@ -367,7 +377,6 @@ void step(CPU *cpu) { if (temp & (1 << bit)) cpu->pc = newPC; break; - } case JMP_BIT_SET_ADDR: { // Jump if bit in register set @@ -482,4 +491,5 @@ void step(CPU *cpu) { printf("Unknown opcode: %d\n", opcode); cpu->mode |= CPU_MODE_ERROR; } + cpu->cycle++; } \ No newline at end of file diff --git a/cpu/core.h b/cpu/core.h index 4163a2b..397dc35 100644 --- a/cpu/core.h +++ b/cpu/core.h @@ -10,7 +10,7 @@ #define MEM_SIZE 65535 // Register count (register names R0 to R7) -#define REG_COUNT 32 +#define REG_COUNT 64 #define STACK_SIZE 255 #define CPU_FLAG_ZERO (1 << 0) @@ -33,6 +33,7 @@ typedef struct { uint32_t stack_ptr; // Stack pointer uint8_t flags; // Status flags uint8_t mode; + uint32_t cycle; } CPU; void step(CPU *cpu); @@ -49,6 +50,8 @@ typedef enum { BRK, // BRK - Pause CPU (halts execution until resumed) + HLT, + 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]) diff --git a/cpu/memory.h b/cpu/memory.h index 3b5c340..b955a13 100644 --- a/cpu/memory.h +++ b/cpu/memory.h @@ -8,29 +8,29 @@ #include #include "../cpu/core.h" -inline uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value); +uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value); -inline uint8_t write_mem16(CPU *cpu, uint32_t addr, uint16_t value); +uint8_t write_mem16(CPU *cpu, uint32_t addr, uint16_t value); -inline uint32_t read_mem32(CPU *cpu, uint32_t addr); +uint32_t read_mem32(CPU *cpu, uint32_t addr); -inline uint32_t read_address_argument(CPU *cpu); +uint32_t read_address_argument(CPU *cpu); void read_stack(CPU *cpu); void write_stack(CPU *cpu); -inline uint8_t read_reg_number(CPU *cpu); +uint8_t read_reg_number(CPU *cpu); -inline uint8_t read_reg(CPU *cpu, uint8_t number); +uint8_t read_reg(CPU *cpu, uint8_t number); -inline uint8_t write_reg(CPU *cpu, uint8_t number, uint8_t value); +uint8_t write_reg(CPU *cpu, uint8_t number, uint8_t value); -inline uint16_t read_mem16(CPU *cpu, uint32_t addr); +uint16_t read_mem16(CPU *cpu, uint32_t addr); -inline uint8_t read_mem(CPU *cpu, uint32_t addr); +uint8_t read_mem(CPU *cpu, uint32_t addr); -inline uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value); +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 42b3b86..4e8f000 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,8 @@ #include "util/font.h" #include "assembler/assembler.h" #include "util/texteditor.h" +#include "util/hexdump.h" +#include "util/cpustatusui.h" //Screen dimension constants const int SCREEN_WIDTH = 1280; @@ -17,19 +19,75 @@ SDL_Window *window = NULL; //The surface contained by the window SDL_Renderer *renderer = NULL; -BitmapFont smallFont; +#define biggerFont fonts[0] +#define smallFont fonts[1] +#define smallerFont fonts[2] + +#define fontCount 3 + +BitmapFont fonts[fontCount]; CPU cpu; -TextEditor codeEditor; +SDL_Texture *cpuStatsTexture; -TextEditor *activeEditor; +SDL_Texture *cpuStateTexture; + +#define codeEditor editors[0] +#define memoryViewer editors[1] +#define editorCount 2 +#define activeEditor editors[activeEditorIndex] + +int activeEditorIndex = 0; +TextEditor editors[editorCount]; + +unsigned long frames = 0; +bool cursor = true; + +char *read_file_as_string(const char *filename) { + FILE *file = fopen(filename, "rb"); // Open file in binary mode + if (file == NULL) { + perror("Error opening file"); + return NULL; + } + + // Seek to the end of the file to determine size + fseek(file, 0, SEEK_END); + long filesize = ftell(file); + rewind(file); // Go back to the beginning + + // Allocate memory for file content (+1 for null terminator) + char *buffer = malloc(filesize + 1); + if (buffer == NULL) { + perror("Memory allocation failed"); + fclose(file); + return NULL; + } + + // Read entire file into buffer + fread(buffer, 1, filesize, file); + buffer[filesize] = '\0'; // Null-terminate the string + + fclose(file); + return buffer; // Caller must free the memory +} + +void updateState() { + cpuStatsTexture = renderVals(&cpu, &smallFont, &smallerFont, renderer); + cpuStateTexture = renderState(&cpu, &biggerFont, renderer); + char *dump = hexdump_to_string(cpu.memory, sizeof(cpu.memory)); + fill_editor_from_string(&memoryViewer, dump, renderer); + free(dump); +} + +void compile(bool erase) { + generate_string(&codeEditor); + completePass(codeEditor.outputString, &cpu, erase); + updateState(); +} -char programString[65535]; int init() { - - //Initialize SDL if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); @@ -42,7 +100,6 @@ int init() { return 1; } - //Create window window = SDL_CreateWindow("SDLko", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); @@ -51,39 +108,62 @@ int init() { return 1; } //Get window surface - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE); if (renderer == NULL) { printf("Renderer could not be created SDL_Error: %s\n", SDL_GetError()); return 1; } + + biggerFont = prepText(renderer, 16, "../PublicPixel.ttf", 255, 255, 255, 255); smallFont = prepText(renderer, 12, "../PublicPixel.ttf", 255, 255, 255, 255); - init_editor(&codeEditor, &smallFont, 50, 50, renderer); - activeEditor = &codeEditor; + smallerFont = prepText(renderer, 8, "../PublicPixel.ttf", 255, 255, 255, 255); + init_editor(&codeEditor, &smallFont, 10, 80, renderer, 34, 1000, 48, false); + init_editor(&memoryViewer, &smallerFont, 550, 80, renderer, 80, MEM_SIZE / 16 + 2, 70, true); SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0); - generate_string_display(&codeEditor, renderer); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL); + for (int i = 0; i < editorCount; i++) { + generate_string_display(&codeEditor, renderer); + } init_cpu(&cpu); + compile(true); return 0; } -SDL_Rect rect1; -SDL_Rect rect2; - int render() { - SDL_SetRenderDrawColor(renderer, 128, 0, 0, 255); + SDL_SetRenderDrawColor(renderer, 32, 32, 32, 255); SDL_RenderClear(renderer); - SDL_SetRenderDrawColor(renderer, 0, 128, 0, 255); - rect1.x = (rect1.x + 1) % 400; - rect1.y = 10; - rect1.w = 50; - rect1.h = 10; - SDL_RenderFillRect(renderer, &rect1); + for (int i = 0; i < editorCount; i++) { + editor_render(&editors[i], renderer, activeEditorIndex == i, cursor); + } - editor_render(&codeEditor, renderer); + SDL_Rect rect2; + rect2.x = 9; + rect2.y = 0; + rect2.w = 0; + rect2.h = 0; + + SDL_QueryTexture(cpuStatsTexture, NULL, NULL, &rect2.w, &rect2.h); + + + SDL_RenderCopy(renderer, cpuStatsTexture, NULL, &rect2); + + rect2.x = 100; + rect2.y = 46; + rect2.w = 0; + rect2.h = 0; + + SDL_QueryTexture(cpuStateTexture, NULL, NULL, &rect2.w, &rect2.h); + + + SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2); SDL_RenderPresent(renderer); + frames++; + if (!(frames % 60)) { + cursor = !cursor; + } return 0; } @@ -158,11 +238,9 @@ SDL_Keycode ConvertKPToNonKP(SDL_Keycode keycode) { case SDLK_KP_GREATER: return SDLK_GREATER; case SDLK_KP_AMPERSAND: - return SDLK_AMPERSAND; case SDLK_KP_DBLAMPERSAND: return SDLK_AMPERSAND; // No direct match, best alternative case SDLK_KP_VERTICALBAR: - return SDLK_BACKSLASH; // Vertical bar alternative case SDLK_KP_DBLVERTICALBAR: return SDLK_BACKSLASH; // No direct match case SDLK_KP_COLON: @@ -175,12 +253,6 @@ SDL_Keycode ConvertKPToNonKP(SDL_Keycode keycode) { return SDLK_AT; case SDLK_KP_EXCLAM: return SDLK_EXCLAIM; - case SDLK_KP_MEMSTORE: - return SDLK_UNKNOWN; // No direct match - case SDLK_KP_MEMRECALL: - return SDLK_UNKNOWN; - case SDLK_KP_MEMCLEAR: - return SDLK_UNKNOWN; case SDLK_KP_MEMADD: return SDLK_PLUS; case SDLK_KP_MEMSUBTRACT: @@ -189,18 +261,15 @@ SDL_Keycode ConvertKPToNonKP(SDL_Keycode keycode) { return SDLK_ASTERISK; case SDLK_KP_MEMDIVIDE: return SDLK_SLASH; + case SDLK_KP_MEMSTORE: + case SDLK_KP_MEMRECALL: + case SDLK_KP_MEMCLEAR: case SDLK_KP_PLUSMINUS: - return SDLK_UNKNOWN; case SDLK_KP_CLEAR: - return SDLK_UNKNOWN; case SDLK_KP_CLEARENTRY: - return SDLK_UNKNOWN; case SDLK_KP_BINARY: - return SDLK_UNKNOWN; case SDLK_KP_OCTAL: - return SDLK_UNKNOWN; case SDLK_KP_DECIMAL: - return SDLK_UNKNOWN; case SDLK_KP_HEXADECIMAL: return SDLK_UNKNOWN; default: @@ -208,6 +277,7 @@ SDL_Keycode ConvertKPToNonKP(SDL_Keycode keycode) { } } +uint8_t cpuSpeedTemp = 0; int processEvent(SDL_Event e) { if (e.type == SDL_QUIT) { return 0; } @@ -220,44 +290,144 @@ int processEvent(SDL_Event e) { SDL_RenderSetViewport(renderer, &viewport); } else if (e.type == SDL_KEYDOWN) { int keySym = ConvertKPToNonKP(e.key.keysym.sym); + int keyMod = e.key.keysym.mod; + cursor = true; switch (keySym) { case SDLK_UP: - if (activeEditor) { - move_cursor_relative(activeEditor, -1, 0); - } + move_cursor_relative(&activeEditor, -1, 0, false, renderer); + break; + case SDLK_PAGEUP: + move_cursor_relative(&activeEditor, -activeEditor.max_lines_display, -1, true, renderer); break; case SDLK_DOWN: - if (activeEditor) { - move_cursor_relative(activeEditor, 1, 0); - } + move_cursor_relative(&activeEditor, 1, 0, false, renderer); + break; + case SDLK_PAGEDOWN: + move_cursor_relative(&activeEditor, activeEditor.max_lines_display, 0, true, renderer); break; case SDLK_LEFT: - if (activeEditor) { - move_cursor_relative(activeEditor, 0, -1); + move_cursor_relative(&activeEditor, 0, -1, false, renderer); + break; + case SDLK_HOME: + if (keyMod & KMOD_CTRL) { + move_cursor(&activeEditor, 0, 0, false, renderer); + break; } + move_cursor(&activeEditor, activeEditor.cursor_line, 0, false, renderer); break; case SDLK_RIGHT: - if (activeEditor) { - move_cursor_relative(activeEditor, 0, 1); + move_cursor_relative(&activeEditor, 0, 1, false, renderer); + break; + case SDLK_END: + int lineLen = strlen(activeEditor.lines[activeEditor.cursor_line].text); + if (keyMod & KMOD_CTRL) { + move_cursor(&activeEditor, activeEditor.line_count, lineLen, false, renderer); + break; + } + move_cursor(&activeEditor, activeEditor.cursor_line, lineLen, false, renderer); + break; + case SDLK_F9: + cpu.mode ^= CPU_MODE_LOOP; + updateState(); + return 1; + case SDLK_F8: + if (++cpuSpeedTemp == 3) { + cpuSpeedTemp = 0; + } + cpu.mode &= ~(CPU_MODE_SECOND | CPU_MODE_STEP); + cpu.mode |= cpuSpeedTemp << 4; + updateState(); + return 1; + case SDLK_F5: + compile(!(keyMod & KMOD_CTRL)); + case SDLK_F7: + if (cpu.mode & (CPU_MODE_HALTED | CPU_MODE_ERROR) || (keyMod & KMOD_SHIFT)) { + cpu.pc = 0; + } + cpu.mode &= ~(CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR); + updateState(); + break; + + case SDLK_ESCAPE: + cpu.mode |= CPU_MODE_PAUSED; + if (keyMod & (KMOD_CTRL | KMOD_SHIFT)) { + cpu.mode |= CPU_MODE_HALTED; + } + updateState(); + break; + + case SDLK_s: + if (keyMod & KMOD_CTRL) { + FILE *fptr; + char fname[20]; + snprintf(fname, sizeof(fname), "riscb%lu.bsm", time(NULL)); + fptr = fopen(fname, "w"); + generate_string(&editors[0]); + fputs(editors[0].outputString, fptr); + fclose(fptr); + return 1; + } + break; + + case SDLK_l: + if (keyMod & KMOD_CTRL) { + FILE *fptr; + char fname[20]; + sscanf(editors[0].lines[0].text, "%s", fname); + toLowerCase(fname); + strcat(fname, ".bsm"); + fptr = fopen(fname, "r"); + char *prog = read_file_as_string(fname); + fill_editor_from_string(&editors[0], prog, renderer); + free(prog); + fputs(editors[0].outputString, fptr); + fclose(fptr); + return 1; + } + break; + case SDLK_BACKSPACE: + if (!activeEditor.readOnly) { + remove_character(&activeEditor, false, renderer); + } + break; + case SDLK_DELETE: + if (!activeEditor.readOnly) { + remove_character(&activeEditor, true, renderer); } break; case SDLK_RETURN: case SDLK_RETURN2: - if (activeEditor && !activeEditor->readOnly) { - insert_line_rel(activeEditor, renderer); + if (keyMod & KMOD_CTRL && activeEditorIndex == 0) { + compile(!(keyMod & KMOD_SHIFT)); + break; } + if (!activeEditor.readOnly) { + insert_line_rel(&activeEditor, renderer); + } + break; + case SDLK_TAB: + activeEditorIndex++; + if (activeEditorIndex >= editorCount) { + activeEditorIndex = 0; + } + activeEditor = editors[activeEditorIndex]; break; default: break; } - if (activeEditor && !activeEditor->readOnly && activeEditor->cursor_pos < MAX_LINE_WIDTH) { - if (keySym >= 32 && keySym <= 126) { - if (keySym > 0x60 && keySym < 0x7b) { - keySym -= 0x20; + } else if (e.type == SDL_TEXTINPUT) { + for (int i = 0; e.text.text[i] != '\0'; i++) { // Iterate over the input string + char keySym = e.text.text[i]; + + if (!activeEditor.readOnly && activeEditor.cursor_pos <= activeEditor.max_line_width) { + if (keySym >= 32 && keySym <= 126) { // Printable ASCII range + if (keySym > 0x60 && keySym < 0x7b) { // Convert lowercase to uppercase + keySym -= 0x20; + } + if (activeEditor.cursor_pos < activeEditor.max_line_width) { + insert_character(&activeEditor, keySym, renderer); + } } - insert_character(activeEditor, (keySym & 0xff), renderer); - } else if (keySym == SDLK_BACKSPACE || keySym == SDLK_DELETE) { - remove_character(activeEditor, renderer); } } } @@ -286,6 +456,22 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) return status; } + if (!(cpu.mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR))) { + if (cpu.mode & CPU_MODE_SECOND) { + if (frames % 60) { + step(&cpu); + updateState(); + } + } else { + step(&cpu); + updateState(); + } + + if (cpu.mode & CPU_MODE_STEP) { + cpu.mode |= CPU_MODE_PAUSED; + updateState(); + } + } end = SDL_GetTicks64(); const unsigned long timeNeeded = end - start; if (timeNeeded < delayNeeded) { @@ -295,12 +481,13 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) } } - uint8_t *program; - int program_size; + for (uint8_t i = 0; i < editorCount; i++) { + destroy_editor(&editors[i]); + } - completePass(programString, &cpu, true); - - step(&cpu); + for (uint8_t i = 0; i < fontCount; i++) { + destroyFont(&fonts[i]); + } //Destroy window SDL_DestroyWindow(window); diff --git a/util/cpustatusui.c b/util/cpustatusui.c new file mode 100644 index 0000000..cf25877 --- /dev/null +++ b/util/cpustatusui.c @@ -0,0 +1,168 @@ +// +// Created by bruno on 8.2.2025. +// + +#include +#include "cpustatusui.h" +#include "font.h" + +SDL_Texture *renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont, + SDL_Renderer *renderer) { + + CPUStatusPart *stats = NULL; + int statsCount = 0; + + + getStats(cpu, &stats, &statsCount); + + const int padding = 4; + const int oneFieldW = (titleFont->size + 1) * (sizeof(stats[0].value) - 1) + padding - 1; + const int oneFieldH = (titleFont->size + 1) * 2 + (valueFont->size + 1); + SDL_Texture *out = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + oneFieldW * statsCount, oneFieldH); + + SDL_SetRenderTarget(renderer, out); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + for (int i = 0; i < statsCount; i++) { + int x = i * oneFieldW; + SDL_Rect rect = {x, 0, oneFieldW, oneFieldH}; + SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255); + SDL_RenderFillRect(renderer, &rect); + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_RenderDrawRect(renderer, &rect); + + // Render the title + int textX = x + padding; + int textY = padding; + for (int j = 0; j < 6 && stats[i].name[j] != '\0'; j++) { + SDL_Texture *charTex = titleFont->texture[(uint8_t) stats[i].name[j]]; + if (charTex) { + SDL_Rect dstRect = {textX, textY, titleFont->size, titleFont->size}; + SDL_RenderCopy(renderer, charTex, NULL, &dstRect); + textX += titleFont->size; + } + } + + // Render the value + char valueStr[12]; + snprintf(valueStr, sizeof(valueStr), "%u", stats[i].value); + textX = x + padding; + textY += titleFont->size + padding; + for (int j = 0; valueStr[j] != '\0'; j++) { + SDL_Texture *charTex = valueFont->texture[(uint8_t) valueStr[j]]; + if (charTex) { + SDL_Rect dstRect = {textX, textY, valueFont->size, valueFont->size}; + SDL_RenderCopy(renderer, charTex, NULL, &dstRect); + textX += valueFont->size; + } + } + } + + SDL_SetRenderTarget(renderer, NULL); + return out; +} + +void getStats(CPU *cpu, CPUStatusPart **cpuStatus, int *cpuStatusCount) { + if (!cpu || !cpuStatus || !cpuStatusCount) return; + + int count = 5 + REG_COUNT; // PC, SP, FLAGS, MODE, CYCLE + registers + + // Free existing memory if allocated + if (*cpuStatus) { + free(*cpuStatus); + *cpuStatus = NULL; + } + + // Allocate the required memory + *cpuStatus = (CPUStatusPart *) malloc(count * sizeof(CPUStatusPart)); + if (!*cpuStatus) return; // Memory allocation failed + + int index = 0; + + strncpy((*cpuStatus)[index].name, "PC", sizeof((*cpuStatus)[index].name)); + (*cpuStatus)[index].value = cpu->pc; + index++; + + strncpy((*cpuStatus)[index].name, "SP", sizeof((*cpuStatus)[index].name)); + (*cpuStatus)[index].value = cpu->stack_ptr; + index++; + + strncpy((*cpuStatus)[index].name, "FLG", sizeof((*cpuStatus)[index].name)); + (*cpuStatus)[index].value = cpu->flags; + index++; + + strncpy((*cpuStatus)[index].name, "MOD", sizeof((*cpuStatus)[index].name)); + (*cpuStatus)[index].value = cpu->mode; + index++; + + strncpy((*cpuStatus)[index].name, "CYC", sizeof((*cpuStatus)[index].name)); + (*cpuStatus)[index].value = cpu->cycle; + index++; + + for (int i = 0; i < 25; i++) { + snprintf((*cpuStatus)[index].name, sizeof((*cpuStatus)[index].name), "R%d", i); + (*cpuStatus)[index].value = cpu->regs[i]; + index++; + } + + *cpuStatusCount = index; // Store the actual number of status parts +} + +SDL_Texture *renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer) { + + // Render the value + char valueStr[20] = ""; + if (cpu->mode & CPU_MODE_ERROR) { + strcat(valueStr, "ERR "); + } else if (cpu->mode & CPU_MODE_HALTED) { + strcat(valueStr, "HLT "); + } else if (cpu->mode & CPU_MODE_PAUSED) { + strcat(valueStr, "PAUS "); + } else { + strcat(valueStr, "RUN "); + } + + if (cpu->mode & CPU_MODE_LOOP) { + strcat(valueStr, "LOOP "); + } else { + strcat(valueStr, "ONCE "); + } + + if (cpu->mode & CPU_MODE_STEP) { + strcat(valueStr, "STP"); + } else if (cpu->mode & CPU_MODE_SECOND) { + strcat(valueStr, "SEC"); + } else { + strcat(valueStr, "BRR"); + } + + const int oneFieldW = (titleFont->size + 1); + const int allFieldW = oneFieldW * strlen(valueStr); + const int oneFieldH = (titleFont->size + 1); + SDL_Texture *out = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + allFieldW, oneFieldH); + + SDL_SetRenderTarget(renderer, out); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + SDL_Rect rect = {0, 0, allFieldW, oneFieldH}; + SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255); + SDL_RenderFillRect(renderer, &rect); + + + int x = 0; + for (int j = 0; valueStr[j] != '\0'; j++) { + SDL_Texture *charTex = titleFont->texture[(uint8_t) valueStr[j]]; + if (charTex) { + SDL_Rect dstRect = {x, 0, titleFont->size, titleFont->size}; + x += titleFont->size + 1; + SDL_RenderCopy(renderer, charTex, NULL, &dstRect); + } + } + + SDL_SetRenderTarget(renderer, NULL); + return out; +} \ No newline at end of file diff --git a/util/cpustatusui.h b/util/cpustatusui.h new file mode 100644 index 0000000..fc4502e --- /dev/null +++ b/util/cpustatusui.h @@ -0,0 +1,23 @@ +// +// Created by bruno on 8.2.2025. +// + +#ifndef RISCB_CPUSTATUSUI_H +#define RISCB_CPUSTATUSUI_H + +#include "font.h" +#include "../cpu/core.h" + +typedef struct { + char name[4]; + uint32_t value; +} CPUStatusPart; + +SDL_Texture *renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont, + SDL_Renderer *renderer); + +SDL_Texture *renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer); + +void getStats(CPU *cpu, CPUStatusPart **cpuStatus, int *cpuStatusCount); + +#endif //RISCB_CPUSTATUSUI_H diff --git a/util/font.c b/util/font.c index 6903927..9b52efa 100644 --- a/util/font.c +++ b/util/font.c @@ -10,13 +10,13 @@ prepText(SDL_Renderer *renderer, unsigned char pxSize, const char *file, uint8_t BitmapFont out; out.size = pxSize; out.color = (SDL_Color) {r, g, b, a}; - unsigned char i = 0; + unsigned int i = 1; do { char tmpOut[2] = {i, 0}; out.surface[i] = TTF_RenderText_Solid(gFont, tmpOut, out.color); out.texture[i] = SDL_CreateTextureFromSurface(renderer, out.surface[i]); i++; - } while (i < 255); + } while (i < 256); TTF_CloseFont(gFont); return out; @@ -43,4 +43,11 @@ void renderText(SDL_Renderer *renderer, BitmapFont font, char *string, uint16_t outRect.x += charRect.w + 1; string++; } +} + +void destroyFont(BitmapFont *font) { + for (uint16_t i = 1; i < 256; i++) { + SDL_DestroyTexture(font->texture[i]); + SDL_FreeSurface(font->surface[i]); + } } \ No newline at end of file diff --git a/util/font.h b/util/font.h index 2dcb38d..98272a2 100644 --- a/util/font.h +++ b/util/font.h @@ -19,6 +19,8 @@ typedef struct { BitmapFont prepText(SDL_Renderer *renderer, unsigned char pxSize, const char *file, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +void destroyFont(BitmapFont *font); + void renderText(SDL_Renderer *renderer, BitmapFont font, char *string, uint16_t x, uint16_t y); #endif //RISCB_FONT_H diff --git a/util/hexdump.c b/util/hexdump.c new file mode 100644 index 0000000..223d32f --- /dev/null +++ b/util/hexdump.c @@ -0,0 +1,50 @@ +// +// Created by bruno on 6.2.2025. +// + +#include "hexdump.h" +#include +#include +#include + +#define BYTES_PER_LINE 16 // Adjust for different widths + + +char *hexdump_to_string(const unsigned char *data, size_t size) { + // Estimate max output size: each line is approx. 80 chars + size_t estimated_size = (size / BYTES_PER_LINE + 1) * 80; + + // Allocate memory for output string + char *output = malloc(estimated_size); + if (!output) return NULL; + + size_t offset = 0; // Track the write position + + for (size_t i = 0; i < size; i += BYTES_PER_LINE) { + offset += snprintf(output + offset, estimated_size - offset, "%08zx ", i); + + // Print hex values + for (size_t j = 0; j < BYTES_PER_LINE; j++) { + if (i + j < size) + offset += snprintf(output + offset, estimated_size - offset, "%02x ", data[i + j]); + else + offset += snprintf(output + offset, estimated_size - offset, " "); // Padding + if (j == 7) offset += snprintf(output + offset, estimated_size - offset, " "); // Extra space + } + + offset += snprintf(output + offset, estimated_size - offset, " |"); + + // Print ASCII representation + for (size_t j = 0; j < BYTES_PER_LINE; j++) { + if (i + j < size) + offset += snprintf(output + offset, estimated_size - offset, "%c", + isprint(data[i + j]) ? data[i + j] : '.'); + else + offset += snprintf(output + offset, estimated_size - offset, " "); + } + + offset += snprintf(output + offset, estimated_size - offset, "|\n"); + } + + return output; +} diff --git a/util/hexdump.h b/util/hexdump.h new file mode 100644 index 0000000..9a948b8 --- /dev/null +++ b/util/hexdump.h @@ -0,0 +1,12 @@ +// +// Created by bruno on 6.2.2025. +// + +#ifndef RISCB_HEXDUMP_H +#define RISCB_HEXDUMP_H + +#include + +char *hexdump_to_string(const unsigned char *data, size_t size); + +#endif //RISCB_HEXDUMP_H diff --git a/util/texteditor.c b/util/texteditor.c index e2b3802..89efcc1 100644 --- a/util/texteditor.c +++ b/util/texteditor.c @@ -1,62 +1,120 @@ -/// +// // Created by bruno on 5.2.2025. -/// +// Modified to use dynamic limits. +// #include "texteditor.h" #include "font.h" -// Initialize the text editor -void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Renderer *renderer) { - for (int i = 0; i < MAX_LINES_ASM; i++) { - editor->lines[i].text[0] = '\0'; // Empty string - editor->lines[i].active = 0; - } - editor->lines[0].active = 1; - editor->line_count = 1; +// Initialize the text editor with dynamic sizes. +void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Renderer *renderer, + int max_line_width, int max_lines_asm, int max_lines_display, bool readOnly) { + editor->max_line_width = max_line_width; + editor->max_lines_asm = max_lines_asm; + editor->max_lines_display = max_lines_display; + editor->line_count = 0; editor->cursor_line = 0; editor->cursor_line_offset = 0; editor->cursor_pos = 0; - editor->readOnly = 0; - editor->rect.x = 2; - editor->rect.y = 2; - editor->rect.w = MAX_LINE_WIDTH * (font->size + 1); - editor->rect.h = MAX_LINES_DISPLAY * (font->size + 4); - editor->outRect = editor->rect; - editor->outRect.x = x; - editor->outRect.y = y; + editor->readOnly = readOnly; editor->font = font; - editor->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, editor->rect.w, - editor->rect.h); + + editor->outRect = malloc(sizeof(SDL_Rect)); + editor->cursorRect = malloc(sizeof(SDL_Rect)); + editor->rect = malloc(sizeof(SDL_Rect)); + + memset(editor->outRect, 0, sizeof(SDL_Rect)); + memset(editor->cursorRect, 0, sizeof(SDL_Rect)); + memset(editor->rect, 0, sizeof(SDL_Rect)); + + + // Allocate dynamic array for lines. + editor->lines = (Line *) malloc(sizeof(Line) * editor->max_lines_asm); + if (!editor->lines) { + fprintf(stderr, "Failed to allocate memory for lines.\n"); + exit(EXIT_FAILURE); + } + // For each line, allocate memory for the text (including space for '\0') + for (int i = 0; i < editor->max_lines_asm; i++) { + editor->lines[i].text = (char *) malloc(sizeof(char) * (editor->max_line_width + 1)); + if (!editor->lines[i].text) { + fprintf(stderr, "Failed to allocate memory for line %d.\n", i); + exit(EXIT_FAILURE); + } + editor->lines[i].text[0] = '\0'; + editor->lines[i].active = 0; + } + + // Allocate output and display strings. + editor->outputString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->max_lines_asm + 1)); + editor->displayString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->max_lines_display + 1)); + if (!editor->outputString || !editor->displayString) { + fprintf(stderr, "Failed to allocate memory for output/display strings.\n"); + exit(EXIT_FAILURE); + } + editor->outputString[0] = '\0'; + editor->displayString[0] = '\0'; + + // Initialize with two active lines (like the original code). + editor->lines[0].active = 1; + editor->lines[1].active = 1; + editor->line_count = 2; + + // Set up the editor rectangle based on font size and dynamic max_line_width and max_lines_display. + editor->rect->x = 2; + editor->rect->y = 2; + editor->rect->w = editor->max_line_width * (font->size + 1) + ((font->size + 1) / 2); + editor->rect->h = editor->max_lines_display * (font->size + 1) + 2; + editor->outRect->w = editor->rect->w; + editor->outRect->h = editor->rect->h; + editor->outRect->x = x; + editor->outRect->y = y; + + editor->cursorRect->x = 3 + editor->cursor_pos * font->size + editor->outRect->x; + editor->cursorRect->y = + 2 + (editor->cursor_line - editor->cursor_line_offset) * (font->size + 1) + editor->outRect->y; + + editor->cursorRect->w = 2; + editor->cursorRect->h = editor->font->size; + + // Create texture for rendering. + editor->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, + editor->rect->w, editor->rect->h); + if (!editor->texture) { + fprintf(stderr, "Failed to create texture: %s\n", SDL_GetError()); + exit(EXIT_FAILURE); + } } -// Insert a new line at a specific position +// Insert a new line at a specific position. void insert_line(TextEditor *editor, int position, const char *text, SDL_Renderer *renderer) { - if (editor->line_count >= MAX_LINES_ASM || position < 0 || position > editor->line_count) { + if (editor->line_count >= editor->max_lines_asm || position < 0 || position > editor->line_count) { printf("Invalid position or max lines reached!\n"); return; } - // Shift lines down if necessary + // Shift lines down. for (int i = editor->line_count; i > position; i--) { strcpy(editor->lines[i].text, editor->lines[i - 1].text); editor->lines[i].active = editor->lines[i - 1].active; } - strncpy(editor->lines[position].text, text, MAX_LINE_WIDTH); - editor->lines[position].text[MAX_LINE_WIDTH] = '\0'; // Ensure null termination + // Copy the text into the new line, ensuring it does not exceed max_line_width. + strncpy(editor->lines[position].text, text, editor->max_line_width); + editor->lines[position].text[editor->max_line_width] = '\0'; editor->lines[position].active = 1; editor->line_count++; + move_cursor(editor, editor->cursor_line + 1, 0, false, renderer); generate_string_display(editor, renderer); } void insert_line_rel(TextEditor *editor, SDL_Renderer *renderer) { + insert_line(editor, editor->cursor_line + (editor->cursor_pos ? 1 : 0), "", renderer); editor->cursor_pos = 0; - insert_line(editor, editor->cursor_line + 1, "", renderer); - editor->cursor_line++; } -// Insert a character at the current cursor position void insert_character(TextEditor *editor, char ch, SDL_Renderer *renderer) { if (editor->cursor_line < 0 || editor->cursor_line >= editor->line_count) { printf("Invalid cursor position!\n"); @@ -66,12 +124,12 @@ void insert_character(TextEditor *editor, char ch, SDL_Renderer *renderer) { Line *line = &editor->lines[editor->cursor_line]; int len = strlen(line->text); - if (len >= MAX_LINE_WIDTH || editor->cursor_pos > len) { + if (len >= editor->max_line_width || editor->cursor_pos > len) { printf("Position out of bounds or line is full!\n"); return; } - // Shift characters to the right + // Shift characters to the right. for (int i = len; i >= editor->cursor_pos; i--) { line->text[i + 1] = line->text[i]; } @@ -81,139 +139,220 @@ void insert_character(TextEditor *editor, char ch, SDL_Renderer *renderer) { generate_string_display(editor, renderer); } -void remove_character(TextEditor *editor, SDL_Renderer *renderer) { +void remove_character(TextEditor *editor, bool isDelete, SDL_Renderer *renderer) { if (editor->cursor_line < 0 || editor->cursor_line >= editor->line_count) { printf("Invalid cursor position!\n"); return; } - if (editor->cursor_pos == 0 && editor->line_count > 1) { - // Remove the current line and shift lines up - for (int i = editor->cursor_line; i < editor->line_count - 1; i++) { - strcpy(editor->lines[i].text, editor->lines[i + 1].text); - editor->lines[i].active = editor->lines[i + 1].active; + Line *line = &editor->lines[editor->cursor_line]; + int len = strlen(line->text); + + if (isDelete) { + // Delete character after cursor + if (editor->cursor_pos < len) { + for (int i = editor->cursor_pos; i < len; i++) { + line->text[i] = line->text[i + 1]; + } } - editor->lines[editor->line_count - 1].text[0] = '\0'; - editor->lines[editor->line_count - 1].active = 0; - editor->line_count--; - if (editor->cursor_line >= editor->line_count) { - editor->cursor_line = editor->line_count - 1; - } - editor->cursor_pos = strlen(editor->lines[editor->cursor_line].text); } else { - Line *line = &editor->lines[editor->cursor_line]; - int len = strlen(line->text); - - if (editor->cursor_pos <= 0 || editor->cursor_pos > len) { - printf("Position out of bounds!\n"); - return; + // Backspace behavior (delete character before cursor) + if (editor->cursor_pos == 0 && editor->line_count > 1) { + // Merge with the previous line + for (int i = editor->cursor_line; i < editor->line_count - 1; i++) { + strcpy(editor->lines[i].text, editor->lines[i + 1].text); + editor->lines[i].active = editor->lines[i + 1].active; + } + editor->lines[editor->line_count - 1].text[0] = '\0'; + editor->lines[editor->line_count - 1].active = 0; + editor->line_count--; + if (editor->cursor_line >= editor->line_count) { + editor->cursor_line = editor->line_count - 1; + } + editor->cursor_pos = strlen(editor->lines[editor->cursor_line].text); + } else if (editor->cursor_pos > 0) { + for (int i = editor->cursor_pos - 1; i < len; i++) { + line->text[i] = line->text[i + 1]; + } + editor->cursor_pos--; } - - // Shift characters to the left to remove the character - for (int i = editor->cursor_pos - 1; i < len; i++) { - line->text[i] = line->text[i + 1]; - } - - editor->cursor_pos--; } generate_string_display(editor, renderer); } -// Move cursor -void move_cursor_relative(TextEditor *editor, int line_offset, int pos_offset) { +void move_cursor_relative(TextEditor *editor, int line_offset, int pos_offset, bool keepPos, SDL_Renderer *renderer) { int new_line = editor->cursor_line + line_offset; int new_pos = editor->cursor_pos + pos_offset; - if (new_line < 0) new_line = 0; - if (new_line >= editor->line_count) new_line = editor->line_count - 1; - - move_cursor(editor, new_line, new_pos); + move_cursor(editor, new_line, new_pos, keepPos, renderer); } -// Move cursor -void move_cursor(TextEditor *editor, int new_line, int new_pos) { +void move_cursor(TextEditor *editor, int new_line, int new_pos, bool keepPos, SDL_Renderer *renderer) { if (new_line < 0) new_line = 0; if (new_line >= editor->line_count) new_line = editor->line_count - 1; + if (keepPos) { + editor->cursor_line_offset = new_line; + } + + if (new_line < editor->cursor_line_offset) { + editor->cursor_line_offset = new_line; + } + if (new_line >= editor->cursor_line_offset + editor->max_lines_display) { + editor->cursor_line_offset = new_line - editor->max_lines_display + 1; + } + int line_length = strlen(editor->lines[new_line].text); if (new_pos < 0) new_pos = 0; if (new_pos > line_length) new_pos = line_length; editor->cursor_line = new_line; editor->cursor_pos = new_pos; + generate_string_display(editor, renderer); } -// Generate a string from a given offset with a max line count void generate_string_display(TextEditor *editor, SDL_Renderer *renderer) { if (editor->cursor_line_offset < 0 || editor->cursor_line_offset >= editor->line_count) { printf("Invalid start line!\n"); return; } - int end_line = editor->cursor_line_offset + MAX_LINES_DISPLAY; - if (end_line > editor->line_count) end_line = editor->line_count; + int end_line = editor->cursor_line_offset + editor->max_lines_display; + if (end_line > editor->line_count) + end_line = editor->line_count; - - memset(editor->displayString, 0, sizeof(editor->displayString)); + // Clear the display string. + editor->displayString[0] = '\0'; SDL_SetRenderTarget(renderer, editor->texture); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); + SDL_Rect charDstRect; + charDstRect.x = 4; + charDstRect.y = 3; + charDstRect.w = editor->font->size; + charDstRect.h = editor->font->size; + SDL_Rect charRect; charRect.x = 0; charRect.y = 0; charRect.w = editor->font->size; charRect.h = editor->font->size; - SDL_Rect outRect = charRect; - outRect.x = 3; - outRect.y = 2; - SDL_Rect cursorRect; - cursorRect.x = 0; - cursorRect.y = 0; - cursorRect.w = 1; - cursorRect.h = editor->font->size; + editor->cursorRect->x = 3 + editor->cursor_pos * (charDstRect.w + 1) + editor->outRect->x; + editor->cursorRect->y = + 2 + (editor->cursor_line - editor->cursor_line_offset) * (charDstRect.h + 1) + editor->outRect->y; for (int line = editor->cursor_line_offset; line < end_line; line++) { if (editor->lines[line].active) { strcat(editor->displayString, editor->lines[line].text); char *linePTR = editor->lines[line].text; - int charIndex = 0; while (*linePTR) { - SDL_RenderCopy(renderer, editor->font->texture[*linePTR], &charRect, &outRect); - outRect.x += charRect.w + 1; - if (line == editor->cursor_line && charIndex == editor->cursor_pos - 1) { - cursorRect.x = outRect.x; - cursorRect.y = outRect.y; - } - charIndex++; + SDL_RenderCopy(renderer, editor->font->texture[(unsigned char) *linePTR], &charRect, &charDstRect); + charDstRect.x += charDstRect.w + 1; linePTR++; } strcat(editor->displayString, "\n"); - outRect.x = 3; - outRect.y += charRect.h + 1; + charDstRect.x = 4; + charDstRect.y += charDstRect.h + 1; } } - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderFillRect(renderer, &cursorRect); SDL_SetRenderTarget(renderer, NULL); } -void editor_render(TextEditor *editor, SDL_Renderer *renderer) { - SDL_RenderCopy(renderer, editor->texture, &editor->rect, &editor->outRect); +void editor_render(TextEditor *editor, SDL_Renderer *renderer, bool isActive, bool cursorBlink) { + if (isActive) { + SDL_Rect bgRect; + bgRect = *editor->outRect; + bgRect.x -= 6; + bgRect.y -= 6; + bgRect.w += 12; + bgRect.h += 12; + SDL_SetRenderDrawColor(renderer, editor->readOnly ? 128 : 0, editor->readOnly ? 0 : 128, 64, 255); + SDL_RenderFillRect(renderer, &bgRect); + } + SDL_RenderCopy(renderer, editor->texture, editor->rect, editor->outRect); + if (isActive && cursorBlink) { + SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255); + SDL_RenderFillRect(renderer, editor->cursorRect); + } } -// Generate a string for assembling void generate_string(TextEditor *editor) { - memset(editor->outputString, 0, sizeof(editor->outputString)); + editor->outputString[0] = '\0'; - for (int i = 0; i < MAX_LINES_ASM; i++) { + for (int i = 0; i < editor->max_lines_asm; i++) { if (editor->lines[i].active) { - strcat(editor->outputString, editor->lines[i].text); - strcat(editor->outputString, "\n"); + if (strlen(editor->lines[i].text)) { + strcat(editor->outputString, editor->lines[i].text); + strcat(editor->outputString, "\n"); + } } } -} \ No newline at end of file +} + +void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Renderer *renderer) { + if (!editor || !content) { + printf("Invalid editor or content pointer!\n"); + return; + } + + // Clear the current editor content + for (int i = 0; i < editor->max_lines_asm; i++) { + editor->lines[i].text[0] = '\0'; + editor->lines[i].active = 0; + } + editor->line_count = 0; + editor->cursor_line = 0; + editor->cursor_pos = 0; + + // Parse the content and fill the editor lines + const char *ptr = content; + int line_index = 0; + + while (*ptr && line_index < editor->max_lines_asm) { + int char_count = 0; + + // Ensure the text buffer does not overflow + while (*ptr && *ptr != '\n' && char_count < editor->max_line_width - 1) { + editor->lines[line_index].text[char_count++] = *ptr++; + } + + editor->lines[line_index].text[char_count] = '\0'; // Null-terminate + editor->lines[line_index].active = 1; + line_index++; + + // Move past the newline character if present + if (*ptr == '\n') ptr++; + } + + // Update the total number of lines in use + editor->line_count = line_index; + + // Generate the visual representation + generate_string_display(editor, renderer); +} + + +// Free all dynamically allocated memory. +void destroy_editor(TextEditor *editor) { + if (editor->lines) { + for (int i = 0; i < editor->max_lines_asm; i++) { + free(editor->lines[i].text); + } + free(editor->lines); + } + free(editor->outputString); + free(editor->displayString); + + free(editor->outRect); + free(editor->rect); + free(editor->cursorRect); + + if (editor->texture) { + SDL_DestroyTexture(editor->texture); + } +} diff --git a/util/texteditor.h b/util/texteditor.h index dfaf861..22d21c7 100644 --- a/util/texteditor.h +++ b/util/texteditor.h @@ -1,67 +1,72 @@ -/// +// // Created by bruno on 5.2.2025. -/// - +// Modified to use dynamic limits. +// #ifndef RISCB_TEXTEDITOR_H #define RISCB_TEXTEDITOR_H -#define MAX_LINE_WIDTH 34 // Max chars per line (excluding '\0') -#define MAX_LINES_ASM 100 // Max number of lines -#define MAX_LINES_DISPLAY 10 // Max number of lines - #include #include #include +#include #include #include #include #include "font.h" typedef struct { - char text[MAX_LINE_WIDTH + 1]; // +1 for null terminator - int active; // Flag to check if the line is in use + char *text; // Dynamically allocated string for this line + int active; // Flag to check if the line is in use } Line; typedef struct { - Line lines[MAX_LINES_ASM]; // Array of lines - int line_count; // Number of active lines - int cursor_line; // Current cursor line - int cursor_line_offset; // Current cursor line - int cursor_pos; // Current cursor position in line - char outputString[MAX_LINE_WIDTH * MAX_LINES_ASM]; - char displayString[MAX_LINE_WIDTH * MAX_LINES_DISPLAY]; - SDL_Rect rect; - SDL_Rect outRect; + Line *lines; // Dynamic array of lines + int line_count; // Number of active lines + int max_lines_asm; // Maximum number of lines (e.g. assembly lines) + int max_line_width; // Maximum characters per line (excluding '\0') + int max_lines_display; // Maximum number of lines for display + + int cursor_line; // Current cursor line + int cursor_line_offset; // Display offset (first line in the display) + int cursor_pos; // Current cursor position in line + + char *outputString; // Dynamically allocated output string (size: max_line_width * max_lines_asm + 1) + char *displayString; // Dynamically allocated display string (size: max_line_width * max_lines_display + 1) + SDL_Rect *rect; + SDL_Rect *outRect; SDL_Texture *texture; bool readOnly; BitmapFont *font; + SDL_Rect *cursorRect; } TextEditor; -// Initialize the text editor -void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Renderer *renderer); +// Initialize the text editor. The parameters max_line_width, max_lines_asm, and max_lines_display +// determine the dynamic sizes for the text editor. +void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Renderer *renderer, + int max_line_width, int max_lines_asm, int max_lines_display, bool readOnly); -// Insert a new line at a specific position +// Other function prototypes remain mostly unchanged but will use the dynamic limits: void insert_line(TextEditor *editor, int position, const char *text, SDL_Renderer *renderer); -// Insert a new line at a specific position void insert_line_rel(TextEditor *editor, SDL_Renderer *renderer); -void editor_render(TextEditor *editor, SDL_Renderer * renderer); +void editor_render(TextEditor *editor, SDL_Renderer *renderer, bool isActive, bool cursorBlink); -void remove_character(TextEditor *editor, SDL_Renderer *renderer); +void remove_character(TextEditor *editor, bool isDelete, SDL_Renderer *renderer); -// Insert a character at the current cursor position void insert_character(TextEditor *editor, char ch, SDL_Renderer *renderer); -// Move cursor (handled externally) -void move_cursor(TextEditor *editor, int new_line, int new_pos); +void move_cursor(TextEditor *editor, int new_line, int new_pos, bool keepPos, SDL_Renderer *renderer); -// Move cursor (handled externally) -void move_cursor_relative(TextEditor *editor, int line_offset, int pos_offset); +void move_cursor_relative(TextEditor *editor, int line_offset, int pos_offset, bool keepPos, SDL_Renderer *renderer); -// Generate a string from a given offset with a max line count void generate_string_display(TextEditor *editor, SDL_Renderer *renderer); void generate_string(TextEditor *editor); -#endif //RISCB_TEXTEDITOR_H \ No newline at end of file +void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Renderer *renderer); + +// A cleanup function to free dynamically allocated memory. +void destroy_editor(TextEditor *editor); + +#endif // RISCB_TEXTEDITOR_H