I reviewed the design of the Motorola 68000. I reviewed the Instruction Set of the 68000, MIPS, and Intel's x86.
Motorola's impressed me the most.
Machine instructions are a group of bytes divided into bit-fields.
The first few bits are the OpCode. The others bitfields are parameters to the Opcode.
The parameters indicate Opcode options, registers touse and memory access modes.
RISC vs. CISC
Reduced Instruction Set
Complex Instruction Set
I didn't get THAT much into MIPS (which is risc), But MIPS was refreshing to review.
The grammar of asembly Language doesn't neccsarily mirror the structure of the Opcodes.
Different Asm Nmeumonics can be the same Opcode, but with different parameters.
Increasing options, increases opcode size.
It's all about trade-offs.
That's why Intel has Opcodes which only use certain registers. It makes the Opcode simplier.
I good idea is: common operations should have small opcodes.
An idea central to CISC is: common groups of operations should be contained in ONE opcode.
I started designing the CPU's register Set and how data can be moved into the registers and amongst them.
It made me learn about addressing modes. All memory addressing is done by calculating an Effective Address.
So the question is: Which registers contain the values needed to calculate the Effective Address.
Also, Should any of those values be multipied by 2, 4, or 8?
Why multiply an index value? Lets say a register holds the value 12, reffering to the 13th item (0 based numbering) in an array.
Those items can be 1, 2, 4, or 8 bytes in size. So multiplying the index-value by the item's size, will give you a pointer to that item.
The tricky part is, desinging that functionality into the opcode.
Certain Addressing modes need more parameter information than others. So when an opcode supports multiple addressing modes, that makes the Opcode variable in size.
Unless you make the opcode suffieciently large to hold the largest case scenario.
Designign an Instruction Set is like MAKING a jigsaw puzzle.
Initially the V-machine had separate 64KB data, stack, user program, and supervisor program space. These are not segments, but separate ram banks. The Memory Spaces were 64KB each, this simplified programming the emulator in DOS.
I had separate Memory Spaces for Stack, code and Data for simplicity of Opcode.
The thing is, I still had to add an extra field into the opcode,
because specifing which Memory Space to access has simular overhead to Specifing a Segment.
So that increased the opcode size... BUT, I later lerned that Segment Selection could be incorporated without Segment registers.
I just need to use a few high-order BITS from an Adressing register. I learned that in an OLD compter Science book.
Old Model's specs:
4 32-bit Data registers.
4 16-bit Address registers.
Flags register
3 16-bit Program Counters
16-bit User-Mode Segment-Control registers:
PB - Program Base
PS - Program Size
DB - Data Base
BS - Data Size
SB - Stack Size
SS - Stack Size
I wanted to multitask, but intel's protected mode was complex, so i gave it a whack.
3 Program Counters
PC3 for User mode,
PC2 for Supervisor Mode,
PC1 for Interupts.
The Supervisor mode had absolute memory access.
User-mode saw a virtual space, whose size and actual place in memory was set by Supervisor-Mode software via Base (PB)
and Size (PB)registers.
Supervisor Mode Software (the Operating System) would load a program into ram,
then set PC3 base and size registers (PB & PS)to the location and size of the program.
Set Data Base, Data Size, Stack Base and Stack Size registers to the correct values. PC3 would be set to 0, registers are then saved and
finaly execution is SWITCHed to PC3 for (n is any word sized number) n instructions. After n instructions,
control reterns to PC2.
PC3 can be interrupted, if so, control goes to PC1 (at the correct Interrupt Vectored Location) and interrupt-handler software
later decides if control should return to PC3 (application)or PC2(OS). Either way, Interrupt software would call an OS scheduluer (to request a service) or deal with the task itself, quickly.
I had yet to work out the details of task switching, which i believed, at the time, to be a horrible obsticle.
One draw back, was that a program couldn't call or jump to any code not in it's virtual space. This architecture puts alot of deoendance os the OS. Which is fine,
cause then the OS programmer can make things as simle or complex as he wants on a simple architecture.
This cpu model would be good for a micro-kernel,cause each litle task doesn't know where anything else is, and depends on the os to direct it's call.
I winded-up swithing to the Intel approach to task switching and memory protection (a simplified version though).
Intel has every a Task State Segment for each task. You can have 100's of tasks and whenever one gets inerrupted, or calls another, all it's State information goes into the TSS. It' great for Big Systems.
I think my original cpu would be good for a cell phone (but what do i know about that crap).
I might go back to the old task-switching model.
Here a diagram of the old model:
This is the new model
I have made a NEW register set.
8 32-bit Data Registers D0-D7
8 32-bit Address Registers A0-A7
Flags Register
6 16-bit Segment Registers
1 32-bit Segment Descriptor Table Register (holds address of Segment Descriptor Table)
1 32-bit IVT Register (holds address of Interrupt Vector Table)
Will contain hidden registers used for internal stuff that will be visible with the debugger.
Stuff like Segment starting address and size, or Location of Task State Segment.
I've also completed the Opcodes:
move / add /sub / mul / div / and / or / xor / shift (thru carry) / rotate / bit scan / bit test / cannot use memory operands
load / store / cmp / call / jump / dec / inc / set/ clear / can use memory operands
there is a Conditional Instruction, which can precede any instruction. It can be used after a COMPARE. If the conditions of the Conditional Instruction are met, the next instruction will execute, else, it'll be passd over.
Exp: NA DEC.d D1
means: DEC if Not Above, target is the register D1, all 32 bits.
MAYBE list:
REPEAT UNTIL
MOV MEM to MEM (or something like intel MOVS)
Assmebler syntax is like the 68000's in that operand size is spicified by appending it to the opcode like so:
mov.b d1, d0             ; this moves the byte (bits 0-7)in d0, into d1. does not affect bits 8-31
loadu.w d1, s0:a1             ; loads a word from memory. uses value in a1 as pointer, value in s0 selects the segment (this is NOT seg:ofs addressing!). clears bits 16-31.
loads.w d1, s0:a1             ; same as above, but extends the sign of the word through bits 16-31.
Size can Byte, Word, or Doubleword.
Adressing with load or store can be (Rx can be D0-D7 - A0-A7):
Rx (x scale) ( + immediate displacement)
Rx + Rx (x scale) ( + immediate displacement)
[Immediate]
LOAD and MOV are different opcodes. LOADsigned and LOADunsigned are the same opcodes, but with different parameters.