REFERENCE (as of
OPERATION
CODES (hexadecimal)
opcode (hexadecimal) |
mnemonic |
|
action |
|
(note 1) |
00 |
(not allowed) |
|
|
|
01 |
ADD s1, sd2 |
|
sd2=sd2+s1 |
|
02 |
ADD s1, s2, d3 |
|
d3=s2+s1 |
|
03 |
SUB s1, sd2 |
|
sd2=sd2-s1 |
|
04 |
SUB s1, s2, d3 |
|
d3=s2-s1 |
|
05 |
MUL s1, sd2 |
|
sd2=sd2´s1 |
|
06 |
MUL s1, s2, d3 |
|
d3=s2´s1 |
|
07 |
DIV s1, sd2 |
|
sd2=sd2¸s1 |
|
08 |
DIV s1, s2, d3 |
|
d3=s2¸s1 |
(note 2) |
09 |
HALT |
|
|
|
0A |
JMP a1 |
|
PC=a1 |
|
0B |
MOVE s1, d2 |
|
d2=s1 |
|
0C |
MOVEA a1, d2 |
|
d2=a1 |
|
0D |
INC sd1 |
|
sd1=sd1+1 |
|
0E |
DEC sd1 |
|
sd1=sd1-1 |
|
0F |
PUSH s1 |
|
SP=SP-4; memory[SP]=s1 |
|
10 |
PUSHA a1 |
|
SP=SP-4; memory[SP]=a1 |
|
11 |
POP d1 |
|
d1=memory[SP];
SP=SP+4 |
(note 7) |
12 |
CALL s1, a2 |
|
SP=SP-4; newfp=SP; memory[SP]=4´s1; SP=SP-4; memory[SP]=FP; SP=SP-4; memory[SP]=PC; FP=newfp; PC=a2 |
|
13 |
RET |
|
retaddr=memory[FP-8]; newfp=memory[FP-4]; nargs=memory[FP]; SP=FP+nargs; FP=newfp; PC=retaddr |
|
14 |
CMP s1, s2 |
|
flagZ=(s1==s2); flagN=(s1<s2) |
|
15 |
JEQL a1 |
|
if (flagZ) PC=a1 |
|
16 |
JNEQ a1 |
|
if (!flagZ) PC=a1 |
|
17 |
JLSS a1 |
|
if (flagN) PC=a1 |
|
18 |
JLEQ a1 |
|
if (flagZ|| flagN) PC=a1 |
|
19 |
JGTR a1 |
|
if (!flagZ&& !flagN) PC=a1 |
|
1A |
JGEQ a1 |
|
if (!flagN) PC=a1 |
|
1B |
TEST s1 |
|
flagZ=(s1==0); flagN=(s1<0) |
|
1C |
AND s1, sd2 |
|
sd2=sd2&s1 |
|
1D |
AND s1, s2, d3 |
|
d3=s2&s1 |
|
1E |
OR s1, sd2 |
|
sd2=sd2
| s1 |
|
1F |
OR s1, s2, d3 |
|
d3=s2
| s1 |
|
20 |
XOR s1, sd2 |
|
sd2=sd2Ĺ s1 |
|
21 |
XOR s1, s2, d3 |
|
d3=s2Ĺ s1 |
|
22 |
NOT sd1 |
|
sd1=Ř sd1 |
|
23 |
NOT s1, d2 |
|
d2=Ř s1 |
|
24 |
NEG sd1 |
|
sd1=-
sd1 |
|
25 |
NEG s1, d2 |
|
d2=-
s1 |
(note 3) |
26 |
JMPI s1, a2 |
|
loc=
a2+4´ s1; PC=memory[loc]+4+loc |
|
27 |
GETFL d1 |
|
d1=FLAGS |
(note 4) |
28 |
SETFL s1 |
|
FLAGS=s1 |
|
29 |
TSTFL s1 |
|
flagZ=((FLAGS&s1)==0); flagN=0 |
(note 4) |
2A |
FLON s1 |
|
FLAGS=FLAGS | s1 |
(note 4) |
2B |
FLOFF s1 |
|
FLAGS=FLAGS & Ř s1 |
|
2C |
SYS s1, s2 |
|
Call system function No. s2, (call gate) s1 is number of args already pushed |
|
2D |
RETI |
|
Return from interrupt |
(note 5) |
2E |
CLHR |
|
Clear hidden registers, all to zero. |
(note 6) |
2F |
MOVEN s1,
a2, a3 |
|
Copy s1 bytes from memory starting at
address a2 to memory
starting at address a3 |
(note 8) |
30 |
CVT12 s1,
d2 |
|
d2=
s1, stretched from 1-byte to 2-byte value |
(note 8) |
31 |
CVT14 s1,
d2 |
|
d2=
s1, stretched from 1-byte to 4-byte value |
(note 8) |
32 |
CVT24 s1,
d2 |
|
d2=
s1, stretched from 2-byte to 4-byte value |
(note 9) |
33 |
DVMD s1, s2,
d3, d4 |
|
d3=s2¸s1; d4=s2-s1´d3 |
|
34 |
MOD s1, sd2 |
|
sd2=|sd2|
mod s1 |
|
35 |
MOD s1, s2,
d3 |
|
d3=|s2|
mod s1 |
(note 7) |
36 |
CALLB s1,
a2 |
|
Same as #12: CALL, except s1 not multiplied
by 4 |
(note 6) |
37 |
PUSHN s1,
a2 |
|
Push s1 bytes from memory starting at
address a2 |
|
38 |
ICALL a1 |
|
Same as #12: CALL, but for interrupt functions a1 must be address of RETI-terminated function. |
(note 10) |
39 |
PSHRM s1 |
|
Operand is a bit-pattern specifying registers. Indicated registers are pushed onto stack. |
(note 10) |
3A |
POPRM s1 |
|
Operand is a bit-pattern specifying registers. Indicated registers are popped from stack. |
|
3B |
BREAK s1 |
|
Stop program and enter debugging mode. |
(note 11) |
3C |
MTSR s1, s2 |
|
Move To Special Reg: SpecialRegister[s2]=s1 |
(note 11) |
3D |
MFSR s1, d2 |
|
Move From Special Reg: d2= SpecialRegister[s1] |
(notes appear on next page)
Operand abbreviations:
si used as a source: read only.
di used as a destination: written only.
sdi used as a source and destination: read then written.
ai used to provide an address.
Notes on Instruction Operations
(note 1) 0x00 causes an error; this traps many accidental jumps to non-executable data.
(note 2) HALT stops the processor.
(note 3) JMPI is useful for the implementation
of switch/case statements, otherwise unimportant.
(note 4) Unless the SYS flag is already set, none of the system-mode protected flags are modified.
(note 5) Long running instructions use the hidden registers to save their state. CLHR is used to ensure that the hidden registers are all zero before such an instruction starts. It should not be necessary, but is provided for safety.
(note 6) MOVEN operation: if HR0=0, set HR0 to 1 and copy operands into HR1, HR2, HR3.
Otherwise, if HR1=0, increment PC and continue;
Otherwise copy one byte from [HR2] to [HR3]; HR1-=1; HR2+=1; HR3+=1; PC unchanged.
(note 6) PUSHN operation: if HR0=0, set HR0 to 1 and copy operands into HR1, HR2.
Otherwise, if HR1=0, increment PC and continue;
Otherwise push one byte from [HR2+HR1-1]; HR1-=1; PC unchanged.
(note 7) It was clearly a mistake to expect all arguments to functions to occupy four bytes. The CALLB instruction (#36) does not make this assumption, and will completely replace CALL (#12) in future versions. CALL is retained for compatibility with existing programs.
(note 8) No special instructions are required for reducing the size of an int; operand sizes do the job (e.g. MOVE R1, 1:[FP-16]). When increasing the size of an int, this method works for unsigned values, but signed values require special attention, hence the instructions CVTxx.
(note 9) DVMD computes the mathematically useful version of remainder, not the usual C % operator.
(note 10) PSHRM and POPRM interpret their operand as a bit-pattern indicating which registers should be pushed or popped. The bit pattern is an integer in which Ri is worth 2i. Registers are popped in reverse order. Example: To push registers R1, R2, R3, R4, R7, and R8 the bit pattern is 110011110 (the two zeros are for the unpushed R6, R5, and R0). This is the binary for of the number 400, so the instruction would be PSHRM #400. The assembler understands bit patterns, so the instruction could also be written PSHRM #{1,2,3,4,7,8}. Note that the # is still required.
(note 11) MTSR and MFSR are used to move values To and From the special processor registers. These instructions may only be executed in system mode. Which special processor register is indicated by a number, normally constant. The correspondence between registers and numbers is:
0: P0BR 2: P1BR 4: S0BR 6: S1BR
1: P0LR 3: P1LR 5: S0LR 7: S1LR
but the assembler understands the names as predefined constants (note that the round characters are zeros). For example, to read the contents of P1BR into general register 1, the instruction is MFSR #P1BR, R1; to set S1LR to 1024, the instruction is MTSR #1024, #S1LR.
CPU Registers
Sixteen 'general-purpose' registers, named R0 to R15.
Three special names: PCşR15, SPşR14, FPşR13.
R0 is used to hold the value returned from a function.
PC is the Program Counter; always contains address of next instruction to be executed.
SP is the Stack Pointer; stack grows downwards from high addresses with each push.
FP is the Frame Pointer; used to find local variables on the stack during a function execution.
There are also four 'hidden' registers, unnamed because they can not normally be accessed. They are used by the few potentially long-running instructions (e.g. MOVEN, PUSHN) to enable their state to be saved if an interrupt occurs during execution. Referred to as HR0, HR1, etc. in this documentation.
There are also a number of special registers that can only be accessed in system mode; they are mostly used to control virtual memory. Their names are P0BR, P0LR, P1BR, P1LR, S0BR, S0LR, S1BR, and S1LR. They are the Base and Length Registers for Process and System address spaces 0 and 1.
Flags
Flags are one-bit values that record state or condition information. They are usually accessed implicitly by a CPU operation (e.g. CMP and TEST change the Z and N flags), or as a group (e.g. by the GETLF or FLON instructions). The FLAGS register (not one of the 16 general purpose registers) contains all the flags grouped as a 32-bit value. The individual flags in the FLAGS register correspond to the hexadecimal values in the following table.
|
flag name |
dflt |
FLAGS value |
prt |
use |
|
Z |
0 |
00000001 |
-- |
Last CMP was equal or last TEST was zero |
|
N |
0 |
00000002 |
-- |
Last CMP was less or last TEST was negative |
|
SYS |
1 |
00010000 |
PR |
Running in system mode (protected features accessible) |
|
PAG |
0 |
00020000 |
PR |
Virtual memory / Paging is turned on. (i.e. if zero all addresses are physical, no translations) |
|
INT |
0 |
00040000 |
PR |
An Interrupt is currently being processed. |
'dflt' = Setting of the flag on system start-up.
'prt' = Is this flag protected? Protected flags may only be modified when the SYS flag is already on. Once SYS is off, no instruction turns it back on.
Operand
Codes
Notation: Rn represents any one of the 16 registers,
n represents a
number or a the value of a label
Notation |
When used as source |
When used as destination |
When used as Address |
Rn |
value = Reg[n] |
Reg[n] = value |
not allowed |
#n |
value = n |
not allowed |
not allowed |
[Rn] 1:[Rn] 2:[Rn] |
value = memory(Reg[n]) |
memory(Reg[n]) = value |
address = Reg[n] |
[Rn+k] 1:[Rn+k] 2:[Rn+k] |
value = memory(Reg[n]+k) |
memory(Reg[n]+k) = value |
address = Reg[n]+k |
[Rn-k] 1:[Rn-k] 2:[Rn-k] |
value = memory(Reg[n]-k) |
memory(Reg[n]-k) = value |
address = Reg[n]-k |
n 1:n 2:n |
value = memory(n) |
memory(n) = value |
n |
Prefixes 1: and 2: set the number of bytes read, written, or modified for memory accesses. For example, "MOV #0, [R2]" sets the four bytes of memory starting from the address stored in R2, to zero. "MOV #0, 1:[R2]" only sets the single byte of memory at the address stored in R2 to zero. The default prefix is 4:.
Operand Encoding (hexadecimal)
Large numbers are always stored least significant byte
first.
operand range encoding by assembler examples
register direct
Rn 0≤n≤15 50+n R3 ® 53
constant
#n -32≤n≤31 n,
six bits only #3 ® 03 #-1 ® 3F
#n (no limits) 40;
n, four bytes always #517 ® 40 05 02 00 00
register indriect
[Rn] 0≤n≤15 60+n [R3] ® 63
2:[Rn] 0≤n≤15 A0+n 2:[R3]
® A3
1:[Rn] 0≤n≤15 80+n 1:[R3]
® 83
[Rn±k] 0≤n≤15 70+n; k, four bytes [R3+37]
® 73 25 00 00 00
2:[Rn±k] 0≤n≤15 B0+n; k, four bytes 2:[FP+517]
® BD 05 02 00 00
1:[Rn±k] 0≤n≤15 90+n; k, four bytes 1:[SP-4]
® 9E FC FF FF FF
absolute address (rarely used)
n (no
limits) F0; n, four bytes 37 ® F0 25 00 00 00
2:n (no limits) F2;
n, four bytes 2:517 ® F2 05 02 00 00
1:n (no limits) F1;
n, four bytes 1:1024 ® F1 00 04 00 00
PC relative address
.±a (no limits) FF;
n, four bytes .+37 ® FF 25 00 00 00
2:.±a (no limits) FD;
n, four bytes 2:.+517 ® FD 05 02 00 00
1:.±a (no limits) FC;
n, four bytes 1:.-4
® FC FC FF FF
FF
System
Functions (accessed through SYS
instruction)
Arguments are pushed onto stack before issuing SYS instruction. First operand is number of arguments pushed, second is the system function's index number. Only system functions stored in the system function vector may be called with SYS; The SYS instruction may change the processor's mode from USER to SYSTEM (setting the flag SYS), but the change is reversed before control returns to the calling program. Arguments are removed from the stack before return.
1 |
SYS$SETIV |
Set Interrupt Vector: arg 1: Interrupt number (n) arg 2: Address of function (f) The action on receiving interrupt number n becomes to call function f. if f is zero, interrupt n is ignored. if f is –1, the default handler is restored. |
|
|
|
|
|
2 |
SYS$SETTI |
Set Timer Interrupt Time arg 1: Delay, in approximate milli-seconds (t) After approximately t milliseconds, the interrupt IV$TIMER will be signalled Not a repeating timer; must re-issue SYS$SETTI inside interrupt handler for repetition. |
|
|
|
|
|
3 |
SYS$SMLSLP |
Small Sleep, no arguments Program pauses for one tenth of a second without using CPU cycles Sleep is broken when an interrupt arrives. Use this in a loop to await interrupts when no work is to be done. |
|
|
|
|
|
4 |
SYS$PUTCH |
Print one character on controlling terminal screen. arg 1: ASCII code of character to be printed. |
|
|
|
|
|
5 |
SYS$DSCMNT |
Mount a disc. arg 1: String, disc drive name. Opens file called name.dsc to use as emulated disc drive. Returns disc number in R0, or -1 for failure. |
|
|
|
|
|
6 |
SYS$DSCDMT |
Dismount a disc. arg 1: Integer (0-9) indicating disc number. |
|
|
|
|
|
7 |
SYS$DSCNBL |
Find size of disc, Number of BLocks. arg 1: Integer (0-9) indicating disc number. Returns in R0 the number of blocks in the disc, 0 for failure. |
|
|
|
|
|
8 |
SYS$DSCRBL |
Read one BLock from disc. arg 1: Address of 512-byte memory area containing data to be written. arg 2: Integer (≥0) indicating block number. arg 3: Integer (0-9) indicating disc number. Returns in R0 1 for success, 0 for failure. |
|
|
|
|
|
9 |
SYS$DSCWBL |
Write one BLock to disc. arg 1: Address of 512-byte memory area to receive data. arg 2: Integer (≥0) indicating block number. arg 3: Integer (0-9) indicating disc number. Returns in R0 1 for success, 0 for failure. |
|
|
|
|
|
10 |
SYS$GETCH |
Read one character from controlling terminal. Does not wait, returns -1 if no character typed yet. Only works in simple programs that do not provide an interrupt vector for IV$CHARIN. Once that interrupt is handled, this function always returns -1. Returns in R0 ASCII code of character typed. |
|
|
|
|
|
11 |
SYS$ECHO |
Turn controlling terminal’s echo on or off. arg 1: Integer, 0 for off, 1 for on. Only works in simple programs that do not provide an interrupt vector for IV$CHARIN. Once that interrupt is handled, input is no longer automatically processed and along with echoing becomes the program’s responsibility. |
|
Example: To arrange for the function DING to be called automatically as an interrupt one second from now:
PUSH #IV$TIMER
PUSHA DING
SYS #2, #SYS$SETIV
PUSH #1000
SYS #1, #SYS$SETTI
LOOP: SYS #0,
#SYS$SMLSLP
JMP LOOP
(note the large number of # signs: these are symbolic constants, not addresses)
Input from the User.
A program that does not attempt to handle the IV$CHARIN interrupt may make use of basic input functions provided by the system. The stdio library function getchar() works as normal: the next character typed by the user is returned (as its ASCII code). Line buffering is used, so no characters are available until the user presses ENTER, and BACKSPACE may be used to correct mistakes until ENTER is pressed. When single-stepping through a program, a prompt is given for a whole line of input.
The system function sys$getch() also reads a single character typed by the user, but there is no buffering, the character (its ASCII code) is returned as soon as it is typed. There is also no blocking: if no character has been typed, -1 is returned immediately.
Characters typed by the user when a program is running, if IV$CHARIN interrupts are not being caught, are stored in a buffer with sufficient capacity for at least 100 characters of type-ahead. Both getchar() and sys$getch() remove characters from this buffer if it is not empty.
Non-ASCII characters may be entered by typing ESC (the “escape” key) followed by their two-digit hexadecimal code. For example ESC 4 1 is equivalent to the single keypress A (0x41 = 65 = ‘A’).
The echoing of characters typed by the user may be controlled with the sys$echo() system call.
Handling
Interrupts
The function that handles an interrupt is written as any normal function, except that it ends with a RETI instruction instead of a plain RET. The contents of all registers and flags are automatically saved before an interrupt function starts, and automatically restored when a RETI instruction is executed. For system security, interrupt functions do not use the user stack. There is a separate stack, called the System Stack, and stack frames for interrupts are created on the system stack. For debugging purposes, interrupt functions may be called directly, but the CALLI instruction must be used, not the normal CALL.
Interrupt
Numbers
1 |
IV$CHARIN |
A Character from the controlling terminal is available for input. The character's ASCII code is in R0. |
2 |
IV$TIMER |
The timer interrupt (set by SYS SYS$SETTI) has expired |
The
Calling Convention (does not apply to
interrupt functions)
If the function fff is declared with 3 parameters and 5 local variables, thus:
int fff(int p1, int p2, int p3)
{ int
v1, v2, v3, v4, v5;
...
return x; }
Then the assembly code equivalent is:
fff:
SUB #20, SP ; increase stack frame size by
20 bytes,
... ;
which is the space required for 5 ints.
... ;
inside fff, local variable vi
is addressed as [FP-8-4´i]
... ;
and parameter pi is addressed as [FP+4´i]
... ;
MOVE R0, ...value of x...
RET
If elsewhere the function is called like this:
...
a = fff(73, x+y, z);
...
Then the assembly code for the call is:
...
PUSH z
MOVE R1, x
ADD
y, R1 ; The values of the parameters are pushed
PUSH R1 ;
in reverse order, so that they will appear
PUSH #73 ;
in the right positions above the new frame
CALL #3, fff ; operand #3 is number of
parameters
... ;
CALL instruction saves num params in new frame
... ; so that RET can remove them automatically
A
Typical Stack Frame
|
(previous frame) |
|
parameter 2 |
|
parameter 1 |
FP ® |
number of parameters |
|
old FP |
|
old PC = return address |
|
local variable 1 |
|
local variable 2 |
SP ® |
local variable 3 |
|
(unused) |
Assembly
Directives
.SEG name,
prot
Define a new segment called name, with the indicated memory protections. Prot may be any combination of the letters R, W, and X. Typical uses:
.SEG data, RW
.SEG code, X
.SEG name
All subsequent code (until another .SEG directive) is to be added to the end of the named segment, which should already have been defined.
.ENTRY name
The label name, which must be defined somewhere in the current program file, is recorded as an official starting point for program execution. If there is only one entry point, or if there is an entry point named main, that is the default starting point when the program is executed.
.EXPORT name
The label name, which must be defined somewhere in the current program file, is to be made available in other separately compiled files.
.IMPORT name
The label name, which must be defined somewhere in another program file which is to be linked with this one after compilation, is to be imported. The symbol is not defined in this file, but may still be referred to as a memory address or jump destination.
.INT value
Value is stored in the next four bytes as an integer.
.BYTE value
Value is stored in the next byte as an integer.
.BLOCK number
Number bytes of memory are left uninitialised and unused.
.DEST label
The number of bytes between the current location and the given label is stored as a four byte integer. This produces a PC-relative address.