mirror of
https://github.com/zeldaret/oot.git
synced 2024-12-01 15:26:01 +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
|
CPP := cpp
|
||||||
MKLDSCRIPT := tools/mkldscript
|
MKLDSCRIPT := tools/mkldscript
|
||||||
|
MKDMADATA := tools/mkdmadata
|
||||||
ELF2ROM := tools/elf2rom
|
ELF2ROM := tools/elf2rom
|
||||||
ZAPD := tools/ZAPD/ZAPD.out
|
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
|
$(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 $@
|
$(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)
|
build/$(SPEC): $(SPEC)
|
||||||
$(CPP) $(CPPFLAGS) $< > build/spec
|
$(CPP) $(CPPFLAGS) $< > $@
|
||||||
$(MKLDSCRIPT) build/spec $@
|
|
||||||
|
build/ldscript.txt: build/$(SPEC)
|
||||||
|
$(MKLDSCRIPT) $< $@
|
||||||
|
|
||||||
build/undefined_syms.txt: undefined_syms.txt
|
build/undefined_syms.txt: undefined_syms.txt
|
||||||
$(CPP) $(CPPFLAGS) $< > build/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 $@ $<
|
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||||
$(OBJCOPY) -O binary $@ $@.bin
|
$(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
|
build/src/overlays/%.o: src/overlays/%.c
|
||||||
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
$(CC) -c $(CFLAGS) $(MIPS_VERSION) $(OPTFLAGS) -o $@ $<
|
||||||
$(CC_CHECK) $<
|
$(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
|
beginseg
|
||||||
name "dmadata"
|
name "dmadata"
|
||||||
include "build/asm/dmadata.o"
|
include "build/src/dmadata/dmadata.o"
|
||||||
endseg
|
endseg
|
||||||
|
|
||||||
beginseg
|
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
|
# Output files
|
||||||
*.exe
|
*.exe
|
||||||
yaz0
|
|
||||||
makeromfs
|
|
||||||
elf2rom
|
elf2rom
|
||||||
|
makeromfs
|
||||||
|
mkdmadata
|
||||||
mkldscript
|
mkldscript
|
||||||
vtxdis
|
vtxdis
|
||||||
|
yaz0
|
||||||
|
|
||||||
graphovl/
|
graphovl/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CFLAGS := -Wall -Wextra -pedantic -std=c99 -g -O2
|
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)
|
ifeq ($(shell command -v clang >/dev/null 2>&1; echo $$?),0)
|
||||||
CC := clang
|
CC := clang
|
||||||
|
@ -28,11 +28,12 @@ distclean: clean
|
||||||
|
|
||||||
.PHONY: all clean distclean
|
.PHONY: all clean distclean
|
||||||
|
|
||||||
mkldscript_SOURCES := mkldscript.c util.c
|
|
||||||
elf2rom_SOURCES := elf2rom.c elf32.c n64chksum.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
|
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 =
|
define COMPILE =
|
||||||
$(1): $($1_SOURCES)
|
$(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 <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "spec.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof(arr[0]))
|
struct Segment *g_segments;
|
||||||
|
int g_segmentsCount;
|
||||||
|
|
||||||
static FILE *fout;
|
static void write_ld_script(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)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
|
@ -436,14 +124,14 @@ static void write_ld_script(void)
|
||||||
|
|
||||||
fprintf(fout, " _%sSegmentSDataEnd = .;\n", seg->name);
|
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++)
|
for (j = 0; j < seg->includesCount; j++)
|
||||||
fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
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))
|
if (seg->fields & (1 << STMT_increment))
|
||||||
fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
||||||
|
@ -489,20 +177,20 @@ static void write_ld_script(void)
|
||||||
//if (seg->fields & (1 << STMT_increment))
|
//if (seg->fields & (1 << STMT_increment))
|
||||||
//fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
//fprintf(fout, " . += 0x%08X;\n", seg->increment);
|
||||||
|
|
||||||
//fprintf(fout, " ..%s.ovl ADDR(..%s) + SIZEOF(..%s) :\n"
|
//fprintf(fout, " ..%s.ovl ADDR(..%s) + SIZEOF(..%s) :\n"
|
||||||
// /*" ..%s.bss :\n"*/
|
// /*" ..%s.bss :\n"*/
|
||||||
// " {\n",
|
// " {\n",
|
||||||
// seg->name, seg->name, seg->name);
|
// seg->name, seg->name, seg->name);
|
||||||
//fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
//fprintf(fout, " _%sSegmentOvlStart = .;\n", seg->name);
|
||||||
|
|
||||||
//for (j = 0; j < seg->includesCount; j++)
|
//for (j = 0; j < seg->includesCount; j++)
|
||||||
// fprintf(fout, " %s (.ovl)\n", seg->includes[j].fpath);
|
// 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)
|
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"
|
"usage: %s SPEC_FILE LD_SCRIPT\n"
|
||||||
"SPEC_FILE file describing the organization of object files into segments\n"
|
"SPEC_FILE file describing the organization of object files into segments\n"
|
||||||
"LD_SCRIPT filename of output linker script\n",
|
"LD_SCRIPT filename of output linker script\n",
|
||||||
execname);
|
execname);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
void *spec;
|
FILE *ldout;
|
||||||
size_t size;
|
void *spec;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spec = util_read_whole_file(argv[1], &size);
|
spec = util_read_whole_file(argv[1], &size);
|
||||||
parse_rom_spec(spec);
|
parse_rom_spec(spec, &g_segments, &g_segmentsCount);
|
||||||
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);
|
|
||||||
|
|
||||||
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