BIFROST* Advanced Programming - part 1

edited August 2012 in Development
In BIFROST*, it's much easier to just make changes in the tile map and let the engine automatically draw & animate everything in multicolor for you. Even so, sometimes experienced developers may decide to access BIFROST* internal routines directly, for the reasons explained in section "Advanced Programming" from the BIFROST* documentation. In order to help this process, I decided to demonstrate how to better use these routines by providing a few examples. Notice however that I will assume you already read the BIFROST* documentation first, so I won't waste time repeating everything again here.

The first demo (posted below) demonstrates how to move a set of multicolor tiles in hi-res coordinates. To avoid visual glitches, it's important for this kind of program to perform these tasks within a single frame:
  • Erase all moving tiles;
  • Adjust their coordinates;
  • Draw them all again.
I implemented this demo using z88dk, although a Boriel's ZX BASIC version would work exactly the same way. Perhaps someone would be willing to translate it to ZX BASIC and post it here?
/* ----------------------------------------------------------------
 * BIFROST* ENGINE HI-DEMO #1
 *
 * Requires "BIFROSTENGINEV1.2H_z88dkInterface.zip" and z88dk

 * This program can be compiled as follows:
 *
 * zcc.exe +zx -lm -lndos -lbifrost -create-app -O2 bifrost_h1.c -obifrost_h1.bin
 *
 * After compiling it, use the following loader to execute:
 *
 * 10 CLEAR VAL "32767"
 * 20 LOAD "TILES"CODE
 * 30 LOAD "BIFROST*"CODE
 * 40 LOAD "bifrost_h1"CODE
 * 50 RANDOMIZE USR VAL "32768"
 *
 * Original version and further information is available at
 * http://www.worldofspectrum.org/infoseekid.cgi?id=0027405
 * ----------------------------------------------------------------
 */

#include <stdio.h>
#include <stdlib.h>
#include <input.h>

#include "bifrost.h"

#define printMode(mode)      printf("\x01%c", mode)
#define printCls()           printf("\x0c")
#define printInk(k)          printf("\x10%c", '0'+k)
#define printPaper(k)        printf("\x11%c", '0'+k)
#define printAt(row, col)    printf("\x16%c%c", ' '+row, ' '+2*col)
#define printAt64c(row, col) printf("\x16%c%c", ' '+row, ' '+col)

#define INSIDE_COLOR      0
#define OUTSIDE_COLOR     7
#define NUM_TILES         3

extern unsigned char lastK(23560);      // last pressed key

unsigned char tile[NUM_TILES];          // tile number
int lin[NUM_TILES];                     // hi-res vertical tile position
int col[NUM_TILES];                     // hi-res horizontal tile position
int dlin[NUM_TILES];                    // hi-res vertical tile movement
int dcol[NUM_TILES];                    // hi-res horizontal tile movement
int px, py;                             // "abstract" coordinates
int f;                                  // generic counter
int frames;

main()
{
    // initialize variables
    randomize();
    for (f = 0; f < NUM_TILES; ++f) {
        tile[f] = 8 + rand()%18;
        lin[f] = rand()%161;
        col[f] = rand()%19;
        dlin[f] = (rand()%2)*2 - 1;
        dcol[f] = (rand()%3) - 1;
    }
    frames = 1;

    // clear bitmap screen
    printMode(32);
    zx_border(OUTSIDE_COLOR);
    printPaper(OUTSIDE_COLOR);
    printInk(OUTSIDE_COLOR);
    printCls();

    // show messages
    printInk(7-OUTSIDE_COLOR);
    printAt(20, 1);
    printf("Delay: %d frame(s)", frames);
    printAt(21, 1);
    printf("Use O/P to change");

    // disable entire tile map
    for (f = 0; f < 81; ++f) {
        BIFROST_tilemap[f] = BIFROST_DISABLED;
    }

    // clear multicolor attributes (no glitches since BIFROST* is still disabled)
    for (px = 0; px < 9; ++px) {
        for (py = 0; py < 9; ++py) {
            BIFROST_fillTileAttrH((px+1)<<4, (py<<1)+1, INSIDE_COLOR*9);
        }
    }

    // start
    BIFROST_start();

    while(1) {

        // process input keys
        if (lastK != 0) {
            if (lastK == 'o' && frames > 1) {
                --frames;
            }
            if (lastK == 'p' && frames < 9) {
                ++frames;
            }
            lastK = 0;
            printAt(20, 8);
            printf("%d", frames);
        }

        // bounce tiles
        for (f = 0; f < NUM_TILES; ++f) {
            // bounce above/below
            if (lin[f]+dlin[f] < 0 || lin[f]+dlin[f] > 160) {
                dlin[f] = -dlin[f];
            }

            // bounce on the sides
            if (col[f]+dcol[f] < 0 || col[f]+dcol[f] > 18) {
                dcol[f] = -dcol[f];
            }
        }

        // delay
        for (f = 0; f < frames; ++f) {
          // frame sync
          asm("halt");
        }

        // erase tiles
        for (f = 0; f < NUM_TILES; ++f) {
            BIFROST_fillTileAttrH(lin[f], col[f], INSIDE_COLOR*9);
        }

        // move and draw tiles
        for (f = 0; f < NUM_TILES; ++f) {
            lin[f] += dlin[f];
            col[f] += dcol[f];
            BIFROST_drawTileH(lin[f], col[f], tile[f]);
        }
    }
}

The compiled version of this demo is available here.

Notice that in a C program there's barely enough time within a frame (with roughly 20K T-states available) to move 3 tiles. If the demo was implemented in Assembly, there would be enough time for 4 tiles, perhaps 5.

Another alternative would be increasing the total number of tiles, but only moving 3 of them per frame. However this would not work so well since a moving frame would erase other frames behind, that would not be redrawn immediately.

I believe it's now clear enough how this program above works. I'm available to answer any questions about it, except for questions already answered in the documentation.

Next week I will demonstrate how to use the "built-in sprite support" provided by BIFROST*, so you will be able to do something similar but with animated tiles in the background... Stay tuned!
Post edited by Einar Saukas on
Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.

Comments

  • edited July 2012
    Impressive. I know I can use this for something... I know I must :)
  • edited July 2012
    na_th_an wrote: »
    Impressive.

    Thanks!
    na_th_an wrote: »
    I know I can use this for something... I know I must :)

    I'll be looking forward to this :)
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited July 2012
    Next week I will demonstrate how to use the "built-in sprite support" provided by BIFROST*, so you will be able to do something similar but with animated tiles in the background... Stay tuned!

    Looking forward to it. Fantastic stuff. This is just what I need for Hippy Space Invaders. 8-)
  • edited July 2012
    Hmmm, I think I might just take a look at this in Z80, looks like it could be fun...

    :)
    So far, so meh :)
  • edited July 2012
    That is so impressive. Look forward to the pure assembler demo
  • edited July 2012
    tstih wrote: »
    Looking forward to it. Fantastic stuff. This is just what I need for Hippy Space Invaders. 8-)

    This is actually a very good idea!

    Are you planning to implement it yourself or just suggesting this idea for others to implement?

    Either way, I'm willing to prepare an initial version to show the best way to use BIFROST* for this, if someone can commit to do all the hard work afterwards (tile images, graphics and remaining logic).
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited July 2012
    BTW while you guys wait for the sprite tile demo I promised, I posted another demo here.
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited July 2012
    Just wanted to say that it looks very interesting. Although I'm not sure how I will use this yet.

    I must say that I didn't read this in the documentation but perhaps it was an somewhat older version.

    Also, making 4 colour graphics is harder than monochrome. :)

    EDIT: I will look at the other thread later. I'm very busy this week.
  • edited August 2012
    I recently provided a brief explanation about BIFROST*'s "sprite tiles" in this thread, where I wrote this:
    Using "sprite tiles" your program won't have to draw 2 tiles anymore, thus leaving more time available for your program to perform other tasks.
    I think it's worthwhile to provide a practical example for this sentence. In the first demo, the program had to perform all these tasks within a single frame:
    • Erase 3 tiles
    • Update coordinates for 3 tiles
    • Redraw 3 tiles
    However, if "sprite tiles" are used for drawing 2 of these sprites, the program will only need to perform the following tasks:
    • Erase 1 tile + 2 sprite tiles
    • Update coordinates for 1 tile + 2 sprite tiles
    • Redraw 1 tile
    Since redrawing both sprite tiles is performed automatically by the engine, the program will save enough time to "manually" process (erase, update and redraw) one more sprite!

    So here's the optimized version (now moving 4 tiles instead of 3):
    /* ----------------------------------------------------------------
     * BIFROST* ENGINE HI-DEMO #1 (optimized)
     *
     * Requires "BIFROSTENGINEV1.2H_z88dkInterface.zip" and z88dk
     *
     * This program can be compiled as follows:
     *
     * zcc.exe +zx -lm -lndos -lbifrost -create-app -O2 bifrost_h1.c -obifrost_h1.bin
     *
     * After compiling it, use the following loader to execute:
     *
     * 10 CLEAR VAL "32767"
     * 20 LOAD "TILES"CODE
     * 30 LOAD "BIFROST*"CODE
     * 40 LOAD "bifrost_h1"CODE
     * 50 RANDOMIZE USR VAL "32768"
     *
     * Original version and further information is available at
     * http://www.worldofspectrum.org/infoseekid.cgi?id=0027405
     * ----------------------------------------------------------------
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <input.h>
    
    #include "bifrost.h"
    
    #define printMode(mode)      printf("\x01%c", mode)
    #define printCls()           printf("\x0c")
    #define printInk(k)          printf("\x10%c", '0'+k)
    #define printPaper(k)        printf("\x11%c", '0'+k)
    #define printAt(row, col)    printf("\x16%c%c", ' '+row, ' '+2*col)
    #define printAt64c(row, col) printf("\x16%c%c", ' '+row, ' '+col)
    
    #define INSIDE_COLOR      0
    #define OUTSIDE_COLOR     7
    #define NUM_TILES         4
    
    extern unsigned char lastK(23560);      // last pressed key
    
    unsigned char tile[NUM_TILES];          // tile number
    int lin[NUM_TILES];                     // hi-res vertical tile position
    int col[NUM_TILES];                     // hi-res horizontal tile position
    int dlin[NUM_TILES];                    // hi-res vertical tile movement
    int dcol[NUM_TILES];                    // hi-res horizontal tile movement
    int px, py;                             // "abstract" coordinates
    int f;                                  // generic counter
    int frames;
    
    main()
    {
        // initialize variables
        randomize();
        for (f = 0; f < NUM_TILES; ++f) {
            tile[f] = 8 + rand()%18;
            lin[f] = rand()%161;
            col[f] = rand()%19;
            dlin[f] = (rand()%2)*2 - 1;
            dcol[f] = (rand()%3) - 1;
        }
        frames = 1;
    
        // clear bitmap screen
        printMode(32);
        zx_border(OUTSIDE_COLOR);
        printPaper(OUTSIDE_COLOR);
        printInk(OUTSIDE_COLOR);
        printCls();
    
        // show messages
        printInk(7-OUTSIDE_COLOR);
        printAt(20, 1);
        printf("Delay: %d frame(s)", frames);
        printAt(21, 1);
        printf("Use O/P to change");
    
        // disable entire tile map
        for (f = 0; f < 81; ++f) {
            BIFROST_tilemap[f] = BIFROST_DISABLED;
        }
    
        // clear multicolor attributes (no glitches since BIFROST* is still disabled)
        for (px = 0; px < 9; ++px) {
            for (py = 0; py < 9; ++py) {
                BIFROST_fillTileAttrH(PX2LIN(px), PY2COL(py), INSIDE_COLOR*9);
            }
        }
    
        // start
        BIFROST_start();
        BIFROST_enableSprites();
    
        while(1) {
    
            // process input keys
            if (lastK != 0) {
                if (lastK == 'o' && frames > 1) {
                    --frames;
                }
                if (lastK == 'p' && frames < 9) {
                    ++frames;
                }
                lastK = 0;
                printAt(20, 8);
                printf("%d", frames);
            }
    
            // bounce tiles
            for (f = 0; f < NUM_TILES; ++f) {
                // bounce above/below
                if (lin[f]+dlin[f] < 0 || lin[f]+dlin[f] > 160) {
                    dlin[f] = -dlin[f];
                }
    
                // bounce on the sides
                if (col[f]+dcol[f] < 0 || col[f]+dcol[f] > 18) {
                    dcol[f] = -dcol[f];
                }
            }
    
            // delay
            for (f = 0; f < frames; ++f) {
              // frame sync
              asm("halt");
            }
    
            // erase tiles
            for (f = 0; f < NUM_TILES; ++f) {
                BIFROST_fillTileAttrH(lin[f], col[f], INSIDE_COLOR*9);
            }
    
            // move and draw tiles
            for (f = 0; f < NUM_TILES; ++f) {
                lin[f] += dlin[f];
                col[f] += dcol[f];
                switch (f) {
                    case 0:
                        *BIFROSTSPRITE1LIN = lin[f];
                        *BIFROSTSPRITE1COL = col[f];
                        *BIFROSTSPRITE1TILE = tile[f];
                        break;
                    case 1:
                        *BIFROSTSPRITE2LIN = lin[f];
                        *BIFROSTSPRITE2COL = col[f];
                        *BIFROSTSPRITE2TILE = tile[f];
                        break;
                    default:
                        BIFROST_drawTileH(lin[f], col[f], tile[f]);
                }
            }
        }
    }
    
    The compiled version of this demo is available here.

    Notice the only differences (in comparison to the original demo at the beginning of this current thread) are that it now enable sprites at the beginning, and it only updates sprite coordinates for the first 2 tiles instead of drawing them at the end. That's all!
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
Sign In or Register to comment.