Monthly Archives: February 2022

X68000 Programming chapter 3, Anatomy of a program

In this article I’m going to explain the structure I use in my X68000 programs.

I normally declare several variables and a series of functions before the main() function which I implement afterwards and are to interact with the computer.

If you read the two previous articles, we can work with two different toolchains, Lydux’s and Jason Stevens’ based on MARIKO which is the original.

As the two toolchains use different header names and Lydux doesn’t have an alias for interruptions I do therefore two branches of compilation with the pre processor which checks whether __MARIKO_CC__ is declared.

#ifdef __MARIKO_CC__
    #include <doslib.h>
    #include <iocslib.h>
#else
    #include <dos.h>
    #include <iocs.h>
    #define interrupt __attribute__ ((interrupt_handler))
#endif

Then, depending on whether we compile with one or the other we use some headers or the others and we declare the interruption alias.

I also include these two libraries:

#include <stdio.h>
#include <signal.h>

The first one is the ever stdio.h which declares constants like NULL and with which we can do standard stuff.

The second library is signal.h for capturing events like pressing Ctrl+C keys to end the program and take us back to the operating system. In this way we can free resources and close the program in a clean manner.

It’s important to do this because we could have changed the screen mode and if we just come out the program, when we are back to Human68k the video mode remains the same and we have to restart the computer.

Then I declare these two variables:

char last_mode;

volatile int _timer = 0;

The variable last_mode is to capture the video mode there was when the program started so that should we change the video mode we can restore it.

The variable _timer is to set up a timer we can use to measure time.
It has to be volatile to prevent the compiler from eliminate it when optimizing the program because it doesn’t understand how we use it.

Then I declare a number of interruptions. For those who don’t know what an interruption is, an Interruption is a function that we attach to an event of the operating system in order to run certain code we want when the event happens.
These interruptions are:

void interrupt timer();         // for timing

void interrupt vsync_disp();    // for vertical sync

void interrupt hsyncst();       // for horizontal sync

void interrupt crtcras();       // for raster sync

The interruption timer() can be attached to the system timer for which we set the frequency it has to be called with and we can do an increment on the variable _timer.

This interruption looks like this:

void interrupt timer()
{
    ++_timer;
}

The interruption vsync_disp can be attached to the vertical synchronization event. This event happens every time the X68000 is going to draw the screen. It can be use to synchronize the program with the screen refresh. This event happens 60 times per second.

The interruption hsyncst is similar to vsync_disp but it triggers every time the X68000 is going to draw a horizontal line. This is that if there are some 512 lines and the screen is drawn 60 times per second, this event is called around 31000 times per second.

It can be used to make special effects with the scan line like changing the palette as the screen is drawn or change sprites position so that it looks there are more sprites.
The code in this interruption has to be extremely fast and optimized otherwise it will never finish running as it gets called again and again before it ends and the computer will block.

The interruption crtcras is like hsyncst but it only triggers when the scan line is on certain line we set before hand.
As it doesn’t get triggered on each line but only in the one we want we have more time to do things than with hsyncst. We can even set the next line we want to trigger it on in the same photogram if we want to.

It’s important that if these interruptions use global variables they are declared volatile.

This is because interruptions aren’t functions called by our program but by the operating system and the compiler doesn’t understand the context in which they access the variables and its optimizations might generate assembly code that don’t use them altogether so that they simply won’t work.

I’ll make an article explaining interrupts more in depth. It’s just that it’s good to know that they exist.

After the interruptions I declare these to functions:

void init();
void terminate();

The function init() is for initializing stuff. For example it’s the function were I capture the screen mode with the command:

last_mode = _iocs_crtmod(-1);   //capture the video mode before the program

Then I usually deactivate the command line cursor:

_iocs_b_curoff(); //disable the cursor

I clear the screen. This isn’t necessary when changing resolution:

_iocs_b_clr_al(); //clear the whole screen

Here I also set up the timer interrupt:

 _iocs_timerdst(
	timer,  //Processing address (interrupt disabled at 0)
	7,       //Unit time (1 = 1.0, 2 = 2.5, 3 = 4.0, 4 = 12.5, 5 = 16.0, 6 = 25.0, 7 = 50.0, micro sec unit)
	20       //Counter (when 0, treat as 256)
);

The first param is the interrupt name. The second is the time unit with it’s 50 microseconds. The third param is the frequency, how many times the unit chosen between executions.
In this example every 20 times 50 microsecs which is 1000 microsecs or every 1 millisecond.

Many things can be done in this function obviously. It’s not mandatory to do it this way but I think it’s a good practice.

Finally I attach the function terminate() to the signal the operating system sends to the program when we press Ctrl+C so that it gets called when this happens.

The function terminate() does the opposite to init() which is to free resources.

In my example I detach the interruptions I might have attached before:

_iocs_vdispst(
    (void *)NULL,
    0,  //0: vertical blanking interval 1: vertical display period
    0
);

_iocs_hsyncst ((void *)NULL); //horizontal interrupt

_iocs_crtcras (
    (void *)NULL,
    0 //int_ luster
);

_iocs_timerdst(
    (void *)NULL,   //Processing address (interrupt disabled at 0)
    0,              //Unit time (1 = 1.0, 2 = 2.5, 3 = 4.0, 4 = 12.5, 5 = 16.0, 6 = 25.0, 7 = 50.0, micro sec unit)
    0               //Counter (when 0, treat as 256)
);

This is important because hadn’t done it, although the program is closed the interrupts are still there running.

I activate back the command line cursor:

_iocs_b_curon();

I restore the screen mode:

_iocs_crtmod(last_mode);

and I finally exit the program with:

_dos_exit();

Lastly I’m going to explain the main() function which is the entry point to our program.

I would start calling the init() function described earlier, then we would have the body of the program. In this case I print the obligated Hello World! And an indication to press a button.

init();

//body of our program
_dos_c_print("Hello world !\r\n");
_dos_c_print("Press a key.\r\n");

//Afterwards we have a loop that stops when pressing a key but prints the number of milliseconds passing otherwise so that we can see the counting.

while(_dos_inpout(0xFF) == 0){
	_dos_c_locate(0,2);
    printf("counting %d \r\n", _timer);
}

In the X68000 we can use _dos_c_print() or printf() to print strings but _dos_c_print() does not support formats. It’s strictly like the command puts() in C++.
With _dos_c_locate(0,2); we set the cursor on the column 0 and row 2 so that the next thing will print from there. Otherwise it will print next line bellow ant the next and so on.

Hitting a key takes out the loop and we call terminate() and the program ends.

The complete code looks like this:

 #ifdef __MARIKO_CC__
	#include <doslib.h>
	#include <iocslib.h>
#else
	#include <dos.h>
	#include <iocs.h>
	#define interrupt __attribute__ ((interrupt_handler))
#endif

#include <stdio.h>
#include <signal.h>

char last_mode;                 // video mode before the program started

volatile int _timer = 0;        // time

void interrupt timer();         // for timing

void interrupt vsync_disp();    // for vertical sync

void interrupt hsyncst();       // for horizontal sync

void interrupt crtcras();       // for raster sync

void init();

void terminate();

int main(void)
{
    init();

    //body of our program
    _dos_c_print("Hello world !\r\n");
    _dos_c_print("Press a key.\r\n");

    while(_dos_inpout(0xFF) == 0){
        _dos_c_locate(0,2);
        printf("counting %d \r\n", _timer);
    }

    //end of our program
    terminate();

    return 0;
}

void init()
{
    last_mode = _iocs_crtmod(-1);   //capture the video mode before the program

    _iocs_b_curoff(); //disable the cursor

    _iocs_b_clr_al(); //clear the whole screen

    /**
     * Other initialization actions.
     */

     _iocs_timerdst(
        timer,  //Processing address (interrupt disabled at 0)
        7,       //Unit time (1 = 1.0, 2 = 2.5, 3 = 4.0, 4 = 12.5, 5 = 16.0, 6 = 25.0, 7 = 50.0, micro sec unit)
        20       //Counter (when 0, treat as 256)
    );

    signal(SIGINT, terminate); //for the Control + C
}

void terminate()
{
    /* Un interrupt */
	_iocs_vdispst(
        (void *)NULL,
        0,//0: vertical blanking interval 1: vertical display period
        0
    );

    _iocs_hsyncst ((void *)NULL); //horizontal interrupt

     _iocs_crtcras (
        (void *)NULL,
        0 //int_ luster
    );

    _iocs_timerdst(
        (void *)NULL,   //Processing address (interrupt disabled at 0)
        0,              //Unit time (1 = 1.0, 2 = 2.5, 3 = 4.0, 4 = 12.5, 5 = 16.0, 6 = 25.0, 7 = 50.0, micro sec unit)
        0               //Counter (when 0, treat as 256)
    );

    //we activate the console cursor
    _iocs_b_curon();

    //we restore the video mode
	_iocs_crtmod(last_mode);

	//we exit the program
	_dos_exit();
}

void interrupt timer()
{
	++_timer;
}

void interrupt vsync_disp()
{
	//whatever you do in this event
}

void interrupt hsyncst()
{
	//whatever you do in this event
	//it has to be real quick
}

void interrupt crtcras()
{
	//whatever you do in this event
}

You can also find it in my Githup at https://github.com/FedericoTech/X68KTutorials/tree/main/Anatomy

I hope this is useful as a starting point to X68000 programming.

Enjoy.