Jump to content

8bit-Dude

Members
  • Content Count

    181
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by 8bit-Dude


  1. if you want boot & load to $0800-$bfff use this:.

    copy your game to disk, rename game to xbios.com and boot

     

    if you want direct load to:

    $0000-$00ff, $0200-$06ff, $0c00-$cfff, $d800-$ffff use xbios (xbios use ataridos fs/mydos up to 16mb, catalogs, custom SIO)

    Awesome, just what I needed!!!


  2. The multicolored tires is a sign that gprior (623)/ prior (53275) are not set. Enabling the 5th player (bit 4) is what allows you to set the missiles to a single color. I see in your code, you set 623. 623 is a shadow register for hardware register 53275. Shadow registers get copied to hardware registers by the OS routines during the vertical blank as I understand it. Additionally, the clock memlocs (18,19,20) are also managed by the OS routines so that should explain the clock not working. I think this explains what is going here, but whether you use the OS or write custom routines to meet your specific needs is up to you.

    Hey Schlortt

    I will try to figure out the tire issue based on your remarks.

    The OS stuff is much more problematic for me. I do not want to disable it, but the CIN code I obtained from Atari Interlaced Studio seems to require setting the OS off.

    By looking at the ASM code, could you tell me why CIN needs it, and if there is a way to avoid switching off the OS.


  3. So, back with more questions!

    I have integrated my sprites animation with a CIN background image as shown here:

     

    post-53448-0-22649200-1508314834.jpg

     

    I still have a couple of issues:

     

    (1) I set the colour of P4 (tires) with: POKE(53273, PMColor[4]); // Set Player colors

    However, the colour flickers based on P0-P3...

     

    (2) I have an issue with this line of code in the asm file: mva #$fe portb

    This functions is used by CIN to disable to ROM, or something like that, but the functions clock() and cgect() in cc65 stop working.
    If I comment this line I can use clock() and cget(), but then the CIN gfx do not appear.

     

    Could you guyz give me again some advice?? Thx a lot in advance!!!!

    Tony

    post-53448-0-22649200-1508314834.jpg

    sources.zip

    freeway.xex

    • Like 1

  4. Hey Guyz,

     

    I have another question: when exporting ASM code from Atari Interlace Studio, I can compile it with MADS and call from CC65 with a JSR.

    I have created some background GFX in mode G11, with no raster, no DLI, really simple case.

    However, after initialzing the G11 graphics, the sprites don't show up properly.

     

    Would you mind having a quick look at the ASM and C code, and let me know what I am doing wrong?

    Cheers!!!

    // Atari Interlaced Studio
    
    buf0	= $6010
    
    gtictl	= $d01b
    skctl	= $d20f
    portb	= $d301
    
    dmactl	= $d400
    dlptr	= $d402
    wsync	= $d40a
    vcount	= $d40b
    nmien	= $d40e
    nmist	= $d40f
    
    /*-------------------------------------------------------------------------------------------------*/
    
    	org $80
    
    regA	.ds 1
    regX	.ds 1
    regY	.ds 1
    cnt	.ds 1
    
    /*-------------------------------------------------------------------------------------------------*/
    
    	.get 'g11.dat',-9		; palette
    
    	org buf0
    	ins 'g11.dat',0,8000
    
    /*-------------------------------------------------------------------------------------------------*/
    
    	.align	$100
    
    	dlist0:	dta d'ppp'
    		dta $4f,a(buf0)
    		:101 dta $f
    		dta $4f,0,h(buf0+$1000)
    		:89 dta $f
    		dta $41,a(dlist0)
    
    /*-------------------------------------------------------------------------------------------------*/
    
    .proc StartG11
    	lda:cmp:req 20
    
    	mva	#$00	nmien
    	mva	#$fe	portb
    
    	mwa	#dlist0	dlptr
    	mwa	#NMI	$fffa
    	mva	#$c0	nmien
    
    	lda:rne vcount
    	rts				; exit
    .endp
    
    .proc StopG11
    	lda:rne vcount
    
    	mva	#$ff	portb
    	mva	#$40	nmien
    	
    	rts
    .endp
    
    /*-------------------------------------------------------------------------------------------------*/
    
    NMI	
    	bit nmist
    	bpl vbl
    
    vdli equ *-2
    
    vbl	sta nmist
    	phr
    
    	;mva #$22 dmactl
    	mva #62 dmactl
    
    	;mva #$c0 gtictl		; graphics 11
    	mva #241 gtictl		; graphics 11
    
    	plr
    	rti
    
    
    #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 (0x4000)
    
    // 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)
    
    const unsigned char BODY[FRAMES][HEIGHT] =
    { { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x32, 0x32, 0x3e, 0x3c, 0x00, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x04, 0x1e, 0x3a, 0x32, 0x36, 0x3e, 0x18, 0x00, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x04, 0x0e, 0x1a, 0x34, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
      { 0x00, 0x00, 0x18, 0x1c, 0x14, 0x28, 0x38, 0x38, 0x38, 0x30, 0x10, 0x00, 0x00 },
      { 0x00, 0x00, 0x10, 0x38, 0x28, 0x28, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, 0x00 },
      { 0x00, 0x00, 0x30, 0x70, 0x50, 0x28, 0x38, 0x38, 0x38, 0x18, 0x10, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x20, 0x70, 0x58, 0x2c, 0x1c, 0x1c, 0x0c, 0x08, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x20, 0x78, 0x5c, 0x4c, 0x6c, 0x7c, 0x18, 0x00, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x4c, 0x4c, 0x7c, 0x3c, 0x00, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x00, 0x18, 0x7c, 0x6c, 0x4c, 0x5c, 0x78, 0x20, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x2c, 0x58, 0x70, 0x20, 0x00, 0x00 },
      { 0x00, 0x00, 0x08, 0x0c, 0x1c, 0x1c, 0x1c, 0x14, 0x28, 0x38, 0x18, 0x00, 0x00 },
      { 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x14, 0x14, 0x1c, 0x08, 0x00, 0x00 },
      { 0x00, 0x00, 0x08, 0x18, 0x1c, 0x1c, 0x1c, 0x14, 0x0a, 0x0e, 0x0c, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x10, 0x30, 0x38, 0x38, 0x34, 0x1a, 0x0e, 0x04, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x18, 0x3e, 0x36, 0x32, 0x3a, 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, 0x04, 0x4a, 0x46, 0x00, 0x08, 0x08, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x20, 0x28, 0x14, 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, 0x50, 0x44, 0x04, 0x00, 0x20, 0x20, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x18, 0x08, 0x20, 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, 0x20, 0x08, 0x18, 0x00, 0x00 },
      { 0x00, 0x00, 0x10, 0x10, 0x00, 0x02, 0x22, 0x28, 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, 0x0a, 0x14, 0x10, 0x00, 0x00, 0x00 },
      { 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x46, 0x4a, 0x04, 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 TIRES
            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], TIRES[frame[i]], HEIGHT);          
    
            // Next Frame
            if (f > 60) { DrawFPS(); f=0; }
            f++;
        }
    }
    
    /*
    ** Hello world program using cc65.
    */
    
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <conio.h>
    #include <unistd.h>
    #include "sprites1.h"
    
    /*****************************************************************************/
    /*                                   Code                                    */
    /*****************************************************************************/
    
    int main (void)
    {       
        // Start CIN
        __asm__("jsr $80CA"); 
        
    	// Start Sprites
        InitSprites();
        ShowSprites();
    
    	// Stop G11
    	__asm__("jsr $80F9");	
        
        // Done
        return EXIT_SUCCESS;
    }
    
    

    freeway.xex

    sources.zip


  5. 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

  6.  

    Yes, it uses Winpcap. I've avoided using this driver because it has multiple problems: poor security (all programs can use it once it has loaded), licensing issues, and it is detected as potentially unwanted software by some antiviruses. At the time, there was also a problem with SoftEther VPN installing a Winpcap lookalike driver that has the packet sending API subtly broken. There is a new driver called Npcap that shows some promise but I haven't tried it yet.

    Thanks for the clarification. Hope something may be worked out in future with Npcap!


  7. 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

  8.  

    Altirra's NAT does not support ICMP. You'll need to use tunneling to a gateway supporting the VXLAN protocol to use pings from within the emulation.

     

    Thanks for the quick answer buddy!

    By the way, WinVICE support ICMP, it is very helpful for networked games (to know the latency).

    I am developping a cross-platform (C64/A8/AppleII) top-down racing game.

    • Like 1

  9.  

    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

  10. Version 2.90 of my emulator Altirra is now officially released:

     

    And, now, time to start the next set of test releases:

     

    http://www.virtualdub.org/beta/Altirra-2.99-test1.zip

    http://www.virtualdub.org/beta/Altirra-2.99-test1-src.zip

     

     

    Hey Phaeron,

     

    Thanks so much for your continued development of Altirra, amazing work!

     

    I am using Altirra to develop an online game, and ran over a strange issue while using IP65.

    Most demos (including GETURL) work perfectly fine.

     

    But the PING example (see attached xex) returns an error code 82 in Altirra (2.8.1 and 2.9.0)

    I tested on my actual 130XE with DragonCart, and the program runs just fine.

     

    Could you kindly investigate the root cause of this problem?

    Thanks in advance!

    ping.xex

    geturl.xex

    ping.s


  11. 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

  12. 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

  13. 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
×
×
  • Create New...