PVM Definition

David Nordstrom
George Mason University

PVM (for PAXI Virtual Machine) is a very simple architecture intended to provide an emulated target machine for PAXI compilers. Its consists of:

Memory

Memory is emulated by two integer arrays, the code store and the data store. Instructions are stored in the code store beginning at location 0. Data (integers and character strings) are stored in the data store.

The first 500 locations of the data store are used as the system stack. The stack pointer SP holds the address of the top of stack item. (Notice that for a non-empty stack this means that SP holds the address of an occupied position, not the next available free position.) The stack grows downwards in memory (i.e. towards lower addresses) so the "top" of stack is the lowest occupied stack location.

Registers

There is an (integer) instruction pointer, IP, which holds the address of the next instruction to be executed. Here an "address" is simply an integer index into the the code store array. There is also a register array consisting of three integers. Register 0 is used as the stack pointer, SP, which always points to the top of the system stack.

The PVM Instruction Set

All PVM instructions consist of three integers: an opcode and two arguments. In the following table, under arguments, D and E are indexes into the data store, R and S are register numbers (indexes into the register array), A is an index into the code store, and N is a constant. If an argument is not used a 0 appears in its place. The mnemonics are for convenience -- only the opcodes are understood by a PVM emulator.

OpcodeMnemonicArguments Description
1 mov D, E Move - copy value from E to D in data store
2 mvi D, N Move immediate - put constant value N in D in data store
3 mif D, E Move indirect from - copy with source indirect and destination direct
4 mit D, E Move indirect to - copy with source direct and destination indirect
5 lri R, N Load register immediate - put constant value N in register R
6 ldr R, D Load register - copy value from D in data store to register R
7 str D, R Store register - copy value from register R to D in data store
8 mvr R, S Move register - copy value from register S to register R
9 add D, E Add - add values in D and E in data store and leave sum in D
10 addri R, N Add register immediate - add constant N to value in register R
11 sub D, E Subtract - subtract value in E from value in D in data store and leave difference in D
12 mul D, E Multiply - multiply values in D and E in data store and leave product in D
13 div D, E Divide - divide value in D by value in E in data store and leave quotient in D
14 or D, E Or - leave OR of Boolean values in D and E in data store in D
15 and D, E And - leave AND of Boolean values in D and E in data store in D
16 not D, 0 Not - replace value in data store at D with its logical complement
17 b A, 0 Branch - transfer control to location A in code store
18 beq A, D Branch on equal - if value at D in data store is zero branch to A in code store
19 bne A, D Branch on not equal - if value at D in data store is not zero branch to A in code store
20 bgt A, D Branch on greater than - if value at D in data store is > 0 branch to A in code store
21 bge A, D Branch on greater than or equal - if value at D in data store is >= 0 branch to A in code store
22 blt A, D Branch on less than - if value at D in data store is < 0 branch to A in code store
23 ble A, D Branch on less than or equal - if value at D in data store is <= 0 branch to A in code store
24 pushd D, 0 Push data - push value at D in data store onto the stack
25 pushr R, 0 Push register - push value from register R onto the stack
26 pushi N, 0 Push immediate - push constant N onto the stack
27 popd D, 0 Pop data - pop top of stack into data store at location D
28 popr R, 0 Pop register - pop top of stack into register R
29 puti D, 0 Put integer - display integer at D in data store
30 puts D, 0 Put string - display NULL-terminated string beginning at D in data store
31 line 0, 0 New line - move cursor to beginning of next line
32 geti D, 0 Get integer - accept integer from keyboard and store at D in data store
33 gets D, 0 Get string - accept string from keyboard and store NULL-terminated at D in data store
34 call A, 0 Call - push IP and enter the subprogram at A in code store
35 ret N, 0 Return - return from subprogram call removing N parameters from the stack
36 stop 0, 0 Stop - end program

The Move Instructions

There are a number of move instructions to provide several addressing modes. All addresses used by the move instructions are indexes into the data store. The mov instruction uses direct addressing for both of its arguments, i.e. they are addresses in the data store. mvi uses direct addressing and immediate for its second, i.e. the second argument is a (constant) value, not an address.

The mif and mit instructions use indirect addressing. In indirect addressing a memory location is used as a pointer. mif treats its second argument (the source) as a pointer and uses direct addressing for its first argument (the target). mit treats is first argument as a pointer and uses direct addressing for its second argument.

The ldr (load register) and str (store register) instructions copy values between memory and registers. ldr R, D copies the value from data store D to register R and str D, R copies the value from register R to data store D. lri R, N (load register immediate) puts the constant value N into register R. mvr (move register) is used to copy a value from one register to another.

The Arithmetic and Logical Instructions

The arithmetic instructions (except addri) all have the same format. The two arguments are addresses in the data store. sub, D, E, for example, has the meaning: data_store[D] = data_store[D] - data_store[E]. addri adds a constant value to a register.

The logical instructions and and or work in the same manner as the arithmetic instructions. not D, 0 replaces data_store[D] with its logical complement. These instructions assume the value used for true or false is 1 or 0. The result is undefined for other values, i.e. if applied to values other than 1 or 0, it doesn't matter what values are left by and, or, and not.

The Branching Instructions

b A, 0 (branch to A) does an unconditional transfer of control to location A in the code store. It accomplishes this by putting A into IP.

The remaining branching instructions are all conditional branching instructions. Each takes two arguments: code store address A and data store address D. It compares D to 0 and if a test is successful transfers control to location A in the code store. The test is given by the name of the instruction. For example bgt A, D branches to A if data_store[D] is greater than 0.

The Stack Operation Instructions

The various push and pop instructions work on the system stack. Recall that SP points to the top of stack item (in the data store) rather than the next available free space. Thus a push instruction adjusts SP before storing a value and a pop instruction adjusts SP after reading a value. Recall also that the stack grows downwards in memory so a push instruction decrements SP.

There are three push instructions: pushd pushes a value from the data store, pushr pushes a register value, and pushi pushes a constant. There are two pop instructions: popd pops top of stack into the data store and popr pops top of stack into a register.

The I/O Instructions

Instead of calling operating system services PVM supplies several instructions to do (very) basic input and output.

puti D, 0 displays the integer stored at data_store[D] and geti D, 0 accepts an integer from the keyboard and stores it in data_store[D]. line displays newline (i.e. moves the cursor to the beginning of the next line).

gets and puts do character string input and output. A string is stored in the data store as an array of ASCII codes terminated by a 0. Each ASCII code is stored in one integer position in the data store since "byte" has no meaning in PVM.

puts D, 0 displays the string beginning at D in the data store. gets D, 0 accepts a string from the keyboard and stores it, with a 0 terminator, in the data store beginning at D. gets accepts all characters entered up to the point where the user hits the enter key. This means that gets behaves like "cin.getline()" rather than "cin >>". It does not store a newline (ASCII 10) at the end of the string.

Call and Return

call A, 0 makes a call to the subprogram at (code store) address A. It pushes IP onto the stack and then copies A into IP. ret N, 0 returns from a subprogram call and clears N parameters from the stack. It pops top of stack into IP and then adds N to SP.

Stop

The stop instruction terminates a running program and exits PVM.

The Fetch-Execute Cycle

PVM runs programs using the classical fetch-execute cycle. This is a loop repeating the following steps in this order: Notice that when an instruction is being executed IP does not point to that instruction but to the instruction which follows it.