65ORG16.b Core

Topics relating to PALs, CPLDs, FPGAs, and other PLDs used for the support or creation of 65-family processors, both hardware and HDL.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Post by BigEd »

fachat wrote:
Do you know how old this design is? There is no date on the page

André
Maybe 1993? (Although the pages from the webserver are dated April and May 2000, and archive.org has a version from 1997.)

Cheers
Ed
Tor
Posts: 597
Joined: 10 Apr 2011
Location: Norway/Japan

Post by Tor »

fachat wrote:
Do you know how old this design is? There is no date on the page

André
The Last-Modified date is 10 May 2000 02:48:27 GMT for the first page and 30 Apr 2000 17:41:48 GMT for the second page (the instructions page).

Edit: Ah, BigEd beat me to it. I didn't see the next page of the topic.

-Tor
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

Been thinking about the Acc-Acc transfers...

I think it's time to define some new base opcodes for the A accumulator. By base I mean in the original 6502 $00-$FF region.

I chose the columns $03 & $0B. Specifically, $BB, $AB & $9B for TAB, TAC, & TAD and $B3, $A3 & $93 for TBA, TCA, TDA. Other Acc to Acc transfers will depend on bits 11, 10, 9 & 8. Still working on it...
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

Ok, now I am getting confused. Could be because I am going back and forth between working on cars and the code, heh. But this is what I have so far for Acc to Acc transfers using the dst_reg. What about the src_reg? I have run out and am confused as to which Acc should be the src and which Acc is the dest.

Code: Select all

16'b0000_00xx_1011_1011:	// TBA
				dst_reg <= SEL_B;
				
		16'b0000_00xx_1010_1011:	// TCA
				dst_reg <= SEL_D;
				
		16'b0000_00xx_1001_1011:	// TDA
				dst_reg <= SEL_E;
				
		16'b0000_01xx_1011_1011:	// TAB
				dst_reg <= SEL_A;
					
		16'b0000_01xx_1010_1011:	// TCB
				dst_reg <= SEL_D;
				
		16'b0000_01xx_1001_1011:	// TDB
				dst_reg <= SEL_E;
				
		16'b0000_10xx_1011_1011:	// TAC
				dst_reg <= SEL_A;
				
		16'b0000_10xx_1010_1011:	// TBC
				dst_reg <= SEL_B;
				
		16'b0000_10xx_1001_1011:	// TDC
				dst_reg <= SEL_E;
				
		16'b0000_11xx_1011_1011:	// TAD
				dst_reg <= SEL_A;
			
		16'b0000_11xx_1010_1011:	// TBD
				dst_reg <= SEL_B;
				
		16'b0000_11xx_1001_1011:	// TCD
				dst_reg <= SEL_D;
				
				
		default: case( IR[9:8] ) 
						2'b00: dst_reg <= SEL_A; 
						2'b01: dst_reg <= SEL_B; 
						2'b10: dst_reg <= SEL_D; 
						2'b11: dst_reg <= SEL_E;
							default: dst_reg <= SEL_A;
					endcase 
SEL_D and SEL_E are actually the C and D Accumulators respectively.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Post by Arlet »

In the TAX instruction, the 'A' is the source, and 'X' is the destination. So src_reg should be SEL_A, and dst_reg should be SEL_X.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

I will rename the C flag (carry) so I can use SEL_C to make the code less confusing. Right now there's a conflict if I try to use SEL_C. For arguments sake SEL_C is Accumulator C.

But, consider a TBC and a TCB. Since the src_reg and dest_reg both are MUX'd into SEL_B-SEL_C. We could pick whether to put these opcode definitions in a dest_reg CASEX or a src_reg CASEX?

I will try a simulation of what I have so far...
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Post by Arlet »

ElEctric_EyE wrote:
But, consider a TBC and a TCB. Since the src_reg and dest_reg both are MUX'd into SEL_B-SEL_C. We could pick whether to put these opcode definitions in a dest_reg CASEX or a src_reg CASEX?
I'm not really following you here.

For a TBC instruction, the src_reg should be SEL_B, and the dst_reg should be SEL_C. The TBC instruction is then executed in two cycles. In the first cycle, the src_reg is read from the register file, so it reads register 'B'. In the second cycle, the result is written to the dst_reg, so it writes register 'C'.

So, dst_reg always points to the register where the ultimate result of the operation is stored, and src_reg points to a register that is read during the instruction to get an operand.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

I'll try to explain the way I have understood the progression.

first was this code, I'll use TAX etc. as an example.

Code: Select all

...
16'b0000_0000_101x_xx10:	// LDX, TAX, TBX, TSX
				dst_reg <= SEL_X;
...
default:  dst_reg <= SEL_A...
then I modified the default with your help to get TBX, TCX, TDX etc.

Code: Select all

16'b0000_00xx_101x_xx10:	// LDX, TAX, TBX, TSX
				dst_reg <= SEL_X;
...
default: case( IR[9:8] ) 
						2'b00: dst_reg <= SEL_A; 
						2'b01: dst_reg <= SEL_B; 
						2'b10: dst_reg <= SEL_C; 
						2'b11: dst_reg <= SEL_D;
So, it makes sense to do this doesn't it?:

Code: Select all

...
16'b0000_00xx_101x_xx10:	// LDX, TAX, TBX, TSX
				dst_reg <= SEL_X;
...
16'b0000_00xx_1011_1011:	// TBA
				dst_reg <= SEL_B;
		16'b0000_00xx_1010_1011:	// TCA
				dst_reg <= SEL_C;
		16'b0000_00xx_1001_1011:	// TDA
				dst_reg <= SEL_D;
				
		16'b0000_01xx_1011_1011:	// TAB
				dst_reg <= SEL_A;
		16'b0000_01xx_1010_1011:	// TCB
				dst_reg <= SEL_C;
		16'b0000_01xx_1001_1011:	// TDB
				dst_reg <= SEL_D;
				
		16'b0000_10xx_1011_1011:	// TAC
				dst_reg <= SEL_A;
		16'b0000_10xx_1010_1011:	// TBC
				dst_reg <= SEL_B;
		16'b0000_10xx_1001_1011:	// TDC
				dst_reg <= SEL_D;
				
		16'b0000_11xx_1011_1011:	// TAD
				dst_reg <= SEL_A;
		16'b0000_11xx_1010_1011:	// TBD
				dst_reg <= SEL_B;
		16'b0000_11xx_1001_1011:	// TCD
				dst_reg <= SEL_D;
				
				
		default: case( IR[9:8] ) 
						2'b00: dst_reg <= SEL_A; 
						2'b01: dst_reg <= SEL_B; 
						2'b10: dst_reg <= SEL_C; 
						2'b11: dst_reg <= SEL_D;
                         default: dst_reg <= SEL_A;
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

I am trying to do much at once, especially with disturbances at work where I cannot focus on the code, although I do think about the code when working...

I ran a Sim and values are changing but not as expected. I will back up a few steps for Acc to Acc transfer opcodes... I should be able to post progress soon with the tools I have learned so far on ISim and your advice.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Post by Arlet »

ElEctric_EyE wrote:

Code: Select all

                16'b0000_00xx_1011_1011:	// TBA
				dst_reg <= SEL_B;
No, the source is B, the destination is A. So you should set dst_reg to SEL_A, and src_reg to SEL_B
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

Ok, finally got through my thick skull!

All Acc to Acc transfers appear to be working. :D

Code: Select all

$01BB...TAB     $009B...TBA     $00AB...TCA     $00BB...TDA
$02BB...TAC     $02AB...TBC     $01AB...TCB     $019B...TDB
$03BB...TAD     $03AB...TBD     $039B...TCD     $029B...TDC
Will do more testing to be sure...

And do a speed test tonight if all goes well.

EDIT: I couldn't wait. It passes with a O2 constraint @ 10.5ns for 95MHz. Now I'll continue testing...
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

I was close. But after a couple cups of coffee on a fresh day (away from work), I got it 100% correct. A few of the transfers were not working, but they are all working now. I had to put TAA TBB TCC TDD in the code (even though the commands are useless) to get my logic straight. Here are the new opcodes. Posted on Github.

I have almost completed what I set out to do with this core according to the header of this thread. I have yet to add a Z index register, but there are a few things I would like to do next...

There are 6 bits left for the transfer opcodes. It would be useful I think to use 2 of the bits and have 4 logic functions (AND, OR, EOR, NOT) built into an Acc-Acc transfer. I think this should still take 2 cycles and shouldn't be too hard to code for and shouldn't slow down the core speed if I understand correctly.

Code: Select all

$008B...TAA     $009B...TBA     $00AB...TCA     $00BB...TDA
$018B...TAB     $019B...TBB     $01AB...TCB     $01BB...TDB
$028B...TAC     $029B...TBC     $02AB...TCC     $02BB...TDC
$038B...TAD     $039B...TBD     $03AB...TCD     $03BB...TDD
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

An index register and adding logic to the transfer opcodes are not as easy as I thought and I grow weary of Verilog.

I'm going to take this core now and work with it on the DevBoard.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Post by ElEctric_EyE »

Back at work again and things are slow, so I thought I would tackle the 'transfer with logic' again with the new accumulators.

I realized I needed a separate register that would contain the result of the logic functions AND, OR, XOR amongst the 4 Accumulators. The opcode names look like TABand, TABor, TABxor etc. in my As65 Macro's.

So this register called LOGOP (Logic Operator) I have working already with TABand and it takes 2 cycles. It may take 1 or more cycle to transfer it to the src_reg or dst_reg. Transferring this value will be the challenge for me at this point... I just finished all the other 4 Acc transfer + Logic.

If in the end these 'transfer with logic' opcodes slow down the core too much, I'll probably remove them. I am always concerned with speed, so we will see if a speed sacrifice is worth it, compared to cycles saved per instruction. I intend to focus on this tradeoff, during this 65Org16.b Core expansion. If they prove good speedwise, then I'll look how to do the same 'transfer with logic' opcodes between the X & Y index registers, stack, and 4 accumulators.
Been thinking recently too, there needs to be a single cycle 16bit NOT (i.e. inverse) on any accumulator/index register.

Code: Select all

always @(posedge clk)
	  if( state == DECODE && RDY )
	  casex( IR[15:0] )
		16'b0000_xx01_1000_1011,	// TABand, TABor, TABxor
      16'b0000_xx00_1001_1011:   // TBAand, TBAor, TBAxor
		case( IR[11:10] )
			2'b01: LOGOP <= ( DCBAXYS[SEL_A] & DCBAXYS[SEL_B] );
			2'b10: LOGOP <= ( DCBAXYS[SEL_A] | DCBAXYS[SEL_B] );
			2'b11: LOGOP <= ( DCBAXYS[SEL_A] ^ DCBAXYS[SEL_B] );
	  endcase
		default:case( IR[11:8] )
			4'b0001: LOGOP <= DCBAXYS[SEL_B]; //no LOGOP, send value to dest_reg
			4'b0000: LOGOP <= DCBAXYS[SEL_A]; //no LOGOP, send value to dest_reg
		endcase
	endcase
Still working on the total opcode value list. Will post when they're all proven on Isim.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Post by Arlet »

Code: Select all

LOGOP <= ( DCBAXYS[SEL_A] & DCBAXYS[SEL_B] );
If you do that, you'll make the register file multi-ported, which will explode its size. It's better to try to fit the logic operators inside the normal path. This would require 3 cycles for the 'TABand' instruction:

1. read A (src_reg)
2. read B (dst_reg)
3. write B (dst_reg)

cycles 1 and 3 are already needed for the regular TAB instruction, so you need to add a cycle in the middle. This is a new one, so it requires an update to the state machine, and it requires an extra MUX in the register file access so you can read from dst_reg. The alternative is to do this:

1. read A (src_reg), src_reg <= B
2. read B (src_reg)
3. write B (dst_reg)

This avoids the extra MUX, but requires an extra write to src_reg.

Anyway, this change is not as straightforward as earlier changes, since register/register operations aren't natural to the 6502. Maybe you should try to come up with something simpler to do first, to get some more experience.

A simpler, but quite powerful, extension to the opcode space is what I've mentioned earlier, which is to allow the source and destination Acc to be different. This does not allow Acc-Acc operations, but it does allow things like ADC A, #10 and store the result in B, with a full set of addressing modes and operands. All this requires is an extra 2 bits in the opcode to designate the destination register, and a few lines of Verilog.
Post Reply