53 lines
2.8 KiB
C
53 lines
2.8 KiB
C
|
|
#define _GNU_SOURCE
|
||
|
|
#include "assembler.h"
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <strings.h>
|
||
|
|
#include <ctype.h>
|
||
|
|
typedef struct { const char *mnemonic; uint8_t family_id; uint8_t sub_id; uint8_t opclass; ExtKind ext_kind; } MnemonicEntry;
|
||
|
|
static const MnemonicEntry MNEMONICS[] = {
|
||
|
|
{"REGION_NEW", 4, 0, 0, EXT_STORE}, {"REGION_FILL_ASC", 5, 2, 0, EXT_NONE}, {"REGION_FILL_NOISE", 5, 3, 0, EXT_NONE}, {"REGION_FILL_CONST", 5, 4, 0, EXT_NONE},
|
||
|
|
{"ADD", 8, 0, 1, EXT_NONE}, {"HALT", 20, 0, 2, EXT_NONE}, {NULL, 0, 0, 0, EXT_NONE}
|
||
|
|
};
|
||
|
|
void asm_init(Assembler *as) { memset(as, 0, sizeof(Assembler)); }
|
||
|
|
static const MnemonicEntry *lookup_mnemonic(const char *name) { for (int i = 0; MNEMONICS[i].mnemonic != NULL; i++) if (strcasecmp(name, MNEMONICS[i].mnemonic) == 0) return &MNEMONICS[i]; return NULL; }
|
||
|
|
static int parse_operand(Assembler *as, const char *tok, uint8_t *out) {
|
||
|
|
if (tok[0] == '%' && (tok[1] == 'r' || tok[1] == 'R')) { *out = (uint8_t)atoi(tok + 2); return 0; }
|
||
|
|
*out = (uint8_t)(atoi(tok) & 0xFF); return 0;
|
||
|
|
}
|
||
|
|
static void process_line(Assembler *as, char *line) {
|
||
|
|
char *comment = strchr(line, ';'); if (comment) *comment = '\0';
|
||
|
|
while (*line && isspace(*line)) line++; if (*line == '\0') return;
|
||
|
|
char *tokens[MAX_TOKENS]; int ntokens = 0; char *save;
|
||
|
|
char *tok = strtok_r(line, " \t,\n\r", &save);
|
||
|
|
while (tok && ntokens < MAX_TOKENS) { tokens[ntokens++] = tok; tok = strtok_r(NULL, " \t,\n\r", &save); }
|
||
|
|
if (ntokens == 0) return;
|
||
|
|
char mnemonic[64]; strncpy(mnemonic, tokens[0], sizeof(mnemonic) - 1); mnemonic[63] = '\0';
|
||
|
|
const MnemonicEntry *entry = lookup_mnemonic(mnemonic);
|
||
|
|
if (!entry) { if (as->pass == 1) fprintf(stderr, "Unknown mnemonic: %s\n", mnemonic); return; }
|
||
|
|
uint8_t op_a = 0, op_b = 0;
|
||
|
|
if (ntokens >= 2) parse_operand(as, tokens[1], &op_a);
|
||
|
|
if (ntokens >= 3) parse_operand(as, tokens[2], &op_b);
|
||
|
|
uint32_t word = glyph_encode(entry->family_id, entry->sub_id, MODE_USER, entry->opclass, glyph_encode_ops(op_a, op_b));
|
||
|
|
if (as->pass == 1 && as->code_len < MAX_CODE_SIZE) as->code[as->code_len] = word;
|
||
|
|
as->code_len++;
|
||
|
|
}
|
||
|
|
int asm_assemble(Assembler *as, const char *path) {
|
||
|
|
as->filename = path;
|
||
|
|
for (int pass = 0; pass <= 1; pass++) {
|
||
|
|
as->pass = pass; if (pass == 1) as->code_len = 0;
|
||
|
|
FILE *f = fopen(path, "r"); if (!f) return -1;
|
||
|
|
char line[MAX_LINE]; as->line_num = 0;
|
||
|
|
while (fgets(line, sizeof(line), f)) { as->line_num++; process_line(as, line); }
|
||
|
|
fclose(f);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
int asm_write_gobj(Assembler *as, const char *path) {
|
||
|
|
FILE *f = fopen(path, "wb"); if (!f) return -1;
|
||
|
|
GobjHeader hdr; memcpy(hdr.magic, GOBJ_MAGIC, 8); hdr.version = GOBJ_VERSION; hdr.code_len = as->code_len; hdr.ext_len = as->ext_len;
|
||
|
|
fwrite(&hdr, sizeof(GobjHeader), 1, f); fwrite(as->code, sizeof(uint32_t), as->code_len, f);
|
||
|
|
fclose(f); return 0;
|
||
|
|
}
|