diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..233187a
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+RISCB
\ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..30bab2a
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 9957783..bff9a85 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,6 +2,7 @@
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index efca100..2446374 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,19 @@
cmake_minimum_required(VERSION 3.31)
-project(sdlzasedaco C)
+project(RISCB C)
set(CMAKE_C_STANDARD 23)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
-add_executable(sdlzasedaco main.c) # Ensure the target is defined before linking
+add_executable(RISCB main.c
+ util/font.c
+ util/font.h
+ assembler/assembler.c
+ assembler/assembler.h
+ cpu/memory.c
+ cpu/memory.h
+ cpu/core.c
+ cpu/core.h) # Ensure the target is defined before linking
-target_link_libraries(sdlzasedaco SDL2 SDL2_ttf)
+target_link_libraries(RISCB SDL2 SDL2_ttf m)
diff --git a/PublicPixel.ttf b/PublicPixel.ttf
new file mode 100644
index 0000000..f6c69fe
Binary files /dev/null and b/PublicPixel.ttf differ
diff --git a/assembler/assembler.c b/assembler/assembler.c
new file mode 100644
index 0000000..aaf1d3c
--- /dev/null
+++ b/assembler/assembler.c
@@ -0,0 +1,525 @@
+//
+// Created by bruno on 1.2.2025.
+//
+
+#include "assembler.h"
+
+Label labels[MAX_LABELS];
+int labelCount = 0;
+
+//
+// Helper functions for string manipulation
+//
+void trim(char *s) {
+ // Remove leading whitespace
+ while (isspace((unsigned char) *s)) s++;
+
+ // Remove trailing whitespace
+ char *end = s + strlen(s) - 1;
+ while (end > s && isspace((unsigned char) *end)) {
+ *end = '\0';
+ end--;
+ }
+}
+
+// Look up a label by name; returns -1 if not found.
+int lookupLabel(const char *name) {
+ for (int i = 0; i < labelCount; i++) {
+ if (strcmp(labels[i].name, name) == 0)
+ return labels[i].address;
+ }
+ return -1;
+}
+
+// Add a label to the table
+void addLabel(const char *name, int address) {
+ if (labelCount >= MAX_LABELS) {
+ fprintf(stderr, "Too many labels!\n");
+ exit(1);
+ }
+ strncpy(labels[labelCount].name, name, sizeof(labels[labelCount].name));
+ labels[labelCount].address = address;
+ labelCount++;
+}
+
+//
+// Parse a register string (e.g., "R0", "R1", etc.) and return it's number.
+// Returns -1 on error.
+int parseRegister(const char *token) {
+ if (token[0] == 'R' || token[0] == 'r') {
+ int reg = atoi(token + 1);
+ if (reg >= 0 && reg < REG_COUNT)
+ return reg;
+ }
+ return -1;
+}
+
+// Parse an immediate value (supports decimal and 0x... hexadecimal)
+uint8_t parseImmediate(const char *token) {
+ int value;
+ if (strlen(token) > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X'))
+ sscanf(token, "%x", &value);
+ else
+ sscanf(token, "%d", &value);
+ return (uint8_t) value;
+}
+
+void toUpperCase(char *string) {
+ while (*string) {
+ if (*string > 0x60 && *string < 0x7b) {
+ (*string) -= 0x20;
+ }
+ }
+}
+
+//
+// Map an instruction mnemonic (string) to its opcode value and expected operand types.
+// For simplicity, we will return the opcode value and then in our parser we’ll decide how many operands to expect.
+// (In a full assembler you might use a more sophisticated data structure.)
+//
+int getOpcode(char *mnemonic) {
+ toUpperCase(mnemonic);
+ if (strcmp(mnemonic, "BRK") == 0)
+ return BRK;
+ else if (strcmp(mnemonic, "NOP") == 0)
+ return NOP;
+ 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)
+ return SWAP;
+ else if (strcmp(mnemonic, "SWAPN") == 0)
+ return SWAPN;
+ else if (strcmp(mnemonic, "ADD") == 0)
+ return -3; // Special: decide between ADD_RN_RM and ADD_RN_IMM
+ else if (strcmp(mnemonic, "SUB") == 0)
+ return -4; // Special: decide between SUB_RN_RM and SUB_RN_IMM
+ else if (strcmp(mnemonic, "MUL") == 0)
+ return -5; // Special: decide between MUL_RN_RM and MUL_RN_IMM
+ else if (strcmp(mnemonic, "DIV") == 0)
+ return -6; // Special: decide between DIV_RN_RM and DIV_RN_IMM
+ else if (strcmp(mnemonic, "MOD") == 0)
+ return -7; // Special: decide between MOD_RN_RM and MOD_RN_IMM
+ else if (strcmp(mnemonic, "NEG") == 0)
+ return NEG_RN;
+ else if (strcmp(mnemonic, "AND") == 0)
+ return -8; // Special: decide between AND_RN_RM and AND_RN_IMM
+ else if (strcmp(mnemonic, "OR") == 0)
+ return -9; // Special: decide between OR_RN_RM and OR_RN_IMM
+ else if (strcmp(mnemonic, "XOR") == 0)
+ return -10; // Special: decide between XOR_RN_RM and XOR_RN_IMM
+ else if (strcmp(mnemonic, "NOT") == 0)
+ return NOT_RN;
+ else if (strcmp(mnemonic, "SHL") == 0)
+ return SHL_RN_IMM;
+ else if (strcmp(mnemonic, "SHR") == 0)
+ return SHR_RN_IMM;
+ else if (strcmp(mnemonic, "SAR") == 0)
+ return SAR_RN_IMM;
+ else if (strcmp(mnemonic, "JMP") == 0)
+ return JMP;
+ else if (strcmp(mnemonic, "CMP") == 0)
+ return CMP;
+ else if (strcmp(mnemonic, "JE") == 0)
+ return JE;
+ else if (strcmp(mnemonic, "JNE") == 0)
+ return JNE;
+ else if (strcmp(mnemonic, "JG") == 0)
+ return JG;
+ else if (strcmp(mnemonic, "JL") == 0)
+ return JL;
+ else if (strcmp(mnemonic, "JGE") == 0)
+ return JGE;
+ else if (strcmp(mnemonic, "JLE") == 0)
+ return JLE;
+ else if (strcmp(mnemonic, "CALL") == 0)
+ return CALL;
+ else if (strcmp(mnemonic, "RET") == 0)
+ return RET;
+ else if (strcmp(mnemonic, "PUSH") == 0)
+ return PUSH;
+ else if (strcmp(mnemonic, "POP") == 0)
+ return POP;
+ else if (strcmp(mnemonic, "PUSHF") == 0)
+ return PUSHF;
+ else if (strcmp(mnemonic, "POPF") == 0)
+ return POPF;
+ else {
+ return -1;
+ }
+}
+
+//
+// In this simple assembler, some instructions share a mnemonic, and we must choose the correct opcode
+// based on the type of the operand (register vs. immediate vs. memory).
+// The following helper functions decide that, given two operands (as strings).
+//
+// For example, "MOV Rn, 42" should choose MOV_RN_IMM, while "MOV Rn, Rm" should choose MOV_RN_RM.
+// We assume that memory addresses are written in square brackets, e.g. "[123]".
+//
+int resolveMOV(const char *dest, const char *src) {
+ // If dest starts with '[' then it is a memory destination.
+ if (dest[0] == '[') return MOV_ADDR_RN; // actually, MOV [Addr], Rn expects Rn in second operand
+ // Otherwise, dest is a register.
+ // Now, check src:
+ if (src[0] == 'R' || src[0] == 'r') {
+ return MOV_RN_RM;
+ } else if (src[0] == '[') {
+ return MOV_RN_ADDR;
+ } else {
+ return MOV_RN_IMM;
+ }
+}
+
+int resolveALU(int baseOpcode, const char *src) {
+ // baseOpcode is one of our special negative values for ADD, SUB, etc.
+ if (src[0] == 'R' || src[0] == 'r')
+ switch (baseOpcode) {
+ case -3:
+ return ADD_RN_RM;
+ case -4:
+ return SUB_RN_RM;
+ case -5:
+ return MUL_RN_RM;
+ case -6:
+ return DIV_RN_RM;
+ case -7:
+ return MOD_RN_RM;
+ case -8:
+ return AND_RN_RM;
+ case -9:
+ return OR_RN_RM;
+ case -10:
+ return XOR_RN_RM;
+ default:
+ return -1;
+ }
+ else
+ switch (baseOpcode) {
+ case -3:
+ return ADD_RN_IMM;
+ case -4:
+ return SUB_RN_IMM;
+ case -5:
+ return MUL_RN_IMM;
+ case -6:
+ return DIV_RN_IMM;
+ case -7:
+ return MOD_RN_IMM;
+ case -8:
+ return AND_RN_IMM;
+ case -9:
+ return OR_RN_IMM;
+ case -10:
+ return XOR_RN_IMM;
+ default:
+ return -1;
+ }
+}
+
+// Reads a single line from the source string.
+const char *readLine(const char *source, char *buffer, size_t maxLen) {
+ size_t i = 0;
+ while (*source && *source != '\n' && i < maxLen - 1) {
+ buffer[i++] = *source++;
+ }
+ buffer[i] = '\0';
+ return (*source == '\n') ? source + 1 : source;
+}
+
+//
+// The first pass scans the assembly source file to record all labels and their addresses.
+// 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) {
+ char line[MAX_LINE_LENGTH];
+ int addr = 0;
+ const char *ptr = source;
+
+ while (*ptr) {
+ // Read a line from the source string
+ ptr = readLine(ptr, line, sizeof(line));
+ trim(line);
+
+ if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
+ continue; // Skip empty or comment lines
+
+ char *colon = strchr(line, ':');
+ if (colon != NULL) {
+ *colon = '\0';
+ trim(line);
+ addLabel(line, addr);
+
+ char *rest = colon + 1;
+ trim(rest);
+ if (strlen(rest) == 0)
+ continue;
+ strcpy(line, rest);
+ }
+
+ // For simplicity, we assume each instruction (with its operands) takes a fixed number of bytes.
+ // Here we calculate the number of bytes by looking at the opcode mnemonic.
+ // (A more robust approach would have a table for instruction sizes.)
+ char mnemonic[32];
+ sscanf(line, "%31s", mnemonic);
+
+ int opcode = getOpcode(mnemonic);
+ if (opcode == -2) {
+ // MOV: two operands separated by comma
+ // e.g. MOV R1, 42
+ // We add 3 bytes: opcode, operand1, operand2.
+ addr += 3;
+ } else if (opcode == -3 || opcode == -4 || opcode == -5 || opcode == -6 ||
+ opcode == -7 || opcode == -8 || opcode == -9 || opcode == -10) {
+ // ALU instructions with two operands: 3 bytes.
+ addr += 3;
+ } else if (opcode == NEG_RN || opcode == SWAPN || opcode == NOT_RN) {
+ // One operand: 2 bytes.
+ addr += 2;
+ } else if (opcode == SWAP || opcode == CMP) {
+ // Two operands: 3 bytes.
+ addr += 3;
+ } else if (opcode == SHL_RN_IMM || opcode == SHR_RN_IMM ||
+ opcode == SAR_RN_IMM) {
+ addr += 3;
+ } else if (opcode == JMP || opcode == JE || opcode == JNE ||
+ opcode == JG || opcode == JL || opcode == JGE || opcode == JLE ||
+ opcode == CALL) {
+ // Jump or call: 2 bytes (opcode and one byte address/immediate).
+ addr += 2;
+ } else if (opcode == RET || opcode == PUSHF || opcode == POPF) {
+ addr += 1;
+ } else if (opcode == PUSH || opcode == POP) {
+ addr += 2;
+ } else {
+ // For other instructions, we assume 3 bytes.
+ addr += 3;
+ }
+ }
+ 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) {
+ char line[MAX_LINE_LENGTH];
+ int addr = 0;
+ const char *ptr = source;
+
+ while (*ptr) {
+ ptr = readLine(ptr, line, sizeof(line));
+ trim(line);
+
+ if (line[0] == '\0' || line[0] == ';' || line[0] == '#')
+ continue;
+
+ char *colon = strchr(line, ':');
+ if (colon != NULL) {
+ *colon = ' ';
+ }
+
+ if (strlen(line) == 0)
+ continue;
+
+ char *token = strtok(line, " ,");
+ if (!token)
+ continue;
+
+ char mnemonic[32];
+ strncpy(mnemonic, token, sizeof(mnemonic));
+ int opcode = getOpcode(mnemonic);
+ code[addr++] = opcode;
+
+ // Handle instructions that need operand disambiguation.
+ if (strcmp(mnemonic, "MOV") == 0) {
+ // Get first operand.
+ char *dest = strtok(NULL, " ,");
+ char *src = strtok(NULL, " ,");
+ if (!dest || !src) {
+ fprintf(stderr, "Error: MOV requires two operands.\n");
+ exit(1);
+ }
+ int opcode2 = resolveMOV(dest, src);
+ code[addr++] = opcode2;
+ // For the MOV instructions we decide that:
+ // - For MOV_RN_IMM: operand bytes: [register, immediate]
+ // - For MOV_RN_RM: operand bytes: [dest register, src register]
+ // - For MOV_RN_ADDR: operand bytes: [dest register, address]
+ // - For MOV_ADDR_RN: operand bytes: [address, register]
+ if (opcode2 == MOV_RN_IMM) {
+ int reg = parseRegister(dest);
+ uint8_t imm = parseImmediate(src);
+ code[addr++] = reg;
+ code[addr++] = imm;
+ } else if (opcode2 == MOV_RN_RM) {
+ int regDest = parseRegister(dest);
+ int regSrc = parseRegister(src);
+ code[addr++] = regDest;
+ code[addr++] = regSrc;
+ } else if (opcode2 == MOV_RN_ADDR) {
+ // src is memory reference like "[123]"
+ int regDest = parseRegister(dest);
+ // Remove the brackets.
+ char addrStr[32];
+ strncpy(addrStr, src + 1, strlen(src) - 2);
+ addrStr[strlen(src) - 2] = '\0';
+ uint8_t memAddr = parseImmediate(addrStr);
+ code[addr++] = regDest;
+ code[addr++] = memAddr;
+ } else if (opcode2 == MOV_ADDR_RN) {
+ // dest is a memory reference, src is a register.
+ // Remove brackets from dest.
+ char addrStr[32];
+ strncpy(addrStr, dest + 1, strlen(dest) - 2);
+ addrStr[strlen(dest) - 2] = '\0';
+ uint8_t memAddr = parseImmediate(addrStr);
+ int regSrc = parseRegister(src);
+ code[addr++] = memAddr;
+ code[addr++] = regSrc;
+ }
+ } else if (strcmp(mnemonic, "ADD") == 0 ||
+ strcmp(mnemonic, "SUB") == 0 ||
+ strcmp(mnemonic, "MUL") == 0 ||
+ strcmp(mnemonic, "DIV") == 0 ||
+ strcmp(mnemonic, "MOD") == 0 ||
+ strcmp(mnemonic, "AND") == 0 ||
+ strcmp(mnemonic, "OR") == 0 ||
+ strcmp(mnemonic, "XOR") == 0) {
+ // ALU instructions with two operands.
+ char *dest = strtok(NULL, " ,");
+ char *src = strtok(NULL, " ,");
+ if (!dest || !src) {
+ fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
+ exit(1);
+ }
+ int baseOpcode;
+ if (strcmp(mnemonic, "ADD") == 0) baseOpcode = -3;
+ else if (strcmp(mnemonic, "SUB") == 0) baseOpcode = -4;
+ else if (strcmp(mnemonic, "MUL") == 0) baseOpcode = -5;
+ else if (strcmp(mnemonic, "DIV") == 0) baseOpcode = -6;
+ else if (strcmp(mnemonic, "MOD") == 0) baseOpcode = -7;
+ else if (strcmp(mnemonic, "AND") == 0) baseOpcode = -8;
+ else if (strcmp(mnemonic, "OR") == 0) baseOpcode = -9;
+ else if (strcmp(mnemonic, "XOR") == 0) baseOpcode = -10;
+ else baseOpcode = -1;
+ int opcode3 = resolveALU(baseOpcode, src);
+ code[addr++] = opcode3;
+ int regDest = parseRegister(dest);
+ code[addr++] = regDest;
+ // For a register source, encode the register; for an immediate, encode the value.
+ if (src[0] == 'R' || src[0] == 'r') {
+ int regSrc = parseRegister(src);
+ code[addr++] = regSrc;
+ } else {
+ uint8_t imm = parseImmediate(src);
+ code[addr++] = imm;
+ }
+ } else if (strcmp(mnemonic, "NEG") == 0 ||
+ strcmp(mnemonic, "SWAPN") == 0 ||
+ strcmp(mnemonic, "NOT") == 0) {
+ // One operand instructions.
+ char *op = strtok(NULL, " ,");
+ if (!op) {
+ fprintf(stderr, "Error: %s requires one operand.\n", mnemonic);
+ exit(1);
+ }
+ int opcode4 = getOpcode(mnemonic);
+ code[addr++] = opcode4;
+ int reg = parseRegister(op);
+ code[addr++] = reg;
+ } else if (strcmp(mnemonic, "SWAP") == 0 || strcmp(mnemonic, "CMP") == 0) {
+ // Two operand instructions: both registers.
+ char *op1 = strtok(NULL, " ,");
+ char *op2 = strtok(NULL, " ,");
+ if (!op1 || !op2) {
+ fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
+ exit(1);
+ }
+ int opcode5 = getOpcode(mnemonic);
+ code[addr++] = opcode5;
+ int r1 = parseRegister(op1);
+ int r2 = parseRegister(op2);
+ code[addr++] = r1;
+ code[addr++] = r2;
+ } else if (strcmp(mnemonic, "SHL") == 0 ||
+ strcmp(mnemonic, "SHR") == 0 ||
+ strcmp(mnemonic, "SAR") == 0 ||
+ strcmp(mnemonic, "SHRS") == 0) {
+ // Shift instructions: one register operand and one immediate.
+ char *regToken = strtok(NULL, " ,");
+ char *immToken = strtok(NULL, " ,");
+ if (!regToken || !immToken) {
+ fprintf(stderr, "Error: %s requires two operands.\n", mnemonic);
+ exit(1);
+ }
+ int opcode6 = getOpcode(mnemonic);
+ code[addr++] = opcode6;
+ int reg = parseRegister(regToken);
+ code[addr++] = reg;
+ uint8_t imm = parseImmediate(immToken);
+ code[addr++] = imm;
+ } else if (strcmp(mnemonic, "JMP") == 0 ||
+ strcmp(mnemonic, "JE") == 0 ||
+ strcmp(mnemonic, "JNE") == 0 ||
+ strcmp(mnemonic, "JG") == 0 ||
+ strcmp(mnemonic, "JL") == 0 ||
+ strcmp(mnemonic, "JGE") == 0 ||
+ strcmp(mnemonic, "JLE") == 0 ||
+ strcmp(mnemonic, "CALL") == 0) {
+ // Jump instructions: one operand which may be a label or an immediate address.
+ char *operand = strtok(NULL, " ,");
+ if (!operand) {
+ fprintf(stderr, "Error: %s requires an operand.\n", mnemonic);
+ exit(1);
+ }
+ int opcode7 = getOpcode(mnemonic);
+ code[addr++] = opcode7;
+ // If the operand is not a number, assume it is a label.
+ if (!isdigit(operand[0])) {
+ int labelAddr = lookupLabel(operand);
+ if (labelAddr < 0) {
+ fprintf(stderr, "Error: undefined label '%s'\n", operand);
+ exit(1);
+ }
+ code[addr++] = (uint8_t) labelAddr;
+ } else {
+ uint8_t imm = parseImmediate(operand);
+ code[addr++] = imm;
+ }
+ } else if (strcmp(mnemonic, "RET") == 0 ||
+ strcmp(mnemonic, "PUSHF") == 0 ||
+ strcmp(mnemonic, "POPF") == 0) {
+ // Instructions with no operand.
+ int opcode8 = getOpcode(mnemonic);
+ code[addr++] = opcode8;
+ } else if (strcmp(mnemonic, "PUSH") == 0 ||
+ strcmp(mnemonic, "POP") == 0) {
+ // One operand (a register)
+ char *regToken = strtok(NULL, " ,");
+ if (!regToken) {
+ fprintf(stderr, "Error: %s requires a register operand.\n", mnemonic);
+ exit(1);
+ }
+ int opcode9 = getOpcode(mnemonic);
+ code[addr++] = opcode9;
+ int reg = parseRegister(regToken);
+ code[addr++] = reg;
+ } else {
+ fprintf(stderr, "Error: Unknown instruction '%s'\n", mnemonic);
+ exit(1);
+ }
+ }
+ return addr;
+}
+
+void completePass(const char *input, CPU *cpu) {
+ // First pass: determine label addresses.
+ firstPass(input);
+
+ memset(cpu->memory, 0, MEM_SIZE);
+
+ // Second pass: generate machine code.
+ secondPass(input, cpu->memory);
+}
\ No newline at end of file
diff --git a/assembler/assembler.h b/assembler/assembler.h
new file mode 100644
index 0000000..350f934
--- /dev/null
+++ b/assembler/assembler.h
@@ -0,0 +1,87 @@
+//
+// Created by bruno on 1.2.2025.
+//
+
+#ifndef RISCB_ASSEMBLER_H
+#define RISCB_ASSEMBLER_H
+
+#include
+#include
+#include
+#include
+#include
+#include "../cpu/core.h"
+
+//
+// Definitions used by the CPU and the assembler
+//
+
+#define MAX_LINE_LENGTH 256
+#define MAX_LABELS 1024
+
+//
+// Label table entry
+//
+typedef struct {
+ char name[64];
+ int address; // address (in the output machine code)
+} Label;
+
+extern Label labels[MAX_LABELS];
+extern int labelCount;
+
+//
+// Helper functions for string manipulation
+//
+void trim(char *s);
+
+// 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);
+
+//
+// Parse a register string (e.g., "R0", "R1", etc.) and return it's number.
+// Returns -1 on error.
+int parseRegister(const char *token);
+
+// Parse an immediate value (supports decimal and 0x... hexadecimal)
+uint8_t parseImmediate(const char *token);
+
+//
+// Map an instruction mnemonic (string) to its opcode value and expected operand types.
+// For simplicity, we will return the opcode value and then in our parser we’ll decide how many operands to expect.
+// (In a full assembler you might use a more sophisticated data structure.)
+//
+int getOpcode(char *mnemonic);
+
+//
+// In this simple assembler, some instructions share a mnemonic, and we must choose the correct opcode
+// based on the type of the operand (register vs. immediate vs. memory).
+// The following helper functions decide that, given two operands (as strings).
+//
+// For example, "MOV Rn, 42" should choose MOV_RN_IMM, while "MOV Rn, Rm" should choose MOV_RN_RM.
+// We assume that memory addresses are written in square brackets, e.g. "[123]".
+//
+int resolveMOV(const char *dest, const char *src);
+
+int resolveALU(int baseOpcode, const char *src);
+
+//
+// The first pass scans the assembly source file to record all labels and their addresses.
+// 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);
+
+//
+// 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);
+
+
+#endif //RISCB_ASSEMBLER_H
diff --git a/cpu/core.c b/cpu/core.c
new file mode 100644
index 0000000..8b9a048
--- /dev/null
+++ b/cpu/core.c
@@ -0,0 +1,463 @@
+//
+// Created by bruno on 2.2.2025.
+//
+
+#include "core.h"
+#include "memory.h"
+#include "string.h"
+
+// Initialize CPU
+void init_cpu(CPU *cpu) {
+ memset(cpu, 0, sizeof(CPU));
+ cpu->sp = MEM_SIZE - 1; // Stack grows downward
+}
+
+// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
+// and bit1 is the Negative flag).
+static inline void set_flags(CPU *cpu, int32_t result) {
+ cpu->flags = 0;
+ if (result == 0)
+ cpu->flags |= 0x01; // Zero flag
+ if (result < 0)
+ cpu->flags |= 0x02; // Negative flag
+}
+
+// Execute a program (a byte array) on the given CPU.
+void step(CPU *cpu) {
+ if (cpu->mode < EverySecond) {
+ return;
+ }
+ if (cpu->pc >= MEM_SIZE) {
+ cpu->mode = Done; //terminate
+ }
+ uint8_t opcode = read_mem(cpu, cpu->pc++);
+ uint8_t reg1, reg2, imm;
+ uint32_t temp, newPC;
+ int32_t cmpResult;
+ switch (opcode) {
+ case NOP:
+ cpu->pc++;
+ break;
+
+ case BRK:
+ cpu->pc++;
+ cpu->mode = Paused;
+ break;
+
+ case INC_RN:
+ cpu->pc++;
+ reg1 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1]++;
+
+ case INC_ADDR:
+ cpu->pc++;
+ imm = read_mem(cpu, cpu->pc++);
+ write_mem(cpu, imm, read_mem(cpu, imm) + 1);
+
+ case DEC_RN:
+ cpu->pc++;
+ reg1 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1]--;
+
+ case DEC_ADDR:
+ cpu->pc++;
+ imm = read_mem(cpu, cpu->pc++);
+ write_mem(cpu, imm, read_mem(cpu, imm) - 1);
+
+ case MOV_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] = imm;
+ break;
+
+ case MOV_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] = cpu->regs[reg2];
+ break;
+
+ case MOV_RN_ADDR:
+ // Load from memory into register.
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] = read_mem(cpu, imm);
+ break;
+
+ case MOV_ADDR_RN:
+ // Store from register into memory.
+ imm = read_mem(cpu, cpu->pc++);
+ reg1 = read_mem(cpu, cpu->pc++);
+ write_mem(cpu, imm, cpu->regs[reg1]);
+ break;
+
+ case SWAP: {
+ // Swap contents of two registers.
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ temp = cpu->regs[reg1];
+ cpu->regs[reg1] = cpu->regs[reg2];
+ cpu->regs[reg2] = temp;
+ break;
+ }
+
+ case SWAPN: {
+ // Swap the nibbles of a register.
+ reg1 = read_mem(cpu, cpu->pc++);
+ uint8_t val = (uint8_t) cpu->regs[reg1];
+ cpu->regs[reg1] = ((val & 0x0F) << 4) | ((val & 0xF0) >> 4);
+ break;
+ }
+
+ case ADD_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] += cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case ADD_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] += imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case SUB_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] -= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case SUB_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] -= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case MUL_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] *= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case MUL_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] *= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case DIV_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ if (cpu->regs[reg2] == 0) {
+ printf("Error: Division by zero!\n");
+ cpu->mode = Error;
+ return;
+ }
+ cpu->regs[reg1] /= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case DIV_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ if (imm == 0) {
+ printf("Error: Division by zero!\n");
+ cpu->mode = Error;
+ return;
+ }
+ cpu->regs[reg1] /= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case MOD_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ if (cpu->regs[reg2] == 0) {
+ printf("Error: Modulo by zero!\n");
+ cpu->mode = Error;
+ return;
+ }
+ cpu->regs[reg1] %= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case MOD_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ if (imm == 0) {
+ printf("Error: Modulo by zero!\n");
+ cpu->mode = Error;
+ return;
+ }
+ cpu->regs[reg1] %= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case NEG_RN:
+ reg1 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] = -((int32_t) cpu->regs[reg1]);
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case AND_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] &= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case AND_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] &= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case OR_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] |= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case OR_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] |= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case XOR_RN_RM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] ^= cpu->regs[reg2];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case XOR_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] ^= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case NOT_RN:
+ reg1 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] = ~cpu->regs[reg1];
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case SHL_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] <<= imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case SHR_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] >>= imm; // Logical right shift (assuming unsigned value)
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case SAR_RN_IMM:
+ reg1 = read_mem(cpu, cpu->pc++);
+ imm = read_mem(cpu, cpu->pc++);
+ // Arithmetic right shift; cast to signed before shifting.
+ cpu->regs[reg1] = ((int32_t) cpu->regs[reg1]) >> imm;
+ set_flags(cpu, cpu->regs[reg1]);
+ break;
+
+ case JMP:
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ cpu->pc = newPC;
+ break;
+
+ case JMP_REL:
+ imm = (int32_t) read_mem(cpu, cpu->pc++);
+ cpu->pc += imm;
+ break;
+
+ case CMP: {
+ // Compare two registers: set flags (Zero, Negative)
+ reg1 = read_mem(cpu, cpu->pc++);
+ reg2 = read_mem(cpu, cpu->pc++);
+ cmpResult = (int32_t) cpu->regs[reg1] - (int32_t) cpu->regs[reg2];
+ set_flags(cpu, cmpResult);
+ break;
+ }
+
+ case JE_BIT_RN: {
+ // Jump if bit in register set
+ reg1 = read_mem(cpu, cpu->pc++);
+ if (reg1 >= REG_COUNT) {
+ reg1 = REG_COUNT - 1;
+ }
+ uint8_t bit = read_mem(cpu, cpu->pc++);
+ if (bit > 7) {
+ bit = 7;
+ }
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (cpu->regs[reg1] & (1 << bit))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JE_BIT_ADDR: {
+ // Jump if bit in register set
+ temp = read_mem32(cpu, cpu->pc);
+ if (temp >= MEM_SIZE) {
+ temp = MEM_SIZE - 1;
+ }
+ uint8_t bit = read_mem(cpu, cpu->pc++);
+ if (bit > 7) {
+ bit = 7;
+ }
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (cpu->memory[temp] & (1 << bit))
+ cpu->pc = newPC;
+ break;
+ }
+
+
+ case JE: {
+ // Jump if equal (Zero flag set)
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (cpu->flags & 0x01)
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JNE_BIT_RN: {
+ // Jump if bit in register set
+ reg1 = read_mem(cpu, cpu->pc++);
+ if (reg1 >= REG_COUNT) {
+ reg1 = REG_COUNT - 1;
+ }
+ uint8_t bit = read_mem(cpu, cpu->pc++);
+ if (bit > 7) {
+ bit = 7;
+ }
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (!(cpu->regs[reg1] & (1 << bit)))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JNE_BIT_ADDR: {
+ // Jump if bit in register set
+ temp = read_mem32(cpu, cpu->pc);
+ if (temp >= MEM_SIZE) {
+ temp = MEM_SIZE - 1;
+ }
+ uint8_t bit = read_mem(cpu, cpu->pc++);
+ if (bit > 7) {
+ bit = 7;
+ }
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (!(cpu->memory[temp] & (1 << bit)))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JNE: {
+ // Jump if not equal (Zero flag clear)
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (!(cpu->flags & 0x01))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JG: {
+ // Jump if greater: not negative and not zero.
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (((cpu->flags & 0x02) == 0) && ((cpu->flags & 0x01) == 0))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JL: {
+ // Jump if less: Negative flag set.
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if (cpu->flags & 0x02)
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JGE: {
+ // Jump if greater or equal: Zero flag set or negative clear.
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if ((cpu->flags & 0x01) || ((cpu->flags & 0x02) == 0))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case JLE: {
+ // Jump if less or equal: Negative flag set or Zero flag set.
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ if ((cpu->flags & 0x02) || (cpu->flags & 0x01))
+ cpu->pc = newPC;
+ break;
+ }
+
+ case CALL: {
+ // Push the current PC onto the stack, then jump to the address.
+ newPC = read_mem32(cpu, cpu->pc);
+ cpu->pc += 4;
+ // Push return address (current PC) onto the stack.
+ write_mem(cpu, cpu->sp--, (uint8_t) (cpu->pc & 0xFF));
+ cpu->pc = newPC;
+ break;
+ }
+
+ case RET:
+ // For RET, assume that the return address was stored on the stack.
+ cpu->pc = read_mem(cpu, ++cpu->sp);
+ break;
+
+ case PUSH: {
+ // Push register value onto the stack.
+ reg1 = read_mem(cpu, cpu->pc++);
+ write_mem(cpu, cpu->sp--, (uint8_t) (cpu->regs[reg1] & 0xFF));
+ break;
+ }
+
+ case POP: {
+ // Pop a value from the stack into a register.
+ reg1 = read_mem(cpu, cpu->pc++);
+ cpu->regs[reg1] = read_mem(cpu, ++cpu->sp);
+ break;
+ }
+
+ case PUSHF:
+ // Push the flags register.
+ write_mem(cpu, cpu->sp--, cpu->flags);
+ break;
+
+ case POPF:
+ // Pop into the flags register.
+ cpu->flags = read_mem(cpu, ++cpu->sp);
+ break;
+
+ default:
+ printf("Unknown opcode: %d\n", opcode);
+ cpu->mode = Error;
+ }
+}
\ No newline at end of file
diff --git a/cpu/core.h b/cpu/core.h
new file mode 100644
index 0000000..6fa30cb
--- /dev/null
+++ b/cpu/core.h
@@ -0,0 +1,132 @@
+//
+// Created by bruno on 2.2.2025.
+//
+
+#ifndef RISCB_CORE_H
+#define RISCB_CORE_H
+
+#include
+#include "stdio.h"
+
+enum Mode {
+ Paused,
+ Done,
+ Error,
+ EverySecond,
+ EveryFrame,
+ MaxSpeed
+};
+
+#define MEM_SIZE 8192
+// Register count (register names R0 to R7)
+#define REG_COUNT 32
+
+// CPU state
+typedef struct {
+ uint32_t regs[REG_COUNT];
+ uint8_t memory[MEM_SIZE];
+ uint32_t pc; // Program counter
+ uint32_t sp; // Stack pointer
+ uint8_t flags; // Status flags
+ enum Mode mode;
+} CPU;
+
+void step(CPU *cpu);
+
+void init_cpu(CPU *cpu);
+
+// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
+// and bit1 is the Negative flag).
+static inline void set_flags(CPU *cpu, int32_t result);
+
+// Opcode definitions (should match the CPU’s enum)
+typedef enum {
+ NOP, // NOP - No operation (does nothing, advances to next instruction)
+
+ BRK, // BRK - Pause CPU (halts execution until resumed)
+
+ INC_RN, //INC Rn - Increment register
+ INC_ADDR, //INC [Addr] - Increment address
+
+ DEC_RN, //DEC Rn - Increment register
+ DEC_ADDR, //DEC [Addr] - Increment address
+
+ MOV_RN_IMM, // MOV Rn, Imm - Move immediate to register (Rn = Imm)
+ MOV_RN_RM, // MOV Rn, Rm - Move value from one register to another (Rn = Rm)
+ MOV_RN_ADDR, // MOV Rn, [Addr] - Load value from memory address into register (Rn = [Addr])
+ MOV_ADDR_RN, // MOV [Addr], Rn - Store register value into memory address ([Addr] = Rn)
+
+ SWAP, // SWAP Rn, Rm - Swap values between two registers (Rn <-> Rm)
+
+ SWAPN, // SWAPN Rn - Swap nibbles within a register (Rn high/low nibbles swapped)
+
+ ADD_RN_RM, // ADD Rn, Rm - Add values of two registers (Rn = Rn + Rm)
+ ADD_RN_IMM, // ADD Rn, Imm - Add immediate value to register (Rn = Rn + Imm)
+
+ SUB_RN_RM, // SUB Rn, Rm - Subtract one register from another (Rn = Rn - Rm)
+ SUB_RN_IMM, // SUB Rn, Imm - Subtract immediate value from register (Rn = Rn - Imm)
+
+ MUL_RN_RM, // MUL Rn, Rm - Multiply two registers (Rn = Rn * Rm)
+ MUL_RN_IMM, // MUL Rn, Imm - Multiply register by immediate (Rn = Rn * Imm)
+
+ DIV_RN_RM, // DIV Rn, Rm - Divide one register by another (Rn = Rn / Rm)
+ DIV_RN_IMM, // DIV Rn, Imm - Divide register by immediate (Rn = Rn / Imm)
+
+ MOD_RN_RM, // MOD Rn, Rm - Compute remainder of division (Rn = Rn % Rm)
+ MOD_RN_IMM, // MOD Rn, Imm - Compute remainder using immediate (Rn = Rn % Imm)
+
+ NEG_RN, // NEG Rn - Negate register value (Rn = -Rn)
+
+ AND_RN_RM, // AND Rn, Rm - Bitwise AND two registers (Rn = Rn & Rm)
+ AND_RN_IMM, // AND Rn, Imm - Bitwise AND register with immediate (Rn = Rn & Imm)
+
+ OR_RN_RM, // OR Rn, Rm - Bitwise OR two registers (Rn = Rn | Rm)
+ OR_RN_IMM, // OR Rn, Imm - Bitwise OR register with immediate (Rn = Rn | Imm)
+
+ XOR_RN_RM, // XOR Rn, Rm - Bitwise XOR two registers (Rn = Rn ^ Rm)
+ XOR_RN_IMM, // XOR Rn, Imm - Bitwise XOR register with immediate (Rn = Rn ^ Imm)
+
+ NOT_RN, // NOT Rn - Bitwise NOT (Rn = ~Rn)
+
+ SHL_RN_IMM, // SHL Rn, Imm - Logical shift left (Rn = Rn << Imm)
+
+ SHR_RN_IMM, // SHR Rn, Imm - Logical shift right (Rn = Rn >> Imm)
+
+ SAR_RN_IMM, // SAR Rn, Imm - Arithmetic shift right (Rn = Rn >> Imm with sign extension)
+
+ JMP, // JMP Addr - Jump to address (PC = Addr)
+
+ JMP_REL, // JMP Offset - relative jump (offset is signed)
+
+ CMP, // CMP Rn, Rm - Compare two registers (sets flags based on Rn - Rm)
+
+ JE, // JE Addr - Jump if equal (if zero flag set, PC = Addr)
+ JE_BIT_RN, // jump relative if a given bit in a register is set
+ JE_BIT_ADDR, // jump relative if a given bit in memory is set
+
+ JNE, // JNE Addr - Jump if not equal (if zero flag not set, PC = Addr)
+ JNE_BIT_RN, // jump relative if a given bit in a register is not set
+ JNE_BIT_ADDR, // jump relative if a given bit in memory is not set
+
+ JG, // JG Addr - Jump if greater (if greater flag set, PC = Addr)
+
+ JL, // JL Addr - Jump if less (if less flag set, PC = Addr)
+
+ JGE, // JGE Addr - Jump if greater or equal (if greater or zero flag set, PC = Addr)
+
+ JLE, // JLE Addr - Jump if less or equal (if less or zero flag set, PC = Addr)
+
+ CALL, // CALL Addr - Call subroutine (push PC to stack, PC = Addr)
+
+ RET, // RET - Return from subroutine (pop PC from stack)
+
+ PUSH, // PUSH Rn - Push register onto stack (stack[top] = Rn)
+
+ POP, // POP Rn - Pop value from stack into register (Rn = stack[top])
+
+ PUSHF, // PUSHF - Push flags onto stack (stack[top] = flags)
+
+ POPF // POPF - Pop flags from stack (flags = stack[top])
+} Opcode;
+
+#endif //RISCB_CORE_H
diff --git a/cpu/memory.c b/cpu/memory.c
new file mode 100644
index 0000000..aed42c1
--- /dev/null
+++ b/cpu/memory.c
@@ -0,0 +1,62 @@
+//
+// Created by bruno on 2.2.2025.
+//
+
+#include "memory.h"
+
+uint8_t write_mem(CPU *cpu, uint32_t addr, uint8_t value) {
+ if (addr >= MEM_SIZE) {
+ return 1;
+ }
+
+ switch (addr) {
+
+ default:
+ cpu->memory[addr] = value;
+ }
+
+ return 0;
+}
+
+uint8_t read_mem(CPU *cpu, uint32_t addr) {
+ if (addr >= MEM_SIZE) {
+ return 0;
+ }
+ switch (addr) {
+
+ default:
+ return cpu->memory[addr];
+ }
+}
+
+uint16_t read_mem16(CPU *cpu, uint32_t addr) {
+ return read_mem(cpu, addr) | (read_mem(cpu, addr + 1) << 8);
+}
+
+uint32_t read_mem32(CPU *cpu, uint32_t addr) {
+ return read_mem16(cpu, addr) | (read_mem16(cpu, addr + 2) << 16);
+}
+
+uint8_t write_mem16(CPU *cpu, uint32_t addr, uint16_t value) {
+ uint8_t status = write_mem(cpu, addr, value & 0xFF);
+ if (status) {
+ return status;
+ }
+ status = write_mem(cpu, addr + 1, (value >> 8) & 0xFF);
+ if (status) {
+ return status;
+ }
+ return 0;
+}
+
+uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value) {
+ uint8_t status = write_mem16(cpu, addr, value & 0xFFFF);
+ if (status) {
+ return status;
+ }
+ status = write_mem16(cpu, addr + 2, (value >> 16) & 0xFFFF);
+ if (status) {
+ return status;
+ }
+ return 0;
+}
diff --git a/cpu/memory.h b/cpu/memory.h
new file mode 100644
index 0000000..9655b78
--- /dev/null
+++ b/cpu/memory.h
@@ -0,0 +1,24 @@
+//
+// Created by bruno on 2.2.2025.
+//
+
+#ifndef RISCB_MEMORY_H
+#define RISCB_MEMORY_H
+
+#include
+#include "../cpu/core.h"
+
+uint8_t write_mem32(CPU *cpu, uint32_t addr, uint32_t value);
+
+uint8_t write_mem16(CPU *cpu, uint32_t addr, uint16_t value);
+
+uint32_t read_mem32(CPU *cpu, uint32_t addr);
+
+uint16_t read_mem16(CPU *cpu, uint32_t addr);
+
+uint8_t read_mem(CPU *cpu, uint32_t addr);
+
+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 3ec13a6..f779d65 100644
--- a/main.c
+++ b/main.c
@@ -1,55 +1,32 @@
#include
-#include
#include
-#include
+#include
+#include "util/font.h"
+#include "assembler/assembler.h"
//Screen dimension constants
-const int SCREEN_WIDTH = 640;
-const int SCREEN_HEIGHT = 480;
+const int SCREEN_WIDTH = 1280;
+const int SCREEN_HEIGHT = 720;
+const int targetFPS = 60;
+const int delayNeeded = 1000 / targetFPS;
-void renderText(SDL_Renderer *renderer, TTF_Font *font, char *string, uint16_t x, uint16_t y, uint8_t r, uint8_t g,
- uint8_t b, uint8_t a) {
- SDL_Texture *fontTempTex;
- SDL_Surface *fontTempSurf;
- SDL_Color color = {r, g, b, a};
- fontTempSurf = TTF_RenderText_Blended(font, string, color);
- fontTempTex = SDL_CreateTextureFromSurface(renderer, fontTempSurf);
+//The window we'll be rendering to
+SDL_Window *window = NULL;
- int iW, iH;
- SDL_QueryTexture(fontTempTex, NULL, NULL, &iW, &iH);
+//The surface contained by the window
+SDL_Renderer *renderer = NULL;
- SDL_Rect srcRect;
- srcRect.x = 0;
- srcRect.y = 0;
- srcRect.w = iW;
- srcRect.h = iH;
+SDL_Rect rect1;
- SDL_Rect dstRect;
- dstRect.x = x;
- dstRect.y = y;
- dstRect.w = iW;
- dstRect.h = iH;
+BitmapFont smallFont;
- SDL_RenderCopy(renderer, fontTempTex, &srcRect, &dstRect);
-
- SDL_FreeSurface(fontTempSurf);
- SDL_DestroyTexture(fontTempTex);
-}
-
-int main(int argc, char *args[]) {
- //The window we'll be rendering to
- SDL_Window *window = NULL;
-
- //The surface contained by the window
- SDL_Renderer *renderer = NULL;
-
- SDL_Rect rect1;
-
- TTF_Font *gFont;
+CPU cpu;
+char programString[65535];
+int init() {
//Initialize SDL
- if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
return 1;
}
@@ -60,55 +37,98 @@ int main(int argc, char *args[]) {
return 1;
}
- gFont = TTF_OpenFont("../rasterthingy.ttf", 50);
- if (gFont == NULL) {
- printf("Failed to load lazy font! SDL_ttf Error: %s\n", TTF_GetError());
- return 1;
- }
//Create window
- window = SDL_CreateWindow("SDLko", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
- SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
+ window = SDL_CreateWindow("SDLko", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
+ SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (window == NULL) {
printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
return 1;
}
//Get window surface
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;
+ }
+ smallFont = prepText(renderer, 10, "../PublicPixel.ttf", 255, 255, 255, 255);
+ SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0);
+ init_cpu(&cpu);
+
+ return 0;
+}
+
+int render() {
+ SDL_SetRenderDrawColor(renderer, 128, 0, 0, 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);
+
+ char textTemp[12];
+ sprintf(textTemp, "%d", rect1.x);
+
+ renderText(renderer, smallFont, textTemp, 100, 100);
+
+ SDL_RenderPresent(renderer);
+ return 0;
+}
+
+int processEvent(SDL_Event e) {
+ if (e.type == SDL_QUIT) { return 0; }
+ else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_RESIZED) {
+ int newWidth = e.window.data1;
+ int newHeight = e.window.data2;
+
+ // Adjust the viewport to match the new window size
+ SDL_Rect viewport = {0, 0, newWidth, newHeight};
+ SDL_RenderSetViewport(renderer, &viewport);
+ }
+ return 1;
+}
+
+int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) {
+ int status = init();
+ if (status) {
+ return status;
+ }
//Hack to get window to stay up
SDL_Event e;
- bool quit = false;
+ bool running = true;
Uint64 start;
Uint64 end;
- while (!quit) {
+ while (running) {
start = SDL_GetTicks64();
while (SDL_PollEvent(&e)) {
- if (e.type == SDL_QUIT) quit = true;
+ running = processEvent(e);
}
+ status = render();
+ if (status) {
+ return status;
+ }
- SDL_SetRenderDrawColor(renderer, 128, 0, 0, 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);
-
- char textTemp[12];
- sprintf(textTemp, "%d", rect1.x);
-
- renderText(renderer, gFont, textTemp, 100, 100, 255, 255, 255, 255);
-
- SDL_RenderPresent(renderer);
end = SDL_GetTicks64();
- SDL_Delay((1000 / 60) - (end - start));
+ const unsigned long timeNeeded = end - start;
+ if (timeNeeded < delayNeeded) {
+ SDL_Delay(delayNeeded - timeNeeded);
+ } else {
+ printf("%lu", timeNeeded);
+ }
}
- TTF_CloseFont(gFont);
+ uint8_t *program;
+ int program_size;
+
+ completePass(programString, &cpu);
+
+ step(&cpu);
//Destroy window
SDL_DestroyWindow(window);
diff --git a/rasterthingy.ttf b/rasterthingy.ttf
deleted file mode 100644
index 8d80982..0000000
Binary files a/rasterthingy.ttf and /dev/null differ
diff --git a/util/font.c b/util/font.c
new file mode 100644
index 0000000..1c07f55
--- /dev/null
+++ b/util/font.c
@@ -0,0 +1,49 @@
+//
+// Created by bruno on 1.2.2025.
+//
+
+#include "font.h"
+
+BitmapFont
+prepText(SDL_Renderer *renderer, unsigned char pxSize, const char *file, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ const unsigned char ptSize = floor(pxSize * 0.75);
+ TTF_Font *gFont = TTF_OpenFont(file, ptSize);
+ BitmapFont out;
+ out.size = pxSize;
+ out.color = (SDL_Color) {r, g, b, a};
+ unsigned char i = 0;
+ SDL_Surface *fontTempSurf = NULL;
+ do {
+ char tmpOut[2] = {i, 0};
+
+ fontTempSurf = TTF_RenderText_Solid(gFont, tmpOut, out.color);
+ SDL_Rect dstRect;
+ dstRect.x = 0;
+ dstRect.y = 0;
+ dstRect.w = pxSize;
+ dstRect.h = pxSize;
+ out.texture[i] = SDL_CreateTextureFromSurface(renderer, fontTempSurf);
+ i++;
+ } while (i < 255);
+
+ SDL_FreeSurface(fontTempSurf);
+ TTF_CloseFont(gFont);
+ return out;
+}
+
+void renderText(SDL_Renderer *renderer, BitmapFont font, char *string, uint16_t x, uint16_t y) {
+ SDL_Rect charRect;
+ charRect.x = 0;
+ charRect.y = 0;
+ charRect.w = font.size;
+ charRect.h = font.size;
+ SDL_Rect outRect = charRect;
+ outRect.x = x;
+ outRect.y = y;
+
+ while (*string) {
+ SDL_RenderCopy(renderer, font.texture[*string], &charRect, &outRect);
+ outRect.x += charRect.w + 1;
+ string++;
+ }
+}
\ No newline at end of file
diff --git a/util/font.h b/util/font.h
new file mode 100644
index 0000000..93f57b1
--- /dev/null
+++ b/util/font.h
@@ -0,0 +1,23 @@
+//
+// Created by bruno on 1.2.2025.
+//
+
+#ifndef RISCB_FONT_H
+#define RISCB_FONT_H
+
+#include
+#include
+#include
+
+typedef struct {
+ SDL_Texture *texture[256];
+ uint8_t size;
+ SDL_Color color;
+} BitmapFont;
+
+BitmapFont
+prepText(SDL_Renderer *renderer, unsigned char pxSize, const char *file, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+
+void renderText(SDL_Renderer *renderer, BitmapFont font, char *string, uint16_t x, uint16_t y);
+
+#endif //RISCB_FONT_H