FIRST STUFF
commit
8adccb7b65
|
|
@ -0,0 +1,3 @@
|
|||
disco
|
||||
out
|
||||
*.o
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
|
||||
section .data
|
||||
text db "abc",10,0
|
||||
|
||||
section .bss
|
||||
stackArguments resb 64
|
||||
trip resb 8 ; temporary return instruction pointer (for stack manipulation)
|
||||
; tem resb 64
|
||||
|
||||
section .text
|
||||
global _start
|
||||
|
||||
_start:
|
||||
push text
|
||||
call _print
|
||||
call _exit
|
||||
|
||||
_print:
|
||||
; first we remove the params from the stack
|
||||
; remembering to not fuck up our return pointer
|
||||
pop qword [trip] ; pop old instruction pointer, save for later.
|
||||
pop rax ; pop first argument
|
||||
push qword [trip] ; push the old execution pointer back
|
||||
|
||||
; reset base stack to here.
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; push in our arguments
|
||||
push rax
|
||||
; rbp+0 => old base pointer
|
||||
; rbp+8 => old instruction pointer
|
||||
; rbp+16 => last param
|
||||
; rbp+24 => first param
|
||||
|
||||
mov rax, 1
|
||||
mov rdi, 1
|
||||
mov rsi, [rbp - 8]
|
||||
mov rdx, 4
|
||||
syscall
|
||||
|
||||
; pop variables (arguments) off stack
|
||||
add rsp, 8
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
_exit:
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
ret
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
link log
|
||||
log("hello\n")
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const AST = {
|
||||
Body(statements) { return { type: 'body', value: statements } },
|
||||
Identifier(name) { return { type: 'iden', value: name } },
|
||||
Link(identifier) { return { type: 'link', value: identifier } },
|
||||
Const(name, value) { return { type: 'const', name, value } },
|
||||
Int(n) { return { type: 'int', value: n } },
|
||||
String(s) { return { type: 'string', value: s } },
|
||||
Invocation(identifier, ...args) { return { type: 'invo', value: identifier, args } }
|
||||
}
|
||||
|
||||
const linkables = {
|
||||
log: {
|
||||
asmName: '_log',
|
||||
asm: `\
|
||||
push rax
|
||||
mov rbx, 0
|
||||
_log_loop:
|
||||
mov cl, [rax]
|
||||
cmp cl, 0
|
||||
je _log_loop_end
|
||||
inc rax
|
||||
inc rbx
|
||||
jmp _log_loop
|
||||
_log_loop_end:
|
||||
mov rdx, rbx
|
||||
mov rax, 1
|
||||
mov rdi, 1
|
||||
pop rsi
|
||||
syscall
|
||||
ret`
|
||||
}
|
||||
}
|
||||
|
||||
function compile(body) {
|
||||
const linkedFunctions = {}
|
||||
|
||||
// add linked functions
|
||||
// add static literals
|
||||
// add main statments
|
||||
const rname = () => (new Array(8).fill('')).map(() => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[Math.floor(Math.random() * 26)]).join('');
|
||||
const literals = {
|
||||
|
||||
}
|
||||
let mainStatements = '';
|
||||
|
||||
const parseLink = (identifier) => {
|
||||
console.assert(identifier.type === 'iden', 'EXPECTED IDENTIFIER AFTER LINK');
|
||||
console.assert(identifier.value in linkables, 'CANNOT FIND LINK ' + identifier.value);
|
||||
linkedFunctions[identifier.value] = linkables[identifier.value];
|
||||
}
|
||||
|
||||
const parseString = (string) => {
|
||||
const name = rname();
|
||||
literals[name] = string.value
|
||||
return name;
|
||||
}
|
||||
|
||||
const parseInvocation = (invocation) => {
|
||||
console.assert(invocation.value.value in linkedFunctions, 'UNKNOWN FUNCTION ' + invocation.value);
|
||||
|
||||
const stuffIdk = [];
|
||||
|
||||
for(const arg of invocation.args) {
|
||||
switch(arg.type) {
|
||||
case 'string': {
|
||||
const asmName = parseString(arg);
|
||||
stuffIdk.push(asmName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainStatements += `\
|
||||
mov rax, ${stuffIdk[0]}
|
||||
call ${linkedFunctions[invocation.value.value].asmName}
|
||||
`
|
||||
|
||||
}
|
||||
|
||||
const parseBody = (body) => {
|
||||
for(const node of body.value) {
|
||||
switch(node.type) {
|
||||
case 'link': {
|
||||
parseLink(node.value);
|
||||
break;
|
||||
}
|
||||
case 'invo': {
|
||||
parseInvocation(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseBody(body);
|
||||
|
||||
return `\
|
||||
section .data
|
||||
${Object.entries(literals).map(([name, string]) => {
|
||||
return " " + name + " db \"" + string + "\",10,0";
|
||||
})}
|
||||
section .text
|
||||
global _start
|
||||
|
||||
_start:
|
||||
${mainStatements}
|
||||
call _exit
|
||||
|
||||
_exit:
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
${Object.values(linkedFunctions).map(({asmName, asm}) => {
|
||||
return `${asmName}:\n${asm}`
|
||||
}).join('\n\n')}`
|
||||
}
|
||||
|
||||
|
||||
require('fs').writeFileSync('out.asm', compile(AST.Body([
|
||||
AST.Link(AST.Identifier('log')),
|
||||
AST.Invocation(
|
||||
AST.Identifier('log'),
|
||||
AST.String('Hello World')
|
||||
)
|
||||
])));
|
||||
|
||||
|
||||
try {
|
||||
require('child_process').execSync('nasm -f elf64 out.asm -o out.o');
|
||||
require('child_process').execSync('ld out.o -o out');
|
||||
require('child_process').execSync('./out', { stdio: 'inherit' });
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
section .data
|
||||
YMYJYNCD db "Hello World",10,0
|
||||
section .text
|
||||
global _start
|
||||
|
||||
_start:
|
||||
mov rax, YMYJYNCD
|
||||
call _log
|
||||
|
||||
call _exit
|
||||
|
||||
_exit:
|
||||
mov rax, 60
|
||||
mov rdi, 0
|
||||
syscall
|
||||
|
||||
_log:
|
||||
push rax
|
||||
mov rbx, 0
|
||||
_log_loop:
|
||||
mov cl, [rax]
|
||||
cmp cl, 0
|
||||
je _log_loop_end
|
||||
inc rax
|
||||
inc rbx
|
||||
jmp _log_loop
|
||||
_log_loop_end:
|
||||
mov rdx, rbx
|
||||
mov rax, 1
|
||||
mov rdi, 1
|
||||
pop rsi
|
||||
syscall
|
||||
ret
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "disco",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "nasm -f elf64 disco.asm -o disco.o && ld disco.o -o disco",
|
||||
"start": "./disco",
|
||||
"dev": "yarn build && yarn start"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue