+bsteux Posted February 8 Share Posted February 8 Hi, I start this new topic to make some announcements about progress on cc2600, my C compiler for Atari 2600 to be found at https://github.com/steux/cc2600. The news for today is the added cc2600 support for TIATracker. I've made a fork on github with a new option in the menu to generate C code : https://github.com/steux/tiatracker/. You will find in attachment an archive with the win32 executable. The C code generated is split into 2 headers (song_tiatracker.h and song_trackdata.h, where song is the song name). The first one contains the variables definition and the player code, the second one the song data itself. Note that these functions and data can be into another bank just by enclosing their definition by a "bankn {}" bracket. 2 functions are defined in the header : tia_tracker_init() and tia_tracker_play(), the first to be called once, the second to be called every frame (50Hz or 60Hz depending on the song definition). Here is an example of code (examples/example_tiatracker.c) that displays a Garfield sprite while playing music : #include "vcs.h" #include "vcs_colors.h" unsigned char X, Y; #define BLANK 40 #define KERNAL 192 #define OVERSCAN 30 char ypos; unsigned char *sprite_ptr; // Pointer to the sprite data to be drawn unsigned char *mask_ptr; // Pointer to the mask for drawing unsigned char *color_ptr; // Pointer to the color table of the sprite // Generated for sprite size 20 with maskgen.c (in cc2600/misc directory) const char sprite_mask[364] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // Drawn with PlayerPal and converted to C code by spritegen.c (in cc2600/misc directory) const unsigned char sprite0[20] = { 0x66, 0x66, 0x76, 0x3a, 0xbc, 0x84, 0x36, 0x76, 0x6e, 0x5e, 0x3e, 0x48, 0xb6, 0xcb, 0x33, 0x55, 0x33, 0x4a, 0x36, 0x12}; const unsigned char colors0[20] = { 0x12, 0x12, 0x2c, 0x2c, 0x38, 0x38, 0x3c, 0x3c, 0x2c, 0x38, 0x3c, 0x3c, 0x3c, 0x3c, 0x0e, 0x0e, 0x0e, 0x3c, 0x3c, 0x3c}; // Music code #include "miniblast_tiatracker.h" #include "miniblast_trackdata.h" void init() { *COLUBK = VCS_GREEN; *COLUPF = VCS_LGREEN; ypos = 100; // Position sprite horizontally strobe(WSYNC); X = 6; do { X--; } while (X >= 0); strobe(RESP0); strobe(WSYNC); } void main() { tia_tracker_init(); init(); while(1) { *VBLANK = 2; // Disable VBLANK *VSYNC = 2; // Set VSYNC strobe(WSYNC); // Hold it for 3 scanlines strobe(WSYNC); strobe(WSYNC); *VSYNC = 0; // Turn VSYNC Off // Blank *TIM64T = ((BLANK - 3) * 76) / 64 + 1; // Do some logic here // Set up sprite pointer sprite_ptr = sprite0 - 1 - ypos; // -1 offset because lower position (ypos = 0) matches sprite_ptr[Y = 1] (line 96) mask_ptr = sprite_mask + KERNAL - sizeof(sprite0) - 1 - ypos; // Same offset as speite_ptr // Set up color pointer color_ptr = colors0 - ypos; // 0 offset baecause lower position (ypos = 0) matches color_ptr[Y = 0] (lint 103) // Joystick input if (!(*SWCHA & 0x80)) { *HMP0 = 0xF0; *REFP0 = 0; } // Right if (!(*SWCHA & 0x40)) { *HMP0 = 0x10; *REFP0 = 8; } // Left if (!(*SWCHA & 0x20) && ypos > 0) ypos--; // Down if (!(*SWCHA & 0x10) && ypos < 192 - sizeof(sprite0)) ypos++; // Up // Apply movement strobe(WSYNC); strobe(HMOVE); // And stop any movement strobe(WSYNC); strobe(HMCLR); Y = KERNAL; // Initialize line counter X = sprite_ptr[Y] & mask_ptr[Y]; // Preload sprite data for the first line Y--; // Load TIA registers for first line *GRP0 = X; *COLUP0 = color_ptr[Y]; *PF2 = Y; // Marker. Just to check that what is displayed is correct // Do some extra logic while (*INTIM); strobe(WSYNC); *VBLANK = 0; load(sprite_ptr[Y] & mask_ptr[Y]); Y--; // Image drawing do { strobe(WSYNC); store(*GRP0); // Apply preloaded sprite data *COLUP0 = color_ptr[Y]; // Load current line sprite color *PF2 = Y; // Marker. Just to check that what is displayed is correct load(sprite_ptr[Y] & mask_ptr[Y]); // Load next line sprite data Y--; } while (Y); // Last line is out of loop and is simpler strobe(WSYNC); store(*GRP0); // Apply preloaded sprite data *COLUP0 = color_ptr[Y]; // Load current line sprite color *PF2 = Y; // Marker. Just to check that what is displayed is correct strobe(WSYNC); // Overscan *VBLANK = 2; // Enable VBLANK *TIM64T = (OVERSCAN * 76) / 64 + 2; // Do some logic here tia_tracker_play(); while (*INTIM); } } See you soon for some other news for cc2600. example_tiatracker.a26 tiatracker_cc_v1.1.zip 7 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted February 11 Author Share Posted February 11 At last, a Helloworld example is provided in cc2600/examples. It uses 48 pixels wide display provided in minikernel.h header : #include "vcs.h" #include "vcs_colors.h" #include "minikernel.h" unsigned char X, Y; #define BLANK 40 #define KERNAL 192 #define OVERSCAN 30 // Helloworld 48 pixels wide generated by tools2600/text2rom2600 aligned(256) const char line0[42] = { 0x53, 0x54, 0x57, 0x75, 0x52, 0x50, 0x50, 0x77, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x20, 0x50, 0x50, 0x50, 0x20, 0x00, 0x00, 0x52, 0x75, 0x75, 0x75, 0x52, 0x50, 0x50, 0x47, 0x42, 0x42, 0x52, 0x62, 0x02, 0x06, 0x32, 0x50, 0x52, 0x52, 0x32, 0x12, 0x12}; void main() { char i; mini_kernel_position_sprites_center(); *COLUP0 = VCS_WHITE; *COLUP1 = VCS_WHITE; while(1) { *VBLANK = 2; // Disable VBLANK *VSYNC = 2; // Set VSYNC strobe(WSYNC); // Hold it for 3 scanlines strobe(WSYNC); strobe(WSYNC); *VSYNC = 0; // Turn VSYNC Off // Blank *TIM64T = ((BLANK - 3) * 76) / 64 + 1; // Do some logic here Y = KERNAL; // Initialize line counter // Do some extra logic while (*INTIM); // Image drawing strobe(WSYNC); *VBLANK = 0; Y--; do { strobe(WSYNC); Y--; } while (Y >= 110); i = Y; *COLUP0 = VCS_BLUE; *COLUP1 = VCS_BLUE; mini_kernel_display_text(line0, 7); Y = i - 11; i = Y; *COLUP0 = VCS_WHITE; *COLUP1 = VCS_WHITE; mini_kernel_display_text(line0, 7); Y = i - 11; i = Y; *COLUP0 = VCS_RED; *COLUP1 = VCS_RED; mini_kernel_display_text(line0, 7); Y = i - 11; do { strobe(WSYNC); Y--; } while (Y); // Last line is out of loop and is generally simpler strobe(WSYNC); strobe(WSYNC); // Overscan *VBLANK = 2; // Enable VBLANK *TIM64T = (OVERSCAN * 76) / 64; // Do some logic here while (*INTIM); } } The "Hello World!" graphics is generated with a new tool called text2rom2600 (https://github.com/steux/tools2600/) using font.png in attachment. helloworld.txt text2rom2600.exe hello_world.a26 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted February 15 Author Share Posted February 15 Here is a new example demonstrating the sfx.h header of cc2600 that plays sounds from sound2tia, a utility designed by @RevEng (https://github.com/7800-devtools/sound2tia). It's very simple to use: sfx_schedule schedules a sound to be played, and sfx_play should be called every frame. The mono sounds are scheduled on any empty channel or the channel playing the lowest priority sound (the second byte of the sound array). I've ported all the sounds found in 7800basic (a collection of 117 sounds) on cc2600, yielding a 32KB cart (4 4KB banks are used for the sounds, 2 for the text menu, the remaining being almost empty) ! The menu for choosing the sound to play is constructed using the text2rom2600 utility found in tools2600 on github. Have fun ! example_sfx.a26 example_sfx.c 3 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted February 17 Author Share Posted February 17 Today, I introduce sprites2600, a tool (written in Rust) designed to generate C code for sprites to be displayed by cc2600 multisprite engine. You can find this tool at https://github.com/steux/tools2600 (windows executable in attachment). sprites2600 processes a YAML file that describes all the sprites to be generated (associated to .png spritesheets). For instance, for the picture above (some shmup sprites - drawn with GIMP), the YAML file is : sprite_sheets: - image: shmup.png sprites: - name: spaceship top: 0 left: 0 height: 16 color_copy: spaceship_exhaust - name: spaceship_exhaust top: 0 left: 0 height: 24 - name: fire top: 0 left: 64 height: 9 color_copy: spaceship_exhaust color_offset: 15 - name: bullet top: 9 left: 64 height: 9 - name: explosion1 top: 0 left: 80 height: 9 color_copy: explosion3 - name: explosion2 top: 0 left: 88 height: 11 color_copy: explosion3 - name: explosion3 top: 0 left: 96 height: 12 - name: explosion4 top: 0 left: 104 height: 12 color_copy: explosion3 - name: explosion5 top: 0 left: 112 height: 12 color_copy: explosion3 - name: enemy1 top: 0 left: 72 height: 16 - name: bigboss top: 24 left: 0 height: 32 pixel_width: 2 - name: letter_g top: 0 left: 8 height: 16 color_copy: spaceship_exhaust - name: letter_a top: 0 left: 16 height: 16 color_copy: spaceship_exhaust - name: letter_m top: 0 left: 24 height: 16 color_copy: spaceship_exhaust - name: letter_e top: 0 left: 32 height: 16 color_copy: spaceship_exhaust - name: letter_o top: 0 left: 40 height: 16 color_copy: spaceship_exhaust - name: letter_v top: 0 left: 48 height: 16 color_copy: spaceship_exhaust - name: letter_r top: 0 left: 56 height: 16 color_copy: spaceship_exhaust The generated C code for the command "sprites2600 shmup.yaml" (examples/example_shmup_gfx.c in cc2600 repositorty) will be : MS_KERNEL_BANK const char spaceship_gfx[20] = {0, 0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x18, 0x18, 0x3c, 0xbd, 0xff, 0xdb, 0xdb, 0xdb, 0x66, 0x66, 0, 0}; MS_KERNEL_BANK const char spaceship_exhaust_gfx[28] = {0, 0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x18, 0x18, 0x3c, 0xbd, 0xff, 0xdb, 0xdb, 0xdb, 0x66, 0x66, 0xf6, 0xff, 0xff, 0x6f, 0x76, 0x6e, 0x66, 0x22, 0, 0}; #ifdef PAL MS_KERNEL_BANK const char spaceship_exhaust_colors[26] = {0, 0, 0x04, 0x04, 0xd4, 0xd0, 0xd0, 0x06, 0x08, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0c, 0x0e, 0x0e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x2a, 0x2a, 0x2c, 0x1c}; #else MS_KERNEL_BANK const char spaceship_exhaust_colors[26] = {0, 0, 0x04, 0x04, 0x84, 0x80, 0x80, 0x06, 0x08, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0c, 0x0e, 0x0e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x1a, 0x1a, 0x1c, 0x1c}; #endif MS_KERNEL_BANK const char fire_gfx[13] = {0, 0, 0x42, 0xe7, 0xe7, 0xe7, 0x42, 0x42, 0x42, 0x42, 0x42, 0, 0}; MS_KERNEL_BANK const char bullet_gfx[13] = {0, 0, 0x3c, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0, 0}; #ifdef PAL MS_KERNEL_BANK const char bullet_colors[11] = {0, 0, 0x2c, 0x2c, 0x2a, 0x2a, 0x48, 0x46, 0x44, 0x42, 0x30}; #else MS_KERNEL_BANK const char bullet_colors[11] = {0, 0, 0x1c, 0x1c, 0x1a, 0x1a, 0x38, 0x36, 0x34, 0x32, 0x30}; #endif MS_KERNEL_BANK const char explosion1_gfx[13] = {0, 0, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x24, 0x34, 0x08, 0, 0}; MS_KERNEL_BANK const char explosion2_gfx[15] = {0, 0, 0x00, 0x00, 0x08, 0x3c, 0x12, 0x26, 0x64, 0x2c, 0x2e, 0x30, 0x08, 0, 0}; MS_KERNEL_BANK const char explosion3_gfx[16] = {0, 0, 0x06, 0x68, 0x77, 0xcf, 0xa3, 0x93, 0xd3, 0x41, 0xf5, 0x48, 0x6a, 0x1e, 0, 0}; #ifdef PAL MS_KERNEL_BANK const char explosion3_colors[14] = {0, 0, 0x2e, 0x2c, 0x2a, 0x48, 0x64, 0x60, 0x60, 0x64, 0x48, 0x2a, 0x2c, 0x1e}; #else MS_KERNEL_BANK const char explosion3_colors[14] = {0, 0, 0x1e, 0x1c, 0x1a, 0x38, 0x44, 0x40, 0x40, 0x44, 0x38, 0x1a, 0x1c, 0x1e}; #endif MS_KERNEL_BANK const char explosion4_gfx[16] = {0, 0, 0x23, 0xe5, 0xaa, 0x84, 0x47, 0x40, 0x00, 0x86, 0xa3, 0xf7, 0x76, 0x52, 0, 0}; MS_KERNEL_BANK const char explosion5_gfx[16] = {0, 0, 0x25, 0xc2, 0x41, 0x81, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x82, 0x64, 0x43, 0, 0}; MS_KERNEL_BANK const char enemy1_gfx[20] = {0, 0, 0x22, 0x44, 0x6e, 0xf7, 0x66, 0x5a, 0xdb, 0xdb, 0x5a, 0x7e, 0x3c, 0x18, 0x58, 0x58, 0x58, 0x18, 0, 0}; #ifdef PAL MS_KERNEL_BANK const char enemy1_colors[18] = {0, 0, 0x2e, 0x2a, 0x48, 0x64, 0x60, 0x28, 0x58, 0x28, 0x28, 0x28, 0x26, 0xd0, 0x26, 0x58, 0x64, 0x40}; #else MS_KERNEL_BANK const char enemy1_colors[18] = {0, 0, 0x1e, 0x1a, 0x38, 0x44, 0x40, 0xe8, 0xc8, 0xe8, 0xe8, 0xe8, 0xe6, 0x80, 0xe6, 0xc8, 0x44, 0x40}; #endif MS_KERNEL_BANK const char bigboss_gfx[36] = {0, 0, 0x04, 0x0c, 0x0c, 0x0e, 0x1e, 0x5e, 0x5e, 0x5e, 0x5f, 0x6f, 0xeb, 0xeb, 0xeb, 0xe7, 0xe7, 0xf7, 0xf7, 0x77, 0x73, 0x73, 0xf7, 0xff, 0xff, 0xff, 0x7f, 0x7f, 0x3f, 0x3f, 0x1e, 0x0c, 0x1e, 0x0c, 0, 0}; #ifdef PAL MS_KERNEL_BANK const char bigboss_colors[34] = {0, 0, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xac, 0xa2, 0xac, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa6, 0xa2, 0xa6, 0xa6, 0xa2, 0xa2, 0xa2, 0xa4, 0xa4, 0xa2, 0xa4, 0xa2, 0xa2, 0x40, 0x46, 0x1c}; #else MS_KERNEL_BANK const char bigboss_colors[34] = {0, 0, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6c, 0x62, 0x6c, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x66, 0x62, 0x66, 0x66, 0x62, 0x62, 0x62, 0x64, 0x64, 0x62, 0x64, 0x62, 0x62, 0x30, 0x36, 0x1c}; #endif MS_KERNEL_BANK const char letter_g_gfx[20] = {0, 0, 0x3c, 0x3e, 0x76, 0x60, 0xe0, 0xc0, 0xc0, 0xcf, 0xc3, 0xc3, 0xc3, 0xc7, 0x66, 0x7e, 0x3e, 0x3c, 0, 0}; MS_KERNEL_BANK const char letter_a_gfx[20] = {0, 0, 0x18, 0x18, 0x1c, 0x3e, 0x36, 0x36, 0x36, 0x62, 0x63, 0x63, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0, 0}; MS_KERNEL_BANK const char letter_m_gfx[20] = {0, 0, 0xfc, 0xde, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0, 0}; MS_KERNEL_BANK const char letter_e_gfx[20] = {0, 0, 0x3f, 0x3f, 0x70, 0x60, 0xe0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xe0, 0x7f, 0x7f, 0x3f, 0x3f, 0, 0}; MS_KERNEL_BANK const char letter_o_gfx[20] = {0, 0, 0x3c, 0x3e, 0x76, 0x63, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0x66, 0x7e, 0x3c, 0x3c, 0, 0}; MS_KERNEL_BANK const char letter_v_gfx[20] = {0, 0, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xe6, 0x66, 0x6c, 0x6c, 0x3c, 0x38, 0x18, 0x18, 0, 0}; MS_KERNEL_BANK const char letter_r_gfx[20] = {0, 0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xfc, 0xcc, 0xc6, 0xc6, 0xc3, 0xc3, 0, 0}; #define MS_NB_SPRITES_DEF 18 MS_KERNEL_BANK const char *ms_grptr[MS_NB_SPRITES_DEF] = {spaceship_gfx, spaceship_exhaust_gfx, fire_gfx, bullet_gfx, explosion1_gfx, explosion2_gfx, explosion3_gfx, explosion4_gfx, explosion5_gfx, enemy1_gfx, bigboss_gfx, letter_g_gfx, letter_a_gfx, letter_m_gfx, letter_e_gfx, letter_o_gfx, letter_v_gfx, letter_r_gfx}; MS_KERNEL_BANK const char *ms_coluptr[MS_NB_SPRITES_DEF] = {spaceship_exhaust_colors, spaceship_exhaust_colors, spaceship_exhaust_colors + 15, bullet_colors, explosion3_colors, explosion3_colors, explosion3_colors, explosion3_colors, explosion3_colors, enemy1_colors, bigboss_colors, spaceship_exhaust_colors, spaceship_exhaust_colors, spaceship_exhaust_colors, spaceship_exhaust_colors, spaceship_exhaust_colors, spaceship_exhaust_colors, spaceship_exhaust_colors}; MS_KERNEL_BANK const char ms_height[MS_NB_SPRITES_DEF] = {19, 27, 12, 12, 12, 14, 15, 15, 15, 19, 35, 19, 19, 19, 19, 19, 19, 19}; #define SPRITE_SPACESHIP 0 #define SPRITE_SPACESHIP_EXHAUST 1 #define SPRITE_FIRE 2 #define SPRITE_BULLET 3 #define SPRITE_EXPLOSION1 4 #define SPRITE_EXPLOSION2 5 #define SPRITE_EXPLOSION3 6 #define SPRITE_EXPLOSION4 7 #define SPRITE_EXPLOSION5 8 #define SPRITE_ENEMY1 9 #define SPRITE_BIGBOSS 10 #define SPRITE_LETTER_G 11 #define SPRITE_LETTER_A 12 #define SPRITE_LETTER_M 13 #define SPRITE_LETTER_E 14 #define SPRITE_LETTER_O 15 #define SPRITE_LETTER_V 16 #define SPRITE_LETTER_R 17 which is a code ready to be used with the cc2600 multisprite code. Node that drawing the picture with GIMP requires a few steps : - The NTSC Atari 2600 Palette should be used in GIMP. Only these colors will be processed by sprites2600 (logically). The .gpl GIMP palette file is in attachment and is shown in the GIMP picture above. - Only one color per sprite line. This is an Atari 2600... - You can reuse others sprites color, using the color_copy option Note that sprites2600 generates the NTSC and PAL colors (activated by #define PAL or -DPAL on the cc2600 compilation command line). Note also that GIMP is perfectly suited to draw Atari 2600 sprites, since it can be configured to use "fat pixels". For this, just change the Image/Print size... to set a non square DPI, like in the picture below : And click in View/Dot for Dot to disable the square display. You will then be able to edit pictures with flat pixels. Now that we have the music (tiatracker support), the sound effects (sfx support), the sprites tool (sprites2600), the multisprite library, we have everything to make 60fps vertical scrolling shmup on the venerable Atari VCS. Do you think it's possible to turn the grandma into a vertical shmup machine ? sprites2600.exe shmup.yaml HW-Atari-2600-NTSC.gpl 1 Quote Link to comment Share on other sites More sharing options...
+karri Posted February 19 Share Posted February 19 On 2/17/2024 at 7:33 PM, bsteux said: Do you think it's possible to turn the grandma into a vertical shmup machine ? With your track record in the cc7800... I would not answer no. I won't be able to do it 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted February 21 Author Share Posted February 21 On 2/19/2024 at 8:40 AM, karri said: With your track record in the cc7800... I would not answer no. I won't be able to do it A preview from my week-end's work... It's starting to take shape on my light sixer... shmup 2600.mp4 2 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted February 22 Author Share Posted February 22 Shmup, shmup, shmup ! Here is a full fledged vertical scrolling shmup. What's inside ? I've tried to put enemies, bullets, spawning, scrolling, game over, scoring, music (tiatracker), sound effects all together. And well, surprinsingly, it fits well inside old' grandma. No ARM code, no assembler. Pure cc2600. The only compromise I had to do was not to move all enemies at each frame. Well, I move them virtually (i.e. I update their coordinates every frame), but the sprite itself is moved / updated on screen... when there is still time left. And the result is rather convincing. What is quite satisfying is the not-so-blinking behaviour of the multisprite library, and the resulting relative simplicity of the code. It served as a good debugging example for cc2600 (latest version in attachment). Note that I'm using here a 16Kb setup (4 banks) with Superchip (i.e. 128 additional RAM bytes, eating 256 bytes in each bank...) : Bank 0 contains the main loop (3131 bytes left). Bank 1 contains the multisprite kernel code, the sprites and the playfield (here very simplistic) (411 bytes still left) Bank 2 contains the game logic (spawning, enemy moves, shooting detection, etc) (1129 bytes still left) Bank 3 contains the bottom minikernel (for score display), the music data & player (TIATracker), and the sound effects (948 bytes left). Still plenty of room for many improvements (pow, other weapons, many enemies). Why not a 256KB cart with many levels ? Note the use of NUSIZ to make rows of enemies (like in Space Invaders) and bullets. A nifty feature of the Atari 2600... And the "big boss" sprites made of 2 sprites. And the 3d effect with the spaceship going inside the circles. No need for a Nvidia RTX ! Why reinvent the wheel when you have the TIA ? Here is the full source code (examples/example_shmup.c) : #include "vcs.h" #include "vcs_colors.h" #define EXTRA_RAM superchip #define MS_OFFSCREEN_BANK bank2 #define MS_OFFSCREEN2_BANK bank3 #define MS_KERNEL_BANK bank1 #define MS_MAX_NB_SPRITES 10 // Music code bank3 { #include "miniblast_tiatracker.h" #include "miniblast_trackdata.h" #include "sfx.h" const char sfx_pewpew[66] = { 0x10, 0, 0x00, 0x1c, 0x04, 0x0f, 0x1c, 0x04, 0x0f, 0x09, 0x04, 0x0b, 0x03, 0x0c, 0x0a, 0x04, 0x0c, 0x0e, 0x12, 0x04, 0x0c, 0x19, 0x04, 0x0f, 0x1c, 0x04, 0x0f, 0x07, 0x04, 0x05, 0x09, 0x04, 0x05, 0x0d, 0x04, 0x06, 0x0c, 0x04, 0x05, 0x18, 0x04, 0x06, 0x1c, 0x04, 0x05, 0x1e, 0x04, 0x03, 0x07, 0x04, 0x03, 0x09, 0x04, 0x03, 0x0c, 0x04, 0x02, 0x04, 0x0c, 0x02, 0x06, 0x0c, 0x01, 0x00, 0x00, 0x00 }; const char sfx_bigboom[261] = { 0x10, 1, 0x00, 0x1d, 0x07, 0x0f, 0x1e, 0x06, 0x0f, 0x00, 0x06, 0x0f, 0x14, 0x07, 0x0f, 0x13, 0x0f, 0x0f, 0x1b, 0x07, 0x0f, 0x0e, 0x07, 0x0f, 0x1b, 0x07, 0x0f, 0x0f, 0x07, 0x0f, 0x10, 0x07, 0x0f, 0x10, 0x06, 0x0f, 0x16, 0x07, 0x0f, 0x0d, 0x0f, 0x0f, 0x1e, 0x0c, 0x0f, 0x16, 0x01, 0x0f, 0x17, 0x01, 0x0f, 0x10, 0x07, 0x0f, 0x10, 0x0f, 0x0f, 0x15, 0x07, 0x0d, 0x1a, 0x07, 0x0f, 0x1a, 0x01, 0x0f, 0x1a, 0x07, 0x0f, 0x14, 0x0f, 0x0f, 0x16, 0x07, 0x0f, 0x16, 0x07, 0x0f, 0x15, 0x07, 0x0f, 0x17, 0x07, 0x0f, 0x13, 0x0f, 0x0f, 0x13, 0x0f, 0x0f, 0x19, 0x0f, 0x0f, 0x18, 0x07, 0x0c, 0x0b, 0x06, 0x0c, 0x1e, 0x01, 0x0d, 0x10, 0x01, 0x0d, 0x14, 0x07, 0x0f, 0x16, 0x06, 0x0c, 0x17, 0x07, 0x0c, 0x1a, 0x01, 0x0c, 0x12, 0x06, 0x0d, 0x17, 0x07, 0x0c, 0x0b, 0x0f, 0x0c, 0x19, 0x07, 0x09, 0x19, 0x07, 0x0b, 0x0b, 0x0f, 0x09, 0x0d, 0x0e, 0x0b, 0x0d, 0x0e, 0x0b, 0x19, 0x0f, 0x09, 0x0e, 0x0f, 0x06, 0x1b, 0x0c, 0x08, 0x18, 0x0f, 0x08, 0x13, 0x07, 0x05, 0x1a, 0x01, 0x05, 0x17, 0x0f, 0x08, 0x16, 0x06, 0x08, 0x0c, 0x06, 0x05, 0x1c, 0x0f, 0x06, 0x16, 0x06, 0x08, 0x0b, 0x06, 0x06, 0x12, 0x06, 0x04, 0x0f, 0x0f, 0x05, 0x11, 0x07, 0x06, 0x09, 0x06, 0x05, 0x10, 0x06, 0x05, 0x10, 0x06, 0x05, 0x10, 0x06, 0x05, 0x11, 0x0f, 0x04, 0x15, 0x0f, 0x04, 0x1e, 0x07, 0x05, 0x16, 0x01, 0x04, 0x16, 0x01, 0x04, 0x1a, 0x0f, 0x04, 0x19, 0x0f, 0x02, 0x1e, 0x0f, 0x02, 0x1b, 0x0f, 0x02, 0x1e, 0x0f, 0x02, 0x1c, 0x0f, 0x02, 0x0d, 0x0f, 0x01, 0x0f, 0x06, 0x02, 0x0e, 0x06, 0x01, 0x18, 0x0f, 0x01, 0x0b, 0x06, 0x02, 0x16, 0x0f, 0x01, 0x17, 0x0f, 0x01, 0x13, 0x06, 0x01, 0x0f, 0x0e, 0x01, 0x00, 0x00, 0x00 }; } // Sprites data (generated by sprite2600 shmup.yaml > example_shmup_gfx.c) #include "example_shmup_gfx.c" #define BLANK 40 #define OVERSCAN 30 #define BULLET_L 0 #define BULLET_BL 1 #define BULLET_B 2 #define BULLET_BR 3 #define BULLET_R 4 #define BULLET_TR 5 #define BULLET_T 6 #define BULLET_TL 7 MS_OFFSCREEN_BANK { const char sprite_width[8] = {8, 24, 40, 40, 72, 16, 72, 32}; const char sprite_is_one_invader[8] = {1, 0, 0, 0, 0, 1, 0, 1}; const char invader_score[2] = {100, 10}; const char sprite_new_nusiz_remove_left[7] = {0, 0, 0, 1, 0, 0, 0}; const char sprite_offset_remove_left[7] = {0, 16, 32, 16, 64, 0, 32}; const char sprite_new_nusiz_remove_right[7] = {0, 0, 0, 1, 0, 0, 2}; const signed char bullet_dx[8] = {-2, -1, 0, 1, 2, 1, 0, -1}; const signed char bullet_dy[8] = {0, 2, 3, 2, 0, -2, -3, -2}; const char bullet_start_direction[8] = {BULLET_BL, BULLET_B, BULLET_BR, BULLET_B, BULLET_L, BULLET_R, BULLET_TL, BULLET_TR}; } #define REG_COLUPF 0x08 #define REG_COLUBK 0x09 #define REG_CTRLPF 0x0a // LSB: Playfield priority / Score mode / Reflective playfield #define REG_PF0 0x0d #define REG_PF1 0x0e #define REG_PF2 0x0f MS_KERNEL_BANK const char playfield[] = { 5, REG_CTRLPF, 0, REG_PF0, 0, REG_PF1, 0, REG_PF2, VCS_BLUE, REG_COLUPF, 0xfc, REG_PF2, 0xff, REG_PF2, 0x03, REG_PF1, 0x0f, REG_PF1, 0x1f, REG_PF1, 0x3f, REG_PF1, 0x7f, REG_PF1, 0xff, REG_PF1, 0x80, REG_PF0, 0, REG_PF2, 0xc0, REG_PF0, 0xf0, REG_PF1, 0xe0, REG_PF0, 0xe0, REG_PF1, 0xc0, REG_PF1, 0xf0, REG_PF0, 0x80, REG_PF1, VCS_WHITE, REG_COLUPF, 0x00, REG_PF1, 1, REG_CTRLPF, 0x70, REG_PF0, VCS_LGREY, REG_COLUPF, 0xf0, REG_PF0, 0xe0, REG_PF0, 0xc0, REG_PF1, VCS_GREY, REG_COLUPF, 0xf0, REG_PF1, 0xc0, REG_PF0, 0xff, REG_PF1, 0x0, REG_PF0, 0xff, REG_PF2, 0x0f, REG_PF1, 0x04, REG_COLUPF, 0, REG_PF1, 0, REG_PF2, 5, REG_CTRLPF, 0, REG_PF0, 0, REG_PF1, 0, REG_PF2, VCS_BLUE, REG_COLUPF, 0xfc, REG_PF2, 0xff, REG_PF2, 0x03, REG_PF1, 0x0f, REG_PF1, 0x1f, REG_PF1, 0x3f, REG_PF1, 0x7f, REG_PF1, 0xff, REG_PF1, 0x80, REG_PF0, 0, REG_PF2, 0xc0, REG_PF0, 0xf0, REG_PF1, 0xe0, REG_PF0, 0xe0, REG_PF1, 0xc0, REG_PF1, 0xf0, REG_PF0, 0x80, REG_PF1, VCS_WHITE, REG_COLUPF, 0x00, REG_PF1, 1, REG_CTRLPF, 0x70, REG_PF0, VCS_LGREY, REG_COLUPF, 0xf0, REG_PF0, 0xe0, REG_PF0, 0xc0, REG_PF1, VCS_GREY, REG_COLUPF, 0xf0, REG_PF1, 0xc0, REG_PF0, 0xff, REG_PF1, 0x0, REG_PF0, 0xff, REG_PF2, 0x0f, REG_PF1, 0x04, REG_COLUPF, 0, REG_PF1, 0, REG_PF2, 5, REG_CTRLPF, 0, REG_PF0, 0, REG_PF1, 0, REG_PF2, VCS_BLUE, REG_COLUPF, 0xfc, REG_PF2, 0xff, REG_PF2, 0x03, REG_PF1, 0x0f, REG_PF1, 0x1f, REG_PF1, 0x3f, REG_PF1, 0x7f, REG_PF1, 0xff, REG_PF1, 0x80, REG_PF0, 0, REG_PF2, 0xc0, REG_PF0, 0xf0, REG_PF1, 0xe0, REG_PF0, 0xe0, REG_PF1, 0xc0, REG_PF1, 0xf0, REG_PF0, 0x80, REG_PF1, VCS_WHITE, REG_COLUPF, 0x00, REG_PF1, 1, REG_CTRLPF, 0x70, REG_PF0, VCS_LGREY, REG_COLUPF, 0xf0, REG_PF0, 0xe0, REG_PF0, 0xc0, REG_PF1, VCS_GREY, REG_COLUPF, 0xf0, REG_PF1, 0xc0, REG_PF0, 0xff, REG_PF1, 0x0, REG_PF0, 0xff, REG_PF2, 0x0f, REG_PF1, 0x04, REG_COLUPF, 0, REG_PF1, 0, REG_PF2, 5, REG_CTRLPF, 0, REG_PF0, 0, REG_PF1, 0, REG_PF2, VCS_BLUE, REG_COLUPF, 0xfc, REG_PF2, 0xff, REG_PF2, 0x03, REG_PF1, 0x0f, REG_PF1, 0x1f, REG_PF1, 0x3f, REG_PF1, 0x7f, REG_PF1, 0xff, REG_PF1, 0x80, REG_PF0, 0, REG_PF2, 0xc0, REG_PF0, 0xf0, REG_PF1, 0xe0, REG_PF0, 0xe0, REG_PF1, 0xc0, REG_PF1, 0xf0, REG_PF0, 0x80, REG_PF1, VCS_WHITE, REG_COLUPF, 0x00, REG_PF1, 1, REG_CTRLPF, 0x70, REG_PF0, VCS_LGREY, REG_COLUPF, 0xf0, REG_PF0, 0xe0, REG_PF0, 0xc0, REG_PF1, VCS_GREY, REG_COLUPF, 0xf0, REG_PF1, 0xc0, REG_PF0, 0xff, REG_PF1, 0x0, REG_PF0, 0xff, REG_PF2, 0x0f, REG_PF1, 0x04, REG_COLUPF, 0, REG_PF1, 0, REG_PF2 }; #define MS_PLAYFIELD_HEIGHT (192 - 12) #define MS_SELECT_FAST #include "multisprite.h" #define MK_ARMY_FONT #define MK_BANK bank3 #include "minikernel.h" MK_BANK const char lives31[7] = {0x02, 0x07, 0x57, 0xf9, 0xf9, 0x20, 0x20}; MK_BANK const char lives32[7] = {0x80, 0xc0, 0xd4, 0x3e, 0x3e, 0x08, 0x08}; MK_BANK const char lives22[7] = {0x80, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00}; MK_BANK const char livesdummy[1] = {0}; MK_BANK const char lives11[7] = {0x00, 0x00, 0x50, 0xf8, 0xf8, 0x20, 0x20}; MK_BANK const char *livesleft[4] = { lives22 + 3, lives11, lives31, lives31 }; MK_BANK const char *livesright[4] = { lives22 + 3, lives22 + 3, lives22, lives32 }; EXTRA_RAM char player_xpos, player_ypos, player_state, player_state2, player_timer, player_rank, nb_lives; EXTRA_RAM char button_pressed; EXTRA_RAM char missile_sprite, missile_rank; EXTRA_RAM int score; EXTRA_RAM char update_score; EXTRA_RAM char game_counter; EXTRA_RAM char game_state; EXTRA_RAM char background_color; EXTRA_RAM char moving_enemy_counter; EXTRA_RAM char do_update_live_display; EXTRA_RAM char do_schedule_sfx; #define GAME_STARTED 0 #define GAME_OVER 1 #define MAX_NB_ENEMIES 3 EXTRA_RAM char enemy_sprite[MAX_NB_ENEMIES], enemy_type[MAX_NB_ENEMIES], enemy_state[MAX_NB_ENEMIES], enemy_counter[MAX_NB_ENEMIES], enemy_rank[MAX_NB_ENEMIES], enemy_x[MAX_NB_ENEMIES], enemy_y[MAX_NB_ENEMIES]; #define MAX_NB_BULLETS 3 EXTRA_RAM char bullet_sprite[MAX_NB_BULLETS], bullet_direction[MAX_NB_BULLETS], bullet_rank[MAX_NB_BULLETS]; #ifdef DEBUG char min_timer_vblank; char min_timer_overscan; #endif MS_KERNEL_BANK prepare_background(char scrolling) { char j, start = 0; scrolling += 12; if (scrolling >= 30) start = scrolling - 30; *PF2 = 0; *COLUBK = background_color; // Replay background to put the correct colors/regs for (Y = start; Y != scrolling;) { j = playfield[Y++]; X = playfield[Y++]; VSYNC[X] = j; } } MK_BANK update_lives_display() { X = nb_lives; mk_s0 = livesleft[X]; mk_s1 = livesright[X]; } void game_init() { multisprite_init(playfield); score = 0; update_score = 1; player_xpos = 76; player_ypos = 192; player_state = 0; moving_enemy_counter = 0; do_update_live_display = 0; do_schedule_sfx = 0; missile_sprite = MS_UNALLOCATED; button_pressed = 1; player_timer = 1; nb_lives = 3; for (X = MAX_NB_ENEMIES - 1; X >= 0; X--) { enemy_type[X] = 0; } for (X = MAX_NB_BULLETS - 1; X >= 0; X--) { bullet_sprite[X] = 0; } update_lives_display(); game_counter = 0; game_state = GAME_STARTED; multisprite_new(SPRITE_SPACESHIP, player_xpos, player_ypos, 0); player_rank = X; background_color = VCS_BLACK; } MS_OFFSCREEN_BANK void spawn_new_enemy(char type, char spec) { char i, r, s; for (X = MAX_NB_ENEMIES - 1; X >= 0; X--) { if (!enemy_type[X]) break; } if (X == -1) return; // No room for this enemy enemy_type[X] = type; i = X; if (type == 1) { enemy_state[X] = 0; enemy_counter[X] = 0; r = multisprite_new(SPRITE_ENEMY1, 60, 22, 3); s = X; X = i; if (r == -1) { enemy_type[X] = 0; // No room left for this enemy } else { enemy_sprite[X] = r; enemy_state[X] = spec; enemy_rank[X] = s; enemy_x[X] = 60; enemy_y[X] = 22; } } else if (type == 128) { enemy_counter[X] = 3; r = multisprite_new(SPRITE_BIGBOSS, spec, 2, 5); Y = X; X = i; enemy_rank[X] = Y; s = multisprite_new(SPRITE_BIGBOSS, spec + 16, 2, 5 | MS_REFLECTED); X = i; if (r == -1) { enemy_type[X] = 0; // No room left for this enemy } else { enemy_sprite[X] = r; enemy_state[X] = s; enemy_x[X] = spec; enemy_y[X] = 2; } } } MS_OFFSCREEN_BANK void fire_new_bullet(char x, char y, char direction, char nusiz) { char i, r, s; for (X = MAX_NB_BULLETS - 1; X >= 0; X--) { if (!bullet_sprite[X]) break; } if (X == -1) return; // No room for this bullet i = X; r = multisprite_new(SPRITE_BULLET, x, y, nusiz); s = X; X = i; if (r != -1) { bullet_sprite[X] = r; bullet_direction[X] = direction; bullet_rank[X] = s; } } MS_OFFSCREEN_BANK void check_shot_at_enemy() { char i, j, my = ms_sprite_y[X = missile_sprite]; char my2 = my - 12; char mx = ms_sprite_x[X]; char mx2 = mx + 7; char hit = 0; for (X = MAX_NB_ENEMIES - 1; X >= 0; X--) { if (enemy_type[X]) { j = enemy_type[X] & 128; Y = enemy_sprite[X]; if (ms_sprite_y[Y] < my && ms_sprite_y[Y] >= my2) { // We are at the right height i = X; char l = sprite_width[X = ms_sprite_nusiz[Y] & 7]; if (j) l <<= 1; if (mx2 >= ms_sprite_x[Y] && mx < ms_sprite_x[Y] + l) { // Let's see if we hit one of these invaders if (sprite_is_one_invader[X]) { hit = 1; X = i; if (j) { enemy_counter[X]--; if (enemy_counter[X] == 0) { j = Y; multisprite_delete_with_rank(enemy_state[X], enemy_rank[X] + 1); Y = j; j = 0; X = i; } } if (!j) { X = enemy_type[X] & 0x7f; // Remove the boss byte score += invader_score[X]; X = i; enemy_type[X] = 0; multisprite_delete_with_rank(Y, enemy_rank[X]); } } else { // Let's see if we hit the left side if (mx < ms_sprite_x[Y] + 8) { // Yes ! hit = 1; // Let's reduce the size of this invader ms_sprite_nusiz[Y] = sprite_new_nusiz_remove_left[X]; ms_sprite_x[Y] += sprite_offset_remove_left[X]; } else if (mx2 >= ms_sprite_x[Y] + l - 8) { // Yes. Wi hit the right side hit = 1; // Let's reduce the size of this invader ms_sprite_nusiz[Y] = sprite_new_nusiz_remove_right[X]; } else if (X == 3 && mx2 >= ms_sprite_x[Y] + 16 && mx < ms_sprite_x[Y] + 24) { // Yes. Wi hit the right side hit = 1; // Let's reduce the size of this invader ms_sprite_nusiz[Y] = 2; } else if (X == 6 && mx2 >= ms_sprite_x[Y] + 32 && mx < ms_sprite_x[Y] + 40) { // Yes. Wi hit the right side hit = 1; // Let's reduce the size of this invader ms_sprite_nusiz[Y] = 4; } } if (hit) { do_schedule_sfx = 2; multisprite_delete_with_rank(missile_sprite, missile_rank); missile_sprite = MS_UNALLOCATED; score += 1; update_score = 1; break; } } X = i; } } } } MS_OFFSCREEN_BANK void game_move_enemies() { char i, j; // "cheap" part of movement first for (X = MAX_NB_ENEMIES - 1; X >= 0; X--) { if (enemy_type[X] == 1) { Y = enemy_sprite[X]; if (enemy_counter[X] & 1) { ms_sprite_nusiz[Y] |= MS_REFLECTED; } else { ms_sprite_nusiz[Y] &= ~MS_REFLECTED; } enemy_counter[X]++; if (enemy_counter[X] < 100) { enemy_y[X]++; if (enemy_counter[X] >= 60) { enemy_x[X]++; if (enemy_counter[X] == 60) { j = ms_sprite_nusiz[Y]; fire_new_bullet(enemy_x[X], enemy_y[X] + 12, bullet_start_direction[Y = enemy_state[X]], j); } } } else { enemy_y[X]--; if (enemy_y[X] == 21) { i = X; multisprite_delete_with_rank(Y, enemy_rank[X]); X = i; enemy_type[X] = 0; } } } else if (enemy_type[X] == 128) { enemy_y[X]++; if (enemy_y[X] == 70) { fire_new_bullet(enemy_x[X] + 6, enemy_y[X] + 24, BULLET_B, 1); } else if (enemy_y[X] == 200) { i = X; multisprite_delete_with_rank(enemy_sprite[X], enemy_rank[X]); X = i; multisprite_delete_with_rank(enemy_state[X], enemy_rank[X]); X = i; enemy_type[X] = 0; } } } // And actually move the sprites if there is still some resources available while (*INTIM >= 23) { X = moving_enemy_counter; if (X == 0) { X = MAX_NB_ENEMIES; } X--; moving_enemy_counter = X; if (enemy_type[X] == 1) { multisprite_move_with_rank(enemy_sprite[X], enemy_x[X], enemy_y[X], enemy_rank[X]); X = moving_enemy_counter; enemy_rank[X] = Y; } else if (enemy_type[X] == 128) { // Move the right one before to save the order multisprite_move_with_rank(enemy_state[X], -1, enemy_y[X], enemy_rank[X] + 1); X = moving_enemy_counter; multisprite_move_with_rank(enemy_sprite[X], -1, enemy_y[X], enemy_rank[X]); X = moving_enemy_counter; enemy_rank[X] = Y; } } } MS_OFFSCREEN_BANK void game_move_bullets() { char i, nx, ny, destroy; for (X = MAX_NB_BULLETS - 1; X >= 0; X--) { Y = bullet_sprite[X]; if (Y) { destroy = 0; i = X; X = bullet_direction[X]; nx = ms_sprite_x[Y] + bullet_dx[X]; if (nx < 3) destroy = 1; if (nx >= 150) destroy = 1; ny = ms_sprite_y[Y] + bullet_dy[X]; if (ny < MS_OFFSET) destroy = 1; if (ny >= MS_PLAYFIELD_HEIGHT + MS_OFFSET - 2) destroy = 1; if (destroy) { X = i; multisprite_delete_with_rank(Y, bullet_rank[X]); X = i; bullet_sprite[X] = 0; } else { X = i; multisprite_move_with_rank(Y, nx, ny, bullet_rank[X]); X = i; bullet_rank[X] = Y; } } } } void game_scenario() { if (!(game_counter & 1)) { if ((game_counter & 7) == 0) { spawn_new_enemy(128, 60); } else { spawn_new_enemy(1, (game_counter >> 2) & 3); } } } MS_OFFSCREEN_BANK game_over() { char i; // Destroy all enemies & bullets for (X = MAX_NB_ENEMIES - 1; X >= 0; X--) { enemy_type[X] = 0; } for (X = MAX_NB_BULLETS - 1; X >= 0; X--) { bullet_sprite[X] = 0; } // Destroy missile missile_sprite = MS_UNALLOCATED; multisprite_clear(); multisprite_new(SPRITE_LETTER_G, 80 - 19, 80, 0); multisprite_new(SPRITE_LETTER_A, 80 - 9, 80, 0); multisprite_new(SPRITE_LETTER_M, 80 + 1, 80, 0); multisprite_new(SPRITE_LETTER_E, 80 + 11, 80, 0); multisprite_new(SPRITE_LETTER_O, 80 - 19, 130, 0); multisprite_new(SPRITE_LETTER_V, 80 - 9, 130, 0); multisprite_new(SPRITE_LETTER_E, 80 + 1, 130, 0); multisprite_new(SPRITE_LETTER_R, 80 + 11, 130, 0); game_state = GAME_OVER; game_counter = 0; background_color = VCS_RED; } MS_OFFSCREEN_BANK lose_one_life() { do_schedule_sfx = 2; player_state = 1; player_state2 = 0; player_timer = 10; nb_lives--; do_update_live_display = 1; } MS_OFFSCREEN_BANK game_logic() { X = 0; Y = 0; if (!(*SWCHA & 0x80) && player_xpos < 153) { player_xpos++; Y = 1; } // Right else if (!(*SWCHA & 0x40) && player_xpos >= 1) { player_xpos--; Y = 1; } // Left if (!(*SWCHA & 0x20) && player_ypos < 200) { player_ypos++; Y = 1;} // Down else if (!(*SWCHA & 0x10) && player_ypos >= 32) { player_ypos--; ms_sprite_model[X] = SPRITE_SPACESHIP_EXHAUST; Y = 1;} // Up else { ms_sprite_model[X] = SPRITE_SPACESHIP; } if (Y) player_rank = multisprite_move_with_rank(0, player_xpos, player_ypos, player_rank); // Missile management if (missile_sprite != MS_UNALLOCATED) { X = missile_sprite; // Check for collision if (ms_sprite_nusiz[X] & MS_PF_COLLISION) { if (ms_sprite_x[X] < 12 || ms_sprite_x[X] >= 153 - 12) { multisprite_delete_with_rank(missile_sprite, missile_rank); missile_sprite = MS_UNALLOCATED; } } // Check if an enemy was destroyed check_shot_at_enemy(); } if (missile_sprite != MS_UNALLOCATED) { X = missile_sprite; char y = ms_sprite_y[X] - 6; if (y < 32) { multisprite_delete_with_rank(missile_sprite, missile_rank); missile_sprite = MS_UNALLOCATED; } else { missile_rank = multisprite_move_with_rank(missile_sprite, -1 /* Go straight */, y, missile_rank); } } if (!(*INPT4 & 0x80)) { if (!button_pressed) { button_pressed = 1; if (missile_sprite == MS_UNALLOCATED) { do_schedule_sfx = 1; missile_sprite = multisprite_new(SPRITE_FIRE, player_xpos, player_ypos - 8, 0); missile_rank = X; } } } else button_pressed = 0; // Player management if (player_state == 0) { // Check collision with playfield if (ms_sprite_nusiz[X = 0] & MS_PF_COLLISION) { if (ms_sprite_x[X] < 12 || ms_sprite_x[X] >= 153 - 12) { lose_one_life(); } } if (ms_sprite_nusiz[X = 0] & MS_COLLISION) { lose_one_life(); } } if (player_state == 0) { if (player_timer >= 1) { player_timer = 0; ms_sprite_nusiz[X] = 0; } else { player_timer = 1; ms_sprite_nusiz[X] = MS_REFLECTED; } } else { // Player explosion if (player_state == 1) { ms_sprite_model[X = 0] = SPRITE_EXPLOSION1 + player_state2; player_timer--; if (player_timer == 0) { player_timer = 10; player_state2++; if (player_state2 == 5) { if (nb_lives == 0) game_over(); else { player_state = 0; ms_sprite_model[X] = SPRITE_SPACESHIP; } } } } } } void game_wait_for_restart() { if (game_counter >= 3) { game_counter = 3; if (!(*INPT4 & 0x80)) { if (!button_pressed) { game_init(); } } else button_pressed = 0; } } void main() { tia_tracker_init(); #ifdef DEBUG min_timer_overscan = 255; min_timer_vblank = 255; #endif char scrolling = 0; game_init(); do { *VBLANK = 2; // Enable VBLANK *VSYNC = 2; // Set VSYNC strobe(WSYNC); // Hold it for 3 scanlines strobe(WSYNC); strobe(WSYNC); *VSYNC = 0; // Turn VSYNC Off // Blank *TIM64T = ((BLANK - 3) * 76) / 64 - 3; // Do some logic here if (game_state == GAME_STARTED) game_logic(); else game_wait_for_restart(); game_move_enemies(); ms_scenery = playfield - MS_OFFSET + 12; ms_scenery += scrolling; multisprite_kernel_prep(); #ifdef DEBUG if (*INTIM < min_timer_vblank) min_timer_vblank = *INTIM; #endif while (*INTIM); // Wait for end of blank multisprite_kernel(); // Overscan strobe(WSYNC); *COLUBK = VCS_RED; *GRP0 = 0; *GRP1 = 0; *PF0 = 0; *PF1 = 0; *PF2 = 0; *COLUP0 = VCS_WHITE; *COLUP1 = VCS_WHITE; mini_kernel_6_sprites(); strobe(WSYNC); *COLUBK = VCS_RED; strobe(WSYNC); *VBLANK = 2; // Enable VBLANK *TIM64T = ((OVERSCAN) * 76) / 64 + 2; // Do some logic here multisprite_kernel_post(); prepare_background(scrolling); scrolling -= 2; if (scrolling < 0) { scrolling = 80; game_counter++; if (game_state == GAME_STARTED) game_scenario(); } else { // This is executed only if the game scenario is not evaluated // So that balances the execution time game_move_bullets(); if (do_schedule_sfx) { if (do_schedule_sfx == 1) { sfx_schedule(sfx_pewpew); } else { sfx_schedule(sfx_bigboom); } do_schedule_sfx = 0; } if (do_update_live_display) { do_update_live_display = 0; update_lives_display(); } if (update_score) { mini_kernel_update_score_4_digits(score); update_score = 0; } } tia_tracker_play(); sfx_play(); #ifdef DEBUG if (*INTIM < min_timer_overscan) min_timer_overscan = *INTIM; #endif while (*INTIM); // Wait for end of overscan } while(1); } Have you played Atari today ? example_shmup_NTSC.a26 example_shmup_PAL.a26 cc2600-0.4.4-x86_64.msi 1 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.