Category : Miscellaneous Language Source Code
Archive   : ADA.ZIP
Filename : ATERP.PAS

 
Output of file : ATERP.PAS contained in archive : ADA.ZIP

Program Aug_Terp;
{ Aug_Terp is an interpreter for Augusta, the public domain compiler }
{ which translates a subset of Ada into pseudo-code. The p-code is the }
{ source for Aug_Terp. See Dr. Dobb's Journal numbers 75,77,79,81 for }
{ extensive documentation. }

Const
terp_version = '1.2';
system_size = 16; { 8 or 16 bit machine for heap size calculations }
nl = #13#10; { characters to start a new line }
buflen = 512; { MUST be a multiple of 128 }
buf_max = 511; { (buflen-1) for use in buffer indexing }
page_limit = 63; { highest legal page number (32k/buflen) }
Type
str_ptr_type = ^anystring;
anystring = string[255];
buf_pointer = ^buf_type;
buf_type = record
data: array[0..buf_max] of byte;
next: buf_pointer;
end;
Var
{ The virtual machine }
CP : integer; { p-code instruction pointer }
SP : integer; { stack pointer }
GF : integer; { global frame pointer }
LF : integer; { local frame pointer }
SB : integer; { stack base (points to the bottom of the stack)}
CB : integer; { points to the 1st code byte in current proc.}
CS : integer; { code segment (points to the first byte of code)}
PN : integer; { number of current proc. }

header : record
code_size : integer; { code size in bytes }
max_record : integer; { # of 128-byte records in the file }
max_proc : integer; { # of procedures }
version : integer; { code file version number }
end;
proctable : array[1..256] of record
offset : integer; { offset from CS to proc code }
local_var_bytes : integer; { # bytes needed for local vars }
parm_bytes : integer; { # bytes needed for parameters }
level : byte; { lexical level of the procedure }
end;
page : array[0..page_limit] of buf_pointer;
max_mem,max_page : integer; { maximum buffer and page indexes }
code_file : file; { used for the p-code file I/O }
work_string : anystring; { a work variable for string operations }


Procedure Error(err_num,value: integer);
{ handles errors consistently, giving appropriate state info w/ the message. }
begin
write(nl,'aug-> ');
case err_num of
1: write('Read offset ',value,' out of range');
2: write('Write offset ',value,' out of range');
3: write('Too many pages with ',value,' bytes allocated');
4: write('Out of memory with ',value,' bytes in use');
5: write('Integer multiplication overflow');
6: write('Integer division overflow');
7: write('Call to unimplemented system procedure ',value);
8: write('Illegal op-code ',value);
9: begin
write('Unable to open ');
if value<0 then begin
writeln(paramstr(1)); halt; end
else write('#',value);
end;
end;
writeln(' at PN=',PN,' CP=',CP,' SP=',SP);
halt;
end;

Function Mem_Avail: real;
{ returns the free heap space }
const
system_size = 16; { either 8 or 16 bit system }
var
X : real;
begin
X := Maxavail;
if X<0 then X := X + 65536.0;
if system_size=16 then X := X * 16.0;
Mem_avail := X;
end;


Procedure Load_Program;
{ gets the name of the p-code file, loads it into memory and initializes }
{ the virtual machine. }
var
file_as_byte : file of byte;{ typed file to allow read()'ing header }
name : string[32]; { filename }
recs_per_buf : integer; { number of 128-byte records in a buffer }
temp1,temp2 : byte; { local work variables }
temp3,temp4 : byte;
I : integer;
begin
{ present the intro screen }
clrscr; writeln('A u g - T e r p',nl,'Version ',terp_version);

{ get the filename from the command line and make sure it's available }
if paramcount<>1 then begin
write(nl,'Usage: ATERP filename');
halt; end
else begin
name := paramstr(1);
{$I-} assign(file_as_byte,name); reset(file_as_byte); {$I+}
if IOResult<>0 then error(9,-1);
end;

{ load the header block and make sure it's an augusta code file }
with header do begin
read(file_as_byte, temp1,temp2,temp3,temp4);
code_size := temp2*256 + temp1 - 1920;
max_record := temp4*256 + temp3;
read(file_as_byte, temp1,temp2,temp3,temp4);
max_proc := temp2*256 + temp1; version := temp4*256 + temp3;
end;
read(file_as_byte, temp1,temp2,temp3,temp4);
if not ((temp1=89) and (temp2=4) and (temp3=0) and (temp4=0))
or (filesize(file_as_byte)<1921) then begin
writeln(name,' is not a valid Augusta p-code file.');
halt; end

{ read in only as many proc table entries as the header says exist }
else begin
writeln('Loading ...');
seek(file_as_byte,128);{ skip 116 unused header bytes to the proc table}
for I:=1 to header.max_proc do
with proctable[i] do begin
read(file_as_byte, temp1,temp2,temp3,temp4);
offset := (temp2 shl 8) + temp1;
local_var_bytes := (temp4 shl 8) + temp3;
read(file_as_byte, temp1,temp2,level);
parm_bytes := (temp2 shl 8) + temp1;
end;
end;
close(file_as_byte);

{ reopen the file as untyped, with an implied 128-byte record length }
assign(code_file,name); reset(code_file);

{ make sure there is enough memory to load the whole file. the }
{ heap_space calculations account for 8 or 16 bit Turbo versions. }
if mem_avail<(header.code_size + 1000) then begin
writeln(nl,'Not enough free memory. Only ',mem_avail:6:0,
' bytes are available.');
close(code_file);
halt; end
else begin
{ read the code into a linked list of buffers. on exit max_page is the }
{ highest legal sequential buffer (the first being #0), and the link }
{ pointer for the last buffer is set to nil. }
seek(code_file,15); { skip to the code area }
max_page := -1; max_mem := -1;
recs_per_buf := buflen div 128;
repeat
max_page := max_page + 1;
getmem(page[max_page],sizeof(buf_type));
blockread(code_file,page[max_page]^.data,recs_per_buf,I);
if I=0 then
max_page := max_page - 1
else begin
max_mem := max_mem + I*buflen;
if max_page>0 then page[max_page-1]^.next := page[max_page];
end;
if max_page>page_limit then error(3,max_mem);
until I close(code_file);

{ get two extra buffers for initial stack space }
for I:=1 to 2 do begin
max_page := max_page + 1;
if max_page>page_limit then error(3,max_mem);
getmem(page[max_page],sizeof(buf_type));
page[max_page-1]^.next := page[max_page];
end;
page[max_page]^.next := nil;
end;
clrscr;
end;

Function Get_byte(var offset: integer): byte;
{ gets the byte at Offset and increments Offset to the next byte. if }
{ the offset is out of allocated memory range, call error (and halt). }
var
page_num,pos: integer;
begin
if (offset>max_mem) or (offset<0) then error(1,offset);

{ page_num is the buffer the byte is in, pos is the offset in that buffer }
page_num := offset div buflen; pos := offset mod buflen;
offset := offset + 1; Get_byte := page[page_num]^.data[pos];
end;

Function Get_Word(offset: integer): integer;
{ gets the word at Offset, leaving Offset as it was on entry. call error }
{ if offset is out of range. }
var
page_num,pos,K: integer;
begin
if (offset>=max_mem) or (offset<0) then error(1,offset);

{ page_num is the buffer the 1st byte is in, pos is the offset into it }
page_num := offset div buflen; pos := offset mod buflen;
K := page[page_num]^.data[pos];
if pos=buf_max then begin
page_num := page_num + 1;
pos := 0; end
else pos := pos + 1;
get_word := (page[page_num]^.data[pos] shl 8) + K;
end;

Procedure Put_Word(offset,data: integer);
{ moves Data into memory word at offset, allocating more memory if necessary }
var
page_num,pos : integer;
begin
if offset<0 then
error(2,offset)
else begin
while (offset>max_mem-1) do
if mem_avail error(4,max_mem)
else begin
max_page := max_page + 1;
if max_page>page_limit then error(3,max_mem);
getmem(page[max_page],sizeof(buf_type));
page[max_page-1]^.next := page[max_page];
page[max_page]^.next := nil;
max_mem := max_mem + buflen;
end;
end;

{ page_num is the buffer the 1st byte is in, pos is the offset into it }
page_num := offset div buflen; pos := offset mod buflen;
page[page_num]^.data[pos] := (data and 255);
if pos=buf_max then begin
page_num := page_num + 1;
pos := 0; end
else pos := pos + 1;
page[page_num]^.data[pos] := (data shr 8);
end;

Procedure Put_Byte(offset: integer; data: byte);
{ moves Data into memory byte at offset, allocating more buffers if need be }
var
page_num,pos: integer;
begin
if offset<0 then
error(2,offset)
else begin
while (offset>max_mem) do
if mem_avail error(4,max_mem)
else begin
max_page := max_page + 1;
if max_page>page_limit then error(3,max_mem);
getmem(page[max_page],sizeof(buf_type));
page[max_page-1]^.next := page[max_page];
page[max_page]^.next := nil;
max_mem := max_mem + buflen;
end;
end;

{ page_num is the buffer the 1st byte is in, pos is the offset into it }
page_num := offset div buflen; pos := offset mod buflen;
page[page_num]^.data[pos] := (data and 255);
end;

Function Get_Str_Ptr(offset : integer): str_ptr_type;
{ returns a pointer to a string at Offset. If the string crosses a }
{ buffer boundary, it is copied to Work_String and the pointer }
{ points there. This avoids the non-program info between buffers. }
{ Note: the string pointed to by the result should be copied before }
{ calling Get_str_ptr again, as Work_string may be used for both. }
var
P,Index,L : integer; { buffer page & offset, string length }
T1,T2 : integer; { temporary vars }
work_ptr : str_ptr_type;
begin
P := offset div buflen; Index := offset mod buflen;
{ if the offset is too big call read error }
if P>max_page then error(1,offset);

{ else point work_ptr at the string }
work_ptr := ptr(seg(page[P]^.data[index]),ofs(page[P]^.data[index]));
L := length(work_ptr^);
if (index+L)>buf_max then begin
{ if it crosses a boundary, Copy the 1st part and Get_byte the 2nd, }
{ then point to the finished copy. }
work_string := copy(work_ptr^,1,buf_max-index);
L := L - buf_max + index; offset := offset + buf_max - index + 1;
for T1:=L downto 1 do begin
T2 := get_byte(offset); work_string := work_string + chr(T2);
end;
work_ptr := ptr(seg(work_string),ofs(work_string));
end;
Get_Str_Ptr := work_ptr;
end;

Procedure Store_Str(offset : integer; st : anystring);
{ stores St at Offset, accounting for boundary crossings }
var
str_ptr : str_ptr_type;
T1,T2 : integer;
begin
{ call a read error if the offset is too big }
T1 := offset div buflen;if T1>max_page then error(2,offset);
{ if the string won't cross a buffer boundary, use Copy }
T2 := length(st);
if (T2+offset)<=buf_max then begin
{ point str_ptr to the real address and copy the string }
offset := offset mod buflen;
str_ptr := ptr(seg(page[T1]^.data[offset]),ofs(page[T1]^.data[offset]));
str_ptr^ := st;
end
{ else store the length and the characters, 1 by 1 }
else begin
put_byte(offset,T2); offset := offset + 1;
for T1:=1 to T2 do begin
put_byte(offset,ord(st[T1])); offset := offset + 1;
end;
end;
end;

Procedure Interpret_Code;
{ interprets the op-code program, reutrning when PN is set to zero }
{ by the return from procedure 1. }
const
{ these codes are unassigned and therefore illegal. new ops may be added }
{ by deleting them here and editing the CASE for this procedure to point }
{ to the new handler. 15 is the EOP code and is assigned but illegal. }
illegal_ops: set of byte = [0,10,15,44,62,82..255];
var
byte1 : byte; { gets the op-code byte }
temp1,temp2,temp3,I : integer; { local work variables }

Procedure Load_Or_Store;
{ performs transfers between memory and the (virtual) stack }
{ Note- this routine does not check for invalid codes. }
begin
case byte1 of
1: begin { LDCI w }
temp1 := get_word(CP); { get the immed. word }
put_word(SP,temp1); SP := SP + 2; { push it }
CP := CP + 2; { fix CP and return }
end;
2: begin { LDL w }
temp1 := get_word(CP) + LF; { get local offset + local frame ptr }
put_word(SP,get_word(temp1)); { push the data at that address }
SP := SP + 2;
CP := CP + 2; { fix CP and return }
end;
3: begin { LLA w }
{ push local offset + lf }
put_word(SP,get_word(CP) + LF); SP := SP + 2;
CP := CP + 2;
end;
4: begin { LDB }
{ replace the address with data without really popping/pushing }
temp1 := get_word(SP-2);
put_word(SP-2,(get_word(temp1) and 255));
end;
5: begin { LDO w }
temp1 := get_word(CP) + GF; { get the address + global frame ptr }
put_word(SP,get_word(temp1)); SP := SP + 2; { push it }
CP := CP + 2;
end;
6: begin { LAO w }
{ push the global offset + gf }
put_word(SP,get_word(CP) + GF); SP := SP + 2;
CP := CP + 2;
end;
8..9: begin { LOD b,w or LOA b,w }
{ get the number of levels to back up and trace back }
{ through static links to get the new LF in temp2 }
temp1 := get_byte(CP); temp2 := LF;
while temp1>0 do begin
temp2 := get_word(temp2-6);
temp1 := temp1 - 1;
end;
{ get the offset in temp1 and point CP to the next op byte }
temp1 := get_word(CP); CP := CP + 2;
{ push the data for op 8 or the address for op 9 }
if byte1=8 then put_word(SP,get_word(temp1+temp2))
else put_word(SP,(temp1+temp2));
SP := SP + 2;
end;
11: begin { STO }
SP := SP - 4; temp1 := get_word(SP+2); { pop the data }
{ move it into the indirectly popped address and return }
put_word(get_word(SP),temp1);
end;
12: begin { SINDO }
{ replace the address with data without pop/push }
{ similar to op 4 but without masking the high byte }
temp1 := get_word(SP-2); put_word(SP-2,get_word(temp1));
end;
end;
end; { load_or_store }

Procedure String_Assignment;
{ basic string assignment }
begin
case byte1 of
13: begin { LCA b, }
{ loads the address of a string starting at }
put_word(SP,CP); SP := SP + 2; { push the string address }
temp1 := get_byte(CP); { get the number of chars }
CP := CP + temp1; { point CP past the string and return }
end;
14: begin { SAS }
{ assigns string at to string at }
{ get the source length by reference from the stack. temp1 }
{ is the source length, temp2 is the source address, and }
{ temp3 is the destination address. }
SP := SP - 2; temp1 := get_word(SP); temp2 := temp1 + 1;
temp1 := get_byte(temp1);

SP := SP - 2; temp3 := get_word(SP); { pop the dest. address }
put_byte(temp3,temp1); { dest length = source length }
while temp1>0 do begin { move the chars over }
put_byte(temp3,get_byte(temp2));
temp1 := temp1 - 1;
end;
end;
end;
end; { string_assignment }

Procedure Logical_Operator;
{ performs logical operations on TOS and TOS-1. when 2 words are involved, }
{ SP is decremented and the data are manipulated on the stack to avoid }
{ using intermediate variables. }
begin
case byte1 of
16: begin { AND }
SP := SP - 2; put_word(SP-2,(get_word(SP-2) and get_word(SP)));
end;
17: begin { OR }
SP := SP - 2; put_word(SP-2,(get_word(SP-2) or get_word(SP)));
end;
18: begin { NOT }
{ only 1 word, so SP stays the same }
put_word(SP-2,(not get_word(SP-2)));
end;
end;
end; { logical_operator }

Procedure Int_Math;
{ performs integer math operations on TOS and TOS-1. as above, temporary }
{ variables are avoided. }
var
rtemp1: real; { work variable used to avoid integer math errors }
begin
case byte1 of
19: begin { ADI }
{ pop TOS and add it to TOS-1 }
SP := SP - 2; put_word(SP-2,(get_word(SP-2) + get_word(SP)));
end;
20: begin { NGI }
put_word(SP-2,(not get_word(SP-2)));
end;
21: begin { SBI }
{ pop TOS and subtract it from TOS-1 }
SP := SP - 2; put_word(SP-2,(get_word(SP-2) - get_word(SP)));
end;
22: begin { MPI }
{ integer multiply TOS and TOS-1. error on signed int. overflow }
SP := SP - 2; rtemp1 := get_word(SP-2) * get_word(SP);
if abs(rtemp1)>maxint then error(5,0)
else put_word(SP-2,round(rtemp1));
end;
23: begin { DVI }
{ pop TOS and signed integer divide TOS-1 by it. error on signed }
{ integer out of range, crash if result is out of real range. }
SP := SP - 2; rtemp1 := get_word(SP-2) / get_word(SP);
if abs(rtemp1)>maxint then error(6,0)
else put_word(SP-2,trunc(rtemp1));
end;

45: begin { MODI }
{ TOS-1 mod TOS }
SP := SP - 2; put_word(SP-2,(get_word(SP-2) mod get_word(SP)));
end;
80: begin { INCL w }
temp1 := get_word(CP) + LF; { get the local address }
put_word(temp1,get_word(temp1)+1); { increment w/o another }
CP := CP + 2; { temp and return. }
end;
81: begin { DECL w }
temp1 := get_word(CP) + LF; { get the local address }
put_word(temp1,get_word(temp1)+1); { decrement w/o another }
CP := CP + 2; { temp and return. }
end;
end;
end; { int_math }

Procedure Array_index;
{ these op-codes translate an array index into an address offset }
begin
case byte1 of
24: begin { IND }
{ TOS-1 is the base of an int array, TOS is the index. the }
{ address of the element = + *2. }
SP := SP - 2;
put_word(SP-2,(get_word(SP-2) + get_word(SP)*2));
end;
48: begin { IXA b }
{ as IND except the element size in 'b' is used instead of 2 }
SP := SP - 2;
put_word(SP-2,(get_word(SP-2) + get_word(SP)*get_byte(CP)));
end;
end;
end; { array_index }

Procedure Int_Compare;
{ compare signed integers TOS and TOS-1 and push -1 if the result is }
{ true, 0 if it is false. }
var
test: boolean;
begin
test := false;
case byte1 of
25: begin { EQUI }
SP := SP - 2;
test := (get_word(SP-2) = get_word(SP));
end;
26: begin { NEQI }
SP := SP - 2;
test := (get_word(SP-2) <> get_word(SP));
end;
27: begin { LEQI }
SP := SP - 2;
test := (get_word(SP-2) <= get_word(SP));
end;
28: begin { LESI }
SP := SP - 2;
test := (get_word(SP-2) < get_word(SP));
end;
29: begin { GEQI }
SP := SP - 2;
test := (get_word(SP-2) >= get_word(SP));
end;
30: begin { GTRI }
SP := SP - 2;
test := (get_word(SP-2) > get_word(SP));
end;
end;
if test=true then put_word(SP-2,-1)
else put_word(SP-2,0);
end; { int_compare }

Procedure Str_Compare;
{ compares character strings for equ, gtr, les, etc. by copying them }
{ into Turbo strings and using pascal string compares. }
var
str_ptr : str_ptr_type;
work : anystring;
t4 : integer;
test : boolean;
begin
test := false;
{ pop @s1 and @s2 into temp1 and temp2 respectively }
SP := SP - 4; temp1 := get_word(SP); temp2 := get_word(SP+2);
{ point to them }
str_ptr := Get_Str_Ptr(temp1); work := str_ptr^;
str_ptr := Get_Str_Ptr(temp2);

case byte1 of
31: begin { EQUSTR }
test := (work = str_ptr^);
end;
32: begin { NEQSTR }
test := (work <> str_ptr^);
end;
33: begin { LEQSTR }
test := (work <= str_ptr^);
end;
34: begin { LESSTR }
test := (work < str_ptr^);
end;
35: begin { GEQSTR }
test := (work >= str_ptr^);
end;
36: begin { GTRSTR }
test := (work > str_ptr^);
end;
end;
if test=true then put_word(SP-2,-1)
else put_word(SP-2,0);
end; { str_compare }

Procedure Jump;
{ conducts conditional and unconditional jumps }
begin
case byte1 of
37: begin { UJP w }
{ unconditional jump to CP + w }
CP := CP + 2 + get_word(CP);
end;
38: begin { FJP w }
{ jump only if TOS = 0 }
SP := SP - 2;
if get_word(SP)=0 then CP := CP + get_word(CP);
CP := CP + 2;
end;
39: begin { XJP w1,w2,w3}
{ implements CASE. TOS is the variable, w1 is the min value, }
{ w2 is the max value, and w3 is the offset to the last op }
{ before the jump table (always a 'UJP w'). Note: The odd }
{ design of Augusta's case makes it harder than it has to be. }

{ temp3=X, temp2=min, temp3=max }
SP := SP - 2; temp3 := get_word(SP);
temp1 := get_word(CP); temp2 := get_word(CP+2);

{ CP-> start of the jump table (a UJP to the OTHERS code) }
CP := CP + get_word(CP+4) + 5;

{ if the var is in range, CP->address of that table entry + }
{ the word there + 2 }
if temp3 in[temp1..temp2] then begin
CP := CP + 3 + 2*(temp3-temp1);
CP := CP + 2 + get_word(CP);
end;
end;
end;
end; { jump }

Procedure Call_Or_Return;
{ processes calls and returns to procedures and functions }
begin
case byte1 of
40: begin { CLP b }
{ get the proc number and push the frame mark }
I := get_byte(CP);
put_word(SP,proctable[I].level); { new level }
put_word(SP+2,PN); { old PN }
put_word(SP+4,CP); { return address }
put_word(SP+6,CB); { old CB }
put_word(SP+8,LF); { static link }
put_word(SP+10,LF); { dynamic link }
put_word(SP+12,proctable[I].parm_bytes);
SP := SP + 14; LF := SP;
CP := proctable[I].offset; PN := I; CB := CP;

{ allocate stack for local vars }
while SP<(LF+proctable[I].local_var_bytes) do begin
put_word(SP,0); SP := SP + 2;
end;
if Odd(proctable[I].local_var_bytes) then SP := SP - 1;
end;
41: begin { CGP b }
I := get_byte(CP);
if I>0 then put_word(SP,proctable[I].level) { new level }
else put_word(SP,0);
put_word(SP+2,PN); { old PN }
if I>0 then put_word(SP+4,CP) { return address }
else put_word(SP+4,-1);
put_word(SP+6,CB); { old CB }
put_word(SP+8,GF); { global frame }
put_word(SP+10,LF);
put_word(SP+12,proctable[I].parm_bytes);
SP := SP + 14; LF := SP;
CP := proctable[I].offset; PN := I; CB := CP;

{ allocate stack for local vars }
while SP<(LF+proctable[I].local_var_bytes) do begin
put_word(SP,0); SP := SP + 2;
end;
if Odd(proctable[I].local_var_bytes) then SP := SP - 1;
end;
46: begin { CIP b }
I := get_byte(CP);
put_word(SP,proctable[I].level); { new level }
put_word(SP+2,PN); { old PN }
put_word(SP+4,CP); { return address }
put_word(SP+6,CB); { old CB }
{ trace back static links until either a lower level frame }
{ or the global frame is found }
temp1 := get_word(LF-6);
repeat
temp2 := get_word(temp1-14);
if temp2<=proctable[I].level then temp1 := get_word(temp1-6);
until (temp2=1) or (temp2>proctable[I].level);
put_word(SP+8,temp1); { static link }
put_word(SP+10,LF); { dynamic link }
put_word(SP+12,proctable[I].parm_bytes);
SP := SP + 14; LF := SP;
CP := proctable[I].offset; PN := I; CB := CP;

{ allocate stack for local vars }
while SP<(LF+proctable[I].local_var_bytes) do begin
put_word(SP,0); SP := SP + 2;
end;
if Odd(proctable[I].local_var_bytes) then SP := SP - 1;
end;
43: begin { RET }
SP := LF - 14 - get_word(LF-2)*2; { pop 7 words + any parms }
CB := get_word(LF-8); { restore the machine regs }
CP := get_word(LF-10); { from the stack frame info }
PN := get_word(LF-12);
LF := get_word(LF-4); { restore LF last and return }
end;
47: begin { RNP }
temp1 := get_word(SP-2); { save for return }
{ restore as above but saving a word for the TOS return value }
SP := LF - 12 - get_word(LF-2)*2;
CB := get_word(LF-8); CP := get_word(LF-10);
PN := get_word(LF-12); LF := get_word(LF-4);
{ put the return value in the saved word and return }
put_word(SP-2,temp1);
end;
end;
end;

Procedure Short_Load;
{ single-byte op codes to load local data or a constant. }
{ the stack pointer is incremented at the end to save code }
begin
case byte1 of
49..56: begin { SLDL0..SLDL7 }
{ short load local word data at offset 0-7 }
temp1 := byte1 - 49 + LF;
put_word(SP,get_word(temp1));
end;
57: begin { SLDO b }
{ load global word data at offset 'b' }
temp1 := get_byte(CP) + GF;
put_word(SP,get_word(temp1));
end;
58: begin { SLAO b }
{ load address of global offset 'b' }
put_word(SP,(get_byte(CP)+GF));
end;
59: begin { SLLA b }
{ load address of local offset 'b' }
put_word(SP,(get_byte(CP)+LF));
end;
60: begin { SLDL b }
{ load data at local offset 'b' }
temp1 := get_byte(CP) + LF;
put_word(SP,get_word(temp1));
end;
61: begin { SLDC b }
{ load constant 'b'}
put_word(SP,get_byte(CP));
end;
63: begin { SLDCN1 }
{ load -1 }
put_word(SP,-1);
end;
64..79: begin { SLDC0..SLDC15 }
{ load a constant in the range 0..15 }
put_word(SP,(byte1 - 64));
end;
end;
SP := SP + 2;
end; { short_load }

Procedure System_Call;
{ handles input/output for the augusta program through procedure calls }
var
Str_Ptr : str_ptr_type; { ptr to real address of a string parm }
Ch : char; { temporary var for character reads }
t4,t5 : integer; { extra work vars }
begin
byte1 := get_byte(CP); { get the function number }
case byte1 of
1: begin {GETSTR}
{ pop the offset}
SP := SP - 2; temp1 := get_word(SP);
{ temp2=page, temp3=index into the page }
temp2 := temp1 div buflen; temp3 := temp1 mod buflen;
{ if it's out of range call write error }
if temp2>max_page then error(2,temp1);

{ else read the string and store it }
read(work_string);
store_str(temp1,work_string);
end;
2,8: begin {PUTLINE, PUTSTR}
{ uses pointers as above. 1st get the offset,page & index }
SP := SP - 2; temp1 := get_word(SP);
{ point str_ptr to the string and call writeln }
str_ptr := Get_Str_Ptr(temp1);
write(str_ptr^);
if byte1=2 then writeln;
end;
3: begin {GETINT}
readln(I);
SP := SP - 2; put_word(get_word(SP),I);
end;
4: begin {PUTINT}
SP := SP - 2; write(get_word(SP));
end;
5: begin {GETCHAR}
SP := SP - 2; temp1 := get_word(SP);
read(ch); put_word(temp1,ord(ch));
end;
6: begin {PUTCHAR}
SP := SP - 2; temp1 := get_word(SP);
write(char(get_word(temp1)));
end;
7: writeln; {NEWLINE}
9: begin {PEEK}
temp1 := get_word(SP-2); temp1 := Mem[DSeg:temp1];
put_word(SP-2,temp1);
end;
10: begin {POKE}
SP := SP - 4; temp1 := get_word(SP+2); temp2 := get_word(SP);
Mem[DSeg:temp2] := temp1;
end;
11: begin {SUBSTR}
{ temp1:[email protected], temp2:[email protected], temp3:=len, T4:=start }
SP := SP - 8; temp1 := get_word(SP+2); temp2 := get_word(SP);
temp3 := get_word(SP+6); T4 := get_word(SP+4); { len & start }
str_ptr := get_str_ptr(temp1);
work_string := copy(str_ptr^,T4,Temp3);
store_str(temp2,work_string);
end;
12..13: begin {MOVELEFT, MOVERIGHT}
SP := SP - 6;
temp1 := get_word(SP+4); temp2 := get_word(SP+2);
temp3 := get_word(SP); temp3 := get_byte(temp3);
while temp1>1 do begin
put_word(temp2,temp3); temp1 := temp1 - 2;
if byte1=12 then temp2 := temp2 + 2
else temp2 := temp2 - 2;
end;
if temp1>0 then put_byte(temp2,temp3);
end;
28: begin {CHAR}
SP := SP - 2; temp1 := get_word(SP); temp2 := get_word(SP-2);
{ if pos>len(s1) then char:=0 else char:=s1[pos] }
if temp1>get_byte(temp2) then
put_word(SP-2,0)
else begin
temp2 := temp2 + temp1 - 1; temp1 := get_byte(temp2);
put_word(SP-2,temp1);
end;
end;
30: begin {PUTBOOL}
SP := SP - 2;
if get_word(SP)=0 then write(false)
else write(true);
end;
34: begin {APPEND}
{ pop the addresses of s2 and s1 respectively }
SP := SP - 4; temp1 := get_word(SP+2); temp2 := get_word(SP);
{ get len(s2) and len(s1) and increment the pointer to each }
temp3 := get_byte(temp1); I := get_byte(temp2);
{ len(s1) := len(s1) + len(s2), point to 1st empty spot in s1 }
put_byte(temp2-1,temp3+I); temp2 := temp2 + I;
{ transfer s2 onto s1 char by char }
while temp3>0 do begin
I := get_byte(temp1); put_byte(temp2,I); temp2 := temp2 + 1;
end;
end;
35: begin {ASSIGN}
{ get the address of s1[pos] }
SP := SP - 6; temp1 := get_word(SP+4) + get_word(SP+2);
{ get value and put it into the string }
temp2 := get_word(SP); put_byte(temp1,temp2);
end;
40: begin {KEYPRESS}
if keypressed then put_word(SP,-1) else put_word(SP,0);
SP := SP + 2;
end;
else error(7,byte1);
end;
end; { system_call }

begin
Repeat

{ get an op-code byte from the buffer }
byte1 := get_byte(CP);

{ if it's an illegal code, print an error and halt }
if byte1 in illegal_ops then error(8,byte1)

{ if it's a legal code, branch to the procedure handling that op class }
else begin
case byte1 of { Note- indented procedures are repeats from }
1..12: load_or_store; { a previous line. }
13..14: string_assignment;
{ 15: this is a special end-of-proc code, assigned but not executed }
16..18: logical_operator;
19..23: int_math;
24: array_index;
25..30: int_compare;
31..36: str_compare;
37..39: jump;
40..41: call_or_return;
42: system_call;
43..44: call_or_return;
45: int_math;
46..47: call_or_return;
48: array_index;
49..79: short_load;
80..81: int_math;
end;
end;

Until PN=0;
end; { interpret_code }


BEGIN

{ load the augusta program into a linked sequence of buffers }
load_program;

{ initialize the stack at the 1st byte after the program }
SB := header.code_size + 1; SP := SB;

{ start execution by faking a call to proc 1 from proc 0 (which doesn't }
{ exist). when the program ends with a return, PN will be set to zero, }
{ signalling the interpreter to stop. }

put_word(SP,$0129); { CGP 1 p-code, last byte first }
PN := 0; CP := SP; CB := CP;
GF := SP + 14; LF := GF;

{ process code until the program terminates itself }
interpret_code;

{ free up all the heap space allocated to the program }
for pn:=0 to max_page do freemem(page[pn],sizeof(buf_type));

END.