/* ".COD" file output for gplink
Copyright 1998-2005
James Bowman, Scott Dattalo
Copyright 2012 Borut Ražem
Copyright 2016 Molnár Károly
*/
#include "stdhdr.h"
#include "libgputils.h"
#include "gplink.h"
#include "cod.h"
static DirBlockInfo *main_dir;
/*------------------------------------------------------------------------------------------------*/
/* Assign each file name unique file number. A file may appear in the symbol table more than once. */
static void _assign_file_id() {
symbol_table_t *file_table;
gp_aux_t *aux;
symbol_t *sym;
int file_id;
int *value;
/* Build a case sensitive file table. */
file_table = gp_sym_push_table(NULL, false);
file_id = 0;
FOR(gp_symbol_list_t,symbol,state.object->symbol_list) {
if (symbol->pclass == C_FILE) {
aux = symbol->aux_list.first;
assert(aux);
sym = gp_sym_get_symbol(file_table, aux->_aux_symbol._aux_file.filename);
if (sym) {
/* Fetch the file number. */
value = (int *)gp_sym_get_symbol_annotation(sym);
}
else {
/* The file hasn't been assigned a value. */
value = new int;
*value = file_id++;
sym = gp_sym_add_symbol(file_table, aux->_aux_symbol._aux_file.filename);
gp_sym_annotate_symbol(sym, value);
}
symbol->number = *value;
}
}
/* Destroy the table. */
file_table = gp_sym_pop_table(file_table);
}
/*------------------------------------------------------------------------------------------------*/
/* cod_write_symbols - Write the symbol table to the .cod file.
*
* This routine will read the symbol table that gplink has created
* and convert it into a format suitable for .cod files. So far, only
* three variable types are supported: address, register, and constants.
*
*/
static void
_write_symbols(const symbol_t **Symbol_list, size_t Num_symbols)
{
size_t i;
unsigned length;
unsigned type;
const gp_coffsymbol_t *var;
const gp_symbol_t *symbol;
const gp_section_t *section;
const char *name;
BlockList *sb;
bool truncated;
if ((Symbol_list == NULL) || (Num_symbols == 0)) {
return;
}
sb = NULL;
for (i = 0; i < Num_symbols; i++) {
name = gp_sym_get_symbol_name(Symbol_list[i]);
var = (const gp_coffsymbol_t *)gp_sym_get_symbol_annotation(Symbol_list[i]);
assert(var);
length = gp_strlen_Plimit(name, COD_LSYMBOL_NAME_MAX_SIZE, &truncated);
if (truncated) {
gp_warning("This symbol name (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
name, length);
}
/* If this symbol extends past the end of the cod block then write this block out. */
if ((sb == NULL) || ((main_dir->lsym.offset + length + COD_LSYMBOL_EXTRA) >= COD_BLOCK_SIZE)) {
sb = gp_cod_block_append(&main_dir->lsym, gp_cod_block_new());
}
symbol = var->symbol;
assert(symbol);
section = symbol->section;
assert(section);
if (FlagIsSet(section->flags, STYP_TEXT)) {
type = COD_ST_ADDRESS;
}
else if (FlagIsSet(section->flags, STYP_RAM_AREA)) {
type = COD_ST_C_SHORT;
}
else {
type = COD_ST_CONSTANT;
}
gp_cod_put_long_symbol(&sb->block[main_dir->lsym.offset], name, symbol->value, type);
main_dir->lsym.offset += length + COD_LSYMBOL_EXTRA;
}
}
/*------------------------------------------------------------------------------------------------*/
static void
_write_symbol_table(const symbol_table_t *Table)
{
const symbol_t **lst;
size_t sym_count;
sym_count = gp_sym_get_symbol_count(Table);
if (!sym_count) return;
lst = gp_sym_clone_symbol_array(Table, gp_sym_compare_fn);
_write_symbols(lst, sym_count);
free((void*)lst);
}
/*------------------------------------------------------------------------------------------------*/
/* _write_source_file_block - Write a code block that contains a list of the source files. */
static void _write_source_file_block() {
BlockList *fb;
int file_id;
unsigned length;
const char *string;
bool truncated;
fb = NULL;
file_id = 0;
FORC(gp_symbol_list_t,symbol,state.object->symbol_list) {
if ((fb == NULL) || (main_dir->file.offset >= (COD_FILE_NAMES_PER_BLOCK * COD_FILE_NAME_SIZE))) {
fb = gp_cod_block_append(&main_dir->file, gp_cod_block_new());
}
if (symbol->pclass == C_FILE && (int)symbol->number == file_id) {
/* Skip the duplicate file symbols. */
file_id++;
/* The file id is used to define the index at which the file name is written within
* the file code block. (The id's are sequentially assigned when the files are opened.)
* If there are too many files, then gpasm will abort.
* Note: The .cod files can handle larger file lists...
*/
string = symbol->aux_list.first->_aux_symbol._aux_file.filename;
length = gp_Pstr_from_str(&fb->block[main_dir->file.offset], COD_FILE_NAME_SIZE, string, &truncated);
if (truncated) {
gp_warning("This source name (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
string, length);
}
main_dir->file.offset += COD_FILE_NAME_SIZE;
}
}
}
/*------------------------------------------------------------------------------------------------*/
/* _write_debug - Write debug symbols to the .cod file. */
static void _write_debug() {
unsigned length;
char command;
const char *string;
bool truncated;
BlockList*db = NULL;
FOR(gp_symbol_list_t,symbol,state.object->symbol_list) {
if (!strcasecmp(".direct", symbol->name)) {
assert(symbol->aux_list.num_nodes == 1);
const gp_aux_t*aux = symbol->aux_list.first;
assert(aux);
command = aux->_aux_symbol._aux_direct.command;
string = aux->_aux_symbol._aux_direct.string;
length = gp_strlen_Plimit(string, COD_DEBUG_MSG_MAX_SIZE, &truncated);
if (truncated) {
gp_warning("This .direct string (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
string, length);
}
/* If this message extends past the end of the cod block then write this block out. */
if ((db == NULL) || ((main_dir->debug.offset + length + COD_DEBUG_EXTRA) >= COD_BLOCK_SIZE)) {
db = gp_cod_block_append(&main_dir->debug, gp_cod_block_new());
}
main_dir->debug.offset += gp_cod_put_debug_symbol(&db->block[main_dir->debug.offset], string,
symbol->value, command);
}
}
}
/*------------------------------------------------------------------------------------------------*/
/* init_cod - Initialize the .cod file. */
void cod_init(void) {
if (state.cod_file != OUT_NAMED) {
snprintf(state.cod_file_name, sizeof(state.cod_file_name), "%s.cod", state.base_file_name);
}
if (gp_num.errors || state.cod_file == OUT_SUPPRESS) {
state.cod.f = NULL;
state.cod.enabled = false;
unlink(state.cod_file_name);
}else{
state.cod.f = fopen(state.cod_file_name, "wb");
if (state.cod.f == NULL) {
perror(state.cod_file_name);
exit(1);
}
state.cod.enabled = true;
}
if (!state.cod.enabled) {
main_dir = NULL;
return;
}
main_dir = gp_cod_init_dir_block(state.cod_file_name, "gplink");
_assign_file_id();
}
/*------------------------------------------------------------------------------------------------*/
/* cod_lst_line - Add a line of information that cross references the
* the opcode's address, the source file and the list file.
*/
void
cod_lst_line(int line_type)
{
static DirBlockInfo *dbi = NULL;
static unsigned _64k_base = 0;
uint8_t smod_flag;
BlockList *lb;
bool first_time;
unsigned address;
unsigned high_address;
uint8_t *record;
if (!state.cod.enabled) {
return;
}
address = gp_processor_insn_from_byte_c(state.pclass, state.lst.was_byte_addr);
high_address = IMemBaseFromAddr(address);
if ((dbi == NULL) || (high_address != _64k_base)) {
_64k_base = high_address;
dbi = gp_cod_find_dir_block_by_high_addr(main_dir, _64k_base);
}
first_time = (gp_cod_block_get_last(&dbi->list) == NULL) ? true : false;
lb = gp_cod_block_get_last_or_new(&dbi->list);
if (dbi->list.offset >= (COD_MAX_LINE_SYM * COD_LINE_SYM_SIZE)) {
lb = gp_cod_block_append(&dbi->list, gp_cod_block_new());
}
assert(state.lst.src);
assert(state.lst.src->symbol);
smod_flag = (uint8_t)(first_time
? COD_LS_SMOD_FLAG_ALL
: state.cod.emitting
? COD_LS_SMOD_FLAG_C1
: COD_LS_SMOD_FLAG_C1 | COD_LS_SMOD_FLAG_D);
record = &lb->block[dbi->list.offset];
record[COD_LS_SFILE] = state.lst.src->symbol->number;
record[COD_LS_SMOD] = smod_flag;
/* Write the source file line number corresponding to the list file line number. */
gp_putl16(&record[COD_LS_SLINE], state.lst.src->line_number);
/* Write the address of the opcode. */
gp_putl16(&record[COD_LS_SLOC], address);
dbi->list.offset += COD_LINE_SYM_SIZE;
}
/*------------------------------------------------------------------------------------------------*/
void
cod_close_file(void)
{
unsigned length;
bool truncated;
const char *name;
if (!state.cod.enabled) {
return;
}
name = gp_processor_name(state.processor, 2);
/* The processor is unknown if not defined in command line at cod_init() call
so it should be set here. */
length = gp_Pstr_from_str(&main_dir->dir[COD_DIR_PROCESSOR], COD_DIR_PROCESSOR_SIZE, name, &truncated);
if (truncated) {
gp_warning("This processor name (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
name, length);
}
/* All external and global symbols are written. */
_write_symbol_table(state.symbol.extern_global);
/* All local symbols are written. */
_write_symbol_table(state.symbol.local);
_write_source_file_block();
gp_cod_write_code(state.pclass, state.i_memory, main_dir);
_write_debug();
gp_cod_enumerate_directory(main_dir);
gp_cod_write_directory(state.cod.f, main_dir);
gp_cod_free_directory(main_dir);
main_dir = NULL;
fclose(state.cod.f);
}
Detected encoding: UTF-8 | 0
|