Jump to content
IGNORED

player editor


playsoft

Recommended Posts

Hey Moon,

 

I have just come across your website, GJ mate!

I have seen that your prog outputs the data as listings for C/ASM.

Could you kindly share some sample code of how to use this data in CC65?

I have searched a lot, but cannot find any example anywhere...

 

Cheers,
Tony

Link to comment
Share on other sites

Hey Moon,

 

I have just come across your website, GJ mate!

 

I have seen that your prog outputs the data as listings for C/ASM.

Could you kindly share some sample code of how to use this data in CC65?

(something like your test.xex demo)

 

I have searched a lot, but cannot find any example anywhere...

 

Cheers,

Tony

Link to comment
Share on other sites

Could you kindly share some sample code of how to use this data in CC65?

I have searched a lot, but cannot find any example anywhere...

 

C is just assembler with other instruments. PMGs are used on system level, there are no library functions for CC65 yet:

#include <atari.h>
#include <peekpoke.h>
#include <string.h>

// --- inserted data from player editor ->

#define FRAMES (1)
#define HEIGHT (28)
#define GAP    (0)
const unsigned char P0COLOURS[FRAMES] =
{
  0x14
};
const unsigned char P1COLOURS[FRAMES] =
{
  0x18
};
const unsigned char P0DATA[FRAMES][HEIGHT] =
{
  {
    0x7e, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x7e, 
    0x60, 0x7c, 0x1e, 0x02, 0x7e, 0x7c, 0x7e, 0x7e, 
    0x18, 0x18, 0x18, 0x18
  }
};
const unsigned char P1DATA[FRAMES][HEIGHT] =
{
  {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e,
    0x60, 0x78, 0x78, 0x60, 0x7e, 0x7e, 0x3e, 0x7e,
    0x60, 0x7c, 0x1e, 0x02, 0x7e, 0x7c, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
  }
};

// <---


int
main(void)
{
	// just use $1000 as location for PMG buffer (DOS will be overwritten!)
	const unsigned char* PMGBase = (unsigned char*)0x1000;
	unsigned char xpos = 100;
	unsigned char ypos = 50;

	bzero(PMGBase+1024,0x100);		// clear player 0
	bzero(PMGBase+1280,0x100);		// clear player 1

	memcpy(PMGBase+1024+ypos, P0DATA, HEIGHT);
	memcpy(PMGBase+1280+ypos, P1DATA, HEIGHT);

	ANTIC.pmbase = (unsigned int)PMGBase >> 8;
	GTIA_WRITE.gractl = 2;
	GTIA_WRITE.hposp0 = GTIA_WRITE.hposp1 = xpos;
	
	POKE(623,32);				// tricolor players
	POKE(559,58);				// DMA Screen + single resolution players
	POKE(704,P0COLOURS[0]);			// COLORS
	POKE(705,P1COLOURS[0]);

	while (1);
	return 0;
}

saved as pmgtest.c and compiled with

 

cl65 -t atari pmgtest.c -o pmgtest.xex

 

Results in:

 

post-7778-0-01694900-1507324202.png

  • Like 2
Link to comment
Share on other sites

Thanks a lot irgwend!

 

To animate the player, should I just copy a different frame n, i.e:

 

memcpy(PMGBase+1280+ypos, P1DATA[n], HEIGHT);

 

The player system is a little strange to me, I am used to C64 sprites in which animation is achieved by changing a registry telling the VIC2 which section of memory to use.

 

On Atari it seems that we need to reset and copy a shunk of memory each time the player moves or animates, right?

Link to comment
Share on other sites

Thanks a lot irgwend!

 

To animate the player, should I just copy a different frame n, i.e:

 

memcpy(PMGBase+1280+ypos, P1DATA[n], HEIGHT);

 

The player system is a little strange to me, I am used to C64 sprites in which animation is achieved by changing a registry telling the VIC2 which section of memory to use.

 

On Atari it seems that we need to reset and copy a shunk of memory each time the player moves or animates, right?

Horizontal moving, you just poke the corresponding HPOS register. Also, you can define separate chunks of RAM (must be aligned to proper boundaries), and simply change the PMBASE pointer to animate frames. However, for vertical motion, the only way is to do a memory move.

Link to comment
Share on other sites

I have a further question: is it possible to display 4 multicolor sprites at different X locations? (each having a different pair of colours)

From what I gather, the system shown here can only display from than 2 multicolor sprites only if they are aligned vertically, and use the same pair of colours.

Link to comment
Share on other sites

I have a further question: is it possible to display 4 multicolor sprites at different X locations? (each having a different pair of colours)

 

Not in the way you imagine, I guess.

 

You have only 4 independent single coloured players and missiles (an individual colour can be assigned to the latter ones in common).

Pairs of the mentioned objects can be used to form 2-bit colour depth objects (parts which overlay form a third, "OR"ed colour).

 

So you have three (somewhat easy accessible) solutions for 4 multicolour sprites:

* make 4 players multicolour by using the missiles -> drawback: the reduced resolution of them as well you only have two different sets of colours

* flicker: apply on each even or odd VBI a different position and colour for the two multicolour player visualizations -> drawback: flicker

* if the vertical position of the players is different, just change colours and positions on the fly -> drawback: no more than two multicolour players on a single scan-line

 

Here the adapted demo for the third case:

#include <atari.h>
#include <peekpoke.h>
#include <string.h>

// --- inserted data from player editor ->

#define FRAMES (1)
#define HEIGHT (28)
#define GAP    (0)
const unsigned char P0COLOURS[FRAMES] =
{
  0x14
};
const unsigned char P1COLOURS[FRAMES] =
{
  0x18
};
const unsigned char P0DATA[FRAMES][HEIGHT] =
{
  {
    0x7e, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x7e, 
    0x60, 0x7c, 0x1e, 0x02, 0x7e, 0x7c, 0x7e, 0x7e, 
    0x18, 0x18, 0x18, 0x18
  }
};
const unsigned char P1DATA[FRAMES][HEIGHT] =
{
  {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e,
    0x60, 0x78, 0x78, 0x60, 0x7e, 0x7e, 0x3e, 0x7e,
    0x60, 0x7c, 0x1e, 0x02, 0x7e, 0x7c, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
  }
};

// <---


int
main(void)
{
	// just use $1000 as location for PMG buffer (DOS will be overwritten!)
	const unsigned char* PMGBase = (unsigned char*)0x1000;
	unsigned char xpos = 100;
	unsigned char ypos = 50;
	unsigned char ypos2 = 150;

	bzero(PMGBase+1024,0x100);		// clear player 0
	bzero(PMGBase+1280,0x100);		// clear player 1
        bzero(PMGBase+1536,0x100);              // clear player 2
        bzero(PMGBase+1792,0x100);              // clear player 3

	memcpy(PMGBase+1024+ypos, P0DATA, HEIGHT);
	memcpy(PMGBase+1280+ypos, P1DATA, HEIGHT);
	memcpy(PMGBase+1536+ypos, P0DATA, HEIGHT);
	memcpy(PMGBase+1792+ypos, P1DATA, HEIGHT);
	memcpy(PMGBase+1024+ypos2, P0DATA, HEIGHT);
	memcpy(PMGBase+1280+ypos2, P1DATA, HEIGHT);
	memcpy(PMGBase+1536+ypos2, P0DATA, HEIGHT);
	memcpy(PMGBase+1792+ypos2, P1DATA, HEIGHT);

	ANTIC.pmbase = (unsigned int)PMGBase >> 8;
	GTIA_WRITE.gractl = 2;

	POKE(623,32);					// tricolor players
	POKE(559,58);					// DMA Screen + single resolution players

	while (1)
	{
		if (ANTIC.vcount==0)
		{
			GTIA_WRITE.hposp0 = GTIA_WRITE.hposp1 = xpos;
			GTIA_WRITE.hposp2 = GTIA_WRITE.hposp3 = xpos+20;
			GTIA_WRITE.colpm0 = P0COLOURS[0];
			GTIA_WRITE.colpm1 = P1COLOURS[0];	
			GTIA_WRITE.colpm2 = 52;
			GTIA_WRITE.colpm3 = 58;
		}		
		else if (ANTIC.vcount==70)
		{
			GTIA_WRITE.hposp0 = GTIA_WRITE.hposp1 = xpos+10;
			GTIA_WRITE.hposp2 = GTIA_WRITE.hposp3 = xpos+30;
			GTIA_WRITE.colpm0 = 66;
			GTIA_WRITE.colpm1 = 68;	
			GTIA_WRITE.colpm2 = 194;
			GTIA_WRITE.colpm3 = 200;
		}
	}
	return 0;
}

In a "real" program you wouldn't busy wait for VCOUNT but use a IRQ instead.

 

post-7778-0-81871500-1507572532.png

Edited by Irgendwer
  • Like 2
Link to comment
Share on other sites

Irgend,

 

Thanks for providing the example, it looks like what I imagined.

In most cases my sprites will not line up horizontally, however, they will sometime do (I am programming a top-down racing game).

For instance, on a straight horizontal piece of track, the 4 sprites may all line up horizontally.

 

So I guess I am left only with using the missiles, perhaps by mixing colors (P0/M1, P1/M2, P2/M3, P3/M0)... But that is too bad, because my sprites ideally need to share 2 colors (black for tires, cyan for cockpit) as shown in the attachements below.

 

Cheers,

Tony

 

 

post-53448-0-13320200-1507616504_thumb.png

post-53448-0-36597400-1507616583.png

  • Like 2
Link to comment
Share on other sites

So I guess I am left only with using the missiles, perhaps by mixing colors (P0/M1, P1/M2, P2/M3, P3/M0)... But that is too bad, because my sprites ideally need to share 2 colors (black for tires, cyan for cockpit) as shown in the attachements below.

 

Maybe conditional flicker is an option then: You only distribute the limited PMG resources to different frames, if the situation that more than two objects are on a scan line occurs.

That's not so uncommon in A8 games (Joust, Ms. Pacman, Crownland to name a few).

 

Edit:

Sample code change:

...
#include <stdbool.h>
...

...
bool otherFrame = false;
...

...
while (1)
{
	if (ANTIC.vcount==0)
	{
		if (otherFrame)
		{
			GTIA_WRITE.hposp0 = GTIA_WRITE.hposp1 = xpos;
			GTIA_WRITE.hposp2 = GTIA_WRITE.hposp3 = xpos+20;
			GTIA_WRITE.colpm0 = P0COLOURS[0];
			GTIA_WRITE.colpm1 = P1COLOURS[0];	
			GTIA_WRITE.colpm2 = 52;
			GTIA_WRITE.colpm3 = 58;
		}
		else
		{
			GTIA_WRITE.hposp0 = GTIA_WRITE.hposp1 = xpos+10;
			GTIA_WRITE.hposp2 = GTIA_WRITE.hposp3 = xpos+30;
			GTIA_WRITE.colpm0 = 66;
			GTIA_WRITE.colpm1 = 68;	
			GTIA_WRITE.colpm2 = 194;
			GTIA_WRITE.colpm3 = 200;
		}
		otherFrame = !otherFrame;
		while(!ANTIC.vcount);
	}
}
...

post-7778-0-66483100-1507674737.png

 

Sample screen shot not really reflecting original machine CRT output. So test for yourself:

 

pmgtest.xex

Edited by Irgendwer
  • Like 2
Link to comment
Share on other sites

So Irgend, I am back with some progress.

I decided to use P0 to P3 for the vehicle body, and flicker P4 (Missiles) for the tires.

 

My first version flickers the tires one vehicle at-a-time, between 4 vehicles, so it looks very shaky (see attached xex and video).

For my second version, I would like to setup a list of scanlines for which I change the P4 horizontal pos.

This way I can show the tires as many times as possible within one frame (i.e: whenever two vehicles do not line up horizontally).

 

Would it be possible to share with me a couple of ASM codelines showing how I can trigger code for specific scanlines?

 

P.S: my current code is shown below:


#include <cc65.h>
#include <conio.h>
#include <peekpoke.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define PLAYERS (4)
#define FRAMES (16)
#define HEIGHT (13)

#define PMGMem (0xA000)

// sprites data
unsigned int PMGBase[5] = {PMGMem+1024, PMGMem+1280, PMGMem+1536, PMGMem+1792, PMGMem+768};
unsigned int PMGAddr[5] = {PMGMem+1024, PMGMem+1280, PMGMem+1536, PMGMem+1792, PMGMem+768};
unsigned char PMColor[5] = {0x72, 0x22, 0xb6, 0xec, 0x00};
unsigned char xpos[4];
unsigned char ypos[4];
unsigned char frame[4];

#define FRAMES (16)
#define HEIGHT (13)
#define GAP    (0)

const unsigned char BODY[FRAMES][HEIGHT] =
{ { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x3e, 0x3e, 0x3e, 0x3c, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x04, 0x1e, 0x3e, 0x3e, 0x3e, 0x3e, 0x18, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1e, 0x3c, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x18, 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00 },
  { 0x00, 0x00, 0x30, 0x70, 0x70, 0x38, 0x38, 0x38, 0x38, 0x18, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x70, 0x78, 0x3c, 0x1c, 0x1c, 0x0c, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x78, 0x7c, 0x7c, 0x7c, 0x7c, 0x18, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x7c, 0x7c, 0x7c, 0x3c, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x18, 0x7c, 0x7c, 0x7c, 0x7c, 0x78, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x3c, 0x78, 0x70, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x38, 0x38, 0x18, 0x00, 0x00 },
  { 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x08, 0x18, 0x1c, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0c, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x10, 0x30, 0x38, 0x38, 0x3c, 0x1e, 0x0e, 0x04, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x3e, 0x3e, 0x3e, 0x3e, 0x1e, 0x04, 0x00, 0x00, 0x00 } };

const unsigned char TYRES[FRAMES][HEIGHT] =
{ { 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x28, 0x28, 0x00 },
  { 0x00, 0x00, 0x08, 0x28, 0x20, 0x04, 0x0c, 0x08, 0x00, 0x04, 0x14, 0x10, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x10, 0x0c, 0x4a, 0x46, 0x00, 0x08, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x28, 0x1c, 0x44, 0x40, 0x00, 0x08, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x28, 0x70, 0x44, 0x04, 0x00, 0x20, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x08, 0x30, 0x52, 0x62, 0x00, 0x10, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x14, 0x04, 0x20, 0x30, 0x10, 0x00, 0x20, 0x28, 0x08, 0x00 },
  { 0x00, 0x00, 0x14, 0x14, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x14, 0x14, 0x00 },
  { 0x00, 0x00, 0x08, 0x28, 0x20, 0x00, 0x10, 0x30, 0x20, 0x04, 0x14, 0x10, 0x00 },
  { 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x62, 0x52, 0x30, 0x08, 0x18, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x10, 0x00, 0x02, 0x22, 0x38, 0x14, 0x04, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x2a, 0x2a, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x04, 0x04, 0x00, 0x20, 0x22, 0x0e, 0x14, 0x10, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x46, 0x4a, 0x0c, 0x10, 0x18, 0x00, 0x00 },
  { 0x00, 0x10, 0x14, 0x04, 0x00, 0x08, 0x0c, 0x04, 0x20, 0x28, 0x08, 0x00, 0x00 } };

clock_t tFPS;
unsigned long f;
void DrawFPS()
{	
	// Calculate stats
    unsigned int fps;
	tFPS = clock() - tFPS;
    fps = (f*CLK_TCK)/tFPS;
	tFPS = clock();	

	// Output stats
    gotoxy(0,0);
    printf("FPS: %u", fps);
}

unsigned char i;

void InitSprites(void)
{
	POKE(54279, PMGMem>>; // Tell ANTIC where the PM data is located
	POKE(53277, 2+1);       // Tell GTIA to enable players + missile	
	POKE(623, 32+16+1);		// Tricolor players + enable fifth player + priority
	POKE(559, 32+16+8+4+2); // DMA Screen + Enable P/M + DMA Players + DMA Missiles + Single resolution
    for (i=0; i<4; i++) {
        bzero(PMGAddr[i],0x100);   // Clear Player memory
        POKE(704+i, PMColor[i]);   // Set Player colors
    }
    bzero(PMGAddr[4],0x100);  // Clear Missile memory
	POKE(711, PMColor[4]);	  // Set Player color
}

void ShowSprites(void)
{ 
    clock_t timer = clock();
    unsigned int angle = 0;
    tFPS = clock();	
	while (!kbhit()) {   
        // Update body position
        if (clock()>timer) {
            timer = clock();
            angle++;
            for (i=0; i<PLAYERS; i++) {
                xpos[i] = 128+cc65_cos((angle+i*90)%360)/6;
                ypos[i] = 128+cc65_sin((angle+i*90)%360)/4;
                frame[i] = ((12-(angle+(i+1)*90))%360)/23;
                POKE(53248+i, xpos[i]);
                bzero(PMGAddr[i], HEIGHT);        
                PMGAddr[i] = PMGBase[i]+ypos[i];
                memcpy(PMGAddr[i], BODY[frame[i]], HEIGHT);
            }       
        }
        
        // Flicker tyres
        i = f%PLAYERS;
        bzero(PMGAddr[4], HEIGHT);
        PMGAddr[4] = PMGBase[4]+ypos[i];
        POKE(53252, xpos[i]+6);
        POKE(53253, xpos[i]+4);
        POKE(53254, xpos[i]+2);
        POKE(53255, xpos[i]+0);
        memcpy(PMGAddr[4], TYRES[frame[i]], HEIGHT);          

        // Next Frame
        if (f > 60) { DrawFPS(); f=0; }
        f++;
    }
}

sprites.mp4

sprites.xex

  • Like 4
Link to comment
Share on other sites

 

My first version flickers the tires one vehicle at-a-time, between 4 vehicles, so it looks very shaky (see attached xex and video).

 

Hi 8bit-Du,

the idea to flicker the tires is a good one, as they are turning and this is the visualization of the motion. ;)

 

Without going the complicate route "vpos-range-check" now, I see two issues with your code:

You flicker the tires in the reduced frequency of your FPS (35 vs 50 (PAL)). If changing them each VBI, the visualization would look much more consistent.

Due to tearing (output of cars is also not synchronized to image output of the screen), even your cars have a bit of flicker/distortion.

 

I would fix both issues first before estimating if a better missile scheduler is needed.

  • Like 1
Link to comment
Share on other sites

Hey guyz,

 

So I have written a VBI / DLI combo to shift the HPOS at various scanline.

My code is *almost* working, in that I can show the tires 4 times, and shift them with the DLI routine.

However, something is not right in my routine DLI:, it corrupts memory and crashes the prog after few seconds...

Would you mind taking a quick look:

  .include "atari.inc"
  
  .export _SetupVBIandDLI
  .export _DLIData

  .data
_DLIData:
	.res 4
  
DLIIndex:
    .res 1

  .code
.proc _SetupVBIandDLI  
    lda #$00
    sta DLIIndex
    
    lda #$07           ; deferred
    ldx #(>VBI)        ; install VBI routine
    ldy #(<VBI)	    
    jsr SETVBV

    ldx #(<DLI)        ; install DLI routine
	ldy #(>DLI)
	stx VDSLST
	sty VDSLST+1
    
    lda #$C0
	sta NMIEN	       ; enable both VBI and DLI    
    rts
.endproc

DLI:
    pha                ; save registers
    ldy DLIIndex
    lda _DLIData,y
    sta $D003          ; set hpos of PM3
    iny
    sty DLIIndex
    pla                ; restore registers
    rti 
    
VBI:
    lda #$00
    sta DLIIndex
    jmp XITVBV   
#include <cc65.h>
#include <conio.h>
#include <peekpoke.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define PLAYERS (4)
#define FRAMES (16)
#define HEIGHT (13)

#define PMMem (0x6000)

// sprites data
unsigned int PMBase[5] = {PMMem+768, PMMem+1024, PMMem+1280, PMMem+1536, PMMem+1792};
unsigned char PMColor[5] = {0x72, 0x22, 0xb6, 0xec, 0x00};
unsigned char PMXpos[4], PMYpos[4], PMFrame[4];
unsigned int PMBody[4], PMTires[4];

#define FRAMES (16)
#define HEIGHT (13)
#define GAP    (0)

const unsigned char BODY[FRAMES][HEIGHT] =
{ { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x3e, 0x3e, 0x3e, 0x3c, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x04, 0x1e, 0x3e, 0x3e, 0x3e, 0x3e, 0x18, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1e, 0x3c, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x18, 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00 },
  { 0x00, 0x00, 0x30, 0x70, 0x70, 0x38, 0x38, 0x38, 0x38, 0x18, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x70, 0x78, 0x3c, 0x1c, 0x1c, 0x0c, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x78, 0x7c, 0x7c, 0x7c, 0x7c, 0x18, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x7c, 0x7c, 0x7c, 0x3c, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x18, 0x7c, 0x7c, 0x7c, 0x7c, 0x78, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x3c, 0x78, 0x70, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x38, 0x38, 0x18, 0x00, 0x00 },
  { 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x08, 0x18, 0x1c, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0c, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x10, 0x30, 0x38, 0x38, 0x3c, 0x1e, 0x0e, 0x04, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x3e, 0x3e, 0x3e, 0x3e, 0x1e, 0x04, 0x00, 0x00, 0x00 } };

const unsigned char TIRES[FRAMES][HEIGHT] =
{ { 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x28, 0x28, 0x00 },
  { 0x00, 0x00, 0x08, 0x28, 0x20, 0x04, 0x0c, 0x08, 0x00, 0x04, 0x14, 0x10, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x10, 0x0c, 0x4a, 0x46, 0x00, 0x08, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x28, 0x1c, 0x44, 0x40, 0x00, 0x08, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x28, 0x70, 0x44, 0x04, 0x00, 0x20, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x08, 0x30, 0x52, 0x62, 0x00, 0x10, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x14, 0x04, 0x20, 0x30, 0x10, 0x00, 0x20, 0x28, 0x08, 0x00 },
  { 0x00, 0x00, 0x14, 0x14, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x14, 0x14, 0x00 },
  { 0x00, 0x00, 0x08, 0x28, 0x20, 0x00, 0x10, 0x30, 0x20, 0x04, 0x14, 0x10, 0x00 },
  { 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x62, 0x52, 0x30, 0x08, 0x18, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x10, 0x00, 0x02, 0x22, 0x38, 0x14, 0x04, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x2a, 0x2a, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x04, 0x04, 0x00, 0x20, 0x22, 0x0e, 0x14, 0x10, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x46, 0x4a, 0x0c, 0x10, 0x18, 0x00, 0x00 },
  { 0x00, 0x10, 0x14, 0x04, 0x00, 0x08, 0x0c, 0x04, 0x20, 0x28, 0x08, 0x00, 0x00 } };

clock_t tFPS;
unsigned long f;
void DrawFPS()
{	
	// Calculate stats
    unsigned int fps;
	tFPS = clock() - tFPS;
    fps = (f*CLK_TCK)/tFPS;
	tFPS = clock();	

	// Output stats
    gotoxy(0,0);
    printf("FPS: %u", fps);
}

unsigned int DLIAddr;
extern char DLIData[4];
void SetupVBIandDLI(void);
unsigned char i;

void InitSprites(void)
{
    // Setup PMG for Body
    for (i=0; i<5; i++) {
        bzero(PMBase[i],0x100);   // Clear Player memory
        if (i==0) { POKE(711, PMColor[i]); } else { POKE(703+i, PMColor[i]); }  // Set Player colors
    }
	POKE(54279, PMMem>>;   // Tell ANTIC where the PM data is located
	POKE(53277, 2+1);        // Tell GTIA to enable players + missile	
	POKE(623, 32+16+1);		 // Tricolor players + enable fifth player + priority
	POKE(559, 32+16+8+4+2);  // DMA Screen + Enable P/M + DMA Players + DMA Missiles + Single resolution
    
    // Setup DLI for Tires
    DLIAddr = PEEK(560) + 256*PEEK(561);
    SetupVBIandDLI();
}

void ShowSprites(void)
{ 
    unsigned int DLI = PEEK(560) + 256*PEEK(561);
    clock_t timer = clock();
    unsigned int angle = 0;
    tFPS = clock();    
	while (!kbhit()) {   
        // Update body position
        if (clock()>timer) {
            timer = clock();
            angle++;
            for (i=0; i<PLAYERS; i++) {
                // Compute current position frame
                POKE(DLIAddr+3+PMYpos[i]/8, PEEK(DLIAddr+3+PMYpos[i]/8) % 128); // Remove DLI                
                PMXpos[i] = 128+cc65_cos((angle+i*90)%360)/6;
                PMYpos[i] = 128+cc65_sin((angle+i*90)%360)/4;
                PMFrame[i] = ((12-(angle+(i+1)*90))%360)/23;
                POKE(DLIAddr+3+PMYpos[i]/8, PEEK(DLIAddr+3+PMYpos[i]/8) % 128 + 128); // Restore DLI                
                DLIData[i] = PMXpos[i];
                
                // Set horizontal position
                if (i==0) {
                    POKE(53255, PMXpos[i]);
                    POKE(53254, PMXpos[i]+2);
                    POKE(53253, PMXpos[i]+4);
                    POKE(53252, PMXpos[i]+6);
                } else {
                    POKE(53247+i, PMXpos[i]);
                }
                
                // Set body data
                bzero(PMBody[i], HEIGHT);
                PMBody[i] = PMBase[i]+PMYpos[i];
                memcpy(PMBody[i], BODY[PMFrame[i]], HEIGHT);

                // Set tire data
                bzero(PMTires[i], HEIGHT);
                PMTires[i] = PMBase[4]+PMYpos[i];
                memcpy(PMTires[i], TIRES[PMFrame[i]], HEIGHT);
            }       
        }    

        // Next Frame
        if (f > 60) { DrawFPS(); f=0; }
        f++;
    }
}
  • Like 1
Link to comment
Share on other sites

 

DLI:
    pha                ; save registers
    ldy DLIIndex
    lda _DLIData,y
    sta $D003          ; set hpos of PM3
    iny
    sty DLIIndex
    pla                ; restore registers
    rti 

Had only a very quick look (5 sec.). I think TYA PHA ... PLA TAY is missing at least...

 

 

Amazing, that was exactly the issue.

The memory corruption has been fixed.

There is still a problem with the code though, see attached xex and sources.

sprites.xex

sources.zip

  • Like 2
Link to comment
Share on other sites

The easiest to fix will be the top car. In your VBI, set the Missiles' horizontal position to match the horizontal position of the highest car. The highest car shouldn't need a DLI. Edited: If you want all your players to be horizontally in sync with the tires, you would update the player horizontals in the VBI instead of the main program.

 

The harder part will be to synchronize the DLI's. If you load in Altirra and use Antic Visualization (CTRL+8 three times), you will see the dynamic interrupts you are creating on the left side of the screen. If you slow down execution (System/Speed Options/1%) you will see that things are a bit out of sync. Using this method should help you see how it is executing.

Edited by Schlortt
Link to comment
Share on other sites

Alright guyz, I am almost there!!!

 

Now my tires follow each car nicely, and I only get a bit of flicker due to the fact that the background is a character based screen (8 rows per DLI).

 

I would like to start using Graphic Mode 15, to refine the DLI, but there is precious little information on how it works in CC65.

I tried writing _graphics(15); and the screen just went black (PMG also completely disappeared).

Would you have some tips and advice to help me get setup?

 

Here is the current version of code by the way:

  .include "atari.inc"
  
  .export _SetupVBIandDLI
  .export _DLIData

  .data
_DLIData:
	.res 4
  
DLIIndex:
    .res 1

  .code
.proc _SetupVBIandDLI  
    lda #$00
    sta DLIIndex
    
    lda #$06           ; deferred
    ldx #(>VBI)        ; install VBI routine
    ldy #(<VBI)	    
    jsr SETVBV

    ldx #(<DLI)        ; install DLI routine
	ldy #(>DLI)
	stx VDSLST
	sty VDSLST+1
    
    lda #$C0
	sta NMIEN	       ; enable both VBI and DLI    
    rts
.endproc

DLI:
    pha                ; save registers
    tya
    pha    
    ldy DLIIndex
    lda _DLIData,y
    sta $D003          ; set hpos of PM3
    iny
    sty DLIIndex
    pla                ; restore registers
    tay
    pla
    rti 
    
VBI:
    lda _DLIData
    sta $D003          ; set hpos of PM3
    lda #$01
    sta DLIIndex
    jmp SYSVBV   

#include <cc65.h>
#include <conio.h>
#include <peekpoke.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define PLAYERS (4)
#define FRAMES (16)
#define HEIGHT (13)

#define PMMem (0x6000)

// sprites data
unsigned int PMBase[5] = {PMMem+768, PMMem+1024, PMMem+1280, PMMem+1536, PMMem+1792};
unsigned char PMColor[5] = {0x72, 0x22, 0xb6, 0xec, 0x00};
unsigned char PMXpos[4], PMYpos[4], PMFrame[4];
unsigned int PMBody[4], PMTires[4];

#define FRAMES (16)
#define HEIGHT (13)
#define GAP    (0)

const unsigned char BODY[FRAMES][HEIGHT] =
{ { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x3e, 0x3e, 0x3e, 0x3c, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x04, 0x1e, 0x3e, 0x3e, 0x3e, 0x3e, 0x18, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1e, 0x3c, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x18, 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00 },
  { 0x00, 0x00, 0x30, 0x70, 0x70, 0x38, 0x38, 0x38, 0x38, 0x18, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x70, 0x78, 0x3c, 0x1c, 0x1c, 0x0c, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x78, 0x7c, 0x7c, 0x7c, 0x7c, 0x18, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x7c, 0x7c, 0x7c, 0x3c, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x18, 0x7c, 0x7c, 0x7c, 0x7c, 0x78, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x3c, 0x78, 0x70, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x38, 0x38, 0x18, 0x00, 0x00 },
  { 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x08, 0x18, 0x1c, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0c, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x10, 0x30, 0x38, 0x38, 0x3c, 0x1e, 0x0e, 0x04, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x3e, 0x3e, 0x3e, 0x3e, 0x1e, 0x04, 0x00, 0x00, 0x00 } };

const unsigned char TIRES[FRAMES][HEIGHT] =
{ { 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x28, 0x28, 0x00 },
  { 0x00, 0x00, 0x08, 0x28, 0x20, 0x04, 0x0c, 0x08, 0x00, 0x04, 0x14, 0x10, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x10, 0x0c, 0x4a, 0x46, 0x00, 0x08, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x20, 0x28, 0x1c, 0x44, 0x40, 0x00, 0x08, 0x08, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x28, 0x70, 0x44, 0x04, 0x00, 0x20, 0x20, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x18, 0x08, 0x30, 0x52, 0x62, 0x00, 0x10, 0x10, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x14, 0x04, 0x20, 0x30, 0x10, 0x00, 0x20, 0x28, 0x08, 0x00 },
  { 0x00, 0x00, 0x14, 0x14, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x14, 0x14, 0x00 },
  { 0x00, 0x00, 0x08, 0x28, 0x20, 0x00, 0x10, 0x30, 0x20, 0x04, 0x14, 0x10, 0x00 },
  { 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x62, 0x52, 0x30, 0x08, 0x18, 0x00, 0x00 },
  { 0x00, 0x00, 0x10, 0x10, 0x00, 0x02, 0x22, 0x38, 0x14, 0x04, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x2a, 0x2a, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x04, 0x04, 0x00, 0x20, 0x22, 0x0e, 0x14, 0x10, 0x00, 0x00, 0x00 },
  { 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x46, 0x4a, 0x0c, 0x10, 0x18, 0x00, 0x00 },
  { 0x00, 0x10, 0x14, 0x04, 0x00, 0x08, 0x0c, 0x04, 0x20, 0x28, 0x08, 0x00, 0x00 } };

clock_t tFPS;
unsigned long f;
void DrawFPS()
{	
	// Calculate stats
    unsigned int fps;
	tFPS = clock() - tFPS;
    fps = (f*CLK_TCK)/tFPS;
	tFPS = clock();	

	// Output stats
    gotoxy(0,0);
    printf("FPS: %u", fps);
}

unsigned int DLIBase;
unsigned int DLIAddr[4];
extern char DLIData[4];
void SetupVBIandDLI(void);
unsigned char i,j,k;

void InitSprites(void)
{   
    // Setup PMG for Body
    for (i=0; i<5; i++) {
        bzero(PMBase[i],0x100);   // Clear Player memory
        if (i==0) { POKE(711, PMColor[i]); } else { POKE(703+i, PMColor[i]); }  // Set Player colors
    }
	POKE(54279, PMMem>>;   // Tell ANTIC where the PM data is located
	POKE(53277, 2+1);        // Tell GTIA to enable players + missile	
	POKE(623, 32+16+1);		 // Tricolor players + enable fifth player + priority
	POKE(559, 32+16+8+4+2);  // DMA Screen + Enable P/M + DMA Players + DMA Missiles + Single resolution
    
    // Setup DLI for Tires
    DLIBase = PEEK(560) + 256*PEEK(561);
    SetupVBIandDLI();
}

void ShowSprites(void)
{ 
    unsigned int DLI = PEEK(560) + 256*PEEK(561);
    clock_t timer = clock();
    unsigned int angle = 0;
    tFPS = clock();    
	while (!kbhit()) {   
        // Update body position
        if (clock()>timer+1) {
            timer = clock();
            angle++;
            
            // Re-compute positions and angles
            for (i=0; i<PLAYERS; i++) {
                PMXpos[i] = 128+cc65_cos((angle+i*90)%360)/6;
                PMYpos[i] = 128+cc65_sin((angle+i*90)%360)/4;
                PMFrame[i] = ((12-(angle+(i+1)*90))%360)/23;
            }
             
            // Refresh PM data
            for (i=0; i<PLAYERS; i++) {
                // Set horizontal position
                if (i==0) {
                    POKE(53255, PMXpos[i]);
                    POKE(53254, PMXpos[i]+2);
                    POKE(53253, PMXpos[i]+4);
                    POKE(53252, PMXpos[i]+6);
                } else {
                    POKE(53247+i, PMXpos[i]);
                }
                
                // Set body data
                bzero(PMBody[i], HEIGHT);
                PMBody[i] = PMBase[i]+PMYpos[i];
                memcpy(PMBody[i], BODY[PMFrame[i]], HEIGHT);

                // Set tire data
                bzero(PMTires[i], HEIGHT);
                PMTires[i] = PMBase[4]+PMYpos[i];
                memcpy(PMTires[i], TIRES[PMFrame[i]], HEIGHT);
            }       
                
            // Reassign DLIs (used to shift tires)
            for (i=0; i<PLAYERS; i++) {
                // Find index of transition (k from 0 to 3)
                k = 0;
                for (j=0; j<PLAYERS; j++) {
                    if (PMYpos[i] > PMYpos[j]) { k++; }
                }
                // Remove previous DLI
                DLIData[k] = PMXpos[i];
                if (DLIAddr[i]) { 
                    *(unsigned char*)(DLIAddr[i]) &= ~(1 << 7); 
                }
                // Set new DLI (except for first index)
                if (k > 0) {
                    DLIAddr[i] = DLIBase+PMYpos[i]/8;
                    *(unsigned char*)(DLIAddr[i]) |= (1 << 7);
                } else {
                    DLIAddr[i] = 0;
                }
            }
        }    

        // Count Frames
        if (f > 60) { DrawFPS(); f=0; }
        f++;
    }
}

sprites.xex

  • Like 1
Link to comment
Share on other sites

Shlortt, Irgend,

 

I have implemented a sorting algorithm that allows me to set DLIs in the right order, as I fill the sprites memory for the tires.

When refreshing, I move all the sprites to hpos zero, then clear the memory and reset the DLIs.

 

Some extras include flicekring between two players when they line up horizontally.

 

The animation is 99% correct, I am very very close, but there is a residual "jumping" of sprites that I cannot get rid of.

Could you please check the attached video/xex, and have another look through my sources to see if you spot something strange?

I would really deeply appreciate it!

sprites.zip

sprites.xex

sprites.mp4

  • Like 1
Link to comment
Share on other sites

Could you please check the attached video/xex, and have another look through my sources to see if you spot something strange?

I would really deeply appreciate it!

 

I only had again a very brief inspection (weather is good here, I have to do gardening), but I see a general problem and no handling for that:

Your main routine which fiddles around with the DLIs is not synchronized to the screen build up. So when changing a DLI f.e. you do not know if affects the current frame or the next one.

And your flicker is still tied to your FPS - that's too slow to look good.

For a solid solution, you have to think the other way around: Display is master and changes are triggered in this "thread". One of the more easier but memory consuming solutions would be e.g. double-buffering and changing the base set of data (PMG-Base, D-List with DLIs, registers) while your display reaches the vertical end.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...