home *** CD-ROM | disk | FTP | other *** search
- 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<recs_per_buf;
- 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<sizeof(buf_type) then
- 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<sizeof(buf_type) then
- 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,<chars> }
- { loads the address of a string starting at <CP> }
- 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 <TOS> to string at <TOS-1> }
- { 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 = <TOS-> + <TOS>*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 <TOS> 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:=@s2, temp2:=@s1, 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.