Uploaded Initial Repo Files

This commit is contained in:
Anachronaut
2024-10-16 23:12:59 -04:00
committed by GitHub
parent f611177a66
commit 2fd311f107
15 changed files with 815 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
BADthisisnotagoodfile.
Binary file not shown.
Binary file not shown.
+97
View File
@@ -0,0 +1,97 @@
// boostrap.c
// Boostrapping Functions for the SplitBit CPU Emulator
// Written by Anachronaut
// 10/16/2024
#include "bootstrap.h"
#include <stdio.h>
#include <string.h>
int byte = 0;
const uint8_t HEADER_LENGTH = 3; // Number of bytes for the header.
const uint8_t LENGTH_SIZE = 2; // Number of bytes for the segment length.
uint32_t readLength(FILE *file) {
uint16_t length = 0;
for (int i = 0; i < LENGTH_SIZE; i++) {
int byte = fgetc(file);
if (byte == EOF) {
fprintf(stderr, "Error: Unexpected end of file while reading a segment length.\n");
return UINT32_MAX;
}
length = (length << 8) | (uint8_t)byte;
}
return length;
}
uint32_t readHeader(FILE *file, const char *expectedHeader) {
uint8_t header[HEADER_LENGTH];
for (int i = 0; i < HEADER_LENGTH; i++) {
int byte = fgetc(file);
if (byte == EOF) {
fprintf(stderr, "Error: Unexpected end of file while reading a header.\n");
return UINT32_MAX;
}
header[i] = (uint8_t)byte;
}
if (strncmp(header, expectedHeader, 3)) {
fprintf(stderr, "Error: Bad header.\n");
printf("Header: %s\nExpected Header: %s\n", header, expectedHeader);
return UINT32_MAX;
}
return 0;
}
uint8_t loadSegment(FILE *file, uint8_t *Memory, uint16_t length) {
for (int16_t i = 0; i < length; i++) {
byte = fgetc(file);
if (byte == EOF) {
fprintf(stderr, "Error: Unexpected end of file while reading a segment.\n");
return 1;
}
Memory[i] = (uint8_t)byte;
}
return 0;
}
uint8_t loadFile(char *path, uint8_t *Program, uint8_t *Data) {
FILE *file = fopen(path, "rb");
if (file == NULL) {
fprintf(stderr, "Error: Couldn't open file: %s\n", path);
return 1;
}
// Read the first three bytes and check if they're the PRG header.
if (readHeader(file, "PRG") == UINT32_MAX) {
fclose(file);
return 1;
}
// Now we need to get the length of the Program Section.
uint32_t length = readLength(file);
if (length == UINT32_MAX) {
fclose(file);
return 1;
}
// Now we load the Program Segment.
if (loadSegment(file, Program, (uint16_t)length)) {
fclose(file);
return 1;
}
// Okay, Program is loaded, now do the same thing but for Data.
// Check the header.
if (readHeader(file, "DAT") == UINT32_MAX) {
return 1;
}
// Get the legnth of the Data Section.
length = readLength(file);
if (length == UINT32_MAX){
fclose(file);
return 1;
}
// Now load the Data Section.
if (loadSegment(file, Data, (uint16_t)length)) {
fclose(file);
return 1;
}
fclose(file);
return 0;
}
+13
View File
@@ -0,0 +1,13 @@
// boostrap.h
// Boostrapping Functions for the SplitBit CPU Emulator
// Written by Anachronaut
// 10/16/2024
#ifndef BOOTSTRAP_H
#define BOOTSTRAP_H
#include <stdint.h>
uint8_t loadFile(char *path, uint8_t *Program, uint8_t *Data);
#endif // BOOTSTRAP_H
+305
View File
@@ -0,0 +1,305 @@
// cpu.c
// SplitBit CPU Emulator Core
// Written by Anachronaut
// 10/16/2024
#include "cpu.h"
#include "io.h"
#include <stdio.h>
void initializeCPU(CPURegisters *cpu, uint8_t *programMemory, uint8_t *dataMemory) {
cpu->A = 0;
cpu->B = 0;
cpu->Q = 0;
cpu->Status = 0;
cpu->ProgramCounter = 0x0000;
cpu->DataPointer = 0x0000;
cpu->StackPointer = 0xFFFF;
cpu->Program = programMemory;
cpu->Data = dataMemory;
}
void genericBranch(CPURegisters *cpu){
// Load the next two bytes from program memory into the Program Counter.
// Byte order is imporant. Most Significant first, then Least Significant.
cpu->ProgramCounter++; // Move to the next byte. (MSB)
uint16_t DestinationAddress;
DestinationAddress = (uint16_t)cpu->Program[cpu->ProgramCounter] << 8; // Cast the 8 bit value to a 16 bit value and shifts it up to the high byte.
cpu->ProgramCounter++; // Move to the next byte. (LSB)
DestinationAddress = DestinationAddress | (uint16_t)cpu->Program[cpu->ProgramCounter]; // Cast the 8 bit value to a 16 bit value and or it to add it to the desination.
cpu->ProgramCounter = DestinationAddress-1;
}
uint8_t executeOperation(uint8_t Instruction, CPURegisters *cpu) {
switch(Instruction) {
// 0x - Arithmetic and Logic Operations.
case 0x00:
// ADD - A + B + Carry -> Q
uint16_t result = (uint16_t)cpu->A + (uint16_t)cpu->B + (cpu->Status & 0x01);
if (result > 255) {
cpu->Status |= 0x01;
} else {
cpu->Status &= ~0x01;
}
cpu->Q = result & 0xFF;
break;
case 0x01:
// SUB - A - B - Carry -> Q
result = (uint16_t)cpu->A - (uint16_t)cpu->B - (cpu->Status & 0x01);
if (result > 255) {
cpu->Status |= 0x01;
} else {
cpu->Status &= ~0x01;
}
cpu->Q = result & 0xFF;
break;
case 0x02:
// AND - A and B -> Q
cpu->Q = cpu->A&cpu->B;
break;
case 0x03:
// OR - A or B -> Q
cpu->Q = cpu->A|cpu->B;
break;
case 0x04:
// NAND - A nand B -> Q
cpu->Q = ~(cpu->A&cpu->B);
break;
case 0x05:
// NOR - A nor B -> Q
cpu->Q = ~(cpu->A|cpu->B);
break;
case 0x06:
// XOR - A xor B -> Q
cpu->Q = cpu->A^cpu->B;
break;
case 0x07:
// NOTA - not A -> Q
cpu->Q = ~cpu->A;
break;
case 0x08:
// NOTB - not B -> Q
cpu->Q = ~cpu->B;
break;
//
// 1x - Branch Operations:
//
case 0x10:
// BRI - Branch Immediately
genericBranch(cpu);
break;
case 0x11:
// BRQ - Branch if Q = 0
if(cpu->Q == 0) {
genericBranch(cpu);
} else {
cpu->ProgramCounter+=2;
}
break;
case 0x12:
// BRA - Branch if A = 0
if(cpu->A == 0) {
genericBranch(cpu);
} else {
cpu->ProgramCounter+=2;
}
break;
case 0x13:
// BRB - if B = 0
if(cpu->B == 0) {
genericBranch(cpu);
} else {
cpu->ProgramCounter+=2;
}
break;
//
// 2x - Register Operations:
//
case 0x20:
// RSTA - Reset A to 0.
cpu->A = 0;
break;
case 0x21:
// RSTB - Reset B to 0.
cpu->B = 0;
break;
case 0x22:
// INCA - Add 1 to A.
cpu->A++;
break;
case 0x23:
// INCB - Add 1 to B.
cpu->B++;
break;
case 0x24:
// DECA - Subtract 1 from A.
cpu->A--;
break;
case 0x25:
// DECB - Subtract 1 from B.
cpu->B--;
break;
case 0x26:
// LDA - Load the byte referenced by the Data Pointer to A.
cpu->A = cpu->Data[cpu->DataPointer];
break;
case 0x27:
// LDB - Load the byte referenced by the Data Pointer to B.
cpu->B = cpu->Data[cpu->DataPointer];
break;
case 0x28:
// INCD - Add 1 to the Data Pointer.
cpu->DataPointer++;
break;
case 0x29:
// DECD - Subtract 1 from the Data Pointer.
cpu->DataPointer--;
break;
//
// 3x - Stack Operations:
//
case 0x30:
// PSHQ - Push Q to the Stack.
cpu->Data[cpu->StackPointer] = cpu->Q;
cpu->StackPointer--;
break;
case 0x31:
// PSHA - Push A to the Stack.
cpu->Data[cpu->StackPointer] = cpu->A;
cpu->StackPointer--;
break;
case 0x32:
// PSHB - Push B to the Stack.
cpu->Data[cpu->StackPointer] = cpu->B;
cpu->StackPointer--;
break;
case 0x33:
// PSHP - Push the Program Counter to the Stack.
// Order, low byte, high byte
cpu->Data[cpu->StackPointer] = cpu->ProgramCounter & 0xFF;
cpu->StackPointer--;
cpu->Data[cpu->StackPointer] = (cpu->ProgramCounter >> 8) & 0xFF;
cpu->StackPointer--;
break;
case 0x34:
// PSHD - Push the Data Pointer to the Stack.
// Order, low byte, high byte
cpu->Data[cpu->StackPointer] = cpu->DataPointer & 0xFF;
cpu->StackPointer--;
cpu->Data[cpu->StackPointer] = (cpu->DataPointer >> 8) & 0xFF;
cpu->StackPointer--;
break;
case 0x35:
// POPA - Pop Data to A.
cpu->A = cpu->Data[cpu->StackPointer];
cpu->StackPointer++;
break;
case 0x36:
// POPB - Pop Data to B.
cpu->B = cpu->Data[cpu->StackPointer];
cpu->StackPointer++;
break;
case 0x37:
// POPP - Pop Data to the Program Counter
cpu->ProgramCounter = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
cpu->StackPointer++;
cpu->ProgramCounter = cpu->ProgramCounter | (uint16_t)cpu->Data[cpu->StackPointer];
cpu->StackPointer++;
break;
case 0x38:
// POPD - Pop Data to the Data Pointer
cpu->DataPointer = (uint16_t)cpu->Data[cpu->StackPointer] << 8;
cpu->StackPointer++;
cpu->DataPointer |= (uint16_t)cpu->Data[cpu->StackPointer];
cpu->StackPointer++;
break;
//
// 4x - Data Operations:
//
case 0x40:
// INCD - Increment Data Pointer.
cpu->DataPointer++;
break;
case 0x41:
// DECD - Decrement Data Pointer.
cpu->DataPointer--;
break;
case 0x42:
// LDA - Load A from Data.
cpu->A = cpu->Data[cpu->DataPointer];
break;
case 0x43:
// LDB - Load B from Data.
cpu->B = cpu->Data[cpu->DataPointer];
break;
case 0x44:
// STQ - Store Q into Data.
cpu->Data[cpu->DataPointer] = cpu->Q;
break;
case 0x45:
// STA - Store A into Data.
cpu->Data[cpu->DataPointer] = cpu->A;
break;
case 0x46:
// STB - Store B into Data.
cpu->Data[cpu->DataPointer] = cpu->B;
break;
//
// Dx - Output Operations:
//
case 0xD0:
// OUTQ - Write the value of Q to an output port.
cpu->ProgramCounter++;
OutputHandler(cpu->Q, cpu->Program[cpu->ProgramCounter]);
break;
case 0xD1:
// OUTA - Write the value of A to an output port.
cpu->ProgramCounter++;
OutputHandler(cpu->A, cpu->Program[cpu->ProgramCounter]);
break;
case 0xD2:
// OUTB - Write the value of B to an output port.
cpu->ProgramCounter++;
OutputHandler(cpu->B, cpu->Program[cpu->ProgramCounter]);
break;
//
// Ex - Input Operations:
//
case 0xE0:
// CIN - Read Input Select to A.
// cpu->A = InputSelect;
break;
case 0xE1:
// RDA - Read an Input to A.
cpu->ProgramCounter++;
cpu->A = InputHandler(cpu->Program[cpu->ProgramCounter]);
break;
case 0xE2:
// RDB - Read an Input to B.
cpu->ProgramCounter++;
cpu->B = InputHandler(cpu->Program[cpu->ProgramCounter]);
break;
case 0xE3:
// RDD - Read an Input to Data Memory.
cpu->ProgramCounter++;
cpu->Data[cpu->DataPointer] = InputHandler(cpu->Program[cpu->ProgramCounter]);
break;
//
// Fx - Special Operations:
//
case 0xF0:
// NOP - Do nothing.
break;
case 0xFF:
// HALT - Set the Halt Bit of the Status Register.
cpu->Status |= 0x80;
break;
default:
// Unknown Instruction.
return 1;
}
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
// cpu.h
// SplitBit CPU Emulator Core
// Written by Anachronaut
// 10/16/2024
#ifndef CPU_H
#define CPU_H
#include <stdint.h>
// The struct containing the CPU registers.
typedef struct {
uint8_t A;
uint8_t B;
uint8_t Q;
uint8_t Status;
uint16_t ProgramCounter;
uint16_t DataPointer;
uint16_t StackPointer;
uint8_t *Program;
uint8_t *Data;
} CPURegisters;
uint8_t executeOperation(uint8_t instruction, CPURegisters *cpu);
void initializeCPU(CPURegisters *cpu, uint8_t *programMemory, uint8_t *dataMemory);
#endif // CPU_H
+61
View File
@@ -0,0 +1,61 @@
// emulator.c
// SplitBit Emulator
// Small 8-Bit Harvard Architecture CPU
// Written by Anachronaut
// 10/15/2024
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "cpu.h"
#include "utility.h"
#include <string.h>
#include <getopt.h>
uint8_t debugEnable = 0;
int cycleCount = 0;
char *programFile = NULL;
// Memory Banks:
uint8_t Program[0x10000], Data[0x10000];
int main (int argc, char *argv[]) {
uint8_t test = parseOptions(argc, argv);
if (test == 1){
// Enable the Debug Mode.
debugEnable = 1;
} else if (test == 2){
// User asked for help or gave a bad option, don't execute.
return 1;
}
if (optind < argc) {
programFile = argv[optind];
optind++;
} else {
fprintf(stderr, "Error: No binary file specified.\n");
printHelp(argv[0]);
return 1;
}
if (optind < argc) {
fprintf(stderr, "Error: Unexpected argument: %s\n", argv[optind]);
return 1;
}
if (loadFile(programFile, Program, Data)) {
fprintf(stderr, "Error: Couldn't read file: %s\n", programFile);
return 1;
}
CPURegisters cpu;
initializeCPU(&cpu, Program, Data);
while (!(cpu.Status & 0x80)) {
if (debugEnable) {
getchar();
printRegisters(&cpu, Program, Data);
}
executeOperation(cpu.Program[cpu.ProgramCounter], &cpu);
cpu.ProgramCounter++;
cycleCount++;
}
printf("Execution halted after %u cycles.\n", cycleCount);
}
BIN
View File
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
// io.c
// I/O for the SplitBit CPU Emulator
// Written by Anachronaut
// 10/16/2024
#include "io.h"
#include <stdio.h>
uint8_t OutputHandler(uint8_t DataByte, uint8_t Address) {
// This function sends the DataByte to the appropriate place based on the Port Address.
switch(Address) {
case 0x00:
// If data is sent here, it should be written to STDOUT.
// For now, I'll implement this so it simply writes each byte out as it comes in.
// Later, I'll want to use a buffer for this for performance, probably.
putchar(DataByte);
break;
default:
// Writes to unused Output Ports are ignored.
return 1;
break;
}
return 0;
}
uint8_t InputHandler(uint8_t Address) {
switch(Address) {
case 0x00:
// If data is sent here, it should be read from STDIN.
return getchar();
break;
default:
// Reading from an unused port is ignored.
return 0;
break;
}
}
+16
View File
@@ -0,0 +1,16 @@
// io.h
// I/O for the SplitBit CPU Emulator
// Written by Anachronaut
// 10/16/2024
#ifndef IO_H
#define IO_H
#include <stdint.h>
#include "cpu.h"
uint8_t OutputHandler(uint8_t DataByte, uint8_t Address);
uint8_t InputHandler(uint8_t Address);
#endif // IO_H
+55
View File
@@ -0,0 +1,55 @@
// utility.c
// Utilities for the SplitBit CPU Emulator
// Written by Anachronaut
// 10/15/2024
#include "utility.h"
#include <stdio.h>
#include <string.h>
#include <getopt.h>
void printHelp(const char *programName) {
printf("Usage: %s [OPTIONS] <binaryfile>\n", programName);
printf("\n");
printf("Options:\n");
printf(" -d, --debug Enable debug mode.\n");
printf(" -h, --help Display this help message.\n");
}
uint8_t parseOptions(int argc, char *argv[]) {
static struct option long_options[] = {
{"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0 }
};
int opt;
int option_index = 0;
// Parse options
while ((opt = getopt_long(argc, argv, "dh", long_options, &option_index)) != -1) {
switch (opt) {
case 'd':
return 1;
break;
case 'h':
printHelp(argv[0]);
return 2;
case '?':
printHelp(argv[0]);
return 2;
default:
printHelp(argv[0]);
return 2;
}
}
return 0;
}
void printRegisters(CPURegisters *cpu, uint8_t *Program, uint8_t *Data) {
printf("***** CPU Registers *****\n");
printf("A: 0x%02X\tB: 0x%02X\tQ: 0x%02X\tStatus: 0b%08b\n", cpu->A, cpu->B, cpu->Q, cpu->Status);
printf("Program Counter: 0x%04X Current Instruction: 0x%02X\n", cpu->ProgramCounter, Program[cpu->ProgramCounter]);
printf(" Data Pointer: 0x%04X Current Data Value: 0x%02X\n", cpu->DataPointer, Data[cpu->DataPointer]);
printf(" Stack Pointer: 0x%04X Current Value: 0x%02X\n", cpu->StackPointer, Data[cpu->StackPointer]);
}
+23
View File
@@ -0,0 +1,23 @@
// utility.h
// Utilities for the SplitBit CPU Emulator
// Written by Anachronaut
// 10/15/2024
#ifndef UTILITY_H
#define UTILITY_H
#include <stdint.h>
#include "cpu.h"
uint8_t parseOptions(int argc, char *argv[]);
void printHelp(const char *programName);
uint8_t loadFile(char *path, uint8_t *Program, uint8_t *Data);
void bootStrap(uint8_t *Program, uint8_t *Data);
void printRegisters(CPURegisters *cpu, uint8_t *Program, uint8_t *Data);
#endif // UTILITY_H
+142
View File
@@ -0,0 +1,142 @@
# General Description:
SplitBit is a small 8 bit CPU.
It is a Harvard Architecture machine with a separate 64k memory space for its Program and another for its Data.
It has six registers:
- The A and B Registers are the 8 bit operands for the ALU. All ALU operations use them as operands. They can also be used as general purpose accumulators.
- The Q Register is the 8 bit ALU output register, all ALU operations store their result in it.
- The Program Counter is a 16 bit pointer into the Program Memory. It points to the current operation the CPU is executing. It initializes at address 0x0000.
- The Data Pointer is a 16 bit pointer into the Data Memory. It points to the current byte of data that the CPU can read or write to, and can be set arbitrarily by the programmer to any value in the Data Memory. It initializes at location 0x0000.
- The Stack Pointer is a 16 bit pointer into the Data Memory. It points to the current element of the stack, and is only ever modified by the use of the push and pop instructions. It initializes at location 0xFFFF.
- The Status register is an 8 bit register whose various bits are used as flags. Only three of these flags are used in the current implementation.
- Bit 0 is the Carry/Borrow Flag. Any arithmetic operation either sets or clears it depending on whether or not the result causes Q to overflow/underflow. It is a 1 if a carry/underflow occurred, and a 0 otherwise.
- Bit 1 is the Stack Collision Flag. It is set if the Data Pointer's value ever meets or exceeds the Stack Pointer's value. This condition also sets the Halt Flag.
- Bit 7 is the Halt Flag. It is set by the HALT instruction, or if there is a stack collision.
## List of Instructions:
### Arithmetic and Logic Operations: 9 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
00 | ADD | Adds A, B, and the Carry Flag, the result is stored in Q.
01 | SUB | Subtracts B and the Carry Flag from A, the result is stored in Q.
02 | AND | Bitwise and of A and B, the result is stored in Q.
03 | OR | Bitwise or of A and B, the result is stored in Q.
04 | NOR | Bitwise nor of A and B, the result is stored in Q.
05 | NAND | Bitwise nand of A and B, the result is stored in Q.
06 | XOR | Bitwise xor of A and B, the result is stored in Q.
07 | NOTA | Bitwise inversion of A, the result is stored in Q.
08 | NOTB | Bitwise inversion of B, the result is stored in Q.
### Branch Operations: 4 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
10 | BRI | Branch Immediately. Loads the immediate next two bytes of Program Memory into the Program Counter, first the most significant byte, then the least.
11 | BRQ | Branch on Q. If Q is zero, loads the immediate next two bytes of Program Memory into the Program Counter.
12 | BRA | Branch on A. If A is zero, loads the immediate next two bytes of Program Memory into the Program Counter.
13 | BRB | Branch on B. If B is zero, loads the immediate next two bytes of Program Memory into the Program Counter.
### Register Operations: 6 Instructions
| Hex Code | Mnemonic | Description |
| -------- | -------- | ------------------- |
| 20 | RSTA | Resets A to 0. |
| 21 | RSTB | Resets B to 0. |
| 22 | INCA | Adds 1 to A. |
| 23 | INCB | Adds 1 to B. |
| 24 | DECA | Subtracts 1 from A. |
| 25 | DECB | Subtracts 1 from B. |
### Stack Operations: 9 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
30 | PSHQ | Stores Q into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
31 | PSHA | Stores A into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
32 | PSHB | Stores B into Data Memory at the location referenced by the Stack Pointer then decrements the Stack Pointer.
33 | PSHP | Stores the Program Counter to the next two bytes in the stack, decrements the Stack Pointer by two.
34 | PSHD | Stores the Data Pointer to the next two bytes in the stack, decrements the Stack Pointer by two.
35 | POPA | Reads the location referenced by the Stack Pointer from Data Memory into A then increments the Stack Pointer.
36 | POPB | Reads the location referenced by the Stack Pointer from Data Memory into B then increments the Stack Pointer.
37 | POPP | Restores the Program Counter from the top two bytes in the stack, increments the Stack Pointer by two.
38 | POPD | Restores the Program Counter from the top two bytes in the stack, increments the Stack Pointer by two.
### Data Operations: 7 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
40 | INCD | Increments the Data Pointer.
41 | DECD | Decrements the Data Pointer.
42 | LDA | Loads the byte referenced from Data Memory by the Data Pointer into A.
43 | LDB | Loads the byte referenced from Data Memory by the Data Pointer into B.
44 | STQ | Stores Q into the byte referenced by the Data Pointer in Data Mmoery.
45 | STA | Stores A into the byte referenced by the Data Pointer in Data Memory.
46 | STB | Stores B into the byte referenced by the Data Pointer in Data Memory.
### Output Operations: 3 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
D0 | OUTQ | Writes the value of Q to an Output specified by the next byte of Program Memory.
D1 | OUTA | Writes the value of A to an Output specified by the next byte of Program Memory.
D2 | OUTB | Writes the value of B to an Output specified by the next byte of Program Memory.
### Input Operations: 3 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
E1 | RDA | Writes the value of an Input to A. The input port is specified the next byte of Program Memory.
E2 | RDB | Writes the value of an Input to B. The input port is specified the next byte of Program Memory.
E3 | RDD | Writes the value of an Input to Data Memory at the location referenced by the Data Pointer. The input port is specified by the next byte of Program Memory.
### Special Operations: 2 Instructions
Hex Code | Mnemonic | Description
-- | -- | --
F0 | HALT | Stops CPU Execution.
FF | NOP | Perform no Operation, increment the Program Counter.
## Input and Output In the Emulator:
The current implementation has Input 0 and Output 0 hooked to stdout and stdin respectively, allowing programs to read to and from the console.
### Example Program: Hello World
```
; Hello World for SplitBit CPU
; First, we define the string in Data Memory.
Data:
0000 0x48 ; 'H'
0001 0x65 ; 'e'
0002 0x6C ; 'l'
0003 0x6C ; 'l'
0004 0x6F ; 'o'
0005 0x2C ; ','
0006 0x20 ; ' '
0007 0x57 ; 'W'
0008 0x6F ; 'o'
0009 0x72 ; 'r'
000A 0x6C ; 'l'
000B 0x64 ; 'd'
000C 0x32 ; '!'
000D 0x0A ; This is a linefeed, it's equivalent to putting '\n' in a string in C.
000E 0x00 ; Zero terminates the string.
; Next, we'll create a loop that outputs each byte of our string to Output 0, the text console.
Program:
0000 LDA 0x42 ; Load the first byte of the string into A.
0001 BRA 0x12 ; If A is zero, branch out of the loop.
0002 0x00 0x00 ; The high byte of the branch.
0003 0x0A 0x0A ; The low byte of the branch.
0004 OUTA 0xD1 ; Output the value in A.
0005 0x01 0x00 ; The Output Port to use.
0006 INCD 0x40 ; Increment the Data Pointer to the next byte of the string.
0007 BRI 0x10 ; Branch immediately to the start of the loop.
0008 0x00 0x00 ; The high byte of the branch address.
0009 0x00 0x00 ; The low byte of the branch address.
000A HALT 0xFF ; The end of the program.
```
### Structure of a SplitBit Binary File:
The Program and Data values are both stored in a single file for loading into the system. The Program Segment must come first, then the Data Segment. The system will look for a three letter header, PRG for program and DAT for data. After the header is a two byte value representing the length of the segment. The system loads the memories with the bytes from the file in sequence starting from address 0x0000.
Here's an example hex dump of the hello world program stored in the proper format:
```
50 52 47 00 0A 42 12 00 0A D1 00 40 10 00 00 FF 44 41 54 00 0E 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A 00
```
+37
View File
@@ -0,0 +1,37 @@
# SplitBit Emulator Makefile
# Anachronaut
# 10/16/2024
# Compiler and flags
CC = gcc
CFLAGS = -Wall
# Directories
SRC_DIR = Source
OBJ_DIR = Object
# Source files
SRCS = emulator.c io.c utility.c cpu.c bootstrap.c
OBJS = $(SRCS:%.c=$(OBJ_DIR)/%.o)
# Output binary name
TARGET = SplitBit
# Default target
all: $(TARGET)
# Create binary by linking object files
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
# Compile source files to object files
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
mkdir -p $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@
# Clean up object and binary files
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
# Phony targets
.PHONY: all clean