---- 0xC8 INY reg_y ---- 001900 3A84B3 3A84B3 to_t <- inc(reg_y),upd_nz 001902 008301 008301 next 001904 11B403 11B403 reg_y <- acc_t ---- 0x69 ADC imm ---- 000D20 D1AB80 D1AB80 to_dpl <- (pc++),irq_to_f 000D22 5D46F0 008301 F:[to_a <- adc(acc_a, dpl),upd_nzc] T:[next] 000D24 000401 5D46F0 F:[interrupt] T:[to_a <- adc(acc_a, dpl),upd_nzc] 000D26 110400 110400 int2 ---- 0x65 ADC zp ---- 000CA0 D1AB80 D1AB80 to_dpl <- (pc++),irq_to_f 000CA2 5DC5F0 008301 F:[to_a <- adc(acc_a, (0|dpl)),upd_nzc] T:[next] 000CA4 000401 5DC5F0 F:[interrupt] T:[to_a <- adc(acc_a, (0|dpl)),upd_nzc] 000CA6 110400 110400 int2 ---- 0x75 ADC zpx ---- 000EA0 D18B80 D18B80 to_t <- (pc++),irq_to_f 000EA2 1DA402 1DA402 to_dpl <- add(acc_t, reg_x) ---- end of ea calculation --- 000EA4 5DC5F0 008301 F:[to_a <- adc(acc_a, (0|dpl)),upd_nzc] T:[next] 000EA6 000401 5DC5F0 F:[interrupt] T:[to_a <- adc(acc_a, (0|dpl)),upd_nzc] 000EA8 110400 110400 int2
You can easily generate the full microcode:
- Go to: this W3Schools page.
- Clear the text in the left window and paste the microcode-generator code there.
- Press Run
- The microcode will appear within a few seconds.
Some remarks about the notation:
- The first hex number on a line is the address in the microcode ROM
- The next two hex numbers are the microcode. These numbers are the same, except when conditional execution is used
- When there is no interrupt, only the T:[ ... ] sections are executed.
- When there is an interrupt, the F:[ ... ] sections are executed.
- 'next' is an indication that the next opcode will be loaded. Due to the pipeline effect, one extra instruction will be executed after 'next'.
- Depending on its role in the instruction, register A will be shown as 'to_a' or 'acc_a'. Same for register T.
It is easy to count the number of cycles for each opcode. Just count the lines up to the first instruction that contains 'next', then add one cycle for the instruction after 'next'. So the shown 'ADC zp' opcode takes 3 cycles.
The operation of some instructions will probably still be unclear to you. In that case, check the microcode script that contains a lot of comments about the used instructions.
In the microcode generator, there is a central role for the function 'ins6( a, b, c, d, e, f )'. The name 'ins6' stands for 'instruction with 6 components'. The six components are:
- a. Condition. An instruction to move a condition to the F flag, like c_to_f, z_to_f, or irq_to_f.
- b. Destination. Data destination, like to_t, to_a, to_dpl.
- c. ALU operation, like ld, adc, and, rol.
- d. Source 1, this can only be acc_a or acc_t. For single-operand instructions, source 1 is unused. For writing to memory it decides if A or T gets written.
- e. Source 2, this will in most cases be a memory location, like 'dir' (zpage location), 'ext' (full 64k range address in dph|dpl), 'pc_pp' ( (pc++) ), or a hardware register (dph, dpl, pch, pcl), or a register location in memory (like reg_s, reg_x), or a small constant lit0, lit2, lit8, lit32 for the values 0, 2, 8, 32. For writing to memory, source 2 is the destination address.
- f. Update flags. It is indicated as upd_nz, upd_c, upd_nzc. Any combination of N, Z, C flags is allowed.
The ins6 simply combines the control-wire values of its arguments, so it would not matter in which order the components are placed. But the comment-printing function 'pr_print' depends on the order of this components.
It is tempting to continue with writing the Z80 micro-instructions, but I will now first make a simulator to check if everything works as intended.