Page 1 of 2

Using bitmap mode from C

Posted: Wed Oct 25, 2023 5:37 pm
by rorodev
I am searching for an example to understand better how I use the bitmap mode in C (or maybe Assembler).
Ultimately, I'm wondering how to initialize this VERA mode and how I set after a pixel in a specific color.
It would be great if someone could give me a headstart. Thanks a lot.

Re: Using bitmap mode from C

Posted: Wed Oct 25, 2023 6:17 pm
by desertfish
The easiest way is probably to use the kernal routines for drawing pixels.

There's the GRAPH routines, and the FB (framebuffer) routines. They can plot pixels, clear the screen, draw lines and other simple shapes. look here https://github.com/X16Community/x16-doc ... r-3-kernal

Unfortunately they only work on the 320*240*256c bitmap mode. So if you want another screen mode, you have to write your own equivalents. Otherwise, use the screen_mode routine to set screen mode 128.

I don't know how to call these routines from C (surely cc65 has some kernal stubs for them?) but here's a snippet of Prog8 code that draws three pixels red,green and blue on a black screen. It should be easily convertable to C or assembler

cx16.screen_mode(128, false) ; lores 256 colors bitmap (+ text) cx16.GRAPH_set_colors(1,2,0) cx16.GRAPH_clear() cx16.FB_cursor_position(100, 100) cx16.FB_set_pixel(2) cx16.FB_cursor_position(105, 100) cx16.FB_set_pixel(5) cx16.FB_cursor_position(110, 100) cx16.FB_set_pixel(6)

... or do you really want to know how to set the pixels in your own code without using the kernal?

Re: Using bitmap mode from C

Posted: Wed Oct 25, 2023 8:18 pm
by rorodev
I have found out in the meantime how the bitmap mode works using VERA mode, but it's probably not the right way
because, as far as I understand, I can send the data in sequential order, which is not the right approach for what I want to do.
Not yet sure ...

Thanks for pointing me to the possibility of using kernal functions. Awesome.
Since most of the examples I found so far are using VERA mode calls, I have not looked in any other direction ;)
That would be precisely what I'm searching for.
I have not found any stubs so far in cc65 for the kernal functions, but maybe someone has already worked on this?

But thanks a lot again. Like this, I know where to start with a different approach.

As a background:
I want to port a flight simulator I have written in C to the X16 and see how far I get with it while I wait for my hardware :)

Re: Using bitmap mode from C

Posted: Wed Oct 25, 2023 9:13 pm
by Microdriver
Funny, i just also wanted to post the question "How do you do PLOT and DRAW (or LINE) in C (CC65) ?" Because i wanted to try to translate the "FIREWORKS"-example, that was just posted, from BASIC to C.

I found this tutorial (also from the forum postings) :

https://github.com/mwiedmann/cx16Coding ... itmap-Mode

There are some examples, that seem to do plotting, but I still don't get, how it's done.
A simplified description would be appreciated. Probably, you can just POKE into some addresses somewhere. But where, and how exactly? That is, what do you have to prepare for it?
In the end, I'd just like to have some result like this:

Code: Select all

void plot(int x, int y, int color) { .... }

Re: Using bitmap mode from C

Posted: Thu Oct 26, 2023 5:09 am
by TomXP411
There has been a lot of talk over the years about using VERA; it's pretty well documented and thoroughly discussed.

Here's the VERA manual
https://github.com/X16Community/x16-doc ... ference.md

Short version is that VERA uses vectors to write to video memory. You set the 3-byte address by setting 3 port values. (Part of the address is the increment value, which you use if you are writing horizontal or vertical lines.)

Then you write to the data port. Whatever you write to the data port gets written to video memory. If you have set an offset, then you can just keep writing data values to the data port, and the address in the address register will increment based on the set value.

Address $9F20 is the low 8 bits of the Video memory address.
$9F21 is the upper 8 bits
$9F22 is the address increment (upper 4 bits), DECR flag (bit 3), and the low bit is the bank number (0 or 1).

Also, bit 0 of $9F25 tells VERA which data port to use (0 or 1). This allows you to quickly flip between two different data addresses, useful for bulk copies or movement of data.

So to write to address $1234, you would:
Set $9F25 to 0 (select data port 0)
Set $9F20 to $34
Set $9F21 to $12
Set $9F22 to 0


Then write data to $9F23.

This will set a single character cell or graphic pixel. If you are writing a horizontal line, setting an increment of 1 (set $9F22 to $1x) will automatically set the address to the next pixel, so you can just keep writing data until you need to change rows.

Re: Using bitmap mode from C

Posted: Thu Oct 26, 2023 7:37 am
by rorodev
Thank you very much for your answer. I understand the concept now, and I am figuring out how to implement
what I want to see if it's performant enough. My first try is by using vpoke() now, and it's pretty promising, but I will see.
Maybe also a good time to consider (re-)learning 6502 assembler ;)

Thanks again for the feedback!

Re: Using bitmap mode from C

Posted: Thu Oct 26, 2023 8:40 am
by Microdriver
TomXP411 wrote: Thu Oct 26, 2023 5:09 amThere has been a lot of talk over the years about using VERA; it's pretty well documented and thoroughly discussed.
Where? I couldn't find good code examples yet.
TomXP411 wrote: Thu Oct 26, 2023 5:09 amHere's the VERA manual
https://github.com/X16Community/x16-doc ... ference.md
What good is a reference, if it's more or less incomprehensable without background knowledge? Still no code examples there either, especially not in C.
TomXP411 wrote: Thu Oct 26, 2023 5:09 amShort version is that VERA uses vectors to write to video memory. You set the 3-byte address by setting 3 port values. (Part of the address is the increment value, which you use if you are writing horizontal or vertical lines.)
Then you write to the data port. Whatever you write to the data port gets written to video memory. If you have set an offset, then you can just keep writing data values to the data port, and the address in the address register will increment based on the set value.

Address $9F20 is the low 8 bits of the Video memory address.
$9F21 is the upper 8 bits
$9F22 is the address increment (upper 4 bits), DECR flag (bit 3), and the low bit is the bank number (0 or 1).

Also, bit 0 of $9F25 tells VERA which data port to use (0 or 1). This allows you to quickly flip between two different data addresses, useful for bulk copies or movement of data.

So to write to address $1234, you would:
Set $9F25 to 0 (select data port 0)
Set $9F20 to $34
Set $9F21 to $12
Set $9F22 to 0

Then write data to $9F23.

This will set a single character cell or graphic pixel. If you are writing a horizontal line, setting an increment of 1 (set $9F22 to $1x) will automatically set the address to the next pixel, so you can just keep writing data until you need to change rows.
Well, let's try that:

Code: Select all

#include <cx16.h>
#include <peekpoke.h>
/* nope.c */
int main() {
    int i;
    POKE(0x9F25, 0);
    POKE(0x9F20, 0x34);
    POKE(0x9F21, 0x12);
    for (i = 0; i < 1000; i++) {
        POKE(0x9F22, i);
        POKE(0x9F23, i);
    }
    return 0;
}
Nope, doesn't do anything ...

This somehow does something though:

Code: Select all

#include <cx16.h>

/* plotsomehow.c */

/* compilation: 
export CC65_HOME=/usr/share/cc65
cl65 -O -o PL.PRG -t cx16 plotsomehow.c */

#define BLACK 0
#define WHITE 85
#define RED 170
#define CYAN 255

#define WIDTH_BYTES_640_2BPP 640 / 4 // 160

void main() {
    unsigned long tileBaseAddr = 0;
    unsigned short x, y;

    VERA.layer1.config   = 0b00000101;
    VERA.layer1.tilebase = tileBaseAddr >> 9 | 0b1;
    VERA.address         = tileBaseAddr;
    VERA.address_hi      = tileBaseAddr >> 16;
    VERA.address_hi     |= 0b10000;

    for (y=0; y < 480; y++) {
        for (x = 0; x < WIDTH_BYTES_640_2BPP; x++) {
            if (x < 20) {
                VERA.data0 = BLACK;
            }
            if (x >= 20 && x < 40) {
                VERA.data0 = WHITE;
            }
            if (x >= 40 && x < 60) {
                VERA.data0 = RED;
            }
            if (x >= 60 && x < 80) {
                VERA.data0 = CYAN;
            }
        }
    }
}
But I have no idea, how.
Maybe you could explain, what's going on here, and how I'm finally getting to my "plot()"-function. A working code example in C would be nice.

Re: Using bitmap mode from C

Posted: Thu Oct 26, 2023 10:08 am
by rorodev
The "problem" is that the existing C examples show nicely how to use the bitmap buffer in a sequential order. They set the address and increment at the beginning and then send data to VERA.data0, filling the bitmap pixel by pixel.

For a proper plot() and line() function, you must change the address and/or increment each time before sending the next data to VERA.data0

I hope I have time over the weekend to create helper functions that include everything. It should not be too complicated.

What I don't know at the moment is how performant that will be. I also experiment with an internal buffer where I track the changes and update the pixels that have changed per frame. But that's probably more related to my use case and has the disadvantage of using additional memory.

Re: Using bitmap mode from C

Posted: Thu Oct 26, 2023 12:47 pm
by rorodev
After researching some code, I have at least a base to draw pixels that work roughly lol:
I started understanding how all works together, but there is still a long way to go.
The next step is to port my game code and see how it works using that SET_PIXEL() function.
After I try to improve all and make it work independent of different video modes

Code is here:
https://github.com/rogerboesch/x16-helloworld-macos/

(Comments, tips, etc., highly welcome)

Re: Using bitmap mode from C

Posted: Thu Oct 26, 2023 4:11 pm
by TomXP411
If you search the board for "vera tutorial"

search.php?st=0&sk=t&sd=d&sr=posts&keyw ... a+tutorial

This thread comes up:

viewtopic.php?t=6589

See if that comes closer to meeting your needs.