mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-25 01:34:18 +00:00
Rework handling of dmadata (#1036)
* Generate dmadata * Remove tab indentations in mkdmadata.c and mkldscript.c * Fix * Review suggestions * Hopefully fix * Fix index Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com> Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
This commit is contained in:
parent
783ef3a117
commit
04a9d51e90
13 changed files with 2034 additions and 3438 deletions
15
Makefile
15
Makefile
|
@ -71,6 +71,7 @@ CC_CHECK := gcc -fno-builtin -fsyntax-only -fsigned-char -std=gnu90 -D _LANGUA
|
|||
|
||||
CPP := cpp
|
||||
MKLDSCRIPT := tools/mkldscript
|
||||
MKDMADATA := tools/mkdmadata
|
||||
ELF2ROM := tools/elf2rom
|
||||
ZAPD := tools/ZAPD/ZAPD.out
|
||||
|
||||
|
@ -172,9 +173,11 @@ $(ROM): $(ELF)
|
|||
$(ELF): $(TEXTURE_FILES_OUT) $(ASSET_FILES_OUT) $(O_FILES) build/ldscript.txt build/undefined_syms.txt
|
||||
$(LD) -T build/undefined_syms.txt -T build/ldscript.txt --no-check-sections --accept-unknown-input-arch --emit-relocs -Map build/z64.map -o $@
|
||||
|
||||
build/ldscript.txt: $(SPEC)
|
||||
$(CPP) $(CPPFLAGS) $< > build/spec
|
||||
$(MKLDSCRIPT) build/spec $@
|
||||
build/$(SPEC): $(SPEC)
|
||||
$(CPP) $(CPPFLAGS) $< > $@
|
||||
|
||||
build/ldscript.txt: build/$(SPEC)
|
||||
$(MKLDSCRIPT) $< $@
|
||||
|
||||
build/undefined_syms.txt: undefined_syms.txt
|
||||
$(CPP) $(CPPFLAGS) $< > build/undefined_syms.txt
|
||||
|
@ -227,6 +230,12 @@ build/assets/%.o: assets/%.c
|
|||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||
$(OBJCOPY) -O binary $@ $@.bin
|
||||
|
||||
build/dmadata_table_spec.h: build/$(SPEC)
|
||||
$(MKDMADATA) $< $@
|
||||
|
||||
build/src/boot/z_std_dma.o: build/dmadata_table_spec.h
|
||||
build/src/dmadata/dmadata.o: build/dmadata_table_spec.h
|
||||
|
||||
build/src/overlays/%.o: src/overlays/%.c
|
||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||
$(CC_CHECK) $<
|
||||
|
|
1545
asm/dmadata.s
1545
asm/dmadata.s
File diff suppressed because it is too large
Load diff
9
include/tables/dmadata_table.h
Normal file
9
include/tables/dmadata_table.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Select dmadata table for version
|
||||
*/
|
||||
#ifdef NON_MATCHING
|
||||
// For non-matching builds, dmadata is generated from the specfile segments
|
||||
#include "dmadata_table_spec.h"
|
||||
#else
|
||||
#include "tables/dmadata_table_mqdbg.h"
|
||||
#endif
|
1535
include/tables/dmadata_table_mqdbg.h
Normal file
1535
include/tables/dmadata_table_mqdbg.h
Normal file
File diff suppressed because it is too large
Load diff
2
spec
2
spec
|
@ -113,7 +113,7 @@ endseg
|
|||
|
||||
beginseg
|
||||
name "dmadata"
|
||||
include "build/asm/dmadata.o"
|
||||
include "build/src/dmadata/dmadata.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
|
|
1540
src/boot/z_std_dma.c
1540
src/boot/z_std_dma.c
File diff suppressed because it is too large
Load diff
24
src/dmadata/dmadata.c
Normal file
24
src/dmadata/dmadata.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "z64dma.h"
|
||||
|
||||
// Linker symbol declarations (used in the table below)
|
||||
#define DEFINE_DMA_ENTRY(name) \
|
||||
extern u8 _##name##SegmentRomStart[]; \
|
||||
extern u8 _##name##SegmentRomEnd[];
|
||||
|
||||
#include "tables/dmadata_table.h"
|
||||
|
||||
#undef DEFINE_DMA_ENTRY
|
||||
|
||||
// dmadata Table definition
|
||||
#define DEFINE_DMA_ENTRY(name) \
|
||||
{ (u32)_##name##SegmentRomStart, (u32)_##name##SegmentRomEnd, (u32)_##name##SegmentRomStart, 0 },
|
||||
|
||||
DmaEntry gDmaDataTable[] = {
|
||||
#include "tables/dmadata_table.h"
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
#undef DEFINE_DMA_ENTRY
|
||||
|
||||
// Additional padding?
|
||||
u8 sDmaDataPadding[0xF0] = { 0 };
|
5
tools/.gitignore
vendored
5
tools/.gitignore
vendored
|
@ -1,9 +1,10 @@
|
|||
# Output files
|
||||
*.exe
|
||||
yaz0
|
||||
makeromfs
|
||||
elf2rom
|
||||
makeromfs
|
||||
mkdmadata
|
||||
mkldscript
|
||||
vtxdis
|
||||
yaz0
|
||||
|
||||
graphovl/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
CFLAGS := -Wall -Wextra -pedantic -std=c99 -g -O2
|
||||
PROGRAMS := yaz0 makeromfs elf2rom mkldscript vtxdis
|
||||
PROGRAMS := elf2rom makeromfs mkdmadata mkldscript vtxdis yaz0
|
||||
|
||||
ifeq ($(shell command -v clang >/dev/null 2>&1; echo $$?),0)
|
||||
CC := clang
|
||||
|
@ -28,11 +28,12 @@ distclean: clean
|
|||
|
||||
.PHONY: all clean distclean
|
||||
|
||||
mkldscript_SOURCES := mkldscript.c util.c
|
||||
elf2rom_SOURCES := elf2rom.c elf32.c n64chksum.c util.c
|
||||
yaz0_SOURCES := yaz0tool.c yaz0.c util.c
|
||||
makeromfs_SOURCES := makeromfs.c n64chksum.c util.c
|
||||
vtxdis_SOURCES := vtxdis.c
|
||||
mkdmadata_SOURCES := mkdmadata.c spec.c util.c
|
||||
mkldscript_SOURCES := mkldscript.c spec.c util.c
|
||||
vtxdis_SOURCES := vtxdis.c
|
||||
yaz0_SOURCES := yaz0tool.c yaz0.c util.c
|
||||
|
||||
define COMPILE =
|
||||
$(1): $($1_SOURCES)
|
||||
|
|
55
tools/mkdmadata.c
Normal file
55
tools/mkdmadata.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spec.h"
|
||||
#include "util.h"
|
||||
|
||||
struct Segment* g_segments;
|
||||
int g_segmentsCount;
|
||||
|
||||
static void write_dmadata_table(FILE *fout)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g_segmentsCount; i++)
|
||||
fprintf(fout, "DEFINE_DMA_ENTRY(%s)\n", g_segments[i].name);
|
||||
}
|
||||
|
||||
static void usage(const char *execname)
|
||||
{
|
||||
fprintf(stderr, "zelda64 dmadata generation tool v0.01\n"
|
||||
"usage: %s SPEC_FILE DMADATA_TABLE\n"
|
||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||
"DMADATA_TABLE filename of output dmadata table header\n",
|
||||
execname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *dmaout;
|
||||
void *spec;
|
||||
size_t size;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spec = util_read_whole_file(argv[1], &size);
|
||||
parse_rom_spec(spec, &g_segments, &g_segmentsCount);
|
||||
|
||||
dmaout = fopen(argv[2], "w");
|
||||
if (dmaout == NULL)
|
||||
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
||||
write_dmadata_table(dmaout);
|
||||
fclose(dmaout);
|
||||
free(spec);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -6,325 +6,13 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spec.h"
|
||||
#include "util.h"
|
||||
|
||||
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
struct Segment *g_segments;
|
||||
int g_segmentsCount;
|
||||
|
||||
static FILE *fout;
|
||||
|
||||
enum
|
||||
{
|
||||
STMT_address,
|
||||
STMT_after,
|
||||
STMT_align,
|
||||
STMT_beginseg,
|
||||
STMT_endseg,
|
||||
STMT_entry,
|
||||
STMT_flags,
|
||||
STMT_include,
|
||||
STMT_include_data_with_rodata,
|
||||
STMT_name,
|
||||
STMT_number,
|
||||
STMT_romalign,
|
||||
STMT_stack,
|
||||
STMT_increment,
|
||||
STMT_pad_text,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FLAG_BOOT = (1 << 0),
|
||||
FLAG_OBJECT = (1 << 1),
|
||||
FLAG_RAW = (1 << 2),
|
||||
};
|
||||
|
||||
struct Include
|
||||
{
|
||||
char *fpath;
|
||||
int linkerPadding;
|
||||
uint8_t dataWithRodata;
|
||||
};
|
||||
|
||||
struct Segment
|
||||
{
|
||||
uint32_t fields;
|
||||
char *name;
|
||||
char *after;
|
||||
uint32_t flags;
|
||||
uint32_t address;
|
||||
uint32_t stack;
|
||||
uint32_t align;
|
||||
uint32_t romalign;
|
||||
uint32_t increment;
|
||||
uint32_t entry;
|
||||
uint32_t number;
|
||||
struct Include *includes;
|
||||
int includesCount;
|
||||
};
|
||||
|
||||
static struct Segment *g_segments = NULL;
|
||||
static int g_segmentsCount = 0;
|
||||
|
||||
static struct Segment *add_segment(void)
|
||||
{
|
||||
struct Segment *seg;
|
||||
|
||||
g_segmentsCount++;
|
||||
g_segments = realloc(g_segments, g_segmentsCount * sizeof(*g_segments));
|
||||
|
||||
seg = &g_segments[g_segmentsCount - 1];
|
||||
memset(seg, 0, sizeof(*seg));
|
||||
|
||||
seg->align = 16;
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
static char *skip_whitespace(char *str)
|
||||
{
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
return str;
|
||||
}
|
||||
|
||||
// null terminates the current token and returns a pointer to the next token
|
||||
static char *token_split(char *str)
|
||||
{
|
||||
while (!isspace(*str))
|
||||
{
|
||||
if (*str == 0)
|
||||
return str; // end of string
|
||||
str++;
|
||||
}
|
||||
*str = 0; // terminate token
|
||||
str++;
|
||||
|
||||
return skip_whitespace(str);
|
||||
}
|
||||
|
||||
// null terminates the current line and returns a pointer to the next line
|
||||
static char *line_split(char *str)
|
||||
{
|
||||
while (*str != '\n')
|
||||
{
|
||||
if (*str == 0)
|
||||
return str; // end of string
|
||||
str++;
|
||||
}
|
||||
*str = 0; // terminate line
|
||||
return str + 1;
|
||||
}
|
||||
|
||||
static bool parse_number(const char *str, unsigned int *num)
|
||||
{
|
||||
char *endptr;
|
||||
long int n = strtol(str, &endptr, 0);
|
||||
*num = n;
|
||||
return endptr > str;
|
||||
}
|
||||
|
||||
static bool parse_flags(char *str, unsigned int *flags)
|
||||
{
|
||||
unsigned int f = 0;
|
||||
|
||||
while (str[0] != 0)
|
||||
{
|
||||
char *next = token_split(str);
|
||||
|
||||
if (strcmp(str, "BOOT") == 0)
|
||||
f |= FLAG_BOOT;
|
||||
else if (strcmp(str, "OBJECT") == 0)
|
||||
f |= FLAG_OBJECT;
|
||||
else if (strcmp(str, "RAW") == 0)
|
||||
f |= FLAG_RAW;
|
||||
else
|
||||
return false;
|
||||
|
||||
str = next;
|
||||
}
|
||||
*flags = f;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_quoted_string(char *str, char **out)
|
||||
{
|
||||
if (*str != '"')
|
||||
return false;
|
||||
|
||||
str++;
|
||||
*out = str;
|
||||
|
||||
while (*str != '"')
|
||||
{
|
||||
if (*str == 0)
|
||||
return false; // unterminated quote
|
||||
str++;
|
||||
}
|
||||
*str = 0;
|
||||
str++;
|
||||
|
||||
str = skip_whitespace(str);
|
||||
if (*str != 0)
|
||||
return false; // garbage after filename
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_pow_of_2(unsigned int n)
|
||||
{
|
||||
return (n & (n - 1)) == 0;
|
||||
}
|
||||
|
||||
static const char *const stmtNames[] =
|
||||
{
|
||||
[STMT_address] = "address",
|
||||
[STMT_after] = "after",
|
||||
[STMT_align] = "align",
|
||||
[STMT_beginseg] = "beginseg",
|
||||
[STMT_endseg] = "endseg",
|
||||
[STMT_entry] = "entry",
|
||||
[STMT_flags] = "flags",
|
||||
[STMT_include] = "include",
|
||||
[STMT_include_data_with_rodata] = "include_data_with_rodata",
|
||||
[STMT_name] = "name",
|
||||
[STMT_number] = "number",
|
||||
[STMT_romalign] = "romalign",
|
||||
[STMT_stack] = "stack",
|
||||
[STMT_increment] = "increment",
|
||||
[STMT_pad_text] = "pad_text",
|
||||
};
|
||||
|
||||
static void parse_rom_spec(char *spec)
|
||||
{
|
||||
int lineNum = 1;
|
||||
char *line = spec;
|
||||
|
||||
struct Segment *currSeg = NULL;
|
||||
|
||||
// iterate over lines
|
||||
while (line[0] != 0)
|
||||
{
|
||||
char *nextLine = line_split(line);
|
||||
char* stmtName;
|
||||
|
||||
if (line[0] != 0)
|
||||
{
|
||||
stmtName = skip_whitespace(line);
|
||||
}
|
||||
|
||||
if (line[0] != 0 && stmtName[0] != 0)
|
||||
{
|
||||
char *args = token_split(stmtName);
|
||||
unsigned int stmt;
|
||||
|
||||
for (stmt = 0; stmt < ARRAY_COUNT(stmtNames); stmt++)
|
||||
if (strcmp(stmtName, stmtNames[stmt]) == 0)
|
||||
goto got_stmt;
|
||||
util_fatal_error("line %i: unknown statement '%s'", lineNum, stmtName);
|
||||
got_stmt:
|
||||
|
||||
if (currSeg != NULL)
|
||||
{
|
||||
// ensure no duplicates (except for 'include' or 'pad_text')
|
||||
if (stmt != STMT_include && stmt != STMT_include_data_with_rodata && stmt != STMT_pad_text &&
|
||||
(currSeg->fields & (1 << stmt)))
|
||||
util_fatal_error("line %i: duplicate '%s' statement", lineNum, stmtName);
|
||||
|
||||
currSeg->fields |= 1 << stmt;
|
||||
|
||||
// statements valid within a segment definition
|
||||
switch (stmt)
|
||||
{
|
||||
case STMT_beginseg:
|
||||
util_fatal_error("line %i: '%s' inside of a segment definition", lineNum, stmtName);
|
||||
break;
|
||||
case STMT_endseg:
|
||||
// verify segment data
|
||||
if (currSeg->name == NULL)
|
||||
util_fatal_error("line %i: no name specified for segment", lineNum);
|
||||
if (currSeg->includesCount == 0)
|
||||
util_fatal_error("line %i: no includes specified for segment", lineNum);
|
||||
currSeg = NULL;
|
||||
break;
|
||||
case STMT_name:
|
||||
if (!parse_quoted_string(args, &currSeg->name))
|
||||
util_fatal_error("line %i: invalid name", lineNum);
|
||||
break;
|
||||
case STMT_after:
|
||||
if (!parse_quoted_string(args, &currSeg->after))
|
||||
util_fatal_error("line %i: invalid name for 'after'", lineNum);
|
||||
break;
|
||||
case STMT_address:
|
||||
if (!parse_number(args, &currSeg->address))
|
||||
util_fatal_error("line %i: expected number after 'address'", lineNum);
|
||||
break;
|
||||
case STMT_number:
|
||||
if (!parse_number(args, &currSeg->number))
|
||||
util_fatal_error("line %i: expected number after 'number'", lineNum);
|
||||
break;
|
||||
case STMT_flags:
|
||||
if (!parse_flags(args, &currSeg->flags))
|
||||
util_fatal_error("line %i: invalid flags", lineNum);
|
||||
break;
|
||||
case STMT_align:
|
||||
if (!parse_number(args, &currSeg->align))
|
||||
util_fatal_error("line %i: expected number after 'align'", lineNum);
|
||||
if (!is_pow_of_2(currSeg->align))
|
||||
util_fatal_error("line %i: alignment is not a power of two", lineNum);
|
||||
break;
|
||||
case STMT_romalign:
|
||||
if (!parse_number(args, &currSeg->romalign))
|
||||
util_fatal_error("line %i: expected number after 'romalign'", lineNum);
|
||||
if (!is_pow_of_2(currSeg->romalign))
|
||||
util_fatal_error("line %i: alignment is not a power of two", lineNum);
|
||||
break;
|
||||
case STMT_include:
|
||||
case STMT_include_data_with_rodata:
|
||||
currSeg->includesCount++;
|
||||
currSeg->includes = realloc(currSeg->includes, currSeg->includesCount * sizeof(*currSeg->includes));
|
||||
|
||||
if (!parse_quoted_string(args, &currSeg->includes[currSeg->includesCount - 1].fpath))
|
||||
util_fatal_error("line %i: invalid filename", lineNum);
|
||||
|
||||
currSeg->includes[currSeg->includesCount - 1].linkerPadding = 0;
|
||||
currSeg->includes[currSeg->includesCount - 1].dataWithRodata = (stmt == STMT_include_data_with_rodata);
|
||||
break;
|
||||
case STMT_increment:
|
||||
if (!parse_number(args, &currSeg->increment))
|
||||
util_fatal_error("line %i: expected number after 'increment'", lineNum);
|
||||
break;
|
||||
case STMT_pad_text:
|
||||
currSeg->includes[currSeg->includesCount - 1].linkerPadding += 0x10;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "warning: '%s' is not implemented\n", stmtName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// commands valid outside a segment definition
|
||||
switch (stmt)
|
||||
{
|
||||
case STMT_beginseg:
|
||||
currSeg = add_segment();
|
||||
break;
|
||||
case STMT_endseg:
|
||||
util_fatal_error("line %i: '%s' outside of a segment definition", lineNum, stmtName);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "warning: '%s' is not implemented\n", stmtName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line = nextLine;
|
||||
lineNum++;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_ld_script(void)
|
||||
static void write_ld_script(FILE *fout)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
@ -436,14 +124,14 @@ static void write_ld_script(void)
|
|||
|
||||
fprintf(fout, " _%sSegmentSDataEnd = .;\n", seg->name);
|
||||
|
||||
fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
||||
fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
||||
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
||||
for (j = 0; j < seg->includesCount; j++)
|
||||
fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
||||
|
||||
fprintf(fout, " . = ALIGN(0x10);\n");
|
||||
fprintf(fout, " . = ALIGN(0x10);\n");
|
||||
|
||||
fprintf(fout, " _%sSegmentOvlEnd = .;\n", seg->name);
|
||||
fprintf(fout, " _%sSegmentOvlEnd = .;\n", seg->name);
|
||||
|
||||
if (seg->fields & (1 << STMT_increment))
|
||||
fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
||||
|
@ -489,20 +177,20 @@ static void write_ld_script(void)
|
|||
//if (seg->fields & (1 << STMT_increment))
|
||||
//fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
||||
|
||||
//fprintf(fout, " ..%s.ovl ADDR(..%s) + SIZEOF(..%s) :\n"
|
||||
// /*" ..%s.bss :\n"*/
|
||||
// " {\n",
|
||||
// seg->name, seg->name, seg->name);
|
||||
//fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
||||
//fprintf(fout, " ..%s.ovl ADDR(..%s) + SIZEOF(..%s) :\n"
|
||||
// /*" ..%s.bss :\n"*/
|
||||
// " {\n",
|
||||
// seg->name, seg->name, seg->name);
|
||||
//fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
||||
|
||||
//for (j = 0; j < seg->includesCount; j++)
|
||||
// fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
||||
//for (j = 0; j < seg->includesCount; j++)
|
||||
// fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
||||
|
||||
////fprintf(fout, " . = ALIGN(0x10);\n");
|
||||
////fprintf(fout, " . = ALIGN(0x10);\n");
|
||||
|
||||
//fprintf(fout, " _%sSegmentOvlEnd = .;\n", seg->name);
|
||||
//fprintf(fout, " _%sSegmentOvlEnd = .;\n", seg->name);
|
||||
|
||||
//fprintf(fout, "\n }\n");
|
||||
//fprintf(fout, "\n }\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -511,32 +199,35 @@ static void write_ld_script(void)
|
|||
|
||||
static void usage(const char *execname)
|
||||
{
|
||||
fprintf(stderr, "Nintendo 64 linker script generation tool v0.01\n"
|
||||
fprintf(stderr, "Nintendo 64 linker script generation tool v0.02\n"
|
||||
"usage: %s SPEC_FILE LD_SCRIPT\n"
|
||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||
"LD_SCRIPT filename of output linker script\n",
|
||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||
"LD_SCRIPT filename of output linker script\n",
|
||||
execname);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *spec;
|
||||
size_t size;
|
||||
FILE *ldout;
|
||||
void *spec;
|
||||
size_t size;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (argc != 3)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spec = util_read_whole_file(argv[1], &size);
|
||||
parse_rom_spec(spec);
|
||||
fout = fopen(argv[2], "w");
|
||||
if (fout == NULL)
|
||||
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
||||
write_ld_script();
|
||||
free(spec);
|
||||
fclose(fout);
|
||||
spec = util_read_whole_file(argv[1], &size);
|
||||
parse_rom_spec(spec, &g_segments, &g_segmentsCount);
|
||||
|
||||
return 0;
|
||||
ldout = fopen(argv[2], "w");
|
||||
if (ldout == NULL)
|
||||
util_fatal_error("failed to open file '%s' for writing", argv[2]);
|
||||
write_ld_script(ldout);
|
||||
fclose(ldout);
|
||||
|
||||
free(spec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
284
tools/spec.c
Normal file
284
tools/spec.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "spec.h"
|
||||
|
||||
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
|
||||
static struct Segment *add_segment(struct Segment **segments, int *segments_count)
|
||||
{
|
||||
struct Segment *seg;
|
||||
|
||||
(*segments_count)++;
|
||||
*segments = realloc(*segments, *segments_count * sizeof(**segments));
|
||||
|
||||
seg = &(*segments)[*segments_count - 1];
|
||||
memset(seg, 0, sizeof(*seg));
|
||||
|
||||
seg->align = 16;
|
||||
|
||||
return seg;
|
||||
}
|
||||
|
||||
static char *skip_whitespace(char *str)
|
||||
{
|
||||
while (isspace(*str))
|
||||
str++;
|
||||
return str;
|
||||
}
|
||||
|
||||
// null terminates the current token and returns a pointer to the next token
|
||||
static char *token_split(char *str)
|
||||
{
|
||||
while (!isspace(*str))
|
||||
{
|
||||
if (*str == 0)
|
||||
return str; // end of string
|
||||
str++;
|
||||
}
|
||||
*str = 0; // terminate token
|
||||
str++;
|
||||
|
||||
return skip_whitespace(str);
|
||||
}
|
||||
|
||||
// null terminates the current line and returns a pointer to the next line
|
||||
static char *line_split(char *str)
|
||||
{
|
||||
while (*str != '\n')
|
||||
{
|
||||
if (*str == 0)
|
||||
return str; // end of string
|
||||
str++;
|
||||
}
|
||||
*str = 0; // terminate line
|
||||
return str + 1;
|
||||
}
|
||||
|
||||
static bool parse_number(const char *str, unsigned int *num)
|
||||
{
|
||||
char *endptr;
|
||||
long int n = strtol(str, &endptr, 0);
|
||||
*num = n;
|
||||
return endptr > str;
|
||||
}
|
||||
|
||||
static bool parse_flags(char *str, unsigned int *flags)
|
||||
{
|
||||
unsigned int f = 0;
|
||||
|
||||
while (str[0] != 0)
|
||||
{
|
||||
char *next = token_split(str);
|
||||
|
||||
if (strcmp(str, "BOOT") == 0)
|
||||
f |= FLAG_BOOT;
|
||||
else if (strcmp(str, "OBJECT") == 0)
|
||||
f |= FLAG_OBJECT;
|
||||
else if (strcmp(str, "RAW") == 0)
|
||||
f |= FLAG_RAW;
|
||||
else
|
||||
return false;
|
||||
|
||||
str = next;
|
||||
}
|
||||
*flags = f;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_quoted_string(char *str, char **out)
|
||||
{
|
||||
if (*str != '"')
|
||||
return false;
|
||||
|
||||
str++;
|
||||
*out = str;
|
||||
|
||||
while (*str != '"')
|
||||
{
|
||||
if (*str == 0)
|
||||
return false; // unterminated quote
|
||||
str++;
|
||||
}
|
||||
*str = 0;
|
||||
str++;
|
||||
|
||||
str = skip_whitespace(str);
|
||||
if (*str != 0)
|
||||
return false; // garbage after filename
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_pow_of_2(unsigned int n)
|
||||
{
|
||||
return (n & (n - 1)) == 0;
|
||||
}
|
||||
|
||||
static const char *const stmtNames[] =
|
||||
{
|
||||
[STMT_address] = "address",
|
||||
[STMT_after] = "after",
|
||||
[STMT_align] = "align",
|
||||
[STMT_beginseg] = "beginseg",
|
||||
[STMT_endseg] = "endseg",
|
||||
[STMT_entry] = "entry",
|
||||
[STMT_flags] = "flags",
|
||||
[STMT_include] = "include",
|
||||
[STMT_include_data_with_rodata] = "include_data_with_rodata",
|
||||
[STMT_name] = "name",
|
||||
[STMT_number] = "number",
|
||||
[STMT_romalign] = "romalign",
|
||||
[STMT_stack] = "stack",
|
||||
[STMT_increment] = "increment",
|
||||
[STMT_pad_text] = "pad_text",
|
||||
};
|
||||
|
||||
void parse_rom_spec(char *spec, struct Segment **segments, int *segment_count)
|
||||
{
|
||||
int lineNum = 1;
|
||||
char *line = spec;
|
||||
|
||||
struct Segment *currSeg = NULL;
|
||||
|
||||
// iterate over lines
|
||||
while (line[0] != 0)
|
||||
{
|
||||
char *nextLine = line_split(line);
|
||||
char* stmtName;
|
||||
|
||||
if (line[0] != 0)
|
||||
{
|
||||
stmtName = skip_whitespace(line);
|
||||
}
|
||||
|
||||
if (line[0] != 0 && stmtName[0] != 0)
|
||||
{
|
||||
char *args = token_split(stmtName);
|
||||
unsigned int stmt;
|
||||
|
||||
for (stmt = 0; stmt < ARRAY_COUNT(stmtNames); stmt++)
|
||||
if (strcmp(stmtName, stmtNames[stmt]) == 0)
|
||||
goto got_stmt;
|
||||
util_fatal_error("line %i: unknown statement '%s'", lineNum, stmtName);
|
||||
got_stmt:
|
||||
|
||||
if (currSeg != NULL)
|
||||
{
|
||||
// ensure no duplicates (except for 'include' or 'pad_text')
|
||||
if (stmt != STMT_include && stmt != STMT_include_data_with_rodata && stmt != STMT_pad_text &&
|
||||
(currSeg->fields & (1 << stmt)))
|
||||
util_fatal_error("line %i: duplicate '%s' statement", lineNum, stmtName);
|
||||
|
||||
currSeg->fields |= 1 << stmt;
|
||||
|
||||
// statements valid within a segment definition
|
||||
switch (stmt)
|
||||
{
|
||||
case STMT_beginseg:
|
||||
util_fatal_error("line %i: '%s' inside of a segment definition", lineNum, stmtName);
|
||||
break;
|
||||
case STMT_endseg:
|
||||
// verify segment data
|
||||
if (currSeg->name == NULL)
|
||||
util_fatal_error("line %i: no name specified for segment", lineNum);
|
||||
if (currSeg->includesCount == 0)
|
||||
util_fatal_error("line %i: no includes specified for segment", lineNum);
|
||||
currSeg = NULL;
|
||||
break;
|
||||
case STMT_name:
|
||||
if (!parse_quoted_string(args, &currSeg->name))
|
||||
util_fatal_error("line %i: invalid name", lineNum);
|
||||
break;
|
||||
case STMT_after:
|
||||
if (!parse_quoted_string(args, &currSeg->after))
|
||||
util_fatal_error("line %i: invalid name for 'after'", lineNum);
|
||||
break;
|
||||
case STMT_address:
|
||||
if (!parse_number(args, &currSeg->address))
|
||||
util_fatal_error("line %i: expected number after 'address'", lineNum);
|
||||
break;
|
||||
case STMT_number:
|
||||
if (!parse_number(args, &currSeg->number))
|
||||
util_fatal_error("line %i: expected number after 'number'", lineNum);
|
||||
break;
|
||||
case STMT_flags:
|
||||
if (!parse_flags(args, &currSeg->flags))
|
||||
util_fatal_error("line %i: invalid flags", lineNum);
|
||||
break;
|
||||
case STMT_align:
|
||||
if (!parse_number(args, &currSeg->align))
|
||||
util_fatal_error("line %i: expected number after 'align'", lineNum);
|
||||
if (!is_pow_of_2(currSeg->align))
|
||||
util_fatal_error("line %i: alignment is not a power of two", lineNum);
|
||||
break;
|
||||
case STMT_romalign:
|
||||
if (!parse_number(args, &currSeg->romalign))
|
||||
util_fatal_error("line %i: expected number after 'romalign'", lineNum);
|
||||
if (!is_pow_of_2(currSeg->romalign))
|
||||
util_fatal_error("line %i: alignment is not a power of two", lineNum);
|
||||
break;
|
||||
case STMT_include:
|
||||
case STMT_include_data_with_rodata:
|
||||
currSeg->includesCount++;
|
||||
currSeg->includes = realloc(currSeg->includes, currSeg->includesCount * sizeof(*currSeg->includes));
|
||||
|
||||
if (!parse_quoted_string(args, &currSeg->includes[currSeg->includesCount - 1].fpath))
|
||||
util_fatal_error("line %i: invalid filename", lineNum);
|
||||
|
||||
currSeg->includes[currSeg->includesCount - 1].linkerPadding = 0;
|
||||
currSeg->includes[currSeg->includesCount - 1].dataWithRodata = (stmt == STMT_include_data_with_rodata);
|
||||
break;
|
||||
case STMT_increment:
|
||||
if (!parse_number(args, &currSeg->increment))
|
||||
util_fatal_error("line %i: expected number after 'increment'", lineNum);
|
||||
break;
|
||||
case STMT_pad_text:
|
||||
currSeg->includes[currSeg->includesCount - 1].linkerPadding += 0x10;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "warning: '%s' is not implemented\n", stmtName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// commands valid outside a segment definition
|
||||
switch (stmt)
|
||||
{
|
||||
case STMT_beginseg:
|
||||
currSeg = add_segment(segments, segment_count);
|
||||
currSeg->includes = NULL;
|
||||
break;
|
||||
case STMT_endseg:
|
||||
util_fatal_error("line %i: '%s' outside of a segment definition", lineNum, stmtName);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "warning: '%s' is not implemented\n", stmtName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line = nextLine;
|
||||
lineNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void free_rom_spec(struct Segment *segments, int segment_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < segment_count; i++)
|
||||
{
|
||||
if (segments[i].includes != NULL)
|
||||
free(segments[i].includes);
|
||||
}
|
||||
free(segments);
|
||||
}
|
58
tools/spec.h
Normal file
58
tools/spec.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef _SPEC_H_
|
||||
#define _SPEC_H_
|
||||
|
||||
enum
|
||||
{
|
||||
STMT_address,
|
||||
STMT_after,
|
||||
STMT_align,
|
||||
STMT_beginseg,
|
||||
STMT_endseg,
|
||||
STMT_entry,
|
||||
STMT_flags,
|
||||
STMT_include,
|
||||
STMT_include_data_with_rodata,
|
||||
STMT_name,
|
||||
STMT_number,
|
||||
STMT_romalign,
|
||||
STMT_stack,
|
||||
STMT_increment,
|
||||
STMT_pad_text,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FLAG_BOOT = (1 << 0),
|
||||
FLAG_OBJECT = (1 << 1),
|
||||
FLAG_RAW = (1 << 2),
|
||||
};
|
||||
|
||||
struct Include
|
||||
{
|
||||
char *fpath;
|
||||
int linkerPadding;
|
||||
uint8_t dataWithRodata;
|
||||
};
|
||||
|
||||
struct Segment
|
||||
{
|
||||
uint32_t fields;
|
||||
char *name;
|
||||
char *after;
|
||||
uint32_t flags;
|
||||
uint32_t address;
|
||||
uint32_t stack;
|
||||
uint32_t align;
|
||||
uint32_t romalign;
|
||||
uint32_t increment;
|
||||
uint32_t entry;
|
||||
uint32_t number;
|
||||
struct Include *includes;
|
||||
int includesCount;
|
||||
};
|
||||
|
||||
void parse_rom_spec(char *spec, struct Segment **segments, int *segment_count);
|
||||
|
||||
void free_rom_spec(struct Segment *segments, int segment_count);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue