Add highlighting, fixed len instructions and more
This commit is contained in:
@@ -290,7 +290,7 @@ const char *readLine(const char *source, char *buffer, size_t maxLen) {
|
||||
// The address is simply the offset into the output machine code buffer.
|
||||
// For this example, every instruction is assumed to have a fixed length (opcode plus operand bytes).
|
||||
//
|
||||
int firstPass(const char *source) {
|
||||
void firstPass(const char *source) {
|
||||
char line[MAX_LINE_LENGTH];
|
||||
int addr = 0;
|
||||
labelCount = 0;
|
||||
@@ -316,152 +316,170 @@ int firstPass(const char *source) {
|
||||
continue;
|
||||
strcpy(line, rest);
|
||||
}
|
||||
|
||||
// Parse the mnemonic and operands.
|
||||
char mnemonic[32], operand1[64], operand2[64], operand3[64];
|
||||
operand1[0] = '\0';
|
||||
operand2[0] = '\0';
|
||||
int tokenCount = sscanf(line, "%31s %63[^, ] %63[^, ] %63s",
|
||||
mnemonic, operand1, operand2, operand3);
|
||||
|
||||
|
||||
// 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:
|
||||
case HLT:
|
||||
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;
|
||||
|
||||
// // Parse the mnemonic and operands.
|
||||
// char mnemonic[32], operand1[64], operand2[64], operand3[64];
|
||||
// operand1[0] = '\0';
|
||||
// operand2[0] = '\0';
|
||||
// int tokenCount = sscanf(line, "%31s %63[^ ] %63[^ ] %63s",
|
||||
// mnemonic, operand1, operand2, operand3);
|
||||
//
|
||||
//
|
||||
// // Use the mapper to get a base opcode.
|
||||
// int baseOpcode = getOpcode(mnemonic);
|
||||
// if (baseOpcode == -1) {
|
||||
// printf("Unknown instruction: %s\n", mnemonic);
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// int size = CPU_INSTRUCTION_SIZE; // 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:
|
||||
// case HLT:
|
||||
// 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;
|
||||
}
|
||||
|
||||
//
|
||||
// The second pass actually translates the assembly instructions to machine code.
|
||||
// The machine code is written into the provided buffer. (It must be large enough.)
|
||||
//
|
||||
int secondPass(const char *source, uint8_t *code) {
|
||||
int completePass(const char *source, CPU *cpu, bool erase) {
|
||||
if (erase) {
|
||||
memset(cpu->memory, 0, sizeof(cpu->memory));
|
||||
memset(cpu->regs, 0, sizeof(cpu->regs));
|
||||
memset(cpu->stack, 0, sizeof(cpu->stack));
|
||||
memset(cpu->addrToLineMapper, 0, sizeof(cpu->addrToLineMapper));
|
||||
cpu->pc = 0;
|
||||
cpu->stack_ptr = 0;
|
||||
cpu->flags = 0;
|
||||
cpu->cycle = 0;
|
||||
}
|
||||
// First pass: determine label addresses.
|
||||
firstPass(source);
|
||||
|
||||
char line[MAX_LINE_LENGTH];
|
||||
int addr = 0;
|
||||
uint32_t addr = 0;
|
||||
const char *ptr = source;
|
||||
|
||||
uint32_t lineIndex = 0;
|
||||
|
||||
while (*ptr) {
|
||||
ptr = readLine(ptr, line, sizeof(line));
|
||||
trim(line);
|
||||
if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
|
||||
if (line[0] == '\0' || line[0] == ';' || line[0] == '#') {
|
||||
lineIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove any label definitions (up to the colon).
|
||||
char *colon = strchr(line, ':');
|
||||
if (colon != NULL) {
|
||||
*colon = ' '; // Replace the colon so the rest of the line can be parsed.
|
||||
lineIndex++;
|
||||
continue;
|
||||
}
|
||||
if (strlen(line) == 0)
|
||||
@@ -470,11 +488,12 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
// 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",
|
||||
int tokenCount = sscanf(line, "%31s %63[^ ] %63[^ ] %63s",
|
||||
mnemonic, operand1, operand2, operand3);
|
||||
|
||||
// (Optionally, you might trim each operand individually here.)
|
||||
|
||||
uint32_t oldAddr = addr;
|
||||
// Map the mnemonic to a base opcode.
|
||||
int baseOpcode = getOpcode(mnemonic);
|
||||
if (baseOpcode == -1) {
|
||||
@@ -489,17 +508,17 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
return 1;
|
||||
}
|
||||
int resolvedOpcode = resolveMOV(operand1, operand2);
|
||||
code[addr++] = resolvedOpcode;
|
||||
cpu->memory[addr++] = resolvedOpcode;
|
||||
if (resolvedOpcode == MOV_RN_IMM) {
|
||||
int reg = parseRegister(operand1);
|
||||
uint8_t imm = parseImmediate(operand2);
|
||||
code[addr++] = reg;
|
||||
code[addr++] = imm;
|
||||
cpu->memory[addr++] = reg;
|
||||
cpu->memory[addr++] = imm;
|
||||
} else if (resolvedOpcode == MOV_RN_RM) {
|
||||
int regDest = parseRegister(operand1);
|
||||
int regSrc = parseRegister(operand2);
|
||||
code[addr++] = regDest;
|
||||
code[addr++] = regSrc;
|
||||
cpu->memory[addr++] = regDest;
|
||||
cpu->memory[addr++] = regSrc;
|
||||
} else if (resolvedOpcode == MOV_RN_ADDR) {
|
||||
int reg = parseRegister(operand1);
|
||||
// Assume source is written as "[address]": remove the brackets.
|
||||
@@ -507,11 +526,10 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
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;
|
||||
code[addr++] = (memAddr >> 16) & 0xFF;
|
||||
code[addr++] = (memAddr >> 8) & 0xFF;
|
||||
code[addr++] = memAddr & 0xFF;
|
||||
cpu->memory[addr++] = reg;
|
||||
cpu->memory[addr++] = memAddr & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
||||
} else if (resolvedOpcode == MOV_ADDR_RN) {
|
||||
// Destination is memory (written as "[address]").
|
||||
char addrStr[32];
|
||||
@@ -519,11 +537,10 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
addrStr[strlen(operand1) - 2] = '\0';
|
||||
uint32_t memAddr = (uint32_t) strtoul(addrStr, NULL, 0);
|
||||
int reg = parseRegister(operand2);
|
||||
code[addr++] = (memAddr >> 24) & 0xFF;
|
||||
code[addr++] = (memAddr >> 16) & 0xFF;
|
||||
code[addr++] = (memAddr >> 8) & 0xFF;
|
||||
code[addr++] = memAddr & 0xFF;
|
||||
code[addr++] = reg;
|
||||
cpu->memory[addr++] = memAddr & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
||||
cpu->memory[addr++] = reg;
|
||||
}
|
||||
}
|
||||
// --- INC and DEC (baseOpcode == -12 or -13) ---
|
||||
@@ -534,20 +551,19 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
return 1;
|
||||
}
|
||||
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||
code[addr++] = resolvedOpcode;
|
||||
cpu->memory[addr++] = resolvedOpcode;
|
||||
if (operand1[0] == 'R' || operand1[0] == 'r') {
|
||||
int reg = parseRegister(operand1);
|
||||
code[addr++] = reg;
|
||||
cpu->memory[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;
|
||||
cpu->memory[addr++] = memAddr & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
||||
}
|
||||
}
|
||||
// --- Other Ambiguous ALU Instructions (ADD, SUB, MUL, etc.) ---
|
||||
@@ -559,15 +575,15 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
return 1;
|
||||
}
|
||||
int resolvedOpcode = resolveALU(baseOpcode, operand2);
|
||||
code[addr++] = resolvedOpcode;
|
||||
cpu->memory[addr++] = resolvedOpcode;
|
||||
int regDest = parseRegister(operand1);
|
||||
code[addr++] = regDest;
|
||||
cpu->memory[addr++] = regDest;
|
||||
if (operand2[0] == 'R' || operand2[0] == 'r') {
|
||||
int regSrc = parseRegister(operand2);
|
||||
code[addr++] = regSrc;
|
||||
cpu->memory[addr++] = regSrc;
|
||||
} else {
|
||||
uint8_t imm = parseImmediate(operand2);
|
||||
code[addr++] = imm;
|
||||
cpu->memory[addr++] = imm;
|
||||
}
|
||||
}
|
||||
// --- JMP Instruction (baseOpcode == -11) ---
|
||||
@@ -577,18 +593,17 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
return 1;
|
||||
}
|
||||
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||
code[addr++] = resolvedOpcode;
|
||||
cpu->memory[addr++] = resolvedOpcode;
|
||||
if (operand1[0] == '+' || operand1[0] == '-') {
|
||||
// Relative jump: one-byte offset.
|
||||
uint8_t offset = parseImmediate(operand1);
|
||||
code[addr++] = offset;
|
||||
cpu->memory[addr++] = offset;
|
||||
} else {
|
||||
// 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;
|
||||
cpu->memory[addr++] = jumpAddr & 0xFF;
|
||||
cpu->memory[addr++] = (jumpAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (jumpAddr >> 16) & 0xFF;
|
||||
}
|
||||
}
|
||||
// --- Jump Bit Set/Clear Instructions (JMPBS, JMPBC) ---
|
||||
@@ -598,33 +613,31 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
return 1;
|
||||
}
|
||||
int resolvedOpcode = resolveALU(baseOpcode, operand1);
|
||||
code[addr++] = resolvedOpcode;
|
||||
cpu->memory[addr++] = resolvedOpcode;
|
||||
// Encode the source operand (register or memory).
|
||||
if (operand1[0] == 'R' || operand1[0] == 'r') {
|
||||
int reg = parseRegister(operand1);
|
||||
code[addr++] = reg;
|
||||
cpu->memory[addr++] = reg;
|
||||
} else {
|
||||
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;
|
||||
cpu->memory[addr++] = memAddr & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (memAddr >> 16) & 0xFF;
|
||||
}
|
||||
// Encode the bit number (a one-byte immediate).
|
||||
uint8_t bitVal = parseImmediate(operand2);
|
||||
code[addr++] = bitVal;
|
||||
cpu->memory[addr++] = bitVal;
|
||||
// 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;
|
||||
cpu->memory[addr++] = jumpAddr & 0xFF;
|
||||
cpu->memory[addr++] = (jumpAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (jumpAddr >> 16) & 0xFF;
|
||||
}
|
||||
// --- Non-ambiguous Instructions ---
|
||||
else if (baseOpcode > 0) {
|
||||
else if (baseOpcode >= 0) {
|
||||
switch (baseOpcode) {
|
||||
case CMP:
|
||||
case SWAP: {
|
||||
@@ -632,11 +645,11 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
||||
return 1;
|
||||
}
|
||||
code[addr++] = baseOpcode;
|
||||
cpu->memory[addr++] = baseOpcode;
|
||||
int r1 = parseRegister(operand1);
|
||||
int r2 = parseRegister(operand2);
|
||||
code[addr++] = r1;
|
||||
code[addr++] = r2;
|
||||
cpu->memory[addr++] = r1;
|
||||
cpu->memory[addr++] = r2;
|
||||
break;
|
||||
}
|
||||
case SWAPN:
|
||||
@@ -646,9 +659,9 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
fprintf(stderr, "Error: %s requires one operand.\n", mnemonic);
|
||||
return 1;
|
||||
}
|
||||
code[addr++] = baseOpcode;
|
||||
cpu->memory[addr++] = baseOpcode;
|
||||
int reg = parseRegister(operand1);
|
||||
code[addr++] = reg;
|
||||
cpu->memory[addr++] = reg;
|
||||
break;
|
||||
}
|
||||
case SHL_RN_IMM:
|
||||
@@ -658,11 +671,11 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
|
||||
return 1;
|
||||
}
|
||||
code[addr++] = baseOpcode;
|
||||
cpu->memory[addr++] = baseOpcode;
|
||||
int reg = parseRegister(operand1);
|
||||
code[addr++] = reg;
|
||||
cpu->memory[addr++] = reg;
|
||||
uint8_t imm = parseImmediate(operand2);
|
||||
code[addr++] = imm;
|
||||
cpu->memory[addr++] = imm;
|
||||
break;
|
||||
}
|
||||
case JE:
|
||||
@@ -676,7 +689,7 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
fprintf(stderr, "Error: %s requires one operand.\n", mnemonic);
|
||||
return 1;
|
||||
}
|
||||
code[addr++] = baseOpcode;
|
||||
cpu->memory[addr++] = baseOpcode;
|
||||
// If the operand isn’t purely numeric, treat it as a label.
|
||||
if (!isdigit(operand1[0])) {
|
||||
int labelAddr = lookupLabel(operand1);
|
||||
@@ -684,16 +697,14 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
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;
|
||||
cpu->memory[addr++] = labelAddr & 0xFF;
|
||||
cpu->memory[addr++] = (labelAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (labelAddr >> 16) & 0xFF;
|
||||
} else {
|
||||
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;
|
||||
cpu->memory[addr++] = immAddr & 0xFF;
|
||||
cpu->memory[addr++] = (immAddr >> 8) & 0xFF;
|
||||
cpu->memory[addr++] = (immAddr >> 16) & 0xFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -701,7 +712,7 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
case BRK:
|
||||
case HLT:
|
||||
case NOP: {
|
||||
code[addr++] = baseOpcode;
|
||||
cpu->memory[addr++] = baseOpcode;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -713,17 +724,14 @@ int secondPass(const char *source, uint8_t *code) {
|
||||
fprintf(stderr, "Error: Unknown instruction '%s'\n", mnemonic);
|
||||
return 1;
|
||||
}
|
||||
const uint32_t remainingBytes = CPU_INSTRUCTION_SIZE - (addr - oldAddr);
|
||||
if (remainingBytes > CPU_INSTRUCTION_SIZE) {
|
||||
printf("HELP, INSTRUCTION SIZE SMALLER THAN INSTRUCTION");
|
||||
}
|
||||
cpu->addrToLineMapper[(addr - (addr % CPU_INSTRUCTION_SIZE)) / CPU_INSTRUCTION_SIZE] = lineIndex;
|
||||
addr += remainingBytes;
|
||||
lineIndex++;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
void completePass(const char *input, CPU *cpu, bool erase) {
|
||||
// First pass: determine label addresses.
|
||||
if (erase) {
|
||||
init_cpu(cpu);
|
||||
}
|
||||
firstPass(input);
|
||||
secondPass(input, cpu->memory);
|
||||
}
|
@@ -77,15 +77,13 @@ int resolveALU(int baseOpcode, const char *src);
|
||||
// The address is simply the offset into the output machine code buffer.
|
||||
// For this example, every instruction is assumed to have a fixed length (opcode plus operand bytes).
|
||||
//
|
||||
int firstPass(const char *source);
|
||||
void firstPass(const char *source);
|
||||
|
||||
//
|
||||
// The second pass actually translates the assembly instructions to machine code.
|
||||
// The machine code is written into the provided buffer. (It must be large enough.)
|
||||
//
|
||||
int secondPass(const char *source, uint8_t *code);
|
||||
|
||||
void completePass(const char *input, CPU *cpu, bool erase);
|
||||
int completePass(const char *input, CPU *cpu, bool erase);
|
||||
|
||||
|
||||
#endif //RISCB_ASSEMBLER_H
|
||||
|
22
cpu/core.c
22
cpu/core.c
@@ -34,11 +34,22 @@ void step(CPU *cpu) {
|
||||
cpu->mode |= CPU_MODE_HALTED;
|
||||
}
|
||||
}
|
||||
uint8_t opcode = read_mem(cpu, cpu->pc++);
|
||||
uint8_t reg1, reg2, imm, temp, temp2;
|
||||
uint32_t newPC, addrTemp;
|
||||
uint32_t newPC, addrTemp, oldPC;
|
||||
int32_t cmpResult;
|
||||
|
||||
oldPC = cpu->pc;
|
||||
newPC = oldPC;
|
||||
|
||||
uint8_t opcode = read_mem(cpu, cpu->pc++);
|
||||
|
||||
|
||||
const uint32_t differenceAlignment = oldPC % CPU_INSTRUCTION_SIZE;
|
||||
|
||||
if (differenceAlignment) {
|
||||
cpu->pc += differenceAlignment;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case NOP:
|
||||
//Don't do anything
|
||||
@@ -491,5 +502,12 @@ void step(CPU *cpu) {
|
||||
printf("Unknown opcode: %d\n", opcode);
|
||||
cpu->mode |= CPU_MODE_ERROR;
|
||||
}
|
||||
if (oldPC == newPC) {
|
||||
const uint32_t remainingBytes = CPU_INSTRUCTION_SIZE - (cpu->pc - oldPC);
|
||||
if (remainingBytes > CPU_INSTRUCTION_SIZE) {
|
||||
printf("HELP, INSTRUCTION SIZE SMALLER THAN INSTRUCTION");
|
||||
}
|
||||
cpu->pc += remainingBytes;
|
||||
}
|
||||
cpu->cycle++;
|
||||
}
|
@@ -23,13 +23,16 @@
|
||||
#define CPU_MODE_STEP (1 << 4)
|
||||
#define CPU_MODE_SECOND (1 << 5)
|
||||
|
||||
#define CPU_INSTRUCTION_SIZE 8 //Biggest instruction
|
||||
|
||||
|
||||
// CPU state
|
||||
typedef struct {
|
||||
uint8_t regs[REG_COUNT];
|
||||
uint8_t memory[MEM_SIZE];
|
||||
uint32_t pc; // Program counter
|
||||
uint32_t stack[STACK_SIZE]; // Stack pointer
|
||||
uint32_t addrToLineMapper[MEM_SIZE / CPU_INSTRUCTION_SIZE];
|
||||
uint32_t pc; // Program counter
|
||||
uint32_t stack_ptr; // Stack pointer
|
||||
uint8_t flags; // Status flags
|
||||
uint8_t mode;
|
||||
|
12
cpu/memory.c
12
cpu/memory.c
@@ -37,9 +37,13 @@ uint32_t read_mem32(CPU *cpu, uint32_t addr) {
|
||||
return read_mem16(cpu, addr) | (read_mem16(cpu, addr + 2) << 16);
|
||||
}
|
||||
|
||||
uint32_t read_mem24(CPU *cpu, uint32_t addr) {
|
||||
return read_mem16(cpu, addr) | (read_mem(cpu, addr + 2) << 16);
|
||||
}
|
||||
|
||||
uint32_t read_address_argument(CPU *cpu) {
|
||||
uint32_t out = read_mem32(cpu, cpu->pc);
|
||||
cpu->pc += 4;
|
||||
uint32_t out = read_mem24(cpu, cpu->pc);
|
||||
cpu->pc += 3;
|
||||
if (out >= MEM_SIZE) {
|
||||
out = MEM_SIZE - 1;
|
||||
}
|
||||
@@ -58,8 +62,7 @@ void write_stack(CPU *cpu) {
|
||||
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);
|
||||
((reg + 2 < REG_COUNT) ? (cpu->regs[reg + 2] << 16) : 0);
|
||||
cpu->stack[++cpu->stack_ptr] = packed;
|
||||
}
|
||||
cpu->stack[++cpu->stack_ptr] = cpu->flags;
|
||||
@@ -83,7 +86,6 @@ void read_stack(CPU *cpu) {
|
||||
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
|
||||
|
53
docs/examples/fulltest.bsm
Normal file
53
docs/examples/fulltest.bsm
Normal file
@@ -0,0 +1,53 @@
|
||||
; Test Program using all opcodes
|
||||
|
||||
START:
|
||||
NOP ; No operation (does nothing, advances to the next instruction)
|
||||
MOV R1, 0x10 ; Move immediate value 0x10 to register R1
|
||||
MOV R2, R1 ; Move value from R1 to R2
|
||||
MOV [0x2000], R3 ; Load value from memory address 0x2000 into R3
|
||||
MOV R1, [0x2000] ; Store R1 value to memory address 0x2000
|
||||
SWAP R1, R2 ; Swap values between R1 and R2
|
||||
SWAPN R1 ; Swap nibbles within R1
|
||||
ADD R1, R2 ; Add R2 to R1
|
||||
ADD R1, 0x10 ; Add immediate 0x10 to R1
|
||||
SUB R1, R2 ; Subtract R2 from R1
|
||||
SUB R1, 0x10 ; Subtract immediate 0x10 from R1
|
||||
MUL R1, R2 ; Multiply R1 by R2
|
||||
MUL R1, 0x10 ; Multiply R1 by immediate 0x10
|
||||
DIV R1, R2 ; Divide R1 by R2
|
||||
DIV R1, 0x10 ; Divide R1 by immediate 0x10
|
||||
MOD R1, R2 ; Compute remainder of R1 / R2
|
||||
MOD R1, 0x10 ; Compute remainder of R1 / 0x10
|
||||
NEG R1 ; Negate R1
|
||||
AND R1, R2 ; Bitwise AND of R1 and R2
|
||||
AND R1, 0x10 ; Bitwise AND R1 with immediate 0x10
|
||||
OR R1, R2 ; Bitwise OR of R1 and R2
|
||||
OR R1, 0x10 ; Bitwise OR R1 with immediate 0x10
|
||||
XOR R1, R2 ; Bitwise XOR of R1 and R2
|
||||
XOR R1, 0x10 ; Bitwise XOR R1 with immediate 0x10
|
||||
NOT R1 ; Bitwise NOT of R1
|
||||
SHL R1, 2 ; Logical shift left R1 by 2 bits
|
||||
SHR R1, 2 ; Logical shift right R1 by 2 bits
|
||||
SAR R1, 2 ; Arithmetic shift right R1 by 2 bits (sign extended)
|
||||
JMP 0x3000 ; Jump to address 0x3000
|
||||
JMP +5 ; Jump 5 bytes forward
|
||||
INC R1 ; Increment R1
|
||||
INC [0x2000] ; Increment value at memory address 0x2000
|
||||
DEC R1 ; Decrement R1
|
||||
DEC [0x2000] ; Decrement value at memory address 0x2000
|
||||
CMP R1, R2 ; Compare R1 and R2 (sets flags based on R1 - R2)
|
||||
JE 0x4000 ; Jump to 0x4000 if equal (zero flag set)
|
||||
JNE 0x4000 ; Jump to 0x4000 if not equal (zero flag not set)
|
||||
JG 0x4000 ; Jump to 0x4000 if greater
|
||||
JL 0x4000 ; Jump to 0x4000 if less
|
||||
JGE 0x4000 ; Jump to 0x4000 if greater or equal
|
||||
JLE 0x4000 ; Jump to 0x4000 if less or equal
|
||||
CALL 0x5000 ; Call subroutine at 0x5000
|
||||
RET ; Return from subroutine
|
||||
JMPBC R1, 3, 0x4000 ; Jump to address 0x4000 if bit 3 in register R1 is not set
|
||||
JMPBC 0x2000, 5, 0x5000 ; Jump to address 0x5000 if bit 5 in memory at address 0x2000 is not set
|
||||
JMPBS R2, 1, 0x6000 ; Jump to address 0x6000 if bit 1 in register R2 is set
|
||||
JMPBS 0x3000, 7, 0x7000 ; Jump to address 0x7000 if bit 7 in memory at address 0x3000 is set
|
||||
|
||||
; Halt the program
|
||||
HLT
|
6
docs/examples/jmptest.bsm
Normal file
6
docs/examples/jmptest.bsm
Normal file
@@ -0,0 +1,6 @@
|
||||
TEST:
|
||||
INC R1
|
||||
INC R2
|
||||
MOV R2 [20]
|
||||
JMP TEST
|
||||
HLT
|
4
docs/examples/test1.bsm
Normal file
4
docs/examples/test1.bsm
Normal file
@@ -0,0 +1,4 @@
|
||||
INC R1
|
||||
BRK
|
||||
INC R2
|
||||
HLT
|
195
docs/instructions.md
Normal file
195
docs/instructions.md
Normal file
@@ -0,0 +1,195 @@
|
||||
### **NOP**
|
||||
|
||||
`NOP` - No operation (does nothing, advances to the next instruction)
|
||||
|
||||
### **BRK**
|
||||
|
||||
`BRK` - Pause CPU (halts execution until resumed)
|
||||
|
||||
### **HLT**
|
||||
|
||||
`HLT` - Halt CPU execution
|
||||
|
||||
### **MOV_RN_IMM**
|
||||
|
||||
`MOV R1, 0x10` - Move immediate value `0x10` to register `R1`
|
||||
|
||||
### **MOV_RN_RM**
|
||||
|
||||
`MOV R2, R1` - Move value from `R1` to `R2`
|
||||
|
||||
### **MOV_RN_ADDR**
|
||||
|
||||
`MOV R3, [0x2000]` - Load value from memory address `0x2000` into `R3`
|
||||
|
||||
### **MOV_ADDR_RN**
|
||||
|
||||
`MOV [0x2000], R1` - Store `R1` value to memory address `0x2000`
|
||||
|
||||
### **SWAP**
|
||||
|
||||
`SWAP R1, R2` - Swap values between `R1` and `R2`
|
||||
|
||||
### **SWAPN**
|
||||
|
||||
`SWAPN R1` - Swap nibbles within `R1`
|
||||
|
||||
### **ADD_RN_RM**
|
||||
|
||||
`ADD R1, R2` - Add `R2` to `R1` (`R1 = R1 + R2`)
|
||||
|
||||
### **ADD_RN_IMM**
|
||||
|
||||
`ADD R1, 0x10` - Add immediate `0x10` to `R1` (`R1 = R1 + 0x10`)
|
||||
|
||||
### **SUB_RN_RM**
|
||||
|
||||
`SUB R1, R2` - Subtract `R2` from `R1` (`R1 = R1 - R2`)
|
||||
|
||||
### **SUB_RN_IMM**
|
||||
|
||||
`SUB R1, 0x10` - Subtract immediate `0x10` from `R1` (`R1 = R1 - 0x10`)
|
||||
|
||||
### **MUL_RN_RM**
|
||||
|
||||
`MUL R1, R2` - Multiply `R1` by `R2` (`R1 = R1 * R2`)
|
||||
|
||||
### **MUL_RN_IMM**
|
||||
|
||||
`MUL R1, 0x10` - Multiply `R1` by immediate `0x10` (`R1 = R1 * 0x10`)
|
||||
|
||||
### **DIV_RN_RM**
|
||||
|
||||
`DIV R1, R2` - Divide `R1` by `R2` (`R1 = R1 / R2`)
|
||||
|
||||
### **DIV_RN_IMM**
|
||||
|
||||
`DIV R1, 0x10` - Divide `R1` by immediate `0x10` (`R1 = R1 / 0x10`)
|
||||
|
||||
### **MOD_RN_RM**
|
||||
|
||||
`MOD R1, R2` - Compute remainder of `R1 / R2` (`R1 = R1 % R2`)
|
||||
|
||||
### **MOD_RN_IMM**
|
||||
|
||||
`MOD R1, 0x10` - Compute remainder of `R1 / 0x10` (`R1 = R1 % 0x10`)
|
||||
|
||||
### **NEG_RN**
|
||||
|
||||
`NEG R1` - Negate `R1` (`R1 = -R1`)
|
||||
|
||||
### **AND_RN_RM**
|
||||
|
||||
`AND R1, R2` - Bitwise AND of `R1` and `R2` (`R1 = R1 & R2`)
|
||||
|
||||
### **AND_RN_IMM**
|
||||
|
||||
`AND R1, 0x10` - Bitwise AND `R1` with immediate `0x10` (`R1 = R1 & 0x10`)
|
||||
|
||||
### **OR_RN_RM**
|
||||
|
||||
`OR R1, R2` - Bitwise OR of `R1` and `R2` (`R1 = R1 | R2`)
|
||||
|
||||
### **OR_RN_IMM**
|
||||
|
||||
`OR R1, 0x10` - Bitwise OR `R1` with immediate `0x10` (`R1 = R1 | 0x10`)
|
||||
|
||||
### **XOR_RN_RM**
|
||||
|
||||
`XOR R1, R2` - Bitwise XOR of `R1` and `R2` (`R1 = R1 ^ R2`)
|
||||
|
||||
### **XOR_RN_IMM**
|
||||
|
||||
`XOR R1, 0x10` - Bitwise XOR `R1` with immediate `0x10` (`R1 = R1 ^ 0x10`)
|
||||
|
||||
### **NOT_RN**
|
||||
|
||||
`NOT R1` - Bitwise NOT of `R1` (`R1 = ~R1`)
|
||||
|
||||
### **SHL_RN_IMM**
|
||||
|
||||
`SHL R1, 2` - Logical shift left `R1` by `2` bits
|
||||
|
||||
### **SHR_RN_IMM**
|
||||
|
||||
`SHR R1, 2` - Logical shift right `R1` by `2` bits
|
||||
|
||||
### **SAR_RN_IMM**
|
||||
|
||||
`SAR R1, 2` - Arithmetic shift right `R1` by `2` bits (sign extended)
|
||||
|
||||
### **JMP**
|
||||
|
||||
`JMP 0x3000` - Jump to address `0x3000`
|
||||
|
||||
### **JMP_REL**
|
||||
|
||||
`JMP +5` - Jump `5` bytes forward
|
||||
|
||||
### **INC_RN**
|
||||
|
||||
`INC R1` - Increment `R1`
|
||||
|
||||
### **INC_ADDR**
|
||||
|
||||
`INC [0x2000]` - Increment value at memory address `0x2000`
|
||||
|
||||
### **DEC_RN**
|
||||
|
||||
`DEC R1` - Decrement `R1`
|
||||
|
||||
### **DEC_ADDR**
|
||||
|
||||
`DEC [0x2000]` - Decrement value at memory address `0x2000`
|
||||
|
||||
### **CMP**
|
||||
|
||||
`CMP R1, R2` - Compare `R1` and `R2` (sets flags based on `R1 - R2`)
|
||||
|
||||
### **JE**
|
||||
|
||||
`JE 0x4000` - Jump to `0x4000` if equal (zero flag set)
|
||||
|
||||
### **JNE**
|
||||
|
||||
`JNE 0x4000` - Jump to `0x4000` if not equal (zero flag not set)
|
||||
|
||||
### **JG**
|
||||
|
||||
`JG 0x4000` - Jump to `0x4000` if greater
|
||||
|
||||
### **JL**
|
||||
|
||||
`JL 0x4000` - Jump to `0x4000` if less
|
||||
|
||||
### **JGE**
|
||||
|
||||
`JGE 0x4000` - Jump to `0x4000` if greater or equal
|
||||
|
||||
### **JLE**
|
||||
|
||||
`JLE 0x4000` - Jump to `0x4000` if less or equal
|
||||
|
||||
### **CALL**
|
||||
|
||||
`CALL 0x5000` - Call subroutine at `0x5000`
|
||||
|
||||
### **RET**
|
||||
|
||||
`RET` - Return from subroutine
|
||||
|
||||
### **JMP_BIT_CLEAR_RN**
|
||||
|
||||
`JMPBC R1, 3, 0x4000` - Jump to address `0x4000` if bit `3` in register `R1` is **not set**.
|
||||
|
||||
### **JMP_BIT_CLEAR_ADDR**
|
||||
|
||||
`JMPBC 0x2000, 5, 0x5000` - Jump to address `0x5000` if bit `5` in memory at address `0x2000` is **not set**.
|
||||
|
||||
### **JMP_BIT_SET_RN**
|
||||
|
||||
`JMPBS R2, 1, 0x6000` - Jump to address `0x6000` if bit `1` in register `R2` **is set**.
|
||||
|
||||
### **JMP_BIT_SET_ADDR**
|
||||
|
||||
`JMPBS 0x3000, 7, 0x7000` - Jump to address `0x7000` if bit `7` in memory at address `0x3000` **is set**.
|
93
main.c
93
main.c
@@ -72,24 +72,26 @@ char *read_file_as_string(const char *filename) {
|
||||
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);
|
||||
void updateState(bool full) {
|
||||
renderVals(&cpu, &smallFont, &smallerFont, renderer, &cpuStatsTexture);
|
||||
renderState(&cpu, &biggerFont, renderer, &cpuStateTexture);
|
||||
char *dump = hexdump_to_string(cpu.memory, full ? 0 : memoryViewer.cursor_line_offset * 16,
|
||||
full ? MEM_SIZE : (memoryViewer.cursor_line_offset * 16) +
|
||||
(memoryViewer.displayLineCount * 16));
|
||||
fill_editor_from_string(&memoryViewer, dump, full ? 0 : memoryViewer.cursor_line_offset, full, renderer);
|
||||
free(dump);
|
||||
}
|
||||
|
||||
void compile(bool erase) {
|
||||
generate_string(&codeEditor);
|
||||
completePass(codeEditor.outputString, &cpu, erase);
|
||||
updateState();
|
||||
updateState(true);
|
||||
}
|
||||
|
||||
|
||||
int init() {
|
||||
//Initialize SDL
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
@@ -108,17 +110,21 @@ int init() {
|
||||
return 1;
|
||||
}
|
||||
//Get window surface
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
if (renderer == NULL) {
|
||||
printf("Renderer could not be created SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_Rect viewport = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
|
||||
SDL_RenderSetViewport(renderer, &viewport);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
biggerFont = prepText(renderer, 16, "../PublicPixel.ttf", 255, 255, 255, 255);
|
||||
smallFont = prepText(renderer, 12, "../PublicPixel.ttf", 255, 255, 255, 255);
|
||||
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);
|
||||
init_editor(&codeEditor, &smallFont, 10, 80, renderer, 54, 1000, 48, false);
|
||||
init_editor(&memoryViewer, &smallerFont, 738, 80, renderer, 59, MEM_SIZE / 16 + 2, 70, true);
|
||||
SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL);
|
||||
for (int i = 0; i < editorCount; i++) {
|
||||
@@ -135,7 +141,7 @@ int render() {
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
for (int i = 0; i < editorCount; i++) {
|
||||
editor_render(&editors[i], renderer, activeEditorIndex == i, cursor);
|
||||
editor_render(&editors[i], renderer, &cpu, i, activeEditorIndex == i, cursor);
|
||||
}
|
||||
|
||||
SDL_Rect rect2;
|
||||
@@ -146,7 +152,6 @@ int render() {
|
||||
|
||||
SDL_QueryTexture(cpuStatsTexture, NULL, NULL, &rect2.w, &rect2.h);
|
||||
|
||||
|
||||
SDL_RenderCopy(renderer, cpuStatsTexture, NULL, &rect2);
|
||||
|
||||
rect2.x = 100;
|
||||
@@ -156,7 +161,6 @@ int render() {
|
||||
|
||||
SDL_QueryTexture(cpuStateTexture, NULL, NULL, &rect2.w, &rect2.h);
|
||||
|
||||
|
||||
SDL_RenderCopy(renderer, cpuStateTexture, NULL, &rect2);
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
@@ -292,43 +296,54 @@ int processEvent(SDL_Event e) {
|
||||
int keySym = ConvertKPToNonKP(e.key.keysym.sym);
|
||||
int keyMod = e.key.keysym.mod;
|
||||
cursor = true;
|
||||
bool moved = false;
|
||||
switch (keySym) {
|
||||
case SDLK_UP:
|
||||
move_cursor_relative(&activeEditor, -1, 0, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_PAGEUP:
|
||||
move_cursor_relative(&activeEditor, -activeEditor.max_lines_display, -1, true, renderer);
|
||||
move_cursor_relative(&activeEditor, -activeEditor.displayLineCount, -1, true, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
move_cursor_relative(&activeEditor, 1, 0, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_PAGEDOWN:
|
||||
move_cursor_relative(&activeEditor, activeEditor.max_lines_display, 0, true, renderer);
|
||||
move_cursor_relative(&activeEditor, activeEditor.displayLineCount, 0, true, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
move_cursor_relative(&activeEditor, 0, -1, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_HOME:
|
||||
if (keyMod & KMOD_CTRL) {
|
||||
move_cursor(&activeEditor, 0, 0, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
move_cursor(&activeEditor, activeEditor.cursor_line, 0, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
move_cursor_relative(&activeEditor, 0, 1, false, renderer);
|
||||
moved = true;
|
||||
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);
|
||||
move_cursor(&activeEditor, activeEditor.maxLines - 1, lineLen, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
}
|
||||
move_cursor(&activeEditor, activeEditor.cursor_line, lineLen, false, renderer);
|
||||
moved = true;
|
||||
break;
|
||||
case SDLK_F9:
|
||||
cpu.mode ^= CPU_MODE_LOOP;
|
||||
updateState();
|
||||
updateState(false);
|
||||
return 1;
|
||||
case SDLK_F8:
|
||||
if (++cpuSpeedTemp == 3) {
|
||||
@@ -336,7 +351,7 @@ int processEvent(SDL_Event e) {
|
||||
}
|
||||
cpu.mode &= ~(CPU_MODE_SECOND | CPU_MODE_STEP);
|
||||
cpu.mode |= cpuSpeedTemp << 4;
|
||||
updateState();
|
||||
updateState(false);
|
||||
return 1;
|
||||
case SDLK_F5:
|
||||
compile(!(keyMod & KMOD_CTRL));
|
||||
@@ -345,7 +360,7 @@ int processEvent(SDL_Event e) {
|
||||
cpu.pc = 0;
|
||||
}
|
||||
cpu.mode &= ~(CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR);
|
||||
updateState();
|
||||
updateState(false);
|
||||
break;
|
||||
|
||||
case SDLK_ESCAPE:
|
||||
@@ -353,7 +368,7 @@ int processEvent(SDL_Event e) {
|
||||
if (keyMod & (KMOD_CTRL | KMOD_SHIFT)) {
|
||||
cpu.mode |= CPU_MODE_HALTED;
|
||||
}
|
||||
updateState();
|
||||
updateState(false);
|
||||
break;
|
||||
|
||||
case SDLK_s:
|
||||
@@ -373,26 +388,30 @@ int processEvent(SDL_Event e) {
|
||||
if (keyMod & KMOD_CTRL) {
|
||||
FILE *fptr;
|
||||
char fname[20];
|
||||
sscanf(editors[0].lines[0].text, "%s", fname);
|
||||
sscanf(editors[0].lines[editors[0].cursor_line].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);
|
||||
if (fptr) {
|
||||
char *prog = read_file_as_string(fname);
|
||||
toUpperCase(prog);
|
||||
fill_editor_from_string(&editors[0], prog, 0, true, renderer);
|
||||
free(prog);
|
||||
fclose(fptr);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case SDLK_BACKSPACE:
|
||||
if (!activeEditor.readOnly) {
|
||||
remove_character(&activeEditor, false, renderer);
|
||||
moved = true;
|
||||
}
|
||||
break;
|
||||
case SDLK_DELETE:
|
||||
if (!activeEditor.readOnly) {
|
||||
remove_character(&activeEditor, true, renderer);
|
||||
moved = true;
|
||||
}
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
@@ -403,6 +422,7 @@ int processEvent(SDL_Event e) {
|
||||
}
|
||||
if (!activeEditor.readOnly) {
|
||||
insert_line_rel(&activeEditor, renderer);
|
||||
moved = true;
|
||||
}
|
||||
break;
|
||||
case SDLK_TAB:
|
||||
@@ -415,6 +435,9 @@ int processEvent(SDL_Event e) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (moved && &activeEditor == &memoryViewer) {
|
||||
updateState(false);
|
||||
}
|
||||
} 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];
|
||||
@@ -458,18 +481,18 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
||||
|
||||
if (!(cpu.mode & (CPU_MODE_HALTED | CPU_MODE_PAUSED | CPU_MODE_ERROR))) {
|
||||
if (cpu.mode & CPU_MODE_SECOND) {
|
||||
if (frames % 60) {
|
||||
if (!(frames % 60)) {
|
||||
step(&cpu);
|
||||
updateState();
|
||||
updateState(false);
|
||||
}
|
||||
} else {
|
||||
step(&cpu);
|
||||
updateState();
|
||||
updateState(false);
|
||||
}
|
||||
|
||||
if (cpu.mode & CPU_MODE_STEP) {
|
||||
cpu.mode |= CPU_MODE_PAUSED;
|
||||
updateState();
|
||||
updateState(false);
|
||||
}
|
||||
}
|
||||
end = SDL_GetTicks64();
|
||||
@@ -477,7 +500,7 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
||||
if (timeNeeded < delayNeeded) {
|
||||
SDL_Delay(delayNeeded - timeNeeded);
|
||||
} else {
|
||||
printf("%lu", timeNeeded);
|
||||
printf("%lu\n", timeNeeded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,11 +512,15 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
||||
destroyFont(&fonts[i]);
|
||||
}
|
||||
|
||||
puts(SDL_GetError());
|
||||
|
||||
//SDL_DestroyRenderer(renderer);
|
||||
|
||||
//Destroy window
|
||||
SDL_DestroyWindow(window);
|
||||
//SDL_DestroyWindow(window);
|
||||
|
||||
//Quit SDL subsystems
|
||||
SDL_Quit();
|
||||
//SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
@@ -6,8 +6,8 @@
|
||||
#include "cpustatusui.h"
|
||||
#include "font.h"
|
||||
|
||||
SDL_Texture *renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont,
|
||||
SDL_Renderer *renderer) {
|
||||
void renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont,
|
||||
SDL_Renderer *renderer, SDL_Texture **out) {
|
||||
|
||||
CPUStatusPart *stats = NULL;
|
||||
int statsCount = 0;
|
||||
@@ -16,12 +16,14 @@ SDL_Texture *renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont,
|
||||
getStats(cpu, &stats, &statsCount);
|
||||
|
||||
const int padding = 4;
|
||||
const int oneFieldW = (titleFont->size + 1) * (sizeof(stats[0].value) - 1) + padding - 1;
|
||||
const int oneFieldW = (titleFont->size + 1) * (sizeof(stats[0].value)) + 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);
|
||||
if (!*out) {
|
||||
*out = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
|
||||
oneFieldW * statsCount, oneFieldH);
|
||||
}
|
||||
|
||||
SDL_SetRenderTarget(renderer, out);
|
||||
SDL_SetRenderTarget(renderer, *out);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
@@ -61,7 +63,7 @@ SDL_Texture *renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont,
|
||||
}
|
||||
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
return out;
|
||||
free(stats);
|
||||
}
|
||||
|
||||
void getStats(CPU *cpu, CPUStatusPart **cpuStatus, int *cpuStatusCount) {
|
||||
@@ -81,27 +83,27 @@ void getStats(CPU *cpu, CPUStatusPart **cpuStatus, int *cpuStatusCount) {
|
||||
|
||||
int index = 0;
|
||||
|
||||
strncpy((*cpuStatus)[index].name, "PC", sizeof((*cpuStatus)[index].name));
|
||||
strncpy((*cpuStatus)[index].name, "PCNT", sizeof((*cpuStatus)[index].name));
|
||||
(*cpuStatus)[index].value = cpu->pc;
|
||||
index++;
|
||||
|
||||
strncpy((*cpuStatus)[index].name, "SP", sizeof((*cpuStatus)[index].name));
|
||||
strncpy((*cpuStatus)[index].name, "SPTR", sizeof((*cpuStatus)[index].name));
|
||||
(*cpuStatus)[index].value = cpu->stack_ptr;
|
||||
index++;
|
||||
|
||||
strncpy((*cpuStatus)[index].name, "FLG", sizeof((*cpuStatus)[index].name));
|
||||
strncpy((*cpuStatus)[index].name, "FLAG", sizeof((*cpuStatus)[index].name));
|
||||
(*cpuStatus)[index].value = cpu->flags;
|
||||
index++;
|
||||
|
||||
strncpy((*cpuStatus)[index].name, "MOD", sizeof((*cpuStatus)[index].name));
|
||||
strncpy((*cpuStatus)[index].name, "MODE", sizeof((*cpuStatus)[index].name));
|
||||
(*cpuStatus)[index].value = cpu->mode;
|
||||
index++;
|
||||
|
||||
strncpy((*cpuStatus)[index].name, "CYC", sizeof((*cpuStatus)[index].name));
|
||||
strncpy((*cpuStatus)[index].name, "CYCL", sizeof((*cpuStatus)[index].name));
|
||||
(*cpuStatus)[index].value = cpu->cycle;
|
||||
index++;
|
||||
|
||||
for (int i = 0; i < 25; i++) {
|
||||
for (int i = 0; i < 18; i++) {
|
||||
snprintf((*cpuStatus)[index].name, sizeof((*cpuStatus)[index].name), "R%d", i);
|
||||
(*cpuStatus)[index].value = cpu->regs[i];
|
||||
index++;
|
||||
@@ -110,7 +112,7 @@ void getStats(CPU *cpu, CPUStatusPart **cpuStatus, int *cpuStatusCount) {
|
||||
*cpuStatusCount = index; // Store the actual number of status parts
|
||||
}
|
||||
|
||||
SDL_Texture *renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer) {
|
||||
void renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer, SDL_Texture **out) {
|
||||
|
||||
// Render the value
|
||||
char valueStr[20] = "";
|
||||
@@ -141,10 +143,12 @@ SDL_Texture *renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer
|
||||
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);
|
||||
if (!*out) {
|
||||
*out = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
|
||||
allFieldW, oneFieldH);
|
||||
}
|
||||
|
||||
SDL_SetRenderTarget(renderer, out);
|
||||
SDL_SetRenderTarget(renderer, *out);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
@@ -164,5 +168,4 @@ SDL_Texture *renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer
|
||||
}
|
||||
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
return out;
|
||||
}
|
@@ -9,14 +9,14 @@
|
||||
#include "../cpu/core.h"
|
||||
|
||||
typedef struct {
|
||||
char name[4];
|
||||
char name[5];
|
||||
uint32_t value;
|
||||
} CPUStatusPart;
|
||||
|
||||
SDL_Texture *renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont,
|
||||
SDL_Renderer *renderer);
|
||||
void renderVals(CPU *cpu, BitmapFont *titleFont, BitmapFont *valueFont,
|
||||
SDL_Renderer *renderer, SDL_Texture ** out);
|
||||
|
||||
SDL_Texture *renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer);
|
||||
void renderState(CPU *cpu, BitmapFont *titleFont, SDL_Renderer *renderer, SDL_Texture **out);
|
||||
|
||||
void getStats(CPU *cpu, CPUStatusPart **cpuStatus, int *cpuStatusCount);
|
||||
|
||||
|
@@ -12,8 +12,12 @@ prepText(SDL_Renderer *renderer, unsigned char pxSize, const char *file, uint8_t
|
||||
out.color = (SDL_Color) {r, g, b, a};
|
||||
unsigned int i = 1;
|
||||
do {
|
||||
if (i == 173) { //specifically this char is 0 width (IDK why)
|
||||
out.surface[i] = SDL_CreateRGBSurface(0,pxSize,pxSize,32,0,0,0,0);
|
||||
} else {
|
||||
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 < 256);
|
||||
|
@@ -6,44 +6,39 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <malloc.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BYTES_PER_LINE 16 // Adjust for different widths
|
||||
|
||||
|
||||
char *hexdump_to_string(const unsigned char *data, size_t size) {
|
||||
char *hexdump_to_string(const unsigned char *data, uint32_t start, uint32_t end) {
|
||||
if (start >= end) return NULL; // Invalid range
|
||||
|
||||
// Estimate max output size: each line is approx. 80 chars
|
||||
size_t estimated_size = (size / BYTES_PER_LINE + 1) * 80;
|
||||
uint32_t estimated_size = ((end - start) / BYTES_PER_LINE + 1) * 60;
|
||||
|
||||
// Allocate memory for output string
|
||||
char *output = malloc(estimated_size);
|
||||
if (!output) return NULL;
|
||||
|
||||
size_t offset = 0; // Track the write position
|
||||
uint32_t out_offset = 0; // Tracks position in output buffer
|
||||
|
||||
for (size_t i = 0; i < size; i += BYTES_PER_LINE) {
|
||||
offset += snprintf(output + offset, estimated_size - offset, "%08zx ", i);
|
||||
for (uint32_t i = start; i < end; i += BYTES_PER_LINE) {
|
||||
// Print offset
|
||||
out_offset += snprintf(output + out_offset, estimated_size - out_offset, "%07x ", 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]);
|
||||
for (uint32_t j = 0; j < BYTES_PER_LINE; j++) {
|
||||
if (i + j < end)
|
||||
out_offset += snprintf(output + out_offset, estimated_size - out_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
|
||||
out_offset += snprintf(output + out_offset, estimated_size - out_offset, " "); // Padding
|
||||
if (j == 7)
|
||||
out_offset += snprintf(output + out_offset, estimated_size - out_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");
|
||||
out_offset += snprintf(output + out_offset, estimated_size - out_offset, "\n");
|
||||
}
|
||||
|
||||
return output;
|
||||
|
@@ -6,7 +6,8 @@
|
||||
#define RISCB_HEXDUMP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
char *hexdump_to_string(const unsigned char *data, size_t size);
|
||||
char *hexdump_to_string(const unsigned char *data, uint32_t start, uint32_t end);
|
||||
|
||||
#endif //RISCB_HEXDUMP_H
|
||||
|
@@ -5,13 +5,14 @@
|
||||
|
||||
#include "texteditor.h"
|
||||
#include "font.h"
|
||||
#include "../cpu/core.h"
|
||||
|
||||
// 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->maxLines = max_lines_asm;
|
||||
editor->displayLineCount = max_lines_display;
|
||||
editor->line_count = 0;
|
||||
editor->cursor_line = 0;
|
||||
editor->cursor_line_offset = 0;
|
||||
@@ -22,20 +23,22 @@ void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Rendere
|
||||
editor->outRect = malloc(sizeof(SDL_Rect));
|
||||
editor->cursorRect = malloc(sizeof(SDL_Rect));
|
||||
editor->rect = malloc(sizeof(SDL_Rect));
|
||||
editor->highlightedLineRect = 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));
|
||||
memset(editor->highlightedLineRect, 0, sizeof(SDL_Rect));
|
||||
|
||||
|
||||
// Allocate dynamic array for lines.
|
||||
editor->lines = (Line *) malloc(sizeof(Line) * editor->max_lines_asm);
|
||||
editor->lines = (Line *) malloc(sizeof(Line) * editor->maxLines);
|
||||
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++) {
|
||||
for (int i = 0; i < editor->maxLines; 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);
|
||||
@@ -46,8 +49,8 @@ void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Rendere
|
||||
}
|
||||
|
||||
// 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));
|
||||
editor->outputString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->maxLines + 1));
|
||||
editor->displayString = (char *) malloc(sizeof(char) * (editor->max_line_width * editor->displayLineCount + 1));
|
||||
if (!editor->outputString || !editor->displayString) {
|
||||
fprintf(stderr, "Failed to allocate memory for output/display strings.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -60,21 +63,26 @@ void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Rendere
|
||||
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.
|
||||
// Set up the editor rectangle based on font size and dynamic max_line_width and displayLineCount.
|
||||
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->rect->h = editor->displayLineCount * (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->highlightedLineRect->x = 2;
|
||||
editor->highlightedLineRect->y = 2;
|
||||
editor->highlightedLineRect->w = editor->outRect->w - (2 * editor->highlightedLineRect->x);
|
||||
editor->highlightedLineRect->h = editor->font->size;
|
||||
|
||||
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->w = editor->font->size;
|
||||
editor->cursorRect->h = editor->font->size;
|
||||
|
||||
// Create texture for rendering.
|
||||
@@ -87,9 +95,8 @@ void init_editor(TextEditor *editor, BitmapFont *font, int x, int y, SDL_Rendere
|
||||
}
|
||||
}
|
||||
|
||||
// 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 >= editor->max_lines_asm || position < 0 || position > editor->line_count) {
|
||||
if (editor->line_count >= editor->maxLines || position < 0 || position > editor->line_count) {
|
||||
printf("Invalid position or max lines reached!\n");
|
||||
return;
|
||||
}
|
||||
@@ -111,7 +118,16 @@ void insert_line(TextEditor *editor, int position, const char *text, SDL_Rendere
|
||||
}
|
||||
|
||||
void insert_line_rel(TextEditor *editor, SDL_Renderer *renderer) {
|
||||
insert_line(editor, editor->cursor_line + (editor->cursor_pos ? 1 : 0), "", renderer);
|
||||
int line = editor->cursor_line;
|
||||
int pos = editor->cursor_pos;
|
||||
char *current_text = editor->lines[line].text;
|
||||
|
||||
// Split the current line at the cursor position
|
||||
char new_line_text[editor->max_line_width + 1];
|
||||
strcpy(new_line_text, ¤t_text[pos]); // Copy the text after cursor to the new line
|
||||
current_text[pos] = '\0'; // Truncate the current line at cursor position
|
||||
|
||||
insert_line(editor, line + 1, new_line_text, renderer);
|
||||
editor->cursor_pos = 0;
|
||||
}
|
||||
|
||||
@@ -154,11 +170,27 @@ void remove_character(TextEditor *editor, bool isDelete, SDL_Renderer *renderer)
|
||||
for (int i = editor->cursor_pos; i < len; i++) {
|
||||
line->text[i] = line->text[i + 1];
|
||||
}
|
||||
} else if (editor->cursor_pos == len && editor->cursor_line < editor->line_count - 1) {
|
||||
// Merge next line with the current line when pressing delete at the end
|
||||
strcat(line->text, editor->lines[editor->cursor_line + 1].text);
|
||||
|
||||
// Shift remaining lines up
|
||||
for (int i = editor->cursor_line + 1; 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--;
|
||||
}
|
||||
} else {
|
||||
// Backspace behavior (delete character before cursor)
|
||||
if (editor->cursor_pos == 0 && editor->line_count > 1) {
|
||||
// Merge with the previous line
|
||||
if (editor->cursor_pos == 0 && editor->cursor_line > 0) {
|
||||
// Append current line to previous line
|
||||
int prev_len = strlen(editor->lines[editor->cursor_line - 1].text);
|
||||
strcat(editor->lines[editor->cursor_line - 1].text, line->text);
|
||||
|
||||
// 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;
|
||||
@@ -166,10 +198,10 @@ void remove_character(TextEditor *editor, bool isDelete, SDL_Renderer *renderer)
|
||||
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);
|
||||
|
||||
// Move cursor to the end of the previous line
|
||||
editor->cursor_line--;
|
||||
editor->cursor_pos = prev_len;
|
||||
} else if (editor->cursor_pos > 0) {
|
||||
for (int i = editor->cursor_pos - 1; i < len; i++) {
|
||||
line->text[i] = line->text[i + 1];
|
||||
@@ -191,7 +223,14 @@ void move_cursor_relative(TextEditor *editor, int line_offset, int pos_offset, b
|
||||
|
||||
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;
|
||||
int trgt = editor->line_count - editor->displayLineCount;
|
||||
if (trgt < 0) {
|
||||
trgt = 0;
|
||||
}
|
||||
if (new_line >= trgt && new_line >= editor->line_count) {
|
||||
new_line = editor->line_count -
|
||||
1;
|
||||
}
|
||||
|
||||
if (keepPos) {
|
||||
editor->cursor_line_offset = new_line;
|
||||
@@ -200,8 +239,15 @@ void move_cursor(TextEditor *editor, int new_line, int new_pos, bool keepPos, SD
|
||||
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;
|
||||
if (new_line >= editor->cursor_line_offset + editor->displayLineCount) {
|
||||
editor->cursor_line_offset = new_line - editor->displayLineCount + 1;
|
||||
}
|
||||
|
||||
if (editor->cursor_line_offset >= editor->line_count - editor->displayLineCount) {
|
||||
editor->cursor_line_offset = editor->line_count - editor->displayLineCount;
|
||||
}
|
||||
if (editor->cursor_line_offset < 0) {
|
||||
editor->cursor_line_offset = 0;
|
||||
}
|
||||
|
||||
int line_length = strlen(editor->lines[new_line].text);
|
||||
@@ -219,7 +265,7 @@ void generate_string_display(TextEditor *editor, SDL_Renderer *renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
int end_line = editor->cursor_line_offset + editor->max_lines_display;
|
||||
int end_line = editor->cursor_line_offset + editor->displayLineCount;
|
||||
if (end_line > editor->line_count)
|
||||
end_line = editor->line_count;
|
||||
|
||||
@@ -227,7 +273,7 @@ void generate_string_display(TextEditor *editor, SDL_Renderer *renderer) {
|
||||
editor->displayString[0] = '\0';
|
||||
|
||||
SDL_SetRenderTarget(renderer, editor->texture);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
SDL_Rect charDstRect;
|
||||
@@ -263,7 +309,8 @@ void generate_string_display(TextEditor *editor, SDL_Renderer *renderer) {
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
}
|
||||
|
||||
void editor_render(TextEditor *editor, SDL_Renderer *renderer, bool isActive, bool cursorBlink) {
|
||||
void editor_render(TextEditor *editor, SDL_Renderer *renderer, CPU *cpu, uint8_t editorIndex, bool isActive,
|
||||
bool cursorBlink) {
|
||||
if (isActive) {
|
||||
SDL_Rect bgRect;
|
||||
bgRect = *editor->outRect;
|
||||
@@ -271,20 +318,57 @@ void editor_render(TextEditor *editor, SDL_Renderer *renderer, bool isActive, bo
|
||||
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);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderFillRect(renderer, editor->outRect);
|
||||
|
||||
uint32_t targetLine = 0;
|
||||
const uint32_t instrLine = cpu->pc / CPU_INSTRUCTION_SIZE;
|
||||
if (editorIndex == 0) {
|
||||
targetLine = cpu->addrToLineMapper[instrLine] - editor->cursor_line_offset;
|
||||
editor->highlightedLineRect->w = editor->rect->w - 1;
|
||||
editor->highlightedLineRect->x = 2;
|
||||
}
|
||||
if (editorIndex == 1) {
|
||||
targetLine = (instrLine / 2) - editor->cursor_line_offset;
|
||||
editor->highlightedLineRect->w = (editor->rect->w / 5 * 2) - 2;
|
||||
editor->highlightedLineRect->x =
|
||||
2 + (11 * editor->font->size) + (instrLine % 2 ? (28 * editor->font->size) : 0);
|
||||
|
||||
}
|
||||
editor->highlightedLineRect->y = 2 + ((editor->font->size + 1) * targetLine);
|
||||
SDL_Rect highlightedLineAbs = *editor->highlightedLineRect;
|
||||
highlightedLineAbs.x += editor->outRect->x;
|
||||
highlightedLineAbs.y += editor->outRect->y;
|
||||
SDL_SetRenderDrawColor(renderer, 0, 72, 64, 255);
|
||||
if (targetLine >= editor->cursor_line_offset &&
|
||||
targetLine < editor->cursor_line_offset + editor->displayLineCount) {
|
||||
SDL_RenderFillRect(renderer, &highlightedLineAbs);
|
||||
}
|
||||
|
||||
|
||||
SDL_SetTextureBlendMode(editor
|
||||
->texture, SDL_BLENDMODE_BLEND);
|
||||
SDL_RenderCopy(renderer, editor
|
||||
->texture, editor->rect, editor->outRect);
|
||||
if (isActive && cursorBlink) {
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
|
||||
SDL_RenderFillRect(renderer, editor->cursorRect);
|
||||
SDL_SetRenderDrawColor(renderer,
|
||||
0, 255, 255, 128);
|
||||
SDL_RenderFillRect(renderer, editor
|
||||
->cursorRect);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void generate_string(TextEditor *editor) {
|
||||
editor->outputString[0] = '\0';
|
||||
|
||||
for (int i = 0; i < editor->max_lines_asm; i++) {
|
||||
for (int i = 0; i < editor->maxLines; i++) {
|
||||
if (editor->lines[i].active) {
|
||||
if (strlen(editor->lines[i].text)) {
|
||||
strcat(editor->outputString, editor->lines[i].text);
|
||||
@@ -294,30 +378,39 @@ void generate_string(TextEditor *editor) {
|
||||
}
|
||||
}
|
||||
|
||||
void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Renderer *renderer) {
|
||||
void fill_editor_from_string(TextEditor *editor, const char *content, int lineStart, bool isComplete,
|
||||
SDL_Renderer *renderer) {
|
||||
if (!editor || !content) {
|
||||
printf("Invalid editor or content pointer!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *str = content;
|
||||
|
||||
int numLines = 0;
|
||||
while (*str) if (*str++ == '\n') ++numLines;
|
||||
|
||||
// Clear the current editor content
|
||||
for (int i = 0; i < editor->max_lines_asm; i++) {
|
||||
for (int i = lineStart; i < editor->maxLines && i < lineStart + numLines + 1; i++) {
|
||||
editor->lines[i].text[0] = '\0';
|
||||
editor->lines[i].active = 0;
|
||||
}
|
||||
editor->line_count = 0;
|
||||
editor->cursor_line = 0;
|
||||
editor->cursor_pos = 0;
|
||||
if (isComplete) {
|
||||
editor->line_count = numLines;
|
||||
if (editor->line_count >= editor->maxLines) {
|
||||
editor->line_count = editor->maxLines - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the content and fill the editor lines
|
||||
const char *ptr = content;
|
||||
int line_index = 0;
|
||||
int line_index = lineStart;
|
||||
|
||||
while (*ptr && line_index < editor->max_lines_asm) {
|
||||
while (*ptr && line_index < editor->maxLines && line_index < numLines + lineStart) {
|
||||
int char_count = 0;
|
||||
|
||||
// Ensure the text buffer does not overflow
|
||||
while (*ptr && *ptr != '\n' && char_count < editor->max_line_width - 1) {
|
||||
while (*ptr && *ptr != '\n' && char_count < editor->max_line_width) {
|
||||
editor->lines[line_index].text[char_count++] = *ptr++;
|
||||
}
|
||||
|
||||
@@ -328,9 +421,11 @@ void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Render
|
||||
// Move past the newline character if present
|
||||
if (*ptr == '\n') ptr++;
|
||||
}
|
||||
editor->lines[line_index].active = 1;
|
||||
|
||||
// Update the total number of lines in use
|
||||
editor->line_count = line_index;
|
||||
//editor->line_count = line_index + (line_index < editor->maxLines - 1 ? 1 : 0);
|
||||
//if (editor->line_count >= editor-> maxLines)
|
||||
|
||||
// Generate the visual representation
|
||||
generate_string_display(editor, renderer);
|
||||
@@ -340,13 +435,13 @@ void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Render
|
||||
// Free all dynamically allocated memory.
|
||||
void destroy_editor(TextEditor *editor) {
|
||||
if (editor->lines) {
|
||||
for (int i = 0; i < editor->max_lines_asm; i++) {
|
||||
for (int i = 0; i < editor->maxLines; i++) {
|
||||
free(editor->lines[i].text);
|
||||
}
|
||||
free(editor->lines);
|
||||
}
|
||||
free(editor->outputString);
|
||||
free(editor->displayString);
|
||||
//free(editor->outputString);
|
||||
//free(editor->displayString);
|
||||
|
||||
free(editor->outRect);
|
||||
free(editor->rect);
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <SDL2/SDL_surface.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include "font.h"
|
||||
#include "../cpu/core.h"
|
||||
|
||||
typedef struct {
|
||||
char *text; // Dynamically allocated string for this line
|
||||
@@ -22,25 +23,26 @@ typedef struct {
|
||||
typedef struct {
|
||||
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 maxLines; // 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 displayLineCount; // 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)
|
||||
char *outputString; // Dynamically allocated output string (size: max_line_width * maxLines + 1)
|
||||
char *displayString; // Dynamically allocated display string (size: max_line_width * displayLineCount + 1)
|
||||
SDL_Rect *rect;
|
||||
SDL_Rect *outRect;
|
||||
SDL_Texture *texture;
|
||||
bool readOnly;
|
||||
BitmapFont *font;
|
||||
SDL_Rect *cursorRect;
|
||||
SDL_Rect *highlightedLineRect;
|
||||
} TextEditor;
|
||||
|
||||
// Initialize the text editor. The parameters max_line_width, max_lines_asm, and max_lines_display
|
||||
// Initialize the text editor. The parameters max_line_width, maxLines, and displayLineCount
|
||||
// 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);
|
||||
@@ -50,7 +52,7 @@ void insert_line(TextEditor *editor, int position, const char *text, SDL_Rendere
|
||||
|
||||
void insert_line_rel(TextEditor *editor, SDL_Renderer *renderer);
|
||||
|
||||
void editor_render(TextEditor *editor, SDL_Renderer *renderer, bool isActive, bool cursorBlink);
|
||||
void editor_render(TextEditor *editor, SDL_Renderer *renderer, CPU *cpu, uint8_t editorIndex, bool isActive, bool cursorBlink);
|
||||
|
||||
void remove_character(TextEditor *editor, bool isDelete, SDL_Renderer *renderer);
|
||||
|
||||
@@ -64,7 +66,8 @@ void generate_string_display(TextEditor *editor, SDL_Renderer *renderer);
|
||||
|
||||
void generate_string(TextEditor *editor);
|
||||
|
||||
void fill_editor_from_string(TextEditor *editor, const char *content, SDL_Renderer *renderer);
|
||||
void fill_editor_from_string(TextEditor *editor, const char *content, int lineStart, bool isComplete,
|
||||
SDL_Renderer *renderer);
|
||||
|
||||
// A cleanup function to free dynamically allocated memory.
|
||||
void destroy_editor(TextEditor *editor);
|
||||
|
Reference in New Issue
Block a user