479 lines
13 KiB
C
479 lines
13 KiB
C
#include "asmln.h"
|
|
#include "asmdata.h"
|
|
#include "asmpp.h"
|
|
#ifdef OLD_CODE
|
|
#include "asmgeneric_old.h"
|
|
#include "asmgen1.h"
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "asmln.c"
|
|
#include "asmdata.c"
|
|
#include "asmpp.c"
|
|
|
|
void usage(int argc, char** argv, int argi) {
|
|
fprintf(stderr, "TODO: USAGE\n");
|
|
}
|
|
|
|
bool assemble_line(void* assembler, const char* line) {
|
|
return true;
|
|
}
|
|
|
|
void dump_indent(FILE* output, const char* indentString, int indent) {
|
|
while (indent-- > 0) {
|
|
fprintf(output, "%s", indentString);
|
|
}
|
|
}
|
|
|
|
void dump_x(FILE* output, int32_t type, asmlnx_t* x, const char* indentString, int indent);
|
|
void dump_x(FILE* output, int32_t type, asmlnx_t* x, const char* indentString, int indent) {
|
|
if (type != ASMT_TOKENTYPE_OPENBR || x == NULL) {
|
|
return;
|
|
}
|
|
|
|
dump_indent(output, indentString, indent);
|
|
fprintf(output, "[LHS]\t[type %d]\t'%s'\n", x->lhstype, x->lhscopy);
|
|
dump_x(output, x->lhstype, x->lhsx, indentString, indent + 1);
|
|
|
|
dump_indent(output, indentString, indent);
|
|
fprintf(output, "[OP]\t'%s'\n", x->opcopy);
|
|
|
|
dump_indent(output, indentString, indent);
|
|
fprintf(output, "[RHS]\t[type %d]\t'%s'\n", x->rhstype, x->rhscopy);
|
|
dump_x(output, x->rhstype, x->rhsx, indentString, indent + 1);
|
|
}
|
|
|
|
void dump_asmln(FILE* output, asmln_t* asmln) {
|
|
if (asmln == NULL) {
|
|
fprintf(output, "ERROR: NULL asmln\n");
|
|
}
|
|
if (asmln->errorcopy != NULL) {
|
|
fprintf(output, "ERROR: %s\n", asmln->errorcopy);
|
|
}
|
|
fprintf(output, "%s:\t%s\t[%d params]\t; %s\n",
|
|
asmln->labelcopy == NULL ? "[no label]" : asmln->labelcopy,
|
|
asmln->instrcopy == NULL ? "[no instr]" : asmln->instrcopy,
|
|
asmln->nparams,
|
|
asmln->commentcopy == NULL ? "[no comment]" : asmln->commentcopy);
|
|
int32_t i;
|
|
for (i = 0; i < asmln->nparams; i++) {
|
|
fprintf(output, "\t[param %d]\t[type %d]\t'%s'\n", i, asmln->paramtype[i], asmln->paramcopy[i]);
|
|
dump_x(output, asmln->paramtype[i], asmln->paramx[i], "\t", 2);
|
|
}
|
|
}
|
|
|
|
int assemble_warn(asmpp_t* pp, asmln_t* asmln) {
|
|
dump_asmln(stderr, asmln);
|
|
return 0;
|
|
}
|
|
|
|
int assemble_include(asmpp_t* pp, asmln_t* asmln) {
|
|
char* filename = asmln->paramcopy[0];
|
|
FILE* input = fopen(filename, "r");
|
|
|
|
if (input == NULL) {
|
|
fprintf(stderr, "Bad filename?");
|
|
return -1;
|
|
}
|
|
|
|
int32_t lnum = 1;
|
|
int xnum = 0;
|
|
int32_t buffermax = 1024 * 1024;
|
|
char* buffer = calloc(buffermax, 1);
|
|
if (buffer == NULL) {
|
|
fprintf(stderr, "Out of memory?");
|
|
return -1;
|
|
}
|
|
|
|
const char* line;
|
|
while ((line = fgets(buffer, buffermax, input)) != NULL) {
|
|
/* First parse the line.*/
|
|
asmln_t* asmln = asmln_new(line);
|
|
/* Now check for parse errors. */
|
|
if (asmln == NULL) {
|
|
fprintf(stderr, "asmln_new failed entirely!");
|
|
return false;
|
|
}
|
|
if (asmln->errorcopy != NULL) {
|
|
dump_asmln(stdout, asmln);
|
|
return false;
|
|
}
|
|
int n = asmpp_expand(pp, asmln);
|
|
if (n < 0) {
|
|
return n;
|
|
}
|
|
xnum += n;
|
|
lnum++;
|
|
}
|
|
|
|
fclose(input);
|
|
|
|
return xnum;
|
|
}
|
|
|
|
int assemble_ppout(asmpp_t* pp, asmln_t* asmln) {
|
|
if (asmln == NULL) {
|
|
fprintf(stderr, "preprocessor failed entirely!");
|
|
return -1;
|
|
}
|
|
|
|
if (asmln->errorcopy != NULL && asmln->errorcopy[0] != 0) {
|
|
dump_asmln(stdout, asmln);
|
|
return -1;
|
|
}
|
|
/* If it's a plain data line, just assemble it: */
|
|
if (asmdata_isvalidasmln(pp->udata, asmln)) {
|
|
bool tmp = asmdata_asmln(pp->udata, asmln);
|
|
//printf("Done.\n");
|
|
if (!tmp) {
|
|
fprintf(stderr, "Failed to interpret plain data line:\n");
|
|
//fprintf(stderr, "Around line %d:\t", lnum);
|
|
dump_asmln(stderr, asmln);
|
|
return -1;
|
|
}
|
|
return 1;
|
|
} else {
|
|
fprintf(stderr, "This instruction can't be interpreted:\n");
|
|
//fprintf(stderr, "Around line %d:\t", lnum);
|
|
dump_asmln(stderr, asmln);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#define MODE_DATA 0
|
|
#define MODE_GEN1 1
|
|
#define MODE_GEN1X 2
|
|
#define MODE_GENERIC 3
|
|
#define MODE_PP 4
|
|
|
|
|
|
bool assemble_input(void* assembler, FILE* input, int32_t mode, const char* modestr) {
|
|
int32_t lnum = 1;
|
|
int32_t buffermax = 1024 * 1024;
|
|
char* buffer = calloc(buffermax, 1);
|
|
if (buffer == NULL) {
|
|
fprintf(stderr, "Out of memory?");
|
|
return false;
|
|
}
|
|
|
|
asmpp_t* pp = asmpp_new(&assemble_ppout, assembler);
|
|
asmpp_quickmacro(pp, asmln_new("include x"), &assemble_include);
|
|
asmpp_quickmacro(pp, asmln_new("warn x"), &assemble_warn);
|
|
|
|
if (modestr != NULL) {
|
|
char* modebuf = calloc(20+strlen(modestr),1);
|
|
strcat(modebuf, "include ");
|
|
strcat(modebuf, modestr);
|
|
strcat(modebuf, ".inc");
|
|
assemble_include(pp, asmln_new(modebuf));
|
|
}
|
|
|
|
const char* line;
|
|
while ((line = fgets(buffer, buffermax, input)) != NULL) {
|
|
/* First parse the line.*/
|
|
asmln_t* asmln = asmln_new(line);
|
|
/* Now check for parse errors. */
|
|
if (asmln == NULL) {
|
|
fprintf(stderr, "asmln_new failed entirely!");
|
|
return false;
|
|
}
|
|
if (asmln->errorcopy != NULL) {
|
|
dump_asmln(stdout, asmln);
|
|
return false;
|
|
}
|
|
if (mode == MODE_PP) {
|
|
int n = asmpp_expand(pp, asmln);
|
|
if (n < 0) {
|
|
fprintf(stderr, "Failed to interpret preprocessed data around line %d:\t", lnum);
|
|
//dump_asmln(stderr, asmln);
|
|
//asmln_delete(asmln);
|
|
free(buffer);
|
|
return false;
|
|
}
|
|
}
|
|
/* If it's a plain data line, just assemble it: */
|
|
else if (asmdata_isvalidasmln(assembler, asmln)) {
|
|
bool tmp = asmdata_asmln(assembler, asmln);
|
|
//printf("Done.\n");
|
|
if (!tmp) {
|
|
fprintf(stderr, "Failed to interpret plain data line:\n");
|
|
fprintf(stderr, "Around line %d:\t", lnum);
|
|
dump_asmln(stderr, asmln);
|
|
asmln_delete(asmln);
|
|
free(buffer);
|
|
return false;
|
|
}
|
|
}
|
|
#ifdef OLD_CODE
|
|
else if (mode == MODE_GEN1 || mode == MODE_GEN1X) {
|
|
bool tmp = asmgen1_asmln(assembler, asmln);
|
|
//printf("Done.\n");
|
|
if (!tmp) {
|
|
fprintf(stderr, "Failed to interpret generic instruction line:\n");
|
|
fprintf(stderr, "Around line %d:\t", lnum);
|
|
dump_asmln(stderr, asmln);
|
|
asmln_delete(asmln);
|
|
free(buffer);
|
|
return false;
|
|
}
|
|
}
|
|
else if (mode == MODE_GENERIC) {
|
|
bool tmp = asmgeneric_asmln(assembler, asmln);
|
|
//printf("Done.\n");
|
|
if (!tmp) {
|
|
fprintf(stderr, "Failed to interpret generic instruction line:\n");
|
|
fprintf(stderr, "Around line %d:\t", lnum);
|
|
dump_asmln(stderr, asmln);
|
|
asmln_delete(asmln);
|
|
free(buffer);
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
fprintf(stderr, "This instruction can't be interpreted:\n");
|
|
fprintf(stderr, "Around line %d:\t", lnum);
|
|
dump_asmln(stderr, asmln);
|
|
asmln_delete(asmln);
|
|
free(buffer);
|
|
return false;
|
|
}
|
|
|
|
asmln = asmln_delete(asmln);
|
|
memset(buffer, 0, buffermax);
|
|
lnum++;
|
|
}
|
|
|
|
asmpp_delete(pp);
|
|
free(buffer);
|
|
return true;
|
|
}
|
|
|
|
bool assemble(void* assembler, const char* filename, int32_t mode, const char* modestr) {
|
|
|
|
FILE* input = fopen(filename, "r");
|
|
|
|
if (input == NULL) {
|
|
fprintf(stderr, "Bad filename?");
|
|
return false;
|
|
}
|
|
|
|
if (!assemble_input(assembler, input, mode, modestr)) {
|
|
fclose(input);
|
|
return false;
|
|
}
|
|
|
|
fclose(input);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool produce_section(void* assembler, int sectionnum, FILE* output, bool readable) {
|
|
asmdata_t* asmdata = assembler;
|
|
asmdata_section_t* section = asmdata->sections[sectionnum];
|
|
if (readable) {
|
|
fprintf(output, "\t[base=0x%016llX reserved=0x%016llX filled=0x%08llX buffered=0x%08llX]\n", (long long)(section->virtualoffset), (long long)(section->reservedsize), (long long)(section->bufferfilled), (long long)(section->buffersize));
|
|
int32_t i = 0;
|
|
while (i < section->bufferfilled) {
|
|
if (i > 0 && i % 16 == 0) {
|
|
fprintf(output, "\n");
|
|
}
|
|
if (i % 16 == 0) {
|
|
fprintf(output, "\tS%04d+0x%016x:\t", sectionnum, i);
|
|
}
|
|
fprintf(output, "%02x ", section->buffer[i]);
|
|
i++;
|
|
}
|
|
fprintf(output, "\n\n");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool produce_output(void* assembler, FILE* output, bool readable, bool header, int32_t pagesize) {
|
|
asmdata_t* asmdata = assembler;
|
|
int32_t sectionnum;
|
|
int32_t offset = 0;
|
|
if (header && readable) {
|
|
fprintf(output, "SECTIONS (%d):\n", asmdata->nsections);
|
|
for (sectionnum = 0; sectionnum < asmdata->nsections; sectionnum++) {
|
|
fprintf(output, "\tSECTION %04d: '%s'\n", sectionnum, asmdata->sections[sectionnum]->namecopy);
|
|
}
|
|
for (sectionnum = 0; sectionnum < asmdata->nsections; sectionnum++) {
|
|
fprintf(output, "[SECTION %04d: '%s']\n", sectionnum, asmdata->sections[sectionnum]->namecopy);
|
|
if (!produce_section(assembler, sectionnum, output, readable)) {
|
|
return false;
|
|
}
|
|
}
|
|
fprintf(output, "\n");
|
|
}
|
|
else if (header) {
|
|
asmdata_section_t* hdrsec = asmdata_findsection(asmdata, "asmdata.fileheader", false);
|
|
if (hdrsec == NULL || hdrsec->buffer == NULL) {
|
|
return false;
|
|
}
|
|
if (fwrite(hdrsec->buffer, 1, hdrsec->bufferfilled, output) != hdrsec->bufferfilled) {
|
|
return false;
|
|
}
|
|
offset += hdrsec->bufferfilled;
|
|
while ((offset % pagesize) != 0) {
|
|
char c = 0;
|
|
if (fwrite(&c, 1, 1, output) != 1) {
|
|
return false;
|
|
}
|
|
offset++;
|
|
}
|
|
for (sectionnum = 0; sectionnum < asmdata->nsections; sectionnum++) {
|
|
asmdata_section_t* sec = asmdata->sections[sectionnum];
|
|
fprintf(stderr, "Writing section '%s' (#%d) at file offset %d\n", sec->namecopy, sectionnum, offset);
|
|
if (sec->bufferfilled > 0 && fwrite(sec->buffer, 1, sec->bufferfilled, output) != sec->bufferfilled) {
|
|
return false;
|
|
}
|
|
offset += sec->bufferfilled;
|
|
while ((offset % pagesize) != 0) {
|
|
char c = 0;
|
|
if (fwrite(&c, 1, 1, output) != 1) {
|
|
return false;
|
|
}
|
|
offset++;
|
|
}
|
|
}
|
|
// We're done now
|
|
return true;
|
|
}
|
|
if (readable) {
|
|
fprintf(output, "REFERENCES (%d):\n", asmdata->nreferences);
|
|
int32_t refnum;
|
|
for (refnum = 0; refnum < asmdata->nreferences; refnum++) {
|
|
fprintf(output, "\tREFERENCE %04d -> SYMBOL %04d\n", refnum, asmdata->references[refnum].symbolindex);
|
|
}
|
|
fprintf(output, "\n");
|
|
fprintf(output, "SYMBOLS (%d):\n", asmdata->nsymbols);
|
|
int32_t symnum;
|
|
for (symnum = 0; symnum < asmdata->nsymbols; symnum++) {
|
|
fprintf(output, "\tSYMBOL %04d -> '%s'\n", symnum, asmdata->symbols[symnum].namecopy);
|
|
}
|
|
fprintf(output, "\n");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
//argc = 4;
|
|
//argv = (char* []){ "test", "--stdout", "--readable", "--stdin"/*"--ast-only","--input","C:\\Users\\Zak\\source\\repos\\ZCC\\Debug\\test2.c"*/ };
|
|
int argi = 1;
|
|
FILE* output = NULL;
|
|
void* assembler = asmdata_new();
|
|
bool somethingtodo = false;
|
|
bool readable = false;
|
|
bool finalise = true;
|
|
bool produceheader = true;
|
|
|
|
int32_t pagesize = 1024;
|
|
const char* hint1 = "gen1";
|
|
const char* hint2 = NULL;
|
|
int32_t inthint = 0;
|
|
int32_t mode = MODE_PP;
|
|
const char* modestr = NULL;
|
|
|
|
while (argi < argc) {
|
|
if (!strcmp(argv[argi], "--usage")) {
|
|
usage(argc, argv, argi);
|
|
return 0;
|
|
}
|
|
else if (!strcmp(argv[argi], "--mode")) {
|
|
if (output != NULL || argi + 1 >= argc) {
|
|
usage(argc, argv, argi);
|
|
return -1;
|
|
}
|
|
argi++;
|
|
modestr = argv[argi];
|
|
}
|
|
else if (!strcmp(argv[argi], "--output") || (argv[argi][0] == '-' && argv[argi][1] == 'o' && argv[argi][2] == 0)) {
|
|
if (output != NULL || argi + 1 >= argc) {
|
|
usage(argc, argv, argi);
|
|
return -1;
|
|
}
|
|
argi++;
|
|
output = fopen(argv[argi], "w+");
|
|
if (output == NULL) {
|
|
fprintf(stderr, "ERROR: Failed to open output file '%s'\n", argv[argi]);
|
|
return -1;
|
|
}
|
|
}
|
|
else if (argv[argi][0] == '-' && argv[argi][1] == 'o') {
|
|
if (output != NULL) {
|
|
usage(argc, argv, argi);
|
|
return -1;
|
|
}
|
|
output = fopen(argv[argi]+2, "w+");
|
|
if (output == NULL) {
|
|
fprintf(stderr, "ERROR: Failed to open output file '%s'\n", argv[argi]+2);
|
|
return -1;
|
|
}
|
|
}
|
|
else if (!strcmp(argv[argi], "--stdin")) {
|
|
fprintf(stderr, "NOTE: Assembling from standard input\n");
|
|
if (!assemble_input(assembler, stdin, mode, modestr)) {
|
|
fprintf(stderr, "ERROR: Failed to assemble from standard input\n");
|
|
return -1;
|
|
}
|
|
somethingtodo = true;
|
|
}
|
|
else if (!strcmp(argv[argi], "--stdout")) {
|
|
fprintf(stderr, "NOTE: Dumping to standard output (use with --readable to easily inspect output)\n");
|
|
output = stdout;
|
|
}
|
|
else if (!strcmp(argv[argi], "--readable")) {
|
|
fprintf(stderr, "NOTE: Will attempt to produce 'readable' output\n");
|
|
readable = true;
|
|
}
|
|
else if (!strcmp(argv[argi], "--nofinalise")) {
|
|
fprintf(stderr, "NOTE: Output will not be finalised\n");
|
|
finalise = false;
|
|
}
|
|
else if (!strcmp(argv[argi], "--noheader")) {
|
|
fprintf(stderr, "NOTE: Output will not include file header\n");
|
|
produceheader = false;
|
|
}
|
|
else {
|
|
if (!assemble(assembler, argv[argi], mode, modestr)) {
|
|
fprintf(stderr, "ERROR: Failed to assemble '%s'\n", argv[argi]);
|
|
return -1;
|
|
}
|
|
somethingtodo = true;
|
|
}
|
|
argi++;
|
|
}
|
|
|
|
if (!somethingtodo || output == NULL) {
|
|
fprintf(stderr, "ERROR: Nothing to do? Please define an input and an output!\n");
|
|
usage(argc, argv, 0);
|
|
return -1;
|
|
}
|
|
|
|
if (finalise && !asmdata_finalise(assembler)) {
|
|
fprintf(stderr, "ERROR: Finalisation failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (produceheader && !asmdata_produceheader(assembler, pagesize, hint1, hint2, inthint)) {
|
|
fprintf(stderr, "ERROR: Failed to produce header\n");
|
|
}
|
|
|
|
if (!produce_output(assembler, output, readable, produceheader, pagesize)) {
|
|
fprintf(stderr, "ERROR: Failed to produce output\n");
|
|
return -1;
|
|
}
|
|
|
|
if (output != stdout) {
|
|
fclose(output);
|
|
}
|
|
output = NULL;
|
|
|
|
fprintf(stderr, "FINISHED.\n");
|
|
|
|
return 0;
|
|
}
|