Below is the simple Hello World
program written in New-Assembly.
_ : data
txt $ string = "Hello world"
_ : start
mov . tlr , string
mov . fdx , 1
syscall . 0 , %ios
retn . 0 , 0
Documentation about newasm
which includes following topics:
- Compiling binaries
- Arguments
- Sections
- Built-in references
- Instructions
- Procedures
- Exit codes
- Comments
- Interesting examples
- Unassigned references
- Structures
- Project files
This project is written purely in C++ using its standard libraries, so compiling it should be easy. To download C++ compiler, please follow instructions on the link below:
- There are some arguments you can use when executing the interpreter.
Argument | Parameters | Description |
---|---|---|
-ver |
- | Displays version information only, and doesn't start the interpreter at all. |
-help |
- | Displays help information. |
-input |
<filename> |
Sets the input file; if -input was not provided, interpreter sets it to input.asm . |
-repl |
- | Enter the read-evaluate-print mode. |
-newproj |
- | Create a new project file if one doesn't already exist. |
-tests |
- | Start the test function. |
Sections are built-in "tags" used to classify code. Each section uses different syntax in terms of instructions. General syntax is:
_ : section_name
In this section, you can setup some settings for your program. It is thus recommended to keep this section on top of the code. General syntax is:
_ : config
configuration ~ value_in_specific_datatype
_ : data
; some stuff
_ : start
mov . fdx , 100
retn . fdx
A simple example would be:
_ : config
memsize ~ 256 ; can be any integer from 1 to 512
Setting name | Data type | Description |
---|---|---|
memsize |
num |
Reallocates the number of addresses for the heap and the stack of the program. |
In this section, you can declare variables to avoid repeated code. General syntax is:
data_type $ variable_name = variable_value
You can use variable_name
as an operand in instructions documented below.
There are 3 data types:
num
for integers;decm
for floats;txt
for strings;ref
for references to data symbols (basically a kind of a pointer);char
for characters.
In this section, you can perform instructions, and cannot create variables, or else program will end with exit code 1.
This language brings some built-in references, or rather operands, with itself - list:
%ios
- used as an operand insyscall
, represents a module of system calls responsible for input and output streaming;%fs
- used as an operand insyscall
, represents a module of system calls responsible for input and output streaming;%exf
- used as an operand insyscall
, represents a module of system calls responsible for execution flow (starting child processes);%endl
- line ending, used instl
;&%null
- used to leave references/pointers unassigned/uninitialized.%nl
- used as a null operand in some instructions.
WARNING: Syntax such as % ios
is invalid.
Below is a list of available instructions. General syntax is:
instruction . suffix , operand
This is also valid:
instruction.suffix,operand
Ends your program with specific exit code.
instruction
-retn
suffix
- no suffix, useretn . 0 , ...
operand
- exit code
instruction
-ret
suffix
- register nameoperand
- no operand (0
)
_ : start
retn . 0 , 23
Output:
[newasm] PROGRAM THREAD @ System info: Program finished with exit code : 23
_ : start
mov . tlr , 8
ret . tlr
Output:
[newasm] PROGRAM THREAD @ System info: Program finished with exit code : 8
Set value of a specific register.
instruction
-mov
suffix
- register nameoperand
- new register value
instruction
-stor
suffix
- register nameoperand
- variable name
In this example, we basically do fdx=1
:
_ : start
mov . fdx , 1
retn . 0 , 23
In this example, we basically do fdx=1
, myvar=fdx
, exit 1
:
_ : data
num $ myvar = 0
_ : start
mov . fdx , 1
stor . fdx , myvar
retn . 0 , myvar
- There are input and output registers. Input registers are used to get the input from the user, and output registers are used to store data which will be used as an argument in a
syscall
or an operand in an instruction.
Register name | Full name | Description |
---|---|---|
fdx |
function index | Holds an index of a function syscall will call. |
tlr |
typeless register | Typeless register (can hold any value, even code literals). Used as an output argument in some syscall s. |
stl |
secondary typeless register | Typeless register; however used to hold built-in operands for syscall s. |
stk |
stack pointer | Points at the top of the stack. |
hea |
heap pointer | Points at an address in the heap. |
psx |
procedure scope exit value | Holds value returned inside a procedure using halt . |
prp |
procedure pointer | Points at the procedure that was called using call . |
cpr |
comparsion result register | Holds a value of the comparsion (cmp ) result; 1 for equal , 2 for less and 3 for greater (although there are 6 logical variants of jmp , only these 3 cases are required and detected by cmp ). |
cr0 |
primary calculation register | Register in which all the calculation results are stored. Read this for more information... |
cr1 |
alternate calculation register | Register which is used as a second operand in calculations. Read this for more information... |
br0 |
primary bit operation register | Register in which all the bitwise calculation results are stored. Read this for more information... |
br1 |
alternate bit operation register | Register which is used as a second operand in bitwise calculations. Read this for more information... |
Set value of a specific register.
instruction
-syscall
suffix
- no suffixoperand
- no operand
_ : start
mov . fdx , 1
mov . tlr , "Hello World"
syscall . 0 , %ios
retn . 0 , 23
Module | ID | Arguments | Description |
---|---|---|---|
%ios |
1 |
tlr , stl |
Prints exclusively text. Uses stl as a help argument. |
%ios |
2 |
tlr , stl |
Prints integers and floating point numbers. Uses stl as a help argument. |
%ios |
3 |
- | Requests textual user input and stores the value in tlr . |
%ios |
4 |
- | Requests numeric (including floats) user input and stores the value in tlr . |
%ios |
5 |
tlr |
Prints the textual value of a built-in operand. |
%ios |
6 |
tlr , stl |
Prints the name of a symbol a reference is pointing to. |
%ios |
7 |
tlr , stl |
Prints a single character. |
%ios |
8 |
- | Requests a single character input from the user and stores the value in tlr . |
%fs |
1 |
tlr |
Create a directory; with tlr being a string containing the directory name. |
%fs |
2 |
tlr |
Remove a directory; with tlr being a string containing the directory name. |
%fs |
3 |
tlr |
Create a file; with tlr being a string containing the file name. |
%fs |
4 |
tlr |
Remove a file; with tlr being a string containing the file name. |
%fs |
5 |
tlr , stl |
Overwrite file content; with tlr being a string containing the file name, and stl being a string containing the new content. |
%fs |
6 |
tlr , stl |
Append content to file; with tlr being a string containing the file name, and stl being a string containing the content to append. |
%fs |
7 |
tlr |
Remove all file content; with tlr being a string containing the file name. |
%fs |
8 |
tlr , stl |
Read a file line; with tlr being a string containing the file name, and stl being the line number. Read content is subsequently stored in tlr . |
%exf |
1 |
tlr |
Start a new child process; with tlr being a string containing the file name. |
Do nothing.
instruction
-nop
suffix
- no suffixoperand
- no operand
_ : start
nop
retn . 0 , 23
Do nothing. NOT RECOMMENDED TO USE!
instruction
-rem
suffix
- no suffixoperand
- your comment
_ : start
rem . 0 , "my comment"
retn . 0 , 23
Ensure that a symbol is available for further use.
instruction
-sysreq
suffix
-proc
,data
operand
- procedure return value
_ : data
_ : start
; Notice how we haven't declared anything in _:data
sysreq . data , variable
; Program will be terminated with exit code 4
; Same happens when we try to access a procedure:
sysreq . proc , some_random_proc
Return a value inside a function.
instruction
-halt
suffix
-proc
operand
- procedure return value
_ : data
num $ variable = 0
_ : start
proc . 0 , testprocedure
halt . proc , 364
end
call . 0 , testprocedure
stor . psx , variable
mov . tlr , variable
mov . fdx , 1
syscall . 0 , %ios
retn . 0 , 1
- Push and pop values to and from the stack, respectively.
instruction
-push
suffix
- no suffixoperand
- constant value or a value of a variable
instruction
-pop
suffix
- no suffixoperand
- variable to pop the value to
_ : data
num $ myvar2 = 0
_ : start
push . 0 , 273
; change myvar2 to something dumb:
stor . fdx , myvar2
pop . 0 , myvar2
mov . tlr , myvar2
mov . fdx , 1
syscall . 0 , %ios
retn . 0 , 0
- TIP: If you just want to pop the value off the stack, and not store it anywhere, just do:
_ : start
pop . 0 , %nl
Resets the register to an invalid value that cannot be used.
instruction
-zero
suffix
- register nameoperand
- no operand
_ : start
zero . stl
; something ?
Move down and up the heap.
instruction
-heap
suffix
- no suffixoperand
- number of addresses to move up to
_ : data
num $ mynum = 0
_ : start
heap . 0 , 3
stor . hea , mynum
mov . tlr , mynum
mov . fdx , 2
syscall . 0 , %ios
- Load data and store data from the heap and into the heap, respectively.
instruction
-load
suffix
-adr
,ref
operand
- literal value or a reference
_ : data
decm $ testdecimal = 0.0
_ : start
; If the suffix of the LOAD instruction is `adr`,
; then we will update the value in the address heap pointer
; is pointing to - HOWEVER, if the suffix is `ref`, then we will
; store the value in the address heap pointer is pointing to
; into some variable in `_:data`.
load . adr , 736.38 ; hea = something
load . ref , testdecimal ; myvar = hea
mov . tlr , testdecimal
mov . stl , %endl
mov . fdx , 2
syscall . 0 , %ios
retn . 0 , 0
Output:
736.38
- If we expand our code, and manually assign addresses before cleaning up the heap, we can do this:
_ : data
decm $ testdecimal = 0.0
decm $ testdecm2 = 0.0
_ : start
load . adr , 736.38 ; hea = something
load . ref , testdecimal ; myvar = hea
mov . tlr , testdecimal
mov . stl , %endl
mov . fdx , 2
syscall . 0 , %ios
; Allocate more space:
heap . 0 , 1
load . adr , 9821.38 ; hea = 63
load . ref , testdecm2 ; myvar = hea
mov . tlr , testdecm2
mov . stl , %endl
mov . fdx , 2
syscall . 0 , %ios
mov . hea , 0 ; manually access the first address
load . ref , testdecm2 ; myvar = hea
mov . tlr , testdecm2
mov . stl , %endl
mov . fdx , 2
syscall . 0 , %ios
mov . hea , 1 ; manually access the second address
load . ref , testdecm2 ; myvar = hea
mov . tlr , testdecm2
mov . stl , %endl
mov . fdx , 2
syscall . 0 , %ios
heap . 0 , -1 ; let all the memory go to avoid getting the memory leak
Output:
736.38
9821.38
736.38
9821.38
- NOTE: Procedures and variables you create in New-Assembly are not located in the stack and the heap of your program, so you have both the stack and the heap for yourself, which means that addresses in the heap start from 0 and go up by 1. You may ask, why is that - well New-Assembly mimics the assembly language and doesn't need to go by the rules set by the standards.
- You can create labels inside
_:start
and then jump to them using thejmp
instruction. General syntax is:
_ : start
_ ! label_name
; somewhere
jmp . 0 , label_name
_ : start
_ ! labelname
mov . fdx , 4
syscall . 0 , %ios
jmp . 0 , labelname
A little too complex example.
_ : start
jmp . 0 , label2
_ ! label
mov . tlr , "label called"
mov . fdx , 1
syscall . 0 , %ios
mov . fdx , 72
jmp . 0 , label3
ret . fdx
_ ! label2
mov . tlr , "label2 called"
mov . fdx , 1
syscall . 0 , %ios
jmp . 0 , label
_ ! label3
mov . tlr , "label3 called"
mov . fdx , 1
syscall . 0 , %ios
retn . 0 , 3873
Output:
label2 called
label called
label3 called
[newasm] PROGRAM THREAD @ System info: Program finished with exit code : 3873
Compare values of registers with values stored in the operands.
cmp . fdx , 3 ; check if fdx is 3
According to the result cmp
stores in its own "hidden" register, you can use the following variants of the jmp
instruction in order to perform jumps to labels according to the value of the cpr
(comparsion result register):
Instruction | Processed if... |
---|---|
je |
comparsion returned equal . |
jne |
comparsion returned not equal . |
jl |
comparsion returned less . |
jg |
comparsion returned greater . |
jle |
comparsion returned less or equal . |
jge |
comparsion returned greater or equal . |
_ : start
mov . tlr , 5.8
cmp . tlr , 1
je . 0 , equal
;jne . 0, notequal
jl . 0 , less
jg . 0 , greater
;jle . 0 , lesseq
;jge . 0 , greatereq
; We just want to check if they are either equal, less or greater.
_ ! equal
mov . tlr , "EQUAL"
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
jmp . 0 , endtheprogram
_ ! notequal
mov . tlr , "NOT EQUAL"
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
jmp . 0 , endtheprogram
_ ! less
mov . tlr , "LESS"
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
jmp . 0 , endtheprogram
_ ! greater
mov . tlr , "GREATER"
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
jmp . 0 , endtheprogram
_ ! lesseq
mov . tlr , "LESS OR EQUAL"
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
jmp . 0 , endtheprogram
_ ! greatereq
mov . tlr , "GREATER OR EQUAL"
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
jmp . 0 , endtheprogram
_ ! endtheprogram
retn . 0 , 0
Output:
GREATER
There are 6 mathematical instructions you can use:
add
- addition,sub
- subtraction,mul
- multiplication,div
- division,exp
- exponentiation,log
- logarithm.
All of these operations use cr0
and cr1
registers. The result is stored in cr0
; so:
_ : start
mov . cr0 , 3.0 ; can be either a float or a whole number
mov . cr1 , 9 ; same
add ; cr0 = cr0 + cr1
sub ; cr0 = cr0 - cr1
mul ; cr0 = cr0 * cr1
div ; cr0 = cr0 / cr1
exp ; cr0 = power(cr0,cr1)
log ; cr0 = log_c1(cr0)
There are 6 bitwise operations you can use:
and
- and,or
- or,not
- not,xor
- exclusive or,shl
- shift left,shr
- shift right.
All of these operations use br0
and br1
registers. The result is stored in br0
; so:
_ : start
mov . br0 , 1 ; must be a number
mov . br1 , 1 ; same
and ; br0 = br0 & br1
or ; br0 = br0 | br1
not ; br0 = ~br1
xor ; br0 = br0 ^ br1
shl ; br0 = br0 << br1
shr ; br0 = br0 >> br1
These instructions are used to increment or decrement register values. Since registers can be of various types, these 2 instructions perform differently.
inc.register_name
dec.register_name
If you operate on a whole number or rational number register, then it may not be so interesting, it just increments it by 1.
inc.br0 ; br0++
dec.cr1 ; cr1--
If you are operating on a typeless register, these operations will check if the register is holding a whole number, and then apply changes to it. Namely, if you try to increment tlr
while tlr
is holding a string, you will get a data type mismatch exception.
However, what if you try to decrement a symbol reference, well - this gets interesting. For instance, if you tried to decrement the prp
register (which is a reference to a last used procedure), you can't just decrement it by 1 since it is internally pointing to a pair in the procedure map, so the interpreter does the work for you and updates it to the pair before the pair prp
is pointing at.
Demonstration:
_ : data
ref $ temporary = &%null
_ : start
proc . 0 , procedure_1
halt . proc , 0
end
proc . 0 , procedure_2
halt . proc , 0
end
proc . 0 , procedure_3
halt . proc , 0
end
mov . prp , &procedure_1
inc . prp
stor . prp , temporary
mov . tlr , temporary
mov . stl , %endl
mov . fdx , 6
syscall . 0 , %ios
mov . prp , &procedure_3
dec . prp
stor . prp , temporary
mov . tlr , temporary
mov . stl , %endl
mov . fdx , 6
syscall . 0 , %ios
Output:
procedure_2
procedure_2
Procedures allow you to use the same piece of code without having to actually repeat it. General syntax is:
proc . 0 , procedure_name
; code
end
To call the procedure, use:
call . 0 , procedure_name
- Here is an example:
_ : start
proc . 0 , test
halt . proc , 1
end
call . 0 , test
retn . 0 , 0
Basically, these are just functions, but in assembly.
When a fatal error happens, program will shut down, returning a specific exit code, below is a list of exit codes.
Exit code | Description |
---|---|
0 |
No termination point (standard exit code if you have no ret or retn in your script). |
1 |
Invalid section (for example, tried to enter a section named _:lol ). |
2 |
Attempted to call a procedure which does not exist. |
3 |
Invalid non-numeric value was passed to retn . |
4 |
sysreq failed, pretty self-explanatory. |
5 |
Stack/heap collision; pretty self-explanatory. |
6 |
Data overflow - tried to pop a value into unallocated address. |
7 |
Data type mismatch. |
8 |
Tried to redefine a label. |
9 |
Bus error; tried to access an invalid label with jmp . |
10 |
Invalid instruction; pretty self-explanatory, also happens if you use . or , (instruction parsing delimiters) in your rem comments. |
11 |
Memory overflow. |
12 |
Memory underflow. |
13 |
Procedure redefinition. |
14 |
Invalid memory access; tried to access an address using mov.hea,... that isn't allocated for the heap. |
15 |
Invalid syntax. |
16 |
Memory leak: neither retn , ret or heap were used at the end of the program while the hea register wasn't at 0. |
17 |
Invalid configuration. |
18 |
You tried to call a procedure within a procedure. |
19 |
Unknown system call index (invalid value moved into fdx ). |
20 |
Tried to create a child process in a child process. |
21 |
Tried to jump to a label in a child process. |
22 |
Attempted to use an unassigned pointer/reference variable. |
23 |
Tried to redefine a variable. |
24 |
Tried to redefine a structure. |
25 |
Unexpected closing brace. |
26 |
Tried to access a structure that hasn't been declared. |
27 |
Tried to access an undefined structure member. |
28 |
Tried to create a struct inside a struct. |
29 |
Expected closing brace. |
30 |
Tried to create an empty procedure. |
Comments are also available:
_ : start
; comment
mov . fdx , 1
mov . tlr , "hello" ; comment
syscall . 0 , %ios ; comment again
Below is a list of interesting examples of using the language.
_ : start
mov . fdx, 3
mov . tlr, "filename"
syscall . 0, %fs
mov . stl, "TEXTeee"
mov . fdx, 6
syscall . 0, %fs
mov . stl, 1
mov . fdx, 8
syscall . 0, %fs
mov . stl, %endl
mov . fdx, 1
syscall . 0, %ios
retn . 0 , 0
Output:
TEXTeee
index.nax
:
_ : start
mov .tlr, "child.nax" ; another nax file containing stuff such as config modifications, variables and procedures
mov .fdx, 1
syscall . 0 , %exf ; create a process and execute it
mov . tlr , "Hi after the process" ; this line will be processed AFTER child.nax finishes executing
mov . stl , %endl
mov . fdx , 1
syscall . 0 , %ios
retn . 0 , 0
child.nax
:
_ : data
num $ mynum = 0
_ : start
heap . 0 , 36
stor . hea , mynum
mov . tlr , mynum
mov . fdx , 2
syscall . 0 , %ios ; prints heap size (36)
heap . 0 , -36 ; free up memory we occupied for the sake of the example
Output:
36
Hi after the process
- You cannot create labels and jump to them in child processes.
- If you use
ret
orretn
inside a child process, it will terminate the whole program with that exit code and not just the child process. - Procedures and variables created inside the child process can be used in the parent process (in our case
index.nax
) after the child process finishes executing.
_:data
ref $ unassigned_pointer = &%null
This reference is left unassigned and an attempt to use it will result in an exception.
In this quite frankly low-level language, there are also structs! General syntax is:
_:data
struct $ structure_name = {
; members
data_type $ member_name = member_value
}
_ : data
struct $ mystruct = {
num $ lol = 98
decm $ decimal = 2.3
txt $ text = "hi from struct"
}
struct $ mystruct2 = {
num $ lol = 45
decm $ decimal = 833.4
txt $ text = "hi from struct again"
ref $ reference = &prptest
}
_ : start
mov . tlr , text @ mystruct
mov . stl , %endl
mov . fdx , 1
syscall . 0 , %ios
mov . tlr , decimal @ mystruct
mov . fdx , 2
syscall . 0 , %ios
mov . tlr , lol @ mystruct
mov . fdx , 2
syscall . 0 , %ios
mov . tlr , text @ mystruct2
mov . stl , %endl
mov . fdx , 1
syscall . 0 , %ios
mov . tlr , decimal @ mystruct2
mov . fdx , 2
syscall . 0 , %ios
mov . tlr , lol @ mystruct2
mov . fdx , 2
syscall . 0 , %ios
mov . tlr , reference @ mystruct2
mov . fdx , 6
syscall . 0 , %ios
mov . stl , 45657
stor . stl , lol @ mystruct2
mov . tlr , lol @ mystruct2
mov . fdx , 2
mov . stl , %endl
syscall . 0 , %ios
mov . psx , "HIII243"
stor . psx , text @ mystruct
mov . tlr , text @ mystruct
mov . fdx , 1
mov . stl , %endl
syscall . 0 , %ios
Output:
hi from struct
2.3
98
hi from struct again
833.4
45
prptest
45657
HIII243
- Currently, if you want to store a value into a struct member, only the
stor
instruction will work - note that instructions such asload
andpop
will not work regarding this. - This syntax is a must:
struct $ name = { ; brace must be HERE
; and not here
num $ number = 384
}
Project files (.newasm_proj
file) are files that define information about your New-ASM project. General name format for them is:
<entry file name>.newasm_proj
For example, if my -input
file is input.asm
, project file for that would be input.asm.newasm_proj
. This is basically an INI file.
Key name | Description |
---|---|
name |
Name for your project. |
version |
Version of your project. |
Example index.asm.newasm_proj
file:
name = Unnamed project
version = 0.0.1