GitPedia

16bitjs

๐Ÿ’ป A 16-bit virtual machine, including assembly language with 37 instructions, binary assembler, and a step through debugger

From francisrstokesยทUpdated April 25, 2026ยทView on GitHubยท

- The definition of an [assembly language](https://en.wikipedia.org/wiki/Assembly_language) with 37 instructions - An [assembler](https://en.wikipedia.org/wiki/Assembly_language#Assembler) to transform a `*.asm` file into a binary executable format - A small [virtual machine](https://en.wikipedia.org/wiki/Virtual_machine) which simulates a basic computer architecture: A memory space, stack, and CPU with 4 general purpose registers and a fetch-decode-execute cycle - A compiler for the [brainfuck]... The project is written primarily in JavaScript, distributed under the MIT License license, first published in 2017. Key topics include: 16-bit, assembly, assembly-language, cpu, javascript.

16-bit Javascript VM

The project consists of:

  • The definition of an assembly language with 37 instructions
  • An assembler to transform a *.asm file into a binary executable format
  • A small virtual machine which simulates a basic computer architecture: A memory space, stack, and CPU with 4 general purpose registers and a fetch-decode-execute cycle
  • A compiler for the brainfuck language directly to the binary executable format

The virtual machine can run in two modes: run (default) and step. Run mode simply run` the entire program in sucession. Step mode runs the program in a debug environment, pausing before executing each instruction and displaying the entire state of the machine.

Running the VM

Running the assembler

node src/assembler -i {infile.asm} -o {outfile.bin}

Running the brainfuck compiler

node src/compilers/bf -i {infile.bf} -o {outfile.bin}

Running a program

node src -p {program.bin}

Assembly language

The assembly language consists of 16 distinct instructions which support all the basic features you would expect: Arithmetic, Loading values to and from memory/registers, conditional/non conditional jumps, functions, system calls for reading and and writing stdio etc.

Comments start with a ; character.
Labels can be defined by :some_label_name on their own line and then referenced in an instruction like so: LDV16 A, :some_label_name.

Examples

A couple of examples illustrating the language can be found in the asm/ folder.

Instruction set

Real instructions

InstructionArguments16 bit representationDescription
MVRD, S, VVVVVVVVVSSDDIIIIAdd a sign-extended byte to value at source register and move it to destination register
MVVD, V, OVVVVVVVVOODDIIIIMove or add an immediate value into destination register, depending on O.
LDAD, MMMMMMMMMMMDDIIIILoad a value from memory into destination register using direct address
STAD, MMMMMMMMMMMDDIIIIStore the value in destination register into memory using direct address
LDRD, S[, V]VVVVVVVVSSDDIIIILoad from memory into the destination register using the source register as a base address
STRD, S[, V]XXXXXXXXSSDDIIIIStore the value in source register into the memory using the destination register as a base address
ATHD, S, O, M, BBBBMOOOOSSDDIIIIPerform an arithmetic operation on the source and destination registers. O specifies the operation (listed below) and M is the mode, where 0 = place result in destination register and 1 = place result in source register. If the instruction is right or left shift then B specifies the shifting value
CALDXXXXXXXXXXDDIIIICall a function in memory pointed at by the destination register
JCPD, S, A, OXXXOOOAASSDDIIIIJump to memory address pointed at by the address register, depending on the comparison specified by the O operation of the destination register and the source register. Operation table specified below.
PSHSXXXXXXXXSSXXIIIIPush the value in source register onto the stack
POPDXXXXXXXXXXDDIIIIPop the stack into the destination register
JMPMVVVVVVVVVVVVIIIIAdd a signed 12-bit offset to the program counter.
JMRSXXXXXXXXSSXXIIIIJump to the address pointed at by the source register
NOAOXXXXXXXXOOOOIIIINo Argument calls. This includes SYS, HLT and RET, which have pseudo instructions

Pseudo Instructions

Pseudo instructions are prepocessed by the assembler and expanded into combinations of the real instructions.

InstructionArgumentsExpanded lengthDescription
MVID, V1Set a zero-extended immediate value to destination register
LDVD, S, V1Alias for MVI to keep retro-compatibility in assembly source
MUID, V1Set a 8-bit left shifted immediate value to destination register
ADID, S1Add a sign-extended immediate value to destination register
INCD1Alias for ADI 1
DECD1Alias for ADI -1
AUID, S1Add a 8-bit left shifted immediate value to destination register
MOVD, S1Copy value at source register to destination register
RET1Return from function
HLT1Program halt
SYS1Perform a system call. This is described below in more detail.
LDMD, S1Alias for STA to keep retro-compatibility in assembly source
LDPD, S1Alias for STR without offset to keep retro-compatibility in assembly source
ADDD, S1Add destination to source and store the result in destination
ADDSD, S1Add destination to source and store the result in source
SUBD, S1Subtract destination from source and store the result in destination
SUBSD, S1Subtract destination from source and store the result in source
MULD, S1Multiply destination with source and store the result in destination
MULSD, S1Multiply destination with source and store the result in source
DIVD, S1Divide destination by source and store the result in destination
DIVSD, S1Divide destination by source and store the result in source
LSFD, A1Binary shift left the destination register by amount A (max 7)
LSRD, A1Binary shift right the destination register by amount A (max 7)
ANDD, S1Binary and the destination and source, and store the result in the destination
ORD, S1Binary or the destination and source, and store the result in the destination
XORD, S1Binary exclusive-or the destination and source, and store the result in the destination
NOTD1Binary not (invert) the destination
LDV16D, V2Load a 16 bit value into destination
SWPD, S3Swap the values in the source and destination registers
JEQD, S, A1Jump to address A if value in destination register is equal to the source register.
JNED, S, A1Jump to address A if value in destination register is not equal to the source register.
JLTD, S, A1Jump to address A if value in destination register is less than the source register.
JGTD, S, A1Jump to address A if value in destination register is greater than the source register.
JLED, S, A1Jump to address A if value in destination register is less than or equal to the source register.
JGED, S, A1Jump to address A if value in destination register is greater than or equal to the source register.
JZED, S, A1Jump to address A if value in destination register is zero.
JNZD, S, A1Jump to address A if value in destination register is not zero.

Arithmetic Operation table

OperationValue
Add0000
Subtract0001
Multiply0010
Divide0011
Increment0100
Decrement0101
Left shift0110
Right shift0111
And1000
Or1001
Xor1010
Not1011

Conditional Jump Operation table

OperationValue
Equal000
Not equal001
Less than010
Greater than011
Less than or equal100
Greater than or equal101
Zero110
Not zero111

System calls

A system call in the VM allows the program to ask resources outside of it's context, such as communication with stdin and stdout. System calls are passed off to the os module and can return their results directly into the CPUs registers.

System callCall code
Write to stdout0000
Read from stdin buffer0001
I/O Modes
Output
ModeDescriptionBCD
0Output register in decimalDestinationMode
1Output register in binaryDestinationMode
2Output register in hexDestinationMode
3Output register as a characterDestinationMode
4Output string in memory address pointed to by registerStart addressMode
Input
ModeDescriptionBCD
0Read single character value of input into registerDestination--

Debugger

Running with the step option (node src -p {program.bin} --step), enables the step through debugger, giving a overview of memory, stack and registers as the program executes.

Memory:
0000    08c1 0009 0008 0009 1d01 030b 1c81 030b 1d41 030b 1941 030b 0281 030b 000a 16c1
0010    0009 0008 0009 1d01 030b 1c81 030b 1d41 030b 1941 030b 0281 030b 000a 0081 000b
0020    2781 0009 0008 0281 0291 0009 1141 030b 1c41 030b 1d41 030b 1841 030b 1b01 030b
0030    0801 030b 18c1 030b 1a01 030b 1941 030b 18c1 030b 1ac1 030b 0e81 030b 000a 10d7
0040    11a1 0089 0008 1351 0049 0008 0049 0010 000a 1357 00e1 0089 0008 0009 1981 030b
0050    1841 030b 1b01 030b 1cc1 030b 1941 030b 0281 030b 000a 02c1 0291 0009 1381 030b
0060    1bc1 030b 1d01 030b 0801 030b 1141 030b 1c41 030b 1d41 030b 1841 030b 1b01 030b
0070    0801 030b 18c1 030b 1a01 030b 1941 030b 18c1 030b 1ac1 030b 0e81 030b 000a 20d7
0080    21a1 0089 0008 2351 0049 0008 0049 0010 000a 2357 2421 0089 0008 04a1 0089 0008
0090    0009 1981 030b 1841 030b 1b01 030b 1cc1 030b 1941 030b 0281 030b 000a 000c 0000
00a0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00b0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00c0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00d0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00e0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00f0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0100
Page 1/255

Stack:
0000    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0010    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0020    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0030    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0040    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0050    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0060    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0070    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0080    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0090    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00a0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00b0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00c0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00d0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00e0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
00f0    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

Instruction: (LDV) 0000100011000001
Registers:
A: 0000 B: 0000 C: 0000 D: 0000 IP: 0000        SP: 0000

(s)tep (e)xit (n)ext / (p)revious memory page >>>

Contributors

Showing top 6 contributors by commit count.

View all contributors on GitHub โ†’

This article is auto-generated from francisrstokes/16bitjs via the GitHub API.Last fetched: 6/28/2026