REFERENCE (as of 8th September 2003)

 

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                 0n15          50+n                                    R3 ® 53

constant

#n                 -32n31     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]             0n15          60+n                                    [R3] ® 63

2:[Rn]         0n15          A0+n                                    2:[R3] ® A3

1:[Rn]         0n15          80+n                                    1:[R3] ® 83

[Rn±k]         0n15          70+n; k, four bytes               [R3+37] ® 73 25 00 00 00

2:[Rn±k]     0n15          B0+n; k, four bytes               2:[FP+517] ® BD 05 02 00 00

1:[Rn±k]     0n15          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.