Compiler/virtual machine interpreter: Difference between revisions
Content added Content deleted
Line 12,054: | Line 12,054: | ||
count is: 8 |
count is: 8 |
||
count is: 9</pre> |
count is: 9</pre> |
||
=={{header|RATFOR}}== |
|||
{{works with|ratfor77|[https://sourceforge.net/p/chemoelectric/ratfor77/ public domain 1.0]}} |
|||
{{works with|gfortran|11.2.1}} |
|||
{{works with|f2c|20100827}} |
|||
<lang ratfor>###################################################################### |
|||
# |
|||
# The Rosetta Code code virtual machine in Ratfor 77. |
|||
# |
|||
# The implementation assumes your FORTRAN compiler supports 1-byte |
|||
# INTEGER*1 and 4-byte INTEGER*4. Integer storage will be |
|||
# native-endian, achieved via EQUIVALENCE. (GNU Fortran and f2c both |
|||
# should work.) |
|||
# |
|||
# |
|||
# How to deal with FORTRAN 77 input is a problem. I use formatted |
|||
# input, treating each line as an array of type CHARACTER--regrettably |
|||
# of no more than some predetermined, finite length. It is a very |
|||
# simple method and presents no significant difficulties, aside from |
|||
# the restriction on line length of the input. |
|||
# |
|||
# |
|||
# On a POSIX platform, the program can be compiled with f2c and run |
|||
# somewhat as follows: |
|||
# |
|||
# ratfor77 vm-in-ratfor.r > vm-in-ratfor.f |
|||
# f2c -C -Nc40 vm-in-ratfor.f |
|||
# cc vm-in-ratfor.c -lf2c |
|||
# ./a.out < compiler-tests/primes.vm |
|||
# |
|||
# With gfortran, a little differently: |
|||
# |
|||
# ratfor77 vm-in-ratfor.r > vm-in-ratfor.f |
|||
# gfortran -fcheck=all -std=legacy vm-in-ratfor.f |
|||
# ./a.out < compiler-tests/primes.vm |
|||
# |
|||
# |
|||
# I/O is strictly from default input and to default output, which, on |
|||
# POSIX systems, usually correspond respectively to standard input and |
|||
# standard output. (I did not wish to have to deal with unit numbers; |
|||
# these are now standardized in ISO_FORTRAN_ENV, but that is not |
|||
# available in FORTRAN 77.) |
|||
# |
|||
#--------------------------------------------------------------------- |
|||
# Some parameters you may wish to modify. |
|||
define(LINESZ, 256) # Size of an input line. |
|||
define(OUTLSZ, 1024) # Size of an output line. |
|||
define(STRNSZ, 4096) # Size of the string pool. |
|||
define(STCKSZ, 4096) # Size of stacks. |
|||
define(MAXVAR, 256) # Maximum number of variables. |
|||
define(MAXSTR, 256) # Maximum number of strings. |
|||
define(CODESZ, 16384) # Maximum size of a compiled program. |
|||
define(STRSZ, 2) # Size of an entry in the VM strings array. |
|||
define(STRI, 1) # Index of the string within strngs. |
|||
define(STRN, 2) # Length of the string. |
|||
#--------------------------------------------------------------------- |
|||
define(NEWLIN, 10) # The Unix newline character (ASCII LF). |
|||
define(DQUOTE, 34) # The double quote character. |
|||
define(BACKSL, 92) # The backslash character. |
|||
#--------------------------------------------------------------------- |
|||
define(OPHALT, 1) |
|||
define(OPADD, 2) |
|||
define(OPSUB, 3) |
|||
define(OPMUL, 4) |
|||
define(OPDIV, 5) |
|||
define(OPMOD, 6) |
|||
define(OPLT, 7) |
|||
define(OPGT, 8) |
|||
define(OPLE, 9) |
|||
define(OPGE, 10) |
|||
define(OPEQ, 11) |
|||
define(OPNE, 12) |
|||
define(OPAND, 13) |
|||
define(OPOR, 14) |
|||
define(OPNEG, 15) |
|||
define(OPNOT, 16) |
|||
define(OPPRTC, 17) |
|||
define(OPPRTI, 18) |
|||
define(OPPRTS, 19) |
|||
define(OPFTCH, 20) |
|||
define(OPSTOR, 21) |
|||
define(OPPUSH, 22) |
|||
define(OPJMP, 23) |
|||
define(OPJZ, 24) |
|||
#--------------------------------------------------------------------- |
|||
function issp (c) |
|||
# Is a character a space character? |
|||
implicit none |
|||
character c |
|||
logical issp |
|||
integer ic |
|||
ic = ichar (c) |
|||
issp = (ic == 32 || (9 <= ic && ic <= 13)) |
|||
end |
|||
function isalph (c) |
|||
# Is c character code for a letter? |
|||
implicit none |
|||
integer c |
|||
logical isalph |
|||
# |
|||
# The following is correct for ASCII and Unicode, but not for |
|||
# EBCDIC. |
|||
# |
|||
isalph = (ichar ('a') <= c && c <= ichar ('z')) _ |
|||
|| (ichar ('A') <= c && c <= ichar ('Z')) |
|||
end |
|||
function isdgt (c) |
|||
# Is c character code for a digit? |
|||
implicit none |
|||
integer c |
|||
logical isdgt |
|||
isdgt = (ichar ('0') <= c && c <= ichar ('9')) |
|||
end |
|||
function skipsp (str, i, imax) |
|||
# Skip past spaces in a string. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipsp |
|||
logical issp |
|||
logical done |
|||
skipsp = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipsp) |
|||
done = .true. |
|||
else if (!issp (str(skipsp))) |
|||
done = .true. |
|||
else |
|||
skipsp = skipsp + 1 |
|||
} |
|||
end |
|||
function skipns (str, i, imax) |
|||
# Skip past non-spaces in a string. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipns |
|||
logical issp |
|||
logical done |
|||
skipns = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipns) |
|||
done = .true. |
|||
else if (issp (str(skipns))) |
|||
done = .true. |
|||
else |
|||
skipns = skipns + 1 |
|||
} |
|||
end |
|||
function trimrt (str, n) |
|||
# Find the length of a string, if one ignores trailing spaces. |
|||
implicit none |
|||
character str(*) |
|||
integer n |
|||
integer trimrt |
|||
logical issp |
|||
logical done |
|||
trimrt = n |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (trimrt == 0) |
|||
done = .true. |
|||
else if (!issp (str(trimrt))) |
|||
done = .true. |
|||
else |
|||
trimrt = trimrt - 1 |
|||
} |
|||
end |
|||
function skipal (str, i, imax) |
|||
# Skip past alphabetic characters in a string. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipal |
|||
logical isalph |
|||
logical done |
|||
skipal = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipal) |
|||
done = .true. |
|||
else if (!isalph (ichar (str(skipal)))) |
|||
done = .true. |
|||
else |
|||
skipal = skipal + 1 |
|||
} |
|||
end |
|||
function skipdg (str, i, imax) |
|||
# Skip past digits in a string. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipdg |
|||
logical isdgt |
|||
logical done |
|||
skipdg = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipdg) |
|||
done = .true. |
|||
else if (!isdgt (ichar (str(skipdg)))) |
|||
done = .true. |
|||
else |
|||
skipdg = skipdg + 1 |
|||
} |
|||
end |
|||
function skipnd (str, i, imax) |
|||
# Skip past nondigits in a string. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipnd |
|||
logical isdgt |
|||
logical done |
|||
skipnd = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipnd) |
|||
done = .true. |
|||
else if (isdgt (ichar (str(skipnd)))) |
|||
done = .true. |
|||
else |
|||
skipnd = skipnd + 1 |
|||
} |
|||
end |
|||
function skipd1 (str, i, imax) |
|||
# Skip past digits and '-' in a string. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipd1 |
|||
logical isdgt |
|||
logical done |
|||
skipd1 = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipd1) |
|||
done = .true. |
|||
else if (!isdgt (ichar (str(skipd1))) && str(skipd1) != '-') |
|||
done = .true. |
|||
else |
|||
skipd1 = skipd1 + 1 |
|||
} |
|||
end |
|||
function skipn1 (str, i, imax) |
|||
# Skip past nondigits in a string, except '-'. |
|||
implicit none |
|||
character str(*) |
|||
integer i |
|||
integer imax |
|||
integer skipn1 |
|||
logical isdgt |
|||
logical done |
|||
skipn1 = i |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (imax <= skipn1) |
|||
done = .true. |
|||
else if (isdgt (ichar (str(skipn1))) || str(skipn1) == '-') |
|||
done = .true. |
|||
else |
|||
skipn1 = skipn1 + 1 |
|||
} |
|||
end |
|||
function tolowr (c) |
|||
implicit none |
|||
character c |
|||
character tolowr |
|||
integer ic |
|||
# The following is correct for ASCII, and will work with Unicode |
|||
# code points, but is incorrect for EBCDIC. |
|||
ic = ichar (c) |
|||
if (ichar ('A') <= ic && ic <= ichar ('Z')) |
|||
ic = ic - ichar('A') + ichar('a') |
|||
tolowr = char (ic) |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
subroutine addstq (strngs, istrng, src, i0, n0, i, n) |
|||
# Add a quoted string to the string pool. |
|||
implicit none |
|||
character strngs(STRNSZ) # String pool. |
|||
integer istrng # String pool's next slot. |
|||
character src(*) # Source string. |
|||
integer i0, n0 # Index and length in source string. |
|||
integer i, n # Index and length in string pool. |
|||
integer j |
|||
logical done |
|||
1000 format ('attempt to treat an unquoted string as a quoted string') |
|||
if (src(i0) != char (DQUOTE) || src(i0 + n0 - 1) != char (DQUOTE)) |
|||
{ |
|||
write (*, 1000) |
|||
stop |
|||
} |
|||
i = istrng |
|||
n = 0 |
|||
j = i0 + 1 |
|||
done = .false. |
|||
while (j != i0 + n0 - 1) |
|||
if (i == STRNSZ) |
|||
{ |
|||
write (*, '(''string pool exhausted'')') |
|||
stop |
|||
} |
|||
else if (src(j) == char (BACKSL)) |
|||
{ |
|||
if (j == i0 + n0 - 1) |
|||
{ |
|||
write (*, '(''incorrectly formed quoted string'')') |
|||
stop |
|||
} |
|||
if (src(j + 1) == 'n') |
|||
strngs(istrng) = char (NEWLIN) |
|||
else if (src(j + 1) == char (BACKSL)) |
|||
strngs(istrng) = src(j + 1) |
|||
else |
|||
{ |
|||
write (*, '(''unrecognized escape sequence'')') |
|||
stop |
|||
} |
|||
istrng = istrng + 1 |
|||
n = n + 1 |
|||
j = j + 2 |
|||
} |
|||
else |
|||
{ |
|||
strngs(istrng) = src(j) |
|||
istrng = istrng + 1 |
|||
n = n + 1 |
|||
j = j + 1 |
|||
} |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
subroutine push (stack, sp, i) |
|||
implicit none |
|||
integer stack(STCKSZ) |
|||
integer sp # Stack pointer. |
|||
integer i # Value to push. |
|||
if (sp == STCKSZ) |
|||
{ |
|||
write (*, '(''stack overflow in push'')') |
|||
stop |
|||
} |
|||
stack(sp) = i |
|||
sp = sp + 1 |
|||
end |
|||
function pop (stack, sp) |
|||
implicit none |
|||
integer stack(STCKSZ) |
|||
integer sp # Stack pointer. |
|||
integer pop |
|||
if (sp == 1) |
|||
{ |
|||
write (*, '(''stack underflow in pop'')') |
|||
stop |
|||
} |
|||
sp = sp - 1 |
|||
pop = stack(sp) |
|||
end |
|||
function nstack (sp) |
|||
implicit none |
|||
integer sp # Stack pointer. |
|||
integer nstack |
|||
nstack = sp - 1 # Current cardinality of the stack. |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
subroutine flushl (outbuf, noutbf) |
|||
# Flush a line from the output buffer. |
|||
implicit none |
|||
character outbuf(OUTLSZ) # Output line buffer. |
|||
integer noutbf # Number of characters in outbuf. |
|||
character*20 fmt |
|||
integer i |
|||
if (noutbf == 0) |
|||
write (*, '()') |
|||
else |
|||
{ |
|||
write (fmt, 1000) noutbf |
|||
1000 format ('(', I10, 'A)') |
|||
write (*, fmt) (outbuf(i), i = 1, noutbf) |
|||
noutbf = 0 |
|||
} |
|||
end |
|||
subroutine wrtchr (outbuf, noutbf, ch) |
|||
# Write a character to output. |
|||
implicit none |
|||
character outbuf(OUTLSZ) # Output line buffer. |
|||
integer noutbf # Number of characters in outbuf. |
|||
character ch # The character to output. |
|||
# This routine silently truncates anything that goes past the buffer |
|||
# boundary. |
|||
if (ch == char (NEWLIN)) |
|||
call flushl (outbuf, noutbf) |
|||
else if (noutbf < OUTLSZ) |
|||
{ |
|||
noutbf = noutbf + 1 |
|||
outbuf(noutbf) = ch |
|||
} |
|||
end |
|||
subroutine wrtstr (outbuf, noutbf, str, i, n) |
|||
# Write a substring to output. |
|||
implicit none |
|||
character outbuf(OUTLSZ) # Output line buffer. |
|||
integer noutbf # Number of characters in outbuf. |
|||
character str(*) # The string from which to output. |
|||
integer i, n # Index and length of the substring. |
|||
integer j |
|||
for (j = 0; j < n; j = j + 1) |
|||
call wrtchr (outbuf, noutbf, str(i + j)) |
|||
end |
|||
subroutine wrtint (outbuf, noutbf, ival, colcnt) |
|||
# Write an integer to output. |
|||
implicit none |
|||
character outbuf(OUTLSZ) # Output line buffer. |
|||
integer noutbf # Number of characters in outbuf. |
|||
integer ival # The non-negative integer to print. |
|||
integer colcnt # Column count, or zero for free format. |
|||
integer skipsp |
|||
character*40 buf |
|||
integer i, j |
|||
write (buf, '(I40)') ival |
|||
i = skipsp (buf, 1, 41) |
|||
if (0 < colcnt) |
|||
for (j = 1; j < colcnt - (40 - i); j = j + 1) |
|||
call wrtchr (outbuf, noutbf, ' ') |
|||
while (i <= 40) |
|||
{ |
|||
call wrtchr (outbuf, noutbf, buf(i:i)) |
|||
i = i + 1 |
|||
} |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
function strnat (str, i, n) |
|||
# Convert a string to a non-negative integer. |
|||
implicit none |
|||
character str(*) |
|||
integer i, n |
|||
integer strnat |
|||
integer j |
|||
strnat = 0 |
|||
for (j = 0; j < n; j = j + 1) |
|||
strnat = (10 * strnat) + (ichar (str(i + j)) - ichar ('0')) |
|||
end |
|||
function strint (str, i, n) |
|||
# Convert a string to an integer |
|||
implicit none |
|||
character str(*) |
|||
integer i, n |
|||
integer strint |
|||
integer strnat |
|||
if (str(i) == '-') |
|||
strint = -strnat (str, i + 1, n - 1) |
|||
else |
|||
strint = strnat (str, i, n) |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
subroutine put1 (code, i, opcode) |
|||
# Store a 1-byte operation. |
|||
implicit none |
|||
integer*1 code(0 : CODESZ - 1) # Byte code. |
|||
integer i # Address to put the code at. |
|||
integer*1 opcode |
|||
if (CODESZ - i < 1) |
|||
{ |
|||
write (*, '(''address beyond the size of memory'')') |
|||
stop |
|||
} |
|||
code(i) = opcode |
|||
end |
|||
subroutine put5 (code, i, opcode, ival) |
|||
# Store a 5-byte operation. |
|||
implicit none |
|||
integer*1 code(0 : CODESZ - 1) # Byte code. |
|||
integer i # Address to put the code at. |
|||
integer*1 opcode # |
|||
integer ival # Immediate integer value. |
|||
integer*4 ival32 |
|||
integer*1 ival8(4) |
|||
equivalence (ival32, ival8) |
|||
if (CODESZ - i < 5) |
|||
{ |
|||
write (*, '(''address beyond the size of memory'')') |
|||
stop |
|||
} |
|||
code(i) = opcode |
|||
# Native-endian storage. |
|||
ival32 = ival |
|||
code(i + 1) = ival8(1) |
|||
code(i + 2) = ival8(2) |
|||
code(i + 3) = ival8(3) |
|||
code(i + 4) = ival8(4) |
|||
end |
|||
function getimm (code, i) |
|||
# Get an immediate value from the code, at address i. |
|||
implicit none |
|||
integer*1 code(0 : CODESZ - 1) # Byte code. |
|||
integer i # Address at which the integer resides. |
|||
integer getimm # Immediate integer value. |
|||
integer*4 ival32 |
|||
integer*1 ival8(4) |
|||
equivalence (ival32, ival8) |
|||
if (i < 0 || CODESZ <= i + 3) |
|||
{ |
|||
write (*, '(''code address out of range'')') |
|||
stop |
|||
} |
|||
# Native-endian storage. |
|||
ival8(1) = code(i) |
|||
ival8(2) = code(i + 1) |
|||
ival8(3) = code(i + 2) |
|||
ival8(4) = code(i + 3) |
|||
getimm = ival32 |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
subroutine rdhead (datsiz, strsiz) |
|||
# Read the header line. |
|||
implicit none |
|||
integer datsiz |
|||
integer strsiz |
|||
integer skipnd |
|||
integer skipdg |
|||
integer strnat |
|||
character line(LINESZ) |
|||
character*20 fmt |
|||
integer i1, j1, i2, j2 |
|||
# Read a line of text as an array of characters. |
|||
write (fmt, '(''('', I10, ''A)'')') LINESZ |
|||
read (*, fmt) line |
|||
i1 = skipnd (line, 1, LINESZ + 1) |
|||
j1 = skipdg (line, i1, LINESZ + 1) |
|||
i2 = skipnd (line, j1, LINESZ + 1) |
|||
j2 = skipdg (line, i2, LINESZ + 1) |
|||
if (i1 == j1 || i2 == j2) |
|||
{ |
|||
write (*, '(''bad header line'')') |
|||
stop |
|||
} |
|||
datsiz = strnat (line, i1, j1 - i1) |
|||
strsiz = strnat (line, i2, j2 - i2) |
|||
end |
|||
subroutine rdstrs (strs, strsiz, strngs, istrng) |
|||
implicit none |
|||
integer strs(1:STRSZ, 0 : MAXSTR - 1) |
|||
integer strsiz |
|||
character strngs(STRNSZ) # String pool. |
|||
integer istrng # String pool's next slot. |
|||
integer trimrt |
|||
integer skipsp |
|||
character line(LINESZ) |
|||
character*20 fmt |
|||
integer j |
|||
integer i, n |
|||
integer i0, n0 |
|||
# Read lines of text as an array of characters. |
|||
write (fmt, '(''('', I10, ''A)'')') LINESZ |
|||
for (j = 0; j < strsiz; j = j + 1) |
|||
{ |
|||
read (*, fmt) line |
|||
n0 = trimrt (line, LINESZ) |
|||
i0 = skipsp (line, 1, n0 + 1) |
|||
if (i0 == n0 + 1) |
|||
{ |
|||
write (*, '(''blank line where a string should be'')') |
|||
stop |
|||
} |
|||
call addstq (strngs, istrng, line, i0, n0 - i0 + 1, i, n) |
|||
strs(STRI, j) = i |
|||
strs(STRN, j) = n |
|||
} |
|||
end |
|||
function stropc (str, i, n) |
|||
# Convert substring to an opcode. |
|||
implicit none |
|||
character str(*) |
|||
integer i, n |
|||
integer*1 stropc |
|||
stropc = -1 |
|||
if (n == 2) |
|||
{ |
|||
if (str(i) == 'l') |
|||
{ |
|||
if (str(i + 1) == 't') |
|||
stropc = OPLT |
|||
else if (str(i + 1) == 'e') |
|||
stropc = OPLE |
|||
} |
|||
else if (str(i) == 'g') |
|||
{ |
|||
if (str(i + 1) == 't') |
|||
stropc = OPGT |
|||
else if (str(i + 1) == 'e') |
|||
stropc = OPGE |
|||
} |
|||
else if (str(i) == 'e' && str(i + 1) == 'q') |
|||
stropc = OPEQ |
|||
else if (str(i) == 'n' && str(i + 1) == 'e') |
|||
stropc = OPNE |
|||
else if (str(i) == 'o' && str(i + 1) == 'r') |
|||
stropc = OPOR |
|||
else if (str(i) == 'j' && str(i + 1) == 'z') |
|||
stropc = OPJZ |
|||
} |
|||
else if (n == 3) |
|||
{ |
|||
if (str(i) == 'a') |
|||
{ |
|||
if (str(i + 1) == 'd' && str(i + 2) == 'd') |
|||
stropc = OPADD |
|||
else if (str(i + 1) == 'n' && str(i + 2) == 'd') |
|||
stropc = OPAND |
|||
} |
|||
else if (str(i) == 'm') |
|||
{ |
|||
if (str(i + 1) == 'o' && str(i + 2) == 'd') |
|||
stropc = OPMOD |
|||
else if (str(i + 1) == 'u' && str(i + 2) == 'l') |
|||
stropc = OPMUL |
|||
} |
|||
else if (str(i) == 'n') |
|||
{ |
|||
if (str(i + 1) == 'e' && str(i + 2) == 'g') |
|||
stropc = OPNEG |
|||
else if (str(i + 1) == 'o' && str(i + 2) == 't') |
|||
stropc = OPNOT |
|||
} |
|||
else if (str(i) == 's' && str(i + 1) == 'u' _ |
|||
&& str(i + 2) == 'b') |
|||
stropc = OPSUB |
|||
else if (str(i) == 'd' && str(i + 1) == 'i' _ |
|||
&& str(i + 2) == 'v') |
|||
stropc = OPDIV |
|||
else if (str(i) == 'j' && str(i + 1) == 'm' _ |
|||
&& str(i + 2) == 'p') |
|||
stropc = OPJMP |
|||
} |
|||
else if (n == 4) |
|||
{ |
|||
if (str(i) == 'p') |
|||
{ |
|||
if (str(i + 1) == 'r' && str(i + 2) == 't') |
|||
{ |
|||
if (str(i + 3) == 'c') |
|||
stropc = OPPRTC |
|||
else if (str(i + 3) == 'i') |
|||
stropc = OPPRTI |
|||
else if (str(i + 3) == 's') |
|||
stropc = OPPRTS |
|||
} |
|||
if (str(i + 1) == 'u' && str(i + 2) == 's' _ |
|||
&& str(i + 3) == 'h') |
|||
stropc = OPPUSH |
|||
} |
|||
else if (str(i) == 'h' && str(i + 1) == 'a' _ |
|||
&& str(i + 2) == 'l' && str(i + 3) == 't') |
|||
stropc = OPHALT |
|||
} |
|||
else if (n == 5) |
|||
{ |
|||
if (str(i) == 'f' && str(i + 1) == 'e' && str(i + 2) == 't' _ |
|||
&& str(i + 3) == 'c' && str(i + 4) == 'h') |
|||
stropc = OPFTCH |
|||
if (str(i) == 's' && str(i + 1) == 't' && str(i + 2) == 'o' _ |
|||
&& str(i + 3) == 'r' && str(i + 4) == 'e') |
|||
stropc = OPSTOR |
|||
} |
|||
if (stropc == -1) |
|||
{ |
|||
write (*, '(''unrecognized opcode name'')') |
|||
stop |
|||
} |
|||
end |
|||
subroutine rdops (code) |
|||
# Read the opcodes and their immediate values. |
|||
implicit none |
|||
integer*1 code(0 : CODESZ - 1) # The byte code. |
|||
integer trimrt |
|||
integer skipsp |
|||
integer skipal |
|||
integer skipdg |
|||
integer skipd1 |
|||
integer skipn1 |
|||
integer strnat |
|||
integer strint |
|||
integer*1 stropc |
|||
character tolowr |
|||
character line(LINESZ) |
|||
character*20 fmt |
|||
integer stat |
|||
integer n |
|||
integer j |
|||
integer iaddr, jaddr # Address index and size. |
|||
integer iopnm, jopnm # Opcode name index and size. |
|||
integer iarg, jarg |
|||
integer addr |
|||
integer arg |
|||
integer*1 opcode |
|||
# Read lines of text as an array of characters. |
|||
write (fmt, '(''('', I10, ''A)'')') LINESZ |
|||
read (*, fmt, iostat = stat) line |
|||
while (stat == 0) |
|||
{ |
|||
n = trimrt (line, LINESZ) |
|||
for (j = 1; j <= n; j = j + 1) |
|||
line(j) = tolowr (line(j)) |
|||
iaddr = skipsp (line, 1, n + 1) |
|||
jaddr = skipdg (line, iaddr, n + 1) |
|||
addr = strnat (line, iaddr, jaddr - iaddr) |
|||
iopnm = skipsp (line, jaddr, n + 1) |
|||
jopnm = skipal (line, iopnm, n + 1) |
|||
opcode = stropc (line, iopnm, jopnm - iopnm) |
|||
if (opcode == OPPUSH || opcode == OPFTCH || opcode == OPSTOR _ |
|||
|| opcode == OPJMP || opcode == OPJZ) |
|||
{ |
|||
iarg = skipn1 (line, jopnm, n + 1) |
|||
jarg = skipd1 (line, iarg, n + 1) |
|||
arg = strint (line, iarg, jarg - iarg) |
|||
call put5 (code, addr, opcode, arg) |
|||
} |
|||
else |
|||
call put1 (code, addr, opcode) |
|||
read (*, fmt, iostat = stat) line |
|||
} |
|||
end |
|||
subroutine rdcode (strs, strngs, istrng, code) |
|||
# Read and parse the "assembly" code. |
|||
implicit none |
|||
integer strs(1:STRSZ, 0 : MAXSTR - 1) |
|||
character strngs(STRNSZ) # String pool. |
|||
integer istrng # String pool's next slot. |
|||
integer*1 code(0 : CODESZ - 1) # The byte code. |
|||
integer datsiz |
|||
integer strsiz |
|||
call rdhead (datsiz, strsiz) |
|||
if (MAXVAR < datsiz) |
|||
{ |
|||
write (*, '(''too many variables'')') |
|||
stop |
|||
} |
|||
if (MAXSTR < strsiz) |
|||
{ |
|||
write (*, '(''too many strings'')') |
|||
stop |
|||
} |
|||
call rdstrs (strs, strsiz, strngs, istrng) |
|||
call rdops (code) |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
subroutine stkbin (sp) |
|||
implicit none |
|||
integer sp |
|||
if (sp < 3) |
|||
{ |
|||
write (*, '(''stack underflow in binary operation'')') |
|||
stop |
|||
} |
|||
end |
|||
subroutine stkun (sp) |
|||
implicit none |
|||
integer sp |
|||
if (sp < 2) |
|||
{ |
|||
write (*, '(''stack underflow in unary operation'')') |
|||
stop |
|||
} |
|||
end |
|||
function logl2i (b) |
|||
implicit none |
|||
logical b |
|||
integer logl2i |
|||
if (b) |
|||
logl2i = 1 |
|||
else |
|||
logl2i = 0 |
|||
end |
|||
subroutine rncode (strs, strngs, code, outbuf, noutbf) |
|||
# Run the code. |
|||
implicit none |
|||
integer strs(1:STRSZ, 0 : MAXSTR - 1) |
|||
character strngs(STRNSZ) # String pool. |
|||
integer*1 code(0 : CODESZ - 1) # The byte code. |
|||
character outbuf(OUTLSZ) # Output line buffer. |
|||
integer noutbf # Number of characters in outbuf. |
|||
integer logl2i |
|||
integer getimm |
|||
integer pop |
|||
integer stack(STCKSZ) |
|||
integer data(0 : MAXVAR - 1) |
|||
integer sp # Stack pointer. |
|||
integer pc # Program counter. |
|||
integer ip # Instruction pointer. |
|||
equivalence (pc, ip) # LOL, use either name. :) |
|||
integer i, n |
|||
integer*1 opcode |
|||
logical done |
|||
sp = 1 |
|||
ip = 0 |
|||
done = .false. |
|||
while (!done) |
|||
{ |
|||
if (ip < 0 || CODESZ <= ip) |
|||
{ |
|||
write (*, '(''code address out of range'')') |
|||
stop |
|||
} |
|||
opcode = code(ip) |
|||
ip = ip + 1 |
|||
if (opcode == OPADD) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = stack (sp - 1) + stack(sp) |
|||
} |
|||
else if (opcode == OPSUB) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = stack (sp - 1) - stack(sp) |
|||
} |
|||
else if (opcode == OPMUL) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = stack (sp - 1) * stack(sp) |
|||
} |
|||
else if (opcode == OPDIV) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = stack (sp - 1) / stack(sp) |
|||
} |
|||
else if (opcode == OPMOD) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = mod (stack (sp - 1), stack(sp)) |
|||
} |
|||
else if (opcode == OPLT) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = logl2i (stack (sp - 1) < stack(sp)) |
|||
} |
|||
else if (opcode == OPGT) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = logl2i (stack (sp - 1) > stack(sp)) |
|||
} |
|||
else if (opcode == OPLE) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = logl2i (stack (sp - 1) <= stack(sp)) |
|||
} |
|||
else if (opcode == OPGE) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = logl2i (stack (sp - 1) >= stack(sp)) |
|||
} |
|||
else if (opcode == OPEQ) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = logl2i (stack (sp - 1) == stack(sp)) |
|||
} |
|||
else if (opcode == OPNE) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = logl2i (stack (sp - 1) != stack(sp)) |
|||
} |
|||
else if (opcode == OPAND) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = _ |
|||
logl2i (stack (sp - 1) != 0 && stack(sp) != 0) |
|||
} |
|||
else if (opcode == OPOR) |
|||
{ |
|||
call stkbin (sp) |
|||
sp = sp - 1 |
|||
stack(sp - 1) = _ |
|||
logl2i (stack (sp - 1) != 0 || stack(sp) != 0) |
|||
} |
|||
else if (opcode == OPNEG) |
|||
{ |
|||
call stkun (sp) |
|||
stack(sp - 1) = -stack(sp - 1) |
|||
} |
|||
else if (opcode == OPNOT) |
|||
{ |
|||
call stkun (sp) |
|||
stack(sp - 1) = logl2i (stack(sp - 1) == 0) |
|||
} |
|||
else if (opcode == OPPRTC) |
|||
{ |
|||
call wrtchr (outbuf, noutbf, char (pop (stack, sp))) |
|||
} |
|||
else if (opcode == OPPRTI) |
|||
{ |
|||
call wrtint (outbuf, noutbf, pop (stack, sp), 0) |
|||
} |
|||
else if (opcode == OPPRTS) |
|||
{ |
|||
i = pop (stack, sp) |
|||
if (i < 0 || MAXSTR <= i) |
|||
{ |
|||
write (*, '(''string address out of range'')') |
|||
stop |
|||
} |
|||
n = strs(STRN, i) |
|||
i = strs(STRI, i) |
|||
call wrtstr (outbuf, noutbf, strngs, i, n) |
|||
} |
|||
else if (opcode == OPFTCH) |
|||
{ |
|||
i = getimm (code, ip) |
|||
ip = ip + 4 |
|||
if (i < 0 || MAXVAR <= i) |
|||
{ |
|||
write (*, '(''data address out of range'')') |
|||
stop |
|||
} |
|||
call push (stack, sp, data(i)) |
|||
} |
|||
else if (opcode == OPSTOR) |
|||
{ |
|||
i = getimm (code, ip) |
|||
ip = ip + 4 |
|||
if (i < 0 || MAXVAR <= i) |
|||
{ |
|||
write (*, '(''data address out of range'')') |
|||
stop |
|||
} |
|||
data(i) = pop (stack, sp) |
|||
} |
|||
else if (opcode == OPPUSH) |
|||
{ |
|||
call push (stack, sp, getimm (code, ip)) |
|||
ip = ip + 4 |
|||
} |
|||
else if (opcode == OPJMP) |
|||
{ |
|||
ip = ip + getimm (code, ip) |
|||
} |
|||
else if (opcode == OPJZ) |
|||
{ |
|||
if (pop (stack, sp) == 0) |
|||
ip = ip + getimm (code, ip) |
|||
else |
|||
ip = ip + 4 |
|||
} |
|||
else |
|||
{ |
|||
# Halt on OPHALT or any unrecognized code. |
|||
done = .true. |
|||
} |
|||
} |
|||
end |
|||
#--------------------------------------------------------------------- |
|||
program vm |
|||
implicit none |
|||
integer strs(1:STRSZ, 0 : MAXSTR - 1) |
|||
character strngs(STRNSZ) # String pool. |
|||
integer istrng # String pool's next slot. |
|||
integer*1 code(0 : CODESZ - 1) # The byte code. |
|||
character outbuf(OUTLSZ) # Output line buffer. |
|||
integer noutbf # Number of characters in outbuf. |
|||
integer j |
|||
istrng = 1 |
|||
noutbf = 0 |
|||
for (j = 0; j < CODESZ; j = j + 1) |
|||
code(j) = OPHALT |
|||
call rdcode (strs, strngs, istrng, code) |
|||
call rncode (strs, strngs, code, outbuf, noutbf) |
|||
if (noutbf != 0) |
|||
call flushl (outbuf, noutbf) |
|||
end |
|||
######################################################################</lang> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |