/* ".COD" file output for gpasm
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
James Bowman, Scott Dattalo
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 "gpasm.h"
#include "lst.h"
#include "cod.h"
static DirBlockInfo main_dir;
static int cod_lst_line_number = 0;
static int blocks = 0;
extern int _16bit_core;
static void
init_DirBlock(DirBlockInfo *a_dir)
{
int i;
/* create space for the directory block: */
gp_cod_create(&a_dir->dir, &blocks);
a_dir->next_dir_block_info = NULL;
/* The code blocks associated with this directory
do not yet exist. */
for(i=0; i<COD_CODE_IMAGE_BLOCKS; i++) {
a_dir->cod_image_blocks[i].block = NULL;
a_dir->cod_image_blocks[i].block_number = 0;
}
/* Initialize the directory block with known data. It'll be written
* to the .cod file after everything else */
gp_cod_strncpy(&a_dir->dir.block[COD_DIR_SOURCE],
state.srcfilename,
COD_DIR_DATE - COD_DIR_SOURCE);
gp_cod_date(&a_dir->dir.block[COD_DIR_DATE],
COD_DIR_TIME - COD_DIR_DATE);
gp_cod_time(&a_dir->dir.block[COD_DIR_TIME],
COD_DIR_VERSION - COD_DIR_TIME);
gp_cod_strncpy(&a_dir->dir.block[COD_DIR_VERSION],
VERSION,
COD_DIR_COMPILER - COD_DIR_VERSION);
gp_cod_strncpy(&a_dir->dir.block[COD_DIR_COMPILER],
"gpasm",
COD_DIR_NOTICE - COD_DIR_COMPILER);
gp_cod_strncpy(&a_dir->dir.block[COD_DIR_NOTICE],
GPUTILS_COPYRIGHT_STRING,
COD_DIR_SYMTAB - COD_DIR_NOTICE);
/* The address is always two shorts or 4 bytes long */
gp_putl16(&a_dir->dir.block[COD_DIR_ADDRSIZE], 4);
}
/*
* init_cod - initialize the cod file
*/
void
cod_init(void)
{
if (state.codfile != named) {
strncpy(state.codfilename, state.basefilename, sizeof(state.codfilename));
strncat(state.codfilename, ".cod", sizeof(state.codfilename));
}
if (state.codfile == suppress) {
state.cod.f = NULL;
state.cod.enabled = false;
unlink(state.codfilename);
} else {
state.cod.f = fopen(state.codfilename, "wb");
if (state.cod.f == NULL) {
perror(state.codfilename);
exit(1);
}
state.cod.enabled = true;
}
if(!state.cod.enabled)
return;
init_DirBlock(&main_dir);
assert(main_dir.dir.block_number == 0);
fseek(state.cod.f, COD_BLOCK_SIZE, SEEK_SET);
}
/*
* write_cod_block - write a cod block to the .cod file and adjust
* the cod block ptrs in the directory block.
*/
static void
write_cod_block(DirBlockInfo *dbp,
int block_ptr_start,
int block_ptr_end,
Block *bptr)
{
/* most of the cod blocks have a 'start' and 'end' pointer in the
* directory block. These pointers are 16 bits wide. If the start
* pointer is zero, then this is the first time a block of this
* type has been written. In this case, the block pointer is written
* to both the start and end pointer locations.
*/
if(!gp_getl16(&dbp->dir.block[block_ptr_start]))
gp_putl16(&dbp->dir.block[block_ptr_start], bptr->block_number);
gp_putl16(&dbp->dir.block[block_ptr_end], bptr->block_number);
fseek(state.cod.f, COD_BLOCK_SIZE*bptr->block_number, SEEK_SET);
fwrite(bptr->block, 1, COD_BLOCK_SIZE, state.cod.f);
}
/*
* write_file_block - write a code block that contains a list of the
* source files.
*/
static void
write_file_block(void)
{
Block fb;
#define FILES_PER_BLOCK COD_BLOCK_SIZE/COD_FILE_SIZE
int id_number=0;
struct file_context *fc;
gp_cod_create(&fb, &blocks);
if(!state.files)
return;
/* Find the head of the file list: */
fc = state.files;
while(fc->prev && id_number++ < 1000) {
fc = fc->prev;
}
if(id_number>=1000) {
/* Too many files to handle in the .cod file */
assert(0);
}
id_number = 0;
while(fc != NULL) {
/* 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: .cod files
* can handle larger file lists...
*/
gp_cod_strncpy(&fb.block[1 + COD_FILE_SIZE * id_number],
fc->name,
COD_FILE_SIZE-1);
id_number++;
if(id_number >= FILES_PER_BLOCK) {
write_cod_block(&main_dir, COD_DIR_NAMTAB, COD_DIR_NAMTAB+2, &fb);
id_number = 0;
gp_cod_next(&fb, &blocks);
}
fc = fc->next;
}
if(id_number)
write_cod_block(&main_dir, COD_DIR_NAMTAB, COD_DIR_NAMTAB+2, &fb);
gp_cod_delete(&fb);
}
/* 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)
{
#define COD_LST_FIRST_LINE 7
unsigned char smod_flag = 0xff;
static int first_time = 1;
static Block lb={NULL,0};
int offset;
if(!state.cod.enabled)
return;
switch(line_type) {
case COD_FIRST_LST_LINE:
case COD_NORMAL_LST_LINE:
/* Don't start until after the source is open */
if(state.src == NULL)
return;
/* If we don't have a block yet then create one: */
if(lb.block == NULL)
gp_cod_create(&lb, &blocks);
/* Ignore the first few line numbers */
if(state.lst.line_number < COD_LST_FIRST_LINE)
return;
if(cod_lst_line_number >= COD_MAX_LINE_SYM) {
write_cod_block(&main_dir, COD_DIR_LSTTAB, COD_DIR_LSTTAB+2, &lb);
cod_lst_line_number = 0;
gp_cod_next(&lb, &blocks);
}
assert(state.src->fc != NULL);
offset = cod_lst_line_number++ * COD_LINE_SYM_SIZE;
lb.block[offset + COD_LS_SFILE] = state.src->fc->id;
if(state.cod.emitting != 0)
smod_flag = 0x080;
else
smod_flag = 0x90;
if(first_time != 0) {
first_time = 0;
smod_flag = 0xff;
}
lb.block[offset + COD_LS_SMOD] = smod_flag;
/* Write the source file line number corresponding to the list file line
number */
gp_putl16(&lb.block[offset + COD_LS_SLINE], state.src->line_number);
/* Write the address of the opcode. */
gp_putl16(&lb.block[offset + COD_LS_SLOC],
state.lst.line.was_org << _16bit_core);
break;
case COD_LAST_LST_LINE:
if(lb.block) {
write_cod_block(&main_dir, COD_DIR_LSTTAB, COD_DIR_LSTTAB+2, &lb);
gp_cod_delete(&lb);
}
break;
default:
assert(0);
}
}
/* cod_write_symbols - write the symbol table to the .cod file
*
* This routine will read the symbol table that gpasm has created
* and convert it into a format suitable for .cod files. So far, only
* three variable types are supported: address, register, and constants.
*
*/
void
cod_write_symbols(struct symbol **symbol_list, int num_symbols)
{
/* Each symbol is written as a dynamically sized structure to the
* .cod file. Its format is like this:
* position 0 length of the symbol name
* positions 1 to len the symbol name
* positions len+1 & len+2 Type of symbol (16 bits)
* positions len+3 to len+7 Value of symbol
*/
#define COD_SYM_TYPE 1 /* type info is 1 byte after the length */
#define COD_SYM_VALUE 3 /* value info is 3 bytes after the length */
#define COD_SYM_EXTRA 7 /* symbol name length + 7 is total structure size */
#define MAX_SYM_LEN 255 /* Maximum length of a symbol name */
int i,offset,len,type;
struct variable *var;
char * s;
Block sb;
if(!state.cod.enabled)
return;
gp_cod_create(&sb, &blocks);
offset = 0;
for(i=0; i<num_symbols; i++) {
var = get_symbol_annotation(symbol_list[i]);
s = get_symbol_name(symbol_list[i]);
len = strlen(s);
/* If this symbol extends past the end of the cod block
* then write this block out */
if((offset + len + COD_SYM_EXTRA) >= COD_BLOCK_SIZE) {
write_cod_block(&main_dir, COD_DIR_LSYMTAB, COD_DIR_LSYMTAB+2, &sb);
gp_cod_next(&sb, &blocks);
offset = 0;
}
gp_cod_strncpy(&sb.block[offset +1], s, MAX_SYM_LEN);
switch(var->type) {
case gvt_cblock:
type = COD_ST_C_SHORT; /* byte craft's nomenclature for a memory byte. */
break;
case gvt_address:
type = COD_ST_ADDRESS;
break;
case gvt_org:
type = COD_ST_ADDRESS;
break;
case gvt_constant:
default:
type = COD_ST_CONSTANT;
}
gp_putl16(&sb.block[offset+len+COD_SYM_TYPE], type);
/* write 32 bits, big endian */
gp_putb32(&sb.block[offset+len+COD_SYM_VALUE], var->value);
offset += (len+COD_SYM_EXTRA);
}
if(offset)
write_cod_block(&main_dir, COD_DIR_LSYMTAB, COD_DIR_LSYMTAB+2, &sb);
gp_cod_delete(&sb);
}
/* cod_emit_opcode - write one opcode to a cod_image_block
*/
static void
cod_emit_opcode(int address,int opcode)
{
DirBlockInfo *dbi;
int block_index;
int found;
int _64k_base;
char * block;
if(!state.cod.enabled)
return;
/* The code image blocks are handled in a different manner than the
* other cod blocks. In theory, it's possible to emit opcodes in a
* non-sequential manner. Furthermore, it's possible that there may
* be gaps in the program memory. These cases are handled by an array
* of code blocks. The lower 8 bits of the opcode's address form an
* index into the code block, while bits 9-15 are an index into the
* array of code blocks. The code image blocks are not written until
* all of the opcodes have been emitted.
*/
block_index = (address >> (COD_BLOCK_BITS-1)) & (COD_CODE_IMAGE_BLOCKS -1);
_64k_base = (address >> 15) & 0xffff;
dbi = &main_dir;
/* find the directory containing this 64k segment */
found = 0;
do {
if(gp_getl16(&dbi->dir.block[COD_DIR_HIGHADDR]) == _64k_base)
found = 1;
else {
/* If the next directory block (in the linked list of directory
blocks) is NULL, then this is the first time to encounter this
_64k segment. So we need to create a new segment. */
if(dbi->next_dir_block_info == NULL) {
dbi->next_dir_block_info = malloc(sizeof(DirBlockInfo));
init_DirBlock(dbi->next_dir_block_info);
gp_putl16(&dbi->dir.block[COD_DIR_NEXTDIR],
dbi->next_dir_block_info->dir.block_number);
gp_putl16(&dbi->next_dir_block_info->dir.block[COD_DIR_HIGHADDR],
_64k_base);
found = 1;
}
dbi = dbi->next_dir_block_info;
}
}
while(!found);
if(dbi->cod_image_blocks[block_index].block == NULL) {
gp_cod_create(&dbi->cod_image_blocks[block_index], &blocks);
}
block = dbi->cod_image_blocks[block_index].block;
gp_putl16(&block[(address*2) & (COD_BLOCK_SIZE - 1)], opcode);
}
static void
write_cod_range_block(unsigned int address, Block *rb)
{
DirBlockInfo *dbi = &main_dir;
unsigned int _64k_base;
_64k_base = (address >> 15) & 0xffff;
do {
if(gp_getl16(&dbi->dir.block[COD_DIR_HIGHADDR]) == _64k_base) {
write_cod_block(dbi, COD_DIR_MEMMAP, COD_DIR_MEMMAP+2, rb);
return;
}
dbi = dbi->next_dir_block_info;
} while(dbi);
assert(0);
}
/* cod_write_code - write all of the assembled pic code to the .cod file
*/
static void
cod_write_code(void)
{
MemBlock *m = state.i_memory;
int mem_base;
int i,offset;
int start_address = 0, used_flag = 0;
DirBlockInfo *dbi;
Block rb = {NULL, 0};
offset = 0;
while(m) {
mem_base = m->base << I_MEM_BITS;
for(i=mem_base; (i-mem_base) <= MAX_I_MEM; i++) {
if ((i_memory_get(state.i_memory, i) & MEM_USED_MASK) &&
((i-mem_base) < MAX_I_MEM)) {
cod_emit_opcode(i, i_memory_get(state.i_memory, i) & 0xffff);
if(used_flag == 0) {
/* Save the start address in a range of opcodes */
start_address = i;
used_flag = 1;
if(rb.block == NULL) {
gp_cod_create(&rb, &blocks);
}
}
}
else {
/* No code at address i, but we need to check if this is the
first empty address after a range of address. */
if(used_flag == 1) {
/* We need to update dir map indicating a range of memory that
is needed. This is done by writing the start and end address to
the directory map. */
gp_putl16(&rb.block[offset], 2*start_address);
gp_putl16(&rb.block[offset+2], 2*(i-1) + 1);
offset += 4;
if(offset>=COD_BLOCK_SIZE) {
/* If there are a whole bunch of non-contiguous pieces of
code then we'll get here. But most pic apps will only need
one directory block (that will give you 64 ranges or non-
contiguous chunks of pic code). */
write_cod_range_block(start_address, &rb);
gp_cod_delete(&rb);
offset = 0;
}
used_flag = 0;
}
}
}
if(offset) {
write_cod_range_block(start_address, &rb);
gp_cod_delete(&rb);
offset = 0;
used_flag = 0;
}
m = m->next;
}
/* write the code image blocks */
dbi = &main_dir;
do {
for(i=0; i<COD_CODE_IMAGE_BLOCKS; i++)
if(dbi->cod_image_blocks[i].block) {
write_cod_block(dbi,
COD_DIR_CODE+i*2,
COD_DIR_CODE+i*2,
&dbi->cod_image_blocks[i]);
free(dbi->cod_image_blocks[i].block);
}
dbi = dbi->next_dir_block_info;
} while(dbi);
}
static void
write_directory(void)
{
DirBlockInfo *dbi;
dbi = &main_dir;
do {
fseek(state.cod.f,COD_BLOCK_SIZE * dbi->dir.block_number, SEEK_SET);
fwrite(dbi->dir.block, 1, COD_BLOCK_SIZE, state.cod.f);
dbi = dbi->next_dir_block_info;
} while(dbi);
}
void
cod_close_file(void)
{
if(!state.cod.enabled)
return;
cod_lst_line(COD_LAST_LST_LINE);
write_file_block();
cod_write_code();
gp_cod_strncpy(&main_dir.dir.block[COD_DIR_PROCESSOR],
state.processor_info->names[2],
COD_DIR_LSYMTAB - COD_DIR_PROCESSOR);
write_directory();
fclose(state.cod.f);
free(main_dir.dir.block);
}
syntax highlighted by Code2HTML, v. 0.9.1