We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
8 icosigits, 200 icosigytes
8 icosigits, 200 icosigytes is yet another programming language designed by PSTF.
Author's comment: Recently, I feel like I'm really good at creating 'assembly' languages. And as of July 2026, this language has the largest base among the assembly language series—we used a base-20 system.
Terminology
- Icosigit: a base-20 digit with values from 0 to 19, represented by the Indo-Arabic numbers 0 to 9 and the Latin letters A to J.
- Icosigyte: One byte equals two icosigits.
- Word: One word equals four icosigytes. All registers are one-word wide.
- Memory is 200-icosigyte long, addressed from (0)20 to (9J)20.
- Vigintenary: Base-20.
All numeric literals must be written in vigintenary.
VCP-400 Codepage
0123456789abcdefghij
0␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓
1␔␕␖␗␘␙␚␛␜␝␞␟ !"#$%&'
2()*+,-./0123456789:;
3<=>?@ABCDEFGHIJKLMNO
4PQRSTUVWXYZ[\]^_`abc
5defghijklmnopqrstuvw
6xyz{|}~␡⎕⍈⌸⌹⌺⌻⌼⍁⍂⍃⍄⍇
7⍌⍍⍐⍓⍔⍗⍞⍠⍯⍰⌷⌽⌾⌿⍀⍅⍆⍊⍉⍋
8 ¡¢£¥¦§¨©ª«¬␦®¯°±²³´
9µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ
aÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜ
bÝÞßàáâãäåæçèéêëìíîïð
cñòóôõö÷øùúûüýþÿ─│┌┐└
d┘├┤┬┴┼━┃┏┓┗┛┣┫┳┻╋╱╲╳
e═║╔╗╚╝╠╣╦╩╬╭╮╯╰╴╵╶╷⍬
f←↑→↓↖↗↘↙↔↕█▀▄▌▐ΑΒΓΔΕ
gΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩα
hβγδεζηθικλμνξοπρςστυ
iφχψω⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞√∑∈∏
j∫∴∵≤≥≠≈░▒▓①②③④⑤⑥⑦⑧⑨⑩
CPU Architecture
Registers
20 general‑purpose registers: R0 – R19.
- R0 is hardwired to zero.
- Program Counter (PC): holds the address of the next instruction (8 icosigits).
- Stack Pointer (SP): initialised to 199 (base‑20 9J) and grows downward.
- Status Register (SR): holds flags – Zero (Z), Sign (N), Carry (C), Overflow (V).
Memory Access
- LOAD / STORE transfer a full word (8 icosigits) to/from 4 consecutive icosigytes.
- LOADB / STOREB transfer a single icosigyte (2 icosigits) to/from the lower part of a register (zero‑extended for loads).
Instruction Set
All instructions are exactly 4 icosigytes (8 icosigits) and must be aligned to a 4‑icosigyte boundary. Encoding like this:
+--------+--------+--------+--------+ | Opcode | Oper1 | Oper2 | Oper3 | +--------+--------+--------+--------+
================COMMAND SHEET================ 00. NOP: No operation. 01. ADD Rd, Rs, k: Set Rd to Rs + k, where Rd and Rs are both registers, and k is either a register or an immediate value. 02. SUB Rd, Rs, k: Set Rd to Rs - k. 03. MUL Rd, Rs, k: Set Rd to Rs × k. 04. DIV Rd, Rs, k: Set Rd to Rs ÷ k, round down towards 0. 05. MOD Rd, Rs, k: Set Rd to Rs mod k. 06. AND Rd, Rs, k: Set Rd to Rs AND k. 07. OR Rd, Rs, k: Set Rd to Rs OR k. 08. XOR Rd, Rs, k: Set Rd to Rs XOR k. 09. NOT Rd, Rs: Set Rd to NOT Rs. 0a. LDW Rd, [Rs+k]: Set Rd to the value at the address Rs + k, where k is an immediate number. 0b. STW [Rd+k], Rs: Set [Rd + k] to Rs. 0c. LDB Rd, [Rs+k]: Set the lowest 2 icosigits of Rd to the value at the address Rs + k. 0d. STB [Rd+k], Rs: Set [Rd + k] to the lowest 2 icosigits of Rs. 0e. CMP Rs1, Rs2: Set the flag register to the result of Rs1 minus Rs2. 0f. JMP label: Just jump to the label, where label is either really a label or a memory address. 0g. ................: Not assigned. 0h. ................: Not assigned. 0i. ................: Not assigned. 0j. ................: Not assigned. 10. JEQ label: If the zero flag is set, jump to the specified label. 11. JNE label: If the zero flag is cleared, jump to the specified label. 12. JGT label: If both the zero flag and the sign flag are cleared, jump to the specified label. 13. JLT label: If the sign flag is set, jump to the specified label. 14. JNL label: If the sign flag is cleared, jump to the specified label. 15. JNG label: If one of the zero flag and the sign flag is set, jump to the specified label. 16. PUSH Rs: Decrease from the stack pointer by 4, then set the value there to the value of Rs, where Rs is a register. 17. POP Rd: Assign the value of Rd to the top of the stack, then increase the stack pointer by 4. 18. OPT Rs: Output the value of Rs in vigintenary to the console. 19. WRT Rs: Output the lowest 2 icosigits of Rs as character to the console. 1a. IPT Rd: Input 8 icosigits of value to Rd. 1b. HLT Rd: Halt. 1c. MOV Rd, k: Set the value of Rd to k, where k is either a register or an immediate number. 1d. REA Rd: Input a character to the lowest 2 icosigits of Rd. 1e. OPD Rs: Output the value of Rs in decimal to the console. 1f. IPD Rd: Input 10 decimal digits of value to Rd.
Assembler Syntax and Directive
Literal and Memory Access
- Immediate numbers start with a hashtag and are written in vigintenary.
- Character constants are enclosed in single quotes. Multiple character constants can be enclosed in double quotes.
- Memory addresses are written in the format [base register + #offset]. If the offset is 0, the plus sign and everything after it up to the right bracket can be omitted.
Directives
.text – start of code section. .data – start of data section. .byte expr – store one icosigyte (2 base‑20 digits). .word expr – store one word (8 base‑20 digits, 4 icosigytes). .ascii "string" – store a null‑terminated string in VCP‑400 (assembler calculates codes). .align 4 – align to next 4‑icosigyte boundary (instruction alignment).
Label
Program labels usually end with a colon and are placed at the beginning of a piece of code, indicating its location, and can be used to replace the actual address during jumps.
Example Programs
"Hello, World!" + Loop Counter from 0 to 9
; -------------------------------------------------------------
; Data section
; -------------------------------------------------------------
.data
msg:
.ascii "Hello, World!" ; VCP-400 encoded string
.byte 0 ; null terminator
newline:
.byte 0x0A ; LF (line feed)
; -------------------------------------------------------------
; Code section
; -------------------------------------------------------------
.text
start:
; Print the string
MOV R1, #msg ; R1 = address of 'msg'
loop_str:
LOADB R2, [R1, #0] ; load one character (byte)
CMP R2, R0 ; compare with R0 (hardwired zero)
JEQ end_str
WRT R2 ; output character
ADD R1, R1, #1 ; increment pointer
JMP loop_str
end_str:
; Print newline
MOV R2, #0x0A ; LF code (10 in decimal)
WRT R2
; Count from 0 to 9 and print
MOV R3, #0 ; counter = 0
MOV R4, #A ; limit = 10 (base-20 'A')
loop_count:
CMP R3, R4
JEQ done
OPT R3 ; print numeric value of R3 (vigintenary)
; print space
MOV R2, #0x20 ; space
WRT R2
ADD R3, R3, #1 ; counter++
JMP loop_count
done:
HLT
Computational Class
The programming language has:
- Conditional branching – JZ and JNZ based on CMP, allowing selection and loops.
- Arithmetic – ADD, SUB to modify counters.
- Arbitrary memory access – LOAD/STORE with base+offset, providing random access storage.
- Indirect control flow – JMP and stack operations (PUSH/POP) allow subroutines.
These features are sufficient to simulate a Minsky machine (two counters with increment, decrement, and zero‑test). For example:
- Use R1 as counter.
- ADD R1, R1, #1 → increment.
- SUB R1, R1, #1 → decrement.
- CMP R1, R0 / JZ label → zero test.
Because a two‑counter Minsky machine is Turing‑complete, the language is as well.
Summary
- CPU: 8‑icosigit registers, 200‑icosigyte memory (byte‑addressable).
- Codepage: VCP‑400 (400 characters, includes ASCII, Latin‑1, box‑drawing, Greek, math).
- I/O: OPT for vigintenary numbers, WRT for VCP‑400 characters, and OPD for decimal numbers.
- Instruction set: fixed‑length, RISC‑style, with all necessary operations for general‑purpose computing.
- Turing‑complete: proven by simulation of a Minsky machine.