CosmOS pre-alpha and launchable application versions of old programs.
This commit is contained in:
@@ -119,6 +119,45 @@ void writeDependencyFile(const char *dependencyPath, const char *outputPath) {
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
// A program that bases one segment and not the other is a mistake the assembler is the
|
||||
// last place to catch. Nothing relocates, so the unbased half keeps the addresses it was
|
||||
// given, which are addresses from zero up, and the loader puts it there: on top of
|
||||
// whatever the system keeps at the bottom of memory. It does not fail at load time and it
|
||||
// does not fail at the jump. It fails later, somewhere else, as corruption.
|
||||
//
|
||||
// Only a segment with something in it can land on anything, so an empty one says nothing.
|
||||
// A base of zero that was actually asked for is left alone, which is how a program says
|
||||
// it meant it.
|
||||
void checkSegmentBases(const char *fileName) {
|
||||
if (!programIsLoadable()) {
|
||||
return; // A boot image. Both segments begin at zero because that is where they go.
|
||||
}
|
||||
|
||||
const char *segmentName[3];
|
||||
segmentName[PROGRAM] = "Program";
|
||||
segmentName[DATA] = "Data";
|
||||
int segmentEnd[3];
|
||||
segmentEnd[PROGRAM] = programLength;
|
||||
segmentEnd[DATA] = dataLength;
|
||||
|
||||
const int segments[2] = { PROGRAM, DATA };
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int mine = segments[i], other = segments[1 - i];
|
||||
int content = segmentEnd[mine] - segmentBase(mine);
|
||||
if (segmentBaseWasGiven(mine) || content <= 0 || !segmentBaseWasGiven(other)) {
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, RED "Error: The %s Segment is based at 0x%04X, but the %s Segment\n"
|
||||
" has %d byte%s at 0x0000 and was never given a #Base.\n"
|
||||
" Half a program loaded at zero lands on whatever is already there.\n" RESET,
|
||||
segmentName[other], segmentBase(other), segmentName[mine],
|
||||
content, content == 1 ? "" : "s");
|
||||
printf(" File: %s\n", fileName);
|
||||
printf(" Say \"#Base 0x0000\" in the %s Segment if that is what you meant.\n", segmentName[mine]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
static struct option long_options[] = {
|
||||
{"output", required_argument, 0, 'o'},
|
||||
@@ -185,7 +224,15 @@ int main(int argc, char *argv[]) {
|
||||
// the buffers are filled, because SWI needs the number its vector was given.
|
||||
populateVectorTable(intermediateArray, index);
|
||||
fillInVectorReferences(intermediateArray, index);
|
||||
// The buffers are filled from wherever each segment is based, so that a byte's place
|
||||
// in the buffer is the address it will have. For a boot image both bases are zero and
|
||||
// this changes nothing.
|
||||
programLength = segmentBase(PROGRAM);
|
||||
dataLength = segmentBase(DATA);
|
||||
populateOutputBuffers(intermediateArray, index, Program, &programLength, Data, &dataLength);
|
||||
// After the buffers, because how much a segment actually holds is not known until it
|
||||
// has been filled, and an empty segment is not a mistake.
|
||||
checkSegmentBases(fileName);
|
||||
if (!outputFileName) {
|
||||
outputFileName = createOutputFileName(fileName);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,30 @@
|
||||
|
||||
int debug = 0;
|
||||
|
||||
static uint16_t segmentBases[3];
|
||||
static int basesGiven = 0;
|
||||
// Per segment, because a base of zero that was asked for and a base of zero that was
|
||||
// never mentioned are different things, and only the second one is likely a mistake.
|
||||
static int baseGiven[3];
|
||||
|
||||
void setSegmentBase(int segment, uint16_t base) {
|
||||
segmentBases[segment] = base;
|
||||
baseGiven[segment] = 1;
|
||||
basesGiven = 1;
|
||||
}
|
||||
|
||||
uint16_t segmentBase(int segment) {
|
||||
return segmentBases[segment];
|
||||
}
|
||||
|
||||
int segmentBaseWasGiven(int segment) {
|
||||
return baseGiven[segment];
|
||||
}
|
||||
|
||||
int programIsLoadable(void) {
|
||||
return basesGiven;
|
||||
}
|
||||
|
||||
void toUppercase(char *str) {
|
||||
for (int i = 0; str[i]; i++) {
|
||||
str[i] = toupper(str[i]);
|
||||
@@ -37,6 +61,10 @@ int checkIfKeyword(intermediateElement *currentElement) {
|
||||
// the number that follows. The file that needs the boundary is then the
|
||||
// file that asks for it, rather than relying on whatever came before.
|
||||
return KEYWORD_ALIGN;
|
||||
} else if (strcmp(currentElement->token, "#Base") == 0) {
|
||||
// Says where this segment is loaded, which makes the program a loadable one
|
||||
// rather than a boot image.
|
||||
return KEYWORD_BASE;
|
||||
} else if (strcmp(currentElement->token, "#Reserve") == 0) {
|
||||
// Puts down the number of zero bytes that follows, so that a label can
|
||||
// stand for a region rather than just its first byte.
|
||||
@@ -172,7 +200,9 @@ int checkIfLiteralValue(intermediateElement *currentElement) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t readCount(intermediateElement *currentElement, const char *what) {
|
||||
// Shared by readCount and readAddress, which differ only in whether zero is an answer.
|
||||
// A count of nothing is a typo; an address of zero is the bottom of memory.
|
||||
static uint16_t readNumber(intermediateElement *currentElement, const char *what, long least) {
|
||||
const char *token = currentElement->token;
|
||||
int base;
|
||||
const char *baseName;
|
||||
@@ -202,14 +232,22 @@ uint16_t readCount(intermediateElement *currentElement, const char *what) {
|
||||
}
|
||||
}
|
||||
long value = strtol(digits, NULL, base);
|
||||
if (value < 1 || value > 0xFFFF) {
|
||||
fprintf(stderr, RED "Error: %s was given \"%s\". It has to be at least 1 and no more than 0xFFFF.\n" RESET, what, token);
|
||||
if (value < least || value > 0xFFFF) {
|
||||
fprintf(stderr, RED "Error: %s was given \"%s\". It has to be at least %ld and no more than 0xFFFF.\n" RESET, what, token, least);
|
||||
printf(" File: %s at line %d.\n", currentElement->fileName, currentElement->lineNumber);
|
||||
exit(1);
|
||||
}
|
||||
return (uint16_t)value;
|
||||
}
|
||||
|
||||
uint16_t readCount(intermediateElement *currentElement, const char *what) {
|
||||
return readNumber(currentElement, what, 1);
|
||||
}
|
||||
|
||||
uint16_t readAddress(intermediateElement *currentElement, const char *what) {
|
||||
return readNumber(currentElement, what, 0);
|
||||
}
|
||||
|
||||
int checkIfLabel(intermediateElement *currentElement) {
|
||||
char *token = currentElement->token;
|
||||
int length = strlen(token);
|
||||
@@ -261,7 +299,15 @@ int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber)
|
||||
if (i < (int)(sizeof(buffer) - 1)) {
|
||||
buffer[i++] = c;
|
||||
} else {
|
||||
fprintf(stderr, "Error: String literal too long.\n");
|
||||
// Say the limit and where it was met. A string long enough to reach this
|
||||
// is usually several lines of help text, and "too long" on its own leaves
|
||||
// somebody counting characters to find out by how much.
|
||||
fprintf(stderr, RED "Error: String literal longer than %d characters.\n"
|
||||
" Every string carries its own zero byte, so two written in a row"
|
||||
" are two strings\n rather than one long one. Give each its own"
|
||||
" label and print them one after another.\n" RESET,
|
||||
(int)(sizeof(buffer) - 1));
|
||||
printf(" File: %s at line %d.\n", currentElement->fileName, *lineNumber);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#define KEYWORD_VECTORS 4
|
||||
#define KEYWORD_ALIGN 5
|
||||
#define KEYWORD_RESERVE 6
|
||||
#define KEYWORD_BASE 7
|
||||
|
||||
// Destination values.
|
||||
#define NOWHERE 0
|
||||
@@ -86,4 +87,31 @@ int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber);
|
||||
// ever emitted as a byte, so there is no reason to hold them to a byte's range.
|
||||
uint16_t readCount(intermediateElement *currentElement, const char *what);
|
||||
|
||||
// The same, but zero is allowed. #Base takes one of these: a segment deliberately based
|
||||
// at the bottom of memory is a thing a program is entitled to say, and saying it out loud
|
||||
// is how it is told apart from a segment nobody based at all.
|
||||
uint16_t readAddress(intermediateElement *currentElement, const char *what);
|
||||
|
||||
// ---- Where a segment is based ----
|
||||
//
|
||||
// A program that says nothing about this is a boot image: both its segments begin at
|
||||
// zero, and the machine loads them there. A program that gives either segment a base is
|
||||
// meant to be loaded somewhere else, so it is written out as a loadable program instead,
|
||||
// with its addresses in front of it and none of the space below them in the file.
|
||||
//
|
||||
// Nothing relocates anything, so the base a program is assembled for has to be the one it
|
||||
// is loaded at.
|
||||
|
||||
void setSegmentBase(int segment, uint16_t base);
|
||||
|
||||
uint16_t segmentBase(int segment);
|
||||
|
||||
// Whether this particular segment was given one. A segment left at zero because nobody
|
||||
// said otherwise cannot be told from one deliberately based at zero by its value alone,
|
||||
// and the difference is what the mismatch check below is about.
|
||||
int segmentBaseWasGiven(int segment);
|
||||
|
||||
// Whether either segment was given one, which is what decides the kind of file written.
|
||||
int programIsLoadable(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -76,6 +76,7 @@ Instruction instruction_set[] = {
|
||||
{0x4A, "LDD"},
|
||||
{0x4B, "STD"},
|
||||
{0x4C, "MVSD"},
|
||||
{0x4D, "MVDS"},
|
||||
// Output Operations:
|
||||
{0xD0, "OUTQ"},
|
||||
{0xD1, "OUTA"},
|
||||
@@ -121,6 +122,7 @@ int dataPointerOperands(uint8_t opcode) {
|
||||
case 0x48: // DPUP
|
||||
case 0x49: // DPDN
|
||||
case 0x4C: // MVSD
|
||||
case 0x4D: // MVDS
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
|
||||
@@ -155,6 +155,9 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
|
||||
// fileName is a path already resolved and recorded by the caller, and the copy
|
||||
// the include list owns, so element fileNames can safely point at it.
|
||||
int status = NOWHERE;
|
||||
// A base has to come before anything else in its segment, so this remembers whether
|
||||
// that segment has had anything put in it yet.
|
||||
static int segmentUsed[3] = {0, 0, 0};
|
||||
int lineNumber = 1; // Line numbers start at 1.
|
||||
// Open the file.
|
||||
FILE *file = fopen(fileName, "r");
|
||||
@@ -229,6 +232,34 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
|
||||
// Set the state to DATA so we mark additional tokens for inclusion into Data Memory.
|
||||
status = DATA;
|
||||
break;
|
||||
case KEYWORD_BASE: {
|
||||
if (status != PROGRAM && status != DATA) {
|
||||
fprintf(stderr, RED "Error: #Base outside the Program or Data Segment.\n There is no segment for it to be the base of.\n" RESET);
|
||||
printf(" File: %s at line %d.\n", fileName, lineNumber);
|
||||
exit(1);
|
||||
}
|
||||
if (segmentUsed[status]) {
|
||||
fprintf(stderr, RED "Error: #Base after something is already in the segment.\n"
|
||||
" A base says where the whole segment begins, so it has to come first.\n" RESET);
|
||||
printf(" File: %s at line %d.\n", fileName, lineNumber);
|
||||
exit(1);
|
||||
}
|
||||
(*intermediateIndex)++;
|
||||
if (!readToken(&(*intermediateArray)[*intermediateIndex], file, &lineNumber)) {
|
||||
fprintf(stderr, RED "Error: #Base without an address.\n" RESET);
|
||||
exit(1);
|
||||
}
|
||||
(*intermediateArray)[*intermediateIndex].fileName = fileName;
|
||||
(*intermediateArray)[*intermediateIndex].lineNumber = lineNumber;
|
||||
setSegmentBase(status, readAddress(&(*intermediateArray)[*intermediateIndex], "#Base"));
|
||||
(*intermediateArray)[*intermediateIndex].type = KEYWORD;
|
||||
(*intermediateArray)[*intermediateIndex].byteLength = 0;
|
||||
(*intermediateArray)[*intermediateIndex].destination = NOWHERE;
|
||||
(*intermediateArray)[*intermediateIndex - 1].destination = NOWHERE;
|
||||
(*intermediateArray)[*intermediateIndex - 1].byteLength = 0;
|
||||
(*intermediateIndex)++;
|
||||
continue;
|
||||
}
|
||||
case KEYWORD_ALIGN:
|
||||
case KEYWORD_RESERVE: {
|
||||
// Both take a count, and both only make sense somewhere that has a
|
||||
@@ -288,8 +319,11 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
|
||||
printf(" File: %s at line %d.\n", fileName, lineNumber);
|
||||
exit(1);
|
||||
}
|
||||
// Next, check if it's a literal value.
|
||||
} else if (checkIfLiteralValue(&(*intermediateArray)[*intermediateIndex])) {
|
||||
// Next, check if it's a literal value. A string is never one, however it begins:
|
||||
// the quotes are gone by now, so a string starting with a zero looks exactly like
|
||||
// a malformed literal and used to be rejected as one.
|
||||
} else if ((*intermediateArray)[*intermediateIndex].type != STRING
|
||||
&& checkIfLiteralValue(&(*intermediateArray)[*intermediateIndex])) {
|
||||
// We should check to make sure we have a destination for it.
|
||||
if (status == NOWHERE) {
|
||||
fprintf(stderr, RED "Error: Attempting to write a value to nowhere!\n Did you forget to use the #Program or #Data keyword?\n" RESET);
|
||||
@@ -320,6 +354,10 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
|
||||
}
|
||||
}
|
||||
(*intermediateArray)[*intermediateIndex].destination = status;
|
||||
if ((status == PROGRAM || status == DATA)
|
||||
&& (*intermediateArray)[*intermediateIndex].type != KEYWORD) {
|
||||
segmentUsed[status] = 1;
|
||||
}
|
||||
(*intermediateIndex)++;
|
||||
}
|
||||
return 0;
|
||||
|
||||
+129
-12
@@ -14,6 +14,7 @@
|
||||
#include "secondPass.h"
|
||||
#include "Assm-util.h"
|
||||
#include "assembly.h"
|
||||
#include "sbex.h"
|
||||
|
||||
int debugSecondPass = 0;
|
||||
|
||||
@@ -62,8 +63,11 @@ void addLabel(char *labelName, uint16_t address, int type, const char *fileName,
|
||||
}
|
||||
|
||||
void populateLabelTable(intermediateElement *intermediateArray, int arraySize) {
|
||||
int programCount = 0;
|
||||
int dataCount = 0;
|
||||
// Counting starts at the base, so a label in a program built to live somewhere else
|
||||
// already holds the address it will have once it is there. Nothing relocates
|
||||
// anything, which is exactly why this has to be right at assembly time.
|
||||
int programCount = segmentBase(PROGRAM);
|
||||
int dataCount = segmentBase(DATA);
|
||||
// Loop through the array, if there's a label definition, add it to the label list.
|
||||
for (int i = 0; i < arraySize ; i++) {
|
||||
// How many zeroes an #Align comes to depends on where the cursor has reached,
|
||||
@@ -159,6 +163,16 @@ static void vectorError(const char *message, intermediateElement *element) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Whether the token at b is written on the same line as the one at a, and so belongs to
|
||||
// the same entry. A line is what tells a name with a handler apart from a name on its own.
|
||||
static int sameLine(intermediateElement *intermediateArray, int a, int b) {
|
||||
if (a < 0 || b < 0) {
|
||||
return 0;
|
||||
}
|
||||
return intermediateArray[a].lineNumber == intermediateArray[b].lineNumber
|
||||
&& intermediateArray[a].fileName == intermediateArray[b].fileName;
|
||||
}
|
||||
|
||||
// The next token belonging to the Vector Segment, or -1 if the segment has run out.
|
||||
static int nextVectorToken(intermediateElement *intermediateArray, int arraySize, int from) {
|
||||
for (int i = from; i < arraySize; i++) {
|
||||
@@ -169,12 +183,28 @@ static int nextVectorToken(intermediateElement *intermediateArray, int arraySize
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void addVector(char *name, uint8_t index, uint16_t base, uint16_t handler, intermediateElement *element) {
|
||||
// A declared vector has a name and a number but no handler, so nothing goes into the
|
||||
// table for it. It exists so that a program can name a service it calls without claiming
|
||||
// to implement it, which is what lets one file be included by both sides.
|
||||
// Where a vector of this name already is, or -1. A name can be met twice: once where it
|
||||
// is declared and once where somebody supplies its handler.
|
||||
static int findVector(const char *name) {
|
||||
for (int i = 0; i < vectorArrayCount; i++) {
|
||||
if (vectorArray[i].name && strcmp(vectorArray[i].name, name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void addVector(char *name, uint8_t index, uint16_t base, uint16_t handler,
|
||||
int declaredOnly, intermediateElement *element) {
|
||||
if (vectorArrayCount >= MAX_VECTORS) {
|
||||
vectorError("Too many vectors defined.", element);
|
||||
}
|
||||
for (int i = 0; i < vectorArrayCount; i++) {
|
||||
if (vectorArray[i].index == index && vectorArray[i].base == base) {
|
||||
if (vectorArray[i].index == index && vectorArray[i].base == base
|
||||
&& !vectorArray[i].declaredOnly && !declaredOnly) {
|
||||
fprintf(stderr, RED "Error: That vector already has a handler.\n" RESET);
|
||||
printf("File: %s at line %d.\n", element->fileName, element->lineNumber);
|
||||
exit(1);
|
||||
@@ -189,6 +219,7 @@ static void addVector(char *name, uint8_t index, uint16_t base, uint16_t handler
|
||||
vectorArray[vectorArrayCount].index = index;
|
||||
vectorArray[vectorArrayCount].base = base;
|
||||
vectorArray[vectorArrayCount].handler = handler;
|
||||
vectorArray[vectorArrayCount].declaredOnly = declaredOnly;
|
||||
vectorArrayCount++;
|
||||
}
|
||||
|
||||
@@ -228,7 +259,26 @@ void populateVectorTable(intermediateElement *intermediateArray, int arraySize)
|
||||
int handlerToken = nextVectorToken(intermediateArray, arraySize, portToken + 1);
|
||||
uint16_t handler = resolveHandler(intermediateArray, handlerToken, "Device");
|
||||
addVector(NULL, intermediateArray[portToken].byteValue, HARDWARE_VECTOR_BASE,
|
||||
handler, &intermediateArray[i]);
|
||||
handler, 0, &intermediateArray[i]);
|
||||
i = nextVectorToken(intermediateArray, arraySize, handlerToken + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int handlerToken = nextVectorToken(intermediateArray, arraySize, i + 1);
|
||||
int hasHandler = sameLine(intermediateArray, i, handlerToken);
|
||||
int already = findVector(token);
|
||||
|
||||
if (already >= 0) {
|
||||
// Met before. A handler now is somebody implementing what was declared
|
||||
// earlier, which is how one shared file can serve both sides.
|
||||
if (!hasHandler) {
|
||||
vectorError("That vector is declared more than once.", &intermediateArray[i]);
|
||||
}
|
||||
if (!vectorArray[already].declaredOnly) {
|
||||
vectorError("That vector already has a handler.", &intermediateArray[i]);
|
||||
}
|
||||
vectorArray[already].handler = resolveHandler(intermediateArray, handlerToken, token);
|
||||
vectorArray[already].declaredOnly = 0;
|
||||
i = nextVectorToken(intermediateArray, arraySize, handlerToken + 1);
|
||||
continue;
|
||||
}
|
||||
@@ -250,9 +300,15 @@ void populateVectorTable(intermediateElement *intermediateArray, int arraySize)
|
||||
nextFreeVector++;
|
||||
}
|
||||
|
||||
int handlerToken = nextVectorToken(intermediateArray, arraySize, i + 1);
|
||||
if (!hasHandler) {
|
||||
// Nothing follows it on the line, so this says what the vector is called and
|
||||
// what number it has, and leaves implementing it to somebody else.
|
||||
addVector(token, index, SOFTWARE_VECTOR_BASE, 0, 1, &intermediateArray[i]);
|
||||
i = handlerToken;
|
||||
continue;
|
||||
}
|
||||
uint16_t handler = resolveHandler(intermediateArray, handlerToken, token);
|
||||
addVector(token, index, SOFTWARE_VECTOR_BASE, handler, &intermediateArray[i]);
|
||||
addVector(token, index, SOFTWARE_VECTOR_BASE, handler, 0, &intermediateArray[i]);
|
||||
i = nextVectorToken(intermediateArray, arraySize, handlerToken + 1);
|
||||
}
|
||||
}
|
||||
@@ -440,6 +496,48 @@ void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize
|
||||
}
|
||||
}
|
||||
|
||||
// A loadable program: sixteen bytes saying where it belongs, then the code and the data.
|
||||
// The space below each base is not written out, because nothing needs to carry it: the
|
||||
// header says where the bytes go and the loader puts them there.
|
||||
static void writeLoadable(const char *outputFileName, uint8_t *Program, int programCount,
|
||||
uint8_t *Data, int dataCount) {
|
||||
uint16_t codeBase = segmentBase(PROGRAM);
|
||||
uint16_t dataBase = segmentBase(DATA);
|
||||
int codeLength = programCount - codeBase;
|
||||
int dataLength = dataCount - dataBase;
|
||||
if (codeLength < 0) codeLength = 0;
|
||||
if (dataLength < 0) dataLength = 0;
|
||||
|
||||
FILE *outputFile = fopen(outputFileName, "wb");
|
||||
if (!outputFile) {
|
||||
fprintf(stderr, RED "Error: Could not open file \"%s\" for writing.\n" RESET, outputFileName);
|
||||
exit(1);
|
||||
}
|
||||
uint8_t header[SBEX_HEADER_BYTES];
|
||||
memset(header, 0, sizeof(header));
|
||||
memcpy(header, SBEX_MAGIC, SBEX_MAGIC_BYTES);
|
||||
header[SBEX_VERSION_AT] = SBEX_VERSION;
|
||||
header[SBEX_CODE_AT] = (uint8_t)(codeBase >> 8);
|
||||
header[SBEX_CODE_AT + 1] = (uint8_t)(codeBase & 0xFF);
|
||||
// Where it starts is where it begins. A program that wants otherwise puts a branch
|
||||
// at its first instruction, which costs three bytes and needs no format for it.
|
||||
header[SBEX_ENTRY_AT] = (uint8_t)(codeBase >> 8);
|
||||
header[SBEX_ENTRY_AT + 1] = (uint8_t)(codeBase & 0xFF);
|
||||
header[SBEX_CODE_LEN_AT] = (uint8_t)(codeLength >> 8);
|
||||
header[SBEX_CODE_LEN_AT + 1] = (uint8_t)(codeLength & 0xFF);
|
||||
header[SBEX_DATA_AT] = (uint8_t)(dataBase >> 8);
|
||||
header[SBEX_DATA_AT + 1] = (uint8_t)(dataBase & 0xFF);
|
||||
header[SBEX_DATA_LEN_AT] = (uint8_t)(dataLength >> 8);
|
||||
header[SBEX_DATA_LEN_AT + 1] = (uint8_t)(dataLength & 0xFF);
|
||||
fwrite(header, 1, sizeof(header), outputFile);
|
||||
fwrite(Program + codeBase, 1, (size_t)codeLength, outputFile);
|
||||
fwrite(Data + dataBase, 1, (size_t)dataLength, outputFile);
|
||||
fclose(outputFile);
|
||||
printf("Successfully wrote SplitBit loadable program to \"%s\".\n", outputFileName);
|
||||
printf(GREEN " Code: %d bytes at 0x%04X.\n Data: %d bytes at 0x%04X.\n Total size: %d bytes.\n" RESET,
|
||||
codeLength, codeBase, dataLength, dataBase, SBEX_HEADER_BYTES + codeLength + dataLength);
|
||||
}
|
||||
|
||||
void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCount, uint8_t *Data, int dataCount) {
|
||||
FILE *outputFile = fopen(outputFileName, "wb");
|
||||
if (!outputFile) {
|
||||
@@ -487,16 +585,35 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// A program with a base is one meant to be loaded, so it is written out with its
|
||||
// addresses in front of it and nothing below them. A boot image carries the padding
|
||||
// because the machine loads it at zero; a loadable one would only be carrying space
|
||||
// it does not use.
|
||||
if (programIsLoadable()) {
|
||||
fclose(outputFile);
|
||||
writeLoadable(outputFileName, Program, programCount, Data, dataCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// The Vector Segment, only if the program named any. Leaving it out entirely is
|
||||
// what lets a binary written before vectors existed still load: the reader treats
|
||||
// the end of the file as an empty table rather than a missing one.
|
||||
int installed = 0;
|
||||
for (int i = 0; i < vectorArrayCount; i++) {
|
||||
if (!vectorArray[i].declaredOnly) {
|
||||
installed++;
|
||||
}
|
||||
}
|
||||
int vectorBytes = 0;
|
||||
if (vectorArrayCount > 0) {
|
||||
if (installed > 0) {
|
||||
fwrite("VEC", sizeof(char), SEGMENT_MARKER_LENGTH, outputFile);
|
||||
vectorBytes = vectorArrayCount * VECTOR_ENTRY_FILE_BYTES;
|
||||
vectorBytes = installed * VECTOR_ENTRY_FILE_BYTES;
|
||||
fputc((vectorBytes >> 8) & 0xFF, outputFile);
|
||||
fputc(vectorBytes & 0xFF, outputFile);
|
||||
for (int i = 0; i < vectorArrayCount; i++) {
|
||||
if (vectorArray[i].declaredOnly) {
|
||||
continue;
|
||||
}
|
||||
uint16_t slot = vectorArray[i].base + (uint16_t)vectorArray[i].index * VECTOR_ENTRY_BYTES;
|
||||
fputc((slot >> 8) & 0xFF, outputFile);
|
||||
fputc(slot & 0xFF, outputFile);
|
||||
@@ -508,10 +625,10 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
|
||||
fclose(outputFile);
|
||||
printf("Successfully wrote SplitBit binary to \"%s\".\n", outputFileName);
|
||||
printf(GREEN " Program Segment size: %d bytes.\n Data Segment size: %d bytes.\n" RESET, programCount, dataCount);
|
||||
if (vectorArrayCount > 0) {
|
||||
printf(GREEN " Vectors: %d.\n" RESET, vectorArrayCount);
|
||||
if (installed > 0) {
|
||||
printf(GREEN " Vectors: %d.\n" RESET, installed);
|
||||
}
|
||||
printf(GREEN " Total size: %d bytes.\n" RESET,
|
||||
(programCount + dataCount + SPLITBIT_HEADER_BYTES
|
||||
+ (vectorArrayCount > 0 ? SEGMENT_MARKER_LENGTH + SEGMENT_LENGTH_BYTES + vectorBytes : 0)));
|
||||
+ (installed > 0 ? SEGMENT_MARKER_LENGTH + SEGMENT_LENGTH_BYTES + vectorBytes : 0)));
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef struct {
|
||||
uint8_t index; // Which vector in its table.
|
||||
uint16_t base; // Which table: software or hardware.
|
||||
uint16_t handler; // Where the handler ended up.
|
||||
int declaredOnly; // Named and numbered, with nobody implementing it here.
|
||||
} VectorEntry;
|
||||
|
||||
void freeLabelList();
|
||||
|
||||
Reference in New Issue
Block a user