// // 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]; uint32_t 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); void toUpperCase(char *string); void toLowerCase(char *string); // Look up a label by name; returns -1 if not found. int lookupLabel(const char *name); // Add a label to the table int addLabel(const char *name, uint32_t 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_IMM_RN, 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). // 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.) // uint32_t completePass(const char *input, CPU *cpu, bool erase); #endif //RISCB_ASSEMBLER_H