/* * Copyright (c) 2022 Marcus Glocker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * 10.02.2022: Version 0.1 */ #include #include #include #include #include #include #include /* * *** Global variables and definitions **************************************** */ #define MACHINE_MEM_SIZE 16 #define LINE_MAX_LEN 80 #define MNEMONIC_NUM 11 #define MNEMONIC_MAX_LEN 3 #define ARGUMENT_MAX_LEN 3 #define DATA_NUM 1 #define DATA_MAX_LEN 5 #define ARGUMENT_MAX_LEN 3 static const struct { char name[MNEMONIC_MAX_LEN + 1]; uint8_t arg_required; uint8_t arg_max_size; uint8_t opcode; } mnemonics[] = { { "NOP", 0, 0x00, 0x00 }, { "LDA", 1, 0x0f, 0x01 }, { "ADD", 1, 0x0f, 0x02 }, { "SUB", 1, 0x0f, 0x03 }, { "STA", 1, 0x0f, 0x04 }, { "LDI", 1, 0x0f, 0x05 }, { "JMP", 1, 0x0f, 0x06 }, { "JC", 1, 0x0f, 0x07 }, { "JZ", 1, 0x0f, 0x08 }, { "OUT", 0, 0x00, 0x0e }, { "HLT", 0, 0x00, 0x0f } }; static const struct { char name[DATA_MAX_LEN + 1]; uint8_t arg_max_size; } sectors[] = { { ".byte", 0xff } }; struct parsed_code { char name[MNEMONIC_MAX_LEN + 1]; int arg; }; int debug = 0; int line_num = 1; int mem_addr = 0; int pos = 0; struct parsed_code pc[MACHINE_MEM_SIZE]; /* * *** Function prototypes ***************************************************** */ void usage(void); void debug_printf(const char *, ...); int fetch_line(FILE *, char *, const int); int parse_file(FILE *); int parse_line_start(char *); int parse_line_mnemonic(char *); int parse_line_data(char *); int parse_line_end(char *); int lookup_mnemonic(char *); int lookup_data(char *); int convert_mnemonic_arg(char *, int, int *); int convert_data_arg(char *, int, int *); void init_code(void); void print_code(void); int write_opcode(FILE *); /* * *** Functions *************************************************************** */ void usage(void) { extern char *__progname; printf("usage: %s [-o output_file] [input_file]\n", __progname); exit(1); } void debug_printf(const char *string, ...) { va_list ap; if (debug == 0) return; va_start(ap, string); vprintf(string, ap); va_end(ap); } int fetch_line(FILE *file, char *buf, const int buf_size) { char *line = NULL; size_t line_size = 0; ssize_t line_len; line_len = getline(&line, &line_size, file); if (line_len == -1) return 0; if (line_len > LINE_MAX_LEN + 1) { free(line); printf("Line %d: Exceeded %d characters\n", line_num, LINE_MAX_LEN); return -1; } line[line_len - 1] = 0x00; strlcpy(buf, line, buf_size); free(line); return line_len - 1; } int parse_file(FILE *file) { int r = 0; int line_type; char line[LINE_MAX_LEN + 2]; while ((r = fetch_line(file, line, sizeof(line))) > 0) { line_type = parse_line_start(line); switch (line_type) { case 0: debug_printf("*** Mnemonic\n"); if (parse_line_mnemonic(line) == -1) return -1; mem_addr++; if (mem_addr > MACHINE_MEM_SIZE) { printf("Machine memory full!\n"); return -1; } if (parse_line_end(line) == -1) return -1; line_num++; break; case 1: debug_printf("*** Data\n"); if (parse_line_data(line) == -1) return -1; mem_addr++; if (mem_addr > MACHINE_MEM_SIZE) { printf("Machine memory full!\n"); return -1; } if (parse_line_end(line) == -1) return -1; line_num++; break; case 2: debug_printf("*** Single line comment\n"); line_num++; break; default: /* Can't be reached! */ printf("The universe is falling apart\n"); break; } } return r; } int parse_line_start(char *line) { char c; /* skip leading spaces and tabs */ for (pos = 0; line[pos] != 0x00; pos++) { c = line[pos]; if (c != 0x20 && c != 0x09) break; } c = line[pos]; switch (c) { case 0x3b: /* ';' */ /* Single line comment */ return 2; case 0x2e: /* '.' */ /* Data */ return 1; default: /* Mnemonic */ return 0; } } int parse_line_mnemonic(char *line) { int i; int index, num = 0; char c; char mnemonic[MNEMONIC_MAX_LEN + 1]; char argument[ARGUMENT_MAX_LEN + 1]; /* parse mnemonic */ memset(mnemonic, 0, sizeof(mnemonic)); for (i = 0; line[pos] != 0x00; i++, pos++) { c = line[pos]; if (c == 0x20 || c == 0x09) break; if (i == MNEMONIC_MAX_LEN) break; mnemonic[i] = c; } debug_printf("mnemonic=%s\n", mnemonic); /* verify mnemonic */ index = lookup_mnemonic(mnemonic); if (index == -1) { printf("Line %d: '%s' isn't a invalid instruction\n", line_num, mnemonic); return -1; } /* verify mnemonic argument */ if (mnemonics[index].arg_required) { if (line[pos] != 0x20) { printf("Line %d: mnemonic '%s' requires an argument\n", line_num, mnemonic); return -1; } /* skip spaces and tabs between mnemonic and argument */ for (; line[pos] != 0x00; pos++) { c = line[pos]; if (c != 0x20 && c != 0x09) break; } memset(argument, 0, sizeof(argument)); for (i = 0; line[pos] != 0x00; i++, pos++) { c = line[pos]; if (c == 0x20 || c == 0x09) break; if (i == ARGUMENT_MAX_LEN) break; argument[i] = c; } debug_printf("argument=%s\n", argument); if (convert_mnemonic_arg(argument, index, &num) == -1) { printf("Line %d: argument '%d' is too large\n", line_num, num); return -1; } pc[mem_addr].arg = num; } strlcpy(pc[mem_addr].name, mnemonics[index].name, MNEMONIC_MAX_LEN + 1); return 0; } int parse_line_data(char *line) { int i; int index, num = 0; char c; char data[DATA_MAX_LEN + 1]; char argument[ARGUMENT_MAX_LEN + 1]; /* parse data */ memset(data, 0, sizeof(data)); for (i = 0; line[pos] != 0x00; i++, pos++) { c = line[pos]; if (c == 0x20 || c == 0x09) break; if (i == DATA_MAX_LEN) break; data[i] = c; } debug_printf("data=%s\n", data); /* verify data */ index = lookup_data(data); if (index == -1) { printf("Line %d: data type '%s' is invalid\n", line_num, data); return -1; } /* verify mnemonic argument */ if (line[pos] != 0x20) { printf("Line %d: data type '%s' requires an argument\n", line_num, data); return -1; } /* skip spaces and tabs between data and argument */ for (; line[pos] != 0x00; pos++) { c = line[pos]; if (c != 0x20 && c != 0x09) break; } memset(argument, 0, sizeof(argument)); for (i = 0; line[pos] != 0x00; i++, pos++) { c = line[pos]; if (c == 0x20 || c == 0x09) break; if (i == ARGUMENT_MAX_LEN) break; argument[i] = c; } debug_printf("argument=%s\n", argument); if (convert_data_arg(argument, index, &num) == -1) { printf("Line %d: argument '%d' is too large\n", line_num, num); return -1; } pc[mem_addr].arg = num; return 0; } int parse_line_end(char *line) { char c; /* skip trailing spaces and tabs */ for (; line[pos] != 0x00; pos++) { c = line[pos]; if (c != 0x20 && c != 0x09) break; } /* check for instruction comment */ if (line[pos] != 0x00 && line[pos] != 0x3b) { printf("Line %d: Garbage after instruction found\n", line_num); return -1; } return 0; } int lookup_mnemonic(char *mnemonic) { int i; for (i = 0; i < MNEMONIC_NUM; i++) { if (strcasecmp(mnemonic, mnemonics[i].name) == 0) return i; } return -1; } int lookup_data(char *sector) { int i; for (i = 0; i < DATA_NUM; i++) { if (strcasecmp(sector, sectors[i].name) == 0) return i; } return -1; } int convert_mnemonic_arg(char *arg, int index, int *num) { long n; char *ep; n = strtol(arg, &ep, 10); if (n == -1) return -1; *num = n; if (n > mnemonics[index].arg_max_size) return -1; return 0; } int convert_data_arg(char *arg, int index, int *num) { long n; char *ep; n = strtol(arg, &ep, 10); if (n == -1) return -1; *num = n; if (n > sectors[index].arg_max_size) return -1; return 0; } void init_code(void) { int i; for (i = 0; i < MACHINE_MEM_SIZE; i++) { memset(pc[i].name, 0, sizeof(pc[i].name)); pc[i].arg = -1; } } void print_code(void) { int i; for (i = 0; i < MACHINE_MEM_SIZE; i++) { if (pc[i].name[0] == 0 && pc[i].arg == -1) break; printf("%d: ", i); if (pc[i].name[0] != 0) printf("%s ", pc[i].name); if (pc[i].arg != -1) printf("%d", pc[i].arg); printf("\n"); } } int write_opcode(FILE *file) { int i; int index; uint8_t byte = 0x00, high = 0x00, low = 0x00; for (i = 0; i < MACHINE_MEM_SIZE; i++) { if (pc[i].name[0] == 0 && pc[i].arg == -1) break; debug_printf("%d: ", i); if (pc[i].name[0] != 0) { index = lookup_mnemonic(pc[i].name); high = mnemonics[index].opcode; } else { high = 0x00; } debug_printf("high=0x%02x ", high); if (pc[i].arg != -1) low = pc[i].arg; else low = 0x00; debug_printf("low=0x%02x: ", low); byte = (high << 4); byte |= low; debug_printf("byte=0x%02x (%d)\n", byte, byte); fwrite(&byte, 1, 1, file); } return 0; } /* * *** Main program ************************************************************ */ int main(int argc, char **argv) { int ch, r; char of[64]; /* output filename */ FILE *file; if (argc < 2) usage(); memset(of, 0, sizeof(of)); while ((ch = getopt(argc, argv, "o:")) != -1) { switch (ch) { case 'o': strlcpy(of, optarg, sizeof(of)); break; default: break; } } if (strlen(of) == 0) strlcpy(of, "a.8bc", sizeof(of)); file = fopen(argv[optind], "r"); if (file == NULL) err(1, "open"); init_code(); r = parse_file(file); if (r == -1) { printf("Assembling ended with error\n"); fclose(file); return 1; } fclose(file); print_code(); file = fopen(of, "w"); if (file == NULL) err(1, "open"); if (write_opcode(file) == -1) { printf("Error on writing output file\n"); fclose(file); return 1; } printf("Output file written to %s\n", of); fclose(file); printf("Machine memory used up %d bytes\n", mem_addr); printf("Assembling done\n"); return 0; }