+playsoft Posted September 28, 2017 Author Share Posted September 28, 2017 I was able to buy a years hosting for £12, so the player editor should now be accessible again. 7 Quote Link to comment Share on other sites More sharing options...
mono Posted September 29, 2017 Share Posted September 29, 2017 Thank you very much. 1 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 6, 2017 Share Posted October 6, 2017 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 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 6, 2017 Share Posted October 6, 2017 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 Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted October 6, 2017 Share Posted October 6, 2017 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: 2 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 6, 2017 Share Posted October 6, 2017 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? Quote Link to comment Share on other sites More sharing options...
+Stephen Posted October 6, 2017 Share Posted October 6, 2017 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. Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 9, 2017 Share Posted October 9, 2017 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. Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted October 9, 2017 Share Posted October 9, 2017 (edited) 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. Edited October 9, 2017 by Irgendwer 2 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 10, 2017 Share Posted October 10, 2017 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 2 Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted October 10, 2017 Share Posted October 10, 2017 (edited) 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); } } ... Sample screen shot not really reflecting original machine CRT output. So test for yourself: pmgtest.xex Edited October 10, 2017 by Irgendwer 2 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 11, 2017 Share Posted October 11, 2017 Thanks for the further help Irgend! I am talking with Popmilo and Schlortt in order to figure out the best strategy for displaying the Map and PMs with best colours possible. I should perhaps start a new thread.... Quote Link to comment Share on other sites More sharing options...
+darryl1970 Posted October 11, 2017 Share Posted October 11, 2017 (edited) My favorite tools for the A8. Is the Dk Jr version (Dual Player) still around? I corrected myself. I had Popeye. That was the one with the special punch, right? Edited October 11, 2017 by darryl1970 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 12, 2017 Share Posted October 12, 2017 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 4 Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted October 12, 2017 Share Posted October 12, 2017 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. 1 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 12, 2017 Share Posted October 12, 2017 I think that I can do something pretty efficient with DLI... working on it now. 1 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 13, 2017 Share Posted October 13, 2017 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++; } } 1 Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted October 13, 2017 Share Posted October 13, 2017 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... 1 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 13, 2017 Share Posted October 13, 2017 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 2 Quote Link to comment Share on other sites More sharing options...
Schlortt Posted October 13, 2017 Share Posted October 13, 2017 (edited) 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 October 13, 2017 by Schlortt Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 13, 2017 Share Posted October 13, 2017 (edited) Good idea about using VBI for setting first position. I have added a sorting algorithm which fixes most of the out-of-sync. There are still some flickers though... mind having again a look? sources.zip sprites.xex Edited October 13, 2017 by 8bit-Dude 1 Quote Link to comment Share on other sites More sharing options...
Schlortt Posted October 13, 2017 Share Posted October 13, 2017 See the attached. In this instance, there needs to be an interrupt where the orange line is. You see that it is missing because the empty orange oval on the left should have a Yellow line in it indicating an interrupt. Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 14, 2017 Share Posted October 14, 2017 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 1 Quote Link to comment Share on other sites More sharing options...
8bit-Dude Posted October 15, 2017 Share Posted October 15, 2017 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 1 Quote Link to comment Share on other sites More sharing options...
Irgendwer Posted October 15, 2017 Share Posted October 15, 2017 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.