With my emulator going strong in its development and me already thinking about the Control Logic and how I want to deal with it… I think that I need another list of operations
Which will have a few modifications and additions that come with another set of types of memory access/addressing. Such as a difference between local and global addressing. Local Addressing can only access the local 64kWords sector of memory, while the global memory can access the entirety of the memory.
Legend
Rd | Destination Register |
Rs, Rs2 | Source Registers |
PR | Program Counter |
C | Constant Number |
Addr 16 | Memory Address (16 bit) |
Addr24 | Memory Address (24 bit) |
ST | Stack Pointer |
F | Flag bit |
MSB | Most Significant Bit |
LSB | Least Significent Bit |
OpCode Encoding
I will also add a list of opcode designs
MSB | LSB | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Word 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 | |
Word 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 | |
Word 0 | Opcode | Destination Register | Source Register |
MSB | LSB | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Word 0 | Opcode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |||||||
Word 1 | Address Low Word |
MSB | LSB | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Word 0 | Opcode | Register/Flag | 0 | 0 | 0 | 0 | ||||||||||
Word 1 | Address Low Word |
MSB | LSB | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Word 0 | Opcode | Destination Register | Source Register | |||||||||||||
Word 1 | Address Low Word |
MSB | LSB | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Word 0 | Opcode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |||||||
Word 1 | Address High Word | |||||||||||||||
Word 2 | Address Low Word |
MSB | LSB | |||||||||||||||
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Word 0 | Opcode | Register/Flag | 0 | 0 | 0 | 0 | ||||||||||
Word 1 | Address High Word | |||||||||||||||
Word 2 | Address Low Word |
Data Transfer Operations (8 Operations)
Command | Mnemonic | Operation | Opcode | OpType | Affected Flags |
---|---|---|---|---|---|
Load Direct Local | ldl Rd, Addr 16 | Rd <= Memory[Addr16] | 0b00001000/0x0 8 | 5 | None |
Load Direct Global | ldg Rd, Addr24 | Rd <= Memory[Addr24] | 0b00001001/0x0 9 | 8 | None |
Load Indirect | ldi Rd | Rd <= Memory[Z] | 0b00001010/0x0 A | 2 | None |
Load Constant | ldc Rd, C | Rd <= C (PC+1) | 0b00001100/0x0 C | 5 | None |
Store Direct Local | stl Rs, Addr 16 | Memory[Addr16] <= Rs | 0b00010000/0x10 | 2 | None |
Load Direct Global | stg Rs, Adr24 | Memory[Addr24] <= Rs | 0b00010001/0x11 | 8 | None |
Store Indirect | sti Rs | Memory[Z] <= Rs | 0b00010010/0x12 | 2 | None |
Move | mv Rd, Rs | Rd <= Rs | 0b00100000/0x20 | 3 | None |
The Load Constant is the new operation, meant to directly load any 16 bit constant into a register, allowing to get around any Z register shenanigans.
Arythmic Logical Operations (13 Operations)
Command | Mnemonic | Operation | Opcode | OpType | Affected Flags |
---|---|---|---|---|---|
Add | add Rd, Rs | Rd <= Rd + Rs | 0b01000101/0x45 | 3 | C, QC, HC, TC, Z, N, O |
Increment | inc Rd | Rd <= Rd + 1 | 0b01010101/0x55 | 2 | C, QC, HC, TC, Z, N, O |
Substract | sub Rd, Rs | Rd <= Rd – Rs | 0b01100101/0x65 | 3 | C, QC, HC, TC, Z, N, O |
Decrement | dec Rd | Rd <= Rd – 1 | 0b01110101/0x75 | 2 | C, QC, HC, TC, Z, N, O |
Bitwise AND | and Rd, Rs | Rd <= Rd & Rs | 0b01000010/0x42 | 3 | Z |
Bitwise OR | or Rd, Rs | Rd <= Rd | Rs | 0b01000011/0x43 | 3 | Z |
Bitwise XOR | xor Rd, Rs | Rd <= Rd ^ Rs | 0b01000001/0x41 | 3 | Z |
Bitwise NOT | not Rd | Rd <= ~Rd | 0b01000000/0x40 | 2 | Z |
Logical Shift Left | lsl Rd | Rd <= Rd << 1 LSB <= Carry Carry <= MSB | 0b01000110/0x46 | 2 | C, Z |
Logical Shift Right | lsr Rd | Rd <= Rd >> 1 MSB <= Carry Carry <= LSB | 0b01000111/0x47 | 2 | C, Z |
Logical Rotate Left | lrl Rd | Rd <= Rd << 1 LSB <= MSB | 0b01010110/0x56 | 2 | None |
Logical Rotate Right | lrr Rd | Rd <= Rd >> 1 MSB <= LSB | 0b01010111/0x57 | 2 | None |
Logical Switch Byte | lsb Rd | Rd[Low] <= Rd[High] Rd[High] <= Rd[Low] | 0b01001000/0x48 | 2 | None |
The Opcodes for the Arythmic Logic Operations look a bit strange, but that is mostly because I want to push the lowest three bits of the Opcode directly into the ALU and the other three bits can then be used to distinguish between the other operations that are similar.
Like Addition, Sustraction making use of the same logic circuits, for example. I also just realised that I can use two of those bits to directly push into the ALU to set the type of ‘addition’. Bit 5 for indicating whether or not the operation is a substractive operation , while Bit 4 indicated of the operation is an increment/decrement.
The same is true for the Shift and Rotate operations, as they make use of the same logic, with the only difference being whether the shift happens through the carry bit or not.
The Logic Switch Byte is meant to switch the two bytes of the 16 bit words used by the DWMC-16 quickly, an operation useful if a program needs to push/pul data from an 8 bit IO device.
Conditional Branch Instructions (10 Operations)
Command | Mnemonic | Operation | Opcode | OpType | Affected Flags |
---|---|---|---|---|---|
Branch local if Flag | blf F, Addr 16 | if (F == 0): PCL <= Addr16 | 0b10000000/0x80 | 4 | None |
Branch if not Flag | bnf F, Addr 16 | if (F != 0): PCL <= Addr16 | 0b10000001/0x81 | 4 | None |
Branch if Bit | bb B, Addr 16 | if (B == 0): PC <= Addr16 | 0b10000010/0x8 2 | 4 | None |
Branch if not Bit | bnb B, Addr 16 | if (B !=0): PC <= Addr16 | 0b10000011/0x8 3 | 4 | None |
Branch if Zero, Decrement | bzd Rd, Addr 16 | if (Rd == 0): PCL <= Addr16 else Rd <= Rd – 1 | 0b10000100/0x8 4 | 4 | C, Z, N |
Branch if not Zero, Decrement | bnzd Rd, Addr 16 | if (Rd != 0): PCL <= Addr16 else Rd <= Rd – 1 | 0b10000101/0x85 | 4 | C, Z, N |
Branch if Registers Equal | breq Rs, Rs2, Addr 16 | if (Rs == Rs2): PCL <= Addr16 | 0b10000110/0x86 | 5 | C, Z, N |
Branch if Registers not Equal | brne Rs, Rs2, Addr 16 | if (Rs != Rs2): PCL <= Addr16 | 0b10000111/0x8 7 | 5 | C, Z, N |
Branch if greater then | bgt Rs, Rs2, Addr 16 | if (Rs > Rs2): PCL <= Addr16 | 0b10001000/0x8 8 | 5 | C, Z, N |
Branch if greater then or equal | bge Rs, Rs2, Addr 16 | if (Rs >= Rs2): PCL <= Addr16 | 0b10001001/0x89 | 5 | C, Z, N |
Even with the new global addressing, I believe that the vast majority (>99%) of branches happen locally, so for the moment, I do not see a need for global branching.
Also added the new Brach if (not) Bit operations for Test Register R08.
Other Operations (12 Operations)
Command | Mnemonic | Operation | Opcode | OpType | Affected Flags |
---|---|---|---|---|---|
Jump Local Direct | jpl Addr 16 | PCL <= Addr16 | 0b11000000/0xC0 | 4 | None |
Jump Global Direct | jpg Addr24 | PC <= Addr24 | 0b11000001/0xC 1 | 7 | None |
Jump Local Indirect | jpli | PCL <= Z | 0b11000010/0xC 2 | 1 | None |
Jump to Local Subroutine | jpls Addr16 | ST <= PC + 1; PCL <= Addr16 | 0b11000100/0xC 4 | 4 | None |
Jump to Global Subroutine | jpgs Addr24 | ST <= PC + 1: PC <= Addr24 | 0b11000101/0XC5 | 6 | None |
Return | ret | PC <= ST | 0b11001000/0xC 8 | 1 | None |
Return from Interrupt | reti | PC <= ST (see post) | 0b11001001/0xC 9 | 1 | Any |
Push to Stack | push Rs | ST <= Rs | 0b11010000/0x D0 | 2 | None, Any |
Pop from Stack | pop Rd | Rd <= ST | 0b11010001/0x D1 | 2 | None, Any |
Set Flag | sf F | F <= 1 | 0b11100000/0x E0 | 2 | Any |
Reset Flag | rf F | F <= 0 | 0b11100001/0x E1 | 2 | Any |
Set Bit | sb B | R08[B] <= 1 | 0b11100010/0xE2 | 2 | None |
Reset Bit | rb B | R08[B] <= 0 | 0b11100011/0xE 3 | 2 | None |
No operation | nop | No operation | 0b11111111/0xFF | 1 | None |
The Return from Interrupt operation does what it says on the tin, and was discussed in this post.
I’ve also decided to have the hardware of Test Register R08 and the Flag Register to be identical, which will allow me to set/reset single bits in the Test Register without need to use an OR operation and a mask.
Conclusion
With that, the number of instructions raises to 45 operations again.