printf ( Control Codes )

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

printf ( Control Codes )

Post by Zetr0 »

Hello there my fellow Z88dk'ers!

I have recently got back into some z80 spectrum coding and messing about with some old code that I have written and well, I am a little perplexed and it's my hope that someone can point out how much of a "special" I am ;)

lets get to some code

Code: Select all

#include <stdio.h>

// ZX Spectrum Colours
#define BLACK   0
#define BLUE    1
#define RED     2
#define MAGENTA 3
#define GREEN   4
#define CYAN    5
#define YELLOW  6
#define WHITE   7

// ZX Specrtum Print Control Codes
#define _INK      16
#define _PAPER    17
#define _BRIGHT   19
#define _FLASH    18
#define _OVER     21
#define _INVERSE  20
#define _AT       22

void printf_at( unsigned char x, unsigned char y, char *str);     // print at cell postion
void printf_at( unsigned char x, unsigned char y, char *str)
{
     printf("%c%c%c%s", 22, y+32, x+32, str );    // put the text to the screen at text cell x,y
     								    // note that cell x is determined by the character column width
}
/* example -
        printf_at( x, y, "some text");         ( auto terminates string when parsed on stack)
        printf_at( x, y, title_string);        ( takes terminated string )
*/

int main( void )
{
	char msg[50];
	
    unsigned char _black   = 0;
    unsigned char _blue    = 1;
    unsigned char _red     = 2;
    unsigned char _green   = 3;
    unsigned char _magenta = 4;
    unsigned char _cyan    = 5;
    unsigned char _yellow = 6;
    unsigned char _white = 7;

    unsigned char _on  = 0;
    unsigned char _off = 0;

    unsigned char _ink   = 16;
    unsigned char _paper = 17;

    sprintf( msg,"%c%c%c%c%cTest Print 0\0", _AT, 10+32, 0+32, _ink, _black);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 1\0", _AT, 11+32, 0+32, _ink, _blue);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 2\0", _AT, 12+32, 0+32, _ink, _red);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 3\0", _AT, 13+32, 0+32, _ink, _green);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 4\0", _AT, 14+32, 0+32, _ink, _magenta);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 5\0", _AT, 15+32, 0+32, _ink, _cyan);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 6\0", _AT, 16+32, 0+32, _ink, _yellow);
    printf( "%s",msg );
    sprintf( msg,"%c%c%c%c%cTest Print 7\0", _AT, 17+32, 0+32, _ink, _white);
    printf( "%s",msg );
		
    return 1;
}

so lets look at the compile line

Code: Select all

#!/bin/bash
zcc +zx -startup=5 -lp3 -DPLUS3 -lm -create-app zxmp.c > build.txt
*NB just a quick note, how cool is it to have Z88dk on my Xubuntu Laptop!!!!


However, here is the output
Image

clearly I have messed up and after a few hours reading / testing... kinda need a clue =)

The "printf_at()" works as expected, the Y is 0 - 23 cells and the X is 0 to 63 columns, however will produces the colour / attribute errors in the above picture.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: printf ( Control Codes )

Post by dom »

Take a look at this: https://github.com/z88dk/z88dk/wiki/Cla ... ur-mapping and add in that pragma if you want to use the natural +zx colour ids.
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

Re: printf ( Control Codes ) *update 1*

Post by Zetr0 »

As always a huge thanks Dom!

After familiarizing myself with printf control codes via conio.h I was able to make it work with just stdio.h library

I am posting here so that it may help others and remind me of what I am supposed to do when I forget and not to bug the mods ;)

Interestingly I have tested various 'C' implementations of getting "console" data to the screen, except for assembly call to ROM function, the fastest I have found is using functions from <conio.h> - this will add a little more size to the code base.
  • 1. gotoxy( int x, int y);
    2. textbackground( int enum_colour );
    3. textcolor( int enum_colour );
then followed by printf("",...);

the enum_colours are more PC centric than ZX Spectrum so you will need to either build your own or use the defines of conio.h - I decided that I really want to stick to stdio.h so I used these defines -

Code: Select all

// printf colours ( zx spectrum organized )
#define _black          0           // zx spectrum colour 0
#define _blue           1           // zx spectrum colour 1
#define _red            4           // zx spectrum colour 2
#define _magenta        5           // zx spectrum colour 3
#define _green          2           // zx spectrum colour 4
#define _cyan           3           // zx spectrum colour 5
#define _yellow         14          // zx spectrum colour 6
#define _white          7           // zx spectrum colour 7
and this enum respectively

Code: Select all

unsigned char zx_colour[ 8 ];

void init_zxcolours( void )
{							/* enum colours for zx spectrum */
    zx_colour[ 0 ] = 0;  // black
    zx_colour[ 1 ] = 1;  // blue
    zx_colour[ 2 ] = 4;  // red
    zx_colour[ 3 ] = 5;  // magenta
    zx_colour[ 4 ] = 2;  // green
    zx_colour[ 5 ] = 3;  // cyan
    zx_colour[ 6 ] = 14; // yellow
    zx_colour[ 7 ] = 7;  // white
}
With the above, I can reference in code in various ways

Code: Select all

colour = zx_colour[ index ];
colour = zx_colour[ _black ];
some example code using "gotoxy(); textcolour(); and textbackground();" from <conio.h>

Code: Select all

#include <stdio.h>
#include <conio.h>

unsigned char zx_colours[ 8 ];

void init_zxcolours( void );

int main( void )
{
	for( unsigned char i = 0; i < 8; i += 1)
	{	
		gotoxy( 2, 2 + i );
        	textbackground( zx_colour[ i ]);
        	textcolor( zx_colour[ 7 - i ]);
        	printf("Hello World %d", i);
	}
	return 1;
}
*NB

I did try to use the pragma mentioned earlier -

Code: Select all

#pragma-define:CLIB_CONIO_NATIVE_COLOUR=1
However, I suspect I have used this incorrectly, as it made no difference to printf colour codes.

I will mention that I am using Xubuntu with Visual Studio Code ( with the z88dk extension ) and obviously the "snap-store" version of z88dk - I just loved the whole availability and little effort to get up and running with C for the z80

Now with that said, let's move onto update two =)
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

Re: printf ( Control Codes ) "update 2"

Post by Zetr0 »

Colour & Control Codes

So moving the above example over to using just using the printf function

Code: Select all

#include <stdio.h>

// printf control characters
#define zxp_at          22
#define zxp_paper       17
#define zxp_ink         16
#define zxp_bright      19
#define zxp_flash       18
#define zxp_over        21       
#define zxp_inverse     20
#define zxp_tab         23
I will be using the following character array to store the colour code values for the zx spectrum

Code: Select all

unsigned char zx_colour[ 8 ];

void init_zxcolours( void );
void init_zxcolours( void )
{ /* zx spectrum enum colours for printf */
    zx_colour[ 0 ] = 0;  // black
    zx_colour[ 1 ] = 1;  // blue
    zx_colour[ 2 ] = 4;  // red
    zx_colour[ 3 ] = 5;  // magenta
    zx_colour[ 4 ] = 2;  // green
    zx_colour[ 5 ] = 3;  // cyan
    zx_colour[ 6 ] = 14; // yellow
    zx_colour[ 7 ] = 7;  // white
}
lets put something to the screen

Code: Select all


int main ( void )
{
	unsigned char character_mode = 32;
	
	//set print render to BIGHT MODE
	printf("%c%c", zxp_bright, 1 );
	
	//set print position
	printf("%c%c%c", zxp_at, character_mode + 5, character_mode + 10 );  /* note that it is row ( y ) and then column ( x ) */
	
	//set print paper colour
	printf("%c%c", zxp_paper, 0);
	
	//set print ink colour
	printf("%c%c", zxp_ink, 7);
	
	//print text to screen
	printf("Hello World!");
	
	return 1;
}
Image

Whoot!

Now lets make it do something a little more interesting

Code: Select all

int main( void )
{
	unsigned char str[10];
	unsigned char character_mode = 32;
	unsigned char x = 4, y = 5;
	
	x = x + character_mode;
	y = y + character_mode;
	
	//set the BRIGHT on
	printf("%c%c", zxp_bright, 1 );
	
	for( unsigned char i = 0; i < 8; i += 1 )
	{
		// set paper
		printf("%c%c", zxp_paper, zx_colour[ i ] );		
		
        	//set ink
		printf("%c%c", zxp_ink, zx_colour[ 7 - i ] );
		
		//set cursor position
		printf("%c%c%c", zxp_at, y + i,  x );
		
		//send text
		printf("Hello World %d", i);
	}
}
Image
Noice!

FYI -for those reading this - the "character_mode" is an off-set for the printf when running 64 column mode - one could define it or just slap a "+32" to the X/Y coordinate when using printf - please note that this is not used with "gotoxy( x, y );"

now, how about we make a nice simple function, eh?
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

Re: printf ( Control Codes ) "update 3"

Post by Zetr0 »

Making it a function

So here is a function

Code: Select all

void test_printer( char *str, char x, char y, char paper, char ink, char bright, char flash )
{
    unsigned char print_code[ 14 ];
    unsigned char character_mode = 32;

    // one printf statement to rule them all - so lets make a string of print codes

    //set position
    print_code[ 0 ] = zxp_at;
    print_code[ 1 ] = y + character_mode;
    print_code[ 2 ] = x + character_mode;

    // set colours
    print_code[ 3 ] = zxp_paper;
    print_code[ 4 ] = paper;
    print_code[ 5 ] = zxp_ink;
    print_code[ 6 ] = ink;

    // set extended attibute(s)
    print_code[ 7 ] = zxp_bright;
    print_code[ 8 ] = bright;
    print_code[ 9 ] = zxp_flash;
    print_code[ 10 ] = flash;

    // terminate the string ( truly, its for the best... )
    print_code[ 11 ] = '\0';

    // slap it to the screen console
    printf("%s%s", print_code, str);
}
So now lets execute that function with this call

Code: Select all

test_printer( "Hello World",5,10, zx_colour[ 1 ], zx_colour[ 6 ], 1, 0);
and voilà....

Image

Oh.... well thats not quite right is it..... whats happened there??
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

Re: printf ( Control Codes ) "update 4"

Post by Zetr0 »

Well I must admit, I have a sneak suspicion it might be the '\0' terminator at the end of the print_code string.

now since we are going to be using it a string we need to know when it ends or printf will likely burn the house down with rage...

Hmmm instead of a NULL lets fix the size of the print_code array to no more that is needed.

Code: Select all

void test_printer( char *str, char x, char y, char paper, char ink, char bright, char flash );
void test_printer( char *str, char x, char y, char paper, char ink, char bright, char flash )
{

    unsigned char print_code[ 11 ];
    unsigned char character_mode = 32;

    // one printf statement to rule them all - so lets make a string of print codes

    //set position
    print_code[ 0 ] = zxp_at;
    print_code[ 1 ] = y + character_mode;
    print_code[ 2 ] = x + character_mode;

    // set colours
    print_code[ 3 ] = zxp_paper;
    print_code[ 4 ] = paper;
    print_code[ 5 ] = zxp_ink;
    print_code[ 6 ] = ink;

    // set extended attibute(s)
    print_code[ 7 ] = zxp_bright;
    print_code[ 8 ] = bright;
    print_code[ 9 ] = zxp_flash;
    print_code[ 10 ] = flash;

    // slap to the screen
    printf("%s%s", print_code, str);
}
and....
Image

Well... thats a thing now... so how about we split the printf statement

Code: Select all

	//slap to the screen
	printf("%s", print_code);
	printf("%s", str);
and...... nope.... same problem....

lets use the elements of print_code instead of the string..

Code: Select all

    // slap to the screen
    printf("%c%c%c%c%c%c%c%c%c%c%c%s", print_code[ 0 ],
                                       print_code[ 1 ],
                                       print_code[ 2 ],
                                       print_code[ 3 ],
                                       print_code[ 4 ],
                                       print_code[ 5 ],
                                       print_code[ 6 ],
                                       print_code[ 7 ],
                                       print_code[ 8 ],
                                       print_code[ 9 ],
                                       print_code[ 10 ],
                                       str);
and -
Image

Well thats much better than before.... but its still quite not right as though the text has been shifted 1 column to the right.

I suspect that one of the command codes has not been parsed or input correctly. but its surprising that by using elements as opposed
to a string we get a different result. is one of the codes expecting two bytes of character information, or just none?

more investigation to continue
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

Re: printf ( Control Codes )

Post by Zetr0 »

soo... errr about that shuffle of characters 1 space to the right...

thats on me... my bad.... i did that.....

I was sending it a value of x = 5 in the functions, and it was 4 in the other one.... soo.... yeah.... totally my fault

with that lets have a look -

Code: Select all

#define cmode64 32

void zx_printf( char *str, char x, char y, char ink, char paper, char bright, char flash, char over);
void zx_printf( char *str, char x, char y, char ink, char paper, char bright, char flash, char over)
{
	printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%s,	zxp_at, 
						y + cmode64, 
						x + cmode64, 
						zxp_paper, 
						paper, 
						zxp_ink, 
						ink,
						zxp_bright,
						bright,
						zxp_flash,
						flash,
						zxp_over,
						over,
						str); 
}
and we call it with

Code: Select all

int main( void )
{
	 zx_printf( "Hello World\0", 4, 10, zx_colour[ 0 ], zx_colour[ 5 ], 1, 0, 0);
	 
	 return 1;
}
and thus -

Image

Whoohoo!
Zetr0
Member
Posts: 59
Joined: Mon Aug 22, 2011 12:38 pm

Re: printf ( Control Codes )

Post by Zetr0 »

My hope is that this thread will explain a little about the print driver, or more so how to use it.

With one last hurrah!

Code: Select all

        for( unsigned char i = 0; i < 8; i += 1)
        {
            sprintf( str, "Hello World %d\0", i);
            zx_printf( str, 4, 5+i, zx_colour[ i ], zx_colour[ 7 - i ], 1, 0, 0 );
        }
Image

\O/\o/ - ! H u r r a h ! - \o/\O/

*NB
Some interesting things to mention, when creating a string of codes to send to the printf driver, using the following

Code: Select all

	printf("%s%s", print_codes, str );
It doesn't render the first element of str string - in fact, it would leave it blank.

Where "Hello World" would appear as " ello World" ( note the space at the beginning )

However, if I insert a space in the format

Code: Select all

	printf("%s %s", print_codes, str );
This would, in fact, print correctly.

the more you know.
stefano
Well known member
Posts: 2137
Joined: Mon Jul 16, 2007 7:39 pm

Re: printf ( Control Codes )

Post by stefano »

That's the expected standard behaviour of the printf formatter then %s is used.
For control codes you should use %c or even a lower level console access, otherwise a value equivalent to zero, such as the black colour or a cursor coordinate will be interpreted as a string termination and never used.

For portability and further control you could use the cross-lib by Fabrizio Caruso, I think you could enjoy it.. but it's another story
Post Reply