1
0
Fork 0
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:
Tharo 2021-11-30 23:29:09 +00:00 committed by GitHub
parent 783ef3a117
commit 04a9d51e90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 2034 additions and 3438 deletions

View file

@ -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) $<

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

2
spec
View file

@ -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

File diff suppressed because it is too large Load diff

24
src/dmadata/dmadata.c Normal file
View 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
View file

@ -1,9 +1,10 @@
# Output files # Output files
*.exe *.exe
yaz0
makeromfs
elf2rom elf2rom
makeromfs
mkdmadata
mkldscript mkldscript
vtxdis vtxdis
yaz0
graphovl/ graphovl/

View file

@ -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
View 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;
}

View file

@ -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
View 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
View 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