Showing posts with label interpreter. Show all posts
Showing posts with label interpreter. Show all posts

Tuesday, 24 February 2009

Implementing Forth in Redcode

I'm currently working on a threaded Forth interpreter in Redcode. The first version should provide about 20 key Forth instructions in 50 lines of code.

Threading

The Forth program will be stored as a list of subroutine calls, for example:
        dat    lit     ; push 1111
dat 1111
dat lit ; push 1234
dat 1234
dat plus ; add TOS to 2OS
dat udot ; display TOS
A more compact representation would be to store addresses in both the a-field and b-field. Unfortunately this would be at the expense of more complex flow control:
        dat    lit,      1111
dat lit, 1234
dat plus, udot
I'm open to suggestions how the interpreter can use the compact representation without causing too many difficulties.

Instruction Set

I'm planning to support the following instructions in version 1. Have I missed anything important, or have I included something which can be left out:

InsDescription
-subtract TOS from 2OS
+add TOS to 2OS
*multiply TOS by 2OS
U.print TOS as an unsigned number
SPACEprint a space
DUPcopy TOS
DROPremove TOS
ABSreplace TOS with its absolute value
NEGATEreplace TOS with -TOS
!2OS is stored at the address in TOS
+!2OS is added to the address in TOS
@fetch the value at the address in TOS
=TRUE if TOS=2OS, else FALSE
SWAPexchange TOS with 2OS
DEPTHnumber of elements on stack
BEGINstart of BEGIN .. UNTIL structure
UNTILif TRUE, return to matching BEGIN
DOstart of DO .. LOOP structure
LOOPinc counter, jump to DO if below limit

Signed vs Unsigned

Unfortunately numbers in Forth are signed and numbers in Redcode are unsigned. This affects a number of instructions, including division and comparison. Would it be worth the extra code to support unsigned numbers in Redcode Forth?

Example Code

The following example interprets +, U. and literals. New instructions can easily be added in Redcode. Support for calling new instuctions written in Forth needs to be added:
        org    next

stack dat 0, 0

; LIT - place the next value on the stack

lit mov.b }ip, <stack
jmp next

; + - remove 2OS and TOS and put their sum on stack

plus add.b >stack, @stack
jmp next

; U. - remove and display TOS as an unsigned number

udot mov.b @stack, <stack
div #10, >stack
mod #10, @stack
add #48, @stack
add #1, udcount
jmn udot, <stack
add #1, stack
udloop sts >stack, 0
udcount djn udloop, #0
sts.a #32, 0
jmp next

; --------------------------------

next
ip jmp @prog, }ip

; --------------------------------

prog
dat lit ; push 1111
dat 1111
dat lit ; push 1234
dat 1234
dat plus ; add TOS to 2OS
dat udot ; display TOS

end

Monday, 16 February 2009

Underload Interpreter in Redcode

Underload is a stack-based esoteric programming language designed by ais523. A set of 8 single character instructions operate on a stack of variable length strings:

InsDescription
:copy the top stack entry
!drop the top stack entry
~swap the top two stack entries
*join the top two stack entries
Sdisplay then drop the top stack entry
^drop then run the top stack entry
aenclose the top stack entry in parentheses
( )add a new stack entry

A combination of different factors make an interpreter for Underload an interesting project to tackle in Redcode:
  • parsing single character instructions is easy, I don't like writing parsers
  • handling variable length data can be tricky
  • no operands / no side effects to worry about
  • only 8 instructions, the size of the interpreter should be reasonable
  • ^ is the only means of flow control
The final code weighs in at a mere 79 instructions. Here's the final version of the Underload Interpreter. If you have any improvements to suggest, please leave a comment below.

Sunday, 25 January 2009

Brainf*** Interpreter in Redcode

Brainf*** is an esoteric programming language developed by Urban Müller. There are 8 instructions in brainf***:

InsMacroDescription
<ltmove pointer left
>gtmove pointer right
+incincrement memory cell at pointer
-decdecrement memory cell at pointer
.outoutput a character from the cell at pointer
,ininput a character and store in cell at pointer
[opjump past matching ] if cell at pointer is 0
]cljump to matching [ if cell at pointer not 0

Brainf*** operates on an array of memory cells and has a pointer into the array. Each instruction translates directly into Redcode. Before executing the brainf*** program, the interpreter calculates and stores the offset for [ and ] to avoid repeated searching.

Here's the code:

        lt equ sub #1, ptr
gt equ add #1, ptr
inc equ add #1, @ptr
dec equ sub #1, @ptr
out equ sts @ptr, 0
in equ lds @ptr, 0
op equ jmz 0, @ptr
cl equ jmn 2, @ptr

org seek

ptr mov.ba #0, #prog

find sne.a #2, *ptr
sub #1, count
sne.a #0, }ptr
jmp find, >count
count jmn find, #0

mov.a ptr, @ptr
sub.ba ptr, >ptr
mov.ba ptr, {ptr
sub.a ptr, *ptr

seek jmn.a seek, >ptr
jmn ptr, <ptr

prog

Sunday, 18 January 2009

Redcode Interpreter for the RSSB Single Instruction Computer

The RSSB single instruction computer is a minimal, Turing complete virtual machine. The instruction used is Reverse Subtract and Skip if Borrow. RSSB subtracts the accumulator from the contents of a memory location and stores the result in both. The next instruction will be skipped if the accumulator was greater than the value in memory.

Jumps can be implemented by manipulating the instruction pointer at memory location 0. Other special locations are as follows:
  1. accumulator
  2. always contains 0
  3. input
  4. output
Here's the code for the interpreter. I've included a Hello World program written in RSSB. If you think you can reduce the size of Hello World, please let me know how.
        org    rssbint

rssbint mov.ba >ip, ip ; load next instruction

mov #0, zero ; set zero

sne.a #3, ip ; handle input
lds in, 0

sne.a #4, ip ; handle output
sts acc, 0
mov acc, out
add out, out

slt *ip, acc ; set flag for skip
mov #2, skip

sub.b acc, *ip ; acc, *ip = *ip - acc
mov.b *ip, acc

skip djn noskip, #1 ; skip if required
nop >ip, >skip

noskip slt #7900, @ip ; protect interpreter

jmn rssbint, ip ; loop until ip = 0

; --------------------------------------

ip dat 5 ; instruction pointer
acc dat 0 ; accumulator
zero dat 0 ; always 0
in dat 0 ; character input
out dat 0 ; character output

; --------------------------------------

loop dat -ip+ acc ; acc = character from ptr
ptr dat -ip+ hello

dat -ip+ out ; display character

dat -ip+ zero ; acc = -acc

dat -ip+ zero ; always skipped

dat -ip+ sum ; subtract acc from sum

dat -ip+ ip ; skipped if sum is negative,
; otherwise jump to 0

one dat -ip+ acc ; subtract 1 from ptr
dat -ip+ one
dat -ip+ ptr

dat -ip+ acc ; jump to loop
dat -ip+ loopoff
dat -ip+ ip
loopoff dat -loop

sum dat -1116

dat 33 ; '!'
dat 100 ; 'd'
dat 108 ; 'l'
dat 114 ; 'r'
dat 111 ; 'o'
dat 87 ; 'W'
dat 32 ; ' '
dat 44 ; ','
dat 111 ; 'o'
dat 108 ; 'l'
dat 108 ; 'l'
dat 101 ; 'e'
hello dat 72 ; 'H'

end