I'm designing and building a CPU from scratch. What should I put in the instruction set to make it interesting? Present design has a 12-bit word, and 8 registers, one of which is the program counter.

Criteria:
• It has to be actually useful
• I have to be able to justify it to my partner, who wants to write Forth on this machine.
Easy stack manipulation is nice, and plenty of general-purpose registers. My biggest complaint about MIPS is its lack of push/pop, and my biggest complaint about z80 is that it doesn't have enough registers.

That being said, my friend made a language that was completely stack-based (no general-purpose registers) that was a lot of fun to program in. But you could do some crazy stuff with the stack, which made up for it. However, he was unyielding in his not letting there be a multiplication instruction, so that was pretty annoying.
12-bit word? interesting

anyways, I think one fun thing I found about assembly languages is an abundance of general-set registers, powerful math functions (at least multiply, divide, add, subtract!), tons of ways to check for conditionals, and simplicity with instructions/assembler notation like with the z80.

EDIT: and I second Merth's Push/Pop suggestion
chronomex wrote:
I'm designing and building a CPU from scratch. What should I put in the instruction set to make it interesting? Present design has a 12-bit word, and 8 registers, one of which is the program counter.

Criteria:
• It has to be actually useful
• I have to be able to justify it to my partner, who wants to write Forth on this machine.

You could have a ?: instruction, or if you wanted to do something really spiffy you could design some elliptic-curve-calculating physics/cryptography oriented instructions. Native floating point is always nice.
If it's going to run Forth, good stack manipulation instructions will be useful. Being able to use a stack pointer as an index register could be handy, and as Forth uses two stacks you could make "push" and "pop" operate on any register, not just a single fixed stack pointer.
benryves wrote:
If it's going to run Forth, good stack manipulation instructions will be useful. Being able to use a stack pointer as an index register could be handy, and as Forth uses two stacks you could make "push" and "pop" operate on any register, not just a single fixed stack pointer.

Currently my ISA doesn't have any explicit stack instructions, or even jump for that matter! To do stack operations you do a MOVE and an ADDQ; to jump (or branch) you do a MOVE (or ADD) to register #8 (PC).

Conditonal jump? Well, I've got an instruction to reduce a word to 0 or 1 (basically is-not-zero), which then you can ADD to PC.

Immediate values? Maybe not! Jump over the value and then load (PC-1). (I may change this, much as I relish this idiom, because it's unbelievably clunky to use given the rest of the instruction set.)

elfprince13 wrote:
You could have a ?: instruction, or if you wanted to do something really spiffy you could design some elliptic-curve-calculating physics/cryptography oriented instructions. Native floating point is always nice.

Seeing as the stated purpose of this processor is "to defeat Facebook", I think encryption is out of scope.
chronomex wrote:
benryves wrote:
If it's going to run Forth, good stack manipulation instructions will be useful. Being able to use a stack pointer as an index register could be handy, and as Forth uses two stacks you could make "push" and "pop" operate on any register, not just a single fixed stack pointer.

Currently my ISA doesn't have any explicit stack instructions, or even jump for that matter! To do stack operations you do a MOVE and an ADDQ; to jump (or branch) you do a MOVE (or ADD) to register #8 (PC).

Conditonal jump? Well, I've got an instruction to reduce a word to 0 or 1 (basically is-not-zero), which then you can ADD to PC.

Immediate values? Maybe not! Jump over the value and then load (PC-1). (I may change this, much as I relish this idiom, because it's unbelievably clunky to use given the rest of the instruction set.)
Sounds incredibly not fun, I thought you were trying for fun?
I once did this, but the instruction set I implemented turned out to be moderate pain to use.

Of course you will need enough instructions to make it turning-complete, that is, a general Instruction pointer register, the ability to use one or more registers as data-pointers, simple math functions (plus/minus or eventually just increase/decrease), and conditional jumps.

You may also add instructions that makes it easier for the programmer to use the CPU, instead of making simple actions over-complicated like I did with my design. What I did was to go for a more register-based design, where instead of having specialized instructions for certain purposes, you had to use special-purpose registers and simple register-move instructions to do the same. Take for example adding two numbers from memory. In order to do this, you would have to do the following:

• Move the address (immediate) of the first number into the address register.
• Read memory.
• Move the data register to first ALU-input register.
• Move the address (immediate) of the second number into the address register.
• Read memory.
• Move data register to second ALU-input register.
• Set ALU operation to "add".
• Move ALU-output register to data register.
• Move the address (immediate) of the target memory to the address register.
• Write memory.
Olav, Turingc-completeness is surprisingly easy to achieve. Just about any half decent set of instructions will have it

Anyway, I've found that general purpose registers, math, and data movement instructions tend to be the key to "fun" instruction sets. For example, push/pop could be implemented as mov reg12,@-reg12 and mov @reg12+,reg12. Similarly, having the idiv instruction from x86 will save the programmer.

Also, merth is right. That sounds like something designed in the spirit of brainf*ck...
Just to put in my 20 millibucks, I think what makes an ISA fun is orthogonality­. That is, any instruction can operate on any register (including "special" registers like the program counter and status register). It's often easier to make a smaller instruction set when they are orthogonal than by having a bunch of special instructions and special registers (I guess this is basically a RISC vs CISC comparison).

One thing that I think is cool is, instead of including an instruction for each type of operation (add, subtract, multiply, divide, etc), set aside special registers or memory locations that give the desired results when read back. For example, using special registers, r0 and r1 are the operands, r2 is the operation register, and r3 is the result register; after loading the values into r0 and r1 and the operation code into r2, reading from r3 gives the result. Since the result might not be available before the next instruction execution time, another instruction could run (one that doesn't read from r3) in the meantime before r3 is read back. This can avoid instruction pipeline stalls (if you choose to get that sophisticated with your ISA).

Orthogonality, of course, results in some nonsensical instruction opcodes, but sometimes those can be put to good use by some clever programmer!

On the other hand, as I think Qwerty.55 suggested, you could include only 8 instructions: < > [ ] + - . ,

Last, why not implement a One Instruction Set Computer (OISC) machine? As its name implies, it has only one instruction, so the opcode itself can be omitted; only the operands need to be specified. A few years ago I personally implemented the "Subtract and branch if less than or equal to zero" type in Z80 assembly. The interpreter itself was fairly small (only 60 bytes or so). See http://en.wikipedia.org/wiki/One_instruction_set_computer#Subtract_and_branch_if_less_than_or_equal_to_zero
Full orthogonality is indeed an important feature. On the 68000, d(pc) addressing modes being forbidden as destination effective address is a major annoyance in terms of performance and code size.

I have mixed feelings for special registers / memory locations, or operations with fixed input and output registers (such as many instructions of the x86 ISA). They tend to increase the number of move-type instructions, and I think that they can make the code flow harder to follow for humans.

For making the ISA fun to program in ASM or C, I could also mention multiplication, division, "many" general-purpose registers, predecremented and postincremented addressing modes, move multiple registers to/from memory, bit manipulation instructions (beyond clearing / setting / inverting, testing, having the "get highest bit" and "get lowest bit" instructions wired in is great), conditional execution of all instructions, instant shift and rotates, immediate values, etc.
This is by no means a complete list. Basically, I'm mentioning every aspect of the 68000 and ARM ISAs that I find interesting, and then some more (e.g. hardware floating-point, which most ARMs don't have)
Lionel Debroux wrote:
Full orthogonality is indeed an important feature. On the 68000, d(pc) addressing modes being forbidden as destination effective address is a major annoyance in terms of performance and code size.

SMC much? I generally don't write self-modifying code, so I haven't noticed this particular limitation of the 68k. I am annoyed by the limited addressing modes for some instructions like ADDC, though.

Even without a lot of addressing modes or many instructions, a good ISA can emulate them with pseudo-instructions. I'm thinking of the MIPS architecture here. It only has two real branch instructions: beq and bne. The other branch instructions are synthesized with slt (set if less than) and beq/bne. The reason for this is that testing for equality and branching is simpler and faster than comparing for less than, greater than, etc, and branching (all instructions on MIPS are only as fast as the slowest instruction). It doesn't have an unconditional branch either—for that a beq is used with the same register on both sides of the comparison. Similarly, MIPS has no instruction to move one register to another. It just uses the add instruction (add 0 to a register and store the result in the other register). MIPS assemblers recognize these and other pseudo-instructions and convert them to the real instructions.
PC-relative mode is useful beyond SMC, and great for mixed .rodata, .data and .text

Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

»
» All times are GMT - 5 Hours

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Advertisement