The cycle-by-cycle execution of 6502 instructions will look familiar as it simply reflects the regualr bus cycles of a 6502. Let's take a look at an ORA # instruction, for example, as follows:
Code:
[ORA #]
ALUin(A, DB, C); AD += 1 # Load the inputs to the ALU, increment bus address
A := ALUop(OR, NZ); IR := DB; PC := AD += 1 # ALU operation; FetchOpcode
As expected, the ALU operation is split across two microinstructions; one to load the ALU inputs and another to carry out the ALU operation. The operand is fetched in the first cycle and the next opcode in the second. Flags and registers are updated in the third cycle, following the ALU operation. Meanwhile, this ORA # operation will execute exactly the same bus cycles as any other 6502, which is exactly the result we are after.
Here is a few more addressing modes:
Code:
[ORA zpg]
ADL := DB; ADH := 0; PC += 1 # Use the operand byte as an zero-page address; increment PC
ALUin(A, DB, C); AD := PC # Load the inputs to the ALU, restore PC
A := ALUop(OR, NZ); IR := DB; PC := AD += 1 # ALU operation; FetchOpcode
[ORA abs]
ALUin(0, DB, 0); PC := AD += 1 # Read Address low-byte
ADL := ALU(ADD); ADH := DB; PC += 1 # Read Address high-byte
ALUin(A, DB, 0); AD := PC # Load the inputs to the ALU, restore PC
A := ALUop(OR, NZ); IR := DB; PC := AD += 1 # ALU operation; FetchOpcode
[ORA zpg,x]
ALUin(X, DB, 0); PC += 1 # Add X to the operand, increment PC
ADL := ALUop(ADD); ADH := 0 # Use the zero-page address; increment PC
ALUin(A, DB, C); AD := PC # Load the inputs to the ALU, restore PC
A := ALUop(OR, NZ); IR := DB; PC := AD += 1 # ALU operation; FetchOpcode
Absolute Indexed addressing needs a little gymnastics to add a cycle when a page is crossed:
Code:
[ORA abs,X]
ALUin(X, DB, 0); PC := AD += 1 # Add X to the Address low-byte
ADL := ALUop(ADD); ADH := DB; ALUin(0, DB, COUT); PC += 1 # Read address high-byte
ALUin(A, DB, C); CC:AD := PC; CS:[ADH := ALUop(ADD); REPEAT] # Adjust high-byte if necessary
A := ALUop(OR, NZ); IR := DB; PC := AD += 1; END # ALU operation; FetchOpcode
Note in the third microinstruction the "CC:AD := PC; CS:[ADH := ALU(ADD); REPEAT]" conditional execution construct. Here "AD := PC" will execute if the carry is clear, and "[ADH := ALU(ADD); REPEAT]" if it is set. The REPEAT keyword will force a pipeline stall if the carry is set. This will execute the same microinstruction again, which has the effect of adding an extra cycle if a page was crossed.
Here is a branch instruction:
Code:
ALUin(ADL+1, DB, 0); PC := AD += 1; EXIT.BTF # EXIT if the Branch Test Fails (generate a FetchOpcode)
ADL := ALUop(ADD); ALUin(PCH, SE, COUT); EXIT.CC # EXIT if the a page boundary is not crossed (generates a FetchOpcode)
ADH := ALUop(ADD) # Adjust the high-byte on a page crossing
IR := DB; PC := AD += 1; END # FetchOpcode
There are a couple of special registers being used here -- "ADL+1" is the low-byte output of the 16-bit incrementer and "SE" is the Sign-Extended value of the ALUB input in the prior cycle. The "EXIT.BTF" keyword will generate a FertchOpcode in the next cycle if the branch test fails. The EXIT.CC keyword will do the same if the ALU output carry (COUT) is clear (i.e., a page was not crossed). A FetchOpcode is specified as an all-zeroes control word, so a FetchOpcode can be generated easily by clearing the control word.
The Logisim model for the pipeline is now complete and I am happy to report that these microcode sequences have all passed their unit tests. So far so good and testing continues.
Cheers for now,
Drass