The Trillium Architecture — obsolete
Part of
http://homepage.cs.uiowa.edu/~dwjones/ternary/
|
The Trillium instruction set is designed for a small ternary computer with a 9-trit word and a 19,682 word address space. This architecture is comparable to some of the smaller 16-bit minicomputers of the 1960s and 1970s, and it might be suitable for a microcontroller or programmable interface processor in modern terms.
Warning: The material presented here is very preliminary. First posting on the web, Nov. 21, 2017.
The basic 9-trit word has the following format:
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
value |
This value can be interpreted as an unsigned ternary value:
0000000003 | = | 00027 | = | 010 |
2222222223 | = | ZZZ27 | = | +19,68310 |
Alternatively, the value can be interpreted as a 3's-complement signed ternary value:
1111111123 | = | DDE27 | = | –9,84110 |
2222222223 | = | ZZZ27 | = | –110 |
0000000003 | = | 00027 | = | 010 |
1111111113 | = | DDD27 | = | +9,84110 |
In the event that balanced ternary is needed, to convert a value to from 3's complement form to balanced form, just add the bias, 1111111113 (DDD27 or +9,841) and reinterpret the trit values 0 as –, 1 as 0, and 2 as +. To convert balanced ternary to 3's complement, reverse the process.
One word may also be used to hold a 9-bit ternary coded binary (TCB) number. Each trit has a value of either 0 or 1, corresponding to the value of one binary bit. There are fast algorithms for conversion from TCB to ternary, so generally, conversion should be done early.
One word can hold one machine instruction (or at least the first word of a multi-word instruction), formatted as follows:
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
op | a | src | dst |
The instruction word contains the following fields:
This instruction format is inspired by the formats of the DEC PDP-11. The Motorola 68000 was also inspired by the PDP-11, but followed it more slavishly. Here, the change of number base and the somewhat reduced information capacity of a word lead to a much looser adaptation.
In the Trillium assembly language, instructions are written as
label: opcode src,dst ; comment opcode+ src,dst opcode- src,dst
The suffix + and - on the op field indicates the setting of the a field. The default if neither is used is simple memory addressing (neither autoincrement nor autodecrement). Labels are always followed by a colon, and comments, if present, are always preceeded by a semicolon, as with the classic MACRO-11 assembly language for the PDP-11.
The a field combined with the first trit of the src and dst fields determines the addressing modes used by the instruction. The final two trits of the src and dst fields usually specify the registers used by the instruction.
02 | 01 | 00 |
m | r |
The three addressing modes specified by the m field are:
Note that, if src.m = 2 and dst.m = 2, they will both use the same addressing mode. It is not possible to have a source operand addressed with autoincrement mode while the destination operand is addressed using autodecrement mode.
Note that, if src.m = 2 and dst.m = 2, and src.r = dst.r, that is, if the same register is used as an address register in both the source and destination fields, autoincrement addressing will increment the register twice, once after using the register for the source address and a second time after using the register for the destination address. Similarly, autodecrement addressing will decrement the register twice, once before taking the source address, and once after taking the destination address.
Because the program counter is part of the register set, autoincrement addressing using the program counter allows an immediate operands to be stored in the instruction stream immediately after the instruction word. This should only be used in the src field.
Because stack pointers are held in registers, autoincrement and autodecrement addressing can be used to push and pop operands.
If a register is used as a stack pointer with the stack growing up toward increasing addresses, then immediate to auto-increment addressing can be used to push immediate operands onto the stack. This slightly biases the architecture toward upward growing stacks.
There is no indexed addressing. For indexed addressing, first compute the address in a register and then use that register as a memory address. Because most objects are small, index displacements will frequently be between 1 and 8. As a result, a common instruction sequence for indexing will be to copy the base register (and its tag) and then add a constant-mode displacement to the copy in order to compute the indexed memory address.
In the Trillium assembly language, the source and destination addressing modes are specified as follows:
label: opcode =0,=8 ; constants 0 and 8 (although the 8 may be ignored) opcode r1,r2 ; registers 1 and 2 as source and destination opcode *r3,*r4 ; registers 3 and 4 as operand pointers opcode+ *pc,= ; auto-increment pc (immediate) to discard
In addition, assembers should take constants that are over 8 and generate immediate operands when possible. In the following, when the assembler encounters the first instruction, it should generate the same code shown by the second sequence of two instructions.
opcode =12,r1 ; out-of-range constant 12 opcode+ *pc,r1 ; immediate 12 to register 1 .word 12
Similarly:
opcode+ =12,*r3 ; out-of-range constant 12 to auto-increment r3 opcode+ *pc,*r3 ; immediate 12 to auto-incrment r3 .word 12
There are 9 registers, of which 7 are the general-purpose operand registers r1 to r7.
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | ||
t8 | sp (r8) | |||||||||
t7 | r7 | |||||||||
t6 | r6 | |||||||||
t5 | r5 | |||||||||
t4 | r4 | |||||||||
t3 | r3 | |||||||||
t2 | r2 | |||||||||
t1 | r1 | |||||||||
t0 | pc (r0) |
The program counter, pc or r0 always points to the next word in the instruction stream. The stack pointer, sp or r8 always points to the next word in the instruction stream. Effectively, fetching an instruction is done using autoincrement addressing.
The stack pointer, sp or r8 always points to the first free word beyond the stack top, assuming that the stack grows upward, or to the topmost word on the stack, assuming that the stack grows downward. None of the user-mode instructions on this machine make any assumptions about the direction of stack growth.
The register tags t0 to t8 are only present on machines able to address more than 19,682 words of memory. These may be as simple as extra bits attached to the register when it is used for memory addressing, or as complex as segment identifiers when used with a memory management unit supporting segmenting.
Whatever their form, tags allow (at minimum) a separation of program and data space. Typically, the program counter will be tagged with the program segment while the stack pointer is tagged with the data segment. Other registers used for addressing will have values derived from either the program counter or stack pointer, and hence, will carry the appropriate tag.
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
cc |
The processor status word is divided into several fields:
02 | 01 | 00 |
s | v | c |
The condition codes correspond closely to the 4-bit condition codes used on the PDP-11 and copied by most more recent binary p rocessors. The s condition code encodes all useful values of the binary N and Z condition codes, while the v and c condition codes correspond to the V and C condition codes of the PDP-11.
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
0 0 | a | src | dst | |||||
move src,dst | ||||||||
dst ← src; set cc |
Copies the source operand to the destination, overwriting anything previously stored there. Register-to-register moves always copy the tag field along with the register. The condition codes are set to report on the result of the move:
move =0,r1 ; clear r1 move r2,= ; set the condition codes according to r2 move r3,*r4 ; store r3 where r4 points in memory
Because the program counter is a register, the move instruction also serves as a branch instruction:
move r5,pc ; branch to the location pointed to by R5 move *r5,pc ; branch indirect through the location move =dst,pc ; absolute branch to dst move+ *pc,pc ; long form of absolute branch .word dst
Because the stack pointer (if used) is a register, assuming that the stack grows upward through memory and the stack pointer always points to the free location just above the stack top, we can use move to push and pop the stack:
move+ r5,*sp ; push r5 onto the upward-growing stack move- *sp,r6 ; pop the stack into r6 move+ =0,*sp ; push zero onto the stack move+ =150,*sp ; push the constant 150 onto the stack move+ *pc,*sp ; long form of push 150 on an upward-growing stack .word 150
We can also push and pop on a downward going stack where the stack pointer always refers to the top element of the stack.
move- r5,*sp ; push r5 onto the downward-growing stack move+ *sp,r6 ; pop the stack into r6 move- =0,*sp ; push zero onto the stack ; constants above 8 must be moved to a register before pushing
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
0 1 | a | src | dst | |||||
add src,dst | ||||||||
dst ← dst + src ; set cc |
Adds the source operand to the destination operand, replacing the destination. The condition codes are set to report on the sum:
add =1,r1 ; increment r1 add r2,=5 ; set the condition codes according to r2 + 5 add r3,*r4 ; add r3 to the memory location pointed to by r4 add- *sp,r6 ; pop from an upward-growing stack and add to r6 add+ *sp,r6 ; pop from a downward-growing stack and add to r6
Because the program counter is a register, the add instruction also serves as a pc-relative branch instruction. The assembler uses the dot (.) as the symbol for the current location, so subtracting this from dst, the destination address, we get the relative address suitable for use in position independent code:
add =dst-(.+1),pc add+ *pc,pc .word dst-.
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | ||
0 2 | a | src | dst | |||||||
sub src,dst | ||||||||||
dst ← dst + (ZZZ27 – src) + 1 ; set cc |
Subtracts the source operand from the destination operand, replacing the destination. The condition codes are set to report on the difference:
Note the inversion of the c conditon code. This is because the Trillium uses 3's-complement arithmetic, where a – b is computed as a + (2222222223 – b) + 1. A carry out of this sum indicates that there was no borrow, that is, it indicates that a ≥ b as unsigned ternary numbers.
sub =1,r1 ; decrement r1 sub r2,=5 ; compare r2 with the constant 5 sub r3,*r4 ; subtract r3 from the memory location pointed to by r4 sub- *sp,r6 ; pop from an upward-growing stack and subtract from r6
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
? ? ? 1 | src | dst | ||||||
addc src,dst | ||||||||
dst ← dst + src + c ; set cc |
Adds the source operand to the destination operand, including the carry bit, replacing the destination. Unlike the regular add instruction, the only addressing mode supported for the source operand is register mode. The condition codes are set to report on the sum:
Add with carry allows high precision addition. Consider computing the 18-trit addition of r1-r2 to r3-r4, where the first register of each pair holds the least significant half:
add r1,r3 ; add less significant trits addc r2,r4 ; add most significant trits
Consider adding the 27-trit value in r1-r3 to a 27-trit value in the memory location pointed to by r4, where the least significant word comes first in memory:
add+ r1,*r4 ; add less significant trits addc+ r2,*r4 ; add middle trits addc r3,*r4 ; add most significant trits sub r4,=2 ; restore pointer
In the above, autoincrement addressing is used to access consecutive words of the operand in memory, and then the register is restored to its original value. After-the-fact restoration using an immediate subtract avoids the need to make a temporary copy of r4.
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | ||
? ? ? 2 | src | dst | ||||||||
subb src,dst | ||||||||||
dst ← dst + (ZZZ27 – src) + c ; set cc |
Subtracts the source operand from the destination operand, including the borrow information conveyed by the carry bit, replacing the destination. Unlike the regular subtract instruction, the only addressing mode supported for the source operand is register mode. The condition codes are set to report on the difference:
Subtract with borrow allows high precision subtraction. Consider computing the 18-trit subtraction of r1-r2 from r3-r4, where the first register of each pair holds the least significant half:
sub r1,r3 ; subtract less significant trits subb r2,r4 ; subtract most significant trits
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
? ? ? 0 | n | dst | ||||||
sl n,dst | ||||||||
dst ← dst <<3 n ; set cc |
Shifts the destination operand left. A shift count of 00 is interpreted as meaning 9. Autoincrement and autodecrement modes are not applicable. The condition codes are set as follows:
In the absence of overflow, dst <<3 n = dst × 3n. In this context, overflow does not merely report that the sign of the result differs from the sign of the operand, but it also reports any loss of precision. So, if dst × 3n (computed with unlimited precision) has a sign different from dst << 3n (computed in a 9-trit word), the overflow bit will be set.
sl 1,r1 ; multiply r1 by 3 sl 2,*r2 ; multiply the memory location pointed to by r2 by 9 sl 9,=0 ; set the condition codes to 000 sl 9,=1 ; set the condition codes to 011 sl 9,=2 ; set the condition codes to 012 sl 8,=1 ; set the condition codes to 100 sl 8,=2 ; set the condition codes to 210 sl 8,=4 ; set the condition codes to 111 sl 8,=5 ; set the condition codes to 211
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
? ? ? 1 | n | dst | ||||||
sru n,dst | ||||||||
dst ← dst >>3 n ; set cc |
Shifts the unsigned destination operand right. A shift count of 00 is interpreted as meaning 9. Autoincrement and autodecrement modes are not applicable. The condition codes are set as follows:
For unsigned right shifts, zeros are shifted in from the left, so the result is always positive or zero. In general, dst >>3 n = dst / 3n, with the unsigned result truncated toward zero. When the overflow bit is set, it indicates that the unsigned operand was not evenly divisible by the indicated power of three.
08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
? ? ? 2 | n | dst | ||||||
sru n,dst | ||||||||
dst ← dst >>3 n ; set cc |
Shifts the unsigned destination operand right. A shift count of 00 is interpreted as meaning 9. Autoincrement and autodecrement modes are not applicable. The condition codes are set as follows:
For signed right shifts, the value shifted in from the left depends on the sign of the operand. Zeros are shifted in for positive operands, and twos are shifted in for negative operands. For example:
In general, dst >>3 n = dst / 3n, with the signed result rounded down so that the remainder is either zero or positive. When the overflow bit is set, it indicates that the unsigned operand was not evenly divisible by the indicated power of three.
The following additional condition code tests are not typically used after comparisons: