Thursday, 10 October 2019

Implementing Sprites: Part 2

Foreword

In the previous post we added some very basic sprite functionality to our C64 emulator that enabled us to show a moving sprite.

In this post we will continue to add some more sprite functionality, which will involve the capability to expand a sprite and multicolor mode.

Sprite Expansion

Sprites on the VIC-II has the capability to be expanded in both the X direction and the Y direction.

Register D017 has a bit for each sprite indicating whether it should be expanded in the Y direction.

Similarly, register D01D has a bit for each sprite indicating whether a sprite should be expanded in the X Direction.

So, let us start off by redirecting the bits of registers D017 and D01D to our sprite_generator module as input ports:

module sprite_generator(
...
  input x_expand,
  input y_expand,
...
    );

The first thing that is effected if a sprite is expanded, is its display region. So let us modify we determine this region:

...
  wire [5:0] sprite_width;
  wire [5:0] sprite_height;
...
  assign sprite_height = y_expand ? 42 : 21;
  assign sprite_width = x_expand ? 48 : 24;
...
  assign sprite_display_region = (raster_y_pos >= sprite_y_pos && raster_y_pos < (sprite_y_pos + sprite_height)) &&
                                 (raster_x_pos >= sprite_x_pos && raster_x_pos < (sprite_x_pos + sprite_width));
...

Next, let us consider what should happen when we expand the sprite in the Y direction. In such a case our 21 line sprite should cover 42 lines of the sprite area on the screen. So, each line should be repeated twice.

We do this by dividing the current line within the active sprite area by two:

...
  wire [5:0] request_line_pre;
...
  assign request_line_pre = next_raster - sprite_y_pos;
  assign request_line = y_expand ? (request_line_pre>>1) : request_line_pre;
...

Similarly, when we expand in the X direction we need to repeat each pixel on a line twice. We do this by shifting a pixel out only every second clock cycle:

...
 reg [1:0] toggle; 
...
  always @(posedge clk_in)
  if (!sprite_display_region)
    toggle <= 0;
  else
    toggle <= toggle + 1;
...
 assign toggle_single_color_bit = x_expand ? toggle[0] : 1;
...
  always @(posedge clk_in)
    if (store_byte)
      sprite_data <= {sprite_data[15:0], data[7:0]};
    else if (sprite_display_region && toggle_single_color_bit)
      sprite_data <= {sprite_data[22:0], 1'b0};
...

We achieve this slow down with the toggle counter. This counter only needs to be one bit wide for now. The reason I made it two bits wide, is for multicolor mode later on.

Multicolor Mode

Let us tackle multicolor mode next.

As we know, in multi color mode our pixels is two pixels wide. This means that we need to shift out two pixels at a time when in multicolor mode. This also implies that we can only do this shift every second clock cycle.

This sounds very similar to the previous section when we need to expand our sprite in the X direction.We will therefore make use again of the toggle counter.

When we are expanding a multicolor sprite in the X direction, we need to slow down the clocking out of the pixels even more: Four clock cycles per pixels.

With all this in mind, we need to add the following code:

...
assign toggle_multi_color_bit = x_expand ? (toggle[1:0] == 2'b11) : toggle[0];
...
 always @(posedge clk_in)
    if (store_byte)
      sprite_data <= {sprite_data[15:0], data[7:0]};
    else if (sprite_display_region && toggle_single_color_bit && !multi_color_mode)
      sprite_data <= {sprite_data[22:0], 1'b0};
    else if (sprite_display_region && toggle_multi_color_bit && multi_color_mode)
      sprite_data <= {sprite_data[21:0], 2'b0};
...

Now, at any point in time, bits [23:22] will be the value for our current pixel. Next, let us have a look of the meanings for the different bit values, as quoted from https://www.c64-wiki.com/wiki/Sprite:


  • Pixels with a bit pair of "00" appear transparent, like "0" bits do in high resolution mode.
  • Pixels with a bit pair of "01" will have the color specified in address 53285/$D025.
  • Pixels with a bit pair of "11" will have the color specified in address 53286/$D026.
  • Pixels with a bit pair of "10" will have the color specified assigned to the sprite in question in the range 53287–53294/$D027–D02E.

For above colors, we need to define extra registers within our VIC-II module, and connect it via input ports on our sprite_generator:

module sprite_generator(
...
  input [3:0] sprite_multi_0,
  input [3:0] sprite_multi_1,
  input [3:0] primary_color,
...
    );

Let us now create a case statement for the different colors:

...
 reg [3:0] output_pixel_multi;
...
  always @*
    case (sprite_data[23:22])
      2'b01: output_pixel_multi = sprite_multi_0;
      2'b10: output_pixel_multi = primary_color;
      2'b11: output_pixel_multi = sprite_multi_1;
      default:  output_pixel_multi = 0;
    endcase
...

Let us wire up some finals:

...
assign output_pixel = multi_color_mode ? output_pixel_multi : output_pixel_single;
...
assign show_pixel = sprite_enabled && (multi_color_mode ? !(sprite_data[23:22] == 2'b0) : sprite_data[23]) && sprite_display_region;
...

Test Program and Test Results

To test the code we have developed, we need a simple test program that displays multicolor sprites that is X- and Y-Expanded.

There is a nice example program for multicolor expanded sprites, in the Book Introduction to Basic: Part 2. The Book was part of a two book series published in 1983, titled: An Introduction to Basic - The Comprehensive Teach yourself programming series.

Both these books are available for download on archive.org. The program appear on pages 300, 301 and is titled Glasgow Bus. In this program we have a multicolored sprite expanded in both directions, which moves from left to right.

The following video shows execution of this program within our FPGA C64 implementation:


In Summary

In this post we implemented Sprite expansion capability and multicolor mode within our C64 emulator.

In the next post we will connect up eight instances of our sprite_generator within our C64 emulator, and see if we can make the characters appear when we play the game Dan Dare.

Till next time!

No comments:

Post a Comment