Compare commits

..

10 Commits

Author SHA1 Message Date
Creeper Lv 0eb5a94cbb Added usage spec in main.c for SagittariusC. 2026-05-25 16:20:29 +10:00
Creeper Lv eb27f05927 Added more comments and definitions. 2026-05-25 16:13:05 +10:00
Creeper Lv 5be0fd7862 Added more definitions. 2026-05-25 16:07:03 +10:00
Creeper Lv bcff8b3859 Implemented using Antigravity and it actually works! 2026-05-25 15:21:34 +10:00
Creeper Lv 2365e0a329 Update assembler main.c 2026-05-25 14:34:37 +10:00
Creeper Lv 1b389d7fff Added a matcher function definition. 2026-05-25 14:32:03 +10:00
Creeper Lv 0d872df456 Implemented print syscall in standalone. 2026-05-25 14:30:04 +10:00
Creeper Lv 7cc7e1bf40 Added more definitions. 2026-05-25 14:24:15 +10:00
Creeper Lv b35596a19a Adding more definitions. 2026-04-11 14:55:25 +08:00
Creeper Lv d34c74f617 Update build script. 2026-04-11 12:53:18 +08:00
14 changed files with 2265 additions and 44 deletions
+91
View File
@@ -0,0 +1,91 @@
#ifndef __SAGITTARIUS_ASSEMBLER_H_
#define __SAGITTARIUS_ASSEMBLER_H_
#include "../Sagittarius.h"
#include <stdbool.h>
/**
*
* The file format of assembly code for Sagittarius is as follows:
*
* Label:
* <label_name>:
* Special Labels:
*
* These labels are section indicators, and they indicate the beginning of a section. The content of the section will be determined by the label, and the assembler will handle the content accordingly:
*
* .code:
* Code section, where instructions are placed. The program counter starts from the beginning of this section.
* .data:
* Data section, where data is placed. The data in this section can be accessed by instructions, and the offset of the data is determined by the order of the data in this section.
* .const:
* Constant section, when the program is being assembled, the assembler will replace the label in the code with the value from constant section. Constant data won't be included in the final program.
*
* To use a label, simply use the label name in the code. The assembler will replace the label with the offset of the label in the final program. For example:
* `set 0 data_0`
*
* Note: special labels cannot be used as normal labels, and normal labels cannot have the same name as special labels.
*
*
*
* Format of instruction:
* <inst_name> <arg1> <arg2> ... <argn>
*
* Format of data:
* <data_name> <type> <data_value>
* Data types:
* - string
* - base64
* - file
*
* Instruction alias:
*
* set.<type> <reg> <value> is an alias for set instruction, which sets the register to the value of the specified type. The assembler will convert the value to the corresponding type and then generate the set instruction. For example:
*
* Comments:
*
* Any text after a semicolon (;) and hash (#) is considered a comment and will be ignored by the assembler.
*
* Example:
* .code:
* start:
* set.uint64 syscal_printf_arg0 data_0
* set.int32 16 0x123
* .data:
* data_0 string "Hello, World!"
* .code:
* set.float 24 3.14
* add int32 8 16 32
* syscall syscall_printf_namespace syscall_printf_func
* .const:
* syscall_printf_namespace 0
* syscall_printf_func 0
* syscal_printf_arg0 40
*/
typedef struct Sag_Str{
char* head;
char* file;
uint64_t line;
uint64_t col; // The column where the string starts, counting from 0. This is used for error reporting and debugging purposes.
uint64_t start;
uint64_t length;
} Sag_Str;
typedef struct Sag_IntermediateInst{
SagittariusInst inst;
Sag_Str* label;
Sag_Str* args;
uint64_t arg_count;
} Sag_IntermediateInst;
typedef struct Sag_IntermediateProgram{
Sag_IntermediateInst* insts;
uint64_t inst_count;
Sag_Str* data;
uint64_t data_count;
Sag_Str* Consts;
uint64_t Consts_count;
} Sag_IntermediateProgram;
bool Sag_NextWord(FILE* f, Sag_Str* out);
bool Sag_MatchStr(Sag_Str* str, char** match_list, uint64_t match_count, uint64_t* out_index);
bool Sag_Scan(FILE* f, Sag_IntermediateProgram* out);
bool Sag_Combine(Sag_IntermediateProgram* L, Sag_IntermediateProgram* R, Sag_IntermediateProgram* out);
bool Sag_Finalize(Sag_IntermediateProgram* intermediate, SagittariusProgram* out);
bool Sag_WriteProgram(FILE* f, SagittariusProgram* program);
#endif
+45 -26
View File
@@ -4,40 +4,59 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef _WIN32
#define SAGITTARIUS_API __declspec(dllexport)
#else
#define SAGITTARIUS_API __attribute__((visibility("default")))
#endif
typedef struct SagittariusRegister{
//256 Usable + Overflow preventing 256 extra bytes.
#include "SagittariusBase.h"
typedef struct SagittariusRegister
{
// 256 Usable + Overflow preventing purpose 256 extra bytes.
uint8_t head[512];
}SagittariusRegister;
typedef struct SagittariusMemory{
uint8_t* data;
} SagittariusRegister;
typedef struct SagittariusMemory
{
uint8_t *data;
uint64_t size;
} SagittariusMemory;
typedef struct SagittariusCore{
typedef struct SagittariusCore
{
SagittariusRegister reg;
SagittariusMemory* memory;
SagittariusMemory *memory;
struct SagittariusVM *vm;
uint64_t pc;
}SagittariusCore;
typedef int32_t(* SagittariusSyscall)(SagittariusCore* core, uint64_t arg_start);
typedef struct SagittariusSyscallEntry{
} SagittariusCore;
typedef int32_t (*SagittariusSyscall)(SagittariusCore *core);
typedef int32_t (*SagittariusPanicHandler)(SagittariusCore *core, uint64_t panic_id, const char *msg);
typedef struct SagittariusSyscallEntry
{
uint64_t id;
SagittariusSyscall syscall;
}SagittariusSyscallEntry;
typedef struct SagittariusVM{
SagittariusSyscallEntry* SagittariusSyscallEntries;
} SagittariusSyscallEntry;
typedef struct SagittariusVM
{
SagittariusSyscallEntry *SagittariusSyscallEntries;
uint64_t SagittariusSyscallCount;
uint64_t SagittariusSyscallCapacity;
SagittariusCore core;
SagittariusMemory Memory;
}SagittariusVM;
SAGITTARIUS_API SagittariusVM* sagittarius_vm_new(uint64_t memory_size);
SAGITTARIUS_API void sagittarius_vm_free(SagittariusVM* vm);
SAGITTARIUS_API void sagittarius_step(SagittariusVM* vm);
SAGITTARIUS_API void sagittarius_mem_resize(SagittariusVM* vm, uint64_t new_size);
SAGITTARIUS_API uint64_t sagittarius_mem_getsize(SagittariusVM* vm);
SagittariusPanicHandler panic_handler;
} SagittariusVM;
/**
* The layout of program in byte form (without the head):
* |0..31|32..95|96..159|160..|...
* |format version|inst count|data size|instructions...|data...|
* |uint32|uint64|uint64|inst1|inst2|...|data...|
*/
typedef struct SagittariusProgram
{
SagittariusInst *instructions;
uint64_t instCount;
uint8_t *data;
uint64_t data_size;
} SagittariusProgram;
SAGITTARIUS_API SagittariusVM *sagittarius_vm_new(uint64_t memory_size);
SAGITTARIUS_API void sagittarius_register_syscall(SagittariusVM *vm, uint64_t id, SagittariusSyscall syscall);
SAGITTARIUS_API void sagittarius_vm_free(SagittariusVM *vm);
SAGITTARIUS_API void sagittarius_step(SagittariusVM *vm);
SAGITTARIUS_API void sagittarius_mem_resize(SagittariusVM *vm, uint64_t new_size);
SAGITTARIUS_API uint64_t sagittarius_mem_getsize(SagittariusVM *vm);
SAGITTARIUS_API void sagittarius_load_program_to_mem(SagittariusVM *vm, SagittariusProgram *program, uint64_t offset);
SAGITTARIUS_API SagittariusProgram *sagittarius_load_program_from_byte_array(uint8_t *data, uint64_t size);
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef _SAGITTARIUS_BASE_H_
#define _SAGITTARIUS_BASE_H_
#ifdef _WIN32
#define SAGITTARIUS_API __declspec(dllexport)
#define internal
#else
#define SAGITTARIUS_API __attribute__((visibility("default")))
#define internal __attribute__((visibility("hidden")))
#endif
#define SAGITTARUIS_PROGRAM_HEAD "SAGPROG"
#define SAGITTARIUS_PROGRAM_FORMAT_VERSION 0x00000001
#endif
+150
View File
@@ -0,0 +1,150 @@
#ifndef __SAGITTARIUS_C_H_
#define __SAGITTARIUS_C_H_
#include "../Sagittarius.h"
#include <stdbool.h>
#include "../Assembler/SagittariusAssembler.h"
/**
* Workflow for Sagittarius C compiler:
*
* Scan -> Parse -> Finalize -> Generate Assembly -> Assemble to Program
*
* Finalize tree will perform type checking, name resolution, and other necessary checks and transformations to ensure the tree is ready for code generation. The finalized tree will be used for both generating assembly.
*
*
* This flavor of C is designed to be simple and easy to compile, and it may not support all features of standard C.
* The main purpose of this compiler is to provide a convenient way for developers to write code for Sagittarius VM, and it may have some limitations compared to a full-featured C compiler.
*
* It also supports a simple module system, where you can include other C files as modules. The included files will be compiled together with the main file, and they can share the same namespaces and symbols.
*
* It have no separate function declaration and definition, and all functions don't need to be defined before they are used.
*
* It should also support generic types.
*
* Sample Sagittarius C code:
*
* import sys;
* namespace sample{
* T add<T>(T a, T b){
* return a + b;
* }
* void main(){
* int x = add(1, 2);
* float y = add(3.14f, 2.71f);
* sys.printint(x);
* printfloat(y); // Compiler will resolve printfloat to correct function in sys namespace.
* }
* }
*
*/
typedef struct SagittariusC_SyntaxNode
{
uint64_t type;
void *data;
} SagittariusC_SyntaxNode;
typedef struct SagittariusC_SyntaxNode_Root
{
SagittariusC_SyntaxNode *Children;
uint64_t ChildCount;
} SagittariusC_SyntaxNode_Root;
typedef struct SagittariusC_SyntaxNode_Namespace
{
Sag_Str *Name;
SagittariusC_SyntaxNode *Children;
uint64_t ChildCount;
} SagittariusC_SyntaxNode_Namespace;
typedef struct SagittariusC_SyntaxNode_Import
{
Sag_Str *namespace;
} SagittariusC_SyntaxNode_Import;
typedef struct SagittariusC_SyntaxNode_Expression
{
void *real_expression_node;
} SagittariusC_SyntaxNode_Expression;
typedef enum SagittariusC_SyntaxNodeType
{
sag_c_node_root,
sag_c_node_import,
sag_c_node_namespace,
sag_c_node_expression,
sag_c_node_expression_value,
sag_c_node_expression_variable,
sag_c_node_expression_call,
sag_c_node_expression_syscall,
sag_c_node_expression_binary_op,
sag_c_node_expression_unary_op,
sag_c_node_control_if,
sag_c_node_control_while,
sag_c_node_control_for,
sag_c_node_control_return,
sag_c_node_control_break,
sag_c_node_control_continue,
sag_c_node_control_block,
sag_c_node_declaration_variable,
sag_c_node_declaration_function,
sag_c_node_control_switch,
sag_c_node_control_case,
sag_c_node_declaration_struct,
sag_c_node_declaration_enum,
sag_c_node_typedef,
sag_c_node_declaration_function_parameter,
sag_c_node_declaration_function_type,
/**
* asm node is a special node that can be used as a replacement for return statement for a function have return value.
*/
sag_c_node_asm,
} SagittariusC_SyntaxNodeType;
typedef struct SagittariusC_Config
{
char **include_paths;
uint64_t include_path_count;
char *output_file;
char *temp_dir;
char *assembler_path;
bool obj_only;
} SagittariusC_Config;
typedef struct SagittariusC_Tree_Cache
{
SagittariusC_SyntaxNode_Root **root;
int64_t *ref_count;
} SagittariusC_Tree_Cache;
typedef struct SagittariusC_Segment
{
Sag_Str *content;
/**
* Null for the first segment, and points to the previous segment for non-first segments. The segments are linked in a doubly linked list manner, and the head and tail of the list are stored in the assembler struct for easy access.
*/
struct SagittariusC_Segment *prev;
/**
* Null for end of segments, and points to the next segment for non-end segments. The segments are linked in a doubly linked list manner, and the head and tail of the list are stored in the assembler struct for easy access.
*/
struct SagittariusC_Segment *next;
} SagittariusC_Segment;
bool sagittarius_c_next_word(FILE *f, char *file_name, Sag_Str *out);
/*
* This function performs basic scanning of the source code, which will read the source code and split it into segments, and also perform some basic processing on the segments, such as removing comments and whitespace. The segments are stored in a linked list manner, and the head of the list is returned through the out parameter.
*This function should be called before post processing and parsing the segments.
*/
SAGITTARIUS_API bool sagittarius_c_scan_segments(FILE *f, char *file_name, SagittariusC_Segment **out_head);
/*
* Post process the segments, which will perform some necessary processing on the segments,
* such as combining segments for floats like "3.14f" into a single segment, and also perform some basic checks on the segments to ensure they are well formed. This function should be called before parsing the segments into a syntax tree.
*/
SAGITTARIUS_API bool sagittarius_c_post_process_segments(SagittariusC_Segment *head);
SAGITTARIUS_API bool sagittarius_c_parse_segments(SagittariusC_Segment *head, SagittariusC_SyntaxNode_Root *out_root);
/**
* By writing the tree to a file, the file is called an object file.
*/
SAGITTARIUS_API bool sagittarius_c_write_tree(SagittariusC_SyntaxNode_Root *root, FILE *f);
SAGITTARIUS_API bool sagittarius_c_compile_tree(SagittariusC_SyntaxNode_Root *root, SagittariusC_Config *config, SagittariusC_SyntaxNode_Root *finalized_root);
SAGITTARIUS_API bool sagittarius_c_tree_to_assembly(SagittariusC_SyntaxNode_Root *root, FILE *f);
SAGITTARIUS_API bool sagittarius_c_assembly_to_program(char *assembly_file, char *program_file);
#endif
+22 -4
View File
@@ -34,7 +34,7 @@ typedef enum SagittariusInstDef
* |0|1|2..7|8..15(another instruction struct)|
* |set|length(1-8 bytes)|padding|payload|
*/
set,
set,
/**
* Move data from one register to another.
* Layout:
@@ -128,10 +128,13 @@ typedef enum SagittariusInstDef
*/
syscall,
/**
* Test System Call
* Test System Call, for its existence.
*
* Layout:
* |0|1|2|3|
* |tsyscall|namespace reg| function id reg| result reg|
*
* 0 - failed, 1 - success, other values are reserved for future use.
*/
tsyscall,
} SagittariusInstDef;
@@ -145,7 +148,8 @@ typedef enum math2op
sag_math2_pow,
} math2op;
typedef enum math1op{
typedef enum math1op
{
sag_math1_sin,
sag_math1_cos,
sag_math1_tan,
@@ -156,7 +160,21 @@ typedef enum math1op{
sag_math1_acos,
sag_math1_atan,
sag_math1_abs,
}math1op;
sag_math1_exp,
} math1op;
typedef enum sagittarius_type
{
st_uint8,
st_uint16,
st_uint32,
st_uint64,
st_int8,
st_int16,
st_int32,
st_int64,
st_single,
st_double
} sagittarius_type;
typedef struct SagittariusInst
{
uint64_t data;
+46
View File
@@ -0,0 +1,46 @@
#ifndef _SAGITTARIUS_INTERNAL_H_
#define _SAGITTARIUS_INTERNAL_H_
#include "Sagittarius.h"
#include "SagittariusPanic.h"
#include <stdbool.h>
internal bool SagMath2Add(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T);
internal bool SagMath2Sub(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T);
internal bool SagMath2Mul(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T);
internal bool SagMath2Div(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T);
internal bool SagMath2Mod(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T);
internal bool SagMath2Pow(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T);
internal bool SagMath1Sin(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Cos(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Tan(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Sinh(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Cosh(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Tanh(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Asin(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Acos(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Atan(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Abs(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool SagMath1Exp(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T);
internal bool Math2Op(SagittariusCore *core, SagittariusInst inst);
internal bool Math1Op(SagittariusCore *core, SagittariusInst inst);
internal bool SagCvt(SagittariusCore *core, SagittariusInst inst);
internal bool SagSet(SagittariusCore *core, SagittariusInst inst);
internal bool SagMv(SagittariusCore *core, SagittariusInst inst);
internal bool SagCp(SagittariusCore *core, SagittariusInst inst);
internal bool SagSave(SagittariusCore *core, SagittariusInst inst);
internal bool SagLoad(SagittariusCore *core, SagittariusInst inst);
internal bool SagJmp(SagittariusCore *core, SagittariusInst inst);
internal bool SagJmpIf(SagittariusCore *core, SagittariusInst inst);
internal bool SagCall(SagittariusCore *core, SagittariusInst inst);
internal bool SagRet(SagittariusCore *core, SagittariusInst inst);
internal bool SagCmp(SagittariusCore *core, SagittariusInst inst);
internal bool SagMathV(SagittariusCore *core, SagittariusInst inst);
internal bool SagHalt(SagittariusCore *core, SagittariusInst inst);
internal bool SagSyscall(SagittariusCore *core, SagittariusInst inst);
internal bool SagTSyscall(SagittariusCore *core, SagittariusInst inst);
#endif
+20
View File
@@ -0,0 +1,20 @@
#ifndef _SAGITTARIUS_PANIC_H_
#define _SAGITTARIUS_PANIC_H_
#include <stdint.h>
typedef enum SagMsgLevel
{
info,
warn,
error,
fatal
} SagMsgLevel;
typedef struct NoticiableMsg
{
SagMsgLevel level;
uint64_t NoticiableId;
char* msg;
} NoticiableMsg;
#define Sagittarius_Msg_Generic 0x0000000000000000ULL
#define Sagittarius_Msg_Unknown 0xFFFFFFFFFFFFFFFFULL
#define Sagittarius_Msg_OOB 0x1000000000000000ULL
#endif
+868
View File
@@ -0,0 +1,868 @@
#include "../../Headers/Assembler/SagittariusAssembler.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Lexer / scanner helpers */
bool Sag_NextWord(FILE* f, Sag_Str* out) {
if (!f || !out) return false;
int c;
// Skip whitespace and comments
while (1) {
c = fgetc(f);
if (c == EOF) return false;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
continue;
}
if (c == ';' || c == '#') {
// Skip until end of line
while (1) {
c = fgetc(f);
if (c == EOF || c == '\n' || c == '\r') break;
}
continue;
}
break;
}
// Read word/token
uint64_t cap = 32;
uint64_t len = 0;
char *buf = malloc(cap);
if (!buf) return false;
if (c == '"') {
// Read string literal
buf[len++] = c;
while (1) {
c = fgetc(f);
if (c == EOF) {
free(buf);
return false;
}
if (len + 2 >= cap) {
cap *= 2;
buf = realloc(buf, cap);
}
buf[len++] = c;
if (c == '"') {
break;
}
}
} else {
// Read normal word until whitespace, comment, or EOF
buf[len++] = c;
while (1) {
c = fgetc(f);
if (c == EOF) break;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ';' || c == '#') {
ungetc(c, f);
break;
}
if (len + 2 >= cap) {
cap *= 2;
buf = realloc(buf, cap);
}
buf[len++] = c;
}
}
buf[len] = '\0';
out->head = buf;
out->start = 0;
out->length = len;
return true;
}
bool Sag_MatchStr(Sag_Str* str, char** match_list, uint64_t match_count, uint64_t* out_index) {
if (!str || !str->head) return false;
for (uint64_t i = 0; i < match_count; ++i) {
if (strlen(match_list[i]) == str->length &&
strncmp(str->head + str->start, match_list[i], str->length) == 0) {
if (out_index) *out_index = i;
return true;
}
}
return false;
}
static uint64_t get_instruction_arg_count(const char *name) {
if (strcmp(name, "add") == 0 || strcmp(name, "sub") == 0 || strcmp(name, "mul") == 0 ||
strcmp(name, "div") == 0 || strcmp(name, "mod") == 0 || strcmp(name, "pow") == 0) {
return 4;
}
if (strcmp(name, "sin") == 0 || strcmp(name, "cos") == 0 || strcmp(name, "tan") == 0 ||
strcmp(name, "sinh") == 0 || strcmp(name, "cosh") == 0 || strcmp(name, "tanh") == 0 ||
strcmp(name, "asin") == 0 || strcmp(name, "acos") == 0 || strcmp(name, "atan") == 0 ||
strcmp(name, "abs") == 0 || strcmp(name, "exp") == 0) {
return 3;
}
if (strcmp(name, "cvt") == 0) return 4;
if (strcmp(name, "set") == 0) return 3;
if (strncmp(name, "set.", 4) == 0) return 2; // alias!
if (strcmp(name, "mv") == 0) return 3;
if (strcmp(name, "cp") == 0) return 3;
if (strcmp(name, "save") == 0) return 3;
if (strcmp(name, "load") == 0) return 3;
if (strcmp(name, "jmp") == 0) return 3;
if (strcmp(name, "jmp_if") == 0) return 4;
if (strcmp(name, "call") == 0) return 4;
if (strcmp(name, "ret") == 0) return 1;
if (strcmp(name, "cmp") == 0) return 5;
if (strcmp(name, "mathv") == 0) return 6;
if (strcmp(name, "halt") == 0) return 0;
if (strcmp(name, "syscall") == 0) return 2;
if (strcmp(name, "tsyscall") == 0) return 3;
return 0xFFFFFFFFFFFFFFFFULL;
}
bool Sag_Scan(FILE* f, Sag_IntermediateProgram* out) {
if (!f || !out) return false;
out->insts = NULL;
out->inst_count = 0;
out->data = NULL;
out->data_count = 0;
out->Consts = NULL;
out->Consts_count = 0;
typedef enum Section { SEC_CODE, SEC_DATA, SEC_CONST } Section;
Section active_sec = SEC_CODE;
Sag_Str* pending_label = NULL;
Sag_Str word;
while (Sag_NextWord(f, &word)) {
// Check for sections
if (strcmp(word.head, ".code:") == 0) {
active_sec = SEC_CODE;
free(word.head);
continue;
}
if (strcmp(word.head, ".data:") == 0) {
active_sec = SEC_DATA;
free(word.head);
continue;
}
if (strcmp(word.head, ".const:") == 0) {
active_sec = SEC_CONST;
free(word.head);
continue;
}
// Check if label definition
if (word.length > 1 && word.head[word.length - 1] == ':') {
word.head[word.length - 1] = '\0';
word.length--;
pending_label = malloc(sizeof(Sag_Str));
*pending_label = word;
continue;
}
if (active_sec == SEC_CODE) {
uint64_t arg_count = get_instruction_arg_count(word.head);
if (arg_count == 0xFFFFFFFFFFFFFFFFULL) {
// Unknown instruction name
printf("Error: Unknown instruction '%s'\n", word.head);
free(word.head);
return false;
}
Sag_IntermediateInst ii;
ii.inst.data = 0;
ii.label = pending_label;
pending_label = NULL; // Consumed
ii.arg_count = arg_count;
ii.args = malloc((arg_count + 1) * sizeof(Sag_Str));
ii.args[0] = word; // First is instruction name
for (uint64_t a = 1; a <= arg_count; ++a) {
if (!Sag_NextWord(f, &ii.args[a])) {
printf("Error: Missing argument for '%s'\n", word.head);
free(ii.args);
return false;
}
}
out->insts = realloc(out->insts, (out->inst_count + 1) * sizeof(Sag_IntermediateInst));
out->insts[out->inst_count] = ii;
out->inst_count++;
} else if (active_sec == SEC_DATA) {
// Read next 2 words (type and value)
Sag_Str type_str, val_str;
if (!Sag_NextWord(f, &type_str) || !Sag_NextWord(f, &val_str)) {
printf("Error: Incomplete data section entry\n");
return false;
}
out->data = realloc(out->data, (out->data_count + 3) * sizeof(Sag_Str));
out->data[out->data_count] = word; // name
out->data[out->data_count + 1] = type_str; // type
out->data[out->data_count + 2] = val_str; // value
out->data_count += 3;
} else if (active_sec == SEC_CONST) {
// Read next 1 word (value)
Sag_Str val_str;
if (!Sag_NextWord(f, &val_str)) {
printf("Error: Incomplete const section entry\n");
return false;
}
out->Consts = realloc(out->Consts, (out->Consts_count + 2) * sizeof(Sag_Str));
out->Consts[out->Consts_count] = word; // name
out->Consts[out->Consts_count + 1] = val_str; // value
out->Consts_count += 2;
}
}
return true;
}
bool Sag_Combine(Sag_IntermediateProgram* L, Sag_IntermediateProgram* R, Sag_IntermediateProgram* out) {
if (!L || !R || !out) return false;
out->inst_count = L->inst_count + R->inst_count;
if (out->inst_count > 0) {
out->insts = malloc(out->inst_count * sizeof(Sag_IntermediateInst));
if (L->inst_count > 0) memcpy(out->insts, L->insts, L->inst_count * sizeof(Sag_IntermediateInst));
if (R->inst_count > 0) memcpy(out->insts + L->inst_count, R->insts, R->inst_count * sizeof(Sag_IntermediateInst));
} else {
out->insts = NULL;
}
out->data_count = L->data_count + R->data_count;
if (out->data_count > 0) {
out->data = malloc(out->data_count * sizeof(Sag_Str));
if (L->data_count > 0) memcpy(out->data, L->data, L->data_count * sizeof(Sag_Str));
if (R->data_count > 0) memcpy(out->data + L->data_count, R->data, R->data_count * sizeof(Sag_Str));
} else {
out->data = NULL;
}
out->Consts_count = L->Consts_count + R->Consts_count;
if (out->Consts_count > 0) {
out->Consts = malloc(out->Consts_count * sizeof(Sag_Str));
if (L->Consts_count > 0) memcpy(out->Consts, L->Consts, L->Consts_count * sizeof(Sag_Str));
if (R->Consts_count > 0) memcpy(out->Consts + L->Consts_count, R->Consts, R->Consts_count * sizeof(Sag_Str));
} else {
out->Consts = NULL;
}
return true;
}
/* Base64 & String parsing helpers */
static inline int b64_char_val(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
return -1;
}
static inline uint8_t* base64_decode(const char* in, size_t* out_len) {
size_t len = strlen(in);
size_t padding = 0;
if (len > 0 && in[len - 1] == '=') padding++;
if (len > 1 && in[len - 2] == '=') padding++;
*out_len = (len * 3) / 4 - padding;
uint8_t* out = malloc(*out_len);
if (!out) return NULL;
size_t j = 0;
uint32_t val = 0;
int valb = -8;
for (size_t i = 0; i < len; ++i) {
char c = in[i];
if (c == '=') break;
int v = b64_char_val(c);
if (v == -1) continue;
val = (val << 6) | v;
valb += 6;
if (valb >= 0) {
out[j++] = (val >> valb) & 0xFF;
valb -= 8;
}
}
return out;
}
static inline uint8_t* parse_string_bytes(const char* in, size_t* out_len) {
size_t len = strlen(in);
size_t start = 0;
size_t end = len;
if (len >= 2 && in[0] == '"' && in[len - 1] == '"') {
start = 1;
end = len - 1;
}
uint8_t* out = malloc(len + 1);
size_t j = 0;
for (size_t i = start; i < end; ++i) {
if (in[i] == '\\' && i + 1 < end) {
i++;
switch (in[i]) {
case 'n': out[j++] = '\n'; break;
case 't': out[j++] = '\t'; break;
case 'r': out[j++] = '\r'; break;
case '\"': out[j++] = '\"'; break;
case '\\': out[j++] = '\\'; break;
default: out[j++] = in[i]; break;
}
} else {
out[j++] = in[i];
}
}
out[j++] = '\0';
*out_len = j;
return out;
}
static inline uint8_t* read_file_bytes(const char* path, size_t* out_len) {
// Strip quotes if path has them
size_t len = strlen(path);
char* clean_path = malloc(len + 1);
size_t start = 0, end = len;
if (len >= 2 && path[0] == '"' && path[len-1] == '"') {
start = 1;
end = len - 1;
}
size_t j = 0;
for (size_t i = start; i < end; ++i) clean_path[j++] = path[i];
clean_path[j] = '\0';
FILE* f = fopen(clean_path, "rb");
free(clean_path);
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long sz = ftell(f);
if (sz < 0) { fclose(f); return NULL; }
fseek(f, 0, SEEK_SET);
uint8_t* out = malloc(sz);
if (out) {
size_t read_bytes = fread(out, 1, sz, f);
*out_len = read_bytes;
}
fclose(f);
return out;
}
typedef struct LabelMap {
const char* name;
uint64_t offset;
} LabelMap;
static inline const char* lookup_const(Sag_IntermediateProgram* prog, const char* name) {
for (uint64_t i = 0; i < prog->Consts_count; i += 2) {
if (strcmp(prog->Consts[i].head, name) == 0) {
return prog->Consts[i+1].head;
}
}
return NULL;
}
static inline bool resolve_token(Sag_IntermediateProgram* prog, LabelMap* labels, size_t label_count, const char* token, int64_t* out_int, double* out_double) {
const char* resolved = lookup_const(prog, token);
if (resolved) {
token = resolved;
}
for (size_t i = 0; i < label_count; ++i) {
if (strcmp(labels[i].name, token) == 0) {
*out_int = labels[i].offset;
*out_double = (double)labels[i].offset;
return true;
}
}
char* end;
if (strncmp(token, "0x", 2) == 0 || strncmp(token, "0X", 2) == 0) {
*out_int = strtoll(token, &end, 16);
*out_double = (double)*out_int;
if (*end == '\0') return true;
}
double d = strtod(token, &end);
if (*end == '\0') {
*out_double = d;
*out_int = (int64_t)d;
return true;
}
return false;
}
static inline sagittarius_type parse_type(const char* name) {
if (strcmp(name, "uint8") == 0) return st_uint8;
if (strcmp(name, "uint16") == 0) return st_uint16;
if (strcmp(name, "uint32") == 0) return st_uint32;
if (strcmp(name, "uint64") == 0) return st_uint64;
if (strcmp(name, "int8") == 0) return st_int8;
if (strcmp(name, "int16") == 0) return st_int16;
if (strcmp(name, "int32") == 0) return st_int32;
if (strcmp(name, "int64") == 0) return st_int64;
if (strcmp(name, "single") == 0 || strcmp(name, "float") == 0) return st_single;
if (strcmp(name, "double") == 0) return st_double;
return st_uint8;
}
static inline uint8_t parse_math2_op(const char* name) {
if (strcmp(name, "add") == 0) return sag_math2_add;
if (strcmp(name, "sub") == 0) return sag_math2_sub;
if (strcmp(name, "mul") == 0) return sag_math2_mul;
if (strcmp(name, "div") == 0) return sag_math2_div;
if (strcmp(name, "mod") == 0) return sag_math2_mod;
if (strcmp(name, "pow") == 0) return sag_math2_pow;
return 0;
}
static inline uint8_t parse_math1_op(const char* name) {
if (strcmp(name, "sin") == 0) return sag_math1_sin;
if (strcmp(name, "cos") == 0) return sag_math1_cos;
if (strcmp(name, "tan") == 0) return sag_math1_tan;
if (strcmp(name, "sinh") == 0) return sag_math1_sinh;
if (strcmp(name, "cosh") == 0) return sag_math1_cosh;
if (strcmp(name, "tanh") == 0) return sag_math1_tanh;
if (strcmp(name, "asin") == 0) return sag_math1_asin;
if (strcmp(name, "acos") == 0) return sag_math1_acos;
if (strcmp(name, "atan") == 0) return sag_math1_atan;
if (strcmp(name, "abs") == 0) return sag_math1_abs;
if (strcmp(name, "exp") == 0) return sag_math1_exp;
return 0;
}
bool Sag_Finalize(Sag_IntermediateProgram* intermediate, SagittariusProgram* out) {
if (!intermediate || !out) return false;
// Pass 1: Calculate Code Labels & instruction sizes
size_t label_capacity = 32;
size_t label_count = 0;
LabelMap* labels = malloc(label_capacity * sizeof(LabelMap));
uint64_t byte_offset = 0;
for (uint64_t i = 0; i < intermediate->inst_count; ++i) {
Sag_IntermediateInst* ii = &intermediate->insts[i];
if (ii->label) {
if (label_count >= label_capacity) {
label_capacity *= 2;
labels = realloc(labels, label_capacity * sizeof(LabelMap));
}
labels[label_count].name = ii->label->head;
labels[label_count].offset = byte_offset;
label_count++;
}
// Calculate instruction size
const char* name = ii->args[0].head;
uint64_t sz = 8;
if (strcmp(name, "set") == 0 || strncmp(name, "set.", 4) == 0) {
sz = 16;
} else if (strcmp(name, "call") == 0) {
// Immediate mode if third argument is "1"
if (ii->arg_count >= 2 && strcmp(ii->args[2].head, "1") == 0) {
sz = 16;
}
}
byte_offset += sz;
}
uint64_t total_inst_bytes = byte_offset;
out->instCount = total_inst_bytes / 8;
out->instructions = malloc(out->instCount * sizeof(SagittariusInst));
memset(out->instructions, 0, out->instCount * sizeof(SagittariusInst));
// Pass 2: Resolve Data Section & Data Labels
uint64_t data_capacity = 1024;
uint64_t data_size = 0;
uint8_t* data_buf = malloc(data_capacity);
for (uint64_t i = 0; i < intermediate->data_count; i += 3) {
const char* d_name = intermediate->data[i].head;
const char* d_type = intermediate->data[i+1].head;
const char* d_val = intermediate->data[i+2].head;
// Record label offset relative to program start (which is loaded at 0 in final VM space)
if (label_count >= label_capacity) {
label_capacity *= 2;
labels = realloc(labels, label_capacity * sizeof(LabelMap));
}
labels[label_count].name = d_name;
labels[label_count].offset = total_inst_bytes + data_size;
label_count++;
size_t entry_len = 0;
uint8_t* entry_bytes = NULL;
if (strcmp(d_type, "string") == 0) {
entry_bytes = parse_string_bytes(d_val, &entry_len);
} else if (strcmp(d_type, "base64") == 0) {
entry_bytes = base64_decode(d_val, &entry_len);
} else if (strcmp(d_type, "file") == 0) {
entry_bytes = read_file_bytes(d_val, &entry_len);
}
if (entry_bytes && entry_len > 0) {
if (data_size + entry_len > data_capacity) {
while (data_size + entry_len > data_capacity) data_capacity *= 2;
data_buf = realloc(data_buf, data_capacity);
}
memcpy(data_buf + data_size, entry_bytes, entry_len);
data_size += entry_len;
free(entry_bytes);
}
}
out->data_size = data_size;
if (data_size > 0) {
out->data = malloc(data_size);
memcpy(out->data, data_buf, data_size);
} else {
out->data = NULL;
}
free(data_buf);
// Pass 3: Assemble & Encode instructions
uint64_t final_inst_idx = 0;
for (uint64_t i = 0; i < intermediate->inst_count; ++i) {
Sag_IntermediateInst* ii = &intermediate->insts[i];
const char* name = ii->args[0].head;
uint64_t current_inst_offset = final_inst_idx * 8;
SagittariusInst main_inst;
main_inst.data = 0;
if (strcmp(name, "add") == 0 || strcmp(name, "sub") == 0 || strcmp(name, "mul") == 0 ||
strcmp(name, "div") == 0 || strcmp(name, "mod") == 0 || strcmp(name, "pow") == 0) {
uint8_t opcode = math2;
sagittarius_type type = parse_type(ii->args[1].head);
uint8_t op = parse_math2_op(name);
int64_t L = 0, R = 0, Dest = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[2].head, &L, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &R, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[4].head, &Dest, &dummy);
main_inst.data = opcode | ((uint64_t)type << 8) | ((uint64_t)op << 16) |
((uint64_t)(L & 0xFF) << 24) | ((uint64_t)(R & 0xFF) << 32) |
((uint64_t)(Dest & 0xFF) << 40);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "sin") == 0 || strcmp(name, "cos") == 0 || strcmp(name, "tan") == 0 ||
strcmp(name, "sinh") == 0 || strcmp(name, "cosh") == 0 || strcmp(name, "tanh") == 0 ||
strcmp(name, "asin") == 0 || strcmp(name, "acos") == 0 || strcmp(name, "atan") == 0 ||
strcmp(name, "abs") == 0 || strcmp(name, "exp") == 0) {
uint8_t opcode = math1;
sagittarius_type type = parse_type(ii->args[1].head);
uint8_t op = parse_math1_op(name);
int64_t L = 0, Dest = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[2].head, &L, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &Dest, &dummy);
main_inst.data = opcode | ((uint64_t)type << 8) | ((uint64_t)op << 16) |
((uint64_t)(L & 0xFF) << 24) | ((uint64_t)(Dest & 0xFF) << 32);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "cvt") == 0) {
uint8_t opcode = cvt;
int64_t src_reg = 0, dst_reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &src_reg, &dummy);
sagittarius_type src_type = parse_type(ii->args[2].head);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &dst_reg, &dummy);
sagittarius_type dst_type = parse_type(ii->args[4].head);
main_inst.data = opcode | ((uint64_t)(src_reg & 0xFF) << 8) | ((uint64_t)src_type << 16) |
((uint64_t)(dst_reg & 0xFF) << 24) | ((uint64_t)dst_type << 32);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "set") == 0) {
uint8_t opcode = set;
int64_t length = 0, reg = 0, val_int = 0;
double val_double = 0;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &length, &val_double);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &reg, &val_double);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &val_int, &val_double);
main_inst.data = opcode | ((uint64_t)(length & 0xFF) << 8) | ((uint64_t)(reg & 0xFF) << 16);
out->instructions[final_inst_idx++] = main_inst;
SagittariusInst payload_inst;
payload_inst.data = (uint64_t)val_int;
out->instructions[final_inst_idx++] = payload_inst;
} else if (strncmp(name, "set.", 4) == 0) {
uint8_t opcode = set;
sagittarius_type type = parse_type(name + 4);
uint8_t length = 8;
switch (type) {
case st_uint8: case st_int8: length = 1; break;
case st_uint16: case st_int16: length = 2; break;
case st_uint32: case st_int32: case st_single: length = 4; break;
default: length = 8; break;
}
int64_t reg = 0, val_int = 0;
double val_double = 0;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &reg, &val_double);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &val_int, &val_double);
main_inst.data = opcode | ((uint64_t)length << 8) | ((uint64_t)(reg & 0xFF) << 16);
out->instructions[final_inst_idx++] = main_inst;
SagittariusInst payload_inst;
payload_inst.data = 0;
if (type == st_single) {
float f = (float)val_double;
uint32_t u32;
memcpy(&u32, &f, 4);
payload_inst.data = u32;
} else if (type == st_double) {
double d = val_double;
uint64_t u64;
memcpy(&u64, &d, 8);
payload_inst.data = u64;
} else {
payload_inst.data = (uint64_t)val_int;
}
out->instructions[final_inst_idx++] = payload_inst;
} else if (strcmp(name, "mv") == 0 || strcmp(name, "cp") == 0) {
uint8_t opcode = (strcmp(name, "mv") == 0) ? mv : cp;
int64_t length = 0, src_reg = 0, dst_reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &length, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &src_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &dst_reg, &dummy);
main_inst.data = opcode | ((uint64_t)(length & 0xFF) << 8) |
((uint64_t)(src_reg & 0xFF) << 16) | ((uint64_t)(dst_reg & 0xFF) << 24);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "save") == 0) {
uint8_t opcode = save;
int64_t length = 0, src_reg = 0, dest_mem_ptr_reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &length, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &src_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &dest_mem_ptr_reg, &dummy);
main_inst.data = opcode | ((uint64_t)(length & 0xFF) << 8) |
((uint64_t)(src_reg & 0xFF) << 16) | ((uint64_t)(dest_mem_ptr_reg & 0xFF) << 24);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "load") == 0) {
uint8_t opcode = load;
int64_t length = 0, dest_reg = 0, src_mem_ptr_reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &length, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &dest_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &src_mem_ptr_reg, &dummy);
main_inst.data = opcode | ((uint64_t)(length & 0xFF) << 8) |
((uint64_t)(dest_reg & 0xFF) << 16) | ((uint64_t)(src_mem_ptr_reg & 0xFF) << 24);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "jmp") == 0) {
uint8_t opcode = jmp;
int64_t mode = 0, val_mode = 0, target_val = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &mode, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &val_mode, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &target_val, &dummy);
if (val_mode == 1 && mode == 1) {
// Relative immediate: adjust target label to be relative offset
target_val = target_val - current_inst_offset;
}
main_inst.data = opcode | ((uint64_t)(mode & 0xFF) << 8) | ((uint64_t)(val_mode & 0xFF) << 16);
if (val_mode == 0) {
main_inst.data |= ((uint64_t)(target_val & 0xFF) << 24);
} else {
main_inst.data |= ((uint64_t)(uint32_t)target_val << 24);
}
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "jmp_if") == 0) {
uint8_t opcode = jmp_if;
int64_t mode = 0, val_mode = 0, flag_reg = 0, target_val = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &mode, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &val_mode, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &flag_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[4].head, &target_val, &dummy);
if (val_mode == 1 && mode == 1) {
target_val = target_val - current_inst_offset;
}
main_inst.data = opcode | ((uint64_t)(mode & 0xFF) << 8) | ((uint64_t)(val_mode & 0xFF) << 16) |
((uint64_t)(flag_reg & 0xFF) << 24);
if (val_mode == 0) {
main_inst.data |= ((uint64_t)(target_val & 0xFF) << 32);
} else {
main_inst.data |= ((uint64_t)(uint32_t)target_val << 32);
}
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "call") == 0) {
uint8_t opcode = call;
int64_t mode = 0, val_mode = 0, reg_to_rem = 0, target_val = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &mode, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &val_mode, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &reg_to_rem, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[4].head, &target_val, &dummy);
if (val_mode == 0) {
main_inst.data = opcode | ((uint64_t)(mode & 0xFF) << 8) | ((uint64_t)(val_mode & 0xFF) << 16) |
((uint64_t)(reg_to_rem & 0xFF) << 24) | ((uint64_t)(target_val & 0xFF) << 32);
out->instructions[final_inst_idx++] = main_inst;
} else {
if (mode == 1) {
target_val = target_val - current_inst_offset;
}
main_inst.data = opcode | ((uint64_t)(mode & 0xFF) << 8) | ((uint64_t)(val_mode & 0xFF) << 16) |
((uint64_t)(reg_to_rem & 0xFF) << 24);
out->instructions[final_inst_idx++] = main_inst;
SagittariusInst payload_inst;
payload_inst.data = (uint64_t)target_val;
out->instructions[final_inst_idx++] = payload_inst;
}
} else if (strcmp(name, "ret") == 0) {
uint8_t opcode = ret;
int64_t reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &reg, &dummy);
main_inst.data = opcode | ((uint64_t)(reg & 0xFF) << 8);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "cmp") == 0) {
uint8_t opcode = cmp;
sagittarius_type type = parse_type(ii->args[1].head);
uint8_t op_val = 0;
const char* op_name = ii->args[2].head;
if (strcmp(op_name, "eq") == 0) op_val = 0;
else if (strcmp(op_name, "ne") == 0) op_val = 1;
else if (strcmp(op_name, "lt") == 0) op_val = 2;
else if (strcmp(op_name, "le") == 0) op_val = 3;
else if (strcmp(op_name, "gt") == 0) op_val = 4;
else if (strcmp(op_name, "ge") == 0) op_val = 5;
int64_t L = 0, R = 0, Dest = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[3].head, &L, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[4].head, &R, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[5].head, &Dest, &dummy);
main_inst.data = opcode | ((uint64_t)type << 8) | ((uint64_t)op_val << 16) |
((uint64_t)(L & 0xFF) << 24) | ((uint64_t)(R & 0xFF) << 32) |
((uint64_t)(Dest & 0xFF) << 40);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "mathv") == 0) {
uint8_t opcode = mathv;
int64_t w = 0, h = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &w, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &h, &dummy);
uint8_t op_val = 0;
const char* op_name = ii->args[3].head;
if (strcmp(op_name, "add") == 0) op_val = 0;
else if (strcmp(op_name, "sub") == 0) op_val = 1;
else if (strcmp(op_name, "mul") == 0) op_val = 2;
else if (strcmp(op_name, "div") == 0) op_val = 3;
int64_t L_ptr = 0, R_ptr = 0, Dest_ptr = 0;
resolve_token(intermediate, labels, label_count, ii->args[4].head, &L_ptr, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[5].head, &R_ptr, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[6].head, &Dest_ptr, &dummy);
main_inst.data = opcode | ((uint64_t)(w & 0xFF) << 8) | ((uint64_t)(h & 0xFF) << 16) |
((uint64_t)op_val << 24) | ((uint64_t)(L_ptr & 0xFF) << 32) |
((uint64_t)(R_ptr & 0xFF) << 40) | ((uint64_t)(Dest_ptr & 0xFF) << 48);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "halt") == 0) {
main_inst.data = halt;
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "syscall") == 0) {
uint8_t opcode = syscall;
int64_t ns_reg = 0, func_reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &ns_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &func_reg, &dummy);
main_inst.data = opcode | ((uint64_t)(ns_reg & 0xFF) << 8) | ((uint64_t)(func_reg & 0xFF) << 16);
out->instructions[final_inst_idx++] = main_inst;
} else if (strcmp(name, "tsyscall") == 0) {
uint8_t opcode = tsyscall;
int64_t ns_reg = 0, func_reg = 0, result_reg = 0;
double dummy;
resolve_token(intermediate, labels, label_count, ii->args[1].head, &ns_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[2].head, &func_reg, &dummy);
resolve_token(intermediate, labels, label_count, ii->args[3].head, &result_reg, &dummy);
main_inst.data = opcode | ((uint64_t)(ns_reg & 0xFF) << 8) | ((uint64_t)(func_reg & 0xFF) << 16) |
((uint64_t)(result_reg & 0xFF) << 24);
out->instructions[final_inst_idx++] = main_inst;
}
}
free(labels);
return true;
}
bool Sag_WriteProgram(FILE* f, SagittariusProgram* program) {
if (!f || !program) return false;
if (fwrite(SAGITTARUIS_PROGRAM_HEAD, 1, 8, f) != 8) return false;
uint32_t version = SAGITTARIUS_PROGRAM_FORMAT_VERSION;
if (fwrite(&version, 4, 1, f) != 1) return false;
if (fwrite(&program->instCount, 8, 1, f) != 1) return false;
if (fwrite(&program->data_size, 8, 1, f) != 1) return false;
if (program->instCount > 0) {
if (fwrite(program->instructions, sizeof(SagittariusInst), program->instCount, f) != program->instCount) {
return false;
}
}
if (program->data_size > 0) {
if (fwrite(program->data, 1, program->data_size, f) != program->data_size) {
return false;
}
}
return true;
}
+146
View File
@@ -0,0 +1,146 @@
#include "../../Headers/Assembler/SagittariusAssembler.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int ac, char** av) {
printf("Copyright (C) 2026 Creeper Lv.\n");
if (ac < 2) {
printf("Usage: SagittariusAssembler <input_file_0> <input_file_1> ... [-o <output_file>]\n");
return 1;
}
char* output_file = NULL;
char** input_files = malloc(ac * sizeof(char*));
int input_count = 0;
for (int i = 1; i < ac; ++i) {
if (strcmp(av[i], "-o") == 0) {
if (i + 1 < ac) {
output_file = av[i+1];
i++;
} else {
printf("Error: Missing output file after -o\n");
free(input_files);
return 1;
}
} else {
input_files[input_count++] = av[i];
}
}
if (input_count == 0) {
printf("Error: No input files specified\n");
free(input_files);
return 1;
}
char* dynamic_out = NULL;
if (!output_file) {
size_t len = strlen(input_files[0]);
dynamic_out = malloc(len + 5);
sprintf(dynamic_out, "%s.out", input_files[0]);
output_file = dynamic_out;
}
printf("Assembling %d file(s) into '%s'...\n", input_count, output_file);
Sag_IntermediateProgram combined;
memset(&combined, 0, sizeof(combined));
for (int i = 0; i < input_count; ++i) {
FILE* f = fopen(input_files[i], "r");
if (!f) {
printf("Error: Cannot open input file '%s'\n", input_files[i]);
free(input_files);
if (dynamic_out) free(dynamic_out);
return 1;
}
Sag_IntermediateProgram ip;
memset(&ip, 0, sizeof(ip));
if (!Sag_Scan(f, &ip)) {
printf("Error scanning '%s'\n", input_files[i]);
fclose(f);
free(input_files);
if (dynamic_out) free(dynamic_out);
return 1;
}
fclose(f);
if (i == 0) {
combined = ip;
} else {
Sag_IntermediateProgram temp;
memset(&temp, 0, sizeof(temp));
if (!Sag_Combine(&combined, &ip, &temp)) {
printf("Error combining intermediate programs\n");
free(input_files);
if (dynamic_out) free(dynamic_out);
return 1;
}
free(combined.insts);
free(combined.data);
free(combined.Consts);
free(ip.insts);
free(ip.data);
free(ip.Consts);
combined = temp;
}
}
// Finalize
SagittariusProgram prog;
memset(&prog, 0, sizeof(prog));
if (!Sag_Finalize(&combined, &prog)) {
printf("Error: Finalization failed!\n");
free(combined.insts);
free(combined.data);
free(combined.Consts);
free(input_files);
if (dynamic_out) free(dynamic_out);
return 1;
}
// Write output
FILE* out_f = fopen(output_file, "wb");
if (!out_f) {
printf("Error: Cannot open output file '%s' for writing\n", output_file);
free(combined.insts);
free(combined.data);
free(combined.Consts);
free(prog.instructions);
if (prog.data) free(prog.data);
free(input_files);
if (dynamic_out) free(dynamic_out);
return 1;
}
if (!Sag_WriteProgram(out_f, &prog)) {
printf("Error writing to output file '%s'\n", output_file);
fclose(out_f);
free(combined.insts);
free(combined.data);
free(combined.Consts);
free(prog.instructions);
if (prog.data) free(prog.data);
free(input_files);
if (dynamic_out) free(dynamic_out);
return 1;
}
fclose(out_f);
printf("Successfully assembled! Instruction count: %llu, Data size: %llu bytes\n",
(unsigned long long)prog.instCount, (unsigned long long)prog.data_size);
// Cleanup
free(combined.insts);
free(combined.data);
free(combined.Consts);
free(prog.instructions);
if (prog.data) free(prog.data);
free(input_files);
if (dynamic_out) free(dynamic_out);
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
#include "../../Headers/SagittariusC/SagittariusC.h"
/**
* Usage:
* SagittariusC [options] <input_file_0> <input_file_1> ... [options]
* Options:
* -I <include_path>: Add an include path for resolving #include directives in Sagittarius C source files. Can be specified multiple times for multiple include paths.
* -o <output_file>: Specify the output file for the compiled Sagittarius program. If not provided, the output file will be named after the first input file with a .out extension.
* -bt only build the tree, no tree finalization or code generation will be performed, and the tree will be written to the output file.
* -f start from finalize only, all input files should be tree files generated by the -bt option, and the compiler will perform tree finalization. If -bt is used together with -f, the compiler will poutput the finalized tree.
* -s only generate assembly code skipping assembler stage.
* -assembler <assembler_path>: Specify the path to the assembler executable for assembling generated assembly code into a Sagittarius program. If not provided, the compiler will look for an assembler named "SagittariusAssembler" in the system PATH.
*/
int main(int ac, char** av) {
return 0;
}
+91 -1
View File
@@ -1,4 +1,94 @@
int main(int ac,char** av){
#include "../../Headers/Sagittarius.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int32_t syscall_print(SagittariusCore* core){
uint64_t str_offset = 0;
memcpy(&str_offset, &core->reg.head[40], 8);
if (str_offset >= core->memory->size) {
printf("[Syscall Print Error: String offset 0x%llx is OOB]\n", (unsigned long long)str_offset);
return -1;
}
printf("%s", (char*)(core->memory->data + str_offset));
return 0;
}
int32_t my_panic_handler(SagittariusCore *core, uint64_t panic_id, const char *msg) {
printf("\n--- VM PANIC! ID: 0x%llx, PC: 0x%llx ---\n", (unsigned long long)panic_id, (unsigned long long)core->pc);
printf("Message: %s\n", msg);
exit(1);
return 0;
}
int main(int ac, char** av) {
if (ac < 2) {
printf("Usage: Sagittarius <compiled_program_file>\n");
return 1;
}
char* filepath = av[1];
FILE* f = fopen(filepath, "rb");
if (!f) {
printf("Error: Cannot open file '%s'\n", filepath);
return 1;
}
fseek(f, 0, SEEK_END);
long sz = ftell(f);
if (sz < 0) { fclose(f); return 1; }
fseek(f, 0, SEEK_SET);
uint8_t* byte_buf = malloc(sz);
if (!byte_buf) {
fclose(f);
return 1;
}
size_t read_bytes = fread(byte_buf, 1, sz, f);
fclose(f);
SagittariusProgram* prog = sagittarius_load_program_from_byte_array(byte_buf, read_bytes);
free(byte_buf);
if (!prog) {
printf("Error: Failed to load program from '%s'\n", filepath);
return 1;
}
// Create VM with 1 MB memory
SagittariusVM* vm = sagittarius_vm_new(1024 * 1024);
vm->panic_handler = my_panic_handler;
// Register print syscall (namespace 0, function 0 -> ID 0)
sagittarius_register_syscall(vm, 0, syscall_print);
// Load program at offset 0
sagittarius_load_program_to_mem(vm, prog, 0);
// Run VM
while (1) {
if (vm->core.pc >= vm->Memory.size) {
printf("Error: PC out of bounds!\n");
break;
}
SagittariusInst inst;
memcpy(&inst, &vm->Memory.data[vm->core.pc], sizeof(SagittariusInst));
uint8_t opcode = inst.data & 0xFF;
if (opcode == 14) { // halt opcode
break;
}
sagittarius_step(vm);
}
// Cleanup
sagittarius_vm_free(vm);
free(prog->instructions);
if (prog->data) free(prog->data);
free(prog);
return 0;
}
+139 -9
View File
@@ -1,23 +1,153 @@
#include "../../Headers/Sagittarius.h"
#include "../../Headers/SagittariusInternal.h"
#include <stdlib.h>
#include <string.h>
SAGITTARIUS_API SagittariusVM* sagittarius_vm_new(uint64_t memory_size){
SagittariusVM* vm=malloc(sizeof(SagittariusVM));
vm->Memory.data=malloc(memory_size);
vm->Memory.size=memory_size;
SagittariusVM* vm = malloc(sizeof(SagittariusVM));
if (!vm) return NULL;
vm->Memory.data = malloc(memory_size);
vm->Memory.size = memory_size;
// Clear registers
memset(vm->core.reg.head, 0, sizeof(vm->core.reg.head));
// Link core
vm->core.memory = &vm->Memory;
vm->core.vm = vm;
vm->core.pc = 0;
// Initialize syscalls
vm->SagittariusSyscallEntries = NULL;
vm->SagittariusSyscallCount = 0;
vm->SagittariusSyscallCapacity = 0;
vm->panic_handler = NULL;
return vm;
}
SAGITTARIUS_API void sagittarius_vm_free(SagittariusVM* vm){
free(vm);
if (vm) {
if (vm->Memory.data) free(vm->Memory.data);
if (vm->SagittariusSyscallEntries) free(vm->SagittariusSyscallEntries);
free(vm);
}
}
SAGITTARIUS_API void sagittarius_step(SagittariusVM* vm){
SAGITTARIUS_API void sagittarius_register_syscall(SagittariusVM *vm, uint64_t id, SagittariusSyscall syscall) {
if (!vm) return;
if (vm->SagittariusSyscallCount >= vm->SagittariusSyscallCapacity) {
vm->SagittariusSyscallCapacity = vm->SagittariusSyscallCapacity == 0 ? 4 : vm->SagittariusSyscallCapacity * 2;
vm->SagittariusSyscallEntries = realloc(vm->SagittariusSyscallEntries, vm->SagittariusSyscallCapacity * sizeof(SagittariusSyscallEntry));
}
vm->SagittariusSyscallEntries[vm->SagittariusSyscallCount].id = id;
vm->SagittariusSyscallEntries[vm->SagittariusSyscallCount].syscall = syscall;
vm->SagittariusSyscallCount++;
}
SAGITTARIUS_API void sagittarius_mem_resize(SagittariusVM* vm, uint64_t new_size){
vm->Memory.data=realloc(vm->Memory.data,new_size);
vm->Memory.size=new_size;
if (vm) {
vm->Memory.data = realloc(vm->Memory.data, new_size);
vm->Memory.size = new_size;
}
}
SAGITTARIUS_API uint64_t sagittarius_mem_getsize(SagittariusVM* vm){
return vm->Memory.size;
return vm ? vm->Memory.size : 0;
}
SAGITTARIUS_API void sagittarius_load_program_to_mem(SagittariusVM *vm, SagittariusProgram *program, uint64_t offset) {
if (!vm || !program) return;
uint64_t required_size = offset + program->instCount * 8 + program->data_size;
if (required_size > vm->Memory.size) {
sagittarius_mem_resize(vm, required_size);
}
if (program->instCount > 0) {
memcpy(vm->Memory.data + offset, program->instructions, program->instCount * 8);
}
if (program->data_size > 0) {
memcpy(vm->Memory.data + offset + program->instCount * 8, program->data, program->data_size);
}
vm->core.pc = offset;
}
SAGITTARIUS_API SagittariusProgram *sagittarius_load_program_from_byte_array(uint8_t *data, uint64_t size) {
if (size < 28) return NULL;
if (memcmp(data, SAGITTARUIS_PROGRAM_HEAD, 7) != 0) return NULL;
uint32_t version;
memcpy(&version, data + 8, 4);
if (version != SAGITTARIUS_PROGRAM_FORMAT_VERSION) return NULL;
uint64_t instCount;
memcpy(&instCount, data + 12, 8);
uint64_t data_size;
memcpy(&data_size, data + 20, 8);
if (size < 28 + instCount * 8 + data_size) return NULL;
SagittariusProgram *prog = malloc(sizeof(SagittariusProgram));
prog->instCount = instCount;
if (instCount > 0) {
prog->instructions = malloc(instCount * sizeof(SagittariusInst));
memcpy(prog->instructions, data + 28, instCount * 8);
} else {
prog->instructions = NULL;
}
prog->data_size = data_size;
if (data_size > 0) {
prog->data = malloc(data_size);
memcpy(prog->data, data + 28 + instCount * 8, data_size);
} else {
prog->data = NULL;
}
return prog;
}
SAGITTARIUS_API void sagittarius_step(SagittariusVM *vm) {
if (!vm) return;
if (vm->core.pc >= vm->Memory.size) {
if (vm->panic_handler) vm->panic_handler(&vm->core, Sagittarius_Msg_OOB, "PC out of bounds");
return;
}
SagittariusInst inst;
memcpy(&inst, &vm->Memory.data[vm->core.pc], sizeof(SagittariusInst));
uint8_t opcode = inst.data & 0xFF;
bool success = false;
switch ((SagittariusInstDef)opcode) {
case math2: success = Math2Op(&vm->core, inst); break;
case math1: success = Math1Op(&vm->core, inst); break;
case cvt: success = SagCvt(&vm->core, inst); break;
case set: success = SagSet(&vm->core, inst); break;
case mv: success = SagMv(&vm->core, inst); break;
case cp: success = SagCp(&vm->core, inst); break;
case save: success = SagSave(&vm->core, inst); break;
case load: success = SagLoad(&vm->core, inst); break;
case jmp: success = SagJmp(&vm->core, inst); break;
case jmp_if: success = SagJmpIf(&vm->core, inst); break;
case call: success = SagCall(&vm->core, inst); break;
case ret: success = SagRet(&vm->core, inst); break;
case cmp: success = SagCmp(&vm->core, inst); break;
case mathv: success = SagMathV(&vm->core, inst); break;
case halt: success = SagHalt(&vm->core, inst); break;
case syscall: success = SagSyscall(&vm->core, inst); break;
case tsyscall: success = SagTSyscall(&vm->core, inst); break;
default: {
if (vm->panic_handler) vm->panic_handler(&vm->core, Sagittarius_Msg_Unknown, "Unknown opcode");
break;
}
}
(void)success;
}
+604
View File
@@ -0,0 +1,604 @@
#include "../../Headers/SagittariusInternal.h"
#include "../../Headers/SagittariusPanic.h"
#include <stdbool.h>
#include <string.h>
#include <math.h>
/* Safe register read/write helpers */
static inline void reg_read(SagittariusCore *core, uint8_t reg_idx, void *dest, sagittarius_type t) {
size_t sz = 0;
switch (t) {
case st_uint8: case st_int8: sz = 1; break;
case st_uint16: case st_int16: sz = 2; break;
case st_uint32: case st_int32: case st_single: sz = 4; break;
case st_uint64: case st_int64: case st_double: sz = 8; break;
}
memcpy(dest, &core->reg.head[reg_idx], sz);
}
static inline void reg_write(SagittariusCore *core, uint8_t reg_idx, const void *src, sagittarius_type t) {
size_t sz = 0;
switch (t) {
case st_uint8: case st_int8: sz = 1; break;
case st_uint16: case st_int16: sz = 2; break;
case st_uint32: case st_int32: case st_single: sz = 4; break;
case st_uint64: case st_int64: case st_double: sz = 8; break;
}
memcpy(&core->reg.head[reg_idx], src, sz);
}
/* Macro for math2 (add, sub, mul) */
#define MATH2_OP_IMPL(name, op) \
internal bool SagMath2##name(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T) { \
switch (t) { \
case st_uint8: { uint8_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_uint16: { uint16_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_uint32: { uint32_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_uint64: { uint64_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_int8: { int8_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_int16: { int16_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_int32: { int32_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_int64: { int64_t l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_single: { float l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
case st_double: { double l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = l op r; reg_write(core, T, &res, t); break; } \
default: return false; \
} \
return true; \
}
MATH2_OP_IMPL(Add, +)
MATH2_OP_IMPL(Sub, -)
MATH2_OP_IMPL(Mul, *)
#define DIV_CASE(st_type, c_type) \
case st_type: { \
c_type l, r, res; \
reg_read(core, L, &l, t); \
reg_read(core, R, &r, t); \
if (r == 0) { \
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_Generic, "Division by zero"); \
return false; \
} \
res = l / r; \
reg_write(core, T, &res, t); \
break; \
}
#define DIV_FLOAT_CASE(st_type, c_type) \
case st_type: { \
c_type l, r, res; \
reg_read(core, L, &l, t); \
reg_read(core, R, &r, t); \
res = l / r; \
reg_write(core, T, &res, t); \
break; \
}
internal bool SagMath2Div(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T) {
switch (t) {
DIV_CASE(st_uint8, uint8_t)
DIV_CASE(st_uint16, uint16_t)
DIV_CASE(st_uint32, uint32_t)
DIV_CASE(st_uint64, uint64_t)
DIV_CASE(st_int8, int8_t)
DIV_CASE(st_int16, int16_t)
DIV_CASE(st_int32, int32_t)
DIV_CASE(st_int64, int64_t)
DIV_FLOAT_CASE(st_single, float)
DIV_FLOAT_CASE(st_double, double)
default: return false;
}
return true;
}
#define MOD_CASE(st_type, c_type) \
case st_type: { \
c_type l, r, res; \
reg_read(core, L, &l, t); \
reg_read(core, R, &r, t); \
if (r == 0) { \
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_Generic, "Modulo by zero"); \
return false; \
} \
res = l % r; \
reg_write(core, T, &res, t); \
break; \
}
internal bool SagMath2Mod(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T) {
switch (t) {
MOD_CASE(st_uint8, uint8_t)
MOD_CASE(st_uint16, uint16_t)
MOD_CASE(st_uint32, uint32_t)
MOD_CASE(st_uint64, uint64_t)
MOD_CASE(st_int8, int8_t)
MOD_CASE(st_int16, int16_t)
MOD_CASE(st_int32, int32_t)
MOD_CASE(st_int64, int64_t)
case st_single: { float l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = fmodf(l, r); reg_write(core, T, &res, t); break; }
case st_double: { double l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = fmod(l, r); reg_write(core, T, &res, t); break; }
default: return false;
}
return true;
}
#define POW_CASE(st_type, c_type) \
case st_type: { \
c_type l, r, res; \
reg_read(core, L, &l, t); \
reg_read(core, R, &r, t); \
res = (c_type)pow((double)l, (double)r); \
reg_write(core, T, &res, t); \
break; \
}
internal bool SagMath2Pow(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t R, uint8_t T) {
switch (t) {
POW_CASE(st_uint8, uint8_t)
POW_CASE(st_uint16, uint16_t)
POW_CASE(st_uint32, uint32_t)
POW_CASE(st_uint64, uint64_t)
POW_CASE(st_int8, int8_t)
POW_CASE(st_int16, int16_t)
POW_CASE(st_int32, int32_t)
POW_CASE(st_int64, int64_t)
case st_single: { float l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = powf(l, r); reg_write(core, T, &res, t); break; }
case st_double: { double l, r, res; reg_read(core, L, &l, t); reg_read(core, R, &r, t); res = pow(l, r); reg_write(core, T, &res, t); break; }
default: return false;
}
return true;
}
internal bool Math2Op(SagittariusCore *core, SagittariusInst inst) {
uint8_t t = (inst.data >> 8) & 0xFF;
uint8_t op = (inst.data >> 16) & 0xFF;
uint8_t L = (inst.data >> 24) & 0xFF;
uint8_t R = (inst.data >> 32) & 0xFF;
uint8_t T = (inst.data >> 40) & 0xFF;
bool status = false;
switch (op) {
case sag_math2_add: status = SagMath2Add(core, (sagittarius_type)t, L, R, T); break;
case sag_math2_sub: status = SagMath2Sub(core, (sagittarius_type)t, L, R, T); break;
case sag_math2_mul: status = SagMath2Mul(core, (sagittarius_type)t, L, R, T); break;
case sag_math2_div: status = SagMath2Div(core, (sagittarius_type)t, L, R, T); break;
case sag_math2_mod: status = SagMath2Mod(core, (sagittarius_type)t, L, R, T); break;
case sag_math2_pow: status = SagMath2Pow(core, (sagittarius_type)t, L, R, T); break;
default: return false;
}
if (status) {
core->pc += 8;
}
return status;
}
/* Math1 unary operators helper */
static inline double my_abs(double v) { return v < 0 ? -v : v; }
static inline float my_absf(float v) { return v < 0 ? -v : v; }
#define MATH1_OP_IMPL(name, func, float_func) \
internal bool SagMath1##name(SagittariusCore *core, sagittarius_type t, uint8_t L, uint8_t T) { \
switch (t) { \
case st_uint8: { uint8_t l, res; reg_read(core, L, &l, t); res = (uint8_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_uint16: { uint16_t l, res; reg_read(core, L, &l, t); res = (uint16_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_uint32: { uint32_t l, res; reg_read(core, L, &l, t); res = (uint32_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_uint64: { uint64_t l, res; reg_read(core, L, &l, t); res = (uint64_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_int8: { int8_t l, res; reg_read(core, L, &l, t); res = (int8_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_int16: { int16_t l, res; reg_read(core, L, &l, t); res = (int16_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_int32: { int32_t l, res; reg_read(core, L, &l, t); res = (int32_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_int64: { int64_t l, res; reg_read(core, L, &l, t); res = (int64_t)func((double)l); reg_write(core, T, &res, t); break; } \
case st_single: { float l, res; reg_read(core, L, &l, t); res = float_func(l); reg_write(core, T, &res, t); break; } \
case st_double: { double l, res; reg_read(core, L, &l, t); res = func(l); reg_write(core, T, &res, t); break; } \
default: return false; \
} \
return true; \
}
MATH1_OP_IMPL(Sin, sin, sinf)
MATH1_OP_IMPL(Cos, cos, cosf)
MATH1_OP_IMPL(Tan, tan, tanf)
MATH1_OP_IMPL(Sinh, sinh, sinhf)
MATH1_OP_IMPL(Cosh, cosh, coshf)
MATH1_OP_IMPL(Tanh, tanh, tanhf)
MATH1_OP_IMPL(Asin, asin, asinf)
MATH1_OP_IMPL(Acos, acos, acosf)
MATH1_OP_IMPL(Atan, atan, atanf)
MATH1_OP_IMPL(Abs, my_abs, my_absf)
MATH1_OP_IMPL(Exp, exp, expf)
internal bool Math1Op(SagittariusCore *core, SagittariusInst inst) {
uint8_t t = (inst.data >> 8) & 0xFF;
uint8_t op = (inst.data >> 16) & 0xFF;
uint8_t L = (inst.data >> 24) & 0xFF;
uint8_t T = (inst.data >> 32) & 0xFF;
bool status = false;
switch (op) {
case sag_math1_sin: status = SagMath1Sin(core, (sagittarius_type)t, L, T); break;
case sag_math1_cos: status = SagMath1Cos(core, (sagittarius_type)t, L, T); break;
case sag_math1_tan: status = SagMath1Tan(core, (sagittarius_type)t, L, T); break;
case sag_math1_sinh: status = SagMath1Sinh(core, (sagittarius_type)t, L, T); break;
case sag_math1_cosh: status = SagMath1Cosh(core, (sagittarius_type)t, L, T); break;
case sag_math1_tanh: status = SagMath1Tanh(core, (sagittarius_type)t, L, T); break;
case sag_math1_asin: status = SagMath1Asin(core, (sagittarius_type)t, L, T); break;
case sag_math1_acos: status = SagMath1Acos(core, (sagittarius_type)t, L, T); break;
case sag_math1_atan: status = SagMath1Atan(core, (sagittarius_type)t, L, T); break;
case sag_math1_abs: status = SagMath1Abs(core, (sagittarius_type)t, L, T); break;
case sag_math1_exp: status = SagMath1Exp(core, (sagittarius_type)t, L, T); break;
default: return false;
}
if (status) {
core->pc += 8;
}
return status;
}
/* CVT (Conversion) */
#define CVT_CASE_SRC(src_c_type, src_t) \
case src_t: { \
src_c_type val; \
reg_read(core, src_reg, &val, src_t); \
switch (dst_type) { \
case st_uint8: { uint8_t r = (uint8_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_uint16: { uint16_t r = (uint16_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_uint32: { uint32_t r = (uint32_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_uint64: { uint64_t r = (uint64_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_int8: { int8_t r = (int8_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_int16: { int16_t r = (int16_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_int32: { int32_t r = (int32_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_int64: { int64_t r = (int64_t)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_single: { float r = (float)val; reg_write(core, dst_reg, &r, dst_type); break; } \
case st_double: { double r = (double)val; reg_write(core, dst_reg, &r, dst_type); break; } \
} \
break; \
}
internal bool SagCvt(SagittariusCore *core, SagittariusInst inst) {
uint8_t src_reg = (inst.data >> 8) & 0xFF;
uint8_t src_type = (inst.data >> 16) & 0xFF;
uint8_t dst_reg = (inst.data >> 24) & 0xFF;
uint8_t dst_type = (inst.data >> 32) & 0xFF;
switch ((sagittarius_type)src_type) {
CVT_CASE_SRC(uint8_t, st_uint8)
CVT_CASE_SRC(uint16_t, st_uint16)
CVT_CASE_SRC(uint32_t, st_uint32)
CVT_CASE_SRC(uint64_t, st_uint64)
CVT_CASE_SRC(int8_t, st_int8)
CVT_CASE_SRC(int16_t, st_int16)
CVT_CASE_SRC(int32_t, st_int32)
CVT_CASE_SRC(int64_t, st_int64)
CVT_CASE_SRC(float, st_single)
CVT_CASE_SRC(double, st_double)
default: return false;
}
core->pc += 8;
return true;
}
/* Set, Mv, Cp, Save, Load */
internal bool SagSet(SagittariusCore *core, SagittariusInst inst) {
uint8_t length = (inst.data >> 8) & 0xFF;
uint8_t reg_idx = (inst.data >> 16) & 0xFF;
if (core->pc + 15 >= core->memory->size) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_OOB, "Set payload out of bounds");
return false;
}
uint64_t payload = 0;
memcpy(&payload, &core->memory->data[core->pc + 8], 8);
if (length > 8) length = 8;
memcpy(&core->reg.head[reg_idx], &payload, length);
core->pc += 16;
return true;
}
internal bool SagMv(SagittariusCore *core, SagittariusInst inst) {
uint8_t length = (inst.data >> 8) & 0xFF;
uint8_t src_reg = (inst.data >> 16) & 0xFF;
uint8_t dst_reg = (inst.data >> 24) & 0xFF;
if (length > 8) length = 8;
memmove(&core->reg.head[dst_reg], &core->reg.head[src_reg], length);
core->pc += 8;
return true;
}
internal bool SagCp(SagittariusCore *core, SagittariusInst inst) {
uint8_t length = (inst.data >> 8) & 0xFF;
uint8_t src_reg = (inst.data >> 16) & 0xFF;
uint8_t dst_reg = (inst.data >> 24) & 0xFF;
if (length > 8) length = 8;
memcpy(&core->reg.head[dst_reg], &core->reg.head[src_reg], length);
core->pc += 8;
return true;
}
internal bool SagSave(SagittariusCore *core, SagittariusInst inst) {
uint8_t length = (inst.data >> 8) & 0xFF;
uint8_t src_reg = (inst.data >> 16) & 0xFF;
uint8_t dest_mem_ptr_reg = (inst.data >> 24) & 0xFF;
uint64_t mem_addr = 0;
memcpy(&mem_addr, &core->reg.head[dest_mem_ptr_reg], 8);
if (mem_addr + length > core->memory->size) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_OOB, "Memory write out of bounds");
return false;
}
if (length > 8) length = 8;
memcpy(&core->memory->data[mem_addr], &core->reg.head[src_reg], length);
core->pc += 8;
return true;
}
internal bool SagLoad(SagittariusCore *core, SagittariusInst inst) {
uint8_t length = (inst.data >> 8) & 0xFF;
uint8_t dest_reg = (inst.data >> 16) & 0xFF;
uint8_t src_mem_ptr_reg = (inst.data >> 24) & 0xFF;
uint64_t mem_addr = 0;
memcpy(&mem_addr, &core->reg.head[src_mem_ptr_reg], 8);
if (mem_addr + length > core->memory->size) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_OOB, "Memory read out of bounds");
return false;
}
if (length > 8) length = 8;
memcpy(&core->reg.head[dest_reg], &core->memory->data[mem_addr], length);
core->pc += 8;
return true;
}
/* Control flow: Jmp, JmpIf, Call, Ret */
internal bool SagJmp(SagittariusCore *core, SagittariusInst inst) {
uint8_t mode = (inst.data >> 8) & 0xFF; // 0 = absolute, 1 = relative
uint8_t val_mode = (inst.data >> 16) & 0xFF; // 0 = register, 1 = immediate
uint64_t target_val = 0;
if (val_mode == 0) {
uint8_t reg_idx = (inst.data >> 24) & 0xFF;
memcpy(&target_val, &core->reg.head[reg_idx], 8);
} else {
int32_t imm;
memcpy(&imm, &((uint8_t*)&inst.data)[3], 4);
target_val = (int64_t)imm; // Sign extended
}
if (mode == 0) {
core->pc = target_val;
} else {
core->pc += target_val;
}
return true;
}
internal bool SagJmpIf(SagittariusCore *core, SagittariusInst inst) {
uint8_t mode = (inst.data >> 8) & 0xFF;
uint8_t val_mode = (inst.data >> 16) & 0xFF;
uint8_t flag_reg = (inst.data >> 24) & 0xFF;
uint8_t flag = 0;
memcpy(&flag, &core->reg.head[flag_reg], 1);
if (flag != 0) {
uint64_t target_val = 0;
if (val_mode == 0) {
uint8_t reg_idx = (inst.data >> 32) & 0xFF;
memcpy(&target_val, &core->reg.head[reg_idx], 8);
} else {
int32_t imm;
memcpy(&imm, &((uint8_t*)&inst.data)[4], 4);
target_val = (int64_t)imm; // Sign extended
}
if (mode == 0) {
core->pc = target_val;
} else {
core->pc += target_val;
}
} else {
core->pc += 8;
}
return true;
}
internal bool SagCall(SagittariusCore *core, SagittariusInst inst) {
uint8_t mode = (inst.data >> 8) & 0xFF;
uint8_t val_mode = (inst.data >> 16) & 0xFF;
uint8_t reg_to_rem = (inst.data >> 24) & 0xFF;
if (val_mode == 0) {
uint8_t reg_idx = (inst.data >> 32) & 0xFF;
uint64_t target_pc = 0;
memcpy(&target_pc, &core->reg.head[reg_idx], 8);
uint64_t ret_pc = core->pc + 8;
memcpy(&core->reg.head[reg_to_rem], &ret_pc, 8);
if (mode == 0) {
core->pc = target_pc;
} else {
core->pc += target_pc;
}
} else {
if (core->pc + 15 >= core->memory->size) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_OOB, "Call payload out of bounds");
return false;
}
uint64_t target_pc = 0;
memcpy(&target_pc, &core->memory->data[core->pc + 8], 8);
uint64_t ret_pc = core->pc + 16;
memcpy(&core->reg.head[reg_to_rem], &ret_pc, 8);
if (mode == 0) {
core->pc = target_pc;
} else {
core->pc += target_pc;
}
}
return true;
}
internal bool SagRet(SagittariusCore *core, SagittariusInst inst) {
uint8_t reg_to_restore = (inst.data >> 8) & 0xFF;
uint64_t target_pc = 0;
memcpy(&target_pc, &core->reg.head[reg_to_restore], 8);
core->pc = target_pc;
return true;
}
/* Compare */
#define CMP_CASE(st_type, c_type, sz) \
case st_type: { \
c_type l, r; \
reg_read(core, L, &l, (sagittarius_type)t); \
reg_read(core, R, &r, (sagittarius_type)t); \
switch (op) { \
case 0: res = (l == r); break; \
case 1: res = (l != r); break; \
case 2: res = (l < r); break; \
case 3: res = (l <= r); break; \
case 4: res = (l > r); break; \
case 5: res = (l >= r); break; \
default: res = 0; break; \
} \
break; \
}
internal bool SagCmp(SagittariusCore *core, SagittariusInst inst) {
uint8_t t = (inst.data >> 8) & 0xFF;
uint8_t op = (inst.data >> 16) & 0xFF;
uint8_t L = (inst.data >> 24) & 0xFF;
uint8_t R = (inst.data >> 32) & 0xFF;
uint8_t T = (inst.data >> 40) & 0xFF;
uint8_t res = 0;
switch ((sagittarius_type)t) {
CMP_CASE(st_uint8, uint8_t, 1)
CMP_CASE(st_uint16, uint16_t, 2)
CMP_CASE(st_uint32, uint32_t, 4)
CMP_CASE(st_uint64, uint64_t, 8)
CMP_CASE(st_int8, int8_t, 1)
CMP_CASE(st_int16, int16_t, 2)
CMP_CASE(st_int32, int32_t, 4)
CMP_CASE(st_int64, int64_t, 8)
CMP_CASE(st_single, float, 4)
CMP_CASE(st_double, double, 8)
default: return false;
}
memcpy(&core->reg.head[T], &res, 1);
core->pc += 8;
return true;
}
/* MathV (Vector Math) */
internal bool SagMathV(SagittariusCore *core, SagittariusInst inst) {
uint8_t w = (inst.data >> 8) & 0xFF;
uint8_t h = (inst.data >> 16) & 0xFF;
uint8_t op = (inst.data >> 24) & 0xFF;
uint8_t L_ptr_reg = (inst.data >> 32) & 0xFF;
uint8_t R_ptr_reg = (inst.data >> 40) & 0xFF;
uint8_t Dest_ptr_reg = (inst.data >> 48) & 0xFF;
uint64_t l_addr = 0, r_addr = 0, d_addr = 0;
memcpy(&l_addr, &core->reg.head[L_ptr_reg], 8);
memcpy(&r_addr, &core->reg.head[R_ptr_reg], 8);
memcpy(&d_addr, &core->reg.head[Dest_ptr_reg], 8);
uint64_t size_in_bytes = (uint64_t)w * h * 8;
if (l_addr + size_in_bytes > core->memory->size ||
r_addr + size_in_bytes > core->memory->size ||
d_addr + size_in_bytes > core->memory->size) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_OOB, "Vector math OOB access");
return false;
}
for (uint64_t i = 0; i < (uint64_t)w * h; ++i) {
double l_val = 0, r_val = 0, d_val = 0;
memcpy(&l_val, &core->memory->data[l_addr + i * 8], 8);
memcpy(&r_val, &core->memory->data[r_addr + i * 8], 8);
switch (op) {
case 0: d_val = l_val + r_val; break;
case 1: d_val = l_val - r_val; break;
case 2: d_val = l_val * r_val; break;
case 3: {
if (r_val == 0.0) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_Generic, "Vector division by zero");
return false;
}
d_val = l_val / r_val;
break;
}
default: d_val = 0; break;
}
memcpy(&core->memory->data[d_addr + i * 8], &d_val, 8);
}
core->pc += 8;
return true;
}
/* Halt */
internal bool SagHalt(SagittariusCore *core, SagittariusInst inst) {
(void)core; (void)inst;
return true;
}
/* Syscall & TSyscall */
internal bool SagSyscall(SagittariusCore *core, SagittariusInst inst) {
uint8_t ns_reg = (inst.data >> 8) & 0xFF;
uint8_t func_reg = (inst.data >> 16) & 0xFF;
uint64_t ns = 0, func_id = 0;
memcpy(&ns, &core->reg.head[ns_reg], 8);
memcpy(&func_id, &core->reg.head[func_reg], 8);
uint64_t syscall_id = (ns << 32) | func_id;
bool found = false;
for (uint64_t i = 0; i < core->vm->SagittariusSyscallCount; ++i) {
if (core->vm->SagittariusSyscallEntries[i].id == syscall_id) {
core->vm->SagittariusSyscallEntries[i].syscall(core);
found = true;
break;
}
}
if (!found) {
if (core->vm->panic_handler) core->vm->panic_handler(core, Sagittarius_Msg_Unknown, "Unregistered syscall called");
return false;
}
core->pc += 8;
return true;
}
internal bool SagTSyscall(SagittariusCore *core, SagittariusInst inst) {
uint8_t ns_reg = (inst.data >> 8) & 0xFF;
uint8_t func_reg = (inst.data >> 16) & 0xFF;
uint8_t result_reg = (inst.data >> 24) & 0xFF;
uint64_t ns = 0, func_id = 0;
memcpy(&ns, &core->reg.head[ns_reg], 8);
memcpy(&func_id, &core->reg.head[func_reg], 8);
uint64_t syscall_id = (ns << 32) | func_id;
uint8_t result = 0;
for (uint64_t i = 0; i < core->vm->SagittariusSyscallCount; ++i) {
if (core->vm->SagittariusSyscallEntries[i].id == syscall_id) {
result = 1;
break;
}
}
memcpy(&core->reg.head[result_reg], &result, 1);
core->pc += 8;
return true;
}
+12 -4
View File
@@ -10,16 +10,24 @@ if [ -d ".git" ];
then
echo "Project is tracked by git!"
git_str=$(git log | head -n 1 | cut -c 8-16)
echo $git_str
echo "Build Commit: $git_str"
else
echo "Project is not tracked by git!"
git_str=""
git_str="null"
fi
if [ -z "$SKIP_STANDALONE"];
then
EXEC="$CC Source/Standalone/*.c Source/VM/*.c -DVER=\"$git_str\" -o Binaries/Sagittarius"
echo "[$INDEX]$EXEC"
EXEC="$CC Source/Standalone/*.c Source/VM/*.c -DSAGITTARIUS_COMMIT=\"$git_str\" -o Binaries/Sagittarius"
echo "[$INDEX] $EXEC"
INDEX=$((INDEX+1))
$EXEC
fi
if [ -z "$SKIP_ASSEMBLER"];
then
EXEC="$CC Source/Assembler/*.c Source/VM/*.c -DSAGITTARIUS_COMMIT=\"$git_str\" -o Binaries/SagittariusAssembler"
echo "[$INDEX] $EXEC"
INDEX=$((INDEX+1))
$EXEC
fi