/* casm - Simple, portable assembler Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.co.uk) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ------------------------------------------------------------------------- Common utilities */ #include #include #include #include #include #include "global.h" #include "state.h" #include "label.h" #include "macro.h" #include "expr.h" #include "varchar.h" #include "listing.h" /* ---------------------------------------- PRIVATE TYPES */ enum option_t { OPT_LIST, OPT_LISTFILE, OPT_LISTPC, OPT_LISTHEX, OPT_LISTMACROS, OPT_LISTLABELS, OPT_LISTRMBLANK }; static const ValueTable option_set[] = { {"list", OPT_LIST}, {"list-file", OPT_LISTFILE}, {"list-pc", OPT_LISTPC}, {"list-hex", OPT_LISTHEX}, {"list-macros", OPT_LISTMACROS}, {"list-labels", OPT_LISTLABELS}, {"list-rm-blank", OPT_LISTRMBLANK}, {NULL} }; typedef enum { LabelsOff = 0, LabelsDump = 1, LabelsDumpPrivate = 2 } LabelMode; typedef enum { MacrosOff = 0, MacrosInvoke = 1, MacrosDump = 2 } MacroMode; typedef struct { int enabled; int dump_PC; int dump_bytes; int rm_blank; LabelMode labels; MacroMode macros; } Options; /* ---------------------------------------- PRIVATE DATA */ static int line_PC; static int last_line_blank; static FILE *output; static Options options = { FALSE, FALSE, FALSE, TRUE, LabelsOff, MacrosOff, }; static ValueTable labels_table[] = { {"off", LabelsOff}, {"no", LabelsOff}, {"on", LabelsDump}, {"yes", LabelsDump}, {"all", LabelsDump | LabelsDumpPrivate}, {NULL} }; static ValueTable macros_table[] = { {"off", MacrosOff}, {"no", MacrosOff}, {"yes", MacrosInvoke}, {"exec", MacrosInvoke}, {"dump", MacrosDump}, {"all", MacrosInvoke | MacrosDump}, {NULL} }; /* ---------------------------------------- PRIVATE FUNCTIONS */ static void BuildArgs(Varchar *str, int argc, char *argv[], int quoted[]) { int f; for(f = 0; f < argc; f++) { if (f > 0) { VarcharAdd(str, ", "); } if (quoted[f] && quoted[f] == '(') { VarcharPrintf(str, "(%s)", argv[f]); } else if (quoted[f]) { VarcharPrintf(str, "%c%s%c", quoted[f], argv[f], quoted[f]); } else { VarcharAdd(str, argv[f]); } } } static void Output(const char *fmt, ...) { if (IsFinalPass() && options.enabled) { va_list va; va_start(va, fmt); if (output) { vfprintf(output, fmt, va); } else { vfprintf(stdout, fmt, va); } va_end(va); } } static FILE *GetOutput(void) { return output ? output : stdout; } static int IsBlankLine(const char *p) { while(*p) { if (!isspace((unsigned char)*p++)) { return FALSE; } } return TRUE; } /* ---------------------------------------- INTERFACES */ const ValueTable *ListOptions(void) { return option_set; } CommandStatus ListSetOption(int opt, int argc, char *argv[], int quoted[], char *err, size_t errsize) { const ValueTable *val; CommandStatus stat = CMD_OK; if (!IsFinalPass()) { return CMD_OK; } CMD_ARGC_CHECK(1); switch(opt) { case OPT_LIST: options.enabled = ParseTrueFalse(argv[0], FALSE); break; case OPT_LISTFILE: if (output) { snprintf(err, errsize, "output file already set"); stat = CMD_FAILED; } else { output = fopen(argv[0], "w"); if (!output) { snprintf(err, errsize, "couldn't open \"%s\"", argv[0]); stat = CMD_FAILED; } } break; case OPT_LISTPC: options.dump_PC = ParseTrueFalse(argv[0], FALSE); break; case OPT_LISTHEX: options.dump_bytes = ParseTrueFalse(argv[0], FALSE); break; case OPT_LISTMACROS: CMD_TABLE(argv[0], macros_table, val); options.macros = val->value; break; case OPT_LISTLABELS: CMD_TABLE(argv[0], labels_table, val); options.labels = val->value; break; case OPT_LISTRMBLANK: options.rm_blank = ParseTrueFalse(argv[0], FALSE); break; default: break; } return stat; } void ListStartLine(void) { line_PC = PC(); } void ListLine(const char *line) { if (IsFinalPass() && options.enabled) { if (options.rm_blank && last_line_blank && IsBlankLine(line)) { return; } last_line_blank = IsBlankLine(line); Output("%s\n", line); /* Generate PC and hex dump and add to comment */ if ((options.dump_PC || options.dump_bytes) && (PC() != line_PC)) { Varchar *hex; int f; hex = VarcharCreate(NULL); VarcharAdd(hex, ";"); if (options.dump_PC) { VarcharPrintf(hex, " $%4.4X:", (unsigned)line_PC); } if (options.dump_bytes && (PC() - line_PC < 256)) { for(f = line_PC; f < PC(); f++) { VarcharPrintf(hex, " $%2.2X", (unsigned)ReadByte(f)); } } Output("%s\n", VarcharContents(hex)); VarcharFree(hex); } } } void ListMacroInvokeStart(int argc, char *argv[], int quoted[]) { if (IsFinalPass() && options.enabled && options.macros & MacrosInvoke) { Varchar *s; s = VarcharCreate(NULL); VarcharPrintf(s, "; START MACRO %s ", argv[0]); BuildArgs(s, argc - 1, argv + 1, quoted + 1); Output("%s\n", VarcharContents(s)); VarcharFree(s); } } void ListMacroInvokeEnd(const char *name) { if (IsFinalPass() && options.enabled && options.macros & MacrosInvoke) { Varchar *s; s = VarcharCreate(NULL); VarcharAdd(s, "; END MACRO "); VarcharAdd(s, name); Output("%s\n", VarcharContents(s)); VarcharFree(s); } } void ListError(const char *fmt, ...) { char buff[4096]; va_list va; va_start(va, fmt); vsnprintf(buff, sizeof buff, fmt, va); va_end(va); fprintf(stderr, "%s\n", buff); if (IsFinalPass() && options.enabled && output) { Output("%s\n", buff); } } void ListFinish(void) { if (IsFinalPass() && options.enabled) { if (options.labels) { Output("\n;\n; LABELS:\n;\n"); LabelDump(GetOutput(), options.labels & LabelsDumpPrivate); } if (options.macros & MacrosDump) { Output("\n;\n; MACROS:\n;\n"); MacroDump(GetOutput()); } } if (output) { if (output != stdout) { fclose(output); } output = NULL; } options.enabled = FALSE; } /* vim: ai sw=4 ts=8 expandtab */