+bsteux Posted April 4, 2023 Share Posted April 4, 2023 Hi, I'm pleased to announce the availability of cc7800, a C compiler (subset of C indeed) dedicated to the Atari 7800, available right now at https://github.com/steux/cc7800. It's a preliminary release (v0.1), but it's functional. I could compile a few examples and run them on my old PAL Atari 7800 (on a Concerto cart). The main limitation today is the poor support of the silly memory map of the 7800 (I still need to manage the shadowing of the zeropage and the stack in the middle of the RAM). What's nice is the "automatic" support of memory scattering and holey DMA (yet another quirk of the 7800). Another limitation is the lack of examples and support libraries (well, at the moment, you have to work your Maria display lines yourself...). I'm working on it, so stay tuned ! In the mean time, here is a sprite display example (you can move it with the joystick and it's PAL and NTSC compatilble) : #include "prosystem.h" char i; unsigned char X, Y; unsigned char xpos, ypos; char *dlpnt; holeydma scattered(16, 1) char sprite[16] = { 0x3c, 0x3c, 0x42, 0x42, 0x99, 0x99, 0xa5, 0xa5, 0x81, 0x81, 0xa5, 0xa5, 0x42, 0x42, 0x3c, 0x3c }; ramchip char dl0[64], dl1[64], dl2[64], dl3[64], dl4[64], dl5[64], dl6[64], dl7[64], dl8[64], dl9[64], dl10[64], dl11[64]; #ifdef PAL #define DLL_ARRAY_SIZE 15 #define YMAX (240 - 16) ramchip char dl12[64], dl13[64], dl14[64]; const char *dls[DLL_ARRAY_SIZE] = { dl0, dl1, dl2, dl3, dl4, dl5, dl6, dl7, dl8, dl9, dl10, dl11, dl12, dl13, dl14 }; #else #define DLL_ARRAY_SIZE 12 #define YMAX (192 - 16) const char *dls[DLL_ARRAY_SIZE] = { dl0, dl1, dl2, dl3, dl4, dl5, dl6, dl7, dl8, dl9, dl10, dl11 }; #endif ramchip char dll[(DLL_ARRAY_SIZE + 4) * 3]; char dlend[DLL_ARRAY_SIZE]; void main() { // Build DLL // 25 blank lines dll[X = 0] = 0x4f; // 16 lines dll[++X] = 0x21; // 0x2100 = Blank DL dll[++X] = 0x00; dll[++X] = 0x48; // 9 lines dll[++X] = 0x21; // 0x2100 = Blank DL dll[++X] = 0x00; // 192 lines divided into 12 regions for (Y = 0; Y != DLL_ARRAY_SIZE; Y++) { dll[++X] = 0x4f; // 16 lines dll[++X] = dls[Y] >> 8; // High address dll[++X] = dls[Y]; // Low address } // 26 blank lines dll[++X] = 0x4f; // 16 lines dll[++X] = 0x21; // 0x2100 = Blank DL dll[++X] = 0x00; dll[++X] = 0x49; // 10 lines dll[++X] = 0x21; // 0x2100 = Blank DL dll[++X] = 0x00; // Setup Maria registers *DPPH = dll >> 8; *DPPL = dll; *P0C1 = 0x18; // Setup Palette 0 *P0C2 = 0x38; *P0C3 = 0x58; *CTRL = 0x43; // Enable DMA *CTLSWA = 0; // Setup ports to read mode *CTLSWB = 0; xpos = 64; ypos = 64; // Main loop do { while (!(*MSTAT & 0x80)); // Wait for VBLANK if (!(*SWCHA & 0x80)) { // Pushed right ? xpos++; // Move right } if (!(*SWCHA & 0x40)) { // Pushed left ? xpos--; } if (!(*SWCHA & 0x20)) { // Pushed down ? if (ypos != YMAX) ypos++; } if (!(*SWCHA & 0x10)) { // Pushed up ? if (ypos != 0) ypos--; } // Reset DL ends for (X = DLL_ARRAY_SIZE - 1; X >= 0; X--) { dlend[X] = 0; } // Build DL entries X = ypos >> 4; dlpnt = dls[X]; // Create DL entry for upper part of sprite Y = dlend[X]; dlpnt[Y++] = sprite; // Low byte of data address dlpnt[Y++] = 0x40; // Mode 320x1 i = ypos & 0x0f; dlpnt[Y++] = (sprite >> 8) | i; dlpnt[Y++] = 0x1f; // Palette 0, 1 byte wide dlpnt[Y++] = xpos; // Horizontal position dlend[X] = Y; if (ypos & 0x0f) { // Is the sprite lying on another region ? X++; dlpnt = dls[X]; // Point to the next region Y = dlend[X]; dlpnt[Y++] = sprite; // Low byte of data address dlpnt[Y++] = 0x40; // Mode 320x1 dlpnt[Y++] = ((sprite - 0x1000) >> 8) | i; dlpnt[Y++] = 0x1f; // Palette 0, 1 byte wide dlpnt[Y++] = xpos; // Horizontal position dlend[X] = Y; } // Add DL end entry on each DL for (X = DLL_ARRAY_SIZE - 1; X >= 0; X--) { dlpnt = dls[X]; Y = dlend[X]; dlpnt[++Y] = 0; } // Wait for VBLANK to end while (*MSTAT & 0x80); } while(1); } What is interesting is the generated code. Very very close to pure assembly with little to no overhead (which was the primary goal). Well, have fun and please contribute if you find this exciting ! Regards. 17 7 Quote Link to comment Share on other sites More sharing options...
RevEng Posted April 6, 2023 Share Posted April 6, 2023 Yay, more dev tools for the 7800! Looking forward to seeing how this develops! 7 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted April 17, 2023 Author Share Posted April 17, 2023 Hi, Yes, let's see if C can get a little room in between Basic and Assembler on the Atari 7800... I've made some progress in terms of usability... - v0.1.2 of cc7800 now correctly handles memory mapping, bankswitching, etc. It's rather complete now. - I've added a multisprite.h header, with all the necessary functions to ease, well, multisprite display... At the moment, it's quite basic, but at least it's working... Tested on real hardware and a7800 (see the video attached). It doesn't handle scrolling, but includes memory protection and DMA time computation so that glitches can be detected and handled if necessary. It also supports double buffering. At the moment, only 160A mode is supported. - I've made a program to read and process images in order to generate C sprite code: sprites7800. It's located in my new tools7800 repository (https://github.com/steux/tools7800), and is written in Rust (and thus can run on any platform). - And I've managed to make an installer for cc7800 for Windows... Here it is in attachment. Please tell me if it's working. I'm only using linux... Here is an example that displays 48 sprites at 50Hz (on my PAL system), with a full tiled background. Quite straightforward to understand for anyone who knows C and has a little bit of MARIA knowledge (the video attached shows the result on my PAL system) : #include "prosystem.h" #include "multisprite.h" char i, xpos, ypos; #define NB_SPRITES 48 ramchip short sp_xpos[NB_SPRITES], sp_ypos[NB_SPRITES]; ramchip char sp_direction[NB_SPRITES]; #ifdef PAL #define YMAX 240 #else #define YMAX 192 #endif const signed short dx[24] = {300, 289, 259, 212, 149, 77, 0, -77, -150, -212, -259, -289, -300, -289, -259, -212, -149, -77, 0, 77, 149, 212, 259, 289}; const signed short dy[24] = {0, 124, 240, 339, 415, 463, 480, 463, 415, 339, 240, 124, 0, -124, -239, -339, -415, -463, -480, -463, -415, -339, -240, -124}; const char horizontal_pingpong[24] = { 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13 }; const char vertical_pingpong[24] = { 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; holeydma reversed scattered(16,2) char bb_char1[32] = { 0x01, 0x00, 0x01, 0x40, 0x0a, 0x94, 0x2a, 0x90, 0x3b, 0xa0, 0xc8, 0xe5, 0xc8, 0xe4, 0xc8, 0xd0, 0xc8, 0xe5, 0xbb, 0x84, 0x0c, 0x20, 0x2a, 0x90, 0x0e, 0x50, 0x3f, 0x94, 0x3d, 0x68, 0x5d, 0x6a }; reversed scattered(16,2) char tiles[32] = { 0x5a, 0x5a, 0x69, 0x69, 0x69, 0x69, 0xa5, 0xa5, 0xa5, 0xa5, 0x96, 0x96, 0x96, 0x96, 0x5a, 0x5a, 0x5a, 0x5a, 0x69, 0x69, 0x69, 0x69, 0xa5, 0xa5, 0xa5, 0xa5, 0x96, 0x96, 0x96, 0x96, 0x5a, 0x5a }; const char background[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; void main() { multisprite_init(); multisprite_set_charbase(tiles); for (i = 0; i != _MS_DLL_ARRAY_SIZE; i++) { // 20 characters (8 pixels wide) on each line, using palette 1 (pink) multisprite_display_chars(0, i, background, 20, 1); } multisprite_save(); *P0C1 = 0x1c; // Setup Palette 0 *P0C2 = 0xd5; // Green *P0C3 = 0x0f; // White *P1C1 = 0x65; // Dark pink *P1C2 = 0x6B; // Light pink // Initialize sprites for (ypos = 0, xpos = 0, i = 0, X = 0; X != NB_SPRITES; xpos++, ypos++, X++) { sp_xpos[X] = xpos << 8; sp_ypos[X] = ypos << 8; sp_direction[X] = i++; if (i == 24) i = 0; } // Main loop do { while (!(*MSTAT & 0x80)); // Wait for VBLANK multisprite_flip(); for (i = 0; i != NB_SPRITES; i++) { X = i; Y = sp_direction[X]; sp_xpos[X] += dx[Y]; sp_ypos[X] += dy[Y]; xpos = sp_xpos[X] >> 8; ypos = sp_ypos[X] >> 8; if ((xpos < 5 && (dx[Y] >> 8) < 0) || (xpos >= 150 && (dx[Y] >> 8) >= 0)) { sp_direction[X] = horizontal_pingpong[Y]; } if ((ypos < 5 && (dy[Y] >> 8) < 0) || (ypos >= YMAX - 20 && (dy[Y] >> 8) >= 0)) { sp_direction[X] = vertical_pingpong[Y]; } multisprite_display_sprite(xpos, ypos, bb_char1, 2, 0); } } while(1); } The sprites are generated using sprites7800, with the following YAML file as input : sprite_sheets: - image: Bubble Bobble.png sprites: - name: bb_char1 top: 0 left: 0 width: 16 holeydma: true - name: tiles top: 16 left: 0 width: 16 holeydma: false I hope this will give an idea of the possibilities C coding is offering to Atari7800 game developers... I'll go on making new headers for vertical scrolling, horizontal scrolling, text display and split screen. See you ! cc7800_example_sprites.mp4 cc7800-0.1.2-x86_64.msi 9 1 Quote Link to comment Share on other sites More sharing options...
SlidellMan Posted April 17, 2023 Share Posted April 17, 2023 Wow, you're making the Atari 7800 equivalent of SGDK. Just keep at it, and the video demo shows promise. Quote Link to comment Share on other sites More sharing options...
+karri Posted April 18, 2023 Share Posted April 18, 2023 Your approach is very interesting! A light C-compiler with a set of headers tuned for the 7800 looks cool. 1 Quote Link to comment Share on other sites More sharing options...
+karri Posted April 20, 2023 Share Posted April 20, 2023 Could you add a get_tv() function for finding out if the console is PAL or NTSC. I like to use 224 visible lines for both targets (8 * 28 zones or 16 * 14 zones). Currently get_tv() sets the global variable paldetected (1 = PAL, 0 = NTSC). The reason why paldetected is nice to have is that you can just change the hue value by two steps to almost have the same colours in PAL and NTSC. For music and sound effects you can also just use 50 Hz update rate by dropping each 6th update cycle for NTSC machines. This would give you a wider audience for the game. PS. My personal workhorse for TIA music today is TiaTracker. It exports the asm file in many formats so it should be easy to get to work. get_tv.s Quote Link to comment Share on other sites More sharing options...
Trebor Posted April 21, 2023 Share Posted April 21, 2023 23 hours ago, karri said: The reason why paldetected is nice to have is that you can just change the hue value by two steps to almost have the same colours in PAL and NTSC. The below conversion typically works best - one step (outside of grayscale): NTSC $0X <--> PAL $0X NTSC $1X <--> PAL $2X NTSC $2X <--> PAL $3X NTSC $3X <--> PAL $4X NTSC $4X <--> PAL $5X NTSC $5X <--> PAL $6X NTSC $6X <--> PAL $7X NTSC $7X <--> PAL $8X NTSC $8X <--> PAL $9X NTSC $9X <--> PAL $AX NTSC $AX <--> PAL $BX NTSC $BX <--> PAL $CX NTSC $CX <--> PAL $DX NTSC $DX <--> PAL $EX NTSC $EX <--> PAL $FX NTSC $FX <--> N/A - Best nearest PAL $2X PAL $1X <--> N/A - Best nearest NTSC $DX The Atari 7800 Color Documentation page on 7800 8BitDev is an excellent resource for this topic. There are also a slew of examples and discussion under this thread. Spoiler A conversion process where two steps takes place is in most instances of equating PAL 7800 hue ranges to PAL 2600: PAL 7800 $2x = PAL 2600 $2x PAL 7800 $3x = PAL 2600 $4x PAL 7800 $4x = PAL 2600 $6x PAL 7800 $5x = PAL 2600 $8x PAL 7800 $6x = PAL 2600 $Ax PAL 7800 $7x = PAL 2600 $Cx PAL 7800 $8x = PAL 2600 $Dx PAL 7800 $9x = PAL 2600 $Bx PAL 7800 $Ax = PAL 2600 $9x PAL 7800 $Bx = PAL 2600 $7x PAL 7800 $Cx = PAL 2600 $5x PAL 7800 $Dx = PAL 2600 $3x PAL 7800 $0x = PAL 2600 $0x PAL 7800 $0x = PAL 2600 $1x PAL 7800 $0x = PAL 2600 $Ex PAL 7800 $0x = PAL 2600 $Fx There is no need to convert hue ranges for NTSC 7800 to NTSC 2600 as it is the same for the two consoles, though only every other luminance value is available under the 2600. 1 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted April 21, 2023 Author Share Posted April 21, 2023 On 4/20/2023 at 3:14 PM, karri said: Could you add a get_tv() function for finding out if the console is PAL or NTSC. I like to use 224 visible lines for both targets (8 * 28 zones or 16 * 14 zones). Currently get_tv() sets the global variable paldetected (1 = PAL, 0 = NTSC). The reason why paldetected is nice to have is that you can just change the hue value by two steps to almost have the same colours in PAL and NTSC. For music and sound effects you can also just use 50 Hz update rate by dropping each 6th update cycle for PAL machines. This would give you a wider audience for the game. PS. My personal workhorse for TIA music today is TiaTracker. It exports the asm file in many formats so it should be easy to get to work. get_tv.s 1.05 kB · 1 download This is an excellent suggestion! I was wondering how to cope with PAL and NTSC versions. Yes, using a 16x14 zones to have a "standard" 224 lines picture seems a good way to go, with an added autodetection of TV model. I've implemented your get_tv function and included it in the header : void multisprite_get_tv() { while (!(*MSTAT & 0x80)); // Wait for VBLANK while (*MSTAT & 0x80); // Wait for end of VBLANK X = 0; do { strobe(WSYNC); strobe(WSYNC); X++; } while (!(*MSTAT & 0x80)); if (X >= 135) _ms_pal_detected = 0xff; } This is called by multisprite_init() now, and it seems to work perfectly (tested on a7800) I've added a macro to process the colors (adding +1 phase shift for NTSC to PAL conversion) : // Macro to convert NTSC colors to PAL colors // To be used outside of grayscale ($0x) and NTSC $Fx #define multisprite_color(color) (color + (_ms_pal_detected & 0x10)) which doesn't cost much CPU... The difference in pixel ratio is partially compensated by the availability of wide screen displays. If we use a 4:3 PAL display and a 16:9 NTSC display, the result is looking similar... Adopted. Thank you ! 2 1 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted April 24, 2023 Author Share Posted April 24, 2023 Hi, I've updated cc7800 to v0.2. It now directly produces .a78 files with the included header., making things way simpler for users. It's now running DASM internally as a second stage compiler (this requires DASM to be present in the current directory or found in the system's path). This release also includes a bug fix which prevented cc7800 to run correctly under Windows (due to CRLF issues). Here is the new installer for windows. I'm working now on a conio.h header to add console-like facilities... cc7800-0.2.0-x86_64.msi 6 Quote Link to comment Share on other sites More sharing options...
+karri Posted April 24, 2023 Share Posted April 24, 2023 21 hours ago, bsteux said: I'm working now on a conio.h header to add console-like facilities... This is really nice! For curiosity I created a database parser for turning scottkit adventures into 7800 C-code. The resulting C-code is pretty simple and plain conio support might be enough for compiling the file with your cc7800. It already works on cc65. And for simplicity it creates all the code in one C-file. The "adventure" code mainly uses gotoxy() and cputc(). For clearing the memory or scrolling I prefer memcpy() or memset(). Edit: I added a small scottkit adventure. The mini.sck is the source file and the mini.c is what my python script generates of it. Scottkit is a ruby tool that you can get in ruby by "gem install scottkit". You can play text adventures in ruby by "scottkit -p mini.sck". Or dump them to be played by scottfree interpreters by "scottkit -c mini.sck > mini.sco". It is also possible to convert existing adventures back into scottkit source by "scottkit -d piratecove.dat > piratecove.sck". mini.sck mini.c mini.a78 2 Quote Link to comment Share on other sites More sharing options...
gambler172 Posted April 26, 2023 Share Posted April 26, 2023 Funny 😉 Quote Link to comment Share on other sites More sharing options...
Jinks Posted April 30, 2023 Share Posted April 30, 2023 Create a game of "War", where each player is dealt half of a shuffled deck of cards, and they take turns drawing a card from their deck and comparing it to their opponent's card. The player with the higher card wins that round and takes both cards. The game ends when one player has all the cards. Here is an example implementation: ``` #include <stdlib.h> #include <stdio.h> #include <time.h> #define DECK_SIZE 52 #define HAND_SIZE 26 typedef enum {CLUB, DIAMOND, HEART, SPADE} Suit; typedef struct { Suit suit; int rank; } Card; typedef struct { Card cards[HAND_SIZE]; int size; } Hand; int main() { // Initialize the deck of cards Card deck[DECK_SIZE]; for (int i = 0; i < DECK_SIZE; i++) { deck[i].rank = i % 13 + 1; if (i < 13) { deck[i].suit = CLUB; } else if (i < 26) { deck[i].suit = DIAMOND; } else if (i < 39) { deck[i].suit = HEART; } else { deck[i].suit = SPADE; } } // Shuffle the deck of cards srand(time(NULL)); for (int i = 0; i < DECK_SIZE; i++) { int j = rand() % DECK_SIZE; Card temp = deck[i]; deck[i] = deck[j]; deck[j] = temp; } // Deal the cards to the players Hand player1, player2; player1.size = player2.size = HAND_SIZE; for (int i = 0; i < HAND_SIZE; i++) { player1.cards[i] = deck[i]; player2.cards[i] = deck[i + HAND_SIZE]; } // Play the game while (player1.size > 0 && player2.size > 0) { // Draw a card from each player's hand Card card1 = player1.cards[--player1.size]; Card card2 = player2.cards[--player2.size]; // Compare the cards int result = card1.rank - card2.rank; if (result > 0) { printf("Player 1 wins the round!\n"); player1.cards[player1.size++] = card1; player1.cards[player1.size++] = card2; } else if (result < 0) { printf("Player 2 wins the round!\n"); player2.cards[player2.size++] = card1; player2.cards[player2.size++] = card2; } else { printf("Tie!\n"); player1.cards[player1.size++] = card1; player2.cards[player2.size++] = card2; } } // Declare the winner if (player1.size > player2.size) { printf("Player 1 wins the game!\n"); } else { printf("Player 2 wins the game!\n"); } return 0; } ``` Quote Link to comment Share on other sites More sharing options...
+bsteux Posted May 15, 2023 Author Share Posted May 15, 2023 Hi, I've made some progress on the Multisprite library for cc7800. I've added vertical scrolling. Here is a working example : #include "prosystem.h" #include "gfx.h" #define VERTICAL_SCROLLING #define _MS_DL_SIZE 64 #define _MS_DL_MALLOC(y) ((y == 6 || y == 7 || y == 8)?_MS_DL_SIZE * 2:_MS_DL_SIZE) #include "multisprite.h" char i, counter, xpos, ypos; char *ptr; char xchest; #define NB_SPRITES 32 ramchip short sp_xpos[NB_SPRITES], sp_ypos[NB_SPRITES]; ramchip char sp_direction[NB_SPRITES]; const signed short dx[24] = {300, 289, 259, 212, 149, 77, 0, -77, -150, -212, -259, -289, -300, -289, -259, -212, -149, -77, 0, 77, 149, 212, 259, 289}; const signed short dy[24] = {0, 124, 240, 339, 415, 463, 480, 463, 415, 339, 240, 124, 0, -124, -239, -339, -415, -463, -480, -463, -415, -339, -240, -124}; const char horizontal_pingpong[24] = { 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13 }; const char vertical_pingpong[24] = { 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; const char background[22] = { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 }; #define LTR(x) (((x) - ' ') * 2) const char hello_world[] = { LTR('H'), LTR('E'), LTR('L'), LTR('L'), LTR('O'), LTR(' '), LTR('W'), LTR('O'), LTR('R'), LTR('L'), LTR('D') }; void main() { counter = 0; multisprite_init(); multisprite_set_charbase(tiles); // Set up a full background for (counter = 0; counter < _MS_DLL_ARRAY_SIZE; counter++) { if (counter & 2) { ptr = background + 2; } else { ptr = background; } multisprite_display_tiles(0, _MS_DLL_ARRAY_SIZE - 1 - counter, ptr, 20, 1); } multisprite_save(); *P0C1 = multisprite_color(0x1c); // Setup Palette 0: Yellow *P0C2 = multisprite_color(0xc5); // Green *P0C3 = 0x0f; // White *P1C1 = multisprite_color(0x55); // Dark pink *P1C2 = multisprite_color(0x5B); // Light pink *P2C1 = multisprite_color(0x32); *P2C2 = multisprite_color(0x3D); *P2C3 = multisprite_color(0x37); *P3C1 = multisprite_color(0x92); *P3C2 = multisprite_color(0x97); *P3C3 = multisprite_color(0x9D); // Initialize sprites for (ypos = 0, xpos = 0, i = 0, X = 0; X != NB_SPRITES; xpos++, ypos++, X++) { sp_xpos[X] = xpos << 8; sp_ypos[X] = ypos << 8; sp_direction[X] = i++; if (i == 24) i = 0; } // Main loop do { // Prepare scrolling data if (multisprite_vscroll_buffer_empty()) { if (counter & 2) { ptr = background + 2; } else { ptr = background; } multisprite_vscroll_buffer_tiles(0, ptr, 20, 1); multisprite_vscroll_buffer_sprite(xchest, chest, 2, 3); xchest += 45; counter++; } multisprite_flip(); multisprite_vertical_scrolling(1); multisprite_reserve_dma(104, sizeof(hello_world), 2); for (i = 0; i != NB_SPRITES; i++) { X = i; Y = sp_direction[X]; sp_xpos[X] += dx[Y]; sp_ypos[X] += dy[Y]; xpos = sp_xpos[X] >> 8; ypos = sp_ypos[X] >> 8; if ((xpos < 5 && (dx[Y] >> 8) < 0) || (xpos >= 150 && (dx[Y] >> 8) >= 0)) { sp_direction[X] = horizontal_pingpong[Y]; } if ((ypos < 5 && (dy[Y] >> 8) < 0) || (ypos >= MS_YMAX - 20 && (dy[Y] >> 8) >= 0)) { sp_direction[X] = vertical_pingpong[Y]; } multisprite_display_sprite(xpos, ypos, bb_char1, 2, 0); } for (xpos = 40, i = 0; i != sizeof(hello_world); xpos += 8, i++) { ptr = chars0 + hello_world[X = i]; multisprite_display_sprite_fast(xpos, 104, ptr, 2, 2); } } while(1); } The result ? See the video. I think it's getting close to what is needed to implement a shm'up. I'm still working on conio.h for console output. It's starting to work, but it is still not usable in a real program (at the moment it generates a display list for every print). The next steps are adding horizontal scrolling, support for joystick and sound (TIA and hopefully POKEY), writing some docs, and cc7800 will be ready for making good Atari 7800 games that eat the NES alive. a7800_vertical_scrolling.mp4 11 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted May 17, 2023 Author Share Posted May 17, 2023 Along with the example source code, here are : - The bitmap file for the graphics (including a 3 colors bitmap font) - The YAML file for the graphics description (so that sprites7800 tool can generate the C code) - A new windows installer for cc7800 v0.2.2 cc7800-0.2.2-x86_64.msi Bubble Bobble.yaml 6 Quote Link to comment Share on other sites More sharing options...
SlidellMan Posted May 18, 2023 Share Posted May 18, 2023 Well, I have to say that this project is progressing quite nicely. Quote Link to comment Share on other sites More sharing options...
+bsteux Posted May 24, 2023 Author Share Posted May 24, 2023 The cc7800 project is going on... Let's now introduce conio.h, a console I/O (well, only O) to display text. It's not complete, but it supports putch, puts, gotoxy, textcolor, clrscr and delline. The included font is the lowercase C64 font. 128 characters are defined, so there is room for another 128 custom characters. Technically, my design of conio favors 3 goals : KISS (Keep it stupid simple), minimizing the CPU load (i.e. minimizing the number of Display List Headers - 0 when nothing is displayed), and maximizing the possibility to use colors. It's possible to use 8 colors on a line, with some limitations (the algorithm tries to use one DL header per color, and due to Maria design, this results in color priorities (the last color used has display priority over the first used)). Here is an example of use (a test indeed). The result is the picture in attachment. The "hearth" character is a custom character that is appended automatically by cc7800 to the C64 font (using the scattered keyword, indicating this is a MARIA graphics). Note the use of assert from assert.h, which turns background to red if the assertion is not correct (in my example, I check the the length of Display Lists) : #include "conio.h" #include "assert.h" char i; reversed scattered(8,1) char special_char[8] = { 0x66, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00 }; void main() { clrscr(); // Draw a square gotoxy(0, 0); textcolor(7); putch(CONIO_TL_CORNER); for (i = 0; i != 20; i++) { putch(CONIO_HBAR); } putch(CONIO_TR_CORNER); for (i = 0; i != 8; i++) { gotoxy(0, i + 1); putch(CONIO_VBAR); gotoxy(21, i + 1); putch(CONIO_VBAR); } gotoxy(0, 9); putch(CONIO_BL_CORNER); for (i = 0; i != 20; i++) { putch(CONIO_HBAR); } putch(CONIO_BR_CORNER); // Write some text for (i = 0; i != 8; i++) { textcolor(i); gotoxy(i + 1, i + 1); cputs("Hello World!"); } // Long text test gotoxy(0, 10); cputs("This is a long text that fits in a line."); gotoxy(10, 11); cputs("World!"); gotoxy(4, 11); cputs("Hello"); gotoxy(10, 12); cputs("World!"); gotoxy(4, 12); textcolor(4); cputs("Hello"); gotoxy(0, 13); for (i = 0; i != 8; i++) { textcolor(i); putch('!'); } gotoxy(0, 14); for (i = 0; i != 8; i++) { textcolor(7 - i); putch(128); // Special character } // Unit tests (Display lines length check) : assert(_ms_dlend[X = 0] == 5); assert(_ms_dlend[X = 1] == 10); assert(_ms_dlend[X = 7] == 10); assert(_ms_dlend[X = 8] == 5); assert(_ms_dlend[X = 9] == 5); assert(_ms_dlend[X = 10] == 10); assert(_ms_dlend[X = 11] == 5); assert(_ms_dlend[X = 12] == 10); assert(_ms_dlend[X = 13] == 40); assert(_ms_dlend[X = 14] == 40); while(1); } cc7800-0.2.3-x86_64.msi 7 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted May 29, 2023 Author Share Posted May 29, 2023 The next step. Support for DLIs (Display List Interrupts). This allows to execute a (short) function on the fly while Maria is displaying the screen. In the following example, I just change the background color to red on Display List 7 (out of 14), i.e. in the middle of the screen. Note that using DLI (which uses 6502 NMI : Non Maskable Iterrupt), it's necessary to declare an "interrupt" function. This is a new keyword that was added to cc7800 in the new v0.2.4. An example of use (this is examples/test_nmi.c from the cc7800 github repository) : #include "prosystem.h" #include "multisprite.h" char i, xpos, ypos; #define NB_SPRITES 64 ramchip short sp_xpos[NB_SPRITES], sp_ypos[NB_SPRITES]; ramchip char sp_direction[NB_SPRITES]; const signed short dx[24] = {300, 289, 259, 212, 149, 77, 0, -77, -150, -212, -259, -289, -300, -289, -259, -212, -149, -77, 0, 77, 149, 212, 259, 289}; const signed short dy[24] = {0, 124, 240, 339, 415, 463, 480, 463, 415, 339, 240, 124, 0, -124, -239, -339, -415, -463, -480, -463, -415, -339, -240, -124}; const char horizontal_pingpong[24] = { 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13 }; const char vertical_pingpong[24] = { 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; holeydma reversed scattered(16,2) char bb_char1[32] = { 0x01, 0x00, 0x01, 0x40, 0x0a, 0x94, 0x2a, 0x90, 0x3b, 0xa0, 0xc8, 0xe5, 0xc8, 0xe4, 0xc8, 0xd0, 0xc8, 0xe5, 0xbb, 0x84, 0x0c, 0x20, 0x2a, 0x90, 0x0e, 0x50, 0x3f, 0x94, 0x3d, 0x68, 0x5d, 0x6a }; void interrupt dli() { *BACKGRND = 0x32; } void main() { multisprite_init(); multisprite_enable_dli(7); *P0C1 = multisprite_color(0x1c); // Setup Palette 0 *P0C2 = multisprite_color(0xc5); // Green *P0C3 = multisprite_color(0x0f); // White // Initialize sprites for (ypos = 0, xpos = 0, i = 0, X = 0; X != NB_SPRITES; xpos++, ypos++, X++) { sp_xpos[X] = xpos << 8; sp_ypos[X] = ypos << 8; sp_direction[X] = i++; if (i == 24) i = 0; } // Main loop do { multisprite_flip(); *BACKGRND = 0; // Back to black background for (i = 0; i != NB_SPRITES; i++) { Y = sp_direction[X = i]; sp_xpos[X] += dx[Y]; sp_ypos[X] += dy[Y]; xpos = sp_xpos[X] >> 8; ypos = sp_ypos[X] >> 8; if ((xpos < 5 && (dx[Y] >> 8) < 0) || (xpos >= 150 && (dx[Y] >> 8) >= 0)) { sp_direction[X] = horizontal_pingpong[Y]; } if ((ypos < 5 && (dy[Y] >> 8) < 0) || (ypos >= MS_YMAX - 20 && (dy[Y] >> 8) >= 0)) { sp_direction[X] = vertical_pingpong[Y]; } multisprite_display_sprite(xpos, ypos, bb_char1, 2, 0); } } while(1); } To compile the example, just type : (Make sure DASM is in the current path) cc7800 -Iheaders examples/test_nmi.c In attachment, a picture of the result, and the new version of cc7800 installer for Windows. cc7800-0.2.4-x86_64.msi 5 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 7, 2023 Author Share Posted June 7, 2023 The next step again. I've added 16 bits numbers comparison to cc6502 (the Rust code used as the base for cc7800 and cc2600) and `itoa` function to the cc7800 library. This enables to display a score on top of the scrolling fiel ! I've also added a define (_MS_TOP_SCROLLING_ZONE) which defines, well, the top of the scrolling zone... Here is a new demo : example_score.c, batteries included and still without a single line of assembly code. Attached is a video of the result on my old PAL Atari 7800. #include "stdlib.h" #include "string.h" #include "prosystem.h" #define VERTICAL_SCROLLING #define _MS_TOP_SCROLLING_ZONE 1 #include "multisprite.h" char i, counter, xpos, ypos; char *ptr; char xchest; #define NB_SPRITES 32 ramchip short sp_xpos[NB_SPRITES], sp_ypos[NB_SPRITES]; ramchip char sp_direction[NB_SPRITES]; const signed short dx[24] = {300, 289, 259, 212, 149, 77, 0, -77, -150, -212, -259, -289, -300, -289, -259, -212, -149, -77, 0, 77, 149, 212, 259, 289}; const signed short dy[24] = {0, 124, 240, 339, 415, 463, 480, 463, 415, 339, 240, 124, 0, -124, -239, -339, -415, -463, -480, -463, -415, -339, -240, -124}; const char horizontal_pingpong[24] = { 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13 }; const char vertical_pingpong[24] = { 0, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; const char background[22] = { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 }; holeydma reversed scattered(16,2) char bb_char1[32] = { 0x01, 0x00, 0x01, 0x40, 0x0a, 0x94, 0x2a, 0x90, 0x3b, 0xa0, 0xc8, 0xe5, 0xc8, 0xe4, 0xc8, 0xd0, 0xc8, 0xe5, 0xbb, 0x84, 0x0c, 0x20, 0x2a, 0x90, 0x0e, 0x50, 0x3f, 0x94, 0x3d, 0x68, 0x5d, 0x6a }; reversed scattered(16,4) char tiles[64] = { 0x5a, 0x5a, 0x95, 0x95, 0x69, 0x69, 0x65, 0x65, 0x69, 0x69, 0x95, 0x95, 0xa5, 0xa5, 0x65, 0x65, 0xa5, 0xa5, 0xa9, 0xa9, 0x96, 0x96, 0xa6, 0xa6, 0x96, 0x96, 0xa9, 0xa9, 0x5a, 0x5a, 0xa6, 0xa6, 0x5a, 0x5a, 0x95, 0x95, 0x69, 0x69, 0x65, 0x65, 0x69, 0x69, 0x95, 0x95, 0xa5, 0xa5, 0x65, 0x65, 0xa5, 0xa5, 0xa9, 0xa9, 0x96, 0x96, 0xa6, 0xa6, 0x96, 0x96, 0xa9, 0xa9, 0x5a, 0x5a, 0xa6, 0xa6 }; #define CHEST_INDEX 4 reversed scattered(16,2) char chest[32] = { 0x00, 0x00, 0x05, 0x50, 0x2b, 0xf4, 0x96, 0xfc, 0x69, 0xbd, 0xbe, 0x6a, 0x55, 0x55, 0xaa, 0xbf, 0xd5, 0xd5, 0xd9, 0xb7, 0xef, 0x9d, 0xef, 0x9d, 0xef, 0x9d, 0xdd, 0x9d, 0xd5, 0xf7, 0xff, 0x95 }; #define DIGITS_INDEX 6 reversed scattered(16,20) char digits[320] = { 0x15, 0x40, 0x05, 0x00, 0x15, 0x40, 0x15, 0x40, 0x01, 0x40, 0x55, 0x50, 0x15, 0x40, 0x55, 0x50, 0x15, 0x40, 0x15, 0x40, 0x15, 0x40, 0x05, 0x00, 0x15, 0x40, 0x15, 0x40, 0x01, 0x40, 0x55, 0x50, 0x15, 0x40, 0x55, 0x50, 0x15, 0x40, 0x15, 0x40, 0x5a, 0x70, 0x0d, 0x00, 0x7a, 0xd0, 0x7a, 0x50, 0x07, 0x40, 0xda, 0xa0, 0x5a, 0xd0, 0x7a, 0xd0, 0x7a, 0x50, 0xda, 0x70, 0x70, 0xd0, 0x05, 0x00, 0x50, 0x50, 0x50, 0xd0, 0x05, 0x40, 0x50, 0x00, 0xd0, 0x70, 0x50, 0x50, 0x50, 0xd0, 0x70, 0x50, 0x51, 0x70, 0x1d, 0x00, 0xa0, 0xd0, 0xa0, 0xd0, 0x37, 0x40, 0xd7, 0x40, 0xd0, 0xa0, 0xa1, 0xe0, 0x70, 0xd0, 0x70, 0x50, 0xd3, 0x50, 0x37, 0x00, 0x00, 0x70, 0x00, 0x70, 0x1d, 0xc0, 0x75, 0xc0, 0x70, 0x00, 0x03, 0x40, 0xd0, 0x70, 0x50, 0xd0, 0x76, 0xd0, 0x27, 0x00, 0x03, 0x60, 0x07, 0x60, 0x6b, 0x40, 0xaa, 0x70, 0x77, 0x40, 0x07, 0x80, 0xb7, 0x60, 0xb7, 0x70, 0xdc, 0x70, 0x07, 0x00, 0x03, 0x40, 0x07, 0x40, 0x43, 0x40, 0x00, 0x70, 0x77, 0x40, 0x07, 0x00, 0x37, 0x40, 0x37, 0x70, 0xd8, 0xf0, 0x0d, 0x00, 0x36, 0x80, 0x0a, 0x70, 0xf7, 0xf0, 0x00, 0x70, 0x7a, 0xf0, 0x07, 0x00, 0x7a, 0x70, 0x2a, 0xf0, 0xf0, 0x70, 0x0f, 0x00, 0x1c, 0x00, 0x00, 0xd0, 0x7d, 0xd0, 0x00, 0xf0, 0xd0, 0x70, 0x0f, 0x00, 0xf0, 0xd0, 0x00, 0xd0, 0xf0, 0xd0, 0x0f, 0x00, 0xf8, 0x00, 0xf0, 0xf0, 0xab, 0xe0, 0xf0, 0xf0, 0xf0, 0x70, 0x0f, 0x00, 0xf0, 0xf0, 0x70, 0xf0, 0xd0, 0xf0, 0x0f, 0x00, 0xd0, 0x00, 0xf0, 0x70, 0x03, 0xc0, 0xd0, 0x70, 0x70, 0xf0, 0x0d, 0x00, 0x70, 0x70, 0xf0, 0xd0, 0xbd, 0xe0, 0xff, 0xf0, 0xff, 0xf0, 0xbf, 0xe0, 0x03, 0xc0, 0xbf, 0xe0, 0xbf, 0xe0, 0x0f, 0x00, 0xbf, 0xe0, 0xbf, 0xe0, 0x3f, 0xc0, 0xff, 0xf0, 0xff, 0xf0, 0x3f, 0xc0, 0x03, 0xc0, 0x3f, 0xc0, 0x3f, 0xc0, 0x0f, 0x00, 0x3f, 0xc0, 0x3f, 0xc0, 0x2a, 0x80, 0xaa, 0xa0, 0xaa, 0xa0, 0x2a, 0x80, 0x02, 0x80, 0x2a, 0x80, 0x2a, 0x80, 0x0a, 0x00, 0x2a, 0x80, 0x2a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ramchip int score; ramchip char display_score_str[5]; ramchip char display_score_ascii[6]; void display_score_update() { itoa(score, display_score_ascii, 10); Y = strlen(display_score_ascii); for (X = 0; X != 5 - Y; X++) { display_score_str[X] = DIGITS_INDEX; // '0' } X = 4; do { display_score_str[X--] = DIGITS_INDEX + ((display_score_ascii[--Y] - '0') << 1); } while (Y); } void main() { score = 0; multisprite_init(); multisprite_set_charbase(tiles); // Set up a full background for (counter = 0; counter < _MS_DLL_ARRAY_SIZE - 1; counter++) { if (counter & 2) { ptr = background + 2; } else { ptr = background; } multisprite_display_tiles(0, _MS_DLL_ARRAY_SIZE - 1 - counter, ptr, 20, 1); } // Score display display_score_update(); multisprite_display_tiles(0, 0, display_score_str, 5, 3); multisprite_save(); *P0C1 = multisprite_color(0x1c); // Setup Palette 0: Yellow *P0C2 = multisprite_color(0xc5); // Green *P0C3 = 0x0f; // White *P1C1 = multisprite_color(0x55); // Dark pink *P1C2 = multisprite_color(0x5B); // Light pink *P2C1 = multisprite_color(0x32); *P2C2 = multisprite_color(0x3D); *P2C3 = multisprite_color(0x37); *P3C1 = multisprite_color(0x92); *P3C2 = multisprite_color(0x9D); *P3C3 = multisprite_color(0x97); // Initialize sprites for (ypos = 16, xpos = 16, i = 0, X = 0; X != NB_SPRITES; xpos++, ypos++, X++) { sp_xpos[X] = xpos << 8; sp_ypos[X] = ypos << 8; sp_direction[X] = i++; if (i == 24) i = 0; } // Main loop do { // Prepare scrolling data if (multisprite_vscroll_buffer_empty()) { if (counter & 2) { ptr = background + 2; } else { ptr = background; } multisprite_vscroll_buffer_tiles(0, ptr, 20, 1); multisprite_vscroll_buffer_sprite(xchest, chest, 2, 3); xchest += 45; counter++; } multisprite_flip(); multisprite_vertical_scrolling(1); for (i = 0; i != NB_SPRITES; i++) { X = i; Y = sp_direction[X]; sp_xpos[X] += dx[Y]; sp_ypos[X] += dy[Y]; xpos = sp_xpos[X] >> 8; ypos = sp_ypos[X] >> 8; if ((xpos < 5 && (dx[Y] >> 8) < 0) || (xpos >= 150 && (dx[Y] >> 8) >= 0)) { sp_direction[X] = horizontal_pingpong[Y]; } if ((ypos < 5 + 16 && (dy[Y] >> 8) < 0) || (ypos >= MS_YMAX - 20 && (dy[Y] >> 8) >= 0)) { sp_direction[X] = vertical_pingpong[Y]; } multisprite_display_sprite(xpos, ypos, bb_char1, 2, 0); } score++; display_score_update(); } while(1); } example_score.mp4 cc7800-0.2.5-x86_64.msi 7 Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 12, 2023 Author Share Posted June 12, 2023 Hi, This week's progress on cc7800/tools7800 : - I've added horizontal scrolling support to headers/multisprite.h. It's working nicely but is not general purpose enough at the moment (it doesn't handle tile map boundaries...) - I've added bidirectional support to vertical scrolling, which enables along with horizontal scrolling to tile a screen (example shown below) - I've added support for 1 and 2 button joystick (and detection of 1 button joystick in order to avoid the destruction of the precious console), in a new header: joystick.h - And I've added support for TileEd (tile editor) in Tools7800 : I generate the C code for the tiles directly from parsing the .tmx files Here is examples/example_tiled.c and a video (sorry for the gfx. If anyone is volunteer to make it better looking, he's welcome) : #include "stdlib.h" #include "string.h" #include "prosystem.h" #define BIDIR_VERTICAL_SCROLLING #define HORIZONTAL_SCROLLING #define _MS_TOP_SCROLLING_ZONE 1 #include "multisprite.h" #include "joystick.h" holeydma reversed scattered(16,2) char bb_char1[32] = { 0x01, 0x00, 0x01, 0x40, 0x0a, 0x94, 0x2a, 0x90, 0x3b, 0xa0, 0xc8, 0xe5, 0xc8, 0xe4, 0xc8, 0xd0, 0xc8, 0xe5, 0xbb, 0x84, 0x0c, 0x20, 0x2a, 0x90, 0x0e, 0x50, 0x3f, 0x94, 0x3d, 0x68, 0x5d, 0x6a }; // Generated by sprites7800 based on tiles.yaml reversed scattered(16,12) char tiles[192] = { 0x50, 0x50, 0x00, 0x00, 0xaa, 0xbf, 0xfe, 0xfe, 0xab, 0xea, 0xcf, 0xcf, 0x50, 0x50, 0x13, 0xc4, 0xd6, 0xff, 0xee, 0xee, 0xaf, 0xfa, 0x3c, 0x3c, 0x50, 0x50, 0x14, 0xc4, 0xff, 0xff, 0xde, 0xde, 0xaa, 0xfa, 0xf3, 0xf3, 0x50, 0x50, 0x12, 0x30, 0x3f, 0xfc, 0xde, 0xde, 0xa6, 0xfe, 0xc3, 0xc3, 0x05, 0x05, 0x12, 0x3c, 0x2f, 0xfc, 0xee, 0xee, 0x96, 0xbe, 0x3c, 0x3c, 0x05, 0x05, 0x12, 0x3c, 0x1e, 0xfc, 0xfe, 0xfe, 0x97, 0xfe, 0x33, 0x33, 0x05, 0x05, 0x0c, 0x4c, 0x1e, 0xfc, 0xaa, 0xaa, 0x98, 0x3e, 0xcc, 0xcc, 0x05, 0x05, 0x3c, 0x4c, 0x1e, 0xfc, 0xaa, 0xaa, 0x94, 0x1e, 0x3c, 0x3c, 0x50, 0x50, 0x0c, 0x40, 0x1e, 0xfc, 0xfe, 0xfe, 0xa4, 0x2e, 0xcf, 0xcf, 0x50, 0x50, 0x21, 0x28, 0x1e, 0xec, 0xee, 0xee, 0xa9, 0x7e, 0x3c, 0x3c, 0x50, 0x50, 0x28, 0xa0, 0x1e, 0xec, 0xde, 0xde, 0xba, 0xbe, 0xf3, 0xf3, 0x50, 0x50, 0x28, 0x84, 0x1e, 0xec, 0xde, 0xde, 0xbe, 0xfa, 0xc3, 0xc3, 0x05, 0x05, 0x21, 0x04, 0x1e, 0xec, 0xee, 0xee, 0xaf, 0xfa, 0x3c, 0x3c, 0x05, 0x05, 0x05, 0x30, 0x2e, 0xec, 0xfe, 0xfe, 0xab, 0xea, 0x33, 0x33, 0x05, 0x05, 0x04, 0xf0, 0x3f, 0xfc, 0xaa, 0xaa, 0xaa, 0xaa, 0xcc, 0xcc, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define DIGITS_INDEX 12 reversed scattered(16,20) char digits[320] = { 0x15, 0x40, 0x05, 0x00, 0x15, 0x40, 0x15, 0x40, 0x01, 0x40, 0x55, 0x50, 0x15, 0x40, 0x55, 0x50, 0x15, 0x40, 0x15, 0x40, ... }; // Edited with tiled, and generated with tiles7800 const char tilemap[1024] = { 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, ... 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4 }; #define TILEMAP_WIDTH 32 #define TILEMAP_HEIGHT 32 char i, xoffset; char *ptr, *ptr2; ramchip char *top_tiles, *bottom_tiles; ramchip int score; ramchip char display_score_str[5]; ramchip char display_score_ascii[6]; void display_score_update() { itoa(score, display_score_ascii, 10); Y = strlen(display_score_ascii); for (X = 0; X != 5 - Y; X++) { display_score_str[X] = DIGITS_INDEX; // '0' } X = 4; do { display_score_str[X--] = DIGITS_INDEX + ((display_score_ascii[--Y] - '0') << 1); } while (Y); } void main() { score = 0; xoffset = 0; multisprite_init(); multisprite_set_charbase(tiles); joystick_init(); // Set up a full background for (ptr = tilemap, i = _MS_TOP_SCROLLING_ZONE; i != _MS_DLL_ARRAY_SIZE; i++) { multisprite_display_tiles(-8, i, ptr, 21, 1); // 21 chars (168 pixels) for horizontal scrolling, out of screen on the left ptr += TILEMAP_WIDTH; } bottom_tiles = ptr; top_tiles = tilemap - TILEMAP_WIDTH; // Score display display_score_update(); multisprite_display_tiles(0, 0, display_score_str, 5, 0); multisprite_save(); *P0C1 = multisprite_color(0x1c); // Setup Palette 0: Yellow *P0C2 = multisprite_color(0xc5); // Green *P0C3 = multisprite_color(0xcd); // Light green *P1C1 = 0x0f; // White *P1C2 = multisprite_color(0x9D); *P1C3 = multisprite_color(0x97); // Main loop do { if (multisprite_vscroll_buffers_refill_status()) { switch (multisprite_vscroll_buffers_refill_status()) { case MS_SCROLL_UP: top_tiles -= TILEMAP_WIDTH; bottom_tiles -= TILEMAP_WIDTH; break; case MS_SCROLL_DOWN: top_tiles += TILEMAP_WIDTH; bottom_tiles += TILEMAP_WIDTH; } xoffset -= 8; multisprite_top_vscroll_buffer_tiles(xoffset, top_tiles, 21, 1); multisprite_bottom_vscroll_buffer_tiles(xoffset, bottom_tiles, 21, 1); xoffset += 8; multisprite_vscroll_buffers_refilled(); } multisprite_flip(); multisprite_display_sprite(76, 100, bb_char1, 2, 0); joystick_update(); if (joystick[0] & JOYSTICK_LEFT) { multisprite_horizontal_scrolling(-1); xoffset++; if (xoffset == 8) { top_tiles--; bottom_tiles--; xoffset = 0; } } else if (joystick[0] & JOYSTICK_RIGHT) { multisprite_horizontal_scrolling(1); xoffset--; if (xoffset < 0) { top_tiles++; bottom_tiles++; xoffset = 7; } } if (joystick[0] & JOYSTICK_UP) { multisprite_vertical_scrolling(1); } else if (joystick[0] & JOYSTICK_DOWN) { multisprite_vertical_scrolling(-1); } score++; display_score_update(); } while(1); } example_tiled.mp4 cc7800-0.2.6-x86_64.msi tiled.a78 test.tiled-project test.tsx test.tmx 9 Quote Link to comment Share on other sites More sharing options...
Eagle Posted June 12, 2023 Share Posted June 12, 2023 Impressive work 👍 2 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted June 12, 2023 Share Posted June 12, 2023 So does bidirectional scrolling require special hardware like Nintendo does? Quote Link to comment Share on other sites More sharing options...
Eagle Posted June 13, 2023 Share Posted June 13, 2023 No. Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted June 13, 2023 Share Posted June 13, 2023 That's cool. Quote Link to comment Share on other sites More sharing options...
+bsteux Posted June 14, 2023 Author Share Posted June 14, 2023 On 6/13/2023 at 1:19 AM, KrunchyTC said: So does bidirectional scrolling require special hardware like Nintendo does? Definitely no, and indeed the Maria chip is very strong at handling bidirectionnal scrolling (in contrary to the NES VPU which requires very specific programming OR special hardware like metroid if the field is bigger than 2 screens wide). BUT unfortunately, a fully tiled background is practically only usable in 160A mode (160 pixels wide and 4 colors only) if you want to keep a little CPU time and/or DMA bandwidth to display some sprites over your field... (roughly, it takes 10 + 3 + 9 * 21 = 202 cycles / 402 available in 160A, and 10 + 3 + 9 * 41 = 382 cycles out of 402 in 160B or 320B/C). A solution would be partially tiled background, a solution I'll try to develop a library for in the future... 1 Quote Link to comment Share on other sites More sharing options...
KrunchyTC Posted June 14, 2023 Share Posted June 14, 2023 12 hours ago, bsteux said: Definitely no, and indeed the Maria chip is very strong at handling bidirectional scrolling (in contrary to the NES VPU which requires very specific programming OR special hardware like metroid if the field is bigger than 2 screens wide). BUT unfortunately, a fully tiled background is practically only usable in 160A mode (160 pixels wide and 4 colors only) if you want to keep a little CPU time and/or DMA bandwidth to display some sprites over your field... (roughly, it takes 10 + 3 + 9 * 21 = 202 cycles / 402 available in 160A, and 10 + 3 + 9 * 41 = 382 cycles out of 402 in 160B or 320B/C). A solution would be partially tiled background, a solution I'll try to develop a library for in the future... Interesting. So bidirectional scrolling is always going to be tough to do. This is a GPU/CPU cycle limited task, rather than RAM limited? 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.