Do some work on the CPU, assembler still needs update

This commit is contained in:
2025-02-02 21:48:35 +01:00
parent b7e5e2aa35
commit 0a0f953f09
15 changed files with 1473 additions and 71 deletions

87
assembler/assembler.h Normal file
View File

@@ -0,0 +1,87 @@
//
// Created by bruno on 1.2.2025.
//
#ifndef RISCB_ASSEMBLER_H
#define RISCB_ASSEMBLER_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#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 well 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