How to print UDG characters with console directives in newlib?

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
Fabrizio
Member
Posts: 86
Joined: Tue Jul 11, 2017 9:28 pm

How to print UDG characters with console directives in newlib?

Post by Fabrizio »

I have been able to redefine characters and use them with the classic lib through
the printf command by using ASCII codes starting from 128.

This does not work if I use the new lib. Which characters correspond to user defined characters when using the new lib?

Fabrizio
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Fabrizio wrote:I have been able to redefine characters and use them with the classic lib through
the printf command by using ASCII codes starting from 128.
This does not work if I use the new lib. Which characters correspond to user defined characters when using the new lib?
Are you using startup=0/1 ? (default=0).
These drivers do not have any concept of UDGs. Instead you can change the font address to point at graphics characters and print those.

There are a couple of ways to do this. One will work with both startup=0 and startup=1:

Code: Select all

// zcc +zx -vn -clib=new test.c -o test -create-app
// zcc +zx -vn -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app

#pragma printf = ""   // list converters you need to reduce printf size

#include <stdio.h>
#include <stropts.h>
#include <arch/zx.h>

unsigned char udg[] = {
        0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa,   // hash
        0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,   // vertical strips
};

void *old_font;

void main(void)
{
        zx_cls(INK_BLACK | PAPER_WHITE);
        
        printf("Testing... ");
        
        old_font = (void*)ioctl(1, IOCTL_OTERM_FONT, udg - 'A'*8);
        
        printf("ABABAB\n");
        
        ioctl(1, IOCTL_OTERM_FONT, old_font);
        
        printf("Back to normal...\n");
}
This one uses an ioctl to change the font on fd=1 (stdout). ioctls are direct communication with the driver. The font address points at the character definition for ascii code 0. Since each char is 8 bytes, I'm adjusting the udg address backwards so that letters A and B will print as the udgs defined in the array.


This second method will only work with startup=1. This is because the font will be changed in the text stream with control codes and only startup=1 understands control codes.

Code: Select all

// zcc +zx -vn -startup=1 -clib=new test.c -o test -create-app
// zcc +zx -vn -startup=1 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app

#pragma printf = "%c"   // list converters you need to reduce printf size

#include <stdio.h>
#include <stropts.h>
#include <arch/zx.h>

unsigned char udg[] = {
        0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa,   // hash
        0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,   // vertical strips
};

extern unsigned char font_8x8_rom[];   // this points at the zx font in rom

#define NEWFONT_W "\x02%c%c"
#define ARG_W(x)  (x)&0xff, (x)/256

void main(void)
{
        zx_cls(INK_BLACK | PAPER_WHITE);
        printf("Testing... " NEWFONT_W "ABABAB\n" NEWFONT_W "Back to normal...\n",
           ARG_W((unsigned int)(udg-'A'*8)), ARG_W((unsigned int)(font_8x8_rom-256)));
}
I'm not sure if the macros make it any clearer but to change font, you send \x02 followed by the two bytes of the font address LSB first. The font address is again the address of ascii code 0. "font_8x8_rom" is an internal symbol in the library that points at the rom font, but it points at ascii code 32 so 256 must be subtracted.


Lastly, startup=30 was added in the past week. This is a lightweight driver that uses the rom routine at 0x10 to print a character and will be much smaller than the other drivers. This startup has no input capability (ie no scanf). Because the rom is used, you must not overwrite the system variables area. The default compile address of 32768 is far away from the system variables area at ~24000.

This one behaves like basic - its control codes will be used and it can print UDG characters. In Sinclair basic, codes 144 and up are UDGs that are stored in upper memory. This example here moves the UDGs to a different place by poking a value into the system variables.

Code: Select all

// zcc +zx -vn -startup=30 -clib=new test.c -o test -create-app
// zcc +zx -vn -startup=30 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app

#pragma printf = ""   // list converters you need to reduce printf size

#include <stdio.h>
#include <stropts.h>
#include <arch/zx.h>
#include <z80.h>

unsigned char udg[] = {
        0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa,   // hash
        0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,   // vertical strips
};

void main(void)
{
        zx_cls(INK_BLACK | PAPER_WHITE);
        
        z80_wpoke(23675, (unsigned int)udg);   // move UDGs by poking system variable at 23675
        
        printf("Testing... \x90\x91\x90\x91\x90\x91\nBack to normal...\n");
}
\x90 is hex for 144 so that will be the first character in the udg definitions.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I should add for startup=30 and the rom 0x10 driver, you can also change the font address by poking a word to 23606. If I remember right, this will be the font address of ascii code 0 as well. You can have your own character set and redefine some of them to graphics so you don't have to fiddle with codes 144+.
Fabrizio
Member
Posts: 86
Joined: Tue Jul 11, 2017 9:28 pm

Post by Fabrizio »

Thanks for the reply!

I would like to use the simple BASIC-like solution but if I change startup=1 to startup=30 I get wrong/unexpected results and the game freezes.

Remark: My code works fine with -startup=1 except for missing USG support.

I am compiling with
zcc +zx -DDEBUG_CHARACTERS -startup=30 -clib=sdcc_iy -vn -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -create-app -o %deliverables%\ZXSpectrum_32col_sdcc_experimental.prg %mypath%\sleep_macros.c %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

I am using the very latest nightly build as of August 5th.

I would like to use whatever works with -startup=1 + UDG possibly through simple console directives.

Fabrizio
Fabrizio
Member
Posts: 86
Joined: Tue Jul 11, 2017 9:28 pm

Post by Fabrizio »

What I am doing is something similar to BASIC.

My code works with classic lib but classic lib makes my game unstable (I suspect classic lib is broken but I am not sure).

#define UDG_BASE 0xFF58

static const char player_up[8] = { 24, 60, 24,102,153, 24, 36,102};

void redefine(unsigned long loc, const unsigned char * data)
{
unsigned short i;
for(i=0;i<8;++i)
{
POKE(loc+i,data);
}
}

redefine(UDG_BASE,player_down); // 0x90

I expect 0x90 to be the UDG with my newly redefined character but with newlib and startup=1 if I print 0x90 I get nothing.
On the other end, this works fine with the classic lib.

If possible I do not want to redefine standard characters.


Fabrizio
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Yes there is no concept of UDG in newlib/startup=0 or 1. It doesn't make a special case for character codes 144 and up so you instead have to change character set to point at your udgs as was done in the examples.

startup=30, on the other hand, is using the basic rom and it will do udgs for character codes 144 and up.

I will have a look at these things later tonight - I am out most of the day unfortunately.
stefano
Well known member
Posts: 1632
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

A totally different approach could be possible with the Spectrum's ansi emulation, e.g. in 32 columns or similar non packed mode, using the ansifont pointer to redefine some font character.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Fabrizio wrote:I am compiling with
zcc +zx -DDEBUG_CHARACTERS -startup=30 -clib=sdcc_iy -vn -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -create-app -o %deliverables%\ZXSpectrum_32col_sdcc_experimental.prg %mypath%\sleep_macros.c %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c
I think the crash is down to different use of the control codes. If a print at using out of bounds coordinates is done, the rom may try to return an error to basic and this would probably cause the program to crash. You have to be a little careful with using startup=30 because it's the rom code that is doing the printing.

I see defines for CLS as \x0c. There is no clear screen control code in Sinclair basic (and therefore startup=30). Have a look at http://www.worldofspectrum.org/ZXBasicM ... nappa.html for codes less than 32. There are only a few control codes: 6,8-11,13,16-23. To clear screen you'll have to do it with zx_cls or something and move the cursor to 0,0 if that's needed.

Next the part that is probably causing a problem is print at. To move the cursor you send char code 22 (0x16) followed by ROW then COL which is the opposite of startup=0/1. It's confusing but it's done this way because we want all machines in newlib to use normal x,y order but since startup=30 is the basic rom, it expects y,x order. The coordinates themselves are not offset in any way. So if you want to print at y=0,x=1 you send 22,0,1. This will be the same for ink, paper controls, etc.

I think this is the problem. Let me know if things still do not work.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

btw, I simplified your compile line like this to avoid the makefile (I'm using windows):

zcc +zx -vn -DDEBUG_CHARACTERS -DSPECTRUM_32COL -startup=30 -clib=sdcc_iy -SO3 -create-app -o acm @zproject.lst

where zproject.lst contains:

sleep_macros.c
display_macros.c
powerUps.c
enemy.c
invincible_enemy.c
level.c
character.c
text.c
missile.c
strategy.c
input.c
main.c

You probably want to remain consistent across all your targets but just fyi to let you know you can do this.


For the sleep_macros, newlib provides a cross platform delay function in z80.h called z80_delay_ms that will do an exact delay in milliseconds. This doesn't count any interrupts that may slow things down further. It uses platforms' declared clock rate to correctly implement the delay. At this time it's not in the classic lib however.
Fabrizio
Member
Posts: 86
Joined: Tue Jul 11, 2017 9:28 pm

Post by Fabrizio »

How can I redefine 13 single characters with startup=1?
I am storing UDG as in BASIC at 0xFF58.

Which characters am I redefining with
printf("\x02%c%c", 0x58, 0xFF)
?
All of them starting from character 0? How do I print character 0?
You mean character '0' or character=0.
In any case the above command messes up all characters...


What I need to do is to define the following new characters and have them associated to some non-letter and non-digit characters:
static const char player_down[8] = { 24, 36, 24,102,153, 24, 36,102};
static const char player_up[8] = { 24, 60, 24,102,153, 24, 36,102};
static const char player_right[8] = { 24, 52, 25,118,152, 24, 20, 20};
static const char player_left[8] = { 24, 44,152,110, 25, 24, 40, 40};
static const char ghost[8] = {129,126,165,129,129,189,129,126};
static const char missile_right[8] = { 0, 0, 15,252,252, 15, 0, 0};
static const char missile_left[8] = { 0, 0,240, 63, 63,240, 0, 0};
static const char invincible_ghost[8] = { 60, 66,165,129, 90, 36, 36, 60};
static const char gun[8] = { 0,128,126,200,248,192,128, 0};
static const char powerUp[8] = { 0, 60, 54,223,231,122, 36, 24};
static const char missile[8] = { 0, 0, 8, 56, 28, 16, 0, 0};
static const char bomb[8] = { 60, 66,165,153,153,165, 66, 60};
static const char bubble[8] = { 24, 60, 60, 60,126, 90, 66, 66};

How can I do this without having to redefine the full font set?
Due to the classic lib + old compiler issues, I need to use the new lib.
startup=30 freezes my program and startup=1 seems to work fine except for the udg.
So I would like to use whatever works with startup=1.

Fabrizio
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Fabrizio wrote:How can I redefine 13 single characters with startup=1?
I am storing UDG as in BASIC at 0xFF58.

Which characters am I redefining with
printf("\x02%c%c", 0x58, 0xFF)
?
Yes that will move the font address to 0xff58 but starting at ascii code 0, which is not printable without some pain.

Better maybe is to fit it in with how you're doing it now and map that to code 144 by subtracting 144*8 from that address:

#define UDG_START (0xff58 - 144*8)
printf("\x02%c%c", UDG_START & 0xff, UDG_START/256);

Then if you print codes 144, etc you will see those udgs.

But you will have to change the font address back again to print regular text as in the example with "font_8x8_rom-256".
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

How can I do this without having to redefine the full font set?
So I would like to use whatever works with startup=1.
Because startup=1 does not know about UDGs, it can only print patterns out of one font. The method above has you changing font to print graphics, then back again to print regular text.

However, if you have the memory space you can easily construct a new font in ram by copying the one in rom and then you can put your udg graphics into this ram character set, point the driver at this character set and you won't have to change font at all. The total memory space requirement is 8 bytes per character. If you copy ascii codes 32-127 from the rom, then add your udgs (say N of them) to codes 128 to 128+N-1 then your total memory requirement is 8*(128-32+N) bytes.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Here's an easy way to do it, maybe not the prettiest. Suppose you have UDG_N udgs:

Code: Select all

#define UDG_N  10

// make space for a custom character set

unsigned char my_font[(128-32+UDG_N)*8];

// at initialization copy rom font into this character set

#include <string.h>

extern unsigned char font_8x8_rom[];   // this points at the zx font in rom ascii code 32

memcpy(my_font, font_8x8_rom, (128-32)*8);

// now put your UDG_N udgs into code 128+

memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);

// point the driver's font to your custom character set

ioctl(1, IOCTL_OTERM_FONT, (void*)(my_font - 256));
Now you can print like normal and if you print codes 128+ you will see UDGs.
Fabrizio
Member
Posts: 86
Joined: Tue Jul 11, 2017 9:28 pm

Post by Fabrizio »

Thanks!

Probably you meant:
memcpy(my_font+(128-32)*8, udg_definitions, UDG_N*8);
instead of
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);

because with my change I get the redefined characters even if not the order I was expecting.
I can easily fix it anyway.


This is what I did:

#define UDG_N 13

#include <stropts.h>

unsigned char my_font[(128-32+UDG_N)*8];

extern unsigned char font_8x8_rom[];

static const char udg_definitions[] = {
24, 36, 24,102,153, 24, 36,102, // player_down
24, 60, 24,102,153, 24, 36,102,
24, 52, 25,118,152, 24, 20, 20,
24, 44,152,110, 25, 24, 40, 40,
129,126,165,129,129,189,129,126,
0, 0, 15,252,252, 15, 0, 0,
0, 0,240, 63, 63,240, 0, 0,
60, 66,165,129, 90, 36, 36, 60,
0,128,126,200,248,192,128, 0,
0, 60, 54,223,231,122, 36, 24,
0, 0, 8, 56, 28, 16, 0, 0,
60, 66,165,153,153,165, 66, 60,
24, 60, 60, 60,126, 90, 66, 66
};

memcpy(my_font, font_8x8_rom, (128-32)*8);
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);
ioctl(1, IOCTL_OTERM_FONT, (void*)(my_font - 256));

Then I used:
GHOST_IMAGE._imageData = 139;
INVINCIBLE_GHOST_IMAGE._imageData = 134;
BOMB_IMAGE._imageData = 138;
PLAYER_IMAGE._imageData = 128;
POWERUP_IMAGE._imageData = 136;
GUN_IMAGE._imageData = 135;
MISSILE_IMAGE._imageData = 137;
LEFT_ENEMY_MISSILE_IMAGE._imageData = 133;
RIGHT_ENEMY_MISSILE_IMAGE._imageData = 132;
BUBBLE_IMAGE._imageData = 140;

Regards

Fabrizio
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Yes that's right I made a mistake there. Looks good!
Post Reply