As I am working through the microcode (and the microcode assembler) this finally required me to sketch a block diagram of the entire machine and the CPUs internal datapaths. The microcode is not ready yet (I have completed about 20 most typical instructions), neither is the assembler (I am adding features to the lexer and parser as I write microcode for new instructions). I hope to share it in a week or two, before I start working on the simulator. Meanwhile, I am posting the block diagram in a hope it is final and will not change significantly. Most instructions are computable in a reasonable number of clock cycles, so I am assuming it is more or less complete. The diagram depicts all elements relevant for microcoding, with the level of details sufficient to track data flow for each of the instructions.
Solid lines were used to denote 2-state connections, dotted lines represent 3-state lines. Here are the details of components:
There are 6 programmer visible registers and 4 registers used internally by the CPU.
- A is a 16-bit accumulator register. In hardware terms it is no different than other data registers but I am assuming most arithmetic and logic operations will use A as the left operand, and will store the operation result in A. I could replicate the same operations also for X and Y but I have limited instruction space, as I am using one-byte opcodes (so only 256 instruction slots are available).
- X and Y are general use 16-bit data registers. They are used as right operands of arithmetic and logic ops, and may be source and destination of most load and store operations.
- SP is a 16-bit stack pointer. It is actually a 16-bit counter and may be incremented and decremented without using the ALU.
- DP is a 16-bit general use data pointer (not a counter).
- PC is a 16-bit program counter. It is also a counter register which may be incremented without the ALU. The PC is not visible to the programmer.
- MDR and MAR are 16-bit scratch registers for data and address, respectively. Additionally, MAR is a counter so it can be incremented without using the ALU, similarly to SP (although it cannot be decremented). Neither of MDR, MAR is directly visible to the programmer.
- IR is an 8-bit instruction register. It is actually part of the control subsystem used to store a currently processed instruction’s opcode. It is not visible to the programmer.
- MSW is an 8-bit machine status word used to store ALU flags, CPU mode (supervisor/user) and the interrupts enable (IE) flag. It is not visible to the programmer and it needs to be automatically pushed onto stack when entering interrupt/fault handlers and restored upon return (that’s why it is connected to the data bus).
The machine features four buses.
- LBUS and RBUS are left and right ALU input buses, both 16-bit wide. LBUS may be driven by one of MDR, A, X or Y. RBUS may be driven by either MDR or the value on the address bus (which is driven by one of MAR, SP, DP or PC).
- ALUBUS is a 16-bit bus for the ALU’s output value. ALUBUS contents may be loaded to any of MDR, A, X, Y or ALUBUS may drive the memory DATABUS via the Data Bus Interface (see below). The ALUBUS itself may be driven by the ALU (16-bits) or by the DATABUS via the Data Bus Interface (low or high byte of the ALU bus is driven).
- DATABUS is an 8-bit memory data bus. It practically means that we are accessing memory using 8-bit words (and every 16-bit load/store requires at least two clock cycles). Similarly, loading 16-bit registers connected to DATABUS (any of MAR, SP, DP or PC) requires two clock cycles – one for the low byte and one for the high byte. I was thinking for a while whether to make the memory word 16-bit wide, but I didn’t like the idea of 2-byte word aligned addressing. On the other hand, a 16-bit memory with no word alignment would be complex to build as it would require implicit address incrementing in case of unaligned memory accesses. Different bus width is one of the reasons for the isolation of ALUBUS and DATABUS. This physical isolation has a very nice feature, though – it allows me to fetch the next instruction while the ALU still performs an operation (as the IR is bound directly to the 8-bit DATABUS, and address registers – including the PC are on the ADDRESSBUS and do not conflict with data registers or the ALUBUS).
- ADDRESSBUS is a 16-bit logical address bus driven by one of MAR, SP, DP or PC and feeding the address decoding and paging logic block (which translates the logical address to a physical address which is used directly to access memories). Actually, the logical address is 17-bit as there is a dedicated control signal indicating whether we are addressing code or data segment.
There are also two bus interfaces allowing to join buses when necessary.
- Data Bus Interface is a bidirectional interface used to transfer data between ALUBUS and DATABUS. When ALUBUS is driven with the contents of DATABUS, we may drive the high word or the low word (sign extended to ease indexed addressing in some cases). In the opposite direction, we also choose whether to drive the DATABUS with low or high word of the ALUBUS. By default, the Data Bus Interface isolates both buses (Hi-Z state in both directions).
- Address Bus Interface is an unidirectional interface used to drive the right ALU input bus (the RBUS) with the address (from one of address registers). It is used primarily to implement addressing modes with offsets, when an arbitrary value needs to be added to the contents of one of the address registers. In such case the RBUS is driven with the address value, which is then modified by the ALU and transferred from the ALUBUS to the DATABUS (via bidirectional Data Bus Interface) and finally to the MAR (address scratch register, as the contents of the original register should not be modified in such cases).
The memory subsystem is presented now as a high-level address decoding and paging block which will be responsible for translating a 16-bit logic address (+1 bit of code/data segment selector) to a physical address pointing to physical memory (RAM/ROM) or devices. This functional block will be also responsible for paging (by means of a dedicated page table, accessed and manipulated only by dedicated instructions working in supervisor mode). I still need to think about this.