Assembler Completed

Added the Assembler.
Added instructions for the assembler to README.md
Added Assembler Manual
Modified Makefile to build the Assembler.
This commit is contained in:
Anachronaut
2024-10-26 15:31:24 -04:00
committed by GitHub
parent 044c80d537
commit dfa5ad2638
23 changed files with 1627 additions and 22 deletions
+109
View File
@@ -0,0 +1,109 @@
// Assembler.c
// Basic Assembler for programs written for the SplitBit CPU
// Written by Anachronaut
// 10/18/2024
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <libgen.h>
#include "Assm-util.h"
#include "firstPass.h"
#include "secondPass.h"
int programLength = 0;
int dataLength = 0;
uint8_t Program[0xFFFF], Data[0xFFFF];
char* createOutputFileName(const char *inputFilePath) {
// Make a copy of inputFilePath, since basename may modify it
char *pathCopy = strdup(inputFilePath);
if (!pathCopy) {
fprintf(stderr, "Error: Memory allocation failed for path copy.\n");
exit(1);
}
// Get the filename from the path
char *fileName = basename(pathCopy);
// Find the length of the filename
size_t len = strlen(fileName);
// Check if the filename ends with ".asm"
char *outputFileName;
if (len > 4 && strcmp(fileName + len - 4, ".asm") == 0) {
// Allocate memory for the new file name with ".bin" extension
outputFileName = malloc(len - 4 + 5); // Remove ".asm" (4 chars) and add ".bin" (4 chars + null terminator)
if (!outputFileName) {
fprintf(stderr, "Error: Memory allocation failed for output file name.\n");
free(pathCopy);
exit(1);
}
// Copy the filename up to ".asm" and add ".bin"
strncpy(outputFileName, fileName, len - 4);
strcpy(outputFileName + len - 4, ".bin");
} else {
// If there's no ".asm" extension, add ".bin" to the full filename
outputFileName = malloc(len + 5); // Original length + ".bin" + null terminator
if (!outputFileName) {
fprintf(stderr, "Error: Memory allocation failed for output file name.\n");
free(pathCopy);
exit(1);
}
strcpy(outputFileName, fileName);
strcat(outputFileName, ".bin");
}
free(pathCopy); // Free the temporary path copy
return outputFileName;
}
// Remember to be a good programmer and free up all the allocated memory.
void assemblerCleanup(intermediateElement *intermediateArray, int arraySize, char *outputFileName) {
// Free each token in intermediateArray.
for (int i = 0; i < arraySize; i++) {
if (intermediateArray[i].token) {
free(intermediateArray[i].token);
}
}
// Free the intermediateArray itself.
free(intermediateArray);
// Free the list of included files.
freeIncludeList();
// Free the list of labels.
freeLabelList();
// Free the output file name
free(outputFileName);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
exit(1);
}
// Allocate initial space for the intermediate array.
size_t arraySize = 1024;
intermediateElement *intermediateArray = malloc(arraySize * sizeof(intermediateElement));
if (!intermediateArray) {
fprintf(stderr, RED "Error: Memory allocation failed.\n" RESET);
exit(1);
}
char *fileName = argv[1];
int index = 0;
loadFile(intermediateArray, fileName, &index, &arraySize);
populateLabelTable(intermediateArray, index);
fillInLabelAddresses(intermediateArray, index);
populateOutputBuffers(intermediateArray, index, Program, &programLength, Data, &dataLength);
char *outputFileName = createOutputFileName(fileName);
writeOutputFile(outputFileName, Program, programLength, Data, dataLength);
assemblerCleanup(intermediateArray, index, outputFileName);
return 0;
}
+176
View File
@@ -0,0 +1,176 @@
// Assm-util.c
// Utility and helper functions for the SplitBit Assembler.
// Written by Anachronaut
// 10/25/2024
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "Assm-util.h"
#include "assembly.h"
int debug = 0;
void toUppercase(char *str) {
for (int i = 0; str[i]; i++) {
str[i] = toupper(str[i]);
}
}
int checkIfKeyword(intermediateElement *currentElement) {
if (currentElement->token[0] == '#') {
if (debug) printf("Token: %s is a keyword.\n", currentElement->token);
currentElement->type = KEYWORD;
currentElement->destination = NOWHERE;
if (strcmp(currentElement->token, "#Include") == 0) {
return KEYWORD_INCLUDE;
} else if (strcmp(currentElement->token, "#Program") == 0) {
// We'll want to remember we encountered this and mark all the additional tokens until we hit another keyword for inclusion in the Program Segment.
return KEYWORD_PROGRAM;
} else if (strcmp(currentElement->token, "#Data") == 0) {
// Same as for #Program, but mark for inclusion in the Data Segment.
return KEYWORD_DATA;
} 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);
exit(1);
}
}
// It's not a keyword.
return 0;
}
int checkIfInstruction(intermediateElement *currentElement) {
char token[32];
strncpy(token, currentElement->token, sizeof(token)-1); //copying over one less than the total size of the buffer ensures we wind up with a null terminated string.
toUppercase(token);
if (getOpcode(token) != 0xFE) {
// It's a valid instruction, save its value and set its type.
currentElement->type = INSTRUCTION;
currentElement->byteValue = getOpcode(token);
currentElement->byteLength = 1;
if (debug) printf("Token: %s is an instruction.\n", currentElement->token);
return 1;
}
return 0;
}
int checkIfLiteralValue(intermediateElement *currentElement) {
char token[32];
strncpy(token, currentElement->token, sizeof(token)-1);
if (token[0] == '0') {
// It's a literal value. Check if it's hex or dec.
if(token[1] == 'x') {
// It's a hex literal. Set its value and type.
currentElement->type = VALUE;
currentElement->byteLength = 1;
memmove(token, token + 2, strlen(token)); // Shift the string over to get rid of the 0x.
currentElement->byteValue = (uint8_t)strtol(token, NULL, 16);
if (debug) printf("Token: %s is a hexadecimal literal. \n", currentElement->token);
} else if (token[1] == 'd') {
// It's a decimal literal. Set its value and type.
currentElement->type = VALUE;
currentElement->byteLength = 1;
memmove(token, token + 2, strlen(token)); // Shift the string over to get rid of the 0d.
currentElement->byteValue = (uint8_t)strtol(token, NULL, 10);
if (debug) printf("Token: %s is a decimal literal. \n", currentElement->token);
}
return 1;
}
return 0;
}
int checkIfLabel(intermediateElement *currentElement) {
char *token = currentElement->token;
int length = strlen(token);
// Check if the last character is a colon.
if (token[length - 1] == ':') {
// It is, so this is a label definition.
currentElement->type = LABEL_DEFINITION;
currentElement->destination = NOWHERE;
if (debug) printf("Token: %s is a label definition.\n", token);
return 1;
} else {
// By process of elimination, if whatever this is wasn't picked up by any of the other checks, it's either a label or is invalid.
currentElement->type = LABEL;
currentElement->byteLength = 2;
if (debug) printf("Token: %s is probably a label?\n", token);
return 1;
}
return 0;
}
int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber) {
int c;
char buffer[256]; // Buffer to hold the token temporarily.
int i = 0;
// Step 1: Skip whitespace and comments.
while ((c = fgetc(file)) != EOF) {
if (isspace(c)) {
if (c == '\n') (*lineNumber)++; // Increment line count on newlines.
continue; // Skip whitespace
} else if (c == ';') {
// A comment: ignore characters until the newline.
while ((c = fgetc(file)) != EOF && c != '\n');
if (c == '\n') (*lineNumber)++; // Increment line count after comment line.
continue;
} else {
break; // Found a non-whitespace, non-comment character
}
}
// Step 2: Handle EOF
if (c == EOF) {
return 0; // Indicate end of file
}
// Step 3: Handle string literals
if (c == '"') {
while ((c = fgetc(file)) != EOF && c != '"') {
if (i < sizeof(buffer) - 1) {
buffer[i++] = c;
} else {
fprintf(stderr, "Error: String literal too long.\n");
exit(1);
}
}
buffer[i] = '\0'; // Null-terminate the string
// Store in intermediateElement and set type
currentElement->token = strdup(buffer);
currentElement->type = STRING;
currentElement->byteLength = strlen(currentElement->token)+1;
if (debug) printf("Token: %s is a string literal.\n", currentElement->token);
return 1; // Success
}
// 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) {
buffer[i++] = c;
} else {
fprintf(stderr, "Error: Token too long.\n");
printf(" File: %s at line %d.\n", currentElement->fileName, currentElement->lineNumber);
exit(1);
}
}
buffer[i] = '\0'; // Null-terminate the token
// Put back the last character if it's not whitespace or EOF
if (c != EOF && c != ';') {
ungetc(c, file);
} else if (c == ';') {
// Skip remaining characters on this line if a comment starts
while ((c = fgetc(file)) != EOF && c != '\n');
}
// Store the token in intermediateElement and set a default type
currentElement->token = strdup(buffer);
currentElement->type = UNKNOWN;
return 1; // Success
}
+67
View File
@@ -0,0 +1,67 @@
// Assm-util.h
// Utility functions for the SplitBit Assembler's first pass.
// Written by Anachronaut
// 10/25/2024
#ifndef ASSMUTL_H
#define ASSMUTL_H
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "Assm-util.h"
#define MAX_INCLUDES 128
// Type values.
#define UNKNOWN 0
#define KEYWORD 1
#define INSTRUCTION 2
#define LABEL 3
#define LABEL_DEFINITION 4
#define VALUE 5
#define STRING 6
// Keyword values.
#define KEYWORD_INCLUDE 1
#define KEYWORD_PROGRAM 2
#define KEYWORD_DATA 3
// Destination values.
#define NOWHERE 0
#define PROGRAM 1
#define DATA 2
// For colorful text.
#define RESET "\x1B[0m"
#define RED "\x1B[31m"
#define GREEN "\x1B[32m"
#define YELLOW "\x1B[33m"
#define BLUE "\x1B[34m"
#define MAGENTA "\x1B[35m"
#define CYAN "\x1B[36m"
#define WHITE "\x1B[37m"
typedef struct {
char* token;
char* fileName;
int lineNumber;
uint8_t byteValue;
int byteLength;
uint16_t address;
int type; // "KEYWORD", "INSTRUCTION" , "LABEL" , "VALUE", "STRING"
int destination; // "NOWHERE", "PROGRAM", "DATA"
} intermediateElement;
int checkIfKeyword(intermediateElement *currentElement);
int checkIfInstruction(intermediateElement *currentElement);
int checkIfLiteralValue(intermediateElement *currentElement);
int checkIfLabel(intermediateElement *currentElement);
int readToken(intermediateElement *currentElement, FILE *file, int *lineNumber);
#endif
+93
View File
@@ -0,0 +1,93 @@
// assembly.c
// These are functions useful for translating assembly mnemonics to hex and vice-versa for the SplitBit CPU.
// Written by Anachronaut
// 10/18/2024
#include "assembly.h"
#include <string.h>
typedef struct {
uint8_t opcode;
const char* mnemonic;
} Instruction;
Instruction instruction_set[] = {
// Arithmetic and Logic Operations:
{0x00, "ADD"},
{0x01, "SUB"},
{0x02, "AND"},
{0x03, "OR"},
{0x04, "NOR"},
{0x05, "NAND"},
{0x06, "XOR"},
{0x07, "NOTA"},
{0x08, "NOTB"},
// Branch Operations:
{0x10, "BRI"},
{0x11, "BRQ"},
{0x12, "BRA"},
{0x13, "BRB"},
// Register Operations:
{0x20, "RSTA"},
{0x21, "RSTB"},
{0x22, "INCA"},
{0x23, "INCB"},
{0x24, "DECA"},
{0x25, "DECB"},
{0x26, "LDA"},
{0x27, "LDB"},
{0x28, "INIA"},
{0x29, "INIB"},
// Stack Operations:
{0x30, "PSHQ"},
{0x31, "PSHA"},
{0x32, "PSHB"},
{0x33, "PSHP"},
{0x34, "PSHD"},
{0x35, "POPA"},
{0x36, "POPB"},
{0x37, "POPP"},
{0x38, "POPD"},
// Data Operations:
{0x40, "INCD"},
{0x41, "DECD"},
{0x42, "LDA"},
{0x43, "LDB"},
{0x44, "STQ"},
{0x45, "STA"},
{0x46, "STB"},
{0x47, "SETD"},
// Output Operations:
{0xD0, "OUTQ"},
{0xD1, "OUTA"},
{0xD2, "OUTB"},
// Input Operations:
{0xE0, "INA"},
{0xE1, "INB"},
{0xE2, "IND"},
// Special Operations:
{0xF0, "NOP"},
{0xFF, "HALT"}
};
int num_instructions = sizeof(instruction_set) / sizeof(Instruction);
const char* getMnemonic(uint8_t opcode) {
for (int i = 0; i < num_instructions; i++) {
if (instruction_set[i].opcode == opcode) {
return instruction_set[i].mnemonic;
}
}
return "---";
}
uint8_t getOpcode(char* mnemonic) {
for (int i = 0; i < num_instructions; i++) {
if (strcmp(instruction_set[i].mnemonic, mnemonic) == 0) {
return instruction_set[i].opcode;
}
}
return 0xFE; // FE is an unused instruction, we'll use it to indicate an error.
}
+15
View File
@@ -0,0 +1,15 @@
// assembly.h
// These are functions useful for translating assembly mnemonics to hex and vice-versa for the SplitBit CPU.
// Written by Anachronaut
// 10/18/2024
#include <stdint.h>
#ifndef ASSEMBLY_H
#define ASSEMBLY_H
const char* getMnemonic(uint8_t opcode);
uint8_t getOpcode(char* mnemonic);
#endif // CPU_H
+130
View File
@@ -0,0 +1,130 @@
// firstPass.c
// Functions for the 'first pass' of the SplitBit Assembler.
// The first pass' primary goal is to get all the assembly files loaded into the Intermediate Array.
// Because I want included files to load and assemble in-line with the current file, that needs to be handled at this stage.
// Written by Anachronaut
// 10/25/2024
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "firstPass.h"
#include "Assm-util.h"
#include "assembly.h"
char *includeList[MAX_INCLUDES];
int includeCount = 0;
void freeIncludeList() {
for (int i = 0; i < includeCount; i++) {
if (includeList[i]) {
free(includeList[i]);
}
}
includeCount = 0;
}
int isFileIncluded(const char *fileName) {
for (int i = 0; i < includeCount; i++) {
if (strcmp(includeList[i], fileName) == 0) {
return 1;
}
}
return 0;
}
void addIncludedFile(const char *fileName) {
if (includeCount < MAX_INCLUDES) {
includeList[includeCount] = strdup(fileName);
includeCount++;
} else {
fprintf(stderr, RED "Error: Too many included files.\n" RESET);
exit(1);
}
}
int loadFile(intermediateElement *intermediateArray, char *fileName, int *intermediateIndex, size_t *arraySize){
// Initial setup.
addIncludedFile(fileName);
int status = NOWHERE;
int lineNumber = 1; // Line numbers start at 1.
// Open the file.
FILE *file = fopen(fileName, "r");
if (!file) {
fprintf(stderr, RED "Error: Couldn't open file \"%s\"\n" RESET, fileName);
exit(1);
}
// 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) {
fprintf(stderr, RED "Error: Memory reallocation failed.\n" RESET);
exit(1);
}
}
//printf("Token number %d\n", intermediateIndex);
// Go ahead and mark what we already know about this token.
intermediateArray[*intermediateIndex].fileName = fileName;
intermediateArray[*intermediateIndex].lineNumber = lineNumber;
// Take a look at it and determine what it is.
int testValue = checkIfKeyword(&intermediateArray[*intermediateIndex]);
if(testValue > 0) {
switch (testValue){
case KEYWORD_INCLUDE:
// We need to load another file and process it before continuing.
status = NOWHERE;
// Get the filename and load up the file.
(*intermediateIndex)++;
readToken(&intermediateArray[*intermediateIndex], file, &lineNumber);
char *includeFile = intermediateArray[*intermediateIndex].token;
if (isFileIncluded(includeFile)) {
fprintf(stderr, RED "Error: File %s is included more than once.\n" RESET, includeFile);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
loadFile(intermediateArray, includeFile, intermediateIndex, arraySize);
break;
case KEYWORD_PROGRAM:
// Set the state PROGRAM so we mark additional tokens for inclusion into Program Memory.
status = PROGRAM;
break;
case KEYWORD_DATA:
// Set the state to DATA so we mark additional tokens for inclusion into Data Memory.
status = DATA;
break;
}
// Next, check to see if it's an instruction.
} else if (checkIfInstruction(&intermediateArray[*intermediateIndex])) {
// We should check if we're set up to mark this for the Program Segment.
if (status != PROGRAM) {
fprintf(stderr, RED "Error: Attempting to assemble outside the Program Segment.\n Did you forget to use the #Program keyword?\n" RESET);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
// Next, check if it's a literal value.
} else if (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);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
// Finally, check if it's a label or label definition.
// First, make sure it hasn't already been marked as a string literal.
} else if (intermediateArray[*intermediateIndex].type != STRING) {
if (checkIfLabel(&intermediateArray[*intermediateIndex])) {
if (status == NOWHERE) {
fprintf(stderr, RED "Error: Attempting to create or use a label nowhere!\n Did you forget to use the #Program or #Data keyword?\n" RESET);
printf(" File: %s at line %d.\n", fileName, lineNumber);
exit(1);
}
}
}
intermediateArray[*intermediateIndex].destination = status;
(*intermediateIndex)++;
}
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
// firstPass.h
// Utility functions for the SplitBit Assembler.
// Written by Anachronaut
// 10/22/2024
#ifndef FIRSTPASS_H
#define FIRSTPASS_H
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "Assm-util.h"
void freeIncludeList();
int loadFile(intermediateElement *intermediateArray, char *fileName, int *intermediateIndex, size_t *arraySize);
#endif // FIRSTPASS_H
+199
View File
@@ -0,0 +1,199 @@
// secondPass.c
// Functions for the 'second pass' of the SplitBit Assembler.
// The goal here is to resolve the addresses of labels.
// We'll want to abort if the program comes out to greater than the maximum memory for SplitBit.
// We'll also want to abort if there's a label used with no definition.
// Written by Anachronaut
// 10/25/2024
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdint.h>
#include "secondPass.h"
#include "Assm-util.h"
int debugSecondPass = 0;
Label labelArray[MAX_LABELS];
int labelCount = 0;
void freeLabelList() {
for (int i = 0; i < labelCount; i++) {
if (labelArray[i].label) {
free(labelArray[i].label);
}
}
labelCount = 0;
}
void addLabel(char *labelName, uint16_t address, int type) {
if (labelCount < MAX_LABELS) {
// Duplicate labelName and remove the trailing colon, if present
char *cleanedLabel = strdup(labelName);
int len = strlen(cleanedLabel);
if (cleanedLabel[len - 1] == ':') {
cleanedLabel[len - 1] = '\0'; // Remove the colon
}
labelArray[labelCount].label = cleanedLabel;
labelArray[labelCount].address = address;
labelArray[labelCount].type = type;
if (debugSecondPass) printf("Added label %s with address %04X\n", labelName, labelArray[labelCount].address);
labelCount++;
} else {
fprintf(stderr, "Error: Too many labels defined.\n");
exit(1);
}
}
void populateLabelTable(intermediateElement *intermediateArray, int arraySize) {
int programCount = 0;
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) {
if (intermediateArray[i].destination == PROGRAM) {
addLabel(intermediateArray[i].token, (uint16_t)programCount, PROGRAM);
} else {
addLabel(intermediateArray[i].token, (uint16_t)dataCount, DATA);
}
}
if (intermediateArray[i].destination == PROGRAM) {
programCount += intermediateArray[i].byteLength;
} else if (intermediateArray[i].destination == DATA) {
dataCount += intermediateArray[i].byteLength;
}
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);
exit(1);
}
if (dataCount > 0xFFFF ) {
fprintf(stderr, RED "Error: Data is too long to fit in Data Memory.\n" RESET);
exit(1);
}
}
int findLabelAddress(const char *labelName) {
for (int i = 0; i < labelCount; i++) {
if (strcmp(labelArray[i].label, labelName) == 0) {
return labelArray[i].address;
}
}
return -1; // Label not found
}
void fillInLabelAddresses(intermediateElement *intermediateArray, int arraySize) {
for (int i = 0; i < arraySize; i++) {
if (intermediateArray[i].type == LABEL) {
// Look up the label in the label table
int address = findLabelAddress(intermediateArray[i].token);
if (address == -1) {
fprintf(stderr, RED "Error: Undefined label \"%s\".\n" RESET, intermediateArray[i].token);
printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber);
exit(1);
}
// Assign the found address to the element
intermediateArray[i].address = address;
}
}
}
void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize, uint8_t *Program, int *programCount, uint8_t *Data, int *dataCount) {
for (int i = 0; i < arraySize; i++) {
if (intermediateArray[i].destination == PROGRAM) {
switch (intermediateArray[i].type) {
case INSTRUCTION:
// Add instruction byte to Program buffer.
Program[(*programCount)++] = intermediateArray[i].byteValue;
// Check if it's a branch instruction.
if ((intermediateArray[i].byteValue & 0xF0) == 0x10) {
if (intermediateArray[i + 1].type != LABEL) {
fprintf(stderr, "Error: Branch without label.\n");
printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber);
exit(1);
}
} else if ((intermediateArray[i].byteValue & 0xF0) == 0xD0 || (intermediateArray[i].byteValue & 0xF0) == 0xE0) {
// The instruction is either an input or output and must be followed by a value
if (intermediateArray[i + 1].type != VALUE) {
fprintf(stderr, RED "Error: I/O without destination port.\n" RESET);
printf("File: %s at line %d.\n", intermediateArray[i].fileName, intermediateArray[i].lineNumber);
printf("Token: %s\n", intermediateArray[i].token);
exit(1);
}
}
break;
case VALUE:
// Add literal value to Program buffer.
Program[(*programCount)++] = intermediateArray[i].byteValue;
break;
case LABEL:
// Split 16-bit label address into high and low bytes.
Program[(*programCount)++] = (intermediateArray[i].address >> 8) & 0xFF; // High byte
Program[(*programCount)++] = intermediateArray[i].address & 0xFF; // Low byte
break;
}
} else if (intermediateArray[i].destination == DATA) {
switch (intermediateArray[i].type) {
case VALUE:
// Add literal value to Data buffer.
Data[(*dataCount)++] = intermediateArray[i].byteValue;
break;
case STRING:
// Copy string literal to Data buffer, including null terminator.
for (int j = 0; intermediateArray[i].token[j] != '\0'; j++) {
Data[(*dataCount)++] = intermediateArray[i].token[j];
}
Data[(*dataCount)++] = '\0'; // Add null terminator to Data buffer
break;
}
}
}
}
void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCount, uint8_t *Data, int dataCount) {
FILE *outputFile = fopen(outputFileName, "wb");
if (!outputFile) {
fprintf(stderr, RED "Error: Could not open file \"%s\" for writing.\n" RESET, outputFileName);
exit(1);
}
// Write the "PRG" header for the program segment
fwrite("PRG", sizeof(char), 3, outputFile);
// Write the program segment length as a 2-byte value (big-endian)
uint16_t programSize = programCount;
fputc((programSize >> 8) & 0xFF, outputFile); // High byte
fputc(programSize & 0xFF, outputFile); // Low byte
// Write the Program buffer to the file
if (fwrite(Program, sizeof(uint8_t), programCount, outputFile) != programCount) {
fprintf(stderr, RED "Error: Failed to write Program data to file \"%s\".\n" RESET, outputFileName);
fclose(outputFile);
exit(1);
}
// Write the "DAT" header for the data segment
fwrite("DAT", sizeof(char), 3, outputFile);
// Write the data segment length as a 2-byte value (big-endian)
uint16_t dataSize = dataCount;
fputc((dataSize >> 8) & 0xFF, outputFile); // High byte
fputc(dataSize & 0xFF, outputFile); // Low byte
// Write the Data buffer to the file
if (fwrite(Data, sizeof(uint8_t), dataCount, outputFile) != dataCount) {
fprintf(stderr, RED "Error: Failed to write Data data to file \"%s\".\n" RESET, outputFileName);
fclose(outputFile);
exit(1);
}
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." RESET, programCount, dataCount, (programCount+dataCount+10));
}
+32
View File
@@ -0,0 +1,32 @@
// secondPass.c
// Functions for the 'second pass' of the SplitBit Assembler.
// Written by Anachronaut
// 10/25/2024
#ifndef SECONDPASS_H
#define SECONDPASS_H
#include <stdint.h>
#include <ctype.h>
#include "Assm-util.h"
#define MAX_LABELS 256
typedef struct {
char* label;
uint16_t address;
int type;
} Label;
void freeLabelList();
void populateLabelTable(intermediateElement *intermediateArray, int arraySize);
void fillInLabelAddresses(intermediateElement *intermediateArray, int arraySize);
void populateOutputBuffers(intermediateElement *intermediateArray, int arraySize, uint8_t *Program, int *programCount, uint8_t *Data, int *dataCount);
void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCount, uint8_t *Data, int dataCount);
#endif