And, once again, I will have to thank someone on the internet for giving me some constructive criticism, which made me do a little light reading and some thinking.
Input/Output & Flags
As a little bit of a side project, I am currently building a MiniMax8085 single board computer, developed by Sergey Kiselev, since I do have a pair of 8085 around, having ‘liberated’ them over twenty years ago at trade school… >_>
Part of it is to have something to play around with on that old CPU, while another part thinks it’s a fun idea to get back some experience working with assembler and such. A bit unexpected was rediscovering something that would make IO on the DWMC-16 perhaps a little easier.
And that would be to, technically, split off the IO space from the normal memory space by using an $ I\overline{M} $ control line. Essentially, $ I\overline{M} $ would be low when accessing Memory and high when accessing IO.
To control $ I\overline{M} $, my idea is to simply add a flag to the Status Flag Register. That way, any IO access only needs to set the IO Flag and then do a load/store operation as if accessing memory. Of course, it would need to reset the IO Flag after the IO access has finished.
It also makes memory access and control a tiny little bit easier, at least when it comes to the circuity. Not losing out on almost 1024 bytes of OS/Monitor memory for one.
LSB | MSB | ||||||||||||||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Z | N.A. | C | QC | HC | TC | N.A. | EQ | N | O | IO | N.A. | IE | I0 | I1 | I2 |
0x000000 – 0x000001 | Reset Vector |
0x000002 – 0x000003 | Interrupt Vector 1 |
0x000004 – 0x000005 | Interrupt Vector 2 |
0x000006 – 0x000007 | Interrupt Vector 3 |
0x000008 – 0x000009 | Interrupt Vector 4 |
0x00000A – 0x00000B | Interrupt Vector 5 |
0x00000C – 0x00000D | Interrupt Vector 6 |
0x00000E – 0x00000F | Interrupt Vector 7 |
0x000010 – 0x01FFFF | Monitor/OS |
0x020000 – 0xFFFFFF | Main RAM |
Concerning the revised memory layout, I am currently thinking of putting the Reset and Interrupt vectors into the CPU itself, based on the same register design I want to use for all the other registers of the CPU.
Memory
Now for the constructive criticism I’ve gotten and that made me think.
Other thing I realised after poking at some sketch code generation is that I’d end up wanting to do small mode usually – so treating the high bit of Z, PC and SP as segment registers.
Rather than working on 24bit addresses all the time you just work on the 16bits you can handle fast and the other registers that provide the upper bits remain constant (on 8086 it’s a bit more complex as the segment is not upper bits but an upper 16bits of 20bits added to the 16bit address to make a 20bit address).
So a C compiler for example would never reload ZH but just keep pointers 16bit and load them into ZL, avoiding all the slow 32bit maths. Likewise you’d call/return without touching PCH ideally so that you can multitask nicely
PenguinOfEvil, Usagi Electric Discord
I’ve done a light bit of reading up on segment registers, and I have to say… I like that idea. I am reasonably sure that most of the jumping and branching is done within any assembler/machine code of the DWMC-16 will happen locally. Like for example within loops, which will only return to the beginning of the loop and the like.
So logically, I would not need to load the high byte of the address, if the jump target is within the same 64k of memory as the address of the jump instruction itself. And if it is not, the assembler/compiler should be able to ‘merely’ load the high byte of the new segment.
In terms of the machine/op code itself, it would reduce memory usage itself, by cutting the size of jump/branch operation from three to two words (6 to 4 bytes), which in turn makes reading the address faster, which in turn might make code execution faster. But that is for later, when I deal with the control logic…
So with a 24 bit address bus, I would have 256 segments of memory available, allowing some limited ability to protect code/data of one process from other processes. The stack could, for example, reside within one of those memory segments.
Another thing that came to mind is that it would allow me to split the FLASH/ROM of the Monitor/OS into two. Perhaps using the lower segment to contain the monitor program and low level system calls and the like, while the upper segment contains the more complex parts of the OS.
Instruction Thoughts
Another thing that PenguinOfEvil notes is that some of the instructions I set up last time, might be easily replaced by other operations, to reduce the instruction count.
While they are correct, this is my first design of a CPU, and I want to follow the KISS principle. At least because I’m likely the only one to write software for it for a while, unless someone wants to do it later. And for that, I need to work with the assembler and likely opcodes on paper first.
And for that, I want to deal more easily with operations and their mnemonics. Later on, when I do have a working assembler, I can always modify it to turn some operations into macros and remove them from the microcode in the control logic.
On the other hand, they did point out some operations that I did cut out, since they could just be done by another operation.
But, in the end, I’m not aiming for a Minimal Instruction Set, which is defined by at most 32 operations, but a reduced instruction set.
Initial OpCode Design
And finally, for this entry in my quest to possibly build the DWMC-16, my first thoughts on OpCodes.
For one, I want to be able to keep the opcodes down to one 1 bit word for most operations, and with the segmentation of memory, I can reduce any operation that needs an address to two words.
So far, I can split the design for the opcodes down to six different types.
- Opcodes without any register (e.g.
nop
) - Opcodes with one register/flag (e.g.
not Rd
orsf F
) - Opcodes with two registers (e.g.
and Rd, Rs
) - Opcodes with address (e.g.
jmp addr
) - Opcodes with one register/flag and address (e.g.
bzd Rd, Addr
orbz F, Addr
) - Opcodes with two registers and address (e.g.
breq Rs, Rs2, Addr
)
The first three types are one word long, while the other two are two words in size.
MSB | LSB | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Opcode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
MSB | LSB | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Opcode | Register/Flag | 0 | 0 | 0 | 0 |
MSB | LSB | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Opcode | Destination Register | Source Register |
MSB | LSB | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Opcode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |||||||
Address |
MSB | LSB | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Opcode | Register/Flag | 0 | 0 | 0 | 0 | ||||||||||
Address |
MSB | LSB | ||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Opcode | Destination Register | Source Register | |||||||||||||
Address |
Another thought is that I will likely split to opcodes into four groups, designated by the MSB
and MSB-1
. In this case, ALU operations, memory operations, branch operations and ‘other’ operations.
Conclusion
Well…
Talking to others and taking in constructive criticism and of course defending what you want to do, especially to yourself, is always a good idea. It will in many cases bring you new ideas.