X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fld65%2Fo65.c;h=6845efed112d8acad023b1d3efaa7f289eb284fa;hb=4786caf496bd8e6989abee74017aa098df860c41;hp=c1441037b94df66279035673cfdd88b1b03fcad8;hpb=5ee8618510eacaca1f779612103bf709f59e9450;p=cc65 diff --git a/src/ld65/o65.c b/src/ld65/o65.c index c1441037b..6845efed1 100644 --- a/src/ld65/o65.c +++ b/src/ld65/o65.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1999-2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 1999-2010, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -35,11 +35,14 @@ #include #include +#include #include #include /* common */ +#include "chartype.h" #include "check.h" +#include "print.h" #include "version.h" #include "xmalloc.h" @@ -49,7 +52,10 @@ #include "expr.h" #include "fileio.h" #include "global.h" +#include "lineinfo.h" +#include "memarea.h" #include "o65.h" +#include "spool.h" @@ -60,8 +66,31 @@ /* Header mode bits */ +#define MF_CPU_65816 0x8000 /* Executable is for 65816 */ +#define MF_CPU_6502 0x0000 /* Executable is for the 6502 */ +#define MF_CPU_MASK 0x8000 /* Mask to extract CPU type */ + +#define MF_RELOC_PAGE 0x4000 /* Page wise relocation */ +#define MF_RELOC_BYTE 0x0000 /* Byte wise relocation */ +#define MF_RELOC_MASK 0x4000 /* Mask to extract relocation type */ + #define MF_SIZE_32BIT 0x2000 /* All size words are 32bit */ -#define MF_CPU_816 0x8000 /* Executable is for 65816 */ +#define MF_SIZE_16BIT 0x0000 /* All size words are 16bit */ +#define MF_SIZE_MASK 0x2000 /* Mask to extract size */ + +#define MF_FTYPE_OBJ 0x1000 /* Object file */ +#define MF_FTYPE_EXE 0x0000 /* Executable file */ +#define MF_FTYPE_MASK 0x1000 /* Mask to extract type */ + +#define MF_ADDR_SIMPLE 0x0800 /* Simple addressing */ +#define MF_ADDR_DEFAULT 0x0000 /* Default addressing */ +#define MF_ADDR_MASK 0x0800 /* Mask to extract addressing */ + +#define MF_ALIGN_1 0x0000 /* Bytewise alignment */ +#define MF_ALIGN_2 0x0001 /* Align words */ +#define MF_ALIGN_4 0x0002 /* Align longwords */ +#define MF_ALIGN_256 0x0003 /* Align pages (256 bytes) */ +#define MF_ALIGN_MASK 0x0003 /* Mask to extract alignment */ /* The four o65 segment types. Note: These values are identical to the values * needed for the segmentID in the o65 spec. @@ -71,18 +100,19 @@ #define O65SEG_TEXT 0x02 #define O65SEG_DATA 0x03 #define O65SEG_BSS 0x04 -#define O65SEG_ZP 0x05 +#define O65SEG_ZP 0x05 /* Relocation type codes for the o65 format */ #define O65RELOC_WORD 0x80 #define O65RELOC_HIGH 0x40 #define O65RELOC_LOW 0x20 -#define O65RELOC_SEGADR 0xc0 -#define O65RELOC_SEG 0xa0 +#define O65RELOC_SEGADR 0xC0 +#define O65RELOC_SEG 0xA0 +#define O65RELOC_MASK 0xE0 /* O65 executable file header */ -typedef struct O65Header_ O65Header; -struct O65Header_ { +typedef struct O65Header O65Header; +struct O65Header { unsigned Version; /* Version number for o65 format */ unsigned Mode; /* Mode word */ unsigned long TextBase; /* Base address of text segment */ @@ -97,75 +127,91 @@ struct O65Header_ { }; /* An o65 option */ -typedef struct O65Option_ O65Option; -struct O65Option_ { +typedef struct O65Option O65Option; +struct O65Option { O65Option* Next; /* Next in option list */ - unsigned char Type; /* Type of option */ - unsigned char Len; /* Data length */ - unsigned char Data [1]; /* Data, dynamically allocated */ + unsigned char Type; /* Type of option */ + unsigned char Len; /* Data length */ + unsigned char Data [1]; /* Data, dynamically allocated */ }; /* A o65 relocation table */ -#define RELOC_BLOCKSIZE 4096 -typedef struct O65RelocTab_ O65RelocTab; -struct O65RelocTab_ { - unsigned Size; /* Size of the table */ - unsigned Fill; /* Amount used */ - unsigned char* Buf; /* Buffer, dynamically allocated */ +typedef struct O65RelocTab O65RelocTab; +struct O65RelocTab { + unsigned Size; /* Size of the table */ + unsigned Fill; /* Amount used */ + unsigned char* Buf; /* Buffer, dynamically allocated */ }; /* Structure describing the format */ -struct O65Desc_ { - O65Header Header; /* File header */ - O65Option* Options; /* List of file options */ - ExtSymTab* Exports; /* Table with exported symbols */ - ExtSymTab* Imports; /* Table with imported symbols */ - unsigned Undef; /* Count of undefined symbols */ - FILE* F; /* The file we're writing to */ - char* Filename; /* Name of the output file */ - O65RelocTab* TextReloc; /* Relocation table for text segment */ - O65RelocTab* DataReloc; /* Relocation table for data segment */ - - unsigned TextCount; /* Number of segments assigned to .text */ - SegDesc** TextSeg; /* Array of text segments */ - unsigned DataCount; /* Number of segments assigned to .data */ - SegDesc** DataSeg; /* Array of data segments */ - unsigned BssCount; /* Number of segments assigned to .bss */ - SegDesc** BssSeg; /* Array of bss segments */ - unsigned ZPCount; /* Number of segments assigned to .zp */ - SegDesc** ZPSeg; /* Array of zp segments */ +struct O65Desc { + O65Header Header; /* File header */ + O65Option* Options; /* List of file options */ + ExtSymTab* Exports; /* Table with exported symbols */ + ExtSymTab* Imports; /* Table with imported symbols */ + unsigned Undef; /* Count of undefined symbols */ + FILE* F; /* The file we're writing to */ + const char* Filename; /* Name of the output file */ + O65RelocTab* TextReloc; /* Relocation table for text segment */ + O65RelocTab* DataReloc; /* Relocation table for data segment */ + + unsigned TextCount; /* Number of segments assigned to .text */ + SegDesc** TextSeg; /* Array of text segments */ + unsigned DataCount; /* Number of segments assigned to .data */ + SegDesc** DataSeg; /* Array of data segments */ + unsigned BssCount; /* Number of segments assigned to .bss */ + SegDesc** BssSeg; /* Array of bss segments */ + unsigned ZPCount; /* Number of segments assigned to .zp */ + SegDesc** ZPSeg; /* Array of zp segments */ /* Temporary data for writing segments */ unsigned long SegSize; O65RelocTab* CurReloc; - long LastOffs; + long LastOffs; }; /* Structure for parsing expression trees */ -typedef struct ExprDesc_ ExprDesc; -struct ExprDesc_ { - O65Desc* D; /* File format descriptor */ - long Val; /* The offset value */ +typedef struct ExprDesc ExprDesc; +struct ExprDesc { + O65Desc* D; /* File format descriptor */ + long Val; /* The offset value */ int TooComplex; /* Expression too complex */ - Section* SegRef; /* Section referenced if any */ - ExtSym* ExtRef; /* External reference if any */ + MemoryArea* MemRef; /* Memory reference if any */ + Segment* SegRef; /* Segment reference if any */ + Section* SecRef; /* Section reference if any */ + ExtSym* ExtRef; /* External reference if any */ }; /*****************************************************************************/ -/* Helper functions */ +/* Helper functions */ /*****************************************************************************/ +static ExprDesc* InitExprDesc (ExprDesc* ED, O65Desc* D) +/* Initialize an ExprDesc structure for use with O65ParseExpr */ +{ + ED->D = D; + ED->Val = 0; + ED->TooComplex = 0; + ED->MemRef = 0; + ED->SegRef = 0; + ED->SecRef = 0; + ED->ExtRef = 0; + return ED; +} + + + static void WriteSize (const O65Desc* D, unsigned long Val) /* Write a "size" word to the file */ { - if (D->Header.Mode & MF_SIZE_32BIT) { - Write32 (D->F, Val); - } else { - Write16 (D->F, (unsigned) Val); + switch (D->Header.Mode & MF_SIZE_MASK) { + case MF_SIZE_16BIT: Write16 (D->F, (unsigned) Val); break; + case MF_SIZE_32BIT: Write32 (D->F, Val); break; + default: Internal ("Invalid size in header: %04X", D->Header.Mode); } } @@ -181,20 +227,113 @@ static unsigned O65SegType (const SegDesc* S) * to check SF_ZP first. */ if (S->Flags & SF_RO) { - return O65SEG_TEXT; + return O65SEG_TEXT; } else if (S->Flags & SF_ZP) { - return O65SEG_ZP; + return O65SEG_ZP; } else if (S->Flags & SF_BSS) { - return O65SEG_BSS; + return O65SEG_BSS; } else { - return O65SEG_DATA; + return O65SEG_DATA; } } +static void CvtMemoryToSegment (ExprDesc* ED) +/* Convert a memory area into a segment by searching the list of run segments + * in this memory area and assigning the nearest one. + */ +{ + /* Get the memory area from the expression */ + MemoryArea* M = ED->MemRef; + + /* Remember the "nearest" segment and its offset */ + Segment* Nearest = 0; + unsigned long Offs = ULONG_MAX; + + /* Walk over all segments */ + unsigned I; + for (I = 0; I < CollCount (&M->SegList); ++I) { + + /* Get the segment and check if it's a run segment */ + SegDesc* S = CollAtUnchecked (&M->SegList, I); + if (S->Run == M) { + + unsigned long O; + + /* Get the segment from the segment descriptor */ + Segment* Seg = S->Seg; + + /* Check the PC. */ + if ((long) Seg->PC <= ED->Val && (O = (ED->Val - Seg->PC)) < Offs) { + /* This is the nearest segment for now */ + Offs = O; + Nearest = Seg; + + /* If we found an exact match, don't look further */ + if (Offs == 0) { + break; + } + } + } + } + + /* If we found a segment, use it and adjust the offset */ + if (Nearest) { + ED->SegRef = Nearest; + ED->MemRef = 0; + ED->Val -= Nearest->PC; + } +} + + + +static const SegDesc* FindSeg (SegDesc** const List, unsigned Count, const Segment* S) +/* Search for a segment in the given list of segment descriptors and return + * the descriptor for a segment if we found it, and NULL if not. + */ +{ + unsigned I; + + for (I = 0; I < Count; ++I) { + if (List[I]->Seg == S) { + /* Found */ + return List[I]; + } + } + + /* Not found */ + return 0; +} + + + +static const SegDesc* O65FindSeg (const O65Desc* D, const Segment* S) +/* Search for a segment in the segment lists and return it's segment descriptor */ +{ + const SegDesc* SD; + + if ((SD = FindSeg (D->TextSeg, D->TextCount, S)) != 0) { + return SD; + } + if ((SD = FindSeg (D->DataSeg, D->DataCount, S)) != 0) { + return SD; + } + if ((SD = FindSeg (D->BssSeg, D->BssCount, S)) != 0) { + return SD; + } + if ((SD = FindSeg (D->ZPSeg, D->ZPCount, S)) != 0) { + return SD; + } + + /* Not found */ + return 0; +} + + + /*****************************************************************************/ -/* Expression handling */ +/* Expression handling */ /*****************************************************************************/ @@ -210,11 +349,7 @@ static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign) switch (Expr->Op) { case EXPR_LITERAL: - if (Sign < 0) { - D->Val -= Expr->V.Val; - } else { - D->Val += Expr->V.Val; - } + D->Val += (Sign * Expr->V.IVal); break; case EXPR_SYMBOL: @@ -239,18 +374,46 @@ static void O65ParseExpr (ExprNode* Expr, ExprDesc* D, int Sign) } } else { MarkExport (E); - O65ParseExpr (E->Expr, D, Sign); + O65ParseExpr (E->Expr, D, Sign); UnmarkExport (E); } break; - case EXPR_SEGMENT: + case EXPR_SECTION: + if (D->SecRef) { + /* We cannot handle more than one segment reference in o65 */ + D->TooComplex = 1; + } else { + /* Remember the segment reference */ + D->SecRef = GetExprSection (Expr); + /* Add the offset of the section to the constant value */ + D->Val += Sign * (D->SecRef->Offs + D->SecRef->Seg->PC); + } + break; + + case EXPR_SEGMENT: if (D->SegRef) { /* We cannot handle more than one segment reference in o65 */ D->TooComplex = 1; } else { - /* Remember the segment reference */ - D->SegRef = GetExprSection (Expr); + /* Remember the segment reference */ + D->SegRef = Expr->V.Seg; + /* Add the offset of the segment to the constant value */ + D->Val += (Sign * D->SegRef->PC); + } + break; + + case EXPR_MEMAREA: + if (D->MemRef) { + /* We cannot handle more than one memory reference in o65 */ + D->TooComplex = 1; + } else { + /* Remember the memory area reference */ + D->MemRef = Expr->V.Mem; + /* Add the start address of the memory area to the constant + * value + */ + D->Val += (Sign * D->MemRef->Start); } break; @@ -287,9 +450,9 @@ static O65RelocTab* NewO65RelocTab (void) O65RelocTab* R = xmalloc (sizeof (O65RelocTab)); /* Initialize the data */ - R->Size = RELOC_BLOCKSIZE; + R->Size = 0; R->Fill = 0; - R->Buf = xmalloc (RELOC_BLOCKSIZE); + R->Buf = 0; /* Return the created struct */ return R; @@ -312,10 +475,12 @@ static void O65RelocPutByte (O65RelocTab* R, unsigned B) /* Do we have enough space in the buffer? */ if (R->Fill == R->Size) { /* We need to grow the buffer */ - unsigned char* NewBuf = xmalloc (R->Size + RELOC_BLOCKSIZE); - memcpy (NewBuf, R->Buf, R->Size); - xfree (R->Buf); - R->Buf = NewBuf; + if (R->Size) { + R->Size *= 2; + } else { + R->Size = 1024; /* Initial size */ + } + R->Buf = xrealloc (R->Buf, R->Size); } /* Put the byte into the buffer */ @@ -342,7 +507,7 @@ static void O65WriteReloc (O65RelocTab* R, FILE* F) /*****************************************************************************/ -/* Option handling */ +/* Option handling */ /*****************************************************************************/ @@ -379,7 +544,7 @@ static void FreeO65Option (O65Option* O) /*****************************************************************************/ -/* Subroutines to write o65 sections */ +/* Subroutines to write o65 sections */ /*****************************************************************************/ @@ -393,7 +558,6 @@ static void O65WriteHeader (O65Desc* D) O65Option* O; - /* Write the fixed header */ WriteData (D->F, Trailer, sizeof (Trailer)); Write8 (D->F, D->Header.Version); @@ -426,16 +590,17 @@ static void O65WriteHeader (O65Desc* D) static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size, - unsigned long Offs, void* Data) + unsigned long Offs, void* Data) /* Called from SegWrite for an expression. Evaluate the expression, check the * range and write the expression value to the file, update the relocation * table. */ { - long Diff; - long BinVal; - ExprNode* Expr; - ExprDesc ED; + long Diff; + unsigned RefCount; + long BinVal; + ExprNode* Expr; + ExprDesc ED; unsigned char RelocType; /* Cast the Data pointer to its real type, an O65Desc */ @@ -454,8 +619,8 @@ static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size, Offs += D->SegSize; /* Calulate full offset */ Diff = ((long) Offs) - D->LastOffs; while (Diff > 0xFE) { - O65RelocPutByte (D->CurReloc, 0xFF); - Diff -= 0xFE; + O65RelocPutByte (D->CurReloc, 0xFF); + Diff -= 0xFE; } O65RelocPutByte (D->CurReloc, (unsigned char) Diff); @@ -465,44 +630,50 @@ static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size, /* Determine the expression to relocate */ Expr = E; if (E->Op == EXPR_BYTE0 || E->Op == EXPR_BYTE1 || - E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 || - E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) { - /* Use the real expression */ + E->Op == EXPR_BYTE2 || E->Op == EXPR_BYTE3 || + E->Op == EXPR_WORD0 || E->Op == EXPR_WORD1) { + /* Use the real expression */ Expr = E->Left; } - /* Initialize the descriptor for expression parsing */ - ED.D = D; - ED.Val = 0; - ED.TooComplex = 0; - ED.SegRef = 0; - ED.ExtRef = 0; - /* Recursively collect information about this expression */ - O65ParseExpr (Expr, &ED, 1); + O65ParseExpr (Expr, InitExprDesc (&ED, D), 1); - /* We cannot handle both, an imported symbol and a segment ref */ - if (ED.SegRef != 0 && ED.ExtRef != 0) { - ED.TooComplex = 1; + /* We cannot handle more than one external reference */ + RefCount = (ED.MemRef != 0) + (ED.SegRef != 0) + + (ED.SecRef != 0) + (ED.ExtRef != 0); + if (RefCount > 1) { + ED.TooComplex = 1; + } + + /* If we have a memory area reference, we need to convert it into a + * segment reference. If we cannot do that, we cannot handle the + * expression. + */ + if (ED.MemRef) { + CvtMemoryToSegment (&ED); + if (ED.SegRef == 0) { + return SEG_EXPR_TOO_COMPLEX; + } } /* Bail out if we cannot handle the expression */ if (ED.TooComplex) { - return SEG_EXPR_TOO_COMPLEX; + return SEG_EXPR_TOO_COMPLEX; } - /* Safety: Check that we are really referencing a symbol or a segment */ - CHECK (ED.SegRef != 0 || ED.ExtRef != 0); + /* Safety: Check that we have exactly one reference */ + CHECK (RefCount == 1); /* Write out the offset that goes into the segment. */ BinVal = ED.Val; switch (E->Op) { - case EXPR_BYTE0: BinVal &= 0xFF; break; - case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break; - case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break; - case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break; - case EXPR_WORD0: BinVal &= 0xFFFF; break; - case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break; + case EXPR_BYTE0: BinVal &= 0xFF; break; + case EXPR_BYTE1: BinVal = (BinVal >> 8) & 0xFF; break; + case EXPR_BYTE2: BinVal = (BinVal >> 16) & 0xFF; break; + case EXPR_BYTE3: BinVal = (BinVal >> 24) & 0xFF; break; + case EXPR_WORD0: BinVal &= 0xFFFF; break; + case EXPR_WORD1: BinVal = (BinVal >> 16) & 0xFFFF; break; } WriteVal (D->F, BinVal, Size); @@ -510,45 +681,79 @@ static unsigned O65WriteExpr (ExprNode* E, int Signed, unsigned Size, * information gathered about the expression. */ if (E->Op == EXPR_BYTE0) { - RelocType = O65RELOC_LOW; + RelocType = O65RELOC_LOW; } else if (E->Op == EXPR_BYTE1) { - RelocType = O65RELOC_HIGH; + RelocType = O65RELOC_HIGH; + } else if (E->Op == EXPR_BYTE2) { + RelocType = O65RELOC_SEG; } else { - switch (Size) { + switch (Size) { - case 1: - RelocType = O65RELOC_LOW; - break; + case 1: + RelocType = O65RELOC_LOW; + break; - case 2: - RelocType = O65RELOC_WORD; - break; + case 2: + RelocType = O65RELOC_WORD; + break; - case 3: - RelocType = O65RELOC_SEGADR; - break; + case 3: + RelocType = O65RELOC_SEGADR; + break; - case 4: - /* 4 byte expression not supported by o65 */ - return SEG_EXPR_TOO_COMPLEX; + case 4: + /* 4 byte expression not supported by o65 */ + return SEG_EXPR_TOO_COMPLEX; - default: - Internal ("O65WriteExpr: Invalid expression size: %u", Size); - RelocType = 0; /* Avoid gcc warnings */ - } + default: + Internal ("O65WriteExpr: Invalid expression size: %u", Size); + RelocType = 0; /* Avoid gcc warnings */ + } } /* Determine which segment we're referencing */ - if (ED.ExtRef) { - /* Imported symbol */ - RelocType |= O65SEG_UNDEF; + if (ED.SegRef || ED.SecRef) { + + const SegDesc* Seg; + + /* Segment or section reference. */ + if (ED.SecRef) { + /* Get segment from section */ + ED.SegRef = ED.SecRef->Seg; + } + + /* Search for the segment and map it to it's o65 segmentID */ + Seg = O65FindSeg (D, ED.SegRef); + if (Seg == 0) { + /* For some reason, we didn't find this segment in the list of + * segments written to the o65 file. + */ + return SEG_EXPR_INVALID; + } + RelocType |= O65SegType (Seg); + O65RelocPutByte (D->CurReloc, RelocType); + + /* Output additional data if needed */ + switch (RelocType & O65RELOC_MASK) { + case O65RELOC_HIGH: + O65RelocPutByte (D->CurReloc, ED.Val & 0xFF); + break; + case O65RELOC_SEG: + O65RelocPutWord (D->CurReloc, ED.Val & 0xFFFF); + break; + } + + } else if (ED.ExtRef) { + /* Imported symbol */ + RelocType |= O65SEG_UNDEF; O65RelocPutByte (D->CurReloc, RelocType); /* Put the number of the imported symbol into the table */ O65RelocPutWord (D->CurReloc, ExtSymNum (ED.ExtRef)); - } else { - /* Segment reference */ + } else { + /* OOPS - something bad happened */ + Internal ("External reference not handled"); } @@ -574,38 +779,39 @@ static void O65WriteSeg (O65Desc* D, SegDesc** Seg, unsigned Count, int DoWrite) /* Get the segment from the list node */ S = Seg [I]; + /* Relocate line info for this segment */ + RelocLineInfo (S->Seg); + /* Keep the user happy */ - if (Verbose) { - printf (" Writing `%s'\n", S->Name); - } + Print (stdout, 1, " Writing `%s'\n", GetString (S->Name)); /* Write this segment */ - if (DoWrite) { - SegWrite (D->F, S->Seg, O65WriteExpr, D); - } + if (DoWrite) { + SegWrite (D->Filename, D->F, S->Seg, O65WriteExpr, D); + } - /* Mark the segment as dumped */ - S->Seg->Dumped = 1; + /* Mark the segment as dumped */ + S->Seg->Dumped = 1; - /* Calculate the total size */ - D->SegSize += S->Seg->Size; + /* Calculate the total size */ + D->SegSize += S->Seg->Size; } - /* Terminate the relocation table for the this segment */ + /* Terminate the relocation table for this segment */ if (D->CurReloc) { O65RelocPutByte (D->CurReloc, 0); } /* Check the size of the segment for overflow */ - if ((D->Header.Mode & MF_SIZE_32BIT) == 0 && D->SegSize > 0xFFFF) { - Error ("Segment overflow in file `%s'", D->Filename); + if ((D->Header.Mode & MF_SIZE_MASK) == MF_SIZE_16BIT && D->SegSize > 0xFFFF) { + Error ("Segment overflow in file `%s'", D->Filename); } } -static void O65WriteTextSeg (O65Desc* D, Memory* M) +static void O65WriteTextSeg (O65Desc* D) /* Write the code segment to the o65 output file */ { /* Initialize variables */ @@ -620,7 +826,7 @@ static void O65WriteTextSeg (O65Desc* D, Memory* M) -static void O65WriteDataSeg (O65Desc* D, Memory* M) +static void O65WriteDataSeg (O65Desc* D) /* Write the data segment to the o65 output file */ { /* Initialize variables */ @@ -635,7 +841,7 @@ static void O65WriteDataSeg (O65Desc* D, Memory* M) -static void O65WriteBssSeg (O65Desc* D, Memory* M) +static void O65WriteBssSeg (O65Desc* D) /* "Write" the bss segments to the o65 output file. This will only update * the relevant header fields. */ @@ -652,7 +858,7 @@ static void O65WriteBssSeg (O65Desc* D, Memory* M) -static void O65WriteZPSeg (O65Desc* D, Memory* M) +static void O65WriteZPSeg (O65Desc* D) /* "Write" the zeropage segments to the o65 output file. This will only update * the relevant header fields. */ @@ -672,20 +878,20 @@ static void O65WriteZPSeg (O65Desc* D, Memory* M) static void O65WriteImports (O65Desc* D) /* Write the list of imported symbols to the O65 file */ { - const ExtSym* E; + const ExtSym* S; - /* Write the number of external symbols */ + /* Write the number of imports */ WriteSize (D, ExtSymCount (D->Imports)); /* Write out the symbol names, zero terminated */ - E = ExtSymList (D->Imports); - while (E) { - /* Get the name */ - const char* Name = ExtSymName (E); - /* And write it to the output file */ - WriteData (D->F, Name, strlen (Name) + 1); - /* Next symbol */ - E = ExtSymNext (E); + S = ExtSymList (D->Imports); + while (S) { + /* Get the name */ + const char* Name = GetString (ExtSymName (S)); + /* And write it to the output file */ + WriteData (D->F, Name, strlen (Name) + 1); + /* Next symbol */ + S = ExtSymNext (S); } } @@ -710,8 +916,87 @@ static void O65WriteDataReloc (O65Desc* D) static void O65WriteExports (O65Desc* D) /* Write the list of exports */ { - /* For now... */ - WriteSize (D, 0); + const ExtSym* S; + + /* Write the number of exports */ + WriteSize (D, ExtSymCount (D->Exports)); + + /* Write out the symbol information */ + S = ExtSymList (D->Exports); + while (S) { + + ExprNode* Expr; + unsigned char SegmentID; + ExprDesc ED; + + /* Get the name */ + unsigned NameIdx = ExtSymName (S); + const char* Name = GetString (NameIdx); + + /* Get the export for this symbol. We've checked before that this + * export does really exist, so if it is unresolved, or if we don't + * find it, there is an error in the linker code. + */ + Export* E = FindExport (NameIdx); + if (E == 0 || IsUnresolvedExport (E)) { + Internal ("Unresolved export `%s' found in O65WriteExports", Name); + } + + /* Get the expression for the symbol */ + Expr = E->Expr; + + /* Recursively collect information about this expression */ + O65ParseExpr (Expr, InitExprDesc (&ED, D), 1); + + /* We cannot handle expressions with imported symbols, or expressions + * with more than one segment reference here + */ + if (ED.ExtRef != 0 || (ED.SegRef != 0 && ED.SecRef != 0)) { + ED.TooComplex = 1; + } + + /* Bail out if we cannot handle the expression */ + if (ED.TooComplex) { + Error ("Expression for symbol `%s' is too complex", Name); + } + + /* Determine the segment id for the expression */ + if (ED.SegRef != 0 || ED.SecRef != 0) { + + const SegDesc* Seg; + + /* Segment or section reference */ + if (ED.SecRef != 0) { + ED.SegRef = ED.SecRef->Seg; /* Get segment from section */ + } + + /* Search for the segment and map it to it's o65 segmentID */ + Seg = O65FindSeg (D, ED.SegRef); + if (Seg == 0) { + /* For some reason, we didn't find this segment in the list of + * segments written to the o65 file. + */ + Error ("Segment for symbol `%s' is undefined", Name); + } + SegmentID = O65SegType (Seg); + + } else { + + /* Absolute value */ + SegmentID = O65SEG_ABS; + + } + + /* Write the name to the output file */ + WriteData (D->F, Name, strlen (Name) + 1); + + /* Output the segment id followed by the literal value */ + Write8 (D->F, SegmentID); + WriteSize (D, ED.Val); + + /* Next symbol */ + S = ExtSymNext (S); + } } @@ -795,10 +1080,26 @@ void FreeO65Desc (O65Desc* D) -void O65Set816 (O65Desc* D) +void O65Set6502 (O65Desc* D) +/* Enable 6502 mode */ +{ + D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_6502; +} + + + +void O65Set65816 (O65Desc* D) /* Enable 816 mode */ { - D->Header.Mode |= MF_CPU_816; + D->Header.Mode = (D->Header.Mode & ~MF_CPU_MASK) | MF_CPU_65816; +} + + + +void O65SetSmallModel (O65Desc* D) +/* Enable a small memory model executable */ +{ + D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_16BIT; } @@ -806,7 +1107,7 @@ void O65Set816 (O65Desc* D) void O65SetLargeModel (O65Desc* D) /* Enable a large memory model executable */ { - D->Header.Mode |= MF_SIZE_32BIT; + D->Header.Mode = (D->Header.Mode & ~MF_SIZE_MASK) | MF_SIZE_32BIT; } @@ -815,14 +1116,14 @@ void O65SetAlignment (O65Desc* D, unsigned Align) /* Set the executable alignment */ { /* Remove all alignment bits from the mode word */ - D->Header.Mode &= ~0x0003; + D->Header.Mode &= ~MF_ALIGN_MASK; /* Set the alignment bits */ switch (Align) { - case 1: break; - case 2: D->Header.Mode |= 0x01; break; - case 4: D->Header.Mode |= 0x02; break; - case 256: D->Header.Mode |= 0x03; break; + case 1: D->Header.Mode |= MF_ALIGN_1; break; + case 2: D->Header.Mode |= MF_ALIGN_2; break; + case 4: D->Header.Mode |= MF_ALIGN_4; break; + case 256: D->Header.Mode |= MF_ALIGN_256; break; default: Error ("Invalid alignment for O65 format: %u", Align); } } @@ -842,32 +1143,35 @@ void O65SetOption (O65Desc* D, unsigned Type, const void* Data, unsigned DataLen -void O65SetOS (O65Desc* D, unsigned OS) +void O65SetOS (O65Desc* D, unsigned OS, unsigned Version, unsigned Id) /* Set an option describing the target operating system */ { - static const unsigned char OSA65 [2] = { O65OS_OSA65, 0 }; - static const unsigned char Lunix [2] = { O65OS_LUNIX, 0 }; + /* Setup the option data */ + unsigned char Opt[4]; + Opt[0] = OS; + Opt[1] = Version; - /* Write the correct option */ + /* Write the correct option length */ switch (OS) { - case O65OS_OSA65: - O65SetOption (D, O65OPT_OS, OSA65, sizeof (OSA65)); - break; - - case O65OS_LUNIX: - O65SetOption (D, O65OPT_OS, Lunix, sizeof (Lunix)); + case O65OS_CC65: + /* Set the 16 bit id */ + Opt[2] = (unsigned char) Id; + Opt[3] = (unsigned char) (Id >> 8); + O65SetOption (D, O65OPT_OS, Opt, 4); break; default: - Internal ("Trying to set invalid O65 operating system: %u", OS); + /* No id for OS/A65, Lunix, and unknown OSes */ + O65SetOption (D, O65OPT_OS, Opt, 2); + break; } } -ExtSym* O65GetImport (O65Desc* D, const char* Ident) +ExtSym* O65GetImport (O65Desc* D, unsigned Ident) /* Return the imported symbol or NULL if not found */ { /* Retrieve the symbol from the table */ @@ -876,7 +1180,7 @@ ExtSym* O65GetImport (O65Desc* D, const char* Ident) -void O65SetImport (O65Desc* D, const char* Ident) +void O65SetImport (O65Desc* D, unsigned Ident) /* Set an imported identifier */ { /* Insert the entry into the table */ @@ -885,7 +1189,7 @@ void O65SetImport (O65Desc* D, const char* Ident) -ExtSym* O65GetExport (O65Desc* D, const char* Ident) +ExtSym* O65GetExport (O65Desc* D, unsigned Ident) /* Return the exported symbol or NULL if not found */ { /* Retrieve the symbol from the table */ @@ -894,20 +1198,27 @@ ExtSym* O65GetExport (O65Desc* D, const char* Ident) -void O65SetExport (O65Desc* D, const char* Ident) +void O65SetExport (O65Desc* D, unsigned Ident) /* Set an exported identifier */ { + /* Get the export for this symbol and check if it does exist and is + * a resolved symbol. + */ + Export* E = FindExport (Ident); + if (E == 0 || IsUnresolvedExport (E)) { + Error ("Unresolved export: `%s'", GetString (Ident)); + } + /* Insert the entry into the table */ NewExtSym (D->Exports, Ident); } -static void O65SetupSegments (O65Desc* D, Memory* M) +static void O65SetupSegments (O65Desc* D, File* F) /* Setup segment assignments */ { - MemListNode* N; - SegDesc* S; + unsigned I; unsigned TextIdx, DataIdx, BssIdx, ZPIdx; /* Initialize the counters */ @@ -916,24 +1227,27 @@ static void O65SetupSegments (O65Desc* D, Memory* M) D->BssCount = 0; D->ZPCount = 0; - /* Walk through the memory list and count the segment types */ - N = M->SegList; - while (N) { - - /* Get the segment from the list node */ - S = N->Seg; - - /* Check the segment type. */ - switch (O65SegType (S)) { - case O65SEG_TEXT: D->TextCount++; break; - case O65SEG_DATA: D->DataCount++; break; - case O65SEG_BSS: D->BssCount++; break; - case O65SEG_ZP: D->ZPCount++; break; - default: Internal ("Invalid return from O65SegType"); - } - - /* Next segment node */ - N = N->Next; + /* Walk over the memory list */ + for (I = 0; I < CollCount (&F->MemoryAreas); ++I) { + /* Get this entry */ + MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I); + + /* Walk through the segment list and count the segment types */ + unsigned J; + for (J = 0; J < CollCount (&M->SegList); ++J) { + + /* Get the segment */ + SegDesc* S = CollAtUnchecked (&M->SegList, J); + + /* Check the segment type. */ + switch (O65SegType (S)) { + case O65SEG_TEXT: D->TextCount++; break; + case O65SEG_DATA: D->DataCount++; break; + case O65SEG_BSS: D->BssCount++; break; + case O65SEG_ZP: D->ZPCount++; break; + default: Internal ("Invalid return from O65SegType"); + } + } } /* Allocate memory according to the numbers */ @@ -944,29 +1258,32 @@ static void O65SetupSegments (O65Desc* D, Memory* M) /* Walk again through the list and setup the segment arrays */ TextIdx = DataIdx = BssIdx = ZPIdx = 0; - N = M->SegList; - while (N) { - - /* Get the segment from the list node */ - S = N->Seg; - - /* Check the segment type. */ - switch (O65SegType (S)) { - case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break; - case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break; - case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break; - case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break; - default: Internal ("Invalid return from O65SegType"); - } - - /* Next segment node */ - N = N->Next; + for (I = 0; I < CollCount (&F->MemoryAreas); ++I) { + /* Get this entry */ + MemoryArea* M = CollAtUnchecked (&F->MemoryAreas, I); + + /* Walk over the segment list and check the segment types */ + unsigned J; + for (J = 0; J < CollCount (&M->SegList); ++J) { + + /* Get the segment */ + SegDesc* S = CollAtUnchecked (&M->SegList, J); + + /* Check the segment type. */ + switch (O65SegType (S)) { + case O65SEG_TEXT: D->TextSeg [TextIdx++] = S; break; + case O65SEG_DATA: D->DataSeg [DataIdx++] = S; break; + case O65SEG_BSS: D->BssSeg [BssIdx++] = S; break; + case O65SEG_ZP: D->ZPSeg [ZPIdx++] = S; break; + default: Internal ("Invalid return from O65SegType"); + } + } } } -static int O65Unresolved (const char* Name, void* D) +static int O65Unresolved (unsigned Name, void* D) /* Called if an unresolved symbol is encountered */ { /* Check if the symbol is an imported o65 symbol */ @@ -982,67 +1299,99 @@ static int O65Unresolved (const char* Name, void* D) +static void O65SetupHeader (O65Desc* D) +/* Set additional stuff in the header */ +{ + /* Set the base addresses of the segments */ + if (D->TextCount > 0) { + SegDesc* FirstSeg = D->TextSeg [0]; + D->Header.TextBase = FirstSeg->Seg->PC; + } + if (D->DataCount > 0) { + SegDesc* FirstSeg = D->DataSeg [0]; + D->Header.DataBase = FirstSeg->Seg->PC; + } + if (D->BssCount > 0) { + SegDesc* FirstSeg = D->BssSeg [0]; + D->Header.BssBase = FirstSeg->Seg->PC; + } + if (D->ZPCount > 0) { + SegDesc* FirstSeg = D->ZPSeg [0]; + D->Header.ZPBase = FirstSeg->Seg->PC; + } + + /* If we have byte wise relocation and an alignment of 1, we can set + * the "simple addressing" bit in the header. + */ + if ((D->Header.Mode & MF_RELOC_MASK) == MF_RELOC_BYTE && + (D->Header.Mode & MF_ALIGN_MASK) == MF_ALIGN_1) { + D->Header.Mode = (D->Header.Mode & ~MF_ADDR_MASK) | MF_ADDR_SIMPLE; + } +} + + + void O65WriteTarget (O65Desc* D, File* F) /* Write an o65 output file */ { - Memory* M; - char OptBuf [256]; /* Buffer for option strings */ - time_t T; + char OptBuf [256]; /* Buffer for option strings */ + unsigned OptLen; + time_t T; /* Place the filename in the control structure */ - D->Filename = F->Name; - - /* The o65 format uses only one memory area per file. Check that. */ - M = F->MemList; - if (M->Next != 0) { - Warning ("Cannot handle more than one memory area for o65 format"); - } + D->Filename = GetString (F->Name); /* Check for unresolved symbols. The function O65Unresolved is called * if we get an unresolved symbol. */ D->Undef = 0; /* Reset the counter */ - CheckExports (O65Unresolved, D); + CheckUnresolvedImports (O65Unresolved, D); if (D->Undef > 0) { /* We had unresolved symbols, cannot create output file */ Error ("%u unresolved external(s) found - cannot create output file", D->Undef); } /* Setup the segment arrays */ - O65SetupSegments (D, M); + O65SetupSegments (D, F); + + /* Setup additional stuff in the header */ + O65SetupHeader (D); /* Open the file */ - D->F = fopen (F->Name, "wb"); + D->F = fopen (D->Filename, "wb"); if (D->F == 0) { - Error ("Cannot open `%s': %s", F->Name, strerror (errno)); + Error ("Cannot open `%s': %s", D->Filename, strerror (errno)); } /* Keep the user happy */ - if (Verbose) { - printf ("Opened `%s'...\n", F->Name); - } + Print (stdout, 1, "Opened `%s'...\n", D->Filename); /* Define some more options: A timestamp and the linker version */ T = time (0); strcpy (OptBuf, ctime (&T)); - O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, strlen (OptBuf) + 1); - sprintf (OptBuf, "ld65 V%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH); + OptLen = strlen (OptBuf); + while (OptLen > 0 && IsControl (OptBuf[OptLen-1])) { + --OptLen; + } + OptBuf[OptLen] = '\0'; + O65SetOption (D, O65OPT_TIMESTAMP, OptBuf, OptLen + 1); + sprintf (OptBuf, "ld65 V%s", GetVersionAsString ()); O65SetOption (D, O65OPT_ASM, OptBuf, strlen (OptBuf) + 1); /* Write the header */ O65WriteHeader (D); /* Write the text segment */ - O65WriteTextSeg (D, M); + O65WriteTextSeg (D); /* Write the data segment */ - O65WriteDataSeg (D, M); + O65WriteDataSeg (D); /* "Write" the bss segments */ - O65WriteBssSeg (D, M); + O65WriteBssSeg (D); /* "Write" the zeropage segments */ - O65WriteZPSeg (D, M); + O65WriteZPSeg (D); /* Write the undefined references list */ O65WriteImports (D); @@ -1062,7 +1411,7 @@ void O65WriteTarget (O65Desc* D, File* F) /* Close the file */ if (fclose (D->F) != 0) { - Error ("Cannot write to `%s': %s", F->Name, strerror (errno)); + Error ("Cannot write to `%s': %s", D->Filename, strerror (errno)); } /* Reset the file and filename */