2 Added original code
source | link

Update

Here's the original assembly source code, assembled using A86. It's quite long:

  enter 64,0
  xor di,di
  mov ax,ds
  add ah,10h
  mov [bp-2],ax   ; program
  mov [bp-14],ax  ; current display
  add ah,10h
  mov w[bp-22],di ; stack off 
  mov [bp-20],ax  ; stack seg
  mov w[bp-6],di  ; exit status
  mov w[bp-8],di  ; size of field -8=lines,-7=columns
  mov w[bp-12],di ; output window position
  add ah,10h
  mov w[bp-10],ax; output segment
  mov w[bp-24],MoveRight

  ; clear output
  mov es,ax
  xor ax,ax
  mov cx,8000h
  rep stosw

  ; random seed
  mov ah,2ch
  int 21h
  mov w[bp-16],dx

  ; get filename
  mov dx,StrNoFile
  mov bl,[80h]
  xor bh,bh
  sub bl,1
  jge NoError

Error:
  call ClearScreen
  mov ah,9
  int 21h
  leave
  ret

NoError:  
  ; open file
  mov b[82h+bx],0
  mov ax,3d00h
  mov dx,82h
  int 21h
  mov dx,StrBadFile
  jc Error
  mov bx,ax
  ; clear program
  mov es,[bp-2]
  mov cx,8000h
  xor ax,ax
  rep stosw
  ; read file
ReadLoop:
  mov cx,1
  mov dx,EOP
  mov ah,3fh
  int 21h
  mov dx,StrBadRead
  jc Error
  or ax,ax
  jz EOF
  mov al,b[EOP]
  cmp al,10
  je EOL
  cmp al,32
  jb ReadLoop
  stosb
  jmp ReadLoop
EOL:
  mov ax,di
  cmp al,[bp-7]
  jb l9
  mov [bp-7],al
l9:
  inc b[bp-8]
  and di,0ff00h  
  add di,100h
  jmp ReadLoop
EOF:
  mov ah,3eh
  int 21h
  or di,di
  mov dx,StrEmptyFile
  jz Error
  ; initialise
  mov ax,3
  int 10h
  xor bx,bx ; PC
  ; execute
Redraw:  
  call DisplayProgram
WaitForInput:  
  mov ah,7
  int 21h
  cmp al,'q'
  je Quit
  cmp al,'r'
  je DoRun
  cmp al,'1'
  je ShowIO
  cmp al,'2'
  je ShowProgram
  cmp al,'s'
  jne WaitForInput
  ; single step
  call Execute
  jmp Redraw

ShowIO:
  mov ax,[bp-10]
  mov [bp-14],ax
  jmp Redraw  

ShowProgram:
  mov ax,[bp-2]
  mov [bp-14],ax
  jmp Redraw

DoRun:
  mov ax,[bp-10]
  mov [bp-14],ax
  call DisplayProgram
  call Execute
  cmp b[bp-6],0
  je DoRun
  jmp Redraw

Quit:
  call ClearScreen
  leave
  ret

ClearScreen:
  mov ax,0b800h
  mov es,ax
  mov ax,720h  
  mov cx,8000h
  rep stosw
  ret

DisplayProgram:
  pusha
  push ds
  mov ds,[bp-14]
  mov ax,0b800h
  mov es,ax
  mov ch,25
  xor di,di
  xor si,si
  mov ah,7
l1:
  mov cl,80
l2:
  lodsb
  stosw
  dec cl
  jnz l2
  add si,256-80
  dec ch
  jnz l1
  push bx
  mov ax,[bp-14]
  cmp ax,[bp-2]
  mov dl,0cfh
  je l33
  mov bx,[bp-12]
  mov dl,0f0h
l33:  
  cmp bl,80
  jae l3
  cmp bh,25
  jae l3
  mov al,bh
  mov ah,160
  mul ah
  add al,bl
  adc ah,0
  add al,bl
  adc ah,0
  mov di,ax
  inc di
  mov al,dl
  stosb
l3:  
  pop bx
  pop ds
  popa
  ret

Execute:
  mov es,[bp-2]
  mov al,es:[bx]

  cmp al,'0'
  jb NotPush
  cmp al,'9'
  ja NotPush

  ; push number
  mov ah,0
  sub al,'0'
PushValueUpdatePC:
  call PushValue
UpdatePC:
  jmp [bp-24]

MoveLeft:
  or bl,bl
  jnz MoveLeftNoWrap
  mov bl,[bp-7]
MoveLeftNoWrap:
  dec bl
  ret

MoveRight:
  inc bl
  cmp bl,[bp-7]
  jne ret
  xor bl,bl
  ret

MoveUp:
  or bh,bh
  jnz MoveUpNoWrap
  mov bh,[bp-8]
MoveUpNoWrap:
  dec bh
  ret

MoveDown:
  inc bh
  cmp bh,[bp-8]
  jne ret
  xor bh,bh
  ret

PushValue:
  push es
  les di,[bp-22]
  stosw
  mov [bp-22],di
  pop es
  ret

PopValue:
  push es
  les di,[bp-22]
  xor ax,ax
  or di,di
  jz PopValueZero
  sub di,2
  mov ax,es:[di]
  mov [bp-22],di
PopValueZero:
  pop es
  ret

NotPush:
  mov si,Functions-3
NextFunction:
  add si,3  
FindFunction:
  cmp b[si],255
  je Endsearch
  cmp b[si],al
  jne NextFunction
  mov ax,[si+1]
  or ax,ax
  je EndSearch
  jmp ax
  ret

EndSearch:
  mov dx,StrEndSearch
  jmp Error  

DoStringMode:
  call UpdatePC  
  mov al,es:[bx]
  cmp al,'"'
  je UpdatePC
  mov ah,0
  call PushValue
  jmp DoStringMode

PopTwoValues:
  call PopValue
  mov cx,ax
  jmp PopValue

DoAdd:
  call PopTwoValues
  add ax,cx
  jmp PushValueUpdatePC

DoSub:
  call PopTwoValues
  sub ax,cx
  jmp PushValueUpdatePC  

DoMultiply:
  call PopTwoValues
  imul cx
  jmp PushValueUpdatePC

DoDivide:
  call PopTwoValues
  cwd
  idiv cx
  jmp PushValueUpdatePC

DoModulo:
  call PopTwoValues
  cwd
  idiv cx
  mov ax,dx
  jmp PushValueUpdatePC

DoNot:
  call PopValue
  or ax,ax
  mov ax,1
  jz DoNotZero
  dec ax
DoNotZero:  
  jmp PushValueUpdatePC

DoGreaterThan:
  call PopTwoValues
  cmp ax,cx
  mov ax,1
  jg DoGreaterThanIs1
  dec ax
DoGreaterThanIs1:
  jmp PushValueUpdatePC  

DoMoveRight:
  mov [bp-24],MoveRight
  jmp UpdatePC

DoLeftRight:
  call PopValue
  or ax,ax
  jz DoMoveRight

DoMoveLeft:
  mov [bp-24],MoveLeft
  jmp UpdatePC

DoMoveUpDown:  
  call PopValue
  or ax,ax
  jz DoMoveDown

DoMoveUp:
  mov [bp-24],MoveUp
  jmp UpdatePC

DoMoveDown:
  mov [bp-24],MoveDown
  jmp UpdatePC

DoRandomDirection:
  mov ax,[bp-16]
  mov dx,31821
  mul dx
  add ax,13849
  mov [bp-16],ax
  and al,6
  add ax,Movements
  mov si,ax
  lodsw
  mov [bp-24],ax
  jmp UpdatePC    

Movements:
  dw MoveUp, MoveDown, MoveLeft, MoveRight  

DoDuplicate:
  call PopValue
  call PushValue
  jmp PushValueUpdatePC  

DoSwap:
  call PopTwoValues
  xchg ax,cx
  call PushValue
  mov ax,cx
  jmp PushValueUpdatePC  

DoPop:
  call PopValue
  jmp UpdatePC

DoPopPrint:
  call PopValue
  or ax,ax
  jns Positive
  neg ax
  push ax
  mov al,'-'
  call PrintChar
  pop ax  
Positive:  
  push bx
  mov bx,EOP
DivLoop:  
  xor dx,dx
  mov cx,10
  div cx
  add dl,'0'
  mov [bx],dl
  inc bx
  or ax,ax
  jnz DivLoop
PrintLoop:  
  dec bx
  mov al,[bx]
  call PrintChar
  cmp bx,EOP
  jne PrintLoop
  pop bx
  jmp UpdatePC

PrintChar:
  cmp al,10
  je AsciiCR
  push es
  les di,[bp-12]
  stosb
  mov ax,di
  cmp al,80
  jne NoLF
  add di,256-80
NoLF:
  mov [bp-12],di
  pop es
  ret
AsciiCR:
  mov b[bp-12],0
  inc b[bp-11]
  ret

DoPopAsciiPrint:  
  call PopValue
  call PrintChar
  jmp UpdatePC

DoTrampoline:
  call UpdatePC
  jmp UpdatePC

DoPut:
  call GetCoord
  call PopValue
  push es
  mov es,[bp-2]
  mov es:[si],al
  pop es
  jmp UpdatePC

GetCoord:
  call PopValue
  mov cl,[bp-8]
DoGet1:  
  or ax,ax
  jns DecrementY
  add al,cl
  adc ah,0
  jmp DoGet1
DecrementY:
  div cl
  mov cl,ah
  mov ch,0
  call PopValue ; x
  push cx
  mov cl,[bp-7]
DoGet2:  
  or ax,ax
  jns DecrementX
  add al,cl
  adc ah,0
  jmp DoGet2
DecrementX:
  div cl
  mov al,ah
  pop cx ; ax=x, cx=y
  mov ah,cl
  mov si,ax
  ret

DoGet:
  call GetCoord
  push es
  mov es,[bp-2]
  mov al,es:[si]
  pop es
  mov ah,0
  jmp PushValueUpdatePC

DoInput:
  push [bp-14]
  mov ax,[bp-10]
  mov [bp-14],ax
  xor si,si
  xor cl,cl
DoInput1:
  call DisplayProgram
  mov ah,7
  int 21h
  cmp al,'-'
  jne DoInputEnter
  or cl,cl
  jnz DoInput1
  inc cl
  call PrintChar
  jmp DoInput1

DoInputEnter:
  cmp al,13
  jnz DoInputDigit

  pop [bp-14]
  mov ax,si
  or cl,cl
  jz DoInput3
  neg ax
DoInput3:  
  jmp PushValueUpdatePC

DoInputDigit:
  mov ch,al
  sub al,'0'
  cmp al,9
  ja DoInput1
  mov ah,0
  push ax
  mov al,10
  mul si
  or dx,dx
  pop dx
  jnz DoInput1
  test ah,80h
  jnz DoInput1
  add dx,ax
  test dh,80h
  jnz DoInput1  
  mov si,dx
  mov al,ch
  call PrintChar
  jmp DoInput1  

DoInputAscii:
  push [bp-14]
  mov ax,[bp-10]
  mov [bp-14],ax
  call DisplayProgram
DoInputAscii1:
  mov ah,7
  int 21h
  cmp al,32
  jb DoInputAscii1
  push ax
  call PrintChar
  pop ax
  mov ah,0
  pop [bp-14]
  jmp PushValueUpdatePC

DoExit:
  mov b[bp-6],1
  ret  

Functions:
  db '+' ; Addition: Pop a then b, push a+b
  dw DoAdd
  db '-' ; Subtraction: Pop a then b, push b-a
  dw DoSub
  db '*' ; Multiplication: Pop a then b, push a*b
  dw DoMultiply
  db '/' ; Integer division: Pop a then b, push b/a, rounded down. If a is 0, result is undefined
  dw DoDivide
  db '%' ; Modulo: Pop a then b, push the remainder of the integer division of b/a. If a is 0, result is undefined
  dw DoModulo
  db '!' ; Logical NOT: Pop a value. If the value is 0, push 1; otherwise, push 0.
  dw DoNot
  db '`' ; Greater than: Pop a then b, push 1 if b>a, otherwise 0.
  dw DoGreaterThan
  db '>' ; Start moving right
  dw DoMoveRight
  db '<' ; Start moving left
  dw DoMoveLeft
  db '^' ; Start moving up
  dw DoMoveUp
  db 'v' ; Start moving down
  dw DoMoveDown
  db '?' ; Start moving in a random cardinal direction
  dw DoRandomDirection
  db '_' ; Pop a value; move right if value=0, left otherwise
  dw DoLeftRight
  db '|' ; Pop a value; move down if value=0, up otherwise
  dw DoMoveUpDown
  db '"' ; Start string mode: push each character's ASCII value all the way up to the next "
  dw DoStringMode
  db ':' ; Duplicate value on top of the stack
  dw DoDuplicate
  db '\' ; Swap two values on top of the stack
  dw DoSwap
  db '$' ; Pop value from the stack
  dw DoPop
  db '.' ; Pop value and output as an integer
  dw DoPopPrint
  db ',' ; Pop value and output as ASCII character
  dw DoPopAsciiPrint
  db '#' ; Trampoline: Skip next cell
  dw DoTrampoline
  db 'p' ; A "put" call (a way to store a value for later use). Pop y then x then v, change the character at the position (x,y) in the program to the character with ASCII value v
  dw DoPut
  db 'g' ; A "get" call (a way to retrieve data in storage). Pop y then x, push ASCII value of the character at that position in the program
  dw DoGet
  db '&' ; Input an integer (may be multiple characters and may be negative) and push it
  dw DoInput
  db '~' ; Input a single character from stdin and push its ASCII value
  dw DoInputAscii
  db '@' ; End program
  dw DoExit
  db ' ' ; NOP
  dw UpdatePC
  db 0 ; NOP
  dw UpdatePC
  db 255

StrNoFile:
  db "1$";"No File$"
StrBadFile:
  db "2$";"Bad File$"
StrBadRead:
  db "3$";"Bad Read$"
StrEmptyFile:
  db "4$";"Empty File$"
StrEndSearch:
  db "5$";"Bad Instruction$"
EOP:

Update

Here's the original assembly source code, assembled using A86. It's quite long:

  enter 64,0
  xor di,di
  mov ax,ds
  add ah,10h
  mov [bp-2],ax   ; program
  mov [bp-14],ax  ; current display
  add ah,10h
  mov w[bp-22],di ; stack off 
  mov [bp-20],ax  ; stack seg
  mov w[bp-6],di  ; exit status
  mov w[bp-8],di  ; size of field -8=lines,-7=columns
  mov w[bp-12],di ; output window position
  add ah,10h
  mov w[bp-10],ax; output segment
  mov w[bp-24],MoveRight

  ; clear output
  mov es,ax
  xor ax,ax
  mov cx,8000h
  rep stosw

  ; random seed
  mov ah,2ch
  int 21h
  mov w[bp-16],dx

  ; get filename
  mov dx,StrNoFile
  mov bl,[80h]
  xor bh,bh
  sub bl,1
  jge NoError

Error:
  call ClearScreen
  mov ah,9
  int 21h
  leave
  ret

NoError:  
  ; open file
  mov b[82h+bx],0
  mov ax,3d00h
  mov dx,82h
  int 21h
  mov dx,StrBadFile
  jc Error
  mov bx,ax
  ; clear program
  mov es,[bp-2]
  mov cx,8000h
  xor ax,ax
  rep stosw
  ; read file
ReadLoop:
  mov cx,1
  mov dx,EOP
  mov ah,3fh
  int 21h
  mov dx,StrBadRead
  jc Error
  or ax,ax
  jz EOF
  mov al,b[EOP]
  cmp al,10
  je EOL
  cmp al,32
  jb ReadLoop
  stosb
  jmp ReadLoop
EOL:
  mov ax,di
  cmp al,[bp-7]
  jb l9
  mov [bp-7],al
l9:
  inc b[bp-8]
  and di,0ff00h  
  add di,100h
  jmp ReadLoop
EOF:
  mov ah,3eh
  int 21h
  or di,di
  mov dx,StrEmptyFile
  jz Error
  ; initialise
  mov ax,3
  int 10h
  xor bx,bx ; PC
  ; execute
Redraw:  
  call DisplayProgram
WaitForInput:  
  mov ah,7
  int 21h
  cmp al,'q'
  je Quit
  cmp al,'r'
  je DoRun
  cmp al,'1'
  je ShowIO
  cmp al,'2'
  je ShowProgram
  cmp al,'s'
  jne WaitForInput
  ; single step
  call Execute
  jmp Redraw

ShowIO:
  mov ax,[bp-10]
  mov [bp-14],ax
  jmp Redraw  

ShowProgram:
  mov ax,[bp-2]
  mov [bp-14],ax
  jmp Redraw

DoRun:
  mov ax,[bp-10]
  mov [bp-14],ax
  call DisplayProgram
  call Execute
  cmp b[bp-6],0
  je DoRun
  jmp Redraw

Quit:
  call ClearScreen
  leave
  ret

ClearScreen:
  mov ax,0b800h
  mov es,ax
  mov ax,720h  
  mov cx,8000h
  rep stosw
  ret

DisplayProgram:
  pusha
  push ds
  mov ds,[bp-14]
  mov ax,0b800h
  mov es,ax
  mov ch,25
  xor di,di
  xor si,si
  mov ah,7
l1:
  mov cl,80
l2:
  lodsb
  stosw
  dec cl
  jnz l2
  add si,256-80
  dec ch
  jnz l1
  push bx
  mov ax,[bp-14]
  cmp ax,[bp-2]
  mov dl,0cfh
  je l33
  mov bx,[bp-12]
  mov dl,0f0h
l33:  
  cmp bl,80
  jae l3
  cmp bh,25
  jae l3
  mov al,bh
  mov ah,160
  mul ah
  add al,bl
  adc ah,0
  add al,bl
  adc ah,0
  mov di,ax
  inc di
  mov al,dl
  stosb
l3:  
  pop bx
  pop ds
  popa
  ret

Execute:
  mov es,[bp-2]
  mov al,es:[bx]

  cmp al,'0'
  jb NotPush
  cmp al,'9'
  ja NotPush

  ; push number
  mov ah,0
  sub al,'0'
PushValueUpdatePC:
  call PushValue
UpdatePC:
  jmp [bp-24]

MoveLeft:
  or bl,bl
  jnz MoveLeftNoWrap
  mov bl,[bp-7]
MoveLeftNoWrap:
  dec bl
  ret

MoveRight:
  inc bl
  cmp bl,[bp-7]
  jne ret
  xor bl,bl
  ret

MoveUp:
  or bh,bh
  jnz MoveUpNoWrap
  mov bh,[bp-8]
MoveUpNoWrap:
  dec bh
  ret

MoveDown:
  inc bh
  cmp bh,[bp-8]
  jne ret
  xor bh,bh
  ret

PushValue:
  push es
  les di,[bp-22]
  stosw
  mov [bp-22],di
  pop es
  ret

PopValue:
  push es
  les di,[bp-22]
  xor ax,ax
  or di,di
  jz PopValueZero
  sub di,2
  mov ax,es:[di]
  mov [bp-22],di
PopValueZero:
  pop es
  ret

NotPush:
  mov si,Functions-3
NextFunction:
  add si,3  
FindFunction:
  cmp b[si],255
  je Endsearch
  cmp b[si],al
  jne NextFunction
  mov ax,[si+1]
  or ax,ax
  je EndSearch
  jmp ax
  ret

EndSearch:
  mov dx,StrEndSearch
  jmp Error  

DoStringMode:
  call UpdatePC  
  mov al,es:[bx]
  cmp al,'"'
  je UpdatePC
  mov ah,0
  call PushValue
  jmp DoStringMode

PopTwoValues:
  call PopValue
  mov cx,ax
  jmp PopValue

DoAdd:
  call PopTwoValues
  add ax,cx
  jmp PushValueUpdatePC

DoSub:
  call PopTwoValues
  sub ax,cx
  jmp PushValueUpdatePC  

DoMultiply:
  call PopTwoValues
  imul cx
  jmp PushValueUpdatePC

DoDivide:
  call PopTwoValues
  cwd
  idiv cx
  jmp PushValueUpdatePC

DoModulo:
  call PopTwoValues
  cwd
  idiv cx
  mov ax,dx
  jmp PushValueUpdatePC

DoNot:
  call PopValue
  or ax,ax
  mov ax,1
  jz DoNotZero
  dec ax
DoNotZero:  
  jmp PushValueUpdatePC

DoGreaterThan:
  call PopTwoValues
  cmp ax,cx
  mov ax,1
  jg DoGreaterThanIs1
  dec ax
DoGreaterThanIs1:
  jmp PushValueUpdatePC  

DoMoveRight:
  mov [bp-24],MoveRight
  jmp UpdatePC

DoLeftRight:
  call PopValue
  or ax,ax
  jz DoMoveRight

DoMoveLeft:
  mov [bp-24],MoveLeft
  jmp UpdatePC

DoMoveUpDown:  
  call PopValue
  or ax,ax
  jz DoMoveDown

DoMoveUp:
  mov [bp-24],MoveUp
  jmp UpdatePC

DoMoveDown:
  mov [bp-24],MoveDown
  jmp UpdatePC

DoRandomDirection:
  mov ax,[bp-16]
  mov dx,31821
  mul dx
  add ax,13849
  mov [bp-16],ax
  and al,6
  add ax,Movements
  mov si,ax
  lodsw
  mov [bp-24],ax
  jmp UpdatePC    

Movements:
  dw MoveUp, MoveDown, MoveLeft, MoveRight  

DoDuplicate:
  call PopValue
  call PushValue
  jmp PushValueUpdatePC  

DoSwap:
  call PopTwoValues
  xchg ax,cx
  call PushValue
  mov ax,cx
  jmp PushValueUpdatePC  

DoPop:
  call PopValue
  jmp UpdatePC

DoPopPrint:
  call PopValue
  or ax,ax
  jns Positive
  neg ax
  push ax
  mov al,'-'
  call PrintChar
  pop ax  
Positive:  
  push bx
  mov bx,EOP
DivLoop:  
  xor dx,dx
  mov cx,10
  div cx
  add dl,'0'
  mov [bx],dl
  inc bx
  or ax,ax
  jnz DivLoop
PrintLoop:  
  dec bx
  mov al,[bx]
  call PrintChar
  cmp bx,EOP
  jne PrintLoop
  pop bx
  jmp UpdatePC

PrintChar:
  cmp al,10
  je AsciiCR
  push es
  les di,[bp-12]
  stosb
  mov ax,di
  cmp al,80
  jne NoLF
  add di,256-80
NoLF:
  mov [bp-12],di
  pop es
  ret
AsciiCR:
  mov b[bp-12],0
  inc b[bp-11]
  ret

DoPopAsciiPrint:  
  call PopValue
  call PrintChar
  jmp UpdatePC

DoTrampoline:
  call UpdatePC
  jmp UpdatePC

DoPut:
  call GetCoord
  call PopValue
  push es
  mov es,[bp-2]
  mov es:[si],al
  pop es
  jmp UpdatePC

GetCoord:
  call PopValue
  mov cl,[bp-8]
DoGet1:  
  or ax,ax
  jns DecrementY
  add al,cl
  adc ah,0
  jmp DoGet1
DecrementY:
  div cl
  mov cl,ah
  mov ch,0
  call PopValue ; x
  push cx
  mov cl,[bp-7]
DoGet2:  
  or ax,ax
  jns DecrementX
  add al,cl
  adc ah,0
  jmp DoGet2
DecrementX:
  div cl
  mov al,ah
  pop cx ; ax=x, cx=y
  mov ah,cl
  mov si,ax
  ret

DoGet:
  call GetCoord
  push es
  mov es,[bp-2]
  mov al,es:[si]
  pop es
  mov ah,0
  jmp PushValueUpdatePC

DoInput:
  push [bp-14]
  mov ax,[bp-10]
  mov [bp-14],ax
  xor si,si
  xor cl,cl
DoInput1:
  call DisplayProgram
  mov ah,7
  int 21h
  cmp al,'-'
  jne DoInputEnter
  or cl,cl
  jnz DoInput1
  inc cl
  call PrintChar
  jmp DoInput1

DoInputEnter:
  cmp al,13
  jnz DoInputDigit

  pop [bp-14]
  mov ax,si
  or cl,cl
  jz DoInput3
  neg ax
DoInput3:  
  jmp PushValueUpdatePC

DoInputDigit:
  mov ch,al
  sub al,'0'
  cmp al,9
  ja DoInput1
  mov ah,0
  push ax
  mov al,10
  mul si
  or dx,dx
  pop dx
  jnz DoInput1
  test ah,80h
  jnz DoInput1
  add dx,ax
  test dh,80h
  jnz DoInput1  
  mov si,dx
  mov al,ch
  call PrintChar
  jmp DoInput1  

DoInputAscii:
  push [bp-14]
  mov ax,[bp-10]
  mov [bp-14],ax
  call DisplayProgram
DoInputAscii1:
  mov ah,7
  int 21h
  cmp al,32
  jb DoInputAscii1
  push ax
  call PrintChar
  pop ax
  mov ah,0
  pop [bp-14]
  jmp PushValueUpdatePC

DoExit:
  mov b[bp-6],1
  ret  

Functions:
  db '+' ; Addition: Pop a then b, push a+b
  dw DoAdd
  db '-' ; Subtraction: Pop a then b, push b-a
  dw DoSub
  db '*' ; Multiplication: Pop a then b, push a*b
  dw DoMultiply
  db '/' ; Integer division: Pop a then b, push b/a, rounded down. If a is 0, result is undefined
  dw DoDivide
  db '%' ; Modulo: Pop a then b, push the remainder of the integer division of b/a. If a is 0, result is undefined
  dw DoModulo
  db '!' ; Logical NOT: Pop a value. If the value is 0, push 1; otherwise, push 0.
  dw DoNot
  db '`' ; Greater than: Pop a then b, push 1 if b>a, otherwise 0.
  dw DoGreaterThan
  db '>' ; Start moving right
  dw DoMoveRight
  db '<' ; Start moving left
  dw DoMoveLeft
  db '^' ; Start moving up
  dw DoMoveUp
  db 'v' ; Start moving down
  dw DoMoveDown
  db '?' ; Start moving in a random cardinal direction
  dw DoRandomDirection
  db '_' ; Pop a value; move right if value=0, left otherwise
  dw DoLeftRight
  db '|' ; Pop a value; move down if value=0, up otherwise
  dw DoMoveUpDown
  db '"' ; Start string mode: push each character's ASCII value all the way up to the next "
  dw DoStringMode
  db ':' ; Duplicate value on top of the stack
  dw DoDuplicate
  db '\' ; Swap two values on top of the stack
  dw DoSwap
  db '$' ; Pop value from the stack
  dw DoPop
  db '.' ; Pop value and output as an integer
  dw DoPopPrint
  db ',' ; Pop value and output as ASCII character
  dw DoPopAsciiPrint
  db '#' ; Trampoline: Skip next cell
  dw DoTrampoline
  db 'p' ; A "put" call (a way to store a value for later use). Pop y then x then v, change the character at the position (x,y) in the program to the character with ASCII value v
  dw DoPut
  db 'g' ; A "get" call (a way to retrieve data in storage). Pop y then x, push ASCII value of the character at that position in the program
  dw DoGet
  db '&' ; Input an integer (may be multiple characters and may be negative) and push it
  dw DoInput
  db '~' ; Input a single character from stdin and push its ASCII value
  dw DoInputAscii
  db '@' ; End program
  dw DoExit
  db ' ' ; NOP
  dw UpdatePC
  db 0 ; NOP
  dw UpdatePC
  db 255

StrNoFile:
  db "1$";"No File$"
StrBadFile:
  db "2$";"Bad File$"
StrBadRead:
  db "3$";"Bad Read$"
StrEmptyFile:
  db "4$";"Empty File$"
StrEndSearch:
  db "5$";"Bad Instruction$"
EOP:
1
source | link

16bit MSDOS .COM File - 1104 bytes

This is Base64 encoded (decoder here), save decoded file as a .com and execute from command line with name of program file to run as the only argument. Tested in WinXP command prompt.

yEAAADP/jNiAxBCJRv6JRvKAxBCJfuqJRuyJfvqJfviJfvSAxBCJRvbHRuiMAo7AM8C5AIDzq7Qs
zSGJVvC6RgWKHoAAMv+A6wF9Cei3ALQJzSHJw8aHggAAuAA9uoIAzSG6SAVy5YvYjkb+uQCAM8Dz
q7kBALpQBbQ/zSG6SgVyygvAdCWgUAU8CnQHPCBy4qrr34vHOkb5cgOIRvn+RviB5wD/gccAAevI
tD7NIQv/ukwFdJa4AwDNEDPb6FQAtAfNITxxdDk8cnQhPDF0DTwydBE8c3Xo6I8A6+CLRvaJRvLr
2ItG/olG8uvQi0b2iUby6B4A6HEAgH76AHTu67zoAgDJw7gAuI7AuCAHuQCA86vDYB6OXvK4ALiO
wLUZM/8z9rQHsVCsq/7JdfqBxrAA/s118FOLRvI7Rv6yz3QFi170svCA+1BzG4D/GXMWise0oPbk
ANiA1AAA2IDUAIv4R4rCqlsfYcOORv4migc8MHJVPDl3UbQALDDoKwD/ZugK23UDil75/svD/sM6
Xvl1+DLbwwr/dQOKfvj+z8P+xzp++HX4Mv/DBsR+6quJfuoHwwbEfuozwAv/dAmD7wImiwWJfuoH
w77uBIPGA4A8/3QOOAR19ItEAQvAdAP/4MO6TgXpZP7olP8migc8InSNtADos//r7+i4/4nB67To
9v8Dwel0/+ju/yvB6Wz/6Ob/9+npZP/o3v+Z9/npW//o1f+Z9/mLwulQ/+iF/wvAuAEAdAFI6UL/
6Lz/O8G4AQB/AUjpNP/HRuiMAukv/+hh/wvAdPHHRuiCAukg/+hS/wvAdAjHRuiWAukR/8dG6KAC
6Qn/i0bwuk189+IFGTaJRvAkBgWSA4vwrYlG6Ont/pYCoAKCAowC6Bf/6Ar/6dn+6FP/kegA/4nI
6c3+6AL/6cr+6Pz+C8B5CffYULAt6CYAWFO7UAUz0rkKAPfxgMIwiBdDC8B170uKB+gKAIH7UAV1
9Fvplf48CnQUBsR+9KqLxzxQdQSBx7AAiX70B8PGRvQA/kb1w+in/uja/+ls/uhp/ulm/ugOAOiV
/gaORv4miAQH6VX+6If+ik74C8B5BwLBgNQA6/X28YjhtQDocP5Rik75C8B5BwLBgNQA6/X28Yjg
WYjMi/DD6Mr/Bo5G/iaKBAe0AOkP/v928otG9olG8jP2Msnol/20B80hPC11CwrJdfH+wehc/+vq
PA11Do9G8ovGCsl0AvfY6dr9iMUsMDwJd9C0AFCwCvfmC9JadcT2xIB1vwHC9saAdbiJ1ojo6CH/
66//dvKLRvaJRvLoPf20B80hPCBy+FDoB/9YtACPRvLpkP3GRvoBwysAAy0IAyoQAy8YAyUhAyEs
A2A6Az5IAzxXA15mA3ZuAz92A19QA3xfAyLoAjqaA1yjAySvAy61AywKBCMTBHAZBGddBCZtBH7L
BEDsBCB/AgB/Av8xJDIkMyQ0JDUk

On startup, the display shows the program. Commands are:

  • r - runs the program (switches to output screen)
  • s - single steps the program
  • q - quits the interpreter
  • 1 - shows output screen (white cursor is current output position)
  • 2 - shows program screen (red cursors is current program position)

When an input command is executed, the display switches to the output screen. Input is echoed to the output screen. For ascii input, only characters in the range 32-255 are accepted. For numeric input, only values in the range -32768 to 32767 are allowed, press enter to complete input (sorry, no backspace).

I really should add a stack screen as well.