Jump to content

bsteux

+AtariAge Subscriber
  • Posts

    84
  • Joined

  • Last visited

Everything posted by bsteux

  1. Thanks, but unfortunately I think cc7800 still requires a lot of documentation to explain all the features and macros already available. An expert in Maria and C will probably find it straightforward, but as of today it's far from reach from the majority of game designers. A best starting point certainly is 7800Basic, which is wonderfully documented. But at least it's good to have a choice, especially for people who have 20+ years of experience in C. Volunteers for documentation are welcome ! I'll do everything possible to provide all the necessary explanations and help.
  2. I've updated multisprite.h in cc7800 to implement DMA Masking vertical scrolling, an idea by @RevEng. It's working like a charm. Previously, my vertical scrolling feature was implemented by drawing a black curtain on the bottom of the screen (consuming DMA, ROM memory of the curtain and a palette color for the black). Now, vertical scrolling consumes less DMA where sprites are displayed in the bottom (46 DMA cycles in that zone vs 10 + 32 * 3 + (8 + 31 * 3) = 409 cycles in the DMA Masking zone). This leaves more DMA cycles for displaying actual sprites, and it uses no ROM memory neither palette color. Now my sparse tiling example in 320BD mode can use the P4C3 color (orange), and now uses all the available colors like in Rikki & Vikki ! 320BD_sparse_tiling.mp4
  3. I've made a similar test on my side (mainly because I couldn't exactly understand what you have done with your bars), and I confirm your observations. Definitely, there is a penalty of 3 cycles for accessing Holey DMA, and not 0 cycles as stated by GCC Maria doc. Here is the source code for my test and the resulting pictures on a7800 v5.2 and on my old PAL Atari 7800. Almost identical, and both show a 3 cycles penalty Holey DMA access. My test just draws horizontal bars (after DMA saturation, so that they are only partially drawn depending on the number of DMA cycles left), and between each line, I add an additional holey DMA access. So by measuring the horizontal gap in pixels between the blue bars (separated by 8 lines, and thus which have 8 * (8 + penalty) DMA cycles between them), I find exactly 3 cycles penalty : 232 pixels between blue bars = 29 * 8 pixels -> 29 * 3 = 87 DMA cycles => / 8 = 11 cycles = 8 cycles + 3 penalty. Here is the source code of my test: #include "conio.h" scattered(8, 32) holeydma char full[8 * 32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; void main() { char i, y; clrscr(); for (y = 0; y != 11; y++) { gotoxy(0, y); textcolor(y & 7); _ms_tmpptr = _ms_dls[X = _conio_y]; Y = _ms_dlend[X]; // Draw 3 grey bars, that consume (8 + 31 * 3) * 3 = 303 DMA cycles for (i = 0; i != 3; i++) { _ms_tmpptr[Y++] = full; _ms_tmpptr[Y++] = -31 & 0x1f; _ms_tmpptr[Y++] = full >> 8; _ms_tmpptr[Y++] = 0; } // Then draw y bars in Holey DMA, that theoretically consume 8 + penalty DMA cycles each for (i = 0; i != y; i++) { _ms_tmpptr[Y++] = 0x00; _ms_tmpptr[Y++] = -31; _ms_tmpptr[Y++] = 0x98; _ms_tmpptr[Y++] = 0; } // Then draw a colorful bar, that consumes 8 + (nb pixels actually drawn / 8) * 3 DMA cycles _ms_tmpptr[Y++] = full; _ms_tmpptr[Y++] = 0x40; _ms_tmpptr[Y++] = full >> 8; _ms_tmpptr[Y++] = _conio_palette; _ms_tmpptr[Y++] = 16; _ms_dlend[X] = Y++; _ms_tmpptr[Y] = 0; } while(1); }
  4. So sad... Unexpected but interesting... I'll have a look at the other thread. Let's keep this one for vertical scrolling innovations 🙂
  5. Thanks for your answer. This is what I thought about Maria - straightforward design: out of screen uses the same logic as in screen, but the line RAM is just not on chip for positions 160 to 255. This gives a good solution for vertical scrolling, since we can eat up DMA cycles for the last lines with any sprite data. No need for using precious holey DMA ROM for transparent or full data, and no need also to eat up a precious palette entry. I will implement this in my multisprites.h header in cc7800 and go back with the results. Wrt 1 cycle DMA penalty existence, that would be surprising (but not excluded, a look at schematics would be required or an experimental check): in Maria, the high address on which the logic to continue the state machine for DMA access is based on (test on A11 or A12 bit) is not the last byte of each DL, so there is plenty of time to add the OFFSET and implement the logic to decide to jump to the next DL or continue with DMA access... The logic is a simple adder, followed by simple combinatorial logic, which certainly runs in a single cycle. I don't see why there would be a need for an additional cycle, and it's also not mentioned in GCC documentation. Anyway, thank you for your answer and your expertise. We'll have a better vertical scrolling available soon... Regards.
  6. Hi, "DMA masking" is I think the idea I was looking wrt vertical scrolling. At the moment, in cc7800, I'm using a black object to overlay the bottom of the screen, which is a similar idea but is not as effective as yours. I've got 2 questions to you Atari 7800 expert (I'm just starting on my side) : 1 - you're stating that on the upper part of the bottom DLL the DMA cost is 44 cycles. Why not 10 * 4 = 40 ? Is there something I misunderstand ? The idea is to overuse DMA on the bottom lines with 4 x 32 bytes. With 4 5-bytes DLs, the actual cost DMA cost would be (10 + 3 * 32) * 4 = 424 cycles, which together with the DMA startup and shutdown times + 7 CPU delay cycles should cover the 454 DMA cycles available. On the Holey DMA covered part, this should consume 40 cycles no ? 2 - What if we draw these 32 bytes outside the screen, at position 160 ? Do you know if Maria eats up DMA cycles in that case or if Maria is clever enough to disengage offscreen writing and spare DMA cycles ? (which would be nice in the general case, but not convenient in our case, since otherwise we would have to really draw something either transparent or background color ON screen). Best regards !
  7. Hi, Here is the example for 320B/D sparse tiling. Frankly, using 320B mode is painful (I discovered the C1 issue). By the way, it's supposed to be a 6 colors + background mode, but in my case, it's more a 3+ colors mode (I have to put P4C3 to black in order to draw the black scrolling border at the bottom of the screen. Is there any other known simple way to implement vertical scrolling (saving 1 color, but not requiring more computation for the last display line) ?, and P0C1 and P4C1 are barely usable due to 320B C1 restriction). The guys from Vikky & Rikky did a great job managing to use it (the rocks nicely get over the restrictions - used in my example as a "tribute" to their work. No intention to steal anything). 320D is also a pain in the ass. The multicolor modes (P1 != 0 or P0 != 0), though economical, are barely usable (I've used it for the waterfall in my example), and the 320A-like mode (P0 = 0 and P1 =0) is restricted to 2 colors P0C2 and P4C2... (red and white in my iteration. I think it's the same in Rikky & Vikky, at least on the screenshot I've used as an example). #include "stdlib.h" #include "string.h" #include "prosystem.h" #include "joystick.h" #define MODE_320BD // Generated from tiles7800 --sparse 320BD_tiles.yaml 320BD_sparse.tmx const char tilemap_0_0[2] = {84, 86}; const char tilemap_0_1[1] = {44}; const char tilemap_0_2[4] = {76, 78, 80, 82}; const char tilemap_0_3[1] = {44}; const char tilemap_0_4[2] = {80, 82}; const char tilemap_0_5[2] = {84, 86}; const char tilemap_0_6[4] = {52, 54, 56, 58}; const char tilemap_0_7[2] = {88, 90}; const char tilemap_0_data[] = {0, 0, tilemap_0_0, 0xe0, tilemap_0_0 >> 8, (0 << 5) | ((-2) & 0x1f), 15, 1, 1, tilemap_0_1, 0x60, tilemap_0_1 >> 8, (5 << 5) | ((-1) & 0x1f), 11, 8, 7, tilemap_0_2, 0xe0, tilemap_0_2 >> 8, (0 << 5) | ((-4) & 0x1f), 24, 9, 9, tilemap_0_3, 0x60, tilemap_0_3 >> 8, (5 << 5) | ((-1) & 0x1f), 11, 10, 10, tilemap_0_4, 0xe0, tilemap_0_4 >> 8, (0 << 5) | ((-2) & 0x1f), 15, 24, 24, tilemap_0_5, 0xe0, tilemap_0_5 >> 8, (0 << 5) | ((-2) & 0x1f), 15, 28, 27, tilemap_0_6, 0xe0, tilemap_0_6 >> 8, (0 << 5) | ((-4) & 0x1f), 24, 31, 31, tilemap_0_7, 0xe0, tilemap_0_7 >> 8, (0 << 5) | ((-2) & 0x1f), 15, 96, 0xff}; .. const char tilemap_data[64] = {tilemap_0_data & 0xff, tilemap_0_data >> 8, tilemap_1_data & 0xff, tilemap_1_data >> 8, tilemap_2_data & 0xff, ... ... tilemap_30_data & 0xff, tilemap_30_data >> 8, tilemap_31_data & 0xff, tilemap_31_data >> 8}; #define TILING_HEIGHT 32 #define TILING_WIDTH 32 #include "sparse_tiling.h" // Generated from sprites7800 320BD_tiles.yaml reversed scattered(16,24) char white_tubes[384] = { 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x30, 0x00, 0x00, 0x18, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0xb6, 0x00, 0x00, 0xda, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfb, 0xfe, 0xdf, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xdf, 0xfc, 0xb6, 0xff, 0xfe, 0xda, 0x05, 0xa0, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xff, 0xf2, 0xff, ... ramchip int x, y; void main() { x = 0; y = 0; multisprite_init(); multisprite_set_charbase(white_tubes); tiling_init(tilemap_data); joystick_init(); tiling_goto(x, y); *P0C1 = multisprite_color(0xbc); // Light turquoie *P0C2 = multisprite_color(0x33); // Red *P0C3 = multisprite_color(0xa5); // Dark turquoise *P4C1 = multisprite_color(0x83); // Dark Blue *P4C2 = 0x0f; // White // P4C3 is black due to bottom scrolling hiding scheme // Main loop do { multisprite_flip(); joystick_update(); if (joystick[0] & JOYSTICK_LEFT) x--; else if (joystick[0] & JOYSTICK_RIGHT) x++; if (joystick[0] & JOYSTICK_UP) y--; else if (joystick[0] & JOYSTICK_DOWN) y++; tiling_goto(x, y); } while(1); } I've attached to this post the first windows release of sprites7800 and tiles7800, so that Windows user can use these tools without using Rust and Cargo. All the examples are in the examples directory in my Github cc7800 repository. Best regards ! 320BD_sparse_tiling.mp4 sprites7800.exe tiles7800.exe 320BD_sparse.tsx 320BD_sparse.tmx 320BD_tiles.yaml
  8. Hi, Here is a "technical" update of Paul's Happybird. @Albert asked me to change the savekey address to $1480 due to an issue with savekey address allocations, so here is an updated version. Note that this version was compiled with cc2600 v0.4, which produces a slightly better optimized code - nothing to be noticed by the player... Regards. happybird-v1.08-PAL-PLUSROM.bin happybird-v1.08-NTSC-PLUSROM.bin
  9. Hi, Here is a new example showing sparse tiling with 320A/C mixed mode (headers/multisprite.h now supports MODE_320AC and MODE320BD options). For economy of DMA and CPU, some tiles are displayed in 320A (one color), while some others (here the colored cube) are in 4 colors + BG (320C mode), yielding a "Rikki & Vikki" look. Note that tiles7800 and sprites7800 (from tools7800 github repository) now support 320C mode tiles and sprites, and has a new "alias" feature, allowing the same tiles to be used with different colors (you can use them with different colors in Tiled, but they will be present only once in ROM). Here is the example with the generated code from tiles7800 and sprites7800 : #include "stdlib.h" #include "string.h" #include "prosystem.h" #include "joystick.h" #define MODE_320AC // Generated from tiles7800 --sparse highres_tiles.yaml highres_sparse.tmx const char tilemap_0_data[] = {96, 0xff}; const char tilemap_1_data[] = {96, 0xff}; const char tilemap_2_data[] = {96, 0xff}; const char tilemap_3_data[] = {96, 0xff}; const char tilemap_4_data[] = {96, 0xff}; const char tilemap_5_0[5] = {16, 12, 6, 12, 8}; const char tilemap_5_data[] = {6, 2, tilemap_5_0, 0x60, tilemap_5_0 >> 8, (1 << 5) | ((-5) & 0x1f), 29, 96, 0xff}; ... const char tilemap_31_data[] = {96, 0xff}; const char tilemap_data[64] = {tilemap_0_data & 0xff, tilemap_0_data >> 8, tilemap_1_data & 0xff, tilemap_1_data >> 8, tilemap_2_data & 0xff, tilemap_2_data >> 8, tilemap_3_data & 0xff, tilemap_3_data >> 8, tilemap_4_data & 0xff, tilemap_4_data >> 8, tilemap_5_data & 0xff, tilemap_5_data >> 8, tilemap_6_data & 0xff, tilemap_6_data >> 8, tilemap_7_data & 0xff, tilemap_7_data >> 8, tilemap_8_data & 0xff, tilemap_8_data >> 8, tilemap_9_data & 0xff, tilemap_9_data >> 8, tilemap_10_data & 0xff, tilemap_10_data >> 8, tilemap_11_data & 0xff, tilemap_11_data >> 8, tilemap_12_data & 0xff, tilemap_12_data >> 8, tilemap_13_data & 0xff, tilemap_13_data >> 8, tilemap_14_data & 0xff, tilemap_14_data >> 8, tilemap_15_data & 0xff, tilemap_15_data >> 8, tilemap_16_data & 0xff, tilemap_16_data >> 8, tilemap_17_data & 0xff, tilemap_17_data >> 8, tilemap_18_data & 0xff, tilemap_18_data >> 8, tilemap_19_data & 0xff, tilemap_19_data >> 8, tilemap_20_data & 0xff, tilemap_20_data >> 8, tilemap_21_data & 0xff, tilemap_21_data >> 8, tilemap_22_data & 0xff, tilemap_22_data >> 8, tilemap_23_data & 0xff, tilemap_23_data >> 8, tilemap_24_data & 0xff, tilemap_24_data >> 8, tilemap_25_data & 0xff, tilemap_25_data >> 8, tilemap_26_data & 0xff, tilemap_26_data >> 8, tilemap_27_data & 0xff, tilemap_27_data >> 8, tilemap_28_data & 0xff, tilemap_28_data >> 8, tilemap_29_data & 0xff, tilemap_29_data >> 8, tilemap_30_data & 0xff, tilemap_30_data >> 8, tilemap_31_data & 0xff, tilemap_31_data >> 8}; #define TILING_HEIGHT 32 #define TILING_WIDTH 32 #include "sparse_tiling.h" // Generated from sprites7800 highres_tiles.yaml reversed scattered(16,24) char blue_tubes[384] = { 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x30, 0x00, 0x00, 0x18, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0xb6, 0x00, 0x00, 0xda, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfb, 0xfe, 0xdf, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xdf, 0xfc, ... 0x1c, 0x1c, 0x01, 0x60, 0x70, 0x0e, 0x1c, 0x1c, 0x01, 0x80, 0x6f, 0xf6, 0x18, 0x18, 0x01, 0x60, 0x5f, 0xfa, 0x08, 0x08, 0x01, 0x80, 0x3f, 0xfc, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; reversed scattered(16,4) char colored_cube[64] = { 0x00, 0x00, 0x00, 0x00, 0x30, 0xf0, 0xf0, 0xc0, 0x50, 0xf0, 0xf0, 0xa2, 0x60, 0xf0, 0xf0, 0x6a, 0x70, 0x00, 0x00, 0xea, 0x70, 0x7f, 0xef, 0xea, 0x70, 0x75, 0xe5, 0xea, 0x70, 0x7f, 0xef, 0xea, 0x70, 0x75, 0xe5, 0xea, 0x70, 0x7f, 0xef, 0xea, 0x70, 0x75, 0xe5, 0xea, 0x70, 0x00, 0x00, 0xea, 0x60, 0xfa, 0xfa, 0x6a, 0x52, 0xfa, 0xfa, 0xaa, 0x32, 0xfa, 0xfa, 0xc8, 0x00, 0x00, 0x00, 0x00 }; ramchip int x, y; void main() { x = 0; y = 0; multisprite_init(); multisprite_set_charbase(blue_tubes); tiling_init(tilemap_data); joystick_init(); tiling_goto(x, y); *P0C2 = 0x0f; // White *P1C2 = multisprite_color(0x9a); // Blue *P2C2 = multisprite_color(0x3a); // Orange *P3C2 = multisprite_color(0x33); // Red // Main loop do { multisprite_flip(); joystick_update(); if (joystick[0] & JOYSTICK_LEFT) x--; else if (joystick[0] & JOYSTICK_RIGHT) x++; if (joystick[0] & JOYSTICK_UP) y--; else if (joystick[0] & JOYSTICK_DOWN) y++; tiling_goto(x, y); } while(1); } Note that the latest 0.2.10 version of cc7800 is now based on cc6502 v1.0, which has most of its limitations alleviated : you can now use functions with parameters and local variables, without any performance hit (parameters and local variables are automatically "optimally" allocated in zeropage based on call tree during link phase). I'm preparing an example based on 320B/D mixed mode (very likely the one use in "Rikky & Vikki", based on the screenshots I've seen), which should finalize the support of all the 7800 graphics modes in Sprite7800 and Tiles7800. I'll then release a win32 binary version of these tools. By now, you still need Rust & Cargo to compile the binaries from the github repository (not complicated, but not immediate either. A good exercise by the way. Get Rusty.). Have a nice day ! sparse_tiling_hires.mp4 highres_tiles.tsx highres_sparse.tmx highres_tiles.yaml cc7800-0.2.10-x86_64.msi
  10. Sparse tiling is a good solution for RPGs and platform games. For vertical only schmups, the VERTICAL_SCROLLING feature of multisprite.h is more efficient, since you only need to provide programmatically the new arriving data when requested by the scrolling algorithm (and thus the level can be of infinite vertical size). With VERTICAL_SCROLLING, you can feed the display with any kind of data : tiles, bitmap data, and they can be sparse if you want to spare some CPU... It's the right method to make a vertical Schump or Run&Gun. See examples/example_vertical_scrolling.c. I'll update it to include 160B graphics (it's only 160A at the moment).
  11. Hi, Here is the latest new thingy in cc7800: sparse tiling. What the #^% is that ? - You want to make some nice Mario Bros like levels ? Draw them with Tiled, the famous tiles editor (see sparse.tmx and test.tsx below). - You provide a description of your tiles (tiles.yaml) and generate the C code for your tiles with sprites7800 (from https://github.com/steux/tools7800) - From your tiled file, you generate the C code for your Maris Bros like level, using tiles7800 and the new --sparse option (from https://github.com/steux/tools7800 again): tiles7800 --sparse tiles.yaml sparse.tmx You write a simple C code around your generates data code, using the brand new sparse_tiling.h header provided in cc7800 (examples/example_sparse_tiling.c) : #include "stdlib.h" #include "string.h" #include "prosystem.h" #include "joystick.h" #define _MS_TOP_SCROLLING_ZONE 1 // Code generated by tiles7800 --sparse const char tilemap_0_0[6] = {0, 0, 0, 0, 0, 0}; const char tilemap_0_data[] = {5, 0, tilemap_0_0, 0x60, tilemap_0_0 >> 8, (1 << 5) | ((-6) & 0x1f), 33, 0x7f, 0xff}; const char tilemap_1_0[5] = {0, 0, 0, 0, 0}; const char tilemap_1_data[] = {4, 0, tilemap_1_0, 0x60, tilemap_1_0 >> 8, (1 << 5) | ((-5) & 0x1f), 29, 0x7f, 0xff}; const char tilemap_2_0[4] = {0, 0, 0, 0}; const char tilemap_2_data[] = {3, 0, tilemap_2_0, 0x60, tilemap_2_0 >> 8, (1 << 5) | ((-4) & 0x1f), 24, 0x7f, 0xff}; const char tilemap_3_0[3] = {0, 0, 0}; const char tilemap_3_1[1] = {22}; ... #define TILING_HEIGHT 32 #define TILING_WIDTH 32 #include "sparse_tiling.h" // Code generated by sprites7800 reversed scattered(16,14) char tiles[224] = { 0x50, 0x50, 0x00, 0x00, 0xaa, 0xbf, 0xfe, 0xfe, 0xab, 0xea, 0xcf, 0xcf, 0x2a, 0xa8, 0x50, 0x50, 0x13, 0xc4, 0xd6, 0xff, 0xee, 0xee, 0xaf, 0xfa, 0x3c, 0x3c, 0x20, 0x08, 0x50, 0x50, 0x14, 0xc4, 0xff, 0xff, 0xde, 0xde, 0xaa, 0xfa, 0xf3, 0xf3, 0x20, 0x08, 0x50, 0x50, 0x12, 0x30, 0x3f, 0xfc, 0xde, 0xde, 0xa6, 0xfe, 0xc3, 0xc3, 0x20, 0x08, 0x05, 0x05, 0x12, 0x3c, 0x2f, 0xfc, 0xee, 0xee, ... 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]; ramchip int x, y; 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; x = 0; y = 0; multisprite_init(); multisprite_set_charbase(tiles); tiling_init(tilemap_data); joystick_init(); 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); *P2C1 = multisprite_color(0x92); *P2C2 = multisprite_color(0x97); *P2C3 = multisprite_color(0x9D); // Rainbow *P4C1 = multisprite_color(0x4A); *P4C2 = multisprite_color(0x5A); *P4C3 = multisprite_color(0x8A); *P5C1 = multisprite_color(0xDA); *P5C2 = multisprite_color(0x1A); *P5C3 = multisprite_color(0x2A); *P6C1 = multisprite_color(0x3A); *P6C2 = 0x0f; tiling_goto(0, 0); // Main loop do { multisprite_flip(); joystick_update(); if (joystick[0] & JOYSTICK_LEFT) x--; else if (joystick[0] & JOYSTICK_RIGHT) x++; if (joystick[0] & JOYSTICK_UP) y--; else if (joystick[0] & JOYSTICK_DOWN) y++; tiling_goto(x, y); score++; display_score_update(); } while(1); } And the tiling_goto function does all the magic : displaying your tilesets at the given pixel precise position. So what are the advantages of this display method ? - All the data are in ROM, no need for RAM (in contrary to bitmap display). - Every time there are holes (sparsity) in the tileset, some CPU is freed for something else than display. - You can mix at will 160A and 160B tiles, and change palette on the fly (160A : economy of DMA, CPU and ROM - 3 colors only, 160B : for colored items (12 colors) -> See the rainbow in my tileset). Say Hello to 25 color backgrounds ! - ROM usage is very low : tiles information gets "compressed" - Double buffering is included : if you need some CPU for your game, turn to 30fps instead of 60fps. At the moment, the full thingy is not optimized yet : I display the whole screen every frame, while it would'nt be very complicated to keep the Display Lists while continuously scrolling... Well, I'll do this later. Anyway, the provided routine is fast enough to display at 60fps without any issue (it consumes ~1.5 display lines of CPU per displayed tileset, whatever is the size of it)? Next step: provide a 320A/320C example using sparse tiling... That's not complicated, but I'm definitely not an artist... See you ! sparse_tiling.mp4 cc7800-0.2.9-x86_64.msi sparse_tiling.a78 sparse.tmx test.tsx tiles.yaml
  12. That's very nice. RMT is a really very good Pokey tracker (MIDI compatible!) for the 7800 platform. I've shown it to my 17-old daughter (who is composing on Abletone Live), and she was amazed.
  13. I think I've found at least one small issue, which may be the one you're referring to. Indeed, the Goto looping in the RMT song list is actually a 0xfe followed by a dummy byte + 2 absolute address bytes. In your 7800rmt2asm.c file, you're looking for a 0xfe followed by 0x00, which is not always the case for a goto (for instance, in my favorite Alien3 song, the end looping goto was 0xfe 0x02, which was then not relocated by your program and miserably failed at the end of music - which I hadn't even noticed, oops). If you change line 249 of 7800rmt2asm.c to if( (buffer[t+rmtstart]==0xFE) /*&& (buffer[t+1+rmtstart]==0x00)*/) the music loops at the end, and everything's fine. Tell me if this helps...
  14. Ah, OK, CEM stands for "Concerto Enhancement Module". Looks cool. Put me on your list when it's ready to ship.
  15. Thanks for the info. I'll have a look at this issue on my side.
  16. Hi, Here are the latest news on cc7800 : the Raster Music Tracker (https://github.com/VinsCool/RASTER-Music-Tracker) integration, using the famous Pokey chip. You can find below the `examples/example_rmtracker.c` source file, as well as the result running on real hardware. A few notes about this example : - This code is based on the RMT player code ported to DASM by @Eckhard Stolberg (Are you still around?). The code is old but seems capable of playing recent RMT files, as least some in the github repository of @VinsCool. Maybe @Vinscool could tell us how to update it to the latest RMT version ? Very cool Music Tracker indeed, and kept up to date : it's running perfectly on my laptop. Amazing tracker indeed. - I've taken a RMT track by pure random (maybe guided by the "Alien3" name...) and converted it into ASM using the 7800rmt2asm.c utility found in the 7800Basic repository (I guess this is a code by @RevEng). By the way, most of the development effort on my side was on the integration of DASM code in cc7800, which is now quite straightforward. - I've slightly modified the produced ASM file (renamed .RMTSTART to RMTSTART, and added an optionnal "; codesize:" tag for cc7800 to figure out what is the compiled size of this assembly code. - I've written a simple player shown below, and tested it in a7800: perfect result. - Tried on the real Atari 7800 with my brand new Concerto cart with soldered Hokey chip... and nothing... No sound. No crash but no sound, except a vaguely faded rhythm (maybe is this just some noise related to processor activity). - OK. Read a little bit about the problems with the Hokey chips, and figured out that I should move the Pokey chip to address 0x4000 instead of 0x450, and it worked... Conclusion : RMTTracker is a very nice way of composing and playing (quality) music on the Atari 7800, BUT apparently (if I didn't make a mistake) there are still some issues with the Concerto firmware when Pokey is located at 0x450 (which is very desirable if you want to use extra RAM at 0x4000). So at the moment, it's Pokey OR extra RAM. Any news about that on the Concerto side @batari ? #include "prosystem.h" #include "rmtplayer.h" #include "examples/RMT_Alien3.asm" #include "conio.h" char i, j, k, l; void main() { clrscr(); gotoxy(6, 0); textcolor(2); cputs("Raster Music Tracker Player"); for (i = 0; i != 4; i++) { gotoxy(14 + (i << 2), 19); putch('1' + i); } asm("LDX #<RMTSTART"); // Give pointer to RMT music asm("LDY #>RMTSTART"); load(0); // Song line in A register asm("JSR rmt_init"); textcolor(1); i = 0; do { // Display the volume for each channel k = 14 + (i << 2); l = 17 - (trackn_audc[Y = i] & 0x0f); for (j = 2; j < l; j++) { gotoxy(k, j); putch(' '); } for (; j < 18; j++) { gotoxy(k, j); putch(127); } i++; if (i == 4) i = 0; *BACKGRND = 0x00; while (*MSTAT & 0x80); // Wait for end of VBLANK while (!(*MSTAT & 0x80)); // Wait for VBLANK *BACKGRND = 0x0f; asm("JSR rmt_play"); } while(1); } example_rmtplayer.mp4 cc7800-0.2.8-x86_64.msi example_rmtplayer.a78
  17. Hi, This week's progress : the integration of TIA sound effects in cc7800 (using a new header: sfx.h). Two functions are defined : sfx_schedule and sfx_play, which mush be called every frame. I've integrated the whole library of TIA sounds from 7800basic (117 sounds!), courtesy of @RevEng (I've used a new tool in Tools7800: basic2cc7800, which grabs the 7800basic arrays and convert them to C arrays...). Here is the example (examples/example_sfx.c) : #include "conio.h" #include "joystick.h" #include "stdlib.h" #include "sfx.h" // SFX converted from 7800basic (soundtest.bas example file) with basic2cc7800 (from tools7800) : // All sounds are copyright Mike Saarna (msaarna), aka RevEng, and I guess most of them have been generated using his // tool sound2tia const char *array_name[117] = { "sfx-salvolasershot", "sfx-spaceinvshoot", "sfx-berzerkrobotdeath", "sfx-echo1", "sfx-echo2", "sfx-jumpman", "sfx-cavalry", "sfx-alientrill1", "sfx-alientrill2", "sfx-pitfalljump", "sfx-advpickup", "sfx-advdrop", "sfx-advbite", "sfx-advdragonslain", "sfx-bling", "sfx-dropmedium", ... "sfx-bigboom", "sfx-thud", "sfx-bump", "sfx-shouty", "sfx-quack" }; const char sfx_salvolasershot[72] = { 0x10, 0x18, 0x01, 0x04, 0x08, 0x08, 0x05, 0x08, 0x07, 0x04, 0x08, 0x07, 0x05, 0x08, 0x07, 0x06, 0x08, 0x07, 0x07, 0x08, 0x06, 0x06, 0x08, 0x06, 0x07, 0x08, 0x06, 0x08, 0x08, 0x06, 0x09, 0x08, ... }; ... const char sfx_quack[42] = { 0x10, 0x10, 0x00, 0x15, 0x06, 0x08, 0x15, 0x06, 0x09, 0x15, 0x06, 0x0a, 0x14, 0x06, 0x0b, 0x14, 0x06, 0x0c, 0x14, 0x06, 0x0d, 0x14, 0x06, 0x0e, 0x13, 0x06, 0x0f, 0x13, 0x06, 0x0f, 0x13, 0x06, 0x0f, 0x13, 0x06, 0x0f, 0x13, 0x06, 0x0f, 0x00, 0x00, 0x00 }; const char *array_data[117] = { sfx_salvolasershot, sfx_spaceinvshoot, sfx_berzerkrobotdeath, sfx_echo1, sfx_echo2, sfx_jumpman, sfx_cavalry, sfx_alientrill1, sfx_alientrill2, sfx_pitfalljump, sfx_advpickup, sfx_advdrop, sfx_advbite, sfx_advdragonslain, sfx_bling, sfx_dropmedium, sfx_electrobump, sfx_explosion, sfx_humanoid, sfx_transporter, sfx_twinkle, sfx_electroswitch, sfx_nonobounce, sfx_70stvcomputer, sfx_alienlife, sfx_chirp, sfx_plonk, sfx_spawn, sfx_maser, sfx_rubbermallet, sfx_alienkitty, sfx_electropunch, sfx_drip, sfx_ribbit, sfx_wolfwhistle, sfx_cabwhistle, sfx_jumpo, sfx_pulsecannon, sfx_spring, sfx_buzzbomb, sfx_bassbump, sfx_hophop, sfx_distressed, sfx_ouch, sfx_laserrecoil, sfx_electrosplosion, sfx_hophip, sfx_hophipquick, sfx_bassbump2, sfx_pickupprize, sfx_distressed2, sfx_pewpew, sfx_denied, sfx_teleported, sfx_alienklaxon, sfx_crystalchimes, sfx_oneup, sfx_babywah, sfx_gotthecoin, sfx_babyribbit, sfx_squeek, sfx_whoa, sfx_gotthering, sfx_yahoo, sfx_warcry, sfx_downthepipe, sfx_powerup, sfx_falling, sfx_eek, sfx_uhoh, sfx_anotherup, sfx_bubbleup, sfx_jump1, sfx_plainlaser, sfx_aliencoo, sfx_simplebuzz, sfx_jump2, sfx_jump3, sfx_dunno, sfx_snore, sfx_uncovered, sfx_doorpound, sfx_distressed3, sfx_eek2, sfx_rubberhammer, sfx_alienbuzz, sfx_anotherjumpman, sfx_anotherjumpdies, sfx_longgongsilver, sfx_strum, sfx_dropped, sfx_alienaggressor, sfx_electroswitch2, sfx_gooditem, sfx_babyribbithop, sfx_distressed4, sfx_hahaha, sfx_yeah, sfx_arfarf, sfx_activate, sfx_hahaha2, sfx_wilhelm, sfx_poof1, sfx_poof2, sfx_dragit, sfx_roarcheep, sfx_roarroar, sfx_deeproar, sfx_echobang, sfx_tom, sfx_clopclop, sfx_museboom, sfx_bigboom, sfx_thud, sfx_bump, sfx_shouty, sfx_quack }; char page, choice, index; char *name; ramchip char i, j, string[10]; ramchip char prevjoy; #define MAXPAGES ((sizeof(array_data) / 2 / 20) + 1) void display_page() { clrscr(); textcolor(0); gotoxy(0, 0); itoa(page, string, 10); cputs(string); putch('/'); itoa(MAXPAGES, string, 10); cputs(string); textcolor(2); gotoxy(6, 0); cputs("SFX Examples"); textcolor(7); for (i = 0, X = 1; X < page; X++) { i += 20; } for (j = 0; j < 20; j++) { Y = i + j; if (Y < sizeof(array_data) / 2) { name = array_name[Y]; gotoxy(2, j + 2); cputs(name); } } textcolor(3); gotoxy(0, choice + 2); putch('>'); } void main() { page = 1; choice = 0; index = 0; prevjoy = 0; joystick_init(); display_page(); do { joystick_update(); if (joystick[0] != prevjoy) { if (joystick[0] & JOYSTICK_UP) { if (choice > 0) { textcolor(3); gotoxy(0, choice + 2); putch(' '); choice--; index--; gotoxy(0, choice + 2); putch('>'); } } else if (joystick[0] & JOYSTICK_DOWN) { if (choice < 19 && index < sizeof(array_data) / 2 - 1) { textcolor(3); gotoxy(0, choice + 2); putch(' '); choice++; index++; gotoxy(0, choice + 2); putch('>'); } } else if (joystick[0] & JOYSTICK_RIGHT) { if (page < MAXPAGES) { page++; index += 20; display_page(); } } else if (joystick[0] & JOYSTICK_LEFT) { if (page > 1) { page--; index -= 20; display_page(); } } else if (joystick[0] & JOYSTICK_BUTTON1) { if (index < sizeof(array_data) / 2) { sfx_schedule(array_data[index]); } } } prevjoy = joystick[0]; // Wait for VBLANK if (*MSTAT & 0x80) { // If in VBLANK sfx_play(); if (multisprite_pal_frame_skip()) sfx_play(); // Advance twice every 5 frames (to cope with 60Hz instead of 50Hz) while (*MSTAT & 0x80); // Wait for end of VBLANK } } while (1); } and the result is in attachment... To follow on my agenda : - The integration of Pokey music through Raster Media Tracker player integration (maybe too ambitious, let's see) - The support of 160B, 320B and 320C graphics in sprites7800 (from tools7800, my collection of 7800 utilities (in Rust!)) - The support of sparse and mixed mode tiling - The support of bitmaps (i.e. direct mode display) - The support of scrolling of bitmaps... if my free time allows it... - Any other suggestion ? Whooo... Let's see how far I can go on this... let's do it while I still have fun doing this... example_sfx.mp4 cc7800-0.2.7-x86_64.msi example_sfx.a78
  18. Indeed, in direct mode, you have much more bandwidth, because 160B background filling requires only 10 * 3 + 81 * 3 = 273 DMA cycles, which leaves 129 cycles per line to display overlaying sprites. A 8 pixels wide sprite display should consumes 10 + 4 * 3 = 22 cycles, so one should be able to display 5 such sprites on the same line. At the moment my cc7800 lib only supports 160A display and was focusing on "tiling" or "character" mode background scrolling / tiling. OK. I understand that I must support more advanced display modes (especially 320C looks promising), which shouldn't be an issue now that everything is in place. I'll work on that for the next version of cc7800.
  19. Thank you for the explanations. I'm learning a lot from that. I've started to make an example playing your sounds in C. Still to be tested on real hardware...
  20. Hi, May I use your soundtest library to make a TIA sound example for cc7800 (keeping your copyright obviously) ? These sounds are very cool... Are they all generated with sound2tia ? The pitfall ones for instance went through your tool or from disassembly of the game ?
  21. It's definitely not RAM limited. With a 16k RAM bank on a 7800, you can use a 128x128 map, which is quite big (a screen is 20 tiles wide and 14 tiles high). And bigger if you're using ROM... It's really not tough to do, as MARIA was designed to do it rather easily, but you have to accept to tile in 160A/320A mode, limit the size of the display, or use background in some areas to spare some CPU/DMA...
  22. 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...
  23. Waouh, thank you for the explanation. I had been reading and rereading the 7800 software guide and the Maria chip reference, and now it suddenly seems clear to me. Very clever color coding indeed. I had never figured out why this mode actually existed...
  24. 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
  25. 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
×
×
  • Create New...