diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs b/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs index 668fdbb..7f7e342 100644 --- a/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs +++ b/Nub.Lang/Nub.Lang/Backend/Custom/Generator.cs @@ -6,7 +6,8 @@ namespace Nub.Lang.Backend.Custom; public class Generator { private const string Entrypoint = "main"; - + private const bool ZeroBasedIndexing = false; + private readonly List _definitions; private readonly SymbolTable _symbolTable; private readonly StringBuilder _builder; @@ -69,6 +70,10 @@ public class Generator } _builder.AppendLine(""" + array_out_of_bounds: + mov rax, 60 + mov rdi, 69 + syscall strcmp: xor rdx, rdx @@ -204,6 +209,9 @@ public class Generator { switch (statement) { + case ArrayIndexAssignmentNode arrayIndexAssignment: + GenerateArrayIndexAssignment(arrayIndexAssignment, func); + break; case FuncCallStatementNode funcCallStatement: GenerateFuncCall(funcCallStatement.FuncCall, func); break; @@ -230,6 +238,15 @@ public class Generator } } + private void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment, LocalFunc func) + { + GenerateExpression(arrayIndexAssignment.Value, func); + _builder.AppendLine(" push rax"); + GenerateArrayIndexPointerAccess(arrayIndexAssignment.Identifier, arrayIndexAssignment.Index, func); + _builder.AppendLine(" pop rdx"); + _builder.AppendLine(" mov [rax], rdx"); + } + private void GenerateIf(IfNode ifStatement, LocalFunc func) { var endLabel = _labelFactory.Create(); @@ -298,6 +315,12 @@ public class Generator { switch (expression) { + case ArrayIndexAccessNode arrayIndexAccess: + GenerateArrayIndexAccess(arrayIndexAccess, func); + break; + case ArrayInitializerNode arrayInitializer: + GenerateArrayInitializer(arrayInitializer, func); + break; case BinaryExpressionNode binaryExpression: GenerateBinaryExpression(binaryExpression, func); break; @@ -318,6 +341,28 @@ public class Generator } } + private void GenerateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess, LocalFunc func) + { + GenerateArrayIndexPointerAccess(arrayIndexAccess.Identifier, arrayIndexAccess.Index, func); + _builder.AppendLine(" mov rax, [rax]"); + } + + private void GenerateArrayInitializer(ArrayInitializerNode arrayInitializer, LocalFunc func) + { + _builder.AppendLine($""" + mov rax, 9 + mov rdi, 0 + mov rsi, {8 + arrayInitializer.Length * 8} + mov rdx, 3 + mov r10, 34 + mov r8, -1 + mov r9, 0 + syscall + + mov QWORD [rax], {arrayInitializer.Length} + """); + } + private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func) { GenerateExpression(binaryExpression.Left, func); @@ -576,4 +621,31 @@ public class Generator _builder.AppendLine(" syscall"); } + + private void GenerateArrayIndexPointerAccess(IdentifierNode identifier, ExpressionNode index, LocalFunc func) + { + GenerateExpression(index, func); + _builder.AppendLine(" push rax"); + GenerateIdentifier(identifier, func); + _builder.AppendLine(" pop rbx"); + + // rcx now holds the length of the array which we can use to check bounds + _builder.AppendLine(" mov rcx, [rax]"); + _builder.AppendLine(" cmp rbx, rcx"); + if (ZeroBasedIndexing) + { + _builder.AppendLine(" jge array_out_of_bounds"); + _builder.AppendLine(" cmp rbx, 0"); + } + else + { + _builder.AppendLine(" jg array_out_of_bounds"); + _builder.AppendLine(" cmp rbx, 1"); + } + _builder.AppendLine(" jl array_out_of_bounds"); + + _builder.AppendLine(" inc rbx"); + _builder.AppendLine(" shl rbx, 3"); + _builder.AppendLine(" add rax, rbx"); + } } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs b/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs index 2ad9b87..bc018aa 100644 --- a/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs +++ b/Nub.Lang/Nub.Lang/Backend/Custom/SymbolTable.cs @@ -7,7 +7,7 @@ public class SymbolTable { private readonly List _funcDefinitions = []; private readonly List _globalVariables = []; - private LabelFactory _labelFactory; + private readonly LabelFactory _labelFactory; public readonly Dictionary Strings = []; diff --git a/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs b/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs index 3d551ba..8561c20 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Lexing/Lexer.cs @@ -14,6 +14,7 @@ public class Lexer ["else"] = Symbol.Else, ["while"] = Symbol.While, ["return"] = Symbol.Return, + ["new"] = Symbol.New, }; private static readonly Dictionary Chians = new() diff --git a/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs b/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs index b9ee726..8b58839 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Lexing/SymbolToken.cs @@ -38,4 +38,5 @@ public enum Symbol Minus, Star, ForwardSlash, + New, } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs new file mode 100644 index 0000000..cecdbee --- /dev/null +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayIndexAccessNode.cs @@ -0,0 +1,9 @@ +namespace Nub.Lang.Frontend.Parsing; + +public class ArrayIndexAccessNode(IdentifierNode identifier, ExpressionNode index) : ExpressionNode +{ + public IdentifierNode Identifier { get; } = identifier; + public ExpressionNode Index { get; } = index; + + public override string ToString() => $"{Identifier}[{Index}]"; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs new file mode 100644 index 0000000..bcee92a --- /dev/null +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayIndexAssignmentNode.cs @@ -0,0 +1,8 @@ +namespace Nub.Lang.Frontend.Parsing; + +public class ArrayIndexAssignmentNode(IdentifierNode identifier, ExpressionNode index, ExpressionNode value) : StatementNode +{ + public IdentifierNode Identifier { get; } = identifier; + public ExpressionNode Index { get; } = index; + public ExpressionNode Value { get; } = value; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs new file mode 100644 index 0000000..7f7414b --- /dev/null +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/ArrayInitializerNode.cs @@ -0,0 +1,7 @@ +namespace Nub.Lang.Frontend.Parsing; + +public class ArrayInitializerNode(long length, Type innerType) : ExpressionNode +{ + public long Length { get; } = length; + public Type InnerType { get; } = innerType; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/IdentifierNode.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/IdentifierNode.cs index be242b8..322b281 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Parsing/IdentifierNode.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/IdentifierNode.cs @@ -4,8 +4,5 @@ public class IdentifierNode(string identifier) : ExpressionNode { public string Identifier { get; } = identifier; - public override string ToString() - { - return Identifier; - } + public override string ToString() => Identifier; } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs index 3eb4492..e9bcec7 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -149,6 +149,15 @@ public class Parser return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters)); } + case Symbol.OpenBracket: + { + var index = ParseExpression(); + ExpectSymbol(Symbol.CloseBracket); + ExpectSymbol(Symbol.Assign); + var value = ParseExpression(); + ExpectSymbol(Symbol.Semicolon); + return new ArrayIndexAssignmentNode(new IdentifierNode(identifier.Value), index, value); + } case Symbol.Assign: { var value = ParseExpression(); @@ -310,10 +319,32 @@ public class Parser return new LiteralNode(literal.Value, literal.Type); case IdentifierToken identifier: return ParseExpressionIdentifier(identifier); - case SymbolToken { Symbol: Symbol.OpenParen }: - var expression = ParseExpression(); - ExpectSymbol(Symbol.CloseParen); - return expression; + case SymbolToken symbolToken: + { + switch (symbolToken.Symbol) + { + case Symbol.OpenParen: + { + var expression = ParseExpression(); + ExpectSymbol(Symbol.CloseParen); + return expression; + } + case Symbol.New: + { + var type = ParseType(); + ExpectSymbol(Symbol.OpenParen); + var size = ExpectLiteral(); + if (size.Type is not PrimitiveType { Kind: PrimitiveTypeKind.Int64 }) + { + throw new Exception($"Array initializer size must be an {PrimitiveTypeKind.Int64}"); + } + ExpectSymbol(Symbol.CloseParen); + return new ArrayInitializerNode(long.Parse(size.Value), type); + } + default: + throw new Exception($"Unknown symbol: {symbolToken.Symbol}"); + } + } default: throw new Exception($"Unexpected token type {token.GetType().Name}"); } @@ -321,6 +352,13 @@ public class Parser private ExpressionNode ParseExpressionIdentifier(IdentifierToken identifier) { + if (TryExpectSymbol(Symbol.OpenBracket)) + { + var index = ParseExpression(); + ExpectSymbol(Symbol.CloseBracket); + return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index); + } + if (TryExpectSymbol(Symbol.OpenParen)) { List parameters = []; @@ -356,30 +394,42 @@ public class Parser private Type ParseType() { var name = ExpectIdentifier().Value; - if (name == "Func") + + switch (name) { - List typeArguments = []; - if (TryExpectSymbol(Symbol.LessThan)) + case "Func": { - while (!TryExpectSymbol(Symbol.GreaterThan)) + List typeArguments = []; + if (TryExpectSymbol(Symbol.LessThan)) { - var type = ParseType(); - typeArguments.Add(type); - TryExpectSymbol(Symbol.Comma); + while (!TryExpectSymbol(Symbol.GreaterThan)) + { + var type = ParseType(); + typeArguments.Add(type); + TryExpectSymbol(Symbol.Comma); + } } + + var returnType = Optional.OfNullable(typeArguments.LastOrDefault()); + + return new DelegateType(typeArguments.Take(typeArguments.Count - 1).ToList(), returnType); + } + case "String": + { + return new StringType(); + } + case "Array": + { + ExpectSymbol(Symbol.LessThan); + var innerType = ParseType(); + ExpectSymbol(Symbol.GreaterThan); + return new ArrayType(innerType); + } + default: + { + return PrimitiveType.Parse(name); } - - var returnType = Optional.OfNullable(typeArguments.LastOrDefault()); - - return new DelegateType(typeArguments.Take(typeArguments.Count - 1).ToList(), returnType); } - - if (name == "String") - { - return new StringType(); - } - - return PrimitiveType.Parse(name); } private Token ExpectToken() diff --git a/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs b/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs index 64c2b2d..905a7df 100644 --- a/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs +++ b/Nub.Lang/Nub.Lang/Frontend/Typing/ExpressionTyper.cs @@ -83,6 +83,9 @@ public class ExpressionTyper { switch (statement) { + case ArrayIndexAssignmentNode arrayIndexAssignment: + PopulateArrayIndexAssignment(arrayIndexAssignment); + break; case FuncCallStatementNode funcCall: PopulateFuncCallStatement(funcCall); break; @@ -109,6 +112,13 @@ public class ExpressionTyper } } + private void PopulateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment) + { + PopulateIdentifier(arrayIndexAssignment.Identifier); + PopulateExpression(arrayIndexAssignment.Index); + PopulateExpression(arrayIndexAssignment.Value); + } + private void PopulateFuncCallStatement(FuncCallStatementNode funcCall) { foreach (var parameter in funcCall.FuncCall.Parameters) @@ -168,6 +178,12 @@ public class ExpressionTyper { switch (expression) { + case ArrayIndexAccessNode arrayIndexAccess: + PopulateArrayIndexAccess(arrayIndexAccess); + break; + case ArrayInitializerNode arrayInitializer: + PopulateArrayInitializer(arrayInitializer); + break; case BinaryExpressionNode binaryExpression: PopulateBinaryExpression(binaryExpression); break; @@ -188,6 +204,30 @@ public class ExpressionTyper } } + private void PopulateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) + { + PopulateExpression(arrayIndexAccess.Index); + PopulateIdentifier(arrayIndexAccess.Identifier); + + var variable = _variables.FirstOrDefault(v => v.Name == arrayIndexAccess.Identifier.Identifier); + if (variable == null) + { + throw new Exception($"Variable {arrayIndexAccess.Identifier} is not defined"); + } + + if (variable.Type is not ArrayType arrayType) + { + throw new Exception($"Variable {arrayIndexAccess.Identifier} is not an array type"); + } + + arrayIndexAccess.Type = arrayType.InnerType; + } + + private void PopulateArrayInitializer(ArrayInitializerNode arrayInitializer) + { + arrayInitializer.Type = arrayInitializer.InnerType; + } + private void PopulateBinaryExpression(BinaryExpressionNode binaryExpression) { PopulateExpression(binaryExpression.Left); diff --git a/Nub.Lang/Nub.Lang/Type.cs b/Nub.Lang/Nub.Lang/Type.cs index 73d9faa..8e0f9ab 100644 --- a/Nub.Lang/Nub.Lang/Type.cs +++ b/Nub.Lang/Nub.Lang/Type.cs @@ -41,6 +41,18 @@ public record StringType : Type public override string ToString() => "String"; } +public record ArrayType : Type +{ + public ArrayType(Type innerType) + { + InnerType = innerType; + } + + public Type InnerType { get; } + + public override string ToString() => $"{InnerType}"; +} + public record DelegateType : Type { public DelegateType(List parameters, Optional returnType) diff --git a/input/core/util.asm b/input/core/util.asm new file mode 100644 index 0000000..12ac3c6 --- /dev/null +++ b/input/core/util.asm @@ -0,0 +1,311 @@ +;********************************************************************* +; util.asm +; Version: 1.2 +; Author: mjbrusso +; Contributors: AlessandroFonseca +; Licensed under the MIT license (see "license.txt"). +;********************************************************************* +global printint + +SYS_READ: equ 0 +SYS_WRITE: equ 1 +SYS_EXIT: equ 60 + +STDIN: equ 0 +STDOUT: equ 1 + +LINEFEED: equ 0x0A + +section .text + +;********************************************************************* +; void exit(int64 code) +; +; Description: +; Quit program +; +; Arguments: +; rdi: int64 code: Exit code (0=Success, >0=Error) +; +; Returns: +; This function does not return +; +;********************************************************************* +exit: + mov rax, SYS_EXIT ; rax: system call number + syscall +;********************************************************************* + +;********************************************************************* +; void exit0() +; +; Description: +; Quit program with status code = 0 +; +; Arguments: +; None +; +; Returns: +; This function does not return +; +;********************************************************************* +exit0: + xor rdi, rdi ; rdi = 0 + jmp exit ; TCO: tail call optimization +;********************************************************************* + + +;********************************************************************* +; int64 strlen(char *s) +; +; Description: +; Calculates the length of string ( excluding the terminating null) +; +; Arguments: +; rdi: char *s: address of a null-terminated string (array of chars terminated by 0) +; +; Returns: +; rax: int64: string size +; +;********************************************************************* +strlen: + xor rax, rax ; rax=0; // reset counter +.loop: ; do{ + cmp byte [rdi], 0 ; if (*s==0); // If zero, skip loop + je strlen.end ; break; + inc rax ; rax++; // increment counter + inc rdi ; s++; // advance to the next char + jmp strlen.loop ; }while(true); +.end: + ret ; return rax; +;********************************************************************* + + +;********************************************************************* +; void itoa(int64 value, char *s) +; +; Description: +; Converts an integer to a null-terminated string. +; +; Arguments: +; rdi: int64 value: Integer value to convert. +; rsi: char *s: Memory address where to store the resulting string. +; +; Returns: +; rax: int64: string size +; +;********************************************************************* +itoa: + test rdi, rdi ; value = rdi + jz itoa.iszero ; value==0 has a direct solution + jns itoa.notneg ; if(value <0 ) + mov byte [rsi], '-' ; *s = '-' + neg rdi ; value = -value + inc rsi ; s++ +.notneg: + mov r9b, 1 ; bool leftzero=true + mov r10, 10 ; base = 10 + mov rcx, 1000000000000000000 ; divisor = 1000000000000000000 + mov r8, 19 ; cont = 19 // Will repeat 19 times +.loop: ; do{ + mov rax, rdi ; dividend[0..31] = value + xor rdx, rdx ; dividend[32..63] = 0 + idiv rcx ; rax=(rdx:rax)/rcx ; rdx=(rdx:rax)%rcx + test al, al ; digit = rax[0..7] + jnz itoa.notdigit0 ; if(digit!=0) + test r9b, r9b ; if(leftzero) + jnz itoa.nextdigit ; continue + jmp itoa.digit0 +.notdigit0: + xor r9b, r9b ; leftzero = false +.digit0: + add eax, 48 ; digit = '0' + digit + mov rdi, rdx ; value %= divisor + mov byte [rsi], al ; *p = digit + inc rsi ; p++ +.nextdigit: + mov rax, rcx ; dividend[0..31] = value + xor rdx, rdx ; dividend[32..63] = 0 + idiv r10 ; rax=(rdx:rax)/10 ; rdx=(rdx:rax)%10 + mov rcx, rax ; divisor /= 10 + dec r8 ; cont-- + jne itoa.loop ; }while(cont!=0) +.end: + mov byte [rsi], 0 ; *p = '\0' + ret +.iszero: + mov word [rsi], 0x0030 ; *p = "0" (x86 is little endian) + ret +;********************************************************************* + + +;********************************************************************* +; int64 atoi(char *s) +; +; Description: +; Convert string to integer. +; +; Arguments: +; rdi: char *s: Address of a null-terminated string (array of chars terminated by 0) +; +; Returns: +; rax: int64: integer value +;********************************************************************* +atoi: + push r12 ; r12 is callee saved + mov r12, rdi ; rdi is caller saved + call strlen + lea rdi, [r12+rax-1] ; char *p = &s[strlen(string)]; //scans string backward + xor rax, rax ; result value + mov rdx, 1 ; multiplier +.beginloop: + cmp rdi, r12 ; while(p>=s){ + jl atoi.end ; + xor rcx, rcx ; + mov cl, byte [rdi] ; cl = current char + cmp cl, '-' ; if(cl=='-') + jne atoi.notneg ; + neg rax ; rax=-rax + jmp atoi.end ; +.notneg: + cmp cl, '9' ; if(!isdigit(cl)) nextdigit + jg atoi.endloop ; + sub cl, '0' ; + jl atoi.endloop ; + imul rcx, rdx ; digit_value = current_char * multiplier + add rax, rcx ; result += digit_value + imul rdx, 10 ; multiplier *= 10 +.endloop: + dec rdi ; previous char //scans string backward + jmp atoi.beginloop ; } +.end: + pop r12 ; restore r12 + ret +;********************************************************************* + + +;********************************************************************* +; void endl() +; +; Description: +; Prints a newline (line break) +; +; Arguments: +; None +; +; Returns: +; Nothing +; +;********************************************************************* +endl: + lea rdi, [endl.str] ; print the string + call printstr + ret + +;********************************************************************* + + +;********************************************************************* +; void printstr(char *s) +; +; Description: +; Print a string +; +; Arguments: +; rdi: char *s: address of a null-terminated string (array of chars terminated by 0) +; +; Returns: +; Nothing +; +;********************************************************************* +printstr: + push r15 ; r15 is callee saved + mov r15, rdi ; save copy (rdi should be caller saved) + call strlen + mov rdx, rax ; string size + mov rsi, r15 ; string + mov rax, SYS_WRITE ; system call number + mov rdi, STDOUT ; file descriptor + syscall ; system call + pop r15 + ret +;********************************************************************* + + +;********************************************************************* +; void printint(int64 n) +; +; Description: +; Print integer number (decimal) +; +; Arguments: +; rdi: int64 n: Value to print +; +; Returns: +; Nothing +; +;********************************************************************* +printint: + sub rsp, 40 ; stack allocate a temp string + mov rsi, rsp ; rdi=value, rsi=&str[0] + call itoa + mov rdi, rsp ; rdi=&str[0] + call printstr ; print number + add rsp, 40 ; deallocate the string + ret +;********************************************************************* + + +;********************************************************************* +; int64 readstr(char *s, int64 maxsize) +; +; Description: +; Read up to *maxsize* chars from standard input into a string. +; +; Arguments: +; rdi: char *s: address of a string (array of chars) +; rsi: int64 maxsize: input size limit +; +; Returns: +; rax: int64: Number of characters read +; +;********************************************************************* +readstr: + mov r8, rdi ; copy of buffer address + mov rax, SYS_READ ; system call number + mov rdx, rsi ; pointer to buffer + mov rsi, rdi ; max size + mov rdi, STDIN ; file descriptor + syscall ; system call + dec rax ; removing trailing newline char + mov byte [r8+rax], 0 ; replace with '\0' + ret +;********************************************************************* + + +;********************************************************************* +; int64 readint() +; +; Description: +; Read int64 from standard input +; +; Arguments: +; None +; +; Returns: +; rax: int64: The value entered +; +;********************************************************************* +readint: + sub rsp, 40 ; char s[40] + mov rdi, rsp ; rdi = &s[0] + mov rsi, 21 ; max input size + call readstr ; read number as string + mov rdi, rsp ; + call atoi ; rax = atoi(s) + add rsp, 40 ; deallocate s from stack + ret +;********************************************************************* + +section .data +endl.str: db LINEFEED, 0 \ No newline at end of file diff --git a/input/core/util.nub b/input/core/util.nub new file mode 100644 index 0000000..7b19af6 --- /dev/null +++ b/input/core/util.nub @@ -0,0 +1 @@ +extern func printint(val: int64); \ No newline at end of file diff --git a/input/program.nub b/input/program.nub index b239cc7..5510d45 100644 --- a/input/program.nub +++ b/input/program.nub @@ -1,9 +1,10 @@ import "core"; func main() { - let i = 1; - while i <= 10 { - println("uwu"); - i = i + 1; - } + let x = new Array(2); + x[1] = "test1"; + x[2] = "test2"; + + println(x[1]); + println(x[2]); } diff --git a/output/build.sh b/output/build.sh index 21e0b40..ec430c9 100755 --- a/output/build.sh +++ b/output/build.sh @@ -1,5 +1,6 @@ #!/bin/sh nasm -g -felf64 out.asm -o out.o nasm -g -felf64 ../input/core/strlen.asm -o strlen.o +nasm -g -felf64 ../input/core/util.asm -o util.o -ld -o out strlen.o out.o +ld -o out strlen.o util.o out.o