Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by svenvandevelde

  1. Nope. Pure C code ... https://github.com/FlightControl-User/X16_Code/blob/main/cx16-equinoxe/equinoxe-flightengine.c
  2. True. I might do a combination of things. A larger tile in the background, and then an animation of the gun on the top of it ... But it will take time for me to program it, and also draw the tiles and the sprites. But I guess this is part of the fun, isn't it?
  3. Well, it can be improved. The tile algorithm is generating for a whole tile row in a new random sequence (building further upon the previous drawn tile sequence), every 32 scan lines Walls need to fit with walls and open space can be closed with walls or can stay open, etc, etc ... Facinating stuff to learn and to code... The algorithm takes quite some CPU actually at the moment, because a "row" is actually a combination of "logical tiles", which are a combination of 4 * 4 * 4 vera tiles. The layer on which the background is drawn, is actually in 16x16 tile mode in 4bpp. So per tile, there is a tile segment, which is 4 * 4 tiles, which makes a 32x32 tile. A whole "tile" is actually then a combination of 4 tile segments. And it may not show, but the algorithm works with about 60 background tile "bitmaps", and the combinations of those create about 56 tile variations of 4x32x32 width and height. You notice that there is sometimes a white "flash" in the border, which is the drawing of the row. It draws a complete row of 2x32x32 tiles (so half of a complete tile), and on top it draws the row the full length, which is 64 vera characters. This will alllow me to design also a panning to the left and right, depending on the player steering. So the enemies can get out of screen focus for a while if the player flies left or right. I really need to do some code crunching now =-). If I can tweak out a couple of cycles, that gives room for more effects or other weapons etc. Also plan to have "towers" or ground installations in the background, that would fire also or would defend together with the player the incoming enemies. Those would be sprites that would be layered on top of the floor plan, but under the enemies and the sprites. And those would fire weapons, or have some sort of magnetic fields or something like that. The imagination only limits the ideas. It is a lot of fun to make this. Sven
  4. Indeed! Spot on! It is a real challenge for the CX16 to do that! I've seen a couple of demos where people float some sprites around but without a lot of change or processing behind. When you build a logic that changes the sprites per item individually with their own logic, that is a completely different story! I need to work on my scrolling engine. By the way, the tiles are placed with a randomization algorithm, no game has the same tile sequence :-).
  5. I would like to wish the new management team the best of luck with this forum. I hope that David is still committed to the project. I am honestly worried. David needs to be more careful with his marketing. I hope he will show a sign of life on the forum.

  6. I see here a great community and demonstration of leadership with this management initiative. It was initially this site that attracted me to the CX16. Although facebook is a great tool for marketing purposes and analytics, it are forums like this that build the core competence and knowledge sharing. Running a forum is a separate skill and requires indeed a good management team. It are the people behind the acronyms that make retro computing on the CX16 a living thing. So good luck to all of you, and thank you. Sven
  7. Thank you all for the massive feedback. Some real experienced people here I will need to do some more code crunching to optimize my logic to get more objects processed. Thank you for your suggestion to count in cycles. Indeed, the instructions used to build a logic varies game per game. One big lesson that I learn out of all of this is that although the x16 has 8mhz it is still a machine with limitations. The prospect of managing 128 sprites in a fluid process really requires a thoughtful design and a coding approach that requires a lot of experience. On top of that the background scrolling logic, collision detection, AI behaviour, player control, scoring, event management adds complexity. Beside the logic to control the sprites, I'm the most afraid of the complexity of the collision detection. The more objects you need to control in the flow, the longer it will take to identify which objects are colliding. Even taking into account that the vera can help with the collision detection events for the specified sprites displayed on the screen. I really would like to design this game using c language. My biggest challenge is how I can use absolute indexed addressing in the 6502. I mean, in a complex game flow the objects and memory locations move around and invite for the usage of pointers. Which results in indirect indexed addressing. I could write a game engine using absolute indexed addressing, but then I would need to copy memory areas around and avoid pointers where I can, which also adds CPU time. Or I would need to follow the route of self modifying code, but this one I really would like to avoid. Not so sure what is the right way to go. I guess the solution will be in the middle. And that makes it fun, to design a software that is portable, fast and designed for speed where needed. Another boundary I'm about to hit is the code size...
  8. Of course it is. My intention is indeed to understand with some science behind, how many objects would be able to be updated per frame, or in my terms a complete horizontal vera display scan. I'm not intending to update the object per vera scan line operation :-).
  9. Hi all, I want to validate a calculation on how much instructions are processed by the 6502, every time the vera scans a display line. Let's assume that the average instruction processing time is 5 cycles. (It might be an overestimation, depending on the type of instructions used). But let us imagine 5, as there is a program that uses a lot of indirect indexed addressing, like LDA ($ZP),Y ... I've understood that the CX16 works at 8Mhz, so translating this, that would be around 8.000.000 cycles per second, correct? The vera display is configured to operate at 640x480 resolution, and no scaling. Let's assume that the vera scans the display 60 times per second. So the average amount of instructions that would be processed by the 6502 would be 8.000.000 cycles per second / 5 cycles per instruction, which would be 1.600.000 instructions / second. The vera scans the complete display 60 times per second, so that would mean that 1.6000.000 instructions per second / 60 vera scans per second would result in 26.666 instructions per single vera display scanning operation. However, there are 480 vera scan lines, so 26.666 instructions per vera display scan / 480 vera scan line would result in around 55 instructions per scan line per 60 times a second. So if we need to control about 40 objects, that would mean that for each object, there would be about 1,375 instructions per scan line available. Is the above calculation correct or is it way off? And what other variants or parameters come into play? Please see the below, it is the reason of my asking ... The white border measures the processing from the start of scanning till the end of scanning. Each object on the display is processing movement logic, behaviour logic, draw logic ... I was surprised that the machine takes time to process all this logic. Which makes it excitiing, because it allows to build a relatively more complex game, but still there are boundaries as a result of the machine configuration. kind regards, Sven
  10. I have the latest updates moved to my cx16 coding area on github. Moving the heap manager out of the kickc compiler did the moment but as a side compile. https://github.com/FlightControl-User/X16_Code/blob/main/cx16_lib/cx16-heap.c An open work item is to be able to configure that allocated blocks are not crossing $a000 - $buff bank boundaries. This is important for data structures. The data elements in the structure must be stored in sequential memory space. For bitmap data such boundary crossing would not be a problem though. For example, if at address $A008 a block would be allocated of 16 bytes, then this would require the remaining 8 to be registered as idle, and the 16 bytes to be allocated from $C000 downwards, with base $BFF0.
  11. Which veralib are you referring to? Can you pls show url?
  12. Amazing ... for testing I can indeed use the above methods, but for production i think it is better to have it really nicely implemented separately. Anyway, the future will be the version 39, right? So that means that in the future the $00 and $01 will switch the banks, which I really like by the way! Note that the heap manager switches banks all the time, as there are no pointers but handles. To refer to a memory block, the function heap_data_ptr is used, which receives the handle. So based on the bank information embedded in the handle, the index block is retrieved, and the data handle is unpacked to set the correct bank and return the 16 bit data pointer, pointing to the previously allocated memory block start position in BRAM (between 0xA000 and 0xBFFF). enemy_handle = heap_alloc(HEAP_SEGMENT_BRAM_ENTITIES, sizeof(Enemy)); Enemy* enemy = (Enemy*)heap_data_ptr(enemy_handle); memset(enemy, 0, sizeof(Enemy)); enemy->health = 1; enemy->x = x; enemy->y = y; enemy->sprite_type = &SpriteEnemy01; enemy->sprite_offset = NextOffset();
  13. How can I identify the ROM version in C? Which adres to peek? I have a feeling I'm going to learn something new ...
  14. Thank you. So i need to make 2 version of the heap memory manager.
  15. Thank you. And does the rom 39 use zeropage 00 and 01 to address the ram and rom banks respectively? Or are these still done through the VIA addressing?
  16. Thanks. Just to make sure, I tried the box16 version 39.2.3. with the Kyoto version 38 rom.bin. But maybe this is not the way as it is not working ... When I read that the rom.bin needs to be build, how can I build the rom of the version 39 of the rom is not released yet? I mean, where is the source code of version 39 of the rom? How to obtain this source code? Is this the master branch of the rom on github?
  17. ah! lovely. So i can just create those macros and move on with the conversion. I"ve noticed that the cc65 does not have a memory manager for the cx16 banked ram. If you're interested @Greg King, I can explain you what this memory manager does and maybe you can embed it into the cc65? It allows to dynamically allocate memory blocks in banked ram and in vera ram. I plan to have it "portable" to other compilers and later also other machines. It works now using the kickc compiler of @Jesper Gravgaard, which is one of the more advanced compilers i've seen in my life :-). So to have the memory manager code as generic as possible, and to be able to create variations based on machine architecture, but following more or less the same syntax. The idea is that the memory manager provides and works with "handles", not pure pointers to a location in memory. These handles are actually "packed" pointers, containing both bank and memory location information in a 16 bit value. This avoids having to "deal" with 24 bits or even worse, unsigned long values like 32 bits, which are very time consuming and costly on a 6502 architecture. Of course, moving a complete 16 bit pointer into a 16 handle, including the extra 8 bits for the bank information is not possible. The idea is that with a balanced choice, information is lost (bitshifts occur) in the handle, that can easily be converted to a bank/pointer combination. In the case of the cx16, that would be that a memory block is aligned to 8 bytes in memory, and a segment on the heap can only reach to about 63 banks, which is the default memory size). As a bank has only 0x2000 bytes. The below is what i mean, and if you look carefully, you see this BYTE1() method in the code, which is the reason of my question :-). inline void heap_bram_bank_set(heap_bank bank) { cx16_bram_bank_set(bank); } inline heap_bank heap_bram_bank_get() { return cx16_bram_bank_get(); } /** * @brief Create a packed banked ram pointer from an 8 bit bank and 16 bit pointer, which is compressed in a 2 byte word. * The returned address is aligned to 8 bytes in bram, and holds the pointer in 64 banks. * So remember the loss of precision: * - The 8 bit bank is compressed to 6 bits. The 2 upper bits of the bank are discarded. * - The 13 bit pointer between 0xA000 and 0xBFFF is compressed to a 10 bit pointer! * * @param bank The 8 bit bank. * @param ptr The 16 bit pointer in banked ram between 0xA000 and 0xBFFF. * @return heap_bram_packed */ inline heap_bram_packed heap_bram_pack(heap_bank bank, heap_ptr ptr) { return (heap_bram_packed)bank << 10 + ((heap_bram_packed)(ptr-0xA000) >> 3); } /** * @brief Return from a packed banked ram pointer the embedded bank in an 8-bit byte. * * @param bram_packed The packed banked ram pointer. * @return heap_bank The 8 bit bank. */ inline heap_bank heap_bram_unpack_bank(heap_bram_packed bram_packed) { return (heap_bank)BYTE1(bram_packed) >> 2; } /** * @brief Return from a packed banked ram pointer the 16 bit address, but is now aligned to 8 bytes in memory space! (due to the loss of precision). * * @param bram_packed The packed banked ram pointer. * @return heap_ptr The 16 bit pointer, pointing to a location in banked ram between 0xA000 and 0xBFFF. */ inline heap_ptr heap_bram_unpack_ptr(heap_bram_packed bram_packed) { return (heap_ptr)((bram_packed << 3 ) & 0x1FFF ) + 0xA000; } /** * @brief Create a packed vera ram pointer from an 8 bit bank and 16 bit offset, which is compressed in a 2 byte word. * The returned address is aligned to 2 bytes in vram in 2 banks. * So remember the loss of precision: * - The 1 bit bank is stored in bit 16 of the packed address. * - The 16 bit offset from 0x0000 till 0xFFFF is 1x shifted to the right to a 15 bit offset, aligning to 2 bytes in vram. * * @param bank The 1 bit bank in vram. * @param offset The 16 bit offset in vram. * @return heap_vram_packed The packed vera ram address. */ inline heap_vram_packed heap_vram_pack(heap_bank bank, heap_offset offset) { return (heap_vram_packed)bank << 13 + (heap_vram_packed)(offset) >> 3; } /** * @brief Return from a packed vera ram address the embedded bank in an 8-bit byte. * * @param vram_packed The packed vera ram address. * @return heap_bank The 8 bit bank. */ inline heap_bank heap_vram_unpack_bank(heap_vram_packed vram_packed) { return (heap_bank)BYTE1(vram_packed) >> 5; } /** * @brief Return from a packed vera ram address the 16 bit offset, but is now aligned to 2 bytes in vera ram! (due to the loss of precision). * * @param bram_packed The packed vera ram address. * @return heap_offset The 16 bit offset, pointing to a location in vera ram between 0x0000 and 0xFFFF in a bank. */ inline heap_offset heap_vram_unpack_offset(heap_vram_packed vram_packed) { return (heap_offset)vram_packed << 3; } /** * @brief * * @param size * @return heap_size_packed */ inline heap_size_packed heap_size_pack(heap_size_large size) { return (heap_size_packed)(size >> 3); } /** * @brief * * @param size_packed * @return heap_size */ inline heap_size heap_size_unpack(heap_size_packed size_packed) { return (heap_size)size_packed << 3; } /** * Get header pointer and bank in bram. */ inline heap_index_ptr heap_get(heap_handle handle) { byte bank = heap_bram_unpack_bank(handle); heap_bram_bank_set(bank); return (heap_index_ptr)heap_bram_unpack_ptr(handle); } /** * Get data handle packed without resetting the banks. */ inline heap_handle heap_data_packed_get(heap_handle heapIndex) { // printf("handle = %x\n", handle); return heap_get(heapIndex)->data; } /** * Set data handle packed without resetting the banks. */ inline void heap_data_packed_set(heap_handle heapIndex, heap_handle data) { heap_get(heapIndex)->data = data; } /** * Get data handle. */ heap_handle heap_data_get(heap_handle heapIndex) { heap_bank old_bank = heap_bram_bank_get(); heap_handle data_handle = heap_data_packed_get(heapIndex); heap_bram_bank_set(old_bank); return data_handle; } /** * Get data header pointer and bank or prepare vera registers. */ heap_bank heap_data_bank(heap_handle handle) { heap_bank old_bank = heap_bram_bank_get(); // printf("handle = %x\n", handle); heap_handle data_handle = heap_data_packed_get(handle); heap_index_info header_info = heap_get(handle)->size; header_info &= heap_type_mask; heap_bram_bank_set(old_bank); // Data blocks in bram or in vram are handled differently. // We only need to bank if the data is in bram, otherwise the programmer will handle it. //printf("heap_data_bank: header info = %x\n", header_info); if(header_info == heap_type_bram) { // return the bank of the data handle, which is expressed in bram format. return heap_bram_unpack_bank(data_handle); } else { // return the bank of the data handle, which is expressed in vram format. return heap_vram_unpack_bank(data_handle); } } /** * Get data header pointer and bank or prepare vera registers. */ heap_ptr heap_data_ptr(heap_handle handle) { heap_handle data_handle = heap_data_packed_get(handle); heap_index_info header_info = heap_get(handle)->size; header_info &= heap_type_mask; // Data blocks in bram or in vram are handled differently. // We only need to bank if the data is in bram, otherwise the programmer will handle it. if(header_info == heap_type_bram) { // heap_bram_bank_set((heap_bank)(>data_handle) >> 2); byte bank = heap_bram_unpack_bank(data_handle); heap_bram_bank_set(bank); return (heap_ptr)heap_bram_unpack_ptr(data_handle); } else { return (heap_ptr)heap_vram_unpack_offset(data_handle); } } There are some significant incompatibilities between the kickc and the cc65 compiler, but i hope to overcome these using #ifdef, #ifndef and #pragma keywords.
  18. I've installed the box16 on my pc, and downloaded the rom.bin and installed it at the root dir of the box16. But when i start the emulator, i get a blank white screen. Does somebody know what may be the issue?
  19. Thanks. But wouldn't the compiler make this resulting code highly inefficient? (This is my biggest worry actually).
  20. I've been using the kickc compiler of Jesper Gravgaard for a while now to model my heap manager. Now i'm looking into cc65 to see if i can port the cx16-heap.c towards a cc65 compatible format, so that it compiles both on the kickc and the cc65. But I'm facing a problem. The unary operators < and > allow to obtain the lower and higher byte or word from an unsigned int or unsigned long respectively. Example what compiles in kickc: unsigned int address = 0x1234; unsigned char lo = <address; // returns 0x34 unsigned char hi = >address; // returns 0x12; printf("lo = %x, hi = %x", lo, hi); The output would be: lo = 34, hi = 12. Jesper has been adaptiing the syntax of his KICKC compiler, introducing BYTEx() syntax: unsigned int address = 0x1234; unsigned char lo = BYTE1(address); // returns 0x34 unsigned char hi = BYTE2(address); // returns 0x12; printf("lo = %x, hi = %x", lo, hi); So the above is now also working, and allows a higher compatibility for other ansi compilers by defining macros. My question is, how can I get a similar result done using the cc65 compiler? I've been searching and searching the internet but cannot find such a solution. Are there other methods on how this can be achieved? I know there are pseudo functions using the ca65 assembler like .LOBYTE and .HIBYTE but there I should write assembly language. How can a macro be defined in C using cc65 that does the similar like in kickc BYTEx() macros? Sven
  21. Merry Christmas. Beautiful pictures in Sweden @martinot
  22. Merry Christmas everyone from Belgium. And indeed, X16 is very much alive. Enjoy a peaceful end year.
  • Create New...

Important Information

Please review our Terms of Use