Yet another TTL 6502
Re: Yet another TTL 6502
This is how I'm going to do the Z flag generation. Using one of the left over OR gates per bit slice.
You may wonder about the propagation delay, but that's no concern because of the ripple carry adder, the ALU outputs will arrive in order, so the OR from 0 through 6 will be waiting at the last input before the ALU7 output arrives.
You may wonder about the propagation delay, but that's no concern because of the ripple carry adder, the ALU outputs will arrive in order, so the OR from 0 through 6 will be waiting at the last input before the ALU7 output arrives.
- Attachments
-
- z-flag.png (2.39 KiB) Viewed 1134 times
Re: Yet another TTL 6502
Arlet wrote:
For BCD operations, the idea is to store the output of the binary addition in A, and at the same time, store the BCD adjustment in M. An extra cycle follows where A+M is calculated and stored in A again.
Thinking about how to optimize the logic for 3 different bit patterns (0000, 0110 and 1010), I realized I could also use the ALU as a subtractor, by inverting B input and setting carry input flag. That way, I only need 0 or +6 as adjustment digits.
Another optimization is that I no longer split my 8 bit adder in two 4 bit pieces. For instance, suppose you add 16 and 16. If you just add them in binary, you get 2C, and then if you add 6 just to lower nibble you get 22, but the answer is 32. In order to fix that, I had split the adder in two 4 bit pieces, and then generate a carry from lower digit into upper digit whenever the result was bigger than 9. So 16 + 16 would first result in 3C, and then the lower nibble would be adjusted C+6=2, resulting in correct answer of 32.
Producting the decimal carry in the middle of the adder adds some gate delay in the ripple chain, plus some extra chaos in the middle of a regular structure.
More or less by accident I tried it without the decimal carry detection. You get 16+16=2C as temporary result. But then when you add the +6 correction to the lower nibble, and just let the carry propagate normally, you end up with 32. And it also works for subtraction, thanks to the +9+Carry trick above. With the old +10 adjustment, it would not work.
Edit: there is a caveat. The carry out is slightly more complicated, because you can get a carry during first addition or during second. This is easily fixed by just OR-ing the second carry into the carry flag. And you can use the binary carry out for this, which should save a little bit of time.
Modified BCD code passes Klaus Dormann's test suite. The Github code has been updated.
Re: Yet another TTL 6502
I've a feeling this might not work (although it would be nice if it did) ... can you run Bruce's decimal tests in simulation?
Re: Yet another TTL 6502
The ALU bit slice looks great, and it really starts to show how simplicity is manifesting in the design. Very nice indeed.
This makes sense, but you do need to adjust the upper nibble to make 99 + 1 work, correct?
Arlet wrote:
More or less by accident I tried it without the decimal carry detection. You get 16+16=2C as temporary result. But then when you add the +6 correction to the lower nibble, and just let the carry propagate normally, you end up with 32. And it also works for subtraction, thanks to the +9+Carry trick above. With the old +10 adjustment, it would not work.
C74-6502 Website: https://c74project.com
Re: Yet another TTL 6502
Quote:
can you run Bruce's decimal tests in simulation?
For example, my code calculates 0A + 0F as 20, instead of 10. Personally, I like 20 better.
I think I'll accept this small deviation from the original. Unless you know of some useful code that depends on "correct" BCD behavior for invalid numbers.
Re: Yet another TTL 6502
Drass wrote:
The ALU bit slice looks great, and it really starts to show how simplicity is manifesting in the design. Very nice indeed.
Quote:
This makes sense, but you do need to adjust the upper nibble to make 99 + 1 work, correct?
Code: Select all
/*
* DC is the decimal carry. It is set when the upper nibble
* of the result is larger than 9. We need to incorporate
* the decimal carry from lower nibble here as well, in case
* the lower nibble generates a carry after correction, while
* upper nibble is equal to 9.
*/
wire DC = (MSD[3:0] + DHC) >= 10;
Re: Yet another TTL 6502
Not necessarily crucial, but here's what Bruce says:
Re: Yet another TTL 6502
For what it's worth, the example that Bruce gave for the ASCII conversion still works with my method. It starts to give different answers when lower nibbles add up to 25 or more.
Re: Yet another TTL 6502
Hurrah!
Re: Yet another TTL 6502
Rewrote ALU to use gates at same level as schematic. Full code on github, but here's the core of it. For completeness, the 2 modifier bits for the BI input should also be moved into this module.
Code: Select all
wire [7:0] AND = AI & BI;
wire [7:0] EOR = AI ^ BI;
wire [7:0] ORA = AI | BI;
// ripple carry bits
wire C1 = (ORA[0] & CI) | AND[0];
wire C2 = (ORA[1] & C1) | AND[1];
wire C3 = (ORA[2] & C2) | AND[2];
wire C4 = (ORA[3] & C3) | AND[3];
wire C5 = (ORA[4] & C4) | AND[4];
wire C6 = (ORA[5] & C5) | AND[5];
wire C7 = (ORA[6] & C6) | AND[6];
wire C8 = (ORA[7] & C7) | AND[7];
// adder
wire [7:0] ADC = EOR ^ { C7, C6, C5, C4, C3, C2, C1, CI };
// mux
always @*
case( op )
ALU_AI: OUT = AI;
ALU_ADC: OUT = ADC;
ALU_ROL: OUT = { AI[6:0], CI };
ALU_ROR: OUT = { CI, AI[7:1] };
ALU_ORA: OUT = ORA;
ALU_EOR: OUT = EOR;
ALU_AND: OUT = AND;
default: OUT = 8'h55;
endcase
Re: Yet another TTL 6502
Updated the ALU interface to have M register input 'MI', instead of BI input, and added two signals (mem_bi and inv_bi) to select actual BI input. This is a better reflection of actual ALU module, since the mem_bi/inv_bi signals will be implemented using spare AND/XOR gates in ALU.
The M (memory) register is a registered version of DB input so we have full cycle available for math in ALU.
https://github.com/Arlet/ttl-6502/blob/master/alu.v
I also decided to assign the unused ALU mux input to BI. This is not strictly needed, but it allows some alternative ways of implementing certain operations, which could be useful in minimizing decoding logic later on. If I find a better purpose, I can always change it later.
The M (memory) register is a registered version of DB input so we have full cycle available for math in ALU.
https://github.com/Arlet/ttl-6502/blob/master/alu.v
I also decided to assign the unused ALU mux input to BI. This is not strictly needed, but it allows some alternative ways of implementing certain operations, which could be useful in minimizing decoding logic later on. If I find a better purpose, I can always change it later.
Re: Yet another TTL 6502
Thanks for sharing the evolution of this design - one can learn a lot more from the journey, even second hand, compared to just seeing the destination.
Re: Yet another TTL 6502
BigEd wrote:
one can learn a lot more from the journey, even second hand, compared to just seeing the destination.
Re: Yet another TTL 6502
Made a small simplification in the BCD adjust logic. Instead of outputting a binary 'half carry' signal, I now output a binary 'half carry/half borrow'. Since the ALU now has a 'inv_bi' signal indicating whether it's doing an add or subtract, I can XOR the C4 bit with inv_bi to create a active high carry or borrow signal. This reduces the logic to pick the proper adjustment value from:
to
It can use one of spare XOR gates in the ALU module. Next step is to use an ALU spare OR gate for the HCB|DHC logic, and do the same thing for the upper nibble.
Since these were free gates anyway, the only real cost for BCD support is the mux in front of the M register that needs to select between DB and {ADJH,ADHL}. This can be done easily with two quad 2:1 muxes, like the SN74CBTLV3257, or in individual AND/OR gates (an OR gate for each '1' in the adjustment term, and an AND for each '0'). Since the binary representation of '6' is 0110, this could be done with a quad AND + quad OR. The disadvantage is that the AND needs an inverted input, so I'll probably use muxes instead. Edit: No, wait, I need to select between 0 and 6 and DB. I can still use the mux, and then use the select pin for DB/ADJ, and then tie inputs 0/3 to ground, and tie 1/2 to the adj_lsd/adj_msd signal.
Edit: done. Instead of all the carry flags, the ALU now outputs two signals which indicate which digit needs adjusting.
Code: Select all
if( bcdadd & (HC|DHC) ) ADJL = 6;
else if( bcdsub & ~HC ) ADJL = 6;
else ADJL = 0;
Code: Select all
if( bcd & (HCB|DHC) ) ADJL = 6;
else ADJL = 0;
Since these were free gates anyway, the only real cost for BCD support is the mux in front of the M register that needs to select between DB and {ADJH,ADHL}. This can be done easily with two quad 2:1 muxes, like the SN74CBTLV3257, or in individual AND/OR gates (an OR gate for each '1' in the adjustment term, and an AND for each '0'). Since the binary representation of '6' is 0110, this could be done with a quad AND + quad OR. The disadvantage is that the AND needs an inverted input, so I'll probably use muxes instead. Edit: No, wait, I need to select between 0 and 6 and DB. I can still use the mux, and then use the select pin for DB/ADJ, and then tie inputs 0/3 to ground, and tie 1/2 to the adj_lsd/adj_msd signal.
Edit: done. Instead of all the carry flags, the ALU now outputs two signals which indicate which digit needs adjusting.
Code: Select all
output adj_lsd, // if set, lower digit needs BCD adjustment
output adj_msd // if set, upper digit needs BCD adjustment
Re: Yet another TTL 6502
Added full schematic of ALU lower nibble
I've moved the ADC select to B0 input of the MUX. The ADC is the most frequently used operation, and by setting it to input 0, this hopefully will reduce decoding logic later on. I've also added the logic for BCD adjust output and the first half of the Z flag. Still need to assign the inputs for AI/BI/ROL and ROR, but I'll do that when I figure out the easiest lay out.
(Also need to attach all power/ground pins, and sprinkle some bypass caps)
I've moved the ADC select to B0 input of the MUX. The ADC is the most frequently used operation, and by setting it to input 0, this hopefully will reduce decoding logic later on. I've also added the logic for BCD adjust output and the first half of the Z flag. Still need to assign the inputs for AI/BI/ROL and ROR, but I'll do that when I figure out the easiest lay out.
(Also need to attach all power/ground pins, and sprinkle some bypass caps)