The NURVE (New University of Regina Virtual Education) machine and assembler is a software package that provides a machine independent environment for learning assembly and machine language programming. The NURVE virtual machine architecture is similar to 'real' micro-computer organizations.
a
The Central Processing Unit (CPU) decodes and executes instructions fetched from memory. These instructions direct the operation of the computer. Instructions other than input/output (I/O) instructions are executed by the CPU. I/O instructions are handled by the appropriate I/O sub-unit. The NURVE CPU has 32 bit architecture. The memory is byte addressable. Byte (8 bits), half-word (16 bits) and word (32 bits) accesses to memory are possible. A suffix to the instruction mnemonic is used to select the correct addressing.
Registers are very fast storage locations in the CPU. There are sixteen registers of 32 bit width, labeled R0, R1, ... R9, RA, RB, RC, RD, RE, and RF; the letters A, B, C, D, E, and F are the hexadecimal (hex) representations for the values: 10, 11, 12, 13, 14, and 15.
The general purpose registers, R0 to RA, can be used to hold any data.
The Base Register (BR), register RB, containing the address of the top of the user memory area, is set by the operating system and cannot be changed by the user. The BR is added to any virtual user memory address to form the actual or physical address.
The Program Counter (PC), register RC, points at the next instruction to be executed. Thus, the PC contains the virtual address of the word containing the next instruction to be executed.
The Stack Pointer (SP), register RD, points at the bottom of the user data stack area. The operating system initializes the SP to the hexadecimal value 7FFF.
The Instruction Register (IR), register RF, contains a copy of the current instruction being executed by the CPU.
The Processor Status (PS) Register, register RE, is used to store a program's condition codes. Each condition code is usually set after each instruction executes and produces a result. The condition codes, the low order eight bits of the PS register, are named I, A, S, T, N, Z, V, and C. The PS bits are usually used to determine selective branching.
Condition Code Mask Set Cleared Interrupt I 80 1 = Interrupts 0 = Interrupts enabled. disabled. Addressing A 40 1 = Relative 0 = Absolute addressing. addressing. Supervisor S 20 1 = Supervisor mode. 0 = User mode. Trace T 10 1 = Trace enabled. 0 = Trace disabled. Negative N 08 1 = Result is 0 = Non negative. negative. Zero Z 04 1 = Result is zero. 0 = Non zero. Overflow V 02 1 = Overflow resulted. 0 = No overflow. Carry C 01 1 = Carry from MSB. 0 = No carry.Processor Status (PS) Register Bits
We will be using only the N, Z, V and C flags. The flags are set when data movement or arithmetic operations are performed on the datum in any register or memory. Look at the values for the N, Z, V and C flags in the 'Mask' in the preceding table. Notice that if you converted the mask to its binary equivalent, only one bit is set. e.g. the hex value 08 corresponds to the binary value 1000 - only one bit set. This bit corresponds to the flag that is currently set.
N Z V C
1 0 0 0 <- hex value 08 means the N bit is set (negative)
0 1 0 0 <- hex value 04 - the Z flag (zero)
0 0 1 0 <- hex value 02 - the V flag (overflow)
0 0 0 1 <- hex value 01 - the C flag (carry)
The following programs (Program 7.13.1.1.1 to 7.13.1.1.4) set the individual flags. Compile, link and execute each of the following programs. When executing, use the option to produce a core dump at the end of the execution. In the core dump, notice the value of the PS register.
From the hexadecimal value of the PS register you will be able to see which flag(s) is/are set. All the flags that we are interested in are in the least significant nibble (4 bits) of the PS register, so ignore all but the least significant hex digit of the PS register value.
The comments in each of the programs will help you understand the working of the program.
All the programs in this manual can be obtained via anonymous ftp from ftp.cs.uregina.ca. Refer to Appendix A in this manual to see an example of how to obtain a file.
Sample program to set the N bit in the Processor Status register:
;=========================================================================
; Filename: 7.13.1.2.1.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: to set the processor status word negative flag.
;
;=========================================================================
.CODE ; Code Segment.
code:
clr r0 ; Initialize.
dec r0, r0 ; Generate a negative result.
halt
.END
As stated previously: after you compile and link these programs use the option to produce a core dump at the end of the execution. In the core dump, notice the value of the PS register. From the hexadecimal value of the PS register you will be able to see which flag(s) is/are set.
Example:
hercules[2]% nurve 7.13.2.1.asm [Return]
Nurve: completed with 0 errors and 0 warnings
hercules[3]% nlink 7.13.2.1.obj [Return]
hercules[6]% nload -c 7.13.2.1.lmn [Return]
Nload: End of Execution.
Execution terminated. PC = 0000000C. 2 instructions executed.
NURVE CORE DUMP (Hex)
----- ---- ---- -----
Code Segment PCB Heap Stack Stack Top
00000000 00000400 00000480 00000880 00000C80
R0 R1 R2 R3 R4 R5 R6 R7
FFFFFFFF 00000000 00000000 00000000 00000000 00000000 00000000 00000000
R8 R9 RA BR PC SP PS IR
00000000 00000000 00000000 41A0E400 0000000C 00000C80 00000108 00000000
ADDR OFFSET 0 04 08 0C 10 14 18 1C
----| -------- -------- -------- -------- -------- -------- -------- --------
0000| 1D000000 3A000000 00000000 00000000 00000000 00000000 00000000 00000000
...
hercules[7]%
From this dump, we see that the PS (Processor Status) register is set to the value 00000108. All the flags that we are interested in are in the least significant nibble of the PS register, so ignore all but the least significant hex digit of the PS register value. In this case it is the value "8" that we are interested in. Remember that hex value 08 means the N bit is set..
Following, are other programs that will allow you to examine other flags in the processor status word.
;=========================================================================
; Filename: 7.13.1.2.2.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: to set the processor status word zero flag.
;
;=========================================================================
.CODE ; Code Segment.
code:
clr r0 ; Generate a zero result.
halt
.END
;=========================================================================; Filename: 7.13.1.2.3.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: to set the processor status word overflow flag.
;
;=========================================================================
.CODE ; Code Segment.
code:
mov #07fffffff, r0
inc r0, r0 ; Generate an overflow.
; This operation not only generates an overflow, but
; also sets the negative flag so we turn it off. This will
; let us see only the overflow flag in the core dump.
clp.n ; There.
halt
.END
;=========================================================================; Filename: 7.13.1.2.4.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: to set the processor status word carry flag.
;
;=========================================================================
.CODE ; Code Segment.
code:
clr r0
dec r0, r0
rot #01, r0, r0 ; Generate a carry.
; This operation not only sets the carry, but
; also sets the negative flag so we turn it off. This will
; let us see only the carry flag in the core dump.
clp.n ; There.
halt
.END
These simple examples show how to set a specific condition code bit in the Processor Status register. More generally, an instruction is used to examine a particular value, causing one of the condition code bits to be set. Then a branch instruction directs program flow depending on the value in the condition codes.
You will see an example of this when you get to the program in section 7.13.3.2 of this manual. For now, be aware that some instructions cause condition code bits to be set and that there are branch instructions that depend on values in the PS.
NURVE uses two's complement arithmetic. The sign bit, used to indicate the sign of a value being stored, is the most significant bit (MSB) and is also called the high order bit. This bit is set to one when the value is negative and cleared to zero when the value is positive.
In two's complement arithmetic, the negative representation of a positive number, P, is obtained by adding 1 to the one's complement of P. The one's complement of P is obtained by flipping each bit in P (change zero's to one's and one's to zero's). Thus, the two's complement of the value one, is a word (or byte or halfword) where all the bits are set to one.
The maximum integer (the largest positive integer value that can be stored), in hex notation, is: 0x7F for a byte, 0x7FFF for a half-word, and 0x7FFFFFFF for a word.
When a byte is copied into a halfword (or a halfword is copied into a word), its sign bit is copied into the whole high order byte (or halfword). This is called sign extension. In NURVE, there will always be sign extension on byte/halfword operations when the destination is a register. There is no sign extension if the destination is the stack.
The NURVE machine memory is composed of a sequence of words divided into blocks. Some blocks, such as the I/O buffers, are reserved by the operating system and others are allocated to individual users. Each word of memory is accessed by a unique address and can contain either an instruction or datum. The differentiation between data and instructions is made at execution time by how the word is used.
Each 32 bit NURVE word can be divided into two halfwords (16 bits each), or four bytes (8 bits each), or eight nibbles (4 bits each). An instruction takes up a full word, but each byte is addressable.
A 32 bit NURVE word
Each user is allocated a 32K block of NURVE's memory space that is accessed by a virtual address, numbered sequentially from hex 0 to 7FFF. A virtual address is converted into a physical address by adding the Base Register (see "Registers") to the virtual address.
User programs are loaded at virtual address 0 followed by a process control block (PCB). The PCB is used by the operating system to save CPU registers during a context switch. A context switch is when a multi-tasking operating system takes back control of the CPU from an executing user process. A multi-tasking operating system shares a single CPU between several different processes executing concurrently.
During execution, the code segment and PCB areas are treated as read only memory by the operating system. The heap and the stack are two data areas at the bottom of the user memory area and expand towards each other.
A user area memory map (extracted from the NURVE Architecture diagram is shown next.
Memory Map
The fetch/execute cycle is the process within the CPU control unit that executes a program residing in memory. When a program is loaded into the user memory area, the PC is set to 0. The CPU repeatedly loads the IR with the instruction pointed to by the PC and increments the PC (by 4) before the instruction in the IR is decoded and executed.
ALGORITHM: FETCH/EXECUTE CYCLE
SET PC to 0
REPEAT
SET IR to contents of word pointed to by PC
ADD 4 to PC
EXIT LOOP if IR is 0 (HALT instruction)
DECODE & EXECUTE instruction in IR
UNTIL IR is 0
END FETCH/EXECUTE CYCLE
The input/output units are the communication media between the NURVE machine and external devices, such as disk drives, keyboards or printers. NURVE does not provide any specific I/O instructions. I/O is performed through calls provided by the operating system (system calls) . Each I/O unit is connected to NURVE via the communication bus.
The next section of the manual describes the general format of machine language instructions.
Opcodes and the specific format of operands are discussed in detail in the following sections.
The NURVE machine only executes machine language instructions. Each NURVE operation is given a specific hex numeric code called an operation code or opcode. Opcodes perform operations on data called operands. Operands are classified as either source (src) operands that identify input data to an operation, or destination (dest) operands that determines where results are stored. A word containing an instruction is partitioned in one of the following ways:
Opcode
No operand instruction word
Opcode Dest
Single Operand instruction word
Opcode SrcA Dest
Double operand instruction word
Opcode SrcA SrcB Dest
Triple operand instruction word
Opcode Address
Jump instruction word
A machine language statement consists of three fields:
ADDRESS CONTENTS ;COMMENT
The machine language equivalent program of the Program 7.13.1.1.1 is shown below.
0000 1D000000 ; CLR r0 ; Initialize.
0004 3A000000 ; DEC r0, r0 ; Generate a negative result.
0008 00000000 ; HALT
Machine language programs require the address as well as the contents of each memory location to be specified for every instruction and datum. An optional comment field can be included after the address and contents fields.
Each statement must be on a single line. Continuation over more than one line is not allowed. The maximum length of a line is 80 characters. The common format of programs is:
columns 1-4 memory addresses
columns 8-13 memory contents
columns 15-80 comments
Looking at the first line in the preceding example:
address memory contents <- comments ->
0000 1D000000 ; CLR r0 ; Initialize.
The four hex digit address field specifies the location in memory where an instruction or datum is placed. In the following line from the preceding program we can see that the address field is 0000 which means that in this line is specified the contents of the memory word beginning at 0000.
0000 1D000000 ; CLR r0 ; Initialize.
The eight hex digit contents field contains an instructions or datum. Hex numbers are in the range from 00000000 to FFFFFFFF. An instruction consists of a valid opcode and its required operands. (Refer to the "Instruction Set" section of this document). In the line referred to earlier, the second field, 1D000000 represents the contents of the word beginning at address 0000. 1D000000 is the machine language equivalent or the opcode for the instruction CLR r0.
0000 1D000000 ; CLR r0 ; Initialize.
Comments are optional and do not affect the execution of a program. They make programs easier to read and simplify the debugging process. They also increase the possibility of getting a good mark on your assignment! It should be noted that some class instructors and lab instructors have been known to refuse to look at programs without adequate documentation. (i.e. comments).
The comments should not simply translate the machine code into words but attempt to explain the underlying logic. For example:
0020 3D000102 ;Move R1 to R2
is not adequate documentation and will not be considered as a comment. It aids little in the understanding of the program. The preceding comment could be rewritten as:
0020 3D000102 ;Save SUM (R1) into GROSS PAY (R2)
The second comment greatly enhances the readability of the code and the level of understanding obtained by the people who read your programs. (i.e. markers, consultants, lab instructors, yourself, etc.).
A comment can also be placed on a line by itself by simply preceding it with a semicolon ';'.
Machine language programming is tedious as programs are difficult to read and require the memorization of numeric opcodes. However, the major difficulty with machine language programming is its inflexibility. When an error is found in a machine language program, new instructions need to be inserted in the program to correct it. To insert new instructions between the extant instructions, the addresses following the inserted instruction must be modified, because each instruction in machine language is associated with a particular memory location. This can lead to serious loss in productivity when frequent changes requiring address changes are to be made not only because it is tedious to do but is highly error-prone by itself.
Assembly Language overcomes these problems by using mnemonic opcodes in place of numeric opcodes to aid readability, and symbolic labels in place of machine addresses to aid modifiability.
An assembler translates each assembly language instruction into its corresponding machine language by translating mnemonic opcodes into numeric opcodes, and binding symbolic labels to actual memory locations.
An assembly language program does not require any more or any fewer actual instructions than its machine language translation. There is a one to one correspondence between each assembly and machine language instruction.
A NURVE assembly language statement consists of three fields:
LABEL: INSTRUCTION ; COMMENT
Each statement must be on a single line.
Each of the label, instruction and comment fields is optional.
Continuation across lines is not allowed.
The maximum line length is 80 characters.
Program 7.13.3.1.asm, shown as follows, illustrates a simple assembly language program.
;=========================================================================; Filename: 7.13.3.1.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Add 10 numbers stored in an array.
;
;=========================================================================
; Pseudo code.
; Let pointer (r3) point to the beginning of the array.
; Let counter (r4) = 10
; Let sum (r5) = 0
; While (counter <> 0)
; Let sum = sum + number pointed to by r3
; Increment the pointer r3
; Let counter = counter - 1
; End
;___________________________________________________________________
.CODE
start:
mov.w #array,r3 ;Register r3 is the pointer
; to the array elements.
mov.w #0a,r4 ;Register r4 is a down counter
; starting at 10 (decimal).
clr.w r5 ;Register r5 will accumulate the
; sum of 10 numbers.
loop: ; Repeat
add.w @r3+, r5, r5; Add the number pointed to by r3
; to running total in r5
dec.w r4, r4 ; Decrement the counter.
tst.w r4 ;
jne @#loop ; until counter is zero
halt ;
.DATA
array: ; Data items for the 10 element array.
.init.w 1
.init.w 3
.init.w 5
.init.w 7
.init.w 9
.init.w b
.init.w d
.init.w f
.init.w 17
.init.w 19
.END
Assembly language uses symbolic labels instead of actual memory addresses. The first character must be alphabetic, the other characters may be alphanumeric. For example, X, M2, R2D2, AABCD, K123, and HELPME, are all valid labels. Program readability is enhanced by using meaningful label names. The label field is optional, but if present, it must appear before the instruction field and must be immediately followed by a colon. For example, notice that the preceding program begins with the label start.
start:
Only statements referenced elsewhere by the program require symbolic labels. In the above example program start is not referenced anywhere but serves the useful purpose of labeling the entry point of the program.
Locations used to hold data (variables and constants) are also often given labels. The assembler assigns each label the value of the address of the memory location where the label is found. Notice that the array is labeled in the data segment at the end of the preceding program.
The optional instruction field may be either
1) a mnemonic instruction described in the Instructions Section or
2) an assembler directive described in the Assembler Directives Section.
The instruction field contains an opcode and any operands when applicable.
The following are both two-operand instructions.
mov.w #array,r3
mov.w #0a,r4
Assembler directives are intended to convey information to the assembler and do not produce any code. In the preceding example, .CODE and .DATA are assembler directives. The former directs the assembler to put the subsequent instructions in the code area of the memory. This area of the memory is also referred to as the code segment. Everything following a .CODE directive and before any .DATA directive will be loaded into the code segment at execution time. Everything following a .DATA directive and before a .CODE directive is placed into the data segment. A NURVE source program can have any number of .CODE and .DATA directives.
All numbers in a NURVE source are assumed to be in hexadecimal representation. They must always begin with a zero.
Comments are optional and do not affect assembly processing or program execution. A comment must begin with a semicolon. It may be preceded by a label and an instruction. (See "Comment Field" in Machine Language). Notice the use of indentation in the comments in the preceding program. Since indenting the assembly language instructions is usually not a good idea, it is helpful to indent the comments to emphasize the scope of the control structures. Also notice that obvious comments are avoided in the preceding program.
There is a danger in documenting an assembly language program, of over-documentation. This is largely because assembly language is so low-level; there are no formal constructs for if/then/else statements or looping. In a higher level language, vertical alignment and indentation are used to make the code readable. For example:
IF condition THEN
do this
and this
ELSE
do this
and this
This type of self-documentation does not translate to assembly language because the higher level constructs do not exist. For example, a single 'if' statement is composed of a compare statement, a conditional branch or jump statement, and then the line(s) of code that are to be performed in the case that the 'if' condition is met. The danger in placing comments beside each assembler statement is that the program becomes so cluttered, that the function of a series of statements becomes lost to the reader.
The solution to this problem is to use vertical alignment and indentation in your comments. Instead of attempting to comment each line of code, place comments beside blocks of code so that your comments look like a high level language or pseudo-code. Pick a standard you like and try to follow its structure throughout your comments. Be consistent!
For example:cmp.b R0, #41 ; if (input_char = 'a')
jne other
jsub aroute ; process for 'a' input
jump next
other: ; else
jsub alt ; process for other input
next:
Use blank lines to improve readability. Also, white space (blanks and tabs) may be inserted anywhere in a statement to improve readability and neatness of a program provided it is not embedded within symbols or numbers. Notice the way the preceding program is neatly laid out in columnar fashion. Easy readability is very important in developing assembly language programs.
Use meaningful names in your comments, e.g.: "input_char" is better than R0
Do include a reasonable number of extra comments in your code; program headers are mandatory, but you might also wish to add some explanatory comments before particularly complex sections of code.
Do not clutter up a program with too many comments; a comment for each line of code is often not always necessary in an assembly language program.
Program header - a program should begin with a block of comments which includes the following information:
;=========================================================A sample header file, header.asm, can be obtained from FTP - refer to Appendix A for an explanation of how to obtain a file from FTP and then include the file in your program.; Filename: rootsearch.asm
; Author: your name
; Student #: 123 456 789
;
; Purpose: include a brief explanation of the purpose of the program
;
;==========================================================
For lab assignments: Documentation and comments will not normally be required - this is only because of time constraints. Also, you do not need to include rigorous error checking in a lab assignment, unless this is specified in the assignment sheet.
The machine language output from an assembler is called an object module. A linker joins one or more object modules to produce an executable load module, also called an executable. System calls (I/O routines) from a system library are included in a user program by the linker (see getc and putc). A loader places a load module in a machine's memory and executes it.
Assembling, linking and running a program. ('file' is the name of a file.)
An assembly language program is converted into machine language (assembled) by the NURVE assembler. The command for assembling is:
nurve filename.asm
where filename is the name of an assembly language program. The extension asm is assumed. The assembler produces a file named filename.obj, which contains the machine language translation of the source code, along with the assembler mnemonics as comments, and any errors that were detected. For example to assemble a file named test.asm, and produce a file named test.obj:
nurve test [Return]
If no errors were detected during assembly, the object file filename.obj can be linked to produce the load module named filename.lmn, using the command:
nlink filename.obj
The NURVE loader places a load module into memory and executes it starting at location 0000. The command for executing a NURVE load module is:
nload [-ctv] [-h=heapsize] [-s=stacksize] [-i=maxinstr] [-o=outfile] filename[.extension]
where:
nload - Command keyword to start the executing a program.
filename - The name of the executable load module. The filename should have a file type of 'lmn'.
extension - Filetype of executable. If omitted, an extension lmn is assumed.
[] - Signifies that the characters within are OPTIONAL. The square brackets are NOT to be typed in.
- - Signifies that the characters following the '-' are flags. The flags change the type and amount of output generated while your program executes. They do NOT affect the output generated from within a user program.
c - Causes a core dump to be printed at the end of the output file when the program stops executing.
t - Traces the step by step execution of each machine language instruction.
v - Verbose mode. Provides more detailed information than a trace.
h=heapsize - Sets the size of the heap area. Heapsize is a hexadecimal value.
s=stacksize - Sets the size of the user stack area. Stacksize is a hexadecimal value.
i=maxinstr - Sets the maximum number of instructions to execute before the program halts. Maxinstr is a hexadecimal value.
o=outfile - Name of the output file. (Defaults to the standard output device.)
Examples of executing NURVE programs, in a file called 'test.lmn':
nload test [Return]
output goes to the standard output device (the user terminal).
nload -c test [Return]
output goes to the standard output device (the user terminal). A core dump follows the output.
nload -tc -o=test.lis test.lmn [Return]
output goes to a file called 'test.lis'. A trace is printed to the screen and to 'test.lis' and a core dump is printed at the bottom of the file.
Instructions are classified as one of: No Operand, Single Operand, Jump Instruction, Double Operand, or Triple Operand. This section lists each instruction showing the hexadecimal opcode, assembler mnemonic, and the instruction format. A more detailed description of each instruction is found in Appendix B.
Let us first look more closely at the complete program presented before in this manual. Hopefully, this will provide some familiarity with assembler instructions, before we examine all instructions in detail.
;=========================================================================
; Filename: 7.13.3.1.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Add 10 numbers stored in an array.
;
;=========================================================================
; Pseudo code.
; Let pointer (r3) point to the beginning of the array.
; Let counter (r4) = 10
; Let sum (r5) = 0
; While (counter <> 0)
; Let sum = sum + number pointed to by r3
; Increment the pointer r3
; Let counter = counter - 1
; End
;______________________________________________________________
.CODE
mov.w #array, r3 ; Register 3 is the pointer to the array elements.
mov.w #0a, r4 ; Register 4 is a down counter starting
; at 10 (decimal).
clr.w r5 ; Register 5 will accumulate the sum of 10 numbers.
loop: ; Repeat
add.w @r3+, r5, r5; Add the number pointed to by r3
; to running total in r5
dec.w r4, r4 ; Decrement the counter.
tst.w r4 ;
jne @#loop ; until counter is zero
halt ;
.DATA
array: ; Data items for the 10 element array.
.init.w 1
.init.w 3
.init.w 5
.init.w 7
.init.w 9
.init.w b
.init.w d
.init.w f
.init.w 17
.init.w 19
.END
Notice the .CODE and .DATA directives that separate the code and data portions of the program.
The first instruction - mov.w #array, r3 -puts the address of the array in register r3. The meanings of the non-alphanumeric characters (such as '@' and '#') will be explained later in the section on addressing modes.
The next instruction - mov.w #0a, r4 - initializes the down counter with 0A hex or 10 decimal.
Next, - clr.w r5 - the register r5,which will accumulate the sum of the ten numbers, is initialized to zero.
The label in the next line - loop: - will be associated with the address at which the `add' instruction in the next line will be stored.
The `add' instruction - add.w @r3+, r5, r5 - takes the value pointed to by the register r3, adds to the current contents of register r5 and stores back the result in register r5 - thus maintaining a running total in register r5.
The down counter in r4 is decremented next - dec.w r4, r4 - and the test instruction in the next line - tst.w r4 - sets the flags in the PS register according to the value in r4. Refer back to the description of Processor Status Register condition code bits if this is not clear.
The `jne' instruction in the next line - jne @#loop - will take the branch if the zero flag is not set in the PS register. When ultimately the value in r4 will reach zero, the `tst.w' instruction will cause the zero flag to be set which in turn will prevent the `jne' instruction from taking the branch and therefor the control falls through to the `halt' instruction.
That will halt the processor and no further changes will occur in the system. Thus the program execution comes to a termination at which time a core dump can be produced by the `nload -c' command, wherein one can inspect the final value in the register r5 which will contain the sum of the ten numbers.
______________________________________________________________
We will now look at some notation that is necessary for a concise description of the instruction set.
LSB Least Significant Bit. MSB Most Significant Bit. Rn Register number (R0-RF). 1 hex digit aaaa 16 bit address. 4 hex digits hhh Hex number. pp Processor Status Flag. [I A S T N Z V C] 2 hex digits t Type of operand (0-2). [W=0 B=1 H=2] 1 hex digit xx Mode + Register. Source Operand A. 2 hex digits yy Mode + Register. Source Operand B. 2 hex digits zz Mode + Register. Destination Operand. 2 hex digits nn 2 digit hex number <- becomes v logical OR v logical exclusive OR
Many opcodes have an extension which refers to the Processor Status (PS) Register bits (I, A, S, T, N, Z, V, C) or operation word size (W, B, H).
Processor Status Register Bit Specification:
PS register bits I,A,S,T,N,Z,V,C may be specified with the CLP, SEP, JC and JS instructions. Refer to the PS Register bits table for a description of all PS register bits. The relevant bit(s) are specified by the appropriate letter. Multiple bits can be specified in a single instruction. For example, CLP.NZV clears the N, Z and V bits in the PS register.
Word Size Specification:
Only one of the letters W,B, or H (W=word, B=byte, H=halfword) may be specified in a single, double or triple operand instruction. For example, MOV is the mnemonic to move all, or a portion of a word, depending on the letter specified. To move the hex byte value 28 to R5, use the instruction MOV.B #28, R5.
Addressing modes, contained in Nurve instructions, define the way registers are used to access operands. For example, a register may contain a value to be manipulated or it may contain the address of the value. You may want to increment or decrement a value that is being used as a counter. Or, you may want to increment the address of a value, to point at the next value in a series. Each of these functions can be specified by an addressing mode. There are 16 addressing modes available in Nurve - a table follows.
An addressing mode is paired with a CPU register to define an operand. When an operand is shown as xx (for source operand A), yy (for source operand B) or zz (for the destination operand) the first hex digit (the high order nibble) is an addressing mode (from 0 to F) and the second hex digit (the low order nibble) is a CPU register (R0 to RF). Thus, the low order nibble specifies one of the 16 CPU registers and the high order nibble defines one of 16 different addressing modes.
An instruction may require no operands, or it may require up to 3 operands; an addressing mode and a register must be specified for each. Think back to the explanation of a machine language instruction.
Opcode
No operand instruction word
Opcode Dest
Single Operand instruction word
Opcode SrcA Dest
Double operand instruction word
Opcode SrcA SrcB Dest
Triple operand instruction word
Part of the instruction contains the code for the actual instruction (the opcode) and the remainder of the word contains the operands. Now we know that each operand consists of an addressing mode digit and a register digit - 4 bits (a nibble) each. Looking at machine instructions with bit positions marked (and remembering that a nibble consists of 4 bits) we can further decompose the format of instructions to see how the addressing modes fit in.
Opcode Dest
Mode Reg
... 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Single Operand instruction word
Opcode SrcA Dest
Mode Reg Mode Reg
... 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Double Operand instruction word
Opcode SrcA SrcB Dest
Mode Reg Mode Reg Mode Reg
... 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Triple Operand instruction word
The following table defines each of the 16 addressing modes available in Nurve.
The simplest mode, Register Direct, uses the value in the register as the operand.
Other modes use a register to contain an address, or use the address in a register combined with the PC (program counter).
There are different modes available for incrementing and decrementing register values. You may wish to increment/decrement the register and then use it, or use the value first and then increment/decrement it.
Addressing Mode Binary hex Assembly Description of Addressing Mode Register Direct 0000 0 Rn Register n is operand. Register Indirect 0001 1 @Rn Register n is address of operand. Double Indirect 0010 2 @@Rn Rn is address of address of operand. Indexed 0011 3 [Rn] (Rn) + (@PC+) is address of operand. Indirect Pre 0100 4 @+Rn Increment register n. Access @Rn. Increment Indirect Pre 0101 5 @-Rn Decrement register n. Access @Rn. Decrement Indirect Post 0110 6 @Rn+ Access @Rn. Increment register n. Increment Indirect Post 0111 7 @Rn- Access @Rn. Decrement register n. Decrement Double Pre Increment 1000 8 @@+Rn Increment register n. Access @@Rn. Double Pre Decrement 1001 9 @@-Rn Decrement register n. Access @@Rn. Double Post Increment 1010 A @@Rn+ Access @@Rn. Increment register n. Double Post Decrement 1011 B @@Rn- Access @@Rn. Decrement register n. Indexed Pre 1100 C [+Rn] Increment register n. Access [Rn. Increment Indexed Pre Decrement 1101 D [-Rn] Decrement register n. Access *Rn. Indexed Post 1110 E [Rn+] Access *Rn. Increment register n. Increment Indexed Post 1111 F [Rn-] Access *Rn. Decrement register Decrement n.
PC addressing modes
Immediate #hhh @PC+ 6C Constant (number) mode follows the instruction. Absolute @#hhh @@PC+ AC Address of operand mode follows the instruction.
All of these addressing modes may appear somewhat overwhelming at first glance. However, each of them is useful depending on what you want to accomplish.
Some simple examples are shown here, followed by some complete programs.
Examples:
i) Both operands are mode 0 (the registers are the operands):
MOV R2, R3 ; machine code is: 3D000203
; 02 - mode 0, register 2
; 03 - mode 0, register 3
ii) First operand is mode 1, second operand is mode 0 (in mode 1, the register contains the address of the operand; in mode 0, the register is the operand)
MOV @R2, R3 ; machine code is: 3D001203
; 12 - mode 1, register 2
; 03 - mode 0, register 3
iii) MOV R2, @#SYMBOL ; machine code is: 3D0002AC
; 02 - mode 0, register 2
; AC - mode A, register C (PC)
iv) MOV #05, R6 ; machine code is: 3D006C06
; 6C - mode 6, register C
; 06 - mode 0, register 6
v) MOV #SYMBOL, R6 ; machine code is: 3D006C06
; 6C - mode 6, register C
; 06 - mode 0, register 6
NOTE: Assume label SYMBOL is located at address 200
The following program 7.13.5a illustrates the use of double indirect addressing mode. The program prints three strings. The pointers to the three strings are stored in a table. Notice the reservation of four words for this table in the data segment. The first three words will store the actual pointers to the three strings and the last word will be set to zero. That is called a NULL pointer. As we step through the table, we will watch for the NULL pointer and when it is encountered, we will terminate the processing there. This technique allows one to write programs that can scale with data size. This very same program can be used to print any number of strings by simply adding the strings in the data area and the code to set-up the pointer table at the beginning of the table. Both of these are trivial changes to the program. The core program does not need to be changed one bit.
Examine the program carefully and then read the further explanation of instructions which follows the program.
The instruction :;=========================================================================
; Filename: 7.13.5a.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Print three strings using double indirect addressing mode.
;
;=========================================================================
; Test Double Indirect Addressing Mode.
; This program is not easy to understand. Simply reading
; it will not help. Use paper and pencil and form a diagram
; of table of pointers and strings they point to. Draw arrows
; to follow the pointers.
;_________________________________________________________________
.CODE ; Code Segment.
code:
; Main program.
; Set up the table of pointers to strings.
mov #table, r0
mov #str0, @r0+
mov #str1, @r0+
mov #str2, @r0+
clr @r0 ; Last pointer is NULL pointer.
mov #table, r0
loop0:
mov.b @@r0, r1
mov r1, r0 ; Pass character to putc.
jsub @#putc
inc @r0, @r0 ; Increment pointer.
tst.b @@r0 ; Is the character there NULL ?
jne @#loop0
mov #crlf, @-sp ; Print a CR/LF.
jsub @#print
tst @sp+ ; Clear stack.
tst @r0+ ;Equivalent to : add r0, #04, r0
tst @r0 ; Is it a NULL pointer ?
jne @#loop0 ; If not, print the string pointed to.
halt
;--------------------- Subroutine Area follows: ----------
; Subroutine Name: print
; Purpose: : Prints a string.
; Arguments : The pointer to the string is received
; on the stack.
; Registers
; used : r0, r1, sp
; modified : none
; Side effects : none
; Remarks : SP is left for the calling program to clear.
; Documentation :
; Register r1 is used to scan through the string, byte at a time.
; Register r0 is used to convert the character byte to a word that
; can be put on the stack before calling putc.
.CODE ; Code Segment.
print:
push r0
push r1
mov.w #0c[sp], r1 ; Get the pointer to the string.
begin0:
jeq @#end0 ; If end of string, exit loop
mov.b @r1+, r0 ; Convert character byte to word.
jsub @#putc
jump @#begin0
end0:
pop r1
pop r0
rsub
;_________________________________________________________________
.DATA ; Data Segment.
data:
.align.w
table: .block.w 4 ; Table of pointers.
str0: .string "A string."
str1: .string "Another string."
str2: .string "Yet another string."
crlf: .init.b 0a
.init.b 0d
.init.b 00
.END ; End of source file.
---------------------------------------------------------------------------------------------------------------------------------------------------
mov #table, r0
loads the address of the beginning of the table in register r0. The instruction
mov #str0, @r0+
stores the address of the first string in the first position of the table. The first position of the table is pointed to by the register r0 as a result of the previous instruction that initialized r0. The @r0+ is to be decoded as follows. The @ causes the contents of the register r0 to be taken as the address of the operand. The `+' suffix causes the address in r0 to be incremented by four. The amount of increment depends upon whether a suffix has been used after the instruction mnemonic. If no suffix is used the increment is by four which is the length of the NURVE word in bytes. A `.b' suffix will cause an increment of 1 and a `.h' suffix will cause an increment of 2. The following two instructions :
mov #str1, @r0+
mov #str2, @r0+
store the addresses of the next two strings in the next two positions of the table. The instruction:
clr @r0
causes the last pointer to be zero. By now, register r0 points at the last element of the table and so in the next instruction it is reset to point to the beginning of the table. Now we want to print the first string. The first character of the first string is obtained by taking the address in r0 at which is stored the actual address of the first character of the first string. Since there are two pointers in this path, we need the double indirect addressing mode as in :
mov.b @@r0, r1
We take that character and put it in the word register r1 and also into r0 for printing. A call to the system function `putc' is made which outputs the character to the standard output or the terminal.
A jsub call to print is made to print a CRLF to improve the readability of the output. The following `tst' instruction is actually not used to set the flags in the PS register but is an efficient way of incrementing the stack pointer. This technique is widely used in assembly language programs and you will see its use in many places in the programs in this manual.
The following program shows the use of the register direct, register indirect, double indirect, indirect postincrement, indirect predecrement, indirect postdecrement addressing modes.
;=========================================================================
; Filename: 7.13.5b.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Print three strings using double indirect addressing mode.
; First the program prints the strings from first to last and
; then from last to first and in the process uses
; most of the addressing modes.
;
;=========================================================================
; Test all the major addressing modes.
; This program is not easy to understand. Simply reading
; it will not help. Use paper and pencil and form a diagram
; of table of pointers and strings they point to. Draw arrows
; to follow the pointers.
; This program uses all the major addressing modes although
; not all the variations of each of those addressing modes.
; However, the reader should be able to get a good understanding
; of all the addressing modes and be able to perceive the need
; for any of those in a real applications and use them.
; The following addressing modes are used :
; Register Direct.
; Register Indirect.
; Double Indirect.
; Indirect Postincrement.
; Indirect Predecrement.
; Indirect Postdecrement.
;_________________________________________________________________
.CODE ; Code Segment.
code:
; Main program.
; Set up the table of pointers to strings.
; There is a NULL pointer at the beginning of the table
; as well as the end of the table which will permit us to
; traverse the list forwards as well as backwards when using
; the NULL pointer as a termination point.
mov #f_table, r2
clr @r2+
mov #beg_ary0, @r2+
mov #beg_ary1, @r2+
mov #beg_ary2, @r2+
mov #beg_ary3, @r2+
clr @r2 ; Last pointer is NULL pointer.
; Print the strings first to last.
mov #f_table, r2 ; Point to the beginning of the table.
tst @r2+ ; Equivalent to add r2, #04, r2
loop0:
mov.b @@r2, r1
mov r1, r0 ; Pass character to putc().
jsub @#putc
inc @r2, @r2 ; Increment pointer.
tst.b @@r2 ; Is the character there a NULL ?
jne @#loop0
mov #crlf, @-sp ; Print a CR/LF.
jsub @#print
tst @sp+ ; Clear stack.
tst @r2+ ; Equivalent to : add r2, #04, r2
; Notice the REGISTER INDIRECT WITH AUTO-INCREMENT ADDRESSING MODE.
tst @r2 ; Is it a NULL pointer ?
jne @#loop0 ; If not, print the string pointed to.
mov #dashes, @-sp
jsub @#print
tst @sp+
mov #crlf, @-sp
jsub @#print
tst @sp+
; Print the strings last to first.
mov #f_table, r2
clr @r2+
mov #beg_ary0, @r2+
mov #beg_ary1, @r2+
mov #beg_ary2, @r2+
mov #beg_ary3, @r2+
clr @r2 ; Last pointer is NULL pointer.
mov #f_table, r2 ; Point to the beginning of the table.
; Take the pointer to the end of the table by searching for
; the NULL pointer.
loop1:
tst @+r2
jne @#loop1
; Now r2 is pointing to the NULL pointer, so we take it one
; step back so as to have r2 point to a string we can work with.
tst @-r2
; Notice the REGISTER INDIRECT WITH AUTO-PREDECREMENT ADDRESSING MODE.
loop2:
mov.b @@r2, r1
mov r1, r0 ; Pass character to putc().
jsub @#putc
inc @r2, @r2 ; Increment pointer.
tst.b @@r2
; Is the character there a NULL ?
jne @#loop2
mov #crlf, @-sp ; Print a CR/LF.
jsub @#print
tst @sp+ ; Clear stack.
tst @r2-
; Equivalent to : sub r2, #04, r2
; Notice the REGISTER INDIRECT WITH AUTO-DECREMENT ADDRESSING MODE.
tst @r2 ; Is it a NULL pointer ?
jne @#loop2 ; If not, print the string pointed to.
halt
;_________________________________________________________________
; Here include the code for subroutine print from program 7.13.5a
.DATA ; Data Segment.
data:
.align.w
; Table of pointers.
f_table: .block.w 4
beg_ary0: .string 'ABCDEF'
beg_ary1: .string 'GHIJKL'
beg_ary2: .string 'MNOPQR'
beg_ary3: .string 'STUVWX'
dashes: .string "------"
crlf: .init.b 0a
.init.b 0d
.init.b 00
.END ; End of source file.
This section contains a tabular summary of all of the Nurve opcodes organized by the number of operands that are required for each instruction.
The entries on the left side of the tables are complete machine code instructions. The opcode is the first part of each 'word'.
Before looking at the following opcode tables, look again at the "Notation" section for a complete description of all abbreviations - for example xx, yy, and zz are used as place-holders for operands; NZVC are for condition code bits, and so on.
Some opcodes can have an extension to indicate if the instruction is to be applied to a word, a byte or a halfword. (WBH) If the extension is not included in your program, Nurve assumes that a word is what you want.
Machine Code Assemble Mnemonic Description
No operand
00000000 HALT Halt process 00000001 RISR Return from interrupt service routine
Single operand
000001pp CLP. IASTNZVC Clear PS Register bit(s). 000002pp SEP. IASTNZVC Set PS Register bit(s). 1Dt000zz CLR. WBH Clear (zero) the destination. 1Et000xx TST. WBH Set PS register relative to zero.
Jump Instructions
20ppaaaa JC. IASTNZVC Jump if PS bit(s) clear. 21ppaaaa JC. * Jump if PS bit(s) clear (special). 22ppaaaa JS. IASTNZVC Jump if PS bit(s) set. 2200aaaa JSUB Jump SUBroutine. 23ppaaaa JS. * Jump if PS bit(s) set (special).
Double Operand
35t0xxzz XCH. WH Exchange Halfwords/Bytes. 36t0xxzz TAS. WH Test and Set. 37t0xxzz COM. WBH One's Complement. (Flip bits.) 38t0xxzz NEG. WBH Negate. (Two's Complement.) 39t0xxzz INC. WBH Increment. (Add 1.) 3At0xxzz DEC. WBH Decrement. (Subtract 1.) 3Bt0xxzz ADC. WBH Add Carry bit. 3Ct0xxzz SBC. WBH Subtract Carry bit. 3Dt0xxzz MOV. WBH Move. (Copy.) 3Et0xxyy CMP. WBH Compare xx-yy and set PS register. 3Fttxxzz CNV. WB WH BW Convert size of xx to size of BH HW HB yy.
Triple Operand
4txxyyzz AND. WBH Logical bitwise AND. 5txxyyzz OR. WBH Logical bitwise OR. 6txxyyzz XOR. WBH Logical bitwise XOR (exclusive or). 7txxyyzz ROT. WBH Rotate (through the Carry bit). 8txxyyzz ASH. WBH Arithmetic Shift. 9txxyyzz ADD. WBH Add. zz <- xx + yy. Atxxyyzz SUB. WBH Subtract. zz <- xx - yy. Btxxyyzz MUL. WBH Multiply. zz <- xx * yy. Ctxxyyzz DIV. WBH Divide. zz <- xx / yy. Dtxxyyzz MOD. WBH Modulo. zz <- remainder( xx / yy )
Examples:
Assembler Machine Description
1) and r1, r2, r3 ; 40010203 r3 <- r1 and r2.
2) and r0, #0f, r0 ; 40006C00 Mask out all but the least significant nibble of r0
; 0000000F (operand follows instruction)
and r1, #0f0, r1 ; 40016C01 Retain only the second least significant nibble of r1
; 000000F0 (operand follows instruction)
or r0, r1, r1 ; 50000101 The least significant byte of r1 is a composite of the
; higher nibble of r1 and lower nibble of r0 of their least
; significant bytes respectively.
3) xor r0, @r1, r0 ; 60001100 XOR r0 with contents of memory pointed to by r1.
xor r0, @r1, r0 ; This brings back the original value of r0.
4) rot #04, r0, r0 ; 706C0000 Rotate the value in r0 through C four times.
; 000000004 (operand follows instruction)
5) ash #01, r0, r0 ; 806C0000 Multiply the value in r0 by two.
; 000000001 (operand follows instruction)
ash #0ffffffff,r0, r0 ; 806C0000 Divide the value in r0 by two.
; FFFFFFFF (operand follows instruction)
6) add r0, @r1, r0 ; 90001100 Add word pointed to by r1 to r0.
7) sub r0, @r1, r0 ; A0001100 Subtract word pointed to by r1 from r0.
8) mul r0, #12, r0 ; B0006C00 Multiply the value in r0 by 17 (decimal).
; 000000012 (operand follows instruction)
9) div r0, #0a, r0 ; C0006C00 Divide the value in r0 by 10 (decimal).
; 00000000A (operand follows instruction)
10) mod r0, #02, r0 ; Take the remainder of r0/2 to check if r0 is even.
An assembler macro is a short hand notation for a previously defined instruction. For example, the macro RSUB is another way of writing MOV @SP+, PC. Macros are made available only for the convenience of an assembler programmer.
00000100 NOOP = CLP NO OPeration. 2000aaaa JUMP = JC Unconditional Jump. 2001aaaa JGEU = JC.C Jump Greater than or Equal Unsigned 2004aaaa JNE = JC.Z Jump Not Equal 2005aaaa JGTU = JC.CZ Jump Greater Than Unsigned 2008aaaa JPL = JC.N Jump PLus 210Aaaaa JGE = JC.N*V Jump Greater than or Equal signed 210Eaaaa JGT = JC.ZN*V Jump Greater Than signed 2201aaaa JLTU = JS.C Jump Less Than Unsigned 2204aaaa JEQ = JS.Z Jump EQual 2205aaaa JLEU = JS.CZ Jump Less than or Equal Unsigned 2208aaaa JMI = JS.N Jump MInus 230Aaaaa JLT = JS.N*V Jump Less Than signed 230Eaaaa JLE = JS.ZN*V Jump Less than or Equal signed 3D006D0C RSUB Return from SUBroutine. 3Dt0xx5D PUSH. WBH PUSH xx onto system stack. 3Dt06Dzz POP. WBH NOTE: Auto inc/dec on SP is +/- 4
The PUSH and POP instructions are used to save registers and values on the stack for subsequent retrieval.
They are mainly used in subroutines to save all the registers that are used within the subroutine. This facilitates the free use of registers by the calling program and the subroutine. The stack is also used in applications like expression parsing.
When a PUSH instruction is executed the SP is decremented by four and the value is stored at the new address in SP.
POP reverses the action of the PUSH instruction. POP retrieves the value currently pointed to by the SP and then increments the SP by four.
Assembler directives (also called Pseudo operations or pseudo-ops) provide information to the assembler and look like assembly language instructions, but do not generate machine language instructions. All assembler directives begin with a period.
Format:LABEL: .ALIGN.WBH ;COMMENT
where LABEL is an optional label. Only one of the letters W, B, or H must be specified.
Example:
.ALIGN.H
SALARY: .BLOCK.H 1 ; Net Employee SALARY (is 16 bits).
The ALIGN.H ensures that SALARY is properly aligned on a half word boundary.
Reserve a block of memory. .BLOCK.W reserves words, .BLOCK.B reserves bytes and .BLOCK.H reserves halfwords. The storage reserved is not initialized. The LC must be at a word boundary for .BLOCK.W or a halfword boundary for .BLOCK.H.
Format:
LABEL: .BLOCK.WBH nn ;COMMENT
where LABEL is an optional label, nn is a 1 or 2 digit positive (hex) integer giving the number of words to be reserved.
Example:
SUM: .BLOCK.W 1 ;Total or daily sales
TABEL: .BLOCK.W 4 ;TABEL will contain
; product codes
This reserves 5 memory words and associates the symbolic address SUM with the first word. The second block can be referenced by TABEL.
User initialized storage is either a program instruction placed in the code segment or a global data item placed in the heap. Thus, any code generated by the assembler must be given a code segment address or a heap address. Code generated following a .CODE directive will be placed in the code segment area (until the next .DATA directive is encountered).
The .END directive must be the last statement in an assembly language program. It informs the assembler that the end of the program has been reached (i.e.. proceed to second pass).
Format:
LABEL: .INIT.WBH nn ;COMMENT
where LABEL is an optional label, and nn is a hex integer specifying the value of a constant.
Example :CRLF: .INIT.B 0A ; ASCII 10 - Line Feed
.INIT.B 0D ; ASCII 13 - Carriage Return
The .STRING directive reserves and initializes consecutive bytes of ASCII codes, assigning one character per byte.
Format:
LABEL: .STRING 'text_string' ;COMMENT
where LABEL is an optional label, and text_string is any string of characters enclosed in (delimited by) quotation marks. A NULL byte (zero byte) is placed at the end of the string.
Example:
LOGIN: .STRING 'Login please: ' ; User login prompt.
will reserve 15 consecutive bytes and initializes them to the ASCII codes of the letters inside the quotes (including blanks and the trailing NULL byte).
System calls are provided by the operating system for the convenience (and safety) of users.
All I/O is performed via system calls.
System calls are invoked using the JSUB subroutine call instruction. The subroutines from the system library are linked into a load module by the linker.
There is no facility for obtaining numbers, or for printing numbers. You must convert from ascii to binary or from binary to ascii. Write your own routines for these functions, or use the subroutines given in the next section of the manual.
Getc accepts a single character from the default input device (usually the user terminal) and returns it in the low byte of R0.
Input is buffered by the operating system. Individual input characters are stored in a system buffer until a control character (usually a newline character) is found or the system buffer is full. Thus, a user program waiting to input a single character will be blocked (forced to wait) until more input (usually a newline character) is typed by the user.
Example:
jsub @#getc ; Get a character
; getc puts it into R0
Putc outputs a single ASCII character passed to it in the low byte of R0 onto the default output device (usually the user terminal).
Example:
mov.b @R5+, R0 ; Place a character into R0
jsub @#putc ; call putc to print the char
Calls to putc are also generally placed in a loop. This is also demonstrated in the next section of this manual.
As you will have observed by now, assembly language is very low level; it takes 3 or more lines of code to implement a simple 'if' statement. A 'small' program can rapidly grow to many lines or pages of code. Despite the best intentions of a programmer to document the program well, it can become difficult to discern the logic flow nestled in the many lines of code.
The solution to this problem is to divide the program into sections of code, each section performing a specific function. The main part of the program can then consist of simple calls to the sections of code required to perform the desired functions. This has the added advantage that, if a section of code is required more than once, it can simply be called again, rather than repeating the code each time the function is required.
Consider the assembly code that would be required to simply prompt the user for a string, and then get the string.
WITHOUT SUBROUTINES: WITH SUBROUTINES
mov #prompt, r1 mov #prompt, r1
putstr: mov @r1+, r0 jsub @#putstr
jeq endprint mov #string, r1
jsub @#putc jsub @#getstr
jump @#putstr
endprint: ;----------------------
mov #string, r1 ; subroutines down here
getwhile: jsub @#getc
cmp.b r0, @#LF
jeq @#endget
mov.b r0, @r1+
jump getwhile
endget:
The program section on the right - the one with subroutines - is much easier to follow than the longer version on the left. If you really want to see the details involved in the subroutines, you can always go down and look at the code; but the main part of the program is kept simple.
In the Nurve programming language, there are two instructions relating to subroutines.
jsub subroutine_label and rsub
The first instruction, jsub subroutine_label (e.g. jsub @#putc), instructs the assembler to jump to the location specified by the subroutine label. At the end of the subroutine, execution of code must return to the main program, at the instruction following the jsub. Question: How will the assembler know what location to return to in the main program? Answer: The jsub instruction actually does 2 things; it causes a jump to a subroutine, but it also, automatically, causes the return address (the address following the jsub instruction) to be placed on the stack.
The second instruction, rsub, instructs the assembler to return to the point in the main program following the jsub call. Question: How does the assembler know what location to return to in the main program? Answer: The rsub instruction automatically pops the return address from the stack; remember this was placed on the stack when the jsub instruction was executed.
It is quite likely that you will be using registers in your main program to hold certain values, such as loop counters and so on. However, you may also need to use some registers in your subroutine. It is a very good idea to save these register contents at the beginning of a subroutine and then restore them at the end of the subroutine. The stack can be used for temporary storage of values.
For example:; Subroutine: putstr
;-------------------------------------
putstr:
push r0 ; save register
putwhile:
mov.b @r1+, r0 ; body of subrtn
...
endput:
pop r0 ; restore register
Use the push and pop instructions as you would parentheses i.e. make sure you always have matched sets - if you push a value on the stack at the beginning of a subroutine, make sure you pop it off at the end.
Also, remember that the stack operates on a LIFO - Last In First Out. - basis. For example, if you wanted to save and restore R1 and R2, you would have to code:
push R1
push R2 ; R2 is the last value to go into the stack
... ; main part of subroutine
pop R2 ; the R2 value is the first out of the stack
pop R1
More often than not, it is necessary to pass one or more values between the calling area, and the subroutine.
e.g. jsub @#putc ; the subroutine 'putc' expects a value in R0
e.g. jsub @#getc ; the 'getc' subroutine places a character into R0
This is the simplest and easiest method of passing arguments - you can put an address or a value in a register.
This method of passing values to and from a subroutine can be dangerous if not used properly. It is included in this manual for the sake of completeness. Use it only if you are sure you completely understand it - otherwise, stick to using registers for passing values.
In order to use the stack to pass values to and from subroutines, you must understand how the stack is used in the execution of subroutines, or you can destroy the return address to the main program. If you put a value or address on the stack and then issue a jsub call, then there are 2 items on the stack. e.g. Consider the instructions:
mov #holdit, @-SP
jsub @#putstring
The address of the label "holdit" would be on the stack, followed by the return address following the jsub call. If you wanted to access the address 'holdit' from the subroutine you would have to use an instruction such as:
mov #04[SP], R4
The offset "04" is required to get around the return address stored on the stack. The stack pointer does not change after this instruction, a value is simply copied from the stack.
If you put something on the stack before a jsub call, you should remove it after the call. e.g.
tst @SP+ ; clean up stack
This instruction does change the stack pointer.
The only facility for I/O in Nurve is for single characters. It is relatively straightforward to set up subroutines that will get a string of characters, and to print a string. You must decide though, what character will be used to terminate a string. The Nurve Assembly directive ".STRING" appends a null byte to the end of each string that the user defines. Given this, it makes sense to use the null byte as a terminator for any string that you input.
The following program contains "getstr" and "putstr", two subroutines that you can use for character string I/O. You will notice in getstr, that the stored string is not terminated with the 'return' character; the 'null' character (ASCII value 0) is used instead.
*** Both subroutines use R1 to pass the address of the string.
These subroutines are stored as individual files in anonymous FTP on Hercules. Refer to the appendix on FTP for an example of how to get a file and add it to your own program. Feel free to encorporate them in your programs - just give credit to this manual for the code.
e.g. ; This subroutine is taken from the C.S. Nurve Users' Manual
;========================================================================
; Filename: 7.13.8.4.asm
; Author: Grace Hopper
; Student #: 000 000 000
;
; Purpose: To test the putstr and getstr subroutines.
;
;========================================================================
.CODE
mov #prompt1, r1 ; prompt for a string
jsub @#putstr
mov #stringin, r1 ; set up the string address for getstr
jsub @#getstr ; get the string
mov #mesg1, r1 ; echo the string
jsub @#putstr
mov #stringin, r1
jsub @#putstr
mov #CRLF, r1 ; add a carriage return line feed
jsub @#putstr
halt
.DATA
prompt1: .string "Input a string: "
.ALIGN
mesg1: .STRING "This is what you entered: "
.ALIGN
stringin: .BLOCK 80
CRLF: .INIT 0A ; ascii value for line feed
.INIT 0D ; ascii for carriage return
;-------------------------- Subroutines ---------------------------------
;--------------------------- putstr --------------------------------
; Subroutine: putstr
; Author: Grace Hopper
; Purpose: to output a character string
; Arguments: r1 holds the address of the string to be printed
;-------------------------------------------------------------------
.CODE
putstr:
push r0 ; save register
putwhile:
mov.b @r1+, r0 ; While (char not a null)
jeq @#endput ;
jsub @#putc ; print the character
jump @#putwhile ; end while
endput:
pop r0 ; restore register
rsub
;--------------------------- getstr --------------------------------
; Subroutine: getstr
; Author: Grace Hopper
; Purpose: to input a character string
; Arguments: r1 holds the address of the string to be input
;-------------------------------------------------------------------
.CODE
getstr:
push r0 ; save register
getwhile:
jsub @#getc ; While (char not a line feed - 0A)
cmp.b r0,@#LF
jeq @#endget
mov.b r0,@r1+ ; store the character
jump @#getwhile ; end while
endget:
mov.b #0,@r1 ; append a null byte to the string
pop r0 ; restore register
rsub
.DATA
LF: .INIT.B 0A ; ascii for line feed
.END
As noted previously, the only facility for I/O in Nurve is for single characters. If you wish to enter a number, you must input the Ascii value for each digit and convert the Ascii values to the corresponding numeric value. Similarly, if you wish to output a numeric value, you must go through each digit, one at a time, and convert the numeric values to the Ascii counterpart.
The following program contains "getnum" and "putnum", two subroutines that you can use for numeric I/O. You will notice that 'getnum' is more complex than 'putnum'; it requires a great deal of error checking to be sure the user has entered valid values.
*** Both subroutines use R1 to pass the address of the string.
You will find that the 'putnum' routine is handy in debugging. It is often desirable to display the contents of a register as a program executes. Simply add putnum to your program, and put in jsub calls wherever.
These subroutines are stored as individual files in anonymous FTP on Hercules. Refer to the appendix on FTP for an example of how to get a file and add it to your own program. Feel free to encorporate them in your programs - just give credit to this manual for the code.
e.g. ; This subroutine is taken from the C.S. Nurve Users' Manual
;========================================================================
; Filename: 7.13.8.5.asm
; Author: Grace Hopper
; Student #: 000 000 000
;
; Purpose: To test the getnum and putnum subroutines.
;
;========================================================================
.CODE
mov #prompt1, r1 ; prompt for a number
jsub @#putstr
jsub @#getnum ; get the number
mov r1, @#numbr1
mov #prompt1, r1 ; prompt for a number
jsub @#putstr
jsub @#getnum ; get the number
mov r1, @#numbr2
add @#numbr1, @#numbr2, @#sum
mov #mesg1, r1 ; print it out
jsub @#putstr
mov @#sum, r1
jsub @#putnum
mov #CRLF, r1
jsub @#putstr
halt
.DATA
prompt1: .string "Input a number: "
.ALIGN
mesg1: .STRING "This is the sum: "
.ALIGN
numbr1: .BLOCK 01
numbr2: .BLOCK 01
sum: .BLOCK 01
CRLF: .INIT 0A ; ascii value for line feed
.INIT 0D ; ascii for carriage return
;-------------------------- Subroutines ---------------------------------
;--------------------------- putstr --------------------------------
; Subroutine: putstr
; Author: Grace Hopper
; Purpose: to output a character string
; Arguments: r1 holds the address of the string to be printed
;-------------------------------------------------------------------
.CODE
putstr:
push r0 ; save register
putwhile:
mov.b @r1+, r0 ; While (char not a null)
jeq @#endput ;
jsub @#putc ; print the character
jump @#putwhile ; end while
endput:
pop r0 ; restore register
rsub
;--------------------------- getnum --------------------------------
; Subroutine: getnum
; Author: Grace Hopper
; Purpose: to input a number, converting from ascii to binary.
; Arguments: r1 will contain the binary number
;-------------------------------------------------------------------
.CODE
getnum:
push r0 ; save registers
push r2
push r3
push r4
push r5
push r6
getinit: ; Repeat (getting a valid string)
mov #gbuffer, r4 ; set pointer to char array
clr r2 ; set up counter for char's entered
getnwhile: ; Repeat (inputing characters)
jsub @#getc ; get a character
cmp.b r0, @#LF ; check it for line feed terminator
jeq @#endgetchars
mov.b r0,@r4+ ; move input char into array
inc r2, r2 ; increment digit counter
jump @#getnwhile ; Until a LF (0A) entered
endgetchars:
cmp #08, r2 ; If less than = 8 char's input
jleu @#checkerr ; carry on to check err rtn
push r1 ; Else
mov #sizerr, r1 ; display an error
jsub @#putstr
pop r1
jump @#getinit ; go back to start of loop
; Repeat (error checking)
; hex 00-2F invalid
; hex 30-39 NUMBERS
; hex 3A-40 invalid
; hex 41-46 LETTERS (A-F)
; hex 60-7F lowercase letters and others
checkerr: ;
mov #gbuffer, r3 ; set up character pointer to input string
errloop:
cmp.b @r3, #030 ; If ascii under 30
jmi @#prnterr ; break to print error routine
cmp.b @r3, #060 ; If ascii over 60
jmi @#nextchk ;
sub.b @r3, #020, @r3 ; map lower case letters to upper case
nextchk:
cmp.b @r3, #040 ; If ascii is for a number (30-39)
jmi @#endchk ; carry on to end of checks
cmp.b #046, @r3 ; If ascii of letter beyond 'F' (46)
jmi @#prnterr ; break to print error routine
cmp.b @r3,#040 ; If ascii between numbers and 'A'
jmi @#prnterr ; break to print error routine
endchk:
tst.b @r3+ ; increment character pointer
cmp r3, r4 ; If all char's ok
jeq @#asci2bin ; carry on to conversion rtn
jump @#errloop ; Until all char's checked
prnterr:
push r1 ; print out error message
mov #invalid, r1 ; and go back to start of loop
jsub @#putstr
pop r1
jump @#getinit ; Until 8 or less valid char's entered
; Input string has been validated, now convert ascii to binary
asci2bin:
clr r1 ; clear target for binary value
mov r2, r5 ; set up for shift counter
a2binloop: ; Repeat (converting characters)
clr r3 ; clear intermediate target for binary value
mov.b @-r4, r3 ; get a character from the array
cmp.b #039, r3 ; If it's a number
jmi @#sub37
sub r3, #030, r3 ; convert number to binary
jump @#storedig ; Else
sub37:
sub.b r3, #037, r3 ; convert letter to binary
storedig:
and #0f, r3, r3 ; clear high order bits
sub.b r5, r2, r6 ; calculate the shift count
mul #04, r6, r6 ; " " "
ash r6, r3, r3 ; move binary digit to position
add r1, r3, r1 ; add it to the target
dec r2, r2 ; decrement the character pointer
jeq @#endgetnum
jump @#a2binloop ; Until all characters converted
endgetnum:
pop r6 ;
pop r5 ;
pop r4 ; restore registers
pop r3
pop r2
pop r0
rsub
.DATA
.ALIGN
gbuffer: .BLOCK 20
sizerr: .string "Please re-enter value with 8 or less characters: "
.ALIGN
invalid: .string "Invalid characters input. Please re-enter: "
.ALIGN
LF: .INIT.B 0A
.ALIGN
;--------------------------- putnum --------------------------------
; Subroutine: putnum
; Author: Grace Hopper
; Purpose: to convert a binary value to ascii and output it.
; Arguments: r1 contains the number
;-------------------------------------------------------------------
.CODE
putnum:
push r4 ; save registers
push r2
push r0
mov #08, r4 ; set up a counter for digits
digits:
; Repeat
mov r1, r2 ; get top digit out of r1
ash #04,r1,r1 ; move the rest of the digits over
ash #0ffffffe4, r2,r2 ; move digit to the right for printing
and #0f, r2, r2 ;
cmp.b #09, r2 ; if digit is numeric
jmi @#leters
add.b #030, r2, r2 ; add 30 to convert to ascii
jump @#prntdig
leters: ; else (letters A-f)
add.b #037, r2, r2 ; add 37 to convert
prntdig:
mov.b r2,r0 ; print the digit
jsub @#putc ;
dec r4,r4 ; decrement the digit counter
jeq @#endputnum ;
jump @#digits ; until 8 digits printed
endputnum:
pop r0 ; restore registers
pop r2
pop r4
rsub
.END
A coredump is a 'snapshot' of the NURVE memory when a program terminates. A core dump is automatically produced if the program terminates abnormally or if the '-c' option is given on the command line. A coredump can be used to aid in debugging a program. The contents of all the non-zero memory locations as well as the contents of the registers and the processor status register are displayed on the default output device.
Example:
;=========================================================================
; Filename: 7.13.1.2.1.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: to set the processor status word negative flag.
;
;=========================================================================
.CODE ; Code Segment.
code:
clr r0 ; Initialize.
dec r0, r0 ; Generate a negative result.
halt
.END
Remember that the hex value 08 in the low order portion of the Processor Status register means that the N bit (the negative flag) is set. Look at the value of the PS in the following dump, and also look at the value of register 0.
hercules[2]% nurve 7.13.1.1.asm [Return]
Nurve: completed with 0 errors and 0 warnings
hercules[3]% nlink 7.13.1.1.obj [Return]
hercules[6]% nload -c 7.13.1.1.lmn [Return]
Nload: End of Execution.
Execution terminated. PC = 0000000C. 2 instructions executed.
NURVE CORE DUMP (Hex)
----- ---- ---- -----
Code Segment PCB Heap Stack Stack Top
00000000 00000400 00000480 00000880 00000C80
R0 R1 R2 R3 R4 R5 R6 R7
FFFFFFFF 00000000 00000000 00000000 00000000 00000000 00000000 00000000
R8 R9 RA BR PC SP PS IR
00000000 00000000 00000000 41A0E400 0000000C 00000C80 00000108 00000000
ADDR OFFSET 0 04 08 0C 10 14 18 1C
----| -------- -------- -------- -------- -------- -------- -------- --------
0000| 1D000000 3A000000 00000000 00000000 00000000 00000000 00000000 00000000
...
hercules[7]%
The '-t' option on the command line activates the trace facility. The trace prints the instruction about to be executed. This is useful when debugging a program.
This section presents some program examples. The first one is the one that adds ten numbers which has been presented before in this manual.
;=========================================================================; Filename: 7.13.3.1.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Add 10 numbers stored in an array.
;
;=========================================================================
; Pseudo code.
; Let pointer (r3) point to the beginning of the array.
; Let counter (r4) = 10
; Let sum (r5) = 0
; While (counter <> 0)
; Let sum = sum + number pointed to by r3
; Increment the pointer r3
; Let counter = counter - 1
; End
;___________________________________________________________________
.CODE
start:
mov.w #array,r3 ;Register r3 is the pointer
; to the array elements.
mov.w #0a,r4 ;Register r4 is a down counter
; starting at 10 (decimal).
clr.w r5 ;Register r5 will accumulate the
; sum of 10 numbers.
loop: ; Repeat
add.w @r3+, r5, r5; Add the number pointed to by r3
; to running total in r5
dec.w r4, r4 ; Decrement the counter.
tst.w r4 ;
jne @#loop ; until counter is zero
halt ;
.DATA
array: ; Data items for the 10 element array.
.init.w 1
.init.w 3
.init.w 5
.init.w 7
.init.w 9
.init.w b
.init.w d
.init.w f
.init.w 17
.init.w 19
.END
The next program shows how to use various branch instructions and subroutines.
;=========================================================================; Filename: 7.13.9.1.asm
; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Sets and tests the N, Z, V and C flags in the PS
; registers.
;
;========================================================================= ; Documentation :
; Performs data movement or arithmetic to set particular PS register
; flags and prints messages accordingly. Prints messages before the
; setting of the flags too, so as to allow the reader to follow the flow of
; control in the program.
;_________________________________________________________________
.CODE ; Code Segment.
start:
; Main program.
mov #00, r0 ; Initialize.
mov #01, r1
tst_neg:
mov #neg_tst, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
sub r0, r1, r0 ; Generate a negative result.
js.n @#pr_neg_msg
jump @#pr_abs_msg ; This line will never be executed.
tst_zip:
mov #zip_tst, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
inc r0, r0 ; Generate a zero result.
js.z @#pr_zip_msg
jump @#pr_abs_msg ; This line will never be executed.
tst_ovf:
mov #ovf_tst, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
mov #07fffffff, r0
inc r0, r0 ; Generate an overflow.
js.v @#pr_ovf_msg
jump @#pr_abs_msg ; This line will never be executed.
tst_cry:
mov #cry_tst, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
clr r0
sub r0, #01, r0
rot #01, r0, r0 ; Generate a carry.
js.c @#pr_cry_msg
jump @#pr_abs_msg ; This line will never be executed.
end_tst:
halt
pr_neg_msg:
mov #neg_msg, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
mov #cr_lf, -sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
jump @#tst_zip
pr_zip_msg:
mov #zip_msg, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
mov #cr_lf, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
jump @#tst_ovf
pr_ovf_msg:
mov #ovf_msg, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
mov #cr_lf, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
jump @#tst_cry
pr_cry_msg:
mov #cry_msg, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
mov #cr_lf, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
jump @#end_tst
pr_abs_msg:
mov #abs_msg, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
mov #cr_lf, @-sp ; Pass pointer of string to print().
jsub @#print
tst @sp+ ; Clear stack.
halt ; Should control come here, it would
; be best to halt the CPU.
;_________________________________________________________________
;
; Subroutine : print
; Function : Prints a string.
;_________________________________________________________________
; Arguments : The pointer to the string is received
; on the stack.
; Registers
; used : r0, r1, sp
; modified : none
; Side effects : none
; Remarks : SP is left for the calling program to clear.
; Documentation :
; Register r1 is used to scan through the string, byte at a time.
; Register r0 is used to convert the character byte to a word that
; can be put on the stack before calling putc().
.CODE ; Code Segment.
print:
push r0
push r1
mov.w #0c[sp], r1 ; Get the pointer to the string.
begin0:
jeq @#end0 ; If end of string, exit loop
mov.b @r1+, r0 ; Convert character byte to word.
jsub @#putc
jump @#begin0
end0:
pop r1
pop r0
rsub
;_________________________________________________________________
.DATA ; Data Segment.
data:
.align.w
neg_tst: .string "Testing Negative flag..."
zip_tst: .string "Testing Zero flag..."
ovf_tst: .string "Testing Overflow flag..."
cry_tst: .string "Testing Carry flag..."
neg_msg: .string "Result is negative."
zip_msg: .string "Result is zero."
ovf_msg: .string "Result caused overflow."
cry_msg: .string "Result caused carry."
abs_msg: .string "Buy a new CPU."
cr_lf: .init.b 0a
.init.b 0d
.init.b 00
.END ; End of source file.
________________________________________________________________________________________
The next program shows how to use getc and putc system calls.
;========================================================================
; Filename: 7.13.8.4.asm
; Author: Grace Hopper
; Student #: 000 000 000
;
; Purpose: To test the putstr and getstr subroutines.
;
;========================================================================
.CODE
mov #prompt1, r1 ; prompt for a string
jsub @#putstr
mov #stringin, r1 ; set up the string address for getstr
jsub @#getstr ; get the string
mov #mesg1, r1 ; echo the string
jsub @#putstr
mov #stringin, r1
jsub @#putstr
mov #CRLF, r1 ; add a carriage return line feed
jsub @#putstr
halt
.DATA
prompt1: .string "Input a string: "
.ALIGN
mesg1: .STRING "This is what you entered: "
.ALIGN
stringin: .BLOCK 80
CRLF: .INIT 0A ; ascii value for line feed
.INIT 0D ; ascii for carriage return
;-------------------------- Subroutines ---------------------------------
;--------------------------- putstr --------------------------------
; Subroutine: putstr
; Author: Grace Hopper
; Purpose: to output a character string
; Arguments: r1 holds the address of the string to be printed
;-------------------------------------------------------------------
.CODE
putstr:
push r0 ; save register
putwhile:
mov.b @r1+, r0 ; While (char not a null)
jeq @#endput ;
jsub @#putc ; print the character
jump @#putwhile ; end while
endput:
pop r0 ; restore register
rsub
;--------------------------- getstr --------------------------------
; Subroutine: getstr
; Author: Grace Hopper
; Purpose: to input a character string
; Arguments: r1 holds the address of the string to be input
;-------------------------------------------------------------------
.CODE
getstr:
push r0 ; save register
getwhile:
jsub @#getc ; While (char not a line feed - 0A)
cmp.b r0,@#LF
jeq @#endget
mov.b r0,@r1+ ; store the character
jump @#getwhile ; end while
endget:
mov.b #0,@r1 ; append a null byte to the string
pop r0 ; restore register
rsub
.DATA
LF: .INIT.B 0A ; ascii for line feed
.END
The following program shows how to pass a parameter to a function via a register and obtain a return value via register.
The sign subroutine receives a parameter in the r1 register and returns a value in the r0 register.
;=========================================================================; Filename: 7.13.9.3.asm; Author: Christian, Burt Berel. S.
; Student #: 123 456 789
;
; Purpose: Test the sign of a number.
;
;=========================================================================
.CODE ; Code Segment.
code:
; Main program.
mov #0ffffffff, r1 ; Comment out this one to test for +ve
mov #02, r1 ; Comment out this one to test for -ve
jsub @#sign
tst r0
jc.z @#its_neg
mov #pos_msg, r1 ; Pass pointer of string to putstr
jsub @#putstr
jump @#end
its_neg:
mov #neg_msg, r1 ; Pass pointer of string to putstr.
jsub @#putstr
end:
halt ; End of main program.
;_________________________________________________________________
;
; Subroutine : sign
; Purpose : Determines the sign of the input argument.
;_________________________________________________________________
; Arguments : The number is received in r1
; Return value : In r0, 0 if negative else 1
; Registers
; used : r0, r1
; modified : none
; Side effects : none
; Remarks : SP is left for the calling program to clear.
; Documentation :
; Register r1 is divided by two and remainder taken in r0
; If register r0 is zero we return right then. Otherwise, since
; we want to return 1 in r0 indicate that the number is not
; even, we divide r0 by itself and take the result in r0 which
; will always be one.
.CODE ; Code Segment.
sign:
clr r0
tst r1
jc.n @#negative
inc r0, r0
rsub
negative:
rsub
;--------------------------- putstr --------------------------------
; Subroutine: putstr
; Author: Grace Hopper
; Purpose: to output a character string
; Arguments: r1 holds the address of the string to be printed
;-------------------------------------------------------------------
.CODE
putstr:
push r0 ; save register
putwhile:
mov.b @r1+, r0 ; While (char not a null)
jeq @#endput ;
jsub @#putc ; print the character
jump @#putwhile ; end while
endput:
pop r0 ; restore register
rsub
;_________________________________________________________________
.DATA ; Data Segment.
data:
.align.w
neg_msg: .string "The number is negative."
pos_msg: .string "The number is positive."
.END ; End of source file.
__________________________________________________________________________
You may wish to obtain the files used as programming examples in this manual. To do this you must logon to your Hercules account, and then access the FTP program as an anonymous user. FTP will ask you to type in your username as the password. When you get on, then change to the subdirectory /pub/170/nurvman and then you can enter "dir" to see what files are there. Use the FTP 'get' command to get a copy of a file from the FTP area to your own account. Enter the 'quit' command to exit the FTP program.
An example follows:
hercules[2]% ftp ftp [Return]
220 mercury.cs.uregina.ca FTP server ready.
Name (ftp:username): anonymous [Return]
331 Guest login ok, type your name as password.
Password: . (enter your username here) . [Return]
230 Guest login ok, access restrictions apply.
ftp> cd /pub/170/nurvman [Return]
250 CWD command successful.
ftp> dir [Return]
200 PORT command successful.
150 Opening ASCII mode data connection for '/bin/ls'.
total 58
-rw-r--r-- 1 11021 archive file.asm
.
.
.
-rw-r--r-- 1 11021 archive file.asm
226 Transfer complete.
745 bytes received in 0.31 seconds (2.4 Kbytes/s)
ftp> get file.asm [Return]
200 PORT command successful.
150 Opening ASCII mode data connection for 'file.asm' (826 bytes).
226 Transfer complete.
local: file.asm remote: file.asm
868 bytes received in 0.091 seconds (9.4 Kbytes/s)
ftp> quit [Return]
221 Goodbye.
hercules[3]%
When you have completed these steps, a copy of the file you specified in the "get file.asm" instruction will be placed in your account.
You may wish to include one of the subroutines into your own program - if so, remember to credit the Nurve Users's Manual.
Suppose you had obtained the file "putnum.asm". To include this file in your program:
* edit your file
* move the cursor to the point in the program that you want the subroutine
* enter :r putnum.asm [Return]
* this tells the editor to read the file into the edit buffer
* now enter :wq [Return]
* assemble, link, and run your program as usual.
Note: There are periods (.) in the file names of the example programs in /pub/170/nurvman
Because of this, you will have to enter the complete file specification (including the extension) when you assemble, link, and run and example. Suppose you had copied the file 7.13.3.1.asm. You would have to enter:
nurve 7.13.3.1.asm [Return]
nlink 7.13.3.1.obj [Return]
nload 7.13.3.1.lmn [Return]
This section contains a detailed explanation of each of the opcodes and macros previously presented in table form. In the preceding tables, opcodes and macros were listed by group, e.g. no operand, single operand, and so on.
This section presents the opcodes and macros in alphabetical order.
ADC. [WBH]
ADd Carry (dest) <- (srcA) plus (C bit)Format: 3Bt0XXZZ ADC srcA, dest
Description: Add the Carry bit of PS register and source A into destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=1 if XX is max_int(signed) and C=1
C=1 if XX is max_int(unsigned) and C=1
Example:
ADC r0, r0
ADC @r0, @r0
ADD (dest) <- (srcA) plus (srcB)
ADD. [WBH]Format: 9txxyyzz ADD srcA, srcB, dest
Description: Add source A and source B into the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=set if both operands have same sign and
result has a different sign
C=Set if carry from MSB
Example:
ADD r0, #0a, r0
ADD @r0, @r1, @r2
ADD @r0, #0a, @r0
AND. [WBH]
AND (Bitwise logical ) (dest) <- (srcA) and (srcB)Format: 4tXXYYZZ AND srcA, srcB, dest
Description: Clear the destination and set only those bits whose corresponding bits in both source A and source B are set (to one).
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=0
Example: AND r0, #0f, r0
ASH. [WBH]
Arithmetic SHift (dest) <- (srcB) shifted (srcA) timesFormat: 8tXXYYZZ ASH count, srcB, dest
Description: The destination becomes source B shifted count times. If count is negative, the shift is towards the right. On a right shift the MSB is duplicated. If count is positive the shift is towards the left. On a left shift the LSB is cleared. The original contents of the destination are lost. The contents of the sources are not affected.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=Set if sign changes during shift
C=Loaded with MSB on left shift, with LSB on right shift
Example:
ASH #04, r0, r0 ; shifts 4 bits to the left
ASH #0fffffffc,r0, r0 ; shifts 4 bits to the right
CLP. [IASTNZVC]
CLear Ps register bit(s)Format: 000001pp CLP
Description: Selectively clear bits in the PS register.
Condition Codes: Indicated Condition code is cleared
Example: CLP.V
CLP.N
CLP.Z
CLP.C
CLeaR (dest) <- 0
CLR. [WBH]
Format: 1Dt000zz CLR dest
Description: The destination is set to zero.
Condition codes: N=0
Z=1
V=0
C=0
Example:
CLR r0
CLR @r0+
CMP. [WBH]
CoMPare (srcA) minus (srcB)Format: 3Et0xxyy CMP srcA, srcB
Description: Compare the source and destination operands and set the PS condition codes, which may then be used for conditional branches. Both operands are unaffected even though the compare is actually done using subtraction.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=set if arithmetic overflow.
C=set if carry from MSB.
Example:
CMP r0, #0a
CMP #0, r0
CMP @-r0, #0
CNV. [WBH] [WBH]
CoNVert (dest) <- (srcA)Format: 3FttXXZZ CNV srcA, dest
Description: Copy source A into destination. XX and ZZ can be different sizes.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=unaffected
Example:
CNV r0, r1
COM. [WBH]
COMplement (dest) <- flip bits of (srcA)Format: 37t0XXZZ COM srcA, dest
Description: Place the one's complement of source A into the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=1
Example:
COM r0, r0
COM @r0, r1
DEC. [WBH]
DECrement (dest) <- (srcA) minus 1Format: 3At0xxzz DEC srcA, dest
Description: Subtract 1 from source A and place in the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=1 if XX is min_int(signed)
C=unaffected
Example:
DEC r0, r0
DEC @r0
DIV. [WBH]
DIVide (dest) <- (srcA) divided by (srcB)Format: Ctxxyyzz DIV srcA, srcB, dest
Description: Divide the source A operand by the source B operand and store the result in the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=set if abs(srcA) < abs(srcB)
C=set if srcB is zero
Example: DIV r0, #0a, r1
HALT
HALTFormat: 00000000 HALT
Description: Stop program execution.
Condition Codes: Not affected
Example: HALT
INCrement (dest) <- (srcA) plus 1
INC. [WBH]Format: 39t0xxzz INC srcA, dest
Description: Destination becomes one plus source A.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=1 if result is max.int (signed)
C=unaffected
Example:
INC r0, r0
INC @r0, @r0
JC. [IASTNZVC]
Jump if PS bit(s) Clear PC <- dest if indicated PS bit(s) clearedFormat: 20ppaaaa JC dest
Format: 21ppaaaa JC.* dest [ * special form * ]
Description: Jump to aaaa if indicated bit(s) in PS is(are) clear (zero). JC special, tests if N XOR V is zero. Other bits (especially the Z bit) can be tested along with the N and V bits.
Condition Codes: Not affected.
Example: JC.N @#begin0
JC.Z @#end0
JC.V @#err0
JC.C @#over0
Jump if EQual PC <- dest if Z = 1
JEQFormat: 2204aaaa JEQ dest
Description: Jump to aaaa if PS Z bit is set, otherwise continue with the next instruction.
Condition Codes: Not affected.
Example: JEQ @#done0
JGE
Jump if Greater than or Equal signed PC <- dest if N v V = 1 or Z=1
Format: 210Aaaaa JGE dest
Description: Jump to aaaa if N exclusive or V is zero. In particular JGE will cause a branch if it follows a CMP instruction where the source A operand was greater than or equal to the source B operand, when they are treated as signed numbers.
Condition Codes: Not affected.
Example: JGE @#continue0
Jump Greater than or Equal Unsigned PC <- dest if C bit is clear (zero)
JGEUFormat: 2001aaaa JGEU dest
Description: Jump to aaaa if the C bit is zero. In particular JGEU will cause a branch if it follows a CMP instruction where the source A operand was greater than or equal to the source operand B, when they are treated as unsigned numbers.
Condition Codes: Not affected.
Example: JGEU @#over
JGT
Jump if Greater Than signed PC <- dest if Z = 0 and (N v V) = 0Format: 210Eaaaa JGT dest
Description: Causes a branch if the Z bit is zero and the N and V bits are both the same. In particular, JGT will cause a branch if it follows a CMP instruction where the source A operand is greater than the source B operand, when they are treated as signed numbers.
Condition Codes: Not affected.
Example: JGT @#done0
Jump Greater Than Unsigned PC <- dest if C = 0 and Z = 0
JGTUFormat: 2005aaaa JGTU dest
Description: Causes a branch if both the Z and C bits are zero. In particular, JGTU will cause a branch if it follows a CMP instruction where the source A operand is greater than the source B operand, when they are treated as unsigned numbers.
Condition Codes: Not affected.
Example: JGTU @#trigger0
Jump if Less than or Equal signed PC <- dest if Z = 1 or (N v V) = 1
JLEFormat: 230Eaaaa JLE dest
Description: Causes a branch if the Z bit is set and the N bit is different from the V bit. In particular, JLE will branch if it follows a CMP instruction where the source A operand is less than or equal to the source B operand, when they are treated as signed numbers.
Condition Codes: Not affected.
Example: JLE @#begin0
JLEU
Jump Less than or Equal Unsigned PC <- dest if Z = 1 and C = 1Format: 2205aaaa JLEU dest
Description: Causes a branch if both the Z and C bits are set. In particular, JLEU will branch if it follows a CMP instruction where the source operand A is less than or equal to the source B operand, when they treated as unsigned numbers.
Condition Codes: Not affected.
Example: JLEU @#finished0
Jump if Less Than signed PC <- dest if N v V = 1
JLT
Format: 230Aaaaa JLT dest
Description: Jump to aaaa if the N and V bits are different. In particular JLT will cause a branch if it follows a CMP instruction where the source A operand was less than the source B operand, when they are treated as signed numbers.
Condition Codes: Not affected.
Example: JLT @#end0
JLTU
Jump Less Than Unsigned PC <- dest if C = 1Jump Less Than Unsigned
Operation: PC <- dest if C = 1
Format: 2201aaaa JLTU dest
Description: Jump to aaaa if the C bit is set. In particular JLTU will cause a branch if it follows a CMP instruction where the source A operand was less than the source B operand, when they are treated as unsigned numbers.
Condition Codes: Not affected.
Example: JLTU @#finished0
JMI
Jump if MInus PC <- dest if N = 1Format: 2208aaaa JMI dest
Description: Jump to aaaa if the N bit is set, otherwise continue with the next instruction.
Condition Codes: Not affected.
Example: JMI @#bad_val0
Jump Not Equal PC <- dest if Z = 0
JNEFormat: 2004aaaa JNE dest
Description: Jump to aaaa if the Z bit is clear. JNE is the complementary operation to JEQ.
Condition Codes: Not affected.
Example: JNE @#continue0
Jump if PLus PC <- dest if N = 0
JPLFormat: 2008aaaa JPL dest
Description: Jump to aaaa if the N bit is clear, otherwise continue with the next instruction. JPL is the complimentary operation to JMI.
Condition Codes: Not affected.
Example: JPL @#start0
JS. [IASTNZVC]
Jump if PS bit(s) Set PC <- dest if indicated PS bit(s) setFormat: 22ppaaaa JS dest
Format: 23ppaaaa JS.* dest [ * special form * ]
Description: Jump to aaaa if indicated bit(s) in PS is(are) set (one). JS special, tests if N XOR V is one. Other bits (especially the Z bit) can be tested along with the N and V bits.
Condition Codes: Not affected.
Example: JS @#unusual0
Jump to SUBroutine
JSUBFormat: 2200aaaa JSUB dest
Operation: PUSH PC onto top of stack
PC <- dest
Description: This instruction causes a jump to the subroutine whose starting address is specified by aaaa. Upon return from the subroutine, execution will continue at the instruction following the JSUB instruction. During the execution of the JSUB, the old contents of the PC are first pushed onto the system stack before the dest is placed in the PC, causing the next instruction to be executed to be the first instruction in the subroutine. Subroutines nested within subroutines are allowed. The maximum number of such nested subroutines depends on the size of the stack.
Condition Codes: Not affected
Example: JSUB @#print
JSUB @#scanf
JSUB @#err0
JUMP
JUMP PC <- destFormat: 2000aaaa JUMP dest
Description: Transfer program control to address aaaa.
Condition Codes: Not affected.
Example: JUMP @#end0
JUMP @#default0
MOD. WBH
MODulus (dest) <- (srcA) remainder (srcB)MODulus
Operation: (dest) <- (srcA) remainder (srcB)
Format: Dtxxyyzz MOD srcA, srcB, dest
Description: Place the integer remainder of source A divided by source B into the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=Set if abs(srcA) < abs(srcB)
C=Set if srcB = 0
Example:
MOD r0, #02, r1
MOV. [WBH]
MOVe (dest) <- (srcA)Format: 3Dt0xxzz MOV srcA, dest
Description: Move the source operand to the destination location. The previous contents of the destination are lost.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=Unaffected
Example:
MOV r0, @r1
MOV @r0, @r1
MOV #base, r0
MOV @@r0, @-SP
MUL. [WBH]
MULtiply (dest) <- (srcA) times (srcB)Format: Btxxyyzz MUL srcA, srcB, dest
Description: Multiply the source A operand by the source B operand and store the result in the destination.
NOTES: If operands are Byte, result is Half-word.
If operands are Half-word result is Word.
If operands are Word result is Word.
ex: MUL.B source is Bytes but destination is Halfword
ex: MUL.H source is Halfwords but destination is Word
Condition Code: N=Set if result < 0
Z=Set if result = 0
V=set if result < min(abs(XX),abs(YY))
C=0
Example:
MUL r0, #0a, r0
NEGate (dest) <- 0 minus (srcA)
NEG. [WBH]
Format: 38t0XXZZ NEG srcA, dest
Description: Place the two's complement of the source A operand into the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=1 if result = min_int (signed)
(min_int is 0x80 for BYTE, 0x8000 for Half and 0x80000000 for word)
C=1 if result = 0
Example:
NEG r0, r0
NEG @r0, r1
NOOP
NO OPerationFormat: 00000100 NOOP
Description: No operation is performed when this instruction is executed. This instruction is usually used by machine language programmers, who place a few NOOP's after every ten instructions or so. If new instructions have to be inserted at a later time, these NOOP's can be replaced with the new instructions.
Condition Codes: Not affected
Example: NOOP
OR. [WBH]
Bitwise OR (dest) <- (srcA) inclusive or with (srcB)Format: 5tYYXXZZ OR srcA, srcB, dest
Description: Set all bits in the destination operand. Clear only those bits that are cleared in both the source operands.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=0
Example:
OR r0, #0ffff0000, r0
OR r0, @r1, r0
POP. [WBH]
POP operand from the stack - this is a special case of the MOV instruction.Operation: (dest) <- @SP
SP <- SP + 4
Format: 3Dt0xx5D POP dest
Description: The value at the location pointed to by the stack pointer is moved to the destination, then the stack pointer is incremented.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=unaffected
Example:
POP r1
PUSH operand onto the stack - this is a special case of the MOV instruction.
PUSH. [WBH]Operation: SP <- SP - 4
@SP <- (srcA)
Format: 3Dt06Dxx PUSH srcA
Description: The stack pointer is decremented then the source A operand is moved to the destination (pointed to by the stack pointer).
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=unaffected
Example:
PUSH r0
Return from Interrupt Service Routine
RISRFormat: 00000001 RISR
Operation: POP PC from stack
POP PS from stack
Description: Interrupt services routines are invoked by the NURVE machine when a (hardware or software) interrupt is signaled. The machine PUSHes the PS and PC registers onto the stack before loading the address of the ISR into the PC. I/O operations are performed using Interrupt Service Routines.
Condition Codes: Not affected.
Example: RISR
ROTate thru the C bit. (dest) <- (srcB) rotated (srcA) times
ROT. [WBH]
Format: 7tXXYYZZ ROT count, srcB, dest
Description: Copy the source B operand into the destination. If count is negative rotate right by shifting the C bit into the MSB and the LSB into the C bit. If count is positive rotate left by shifting the C bit into the LSB and the MSB into the C bit. Repeat this process count times.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=N xor C
C=LSB (ROT right) or MSB (ROT left)
Example: ROT #04, r0, r0
ROT #0ffffffff, r0, r0
Return from SUBroutine
RSUB
Operation: POP top of stack into PC (PC <- @SP+)
Format: 3D006D0C RSUB
Description: The value on the top of the stack is popped and placed into the PC. Exit from a subroutine and resume execution at the instruction following the JSUB instruction, in the part of the program that called the subroutine.
Condition Codes: Not affected.
Example: RSUB
SBC. [WBH]
SuBtract Carry (dest) <- (srcA) minus (Carry)Format: 3Ct0xxzz SBC srcA, dest
Description: Place the source A operand minus the Carry bit into the destination.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=1 if srcA is min_int(signed) and C=1
C=0 if srcA is 0 and C=1
Example: SBC r0, r0
SEP. IASTNZVC]
SEt Ps register bit(s)
Format: 000002pp SEP dest
Condition Codes: Indicated Condition code is set
Example: SEP.N
SEP.Z
SEP.V
SEP.C
SUBtract (dest) <- (srcA) minus (srcB)
SUB. [WBH]Format: Atxxyyzz SUB srcA, srcB, dest
Description: Subtract the source B operand from the source A operand and store the result at the destination address.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=Set if operands have opposite signs and result has same sign as srcB.
C=Set if borrow from MSB
Example: SUB r0, #05, r0
Test And Set (dest) <- (dest)
TAS. [WH]
Format: 36t0xxzz TAS dest
Description: For TAS.W, copy the low halfword of the source A operand into the destination while setting the high halfword of destination to -1. For TAS.H, copy the low byte of the source A operand into the destination while setting the high byte of destination to -1.
Condition Codes: N=Set if dest.low halfword(byte) < 0
Z=Set if dest.low halfword(byte) = 0
V=0
C=0
Example: TAS.W r0
TAS.H r1
TST. [WBH]
TeSTFormat: 1Et000xx TST srcA
Description: Set the N and Z bits according to the value of source A operand.
Condition Codes: N=Set if srcA < 0
Z=Set if srcA = 0
V=0
C=0
Example:
TST @r0+
TST @@r0
TST r0
TST @SP+
XCH. [WH]
eXCHange halfwords or byteshigh low high low
Operation: BYTE 1 BYTE 0 becomes BYTE 0 BYTE 1
Format: 35t0xxzz XCH srcA, dest
Description: If XCH.W, exchange the high-order halfword and low-order halfword of the source A operand and place in the destination. If XCH.H, exchange the high-order byte and low-order byte of the source A operand and place in the destination.
Condition Codes: N=Set result < 0
Z=Set if result = 0
V=0
C=0
Example: ; r1 contains: 12345678
XCH.W r1, r1 ; after this, r1 holds: 56781234
XCH.H r1, r1 ; after this, r1 has: 00003412
XOR. [WBH]
Bitwise XOR (dest) <- (srcA) v (srcB)
Format: 6tYYXXZZ XOR srcA, srcB, dest
Description: After setting all bits in the destination, clear only those bits in the destination that are the same in both source operands.
Condition Codes: N=Set if result < 0
Z=Set if result = 0
V=0
C=0
Example: XOR r0, #055555555, r0
XOR r0, r1, r0
NOTATION
LSB Least Significant Bit. MSB Most Significant Bit. LC Assembler Location Counter Rn Register number (R0-RF). 1 hex digit aaaa 16 bit address. 4 hex digits hhh Hex number. pp Processor Status Flag. [I A S T N Z V C] 2 hex digits t Type of operand (0-2). [W=0 B=1 H=2] 1 hex digit xx Mode + Register. Source Operand A. 2 hex digits yy Mode + Register. Source Operand B. 2 hex digits zz Mode + Register. Destination Operand. 2 hex digits nn 2 digit hex number <- becomes v logical OR v logical exclusive OR
REGISTERS
PROCESSOR STATUS (PS) REGISTER BITS
PS BIT Mask Set = 1 Cleared = 0 Interrupt I 80 Interrupts Interrupts enabled. disabled. Addressing A 40 Relative Absolute addressing. addressing. Supervisor S 20 Supervisor mode. User mode. Trace T 10 Trace enabled. Trace disabled. Negative N 08 Result is Non negative. negative. Zero Z 04 Result is zero. Non zero. Overflow V 02 Overflow resulted. No overflow. Carry C 01 Carry from MSB. No carry.
USER MEMORY AREA
THE 32 BIT NURVE WORD
ADDRESING MODES
Mode hex Assembly Description of Addressing Mode Register 0 Rn Register n is operand. Direct Register 1 @Rn Register n is address of operand. Indirect Double 2 @@Rn Rn is address of address of Indirect operand. Indexed 3 [Rn] (Rn) + (@PC+) is address of operand. Indirect Pre 4 @+Rn Increment register n. Access @Rn. Inc Indirect Pre 5 @-Rn Decrement register n. Access @Rn. Dec Indirect Post 6 @Rn+ Access @Rn. Increment register n. Inc Indirect Post 7 @Rn- Access @Rn. Decrement register n. Dec Double Pre Inc 8 @@+Rn Increment register n. Access @@Rn. Double Pre Dec 9 @@-Rn Decrement register n. Access @@Rn. Double Post A @@Rn+ Access @@Rn. Increment register Inc n. Double Post B @@Rn- Access @@Rn. Decrement register Dec n. Indexed Pre C [+Rn] Increment register n. Access Inc [Rn]. Indexed Pre D [-Rn] Decrement register n. Access Dec [Rn]. Indexed Post E [Rn+] Access [Rn]. Increment register Inc n. Indexed Post F [Rn-] Access [Rn]. Decrement register Dec n.
PC ADDRESING MODES
Immediate #hhh @PC+ 6C Constant (number) mode follows the instruction. Absolute @#hhh @@PC+ AC Address of operand mode follows the instruction.
NO OPERAND
00000000 HALT Halt process 00000001 RISR Return from interrupt service routine
SINGLE OPERAND
000001pp CLP. IASTNZVC Clear PS Register bit(s). 000002pp SEP. IASTNZVC Set PS Register bit(s). 1Dt000zz CLR. WBH Clear (zero) the destination. 1Et000xx TST. WBH Set PS register relative to zero.
JUMP INSTRUCTIONS
20ppaaaa JC. IASTNZVC Jump if PS bit(s) clear. 21ppaaaa JC. * Jump if PS bit(s) clear (special). 22ppaaaa JS. IASTNZVC Jump if PS bit(s) set. 2200aaaa JSUB Jump SUBroutine. 23ppaaaa JS. * Jump if PS bit(s) set (special).
DOUBLE OPERAND
35t0xxzz XCH. WH Exchange Halfwords/Bytes. 36t0xxzz TAS. WH Test and Set. 37t0xxzz COM. WBH One's Complement. (Flip bits.) 38t0xxzz NEG. WBH Negate. (Two's Complement.) 39t0xxzz INC. WBH Increment. (Add 1.) 3At0xxzz DEC. WBH Decrement. (Subtract 1.) 3Bt0xxzz ADC. WBH Add Carry bit. 3Ct0xxzz SBC. WBH Subtract Carry bit. 3Dt0xxzz MOV. WBH Move. (Copy.) 3Et0xxyy CMP. WBH Compare xx-yy and set PS register. 3Fttxxzz CNV. WB WH Convert size of xx to size of BW BH HW HB yy.
TRIPLE OPERAND
4txxyyzz AND. WBH Logical bitwise AND. 5txxyyzz OR. WBH Logical bitwise OR. 6txxyyzz XOR. WBH Logical bitwise XOR (exclusive or). 7txxyyzz ROT. WBH Rotate (through the Carry bit). 8txxyyzz ASH. WBH Arithmetic Shift. 9txxyyzz ADD. WBH Add. zz <- xx + yy. Atxxyyzz SUB. WBH Subtract. zz <- xx - yy. Btxxyyzz MUL. WBH Multiply. zz <- xx * yy. Ctxxyyzz DIV. WBH Divide. zz <- xx / yy. Dtxxyyzz MOD. WBH Modulo. zz <- remainder( xx / yy )
MACROS
00000100 NOOP = CLP NO OPeration. 2000aaaa JUMP = JC Unconditional Jump. 2001aaaa JGEU = JC.C Jump > or = Unsigned 2004aaaa JNE = JC.Z Jump Not = 2005aaaa JGTU = JC.CZ Jump > Unsigned 2008aaaa JPL = JC.N Jump PLus 210Aaaaa JGE = JC.N*V Jump > or = signed 210Eaaaa JGT = JC.ZN*V Jump > signed 2201aaaa JLTU = JS.C Jump < Unsigned 2204aaaa JEQ = JS.Z Jump = 2205aaaa JLEU = JS.CZ Jump < or = Unsigned 2208aaaa JMI = JS.N Jump MInus 230Aaaaa JLT = JS.N*V Jump < signed 230Eaaaa JLE = JS.ZN*V Jump < or = signed 3D006D0C RSUB Return from SUBroutine. 3Dt0xx5D PUSH. WBH @-SP <- xx 3Dt06Dzz POP. WBH zz <- @SP+
OPCODES IN NUMERIC ORDER
.ALIGN. WBH Align LC on boundary .BLOCK. WBH Reserve storage .CODE Position LC in CODE area .DATA Position LC in HEAP area .END End of program. .INIT. WBH Initialize storage 00000000 HALT Halt process 00000001 RISR Return from interrupt routine. 00000100 NOOP = CLP No Operation. 000001pp CLP. IASTNZVC Clear PS Register bit(s). 000002pp SEP. IASTNZVC Set PS Register bit(s). 1Dt000zz CLR. WBH Clear (zero) the destination. 1Et000xx TST. WBH Set PS register relative to #0. 2000aaaa JUMP = JC Unconditional Jump. 2001aaaa JGEU = JC.C Jump > or = Unsigned 2004aaaa JNE = JC.Z Jump Not = 2005aaaa JGTU = JC.CZ Jump > Unsigned 2008aaaa JPL = JC.N Jump PLus 20ppaaaa JC. IASTNZVC Jump if PS bit(s) clear. 210Aaaaa JGE = JC.N*V Jump > or = signed 210Eaaaa JGT = JC.ZN*V Jump > signed 21ppaaaa JC. * Jump if PS bit(s) clear (special). 2200aaaa JSUB Jump SUBroutine. 2201aaaa JLTU = JS.C Jump < Unsigned 2204aaaa JEQ = JS.Z Jump = 2205aaaa JLEU = JS.CZ Jump < or = Unsigned 2208aaaa JMI = JS.N Jump MInus 22ppaaaa JS. IASTNZVC Jump if PS bit(s) set. 230Aaaaa JLT = JS.N*V Jump < signed 230Eaaaa JLE = JS.ZN*V Jump < or = signed 23ppaaaa JS. * Jump if PS bit(s) set (special). 35t0xxzz XCH. WH Exchange Halfwords/Bytes. 36t0xxzz TAS. WH Test and Set. 37t0xxzz COM. WBH Complement. 38t0xxzz NEG. WBH Negate. 39t0xxzz INC. WBH Increment. 3At0xxzz DEC. WBH Decrement. 3Bt0xxzz ADC. WBH Add carry. 3Ct0xxzz SBC. WBH Subtract carry. 3D006D0C RSUB Return from subroutine. 3Dt06Dzz POP. WBH zz <- @SP+ 3Dt0xx5D PUSH. WBH @-SP <- xx 3Dt0xxzz MOV. WBH Move (copy). 3Et0xxyy CMP. WBH Compare. xx-yy & set PS register 3Fttxxzz CNV. WBH WBH Convert size of xx to size of yy 4txxyyzz AND. WBH Logical bitwise AND. 5txxyyzz OR. WBH Logical bitwise OR. 6txxyyzz XOR. WBH Logical bitwise XOR (exculsive or). 7txxyyzz ROT. WBH Rotate. 8txxyyzz ASH. WBH Arithmetic Shift. 9txxyyzz ADD. WBH Add. zz <- xx + yy. Atxxyyzz SUB. WBH Subtract. zz <- xx - yy. Btxxyyzz MUL. WBH Multiply. zz <- xx * yy. Ctxxyyzz DIV. WBH Divide. zz <- xx / yy. Dtxxyyzz MOD. WBH Modulo. zz <- remainder (xx / yy).
OPCODES IN ALPHABETICAL ORDER
.ALIGN. WBH Align LC on boundary .BLOCK. WBH Reserve storage .CODE Position LC in CODE area .DATA Position LC in HEAP area .END End of program. .INIT. WBH Initialize storage 3Bt0xxzz ADC. WBH Add carry. 9txxyyzz ADD. WBH Add. zz <- xx + yy. 4txxyyzz AND. WBH Logical bitwise AND. 8txxyyzz ASH. WBH Arithmetic Shift. 000001pp CLP. IASTNZVC Clear PS Register bit(s). 1Dt000zz CLR. WBH Clear (zero) the destination. 3Et0xxyy CMP. WBH Compare. xx-yy & set PS register. 3Fttxxzz CNV. WBH WBH Convert size of xx to size of yy 37t0xxzz COM. WBH Complement. 3At0xxzz DEC. WBH Decrement. Ctxxyyzz DIV. WBH Divide. zz <- xx / yy. 00000000 HALT Halt process 39t0xxzz INC. WBH Increment. 21ppaaaa JC. * Jump if PS bit(s) clear (special). 20ppaaaa JC. IASTNZVC Jump if PS bit(s) clear. 2204aaaa JEQ = JS.Z Jump EQual 210Aaaaa JGE = JC.N*V Jump > or = signed 2001aaaa JGEU = JC.C Jump > or = Unsigned 210Eaaaa JGT = JC.ZN*V Jump > signed 2005aaaa JGTU = JC.CZ Jump > Unsigned 230Eaaaa JLE = JS.ZN*V Jump < or = signed 2205aaaa JLEU = JS.CZ Jump < or = Unsigned 230Aaaaa JLT = JS.N*V Jump < signed 2201aaaa JLTU = JS.C Jump < Unsigned 2208aaaa JMI = JS.N Jump MInus 2004aaaa JNE = JC.Z Jump Not = 2008aaaa JPL = JC.N Jump PLus 23ppaaaa JS. * Jump if PS bit(s) set (special). 22ppaaaa JS. IASTNZVC Jump if PS bit(s) set. 2200aaaa JSUB Jump SUBroutine. 2000aaaa JUMP = JC Unconditional Jump. Dtxxyyzz MOD. WBH Modulo. zz <- remainder (xx / yy). 3Dt0xxzz MOV. WBH Move (copy). Btxxyyzz MUL. WBH Multiply. zz <- xx * yy. 38t0xxzz NEG. WBH Negate. 00000100 NOOP = CLP No Operation. 5txxyyzz OR. WBH Logical bitwise OR. 3Dt06Dzz POP. WBH zz <- @SP+ 3Dt0xx5D PUSH. WBH @-SP <- xx 00000001 RISR Return from interrupt routine. 7txxyyzz ROT. WBH Rotate. 3D006D0C RSUB Return from subroutine. 3Ct0xxzz SBC. WBH Subtract carry. 000002pp SEP. IASTNZVC Set PS Register bit(s). Atxxyyzz SUB. WBH Subtract. zz <- xx - yy. 36t0xxzz TAS. WH Test and Set. 1Et000xx TST. WBH Set PS register relative to #0. 35t0xxzz XCH. WH Exchange Halfwords/Bytes. 6txxyyzz XOR. WBH Logical bitwise XOR (exculsive or).
HEX ASCII TABLE
MSB
LSB 0 1 2 3 4 5 6 7 0 NUL DLE SP 0 @ P ` p 1 SOH DC1 ! 1 A Q a q 2 STX DC2 " 2 B R b r 3 ETX DC3 # 3 C S c s 4 EOT DC4 $ 4 D T d t 5 ENQ NAK % 5 E U e u 6 ACK SYN & 6 F V f v 7 BEL ETB ' 7 G W g w 8 BS CAN ( 8 H X h x 9 HT EM ) 9 I Y i y A LF SUB * : J Z j z B VT ESC + ; K [ k { C FF FS , < L \ l | D CR GS - = M ] m } E SO RS . > N ^ n ~ F SI US / ? O _ o DEL
eg: B = 42 hex
TABLE OF CONTENTS
NOTATION 1
REGISTERS 1
PROCESSOR STATUS (PS) REGISTER BITS 1
USER MEMORY AREA 2
THE 32 BIT NURVE WORD 2
INSTRUCTION FORMATS 2
ADDRESSING MODES 2
PC ADDRESSING MODES 2
INSTRUCTION SET 3
NO OPERAND 3
SINGLE OPERAND 3
DOUBLE OPERAND 3
TRIPLE OPERAND 3
MACROS 3
OPCODES IN NUMERIC ORDER 4
OPCODES IN ALPHABETICAL ORDER 5
HEX ASCII TABLE 6
TABLE OF CONTENTS 6