/* ".LST" file output for gplink Copyright (C) 2004, 2005 Craig Franklin This file is part of gputils. gputils 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 2, or (at your option) any later version. gputils 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 gputils; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdhdr.h" #include "libgputils.h" #include "gplink.h" #include "cod.h" #ifdef STDC_HEADERS #include #endif static gp_boolean list_enabled; static gp_section_type *line_section; static void open_src(char *name, gp_symbol_type *symbol) { struct list_context *new = malloc(sizeof(*new)); assert(name != NULL); new->f = fopen(name, "rt"); if(new->f) { new->name = strdup(name); new->missing_source = false; } else { new->missing_source = true; } new->symbol = symbol; new->line_number = 1; new->prev = state.lst.src; state.lst.src = new; } static void close_src(void) { struct list_context *old; assert(state.lst.src != NULL); old = state.lst.src; state.lst.src = state.lst.src->prev; free(old); } static void lst_line(const char *format, ...) { va_list args; char buffer[BUFSIZ]; if (state.lst.f == NULL) return; va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); fprintf(state.lst.f, "%s\n", buffer); return; } gp_linenum_type * find_line_number(gp_symbol_type *symbol, int line_number) { gp_section_type *section; gp_linenum_type *line = NULL; section = state.object->sections; /* FIXME: This too slow. */ while (section != NULL) { line = section->line_numbers; while (line != NULL) { if ((line->symbol == symbol) && (line->line_number == line_number)) { if (section != line_section) { /* switching sections, so update was_org with the new section address */ state.lst.was_org = section->address >> state.byte_addr; line_section = section; } return line; } line = line->next; } section = section->next; } return NULL; } static void write_src(int last_line) { #define LINESIZ 520 char linebuf[LINESIZ]; char dasmbuf[LINESIZ]; char *pc; gp_linenum_type *line = NULL; gp_boolean first_time; int org = 0; int data; int num_words; /* if the source file wasn't found, can't write it to the list file */ if (state.lst.src->missing_source) return; while (1) { /* when last_line is 0 print all lines, else print to last_line */ if ((last_line) && (state.lst.src->line_number > last_line)) break; if (fgets(linebuf, LINESIZ, state.lst.src->f) == NULL) break; state.lst.was_org = org; if (list_enabled) { /* Eat the trailing newline */ pc = strrchr(linebuf, '\n'); if (pc) *pc = 0; first_time = true; line = find_line_number(state.lst.src->symbol, state.lst.src->line_number); while (line) { /* print all instructions generated by this line of the source */ if (line->line_number != state.lst.src->line_number) break; if (first_time == false) { /* only print the source line the first time */ linebuf[0] = '\0'; } state.cod.emitting = 1; org = line->address >> state.byte_addr; data = i_memory_get(line_section->data, org); assert(data & MEM_USED_MASK); num_words = gp_disassemble(line_section->data, org, state.class, dasmbuf, sizeof(dasmbuf)); lst_line("%06lx %04x %-24s %s", line->address, data & 0xffff, dasmbuf, linebuf); cod_lst_line(COD_NORMAL_LST_LINE); org++; if (num_words != 1) { state.lst.was_org = org; data = i_memory_get(line_section->data, org); assert(data & MEM_USED_MASK); lst_line("%06lx %04x", org << state.byte_addr, data & 0xffff); cod_lst_line(COD_NORMAL_LST_LINE); org++; if (line->next) { /* skip the line number for the other half of this instruction */ line = line->next; } } first_time = false; line = line->next; } if (first_time) { lst_line("%42s %s", " ", linebuf); state.cod.emitting = 0; cod_lst_line(COD_NORMAL_LST_LINE); } } state.lst.src->line_number++; } } /* * lst_init - initialize the lst file */ static void lst_init(void) { if (state.lstfile != named) { strncpy(state.lstfilename, state.basefilename, sizeof(state.lstfilename)); strncat(state.lstfilename, ".lst", sizeof(state.lstfilename)); } if ((gp_num_errors) || (state.lstfile == suppress)) { state.lst.f = NULL; state.lst.enabled = false; unlink(state.lstfilename); } else { state.lst.f = fopen(state.lstfilename, "wt"); if (state.lst.f == NULL) { perror(state.lstfilename); exit(1); } state.lst.enabled = true; } if(!state.lst.enabled) return; state.lst.was_org = 0; state.cod.emitting = 0; lst_line("%s", GPLINK_VERSION_STRING); lst_line("%s", GPUTILS_COPYRIGHT_STRING); lst_line("Listing File Generated: %s", state.startdate); lst_line(" "); lst_line(" "); lst_line("Address Value Disassembly Source"); lst_line("------- ----- ----------- ------"); } void write_lst(void) { gp_symbol_type *symbol = state.object->symbols; gp_aux_type *aux; gp_boolean first_time = true; lst_init(); if(!state.lst.enabled) return; list_enabled = true; state.lst.src = NULL; /* scan through the file symbols */ while (symbol != NULL) { if (symbol->class == C_FILE) { /* open a new file */ aux = symbol->aux_list; assert(aux != NULL); if (aux->_aux_symbol._aux_file.line_number) { /* it is an include file, so print the current file until the line number is reached */ assert(state.lst.src != NULL); write_src(aux->_aux_symbol._aux_file.line_number); } else { /* it is not an include, so enable listing */ list_enabled = true; } open_src(aux->_aux_symbol._aux_file.filename, symbol); if (first_time) { /* write the line numbers for the lst file header */ cod_lst_line(COD_FIRST_LST_LINE); cod_lst_line(COD_NORMAL_LST_LINE); cod_lst_line(COD_NORMAL_LST_LINE); cod_lst_line(COD_NORMAL_LST_LINE); cod_lst_line(COD_NORMAL_LST_LINE); cod_lst_line(COD_NORMAL_LST_LINE); cod_lst_line(COD_NORMAL_LST_LINE); first_time = false; } } else if (symbol->class == C_EOF) { /* print the rest of the current file then, close it */ write_src(0); close_src(); } else if (symbol->class == C_LIST) { if (strcasecmp(symbol->name, ".list") == 0) { write_src(symbol->value); list_enabled = true; } else if (strcasecmp(symbol->name, ".nolist") == 0) { write_src(symbol->value); list_enabled = false; } else { assert(0); } } symbol = symbol->next; } fclose(state.lst.f); }