OpenCores
URL https://opencores.org/ocsvn/aoocs/aoocs/trunk

Subversion Repositories aoocs

[/] [aoocs/] [trunk/] [rtl/] [ocs_video.v] - Rev 2

Compare with Previous | Blame | View Log

/*
 * Copyright 2010, Aleksander Osman, alfik@poczta.fm. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list
 *     of conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
/*! \file
 * \brief OCS video implementation with WISHBONE master and slave interface.
 */
 
/*! \brief \copybrief ocs_video.v
 
List of video registers:
\verbatim
Implemented:
    DIWSTRT      08E  W   A       Display window start (upper left vert-horiz position)
    DIWSTOP      090  W   A       Display window stop (lower right vert.-horiz. position)
    DDFSTRT      092  W   A       Display bitplane data fetch start (horiz. position)
    DDFSTOP      094  W   A       Display bitplane data fetch stop              write implemented here
     [DMACON     096  W   ADP     DMA control write (clear or set)              write implemented here]
 
     [JOY1DAT   *00C  R   D       Joystick-mouse 1 data (vert,horiz)            read not implemented here]
    CLXDAT      *00E  R   D       Collision data register (read and clear)      read not implemented here
    CLXCON       098  W   D       Collision control                             write implemented here
     [INTENA     09A  W   P       Interrupt enable bits (clear or set bits)     write implemented here]
 
    BPLCON0      100  W   AD( E ) Bitplane control register (misc. control bits)
    BPLCON1      102  W   D       Bitplane control reg. (scroll value PF1, PF2)
    BPLCON2      104  W   D( E )  Bitplane control reg. (priority control)
 
    BPL1MOD      108  W   A       Bitplane modulo (odd planes)
    BPL2MOD      10A  W   A       Bitplane modulo (even planes)
 
    BPL1PTH   +  0E0  W   A       Bitplane 1 pointer (high 3 bits)
    BPL1PTL   +  0E2  W   A       Bitplane 1 pointer (low 15 bits)
    BPL2PTH   +  0E4  W   A       Bitplane 2 pointer (high 3 bits)
    BPL2PTL   +  0E6  W   A       Bitplane 2 pointer (low 15 bits)
    BPL3PTH   +  0E8  W   A       Bitplane 3 pointer (high 3 bits)
    BPL3PTL   +  0EA  W   A       Bitplane 3 pointer (low 15 bits)
    BPL4PTH   +  0EC  W   A       Bitplane 4 pointer (high 3 bits)
    BPL4PTL   +  0EE  W   A       Bitplane 4 pointer (low 15 bits)
    BPL5PTH   +  0F0  W   A       Bitplane 5 pointer (high 3 bits)
    BPL5PTL   +  0F2  W   A       Bitplane 5 pointer (low 15 bits)
    BPL6PTH   +  0F4  W   A       Bitplane 6 pointer (high 3 bits)
    BPL6PTL   +  0F6  W   A       Bitplane 6 pointer (low 15 bits)
 
    BPL1DAT   &  110  W   D       Bitplane 1 data (parallel-to-serial convert)
    BPL2DAT   &  112  W   D       Bitplane 2 data (parallel-to-serial convert)
    BPL3DAT   &  114  W   D       Bitplane 3 data (parallel-to-serial convert)
    BPL4DAT   &  116  W   D       Bitplane 4 data (parallel-to-serial convert)
    BPL5DAT   &  118  W   D       Bitplane 5 data (parallel-to-serial convert)
    BPL6DAT   &  11A  W   D       Bitplane 6 data (parallel-to-serial convert)
 
    SPR0PTH   +  120  W   A       Sprite 0 pointer (high 3 bits)
    SPR0PTL   +  122  W   A       Sprite 0 pointer (low 15 bits)
    SPR0POS   %  140  W   AD      Sprite 0 vert-horiz start position data
    SPR0CTL   %  142  W   AD( E ) Sprite 0 vert stop position and control data
    SPR0DATA  %  144  W   D       Sprite 0 image data register A
    SPR0DATB  %  146  W   D       Sprite 0 image data register B
    SPR1PTH   +  124  W   A       Sprite 1 pointer (high 3 bits)
    SPR1PTL   +  126  W   A       Sprite 1 pointer (low 15 bits)
    SPR1POS   %  148  W   AD      Sprite 1 vert-horiz start position  data
    SPR1CTL   %  14A  W   AD      Sprite 1 vert stop position and control data
    SPR1DATA  %  14C  W   D       Sprite 1 image data register A
    SPR1DATB  %  14E  W   D       Sprite 1 image data register B
    SPR2PTH   +  128  W   A       Sprite 2 pointer (high 3 bits)
    SPR2PTL   +  12A  W   A       Sprite 2 pointer (low 15 bits)
    SPR2POS   %  150  W   AD      Sprite 2 vert-horiz start position data
    SPR2CTL   %  152  W   AD      Sprite 2 vert stop position and control data
    SPR2DATA  %  154  W   D       Sprite 2 image data register A
    SPR2DATB  %  156  W   D       Sprite 2 image data register B
    SPR3PTH   +  12C  W   A       Sprite 3 pointer (high 3 bits)
    SPR3PTL   +  12E  W   A       Sprite 3 pointer (low 15 bits)
    SPR3POS   %  158  W   AD      Sprite 3 vert-horiz start position data
    SPR3CTL   %  15A  W   AD      Sprite 3 vert stop position and control data
    SPR3DATA  %  15C  W   D       Sprite 3 image data register A
    SPR3DATB  %  15E  W   D       Sprite 3 image data register B
    SPR4PTH   +  130  W   A       Sprite 4 pointer (high 3 bits)
    SPR4PTL   +  132  W   A       Sprite 4 pointer (low 15 bits)
    SPR4POS   %  160  W   AD      Sprite 4 vert-horiz start position data
    SPR4CTL   %  162  W   AD      Sprite 4 vert stop position and control data
    SPR4DATA  %  164  W   D       Sprite 4 image data register A
    SPR4DATB  %  166  W   D       Sprite 4 image data register B
    SPR5PTH   +  134  W   A       Sprite 5 pointer (high 3 bits)
    SPR5PTL   +  136  W   A       Sprite 5 pointer (low 15 bits)
    SPR5POS   %  168  W   AD      Sprite 5 vert-horiz start position data
    SPR5CTL   %  16A  W   AD      Sprite 5 vert stop position and control data
    SPR5DATA  %  16C  W   D       Sprite 5 image data register A
    SPR5DATB  %  16E  W   D       Sprite 5 image data register B
    SPR6PTH   +  138  W   A       Sprite 6 pointer (high 3 bits)
    SPR6PTL   +  13A  W   A       Sprite 6 pointer (low 15 bits)
    SPR6POS   %  170  W   AD      Sprite 6 vert-horiz start position data
    SPR6CTL   %  172  W   AD      Sprite 6 vert stop position and control data
    SPR6DATA  %  174  W   D       Sprite 6 image data register A
    SPR6DATB  %  176  W   D       Sprite 6 image data register B
    SPR7PTH   +  13C  W   A       Sprite 7 pointer (high 3 bits)
    SPR7PTL   +  13E  W   A       Sprite 7 pointer (low 15 bits)
    SPR7POS   %  178  W   AD      Sprite 7 vert-horiz start position data
    SPR7CTL   %  17A  W   AD      Sprite 7 vert stop position and control data
    SPR7DATA  %  17C  W   D       Sprite 7 image data register A
    SPR7DATB  %  17E  W   D       Sprite 7 image data register B
 
    COLOR00      180  W   D       Color table 00
    COLOR01      182  W   D       Color table 01
    COLOR02      184  W   D       Color table 02
    COLOR03      186  W   D       Color table 03
    COLOR04      188  W   D       Color table 04
    COLOR05      18A  W   D       Color table 05
    COLOR06      18C  W   D       Color table 06
    COLOR07      18E  W   D       Color table 07
    COLOR08      190  W   D       Color table 08
    COLOR09      192  W   D       Color table 09
    COLOR10      194  W   D       Color table 10
    COLOR11      196  W   D       Color table 11
    COLOR12      198  W   D       Color table 12
    COLOR13      19A  W   D       Color table 13
    COLOR14      19C  W   D       Color table 14
    COLOR15      19E  W   D       Color table 15
    COLOR16      1A0  W   D       Color table 16
    COLOR17      1A2  W   D       Color table 17
    COLOR18      1A4  W   D       Color table 18
    COLOR19      1A6  W   D       Color table 19
    COLOR20      1A8  W   D       Color table 20
    COLOR21      1AA  W   D       Color table 21
    COLOR22      1AC  W   D       Color table 22
    COLOR23      1AE  W   D       Color table 23
    COLOR24      1B0  W   D       Color table 24
    COLOR25      1B2  W   D       Color table 25
    COLOR26      1B4  W   D       Color table 26
    COLOR27      1B6  W   D       Color table 27
    COLOR28      1B8  W   D       Color table 28
    COLOR29      1BA  W   D       Color table 29
    COLOR30      1BC  W   D       Color table 30
    COLOR31      1BE  W   D       Color table 31
\endverbatim
*/
module ocs_video(
    //% \name Clock and reset
    //% @{
    input               CLK_I,
    input               reset_n,
    //% @}
 
    //% \name WISHBONE master
    //% @{
    output reg          CYC_O,
    output reg          STB_O,
    output              WE_O,
    output reg [31:2]   ADR_O,
    output [3:0]        SEL_O,
    input [31:0]        master_DAT_I,
    input               ACK_I,
    //% @}
 
    //% \name WISHBONE slave
    //% @{
    input               CYC_I,
    input               STB_I,
    input               WE_I,
    input [8:2]         ADR_I,
    input [3:0]         SEL_I,
    input [31:0]        slave_DAT_I,
    output reg          ACK_O,
    //% @}
 
    //% \name Not aligned register access on a 32-bit WISHBONE bus
    //% @{
        // CLXDAT read not implemented here
    input               na_clx_dat_read,
    output [15:0]       na_clx_dat,
        // INTENA write implemented here
    output              na_int_ena_write,
    output [15:0]       na_int_ena,
    output [1:0]        na_int_ena_sel,
        // DMACON write implemented here
    output              na_dma_con_write,
    output [15:0]       na_dma_con,
    output [1:0]        na_dma_con_sel,
    //% @}
 
    //% \name Direct drv_ssram read/write DMA burst video interface
    //% @{
	// bitplain burst read
	output              burst_read_request,
	output [31:2]       burst_read_address,
	input               burst_read_ready,
	input [31:0]        burst_read_data,
 
    // video output burst write
    output              burst_write_request,
    output [31:2]       burst_write_address,
    output [35:0]       burst_write_data,
    input               burst_write_ready,
    //% @}
 
    //% \name Internal OCS ports
    //% @{
    input               line_start,
    input               line_pre_start,
    input [8:0]         line_number,
    input [8:0]         column_number,
 
    input [10:0]        dma_con
    //% @}
);
 
`define VIDEO_BUFFER            32'h10180000
`define VIDEO_BUFFER_DIV_4      30'h04060000
 
// No: BPLCON0: External synchronize, Lace mode, lightpen, genlock audio enable, color composite
// No: data fetch word after word - all fetched at once
 
/*                              16-bit      32-bit
- get sprite data:              2x8         2x8     
- get bitplain data:            40x6        21x6
- save line to video memory:    214         214
16-bit: 16+240+214 = 470 = 7.834 us
32-bit: 16+126+640+214 = 996 = 16599.993 us
 
PAL: 52.000 us / 64.000 us
display out: 3.567 us / 26.667 us
*/
assign na_int_ena_write = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && { ADR_I, 2'b0 } == 9'h098 && ACK_O == 1'b0);
assign na_int_ena = slave_DAT_I[15:0];
assign na_int_ena_sel = SEL_I[1:0];
 
assign na_dma_con_write = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && { ADR_I, 2'b0 } == 9'h094 && ACK_O == 1'b0);
assign na_dma_con = slave_DAT_I[15:0];
assign na_dma_con_sel = SEL_I[1:0];
 
assign SEL_O = 4'b1111;
assign WE_O = 1'b0;
 
wire [3:0] dma_select;
assign dma_select = 
    (sprite0_dma_req == 1'b1) ? 4'd1 :
    (sprite1_dma_req == 1'b1) ? 4'd2 :
    (sprite2_dma_req == 1'b1) ? 4'd3 :
    (sprite3_dma_req == 1'b1) ? 4'd4 :
    (sprite4_dma_req == 1'b1) ? 4'd5 :
    (sprite5_dma_req == 1'b1) ? 4'd6 :
    (sprite6_dma_req == 1'b1) ? 4'd7 :
    (sprite7_dma_req == 1'b1) ? 4'd8 :
    4'd0;
 
wire [31:2] dma_address_select;
assign dma_address_select =
    (dma_select == 4'd1) ? sprite0_dma_address :
    (dma_select == 4'd2) ? sprite1_dma_address :
    (dma_select == 4'd3) ? sprite2_dma_address :
    (dma_select == 4'd4) ? sprite3_dma_address :
    (dma_select == 4'd5) ? sprite4_dma_address :
    (dma_select == 4'd6) ? sprite5_dma_address :
    (dma_select == 4'd7) ? sprite6_dma_address :
    sprite7_dma_address;
 
wire [5:0] bpl_color;
wire [1:0] sprite0_color;
wire [1:0] sprite1_color;
wire sprite01_attached;
wire [1:0] sprite2_color;
wire [1:0] sprite3_color;
wire sprite23_attached;
wire [1:0] sprite4_color;
wire [1:0] sprite5_color;
wire sprite45_attached;
wire [1:0] sprite6_color;
wire [1:0] sprite7_color;
wire sprite67_attached;
wire [10:0] bpl_con0;
wire window_line_enable;
 
wire priority_write_ena;
assign priority_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    (   ({ ADR_I, 2'b0 } >= 9'h180 && { ADR_I, 2'b0 } <= 9'h1BC) || 
        { ADR_I, 2'b0 } == 9'h08C || { ADR_I, 2'b0 } == 9'h090 || { ADR_I, 2'b0 } == 9'h098 || { ADR_I, 2'b0 } == 9'h104
    ));
 
video_priority video_priority_inst (
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_pre_start(line_pre_start),
    .line_number(line_number),
    .column_number(column_number),
    .window_line_enable(window_line_enable),
 
    .write_ena(priority_write_ena),
    // 0:   COLOR00,    COLOR01,
    // 1:   COLOR02,    COLOR03,
    // 2:   COLOR04,    COLOR05,
    // 3:   COLOR06,    COLOR07,
    // 4:   COLOR08,    COLOR09,
    // 5:   COLOR10,    COLOR11,
    // 6:   COLOR12,    COLOR13,
    // 7:   COLOR14,    COLOR15,
    // 8:   COLOR16,    COLOR17,
    // 9:   COLOR18,    COLOR19,
    // 10:  COLOR20,    COLOR21,
    // 11:  COLOR22,    COLOR23,
    // 12:  COLOR24,    COLOR25,
    // 13:  COLOR26,    COLOR27,
    // 14:  COLOR28,    COLOR29,
    // 15:  COLOR30,    COLOR31,
    // 16:      DIWSTRT [15:0],     COPINS      [31:16], * COPINS not implemented  
    // 17:      DIWSTOP [31:16],    DDFSTART    [15:0], *
    // 18:      CLXCON  [31:16],    INTENA      [15:0], *
    // 19:      BPLCON2 [31:16],    NOT USED    [15:0],
    // read:    CLXDAT  [15:0],     JOY1DAT     [31:16] *
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h180) ? 5'd0 :
        ({ ADR_I, 2'b0 } == 9'h184) ? 5'd1 :
        ({ ADR_I, 2'b0 } == 9'h188) ? 5'd2 :
        ({ ADR_I, 2'b0 } == 9'h18C) ? 5'd3 :
        ({ ADR_I, 2'b0 } == 9'h190) ? 5'd4 :
        ({ ADR_I, 2'b0 } == 9'h194) ? 5'd5 :
        ({ ADR_I, 2'b0 } == 9'h198) ? 5'd6 :
        ({ ADR_I, 2'b0 } == 9'h19C) ? 5'd7 :
        ({ ADR_I, 2'b0 } == 9'h1A0) ? 5'd8 :
        ({ ADR_I, 2'b0 } == 9'h1A4) ? 5'd9 :
        ({ ADR_I, 2'b0 } == 9'h1A8) ? 5'd10 :
        ({ ADR_I, 2'b0 } == 9'h1AC) ? 5'd11 :
        ({ ADR_I, 2'b0 } == 9'h1B0) ? 5'd12 :
        ({ ADR_I, 2'b0 } == 9'h1B4) ? 5'd13 :
        ({ ADR_I, 2'b0 } == 9'h1B8) ? 5'd14 :
        ({ ADR_I, 2'b0 } == 9'h1BC) ? 5'd15 :
        ({ ADR_I, 2'b0 } == 9'h08C) ? 5'd16 :
        ({ ADR_I, 2'b0 } == 9'h090) ? 5'd17 :
        ({ ADR_I, 2'b0 } == 9'h098) ? 5'd18 :
        5'd19
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .bpl_color(bpl_color),
    .sprite0_color(sprite0_color),
    .sprite1_color(sprite1_color),
    .sprite01_attached(sprite01_attached),
    .sprite2_color(sprite2_color),
    .sprite3_color(sprite3_color),
    .sprite23_attached(sprite23_attached),
    .sprite4_color(sprite4_color),
    .sprite5_color(sprite5_color),
    .sprite45_attached(sprite45_attached),
    .sprite6_color(sprite6_color),
    .sprite7_color(sprite7_color),
    .sprite67_attached(sprite67_attached),
 
    .clx_dat_read(na_clx_dat_read),
    .clx_dat(na_clx_dat),
 
    .bpl_con0(bpl_con0),
 
    // video interface
    .burst_write_request(burst_write_request),
    .burst_write_address(burst_write_address),
    .burst_write_data(burst_write_data),
    .burst_write_ready(burst_write_ready)
);
 
wire bitplains_write_ena;
assign bitplains_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    (   ({ ADR_I, 2'b0 } >= 9'h0E0 && { ADR_I, 2'b0 } <= 9'h0F4) || 
        { ADR_I, 2'b0 } == 9'h090 ||
        { ADR_I, 2'b0 } == 9'h094 ||
        { ADR_I, 2'b0 } == 9'h100 ||
        { ADR_I, 2'b0 } == 9'h108 ||
        ({ ADR_I, 2'b0 } >= 9'h110 && { ADR_I, 2'b0 } <= 9'h118)
    ));
 
wire [7:0] disabled_sprites;
 
bitplains bitplains_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    // video interface - read
    .burst_read_enabled(dma_con[9] == 1'b1 && dma_con[8] == 1'b1 && window_line_enable == 1'b1),
	.burst_read_request(burst_read_request),
	.burst_read_address(burst_read_address),
	.burst_read_ready(burst_read_ready),
	.burst_read_data(burst_read_data),
 
    .write_ena(bitplains_write_ena),
    // 0:   BPL1PTH,    BPL1PTL,
    // 1:   BPL2PTH,    BPL2PTL,
    // 2:   BPL3PTH,    BPL3PTL,
    // 3:   BPL4PTH,    BPL4PTL,
    // 4:   BPL5PTH,    BPL5PTL,
    // 5:   BPL6PTH,    BPL6PTL,
    // 6:       DDFSTRT [15:0],     DIWSTOP [31:16], *
    // 7:       DDFSTOP [31:16],    DMACON  [15:0], *
    // 8:   BPLCON0,    BPLCON1,
    // 9:   BPL1MOD,    BPL2MOD,
    // 10:  BPL1DAT,    BPL2DAT,
    // 11:  BPL3DAT,    BPL4DAT,
    // 12:  BPL5DAT,    BPL6DAT
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h0E0) ? 4'd0 :
        ({ ADR_I, 2'b0 } == 9'h0E4) ? 4'd1 :
        ({ ADR_I, 2'b0 } == 9'h0E8) ? 4'd2 :
        ({ ADR_I, 2'b0 } == 9'h0EC) ? 4'd3 :
        ({ ADR_I, 2'b0 } == 9'h0F0) ? 4'd4 :
        ({ ADR_I, 2'b0 } == 9'h0F4) ? 4'd5 :
        ({ ADR_I, 2'b0 } == 9'h090) ? 4'd6 :
        ({ ADR_I, 2'b0 } == 9'h094) ? 4'd7 :
        ({ ADR_I, 2'b0 } == 9'h100) ? 4'd8 :
        ({ ADR_I, 2'b0 } == 9'h108) ? 4'd9 :
        ({ ADR_I, 2'b0 } == 9'h110) ? 4'd10 :
        ({ ADR_I, 2'b0 } == 9'h114) ? 4'd11 :
        4'd12
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .disable_sprites(disabled_sprites),
    .bpl_con0(bpl_con0),
    .color(bpl_color),
 
    .line_number(line_number)
);
 
wire sprite0_write_ena;
wire sprite0_dma_req;
wire [31:2] sprite0_dma_address;
 
assign sprite0_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h120 || { ADR_I, 2'b0 } == 9'h140 || { ADR_I, 2'b0 } == 9'h144) );
 
sprite sprite0_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[0] == 1'b0),
    .dma_req(sprite0_dma_req),
    .dma_address(sprite0_dma_address),
    .dma_done(dma_select == 4'd1 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite0_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h120) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h140) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(),
    .color(sprite0_color)
);
 
wire sprite1_write_ena;
wire sprite1_dma_req;
wire [31:2] sprite1_dma_address;
 
assign sprite1_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h124 || { ADR_I, 2'b0 } == 9'h148 || { ADR_I, 2'b0 } == 9'h14C) );
 
sprite sprite1_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[1] == 1'b0),
    .dma_req(sprite1_dma_req),
    .dma_address(sprite1_dma_address),
    .dma_done(dma_select == 4'd2 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite1_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h124) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h148) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(sprite01_attached),
    .color(sprite1_color)
);
 
wire sprite2_write_ena;
wire sprite2_dma_req;
wire [31:2] sprite2_dma_address;
 
assign sprite2_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h128 || { ADR_I, 2'b0 } == 9'h150 || { ADR_I, 2'b0 } == 9'h154) );
 
sprite sprite2_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[2] == 1'b0),
    .dma_req(sprite2_dma_req),
    .dma_address(sprite2_dma_address),
    .dma_done(dma_select == 4'd3 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite2_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h128) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h150) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(),
    .color(sprite2_color)
);
 
wire sprite3_write_ena;
wire sprite3_dma_req;
wire [31:2] sprite3_dma_address;
 
assign sprite3_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h12C || { ADR_I, 2'b0 } == 9'h158 || { ADR_I, 2'b0 } == 9'h15C) );
 
sprite sprite3_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[3] == 1'b0),
    .dma_req(sprite3_dma_req),
    .dma_address(sprite3_dma_address),
    .dma_done(dma_select == 4'd4 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite3_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h12C) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h158) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(sprite23_attached),
    .color(sprite3_color)
);
 
wire sprite4_write_ena;
wire sprite4_dma_req;
wire [31:2] sprite4_dma_address;
 
assign sprite4_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h130 || { ADR_I, 2'b0 } == 9'h160 || { ADR_I, 2'b0 } == 9'h164) );
 
sprite sprite4_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[4] == 1'b0),
    .dma_req(sprite4_dma_req),
    .dma_address(sprite4_dma_address),
    .dma_done(dma_select == 4'd5 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite4_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h130) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h160) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(),
    .color(sprite4_color)
);
 
wire sprite5_write_ena;
wire sprite5_dma_req;
wire [31:2] sprite5_dma_address;
 
assign sprite5_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h134 || { ADR_I, 2'b0 } == 9'h168 || { ADR_I, 2'b0 } == 9'h16C) );
 
sprite sprite5_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[5] == 1'b0),
    .dma_req(sprite5_dma_req),
    .dma_address(sprite5_dma_address),
    .dma_done(dma_select == 4'd6 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite5_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h134) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h168) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(sprite45_attached),
    .color(sprite5_color)
);
 
wire sprite6_write_ena;
wire sprite6_dma_req;
wire [31:2] sprite6_dma_address;
 
assign sprite6_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h138 || { ADR_I, 2'b0 } == 9'h170 || { ADR_I, 2'b0 } == 9'h174) );
 
sprite sprite6_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[6] == 1'b0),
    .dma_req(sprite6_dma_req),
    .dma_address(sprite6_dma_address),
    .dma_done(dma_select == 4'd7 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite6_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h138) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h170) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(),
    .color(sprite6_color)
);
 
wire sprite7_write_ena;
wire sprite7_dma_req;
wire [31:2] sprite7_dma_address;
 
assign sprite7_write_ena = (CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1 && ACK_O == 1'b0 &&
    ({ ADR_I, 2'b0 } == 9'h13C || { ADR_I, 2'b0 } == 9'h178 || { ADR_I, 2'b0 } == 9'h17C) );
 
sprite sprite7_inst(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .line_number(line_number),
    .column_number(column_number),
 
    .dma_ena(dma_con[9] == 1'b1 && dma_con[5] == 1'b1 && disabled_sprites[7] == 1'b0),
    .dma_req(sprite7_dma_req),
    .dma_address(sprite7_dma_address),
    .dma_done(dma_select == 4'd8 && ACK_I == 1'b1),
    .dma_data(master_DAT_I),
 
    .write_ena(sprite7_write_ena),
    // 0:   SPR0PTH,    SPR0PTL,
    // 1:   SPR0POS,    SPR0CTL,
    // 2:   SPR0DATA,   SPR0DATB,
    .write_address(
        ({ ADR_I, 2'b0 } == 9'h13C) ? 2'd0 :
        ({ ADR_I, 2'b0 } == 9'h178) ? 2'd1 :
        2'd2
    ),
    .write_data(slave_DAT_I),
    .write_sel(SEL_I),
 
    .attached(sprite67_attached),
    .color(sprite7_color)
);
 
always @(posedge CLK_I or negedge reset_n) begin
    if(reset_n == 1'b0) begin
        CYC_O <= 1'b0;
        STB_O <= 1'b0;
        ADR_O <= 19'd0;
        ACK_O <= 1'b0;
    end
    else begin
        // write/read registers as slave
 
        // if start of new line:
        //      get sprite data as master
        //      get bitmap data as master
 
        // concurrent save line to video memory
 
        if(ACK_O == 1'b1) begin
            ACK_O <= 1'b0;
        end
        else if((CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b0) || priority_write_ena == 1'b1 || bitplains_write_ena == 1'b1 ||
            sprite0_write_ena == 1'b1 || sprite1_write_ena == 1'b1 || sprite2_write_ena == 1'b1 || sprite3_write_ena == 1'b1 || 
            sprite4_write_ena == 1'b1 || sprite5_write_ena == 1'b1 || sprite6_write_ena == 1'b1 || sprite7_write_ena == 1'b1 ||
            na_int_ena_write == 1'b1 || na_dma_con_write == 1'b1)
        begin
            ACK_O <= 1'b1;
        end
 
        if(CYC_O == 1'b0 && STB_O == 1'b0 && dma_select != 4'd0) begin
            ADR_O <= dma_address_select;
            CYC_O <= 1'b1;
            STB_O <= 1'b1;
        end
        else if(CYC_O == 1'b1 && STB_O == 1'b1 && ACK_I == 1'b1) begin
            CYC_O <= 1'b0;
            STB_O <= 1'b0;
        end
 
        // WARNING: disable sprites if ddf_start early
        // WARNING: line_start, line_number and line_ena change at once
    end
end
 
endmodule
 
/*! \brief Video output generator from bitplain and sprite video data input.
 */
module video_priority(
    input CLK_I,
    input reset_n,
 
    input line_start,
    input line_pre_start,
    input [8:0] line_number,
    input [8:0] column_number,
    output window_line_enable,
 
    input write_ena,
    // 0:   COLOR00,    COLOR01,
    // 1:   COLOR02,    COLOR03,
    // 2:   COLOR04,    COLOR05,
    // 3:   COLOR06,    COLOR07,
    // 4:   COLOR08,    COLOR09,
    // 5:   COLOR10,    COLOR11,
    // 6:   COLOR12,    COLOR13,
    // 7:   COLOR14,    COLOR15,
    // 8:   COLOR16,    COLOR17,
    // 9:   COLOR18,    COLOR19,
    // 10:  COLOR20,    COLOR21,
    // 11:  COLOR22,    COLOR23,
    // 12:  COLOR24,    COLOR25,
    // 13:  COLOR26,    COLOR27,
    // 14:  COLOR28,    COLOR29,
    // 15:  COLOR30,    COLOR31,
    // 16:      DIWSTRT [15:0],     COPINS      [31:16], * COPINS not implemented  
    // 17:      DIWSTOP [31:16],    DDFSTART    [15:0], *
    // 18:      CLXCON  [31:16],    INTENA      [15:0], *
    // 19:      BPLCON2 [31:16],    NOT USED    [15:0],
    // read:    CLXDAT  [15:0],     JOY1DAT     [31:16] *
    input [4:0] write_address,
    input [31:0] write_data,
    input [3:0] write_sel,
 
    input [5:0] bpl_color,
    input [1:0] sprite0_color,
    input [1:0] sprite1_color,
    input sprite01_attached,
    input [1:0] sprite2_color,
    input [1:0] sprite3_color,
    input sprite23_attached,
    input [1:0] sprite4_color,
    input [1:0] sprite5_color,
    input sprite45_attached,
    input [1:0] sprite6_color,
    input [1:0] sprite7_color,
    input sprite67_attached,
 
    input clx_dat_read,
    output reg [15:0] clx_dat,
 
    input [10:0] bpl_con0,
 
    // video interface
    output reg burst_write_request,
    output reg [31:2] burst_write_address,
    output [35:0] burst_write_data,
    input burst_write_ready
);
 
// lowres, noninterlaced
reg [15:0] diw_start;
wire [8:0] hstart;
wire [8:0] vstart;
assign hstart = { 1'b0, diw_start[7:0] };
assign vstart = { 1'b0, diw_start[15:8]};
 
// lowres, noninterlaced
reg [15:0] diw_stop;
wire [8:0] hstop;
wire [8:0] vstop;
assign hstop = { 1'b1, diw_stop[7:0] };
assign vstop = { ~diw_stop[15], diw_stop[15:8] };
 
assign window_line_enable = (line_number >= vstart && line_number < vstop);
wire screen_line_enable;
assign screen_line_enable = (line_number >= 9'h2C && line_number < 9'h12C);
 
// output reg [15:0] clx_dat;
reg [15:0] clx_con;
 
reg [6:0] bpl_con2;
 
reg [11:0] color00;
reg [11:0] color01;
reg [11:0] color02;
reg [11:0] color03;
reg [11:0] color04;
reg [11:0] color05;
reg [11:0] color06;
reg [11:0] color07;
reg [11:0] color08;
reg [11:0] color09;
reg [11:0] color10;
reg [11:0] color11;
reg [11:0] color12;
reg [11:0] color13;
reg [11:0] color14;
reg [11:0] color15;
reg [11:0] color16;
reg [11:0] color17;
reg [11:0] color18;
reg [11:0] color19;
reg [11:0] color20;
reg [11:0] color21;
reg [11:0] color22;
reg [11:0] color23;
reg [11:0] color24;
reg [11:0] color25;
reg [11:0] color26;
reg [11:0] color27;
reg [11:0] color28;
reg [11:0] color29;
reg [11:0] color30;
reg [11:0] color31;
 
reg [7:0] line_ram_addr;
reg [35:0] line_ram_data;
altsyncram line_ram_inst(
	.clock0(CLK_I),
 
	.address_a(line_ram_addr),
	.wren_a(screen_line_enable == 1'b1 && column_number >= 9'h81 &&
	    ((column_number == 9'h1C1 && line_ram_counter == 3'd1) || (column_number < 9'h1C1 && line_ram_counter == 3'd3))),
	.data_a(
        (column_number == 9'h1C1 && line_ram_counter == 3'd1)? { final_color_value, 24'd0 } : { line_ram_data[23:0], final_color_value }
    ),
	.q_a(burst_write_data)
);
defparam 
    line_ram_inst.operation_mode = "SINGLE_PORT",
    line_ram_inst.width_a = 36,
    line_ram_inst.widthad_a = 8;
 
// Collision detection
// - sprite group - sprite group(1); sprite group - any playfield(2); playfield - playfield(3)
// - clxdat: does not depend on dual-playfield mode
// - clxcon: all disabled bitplains = collision always detected
wire clx_sprite_group_01;
assign clx_sprite_group_01 = (sprite0_color != 2'b00 || (clx_con[12] == 1'b1 && sprite1_color != 2'b00));
wire clx_sprite_group_23;
assign clx_sprite_group_23 = (sprite2_color != 2'b00 || (clx_con[13] == 1'b1 && sprite3_color != 2'b00));
wire clx_sprite_group_45;
assign clx_sprite_group_45 = (sprite4_color != 2'b00 || (clx_con[14] == 1'b1 && sprite5_color != 2'b00));
wire clx_sprite_group_67;
assign clx_sprite_group_67 = (sprite6_color != 2'b00 || (clx_con[15] == 1'b1 && sprite7_color != 2'b00));
 
wire clx_even_bpls;
assign clx_even_bpls =  (clx_con[7] == 1'b1 && clx_con[1] == bpl_color[1]) ||  (clx_con[9] == 1'b1 && clx_con[3] == bpl_color[3]) ||
                        (clx_con[11] == 1'b1 && clx_con[5] == bpl_color[5]);
wire clx_odd_bpls;
assign clx_odd_bpls =   (clx_con[6] == 1'b1 && clx_con[0] == bpl_color[0]) || (clx_con[8] == 1'b1 && clx_con[2] == bpl_color[2]) ||
                        (clx_con[10] == 1'b1 && clx_con[4] == bpl_color[4]);
 
wire [14:0] clx_detected;
assign clx_detected = {
    clx_sprite_group_45 && clx_sprite_group_67,
    clx_sprite_group_23 && clx_sprite_group_67,
    clx_sprite_group_23  && clx_sprite_group_45,
    clx_sprite_group_01 && clx_sprite_group_67,
    clx_sprite_group_01  && clx_sprite_group_45,
    clx_sprite_group_01  && clx_sprite_group_23,
    (clx_con[11:6] == 6'd0) || (clx_even_bpls == 1'b1 && clx_sprite_group_67),
    (clx_con[11:6] == 6'd0) || (clx_even_bpls == 1'b1 && clx_sprite_group_45),
    (clx_con[11:6] == 6'd0) || (clx_even_bpls == 1'b1 && clx_sprite_group_23),
    (clx_con[11:6] == 6'd0) || (clx_even_bpls == 1'b1 && clx_sprite_group_01),
    (clx_con[11:6] == 6'd0) || (clx_odd_bpls == 1'b1 && clx_sprite_group_67),
    (clx_con[11:6] == 6'd0) || (clx_odd_bpls == 1'b1 && clx_sprite_group_45),
    (clx_con[11:6] == 6'd0) || (clx_odd_bpls == 1'b1 && clx_sprite_group_23),
    (clx_con[11:6] == 6'd0) || (clx_odd_bpls == 1'b1 && clx_sprite_group_01),
    (clx_con[11:6] == 6'd0) || (clx_odd_bpls == 1'b1 && clx_even_bpls == 1'b1)
};
 
// Video priority
// - sprite 0 more important than 1,2,3,4,5,6,7
// - for priority and collision, sprite groups: 0-1, 2-3, 4-5, 6-7
// - PF2PRI = 0, PF1 more important than PF2; PF2PRI = 1, PL2 more important than PL1
// - PF2P2 - PF2P0 for non-dual playfield or PF2
// - PF1P2 - PF1P0 for PF1
//      - PF(0) SP01 PF(1) SP23 PF(2) SP45 PF(3) SP67 PF(4)
//
// Dual-playfield: bpl_con0[5] == 1'b1; PF1 color lookup 0(transparent)-7; PF2 color lookup 8(transparent)-15
// Attached sprites: 0-1, 2-3, 4-5, 6-7; color 16(transparent)-31; { sprite1_color[1:0], sprite0_color[1:0] }
wire [4:0] spr0_color;
assign spr0_color = { 1'b1, (sprite01_attached == 1'b1)? sprite1_color[1:0] : 2'b00, sprite0_color[1:0] };
wire [4:0] spr1_color;
assign spr1_color = (sprite01_attached == 1'b1)? spr0_color : { 3'b100, sprite1_color[1:0] };
 
wire [4:0] spr2_color;
assign spr2_color = { 1'b1, (sprite23_attached == 1'b1)? sprite3_color[1:0] : 2'b01, sprite2_color[1:0] };
wire [4:0] spr3_color;
assign spr3_color = (sprite23_attached == 1'b1)? spr2_color : { 3'b101, sprite3_color[1:0] };
 
wire [4:0] spr4_color;
assign spr4_color = { 1'b1, (sprite45_attached == 1'b1)? sprite5_color[1:0] : 2'b10, sprite4_color[1:0] };
wire [4:0] spr5_color;
assign spr5_color = (sprite45_attached == 1'b1)? spr4_color : { 3'b110, sprite5_color[1:0] };
 
wire [4:0] spr6_color;
assign spr6_color = { 1'b1, (sprite67_attached == 1'b1)? sprite7_color[1:0] : 2'b11, sprite6_color[1:0] };
wire [4:0] spr7_color;
assign spr7_color = (sprite67_attached == 1'b1)? spr6_color : { 3'b111, sprite7_color[1:0] };
 
wire sprite01_exists;
assign sprite01_exists = (sprite0_color[1:0] != 2'b00 || sprite1_color[1:0] != 2'b00);
wire sprite23_exists;
assign sprite23_exists = (sprite2_color[1:0] != 2'b00 || sprite3_color[1:0] != 2'b00);
wire sprite45_exists;
assign sprite45_exists = (sprite4_color[1:0] != 2'b00 || sprite5_color[1:0] != 2'b00);
wire sprite67_exists;
assign sprite67_exists = (sprite6_color[1:0] != 2'b00 || sprite7_color[1:0] != 2'b00);
 
wire [4:0] pf1_color;
assign pf1_color = { 2'b0, bpl_color[4], bpl_color[2], bpl_color[0] };
 
// Dual playfield: bpl_con0[5] == 1'b1
wire [4:0] pf2_color;
assign pf2_color = (bpl_con0[5] == 1'b1) ? { 2'b1, bpl_color[5], bpl_color[3], bpl_color[1] } : bpl_color[4:0];
 
// bpl_con2[6] PF2PRI
wire pf1_exists;
assign pf1_exists = (bpl_con0[5] == 1'b1) ?
    ((bpl_con2[6] == 1'b0) ? (pf1_color[2:0] != 3'd0) : ((pf2_color[2:0] != 3'd0) ? 1'b0 : (pf1_color[2:0] != 3'd0))) :
    1'b0;
 
wire pf2_exists;
assign pf2_exists = (bpl_con0[5] == 1'b1) ?
    ((bpl_con2[6] == 1'b1) ? (pf2_color[2:0] != 3'd0) : ((pf1_color[2:0] != 3'd0) ? 1'b0 : (pf2_color[2:0] != 3'd0))) :
    (pf2_color[4:0] != 5'd0);
 
wire [4:0] final_color;
assign final_color =
    (pf1_exists == 1'b1 && bpl_con2[2:0] == 3'd0) ? pf1_color :
    (pf2_exists == 1'b1 && bpl_con2[5:3] == 3'd0) ? pf2_color :
    (sprite01_exists) ? ((sprite0_color[1:0] != 2'b00) ? spr0_color : spr1_color) :
    (pf1_exists == 1'b1 && bpl_con2[2:0] == 3'd1) ? pf1_color :
    (pf2_exists == 1'b1 && bpl_con2[5:3] == 3'd1) ? pf2_color :
    (sprite23_exists) ? ((sprite2_color[1:0] != 2'b00) ? spr2_color : spr3_color) :
    (pf1_exists == 1'b1 && bpl_con2[2:0] == 3'd2) ? pf1_color :
    (pf2_exists == 1'b1 && bpl_con2[5:3] == 3'd2) ? pf2_color :
    (sprite45_exists) ? ((sprite4_color[1:0] != 2'b00) ? spr4_color : spr5_color) : 
    (pf1_exists == 1'b1 && bpl_con2[2:0] == 3'd3) ? pf1_color :
    (pf2_exists == 1'b1 && bpl_con2[5:3] == 3'd3) ? pf2_color :
    (sprite67_exists) ? ((sprite6_color[1:0] != 2'b00) ? spr6_color : spr7_color) :
    (pf1_exists == 1'b1 && bpl_con2[2:0] == 3'd4) ? pf1_color :
    (pf2_exists == 1'b1 && bpl_con2[5:3] == 3'd4) ? pf2_color :
    4'd0;
 
// HAM mode
// - Hold and Modify mode, 4096 colors on screen at once, 6-bit pixel: 2-bit control + 4-bit data:
//            - set[6'b00DDDD bpl654321](data - regular color lookup 0-15)
//            - modify-red 6'b01DDDDDD, modify-green 6'b10DDDDDD, modify-blue 6'b11DDDDDD (data - modify that component, leave rest unchanged)
// EHB mode 
wire ham_enabled;
assign ham_enabled = (bpl_con0[6] == 1'b1 && bpl_con0[5] == 1'b0 && bpl_con0[10] == 1'b0 && (bpl_con0[9:7] == 3'd6 || bpl_con0[9:7] == 3'd5));
wire ehb_enabled;
assign ehb_enabled = (bpl_con0[6] == 1'b0 && bpl_con0[5] == 1'b0 && bpl_con0[10] == 1'b0 && bpl_con0[9:7] == 3'd6);
 
wire [11:0] color_value_before_ehb;
assign color_value_before_ehb =
    (ham_enabled == 1'b1 && bpl_color[5:4] == 2'b01) ? { bpl_color[3:0], last_color_value[7:0] } :
    (ham_enabled == 1'b1 && bpl_color[5:4] == 2'b10) ? { last_color_value[11:8], bpl_color[3:0], last_color_value[3:0] } :
    (ham_enabled == 1'b1 && bpl_color[5:4] == 2'b11) ? { last_color_value[11:4], bpl_color[3:0] } :
    (final_color == 5'd0) ? color00 :
    (final_color == 5'd1) ? color01 :
    (final_color == 5'd2) ? color02 :
    (final_color == 5'd3) ? color03 :
    (final_color == 5'd4) ? color04 :
    (final_color == 5'd5) ? color05 :
    (final_color == 5'd6) ? color06 :
    (final_color == 5'd7) ? color07 :
    (final_color == 5'd8) ? color08 :
    (final_color == 5'd9) ? color09 :
    (final_color == 5'd10) ? color10 :
    (final_color == 5'd11) ? color11 :
    (final_color == 5'd12) ? color12 :
    (final_color == 5'd13) ? color13 :
    (final_color == 5'd14) ? color14 :
    (final_color == 5'd15) ? color15 :
    (final_color == 5'd16) ? color16 :
    (final_color == 5'd17) ? color17 :
    (final_color == 5'd18) ? color18 :
    (final_color == 5'd19) ? color19 :
    (final_color == 5'd20) ? color20 :
    (final_color == 5'd21) ? color21 :
    (final_color == 5'd22) ? color22 :
    (final_color == 5'd23) ? color23 :
    (final_color == 5'd24) ? color24 :
    (final_color == 5'd25) ? color25 :
    (final_color == 5'd26) ? color26 :
    (final_color == 5'd27) ? color27 :
    (final_color == 5'd28) ? color28 :
    (final_color == 5'd29) ? color29 :
    (final_color == 5'd30) ? color30 :
    color31;
 
wire [11:0] final_color_value;
assign final_color_value =
    (column_number < hstart || column_number > hstop) ? color00 :
    (ehb_enabled == 1'b1) ? { 1'b0,color_value_before_ehb[11:9], 1'b0,color_value_before_ehb[7:5], 1'b0,color_value_before_ehb[3:1] } :
    color_value_before_ehb;
 
reg [11:0] last_color_value;
reg [2:0] line_ram_counter;
always @(posedge CLK_I or negedge reset_n) begin
    if(reset_n == 1'b0) begin
        clx_dat <= 16'd0;
        burst_write_request <= 1'b0;
        burst_write_address <= 30'd0;
        diw_start <= 16'h2C81;
        diw_stop <= 16'h2CC1;
        clx_con <= 16'd0;
        bpl_con2 <= 7'd0;
        color00 <= 12'd0;
        color01 <= 12'd0;
        color02 <= 12'd0;
        color03 <= 12'd0;
        color04 <= 12'd0;
        color05 <= 12'd0;
        color06 <= 12'd0;
        color07 <= 12'd0;
        color08 <= 12'd0;
        color09 <= 12'd0;
        color10 <= 12'd0;
        color11 <= 12'd0;
        color12 <= 12'd0;
        color13 <= 12'd0;
        color14 <= 12'd0;
        color15 <= 12'd0;
        color16 <= 12'd0;
        color17 <= 12'd0;
        color18 <= 12'd0;
        color19 <= 12'd0;
        color20 <= 12'd0;
        color21 <= 12'd0;
        color22 <= 12'd0;
        color23 <= 12'd0;
        color24 <= 12'd0;
        color25 <= 12'd0;
        color26 <= 12'd0;
        color27 <= 12'd0;
        color28 <= 12'd0;
        color29 <= 12'd0;
        color30 <= 12'd0;
        color31 <= 12'd0;
        line_ram_addr <= 8'd0;
        line_ram_data <= 36'd0;
        last_color_value <= 12'd0;
        line_ram_counter <= 3'd0;
    end
    else begin
 
        if(screen_line_enable == 1'b1 && column_number >= 9'h81 && column_number == 9'h1C1 && line_ram_counter == 3'd1) begin
            line_ram_addr <= 8'd0;
            line_ram_counter <= 3'd4;
            last_color_value <= final_color_value;
        end
        else if(screen_line_enable == 1'b1 && column_number >= 9'h81 && column_number < 9'h1C1 && line_ram_counter == 3'd3) begin
            line_ram_addr <= line_ram_addr + 8'd1;
            line_ram_counter <= 3'd1;
            last_color_value <= final_color_value;
        end
        else if(screen_line_enable == 1'b1 && column_number >= 9'h81 && column_number < 9'h1C1 && line_ram_counter == 3'd0) begin
            line_ram_counter <= line_ram_counter + 3'd1;
        end
        else if(screen_line_enable == 1'b1 && column_number >= 9'h81 && column_number < 9'h1C1 && line_ram_counter < 3'd4) begin
            line_ram_counter <= line_ram_counter + 3'd1;
            line_ram_data <= { line_ram_data[23:0], final_color_value };
            last_color_value <= final_color_value;
        end
        else if(line_ram_counter == 3'd4) begin
            if(burst_write_ready == 1'b1 && line_ram_addr <= 8'd213) begin
                line_ram_addr <= line_ram_addr + 8'd1;
            end
            else if(burst_write_ready == 1'b1 && line_ram_addr == 8'd214) begin
                line_ram_counter <= 3'd0;
            end
        end
        else begin
            line_ram_addr <= 8'd0;
            last_color_value <= color00;
            line_ram_counter <= 3'd0;
        end
 
        if(screen_line_enable == 1'b1 && column_number >= 9'h81 && column_number == 9'h1C1 && line_ram_counter == 3'd1) begin
            burst_write_request <= 1'b1;
        end
        else if(line_ram_counter == 3'd4 && line_ram_addr == 8'd214) begin
            burst_write_request <= 1'b0;
            burst_write_address <= burst_write_address + 30'd216; // 640/3 = 213.(3) = 214 + 2 for %4 =0
        end
        else if(screen_line_enable == 1'b0) begin
            burst_write_address <= `VIDEO_BUFFER_DIV_4; // start of video buffer
        end
 
        if(clx_dat_read == 1'b1) clx_dat <= 16'd0;
        else clx_dat <= clx_dat | { 1'b0, clx_detected };
 
        if(write_ena == 1'b1) begin
            if(write_address == 5'd0 && write_sel[0] == 1'b1) color01[7:0] <= write_data[7:0];
            if(write_address == 5'd0 && write_sel[1] == 1'b1) color01[11:8] <= write_data[11:8];
            if(write_address == 5'd0 && write_sel[2] == 1'b1) color00[7:0] <= write_data[23:16];
            if(write_address == 5'd0 && write_sel[3] == 1'b1) color00[11:8] <= write_data[27:24];
            if(write_address == 5'd1 && write_sel[0] == 1'b1) color03[7:0] <= write_data[7:0];
            if(write_address == 5'd1 && write_sel[1] == 1'b1) color03[11:8] <= write_data[11:8];
            if(write_address == 5'd1 && write_sel[2] == 1'b1) color02[7:0] <= write_data[23:16];
            if(write_address == 5'd1 && write_sel[3] == 1'b1) color02[11:8] <= write_data[27:24];
            if(write_address == 5'd2 && write_sel[0] == 1'b1) color05[7:0] <= write_data[7:0];
            if(write_address == 5'd2 && write_sel[1] == 1'b1) color05[11:8] <= write_data[11:8];
            if(write_address == 5'd2 && write_sel[2] == 1'b1) color04[7:0] <= write_data[23:16];
            if(write_address == 5'd2 && write_sel[3] == 1'b1) color04[11:8] <= write_data[27:24];
            if(write_address == 5'd3 && write_sel[0] == 1'b1) color07[7:0] <= write_data[7:0];
            if(write_address == 5'd3 && write_sel[1] == 1'b1) color07[11:8] <= write_data[11:8];
            if(write_address == 5'd3 && write_sel[2] == 1'b1) color06[7:0] <= write_data[23:16];
            if(write_address == 5'd3 && write_sel[3] == 1'b1) color06[11:8] <= write_data[27:24];
            if(write_address == 5'd4 && write_sel[0] == 1'b1) color09[7:0] <= write_data[7:0];
            if(write_address == 5'd4 && write_sel[1] == 1'b1) color09[11:8] <= write_data[11:8];
            if(write_address == 5'd4 && write_sel[2] == 1'b1) color08[7:0] <= write_data[23:16];
            if(write_address == 5'd4 && write_sel[3] == 1'b1) color08[11:8] <= write_data[27:24];
            if(write_address == 5'd5 && write_sel[0] == 1'b1) color11[7:0] <= write_data[7:0];
            if(write_address == 5'd5 && write_sel[1] == 1'b1) color11[11:8] <= write_data[11:8];
            if(write_address == 5'd5 && write_sel[2] == 1'b1) color10[7:0] <= write_data[23:16];
            if(write_address == 5'd5 && write_sel[3] == 1'b1) color10[11:8] <= write_data[27:24];
            if(write_address == 5'd6 && write_sel[0] == 1'b1) color13[7:0] <= write_data[7:0];
            if(write_address == 5'd6 && write_sel[1] == 1'b1) color13[11:8] <= write_data[11:8];
            if(write_address == 5'd6 && write_sel[2] == 1'b1) color12[7:0] <= write_data[23:16];
            if(write_address == 5'd6 && write_sel[3] == 1'b1) color12[11:8] <= write_data[27:24];
            if(write_address == 5'd7 && write_sel[0] == 1'b1) color15[7:0] <= write_data[7:0];
            if(write_address == 5'd7 && write_sel[1] == 1'b1) color15[11:8] <= write_data[11:8];
            if(write_address == 5'd7 && write_sel[2] == 1'b1) color14[7:0] <= write_data[23:16];
            if(write_address == 5'd7 && write_sel[3] == 1'b1) color14[11:8] <= write_data[27:24];
            if(write_address == 5'd8 && write_sel[0] == 1'b1) color17[7:0] <= write_data[7:0];
            if(write_address == 5'd8 && write_sel[1] == 1'b1) color17[11:8] <= write_data[11:8];
            if(write_address == 5'd8 && write_sel[2] == 1'b1) color16[7:0] <= write_data[23:16];
            if(write_address == 5'd8 && write_sel[3] == 1'b1) color16[11:8] <= write_data[27:24];
            if(write_address == 5'd9 && write_sel[0] == 1'b1) color19[7:0] <= write_data[7:0];
            if(write_address == 5'd9 && write_sel[1] == 1'b1) color19[11:8] <= write_data[11:8];
            if(write_address == 5'd9 && write_sel[2] == 1'b1) color18[7:0] <= write_data[23:16];
            if(write_address == 5'd9 && write_sel[3] == 1'b1) color18[11:8] <= write_data[27:24];
            if(write_address == 5'd10 && write_sel[0] == 1'b1) color21[7:0] <= write_data[7:0];
            if(write_address == 5'd10 && write_sel[1] == 1'b1) color21[11:8] <= write_data[11:8];
            if(write_address == 5'd10 && write_sel[2] == 1'b1) color20[7:0] <= write_data[23:16];
            if(write_address == 5'd10 && write_sel[3] == 1'b1) color20[11:8] <= write_data[27:24];
            if(write_address == 5'd11 && write_sel[0] == 1'b1) color23[7:0] <= write_data[7:0];
            if(write_address == 5'd11 && write_sel[1] == 1'b1) color23[11:8] <= write_data[11:8];
            if(write_address == 5'd11 && write_sel[2] == 1'b1) color22[7:0] <= write_data[23:16];
            if(write_address == 5'd11 && write_sel[3] == 1'b1) color22[11:8] <= write_data[27:24];
            if(write_address == 5'd12 && write_sel[0] == 1'b1) color25[7:0] <= write_data[7:0];
            if(write_address == 5'd12 && write_sel[1] == 1'b1) color25[11:8] <= write_data[11:8];
            if(write_address == 5'd12 && write_sel[2] == 1'b1) color24[7:0] <= write_data[23:16];
            if(write_address == 5'd12 && write_sel[3] == 1'b1) color24[11:8] <= write_data[27:24];
            if(write_address == 5'd13 && write_sel[0] == 1'b1) color27[7:0] <= write_data[7:0];
            if(write_address == 5'd13 && write_sel[1] == 1'b1) color27[11:8] <= write_data[11:8];
            if(write_address == 5'd13 && write_sel[2] == 1'b1) color26[7:0] <= write_data[23:16];
            if(write_address == 5'd13 && write_sel[3] == 1'b1) color26[11:8] <= write_data[27:24];
            if(write_address == 5'd14 && write_sel[0] == 1'b1) color29[7:0] <= write_data[7:0];
            if(write_address == 5'd14 && write_sel[1] == 1'b1) color29[11:8] <= write_data[11:8];
            if(write_address == 5'd14 && write_sel[2] == 1'b1) color28[7:0] <= write_data[23:16];
            if(write_address == 5'd14 && write_sel[3] == 1'b1) color28[11:8] <= write_data[27:24];
            if(write_address == 5'd15 && write_sel[0] == 1'b1) color31[7:0] <= write_data[7:0];
            if(write_address == 5'd15 && write_sel[1] == 1'b1) color31[11:8] <= write_data[11:8];
            if(write_address == 5'd15 && write_sel[2] == 1'b1) color30[7:0] <= write_data[23:16];
            if(write_address == 5'd15 && write_sel[3] == 1'b1) color30[11:8] <= write_data[27:24];
            // 16:      DIWSTRT [15:0],     COPINS      [31:16], * COPINS not implemented  
            // 17:      DIWSTOP [31:16],    DDFSTART    [15:0], *
            // 18:      CLXCON  [31:16],    INTENA      [15:0], *
            // 19:      BPLCON2 [31:16],    NOT USED    [15:0],
            if(write_address == 5'd16 && write_sel[0] == 1'b1) diw_start[7:0] <= write_data[7:0];
            if(write_address == 5'd16 && write_sel[1] == 1'b1) diw_start[15:8] <= write_data[15:8];
            if(write_address == 5'd16 && write_sel[2] == 1'b1) ;
            if(write_address == 5'd16 && write_sel[3] == 1'b1) ;
            if(write_address == 5'd17 && write_sel[0] == 1'b1) ;
            if(write_address == 5'd17 && write_sel[1] == 1'b1) ;
            if(write_address == 5'd17 && write_sel[2] == 1'b1) diw_stop[7:0] <= write_data[23:16];
            if(write_address == 5'd17 && write_sel[3] == 1'b1) diw_stop[15:8] <= write_data[31:24];
            if(write_address == 5'd18 && write_sel[0] == 1'b1) ;
            if(write_address == 5'd18 && write_sel[1] == 1'b1) ;
            if(write_address == 5'd18 && write_sel[2] == 1'b1) clx_con[7:0] <= write_data[23:16];
            if(write_address == 5'd18 && write_sel[3] == 1'b1) clx_con[15:8] <= write_data[31:24];
            if(write_address == 5'd19 && write_sel[0] == 1'b1) ;
            if(write_address == 5'd19 && write_sel[1] == 1'b1) ;
            if(write_address == 5'd19 && write_sel[2] == 1'b1) bpl_con2 <= write_data[22:16];
            if(write_address == 5'd19 && write_sel[3] == 1'b1) ;
        end
    end
end
 
endmodule
 
/*! \brief Bitplain top level module with multiplexers to internal bitplain module instances.
 */
module bitplains(
    input CLK_I,
    input reset_n,
 
    input line_start,
    input [8:0] column_number,
 
    // video interface - read
    input burst_read_enabled,
	output burst_read_request,
	output [31:2] burst_read_address,
	input burst_read_ready,
	input [31:0] burst_read_data,  
 
    input write_ena,
    // 0:   BPL1PTH,    BPL1PTL,
    // 1:   BPL2PTH,    BPL2PTL,
    // 2:   BPL3PTH,    BPL3PTL,
    // 3:   BPL4PTH,    BPL4PTL,
    // 4:   BPL5PTH,    BPL5PTL,
    // 5:   BPL6PTH,    BPL6PTL,
    // 6:       DDFSTRT [15:0],     DIWSTOP [31:16], *
    // 7:       DDFSTOP [31:16],    DMACON  [15:0], *
    // 8:   BPLCON0,    BPLCON1,
    // 9:   BPL1MOD,    BPL2MOD,
    // 10:  BPL1DAT,    BPL2DAT,
    // 11:  BPL3DAT,    BPL4DAT,
    // 12:  BPL5DAT,    BPL6DAT
    input [3:0] write_address,
    input [31:0] write_data,
    input [3:0] write_sel,
 
    output [7:0] disable_sprites,
    output reg [10:0] bpl_con0,
    output [5:0] color,
 
    input [8:0] line_number
);
 
assign disable_sprites = {
    (ddf_start[8:3] < 6'd14),
    (ddf_start[8:3] < 6'd13),
    (ddf_start[8:3] < 6'd12),
    (ddf_start[8:3] < 6'd11),
    (ddf_start[8:3] < 6'd10),
    (ddf_start[8:3] < 6'd9),
    (ddf_start[8:3] < 6'd8),
    (ddf_start[8:3] < 6'd7)
};
 
reg [8:3] ddf_start;
reg [8:3] ddf_stop;
 
reg [31:0] bpl_modulo;
 
// output reg [10:0] bpl_con0
reg [7:0] bpl_con1;
 
reg [5:0] dma_reqs_reg;
wire [5:0] dma_reqs;
wire [2:0] selected_bpl;
assign selected_bpl = 
    (dma_reqs_reg[0] == 1'b1) ? 3'd1 :
    (dma_reqs_reg[1] == 1'b1) ? 3'd2 :
    (dma_reqs_reg[2] == 1'b1) ? 3'd3 :
    (dma_reqs_reg[3] == 1'b1) ? 3'd4 :
    (dma_reqs_reg[4] == 1'b1) ? 3'd5 :
    (dma_reqs_reg[5] == 1'b1) ? 3'd6 :
    3'd0;
 
assign burst_read_address =
    (selected_bpl == 3'd1) ? dma_address_1 :
    (selected_bpl == 3'd2) ? dma_address_2 :
    (selected_bpl == 3'd3) ? dma_address_3 :
    (selected_bpl == 3'd4) ? dma_address_4 :
    (selected_bpl == 3'd5) ? dma_address_5 :
    (selected_bpl == 3'd6) ? dma_address_6 :
    30'd0;
 
assign burst_read_request =
    (selected_bpl == 3'd1) ? dma_reqs[0] :
    (selected_bpl == 3'd2) ? dma_reqs[1] :
    (selected_bpl == 3'd3) ? dma_reqs[2] :
    (selected_bpl == 3'd4) ? dma_reqs[3] :
    (selected_bpl == 3'd5) ? dma_reqs[4] :
    (selected_bpl == 3'd6) ? dma_reqs[5] :
    1'b0;
 
wire [31:2] dma_address_1;
bitplain bitplain_1(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    .burst_read_enabled(burst_read_enabled == 1'b1 && bpl_con0[9:7] >= 3'd1), 
    .burst_read_request(dma_reqs[0]),
    .burst_read_address(dma_address_1),
    .burst_read_ready(burst_read_ready == 1'b1 && selected_bpl == 3'd1),
    .burst_read_data(burst_read_data),
 
    .write_ena(write_ena == 1'b1 && (write_address == 4'd0 || write_address == 4'd10)),
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    .write_address( (write_address == 4'd0)? 1'b0       : 1'b1),
    .write_data(    (write_address == 4'd0)? write_data : {write_data[31:16], 16'd0}),
    .write_sel(     (write_address == 4'd0)? write_sel  : {write_sel[3:2], 2'b0}),
 
    .bpl_con0(bpl_con0),
    .bpl_delay(bpl_con1[3:0]),
    .bpl_modulo(bpl_modulo[31:16]),
    .ddf_start(ddf_start),
    .ddf_stop(ddf_stop),
 
    .color(color[0]),
 
    .line_number(line_number)
);
wire [31:2] dma_address_2;
bitplain bitplain_2(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    .burst_read_enabled(burst_read_enabled == 1'b1 && bpl_con0[9:7] >= 3'd2), 
    .burst_read_request(dma_reqs[1]),
    .burst_read_address(dma_address_2),
    .burst_read_ready(burst_read_ready == 1'b1 && selected_bpl == 3'd2),
    .burst_read_data(burst_read_data),
 
    .write_ena(write_ena == 1'b1 && (write_address == 4'd1 || write_address == 4'd10)),
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    .write_address( (write_address == 4'd1)? 1'b0       : 1'b1),
    .write_data(    (write_address == 4'd1)? write_data : {write_data[15:0], 16'd0}),
    .write_sel(     (write_address == 4'd1)? write_sel  : {write_sel[1:0], 2'b0}),
 
    .bpl_con0(bpl_con0),
    .bpl_delay(bpl_con1[7:4]),
    .bpl_modulo(bpl_modulo[15:0]),
    .ddf_start(ddf_start),
    .ddf_stop(ddf_stop),
 
    .color(color[1]),
 
    .line_number(line_number)
);
wire [31:2] dma_address_3;
bitplain bitplain_3(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    .burst_read_enabled(burst_read_enabled == 1'b1 && bpl_con0[9:7] >= 3'd3), 
    .burst_read_request(dma_reqs[2]),
    .burst_read_address(dma_address_3),
    .burst_read_ready(burst_read_ready == 1'b1 && selected_bpl == 3'd3),
    .burst_read_data(burst_read_data),
 
    .write_ena(write_ena == 1'b1 && (write_address == 4'd2 || write_address == 4'd11)),
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    .write_address( (write_address == 4'd2)? 1'b0       : 1'b1),
    .write_data(    (write_address == 4'd2)? write_data : {write_data[31:16], 16'd0}),
    .write_sel(     (write_address == 4'd2)? write_sel  : {write_sel[3:2], 2'b0}),
 
    .bpl_con0(bpl_con0),
    .bpl_delay(bpl_con1[3:0]),
    .bpl_modulo(bpl_modulo[31:16]),
    .ddf_start(ddf_start),
    .ddf_stop(ddf_stop),
 
    .color(color[2]),
 
    .line_number(line_number)
);
wire [31:2] dma_address_4;
bitplain bitplain_4(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    .burst_read_enabled(burst_read_enabled == 1'b1 && bpl_con0[9:7] >= 3'd4), 
    .burst_read_request(dma_reqs[3]),
    .burst_read_address(dma_address_4),
    .burst_read_ready(burst_read_ready == 1'b1 && selected_bpl == 3'd4),
    .burst_read_data(burst_read_data),
 
    .write_ena(write_ena == 1'b1 && (write_address == 4'd3 || write_address == 4'd11)),
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    .write_address( (write_address == 4'd3)? 1'b0       : 1'b1),
    .write_data(    (write_address == 4'd3)? write_data : {write_data[15:0], 16'd0}),
    .write_sel(     (write_address == 4'd3)? write_sel  : {write_sel[1:0], 2'b0}),
 
    .bpl_con0(bpl_con0),
    .bpl_delay(bpl_con1[7:4]),
    .bpl_modulo(bpl_modulo[15:0]),
    .ddf_start(ddf_start),
    .ddf_stop(ddf_stop),
 
    .color(color[3]),
 
    .line_number(line_number)
);
wire [31:2] dma_address_5;
bitplain bitplain_5(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    .burst_read_enabled(burst_read_enabled == 1'b1 && bpl_con0[9:7] >= 3'd5), 
    .burst_read_request(dma_reqs[4]),
    .burst_read_address(dma_address_5),
    .burst_read_ready(burst_read_ready == 1'b1 && selected_bpl == 3'd5),
    .burst_read_data(burst_read_data),
 
    .write_ena(write_ena == 1'b1 && (write_address == 4'd4 || write_address == 4'd12)),
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    .write_address( (write_address == 4'd4)? 1'b0       : 1'b1),
    .write_data(    (write_address == 4'd4)? write_data : {write_data[31:16], 16'd0}),
    .write_sel(     (write_address == 4'd4)? write_sel  : {write_sel[3:2], 2'b0}),
 
    .bpl_con0(bpl_con0),
    .bpl_delay(bpl_con1[3:0]),
    .bpl_modulo(bpl_modulo[31:16]),
    .ddf_start(ddf_start),
    .ddf_stop(ddf_stop),
 
    .color(color[4]),
 
    .line_number(line_number)
);
wire [31:2] dma_address_6;
bitplain bitplain_6(
    .CLK_I(CLK_I),
    .reset_n(reset_n),
 
    .line_start(line_start),
    .column_number(column_number),
 
    .burst_read_enabled(burst_read_enabled == 1'b1 && bpl_con0[9:7] >= 3'd6), 
    .burst_read_request(dma_reqs[5]),
    .burst_read_address(dma_address_6),
    .burst_read_ready(burst_read_ready == 1'b1 && selected_bpl == 3'd6),
    .burst_read_data(burst_read_data),
 
    .write_ena(write_ena == 1'b1 && (write_address == 4'd5 || write_address == 4'd12)),
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    .write_address( (write_address == 4'd5)? 1'b0       : 1'b1),
    .write_data(    (write_address == 4'd5)? write_data : {write_data[15:0], 16'd0}),
    .write_sel(     (write_address == 4'd5)? write_sel  : {write_sel[1:0], 2'b0}),
 
    .bpl_con0(bpl_con0),
    .bpl_delay(bpl_con1[7:4]),
    .bpl_modulo(bpl_modulo[15:0]),
    .ddf_start(ddf_start),
    .ddf_stop(ddf_stop),
 
    .color(color[5]),
 
    .line_number(line_number)
);
 
always @(posedge CLK_I or negedge reset_n) begin
    if(reset_n == 1'b0) begin
        bpl_con0 <= 11'd0;
        ddf_start <= 6'd0;
        ddf_stop <= 6'd0;
        bpl_modulo <= 32'd0;
        bpl_con1 <= 8'd0;
 
        dma_reqs_reg <= 6'd0;
    end
    else begin
        dma_reqs_reg <= dma_reqs;
 
        // 6:   DDFSTRT [15:0],     DIWSTOP [31:16], *
        // 7:   DDFSTOP [31:16],    DMACON  [15:0], *
        // 8:   BPLCON0,            BPLCON1,
        // 9:   BPL1MOD,            BPL2MOD,
        if(write_ena == 1'b1) begin
            if(write_address == 4'd6 && write_sel[0] == 1'b1) ddf_start <= write_data[7:2];
            if(write_address == 4'd6 && write_sel[1] == 1'b1) ;
            if(write_address == 4'd6 && write_sel[2] == 1'b1) ;
            if(write_address == 4'd6 && write_sel[3] == 1'b1) ;
            if(write_address == 4'd7 && write_sel[0] == 1'b1) ;
            if(write_address == 4'd7 && write_sel[1] == 1'b1) ;
            if(write_address == 4'd7 && write_sel[2] == 1'b1) ddf_stop <= write_data[23:18];
            if(write_address == 4'd7 && write_sel[3] == 1'b1) ;
            if(write_address == 4'd8 && write_sel[0] == 1'b1) bpl_con1 <= write_data[7:0];
            if(write_address == 4'd8 && write_sel[1] == 1'b1) ;
            if(write_address == 4'd8 && write_sel[2] == 1'b1) bpl_con0[2:0] <= write_data[19:17];
            if(write_address == 4'd8 && write_sel[3] == 1'b1) bpl_con0[10:3] <= write_data[31:24];
            if(write_address == 4'd9 && write_sel[0] == 1'b1) bpl_modulo[7:0] <= write_data[7:0];
            if(write_address == 4'd9 && write_sel[1] == 1'b1) bpl_modulo[15:8] <= write_data[15:8];
            if(write_address == 4'd9 && write_sel[2] == 1'b1) bpl_modulo[23:16] <= write_data[23:16];
            if(write_address == 4'd9 && write_sel[3] == 1'b1) bpl_modulo[31:24] <= write_data[31:24];
        end
    end
end
 
endmodule
 
/*! \brief Single bitplain module.
 */
module bitplain(
    input CLK_I,
    input reset_n,
 
    input line_start,
    input [8:0] column_number,
 
    // video interface - read
    input burst_read_enabled,
	output reg burst_read_request,
	output [31:2] burst_read_address,
	input burst_read_ready,
	input [31:0] burst_read_data,
 
    input write_ena,
    // 0:   BPLxPTH,    BPLxPTL,
    // 1:   BPLxDAT,    16'd0
    input write_address,
    input [31:0] write_data,
    input [3:0] write_sel,
 
    input [10:0] bpl_con0,
    input [3:0] bpl_delay,
    input [15:0] bpl_modulo,
    input [8:3] ddf_start,
    input [8:3] ddf_stop,
 
    output color,
 
    input [8:0] line_number
);
 
assign burst_read_address = dma_address_full[31:2];
reg [31:0] dma_address_full;
reg new_address;
reg dma_started;
 
reg shift_delay;
reg [5:0] shift_counter;
reg [31:0] shift;
assign color = shift[31];
 
reg [15:0] even_data;
 
reg [4:0] bitplain_ram_addr;
wire [31:0] bitplain_ram_q;
altsyncram bitplain_ram_inst(
	.clock0(CLK_I),
 
	.address_a(bitplain_ram_addr),
	.wren_a(burst_read_ready == 1'b1 && burst_read_request == 1'b1),
	.data_a((dma_address_full[1] == 1'b0) ? burst_read_data : {even_data, burst_read_data[31:16]}),
	.q_a(bitplain_ram_q)
);
defparam 
    bitplain_ram_inst.operation_mode = "SINGLE_PORT",
    bitplain_ram_inst.width_a = 32,
    bitplain_ram_inst.widthad_a = 5;
 
reg [1:0] dma_state;
parameter [1:0]
    DMA_DISABLED    = 2'd0,
    DMA_ACTIVE      = 2'd1,
    DMA_INACTIVE    = 2'd2;
 
// in multiples of 8, usually 38
wire [8:3] ddf_diff;
assign ddf_diff = ddf_stop[8:3] - ddf_start[8:3];
 
wire [8:3] ddf_stop_final;
assign ddf_stop_final = (ddf_diff[3] == 1'b1)? ddf_stop + 6'd1 : ddf_stop;
 
wire [8:3] ddf_diff_final;
assign ddf_diff_final = ddf_stop_final[8:3] - ddf_start[8:3];
 
// difference,  diff,   ram_addr
// lowres
// 0-7,         0,      0+1 >= 0 --> 1
// 8-15,        2,      1+1 >= 0,2 --> 2
// 16-23,       4,      2+1 >= 0,2 --> 2
// 24-31,       6,      3+1 >= 0,2,4 --> 3
// .....
// 144-151,     36,     18+1 >= 0,2,4,6,8,10,12,14,16,18 --> 10
// 152-159,     38,     19+1 >= 0,2,4,6,8,10,12,14,16,18,20 --> 11
// hires
// 0-3,         0,      0+2 >= 0,2 --> 2
// 4-7,         1,      1+2 >= 0,2 --> 2
// 8-11,        2,      2+2 >= 0,2,4 --> 3
// 12-15,       3,      3+2 >= 0,2,4 --> 3
// .....
// 148-151,     37,     37+2 >= 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38 --> 20
// 152-155,     38,     38+2 >= 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40 --> 21
wire ddf_continue;
assign ddf_continue = 
    (bpl_con0[10] == 1'b0 && { 1'b0, ddf_diff_final[8:4] } + 6'd1 >= { bitplain_ram_addr + 5'd1, 1'b0 } ) ||
    (bpl_con0[10] == 1'b1 && ddf_diff_final[8:3] + 6'd2           >= { bitplain_ram_addr + 5'd1, 1'b0 } );
 
// lowres: 1C1 / 2 - 8.5 - 8 = D8 - 8 = D0,     x - 17 - 16 = {ddf_stop[8:3], 3'b0}
// hires:  1C1 / 2 - 4.5 - 8 = DC - 8 = D4,     x - 9  - 16 = {ddf_stop[8:3], 3'b0}
wire ddf_finished;
assign ddf_finished =
    (column_number > { ddf_stop_final[8:3], 3'b0 } + 9'd32) ||
    (column_number == 9'd451);
 
always @(posedge CLK_I or negedge reset_n) begin
    if(reset_n == 1'b0) begin
        burst_read_request <= 1'b0;
        dma_address_full <= 32'd0;
        new_address <= 1'b0;
        dma_started <= 1'b0;
        shift_delay <= 1'b0;
        shift_counter <= 6'd0;
        shift <= 32'd0;
        even_data <= 16'd0;
        bitplain_ram_addr <= 5'd0;
        dma_state <= DMA_DISABLED;
    end
    else begin
        if(write_ena == 1'b1 && write_address == 1'b1 && write_sel[3:2] != 2'b00) begin
            if(write_sel[3] == 1'b1) shift[31:24] <= write_data[31:24];
            if(write_sel[2] == 1'b1) shift[23:16] <= write_data[23:16];
        end
        else if(dma_state == DMA_ACTIVE || dma_state == DMA_DISABLED) begin
            shift <= 32'd0;
            shift_delay <= 1'b0;
            shift_counter <= 6'd32 + {1'b0, bpl_delay, 1'b0};
            // delay in lowres pixels == 2 hires pixels == 0.5 color clock
        end
        else if(ddf_finished == 1'b1) begin
            // ddf_stop in 2 lowres pixels == 4 hires pixels == 1.0 color clock
            shift <= 32'd0;
            shift_delay <= 1'b0;
            shift_counter <= 6'd32;
        end
        else if(shift_delay == 1'b1) begin
            shift_delay <= 1'b0;
        end
        else if((bpl_con0[10] == 1'b0 && column_number >= { ddf_start[8:3], 3'b0 } + 9'd17) ||
                (bpl_con0[10] == 1'b1 && column_number >= { ddf_start[8:3], 3'b0 } + 9'd9))
        begin
            if(shift_counter > 6'd32) begin
                shift_counter <= shift_counter - 6'd1;
            end
            else if(shift_counter == 6'd32) begin
                if(bpl_con0[10] == 1'b0) shift_delay <= 1'b1; // HIRES==0
                shift <= bitplain_ram_q;
                shift_counter <= shift_counter - 6'd1;
            end
            else begin
                if(bpl_con0[10] == 1'b0) shift_delay <= 1'b1; // HIRES==0
                shift <= { shift[30:0], 1'b0 };
 
                if(shift_counter == 6'd1) shift_counter <= 6'd32;
                else shift_counter <= shift_counter - 6'd1;
            end
        end
 
        if(burst_read_enabled == 1'b0) begin
            bitplain_ram_addr <= 5'd0;
            dma_state <= DMA_DISABLED;
        end
        else if(dma_state == DMA_DISABLED && line_start == 1'b1) begin
            bitplain_ram_addr <= 5'd0;
            dma_state <= DMA_ACTIVE;
            burst_read_request <= 1'b1;
            dma_started <= 1'b0;
            new_address <= 1'b0;
        end
        else if(dma_state == DMA_ACTIVE && burst_read_ready == 1'b1) begin
            if(ddf_continue == 1'b1) begin
                if(dma_address_full[1] == 1'b0 || dma_started == 1'b1) bitplain_ram_addr <= bitplain_ram_addr + 5'd1;
            end
            else begin
                bitplain_ram_addr <= 5'd0;
                dma_state <= DMA_INACTIVE;
                burst_read_request <= 1'b0;
            end
        end
        else if(dma_state == DMA_INACTIVE && ddf_finished == 1'b1) begin
            bitplain_ram_addr <= 5'd0;
            dma_state <= DMA_DISABLED;
        end
        else if(dma_state == DMA_INACTIVE && shift_counter == 6'd2 && shift_delay == 1'b0) begin
            bitplain_ram_addr <= bitplain_ram_addr + 5'd1;
        end
 
        if(write_ena == 1'b1) begin
            if(write_address == 1'b0 && write_sel[0] == 1'b1) dma_address_full[7:0] <= write_data[7:0];
            if(write_address == 1'b0 && write_sel[1] == 1'b1) dma_address_full[15:8] <= write_data[15:8];
            if(write_address == 1'b0 && write_sel[2] == 1'b1) dma_address_full[23:16] <= write_data[23:16];
            if(write_address == 1'b0 && write_sel[3] == 1'b1) dma_address_full[31:24] <= write_data[31:24];
            new_address <= 1'b1;
        end
        else if(dma_state == DMA_ACTIVE && burst_read_ready == 1'b1) begin
            if(ddf_continue == 1'b0) begin
                if(new_address == 1'b1)         dma_address_full <= dma_address_full;
                else if(bpl_con0[10] == 1'b0)   dma_address_full <= dma_address_full + { {16{bpl_modulo[15]}}, bpl_modulo } + { { 1'b0, ddf_diff_final[8:4] } + 6'd1, 1'b0 };
                else if(bpl_con0[10] == 1'b1)   dma_address_full <= dma_address_full + { {16{bpl_modulo[15]}}, bpl_modulo } + { ddf_diff_final[8:3] + 6'd2, 1'b0 };
 
                new_address <= 1'b0;
            end
 
            dma_started <= 1'b1;
            even_data <= burst_read_data[15:0];
        end
    end
end
 
endmodule
 
/*! \brief Single sprite module.
 */
module sprite(
    input CLK_I,
    input reset_n,
 
    input line_start,
    input [8:0] line_number,
    input [8:0] column_number,
 
    input dma_ena,
    output reg dma_req,
    output [31:2] dma_address,
    input dma_done,
    input [31:0] dma_data,
 
    input write_ena,
    // 0:   SPRxPTH,    SPRxPTL,
    // 1:   SPRxPOS,    SPRxCTL,
    // 2:   SPRxDATA,   SPRxDATB,
    input [1:0] write_address,
    input [31:0] write_data,
    input [3:0] write_sel,
 
    output reg attached,
    output [1:0] color
);
 
// { SPRxPTH high 3 bits, SPRxPTL low 15 bits, lowest bit }
reg [31:0] dma_address_full;
assign dma_address = dma_address_full[31:2];
reg dma_address_bit1;
 
// { SPRxDATB, SPRxDATA }
reg [31:0] data;
// { SPRxCTL[2], SPRxPOS[15:8] }
reg [8:0] vert_start;
// { SPRxCTL[1], SPRxCTL[15:8] }
reg [8:0] vert_end;
// { SPRxPOS[7:0], SPRxCTL[0] }
reg [8:0] horiz_start;
// SPRxCTL[7]
// output reg attached
 
// disabled by write to SPRxCTL, enabled by write to SPRxDATA
reg ena_horiz_comp;
 
reg shift_delay;
reg [15:0] shiftA;
reg [15:0] shiftB;
assign color = { shiftA[15], shiftB[15] };
 
reg [1:0] dma_state;
parameter [1:0]
    DMA_DISABLED = 2'd0,
    DMA_POS_CTL = 2'd1,
    DMA_DAT = 2'd2;
 
always @(posedge CLK_I or negedge reset_n) begin
    if(reset_n == 1'b0) begin
        dma_req <= 1'b0;
        dma_address_full <= 32'd0;
        dma_address_bit1 <= 1'b0;
        attached <= 1'b0;
        data <= 32'd0;
        vert_start <= 9'd0;
        vert_end <= 9'd0;
        horiz_start <= 9'd0;
        ena_horiz_comp <= 1'b0;
        shift_delay <= 1'b0;
        shiftA <= 16'd0;
        shiftB <= 16'd0;
        dma_state <= DMA_DISABLED;
    end
    else begin
        if(ena_horiz_comp == 1'b1 && horiz_start == column_number) begin
            shift_delay <= 1'b0;
            shiftA <= data[15:0];
            shiftB <= data[31:16];
        end
        else if(shift_delay == 1'b1) begin
            shift_delay <= 1'b0;
        end
        else begin
            shift_delay <= 1'b1;
            shiftA <= { shiftA[14:0], 1'b0 };
            shiftB <= { shiftB[14:0], 1'b0 };
        end
 
        if(         (write_ena == 1'b1 && write_address == 2'd1 && (write_sel[0] == 1'b1 || write_sel[1] == 1'b1)) ||
                    (dma_done == 1'b1 && dma_state == DMA_POS_CTL) )    ena_horiz_comp <= 1'b0;
        else if(    (write_ena == 1'b1 && write_address == 2'd2 && (write_sel[2] == 1'b1 || write_sel[3] == 1'b1)) ||
                    (dma_done == 1'b1 && dma_state == DMA_DAT) )        ena_horiz_comp <= 1'b1;
 
        if(dma_ena == 1'b0 || (write_ena == 1'b1 && write_address == 2'd0 && write_sel[3:0] != 4'b0000)) begin
            dma_state <= DMA_DISABLED;
            dma_req <= 1'b0;
            dma_address_bit1 <= 1'b0;
        end
        else if(dma_state != DMA_DISABLED && dma_done == 1'b1 && dma_address_full[1] == 1'b1 && dma_address_bit1 == 1'b0) begin
            dma_req <= 1'b1;
            dma_address_bit1 <= 1'b1;
            dma_address_full <= dma_address_full + 19'd4;
        end
        else if(line_start == 1'b1 && (dma_state == DMA_DISABLED ||
            (dma_state == DMA_DAT && line_number == vert_end && vert_start != vert_end)) )
        begin
            dma_state <= DMA_POS_CTL;
            dma_req <= 1'b1;
        end
        else if(dma_done == 1'b1 && dma_state == DMA_POS_CTL) begin
            dma_state <= DMA_DAT;
            dma_req <= 1'b0;
            if(dma_address_bit1 == 1'b0) dma_address_full <= dma_address_full + 19'd4;
            dma_address_bit1 <= 1'b0;
        end
        else if(line_start == 1'b1 && dma_state == DMA_DAT && line_number >= vert_start && line_number < vert_end) begin
            dma_req <= 1'b1;
        end
        else if(dma_done == 1'b1 && dma_state == DMA_DAT) begin
            dma_req <= 1'b0;
            if(dma_address_bit1 == 1'b0) dma_address_full <= dma_address_full + 19'd4;
            dma_address_bit1 <= 1'b0;
        end
 
        if(write_ena == 1'b1) begin
            if(write_address == 2'd0 && write_sel[0] == 1'b1) dma_address_full[7:0] <= write_data[7:0];
            if(write_address == 2'd0 && write_sel[1] == 1'b1) dma_address_full[15:8] <= write_data[15:8];
            if(write_address == 2'd0 && write_sel[2] == 1'b1) dma_address_full[23:16] <= write_data[23:16];
            if(write_address == 2'd0 && write_sel[3] == 1'b1) dma_address_full[31:24] <= write_data[31:24];
            if(write_address == 2'd1 && write_sel[0] == 1'b1) 
                { attached, vert_start[8], vert_end[8], horiz_start[0]} <= { write_data[7], write_data[2:0] };
            if(write_address == 2'd1 && write_sel[1] == 1'b1) vert_end[7:0] <= write_data[15:8];
            if(write_address == 2'd1 && write_sel[2] == 1'b1) horiz_start[8:1] <= write_data[23:16];
            if(write_address == 2'd1 && write_sel[3] == 1'b1) vert_start[7:0] <= write_data[31:24];
            if(write_address == 2'd2 && write_sel[0] == 1'b1) data[7:0] <= write_data[7:0];
            if(write_address == 2'd2 && write_sel[1] == 1'b1) data[15:8] <= write_data[15:8];
            if(write_address == 2'd2 && write_sel[2] == 1'b1) data[23:16] <= write_data[23:16];
            if(write_address == 2'd2 && write_sel[3] == 1'b1) data[31:24] <= write_data[31:24];
        end
        else if(dma_done == 1'b1) begin 
            if(dma_state == DMA_POS_CTL) begin
                if(dma_address_full[1] == 1'b1 && dma_address_bit1 == 1'b0) begin
                    horiz_start[8:1] <= dma_data[7:0];
                    vert_start[7:0] <= dma_data[15:8];
                end
                else if(dma_address_full[1] == 1'b1 && dma_address_bit1 == 1'b1) begin
                    { attached, vert_start[8], vert_end[8], horiz_start[0]} <= { dma_data[23], dma_data[18:16] };
                    vert_end[7:0] <= dma_data[31:24];
                end
                else begin
                    { attached, vert_start[8], vert_end[8], horiz_start[0]} <= { dma_data[7], dma_data[2:0] };
                    vert_end[7:0]       <= dma_data[15:8];
                    horiz_start[8:1]    <= dma_data[23:16];
                    vert_start[7:0]     <= dma_data[31:24]; // vert_end*256 + horiz_start*2*655536 + vert_start*16777216
                end
            end
            else if(dma_state == DMA_DAT) begin
                if(dma_address_full[1] == 1'b1 && dma_address_bit1 == 1'b0) begin
                    data[31:16] <= dma_data[15:0];
                end
                else if(dma_address_full[1] == 1'b1 && dma_address_bit1 == 1'b1) begin
                    data[15:0] <= dma_data[31:16];
                end
                else begin
                    data <= dma_data;
                end
            end
        end
    end
end
 
endmodule
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.