// Copyright 2006, 2007 Dennis van Weeren
//
// This file is part of Minimig
//
// Minimig is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Minimig is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//
//
// This is the sprites part of denise 
// It supports all OCS sprite modes.
//
// 12-06-2005		-started coding
//				-first finished version
// 21-06-2005		-changed sprite priority logic and nsprite output
// 02-10-2005		-sprites are now attached if odd,even or both sprites SPRXCTL bit 7 is set
// 17-10-2005		-sprites were displayed one pixel too early, fixed.

module sprites(clk,reset,regaddress,horbeam,datain,nsprite,sprdata);
input 	clk;					//bus clock	
input 	reset;		    		//reset
input	[8:1]regaddress;		//register address input
input	[8:0]horbeam;			//horizontal beam counter
input 	[15:0]datain; 			//bus data in
output 	[7:0]nsprite;		  	//sprite data valid signals 
output	[3:0]sprdata;			//sprite data out

//register names and adresses		
parameter	SPRPOSCTLBASE=9'h140;	//sprite data, position and control register base address

//local signals
reg		[3:0]sprdata;			//see above

wire		selspr0;				//select sprite 0
wire		selspr1;				//select sprite 1
wire		selspr2;				//select sprite 2
wire		selspr3;				//select sprite 3
wire		selspr4;				//select sprite 4
wire		selspr5;				//select sprite 5
wire		selspr6;				//select sprite 6
wire		selspr7;				//select sprite 7

wire		[1:0]sprdat0;			//data sprite 0
wire		[1:0]sprdat1;			//data sprite 1
wire		[1:0]sprdat2;			//data sprite 2
wire		[1:0]sprdat3;			//data sprite 3
wire		[1:0]sprdat4;			//data sprite 4
wire		[1:0]sprdat5;			//data sprite 5
wire		[1:0]sprdat6;			//data sprite 6
wire		[1:0]sprdat7;			//data sprite 7

wire		attach0;				//attach sprite 0,1
wire		attach1;				//attach sprite 0,1
wire		attach2;				//attach sprite 2,3
wire		attach3;				//attach sprite 2,3
wire		attach4;				//attach sprite 4,5
wire		attach5;				//attach sprite 4,5
wire		attach6;				//attach sprite 6,7
wire		attach7;				//attach sprite 6,7

//--------------------------------------------------------------------------------------

//sprite register address decoder
wire	selsprx;

assign selsprx=(SPRPOSCTLBASE[8:6]==regaddress[8:6])?1:0;//base address
assign selspr0=(selsprx&&(regaddress[5:3]==0))?1:0;
assign selspr1=(selsprx&&(regaddress[5:3]==1))?1:0;
assign selspr2=(selsprx&&(regaddress[5:3]==2))?1:0;
assign selspr3=(selsprx&&(regaddress[5:3]==3))?1:0;
assign selspr4=(selsprx&&(regaddress[5:3]==4))?1:0;
assign selspr5=(selsprx&&(regaddress[5:3]==5))?1:0;
assign selspr6=(selsprx&&(regaddress[5:3]==6))?1:0;
assign selspr7=(selsprx&&(regaddress[5:3]==7))?1:0;

//--------------------------------------------------------------------------------------

//instantiate sprite 0
sprshift	sps0(	.clk(clk),
				.reset(reset),
				.aen(selspr0),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat0),
				.attach(attach0)	);

//instantiate sprite 1
sprshift	sps1(	.clk(clk),
				.reset(reset),
				.aen(selspr1),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat1),
				.attach(attach1)	);

//instantiate sprite 2
sprshift	sps2(	.clk(clk),
				.reset(reset),
				.aen(selspr2),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat2),
				.attach(attach2)	);

//instantiate sprite 3
sprshift	sps3(	.clk(clk),
				.reset(reset),
				.aen(selspr3),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat3),
				.attach(attach3)	);

//instantiate sprite 4
sprshift	sps4(	.clk(clk),
				.reset(reset),
				.aen(selspr4),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat4),
				.attach(attach4)	);

//instantiate sprite 5
sprshift	sps5(	.clk(clk),
				.reset(reset),
				.aen(selspr5),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat5),
				.attach(attach5)	);

//instantiate sprite 6
sprshift	sps6(	.clk(clk),
				.reset(reset),
				.aen(selspr6),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat6),
				.attach(attach6)	);

//instantiate sprite 7
sprshift	sps7(	.clk(clk),
				.reset(reset),
				.aen(selspr7),
				.address(regaddress[2:1]),
				.horbeam(horbeam),
				.datain(datain),
				.sprdata(sprdat7),
				.attach(attach7)	);

//--------------------------------------------------------------------------------------

//generate sprite data valid signals
assign nsprite[0]=(sprdat0[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[1]=(sprdat1[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[2]=(sprdat2[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[3]=(sprdat3[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[4]=(sprdat4[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[5]=(sprdat5[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[6]=(sprdat6[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data
assign nsprite[7]=(sprdat7[1:0]!=2'b00)?1:0;//if any non-zero bit -> valid video data

//--------------------------------------------------------------------------------------

//sprite video priority logic and color decoder
always @(	attach0 or attach1 or attach2 or attach3 or
		attach4 or attach5 or attach6 or attach7 or
		sprdat0 or sprdat1 or sprdat2 or sprdat3 or
		sprdat4 or sprdat5 or sprdat6 or sprdat7 or
		nsprite )
begin
	if(nsprite[1:0]!=2'b00)//sprites 0,1 non transparant ?
	begin
		if(attach0 || attach1)//sprites are attached -> 15 colors + transparant
			sprdata[3:0]={sprdat1[1:0],sprdat0[1:0]};
	   	else if(nsprite[0])//output lowered number sprite
			sprdata[3:0]={2'b00,sprdat0[1:0]};
	   	else//output higher numbered sprite
			sprdata[3:0]={2'b00,sprdat1[1:0]};
	end
		
	else if(nsprite[3:2]!=2'b00)//sprites 2,3 non transparant ?
	begin
		if(attach2 || attach3)//sprites are attached -> 15 colors + transparant
			sprdata[3:0]={sprdat3[1:0],sprdat2[1:0]};
	   	else if(nsprite[2])//output lowered number sprite
			sprdata[3:0]={2'b01,sprdat2[1:0]};
	   	else//output higher numbered sprite
			sprdata[3:0]={2'b01,sprdat3[1:0]};
	end
		
	else if(nsprite[5:4]!=2'b00)//sprites 4,5 non transparant ?
	begin
		if(attach4 || attach5)//sprites are attached -> 15 colors + transparant
			sprdata[3:0]={sprdat5[1:0],sprdat4[1:0]};
	   	else if(nsprite[4])//output lowered number sprite
			sprdata[3:0]={2'b10,sprdat4[1:0]};
	   	else//output higher numbered sprite
			sprdata[3:0]={2'b10,sprdat5[1:0]};
	end
		
	else if(nsprite[7:6]!=2'b00)//sprites 6,7 non transparant ?
	begin
		if(attach6 || attach7)//sprites are attached -> 15 colors + transparant
			sprdata[3:0]={sprdat7[1:0],sprdat6[1:0]};
	   	else if(nsprite[6])//output lowered number sprite
			sprdata[3:0]={2'b11,sprdat6[1:0]};
	   	else//output higher numbered sprite
			sprdata[3:0]={2'b11,sprdat7[1:0]};
	end

	else//all sprites transparant
	begin
		sprdata[3:0]=4'b0000;	
	end	
end

//--------------------------------------------------------------------------------------

endmodule

//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------

//this is the sprite parallel to serial converter
//clk is 7.09379 MHz (low resolution pixel clock)
//the sprdata assign circuitry is constructed differently from the hardware
//as described	in the amiga hardware reference manual
//this is to make sure that the horizontal start position of a sprite
//aligns with the bitplane/playfield start position
module sprshift(clk,reset,aen,address,horbeam,datain,sprdata,attach);
input 	clk;					//bus clock	
input 	reset;		    		//reset
input	aen;					//address enable
input	[1:0]address;		    	//register address input
input	[8:0]horbeam;			//horizontal beam counter
input 	[15:0]datain; 			//bus data in
output	[1:0]sprdata;			//serialized sprite data out
output	attach;				//sprite is attached

//register names and adresses		
parameter POS=2'b00;  		
parameter CTL=2'b01;  		
parameter DATA=2'b10;  		
parameter DATB=2'b11;  		

//local signals
reg		attach;				//see above
reg		[15:0]datla;			//data register A
reg		[15:0]datlb;			//data register B
reg		[15:0]shifta;			//shift register A
reg		[15:0]shiftb;			//shift register B
reg		[8:0]hstart;			//horizontal start value
reg		armed;				//sprite "armed" signal
wire		load;				//load shift register signal

//--------------------------------------------------------------------------------------

//generate armed signal
always @(posedge clk)
	if(reset)//reset disables sprite
		armed<=0;
	else if(aen&&(address==CTL))//writing CTL register disables sprite
		armed<=0;
	else if (aen&&(address==DATA))//writing data register A arms sprite
		armed<=1;

//--------------------------------------------------------------------------------------

//generate load signal
assign load=(armed&&(horbeam[8:0]==hstart[8:0]))?1:0;

//--------------------------------------------------------------------------------------

//POS register
always @(posedge clk)
	if(aen&&(address==POS))
		hstart[8:1]<=datain[7:0];

//CTL register
always @(posedge clk)
	if(aen&&(address==CTL))
		{attach,hstart[0]}<={datain[7],datain[0]};

//data register A
always @(posedge clk)
	if(aen&&(address==DATA))
		datla[15:0]<=datain[15:0];

//data register B
always @(posedge clk)
	if(aen&&(address==DATB))
		datlb[15:0]<=datain[15:0];

//--------------------------------------------------------------------------------------

//sprite shift register
always @(posedge clk)
	if(load)//load new data into shift register
	begin
		shifta[15:0]<=datla[15:0];
		shiftb[15:0]<=datlb[15:0];
	end
	else//shift out data
	begin
		shifta[15:0]<={shifta[14:0],1'b0};
		shiftb[15:0]<={shiftb[14:0],1'b0};
	end

//assign serialized output data
assign sprdata[1:0]={shifta[15],shiftb[15]};

//--------------------------------------------------------------------------------------

endmodule
