Examine individual changes

Abuse Filter navigation (Home | Recent filter changes | Examine past edits | Abuse log)
Jump to navigation Jump to search

This page allows you to examine the variables generated by the Abuse Filter for an individual change.

Variables generated for this change

VariableValue
Edit count of the user (user_editcount)
2
Name of the user account (user_name)
'Oscarlo'
Age of the user account (user_age)
3320
Page ID (page_id)
24813
Page namespace (page_namespace)
0
Page title (without namespace) (page_title)
'Tina'
Full page title (page_prefixedtitle)
'Tina'
Action (action)
'edit'
Edit summary/reason (summary)
''
Old content model (old_content_model)
'wikitext'
New content model (new_content_model)
'wikitext'
Old page wikitext, before the edit (old_wikitext)
''''Tina''' ('''T'''his '''i'''s '''n'''ot '''a'''ssembly) is an [[esoteric programming language]] with an assembly-like syntax and a deliberately overpowered “ALU matrix” instruction format. A Tina program is assembled into a linear list of instructions plus an initial memory image, then executed on a simple machine with unbounded signed integer cells and non-negative addresses. A reference interpreter/assembler is implemented in Python. ==Etymology== “Tina” is a backronym for “This is not assembly”. The language is intentionally close to assembly in feel (labels, cells, jumps, stack operations), while also featuring higher-level conveniences like conditional-branching ALU instructions and built-in memory/string operations. ==Computational model== Tina executes a sequence of instructions over: * An unbounded memory of signed integers (cells default to 0). * A program counter (PC) indexing the instruction list. * Optional stack/frame support using two conventional cells named <code>SP</code> and <code>FP</code> when stack instructions are used. Memory addresses must be non-negative; attempting to access a negative address is an error. ==Syntax overview== * One instruction or directive per line. * Labels are written as <code>name:</code> (multiple labels may precede a statement). * Comments begin with <code>;</code> and continue to end of line. * Mnemonics are case-insensitive (the reference interpreter uppercases internally). Example line: <pre> loop: ADD32SNEZ #1, counter, loop ; increment and loop while counter != 0 (example only) </pre> ==Assembler directives== These directives allocate and/or initialize memory in the initial image. ; <code>.cell name = value</code> : Allocate one cell, optionally initialized. If omitted, initializes to 0. Character literals like <code>'A'</code> are allowed (one character only). ; <code>.block name, n</code> : Allocate <code>n</code> zero-initialized cells. ; <code>.data name v1, v2, ...</code> : Allocate a list of cells initialized to the given integers (supports decimal/hex via Python-style <code>0x</code>). ; <code>.zstr name "text"</code> : Allocate a null-terminated byte string. Each character must be in 0..255, and a trailing 0 cell is appended. Symbols defined by these directives can be used as memory operands and (in immediates) as numeric addresses. ==Operands and addressing modes== Tina instructions take ''operands'' which can be immediate values or memory references. ===Immediate=== ; <code>#n</code> : Immediate integer <code>n</code>. In immediates, <code>n</code> may be: :* a numeric literal (<code>#10</code>, <code>#0xFF</code>, <code>#-3</code>) :* a memory symbol (<code>#MSG</code> yields MSG’s address) :* a code label (<code>#loop</code> yields the instruction index of <code>loop:</code>) :* optionally with a small decimal offset: <code>#LABEL+3</code>, <code>#var-1</code> Immediates are not addressable (you cannot write to <code>#...</code>). ===Memory (direct / indexed)=== ; <code>x</code> : Direct cell at address <code>x</code> (where <code>x</code> is a symbol or numeric address). ; <code>x+K</code> / <code>x-K</code> : Direct cell at address <code>x+K</code> (offset is decimal in the reference interpreter). ===Indirect (pointer)=== ; <code>@x</code> : Indirect cell at address <code>mem[x]</code>. ; <code>@x+K</code> : Indirect indexed cell at address <code>mem[x]+K</code>. In pseudocode, reading <code>@p+2</code> means <code>mem[mem[p] + 2]</code>. ==ALU instruction matrix== Most arithmetic/logic operations are expressed with a single general pattern: <pre> <OP><WIDTH><OVF><COND> src, dst [, label] </pre> Execution model: 1. Read <code>src</code> and <code>dst</code>. 2. Compute a new value (<code>new_dst</code>) using <code>OP</code>. 3. Write <code>new_dst</code> back to <code>dst</code>. 4. If a condition suffix (<code>COND</code>) is present, branch to <code>label</code> if the condition is true when applied to <code>new_dst</code>; otherwise fall through. ===Base operations (OP)=== The reference interpreter implements these ALU base ops: * Data movement/arithmetic: <code>MOV ADD SUB MUL DIV MOD</code> * Convenience arithmetic: <code>INC DEC NEG ABS MIN MAX</code> * Bitwise: <code>AND OR XOR XNOR NOR NAND NOT</code> * Shifts/rotates: <code>SHL SHR SAR ROL ROR</code> * Bit tricks: <code>POPCNT CLZ CTZ</code> * Comparisons (write results into <code>dst</code>): <code>CMPEQ CMPLT CMPLE CMPGT CMP3</code> * Swap: <code>SWP</code> (special: swaps two addressable operands; immediates are not allowed) === Width (WIDTH) === Optional: <code>8</code>, <code>16</code>, <code>32</code>, <code>64</code>. If provided, results are interpreted as signed integers of that width. If omitted, Tina uses unbounded integers (except some bit-operations internally assume a 64-bit “view” for operations like rotate/counting, per the reference interpreter). === Overflow/checked behavior (OVF) === Only meaningful when a width is present: * (none): wrap to the given width (two’s complement wrap) * <code>S</code>: saturating (clamp to min/max representable signed value) * <code>C</code>: checked (raises an error on overflow) === Conditional suffix (COND) === If present, the instruction takes an additional <code>label</code> argument. Simple conditions (applied to <code>new_dst</code>): * <code>LEQ</code> (≤ 0), <code>EQZ</code> (== 0), <code>NEZ</code> (!= 0) * <code>LTZ</code>, <code>GEZ</code>, <code>GTZ</code> * <code>ODD</code>, <code>EVN</code> * <code>POS</code> (≥ 0), <code>NEG</code> (< 0) Bit-test conditions: * <code>BSET0</code>..<code>BSET63</code> (branch if bit k is 1) * <code>BCLR0</code>..<code>BCLR63</code> (branch if bit k is 0) === SUBLEQ alias === <code>SUBLEQ</code> is an alias for: <pre> SUBLEQ src, dst, label ≡ SUB src, dst, label (branch if new_dst <= 0) </pre> This makes it easy to write classic SUBLEQ-style code while still having a larger instruction set available. ==Control flow and non-ALU instructions== These instructions are not expressed through the ALU matrix: * <code>JMP label</code> — unconditional jump * <code>JMPI op</code> — jump to the value read from <code>op</code> * <code>BR op, label</code> — branch if <code>op != 0</code> Dedicated branches (branch based on <code>op</code>): * <code>BZ</code>, <code>BNZ</code>, <code>BLTZ</code>, <code>BLEQZ</code>, <code>BGEZ</code>, <code>BGTZ</code>, <code>BODD</code>, <code>BEVN</code> Loop helper: * <code>DJNZ dst, label</code> — decrement <code>dst</code>, branch if the result is not zero Misc memory ops: * <code>ZAP dst</code> — set destination to 0 * <code>XCH a, b</code> — exchange two addressable operands (no immediates) ==Stack and calls== These instructions use a conventional stack in memory and rely on two special cells if used: * <code>.cell SP = ...</code> must exist for <code>PUSH/POP/CALL/RET/ENTER/LEAVE</code> * <code>.cell FP = ...</code> must exist for <code>ENTER/LEAVE</code> Instructions: * <code>PUSH op</code> — store value at <code>mem[SP]</code>, then increment <code>SP</code> * <code>POP dst</code> — decrement <code>SP</code>, then load from <code>mem[SP]</code> into <code>dst</code> * <code>CALL label</code>, <code>CALLI op</code> — push return address, jump * <code>RET</code> — pop return address into PC * <code>ENTER #n</code> — push old FP, set FP = SP, allocate <code>n</code> locals by advancing SP * <code>LEAVE</code> — restore SP = FP, restore old FP from stack ==Built-in memory/string instructions== These are “library-like” instructions implemented directly by the interpreter: * <code>MEMSET dstAddr, byte, n</code> : Set <code>n</code> bytes at address <code>dstAddr</code> to <code>byte & 0xFF</code>. * <code>MEMCPY srcAddr, dstAddr, n</code> : Copy <code>n</code> cells; overlap-safe in the reference interpreter. * <code>MEMCMP aAddr, bAddr, n, dst</code> : Compare <code>n</code> cells lexicographically; write -1/0/1 into <code>dst</code>. * <code>STRLENZ srcAddr, dst</code> : Length of a null-terminated byte string. * <code>STRCPYZ srcAddr, dstAddr</code> : Copy a null-terminated byte string including terminator. * <code>STRCMPZ aAddr, bAddr, dst</code> : Compare two null-terminated byte strings; write -1/0/1. Note: <code>srcAddr</code>/<code>dstAddr</code> operands are read as values (addresses). For example, if <code>p</code> contains 100, then <code>STRLENZ p, len</code> measures from address 100. ==Input and output== Input is byte-based or integer-based and shares a single input stream. * <code>INB dst, labelEOF</code> : Read one byte. On EOF, writes -1 to <code>dst</code> and jumps to <code>labelEOF</code>. * <code>INN dst, labelEOF</code> : Read a signed decimal integer token (skipping whitespace). On EOF (or no integer available), jumps to <code>labelEOF</code> (reference interpreter does not guarantee writing anything to <code>dst</code> in this case). Output: * <code>OUTB op</code> — output low byte (<code>&amp; 0xFF</code>) * <code>OUTD op</code> — output decimal integer text (no newline) * <code>OUTHEX op</code> — output as 64-bit-masked hex with <code>0x</code> prefix * <code>OUTBIN op</code> — output as 64-bit-masked binary with <code>0b</code> prefix * <code>OUTZ symbolOrAddr</code> — output null-terminated bytes starting at that address (note: this takes a symbol/address, not a full addressing-mode operand) * <code>OUTZI op</code> — output null-terminated bytes starting at the address read from <code>op</code> * <code>OUTS op</code> — output a length-prefixed string: <code>addr = op</code>, <code>mem[addr]</code> is length, bytes follow at <code>addr+1..</code> * <code>EOL</code> — output newline ==Debug/abort== * <code>TRAP #code</code> — terminate the program with exit code <code>code</code> * <code>ASSERT op, #code</code> — if <code>op == 0</code>, terminate with <code>code</code> * <code>BREAK</code>, <code>WATCH op</code> — present but treated as no-ops by the reference interpreter (useful with tracing/debugging in other implementations) ==Program termination== * <code>HALT</code> returns exit code 0. * Falling off the end of the instruction list ends execution (exit code 0 in the reference interpreter). * <code>TRAP</code>/<code>ASSERT</code> can terminate with non-zero codes. ==Implementation== The reference implementation is a combined assembler and interpreter written in Python 3. Usage: <pre> python tina.py program.tina &lt; input &gt; output python tina.py program.tina --trace </pre> ==Examples== This is a traditional "Hello World" program in Tina: <pre> .zstr MSG "Hello, world!\n" start: OUTZ MSG HALT </pre> This program is a typical truth machine: <pre> .cell ZERO = 0 .cell C48 = 48 ; '0' .cell ch = 0 .cell tmp = 0 start: INB ch, done OUTB ch MOV ch, tmp SUBEQZ C48, tmp, done ; tmp = ch - 48; if tmp == 0 => input was '0' loop: OUTB ch SUBLEQ ZERO, ZERO, loop ; unconditional jump (classic SUBLEQ trick) done: HALT </pre> This program is a simple cat program: <pre> .cell ZERO = 0 .cell ch = 0 loop: INB ch, done OUTB ch SUBLEQ ZERO, ZERO, loop done: HALT </pre> This program is an implementation of Fizz Buzz: <pre> .cell ZERO = 0 .cell ONE = 1 .cell THREE = 3 .cell FIVE = 5 .cell i = 1 .cell rem = 100 .cell c3 = 3 .cell c5 = 5 .cell f = 0 .cell b = 0 .cell tmp = 0 .cell sum = 0 .zstr SFIZZ "Fizz" .zstr SBUZZ "Buzz" loop: ZAP f ZAP b DJNZ c3, no_fizz MOV THREE, c3 MOV ONE, f no_fizz: DJNZ c5, no_buzz MOV FIVE, c5 MOV ONE, b no_buzz: ; if f != 0 print "Fizz" MOV f, tmp SUBEQZ ZERO, tmp, skip_fizz OUTZ SFIZZ skip_fizz: ; if b != 0 print "Buzz" MOV b, tmp SUBEQZ ZERO, tmp, skip_buzz OUTZ SBUZZ skip_buzz: ; if (f+b)==0 print the number MOV f, sum ADD b, sum SUBEQZ ZERO, sum, print_num SUBLEQ ZERO, ZERO, after_num print_num: OUTD i after_num: EOL ADD ONE, i DJNZ rem, loop HALT </pre> This takes a number as input and calculates its factorial: <pre> .cell ZERO = 0 .cell ONE = 1 .cell n = 0 .cell fact = 1 .cell tmp = 0 start: INN n, eof MOV ONE, fact loop: MOV n, tmp SUBLEQ ONE, tmp, print ; tmp = n-1; if tmp <= 0 => n <= 1 => done MUL n, fact ; fact *= n SUB ONE, n ; n-- SUBLEQ ZERO, ZERO, loop print: OUTD fact EOL HALT eof: HALT </pre> This implements a simple [[Brainfuck]] interpreter: <pre> ; Brainfuck interpreter in Tina ; ; Input format on stdin: ; 1) First line: the Brainfuck program (only ><+-.,[] are kept; other chars ignored) ; 2) Remaining bytes after the newline are used as Brainfuck input for ','. ; ; Notes: ; - Tape cells are treated as 8-bit (wrapping) via ADD8/SUB8. ; - Data pointer moves right/left by 1 cell. Moving to a negative address will error. ; -------- fixed “heap” base addresses (picked far away from our .cell area) -------- .cell PROGBASE = 100000 ; program bytes stored at PROGBASE + i .cell JUMPBASE = 200000 ; matching-bracket table at JUMPBASE + i (only for [ and ]) .cell STACKBASE = 300000 ; stack for bracket matching during load .cell TAPEBASE = 400000 ; BF data tape starts here ; -------- required register cells for PUSH/POP -------- .cell SP = 0 .cell FP = 0 ; -------- interpreter state -------- .cell PLEN = 0 ; program length (#instructions kept) .cell IP = 0 ; BF instruction pointer (0..PLEN) .cell DP = 0 ; BF data pointer (address into tape) .cell OP = 0 ; current program byte .cell TMP = 0 .cell FLAG = 0 ; pointers for indirect addressing .cell PPROG = 0 .cell PJUMP = 0 start: MOV STACKBASE, SP ZAP PLEN ZAP IP MOV TAPEBASE, DP JMP load_loop ; --------------------------- load / filter program --------------------------- load_loop: INB OP, load_done ; EOF => done ; stop reading program at newline (LF) MOV OP, FLAG CMPEQNEZ #10, FLAG, load_done ; keep only ><+-.,[] MOV OP, FLAG CMPEQNEZ #62, FLAG, load_store ; '>' MOV OP, FLAG CMPEQNEZ #60, FLAG, load_store ; '<' MOV OP, FLAG CMPEQNEZ #43, FLAG, load_store ; '+' MOV OP, FLAG CMPEQNEZ #45, FLAG, load_store ; '-' MOV OP, FLAG CMPEQNEZ #46, FLAG, load_store ; '.' MOV OP, FLAG CMPEQNEZ #44, FLAG, load_store ; ',' MOV OP, FLAG CMPEQNEZ #91, FLAG, load_store ; '[' MOV OP, FLAG CMPEQNEZ #93, FLAG, load_store ; ']' JMP load_loop load_store: ; PROG[PLEN] = OP MOV PROGBASE, PPROG ADD PLEN, PPROG MOV OP, @PPROG ; if OP == '[' => push its index MOV OP, FLAG CMPEQNEZ #91, FLAG, load_push ; if OP == ']' => pop and create jump links MOV OP, FLAG CMPEQNEZ #93, FLAG, load_pop INC #0, PLEN JMP load_loop load_push: PUSH PLEN INC #0, PLEN JMP load_loop load_pop: ; underflow check: if (SP-STACKBASE) <= 0 => unmatched ']' MOV SP, TMP SUB STACKBASE, TMP BLEQZ TMP, trap_unmatched_close POP TMP ; TMP = matching '[' index ; JUMP[TMP] = PLEN MOV JUMPBASE, PJUMP ADD TMP, PJUMP MOV PLEN, @PJUMP ; JUMP[PLEN] = TMP MOV JUMPBASE, PJUMP ADD PLEN, PJUMP MOV TMP, @PJUMP INC #0, PLEN JMP load_loop load_done: ; if stack not empty => unmatched '[' MOV SP, TMP SUB STACKBASE, TMP BNZ TMP, trap_unmatched_open MOV STACKBASE, SP ; reset stack ZAP IP ; start execution JMP exec_loop ; --------------------------- execute BF program --------------------------- exec_loop: ; if IP >= PLEN => halt MOV PLEN, TMP SUB IP, TMP BLEQZ TMP, done ; OP = PROG[IP] MOV PROGBASE, PPROG ADD IP, PPROG MOV @PPROG, OP ; dispatch on OP MOV OP, FLAG CMPEQNEZ #62, FLAG, op_gt ; '>' MOV OP, FLAG CMPEQNEZ #60, FLAG, op_lt ; '<' MOV OP, FLAG CMPEQNEZ #43, FLAG, op_plus ; '+' MOV OP, FLAG CMPEQNEZ #45, FLAG, op_minus ; '-' MOV OP, FLAG CMPEQNEZ #46, FLAG, op_dot ; '.' MOV OP, FLAG CMPEQNEZ #44, FLAG, op_comma ; ',' MOV OP, FLAG CMPEQNEZ #91, FLAG, op_lbr ; '[' MOV OP, FLAG CMPEQNEZ #93, FLAG, op_rbr ; ']' ; should not happen INC #0, IP JMP exec_loop op_gt: ; '>' INC #0, DP INC #0, IP JMP exec_loop op_lt: ; '<' DEC #0, DP INC #0, IP JMP exec_loop op_plus: ; '+' ADD8 #1, @DP INC #0, IP JMP exec_loop op_minus: ; '-' SUB8 #1, @DP INC #0, IP JMP exec_loop op_dot: ; '.' OUTB @DP INC #0, IP JMP exec_loop op_comma: ; ',' INB TMP, op_comma_eof MOV TMP, @DP INC #0, IP JMP exec_loop op_comma_eof: MOV #0, @DP INC #0, IP JMP exec_loop op_lbr: ; '[' BZ @DP, op_lbr_zero INC #0, IP JMP exec_loop op_lbr_zero: ; IP = JUMP[IP] + 1 MOV JUMPBASE, PJUMP ADD IP, PJUMP MOV @PJUMP, TMP MOV TMP, IP INC #0, IP JMP exec_loop op_rbr: ; ']' BNZ @DP, op_rbr_nz INC #0, IP JMP exec_loop op_rbr_nz: ; IP = JUMP[IP] + 1 (jump back to after matching '[') MOV JUMPBASE, PJUMP ADD IP, PJUMP MOV @PJUMP, TMP MOV TMP, IP INC #0, IP JMP exec_loop done: HALT ; --------------------------- error exits --------------------------- trap_unmatched_close: TRAP #1 ; saw ']' with no matching '[' trap_unmatched_open: TRAP #2 ; program ended with unmatched '[' remaining </pre> ==Computational class== With unbounded memory, arithmetic, and conditional/unconditional jumps, Tina can implement a register machine (or simulate other Turing-complete models). Therefore Tina is [[Turing-complete]] (assuming unbounded time and memory). ==See also== * [[OISC]] * [[Assembly language]] ==External resources== * Reference interpreter/assembler: [[https://github.com/OscarLo11212821/Tina/tree/main|Tina Interpreter]] [[Category:Languages]] [[Category:Implemented]] [[Category:Low-level]] [[Category:Assembly]] [[Category:Turing complete]]'
New page wikitext, after the edit (new_wikitext)
''''Tina''' ('''T'''his '''i'''s '''n'''ot '''a'''ssembly) is an [[esoteric programming language]] made by user [[User:Oscarlo|Oscarlo]] with an assembly-like syntax and a deliberately overpowered “ALU matrix” instruction format. A Tina program is assembled into a linear list of instructions plus an initial memory image, then executed on a simple machine with unbounded signed integer cells and non-negative addresses. A reference interpreter/assembler is implemented in Python. ==Etymology== “Tina” is a backronym for “This is not assembly”. The language is intentionally close to assembly in feel (labels, cells, jumps, stack operations), while also featuring higher-level conveniences like conditional-branching ALU instructions and built-in memory/string operations. ==Computational model== Tina executes a sequence of instructions over: * An unbounded memory of signed integers (cells default to 0). * A program counter (PC) indexing the instruction list. * Optional stack/frame support using two conventional cells named <code>SP</code> and <code>FP</code> when stack instructions are used. Memory addresses must be non-negative; attempting to access a negative address is an error. ==Syntax overview== * One instruction or directive per line. * Labels are written as <code>name:</code> (multiple labels may precede a statement). * Comments begin with <code>;</code> and continue to end of line. * Mnemonics are case-insensitive (the reference interpreter uppercases internally). Example line: <pre> loop: ADD32SNEZ #1, counter, loop ; increment and loop while counter != 0 (example only) </pre> ==Assembler directives== These directives allocate and/or initialize memory in the initial image. ; <code>.cell name = value</code> : Allocate one cell, optionally initialized. If omitted, initializes to 0. Character literals like <code>'A'</code> are allowed (one character only). ; <code>.block name, n</code> : Allocate <code>n</code> zero-initialized cells. ; <code>.data name v1, v2, ...</code> : Allocate a list of cells initialized to the given integers (supports decimal/hex via Python-style <code>0x</code>). ; <code>.zstr name "text"</code> : Allocate a null-terminated byte string. Each character must be in 0..255, and a trailing 0 cell is appended. Symbols defined by these directives can be used as memory operands and (in immediates) as numeric addresses. ==Operands and addressing modes== Tina instructions take ''operands'' which can be immediate values or memory references. ===Immediate=== ; <code>#n</code> : Immediate integer <code>n</code>. In immediates, <code>n</code> may be: :* a numeric literal (<code>#10</code>, <code>#0xFF</code>, <code>#-3</code>) :* a memory symbol (<code>#MSG</code> yields MSG’s address) :* a code label (<code>#loop</code> yields the instruction index of <code>loop:</code>) :* optionally with a small decimal offset: <code>#LABEL+3</code>, <code>#var-1</code> Immediates are not addressable (you cannot write to <code>#...</code>). ===Memory (direct / indexed)=== ; <code>x</code> : Direct cell at address <code>x</code> (where <code>x</code> is a symbol or numeric address). ; <code>x+K</code> / <code>x-K</code> : Direct cell at address <code>x+K</code> (offset is decimal in the reference interpreter). ===Indirect (pointer)=== ; <code>@x</code> : Indirect cell at address <code>mem[x]</code>. ; <code>@x+K</code> : Indirect indexed cell at address <code>mem[x]+K</code>. In pseudocode, reading <code>@p+2</code> means <code>mem[mem[p] + 2]</code>. ==ALU instruction matrix== Most arithmetic/logic operations are expressed with a single general pattern: <pre> <OP><WIDTH><OVF><COND> src, dst [, label] </pre> Execution model: 1. Read <code>src</code> and <code>dst</code>. 2. Compute a new value (<code>new_dst</code>) using <code>OP</code>. 3. Write <code>new_dst</code> back to <code>dst</code>. 4. If a condition suffix (<code>COND</code>) is present, branch to <code>label</code> if the condition is true when applied to <code>new_dst</code>; otherwise fall through. ===Base operations (OP)=== The reference interpreter implements these ALU base ops: * Data movement/arithmetic: <code>MOV ADD SUB MUL DIV MOD</code> * Convenience arithmetic: <code>INC DEC NEG ABS MIN MAX</code> * Bitwise: <code>AND OR XOR XNOR NOR NAND NOT</code> * Shifts/rotates: <code>SHL SHR SAR ROL ROR</code> * Bit tricks: <code>POPCNT CLZ CTZ</code> * Comparisons (write results into <code>dst</code>): <code>CMPEQ CMPLT CMPLE CMPGT CMP3</code> * Swap: <code>SWP</code> (special: swaps two addressable operands; immediates are not allowed) === Width (WIDTH) === Optional: <code>8</code>, <code>16</code>, <code>32</code>, <code>64</code>. If provided, results are interpreted as signed integers of that width. If omitted, Tina uses unbounded integers (except some bit-operations internally assume a 64-bit “view” for operations like rotate/counting, per the reference interpreter). === Overflow/checked behavior (OVF) === Only meaningful when a width is present: * (none): wrap to the given width (two’s complement wrap) * <code>S</code>: saturating (clamp to min/max representable signed value) * <code>C</code>: checked (raises an error on overflow) === Conditional suffix (COND) === If present, the instruction takes an additional <code>label</code> argument. Simple conditions (applied to <code>new_dst</code>): * <code>LEQ</code> (≤ 0), <code>EQZ</code> (== 0), <code>NEZ</code> (!= 0) * <code>LTZ</code>, <code>GEZ</code>, <code>GTZ</code> * <code>ODD</code>, <code>EVN</code> * <code>POS</code> (≥ 0), <code>NEG</code> (< 0) Bit-test conditions: * <code>BSET0</code>..<code>BSET63</code> (branch if bit k is 1) * <code>BCLR0</code>..<code>BCLR63</code> (branch if bit k is 0) === SUBLEQ alias === <code>SUBLEQ</code> is an alias for: <pre> SUBLEQ src, dst, label ≡ SUB src, dst, label (branch if new_dst <= 0) </pre> This makes it easy to write classic SUBLEQ-style code while still having a larger instruction set available. ==Control flow and non-ALU instructions== These instructions are not expressed through the ALU matrix: * <code>JMP label</code> — unconditional jump * <code>JMPI op</code> — jump to the value read from <code>op</code> * <code>BR op, label</code> — branch if <code>op != 0</code> Dedicated branches (branch based on <code>op</code>): * <code>BZ</code>, <code>BNZ</code>, <code>BLTZ</code>, <code>BLEQZ</code>, <code>BGEZ</code>, <code>BGTZ</code>, <code>BODD</code>, <code>BEVN</code> Loop helper: * <code>DJNZ dst, label</code> — decrement <code>dst</code>, branch if the result is not zero Misc memory ops: * <code>ZAP dst</code> — set destination to 0 * <code>XCH a, b</code> — exchange two addressable operands (no immediates) ==Stack and calls== These instructions use a conventional stack in memory and rely on two special cells if used: * <code>.cell SP = ...</code> must exist for <code>PUSH/POP/CALL/RET/ENTER/LEAVE</code> * <code>.cell FP = ...</code> must exist for <code>ENTER/LEAVE</code> Instructions: * <code>PUSH op</code> — store value at <code>mem[SP]</code>, then increment <code>SP</code> * <code>POP dst</code> — decrement <code>SP</code>, then load from <code>mem[SP]</code> into <code>dst</code> * <code>CALL label</code>, <code>CALLI op</code> — push return address, jump * <code>RET</code> — pop return address into PC * <code>ENTER #n</code> — push old FP, set FP = SP, allocate <code>n</code> locals by advancing SP * <code>LEAVE</code> — restore SP = FP, restore old FP from stack ==Built-in memory/string instructions== These are “library-like” instructions implemented directly by the interpreter: * <code>MEMSET dstAddr, byte, n</code> : Set <code>n</code> bytes at address <code>dstAddr</code> to <code>byte & 0xFF</code>. * <code>MEMCPY srcAddr, dstAddr, n</code> : Copy <code>n</code> cells; overlap-safe in the reference interpreter. * <code>MEMCMP aAddr, bAddr, n, dst</code> : Compare <code>n</code> cells lexicographically; write -1/0/1 into <code>dst</code>. * <code>STRLENZ srcAddr, dst</code> : Length of a null-terminated byte string. * <code>STRCPYZ srcAddr, dstAddr</code> : Copy a null-terminated byte string including terminator. * <code>STRCMPZ aAddr, bAddr, dst</code> : Compare two null-terminated byte strings; write -1/0/1. Note: <code>srcAddr</code>/<code>dstAddr</code> operands are read as values (addresses). For example, if <code>p</code> contains 100, then <code>STRLENZ p, len</code> measures from address 100. ==Input and output== Input is byte-based or integer-based and shares a single input stream. * <code>INB dst, labelEOF</code> : Read one byte. On EOF, writes -1 to <code>dst</code> and jumps to <code>labelEOF</code>. * <code>INN dst, labelEOF</code> : Read a signed decimal integer token (skipping whitespace). On EOF (or no integer available), jumps to <code>labelEOF</code> (reference interpreter does not guarantee writing anything to <code>dst</code> in this case). Output: * <code>OUTB op</code> — output low byte (<code>&amp; 0xFF</code>) * <code>OUTD op</code> — output decimal integer text (no newline) * <code>OUTHEX op</code> — output as 64-bit-masked hex with <code>0x</code> prefix * <code>OUTBIN op</code> — output as 64-bit-masked binary with <code>0b</code> prefix * <code>OUTZ symbolOrAddr</code> — output null-terminated bytes starting at that address (note: this takes a symbol/address, not a full addressing-mode operand) * <code>OUTZI op</code> — output null-terminated bytes starting at the address read from <code>op</code> * <code>OUTS op</code> — output a length-prefixed string: <code>addr = op</code>, <code>mem[addr]</code> is length, bytes follow at <code>addr+1..</code> * <code>EOL</code> — output newline ==Debug/abort== * <code>TRAP #code</code> — terminate the program with exit code <code>code</code> * <code>ASSERT op, #code</code> — if <code>op == 0</code>, terminate with <code>code</code> * <code>BREAK</code>, <code>WATCH op</code> — present but treated as no-ops by the reference interpreter (useful with tracing/debugging in other implementations) ==Program termination== * <code>HALT</code> returns exit code 0. * Falling off the end of the instruction list ends execution (exit code 0 in the reference interpreter). * <code>TRAP</code>/<code>ASSERT</code> can terminate with non-zero codes. ==Implementation== The reference implementation is a combined assembler and interpreter written in Python 3. Usage: <pre> python tina.py program.tina &lt; input &gt; output python tina.py program.tina --trace </pre> ==Examples== This is a traditional "Hello World" program in Tina: <pre> .zstr MSG "Hello, world!\n" start: OUTZ MSG HALT </pre> This program is a typical truth machine: <pre> .cell ZERO = 0 .cell C48 = 48 ; '0' .cell ch = 0 .cell tmp = 0 start: INB ch, done OUTB ch MOV ch, tmp SUBEQZ C48, tmp, done ; tmp = ch - 48; if tmp == 0 => input was '0' loop: OUTB ch SUBLEQ ZERO, ZERO, loop ; unconditional jump (classic SUBLEQ trick) done: HALT </pre> This program is a simple cat program: <pre> .cell ZERO = 0 .cell ch = 0 loop: INB ch, done OUTB ch SUBLEQ ZERO, ZERO, loop done: HALT </pre> This program is an implementation of Fizz Buzz: <pre> .cell ZERO = 0 .cell ONE = 1 .cell THREE = 3 .cell FIVE = 5 .cell i = 1 .cell rem = 100 .cell c3 = 3 .cell c5 = 5 .cell f = 0 .cell b = 0 .cell tmp = 0 .cell sum = 0 .zstr SFIZZ "Fizz" .zstr SBUZZ "Buzz" loop: ZAP f ZAP b DJNZ c3, no_fizz MOV THREE, c3 MOV ONE, f no_fizz: DJNZ c5, no_buzz MOV FIVE, c5 MOV ONE, b no_buzz: ; if f != 0 print "Fizz" MOV f, tmp SUBEQZ ZERO, tmp, skip_fizz OUTZ SFIZZ skip_fizz: ; if b != 0 print "Buzz" MOV b, tmp SUBEQZ ZERO, tmp, skip_buzz OUTZ SBUZZ skip_buzz: ; if (f+b)==0 print the number MOV f, sum ADD b, sum SUBEQZ ZERO, sum, print_num SUBLEQ ZERO, ZERO, after_num print_num: OUTD i after_num: EOL ADD ONE, i DJNZ rem, loop HALT </pre> This takes a number as input and calculates its factorial: <pre> .cell ZERO = 0 .cell ONE = 1 .cell n = 0 .cell fact = 1 .cell tmp = 0 start: INN n, eof MOV ONE, fact loop: MOV n, tmp SUBLEQ ONE, tmp, print ; tmp = n-1; if tmp <= 0 => n <= 1 => done MUL n, fact ; fact *= n SUB ONE, n ; n-- SUBLEQ ZERO, ZERO, loop print: OUTD fact EOL HALT eof: HALT </pre> This implements a simple [[Brainfuck]] interpreter: <pre> ; Brainfuck interpreter in Tina ; ; Input format on stdin: ; 1) First line: the Brainfuck program (only ><+-.,[] are kept; other chars ignored) ; 2) Remaining bytes after the newline are used as Brainfuck input for ','. ; ; Notes: ; - Tape cells are treated as 8-bit (wrapping) via ADD8/SUB8. ; - Data pointer moves right/left by 1 cell. Moving to a negative address will error. ; -------- fixed “heap” base addresses (picked far away from our .cell area) -------- .cell PROGBASE = 100000 ; program bytes stored at PROGBASE + i .cell JUMPBASE = 200000 ; matching-bracket table at JUMPBASE + i (only for [ and ]) .cell STACKBASE = 300000 ; stack for bracket matching during load .cell TAPEBASE = 400000 ; BF data tape starts here ; -------- required register cells for PUSH/POP -------- .cell SP = 0 .cell FP = 0 ; -------- interpreter state -------- .cell PLEN = 0 ; program length (#instructions kept) .cell IP = 0 ; BF instruction pointer (0..PLEN) .cell DP = 0 ; BF data pointer (address into tape) .cell OP = 0 ; current program byte .cell TMP = 0 .cell FLAG = 0 ; pointers for indirect addressing .cell PPROG = 0 .cell PJUMP = 0 start: MOV STACKBASE, SP ZAP PLEN ZAP IP MOV TAPEBASE, DP JMP load_loop ; --------------------------- load / filter program --------------------------- load_loop: INB OP, load_done ; EOF => done ; stop reading program at newline (LF) MOV OP, FLAG CMPEQNEZ #10, FLAG, load_done ; keep only ><+-.,[] MOV OP, FLAG CMPEQNEZ #62, FLAG, load_store ; '>' MOV OP, FLAG CMPEQNEZ #60, FLAG, load_store ; '<' MOV OP, FLAG CMPEQNEZ #43, FLAG, load_store ; '+' MOV OP, FLAG CMPEQNEZ #45, FLAG, load_store ; '-' MOV OP, FLAG CMPEQNEZ #46, FLAG, load_store ; '.' MOV OP, FLAG CMPEQNEZ #44, FLAG, load_store ; ',' MOV OP, FLAG CMPEQNEZ #91, FLAG, load_store ; '[' MOV OP, FLAG CMPEQNEZ #93, FLAG, load_store ; ']' JMP load_loop load_store: ; PROG[PLEN] = OP MOV PROGBASE, PPROG ADD PLEN, PPROG MOV OP, @PPROG ; if OP == '[' => push its index MOV OP, FLAG CMPEQNEZ #91, FLAG, load_push ; if OP == ']' => pop and create jump links MOV OP, FLAG CMPEQNEZ #93, FLAG, load_pop INC #0, PLEN JMP load_loop load_push: PUSH PLEN INC #0, PLEN JMP load_loop load_pop: ; underflow check: if (SP-STACKBASE) <= 0 => unmatched ']' MOV SP, TMP SUB STACKBASE, TMP BLEQZ TMP, trap_unmatched_close POP TMP ; TMP = matching '[' index ; JUMP[TMP] = PLEN MOV JUMPBASE, PJUMP ADD TMP, PJUMP MOV PLEN, @PJUMP ; JUMP[PLEN] = TMP MOV JUMPBASE, PJUMP ADD PLEN, PJUMP MOV TMP, @PJUMP INC #0, PLEN JMP load_loop load_done: ; if stack not empty => unmatched '[' MOV SP, TMP SUB STACKBASE, TMP BNZ TMP, trap_unmatched_open MOV STACKBASE, SP ; reset stack ZAP IP ; start execution JMP exec_loop ; --------------------------- execute BF program --------------------------- exec_loop: ; if IP >= PLEN => halt MOV PLEN, TMP SUB IP, TMP BLEQZ TMP, done ; OP = PROG[IP] MOV PROGBASE, PPROG ADD IP, PPROG MOV @PPROG, OP ; dispatch on OP MOV OP, FLAG CMPEQNEZ #62, FLAG, op_gt ; '>' MOV OP, FLAG CMPEQNEZ #60, FLAG, op_lt ; '<' MOV OP, FLAG CMPEQNEZ #43, FLAG, op_plus ; '+' MOV OP, FLAG CMPEQNEZ #45, FLAG, op_minus ; '-' MOV OP, FLAG CMPEQNEZ #46, FLAG, op_dot ; '.' MOV OP, FLAG CMPEQNEZ #44, FLAG, op_comma ; ',' MOV OP, FLAG CMPEQNEZ #91, FLAG, op_lbr ; '[' MOV OP, FLAG CMPEQNEZ #93, FLAG, op_rbr ; ']' ; should not happen INC #0, IP JMP exec_loop op_gt: ; '>' INC #0, DP INC #0, IP JMP exec_loop op_lt: ; '<' DEC #0, DP INC #0, IP JMP exec_loop op_plus: ; '+' ADD8 #1, @DP INC #0, IP JMP exec_loop op_minus: ; '-' SUB8 #1, @DP INC #0, IP JMP exec_loop op_dot: ; '.' OUTB @DP INC #0, IP JMP exec_loop op_comma: ; ',' INB TMP, op_comma_eof MOV TMP, @DP INC #0, IP JMP exec_loop op_comma_eof: MOV #0, @DP INC #0, IP JMP exec_loop op_lbr: ; '[' BZ @DP, op_lbr_zero INC #0, IP JMP exec_loop op_lbr_zero: ; IP = JUMP[IP] + 1 MOV JUMPBASE, PJUMP ADD IP, PJUMP MOV @PJUMP, TMP MOV TMP, IP INC #0, IP JMP exec_loop op_rbr: ; ']' BNZ @DP, op_rbr_nz INC #0, IP JMP exec_loop op_rbr_nz: ; IP = JUMP[IP] + 1 (jump back to after matching '[') MOV JUMPBASE, PJUMP ADD IP, PJUMP MOV @PJUMP, TMP MOV TMP, IP INC #0, IP JMP exec_loop done: HALT ; --------------------------- error exits --------------------------- trap_unmatched_close: TRAP #1 ; saw ']' with no matching '[' trap_unmatched_open: TRAP #2 ; program ended with unmatched '[' remaining </pre> ==Computational class== With unbounded memory, arithmetic, and conditional/unconditional jumps, Tina can implement a register machine (or simulate other Turing-complete models). Therefore Tina is [[Turing-complete]] (assuming unbounded time and memory). ==See also== * [[OISC]] * [[Assembly language]] ==External resources== * Reference interpreter/assembler: [[https://github.com/OscarLo11212821/Tina/tree/main|Tina Interpreter]] [[Category:Languages]] [[Category:Implemented]] [[Category:Low-level]] [[Category:Assembly]] [[Category:Turing complete]]'
Unified diff of changes made by edit (edit_diff)
'@@ -1,3 +1,3 @@ -'''Tina''' ('''T'''his '''i'''s '''n'''ot '''a'''ssembly) is an [[esoteric programming language]] with an assembly-like syntax and a deliberately overpowered “ALU matrix” instruction format. A Tina program is assembled into a linear list of instructions plus an initial memory image, then executed on a simple machine with unbounded signed integer cells and non-negative addresses. +'''Tina''' ('''T'''his '''i'''s '''n'''ot '''a'''ssembly) is an [[esoteric programming language]] made by user [[User:Oscarlo|Oscarlo]] with an assembly-like syntax and a deliberately overpowered “ALU matrix” instruction format. A Tina program is assembled into a linear list of instructions plus an initial memory image, then executed on a simple machine with unbounded signed integer cells and non-negative addresses. A reference interpreter/assembler is implemented in Python. '
New page size (new_size)
18238
Old page size (old_size)
18200
Lines added in edit (added_lines)
[ 0 => ''''Tina''' ('''T'''his '''i'''s '''n'''ot '''a'''ssembly) is an [[esoteric programming language]] made by user [[User:Oscarlo|Oscarlo]] with an assembly-like syntax and a deliberately overpowered “ALU matrix” instruction format. A Tina program is assembled into a linear list of instructions plus an initial memory image, then executed on a simple machine with unbounded signed integer cells and non-negative addresses.' ]
Unix timestamp of change (timestamp)
'1769506156'