stork
is a combination of a virtual stack machine, an instruction set for the stack machine, and an assembler to bridge the two, all written in Python. It is derived from Philip Koopman's book, "Stack Computers: the new wave" first published in 1989. An assembler translates input assembly into machine code. The virtual stack machine runs the below instruction set by decoding each 8-bit opcode generated by the assembler.
In addition to running instructions, the runtime provides a system call interface for I/O operations. The following are currently supported:
0x1
: print number from stack- Data stack:
N1 ->
N1
: syscall number (0x1
)
- Result: prints the decimal version of a number from the stack, followed by a newline.
- Data stack:
0x2
: print string from memory- Data stack:
ADDR N1 N2 -> N3
ADDR
: address of string to printN1
: length of string to printN2
: syscall number (0x2
)N3
: number of characters printed (in the success case, should be the same asN1
)
- Data stack:
0x3
: read integer to stack- Data stack:
N1 -> N2
N1
: syscall number (0x3
)N2
: integer result
- Result: reads the decimal version of a number to the stack
- Data stack:
0x4
: print string from memory- Data stack:
ADDR N1 N2 -> N3
ADDR
: address of string to read intoN1
: number of bytes to readN2
: syscall number (0x4
)N3
: number of characters read (in the success case, less than or equal toN1
)
- Data stack:
Any other syscall number replaces the number with an error code, 0x0
.
Another feature of the runtime is a simple debugger. The following is a list of acceptable commands at the debug prompt:
break 0x060a
: set a breakpoint at an address (0x060a
)continue
: continue execution until the next breakpoint or until the program halts.step
: step to the next instructionreset
: reset the virtual stack machine and return to the beginning of the program (0x0600
)jump 0x0701
: jump over instructions to an address (0x0701
)memdump 0x0000 0xff
: print a dump memory at an address (0x0000
) for a given length (0xff
)hexdump
: print a hexdump of the current assembly programdisasm
: disassemble the current instructionprint data
/print return
: print the state of the data/return stacks
The stack machine implements the instructions set forth by Koopman, found here, as well as a few extensions.
Instr- Opcode Data Stack
uction (hex) input -> output Function
. 00 -> Halt execution.
! 01 N1 ADDR -> Store N1 at location ADDR in
program memory
+ 02 N1 N2 -> N3 Add N1 and N2, giving sum N3
- 03 N1 N2 -> N3 Subtract N2 from N1, giving
difference N3
+C 04 N1 N2 C -> N3 C2 Add N1 and N2, with the carry
flag C (C = 1 set, C = 0 not set)
The carry set as a result of the
operation is also pushed to the
stack as C2 with the sum N3.
-C 04 N1 N2 C -> N3 C2 Subtract N2 from N1, with the
not of the carry flag C (C = 1 set,
C = 0 not set) The carry set as a
result of the operation is also
pushed to the stack as C2 with the
difference N3.
>R 06 N1 -> Push N1 onto the return stack
@ 07 ADDR -> N1 Fetch the value at location
ADDR in program memory,
returning N1
AND 08 N1 N2 -> N3 Perform a bitwise AND on N1 and
N2, giving result N3
DROP 09 N1 -> Drop N1 from the stack
DUP 0A N1 -> N1 N1 Duplicate N1, returning a
second copy of it on the stack
OR 0B N1 N2 -> N3 Perform a bitwise OR on N1 and
N2, giving result N3
OVER 0C N1 N2 -> N1 N2 N1 Push a copy of the second
element on the stack, N1, onto
the top of the stack
R> 0D -> N1 Pop the top element of the
return stack, and push it onto
the data stack as N1
SHR 0E N1 N2 -> N3 Perform a right shift of N2
by N1 bits, giving result N3
SHL 0F N1 N2 -> N3 Perform a left shift of N2
by N1 bits, giving result N3
SWAP 10 N1 N2 -> N2 N1 Swap the order of the top two
stack elements
XOR 11 N1 N2 -> N3 Perform a bitwise eXclusive OR
on N1 and N2, giving result N3
[IF] 12 N1 -> If N1 is false (value is 0)
perform a branch to the address
in the next program cell,
otherwise continue
[CALL] 13 -> Perform a subroutine call to
the address in the next program
cell
[EXIT] 14 -> Perform a subroutine return
[LIT] 15 -> N1 Treat the value in the next
program cell as an integer
constant, and push it onto the
stack as N1
[SYS] 16 ... N1 -> ... Perform a system call, with
the system call number as N1.
(See above)
[NOP] 17 -> No operation.
Before the compiler runs, the preprocessor indexes labels to replace them in code with addresses. Labels are of the format identifier:
, where identifier
is any set of ASCII letters and numbers that do not correspond to an instruction. Note that identifiers must start with a letter. It also parses include
directives and appends those files to the end of the program. The assembler converts instruction source files into binary files readable by the Virtual Stack Machine. Newlines ("\n"
) delineate between instructions in source files. Tabs ("\t"
) and spaces (" "
) can be used for formatting. Comments are indicated by #
; the assembler ignores everything after a #
up to the next newline.
Example source files are included in the examples/
directory of the repository.