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.
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:
What kind of input does an "ask what user wants" on division by zero take? Full integer parsing? 0 to 9 digit? ASCII value? – J B – 2011-02-07T21:14:43.093
@J B: I'll remove that requirement. That's what Wikipedia says it should do but none of the interpreters I've seen do that. I'll just make it undefined. – Nemo157 – 2011-02-07T21:39:10.837