fucked shit up

This commit is contained in:
anon 2024-07-22 18:35:21 +02:00
parent 4cbec3ec8a
commit b0a54971b3
11 changed files with 390 additions and 238 deletions

View File

@ -76,8 +76,8 @@ ${OBJECT.d}/%.pluglock: ${OBJECT.d}/%.pp
touch $@
test: ${OUT}
ORIGIN="$$(realpath .)" PATH="$$(realpath .):${PATH}" cmdtest
${WRAP} ./${OUT} test/nop.eax
-ORIGIN="$$(realpath .)" PATH="$$(realpath .):${PATH}" cmdtest
-${WRAP} ./${OUT} test/nop.eax
bootstrap:
./library/bootstrap.sh

View File

@ -1,4 +1,5 @@
#ifndef ASSEMBLER_H
#define ASSEMBLER_H
enum {
D8, // 8-bit
@ -183,5 +184,4 @@ extern int was_instruction_array_empty;
extern void assemble (unsigned int count, unsigned int * array);
#define ASSEMBLER_H
#endif

View File

@ -32,7 +32,7 @@ int compile_deinit(void) {
static
void dump_variable_to_assembler(void * data) {
symbol_t * variable = (symbol_t*)data;
if (variable->symbol_type != VARIABLE) {
if (variable->symbol_type != VARIABLE_SYMBOL) {
return;
}

View File

@ -6,12 +6,14 @@
#include <stdarg.h>
#include "eaxhla.h"
__attribute__((unused))
static
void breakpoint(void) { ; }
# define debug_puts(msg) do { puts(msg); } while (0)
static // this is less horid than macro varargs
__attribute__((unused))
static
void debug_printf(const char * const fmt, ...) {
va_list args;
va_start(args, fmt);
@ -19,19 +21,22 @@ void debug_printf(const char * const fmt, ...) {
va_end(args);
}
__attribute__((unused))
static
void dump_variable(void * data) {
symbol_t * variable = (symbol_t*)data;
if (variable->symbol_type != VARIABLE) { return; }
if (variable->symbol_type != VARIABLE_SYMBOL) { return; }
if (variable->elements == 1) {
printf("{ .name = '%s', .value = '%ld' }\n",
printf("{ .name = '%s', .id = %d, .value = '%ld' }\n",
variable->name,
variable->_id,
variable->value
);
} else {
printf("{ .name = '%s', .elements = '%llu', .array_value = \"%.*s\" }\n",
printf("{ .name = '%s', .id = %d, .elements = '%lu', .array_value = \"%.*s\" }\n",
variable->name,
variable->_id,
variable->elements,
(int)variable->elements,
(char*)variable->array_value
@ -39,28 +44,33 @@ void dump_variable(void * data) {
}
}
__attribute__((unused))
static
void debug_dump_variables(void) {
puts("# Variables:");
tommy_hashtable_foreach(&symbol_table, dump_variable);
}
__attribute__((unused))
static
void dump_function(void * data) {
symbol_t * function = (symbol_t*)data;
if (function->symbol_type != FUNCTION) { return; }
if (function->symbol_type != LABEL_SYMBOL) { return; }
printf("{ .name = '%s' }\n",
function->name
printf("{ .name = '%s', .id = %d }\n",
function->name,
function->_id
);
}
__attribute__((unused))
static
void debug_dump_functions(void) {
puts("# Functions:");
tommy_hashtable_foreach(&symbol_table, dump_function);
}
__attribute__((unused))
static
void debug_dump_symbols(void) {
debug_dump_variables();
@ -68,6 +78,7 @@ void debug_dump_symbols(void) {
printf("# Total variable size: '%d'\n", variable_size_sum());
}
__attribute__((unused))
static
void debug_token_dump(void) {
extern unsigned int * token_array;

View File

@ -9,6 +9,7 @@
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include "eaxhla.tab.h"
@ -16,7 +17,14 @@
#include "assembler.h"
#include "compile.h"
unsigned long long anon_variable_counter = 0;
/* Used for naming variables constructed from literals
*/
size_t anon_variable_counter = 0;
/* Used to check whether all labels without
* previous declarations (forward jumps)
* have been declared later in code
*/
size_t unresolved_label_counter = 0;
static unsigned symbol_id = 1;
tommy_hashtable symbol_table;
@ -38,6 +46,15 @@ int eaxhla_init(void) {
return 0;
}
symbol_t * new_symbol(const char * const name) {
symbol_t * r;
r = (symbol_t *)calloc(sizeof(symbol_t), 1);
r->name = strdup(name);
return r;
}
static
void free_symbol(void * data) {
symbol_t * variable = (symbol_t*)data;
@ -46,96 +63,19 @@ void free_symbol(void * data) {
}
int eaxhla_deinit(void) {
empty_out_scope();
tommy_hashtable_foreach(&symbol_table, free_symbol);
tommy_hashtable_done(&symbol_table);
return 0;
}
static
int table_compare_unsigned(const void * arg, const void * obj) {
return *(const unsigned *) arg != ((const symbol_t*)obj)->_hash;
}
static
void * symbol_lookup(const char * const name) {
unsigned lookup_hash = tommy_strhash_u32(0, name);
void * r = tommy_hashtable_search(&symbol_table,
table_compare_unsigned,
&lookup_hash,
lookup_hash
);
return r;
}
static
void symbol_insert(symbol_t * symbol) {
symbol->_hash = tommy_strhash_u32(0, symbol->name);
tommy_hashtable_insert(&symbol_table,
&symbol->_node,
symbol,
symbol->_hash
);
}
void add_program(const char * const name) {
if (is_program_found) {
issue_error("only 1 entry point is allowed and a program block was already found");
}
is_program_found = 1;
append_instructions(ASMDIRMEM, 0);
scope = strdup(name);
}
void add_variable(symbol_t variable) {
if (get_variable(variable.name)) {
issue_error("symbol '%s' redeclared as new variable", variable.name);
return;
}
variable._id = symbol_id++;
variable.symbol_type = VARIABLE;
symbol_t * heap_variable = malloc(sizeof(variable));
memcpy(heap_variable, &variable, sizeof(variable));
symbol_insert(heap_variable);
}
void add_procedure(symbol_t procedure) {
if (get_symbol(procedure.name)) {
issue_error("symbol '%s' redeclared as new function", procedure.name);
return;
}
procedure._id = symbol_id++;
procedure.symbol_type = FUNCTION;
symbol_t * heap_procedure = malloc(sizeof(procedure));
memcpy(heap_procedure, &procedure, sizeof(procedure));
symbol_insert(heap_procedure);
//
append_instructions(ASMDIRMEM, procedure._id);
}
void add_fastcall(const char * const destination) {
symbol_t * function = get_function(destination);
if (!function) {
issue_error("can't fastcall '%s', no such known symbol", destination);
return;
}
append_instructions(CALL, REL, function->_id);
}
/* Are these literals ugly? yes.
* However it would be much more painful to calculate the values inline.
*/
int can_fit(const int type, const long long value) {
unsigned long long max = 0;
size_t max = 0;
long long min = 0;
switch (type) {
case U8: {
@ -167,9 +107,216 @@ int can_fit(const int type, const long long value) {
max = 4294967295;
} break;
}
return value > 0 ? (unsigned long long)value <= max : value >= min;
return value > 0 ? (size_t)value <= max : value >= min;
}
int validate_array_size(const int size) {
if (size < 1) {
issue_error("cannot create an array of size '%d', because its less than 1", size);
return 1;
}
return 0;
}
static
int table_compare_unsigned(const void * arg, const void * obj) {
return *(const unsigned *) arg != ((const symbol_t*)obj)->_hash;
}
static
void * symbol_lookup(const char * const name) {
unsigned lookup_hash = tommy_strhash_u32(0, name);
void * r = tommy_hashtable_search(&symbol_table,
table_compare_unsigned,
&lookup_hash,
lookup_hash
);
return r;
}
static
void symbol_insert(symbol_t * symbol) {
symbol->_hash = tommy_strhash_u32(0, symbol->name);
tommy_hashtable_insert(&symbol_table,
&symbol->_node,
symbol,
symbol->_hash
);
}
void add_scope(const char * const name){
free(scope);
scope = strdup(name);
}
// XXX: alternative version on the stack
static
char * make_scoped_name(const char * const scope, const char * const name) {
if (!scope) {
return (char*)name;
}
char * r;
const long scl = strlen(scope);
const long nml = strlen(name);
r = malloc(2 + scl + 1 + nml + 1);
r[0] = '_';
r[1] = '_';
memcpy(r + 2, scope, scl);
r[2 + scl] = '_';
memcpy(r + 2 + scl + 1, name, nml);
r[2 + scl + 1 + nml] = '\0';
return r;
}
symbol_t * get_symbol(const char * const name) {
symbol_t * r;
r = symbol_lookup(name);
if (r) {
return r;
}
char * alternative_name = make_scoped_name(scope, name);
r = symbol_lookup(alternative_name);
free(alternative_name);
return r;
}
void add_program(const char * const name) {
(void)name;
if (is_program_found) {
issue_error("only 1 entry point is allowed and a program block was already found");
}
is_program_found = 1;
append_instructions(ASMDIRMEM, 0);
}
static
void _add_variable(unsigned type, const char * const name, size_t size, void * value) {
char * full_name = make_scoped_name(scope, name);
if (get_variable(full_name)) {
issue_error("symbol '%s' redeclared as new variable", full_name);
return;
}
symbol_t * variable = new_symbol(full_name);
variable->elements = size;
variable->type = type;
if (size == 1) {
variable->value = (long)value;
if (!can_fit(type, variable->value)) {
issue_warning("the value \033[1m'%lld'\033[0m will overflow in assignement",
variable->value
);
}
} else {
variable->array_value = value;
validate_array_size(size);
}
variable->_id = symbol_id++;
variable->symbol_type = VARIABLE_SYMBOL;
symbol_insert(variable);
}
void add_variable(unsigned type, const char * const name) {
_add_variable(type, name, 1, 0);
}
void add_variable_with_value(unsigned type, const char * const name, size_t value) {
_add_variable(type, name, 1, (void *)value);
}
void add_array_variable(unsigned type, const char * const name, size_t size) {
_add_variable(type, name, size, 0);
}
void add_array_variable_with_value(unsigned type, const char * const name, size_t size, void * value, size_t value_size) {
if (size < value_size) {
issue_warning("declared array size is smaller than assigned literal, this will cause truncation");
}
_add_variable(type, name, size, value);
}
void add_procedure(const char * const name) {
if (get_symbol(name)) {
issue_error("symbol '%s' redeclared as new function", name);
return;
}
symbol_t * procedure = new_symbol(name);
procedure->_id = symbol_id++;
procedure->symbol_type = LABEL_SYMBOL;
procedure->is_resolved = true;
symbol_insert(procedure);
append_instructions(ASMDIRMEM, procedure->_id);
}
void add_literal(void * data, size_t size) {
char * name;
int ignore = asprintf(&name, "_anon_%lu", anon_variable_counter++);
(void)ignore;
symbol_t * literal = (symbol_t *)calloc(sizeof(symbol_t), 1);
literal->name = name;
literal->elements = size;
literal->array_value = data;
}
static
symbol_t * _add_label(const char * const name, int is_resolved) {
char * full_name = make_scoped_name(scope, name);
symbol_t * label = get_symbol(full_name);
if (label) {
if (label->is_resolved) {
issue_error("symbol '%s' redeclared as new label", name);
} else {
label->is_resolved = true;
--unresolved_label_counter;
}
return label;
}
label = new_symbol(full_name);
label->_id = symbol_id++;
label->symbol_type = LABEL_SYMBOL;
label->is_resolved = is_resolved;
if (!is_resolved) {
++unresolved_label_counter;
}
symbol_insert(label);
return label;
}
void add_label(const char * const name, int is_resolved) {
_add_label(name, is_resolved);
}
void add_fastcall(const char * const destination) {
symbol_t * function = get_symbol(destination);
if (!function) {
issue_error("can't fastcall '%s', no such known symbol", destination);
return;
}
append_instructions(CALL, REL, function->_id);
}
int type2size(const int type) {
switch (type) {
case U8:
@ -205,7 +352,7 @@ int size2bytes(const int size) {
static
void _variable_size_sum_iteration(void * i, void * data) {
symbol_t * variable = (symbol_t*)data;
if (variable->symbol_type != VARIABLE) { return; }
if (variable->symbol_type != VARIABLE_SYMBOL) { return; }
int * sum = i;
@ -218,54 +365,13 @@ int variable_size_sum(void) {
return r;
}
int validate_array_size(const int size) {
if (size < 1) {
issue_error("cannot create an array of size '%d', because its less than 1", size);
return 1;
}
return 0;
}
char * make_scoped_name(const char * const scope, const char * const name) {
if (!scope) {
return (char*)name;
}
char * r;
const long scl = strlen(scope);
const long nml = strlen(name);
r = malloc(2 + scl + 1 + nml + 1);
r[0] = '_';
r[1] = '_';
memcpy(r + 2, scope, scl);
r[2 + scl] = '_';
memcpy(r + 2 + scl + 1, name, nml);
r[2 + scl + 1 + nml] = '\0';
return r;
}
symbol_t * get_symbol(const char * const name) {
symbol_t * r;
r = symbol_lookup(name);
if (r) {
return r;
}
char * alternative_name = make_scoped_name(scope, name);
r = symbol_lookup(alternative_name);
free(alternative_name);
return r;
}
symbol_t * get_variable(const char * const name) {
symbol_t * r;
char * varname = make_scoped_name(scope, name);
r = symbol_lookup(varname);
if (r
&& r->symbol_type != VARIABLE) {
&& r->symbol_type != VARIABLE_SYMBOL) {
issue_error("the symbol '%s' is not a variable", name);
return NULL;
}
@ -276,13 +382,46 @@ symbol_t * get_function(const char * const name) {
symbol_t * r;
r = symbol_lookup(name);
if (r
&& r->symbol_type != FUNCTION) {
&& r->symbol_type != LABEL_SYMBOL) {
issue_error("the symbol '%s' is not a function", name);
return NULL;
}
return r;
}
symbol_t * get_relative(const char * const name) {
symbol_t * r;
r = get_symbol(name);
if (r) {
return r;
}
r = _add_label(name, false);
return r;
}
void fin_procedure(void) {
append_instructions(RETN);
}
static
void unresolved_check(void * data) {
symbol_t * label = (symbol_t*)data;
if (label->type != LABEL_SYMBOL) {
return;
}
if (!label->is_resolved) {
issue_error("the label '%s' was never resolved.", label->name);
}
}
void fin_hla(void) {
if (anon_variable_counter) {
tommy_hashtable_foreach(&symbol_table, unresolved_check);
}
}
void issue_warning(const char * const format, ...) {
extern char * yyfilename;

View File

@ -1,27 +1,29 @@
#ifndef EAXHLA_H
#define EAXHLA_H
#include <tommyds/tommyds/tommyhashtbl.h>
#define WORD_SIZE_IN_BYTES (64/8)
// XXX these should be private
typedef enum {
VARIABLE,
FUNCTION,
VARIABLE_SYMBOL,
LABEL_SYMBOL,
} symbol_type_t;
typedef struct {
symbol_type_t symbol_type;
union {
struct { // VARIABLE
struct { // VARIABLE_SYMBOL
int type;
unsigned long long elements;
size_t elements;
union {
long value;
void * array_value;
};
};
struct { // FUNCTION
void * unused;
struct { // LABEL_SYMBOL
int is_resolved;
};
};
char * name;
@ -30,10 +32,12 @@ typedef struct {
tommy_node _node;
} symbol_t;
extern tommy_hashtable symbol_table;
symbol_t * new_symbol(const char * const name);
/* private:
void free_symbol(void * name);
*/
// Used for naming variables constructed from literals
extern unsigned long long anon_variable_counter;
extern tommy_hashtable symbol_table;
typedef struct {
unsigned number : 6;
@ -42,39 +46,54 @@ typedef struct {
extern int system_type;
/* Used for error discovery
*/
extern int is_program_found;
extern int has_encountered_error;
extern char * scope;
extern void empty_out_scope(void);
// Used for error reporting
/* Used for error reporting
*/
extern char * yyfilename;
extern int eaxhla_init(void);
extern int eaxhla_deinit(void);
extern char * make_scoped_name(const char * const scope, const char * const name);
extern int can_fit(const int type, const long long value);
extern int validate_array_size(const int size);
extern void add_variable(symbol_t variable);
extern symbol_t * get_variable(const char * const name);
extern symbol_t * get_symbol(const char * const name);
//extern void add_function(symbol_t function);
extern void add_procedure(symbol_t procedure);
extern symbol_t * get_function(const char * const name);
extern void add_program(const char * const name);
extern void add_fastcall(const char * const destination);
extern int type2size(int type);
extern int size2bytes(const int size);
extern int variable_size_sum(void);
// Language constructs
extern void add_program(const char * const name);
extern void add_scope(const char * const name);
extern void add_variable(unsigned type, const char * const name);
extern void add_variable_with_value(unsigned type, const char * const name, size_t value);
extern void add_array_variable(unsigned type, const char * const name, size_t size);
extern void add_array_variable_with_value(unsigned type, const char * const name, size_t size, void * value, size_t value_size);
extern void add_literal(void * data, size_t size);
extern void add_label(const char * const name, int is_resolved);
extern void add_procedure(const char * const name);
extern void add_fastcall(const char * const destination);
extern void fin_procedure(void);
extern void fin_hla(void);
/* Not implemented
extern symbol_t * add_function(symbol_t function);
extern symbol_t * get_function(const char * const name);
*/
// Asm value constructs
/* These functions MUST return a valid symbol_t or
* we segv and catch fire.
* Unresolved symbol errors are handled internally.
*/
extern symbol_t * get_relative(const char * const name);
extern symbol_t * get_variable(const char * const name);
// XXX: move to assembler.h,
// delete the switch bullshit, you can get away with artimetrics,
// also typedef the D\d+ type
extern int type2size(int type); // XXX: the return type is an anon enum
extern int size2bytes(const int size); // XXX: size is an anon enum
// Error reporting
extern void issue_warning(const char * format, ...);
extern void issue_error(const char * format, ...);
#define EAXHLA_H
#endif

View File

@ -1,6 +1,7 @@
%{
#include <stdlib.h>
#include "sds/sds.h"
#include "eaxhla.h"
#include "eaxhla.tab.h"
sds string_literal_buffer;
@ -253,13 +254,13 @@ library { BEGIN INITIAL; return END_LIBRARY; }
<IN_COMMENT>{
\n { BEGIN INITIAL; }
.* { ; }
<<EOF>> { yytext = strdup("<EOF>"); issue_error("unterminated comment"); yyterminate(); }
<<EOF>> { issue_error("unterminated comment"); yyterminate(); }
}
<IN_MULTILINE_COMMENT>{
\*\/ { BEGIN INITIAL; }
.|\n { ; }
<<EOF>> { yytext = strdup("<EOF>"); issue_error("unterminated comment"); yyterminate(); }
<<EOF>> { issue_error("unterminated comment"); yyterminate(); }
}
<INITIAL,IN_ARTIMETRIC_BLOCK>{
@ -285,7 +286,7 @@ library { BEGIN INITIAL; return END_LIBRARY; }
}
<IN_ARTIMETRIC_BLOCK>{
<<EOF>> { yytext = strdup("<EOF>"); issue_error("unterminated artimetric block"); yyterminate(); }
<<EOF>> { issue_error("unterminated artimetric block"); yyterminate(); }
}
{identifier} { yylval.strval = strdup(yytext); return IDENTIFIER; }

View File

@ -1,14 +1,21 @@
/* EAXHLA parser
* This source file has minimal knowledge about highlevel constructs.
* The only time it interacts with the assembler interface directly is when
* instructions are encountered or processing a machine code block,
* everything else is delegated to eaxhla.c
*/
%code requires {
#include "eaxhla.h"
}
%{
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include "eaxhla.h"
#include "eaxhla.yy.h"
#include "assembler.h"
#include "compile.h"
#include "eaxhla.h"
#include "debug.h"
extern void yyfree_leftovers(void);
@ -70,7 +77,7 @@
%token U8 U16 U32 U64
%type<intval> type
%type<varval> declaration
%type<varval> anon_variable
%type<varval> stored_literal
// Registers
%type<regval> register register64s register32s register16s register8s
@ -94,6 +101,9 @@
%token EXIT BREAK
%%
document: hla { fin_hla(); }
;
hla: %empty
// | library hla
| declaration hla // tmp
@ -102,11 +112,11 @@ hla: %empty
;
program: program_head declaration_section MYBEGIN code END_PROGRAM {
empty_out_scope();
}
;
program_head: program_specifier PROGRAM IDENTIFIER {
add_scope($3);
add_program($3);
free($3);
};
@ -121,17 +131,14 @@ system_specifier: UNIX { system_type = UNIX; }
// XXX: end procedure thing
function: function_head declaration_section MYBEGIN code END_PROCEDURE {
append_instructions(RETN);
empty_out_scope();
fin_procedure();
}
;
function_head: function_specifier PROCEDURE IDENTIFIER {
scope = strdup($3);
symbol_t procedure;
procedure.name = $3;
add_procedure(procedure);
add_scope($3);
add_procedure($3);
free($3);
}
;
@ -146,58 +153,23 @@ declaration_section: %empty
declaration:
variable_specifier type IDENTIFIER {
$$.type = $2;
$$.name = make_scoped_name(scope, $3);
$$.elements = 1;
add_variable($$);
add_variable($2, $3);
free($3);
}
| variable_specifier type IDENTIFIER '=' LITERAL {
$$.type = $2;
$$.name = make_scoped_name(scope, $3);
if (!can_fit($2, $5)) {
issue_warning("the value \033[1m'%lld'\033[0m will overflow in assignement", $5);
}
$$.elements = 1;
$$.value = $5;
add_variable($$);
add_variable_with_value($2, $3, $5);
free($3);
}
| variable_specifier type '<' value '>' IDENTIFIER {
$$.type = $2;
if (validate_array_size($4)) {
break;
}
$$.name = make_scoped_name(scope, $6);
$$.elements = $4;
add_variable($$);
add_array_variable($2, $6, $4);
free($6);
}
| variable_specifier type '<' value '>' IDENTIFIER '=' ARRAY_LITERAL {
$$.type = $2;
if (validate_array_size($4)) {
break;
}
if ((unsigned long long)$4 < $8.len) {
issue_warning("declared array size is smaller than assigned literal, this will cause truncation");
}
$$.name = make_scoped_name(scope, $6);
$$.elements = $4;
$$.array_value = $8.data;
add_variable($$);
add_array_variable_with_value($2, $6, $4, $8.data, $8.len);
free($6);
}
| variable_specifier type '<' '>' IDENTIFIER '=' ARRAY_LITERAL {
$$.type = $2;
$$.name = make_scoped_name(scope, $5);
$$.elements = $7.len;
$$.array_value = $7.data;
add_variable($$);
add_array_variable_with_value($2, $5, $7.len, $7.data, $7.len);
free($5);
}
;
@ -224,7 +196,6 @@ immediate: LITERAL {
symbol_t * variable = get_variable($1);
$$.type = REL;
$$.value = variable->_id;
free($1);
}
;
@ -239,9 +210,8 @@ dereference: '[' IDENTIFIER ']' { $$ = 0; /* XXX: how the fuck do i dereference?
;
relative: IDENTIFIER {
symbol_t * relative = get_symbol($1);
/*breakpoint();*/
$$ = relative->_id;
$$ = get_relative($1)->_id;
free($1);
}
;
@ -254,12 +224,8 @@ value: artimetric_block
}
;
anon_variable: ARRAY_LITERAL {
$$.array_value = $1.data;
$$.elements = $1.len;
int ignore = asprintf(&$$.name, "_anon_%llu", anon_variable_counter++);
(void)ignore;
add_variable($$);
stored_literal: ARRAY_LITERAL {
add_literal($1.data, $1.len);
}
;
@ -276,9 +242,7 @@ code: %empty
;
label: LABEL {
symbol_t label;
label.name = make_scoped_name(scope, $1);
add_procedure(label);
add_label($1, true);
}
;
@ -337,7 +301,7 @@ arguments: %empty
| LITERAL arguments
| register arguments
| artimetric_block arguments
| anon_variable arguments
| stored_literal arguments
;
register: register64s { $$ = $1; $$.size = D64; }
@ -436,7 +400,9 @@ artimetric_expression: %empty { $$ = 0; }
;
artimetric_operand: LITERAL
| IDENTIFIER { $$ = 0; /*XXX*/ }
| IDENTIFIER {
$$ = get_variable($1)->value;
}
;
exit: EXIT value { append_exit($2); }

2
test/die.eax Normal file
View File

@ -0,0 +1,2 @@
program die
end program

14
test/forward_jump.eax Normal file
View File

@ -0,0 +1,14 @@
program forward
s8 <> heyo_data = "Heyo world\n"
begin
jmp lol
mov eax 1
mov edi 1
mov esi heyo_data
mov edx 12
syscall
lol:
exit 86
end program

View File

@ -1,4 +1,4 @@
; @BAKE fasm $@ $*
; @BAKE fasm $@ $*.out
format ELF64 executable 3
segment readable executable