A simple stack-style machine language interpreter.
Project description
simple-stack-machine
A simple stack-style machine language interpreter.
Install
pip install simple-stack-machine
Usage
python3 -m simple_stack_machine <filepath.asm>
python3 -m simple_stack_machine --debug <filepath.asm>
Example Programs
See https://github.com/GGN-2015/simple-stack-machine/tree/main/sample_asm
Instructions and Data
You have a 64-bit address space where each position stores one byte. This space holds all program code and data.
All instructions (except PUSHIMM) are 1 byte long. The address of the currently executing instruction is stored in a 64-bit Program Counter (PC).
Register Set
- Program Counter: PC (64-bit)
- Stack Pointer: SP (64-bit)
- Address Pointer: AP (64-bit)
The position pointed to by SP is the address of the top stack element + 8 (the machine word size is 64 bits).
At program startup:
- Initial PC is stored at:
mem[8] ... mem[15] - Initial SP is stored at:
mem[16] ... mem[23] - AP is initialized to 0
Calling Convention
Before calling a function, the caller is responsible for creating the parameter list:
stack: [..., arg_1, arg_2, ..., arg_n]
Then, the caller creates a storage location for the return value:
stack: [..., arg_1, arg_2, ..., arg_n, 0]
Next, push the return address (ret_addr) onto the stack and jump unconditionally to the target function entry. When the program reaches the function entry, the stack must be in this state:
stack: [..., arg_1, arg_2, ..., arg_n, 0, ret_addr]
Inside the target function:
- You may copy and use
arg_1, ..., arg_nas needed - You must assign the function’s return value to the position of the
0(we mandate all return values are integers) - Avoid modifying
ret_addror any positions before it (except the return value storage location)
After the target function returns, the stack state becomes:
stack: [..., arg_1, arg_2, ..., arg_n, ret_ans]
Where ret_ans is the function’s computed result.
Instruction Set
0. Do Nothing: NOP
PC += 1;
1. Halt Instruction: HALT
SP -= 8;
exit(make_interger(mem, SP, SP + 8));
2. Discard Top Stack Element: POP
SP -= 8;
PC += 1;
3. Assign to Address Pointer: POPAP
{
SP -= 8;
AP = make_interger(mem, SP, SP + 8);
}
PC += 1;
4. Push Immediate Value to Stack: PUSHIMM
for(int i = 0; i < 8; i += 1) {
mem[SP + i] = mem[PC + 1 + i];
}
SP += 8;
PC += 9; // Occupies 9 bytes in the program
5. Assign to Stack Pointer: POPSP
{
SP -= 8;
SP = make_interger(mem, SP, SP + 8);
}
PC += 1;
6. Push Stack Pointer Value to Stack: PUSHSP
save_interger(SP, mem, SP, SP + 8);
SP += 8;
PC += 1;
7. Push Program Counter Value to Stack: PUSHPC
save_interger(PC, mem, SP, SP + 8);
SP += 8;
PC += 1;
8. Function Call: CALL
{
long long old_pc = PC;
SP -= 8;
PC = make_interger(mem, SP, SP + 8);
save_interger(old_pc + 1, mem, SP, SP + 8)
SP += 8;
}
9. Load Memory to Stack Top: LOAD
for(int i = 0; i < 8; i += 1) {
mem[SP + i] = mem[AP + i];
}
SP += 8;
PC += 1;
10. Write Stack Top to Memory: SAVE
SP -= 8;
for(int i = 0; i < 8; i += 1) {
mem[AP + i] = mem[SP + i];
}
PC += 1;
11. System Call: SYSCAL
mem[0] = 1; // Trigger system call
// System call arguments are stored in mem[8] ~ mem[63]
// After completion, mem[0] is cleared to 0
// mem[8] ~ mem[63] may contain return results
PC += 1;
12. Signed Addition: ADD
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = val_1 + val_2;
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
13. Signed Subtraction: SUB
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = val_1 - val_2;
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
14. Signed Multiplication: MUL
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = val_1 * val_2;
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
15. Signed Division: DIV
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = val_1 / val_2;
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
16. Signed Modulo: MOD
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = val_1 % val_2;
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
17. Negation: NEG
{
SP -= 8;
long long val = make_interger(mem, SP, SP + 8);
long long ans = -val;
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
18. Logical NOT: NOT
{
SP -= 8;
long long val = make_interger(mem, SP, SP + 8);
long long ans = (!val);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
19. Bitwise AND: AND
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 & val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
20. Bitwise OR: OR
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 | val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
21. Bitwise XOR: XOR
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 ^ val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
22. Logical Right Shift: RSH
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 >> val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
23. Logical Left Shift: LSH
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 << val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
24. Unconditional Jump: JMP
SP -= 8;
PC = make_interger(mem, SP, SP + 8);
25. Conditional Jump: BR
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
if(val_1 != 0) {
PC = val_2 - 1;
}
}
PC += 1;
26. Less Than or Equal: LEQ
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 <= val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
27. Less Than: LT
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 < val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
28. Equal To: EQU
{
SP -= 8;
long long val_2 = make_interger(mem, SP, SP + 8); // mem[SP] ... mem[SP + 7] form a 64-bit integer
SP -= 8;
long long val_1 = make_interger(mem, SP, SP + 8);
long long ans = (val_1 == val_2);
save_interger(ans, mem, SP, SP + 8); // Store result back to stack top
SP += 8;
}
PC += 1;
29. Duplicate Stack Element: DUP
{
SP -= 8;
long long offset = make_interger(mem, SP, SP + 8);
for(int i = 0; i < 8; i += 1) {
mem[SP + i] = mem[SP - 8 * offset - 8 + i];
}
SP += 8;
}
PC += 1;
30. Set Stack Element: POPS
{
SP -= 8;
long long offset = make_interger(mem, SP, SP + 8);
SP -= 8;
val_pos = SP;
for(int i = 0; i < 8; i += 1) {
mem[SP - 8 * offset - 8 + i] = mem[val_pos + i];
}
}
PC += 1;
31. Swap Top Two Stack Elements: EXCH
{
for(int i = 0; i < 8; i += 1) {
unsigned char t = mem[SP - 16 + i];
mem[SP - 16 + i] = mem[SP - 8 + i];
mem[SP - 8 + i] = t;
}
}
PC += 1;
System Calls
Only three system calls are supported:
- Read a character from standard input
- Output a null-terminated string (
\0) to standard output - Enable/disable debug mode
Read a Character from Standard Input
PUSHIMM 8 // Address 8 stores the system call type
POPAP
PUSHIMM 1 // System call 1 = read a character from stdin
SAVE
SYSCAL
// After execution, the character's ASCII code is stored in the 64-bit variable at address 8
PUSHIMM 8
POPAP
LOAD // Push this value onto the stack
Output a String
PUSHIMM 16 // Address 16 stores system call arguments
POPAP
PUSHIMM str_ptr // Store the string's starting address here
SAVE
PUSHIMM 8 // Address 8 stores the system call type
POPAP
PUSHIMM 2 // System call 2 = output a string
SAVE
SYSCAL
// After execution, the number of successfully output characters is stored in the 64-bit variable at address 8
PUSHIMM 8
POPAP
LOAD // Push this value onto the stack
Enable/Disable Debug Mode
PUSHIMM 16 // Address 16 stores system call arguments
POPAP
PUSHIMM 1 // Non-zero = enable debug mode; 0 = disable debug mode
SAVE
PUSHIMM 8 // Address 8 stores the system call type
POPAP
PUSHIMM 3 // System call 3 = toggle debug mode
SAVE
SYSCAL
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simple_stack_machine-0.1.1.tar.gz.
File metadata
- Download URL: simple_stack_machine-0.1.1.tar.gz
- Upload date:
- Size: 11.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.13.12 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba965c787255bc4e129233adc269c9bc6e71dd47d50178abed08a29e7e95371d
|
|
| MD5 |
7cac33ff611677b48449786845de13a7
|
|
| BLAKE2b-256 |
996a089ea8ed401cc677b8e156b24db5fc22168d08b0a38d65c8a097a579a6c4
|
File details
Details for the file simple_stack_machine-0.1.1-py3-none-any.whl.
File metadata
- Download URL: simple_stack_machine-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.13.12 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0434cc18a29931ab5e28b2e34d7242fe052f5972bd26864b10894661f016f4a5
|
|
| MD5 |
82b109d02dce10cb977e544ec594ee4f
|
|
| BLAKE2b-256 |
8b5e0165024cd9aed64db3a01b0baa5cb302dc0b86ea1bc8a0ad0090baaff6b0
|