2000-05-28 13:40:48 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* config.c */
|
|
|
|
/* */
|
|
|
|
/* Target configuration file for the ld65 linker */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* */
|
2002-04-25 18:11:05 +00:00
|
|
|
/* (C) 1998-2002 Ullrich von Bassewitz */
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Wacholderweg 14 */
|
|
|
|
/* D-70597 Stuttgart */
|
|
|
|
/* EMail: uz@musoftware.de */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
|
|
/* arising from the use of this software. */
|
|
|
|
/* */
|
|
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
|
|
/* freely, subject to the following restrictions: */
|
|
|
|
/* */
|
|
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
|
|
/* claim that you wrote the original software. If you use this software */
|
|
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
|
|
/* appreciated but is not required. */
|
|
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
|
|
/* be misrepresented as being the original software. */
|
|
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
|
|
/* distribution. */
|
|
|
|
/* */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2000-08-01 15:17:43 +00:00
|
|
|
/* common */
|
|
|
|
#include "check.h"
|
|
|
|
#include "bitops.h"
|
2001-03-10 10:21:03 +00:00
|
|
|
#include "print.h"
|
2000-08-01 15:17:43 +00:00
|
|
|
#include "xmalloc.h"
|
2000-11-20 15:22:57 +00:00
|
|
|
|
2000-08-01 15:17:43 +00:00
|
|
|
/* ld65 */
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "bin.h"
|
|
|
|
#include "binfmt.h"
|
2000-11-20 15:22:57 +00:00
|
|
|
#include "condes.h"
|
|
|
|
#include "error.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "exports.h"
|
2000-11-20 15:22:57 +00:00
|
|
|
#include "global.h"
|
|
|
|
#include "o65.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "scanner.h"
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Data */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* File list */
|
|
|
|
static File* FileList; /* Single linked list */
|
|
|
|
static unsigned FileCount; /* Number of entries in the list */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Memory list */
|
|
|
|
static Memory* MemoryList; /* Single linked list */
|
|
|
|
static Memory* MemoryLast; /* Last element in list */
|
|
|
|
static unsigned MemoryCount; /* Number of entries in the list */
|
|
|
|
|
|
|
|
/* Memory attributes */
|
|
|
|
#define MA_START 0x0001
|
|
|
|
#define MA_SIZE 0x0002
|
|
|
|
#define MA_TYPE 0x0004
|
|
|
|
#define MA_FILE 0x0008
|
|
|
|
#define MA_DEFINE 0x0010
|
|
|
|
#define MA_FILL 0x0020
|
|
|
|
#define MA_FILLVAL 0x0040
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Segment list */
|
|
|
|
SegDesc* SegDescList; /* Single linked list */
|
|
|
|
unsigned SegDescCount; /* Number of entries in list */
|
|
|
|
|
|
|
|
/* Segment attributes */
|
|
|
|
#define SA_TYPE 0x0001
|
|
|
|
#define SA_LOAD 0x0002
|
|
|
|
#define SA_RUN 0x0004
|
|
|
|
#define SA_ALIGN 0x0008
|
|
|
|
#define SA_DEFINE 0x0010
|
|
|
|
#define SA_OFFSET 0x0020
|
|
|
|
#define SA_START 0x0040
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Descriptor holding information about the binary formats */
|
|
|
|
static BinDesc* BinFmtDesc = 0;
|
|
|
|
static O65Desc* O65FmtDesc = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-11-20 15:22:57 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* Forwards */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static File* NewFile (const char* Name);
|
|
|
|
/* Create a new file descriptor and insert it into the list */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* List management */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static File* FindFile (const char* Name)
|
|
|
|
/* Find a file with a given name. */
|
|
|
|
{
|
|
|
|
File* F = FileList;
|
|
|
|
while (F) {
|
|
|
|
if (strcmp (F->Name, Name) == 0) {
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
F = F->Next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static File* GetFile (const char* Name)
|
|
|
|
/* Get a file entry with the given name. Create a new one if needed. */
|
|
|
|
{
|
|
|
|
File* F = FindFile (Name);
|
|
|
|
if (F == 0) {
|
|
|
|
/* Create a new one */
|
|
|
|
F = NewFile (Name);
|
|
|
|
}
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void FileInsert (File* F, Memory* M)
|
|
|
|
/* Insert the memory area into the files list */
|
|
|
|
{
|
|
|
|
M->F = F;
|
|
|
|
if (F->MemList == 0) {
|
|
|
|
/* First entry */
|
|
|
|
F->MemList = M;
|
|
|
|
} else {
|
|
|
|
F->MemLast->FNext = M;
|
|
|
|
}
|
|
|
|
F->MemLast = M;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static Memory* CfgFindMemory (const char* Name)
|
|
|
|
/* Find the memory are with the given name. Return NULL if not found */
|
|
|
|
{
|
|
|
|
Memory* M = MemoryList;
|
|
|
|
while (M) {
|
|
|
|
if (strcmp (M->Name, Name) == 0) {
|
|
|
|
return M;
|
|
|
|
}
|
|
|
|
M = M->Next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static Memory* CfgGetMemory (const char* Name)
|
|
|
|
/* Find the memory are with the given name. Print an error on an invalid name */
|
|
|
|
{
|
|
|
|
Memory* M = CfgFindMemory (Name);
|
|
|
|
if (M == 0) {
|
|
|
|
CfgError ("Invalid memory area `%s'", Name);
|
|
|
|
}
|
|
|
|
return M;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static SegDesc* CfgFindSegDesc (const char* Name)
|
|
|
|
/* Find the segment descriptor with the given name, return NULL if not found. */
|
|
|
|
{
|
|
|
|
SegDesc* S = SegDescList;
|
|
|
|
while (S) {
|
|
|
|
if (strcmp (S->Name, Name) == 0) {
|
|
|
|
/* Found */
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
S = S->Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not found */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SegDescInsert (SegDesc* S)
|
|
|
|
/* Insert a segment descriptor into the list of segment descriptors */
|
|
|
|
{
|
|
|
|
/* Insert the struct into the list */
|
|
|
|
S->Next = SegDescList;
|
|
|
|
SegDescList = S;
|
|
|
|
++SegDescCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void MemoryInsert (Memory* M, SegDesc* S)
|
|
|
|
/* Insert the segment descriptor into the memory area list */
|
|
|
|
{
|
|
|
|
/* Create a new node for the entry */
|
|
|
|
MemListNode* N = xmalloc (sizeof (MemListNode));
|
|
|
|
N->Seg = S;
|
|
|
|
N->Next = 0;
|
|
|
|
|
|
|
|
if (M->SegLast == 0) {
|
|
|
|
/* First entry */
|
|
|
|
M->SegList = N;
|
|
|
|
} else {
|
|
|
|
M->SegLast->Next = N;
|
|
|
|
}
|
|
|
|
M->SegLast = N;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* Constructors/Destructors */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static File* NewFile (const char* Name)
|
|
|
|
/* Create a new file descriptor and insert it into the list */
|
|
|
|
{
|
|
|
|
/* Get the length of the name */
|
|
|
|
unsigned Len = strlen (Name);
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2000-06-14 09:57:42 +00:00
|
|
|
File* F = xmalloc (sizeof (File) + Len);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Initialize the fields */
|
|
|
|
F->Flags = 0;
|
|
|
|
F->Format = BINFMT_DEFAULT;
|
|
|
|
F->MemList = 0;
|
|
|
|
F->MemLast = 0;
|
|
|
|
memcpy (F->Name, Name, Len);
|
|
|
|
F->Name [Len] = '\0';
|
|
|
|
|
|
|
|
/* Insert the struct into the list */
|
|
|
|
F->Next = FileList;
|
|
|
|
FileList = F;
|
|
|
|
++FileCount;
|
|
|
|
|
|
|
|
/* ...and return it */
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static Memory* NewMemory (const char* Name)
|
|
|
|
/* Create a new memory section and insert it into the list */
|
|
|
|
{
|
|
|
|
/* Get the length of the name */
|
|
|
|
unsigned Len = strlen (Name);
|
|
|
|
|
|
|
|
/* Check for duplicate names */
|
2000-11-20 15:22:57 +00:00
|
|
|
Memory* M = CfgFindMemory (Name);
|
|
|
|
if (M) {
|
|
|
|
CfgError ("Memory area `%s' defined twice", Name);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2000-06-14 09:57:42 +00:00
|
|
|
M = xmalloc (sizeof (Memory) + Len);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Initialize the fields */
|
|
|
|
M->Next = 0;
|
|
|
|
M->FNext = 0;
|
|
|
|
M->Attr = 0;
|
|
|
|
M->Flags = 0;
|
|
|
|
M->Start = 0;
|
|
|
|
M->Size = 0;
|
|
|
|
M->FillLevel = 0;
|
|
|
|
M->FillVal = 0;
|
|
|
|
M->SegList = 0;
|
|
|
|
M->SegLast = 0;
|
|
|
|
M->F = 0;
|
|
|
|
memcpy (M->Name, Name, Len);
|
|
|
|
M->Name [Len] = '\0';
|
|
|
|
|
|
|
|
/* Insert the struct into the list */
|
|
|
|
if (MemoryLast == 0) {
|
|
|
|
/* First element */
|
|
|
|
MemoryList = M;
|
|
|
|
} else {
|
|
|
|
MemoryLast->Next = M;
|
|
|
|
}
|
|
|
|
MemoryLast = M;
|
|
|
|
++MemoryCount;
|
|
|
|
|
|
|
|
/* ...and return it */
|
|
|
|
return M;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static SegDesc* NewSegDesc (const char* Name)
|
|
|
|
/* Create a segment descriptor */
|
|
|
|
{
|
|
|
|
Segment* Seg;
|
|
|
|
|
|
|
|
/* Get the length of the name */
|
|
|
|
unsigned Len = strlen (Name);
|
|
|
|
|
|
|
|
/* Check for duplicate names */
|
2000-11-20 15:22:57 +00:00
|
|
|
SegDesc* S = CfgFindSegDesc (Name);
|
|
|
|
if (S) {
|
|
|
|
CfgError ("Segment `%s' defined twice", Name);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify that the given segment does really exist */
|
|
|
|
Seg = SegFind (Name);
|
|
|
|
if (Seg == 0) {
|
|
|
|
CfgWarning ("Segment `%s' does not exist", Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2000-06-14 09:57:42 +00:00
|
|
|
S = xmalloc (sizeof (SegDesc) + Len);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Initialize the fields */
|
|
|
|
S->Next = 0;
|
|
|
|
S->Seg = Seg;
|
|
|
|
S->Attr = 0;
|
|
|
|
S->Flags = 0;
|
|
|
|
S->Align = 0;
|
|
|
|
memcpy (S->Name, Name, Len);
|
|
|
|
S->Name [Len] = '\0';
|
|
|
|
|
|
|
|
/* ...and return it */
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void FreeSegDesc (SegDesc* S)
|
|
|
|
/* Free a segment descriptor */
|
|
|
|
{
|
2000-06-14 09:57:42 +00:00
|
|
|
xfree (S);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Code */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void FlagAttr (unsigned* Flags, unsigned Mask, const char* Name)
|
|
|
|
/* Check if the item is already defined. Print an error if so. If not, set
|
|
|
|
* the marker that we have a definition now.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
if (*Flags & Mask) {
|
|
|
|
CfgError ("%s is already defined", Name);
|
|
|
|
}
|
|
|
|
*Flags |= Mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void AttrCheck (unsigned Attr, unsigned Mask, const char* Name)
|
|
|
|
/* Check that a mandatory attribute was given */
|
|
|
|
{
|
|
|
|
if ((Attr & Mask) == 0) {
|
|
|
|
CfgError ("%s attribute is missing", Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ParseMemory (void)
|
|
|
|
/* Parse a MEMORY section */
|
|
|
|
{
|
|
|
|
static const IdentTok Attributes [] = {
|
|
|
|
{ "START", CFGTOK_START },
|
|
|
|
{ "SIZE", CFGTOK_SIZE },
|
|
|
|
{ "TYPE", CFGTOK_TYPE },
|
|
|
|
{ "FILE", CFGTOK_FILE },
|
|
|
|
{ "DEFINE", CFGTOK_DEFINE },
|
|
|
|
{ "FILL", CFGTOK_FILL },
|
|
|
|
{ "FILLVAL", CFGTOK_FILLVAL },
|
|
|
|
};
|
|
|
|
static const IdentTok Types [] = {
|
|
|
|
{ "RO", CFGTOK_RO },
|
|
|
|
{ "RW", CFGTOK_RW },
|
|
|
|
};
|
|
|
|
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
/* Create a new entry on the heap */
|
|
|
|
Memory* M = NewMemory (CfgSVal);
|
|
|
|
|
|
|
|
/* Skip the name and the following colon */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgConsumeColon ();
|
|
|
|
|
|
|
|
/* Read the attributes */
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
2000-11-20 15:22:57 +00:00
|
|
|
cfgtok_t AttrTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
|
|
|
|
AttrTok = CfgTok;
|
|
|
|
|
|
|
|
/* An optional assignment follows */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Check which attribute was given */
|
|
|
|
switch (AttrTok) {
|
|
|
|
|
|
|
|
case CFGTOK_START:
|
|
|
|
FlagAttr (&M->Attr, MA_START, "START");
|
|
|
|
CfgAssureInt ();
|
|
|
|
M->Start = CfgIVal;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_SIZE:
|
|
|
|
FlagAttr (&M->Attr, MA_SIZE, "SIZE");
|
|
|
|
CfgAssureInt ();
|
|
|
|
M->Size = CfgIVal;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_TYPE:
|
|
|
|
FlagAttr (&M->Attr, MA_TYPE, "TYPE");
|
|
|
|
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
|
|
|
|
if (CfgTok == CFGTOK_RO) {
|
|
|
|
M->Flags |= MF_RO;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_FILE:
|
|
|
|
FlagAttr (&M->Attr, MA_FILE, "FILE");
|
|
|
|
CfgAssureStr ();
|
|
|
|
/* Get the file entry and insert the memory area */
|
|
|
|
FileInsert (GetFile (CfgSVal), M);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_DEFINE:
|
|
|
|
FlagAttr (&M->Attr, MA_DEFINE, "DEFINE");
|
|
|
|
/* Map the token to a boolean */
|
|
|
|
CfgBoolToken ();
|
|
|
|
if (CfgTok == CFGTOK_TRUE) {
|
|
|
|
M->Flags |= MF_DEFINE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_FILL:
|
|
|
|
FlagAttr (&M->Attr, MA_FILL, "FILL");
|
|
|
|
/* Map the token to a boolean */
|
|
|
|
CfgBoolToken ();
|
|
|
|
if (CfgTok == CFGTOK_TRUE) {
|
|
|
|
M->Flags |= MF_FILL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_FILLVAL:
|
|
|
|
FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL");
|
|
|
|
CfgAssureInt ();
|
|
|
|
CfgRangeCheck (0, 0xFF);
|
|
|
|
M->FillVal = (unsigned char) CfgIVal;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ("Unexpected attribute token");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the attribute value and an optional comma */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalComma ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the semicolon */
|
|
|
|
CfgConsumeSemi ();
|
|
|
|
|
|
|
|
/* Check for mandatory parameters */
|
|
|
|
AttrCheck (M->Attr, MA_START, "START");
|
|
|
|
AttrCheck (M->Attr, MA_SIZE, "SIZE");
|
|
|
|
|
|
|
|
/* If we don't have a file name for output given, use the default
|
|
|
|
* file name.
|
|
|
|
*/
|
|
|
|
if ((M->Attr & MA_FILE) == 0) {
|
|
|
|
FileInsert (GetFile (OutputName), M);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ParseFiles (void)
|
|
|
|
/* Parse a FILES section */
|
|
|
|
{
|
|
|
|
static const IdentTok Attributes [] = {
|
|
|
|
{ "FORMAT", CFGTOK_FORMAT },
|
|
|
|
};
|
|
|
|
static const IdentTok Formats [] = {
|
|
|
|
{ "O65", CFGTOK_O65 },
|
|
|
|
{ "BIN", CFGTOK_BIN },
|
|
|
|
{ "BINARY", CFGTOK_BIN },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Parse all files */
|
|
|
|
while (CfgTok != CFGTOK_RCURLY) {
|
|
|
|
|
|
|
|
File* F;
|
|
|
|
|
|
|
|
/* We expect a string value here */
|
|
|
|
CfgAssureStr ();
|
|
|
|
|
|
|
|
/* Search for the file, it must exist */
|
|
|
|
F = FindFile (CfgSVal);
|
|
|
|
if (F == 0) {
|
|
|
|
CfgError ("No such file: `%s'", CfgSVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the token and the following colon */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgConsumeColon ();
|
|
|
|
|
|
|
|
/* Read the attributes */
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
2000-11-20 15:22:57 +00:00
|
|
|
cfgtok_t AttrTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
|
|
|
|
AttrTok = CfgTok;
|
|
|
|
|
|
|
|
/* An optional assignment follows */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Check which attribute was given */
|
|
|
|
switch (AttrTok) {
|
|
|
|
|
|
|
|
case CFGTOK_FORMAT:
|
|
|
|
if (F->Format != BINFMT_DEFAULT) {
|
|
|
|
/* We've set the format already! */
|
|
|
|
Error ("Cannot set a file format twice");
|
|
|
|
}
|
|
|
|
/* Read the format token */
|
|
|
|
CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
|
|
|
|
switch (CfgTok) {
|
|
|
|
|
|
|
|
case CFGTOK_BIN:
|
|
|
|
F->Format = BINFMT_BINARY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_O65:
|
|
|
|
F->Format = BINFMT_O65;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Error ("Unexpected format token");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ("Unexpected attribute token");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the attribute value and an optional comma */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalComma ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the semicolon */
|
|
|
|
CfgConsumeSemi ();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ParseSegments (void)
|
|
|
|
/* Parse a SEGMENTS section */
|
|
|
|
{
|
|
|
|
static const IdentTok Attributes [] = {
|
|
|
|
{ "LOAD", CFGTOK_LOAD },
|
2001-03-10 14:10:38 +00:00
|
|
|
{ "RUN", CFGTOK_RUN },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "TYPE", CFGTOK_TYPE },
|
|
|
|
{ "ALIGN", CFGTOK_ALIGN },
|
|
|
|
{ "DEFINE", CFGTOK_DEFINE },
|
2001-03-10 14:10:38 +00:00
|
|
|
{ "OFFSET", CFGTOK_OFFSET },
|
|
|
|
{ "START", CFGTOK_START },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
static const IdentTok Types [] = {
|
2001-03-10 14:10:38 +00:00
|
|
|
{ "RO", CFGTOK_RO },
|
|
|
|
{ "RW", CFGTOK_RW },
|
|
|
|
{ "BSS", CFGTOK_BSS },
|
|
|
|
{ "ZP", CFGTOK_ZP },
|
|
|
|
{ "WP", CFGTOK_WPROT },
|
|
|
|
{ "WPROT", CFGTOK_WPROT },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
unsigned Count;
|
|
|
|
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
SegDesc* S;
|
|
|
|
|
|
|
|
/* Create a new entry on the heap */
|
|
|
|
S = NewSegDesc (CfgSVal);
|
|
|
|
|
|
|
|
/* Skip the name and the following colon */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgConsumeColon ();
|
|
|
|
|
|
|
|
/* Read the attributes */
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
2000-11-20 15:22:57 +00:00
|
|
|
cfgtok_t AttrTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
|
|
|
|
AttrTok = CfgTok;
|
|
|
|
|
|
|
|
/* An optional assignment follows */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Check which attribute was given */
|
|
|
|
switch (AttrTok) {
|
|
|
|
|
|
|
|
case CFGTOK_LOAD:
|
|
|
|
FlagAttr (&S->Attr, SA_LOAD, "LOAD");
|
|
|
|
S->Load = CfgGetMemory (CfgSVal);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_RUN:
|
|
|
|
FlagAttr (&S->Attr, SA_RUN, "RUN");
|
|
|
|
S->Run = CfgGetMemory (CfgSVal);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_TYPE:
|
|
|
|
FlagAttr (&S->Attr, SA_TYPE, "TYPE");
|
|
|
|
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
|
|
|
|
switch (CfgTok) {
|
|
|
|
case CFGTOK_RO: S->Flags |= SF_RO; break;
|
2000-11-20 15:22:57 +00:00
|
|
|
case CFGTOK_RW: /* Default */ break;
|
2000-05-28 13:40:48 +00:00
|
|
|
case CFGTOK_BSS: S->Flags |= SF_BSS; break;
|
|
|
|
case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break;
|
|
|
|
case CFGTOK_WPROT: S->Flags |= (SF_RO | SF_WPROT); break;
|
2000-11-20 15:22:57 +00:00
|
|
|
default: Internal ("Unexpected token: %d", CfgTok);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_ALIGN:
|
|
|
|
CfgAssureInt ();
|
|
|
|
FlagAttr (&S->Attr, SA_ALIGN, "ALIGN");
|
|
|
|
CfgRangeCheck (1, 0x10000);
|
|
|
|
S->Align = BitFind (CfgIVal);
|
|
|
|
if ((0x01UL << S->Align) != CfgIVal) {
|
|
|
|
CfgError ("Alignment must be a power of 2");
|
|
|
|
}
|
|
|
|
S->Flags |= SF_ALIGN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_DEFINE:
|
|
|
|
FlagAttr (&S->Attr, SA_DEFINE, "DEFINE");
|
|
|
|
/* Map the token to a boolean */
|
|
|
|
CfgBoolToken ();
|
|
|
|
if (CfgTok == CFGTOK_TRUE) {
|
|
|
|
S->Flags |= SF_DEFINE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_OFFSET:
|
|
|
|
CfgAssureInt ();
|
|
|
|
FlagAttr (&S->Attr, SA_OFFSET, "OFFSET");
|
|
|
|
CfgRangeCheck (1, 0x1000000);
|
|
|
|
S->Addr = CfgIVal;
|
|
|
|
S->Flags |= SF_OFFSET;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_START:
|
|
|
|
CfgAssureInt ();
|
|
|
|
FlagAttr (&S->Attr, SA_START, "START");
|
|
|
|
CfgRangeCheck (1, 0x1000000);
|
|
|
|
S->Addr = CfgIVal;
|
|
|
|
S->Flags |= SF_START;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ("Unexpected attribute token");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the attribute value and an optional comma */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalComma ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for mandatory parameters */
|
|
|
|
AttrCheck (S->Attr, SA_LOAD, "LOAD");
|
|
|
|
|
|
|
|
/* Set defaults for stuff not given */
|
|
|
|
if ((S->Attr & SA_RUN) == 0) {
|
|
|
|
S->Attr |= SA_RUN;
|
|
|
|
S->Run = S->Load;
|
|
|
|
} else {
|
|
|
|
/* Both attributes given */
|
|
|
|
S->Flags |= SF_LOAD_AND_RUN;
|
|
|
|
}
|
|
|
|
if ((S->Attr & SA_ALIGN) == 0) {
|
|
|
|
S->Attr |= SA_ALIGN;
|
|
|
|
S->Align = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the segment is marked as BSS style, check that there's no
|
|
|
|
* initialized data in the segment.
|
|
|
|
*/
|
|
|
|
if ((S->Flags & SF_BSS) != 0 && !IsBSSType (S->Seg)) {
|
|
|
|
Warning ("%s(%u): Segment with type `bss' contains initialized data",
|
|
|
|
CfgGetName (), CfgErrorLine);
|
|
|
|
}
|
|
|
|
|
2002-12-11 18:29:58 +00:00
|
|
|
/* If the segment is marked as BSS style, it may not have separate
|
|
|
|
* load and run memory areas, because it's is never written to disk.
|
|
|
|
*/
|
|
|
|
if ((S->Flags & SF_BSS) != 0 && (S->Flags & SF_LOAD_AND_RUN) != 0) {
|
|
|
|
Warning ("%s(%u): Segment with type `bss' has both LOAD and RUN "
|
|
|
|
"memory areas assigned", CfgGetName (), CfgErrorLine);
|
|
|
|
}
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Don't allow read/write data to be put into a readonly area */
|
|
|
|
if ((S->Flags & SF_RO) == 0) {
|
|
|
|
if (S->Run->Flags & MF_RO) {
|
2002-12-11 18:29:58 +00:00
|
|
|
CfgError ("Cannot put r/w segment `%s' in r/o memory area `%s'",
|
|
|
|
S->Name, S->Run->Name);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only one of ALIGN, START and OFFSET may be used */
|
|
|
|
Count = ((S->Flags & SF_ALIGN) != 0) +
|
|
|
|
((S->Flags & SF_OFFSET) != 0) +
|
|
|
|
((S->Flags & SF_START) != 0);
|
|
|
|
if (Count > 1) {
|
|
|
|
CfgError ("Only one of ALIGN, START, OFFSET may be used");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this segment does exist in any of the object files, insert the
|
|
|
|
* descriptor into the list of segment descriptors. Otherwise discard
|
|
|
|
* it silently, because the segment pointer in the descriptor is
|
|
|
|
* invalid.
|
|
|
|
*/
|
|
|
|
if (S->Seg != 0) {
|
|
|
|
/* Insert the descriptor into the list of all descriptors */
|
|
|
|
SegDescInsert (S);
|
|
|
|
/* Insert the segment into the memory area list */
|
|
|
|
MemoryInsert (S->Run, S);
|
|
|
|
if ((S->Flags & SF_LOAD_AND_RUN) != 0) {
|
|
|
|
/* We have a separate RUN area given */
|
|
|
|
MemoryInsert (S->Load, S);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Segment does not exist, discard the descriptor */
|
|
|
|
FreeSegDesc (S);
|
|
|
|
}
|
2002-12-11 18:29:58 +00:00
|
|
|
|
|
|
|
/* Skip the semicolon */
|
|
|
|
CfgConsumeSemi ();
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ParseO65 (void)
|
|
|
|
/* Parse the o65 format section */
|
|
|
|
{
|
|
|
|
static const IdentTok Attributes [] = {
|
2002-04-21 14:30:40 +00:00
|
|
|
{ "EXPORT", CFGTOK_EXPORT },
|
|
|
|
{ "IMPORT", CFGTOK_IMPORT },
|
|
|
|
{ "TYPE", CFGTOK_TYPE },
|
|
|
|
{ "OS", CFGTOK_OS },
|
2002-04-21 20:00:04 +00:00
|
|
|
{ "ID", CFGTOK_ID },
|
|
|
|
{ "VERSION", CFGTOK_VERSION },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
static const IdentTok Types [] = {
|
2002-04-21 14:30:40 +00:00
|
|
|
{ "SMALL", CFGTOK_SMALL },
|
|
|
|
{ "LARGE", CFGTOK_LARGE },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
static const IdentTok OperatingSystems [] = {
|
2002-04-21 14:30:40 +00:00
|
|
|
{ "LUNIX", CFGTOK_LUNIX },
|
|
|
|
{ "OSA65", CFGTOK_OSA65 },
|
|
|
|
{ "CC65", CFGTOK_CC65 },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
|
2002-04-21 20:00:04 +00:00
|
|
|
/* Bitmask to remember the attributes we got already */
|
|
|
|
enum {
|
|
|
|
atNone = 0x0000,
|
|
|
|
atOS = 0x0001,
|
|
|
|
atOSVersion = 0x0002,
|
|
|
|
atType = 0x0004,
|
|
|
|
atImport = 0x0008,
|
|
|
|
atExport = 0x0010,
|
|
|
|
atID = 0x0020,
|
|
|
|
atVersion = 0x0040
|
|
|
|
};
|
|
|
|
unsigned AttrFlags = atNone;
|
|
|
|
|
|
|
|
/* Remember the attributes read */
|
|
|
|
unsigned OS = 0; /* Initialize to keep gcc happy */
|
|
|
|
unsigned Version = 0;
|
|
|
|
|
|
|
|
/* Read the attributes */
|
2000-05-28 13:40:48 +00:00
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
2000-11-20 15:22:57 +00:00
|
|
|
cfgtok_t AttrTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
|
|
|
|
AttrTok = CfgTok;
|
|
|
|
|
|
|
|
/* An optional assignment follows */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Check which attribute was given */
|
|
|
|
switch (AttrTok) {
|
|
|
|
|
|
|
|
case CFGTOK_EXPORT:
|
2002-04-21 20:00:04 +00:00
|
|
|
/* Remember we had this token (maybe more than once) */
|
|
|
|
AttrFlags |= atExport;
|
2000-05-28 13:40:48 +00:00
|
|
|
/* We expect an identifier */
|
|
|
|
CfgAssureIdent ();
|
2001-12-01 17:14:12 +00:00
|
|
|
/* Check if the export symbol is also defined as an import. */
|
|
|
|
if (O65GetImport (O65FmtDesc, CfgSVal) != 0) {
|
|
|
|
CfgError ("Exported symbol `%s' cannot be an import", CfgSVal);
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Check if we have this symbol defined already. The entry
|
|
|
|
* routine will check this also, but we get a more verbose
|
|
|
|
* error message when checking it here.
|
|
|
|
*/
|
|
|
|
if (O65GetExport (O65FmtDesc, CfgSVal) != 0) {
|
2002-04-21 20:00:04 +00:00
|
|
|
CfgError ("Duplicate exported symbol: `%s'", CfgSVal);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
/* Insert the symbol into the table */
|
2002-04-21 20:00:04 +00:00
|
|
|
O65SetExport (O65FmtDesc, CfgSVal);
|
2000-05-28 13:40:48 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_IMPORT:
|
2002-04-21 20:00:04 +00:00
|
|
|
/* Remember we had this token (maybe more than once) */
|
|
|
|
AttrFlags |= atImport;
|
2000-05-28 13:40:48 +00:00
|
|
|
/* We expect an identifier */
|
|
|
|
CfgAssureIdent ();
|
2001-12-01 17:14:12 +00:00
|
|
|
/* Check if the imported symbol is also defined as an export. */
|
|
|
|
if (O65GetExport (O65FmtDesc, CfgSVal) != 0) {
|
|
|
|
CfgError ("Imported symbol `%s' cannot be an export", CfgSVal);
|
|
|
|
}
|
|
|
|
/* Check if we have this symbol defined already. The entry
|
|
|
|
* routine will check this also, but we get a more verbose
|
|
|
|
* error message when checking it here.
|
|
|
|
*/
|
|
|
|
if (O65GetImport (O65FmtDesc, CfgSVal) != 0) {
|
|
|
|
CfgError ("Duplicate imported symbol: `%s'", CfgSVal);
|
|
|
|
}
|
|
|
|
/* Insert the symbol into the table */
|
|
|
|
O65SetImport (O65FmtDesc, CfgSVal);
|
2000-05-28 13:40:48 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_TYPE:
|
|
|
|
/* Cannot have this attribute twice */
|
2002-04-21 20:00:04 +00:00
|
|
|
FlagAttr (&AttrFlags, atType, "TYPE");
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Get the type of the executable */
|
|
|
|
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
|
|
|
|
switch (CfgTok) {
|
|
|
|
|
|
|
|
case CFGTOK_SMALL:
|
2001-12-01 13:34:51 +00:00
|
|
|
O65SetSmallModel (O65FmtDesc);
|
2000-05-28 13:40:48 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_LARGE:
|
2002-04-21 14:30:40 +00:00
|
|
|
O65SetLargeModel (O65FmtDesc);
|
2000-05-28 13:40:48 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2000-11-20 15:22:57 +00:00
|
|
|
CfgError ("Unexpected type token");
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_OS:
|
|
|
|
/* Cannot use this attribute twice */
|
2002-04-21 20:00:04 +00:00
|
|
|
FlagAttr (&AttrFlags, atOS, "OS");
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Get the operating system */
|
|
|
|
CfgSpecialToken (OperatingSystems, ENTRY_COUNT (OperatingSystems), "OS type");
|
|
|
|
switch (CfgTok) {
|
2002-04-21 20:00:04 +00:00
|
|
|
case CFGTOK_LUNIX: OS = O65OS_LUNIX; break;
|
|
|
|
case CFGTOK_OSA65: OS = O65OS_OSA65; break;
|
|
|
|
case CFGTOK_CC65: OS = O65OS_CC65; break;
|
|
|
|
default: CfgError ("Unexpected OS token");
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2002-04-21 20:00:04 +00:00
|
|
|
case CFGTOK_ID:
|
|
|
|
/* Cannot have this attribute twice */
|
|
|
|
FlagAttr (&AttrFlags, atID, "ID");
|
|
|
|
/* We're expecting a number in the 0..$FFFF range*/
|
|
|
|
CfgAssureInt ();
|
|
|
|
CfgRangeCheck (0, 0xFFFF);
|
2002-04-25 18:11:05 +00:00
|
|
|
ModuleId = (unsigned) CfgIVal;
|
2002-04-21 20:00:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_VERSION:
|
|
|
|
/* Cannot have this attribute twice */
|
|
|
|
FlagAttr (&AttrFlags, atVersion, "VERSION");
|
|
|
|
/* We're expecting a number in byte range */
|
|
|
|
CfgAssureInt ();
|
|
|
|
CfgRangeCheck (0, 0xFF);
|
|
|
|
Version = (unsigned) CfgIVal;
|
|
|
|
break;
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
default:
|
|
|
|
FAIL ("Unexpected attribute token");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the attribute value and an optional comma */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalComma ();
|
|
|
|
}
|
2002-04-21 20:00:04 +00:00
|
|
|
|
|
|
|
/* Check if we have all mandatory attributes */
|
|
|
|
AttrCheck (AttrFlags, atOS, "OS");
|
|
|
|
|
|
|
|
/* Check for attributes that may not be combined */
|
|
|
|
if (OS == O65OS_CC65) {
|
|
|
|
if ((AttrFlags & (atImport | atExport)) != 0) {
|
|
|
|
CfgError ("OS type CC65 may not have imports or exports");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (AttrFlags & atID) {
|
|
|
|
CfgError ("Operating system does not support the ID attribute");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the O65 operating system to use */
|
2002-04-25 18:11:05 +00:00
|
|
|
O65SetOS (O65FmtDesc, OS, Version, ModuleId);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void ParseFormats (void)
|
|
|
|
/* Parse a target format section */
|
|
|
|
{
|
|
|
|
static const IdentTok Formats [] = {
|
|
|
|
{ "O65", CFGTOK_O65 },
|
|
|
|
{ "BIN", CFGTOK_BIN },
|
|
|
|
{ "BINARY", CFGTOK_BIN },
|
|
|
|
};
|
|
|
|
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
2000-11-20 15:22:57 +00:00
|
|
|
cfgtok_t FormatTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
CfgSpecialToken (Formats, ENTRY_COUNT (Formats), "Format");
|
|
|
|
FormatTok = CfgTok;
|
|
|
|
|
|
|
|
/* Skip the name and the following colon */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgConsumeColon ();
|
|
|
|
|
|
|
|
/* Parse the format options */
|
|
|
|
switch (FormatTok) {
|
|
|
|
|
|
|
|
case CFGTOK_O65:
|
|
|
|
ParseO65 ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_BIN:
|
|
|
|
/* No attribibutes available */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Error ("Unexpected format token");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the semicolon */
|
|
|
|
CfgConsumeSemi ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-11-20 15:22:57 +00:00
|
|
|
static void ParseConDes (void)
|
|
|
|
/* Parse the CONDES feature */
|
|
|
|
{
|
|
|
|
static const IdentTok Attributes [] = {
|
2002-04-21 20:00:04 +00:00
|
|
|
{ "SEGMENT", CFGTOK_SEGMENT },
|
|
|
|
{ "LABEL", CFGTOK_LABEL },
|
|
|
|
{ "COUNT", CFGTOK_COUNT },
|
|
|
|
{ "TYPE", CFGTOK_TYPE },
|
|
|
|
{ "ORDER", CFGTOK_ORDER },
|
2000-11-20 15:22:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const IdentTok Types [] = {
|
|
|
|
{ "CONSTRUCTOR", CFGTOK_CONSTRUCTOR },
|
|
|
|
{ "DESTRUCTOR", CFGTOK_DESTRUCTOR },
|
|
|
|
};
|
|
|
|
|
2000-11-20 21:56:48 +00:00
|
|
|
static const IdentTok Orders [] = {
|
|
|
|
{ "DECREASING", CFGTOK_DECREASING },
|
|
|
|
{ "INCREASING", CFGTOK_INCREASING },
|
|
|
|
};
|
|
|
|
|
2000-11-20 15:22:57 +00:00
|
|
|
/* Attribute values. */
|
|
|
|
char SegName[sizeof (CfgSVal)];
|
|
|
|
char Label[sizeof (CfgSVal)];
|
2000-11-20 21:56:48 +00:00
|
|
|
char Count[sizeof (CfgSVal)];
|
|
|
|
/* Initialize to avoid gcc warnings: */
|
|
|
|
int Type = -1;
|
|
|
|
ConDesOrder Order = cdIncreasing;
|
2000-11-20 15:22:57 +00:00
|
|
|
|
|
|
|
/* Bitmask to remember the attributes we got already */
|
|
|
|
enum {
|
|
|
|
atNone = 0x0000,
|
|
|
|
atSegName = 0x0001,
|
|
|
|
atLabel = 0x0002,
|
2000-11-20 21:56:48 +00:00
|
|
|
atCount = 0x0004,
|
|
|
|
atType = 0x0008,
|
|
|
|
atOrder = 0x0010
|
2000-11-20 15:22:57 +00:00
|
|
|
};
|
|
|
|
unsigned AttrFlags = atNone;
|
|
|
|
|
|
|
|
/* Parse the attributes */
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
|
|
|
cfgtok_t AttrTok;
|
|
|
|
CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
|
|
|
|
AttrTok = CfgTok;
|
|
|
|
|
|
|
|
/* An optional assignment follows */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Check which attribute was given */
|
|
|
|
switch (AttrTok) {
|
|
|
|
|
|
|
|
case CFGTOK_SEGMENT:
|
|
|
|
/* Don't allow this twice */
|
|
|
|
FlagAttr (&AttrFlags, atSegName, "SEGMENT");
|
|
|
|
/* We expect an identifier */
|
|
|
|
CfgAssureIdent ();
|
|
|
|
/* Remember the value for later */
|
|
|
|
strcpy (SegName, CfgSVal);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_LABEL:
|
|
|
|
/* Don't allow this twice */
|
|
|
|
FlagAttr (&AttrFlags, atLabel, "LABEL");
|
|
|
|
/* We expect an identifier */
|
|
|
|
CfgAssureIdent ();
|
|
|
|
/* Remember the value for later */
|
|
|
|
strcpy (Label, CfgSVal);
|
|
|
|
break;
|
|
|
|
|
2000-11-20 21:56:48 +00:00
|
|
|
case CFGTOK_COUNT:
|
|
|
|
/* Don't allow this twice */
|
|
|
|
FlagAttr (&AttrFlags, atCount, "COUNT");
|
|
|
|
/* We expect an identifier */
|
|
|
|
CfgAssureIdent ();
|
|
|
|
/* Remember the value for later */
|
|
|
|
strcpy (Count, CfgSVal);
|
|
|
|
break;
|
2000-11-20 15:22:57 +00:00
|
|
|
|
|
|
|
case CFGTOK_TYPE:
|
|
|
|
/* Don't allow this twice */
|
|
|
|
FlagAttr (&AttrFlags, atType, "TYPE");
|
|
|
|
/* The type may be given as id or numerical */
|
|
|
|
if (CfgTok == CFGTOK_INTCON) {
|
|
|
|
CfgRangeCheck (CD_TYPE_MIN, CD_TYPE_MAX);
|
|
|
|
Type = (int) CfgIVal;
|
|
|
|
} else {
|
|
|
|
CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type");
|
|
|
|
switch (CfgTok) {
|
|
|
|
case CFGTOK_CONSTRUCTOR: Type = CD_TYPE_CON; break;
|
|
|
|
case CFGTOK_DESTRUCTOR: Type = CD_TYPE_DES; break;
|
|
|
|
default: FAIL ("Unexpected type token");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2000-11-20 21:56:48 +00:00
|
|
|
case CFGTOK_ORDER:
|
|
|
|
/* Don't allow this twice */
|
|
|
|
FlagAttr (&AttrFlags, atOrder, "ORDER");
|
|
|
|
CfgSpecialToken (Orders, ENTRY_COUNT (Orders), "Order");
|
|
|
|
switch (CfgTok) {
|
|
|
|
case CFGTOK_DECREASING: Order = cdDecreasing; break;
|
|
|
|
case CFGTOK_INCREASING: Order = cdIncreasing; break;
|
|
|
|
default: FAIL ("Unexpected order token");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2000-11-20 15:22:57 +00:00
|
|
|
default:
|
|
|
|
FAIL ("Unexpected attribute token");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the attribute value */
|
|
|
|
CfgNextTok ();
|
|
|
|
|
|
|
|
/* Semicolon ends the ConDes decl, otherwise accept an optional comma */
|
|
|
|
if (CfgTok == CFGTOK_SEMI) {
|
|
|
|
break;
|
|
|
|
} else if (CfgTok == CFGTOK_COMMA) {
|
|
|
|
CfgNextTok ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we have all mandatory attributes */
|
|
|
|
AttrCheck (AttrFlags, atSegName, "SEGMENT");
|
|
|
|
AttrCheck (AttrFlags, atLabel, "LABEL");
|
|
|
|
AttrCheck (AttrFlags, atType, "TYPE");
|
|
|
|
|
|
|
|
/* Check if the condes has already attributes defined */
|
|
|
|
if (ConDesHasSegName(Type) || ConDesHasLabel(Type)) {
|
|
|
|
CfgError ("CONDES attributes for type %d are already defined", Type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Define the attributes */
|
|
|
|
ConDesSetSegName (Type, SegName);
|
|
|
|
ConDesSetLabel (Type, Label);
|
2000-11-20 21:56:48 +00:00
|
|
|
if (AttrFlags & atCount) {
|
|
|
|
ConDesSetCountSym (Type, Count);
|
|
|
|
}
|
|
|
|
if (AttrFlags & atOrder) {
|
|
|
|
ConDesSetOrder (Type, Order);
|
|
|
|
}
|
2000-11-20 15:22:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
static void ParseStartAddress (void)
|
|
|
|
/* Parse the STARTADDRESS feature */
|
|
|
|
{
|
|
|
|
static const IdentTok Attributes [] = {
|
|
|
|
{ "DEFAULT", CFGTOK_DEFAULT },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Attribute values. */
|
|
|
|
unsigned long DefStartAddr = 0;
|
|
|
|
|
|
|
|
/* Bitmask to remember the attributes we got already */
|
|
|
|
enum {
|
|
|
|
atNone = 0x0000,
|
|
|
|
atDefault = 0x0001
|
|
|
|
};
|
|
|
|
unsigned AttrFlags = atNone;
|
|
|
|
|
|
|
|
/* Parse the attributes */
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
/* Map the identifier to a token */
|
|
|
|
cfgtok_t AttrTok;
|
|
|
|
CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute");
|
|
|
|
AttrTok = CfgTok;
|
|
|
|
|
|
|
|
/* An optional assignment follows */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Check which attribute was given */
|
|
|
|
switch (AttrTok) {
|
|
|
|
|
|
|
|
case CFGTOK_DEFAULT:
|
|
|
|
/* Don't allow this twice */
|
|
|
|
FlagAttr (&AttrFlags, atDefault, "DEFAULT");
|
|
|
|
/* We expect a number */
|
|
|
|
CfgAssureInt ();
|
|
|
|
CfgRangeCheck (0, 0xFFFFFF);
|
|
|
|
/* Remember the value for later */
|
|
|
|
DefStartAddr = CfgIVal;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ("Unexpected attribute token");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the attribute value */
|
|
|
|
CfgNextTok ();
|
|
|
|
|
|
|
|
/* Semicolon ends the ConDes decl, otherwise accept an optional comma */
|
|
|
|
if (CfgTok == CFGTOK_SEMI) {
|
|
|
|
break;
|
|
|
|
} else if (CfgTok == CFGTOK_COMMA) {
|
|
|
|
CfgNextTok ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we have all mandatory attributes */
|
|
|
|
AttrCheck (AttrFlags, atDefault, "DEFAULT");
|
|
|
|
|
|
|
|
/* If no start address was given on the command line, use the one given
|
|
|
|
* here
|
|
|
|
*/
|
|
|
|
if (!HaveStartAddr) {
|
|
|
|
StartAddr = DefStartAddr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-11-20 15:22:57 +00:00
|
|
|
static void ParseFeatures (void)
|
|
|
|
/* Parse a features section */
|
|
|
|
{
|
|
|
|
static const IdentTok Features [] = {
|
2002-12-03 22:32:38 +00:00
|
|
|
{ "CONDES", CFGTOK_CONDES },
|
|
|
|
{ "STARTADDRESS", CFGTOK_STARTADDRESS },
|
2000-11-20 15:22:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
/* Map the identifier to a token */
|
|
|
|
cfgtok_t FeatureTok;
|
2000-11-20 15:22:57 +00:00
|
|
|
CfgSpecialToken (Features, ENTRY_COUNT (Features), "Feature");
|
|
|
|
FeatureTok = CfgTok;
|
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
/* Skip the name and the following colon */
|
|
|
|
CfgNextTok ();
|
|
|
|
CfgConsumeColon ();
|
2000-11-20 15:22:57 +00:00
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
/* Parse the format options */
|
|
|
|
switch (FeatureTok) {
|
2000-11-20 15:22:57 +00:00
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
case CFGTOK_CONDES:
|
|
|
|
ParseConDes ();
|
|
|
|
break;
|
2000-11-20 15:22:57 +00:00
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
case CFGTOK_STARTADDRESS:
|
|
|
|
ParseStartAddress ();
|
|
|
|
break;
|
2000-11-20 15:22:57 +00:00
|
|
|
|
2002-12-03 22:32:38 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
Error ("Unexpected feature token");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the semicolon */
|
|
|
|
CfgConsumeSemi ();
|
2000-11-20 15:22:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-03-10 14:10:38 +00:00
|
|
|
static void ParseSymbols (void)
|
|
|
|
/* Parse a symbols section */
|
|
|
|
{
|
|
|
|
while (CfgTok == CFGTOK_IDENT) {
|
|
|
|
|
|
|
|
long Val;
|
|
|
|
|
|
|
|
/* Remember the name */
|
|
|
|
char Name [sizeof (CfgSVal)];
|
|
|
|
strcpy (Name, CfgSVal);
|
|
|
|
CfgNextTok ();
|
|
|
|
|
|
|
|
/* Allow an optional assignment */
|
|
|
|
CfgOptionalAssign ();
|
|
|
|
|
|
|
|
/* Make sure the next token is an integer, read and skip it */
|
|
|
|
CfgAssureInt ();
|
|
|
|
Val = CfgIVal;
|
|
|
|
CfgNextTok ();
|
|
|
|
|
|
|
|
/* Generate an export with the given value */
|
|
|
|
CreateConstExport (Name, Val);
|
|
|
|
|
|
|
|
/* Skip the semicolon */
|
|
|
|
CfgConsumeSemi ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
static void ParseConfig (void)
|
|
|
|
/* Parse the config file */
|
|
|
|
{
|
|
|
|
static const IdentTok BlockNames [] = {
|
2000-11-20 15:22:57 +00:00
|
|
|
{ "MEMORY", CFGTOK_MEMORY },
|
|
|
|
{ "FILES", CFGTOK_FILES },
|
|
|
|
{ "SEGMENTS", CFGTOK_SEGMENTS },
|
|
|
|
{ "FORMATS", CFGTOK_FORMATS },
|
|
|
|
{ "FEATURES", CFGTOK_FEATURES },
|
2001-03-10 14:10:38 +00:00
|
|
|
{ "SYMBOLS", CFGTOK_SYMBOLS },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
2000-11-20 15:22:57 +00:00
|
|
|
cfgtok_t BlockTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
|
|
|
|
/* Read the block ident */
|
|
|
|
CfgSpecialToken (BlockNames, ENTRY_COUNT (BlockNames), "Block identifier");
|
|
|
|
BlockTok = CfgTok;
|
|
|
|
CfgNextTok ();
|
|
|
|
|
|
|
|
/* Expected a curly brace */
|
|
|
|
CfgConsume (CFGTOK_LCURLY, "`{' expected");
|
|
|
|
|
|
|
|
/* Read the block */
|
|
|
|
switch (BlockTok) {
|
|
|
|
|
|
|
|
case CFGTOK_MEMORY:
|
2000-11-20 15:22:57 +00:00
|
|
|
ParseMemory ();
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
case CFGTOK_FILES:
|
2000-11-20 15:22:57 +00:00
|
|
|
ParseFiles ();
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
case CFGTOK_SEGMENTS:
|
2000-11-20 15:22:57 +00:00
|
|
|
ParseSegments ();
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
case CFGTOK_FORMATS:
|
2000-11-20 15:22:57 +00:00
|
|
|
ParseFormats ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFGTOK_FEATURES:
|
|
|
|
ParseFeatures ();
|
2000-05-28 13:40:48 +00:00
|
|
|
break;
|
|
|
|
|
2001-03-10 14:10:38 +00:00
|
|
|
case CFGTOK_SYMBOLS:
|
|
|
|
ParseSymbols ();
|
|
|
|
break;
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
default:
|
2000-11-20 15:22:57 +00:00
|
|
|
FAIL ("Unexpected block token");
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip closing brace */
|
|
|
|
CfgConsume (CFGTOK_RCURLY, "`}' expected");
|
|
|
|
|
|
|
|
} while (CfgTok != CFGTOK_EOF);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CfgRead (void)
|
|
|
|
/* Read the configuration */
|
|
|
|
{
|
|
|
|
/* Create the descriptors for the binary formats */
|
|
|
|
BinFmtDesc = NewBinDesc ();
|
|
|
|
O65FmtDesc = NewO65Desc ();
|
|
|
|
|
|
|
|
/* If we have a config name given, open the file, otherwise we will read
|
|
|
|
* from a buffer.
|
|
|
|
*/
|
|
|
|
CfgOpenInput ();
|
|
|
|
|
|
|
|
/* Parse the file */
|
|
|
|
ParseConfig ();
|
|
|
|
|
|
|
|
/* Close the input file */
|
|
|
|
CfgCloseInput ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void CreateRunDefines (Memory* M, SegDesc* S, unsigned long Addr)
|
|
|
|
/* Create the defines for a RUN segment */
|
|
|
|
{
|
|
|
|
char Buf [256];
|
|
|
|
|
|
|
|
sprintf (Buf, "__%s_RUN__", S->Name);
|
|
|
|
CreateMemExport (Buf, M, Addr - M->Start);
|
|
|
|
sprintf (Buf, "__%s_SIZE__", S->Name);
|
|
|
|
CreateConstExport (Buf, S->Seg->Size);
|
|
|
|
S->Flags |= SF_RUN_DEF;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void CreateLoadDefines (Memory* M, SegDesc* S, unsigned long Addr)
|
|
|
|
/* Create the defines for a LOAD segment */
|
|
|
|
{
|
|
|
|
char Buf [256];
|
|
|
|
|
|
|
|
sprintf (Buf, "__%s_LOAD__", S->Name);
|
|
|
|
CreateMemExport (Buf, M, Addr - M->Start);
|
|
|
|
S->Flags |= SF_LOAD_DEF;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CfgAssignSegments (void)
|
|
|
|
/* Assign segments, define linker symbols where requested */
|
|
|
|
{
|
|
|
|
/* Walk through each of the memory sections. Add up the sizes and check
|
|
|
|
* for an overflow of the section. Assign the start addresses of the
|
|
|
|
* segments while doing this.
|
|
|
|
*/
|
|
|
|
Memory* M = MemoryList;
|
|
|
|
while (M) {
|
|
|
|
|
|
|
|
/* Get the start address of this memory area */
|
|
|
|
unsigned long Addr = M->Start;
|
|
|
|
|
|
|
|
/* Walk through the segments in this memory area */
|
|
|
|
MemListNode* N = M->SegList;
|
|
|
|
while (N) {
|
|
|
|
|
|
|
|
/* Get the segment from the node */
|
|
|
|
SegDesc* S = N->Seg;
|
|
|
|
|
|
|
|
/* Handle ALIGN and OFFSET/START */
|
|
|
|
if (S->Flags & SF_ALIGN) {
|
|
|
|
/* Align the address */
|
|
|
|
unsigned long Val = (0x01UL << S->Align) - 1;
|
|
|
|
Addr = (Addr + Val) & ~Val;
|
|
|
|
} else if (S->Flags & (SF_OFFSET | SF_START)) {
|
|
|
|
/* Give the segment a fixed starting address */
|
|
|
|
unsigned long NewAddr = S->Addr;
|
|
|
|
if (S->Flags & SF_OFFSET) {
|
|
|
|
/* An offset was given, no address, make an address */
|
|
|
|
NewAddr += M->Start;
|
|
|
|
}
|
|
|
|
if (Addr > NewAddr) {
|
|
|
|
/* Offset already too large */
|
|
|
|
if (S->Flags & SF_OFFSET) {
|
|
|
|
Error ("Offset too small in `%s', segment `%s'",
|
|
|
|
M->Name, S->Name);
|
|
|
|
} else {
|
|
|
|
Error ("Start address too low in `%s', segment `%s'",
|
|
|
|
M->Name, S->Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Addr = NewAddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this is the run area, set the start address of this segment */
|
|
|
|
if (S->Run == M) {
|
|
|
|
S->Seg->PC = Addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increment the fill level of the memory area and check for an
|
|
|
|
* overflow.
|
|
|
|
*/
|
|
|
|
M->FillLevel = Addr + S->Seg->Size - M->Start;
|
|
|
|
if (M->FillLevel > M->Size) {
|
|
|
|
Error ("Memory area overflow in `%s', segment `%s' (%lu bytes)",
|
|
|
|
M->Name, S->Name, M->FillLevel - M->Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If requested, define symbols for the start and size of the
|
|
|
|
* segment.
|
|
|
|
*/
|
|
|
|
if (S->Flags & SF_DEFINE) {
|
|
|
|
if ((S->Flags & SF_LOAD_AND_RUN) && S->Run == S->Load) {
|
|
|
|
/* RUN and LOAD given and in one memory area.
|
|
|
|
* Be careful: We will encounter this code twice, the
|
|
|
|
* first time when walking the RUN list, second time when
|
|
|
|
* walking the LOAD list. Be sure to define only the
|
|
|
|
* relevant symbols on each walk.
|
|
|
|
*/
|
|
|
|
if (S->Load == M) {
|
|
|
|
if ((S->Flags & SF_LOAD_DEF) == 0) {
|
|
|
|
CreateLoadDefines (M, S, Addr);
|
|
|
|
} else {
|
|
|
|
CHECK ((S->Flags & SF_RUN_DEF) == 0);
|
|
|
|
CreateRunDefines (M, S, Addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* RUN and LOAD in different memory areas, or RUN not
|
|
|
|
* given, so RUN defaults to LOAD. In the latter case, we
|
|
|
|
* have only one copy of the segment in the area.
|
|
|
|
*/
|
|
|
|
if (S->Run == M) {
|
|
|
|
CreateRunDefines (M, S, Addr);
|
|
|
|
}
|
|
|
|
if (S->Load == M) {
|
|
|
|
CreateLoadDefines (M, S, Addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate the new address */
|
|
|
|
Addr += S->Seg->Size;
|
|
|
|
|
|
|
|
/* Next segment */
|
|
|
|
N = N->Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If requested, define symbols for start and size of the memory area */
|
|
|
|
if (M->Flags & MF_DEFINE) {
|
|
|
|
char Buf [256];
|
|
|
|
sprintf (Buf, "__%s_START__", M->Name);
|
|
|
|
CreateMemExport (Buf, M, 0);
|
|
|
|
sprintf (Buf, "__%s_SIZE__", M->Name);
|
|
|
|
CreateConstExport (Buf, M->Size);
|
|
|
|
sprintf (Buf, "__%s_LAST__", M->Name);
|
|
|
|
CreateConstExport (Buf, M->FillLevel);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next memory area */
|
|
|
|
M = M->Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CfgWriteTarget (void)
|
|
|
|
/* Write the target file(s) */
|
|
|
|
{
|
|
|
|
Memory* M;
|
|
|
|
|
|
|
|
/* Walk through the files list */
|
|
|
|
File* F = FileList;
|
|
|
|
while (F) {
|
2001-03-03 12:01:46 +00:00
|
|
|
/* We don't need to look at files with no memory areas */
|
|
|
|
if (F->MemList) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
/* Is there an output file? */
|
|
|
|
if (strlen (F->Name) > 0) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
/* Assign a proper binary format */
|
|
|
|
if (F->Format == BINFMT_DEFAULT) {
|
|
|
|
F->Format = DefaultBinFmt;
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call the apropriate routine for the binary format */
|
2001-03-03 12:01:46 +00:00
|
|
|
switch (F->Format) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
case BINFMT_BINARY:
|
|
|
|
BinWriteTarget (BinFmtDesc, F);
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
case BINFMT_O65:
|
|
|
|
O65WriteTarget (O65FmtDesc, F);
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
default:
|
|
|
|
Internal ("Invalid binary format: %u", F->Format);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* No output file. Walk through the list and mark all segments
|
|
|
|
* loading into these memory areas in this file as dumped.
|
|
|
|
*/
|
|
|
|
M = F->MemList;
|
|
|
|
while (M) {
|
|
|
|
|
|
|
|
MemListNode* N;
|
|
|
|
|
|
|
|
/* Debugging */
|
2001-03-10 10:21:03 +00:00
|
|
|
Print (stdout, 2, "Skipping `%s'...\n", M->Name);
|
2001-03-03 12:01:46 +00:00
|
|
|
|
|
|
|
/* Walk throught the segments */
|
|
|
|
N = M->SegList;
|
|
|
|
while (N) {
|
|
|
|
if (N->Seg->Load == M) {
|
|
|
|
/* Load area - mark the segment as dumped */
|
|
|
|
N->Seg->Seg->Dumped = 1;
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2001-03-03 12:01:46 +00:00
|
|
|
/* Next segment node */
|
|
|
|
N = N->Next;
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
/* Next memory area */
|
|
|
|
M = M->FNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next file */
|
|
|
|
F = F->Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|