/* xas.c * Assembler for XCPU * */ #define _GNU_SOURCE #include #include #include #include #include "xas.h" #define PROGRAM_NAME "XAS" #define VERSION "0.9" #define AUTHOR "Jonathan duSaint" #define AUTHOR_EMAIL "jon@rockgeeks.net" #define YY_BUFFER_STATE struct yy_buffer_state * #define YY_BUF_SIZE 16384 YY_BUFFER_STATE yy_create_buffer (FILE *file, int size); void yypush_buffer_state (YY_BUFFER_STATE buffer); void yypop_buffer_state (void); int line_pos = 0; int line_no = 1; int line_col = 0; char *current_file = NULL; int assemble_file (char *filename); FILE *out; uint32_t load_address; uint32_t text_start; uint32_t nbytes_out; char *pname; char *version; char *author; char *author_email; int debug = 0; int verbose = 0; int loud = 0; hash_t symtab; void panic (long line, char *file) { fprintf (stderr, "%s: PANIC at %ld of %s: %s\n", pname, line, file, strerror (errno)); abort (); } void print_version (void) { printf ("%s %s\n" "Copyright (C) 2006 %s <%s>\n" "%s is free software under the terms of the GNU General Public\n" "License version 2; see the file COPYING in the source\n" "distribution for details. This software comes with ABSOLUTELY\n" "NO WARRANTY.\n", pname, version, author, author_email, pname); } void help (void) { printf ( "Usage: xas [options] file\n" "Assembler for XCPU\n" "\n" "Options:\n" " -h, --help display this help and exit\n" " -v, --version output version information and exit\n" " -V, --verbose print status information while running\n" " -L, --LOUD print even more information - probably only useful\n" " for debugging XAS\n" " -a
, --address=
specify load address, overriding\n" " default and any specified in file\n" " -D= define a with as its value\n" " -g, --debug include debug information in the object file\n" " -o , --output= specify name of output file\n"); } #define byte_p(n) ((uint8_t *)(&(n))) uint32_t bswap4 (uint32_t in) { uint32_t sw; *(byte_p (sw) + 0) = *(byte_p (in) + 3); *(byte_p (sw) + 1) = *(byte_p (in) + 2); *(byte_p (sw) + 2) = *(byte_p (in) + 1); *(byte_p (sw) + 3) = *(byte_p (in) + 0); return sw; } void yyerror (const char *err) { fprintf (stderr, "%s:%d.%d: %s\n", current_file, line_no, line_col, err); } void write_header (void) { uint32_t data; /* file header */ fwrite ("XCPU", 1, 4, out); /* offset of start of text section */ data = bswap4 (text_start); fwrite (&data, 1, 4, out); /* text size (will fill in later) */ data = 0; fwrite (&data, 1, 4, out); /* load address */ data = bswap4 (load_address); fwrite (&data, 1, 4, out); } void finish_header (void) { uint32_t data; /* align text to 4 byte boundary */ if (nbytes_out % 4) { uint8_t buf[3] = { 0, 0, 0 }; uint32_t rem = 4 * ((nbytes_out + 3) >> 2) - nbytes_out; fseek (out, 0, SEEK_END); fwrite (&buf, 1, rem, out); nbytes_out += rem; } /* write text size now that it's known */ fseek (out, 8, SEEK_SET); data = bswap4 (nbytes_out); fwrite (&data, 1, 4, out); } int assemble_file (char *filename) { int rv; FILE *in; YY_BUFFER_STATE b; int line_pos_save = line_pos; int line_no_save = line_no; int line_col_save = line_col; char *current_file_save = current_file; current_file = filename; in = fopen (filename, "rt"); if (in == NULL) { line_pos = line_pos_save; line_no = line_no_save; line_col = line_col_save; current_file = current_file_save; fprintf (stderr, "%s:%d.%d: unable to open included file `%s'\n", current_file, line_no, line_col, filename); return 1; } b = yy_create_buffer (in, YY_BUF_SIZE); yypush_buffer_state (b); rv = yyparse (); yypop_buffer_state (); fclose (in); line_pos = line_pos_save; line_no = line_no_save; line_col = line_col_save; current_file = current_file_save; return rv; } uint8_t regcodes (char reg) { uint8_t code; switch (reg) { case 'A': code = 1; break; case 'B': code = 2; break; case 'C': code = 4; break; case 'D': code = 8; break; default: code = 0; } return code; } enum opcode_names { INS_NOP, INS_JUMP, INS_PUSH, INS_POP, INS_MEMOP, INS_CPUOP, INS_ASSIGN, INS_ADD, INS_SHIFT, INS_AND, INS_OR, INS_NOT, INS_MOVE, INS_COMPARE, INS_HALT, INS_CONFIG }; uint8_t opcodes[] = { 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0 }; void ins_config (uint32_t arg) { uint8_t op = opcodes[INS_CONFIG]; op |= (uint8_t)(arg & 0xf); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_jump (uint32_t arg) { uint8_t op = opcodes[INS_JUMP]; op |= (uint8_t)(arg & 0xf); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_push (char reg) { uint8_t op = opcodes[INS_PUSH]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_pop (char reg) { uint8_t op = opcodes[INS_POP]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_memop (char reg) { uint8_t op = opcodes[INS_MEMOP]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_cpuop (uint32_t val, uint32_t bit) { uint8_t op = opcodes[INS_CPUOP]; op |= (((uint8_t)bit << 3) & 0x8) | ((uint8_t)val & 0x7); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_assign (char reg, uint32_t arg) { uint8_t op[5]; op[0] = opcodes[INS_ASSIGN] | regcodes (reg); op[1] = *(byte_p (arg) + 3); op[2] = *(byte_p (arg) + 2); op[3] = *(byte_p (arg) + 1); op[4] = *(byte_p (arg) + 0); fwrite (&op, 1, 5, out); nbytes_out += 5; } void ins_add (char reg) { uint8_t op = opcodes[INS_ADD]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_shift (char reg) { uint8_t op = opcodes[INS_SHIFT]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_and (char reg) { uint8_t op = opcodes[INS_AND]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_or (char reg) { uint8_t op = opcodes[INS_OR]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_not (char reg) { uint8_t op = opcodes[INS_NOT]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_move (char reg) { uint8_t op = opcodes[INS_MOVE]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_compare (char reg) { uint8_t op = opcodes[INS_COMPARE]; op |= regcodes (reg); fwrite (&op, 1, 1, out); nbytes_out++; } void ins_halt (void) { uint8_t op = opcodes[INS_HALT]; fwrite (&op, 1, 1, out); nbytes_out++; } void ins_nop (void) { uint8_t op = opcodes[INS_NOP]; fwrite (&op, 1, 1, out); nbytes_out++; } void dir_include (char *filename) { int rv; if (loud) printf ("Including '%s'\n", filename); rv = assemble_file (filename); if (loud) printf ("Out of included '%s'\n", filename); } void dir_define (char *id, uint32_t arg) { int exists = 0; struct symbol_data *symdata; if ((symdata = hash_get (symtab, id)) != NULL) exists = 1; else { symdata = xmalloc (sizeof (struct symbol_data)); symdata->location_data = NULL; symdata->is_resolved = 0; } if (symdata->is_resolved == 0) { symdata->value = arg; symdata->is_resolved = 1; } if (verbose) printf ("Creating define `%s' with value %u\n", id, arg); if (!exists) hash_add (symtab, id, symdata); } void dir_label (char *id) { uint32_t loc; struct symbol_data *symdata; int exists = 0; /* get label location */ loc = nbytes_out + load_address; if ((symdata = hash_get (symtab, id)) != NULL) { /* if this label exists, it was used as a forward ref */ exists = 1; } else { symdata = xmalloc (sizeof (struct symbol_data)); symdata->location_data = NULL; } symdata->value = loc; symdata->is_resolved = 1; if (verbose) printf ("Creating label `%s' at %08x\n", id, loc); if (!exists) hash_add (symtab, id, symdata); } void dir_immed_string (char *str) { int s = 0, e = 0, n = strlen (str);; char *strout; uint32_t len; strout = xmalloc (n + 1); while (e < n) { if (str[e] == '\\') { switch (str[++e]) { case 'n': strout[s++] = '\n'; break; case 't': strout[s++] = '\t'; break; case '\\': strout[s++] = '\\'; break; default: strout[s++] = '?'; } e++; } else strout[s++] = str[e++]; } len = (uint32_t)e; fwrite (strout, 1, len, out); xfree (strout); nbytes_out += len; } void dir_immed_bytes (uint32_t nbytes) { uint32_t k; uint8_t zero = '\0'; for (k = 0; k < nbytes; k++) fwrite (&zero, 1, 1, out); nbytes_out += nbytes; } uint32_t resolve_identifier (char *id) { struct symbol_data *symdata; symdata = hash_get (symtab, id); if (symdata == NULL) { /* symbol doesn't exist - create unresolved symbol */ symdata = xmalloc (sizeof (struct symbol_data)); symdata->value = 0; symdata->is_resolved = 0; symdata->location_data = xmalloc (sizeof (struct location_data)); symdata->location_data->next = NULL; symdata->location_data->location = ftell (out); symdata->location_data->file = xmalloc (strlen (current_file) + 1); strcpy (symdata->location_data->file, current_file); symdata->location_data->line_no = line_no; symdata->location_data->line_col = line_col; hash_add (symtab, id, symdata); if (verbose) printf ("Unresolved symbol `%s' created\n", id); } else if (symdata->is_resolved == 0) { /* symbol exists, but is unresolved - add this ref to the locations */ struct location_data *p; p = symdata->location_data; if (p == NULL) { fprintf (stderr, "%s internal: symdata exists, is_resolved == 0, but location_data == NULL for ID `%s'\n", pname, id); symdata->location_data = xmalloc (sizeof (struct location_data)); symdata->location_data->next = NULL; p = symdata->location_data; } while (p->next != NULL) p = p->next; p->next = xmalloc (sizeof (struct location_data)); p->next->next = NULL; p->next->location = ftell (out); p->next->file = xmalloc (strlen (current_file) + 1); strcpy (p->next->file, current_file); p->next->line_no = line_no; p->next->line_col = line_col; if (verbose) printf ("Unresolved symbol `%s' referenced\n", id); } else if (verbose) printf ("Symbol `%s' (%u) referenced\n", id, symdata->value); return symdata->value; } void resolve_symbols (void) { char *id; struct symbol_data *symdata; struct location_data *locdata; if (loud) printf ("Resolving symbols\n"); hash_walk (NULL); while ((id = hash_walk (symtab)) != HASH_END) { uint32_t val; symdata = hash_get (symtab, id); locdata = symdata->location_data; val = bswap4 (symdata->value); while (locdata != NULL) { struct location_data *ldtmp; if (verbose) printf ("Writing symbol `%s' data (%u) to %ld\n", id, bswap4 (val), locdata->location + 1); if (symdata->is_resolved == 0) fprintf (stderr, "%s:%d.%d: unresolved symbol `%s'\n", locdata->file, locdata->line_no, locdata->line_col, id); else { fseek (out, locdata->location + 1, SEEK_SET); fwrite (&val, 4, 1, out); } ldtmp = locdata; locdata = locdata->next; xfree (ldtmp->file); xfree (ldtmp); } /* need to delete the symbol due to a bug in hash_walk */ hash_remove (symtab, id); } } int main (int argc, char *argv[]) { char *output_file = NULL, *input_file; int isok; int opt, index; FILE *realout; struct option options[] = { { "address", required_argument, NULL, 'a' }, { "", required_argument, NULL, 'D' }, { "debug", no_argument, NULL, 'g' }, { "output", required_argument, NULL, 'o' }, { "verbose", no_argument, NULL, 'V' }, { "LOUD", no_argument, NULL, 'L' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { 0, 0, 0, 0 }, }; pname = PROGRAM_NAME; version = VERSION; author = AUTHOR; author_email = AUTHOR_EMAIL; load_address = 0x110; /* start of memory with normal bootstrap */ text_start = 0x10; /* file offset without debug info */ opterr = 0; symtab = hash_new (0); while (1) { opt = getopt_long (argc, argv, "a:D:go:VLhv", options, &index); if (opt == EOF) break; switch (opt) { case 'a': load_address = strtol (optarg, NULL, 0); break; case 'D': /* parse define */ break; case 'g': debug = 1; break; case 'o': output_file = xmalloc (strlen (optarg) + 1); strcpy (output_file, optarg); break; case 'V': verbose = 1; break; case 'L': loud = 1; break; case 'h': help (); return 0; case 'v': print_version (); return 0; case ':': exit (1); case '?': fprintf (stderr, "%s: unknown option character `%c'\n", pname, optopt); break; default: fprintf (stderr, "%s: ?? getopt returned character code 0x%x ??\n", pname, opt); } } input_file = argv[optind]; if (output_file == NULL) { size_t nchar = strlen (input_file); output_file = xmalloc (nchar + 5); if (input_file[nchar-2] == '.' && input_file[nchar-1] == 's') nchar -= 2; strncpy (output_file, input_file, nchar); strcat (output_file, ".xcpu"); } nbytes_out = 0; /* position info within output */ errno = 0; out = tmpfile (); if (out == NULL) { fprintf (stderr, "%s: unable to create temporary output file: %s\n", pname, strerror (errno)); exit (1); } write_header (); isok = assemble_file (input_file); resolve_symbols (); finish_header (); /* now move tmpfile to outfile */ errno = 0; realout = fopen (output_file, "wb"); if (realout == NULL) { fprintf (stderr, "%s: unable to create output file `%s': %s\n", pname, output_file, strerror (errno)); exit (1); } fseek (out, 0, SEEK_SET); while (!feof (out)) { uint8_t buf; fread (&buf, 1, 1, out); fwrite (&buf, 1, 1, realout); } fclose (realout); fclose (out); xfree (output_file); hash_delete (symtab); free_all (); return 0; }