Interrupt system implemented, some new programs.

This commit is contained in:
Anachronaut
2026-08-15 00:44:13 -04:00
parent 638b68b25c
commit 6d1966d500
79 changed files with 2778 additions and 88 deletions
+11 -2
View File
@@ -80,6 +80,9 @@ void assemblerCleanup(intermediateElement *intermediateArray, int arraySize, cha
// Free the list of labels.
freeLabelList();
// Free the list of vectors.
freeVectorList();
// Free the output file name
free(outputFileName);
}
@@ -161,9 +164,11 @@ int main(int argc, char *argv[]) {
return 1;
}
// Allocate initial space for the intermediate array.
// Allocate initial space for the intermediate array. calloc rather than malloc,
// because not every element sets every one of its own fields, and a stray
// byteLength would quietly shift every address that follows it.
size_t arraySize = 1024;
intermediateElement *intermediateArray = malloc(arraySize * sizeof(intermediateElement));
intermediateElement *intermediateArray = calloc(arraySize, sizeof(intermediateElement));
if (!intermediateArray) {
fprintf(stderr, RED "Error: Memory allocation failed.\n" RESET);
exit(1);
@@ -176,6 +181,10 @@ int main(int argc, char *argv[]) {
loadFile(&intermediateArray, source, &index, &arraySize);
populateLabelTable(intermediateArray, index);
fillInLabelAddresses(intermediateArray, index);
// Vectors come after the labels, because a handler is named by its label, and before
// the buffers are filled, because SWI needs the number its vector was given.
populateVectorTable(intermediateArray, index);
fillInVectorReferences(intermediateArray, index);
populateOutputBuffers(intermediateArray, index, Program, &programLength, Data, &dataLength);
if (!outputFileName) {
outputFileName = createOutputFileName(fileName);
+53 -2
View File
@@ -32,6 +32,19 @@ int checkIfKeyword(intermediateElement *currentElement) {
} else if (strcmp(currentElement->token, "#Data") == 0) {
// Same as for #Program, but mark for inclusion in the Data Segment.
return KEYWORD_DATA;
} else if (strcmp(currentElement->token, "#Align") == 0) {
// Puts down as many zero bytes as it takes to reach the next multiple of
// 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, "#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.
return KEYWORD_RESERVE;
} else if (strcmp(currentElement->token, "#Vectors") == 0) {
// Names which handler belongs to which vector. Nothing here is assembled
// into either segment; it is worked out and written into the vector table.
return KEYWORD_VECTORS;
} else {
// It's a malformed keyword.
fprintf(stderr, RED "Error: Invalid Keyword \"%s\" in file \"%s\" at line number %d.\n" RESET, currentElement->token, currentElement->fileName, currentElement->lineNumber);
@@ -159,6 +172,44 @@ int checkIfLiteralValue(intermediateElement *currentElement) {
return 1;
}
uint16_t readCount(intermediateElement *currentElement, const char *what) {
const char *token = currentElement->token;
int base;
const char *baseName;
if (token[0] != '0' || (token[1] != 'x' && token[1] != 'd')) {
fprintf(stderr, RED "Error: %s needs a number, prefaced with 0x or 0d. Found \"%s\".\n" RESET, what, token);
printf(" File: %s at line %d.\n", currentElement->fileName, currentElement->lineNumber);
exit(1);
}
if (token[1] == 'x') {
base = 16;
baseName = "hexadecimal";
} else {
base = 10;
baseName = "decimal";
}
const char *digits = token + 2;
if (*digits == '\0') {
fprintf(stderr, RED "Error: %s was given \"%s\", which has no digits after its prefix.\n" RESET, what, token);
printf(" File: %s at line %d.\n", currentElement->fileName, currentElement->lineNumber);
exit(1);
}
for (const char *c = digits; *c; c++) {
if (!(base == 16 ? isxdigit((unsigned char)*c) : isdigit((unsigned char)*c))) {
fprintf(stderr, RED "Error: \"%c\" is not a %s digit, in \"%s\".\n" RESET, *c, baseName, token);
printf(" File: %s at line %d.\n", currentElement->fileName, currentElement->lineNumber);
exit(1);
}
}
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);
printf(" File: %s at line %d.\n", currentElement->fileName, currentElement->lineNumber);
exit(1);
}
return (uint16_t)value;
}
int checkIfLabel(intermediateElement *currentElement) {
char *token = currentElement->token;
int length = strlen(token);
@@ -207,7 +258,7 @@ int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber)
// Step 3: Handle string literals
if (c == '"') {
while ((c = fgetc(file)) != EOF && c != '"') {
if (i < sizeof(buffer) - 1) {
if (i < (int)(sizeof(buffer) - 1)) {
buffer[i++] = c;
} else {
fprintf(stderr, "Error: String literal too long.\n");
@@ -228,7 +279,7 @@ int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber)
// Step 4: Handle non-string tokens
ungetc(c, file); // Put the first character back
while ((c = fgetc(file)) != EOF && !isspace(c) && c != ';') {
if (i < sizeof(buffer) - 1) {
if (i < (int)(sizeof(buffer) - 1)) {
buffer[i++] = c;
} else {
fprintf(stderr, "Error: Token too long.\n");
+20
View File
@@ -23,16 +23,31 @@
#define LABEL_DEFINITION 4
#define VALUE 5
#define STRING 6
// A name from the Vector Segment, used as the operand of SWI. It stands for a vector
// number rather than an address, so it emits one byte where a label emits two.
#define VECTOR_REFERENCE 7
// Zero bytes put down to move the cursor along, from #Reserve. How many is known as
// soon as it is read.
#define PADDING 8
// The same, from #Align, where how many depends on where the cursor has got to. The
// count is worked out in the second pass and the alignment itself is kept in address.
#define ALIGNMENT 9
// Keyword values.
#define KEYWORD_INCLUDE 1
#define KEYWORD_PROGRAM 2
#define KEYWORD_DATA 3
#define KEYWORD_VECTORS 4
#define KEYWORD_ALIGN 5
#define KEYWORD_RESERVE 6
// Destination values.
#define NOWHERE 0
#define PROGRAM 1
#define DATA 2
// The Vector Segment does not become bytes at an address the way the other two do. It
// says which handler belongs to which vector, and the assembler works out the rest.
#define VECTORS 3
// For colorful text.
#define RESET "\x1B[0m"
@@ -66,4 +81,9 @@ int checkIfLabel(intermediateElement *currentElement);
int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber);
// Reads a count written the way a literal is, but allowing the full range of an address
// rather than a single byte. #Align and #Reserve both take one, and neither number is
// 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);
#endif
+10
View File
@@ -28,7 +28,10 @@ Instruction instruction_set[] = {
{0x12, "BRA"},
{0x13, "BRB"},
{0x14, "BRC"},
{0x15, "BRD"},
{0x17, "CALL"},
{0x18, "SWI"},
{0x19, "RETI"},
{0x1F, "RET"},
// Register Operations:
{0x20, "RSTA"},
@@ -40,6 +43,10 @@ Instruction instruction_set[] = {
{0x26, "INIA"},
{0x27, "INIB"},
{0x28, "CCF"},
{0x29, "MVQA"},
{0x2A, "MVQB"},
{0x2B, "SIF"},
{0x2C, "CIF"},
// Stack Operations:
{0x30, "PSHQ"},
{0x31, "PSHA"},
@@ -61,6 +68,7 @@ Instruction instruction_set[] = {
{0x49, "DPDN"},
{0x4A, "LDD"},
{0x4B, "STD"},
{0x4C, "MVSD"},
// Output Operations:
{0xD0, "OUTQ"},
{0xD1, "OUTA"},
@@ -92,6 +100,7 @@ int dataPointerOperands(uint8_t opcode) {
case 0x4A: // LDD
case 0x4B: // STD
return 2;
case 0x15: // BRD
case 0x33: // PSHD
case 0x36: // POPD
case 0x40: // INCD
@@ -104,6 +113,7 @@ int dataPointerOperands(uint8_t opcode) {
case 0x47: // SETD
case 0x48: // DPUP
case 0x49: // DPDN
case 0x4C: // MVSD
return 1;
default:
return 0;
+44
View File
@@ -23,18 +23,62 @@
// .. 3 "DAT"
// .. 2 Data Segment length
// .. M Data Segment
// .. 3 "VEC", optional
// .. 2 Vector Segment length, in bytes
// .. K Vector Segment, four bytes per entry
//
// The Vector Segment is optional and comes last, so a binary written before it existed
// simply ends after its Data Segment and still loads. Each entry is two bytes saying
// where in Program Memory the vector sits, then two bytes saying where its handler is,
// most significant byte first. It is a list rather than an image of the table, so a
// program with three handlers costs twelve bytes instead of a padded kilobyte.
//
// The feature flags are how a binary says it needs something the base machine does
// not provide, so that an emulator which cannot provide it refuses to run the binary
// rather than quietly doing the wrong thing. No features are defined yet; the field
// is here so that adding one later does not need another format version.
// ---- The vector table ----
//
// The top kilobyte of Program Memory is reserved for vectors. Both tools have to
// agree on where it begins: the CPU starts execution through it, and the assembler
// has to refuse program text that would run into it.
//
// Entries are two bytes each, most significant byte first, the same order the branch
// instructions and this file format already use.
//
// 0xFC00 Software vectors 0 to 255
// 0xFE00 Hardware vectors 0 to 255, one for each I/O port
//
// Software vectors 0 and 1 are start addresses rather than handlers. Vector 0 is
// where the machine begins at power on and vector 1 is a warm restart, so a zero in
// either of them is not "nothing installed" but the address 0x0000, which is where a
// program carrying no vector table of its own begins. A zero in any other entry does
// mean no handler is installed, and dispatching through one is a fault.
#define SOFTWARE_VECTOR_BASE 0xFC00
#define HARDWARE_VECTOR_BASE 0xFE00
#define VECTOR_ENTRY_BYTES 2
#define VECTOR_BOOT 0
#define VECTOR_SOFT_RESET 1
#define VECTOR_INVALID_OPCODE 2
// Vectors 3 to 15 are held back for faults that do not exist yet, so that each cause
// can have an entry of its own rather than sharing one and needing a cause register to
// tell them apart. Everything from 16 up belongs to programs.
#define VECTOR_FIRST_FREE 16
// The first address the vector table occupies, and so the first address that program
// text may not use.
#define PROGRAM_TEXT_LIMIT SOFTWARE_VECTOR_BASE
#define SPLITBIT_MAGIC "SPBT"
#define SPLITBIT_MAGIC_LENGTH 4
#define SPLITBIT_FORMAT_VERSION 1
#define SPLITBIT_FLAGS_LENGTH 4
#define SEGMENT_MARKER_LENGTH 3
#define SEGMENT_LENGTH_BYTES 2
// Where a vector sits, and where its handler is.
#define VECTOR_ENTRY_FILE_BYTES 4
// Everything the format costs a file, on top of the two segments themselves.
#define SPLITBIT_HEADER_BYTES (SPLITBIT_MAGIC_LENGTH + 1 + SPLITBIT_FLAGS_LENGTH \
+87 -5
View File
@@ -164,13 +164,22 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
}
// Read off tokens.
while (readToken(&(*intermediateArray)[*intermediateIndex], file, &lineNumber)) {
if (*intermediateIndex >= *arraySize - 1) {
*arraySize *= 2; // Double the size of the array
*intermediateArray = realloc(*intermediateArray, *arraySize * sizeof(intermediateElement));
if (!intermediateArray) {
if ((size_t)*intermediateIndex >= *arraySize - 1) {
size_t grownSize = *arraySize * 2; // Double the size of the array.
// Into a temporary, so that the old allocation is still ours to free if
// this fails, rather than being lost the moment realloc returns NULL.
intermediateElement *grown = realloc(*intermediateArray, grownSize * sizeof(intermediateElement));
if (!grown) {
fprintf(stderr, RED "Error: Memory reallocation failed.\n" RESET);
exit(1);
}
// New elements have to start blank. realloc leaves the new space holding
// whatever the heap had in it before, and an element that never sets its
// own byteLength, such as a keyword or a label definition, would then add
// rubbish to the running address and move everything after it.
memset(grown + *arraySize, 0, (grownSize - *arraySize) * sizeof(intermediateElement));
*intermediateArray = grown;
*arraySize = grownSize;
}
//printf("Token number %d\n", intermediateIndex);
// Go ahead and mark what we already know about this token.
@@ -185,13 +194,24 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
status = NOWHERE;
// Get the filename and work out where it actually is.
(*intermediateIndex)++;
readToken(&(*intermediateArray)[*intermediateIndex], file, &lineNumber);
if (!readToken(&(*intermediateArray)[*intermediateIndex], file, &lineNumber)) {
// The file ended straight after the keyword, so there is no
// name to read and nothing sensible to go looking for.
fprintf(stderr, RED "Error: #Include without a file name.\n" RESET);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
char *requested = (*intermediateArray)[*intermediateIndex].token;
char *resolved = resolveInclude(fileName, requested);
if (!resolved) {
reportMissingInclude(fileName, requested, lineNumber);
exit(1);
}
// The included file's first token is about to be read into this
// same slot, so let the file name go now. Leaving it would strand
// the only pointer to it the moment it is overwritten.
free((*intermediateArray)[*intermediateIndex].token);
(*intermediateArray)[*intermediateIndex].token = NULL;
// recordSourceFile takes the path, and hands back NULL if this file
// has already been assembled. Including it twice is harmless, which
// is what lets two libraries depend on a third.
@@ -209,6 +229,56 @@ 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_ALIGN:
case KEYWORD_RESERVE: {
// Both take a count, and both only make sense somewhere that has a
// cursor to move along.
const char *what = (testValue == KEYWORD_ALIGN) ? "#Align" : "#Reserve";
if (status != PROGRAM && status != DATA) {
fprintf(stderr, RED "Error: %s outside the Program or Data Segment.\n There is nothing there for it to move along.\n" RESET, what);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
// The count is read here rather than being left to the literal check,
// because it is an instruction to the assembler and never becomes a
// byte, so a byte's range would be the wrong limit for it. A page
// alignment needs 256, and a reservation is often far larger.
intermediateElement *directive = &(*intermediateArray)[*intermediateIndex];
(*intermediateIndex)++;
if (!readToken(&(*intermediateArray)[*intermediateIndex], file, &lineNumber)) {
fprintf(stderr, RED "Error: %s without a number.\n" RESET, what);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
(*intermediateArray)[*intermediateIndex].fileName = fileName;
(*intermediateArray)[*intermediateIndex].lineNumber = lineNumber;
uint16_t count = readCount(&(*intermediateArray)[*intermediateIndex], what);
// The count token itself contributes nothing; the directive carries
// everything, so that one element stands for one run of zeroes.
(*intermediateArray)[*intermediateIndex].type = KEYWORD;
(*intermediateArray)[*intermediateIndex].byteLength = 0;
(*intermediateArray)[*intermediateIndex].destination = NOWHERE;
directive->destination = status;
if (testValue == KEYWORD_ALIGN) {
// How many zeroes this comes to depends on where the cursor has
// reached, which is not known until the second pass walks it.
directive->type = ALIGNMENT;
directive->address = count;
directive->byteLength = 0;
} else {
directive->type = PADDING;
directive->byteLength = count;
}
(*intermediateIndex)++;
continue;
}
case KEYWORD_VECTORS:
// Set the state to VECTORS. Tokens from here on name handlers rather
// than becoming bytes, and the second pass reads them.
status = VECTORS;
break;
}
// Next, check to see if it's an instruction.
} else if (checkIfInstruction(&(*intermediateArray)[*intermediateIndex])) {
@@ -235,6 +305,18 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
// A name written after SWI is a vector rather than an address, so it
// stands for one byte instead of two. This is settled by what the name
// follows, so that it does not depend on the Vector Segment having been
// read first, which it may not have been: it can live in another file.
if (status == PROGRAM
&& (*intermediateArray)[*intermediateIndex].type == LABEL
&& *intermediateIndex > 0
&& (*intermediateArray)[*intermediateIndex - 1].type == INSTRUCTION
&& (*intermediateArray)[*intermediateIndex - 1].byteValue == 0x18) {
(*intermediateArray)[*intermediateIndex].type = VECTOR_REFERENCE;
(*intermediateArray)[*intermediateIndex].byteLength = 1;
}
}
}
(*intermediateArray)[*intermediateIndex].destination = status;
+267 -10
View File
@@ -29,7 +29,7 @@ void freeLabelList() {
labelCount = 0;
}
void addLabel(char *labelName, uint16_t address, int type) {
void addLabel(char *labelName, uint16_t address, int type, const char *fileName, int lineNumber) {
if (labelCount < MAX_LABELS) {
// Duplicate labelName and remove the trailing colon, if present
char *cleanedLabel = strdup(labelName);
@@ -38,6 +38,18 @@ void addLabel(char *labelName, uint16_t address, int type) {
cleanedLabel[len - 1] = '\0'; // Remove the colon
}
// A name may only be defined once. Without this check a reference quietly
// resolves to whichever definition came first, so a typo or a name that two
// libraries both happen to use is very hard to track down.
for (int i = 0; i < labelCount; i++) {
if (strcmp(labelArray[i].label, cleanedLabel) == 0) {
fprintf(stderr, RED "Error: Label \"%s\" is defined more than once.\n" RESET, cleanedLabel);
printf("File: %s at line %d.\n", fileName, lineNumber);
free(cleanedLabel);
exit(1);
}
}
labelArray[labelCount].label = cleanedLabel;
labelArray[labelCount].address = address;
labelArray[labelCount].type = type;
@@ -54,11 +66,22 @@ void populateLabelTable(intermediateElement *intermediateArray, int arraySize) {
int dataCount = 0;
// Loop through the array, if there's a label definition, add it to the label list.
for (int i = 0; i < arraySize ; i++) {
if (intermediateArray[i].type == LABEL_DEFINITION) {
// How many zeroes an #Align comes to depends on where the cursor has reached,
// so it can only be worked out here, walking the tokens in order. It has to be
// settled before the running count moves past it, or every label after it lands
// in the wrong place.
if (intermediateArray[i].type == ALIGNMENT) {
int cursor = (intermediateArray[i].destination == PROGRAM) ? programCount : dataCount;
int alignment = intermediateArray[i].address;
intermediateArray[i].byteLength = (alignment - (cursor % alignment)) % alignment;
}
if (intermediateArray[i].type == LABEL_DEFINITION && intermediateArray[i].destination != VECTORS) {
if (intermediateArray[i].destination == PROGRAM) {
addLabel(intermediateArray[i].token, (uint16_t)programCount, PROGRAM);
addLabel(intermediateArray[i].token, (uint16_t)programCount, PROGRAM,
intermediateArray[i].fileName, intermediateArray[i].lineNumber);
} else {
addLabel(intermediateArray[i].token, (uint16_t)dataCount, DATA);
addLabel(intermediateArray[i].token, (uint16_t)dataCount, DATA,
intermediateArray[i].fileName, intermediateArray[i].lineNumber);
}
}
if (intermediateArray[i].destination == PROGRAM) {
@@ -69,8 +92,10 @@ void populateLabelTable(intermediateElement *intermediateArray, int arraySize) {
if (debugSecondPass) printf("Token: %s with byte length %d to destination %d of type %d\n", intermediateArray[i].token ,intermediateArray[i].byteLength, intermediateArray[i].destination, intermediateArray[i].type);
}
if (programCount > 0xFFFF ) {
fprintf(stderr, RED "Error: Program is too long to fit in Program Memory.\n" RESET);
if (programCount > PROGRAM_TEXT_LIMIT ) {
fprintf(stderr, RED "Error: Program is too long to fit in Program Memory.\n"
" Program text may not run past 0x%04X, where the vector table begins.\n" RESET,
PROGRAM_TEXT_LIMIT - 1);
exit(1);
}
if (dataCount > 0xFFFF ) {
@@ -79,6 +104,182 @@ void populateLabelTable(intermediateElement *intermediateArray, int arraySize) {
}
}
// ---- The Vector Segment ----
int findLabelAddress(const char *labelName);
VectorEntry vectorArray[MAX_VECTORS];
int vectorArrayCount = 0;
int vectorCount() {
return vectorArrayCount;
}
void freeVectorList() {
for (int i = 0; i < vectorArrayCount; i++) {
if (vectorArray[i].name) {
free(vectorArray[i].name);
}
}
vectorArrayCount = 0;
}
// The words the Vector Segment understands. These are spelled without regard to case,
// the way mnemonics are, because they are part of the language rather than names the
// programmer chose.
static int sameWord(const char *a, const char *b) {
while (*a && *b) {
if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) {
return 0;
}
a++;
b++;
}
return *a == *b;
}
// The vectors that already mean something. Everything else a program names is numbered
// for it, starting above the range held back for faults.
static const struct {
const char *name;
uint8_t index;
} reservedVectors[] = {
{ "Boot", VECTOR_BOOT },
{ "SoftReset", VECTOR_SOFT_RESET },
{ "BadOpcode", VECTOR_INVALID_OPCODE },
};
static const int reservedVectorCount = (int)(sizeof(reservedVectors) / sizeof(reservedVectors[0]));
static void vectorError(const char *message, intermediateElement *element) {
fprintf(stderr, RED "Error: %s\n" RESET, message);
printf("File: %s at line %d.\n", element->fileName, element->lineNumber);
printf("Token: %s\n", element->token);
exit(1);
}
// 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++) {
if (intermediateArray[i].destination == VECTORS && intermediateArray[i].type != KEYWORD) {
return i;
}
}
return -1;
}
static void addVector(char *name, uint8_t index, uint16_t base, uint16_t handler, 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) {
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);
}
if (name && vectorArray[i].name && strcmp(vectorArray[i].name, name) == 0) {
fprintf(stderr, RED "Error: Vector \"%s\" is named more than once.\n" RESET, name);
printf("File: %s at line %d.\n", element->fileName, element->lineNumber);
exit(1);
}
}
vectorArray[vectorArrayCount].name = name ? strdup(name) : NULL;
vectorArray[vectorArrayCount].index = index;
vectorArray[vectorArrayCount].base = base;
vectorArray[vectorArrayCount].handler = handler;
vectorArrayCount++;
}
// Resolves the handler named by the token at the given index.
static uint16_t resolveHandler(intermediateElement *intermediateArray, int at, const char *what) {
if (at < 0) {
fprintf(stderr, RED "Error: %s is not followed by a handler to go to.\n" RESET, what);
exit(1);
}
if (intermediateArray[at].type != LABEL) {
vectorError("A vector's handler has to be named by a label.", &intermediateArray[at]);
}
int address = findLabelAddress(intermediateArray[at].token);
if (address == -1) {
vectorError("That handler does not exist.", &intermediateArray[at]);
}
return (uint16_t)address;
}
void populateVectorTable(intermediateElement *intermediateArray, int arraySize) {
// Software vectors a program names for itself are numbered in the order they are
// written, starting above the block held back for faults. A programmer never types
// one, so there is no way to land on a reserved vector by accident.
int nextFreeVector = VECTOR_FIRST_FREE;
int i = nextVectorToken(intermediateArray, arraySize, 0);
while (i >= 0) {
char *token = intermediateArray[i].token;
if (sameWord(token, "Device")) {
// A device is named by the port it is plugged into, because that is what
// decides which vector it arrives through. There is nothing to allocate.
int portToken = nextVectorToken(intermediateArray, arraySize, i + 1);
if (portToken < 0 || intermediateArray[portToken].type != VALUE) {
vectorError("Device has to say which port, as a number.", &intermediateArray[i]);
}
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]);
i = nextVectorToken(intermediateArray, arraySize, handlerToken + 1);
continue;
}
uint8_t index;
int reserved = 0;
for (int r = 0; r < reservedVectorCount; r++) {
if (sameWord(token, reservedVectors[r].name)) {
index = reservedVectors[r].index;
reserved = 1;
break;
}
}
if (!reserved) {
if (nextFreeVector > 255) {
vectorError("There are no software vectors left to give this one.", &intermediateArray[i]);
}
index = (uint8_t)nextFreeVector;
nextFreeVector++;
}
int handlerToken = nextVectorToken(intermediateArray, arraySize, i + 1);
uint16_t handler = resolveHandler(intermediateArray, handlerToken, token);
addVector(token, index, SOFTWARE_VECTOR_BASE, handler, &intermediateArray[i]);
i = nextVectorToken(intermediateArray, arraySize, handlerToken + 1);
}
}
void fillInVectorReferences(intermediateElement *intermediateArray, int arraySize) {
for (int i = 0; i < arraySize; i++) {
if (intermediateArray[i].type != VECTOR_REFERENCE) {
continue;
}
int found = 0;
for (int v = 0; v < vectorArrayCount; v++) {
if (vectorArray[v].name && strcmp(vectorArray[v].name, intermediateArray[i].token) == 0) {
if (vectorArray[v].base != SOFTWARE_VECTOR_BASE) {
vectorError("SWI can only reach a software vector.", &intermediateArray[i]);
}
intermediateArray[i].byteValue = vectorArray[v].index;
found = 1;
break;
}
}
if (!found) {
fprintf(stderr, RED "Error: \"%s\" is not a vector.\n Names used with SWI have to be given a handler in a #Vectors section.\n" RESET,
intermediateArray[i].token);
printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber);
exit(1);
}
}
}
int findLabelAddress(const char *labelName) {
for (int i = 0; i < labelCount; i++) {
if (strcmp(labelArray[i].label, labelName) == 0) {
@@ -90,6 +291,12 @@ int findLabelAddress(const char *labelName) {
void fillInLabelAddresses(intermediateElement *intermediateArray, int arraySize) {
for (int i = 0; i < arraySize; i++) {
// The Vector Segment is resolved separately. Most of what it holds is not a
// label at all: the words that name a vector are the segment's own, and looking
// them up here would report them as undefined.
if (intermediateArray[i].destination == VECTORS) {
continue;
}
if (intermediateArray[i].type == LABEL) {
// Look up the label in the label table
int address = findLabelAddress(intermediateArray[i].token);
@@ -122,12 +329,21 @@ static void checkOperands(intermediateElement *intermediateArray, int arraySize,
int nextType = nextTokenType(intermediateArray, arraySize, i);
const char *problem = NULL;
if (((opcode & 0xF0) == 0x10) && (opcode != 0x1F)) {
// Listed rather than matched on the high nibble, because not every instruction in
// the branch block takes an address: RET has none, and BRD gets its destination
// from a Data Pointer instead of from the program.
if (opcode == 0x10 || opcode == 0x11 || opcode == 0x12 ||
opcode == 0x13 || opcode == 0x14 || opcode == 0x17) {
// Branches and CALL take a two byte address, which only a label can supply.
if (nextType != LABEL) problem = "Branch without label.";
} else if ((opcode & 0xF0) == 0xD0 || (opcode & 0xF0) == 0xE0) {
// The instruction is either an input or output and must be followed by a value.
if (nextType != VALUE) problem = "I/O without destination port.";
} else if (opcode == 0x18) {
// SWI names a vector, either by the name it was given in the Vector Segment or,
// rarely, as a literal number. Without one it swallows whatever follows it and
// every address after that shifts.
if (nextType != VECTOR_REFERENCE && nextType != VALUE) problem = "SWI without a vector to go to.";
} else if (opcode == 0x26 || opcode == 0x27) {
// INIA and INIB must be followed by the literal value to load.
if (nextType != VALUE) problem = "Immediate load without a value to load.";
@@ -172,6 +388,17 @@ void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize
// Add literal value to Program buffer.
Program[(*programCount)++] = intermediateArray[i].byteValue;
break;
case VECTOR_REFERENCE:
// A vector is a number rather than a place, so this is one byte
// where a label would be two.
Program[(*programCount)++] = intermediateArray[i].byteValue;
break;
case PADDING:
case ALIGNMENT:
for (int z = 0; z < intermediateArray[i].byteLength; z++) {
Program[(*programCount)++] = 0x00;
}
break;
case LABEL:
// Split 16-bit label address into high and low bytes.
Program[(*programCount)++] = (intermediateArray[i].address >> 8) & 0xFF; // High byte
@@ -199,6 +426,12 @@ void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize
Data[(*dataCount)++] = (intermediateArray[i].address >> 8) & 0xFF; // High byte
Data[(*dataCount)++] = intermediateArray[i].address & 0xFF; // Low byte
break;
case PADDING:
case ALIGNMENT:
for (int z = 0; z < intermediateArray[i].byteLength; z++) {
Data[(*dataCount)++] = 0x00;
}
break;
}
}
}
@@ -230,7 +463,7 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
fputc(programSize & 0xFF, outputFile); // Low byte
// Write the Program buffer to the file
if (fwrite(Program, sizeof(uint8_t), programCount, outputFile) != programCount) {
if (fwrite(Program, sizeof(uint8_t), programCount, outputFile) != (size_t)programCount) {
fprintf(stderr, RED "Error: Failed to write Program data to file \"%s\".\n" RESET, outputFileName);
fclose(outputFile);
exit(1);
@@ -245,13 +478,37 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
fputc(dataSize & 0xFF, outputFile); // Low byte
// Write the Data buffer to the file
if (fwrite(Data, sizeof(uint8_t), dataCount, outputFile) != dataCount) {
if (fwrite(Data, sizeof(uint8_t), dataCount, outputFile) != (size_t)dataCount) {
fprintf(stderr, RED "Error: Failed to write Data data to file \"%s\".\n" RESET, outputFileName);
fclose(outputFile);
exit(1);
}
// 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 vectorBytes = 0;
if (vectorArrayCount > 0) {
fwrite("VEC", sizeof(char), SEGMENT_MARKER_LENGTH, outputFile);
vectorBytes = vectorArrayCount * VECTOR_ENTRY_FILE_BYTES;
fputc((vectorBytes >> 8) & 0xFF, outputFile);
fputc(vectorBytes & 0xFF, outputFile);
for (int i = 0; i < vectorArrayCount; i++) {
uint16_t slot = vectorArray[i].base + (uint16_t)vectorArray[i].index * VECTOR_ENTRY_BYTES;
fputc((slot >> 8) & 0xFF, outputFile);
fputc(slot & 0xFF, outputFile);
fputc((vectorArray[i].handler >> 8) & 0xFF, outputFile);
fputc(vectorArray[i].handler & 0xFF, outputFile);
}
}
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 Total size: %d bytes.\n" RESET, programCount, dataCount, (programCount + dataCount + SPLITBIT_HEADER_BYTES));
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);
}
printf(GREEN " Total size: %d bytes.\n" RESET,
(programCount + dataCount + SPLITBIT_HEADER_BYTES
+ (vectorArrayCount > 0 ? SEGMENT_MARKER_LENGTH + SEGMENT_LENGTH_BYTES + vectorBytes : 0)));
}
+21
View File
@@ -12,6 +12,7 @@
#include "Assm-util.h"
#define MAX_LABELS 256
#define MAX_VECTORS 256
typedef struct {
char* label;
@@ -19,8 +20,28 @@ typedef struct {
int type;
} Label;
// One line of the Vector Segment, once it has been worked out.
typedef struct {
char* name; // What it was called, or NULL for a device, which is named by its port.
uint8_t index; // Which vector in its table.
uint16_t base; // Which table: software or hardware.
uint16_t handler; // Where the handler ended up.
} VectorEntry;
void freeLabelList();
void freeVectorList();
// Reads the Vector Segment: allocates a number to every named vector, works out which
// vector each device line means, and resolves the handlers. Runs after the labels are
// known, because a handler is named by its label.
void populateVectorTable(intermediateElement *intermediateArray, int arraySize);
// Turns each vector name used as an operand of SWI into the number it was given.
void fillInVectorReferences(intermediateElement *intermediateArray, int arraySize);
int vectorCount();
void populateLabelTable(intermediateElement *intermediateArray, int arraySize);
void fillInLabelAddresses(intermediateElement *intermediateArray, int arraySize);