NOTE: Planning upgrades around 8pm-10pm UTC today. Expect some downtime and possibly read-only wiki. Avoid major editing sessions during that time.
2 septendigits, 289 septendigytes
2 septendigits, 289 septendigytes is another assembly-like programming language sort of like 2 undigits, 121 undigytes, 7 heptits, 49 heptytes and 3 pentits, 25 pentytes made by User:Mutasimos. There is a slight chance I won't continue going up the memory size because it just removes the tight memory constraints of 2 trits, 3 trytes, the first language in the family that didnt use base-2.
Terminology
- Septendigit
- A single base-17 / septendecimal digit. We use these characters for the 17 digits: 0123456789ABCDEFG.
- Septendigyte
- Two septendigits, and also the word size of the machine.
- The 0s prefix
- Throughout this page the 0s prefix is added to numbers if it is written in septendecimal.
ISA
| Septendecimal | Register | What it is | Initial value |
|---|---|---|---|
| 0s0 | r0 | General purpose register | 0s00 |
| 0s1 | r1 | General purpose register | 0s00 |
| 0s2 | r2 | General purpose register | 0s00 |
| 0s3 | r3 | General purpose register | 0s00 |
| 0s4 | r4 | General purpose register | 0s00 |
| 0s5 | r5 | General purpose register | 0s00 |
| 0s6 | r6 | General purpose register | 0s00 |
| 0s7 | r7 | General purpose register | 0s00 |
| 0s8 | r8 | General purpose register | 0s00 |
| 0s9 | r9 | General purpose register | 0s00 |
| 0sA | ra | General purpose register | 0s00 |
| 0sB | rb | General purpose register | 0s00 |
| 0sC | rc | General purpose register | 0s00 |
| 0sD | rd | General purpose register | 0s00 |
| 0sE | re | General purpose register | 0s00 |
| 0sF | sp | Stack pointer register | 0sGG |
| 0sG | bp | Base pointer register | 0s00 |
| Opcode | Instruction | What it does |
|---|---|---|
| 0s00 | nop | Do nothing (NOP) |
| 0s01 | hlt | Halt the program |
| 0s02 - 0s0G | Reserved | |
| 0s10 | mov [dst] [src] | Sets [dst] to the value in [src] |
| 0s11 | mov [dst] [imm] | Sets [dst] to [imm] |
| 0s12 | ldr [dst] [abs] | Loads value from memory into [dst] where address is [abs] |
| 0s13 | ldr [dst] [reg] | Loads value from memory into [dst] where address is in [reg] |
| 0s14 | str [abs] [src] | Store value from [src] into memory where address is [abs] |
| 0s15 | str [reg] [src] | Store value from [src] into memory where address is in [reg] |
| 0s16 | push [src] | Pushes register [src] onto the stack |
| 0s17 | push [imm] | Pushes [imm] onto the stack |
| 0s18 | pop [dst] | Pops the top of the stack into [dst] |
| 0s19 - 0s1G | Reserved | |
| 0s20 | add [dst] [src] | Adds the value in [src] to [dst] (can wrap) |
| 0s21 | sub [dst] [src] | Subtracts the value in [src] from [dst] (can wrap) |
| 0s22 | mul [dst] [src] | Multiplies [dst] with [src] (can wrap) |
| 0s23 | div [dst] [src] | Divides [dst] by [src] (crashes on division by zero) |
| 0s24 | mod [dst] [src] | Remainder of division of [dst] by [src] (crashes on division by zero) |
| 0s25 | add [dst] [imm] | Adds [imm] to [dst] (can wrap) |
| 0s26 | sub [dst] [imm] | Subtracts [imm] from [dst] (can wrap) |
| 0s27 | mul [dst] [imm] | Multiplies [dst] by [imm] (can wrap) |
| 0s28 | div [dst] [imm] | Divides [dst] by [imm] (crashes on division by zero) |
| 0s29 | mod [dst] [imm] | Remainder of division of [dst] by [imm] (crashes on division by zero) |
| 0s2A | neg [dst] | Negates [dst] (two's complement, can wrap) |
| 0s2B | dnot [dst] | Sets [dst] to its digit-wise complement (each septendigit d becomes 16 - d) |
| 0s2C | dmin [dst] [src] | Sets [dst] to whichever of [dst] and [src] is smaller |
| 0s2D | dmax [dst] [src] | Sets [dst] to whichever of [dst] and [src] is larger |
| 0s2E | dmin [dst] [imm] | Sets [dst] to whichever of [dst] and [imm] is smaller |
| 0s2F | dmax [dst] [imm] | Sets [dst] to whichever of [dst] and [imm] is larger |
| 0s2G | Reserved | |
| 0s30 | jiz [reg] [abs] | If [reg] is 0, go to [abs] |
| 0s31 | jnz [reg] [abs] | If [reg] is not 0, go to [abs] |
| 0s32 | jeq [a] [b] [abs] | If [a] register equals [b] register, go to [abs] |
| 0s33 | jne [a] [b] [abs] | If [a] register does not equal [b] register, go to [abs] |
| 0s34 | jlt [a] [b] [abs] | If [a] register is less than [b] register, go to [abs] (unsigned comparison) |
| 0s35 | jgt [a] [b] [abs] | If [a] register is greater than [b] register, go to [abs] (unsigned comparison) |
| 0s36 | jlti [a] [b] [abs] | If [a] register is less than [b] register, go to [abs] (signed comparison) |
| 0s37 | jgti [a] [b] [abs] | If [a] register is greater than [b] register, go to [abs] (signed comparison) |
| 0s38 - 0s3G | Reserved | |
| 0s40 | jmp [abs] | Jumps to the specified absolute address |
| 0s41 | call [abs] | Calls the specified absolute address, pushing the next instruction's address onto the stack |
| 0s42 | ret | Pops the new instruction pointer off the stack |
| 0s43 - 0s4G | Reserved | |
| 0s50 | out17 [reg] | Writes [reg] as a septendecimal number, prefixed with 0s, to standard out |
| 0s51 | out10 [reg] | Writes [reg] as a decimal number, without any prefix, to standard out |
| 0s52 | outc [reg] | Writes [reg] as a single character according to the text encoding |
| 0s53 | outc [imm] | Writes [imm] as a single character according to the text encoding |
| 0s54 | outs [ptr_reg] [len_reg] | Writes the number of septendigytes given by [len_reg] from [ptr_reg] according to the text encoding |
| 0s55 | outs [ptr_reg] [len_imm] | Writes the number of septendigytes given by [len_imm] from [ptr_reg] according to the text encoding |
| 0s56 - 0sGG | Reserved |
Whenever an instruction takes two registers, the first register is the higher septendigit and the second register is the lower septendigit of an entire septendigyte, allowing for 2 septendigytes instead of 3 septendigytes of memory. For example, assembling "mov r0, r1" into machine code would give us "0s10 0s01". For instructions that take one register and an immediate or address (like ldr, str, push, pop, neg, dnot, outc, or the immediate arithmetic instructions), the register is encoded as the lower septendigit of the septendigyte following the opcode, with the upper septendigit unused.
Memory is 289 septendigytes, addressed 0s00 through 0sGG, and the instruction pointer wraps around at the end back to 0s00, allowing for more "complicated" programs (but the bigger memory space should be more than enough). Execution keeps looping unless a HLT instruction is encountered.
Text Encoding
Septendigytes 0s00 through 0sF0 (0 through 255 in decimal) map directly to their corresponding Latin-1 / extended ASCII byte value.
The remaining septendigytes, 0sF1 through 0sGG, are given over to a small set of box-drawing and mathematical symbols, since 17² conveniently leaves 33 codepoints spare above the byte range:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | G | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| F | ═ | ║ | ╔ | ╗ | ╚ | ╝ | ╠ | ╣ | ╦ | ╩ | ╬ | █ | ▀ | ▄ | ▌ | ▐ | |
| G | ∴ | ─ | │ | ┌ | ┐ | └ | ┘ | ├ | ┤ | ┬ | ┴ | ┼ | ∵ | ≤ | ≥ | ≠ | ≈ |
Examples
The starting septendigytes in memory in the examples given here are separated by spaces, and the ones which start with 0s are in septendecimal and if not they are in decimal. There is also comments which start with ";".
Hello, world!
0s11 0s00 0s07 ; mov r0, msg (0s07) 0s55 0s00 14 ; outs ra, 14 0s01 ; hlt 0s44 0s5g 0s66 ; msg: ds "Hello, World!\n" 0s66 0s69 0s2a 0s1f 0s52 0s69 0s6c 0s66 0s5f 0s1g 0s0a
truth-machine
0s12 0s00 0s09 ; ldr r0, val (0s09)
; loop:
0s51 0s00 ; out10 r0
0s31 0s00 0s03 ; jnz r0, loop
0s01 ; hlt
;; Change this value to 1 for the other outcome
0 ; val: ds 0
= Fibonacci sequence
0s11 0s00 0 ; mov r0, 0
0s11 0s01 1 ; mov r1, 1
; loop:
0s51 0s00 ; out10 r0
0s53 0s0a ; outc '\n'
0s10 0s20 ; mov r2, r0
0s10 0s01 ; mov r0, r1
0s20 0s12 ; add r1, r2
0s34 0s12 0s14 ; jlt r1, r2, drain (0s14) ; if rb < rc, it wrapped
0s40 0s06 ; jmp loop (0s06)
; drain:
0s51 0s00 ; out10 r0 ; print the last still-valid term
0s53 0s0a ; outc '\n'
0s01 ; hlt
This program prints numbers until 223, otherwise the number would overflow.