Tuesday, April 26, 2022

Subleq Plus

I got the Subleq+ compiler and interpreter working.  They're posted on GitHub.  This is a huge advance over hand jamming the physical addresses.  Many thanks to Chris Lloyd for the basis of the system.  The parser is still mostly his code.

Subleq is a single instruction computer.  That instruction consists of three words, A B C.  The function is [B] = [B] - [A]; if [B] <= 0, jump to C.  "[x]" means the contents of memory at location x.  As limited as it seems, you can theoretically do anything with a computer like this.  It just takes longer.

Why am I interested in this?

  1. I'm a geek.  If that wasn't obvious by now, you haven't been paying attention.
  2. Subleq reminds me of a programming project I found in a magazine for the Commodore-64 back when I was a kid.  There was some toy language that only had two instructions:  Increment an address by one, and decrement an address by one and jump if it was zero.  I played around with that for weeks.  Good times.  Happy memories.
  3. This has gotten me back into programming for the first time in almost 30 years.  I actually went to college for programming back in the early 90's.  I dropped out (the second time) when I noticed that I, as a Junior, was helping the grad students with their projects.  I also saw that my friends were having trouble finding local jobs in the field, as the industry was leaving Ohio and moving to California at the time.  I just lost interest and motivation.
  4. Being partially disabled and mostly stuck at home, I have a lot of time on my hands and I get bored.  This is a nice challenge.
So, where to go from here?  I could adapt the system for OISC:2.  But I would want to update the design to accommodate some of the new ideas I've had for OISC:3.  Negative memory references actually to go negative memory, where they are automagically redirected.  OISC:3 will use a stack, and the program counters, etc, won't be in addressable memory.  Input and output will also be stack commands.  This, of course, won't work well for OISC:2, because of the constraints of the language.  So I think I'll just leave it alone, and maybe adapt the parser to it.  Oh, and I'm renumbering the mode commands to use negative numbers, because I like the symmetry of having things like addition/subtraction, multiplication/division, power/log, sin/asin and or/xor having opposite signs.

Is a stack too much power for an esolang like this?  If course it is!  Could it take some of the challenge of using a language like this away?  Of course it can!  But that's not really the point, is it?  It's not to use the language so much as to create a usable language.  And it still doesn't have an explicit instruction.  It's all just data and memory locations.  But then, that's all computers really are.

Monday, April 18, 2022

Esolang OISC:2b

Obfuscated Indirect Subleq with Coprocessor
2 word instructions
version b

I finally have a working emulator for OISC:2b that accepts positive and (optional) negative memory files.  The coprocessor has been improved.  Positive memory is loaded as integers only.  Jumps to negative memory Halt.

Two word instructions:  A B

If A & B are both positive: [B]=[B]-[A]
If A & B are both negative: [[B]]=[[B]]-[[A]]

If A is positive and B is negative: IF [A] <= 0 Jump |B|
If A is negative and B is positive: IF [[A]] <= 0 Jump B

If A is 0 and B is positive: STDIN -> [B]
If A is 0 and B is negative: STDIN -> [[B]] 

If A is positive and B is 0: [A] -> STDOUT
If A is negative and B is 0: [[A]] -> STDOUT

If A & B are both 0: HALT

Negative memory can be addressed by indirection, but instructions are only in positive memory.

Negative Memory
Address        Function
-1                IP (initially 0)
-2                NEXT (always IP+3)
-3                RETURN (initially 0, set to NEXT before any jump.)
-4                Register a
-5                Register b
-6                Register c
-7                Mode (activates a, b, c; then resets to 0)
-8                MaxPos memory size
-9                MaxNeg memory size
-10...           Data (address: start from 0, add 10, negate)

Coprocessor
Mode    Function
0          NOP
1          c = ~b (bitwise not)
2          c = b & a (bitwise and)
3          c = b | a (bitwise or)
4          c = b ^ a (bitwise xor)
5          c = b << a (shift b left by a bits)
6          c = b >> a (shift b right by a bits)
7          sign of b   (+1, 0, -1)
8          floor of b (integer)
9          truncate b (round down, integer)
10        c = b - a
11        c = b + a
12        c = b * a
13        c = b // a (integer division)
14        c = b % a (mod, integer remainder)
15        c = b / a
16        c = b to power of a
17        c = a root of b
18        c = log base b of a
19        c = sin(b)
20        c = cos(b)
21        c = tan(b)
22        c = asin(b)
23        c = acos(b)
24        c = atan(b)
25        c = sinh(b)
26        c = cosh(b)
27        c = tanh(b)
28        c = asinh(b)
29        c = acosh(b)
30        c = atanh(b)
31        c = sqrt(b**2 + a**2)  (hypotenuse)
32        a = pi, b = e, c = phi (golden ration)
33        c radians <-- b degrees
34        c degrees <-- b radians
35        c = greatest common divisor of b and a
36        c = a permutations of b items (unique)
37        c = a combinations of b items (not unique)
38        c = b! (factorial)
39        c = sum of 0..b (integers, works with negatives)


Sunday, April 17, 2022

Esolang OISC:3d

Updated:  18 April 2022

No, I can't leave well enough alone.  I think we've established that by now.  This one might be more useful.  Too useful?  Negative addresses were a pain in the previous versions, and most instructions aren't indirect, so I've swapped functionality to simplify the interpreter.  It should also make programming easier and negative memory much more useful.  Note that memory address 0 is still impossible to directly address.  

Positive memory is loaded as integers only.
A jump to negative memory Halts & Fails.

Instruction    Function
A B C            [C] = [B] - [A]
0 B C            If [B] <= 0, Jump to C//[-C]
A 0 C            If [A] <= 0, Relative jump by C
A B 0            [[B]] = [[B]] - [[A]]  (indirect addressing)
A 0 0            Input [A] as character
0 B 0            Output [B] as character  (if [B] < 0, Halt & Fail)
0 0 C            Output [C] as number
0 0 0            Halt & Succeed

Address        Function
-1                IP (initially 0)
-2                NEXT (always IP+3)
-3                RETURN (initially 0, set to NEXT before any jump.)
-4                Register a
-5                Register b
-6                Register c
-7                Mode (activates a, b, c; then resets to 0)
-8                MaxPos memory size
-9                MaxNeg memory size
-10...           Data (address: start from 0, add 10, negate)

Mode    Function
0          NOP
1          c = ~b (bitwise not)
2          c = b & a (bitwise and)
3          c = b | a (bitwise or)
4          c = b ^ a (bitwise xor)
5          c = b << a (shift b left by a bits)
6          c = b >> a (shift b right by a bits)
7          sign of b, +1, 0, -1
8          floor of b (integer)
9          truncate b (round down, integer)
10        c = b - a
11        c = b + a
12        c = b * a
13        c = b // a (integer division)
14        c = b % a (mod, integer remainder)
15        c = b / a
16        c = b to power of a
17        c = a root of b
18        c = log base b of a
19        c = sin(b)
20        c = cos(b)
21        c = tan(b)
22        c = asin(b)
23        c = acos(b)
24        c = atan(b)
25        c = sinh(b)
26        c = cosh(b)
27        c = tanh(b)
28        c = asinh(b)
29        c = acosh(b)
30        c = atanh(b)
31        c = sqrt(b**2 + a**2)  (hypotenuse)
32        a = pi, b = e, c = phi (golden ration)
33        c radians <-- b degrees
34        c degrees <-- b radians
35        c = gcd(b, a)  (greatest common divisor)
36        c = a permutations of b items (unique)
37        c = a combinations of b items (not unique)
38        c = b! (factorial)
39        c = sum of 0..b (integers, works with negatives)



Friday, April 15, 2022

OISC:2 It works! Mostly.

Here is a very basic implementation of OISC:2, written in Python.
Please feel free to play with it, but leave the attribution header.

Improved code with examples and a ReadMe can be found on GitHub.

# OISC2 without file reading or coprocessor.
# This is based on an implementation of Subleq found here:
#           https://rosettacode.org/wiki/Subleq#Python
# It has very limited error testing.  Buyer beware.
# McChuck, April 15, 2022

import sys

def oisc2(mem, negmem):

    negmem = [0,2,0,0,0,0,0]+negmem

#   IP, Next, Return, Reg a, Reg b, Reg c, Mode

    IP = -1

    NEXT = -2

    RET = -3

    negmem.reverse()

    mem=mem+negmem

    try:

        while mem[IP] >= 0:

            a=mem[mem[IP]]

            b=mem[mem[IP]+1]

            mem[NEXT]=mem[IP]+2

            if a>0:

                if b>0:

                    mem[b]-=mem[a]

                elif b==0:

                    print(chr(mem[a]), end="")

                else:

                    if mem[a]<=0:

                        mem[RET]=mem[NEXT]

                        mem[NEXT]=abs(b)

            elif a==0:

                if b>0:

                    mem[b]=ord(sys.stdin.read(1))

                elif b==0:

                    mem[NEXT]=-1

                else:

                    mem[mem[abs(b)]]=ord(sys.stdin.read(1))

            else:

                if b>0:

                    if mem[mem[abs(a)]]<=0:

                        mem[RET]=mem[NEXT]

                        mem[NEXT]=b

                elif b==0:

                    print(chr(mem[mem[abs(a)]]), end="")

                else:

                    mem[mem[abs(b)]]-=mem[mem[abs(a)]]

            mem[IP] = mem[NEXT]


        print("\nOISC2 completed successfully.")


    except (ValueError, IndexError, KeyboardInterrupt):

        print("OISC2 aborted at: ", ip)

        print("A: ", a, "B: ", b)

        print(mem)


    finally:

        print("\nFinished.")


oisc2([12, 12, 14, -30, 14, 0, 13, 2, 13, 4, 12, -2,
        0, -1, 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 10, 0, 14,
        -29, 38, -29, 0, -40, -41, -40, 30, 0, 0, 13, 29], [0,0,1,2,3,4,5,6,0,0])


# 0 Z, Z     # You can't jump back to 0, so have to pad the beginning.
# 2 Start: L, -Cont
# 4 L, 0              # Print "Hello, world!" directly
# 6 M1, Start
# 8 M1, Start+2
# 10 Z, -Start
# 12 Z: 0 .
# 13 M1: -1 .
# 14 L: "Hello, world!\n"
# 29 LP: L .
# 30 Cont: -LP Halt
# 32 -LP 0            # Now print it again, but indirectly
# 34 -M1P -LPP
# 36 -M1P Cont
# 38 Halt: 0 0
# 40 M1P: M1 .
# 41 LPP: LP .


Thursday, April 14, 2022

Esolang OISC:3c

No, as a matter of fact I can't stop tinkering with things.

This is a simplification of OISC:3b, which had grown to be a rather unwieldy monster.

Instructions and data go in positive memory space.  Coprocessor functions and data go in negative memory space.  Negative addresses imply indirect addressing, unless specifically overridden below.  Jumping to a negative address Halts and Fails.  All words except NEXT are initialized to 0.

3 word instructions, A B C

A B C    Function
A B C    [C] = [B] - [A]   (Indirect is [[|-X|]])
0 B C    If [B] <= 0, Jump to C // [|-C|], else Next
A 0 C    If [A] <= 0, Relative Jump by #C (-2 is -2), else Next
A B 0    [#B] = [#B] - [#A] (-2 is -2)
A 0 0    Output [A] as character (if negative, Halt & Fail)
0 B 0    Input character to [B]
0 0 C    Output [C] as number
0 0 0    Halt and Succeed

Mem    Function
-1        IP
-2        NEXT (always IP+3)
-3        RETURN (set to NEXT before Jump)
-4        Register a
-5        Register b
-6        Register c
-7        Mode (immediately acts on a,b,c; then resets to 0)
-8        Flag
-9...     Data

Mode  Effect
0        NOP
1        c = bitwise NOT b
2        c = b bitwise AND a
3        c = b bitwise OR a
4        c = b bitwise XOR a
5        c = b << a bits
6        c = b >> a bits
7        c = Integer b*a
8        c = b/a (floored integer division)
                    (Halt & Fail on a=0)
9        c = b%a (Mod) (floored)
                    (Halt & Fail on a=0)
10        c = Sign of b (+1, 0, -1)
11        c = Floor of b
12        Int c --> Float c
13        Float c --> Int c
14        Int a,b --> Float a,b
15        Float a,b --> Int a,b
16        c = b-a
17        c = b+a
18        c = b*a
19        c = b/a
                    (Halt & Fail on a=0)
20        c = power a of b
21        c = root a of b
                    (Halt & Fail on a=0)
22        c = Log base b of a
                    (Halt & Fail on a=0 or b=0)
23        c = SIN b
24        c = COS b
25        c = TAN b
26        c = CSC b
27        c = SEC b
28        c = COT b
29        c = ASIN b
30        c = ACOS b
31        c = ATAN b
32        c = ACSC b
33        c = ASEC b
34        c = ACOT b
35        a = pi, b = e, c = phi
36        a = 1.0, b = 0.0, c = -1.0
37        c = b Rad --> Deg
38        c = b Deg --> Rad

Extensions to Mode are optional and left to the user.

Memories of Albania

 Yes, it was like this.  But the base was a corduroy road on slick spring mud, not rock.




And the next morning, we had to go back down.

Monday, April 11, 2022

Esolang: OISC 3b

In a (vain, I know) attempt to standardize things, here is OISC:3b, based on OISC:2.  I really do like this format better.  And there are 27 distinct combinations of +/0/- leading to obfuscatory effects!

Obfuscated Indirect Subleq with Coprocessor

Three word instruction:  A B C   

General form: C=B-A 
        Why not A-B=C?  Obfuscation is in the name!

Negative words are indirect addressing, which can be mixed: 
        [C]/[[|-C|]] = [B]/[[|-B|]] - [A]/[[|-A|]]

Instructions are only in positive memory.

If B=0 & C>0:  Output [A]/[[|-A|]] to I/O indicated by C. 

        1= STDOUT, 2= STDERR, 3...= File.
If A=0 & C>0:  Input [B]/[[|-B|]] from I/O indicated by C. 
        1= STDIN, 2= STDERR, 3...= File.

If B=0 & C<0:  Copy [A]/[[|-A|]] to [C]  (to negative memory)
If A=0 & C<0:  Copy [C] to [B]/[[|-B|]] (from negative memory)

If C=0:  If [A]/[[|-A|]] <=0, Jump B/[|-B|]
If A,C=0:  Jump [B]/[[|-B|]]
If B,C=0:  Relative Jump A

If A,B=0:  If [C]/[[|-C|]]<=0, HALT
If A,B,C=0:  HALT


Mem    Function

-1        IP (initialized to 0)
-2        NEXT (always IP+3)
-3        RETURN (set to NEXT before Jump]
-4        Register a
-5        Register b
-6        Register c
-7        Instruction Mode (activates on a,b,c; then resets to 0=NOP)
-8...     Data


Instruction Mode :  Takes immediate action on a,b,c, then resets to 0.

Mode       Effect

0        NOP
1        c = NOT a
2        c = b AND a
3        c = b OR a
4        c = b XOR a
5        c = b << a bits
6        c = b >> a bits
7        c = b*a
8        c = b/a (floored integer division) (HALT on /0)
9        c = b%a (Mod) (floored) (HALT on %0)
10        c = Sign of Int b (+1, 0, -1)
11        c = Sign of Float b (+1, 0, -1)
12        Int c --> Float c
13        Float c --> Int c
14        Int a,b --> Float a,b
15        Float a,b --> Int a,b
16        Float: c = b-a
17        Float: c = b+a
18        Float: c = b*a
19        Float: c = b/a (HALT on /0)
20        Float: c = power a of b
21        Float: c = root a of b (HALT on root 0)
22        Float: c = Log base b of a (HALT on base 0)
23        Float: c = SIN b
24        Float: c = COS b
25        Float: c = TAN b
26        Float: c = CSC b
27        Float: c = SEC b
28        Float: c = COT b
29        Float: c = ASIN b
30        Float: c = ACOS b
31        Float: c = ATAN b
32        Float: c = ACSC b
33        Float: c = ASEC b
34        Float: c = ACOT b
35        Float: a = pi, b = e, c = phi
36        Float: a = 1.0, b = 0.0, c = -1.0

Extensions to Mode are left to the user.