kopia lustrzana https://github.com/hoglet67/RGBtoHDMI
629 wiersze
14 KiB
C
629 wiersze
14 KiB
C
/* output_o65.c Andre Fachat's o65 relocatable binary format for vasm */
|
|
/* (c) in 2021 by Frank Wille */
|
|
|
|
#include <time.h>
|
|
#include "vasm.h"
|
|
|
|
#if defined(OUTO65) && defined(VASM_CPU_650X)
|
|
static char *out_copyright="vasm o65 output module 0.1 (c) 2021 Frank Wille";
|
|
|
|
#define S_ZERO (S_BSS+1)
|
|
#define NSECS (S_ZERO+1) /* o65 supports text, data, bss, zero */
|
|
|
|
/* o65 mode flags */
|
|
#define MO65_65816 (1<<15)
|
|
#define MO65_PAGED (1<<14)
|
|
#define MO65_LONG (1<<13)
|
|
#define MO65_OBJ (1<<12)
|
|
#define MO65_SIMPLE (1<<11)
|
|
#define MO65_CHAIN (1<<10)
|
|
#define MO65_BSSZERO (1<<9)
|
|
|
|
struct o65Import {
|
|
struct o65Import *next;
|
|
char *name;
|
|
int idx;
|
|
};
|
|
|
|
struct o65Reloc {
|
|
struct o65Reloc *next;
|
|
utaddr offset;
|
|
uint8_t type; /* o65 absolute relocation type */
|
|
uint8_t segid;
|
|
uint16_t import; /* valid when segid=SEG_UND */
|
|
taddr addend;
|
|
};
|
|
|
|
/* segid values */
|
|
#define SEG_UND 0 /* reference to external symbol */
|
|
#define SEG_ABS 1 /* reference to absolute symbol */
|
|
#define SEG_SEC 2 /* reference to text(2), data(3), bss(4), zero(5) */
|
|
|
|
static section *sections[NSECS];
|
|
static utaddr secsizes[NSECS];
|
|
static utaddr secorgs[NSECS];
|
|
static int org_is_set[NSECS];
|
|
static int o65size = 2; /* word size may be 2 bytes or 4 bytes */
|
|
|
|
static int secalign; /* minimum alignment of all sections (LS zero-bits) */
|
|
static int stacksz;
|
|
static int paged;
|
|
static int bsszero;
|
|
|
|
static int fopts;
|
|
static const char *foauthor;
|
|
static const char *foname;
|
|
|
|
#define IMPHTABSIZE 0x100
|
|
static hashtable *importhash;
|
|
static struct o65Import *importlist;
|
|
static int num_imports;
|
|
static struct o65Reloc *relocs[S_DATA+1]; /* text and data relocs */
|
|
|
|
|
|
static int is_bss_name(const char *p)
|
|
{
|
|
size_t len = strlen(p);
|
|
|
|
while (len >= 3) {
|
|
if (toupper((unsigned char)p[0]) == 'B' &&
|
|
toupper((unsigned char)p[1]) == 'S' &&
|
|
toupper((unsigned char)p[2]) == 'S')
|
|
return 1;
|
|
p++;
|
|
len--;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
static void fwsize(FILE *f,unsigned w)
|
|
{
|
|
if (o65size == 2)
|
|
fw16(f,w,0);
|
|
else if (o65size == 4)
|
|
fw32(f,w,0);
|
|
else
|
|
ierror(0);
|
|
}
|
|
|
|
|
|
static void o65_initwrite(section *sec)
|
|
{
|
|
utaddr addr;
|
|
int i,valid;
|
|
|
|
/* find exactly one .text, .data, .bss and .zero section */
|
|
for (; sec; sec=sec->next) {
|
|
/* section size is assumed to be in in (sec->pc - sec->org), otherwise
|
|
we would have to calculate it from the atoms and store it there */
|
|
if ((sec->pc - sec->org) > 0 || (sec->flags & HAS_SYMBOLS)) {
|
|
i = get_sec_type(sec);
|
|
|
|
if (i == S_ABS)
|
|
i = (sections[S_TEXT]!=NULL&§ions[S_DATA]==NULL) ? S_DATA : S_TEXT;
|
|
else if (i<S_TEXT || i>S_BSS) {
|
|
output_error(3,sec->attr); /* section attributes not supported */
|
|
i = S_TEXT;
|
|
}
|
|
else if (i == S_BSS && !is_bss_name(sec->name))
|
|
i = S_ZERO;
|
|
|
|
if (!sections[i]) {
|
|
sections[i] = sec;
|
|
secsizes[i] = get_sec_size(sec);
|
|
sec->idx = i; /* section index 0:text, 1:data, 2:bss, 3:zero */
|
|
while (sec->align > (taddr)1<<secalign)
|
|
++secalign;
|
|
}
|
|
else
|
|
output_error(7,sec->name);
|
|
}
|
|
}
|
|
|
|
/* calculate section start addresses */
|
|
for (i=0,valid=0; i<NSECS; i++) {
|
|
if (sec = sections[i]) {
|
|
if (!(sec->flags & ABSOLUTE)) {
|
|
if (!org_is_set[i]) {
|
|
if (!valid) {
|
|
addr = sec->org;
|
|
valid = 1;
|
|
}
|
|
if (exec_out && sec->org==0) {
|
|
/* sections will be consecutive in executables */
|
|
sec->org = secorgs[i] = addr;
|
|
}
|
|
}
|
|
else {
|
|
/* start address set by option */
|
|
addr = sec->org = secorgs[i];
|
|
valid = 1;
|
|
}
|
|
}
|
|
else {
|
|
/* absolute code always has its fixed address */
|
|
addr = secorgs[i] = sec->org;
|
|
valid = 1;
|
|
}
|
|
addr += secsizes[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef VASM_CPU_650X
|
|
static uint16_t o65_cpu(void)
|
|
{
|
|
uint16_t c;
|
|
|
|
if (cpu_type & ILL)
|
|
c = 4; /* NMOS6502 including illegal opcodes */
|
|
else if (cpu_type & HU6280)
|
|
c = 1; /* @@@ HU6280 bit does not exit - flag as C02 */
|
|
else if (cpu_type & CSGCE02)
|
|
c = 3; /* 65CE02 */
|
|
else if (cpu_type & WDC02)
|
|
c = 2; /* @@@ WDC02 has more instructions than SC02 */
|
|
else if (cpu_type & M65C02)
|
|
c = 1; /* 65C02 */
|
|
else
|
|
c = 0;
|
|
return c << 4;
|
|
}
|
|
#else /* 65816 */
|
|
static uint16_t o65_cpu(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
static int o65_simple(void)
|
|
{
|
|
/* return true, when text, data, bss are consecutive */
|
|
utaddr addr;
|
|
int i;
|
|
|
|
for (i=0; i<=S_BSS; i++) {
|
|
if (sections[i] != NULL)
|
|
break;
|
|
}
|
|
if (i <= S_BSS) {
|
|
for (addr=secorgs[i]; i<=S_BSS; i++) {
|
|
if (sections[i] != NULL) {
|
|
if ((utaddr)secorgs[i] != addr)
|
|
return 0;
|
|
addr += secsizes[i];
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void o65_fopt(FILE *f,int type,const char *data,size_t len)
|
|
{
|
|
if (len <= 253) {
|
|
fw8(f,len+2);
|
|
fw8(f,type);
|
|
fwdata(f,data,len);
|
|
}
|
|
else
|
|
output_error(14,type,len+2); /* max file option size exceeded */
|
|
}
|
|
|
|
|
|
static void o65_header(FILE *f)
|
|
{
|
|
static const uint8_t o65magic[] = { 1,0,'o','6','5',0 };
|
|
uint16_t mode;
|
|
int i;
|
|
|
|
/* magic id */
|
|
fwdata(f,o65magic,sizeof(o65magic));
|
|
|
|
/* mode */
|
|
mode = secalign>2 ? 3 : (secalign&3);
|
|
mode |= o65_cpu();
|
|
if (paged)
|
|
mode |= MO65_PAGED;
|
|
if (exec_out) {
|
|
if (bsszero)
|
|
mode |= MO65_BSSZERO;
|
|
if (o65_simple())
|
|
mode |= MO65_SIMPLE;
|
|
}
|
|
else
|
|
mode |= MO65_OBJ;
|
|
fw16(f,mode,0);
|
|
|
|
/* section origins and sizes */
|
|
for (i=0; i<NSECS; i++) {
|
|
fwsize(f,secorgs[i]);
|
|
fwsize(f,secsizes[i]);
|
|
}
|
|
fwsize(f,stacksz);
|
|
|
|
if (fopts) {
|
|
/* file options */
|
|
if ((fopts & 2) && foname==NULL)
|
|
foname = (const char *)outname;
|
|
|
|
if (foname)
|
|
o65_fopt(f,0,foname,strlen(foname)+1);
|
|
|
|
if (fopts & 2) {
|
|
/* write assembler name and version */
|
|
extern char *copyright;
|
|
char *prod = cnvstr(copyright,strchr(copyright,'(')-copyright-1);
|
|
|
|
o65_fopt(f,2,prod,strlen(prod)+1);
|
|
myfree(prod);
|
|
}
|
|
|
|
if (foauthor)
|
|
o65_fopt(f,3,foauthor,strlen(foauthor)+1);
|
|
|
|
if (fopts & 2) {
|
|
/* write creation date */
|
|
char datebuf[32];
|
|
time_t now;
|
|
|
|
(void)time(&now);
|
|
strftime(datebuf,32,"%a %b %d %T %Z %Y",localtime(&now));
|
|
o65_fopt(f,4,datebuf,strlen(datebuf)+1);
|
|
}
|
|
}
|
|
fw8(f,0);
|
|
}
|
|
|
|
|
|
static int o65_rtype(rlist *rl)
|
|
{
|
|
if (rl->type == REL_ABS) {
|
|
nreloc *r = (nreloc *)rl->reloc;
|
|
|
|
if (r->bitoffset)
|
|
return 0;
|
|
|
|
if ((r->mask & 0xffffff) != 0xffffff && r->size==8) {
|
|
if ((r->mask & 0xffffff) == 0xff0000)
|
|
return 0xa0; /* segment-byte of 24-bit address */
|
|
else if ((r->mask & 0xffff) == 0xff00)
|
|
return 0x40; /* high-byte of 16-bit address */
|
|
else if (r->mask == 0xff)
|
|
return 0x20; /* low-byte of 16-bit address */
|
|
}
|
|
else if (r->size == 8)
|
|
return 0x20; /* probably an 8-bit xref to an absolute value */
|
|
else if (r->size == 16)
|
|
return 0x80; /* 16-bit address */
|
|
else if (r->size == 24)
|
|
return 0xc0; /* 24-bit address */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void add_o65reloc(int secno,taddr offs,uint8_t type,uint8_t segid,
|
|
uint16_t import,taddr addend)
|
|
/* add to section's reloc-list, sorted by offset */
|
|
{
|
|
struct o65Reloc *new,*r,**link;
|
|
|
|
if (secno!=S_TEXT && secno!=S_DATA)
|
|
ierror(0);
|
|
|
|
new = mymalloc(sizeof(struct o65Reloc));
|
|
new->offset = offs;
|
|
new->type = type;
|
|
new->segid = segid;
|
|
new->import = import;
|
|
new->addend = addend;
|
|
|
|
link = &relocs[secno];
|
|
while (r = *link) {
|
|
if ((utaddr)offs < (utaddr)r->offset)
|
|
break;
|
|
link = &r->next;
|
|
}
|
|
new->next = r;
|
|
*link = new;
|
|
}
|
|
|
|
|
|
static int add_import(char *name)
|
|
{
|
|
static struct o65Import *last;
|
|
hashdata data;
|
|
|
|
if (!find_name(importhash,name,&data)) {
|
|
struct o65Import *impnode = mymalloc(sizeof(struct o65Import));
|
|
|
|
impnode->next = NULL;
|
|
impnode->name = name;
|
|
impnode->idx = num_imports++;
|
|
if (importlist) {
|
|
last->next = impnode;
|
|
last = impnode;
|
|
}
|
|
else
|
|
importlist = last = impnode;
|
|
data.ptr = impnode;
|
|
add_hashentry(importhash,name,data);
|
|
}
|
|
return ((struct o65Import *)data.ptr)->idx;
|
|
}
|
|
|
|
|
|
static void do_relocs(int secno,taddr offs,atom *p)
|
|
/* Try to resolve all relocations in a DATA or SPACE atom. */
|
|
{
|
|
rlist *rl;
|
|
|
|
if (p->type == DATA)
|
|
rl = p->content.db->relocs;
|
|
else if (p->type == SPACE)
|
|
rl = p->content.sb->relocs;
|
|
else
|
|
rl = NULL;
|
|
|
|
for (; rl!=NULL; rl=rl->next) {
|
|
uint8_t type,segid;
|
|
int import;
|
|
|
|
if (type = o65_rtype(rl)) {
|
|
symbol *sym = ((nreloc *)rl->reloc)->sym;
|
|
taddr a = ((nreloc *)rl->reloc)->addend;
|
|
|
|
if (sym->type == IMPORT) {
|
|
/* reference to external symbol, add its name to import-list */
|
|
import = add_import(sym->name);
|
|
segid = SEG_UND;
|
|
}
|
|
else {
|
|
if (sym->sec == NULL)
|
|
ierror(0);
|
|
segid = SEG_SEC + sym->sec->idx;
|
|
a += sym->sec->org;
|
|
}
|
|
|
|
switch (type) {
|
|
case 0x20: /* address low-byte */
|
|
patch_nreloc(p,rl,0,a&0xff,0);
|
|
break;
|
|
case 0x40: /* address high-byte */
|
|
patch_nreloc(p,rl,0,(a&0xff00)>>8,0);
|
|
break;
|
|
case 0xa0: /* segment-byte of 24-bit address */
|
|
patch_nreloc(p,rl,0,(a&0xff0000)>>16,0);
|
|
break;
|
|
default:
|
|
patch_nreloc(p,rl,1,a,0);
|
|
break;
|
|
}
|
|
add_o65reloc(secno,offs+((nreloc *)rl->reloc)->byteoffset,
|
|
type,segid,import,a);
|
|
}
|
|
else
|
|
unsupp_reloc_error(rl);
|
|
}
|
|
}
|
|
|
|
|
|
static void o65_writesection(FILE *f,section *sec)
|
|
{
|
|
if (sec) {
|
|
int secno = sec->idx;
|
|
utaddr pc = sec->org;
|
|
utaddr npc;
|
|
atom *a;
|
|
|
|
for (a=sec->first; a; a=a->next) {
|
|
npc = fwpcalign(f,a,sec,pc);
|
|
do_relocs(secno,npc-sec->org,a);
|
|
if (a->type == DATA)
|
|
fwdata(f,a->content.db->data,a->content.db->size);
|
|
else if (a->type == SPACE)
|
|
fwsblock(f,a->content.sb);
|
|
pc = npc + atom_size(a,sec,npc);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void o65_writeimports(FILE *f)
|
|
{
|
|
struct o65Import *impnode;
|
|
|
|
fwsize(f,num_imports);
|
|
for (impnode=importlist; impnode!=NULL; impnode=impnode->next) {
|
|
fwdata(f,impnode->name,strlen(impnode->name)+1);
|
|
num_imports--;
|
|
}
|
|
if (num_imports)
|
|
ierror(0);
|
|
}
|
|
|
|
|
|
static void o65_writerelocs(FILE *f,struct o65Reloc *r)
|
|
{
|
|
int lastoffs = -1;
|
|
int newoffs,diff;
|
|
|
|
while (r) {
|
|
newoffs = r->offset;
|
|
if ((diff = newoffs - lastoffs) <= 0)
|
|
ierror(0);
|
|
lastoffs = newoffs;
|
|
|
|
/* write offset to next relocation field */
|
|
while (diff > 254) {
|
|
fw8(f,255);
|
|
diff -= 254;
|
|
}
|
|
fw8(f,diff);
|
|
|
|
/* write reloc type and additional bytes */
|
|
fw8(f,r->type|r->segid);
|
|
if (r->segid == SEG_UND)
|
|
fwsize(f,r->import); /* index of imported symbol name */
|
|
switch (r->type) {
|
|
case 0x40: /* address high-byte */
|
|
if (!paged)
|
|
fw8(f,r->addend&0xff); /* write low-byte */
|
|
break;
|
|
case 0xa0: /* segment-byte of 24-bit address */
|
|
fw16(f,r->addend&0xffff,0);
|
|
break;
|
|
}
|
|
|
|
r = r->next;
|
|
}
|
|
fw8(f,0); /* end of reloc table */
|
|
}
|
|
|
|
|
|
static void o65_writeglobals(FILE *f,symbol *sym)
|
|
{
|
|
long cntoffs = ftell(f);
|
|
size_t n = 0;
|
|
|
|
fwsize(f,0); /* remember to write number of globals here */
|
|
|
|
for (; sym!=NULL; sym=sym->next) {
|
|
if ((sym->type==LABSYM || sym->type==EXPRESSION) &&
|
|
!(sym->flags & VASMINTERN) && (sym->flags & EXPORT)) {
|
|
|
|
if (sym->flags & WEAK) {
|
|
output_error(10,sym->name); /* weak symbol not supported */
|
|
continue;
|
|
}
|
|
|
|
/* write global label or expression symbol */
|
|
fwdata(f,sym->name,strlen(sym->name)+1);
|
|
if (sym->type == LABSYM) {
|
|
if (sym->sec == NULL)
|
|
ierror(0);
|
|
fw8(f,SEG_SEC+sym->sec->idx);
|
|
fwsize(f,sym->sec->org+sym->pc);
|
|
}
|
|
else {
|
|
fw8(f,SEG_ABS);
|
|
fwsize(f,get_sym_value(sym));
|
|
}
|
|
n++;
|
|
}
|
|
}
|
|
|
|
/* patch number of exported symbols */
|
|
if (n) {
|
|
fseek(f,cntoffs,SEEK_SET);
|
|
fwsize(f,n);
|
|
fseek(f,0,SEEK_END);
|
|
}
|
|
}
|
|
|
|
|
|
static void write_output(FILE *f,section *sec,symbol *sym)
|
|
{
|
|
importhash = new_hashtable(IMPHTABSIZE);
|
|
o65_initwrite(sec);
|
|
o65_header(f);
|
|
o65_writesection(f,sections[S_TEXT]);
|
|
o65_writesection(f,sections[S_DATA]);
|
|
o65_writeimports(f);
|
|
o65_writerelocs(f,relocs[S_TEXT]);
|
|
o65_writerelocs(f,relocs[S_DATA]);
|
|
o65_writeglobals(f,sym);
|
|
}
|
|
|
|
|
|
static int common_args(char *p)
|
|
{
|
|
long val;
|
|
|
|
if (!strncmp(p,"-bss=",5)) {
|
|
sscanf(p+5,"%li",&val);
|
|
secorgs[S_BSS] = val;
|
|
org_is_set[S_BSS] = 1;
|
|
}
|
|
else if (!strncmp(p,"-data=",6)) {
|
|
sscanf(p+6,"%li",&val);
|
|
secorgs[S_DATA] = val;
|
|
org_is_set[S_DATA] = 1;
|
|
}
|
|
else if (!strcmp(p,"-fopts"))
|
|
fopts |= 3;
|
|
else if (!strncmp(p,"-foauthor=",10)) {
|
|
fopts |= 1;
|
|
foauthor = get_str_arg(p+10);
|
|
}
|
|
else if (!strncmp(p,"-foname=",8)) {
|
|
fopts |= 1;
|
|
foname = get_str_arg(p+8);
|
|
}
|
|
else if (!strcmp(p,"-paged"))
|
|
paged = 1;
|
|
else if (!strncmp(p,"-secalign=",10)) {
|
|
sscanf(p+10,"%li",&val);
|
|
if (val<0 || val>3)
|
|
general_error(78,p);
|
|
else
|
|
secalign = val>2 ? 8 : val;
|
|
}
|
|
else if (!strncmp(p,"-stack=",7)) {
|
|
sscanf(p+7,"%li",&val);
|
|
if (val < 0)
|
|
general_error(78,p);
|
|
else
|
|
stacksz = val;
|
|
}
|
|
else if (!strncmp(p,"-text=",6)) {
|
|
sscanf(p+6,"%li",&val);
|
|
secorgs[S_TEXT] = val;
|
|
org_is_set[S_TEXT] = 1;
|
|
}
|
|
else if (!strncmp(p,"-zero=",6)) {
|
|
sscanf(p+6,"%li",&val);
|
|
secorgs[S_ZERO] = val;
|
|
org_is_set[S_ZERO] = 1;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int exec_args(char *p)
|
|
{
|
|
if (!strcmp(p,"-bsszero")) {
|
|
bsszero = 1;
|
|
return 1;
|
|
}
|
|
return common_args(p);
|
|
}
|
|
|
|
|
|
int init_output_o65(char **cp,void (**wo)(FILE *,section *,symbol *),
|
|
int (**oa)(char *))
|
|
{
|
|
*cp = out_copyright;
|
|
*wo = write_output;
|
|
*oa = exec_out ? exec_args : common_args;
|
|
secname_attr = 1;
|
|
return 1;
|
|
}
|
|
|
|
#else
|
|
|
|
int init_output_o65(char **cp,void (**wo)(FILE *,section *,symbol *),
|
|
int (**oa)(char *))
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|