Yet another TTL 6502

For discussing the 65xx hardware itself or electronics projects.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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.
Attachments
z-flag.png
z-flag.png (2.39 KiB) Viewed 1147 times
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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.
I was thinking that the BCD adjustment term was either 0, +6 or -6, which is equal to +10. That's how I've done it before in the FPGA/CPLD verilog code.

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.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Yet another TTL 6502

Post by BigEd »

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?
User avatar
Drass
Posts: 428
Joined: 18 Oct 2015
Location: Toronto, ON

Re: Yet another TTL 6502

Post by Drass »

The ALU bit slice looks great, and it really starts to show how simplicity is manifesting in the design. Very nice indeed.
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.
This makes sense, but you do need to adjust the upper nibble to make 99 + 1 work, correct?
C74-6502 Website: https://c74project.com
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

Quote:
can you run Bruce's decimal tests in simulation?
It passes Bruce's decimal tests if I modify the code to only check valid BCD numbers. It fails on invalid ones.

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.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

Drass wrote:
The ALU bit slice looks great, and it really starts to show how simplicity is manifesting in the design. Very nice indeed.
Thanks. Still a long way to go to squeeze it into a Eurocard format :)
Quote:
This makes sense, but you do need to adjust the upper nibble to make 99 + 1 work, correct?
Correct. Whenever the lower nibble is between 10 and 15, and the upper nibble is 9, then you need to force an adjustment. Here's the code (MSD is most significant digit after binary add, DHC is the digital half carry, which indicates lower nibble >= 10)

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;
If you write it out in logic you get: DC = MSD[3] & ( MSD[2] | MSD[1] | (MSD[0] & DHC)), which is simple enough.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Yet another TTL 6502

Post by BigEd »

Not necessarily crucial, but here's what Bruce says:
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Yet another TTL 6502

Post by BigEd »

Hurrah!
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Yet another TTL 6502

Post by BigEd »

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.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

BigEd wrote:
one can learn a lot more from the journey, even second hand, compared to just seeing the destination.
Yes, one of the goals is to make the project educational, so I'm purposely updating the design in small increments, verifying it still passes the test suite, and then checking in results in Github. The beauty of Github is that you can easily go back to older revisions, compare the changes and recreate the evolution.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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:

Code: Select all

    if( bcdadd & (HC|DHC) )             ADJL = 6;
    else if( bcdsub & ~HC )             ADJL = 6;
    else                                ADJL = 0;
to

Code: Select all

    if( bcd & (HCB|DHC) )               ADJL = 6;
    else                                ADJL = 0;
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

    output adj_lsd,             // if set, lower digit needs BCD adjustment
    output adj_msd              // if set, upper digit needs BCD adjustment
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Yet another TTL 6502

Post by Arlet »

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)
Post Reply