2D Starfield

Pincha aquí para verlo en español

In the previous tutorial Painting pixels: Introduction to video memory we learned how to paint pixels on the screen, we will use it to generate a nice and simple star field effect.

To do this, we will set the mode 0 and change the palette to use the colors black, yellow, pastel yellow and bright yellow. We will use the black for the background, and three shades of yellow to distinguish more or less distant stars and moving with varying speed. As we saw in previous tutorials, to switch to mode 0, we have to do from assembler, using the firmware command SCR SET MODE (BC0E), to change the palette we have to use the command SCR SET INK (BC32) and to put the border to black color, we have to use the command SET BORDER SCR (BC38), as follows :

//SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //PALETE
  __asm
    ld a, #0
    ld b, #0 ;black
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #1
    ld b, #12 ;Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #2
    ld b, #25 ;Pastel Yellow    
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #3
    ld b, #24 ;Bright Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;

For the stars we will use a c structure with the position and type of star. In the main loop will go one to one star, erasing the previous pixel position, updating the position and repainting the star. Here is the complete source code for sdcc (at the end of tutorial you can download in a zip) :

////////////////////////////////////////////////////////////////////////
// starfield.c
// 2D Star Field
// Mochilote - www.cpcmania.com
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void SetMode0PixelColor(unsigned char *pByteAddress, unsigned char nColor, unsigned char nPixel)
{
  unsigned char nByte = *pByteAddress;

  if(nPixel == 0)
  {
    nByte &= 85;

    if(nColor & 1)
      nByte |= 128;

    if(nColor & 2)
      nByte |= 8;

    if(nColor & 4)
      nByte |= 32;

    if(nColor & 8)
      nByte |= 2;
  }
  else
  {
    nByte &= 170;

    if(nColor & 1)
      nByte |= 64;

    if(nColor & 2)
      nByte |= 4;

    if(nColor & 4)
      nByte |= 16;

    if(nColor & 8)
      nByte |= 1;
  }

  *pByteAddress = nByte;
}

void PutPixelMode0(unsigned char nX, unsigned char nY, unsigned char nColor)
{
  unsigned char nPixel = 0;
  unsigned int nAddress = 0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 2);
  nPixel = nX % 2;

  SetMode0PixelColor((unsigned char *)nAddress, nColor, nPixel);
}

struct _tStar
{
  unsigned char nX;
  unsigned char nY;
  unsigned char nStarType;
};

#define STARS_NUM 40
struct _tStar aStars[STARS_NUM];

void main()
{
  unsigned char nStar = 0;
  memset(aStars, 0, sizeof(aStars));
  
  //SCR_SET_MODE 0
  __asm
    ld a, #0
    call #0xBC0E
  __endasm;

  //PALETE
  __asm
    ld a, #0
    ld b, #0 ;black
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #1
    ld b, #12 ;Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #2
    ld b, #25 ;Pastel Yellow    
    ld c, b
    call #0xBC32 ;SCR SET INK

    ld a, #3
    ld b, #24 ;Bright Yellow
    ld c, b
    call #0xBC32 ;SCR SET INK
  __endasm;

  //SCR SET BORDER 0
  __asm
    ld b, #0 ;black
    ld c, b
    call #0xBC38
  __endasm;

  //Init
  for(nStar = 0; nStar < STARS_NUM; nStar++)
  {
    aStars[nStar].nX = rand() % 160;
    aStars[nStar].nY = rand() % 200;
    aStars[nStar].nStarType = rand() % 3;
  }

  while(1)
  {

    for(nStar = 0; nStar < STARS_NUM; nStar++)
    {
      //delete star
      PutPixelMode0(aStars[nStar].nX, aStars[nStar].nY, 0);

      //move star
      switch(aStars[nStar].nStarType)
      {
        case 0: //slow star
          aStars[nStar].nX += 1;
          break;
        case 1: //medium star
          aStars[nStar].nX += 2;
          break;
        case 2: //fast star
          aStars[nStar].nX += 3;
          break;
      }
      
      if(aStars[nStar].nX >= 160)
      {
        aStars[nStar].nX = 0;
        aStars[nStar].nY = rand() % 200;
        aStars[nStar].nStarType = rand() % 3;
        continue;
      }

      //paint star
      PutPixelMode0(aStars[nStar].nX, aStars[nStar].nY, aStars[nStar].nStarType + 1);
    }
  }
}
////////////////////////////////////////////////////////////////////////

Compile and generate dsk with the following commands (starfield.bat):

sdcc -mz80 --code-loc 0x6038 --data-loc 0 --no-std-crt0 crt0_cpc.rel starfield.c
hex2bin starfield.ihx
CPCDiskXP -File starfield.bin -AddAmsdosHeader 6000 -AddToNewDsk starfield.dsk    
    

Run in the emulator and get something like this (in the emulator works best):

fill

Is limited to 40 stars simultaneously, because if we put more stars, the program starts to run slow and we would have to optimize the code and implement parts in assembly ... perhaps in a future tutorial.

You could download a zip with all files (source code, bat to compile, binary and dsk's) here: 2D_Starfield.zip

 

www.CPCMania.com 2012